ストラテジーテスターのデバッグ方法

ストラテジーテスターのデバッグ方法 解説・方法

 ここでは作成した EAが、ストラテジーテスターによるEAの動作確認でロジック通り動かなかった時のデバッグ方法について解説します。
 EAを組む工程で非常に重要なことになりますので、是非マスターするようにしてください。

 EAを作成する多くの人はストラテジーテスターを使った EAの動作確認でエラーを発見していくことになります。ただストラテジーテスターを使ってのデバッグは MetaEditorのデバッグ機能は使えません。ですので、Print関数とストラテジーテスターの操作履歴を使ってエラーの発見と修正をしていくことになります。

ストラテジーテスターのデバッグ処理

 ストラテジーテスターでエラーが発生した場合、ストラテジーテスターの操作履歴でエラーを確認することが出来ます。
 次の、例1の EAを作成して「適用するEAの設定内容」と同じ設定でストラテジーテスターでテストしてみてください。(ヒストリカルデータが無い場合はこちらを参考にしてダウンロードしてください)

// 例1
#property strict
void OnInit() {
int ticket = OrderSend(Symbol(), 2, 0.01, 300, 30, 0, 400, "TEST", 1001, 0, clrNONE); //イフダン注文
}
テスター設定値
適用するEAの設定内容

 今回の場合、USDJPYを 200円の買い指値注文をしようとしてエラーが出ました。買い指値注文はその時の価格より低い価格でしか注文が出来ません。
 このように、ストラテジーテスターの操作履歴にはエラーが発生した場合、メッセージが出力されるようになっています。

ストラテジーテスターのデバッグ処理 (エラー無し)

 では、次の場合はどうでしょうか?
 比較用に次の EAを使います。EAの内容は「買いのみトラリピEA」です。こちらはエラーはありません。まずは「買いのみトラリピEA」のコードをコピペしてテストしてみてください。

 ストラテジーテスターの設定とエキスパート設定は次のようにしてください。

買いのみトラリピEAの設定

 EAが正常稼働すれば結果は次のようになります。

結果は、使用するヒストリカルデータ、FX会社、スワップによって上記結果とは誤差は出ます。比較テストをする時は、同じヒストリカルデータ、FX会社を使用するようにしてください。スワップは通常、テスト時のスワップで計算されますが、FX会社によってはスワップを含めないところもあります。

 レポートについては、ここでは詳しく見なくても大丈夫ですが、興味があればこちらを参考にしてください。

ストラテジーテスターのデバッグ処理 (エラー有り)

 次の「買いのみトラリピEAバグ有」のコードはエラーを仕込んでいます。これを MetaEditorへコピペしてコンパイルしてください。

// BuyOnlyTraripEA-bug.mq4(買いのみトラリピEAバグ有 フランのなるほどMT4)
//★1.初期設定値の決定★★★★★★★★★★★★
#property strict
extern double lots = 0.01;             // ロット数
extern double maxPrice = 100.0;        // 取引最大値(各通貨単位)
extern int trapsu = 100;               // トラップ数(個)
extern int profitPips = 100;           // 利益幅(Pips)
extern int trapIntervalPips = 50;      // トラップ幅(Pips)
extern int slippage = 3;               // スリッページ(Pips)
extern int magic = 1001;               // マジックナンバー
extern int orderRangePips = 100;       // 指値注文範囲(Pips)
int slippagePips;                      // スリッページの単位をPipsからPointへ変換した変数
double PipsPerCunit;                   // 1Pipsあたり各国通貨単位に変換する係数
int i, j, ticket;                      // i:トラップ数計数用の変数, j:ポジション数計数用の変数, ticket:チケットNoの変数
//★2.初期処理★★★★★★★★★★★★★★★★
void OnInit() {                        // 初期処理
    int coe = 1;                       // 2,4桁表示業者の場合の係数は「1」
    if(_Digits == 3 || _Digits == 5) { // 3,5桁表示業者の場合の係数は「10」
        coe = 10;
    }
    PipsPerCunit = _Point * coe;       // 1Pipsあたりを各国通貨単位に変換する係数
    slippagePips = slippage * coe;     // スリッページの単位をPipsからPointへ変換
}
//★3.終了処理★★★★★★★★★★★★★★★★
void OnDeinit(const int reason) { //終了処理
   ObjectsDeleteAll();            // オブジェクトテキストをEA削除時消すための処理
}
//★4.レート変動毎の処理★★★★★★★★★★★
void OnTick() { //レート変動毎の処理
   for(i=0; i < trapsu; i++){ 
//●4.①注文範囲内かのチェック●●●●●●●●
      double openPrice = maxPrice - trapIntervalPips * i * PipsPerCunit;
      RefreshRates();
      double currentPrice = Ask;
      if( orderRangePips != 0 && (openPrice < currentPrice - orderRangePips * PipsPerCunit || openPrice > currentPrice + orderRangePips * PipsPerCunit) ){
         continue; // 現在値が指値注文範囲より乖離した注文は出さない
      }
      
//●4.②決済価格とマジックナンバーの設定●●●
      //注文範囲内に入ったので処理を続行
      double closePrice = openPrice + profitPips * PipsPerCunit; //イフダン注文の決済価格を設定
      int magicNumber = magic + i;
      
//●4.③注文有無のチェック●●●●●●●●●●
      ticket = -1; // 既に注文orポジション化されているかチェック。無しの時、ticket=-1。
      for(i=0; i < OrdersTotal(); i++) { 
         if(OrderSelect(i, SELECT_BY_POS) == false) {
            break;
         }
         
         if(magicNumber == OrderMagicNumber() && OrderSymbol() == _Symbol) {
            ticket = OrderTicket();
            break;
         }
      }
      
//●4.④注文無しの時は注文する●●●●●●●●
      if (ticket <= 0) { // 注文がなければ(ticket=-1の時)リピートする
         int tradeType;               // 注文形式を決定する
         if(openPrice <= Ask) {
            tradeType = OP_BUYLIMIT;  // 指値 <= 現在価格(Ask) の時、買い指値注文
         } else {
            tradeType = OP_BUYSTOP;   // 指値 >  現在価格(Ask) の時、買い逆指値注文
         }
         openPrice = NormalizeDouble(openPrice, Digits);   //指値価格の有効桁丸め
         closePrice = NormalizeDouble(closePrice, Digits); //決済価格の有効桁丸め
         ticket = OrderSend(Symbol(), tradeType, lots, openPrice, slippagePips, 0, closePrice, "B_T_EA:" + (string)magicNumber, magicNumber, 0, clrNONE); //イフダン注文
      }
   }
}

 コンパイルしてもエラーは発生しません。
 この「買いのみトラリピEAバグ有」を先ほどと同様の設定でテストをしてみてください。結果は次のようになります。

 テストをスタートさせても終了されません。(終了しないのでレポートは出ません)結果タグでは 1つだけイフダン注文されています。又、操作履歴にもエラーメッセージは出ていません。

 しかし、EAを作成した意図通りにプログラムが動いていないことが分かります。
 次からが不具合箇所の発見作業になります。

