MQL5重複エントリー防止を実コードで解説|Duplicate Gate・同一バー制御・in-flight lock

EAファンクラブ

MQL5 EAでは、エントリー条件そのものが正しくても、実装上の制御が不足していると重複エントリーが起きることがあります。

たとえば、同じバー内で何度もOnTickが呼ばれる、ボタンを連打する、OrderSend直後にポジション反映が遅れる、発注失敗後に同じ条件を何度も再評価する、BUYとSELLの管理状態が混ざる。こうした状態を放置すると、想定より多いポジションが建つ原因になります。

この記事では、MQL5 EAの重複エントリー防止を、学習用に一般化したコード断片で解説します。完成EAソース全文、外部シート制御、認証、endpoint、token、URL、実運用set値、中核エントリー条件は公開しません。

扱うのは、Duplicate Gate、同一バー制御、in-flight lock、cooldown、同方向重複防止、reason付きログです。売買判断そのものではなく、EAが同じ条件で何度も発注しないための安全設計を整理します。

この記事で扱う範囲

項目この記事で扱う内容扱わない内容
目的同一バー・同一方向・同一requestの重複発注を防ぐ設計利益を狙う売買ロジック、具体的なエントリー条件
主な構造Duplicate Gate、same bar guard、in-flight lock、cooldown完成EAソース全文、実運用コード全文
設計対象OrderSend前のentry guard、発注中状態、失敗後処理、ログ外部制御、認証、実運用set値
コード例学習用に簡略化した短いコード断片中核エントリー条件、独自優位性のある判定式
確認対象DUPLICATE_GATE、SAME_BAR_BLOCK、IN_FLIGHT、COOLDOWN推奨ロット、推奨銘柄、売買判断

重複エントリーが起きる主な原因

MQL5 EAで重複エントリーが起きる原因は、シグナル条件だけではありません。OnTickの呼び出し頻度、注文結果反映、ボタン操作、発注後の状態管理が関係します。

原因起きる問題改善方針
同じバー内でOnTickが何度も動く同じシグナルで複数回発注するlast entry barを記録する
OrderSend直後にポジション反映が遅れるまだポジションなしと判断して再送するin-flight lockを持つ
ボタンを連打するUI requestが複数回処理されるrequest処理後に即clearし、cooldownを入れる
BUY / SELLを同じ状態変数で管理する方向別の制御が崩れるside別にgate状態を分ける
発注失敗後の再試行が無制限短時間に何度もOrderSendするretry対象とcooldownを分ける
position snapshotが古い実際の保有状態と判断がずれるsnapshot取得時刻とreasonをログに残す

重複エントリー防止では、シグナルが出たかどうかよりも、そのシグナルで今発注してよい状態かを分けて確認することが重要です。

Duplicate Gateとは

Duplicate Gateとは、発注処理に進む前に、同じ条件・同じ方向・同じバー・同じrequestで再度エントリーしないように確認する入口です。

Duplicate Gateで確認する代表的な項目です。

  • 同じバーで同じ方向へすでに発注していないか
  • 現在OrderSend中、または結果反映待ちではないか
  • 直近発注からcooldown秒数が経過しているか
  • すでに同じ方向のポジションを保有していないか
  • 同じUI requestを再処理していないか
  • 前回失敗理由が即再試行してよい種類か

Duplicate Gateは、売買シグナルの良し悪しを判定する層ではありません。発注直前の安全確認として扱います。

signalとduplicate gateを分ける理由

EA設計では、signalとduplicate gateを混同しないことが重要です。

役割
signalエントリー候補があるかを評価するBUY候補、SELL候補、NO_SIGNAL
gate候補があっても実行してよいか確認するspread、position、same bar、cooldown
execution注文requestを作り、OrderSendへ進むMqlTradeRequest、MqlTradeResult
log候補・見送り・実行・失敗理由を残すSIGNAL_OK、GATE_BLOCK、SEND_FAIL

シグナルが成立していても、同じバーで既に発注済みなら見送る必要があります。これは「シグナル不成立」ではなく、「duplicate gateで止めた」という扱いです。

同一バー制御の考え方

バー確定型EAでは、同じバーで同じ方向へ何度も発注しないようにすることが重要です。

たとえば、BUY条件が成立したバーで1回発注したら、そのバーが変わるまでBUYの再発注を止めます。SELLも同様に、方向別に最後に発注したバー時刻を記録します。

