コンテンツにスキップ

【検証#11】バックテストが終わらない!リトライ処理の無限ループ

この記事の3行まとめ

  • 🔄 注文失敗時に while でリトライするのは定石だが…
  • ⚡ バックテストでは Sleep() が無視される ため、一瞬で無限ループする!
  • ✅ 対策:テスト中はループを抜けるか、回数制限を厳格にする

はじめに

この記事は、「とあるMetaTraderの備忘秘録」様のブログ記事を検証・紹介するシリーズの第11弾です。

EAを作っていると、約定拒否(Requote)や通信エラーに対処するために「注文が通るまでリトライする」処理を書くことがあります。しかし、それがバックテストで牙を剥くことがあります。

元ネタ

MyOrderClose
https://fai-fx.hatenadiary.org/entry/20100627/1277641026
(2010年6月27日 公開)

時代背景に関する注意

元記事はMQL4時代のものですが、MQL5でも「テスターでのSleep挙動」など共通する仕様があります。


問題のコード

以下のようなリトライ処理を書いたとします。

MQL
// 注文が通るまで諦めない!
while(true)
{
    if(OrderSend(...) == true) break;

    Print("注文失敗。リトライします...");
    Sleep(1000); // 1秒待って再挑戦
}

本番環境(ライブ/デモ口座)では、これは正しく機能します。1秒ごとに再試行し、サーバーに負荷をかけすぎることもありません。

バックテストでの挙動

しかし、これをストラテジーテスターで動かすとどうなるでしょうか?

MetaTraderの仕様として、バックテスト中は Sleep() 関数は完全に無視されます。時間は進みませんし、処理ウェイトも発生しません。

その結果: 1. 注文失敗 2. Sleep(1000)0ミリ秒 で通過 3. 即座に次回の while ループ 4. 注文失敗 5. ...

これらが 1ティックの間(一瞬) に何万回も繰り返されます。

発生する現象

  • ログの肥大化: 数GB単位のログファイルが生成され、ディスクを圧迫する。
  • テスターのフリーズ: 処理が終わらず、テストが先に進まない。
  • Visual Modeの停止: チャート描画更新の暇もなくループし続ける。

MQL5での検証コード

実際にこの現象を確認するEAを作成しました。(安全のためリトライ回数制限を設けています)

ファイル名: 11_InfiniteLoop_EA.mq5
保存先: MQL5\Experts

MQL
//+------------------------------------------------------------------+
//|                                       11_InfiniteLoop_EA.mq5      |
//| MQL検証シリーズ #11 検証用EA                                      |
//+------------------------------------------------------------------+
#property copyright "FXおもしろラボ"
#property version   "1.00"

input int MaxRetry = 10000;       // リトライ上限
input bool UseIsTesting = false;  // 対策ON/OFF

void OnTick()
{
   static bool isExecuted = false;
   if(isExecuted) return;

   Print("=== リトライ実験開始 ===");
   int retryCount = 0;

   while(true)
   {
      bool result = TryOrder(); // 常にfalseを返す関数
      if(result) break;

      // バックテスト対策を入れるかどうか
      if(UseIsTesting && MQLInfoInteger(MQL_TESTER))
      {
         Print("テストモード検知:リトライを中止します");
         break;
      }

      retryCount++;
      if(retryCount >= MaxRetry) break;

      // テスターでは無視される!
      Sleep(1000);
   }
   PrintFormat("実験終了: %d回リトライしました", retryCount);
   isExecuted = true;
}

bool TryOrder() { return false; }

結果

UseIsTesting = false の場合、一瞬で「10000回リトライしました」と表示されます。もし MaxRetry がなければ、強制終了するまで止まりません。


対策

バックテスト中であることを検知し、ループを抜ける処理を入れましょう。

MQL5での実装

MQL
// whileループの中
if(MQLInfoInteger(MQL_TESTER))
{
    Print("バックテスト中のためリトライ中止");
    break;
}

あるいは、MQL4/MQL5共通で使えるラッパー関数を作るのも良いでしょう。

MQL
bool IsBackTesting()
{
    return MQLInfoInteger(MQL_TESTER);
}

そもそも無限ループにしない

while(true) は避け、必ず for 文で回数制限を設けるのが、防御的プログラミングの基本です。

MQL
for(int i=0; i<10; i++) // 最大10回まで
{
    if(OrderSend(...)) break;
    Sleep(1000);
}

これもテスターでは一瞬で終わりますが、無限ループによるフリーズは防げます。


まとめ

ポイント 内容
Sleep()の罠 バックテスト中は無視される(待ち時間ゼロ)
影響 無限リトライが一瞬で行われ、フリーズやログ肥大化を招く
対策 MQLInfoInteger(MQL_TESTER) でテスト検知し、breakする

EA開発者は「テスト環境」と「本番環境」の違いを常に意識する必要があります。


ソースコードのダウンロード

この記事で紹介したコードをダウンロードできます。

ファイルの種類:EA(Expert Advisor)

保存先: MQL5/Experts/ フォルダ
使い方: MT5のナビゲーターから「エキスパートアドバイザ」を展開し、チャートにドラッグ&ドロップで適用

11_InfiniteLoop_EA.mq5 をダウンロード


関連記事・用語