MQL5エントリー確認ロジック設計

目次

この記事の結論

MQL5のentry confirmation logicは、売買シグナルが出た直後にすぐ注文せず、複数の条件でエントリー可否を確認する仕組みです。
移動平均線、RSI、ATR、スプレッド、既存ポジション、取引時間などを分けて判定すると、EAの挙動を検証しやすくなります。
MQL5では、インジケータ値を使う場合にハンドルを作成し、CopyBufferで値を取得する構造が多くなります。
バックテスト結果は将来の利益を保証しないため、実運用前にフォワードテストで約定差やスプレッド拡大の影響を確認する必要があります。

1. このロジックの役割

【結論】
エントリー確認ロジックの役割は、EAの売買シグナルをそのまま注文に変換せず、取引してよい状態かを追加判定することです。
シグナル判定と確認判定を分けることで、ロジックの原因分析と改善がしやすくなります。

【定義】
エントリー確認ロジックとは、売買方向のシグナルが出たあとに、相場環境、リスク、取引条件、既存ポジションを確認して注文可否を決める処理です。

MQL5のEAでは、OnTickで毎回すべてを注文処理に直結させると、意図しない連続注文や条件の重複が起きやすくなります。
そのため、処理を次のように分離します。

相場認識
↓
フィルター判定
↓
シグナル判定
↓
エントリー確認
↓
リスク確認
↓
注文前チェック
↓
注文送信
↓
約定後管理

1.1 シグナルと確認条件の違い

シグナルは「買いまたは売りの候補」を見つける処理です。
確認条件は「その候補を実際に注文してよいか」を判断する処理です。

例えば、移動平均線の上抜けは買いシグナルになり得ます。
ただし、スプレッドが広い、ATRが低すぎる、すでに同方向のポジションがある、取引時間外に近い、といった条件では注文を見送る設計が必要になります。

1.2 MQL5で分離設計が重要な理由

MQL5のEAは、新しいティックを受け取るたびにOnTickが実行されます。
同じ足の中で何度も条件が成立する場合があるため、足確定の確認、ポジション状態の確認、注文済みフラグの管理が重要です。

MQL5のエントリー確認ロジックは、単なるif文の追加ではありません。
EAの状態、相場条件、注文条件を分けて判定することで、検証しやすい売買ロジックになります。

2. 基本的な考え方

【結論】
エントリー確認ロジックは、相場条件、シグナル条件、リスク条件、注文条件を別々の関数に分けると管理しやすくなります。
条件を詰め込みすぎると取引回数が減り、過剰最適化の原因になるため、確認条件の目的を明確にする必要があります。

エントリー確認は、勝つための魔法の条件ではありません。
目的は、EAが避けたい場面を明確にして、検証可能な形で取引可否を判断することです。

2.1 確認条件の主な分類

分類役割注意点
相場環境確認取引しやすい相場かを見るトレンド方向、ボラティリティ条件が多いと機会が減る
シグナル確認売買方向の根拠を確認する移動平均線、RSI、ブレイク最新足だけで判断するとだましが増えやすい
リスク確認損失拡大を抑えやすくするロット、損切り幅、ドローダウン銘柄仕様を無視すると計算がずれる
注文条件確認注文が通る状態かを見るスプレッド、証拠金、取引時間ブローカー条件により結果が変わる
状態確認EAの重複動作を防ぐ既存ポジション、注文済みフラグnetting口座とhedging口座で扱いが異なる

2.2 最新足と確定足の違い

エントリー確認では、最新足と確定足を区別する必要があります。
最新足はまだ形成中の足であり、価格変動により条件が変わります。
確定足はすでに終了した足であり、バックテストと実運用の判定をそろえやすくなります。

一般的に、MQL5でインジケータ配列を時系列として扱う場合、インデックス0が最新足、インデックス1が1本前の確定足になります。
ただし、配列の向きはArraySetAsSeriesで明示する設計が分かりやすくなります。

3. 代表的な設計パターン

【結論】
エントリー確認ロジックには、トレンド確認型、ボラティリティ確認型、オシレーター確認型、取引条件確認型があります。
最初は少数の条件で設計し、バックテストとフォワードテストで条件の意味を確認する方法が現実的です。

3.1 トレンド確認型

トレンド確認型は、売買方向が相場の大きな流れに合っているかを確認します。
移動平均線の傾き、短期線と長期線の位置関係、上位足の方向などを使います。

