ソースコードを見やすくしよう!「リファクタリング」②

この記事は、ソースコードを見やすくしよう!「リファクタリング」①からの続きです。

DRY『重複は悪』

    int result = OrderClose( OrderTicket(), OrderLots(), OrderClosePrice(), Slippage, clrRed );
    if(result < 0) {
       Print("注文に失敗しました");
     } else {
        bool orderSelectResult = OrderSelect (result, SELECT_BY_TICKET);
        Print ("注文に成功しました。決済価格は " + (string)OrderClosePrice() + " です。");
     }
  }
//ショートの時、パーフェクトオーダー終了で決済する
} else if (OrderType() == OP_SELL && PerfectOrderFalling ( smashort, smamiddle, smalong ) == false ) {
       int result = OrderClose( OrderTicket(), OrderLots(), OrderClosePrice(), Slippage, clrRed );
       if(result < 0) {
          Print("注文に失敗しました");
        } else {
           bool orderSelectResult = OrderSelect(result, SELECT_BY_TICKET);
           Print("注文に成功しました。決済価格は " + (string)OrderClosePrice() + "です。");

この中では、前回の記事で修正した10行目以外は内容が完全に重複しています。

プログラミングの世界には『DRY(Don’t repeat yourself)』という言葉があります。
日本語では『重複は悪』と言います。

プログラミングでは同じ命令のコードを重ねて書くことはバグの原因となり、また人間が見直す時も非常に見づらく、百害あって一利なしなのです。

 

今回の場合は実行内容が全く一緒なので、

if( 条件1 || 条件2 ){
    実行
}

↑このようなシンプルな形にできます。

 

//利益確定の条件(パーフェクトオーダー終了で決済)
   for( int i = OrdersTotal() - 1; i >= 0; i--) {
      if( OrderSelect ( i, SELECT_BY_POS )) {
         if(( OrderType() == OP_BUY && PerfectOrderIncrease ( smashort, smamiddle, smalong ) == false )          //買いの場合
                || (OrderType() == OP_SELL && PerfectOrderFalling ( smashort, smamiddle, smalong ) == false )){  //売りの場合
            int result = OrderClose( OrderTicket(), OrderLots(), OrderClosePrice(), Slippage, clrRed );
               if(result < 0) {
                  Print("注文に失敗しました");
               } else {
                  bool orderSelectResult = OrderSelect (result, SELECT_BY_TICKET);
                  Print ("注文に成功しました。決済価格は " + (string)OrderClosePrice() + " です。");
               }
            }
        }
    }

ここまで簡潔な形にできました!!

最初と比べてかなりスッキリしましたが、もう少し簡潔に書いていきます。

 

if( 条件1 == false ){

}

↑この形は

if( !条件1 ){

}

↑このような形で書くことができます。

カオチャイ
カオチャイ
条件の前に『 ! 』を付けたら『falseの時』という意味になり、なにもつけないと『trueの時』という意味になりますよ

 

//利益確定の条件(パーフェクトオーダー終了で決済)
   for( int i = OrdersTotal() - 1; i >= 0; i--) {
      if( OrderSelect ( i, SELECT_BY_POS )) {
         if(( OrderType() == OP_BUY && !PerfectOrderIncrease ( smashort, smamiddle, smalong ) )           //買いの場合
                || (OrderType() == OP_SELL && !PerfectOrderFalling ( smashort, smamiddle, smalong ))){    //売りの場合
            int result = OrderClose( OrderTicket(), OrderLots(), OrderClosePrice(), Slippage, clrRed );
               if(result < 0) {
                  Print("注文に失敗しました");
               } else {
                  bool orderSelectResult = OrderSelect (result, SELECT_BY_TICKET);
                  Print ("注文に成功しました。決済価格は " + (string)OrderClosePrice() + " です。");
               }
            }
        }
    }

↑このようになりました。

『 ! 』に関しては人によっては「『 == false 』のほうが見やすくて好き!」という人がいるかもしれませんが、お好みで選んでくださいね。

 

いらない関数を削除しよう

//エントリー状態であればエントリー許可を出さない
if( ENTRIED() == true ) {
   EntriePermission = false;
} else {
   EntriePermission = true;
}

//条件を満たせばエントリーする(終値監視オフの時)
if( EntriePermission == true && switchonoff == SWITCH_OFF )
//上昇パーフェクトオーダーおよび下ヒゲピンバー発生時、下ヒゲが中期化長期のSMAに触れるとロングエントリー
   if(PerfectOrder1increace( smashort, smamiddle, smalong ) == true &&
         PositivePinBar( ClosePrice, OpenPrice, HighPrice, LowPrice ) == true ) {
      if((MathMin(OpenPrice, ClosePrice) >= smamiddle && smamiddle >= LowPrice ) ||
            (MathMin(OpenPrice, ClosePrice) >= smalong && smalong >= LowPrice)) {
         EntriePermission = false;
         int result = OrderSend( Symbol(), OP_BUY, buy_lot, Ask, Slippage, Ask - buy_stoploss_pips * Point, 0, MagicNumber, 0, clrBlue );
      }
   }

次にエントリーを行う部分のコードを見てみましょう。条件が揃えばエントリーを出します。

 

まず、↓こちらについてです。

//エントリー状態であればエントリー許可を出さない
if( ENTRIED() == true ) {
   EntriePermission = false;
} else {
   EntriePermission = true;
}

 

これは

ENTRIED()がtrueのときEntriePermissionがfalse
ENTRIED()がfalseのときEntriePermissionがtrue

という意味で、

//条件を満たせばエントリーする(終値監視オフの時)
if( EntriePermission == true && switchonoff == SWITCH_OFF )

上記の部分にかかってきますので、

 

EntriePermissionがtrueのときif文の中が実行される

という意味になります。ということはですね・・・

 

//条件を満たせばエントリーする(終値監視オフの時)
if( ENTRIED() == false && switchonoff == SWITCH_OFF )

これでよくなります。

 

また、ENTRIED()関数ですが、どのような判定の関数なのか見ますと、

bool ENTRIED() {
   if(OrdersTotal() > 0) return true;
   else return false;
}

 

OrdersTotal()が1以上ならtrue
OrdersTotal()が0以下ならfalse

↑という意味です。なので

//条件を満たせばエントリーする(終値監視オフの時)
if( OedersTotal() == 0 && switchonoff == SWITCH_OFF )

↑関数を作らなくてもこれで大丈夫です。

 

//エントリー状態であればエントリー許可を出さない
if( ENTRIED() == true ) {
   EntriePermission = false;
} else {
   EntriePermission = true;
}

↑このif文はまるまる要りませんでしたね。

 

関数を作ってわかりやすくしよう

//上昇パーフェクトオーダーおよび下ヒゲピンバー発生時、下ヒゲが中期化長期のSMAに触れるとロングエントリー
   if(PerfectOrder1increace( smashort, smamiddle, smalong ) == true &&
         PositivePinBar( ClosePrice, OpenPrice, HighPrice, LowPrice ) == true ) {
      if((MathMin(OpenPrice, ClosePrice) >= smamiddle && smamiddle >= LowPrice ) ||
            (MathMin(OpenPrice, ClosePrice) >= smalong && smalong >= LowPrice)) {
         EntriePermission = false;
         int result = OrderSend( Symbol(), OP_BUY, buy_lot, Ask, Slippage, Ask - buy_stoploss_pips * Point, 0, MagicNumber, 0, clrBlue );
      }
   }

次のコードを見ていきます。

上記のコードはなにを表しているのかと言うとまずコード1行目部分、

if(PerfectOrder1increace( smashort, smamiddle, smalong ) == true &&

は、『上昇のパーフェクトオーダーが発生しているか』を判定します。

※パーフェクトオーダーの解説はこちら↓

 

次にコード部分2行目の

 PositivePinBar( ClosePrice, OpenPrice, HighPrice, LowPrice ) == true ){

は、『陽線のローソク足に下ヒゲのピンバーが発生しているか』を判定します。

※ピンバーの解説はこちら↓

 

次にコード部分3,4行目の

   if((MathMin(OpenPrice, ClosePrice) >= smamiddle && smamiddle >= LowPrice ) ||
         (MathMin(OpenPrice, ClosePrice) >= smalong && smalong >= LowPrice)) {

は、『下ヒゲが中期か長期のSMAに触れているか』を判定しています。

 

以上がエントリーをするための3つの条件ですが、ここまでで1点改善すべきポイントがあります。

3つ目の条件の『下ヒゲが中期か長期のSMAに触れているか』だけ、関数にしていないのです。

そこで下記のコードを別で作ります。

//下ヒゲが中期か長期のSMAに触れているかを判定する
bool UpperShadowTouchMA(double OpenPrice, double ClosePrice, double smamiddle, double smalong, double LowPrice) {
   if((MathMin(OpenPrice, ClosePrice) >= smamiddle && smamiddle >= LowPrice) ||
         (MathMin(OpenPrice, ClosePrice) >= smalong && smalong >= LowPrice)) return true;
   else return false;
}

 

そしてエントリー部分を整理します。

//上昇パーフェクトオーダーおよび下ヒゲピンバーが発生時、下ヒゲが中期か長期のSMAに触れるとロングエントリー
   if(PerfectOrderIncrease( smashort, smamiddle, smalong ) == true &&
         PositivePinBar( ClosePrice, OpenPrice, HighPrice, LowPrice ) == true &&
         UpperShadowTouchMA( OpenPrice, ClosePrice, smamiddle, smalong, LowPrice)) == true {

 

 

つづいて、↓こちら

EntriePermission = false;

こちらですが、これは『エントリーをするとエントリー許可をオフにする』という処理です。
元々は『条件が継続していれば連続してエントリーしてしまうことを防ぐ』つもりで作ったのかと思いますが、当記事の前半部分でいらないことが判明していますので削除します。

そんなこんなでリファクタリングが終了しました。

リファクタリング完成

//条件を満たせばエントリーする(終値監視オフの時)
if( OedersTotal() == 0 && switchonoff == SWITCH_OFF ) {
//上昇パーフェクトオーダーおよび下ヒゲピンバーが発生時、下ヒゲが中期か長期のSMAに触れるとロングエントリー
   if(PerfectOrderIncrease( smashort, smamiddle, smalong ) == true &&
         PositivePinBar( ClosePrice, OpenPrice, HighPrice, LowPrice ) == true &&
         UpperShadowTouchMA( OpenPrice, ClosePrice, smamiddle, smalong, LowPrice)) == true {
      int result = OrderSend( Symbol(), OP_BUY, buy_lot, Ask, Slippage, Ask - buy_stoploss_pips * Point, 0, MagicNumber, 0, clrBlue );
   }
}

 

結果的に今回のテーマになってしまったのが『関数化』というところだと思います。

修正箇所の1つ目が『不要な関数を削除することで簡潔化』できており、
修正箇所の2つ目が『新たな関数をつくることで簡潔化』できています。

ここはある程度経験が必要な部分なので精進してくださいね。

 

おまけ

EntriePermission

スルーしていましたが、一応コードのお話なので訂正を・・・

エントリーの正しいスペルは『Entry』です。

 

作ったツールのコピーを防ぐ口座認証実装はこちらから
EAインジケーターに口座番号認証を実装
EAやインジケーターに口座縛りをつけてコピー対策【MQLAuth実装代行】 ※MQLAuth(エム・キュー・エル・オースと呼びます) MQLAuthのイメージ こちらは口座番号を登録するM...