状態扱い
BUY発注済みバーと現在バーが同じBUY再発注を止める
SELL発注済みバーと現在バーが同じSELL再発注を止める
BUY発注済みだがSELLは未発注設計方針に応じてSELL側を別判定にする
バーが変わった同一バー制御を次バーへ進める

BUYとSELLを同じ変数で管理すると、方向別制御が曖昧になります。sideごとに状態を持つ方が確認しやすくなります。

in-flight lockとは

in-flight lockとは、OrderSendを呼んでから結果反映や後処理が完了するまで、同じ経路の再発注を止める状態です。

OrderSend直後は、ポジション一覧への反映がEA側の次処理と完全に同期しない場合があります。その一瞬に「まだポジションがない」と判断して再度OrderSendへ進むと、重複発注の原因になります。

in-flight lockでは、次のような状態を持ちます。

  • 現在発注中か
  • どのsideの発注か
  • どのバーで発注したか
  • いつ発注したか
  • どのrouteから発注したか
  • 解除条件は何か

in-flight lockは、注文成功時だけでなく、失敗時にも解除タイミングを明確にする必要があります。

cooldownの考え方

cooldownは、直近発注や直近失敗から一定時間、再発注を止める制御です。

同一バー制御だけでは不十分な場面があります。たとえば、リアルタイム型EAやボタンクリック型EAでは、同じバー内でなくても短時間に連続操作が起きる場合があります。

制御主な目的
same bar guard同じシグナルバーでの再発注防止
cooldown短時間連続発注や連打の防止
in-flight lock注文中・反映待ちの再送防止
position gate既存ポジション保有中の新規発注制御
retry gate失敗後の再試行条件を制御

どれか1つだけで重複を防ぐのではなく、用途ごとに分けて設計すると安全です。

実コード断片1:side別のEntry Gate状態を持つ

以下は、実際のEAで使う考え方を学習用に簡略化したコード例です。完成EAのソース全文ではありません。

// 学習用に一般化したEntry Gate状態例です。
// 実運用EAの完成ソースではありません。

enum EntrySide
{
   ENTRY_SIDE_BUY = 0,
   ENTRY_SIDE_SELL = 1
};

struct EntryGateState
{
   datetime lastEntryBarTime;
   datetime lastAttemptTime;
   bool     inFlight;
   string   lastRoute;
   string   lastReason;
};

EntryGateState g_buyGate;
EntryGateState g_sellGate;

void ResetEntryGateState(EntryGateState &gate)
{
   gate.lastEntryBarTime = 0;
   gate.lastAttemptTime = 0;
   gate.inFlight = false;
   gate.lastRoute = "NONE";
   gate.lastReason = "INIT";
}

void InitEntryGates()
{
   ResetEntryGateState(g_buyGate);
   ResetEntryGateState(g_sellGate);

   Print("DIAG/DUPLICATE_GATE: event=INIT",
         " reason=GATE_STATE_RESET");
}

この例では、BUY側とSELL側で別々のEntryGateStateを持っています。方向別に状態を分けることで、BUYの発注履歴とSELLの発注履歴を混同しにくくなります。

このコードの役割

処理役割
EntrySideBUY側とSELL側を明示する
EntryGateState重複発注防止に必要な状態をまとめる
lastEntryBarTime最後に発注したバー時刻を記録する
lastAttemptTime最後に発注を試みた時刻を記録する
inFlightOrderSend中または反映待ち状態を示す
InitEntryGates起動時にgate状態を初期化する

実コード断片2:現在バー時刻を取得する

以下は、同一バー制御で使う現在バー時刻を取得する学習用コード例です。

// 学習用に一般化した現在バー時刻取得例です。
// 実運用EAの完成ソースではありません。

datetime GetCurrentBarTime()
{
   datetime barTime = iTime(_Symbol, PERIOD_CURRENT, 0);

   if(barTime == 0)
   {
      Print("DIAG/DUPLICATE_GATE: event=BAR_TIME_FAIL",
            " reason=ITIME_ZERO");
   }

   return barTime;
}

bool IsSameBar(datetime a, datetime b)
{
   if(a == 0 || b == 0)
      return false;

   return (a == b);
}

同一バー制御では、TimeCurrentではなく、対象チャートのバー時刻を基準にする方が確認しやすい場面があります。

このコードの役割

処理役割
iTime現在チャートのバー時刻を取得する
BAR_TIME_FAILバー時刻が取れなかった状態をログへ残す
IsSameBar2つのバー時刻が同じか判定する