買い確認の例は、短期移動平均線が長期移動平均線より上にあり、かつ確定足の終値が短期移動平均線より上にある状態です。
売り確認では、反対の条件を使います。

3.2 ボラティリティ確認型

ボラティリティ確認型は、相場の変動幅が十分か、または大きすぎないかを確認します。
ATRを使うと、直近の値動きの大きさを条件化しやすくなります。

ATRが低すぎる相場では、スプレッドや手数料の影響が相対的に大きくなりやすいです。
ATRが高すぎる相場では、約定差や損切り幅の拡大に注意が必要です。

3.3 オシレーター確認型

オシレーター確認型は、買われすぎや売られすぎ、勢いの変化を確認する方法です。
RSIなどを使う場合、単純な閾値だけでなく、トレンド方向との組み合わせを検討します。

RSIだけで売買方向を決めると、強いトレンドで逆張りが続く場合があります。
トレンド確認と組み合わせると、条件の役割を分けやすくなります。

3.4 取引条件確認型

取引条件確認型は、シグナルの品質ではなく、注文してよい環境かを確認します。
スプレッド、最小ロット、最大ロット、ロットステップ、証拠金、ストップレベル、取引可能時間などを確認します。

MQL5で注文処理を行う場合、OrderSendの前にOrderCheckを使って注文条件を確認する設計が有効です。
注文が通らない原因を事前に切り分けやすくなります。

4. 実装方法

【結論】
MQL5では、OnInitでインジケータハンドルを作成し、OnTickでCopyBufferを使って値を取得し、確認条件を関数化して判定します。
ハンドル作成失敗、データ不足、配列方向、確定足の扱いを明示することが重要です。

4.1 実装の全体像

エントリー確認ロジックは、次のように関数を分けると読みやすくなります。

IsNewBar()
GetIndicatorValues()
HasBuySignal()
HasSellSignal()
IsEntryAllowed()
CheckRisk()
CheckTradeCondition()
SendOrder()

すべてをOnTickに直接書くと、条件の変更やテストが難しくなります。
関数化すると、どの確認条件が注文を止めたのかをログで追いやすくなります。

4.2 インジケータハンドルの作成

MQL5では、iMAやiRSIなどの多くのインジケータ関数は、計算済みの値を直接返すのではなく、インジケータハンドルを返します。
EAではOnInitでハンドルを作成し、OnDeinitで必要に応じてIndicatorReleaseを使って解放します。

4.3 CopyBufferで値を取得する

CopyBufferは、インジケータハンドルからバッファ値を配列へ取得する処理です。
取得件数が期待値未満の場合は、データ不足または取得失敗として処理を止める必要があります。

5. サンプルコード

【結論】
次のコードは、移動平均線とRSIを使ってエントリー確認を行う検証用サンプルです。
実運用では、銘柄仕様、ロット計算、スプレッド、ストップレベル、口座タイプ、約定条件を追加で確認する必要があります。

#property strict

input int FastMAPeriod = 20;
input int SlowMAPeriod = 50;
input int RSIPeriod = 14;
input double MaxSpreadPoints = 30.0;

int fastMaHandle = INVALID_HANDLE;
int slowMaHandle = INVALID_HANDLE;
int rsiHandle = INVALID_HANDLE;

const int CURRENT_BAR = 0;
const int CONFIRMED_BAR = 1;
const int PREVIOUS_CONFIRMED_BAR = 2;

datetime lastBarTime = 0;

int OnInit()
{
   fastMaHandle = iMA(_Symbol, _Period, FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE);
   slowMaHandle = iMA(_Symbol, _Period, SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE);
   rsiHandle = iRSI(_Symbol, _Period, RSIPeriod, PRICE_CLOSE);

   if(fastMaHandle == INVALID_HANDLE ||
      slowMaHandle == INVALID_HANDLE ||
      rsiHandle == INVALID_HANDLE)
   {
      Print("Failed to create indicator handle");
      return INIT_FAILED;
   }

   return INIT_SUCCEEDED;
}

void OnDeinit(const int reason)
{
   if(fastMaHandle != INVALID_HANDLE)
      IndicatorRelease(fastMaHandle);

   if(slowMaHandle != INVALID_HANDLE)
      IndicatorRelease(slowMaHandle);

   if(rsiHandle != INVALID_HANDLE)
      IndicatorRelease(rsiHandle);
}

