EAを動かしていたら、
同じ条件で何度もエントリーしてしまい、
気づけば想定外のロット数を抱えていた、
という経験はありませんか。
この記事では、ポジション重複エントリーを防ぐ3つの書き方と、
それぞれの確実性の違い、
EA再起動時のリスクについて、実際に動くコード付きで解説します。
・ポジション重複が起きる典型的な原因
・OrdersTotal()ループでの確認方法(最も確実)
・新しいバー判定・フラグ変数という他2つの方式
・3つの方式の確実性・再起動時のリスクの比較
なぜポジションが重複してしまうのか
まず、重複エントリーが起きる典型的な原因を整理します。
OnTick()は1秒に何度も呼ばれる
OnTick()は新しい価格が来るたびに呼ばれ、
条件を満たす間はティックごとに何度も実行されます。
エントリー条件を満たす間、何も止めなければ連発する
「RSIが30以下ならエントリー」のような条件は、
RSIが30以下である間、
ティックのたびに条件を満たし続けます。
この記事で扱う範囲
この記事では、重複エントリーを防ぐ3つの実装方式と、
それぞれの確実性の違いを中心に扱います。
方式1: OrdersTotal()ループで保有状況を確認する
最も確実性が高い、標準的な方式を確認します。
関数の書式
int OrdersTotal();
OrdersTotal()は、
保有中・予約中の注文の合計数を返す関数です。
自分のEA・シンボルの注文だけを数える実装
bool HasOpenPosition(int magic, string symbol)
{
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderMagicNumber() == magic && OrderSymbol() == symbol)
{
return(true);
}
}
}
return(false);
}
マジックナンバーとシンボルで絞り込むことで、
他のEAや他の通貨ペアの注文を誤って数えません。
この方式が最も確実な理由
・実際の注文状況をその都度サーバー側のデータから確認する
・EA再起動・端末再起動があっても結果が変わらない
・変数の初期化忘れによる誤動作が起きない
方式2: 新しいバー判定でエントリー回数を制限する
バー単位でエントリー回数を制限する方式です。
新しいバーの判定方法
datetime lastBarTime = 0;
bool IsNewBar()
{
if(Time[0] != lastBarTime)
{
lastBarTime = Time[0];
return(true);
}
return(false);
}
最新バーの時刻が前回と変わったかどうかで、
新しいバーが来たかを判定します。
エントリー条件と組み合わせる
IsNewBar()がtrueの間だけエントリー条件を評価すれば、
同じバー内で何度もエントリーすることを防げます。
この方式の限界
1本のバー内で1回のエントリーは防げますが、
複数のバーをまたいでポジションが残っている場合、
重複を防ぐ保証にはなりません。
方式3: フラグ変数でエントリー済みを管理する
bool型の変数で状態を管理する方式です。
フラグ変数の実装
bool hasEntered = false;
void OnTick()
{
if(!hasEntered && /* エントリー条件 */ true)
{
// OrderSend(...)
hasEntered = true;
}
}
エントリーした直後にフラグをtrueにし、
決済したタイミングでfalseに戻す設計です。
EA再起動でフラグが消えるリスク
・hasEnteredはEA再起動時にfalseへ戻る
・実際にはポジションが残っていても重複発注してしまう
・グローバル変数(GlobalVariableSet)で永続化しない限りこのリスクは残る
この方式を使う場面
フラグ変数は、
OrdersTotal()ループと組み合わせた補助的な判定にのみ使い、
単独の重複防止策としては推奨しません。
実際に動くコード全体(3方式を組み合わせたEA)
最も確実なOrdersTotal()ループを基本に、
新しいバー判定を補助として使う実装です。
設計のポイント(確実な方式を基本にする)
OrdersTotal()ループを必ず通し、
新しいバー判定はティックの処理回数を減らす最適化として使います。
コード全文
//+------------------------------------------------------------------+
//| DuplicatePositionPreventionEA.mq4 |
//| Copyright 2026, FX-EA System Project Creator |
//| https://creator.fx-ea-system-project.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, FX-EA System Project Creator"
#property link "https://creator.fx-ea-system-project.com/"
#property version "1.00"
#property strict
input int MagicNumber = 20261012;
input double LotSize = 0.1;
input int StopLossPips = 300;
input int TakeProfitPips = 600;
datetime lastBarTime = 0;
bool IsNewBar()
{
if(Time[0] != lastBarTime)
{
lastBarTime = Time[0];
return(true);
}
return(false);
}
bool HasOpenPosition(int magic, string symbol)
{
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderMagicNumber() == magic && OrderSymbol() == symbol)
{
return(true);
}
}
}
return(false);
}
void OnTick()
{
if(!IsNewBar())
return;
if(HasOpenPosition(MagicNumber, Symbol()))
{
Comment("すでにポジションを保有中のため新規エントリーをスキップします");
return;
}
double sl = Bid - StopLossPips * Point;
double tp = Bid + TakeProfitPips * Point;
int ticket = OrderSend(Symbol(), OP_BUY, LotSize, Ask, 3, sl, tp,
"duplicate-check-ea", MagicNumber, 0, clrBlue);
if(ticket < 0)
{
Print("OrderSendに失敗しました。エラーコード: ", GetLastError());
}
}
MetaEditorでのコンパイル・実行確認
MetaEditorで保存してコンパイルし、
エラーが0件であることを確認したうえで、
デモ口座で重複発注が起きないことを実際に確認してください。
3つの方式の比較
確実性と再起動時のリスクを表で比較します。
比較表
| 方式 | 確実性 | EA再起動時の挙動 |
|---|---|---|
| OrdersTotal()ループ | 高い(サーバー側の実データ基準) | 影響を受けない |
| 新しいバー判定 | 中(バー単位のみ防止) | 影響を受けない |
| フラグ変数 | 低(単独では不十分) | falseに戻り誤発注の恐れ |
基本はOrdersTotal()ループを必ず通す
新しいバー判定・フラグ変数は、
処理効率を上げる補助的な工夫として使い、
重複防止の最終判断は必ずOrdersTotal()ループに任せます。
既刊マジックナンバー記事との接続
複数のEAを同じ口座で動かす場合は、
既刊のマジックナンバー記事と組み合わせて、
自分のEAの注文だけを正しく数える設計にしてください。
よくある注意点
実装時につまずきやすい点をまとめます。
OrderSelect後の値の取得忘れ
OrderSelect()が成功したかを確認せずに、
OrderMagicNumber()等を呼び出すと、
意図しない値を参照してしまう場合があります。
決済済み注文(履行済み)を含めてしまう
MODE_TRADESを指定すれば、
保有中・予約中の注文だけを対象にできます。
MODE_HISTORYと混同しないよう注意してください。
両建てを許可する場合の考慮
本記事のHasOpenPosition()は、
同じシンボルの保有有無だけを見る設計です。
買いと売りを両方許可したいEAでは、
OP_BUY・OP_SELLそれぞれの保有数を分けて数える必要があります。
よくある質問
ポジション重複防止について、よくある疑問に回答します。
複数ポジションをあえて持ちたい場合はどうしますか?
HasOpenPosition()をそのまま使うと1件でも保有中なら止まるため、
複数ポジションを許可する場合は、
保有数の上限値を比較する条件に変更してください。
OrdersTotal()は重いですか?
保有注文数が極端に多くない限り、
1ティックごとのループ処理は実用上問題にならない負荷です。
新しいバー判定だけでは本当に不十分ですか?
はい。EAを再起動したタイミングで、
既にポジションを保有している状態から再開すると、
新しいバー判定だけでは重複を防げません。
【MQL4】ポジション重複エントリーを防ぐ3つの書き方について解説してみた(確実性で比較):まとめ
ポジションの重複エントリーは、
OnTick()がティックごとに何度も呼ばれる仕組みを踏まえずにEAを書くと、
誰でも起こしてしまう典型的なミスです。
新しいバー判定・フラグ変数はそれぞれ便利な工夫ですが、
単独では確実な防止策にはなりません。
OrdersTotal()ループで実際の保有状況を毎回確認する方式を基本に据え、
他の2つを補助として組み合わせることをおすすめします。
コメント