バー時刻が取得できない場合に、そのまま発注へ進むのではなく、reason付きで見送る設計にすると安全です。

実コード断片3:Duplicate Gateで発注可否を確認する

以下は、OrderSendへ進む前に、同一バー・in-flight・cooldownを確認する学習用コード例です。中核エントリー条件は含めていません。

// 学習用に一般化したDuplicate Gate例です。
// 実運用EAの完成ソースではありません。

EntryGateState* ResolveGateBySide(EntrySide side)
{
   if(side == ENTRY_SIDE_BUY)
      return &g_buyGate;

   return &g_sellGate;
}

string EntrySideToText(EntrySide side)
{
   return (side == ENTRY_SIDE_BUY ? "BUY" : "SELL");
}

bool CanPassDuplicateGate(EntrySide side,
                          datetime currentBarTime,
                          int cooldownSeconds,
                          string routeName)
{
   EntryGateState* gate = ResolveGateBySide(side);
   string sideText = EntrySideToText(side);
   datetime nowTime = TimeCurrent();

   if(currentBarTime == 0)
   {
      gate.lastReason = "BAR_TIME_NOT_READY";

      Print("DIAG/DUPLICATE_GATE: side=", sideText,
            " event=BLOCK",
            " reason=", gate.lastReason,
            " route=", routeName);

      return false;
   }

   if(gate.inFlight)
   {
      gate.lastReason = "IN_FLIGHT";

      Print("DIAG/DUPLICATE_GATE: side=", sideText,
            " event=BLOCK",
            " reason=", gate.lastReason,
            " route=", routeName);

      return false;
   }

   if(IsSameBar(gate.lastEntryBarTime, currentBarTime))
   {
      gate.lastReason = "SAME_BAR_ALREADY_ENTRY";

      Print("DIAG/DUPLICATE_GATE: side=", sideText,
            " event=BLOCK",
            " reason=", gate.lastReason,
            " bar=", TimeToString(currentBarTime, TIME_DATE | TIME_MINUTES),
            " route=", routeName);

      return false;
   }

   if(gate.lastAttemptTime > 0 &&
      (nowTime - gate.lastAttemptTime) < cooldownSeconds)
   {
      gate.lastReason = "COOLDOWN";

      Print("DIAG/DUPLICATE_GATE: side=", sideText,
            " event=BLOCK",
            " reason=", gate.lastReason,
            " elapsed=", (nowTime - gate.lastAttemptTime),
            " route=", routeName);

      return false;
   }

   gate.lastReason = "PASS";

   Print("DIAG/DUPLICATE_GATE: side=", sideText,
         " event=PASS",
         " route=", routeName);

   return true;
}

この例では、発注に進む前に、バー時刻、in-flight、同一バー、cooldownを確認しています。

このコードの役割

処理役割
ResolveGateBySideBUY / SELL別のgate状態を取り出す
BAR_TIME_NOT_READYバー時刻が取得できない時に止める
IN_FLIGHT発注中・反映待ちの再送を止める
SAME_BAR_ALREADY_ENTRY同じバー内の同方向再Entryを止める
COOLDOWN短時間の連続発注を止める
PASSDuplicate Gateを通過したことをログへ残す

Duplicate Gateで止めた場合は、シグナル不成立ではなく、発注前安全条件で止めたものとしてログに残します。

実コード断片4:発注試行をmarkする

以下は、OrderSendへ進む直前に、gate状態へ発注試行中であることを記録する学習用コード例です。

// 学習用に一般化した発注試行mark例です。
// 実運用EAの完成ソースではありません。

void MarkEntryAttemptStart(EntrySide side,
                           datetime currentBarTime,
                           string routeName)
{
   EntryGateState* gate = ResolveGateBySide(side);
   string sideText = EntrySideToText(side);

   gate.inFlight = true;
   gate.lastAttemptTime = TimeCurrent();
   gate.lastRoute = routeName;
   gate.lastReason = "ATTEMPT_START";

   Print("DIAG/DUPLICATE_GATE: side=", sideText,
         " event=ATTEMPT_START",
         " bar=", TimeToString(currentBarTime, TIME_DATE | TIME_MINUTES),
         " route=", routeName);
}