void OnTick()
{
   if(!IsNewBar())
      return;

   if(!IsSpreadAcceptable())
      return;

   if(HasOpenPosition())
      return;

   if(IsBuyEntryConfirmed())
   {
      Print("Buy entry confirmed. SendOrder should be called after OrderCheck.");
   }

   if(IsSellEntryConfirmed())
   {
      Print("Sell entry confirmed. SendOrder should be called after OrderCheck.");
   }
}

bool IsNewBar()
{
   datetime currentBarTime = iTime(_Symbol, _Period, CURRENT_BAR);

   if(currentBarTime == 0)
      return false;

   if(currentBarTime == lastBarTime)
      return false;

   lastBarTime = currentBarTime;
   return true;
}

bool IsSpreadAcceptable()
{
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);

   if(ask <= 0.0 || bid <= 0.0)
      return false;

   double spreadPoints = (ask - bid) / _Point;

   if(spreadPoints > MaxSpreadPoints)
   {
      Print("Spread is too wide: ", spreadPoints);
      return false;
   }

   return true;
}

bool HasOpenPosition()
{
   if(PositionSelect(_Symbol))
      return true;

   return false;
}

bool GetIndicatorValues(double &fastMa1, double &fastMa2,
                        double &slowMa1, double &slowMa2,
                        double &rsi1)
{
   if(BarsCalculated(fastMaHandle) < 3 ||
      BarsCalculated(slowMaHandle) < 3 ||
      BarsCalculated(rsiHandle) < 3)
   {
      Print("Not enough calculated bars");
      return false;
   }

   double fastMa[];
   double slowMa[];
   double rsi[];

   ArraySetAsSeries(fastMa, true);
   ArraySetAsSeries(slowMa, true);
   ArraySetAsSeries(rsi, true);

   int copiedFast = CopyBuffer(fastMaHandle, 0, 0, 3, fastMa);
   int copiedSlow = CopyBuffer(slowMaHandle, 0, 0, 3, slowMa);
   int copiedRsi = CopyBuffer(rsiHandle, 0, 0, 3, rsi);

   if(copiedFast < 3 || copiedSlow < 3 || copiedRsi < 3)
   {
      Print("CopyBuffer failed or not enough data");
      return false;
   }

   fastMa1 = fastMa[CONFIRMED_BAR];
   fastMa2 = fastMa[PREVIOUS_CONFIRMED_BAR];
   slowMa1 = slowMa[CONFIRMED_BAR];
   slowMa2 = slowMa[PREVIOUS_CONFIRMED_BAR];
   rsi1 = rsi[CONFIRMED_BAR];

   return true;
}

bool IsBuyEntryConfirmed()
{
   double fastMa1, fastMa2, slowMa1, slowMa2, rsi1;

   if(!GetIndicatorValues(fastMa1, fastMa2, slowMa1, slowMa2, rsi1))
      return false;

   bool trendUp = fastMa1 > slowMa1 && fastMa1 > fastMa2;
   bool rsiFilter = rsi1 > 50.0 && rsi1 < 70.0;

   return trendUp && rsiFilter;
}

bool IsSellEntryConfirmed()
{
   double fastMa1, fastMa2, slowMa1, slowMa2, rsi1;

   if(!GetIndicatorValues(fastMa1, fastMa2, slowMa1, slowMa2, rsi1))
      return false;

   bool trendDown = fastMa1 < slowMa1 && fastMa1 < fastMa2;
   bool rsiFilter = rsi1 < 50.0 && rsi1 > 30.0;

   return trendDown && rsiFilter;
}

5.1 注文処理を追加する場合の注意

このサンプルは、エントリー確認部分に焦点を当てています。
注文送信を追加する場合は、MqlTradeRequest、MqlTradeResult、MqlTradeCheckResultを使い、OrderCheckで注文前の条件を確認します。

確認すべき項目は、最小ロット、最大ロット、ロットステップ、証拠金、ストップレベル、フリーズレベル、取引可能時間、スプレッド、既存ポジションです。
netting口座では同一銘柄のポジションが集約され、hedging口座では複数ポジションを持てるため、ポジション管理の設計が変わります。

6. パターン別比較

【結論】
エントリー確認ロジックは、1つの条件で完結させるよりも、目的の異なる条件を少数組み合わせるほうが検証しやすくなります。
ただし、条件を増やすほど過剰最適化と取引機会減少のリスクが高まります。