EA不具合箇所の発見と修正

Print関数の設置

 不具合作業の発見には Print関数を使ってストラテジーテスターの操作履歴を確認しながら作業をしていくことになります。

 まずは、OrderSend関数まで命令が伝わっているのかを確認します。

次の Print関数を「ticket = OrderSend(…」の前に設置してください。

Print("★★★チェック");

 これでテストをスタートさせてみます。OrderSend関数まで命令が来ていれば、ストラテジーテスターの操作履歴に Print関数は表示されるはずです。
 結果は、1つだけ表示されますが、2つめ以降の注文命令が届いていないことが分かります。

次に「if (ticket <= 0)」の前に次の Print関数を置いてテストしてください。ここではチケット番号の変化も合わせて見るようにします。

  Print("★★★チケット番号:", ticket);

 テストをスタートして、操作履歴を確認すると「★★★チケット番号:1」になっていることが分かります。また、ここまでは命令が届いていることが分かりました。
 そして何故、チケット番号が「1」になっているかを調べます。「★★★チケット番号:1」は既にチケット番号がと取られている、つまり既に注文が入っていることを意味します。このプログラムでは順次、他のポジションが取得されているかチェックする際、ポジションが取得されていなければ 44行目の「//●4.③注文有無のチェック●●●●●●●●●●」以降のところで、変数 ticketは「-1」になります。今回の場合、1つだけしかポジションが取られていないのにずっと「1」ということはおかしいということが分かりました。

次に「for(i=0; i < OrdersTotal(); i++) {」の前に次の Print関数を置いてテストを実行します。

Print("★★★for文前");

 ここでもfor文まで命令が来ていることが分かります。

次はの for文の中に Print関数を設置します。
 「if(magicNumber == OrderMagicNumber()」の後ろに次の Print関数を置いて命令が通っているかチェックします。その際、マジックナンバーの変化も確認するようにします。(他の Print関数のメッセージが邪魔な場合はコメント化してください)
 テストをスタートしてください。

Print("★★★マジックナンバー:",magicNumber);

 マジックナンバーが変化していないことが分かりました。


マジックナンバーは 42行目の
int magicNumber = magic + i; より
変数iが 1ずつ増えることによってマジックナンバーが大きくなるようになっています。しかし、変化していないのでこの原因を調べていきます。

新たなPrint関数の設置

 これまでの Print関数は削除してください。
 変数iの変化を追うため次の Print関数をに設置してください。

Print("★★★変数①", i);
Print("★★★変数②", i);
Print("★★★変数③", i);

 テストをスタートさせて、操作履歴を確認すると、変数が 19だったのが 0に戻されています。原因は Print関数を設置した❶➋の間にあることが分かります。

 このようにして範囲を絞り込んでいくと、変数iがどこで 0になったかが分かります。

 今回の場合、「原因のfor文」で変数i以外を使用しなければならないところ同じ変数iを使用していました。これが原因で変数iが初期化され次のマジックナンバーへカウント出来なかったことが分かります。

 これを修正して、変数iを変数jに変えて再度テストを実行すると、問題なくトラリピが出来るようになります。これで完了です。
 作業が終わった後は、Print関数を削除することを忘れないようにしてください。

ストラテジーテスターのデバッグ処理は重要!!

 以上、ストラテジーテスターのデバッグ作業です。非常に地道な作業になりますが、プログラムを作る上では必ず必要かつ重要な作業になります。それはプログラムの誤作動によって損失を被ることもあるからです。ストラテジーテスターのシミュレーションは数年分のデータを元にその検証が出来ますので、面倒くさがらずに頑張ってやっていってください。

 自分以外の人が作ったプログラムのデバッグ作業はまたまた大変になります。これはプログラムを作る人の思想、考え方が違うからです。プログラミングでは個性がはっきり出てきます。しかし、他の人が作ったプログラムは自分自身の勉強にもなりますので面倒くさがらないで新しい技術を学べる機会だと思ってデバッグ処理をやっていきましょう!

一言メモ

 操作履歴で文字を拾っていくことが大変な時もありますので、目印になるようなものをつけると操作履歴が見やすくなります。

 例えば、英語などで表示させると、元々の自動出力での表示内容なのか Print関数のものなのか分かり難いですが、文字の前に★や●、▲、■などを付けておくと操作履歴を拾いやすくなります。

タイトルとURLをコピーしました