void MarkEntryAttemptSuccess(EntrySide side,
                             datetime currentBarTime,
                             string routeName)
{
   EntryGateState* gate = ResolveGateBySide(side);
   string sideText = EntrySideToText(side);

   gate.inFlight = false;
   gate.lastEntryBarTime = currentBarTime;
   gate.lastAttemptTime = TimeCurrent();
   gate.lastRoute = routeName;
   gate.lastReason = "ENTRY_SUCCESS";

   Print("DIAG/DUPLICATE_GATE: side=", sideText,
         " event=ENTRY_SUCCESS",
         " bar=", TimeToString(currentBarTime, TIME_DATE | TIME_MINUTES),
         " route=", routeName);
}

void MarkEntryAttemptFail(EntrySide side,
                          string routeName,
                          string failReason)
{
   EntryGateState* gate = ResolveGateBySide(side);
   string sideText = EntrySideToText(side);

   gate.inFlight = false;
   gate.lastAttemptTime = TimeCurrent();
   gate.lastRoute = routeName;
   gate.lastReason = failReason;

   Print("DIAG/DUPLICATE_GATE: side=", sideText,
         " event=ENTRY_FAIL",
         " reason=", failReason,
         " route=", routeName);
}

この例では、発注開始、成功、失敗を別々に記録しています。OrderSendへ進む前にin-flightを立て、成功または失敗後に解除します。

このコードの役割

処理役割
MarkEntryAttemptStart発注試行開始時にin-flightを立てる
MarkEntryAttemptSuccess発注成功後に同一バー発注済みとして記録する
MarkEntryAttemptFail発注失敗後にin-flightを解除し、失敗理由を残す
lastEntryBarTime同一バー再発注防止に使う
lastAttemptTimecooldown制御に使う

発注成功時だけでなく、失敗時にも状態を更新することが重要です。失敗後にin-flightが残り続けると、以後の発注が止まったままになります。

実コード断片5:OnTickからDuplicate Gateを呼ぶ

以下は、OnTick側からsignal候補を受け取り、Duplicate Gateを通過した場合だけexecution確認へ進む学習用コード例です。実際のエントリー条件やOrderSend本体は含めていません。

// 学習用に一般化したOnTick接続例です。
// 実運用EAの完成ソースではありません。

struct EntryCandidate
{
   bool ready;
   EntrySide side;
   string routeName;
   string reason;
};

EntryCandidate BuildEntryCandidate()
{
   EntryCandidate candidate;
   candidate.ready = false;
   candidate.side = ENTRY_SIDE_BUY;
   candidate.routeName = "SIGNAL_ROUTE_SAMPLE";
   candidate.reason = "NO_SIGNAL";

   // 実際のEAでは、ここでsignal評価を行います。
   // 中核エントリー条件はこの記事では扱いません。

   return candidate;
}

void RouteToExecutionCheck(const EntryCandidate &candidate,
                           datetime currentBarTime)
{
   Print("DIAG/ENTRY_ROUTE: side=", EntrySideToText(candidate.side),
         " event=ROUTE_TO_EXECUTION_CHECK",
         " route=", candidate.routeName);

   // 実際のEAでは、ここでOrderSend前precheckやexecution層へ渡します。
   // この記事では実発注処理は扱いません。
}

void OnTick()
{
   EntryCandidate candidate = BuildEntryCandidate();

   if(!candidate.ready)
   {
      Print("DIAG/ENTRY: event=SKIP",
            " reason=", candidate.reason);
      return;
   }

   datetime currentBarTime = GetCurrentBarTime();

   if(!CanPassDuplicateGate(candidate.side,
                            currentBarTime,
                            10,
                            candidate.routeName))
   {
      return;
   }

   MarkEntryAttemptStart(candidate.side,
                         currentBarTime,
                         candidate.routeName);

   RouteToExecutionCheck(candidate,
                         currentBarTime);

   // 実際のEAではOrderSend結果に応じて
   // MarkEntryAttemptSuccess または MarkEntryAttemptFail を呼びます。
}

この例では、signal候補、duplicate gate、execution確認を分けています。エントリー条件が成立しても、gateを通過しなければexecutionへ進みません。

このコードの役割

処理役割
BuildEntryCandidateEntry候補を作る。中核条件はこの記事では扱わない
CanPassDuplicateGate重複発注防止の安全確認を行う
MarkEntryAttemptStartexecution確認へ進む前にin-flightを立てる
RouteToExecutionCheckOrderSend前precheckやexecution層へ渡す入口
MarkEntryAttemptSuccess / FailOrderSend結果に応じてgate状態を更新する

このように、signal、duplicate gate、executionを分けると、どの段階で止まったかをExpertsログで確認しやすくなります。

実コード断片6:UI request連打を防ぐ

パネルEAや裁量補助EAでは、ボタン連打による重複requestにも注意が必要です。