方法メリットデメリット向いている場面
移動平均線確認実装しやすく方向性を見やすいレンジでだましが出やすい初期設計、トレンド追随
RSI確認勢いや過熱感を条件化しやすい強いトレンドで逆方向に偏る場合があるエントリーの補助判定
ATR確認ボラティリティを反映しやすい売買方向は判断できない低変動回避、損切り幅調整
スプレッド確認実運用のコスト悪化を避けやすい取引機会が減る場合がある短期売買、低ボラティリティ相場
上位足確認大きな流れに合わせやすい反応が遅くなる場合があるトレンドフィルター
既存ポジション確認重複注文を防ぎやすいhedging口座では設計意図を明確にする必要があるEAの状態管理

7. 誤作動しやすい場面

【結論】
エントリー確認ロジックは、データ不足、最新足の変動、スプレッド拡大、重複注文、口座タイプ差で誤作動しやすくなります。
ログ出力と条件分離により、どの条件で注文が止まったかを確認できる設計が必要です。

7.1 最新足で条件が変わる

最新足のインジケータ値は、足が確定するまで変化します。
最新足だけでエントリー確認を行うと、バックテストと実運用の挙動がずれやすくなります。

7.2 CopyBufferの取得件数を確認しない

CopyBufferは、期待した件数を必ず返すとは限りません。
取得件数が不足している状態で配列を読むと、不正な判定につながります。

7.3 スプレッド拡大を無視する

スプレッドが広がると、エントリー直後の不利な価格差が大きくなります。
特に短期売買では、スプレッド条件が成績に与える影響が大きくなりやすいです。

7.4 既存ポジションの扱いが曖昧

同じ銘柄にすでにポジションがある場合、新規注文を許可するか止めるかを設計で決める必要があります。
netting口座とhedging口座では、同じ注文ロジックでもポジションの見え方が異なる場合があります。

8. バックテストで確認すべき項目

【結論】
バックテストでは、総損益だけでなく、最大ドローダウン、取引回数、連敗数、スプレッド条件、期間依存性、パラメータ依存性を確認します。
エントリー確認条件を増やした場合は、どの条件が成績変化の原因になったかを分けて確認する必要があります。

確認項目は次のとおりです。

項目確認する理由
総損益全体の傾向を見るため
最大ドローダウン資金変動の大きさを把握するため
勝率シグナルの成立後の方向性を確認するため
損益比利益幅と損失幅のバランスを見るため
取引回数条件が厳しすぎないか確認するため
連敗数心理面と資金管理の許容度を見るため
スプレッド条件実運用との差を見積もるため
期間依存性特定期間だけに適合していないか見るため
パラメータ依存性過剰最適化の可能性を確認するため

バックテスト結果は、実運用の利益を保証しません。
検証では、条件を1つずつ追加し、どの確認条件が改善または悪化に影響したかを記録します。

9. フォワードテストで確認すべき項目

【結論】
フォワードテストでは、バックテストで見えにくい約定差、スプレッド拡大、取引頻度、VPS環境での安定性を確認します。
エントリー確認ロジックは、実際のティック更新とブローカー条件の影響を受けます。

フォワードテストで確認する項目は次のとおりです。

項目確認する理由
約定差想定価格と実際の約定価格の差を見るため
スプレッド拡大時の挙動不利な時間帯の注文を避けられるか見るため
取引頻度条件が実環境で成立するか確認するため
ドローダウン資金変動が許容範囲か確認するため
バックテストとの乖離検証環境との差を把握するため
ブローカー差銘柄仕様や約定条件の違いを見るため
VPS環境での安定性EAが継続動作するか確認するため

デモ口座とリアル口座では、約定条件が異なる場合があります。
実運用前には、ロットを抑えた段階的な確認と、停止条件の明確化が必要です。

10. 実運用での注意点

【結論】
実運用では、エントリー確認ロジックだけでリスクを完全に制御できません。
スプレッド、約定遅延、スリッページ、レバレッジ、ドローダウン、ブローカー仕様を含めて管理する必要があります。

10.1 リスク管理を別モジュールにする

エントリー確認は、注文してよい候補を絞る処理です。
損失許容額、ロット計算、最大ドローダウン、連敗停止などは、別のリスク管理処理として分けるほうが安全です。

ロット計算では、最小ロット、最大ロット、ロットステップ、ティックバリュー、ティックサイズ、損切り幅、有効証拠金を考慮します。
固定ロットだけで設計すると、資金変動や銘柄仕様の違いに対応しにくくなります。