// 学習用に一般化したUI request重複防止例です。
// 実運用EAの完成ソースではありません。

datetime g_lastUiRequestTime = 0;
int g_uiRequestCooldownSeconds = 2;

bool CanAcceptUiRequest(string objectName)
{
   datetime nowTime = TimeCurrent();

   if(g_lastUiRequestTime > 0 &&
      (nowTime - g_lastUiRequestTime) < g_uiRequestCooldownSeconds)
   {
      Print("DIAG/UI_REQUEST: event=BLOCK",
            " reason=UI_COOLDOWN",
            " object=", objectName);

      return false;
   }

   g_lastUiRequestTime = nowTime;

   Print("DIAG/UI_REQUEST: event=ACCEPT",
         " object=", objectName);

   return true;
}

UI requestのcooldownは、OrderSendのretry制御とは別です。クリック連打を受けないためのUI層の制御として扱います。

このコードの役割

処理役割
g_lastUiRequestTime最後にUI requestを受けた時刻を記録する
UI_COOLDOWN短時間の連続クリックを止める
ACCEPTUI requestを受け付けたことをログへ残す

ボタン操作は、OnChartEventから直接発注へ進めず、UI requestとして受け取り、Duplicate Gateやexecution precheckへ渡す構造にします。

よくある失敗例

失敗1:シグナル成立だけでOrderSendへ進む

シグナルが成立していても、同一バーで既に発注済み、in-flight中、cooldown中であれば、OrderSendへ進むべきではありません。signalとduplicate gateを分ける必要があります。

失敗2:BUYとSELLを同じ発注履歴で管理する

方向別の状態を分けないと、BUYの履歴がSELL側の制御へ影響したり、逆にSELLの履歴でBUYが止まったりします。side別にgate状態を持つ方が安全です。

失敗3:OrderSend失敗後にin-flightを解除していない

OrderSend失敗時にin-flightを解除しないと、EAが発注中のまま固定され、以後の発注が止まり続ける可能性があります。

失敗4:失敗後に無制限retryする

価格変動、ロット不正、証拠金不足、市場クローズなどを同じ扱いでretryすると危険です。retry対象と即停止対象を分ける必要があります。

失敗5:UIボタン連打をそのまま処理する

ボタンを連打された場合に、同じrequestを連続処理すると重複発注につながります。UI request側にもcooldownや処理済みclearが必要です。

失敗6:ログにblock reasonがない

Duplicate Gateで止めた理由がログに出ていないと、シグナル不成立なのか、同一バー制御なのか、in-flightなのか分かりません。reasonを固定名で出します。

ログで確認するポイント

重複エントリー防止の問題を確認する時は、Expertsログで次の情報を見ます。

ログ見るポイント
DUPLICATE_GATE INIT起動時にgate状態が初期化されているか
DUPLICATE_GATE PASS発注前の重複防止チェックを通過したか
SAME_BAR_ALREADY_ENTRY同じバー内の再発注を止めているか
IN_FLIGHT発注中・反映待ちの再送を止めているか
COOLDOWN短時間の連続発注を止めているか
ATTEMPT_STARTOrderSend前に発注試行状態をmarkしているか
ENTRY_SUCCESS成功時に発注済みバーを記録しているか
ENTRY_FAIL失敗時にin-flightを解除し、理由を残しているか

ログ確認の基本は、次のページも参考にしてください。

MT5のログ確認方法|Experts・Journalの見方

開発依頼前に整理する情報

EAの重複発注防止を開発・改修する場合、次の情報を先に整理しておくと仕様のすれ違いを減らせます。

  • バー確定型か、リアルタイム反応型か
  • 同一バー内で同方向再Entryを許可するか
  • BUY / SELLを方向別に管理するか
  • ポジション保有中の追加Entryを許可するか
  • OrderSend直後のin-flight lockを使うか
  • 発注失敗後のcooldown秒数
  • retry対象にするretcode
  • UIボタン連打への対策
  • 手動Entryと自動Entryの扱い
  • Duplicate Gateのログ粒度

仕様書、setファイル、ログ、スクリーンショットを整理する場合は、次のページも確認してください。

MT5開発依頼前に用意する資料まとめ|仕様書・setファイル・ログ・スクリーンショット

公開しない情報