10.2 OrderCheckを注文前に使う

MQL5で注文処理を行う場合、OrderCheckで注文前の妥当性を確認すると、証拠金不足や取引条件の問題を見つけやすくなります。
OrderSendの結果だけを見る設計では、原因の切り分けが遅れやすくなります。

10.3 過剰最適化を避ける

エントリー確認条件を増やすほど、過去データにだけ合ったロジックになる場合があります。
パラメータを細かく調整しすぎると、フォワードテストで成績が崩れやすくなります。

11. 改善案と代替手段

【結論】
エントリー確認ロジックを改善する場合は、条件を増やす前に、各条件の目的と停止理由をログで確認します。
代替手段として、上位足確認、ATRによる変動幅判定、時間帯フィルター、注文前チェックの強化があります。

11.1 ログを使って停止理由を残す

注文が出ないEAは、条件が厳しいのか、データ取得に失敗しているのか、取引条件で止まっているのかを判断しにくくなります。
各判定で停止理由をPrintに残すと、テスターのログで原因を確認しやすくなります。

11.2 条件を段階的に追加する

最初から多くの確認条件を入れると、どの条件が有効だったのか分かりにくくなります。
移動平均線確認、RSI確認、スプレッド確認、ポジション確認のように、条件を段階的に追加して検証します。

11.3 売買方向と取引可否を分ける

買いか売りかを決める処理と、取引してよいかを決める処理は分けます。
この分離により、シグナルの精度と取引環境の問題を別々に評価できます。

12. まとめ

【結論】
MQL5のentry confirmation logicは、EAの売買シグナルを注文前に確認し、誤作動や過剰な取引を抑えやすくする設計です。
OnInitでハンドルを作成し、OnTickでCopyBufferを使って確定足の値を取得し、リスクと注文条件を分けて判定します。

エントリー確認ロジックでは、相場環境、シグナル、リスク、注文条件、EAの状態を分けて考えます。
比較表やログを使って条件の役割を明確にすると、バックテストとフォワードテストで改善点を見つけやすくなります。

実運用では、バックテストだけで判断せず、スプレッド、約定差、ブローカー仕様、口座タイプ、ドローダウン許容度を確認する必要があります。
エントリー確認ロジックは利益を保証するものではなく、検証可能なEA設計を作るための構造です。

FAQ

MQL5のentry confirmation logicとは何ですか?

MQL5のentry confirmation logicとは、売買シグナルが出たあとに、相場環境、リスク、取引条件、既存ポジションを確認して注文可否を決める処理です。EAの誤作動や重複注文を抑えやすくする目的で使います。

エントリー確認ロジックはOnTickに書けばよいですか?

OnTickで呼び出す形にするのが基本ですが、処理本体は関数に分けるほうが管理しやすくなります。シグナル判定、フィルター判定、リスク確認、注文前確認を分けると原因分析がしやすくなります。

CopyBufferでは最新足と確定足のどちらを使うべきですか?

検証の再現性を重視する場合は、確定足の値を使う設計が扱いやすくなります。最新足は形成中に値が変わるため、実運用とバックテストの判定がずれる場合があります。

RSIや移動平均線を増やせばエントリー精度は上がりますか?

条件を増やすと不要な取引を減らせる場合がありますが、過剰最適化や取引回数の減少につながる場合もあります。条件の目的を分けて、バックテストとフォワードテストで確認する必要があります。

注文処理を追加する場合に何を確認すべきですか?

注文処理を追加する場合は、MqlTradeRequest、MqlTradeResult、MqlTradeCheckResultを使い、OrderCheckで注文前の妥当性を確認します。最小ロット、最大ロット、ロットステップ、証拠金、ストップレベル、スプレッドも確認します。

netting口座とhedging口座で設計は変わりますか?

netting口座とhedging口座では、同一銘柄のポジション管理が変わります。netting口座ではポジションが集約され、hedging口座では複数ポジションを持てるため、重複注文の扱いを明確にする必要があります。

バックテストで最も注意すべき点は何ですか?

総損益だけで判断しないことが重要です。最大ドローダウン、取引回数、連敗数、スプレッド条件、期間依存性、パラメータ依存性を確認します。

実運用前にフォワードテストは必要ですか?

実運用前にはフォワードテストが必要です。バックテストでは見えにくい約定差、スプレッド拡大、ブローカー条件、VPS環境での安定性を確認するためです。