この記事では、MQL5の重複エントリー防止を学ぶために、公開可能な範囲だけを一般化して解説しています。以下は公開しません。

  • 完成EAソース全文
  • 外部シート参照による動作制御ロジック
  • 外部シートCSV取得、解析、時間枠、方向制御ロジック
  • 認証、口座許可判定、購入者管理ロジック
  • endpoint、token、URL、Google Sheet ID、Webhook URL、GAS URL
  • APIキー、認証トークン、口座番号、サーバー名
  • 実運用set値
  • 中核エントリー条件
  • 中核Trail / Nanpin判定
  • 独自優位性のある売買判定式
  • 利益保証、勝率保証、推奨ロット、推奨銘柄、推奨エントリー

本記事は、投資判断や売買指示ではなく、MQL5開発におけるDuplicate Gate、同一バー制御、in-flight lock、cooldownの考え方を理解するための技術解説です。

FAQ

Duplicate Gateとは何ですか?

OrderSendへ進む前に、同じ方向・同じバー・同じrequestで重複発注しないよう確認する安全確認層です。signal評価とは別に設計します。

同じバーで再エントリーしないようにするには何を記録しますか?

方向別にlastEntryBarTimeを記録します。BUYとSELLを分け、現在バー時刻と最後に発注したバー時刻が一致する場合は同方向の再Entryを止めます。

in-flight lockはなぜ必要ですか?

OrderSend直後は、ポジション一覧への反映や後処理が完全に終わっていない場合があります。その間に再度OrderSendへ進まないよう、発注中・反映待ち状態を持つためです。

cooldownと同一バー制御は同じですか?

別です。同一バー制御はバー単位の再発注防止、cooldownは秒数ベースの連続処理防止です。ボタン連打や短時間retry対策にはcooldownが有効です。

OrderSend失敗後はすぐretryしてよいですか?

すべてをretryするのは危険です。価格変動のような再評価候補と、ロット不正、証拠金不足、市場クローズのような設定・環境確認対象を分ける必要があります。

UIボタン操作でもDuplicate Gateは必要ですか?

必要です。ボタンクリックはUI requestとして受け取り、その後にDuplicate Gate、precheck、executionを通す設計が安全です。OnChartEventから直接発注する構造は避けます。

このコードは完成EAとして使えますか?

いいえ。この記事のコード例は、重複エントリー防止の考え方を理解するために簡略化した学習用コードです。完成EAとしてそのまま使うことは想定していません。

実際のEAソース全文は公開されていますか?

公開していません。完成EAソース全文、外部シート制御、認証、endpoint、token、URL、実運用set値、中核エントリー条件は公開対象外です。

関連ページ

ページ確認できること
MQL5 OrderSend結果ログを実コードで解説OrderSend前後のrequest、result、retcode確認
MQL5ログファースト設計を実コードで解説Duplicate Gateのblock reasonをログで追う考え方
MQL5 EAの基本イベント構造を実コードで解説OnTickからentry guardへ進む責務分離
MQL5パネルUI・チャートObject表示を実コードで解説UI requestとexecutionを分ける設計
MQL5 Multi-Logic EA設計を実コードで解説ロジック別のposition snapshotとentry管理
MT5のログ確認方法|Experts・Journalの見方ExpertsログとJournalログの基本確認
MT5開発依頼前に用意する資料まとめ|仕様書・setファイル・ログ・スクリーンショット開発相談前に整理する資料
不具合報告・調査依頼について不具合相談前に送る情報
免責事項投資判断、損益、利用上の注意に関する確認
投資助言を行わない方針売買助言ではなく技術支援として扱う方針

まとめ

MQL5 EAの重複エントリー防止では、シグナル条件だけでなく、OrderSendへ進む直前の安全確認を設計することが重要です。

特に、次の4点を分けておくと、同一バー内の再発注、OrderSend直後の再送、ボタン連打、無制限retryを防ぎやすくなります。

  • same bar guard:同じバーで同方向へ再発注しない
  • in-flight lock:OrderSend中・反映待ちの再送を止める
  • cooldown:短時間の連続発注やクリック連打を止める
  • reason付きログ:なぜ発注しなかったかをExpertsログで追えるようにする

シグナルが成立しているのに発注しない場合でも、それが不具合とは限りません。Duplicate Gateで同一バー、in-flight、cooldown、position状態などにより安全側で止めている可能性があります。

完成EAの中核エントリー条件を公開しなくても、Duplicate Gate、同一バー制御、in-flight lock、cooldownの考え方を理解しておくことで、EAの重複発注、誤発注、連続発注、不具合調査を切り分けやすくなります。

ABOUT ME
記事URLをコピーしました