MQL5 invalid stops対策:失敗しない注文設計

目次

1. MQL5の「invalid stops」とは何か

【結論】
「invalid stops」は、注文時に設定したストップロス(SL)やテイクプロフィット(TP)が、ブローカーの許容条件を満たしていない場合に発生するエラーです。
主因は「価格距離不足」「価格精度の不一致」「取引条件の未考慮」です。

1.1 invalid stopsの定義

【結論】
invalid stopsとは、「注文に付与したSL/TPが、ブローカーの最小距離や価格ルールに違反している状態」を指します。

【定義】
MQL5における「invalid stops」は、OrderSendやtrade関数実行時に返されるエラーコードの一つであり、以下のような条件違反で発生します。

  • ストップレベル(最小距離)未満
  • 現在価格に対して不正な方向
  • 桁数(Digits)不一致
  • フリーズレベル違反

これらはすべて「注文条件(order条件)」の不整合に分類されます。

1.2 なぜinvalid stopsが発生するのか

【結論】
invalid stopsは「ブローカー仕様を無視した価格設定」により発生します。特に自動売買(EA)では頻発します。

主な原因は以下です。

  • ストップレベル(SYMBOL_TRADE_STOPS_LEVEL)を考慮していない
  • Bid/Askの違いを無視している
  • スプレッド拡大時の距離不足
  • Digits(価格精度)の丸めミス

例えば、買い注文(Buy)の場合:

  • SLはBidより下
  • TPはBidより上

でなければなりませんが、これを逆にすると即エラーになります。

さらに、ブローカーは「最小距離(例:10ポイント)」を要求するため、以下のようなコードは失敗します。

double sl = Bid - 5 * _Point;  // 距離不足でinvalid stops

この場合、必要距離を満たしていないためエラーになります。

1.3 実際のエラー発生パターン

【結論】
invalid stopsは「正常に見えるコードでも発生する」ため、構造的に理解する必要があります。

代表的なパターン:

  • バックテストでは通るがリアルで失敗
  • スプレッド拡大時のみ失敗
  • 指標発表時にのみ発生(slippage影響)
  • ECN口座でのみ発生(約定条件が厳しい)

特に注意すべき点:

  • execution方式(Market/Instant Execution)により挙動が変わる
  • スプレッド(spread)が動的に変化する
  • フリーズレベルで修正不可になるケース

これらはすべて「実行環境依存」の要素です。

1.4 初心者がつまずきやすいポイント

【結論】
最大の失敗は「固定値でSL/TPを決めてしまうこと」です。

よくあるミス:

  • 固定ポイントでSL/TPを設定
  • SYMBOL_TRADE_STOPS_LEVELを未取得
  • NormalizeDoubleを使っていない
  • Ask/Bidの使い分けミス

特に以下は典型例です。

double sl = Bid - 10 * _Point;
double tp = Bid + 10 * _Point;

問題点:

  • 最小距離未考慮
  • スプレッド無視
  • 桁数調整なし

この状態では、環境によって成功・失敗が分かれます。

1.5 なぜ事前チェックが重要なのか

【結論】
invalid stopsは「事前に100%防げるエラー」であり、チェック処理の有無が品質差になります。

理由:

  • 注文失敗=機会損失(期待値低下)
  • EAの再現性が崩れる
  • ログが汚れデバッグ困難になる

そのため、実務では必ず以下を行います。

  • ストップレベル取得
  • 現在価格との距離チェック
  • 正規化(NormalizeDouble)
  • 注文前バリデーション

これはアルゴリズムトレードにおける「リスク管理」の一部です。

2. invalid stopsを回避するための実装手順

【結論】
invalid stopsは「最小距離の取得 → 価格計算 → 正規化 → バリデーション」の順で処理すれば確実に防げます。
この4ステップをテンプレート化することが再現性の鍵です。

2.1 ストップレベル(最小距離)を取得する

【結論】
まず、ブローカーごとに異なる最小距離(ストップレベル)を必ず取得します。

MQL5では以下で取得できます。

int stopLevel = (int)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
double minDistance = stopLevel * _Point;

ポイント:

  • stopLevelは「ポイント単位」
  • _Pointを掛けて価格単位に変換する

注意点:

  • 通貨ペアごとに異なる
  • 口座タイプ(ECN/スタンダード)でも変わる
  • 0が返る場合もある(その場合でも安全側で距離を取る)

なぜ必要か:

  • ブローカーのexecution条件を満たさないと注文が拒否されるため

2.2 Bid/Askを正しく使ってSL/TPを計算する

【結論】
BuyとSellで基準価格が異なるため、Bid/Askを正確に使い分ける必要があります。

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

Buy注文の場合:

double sl = bid - minDistance;
double tp = bid + minDistance;

Sell注文の場合:

double sl = ask + minDistance;
double tp = ask - minDistance;

重要ポイント:

  • BuyはBid基準、SellはAsk基準
  • spreadを無視すると距離不足になる

よくある失敗:

  • 全てBid基準で計算する
  • spread拡大時にSLが近すぎる

2.3 価格をNormalizeDoubleで正規化する

【結論】
価格は必ずDigitsに合わせて丸める必要があります。これを怠るとinvalid stopsになります。

int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);

sl = NormalizeDouble(sl, digits);
tp = NormalizeDouble(tp, digits);

理由:

  • ブローカーは価格精度(Digits)を厳密にチェックするため
  • 小数誤差でも注文拒否される

補足:

  • Normalizeしないと「見た目は正しいが内部的に不正」になる

2.4 注文前に距離チェックを行う

【結論】
最終的に「現在価格との距離」をチェックすれば、invalid stopsは完全に防げます。

if((bid - sl) < minDistance)
{
    Print("SL too close");
    return;
}

if((tp - bid) < minDistance)
{
    Print("TP too close");
    return;
}

Sellの場合は逆方向になるため注意:

if((sl - ask) < minDistance)
{
    Print("SL too close");
    return;
}

重要な考え方:

  • 「注文前に落とす」ことで無駄なOrderSendを防ぐ
  • ログが整理され、デバッグ効率が上がる

2.5 実務用テンプレートコード

【結論】
以下のように一連の処理をまとめると、再利用性が高くなります。

double GetValidSL(bool isBuy, double price)
{
    int stopLevel = (int)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
    double minDistance = stopLevel * _Point;
    int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);

    double sl;

    if(isBuy)
        sl = price - minDistance;
    else
        sl = price + minDistance;

    return NormalizeDouble(sl, digits);
}

このように関数化することで:

  • ロジックの統一
  • バグの局所化
  • EA全体の品質向上

が実現できます。

2.6 よくある失敗と対策

【結論】
invalid stopsの多くは「環境変化を考慮していない設計」によって発生します。

失敗例と対策:

  • 固定値SL → 最小距離ベースに変更
  • スプレッド無視 → ask/bid基準に変更
  • 正規化なし → NormalizeDouble必須
  • テストのみ確認 → 実口座で検証

特に重要:

  • バックテストはspread固定のため再現性が低い
  • 実運用ではslippageやexecutionの影響を受ける

3. それでもinvalid stopsが発生するケースと対処法

【結論】
適切に実装してもinvalid stopsが発生する場合は、「市場状態」または「ブローカー仕様」による例外が原因です。
この章では、実務で遭遇する再現性の低いケースと対処法を整理します。

3.1 スプレッド拡大による距離不足

【結論】
スプレッド(spread)が急拡大すると、事前に計算したSL/TPが瞬時に最小距離未満になることがあります。

典型例:

  • 指標発表時
  • ロールオーバー時間帯
  • 流動性が低い時間帯(早朝など)

問題の構造:

  • SL/TPはBid/Ask基準で計算
  • しかし発注直前にspreadが変動
  • 結果として距離不足になる

対策:

double spread = SymbolInfoDouble(_Symbol, SYMBOL_ASK) - SymbolInfoDouble(_Symbol, SYMBOL_BID);

// 最低距離にスプレッドを上乗せ
double safeDistance = minDistance + spread;

重要ポイント:

  • 「理論値」ではなく「安全マージン」を持たせる
  • 実運用では+α(例:1.2倍)することもある

3.2 フリーズレベルによる制限

【結論】
フリーズレベル(SYMBOL_TRADE_FREEZE_LEVEL)により、注文直前や変更時に制限がかかることがあります。

取得方法:

int freezeLevel = (int)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_FREEZE_LEVEL);

影響:

  • 指定距離内では注文変更不可
  • SL/TPの設定・修正が拒否される

よくあるケース:

  • 成行注文後にSL/TPを後付け
  • 価格が急変してフリーズ範囲に入る

対策:

  • 初回注文時にSL/TPを同時設定
  • もしくはフリーズ距離外でのみ変更

3.3 ECN口座特有の挙動

【結論】
ECN口座では「注文時にSL/TPを付けられない」ケースがあり、invalid stopsの原因になります。

特徴:

  • 約定優先(execution重視)
  • SL/TPは後付けが前提

典型的なエラー:

// 注文時にSL/TPを指定 → invalid stops

対処手順:

  1. OrderSendでポジションのみ取得
  2. 約定後にPositionModifyでSL/TP設定
// 疑似フロー
OrderSend(...);  
PositionModify(...);

注意点:

  • フリーズレベルと併用で失敗する場合あり
  • 約定直後の価格変動に注意

3.4 約定遅延とslippageの影響

【結論】
注文送信から約定までの遅延により、価格が変化し、SL/TPが無効になることがあります。

原因:

  • ネットワーク遅延
  • サーバー負荷
  • execution方式

影響:

  • 計算時の価格 ≠ 約定時の価格
  • 結果として距離条件を満たさない

対策:

double deviation = 10; // 許容スリッページ

さらに重要:

  • SL/TPを「価格固定」ではなく「距離ベース」で再計算
  • 約定後に再設定する設計も有効

3.5 バックテストとリアル環境の差異

【結論】
バックテストではinvalid stopsが出ないのに、リアルで発生するのは正常です。

理由:

  • スプレッドが固定
  • 約定遅延が存在しない
  • execution条件が単純化されている

そのため:

  • テストで通る = 安全ではない
  • 実運用で検証が必須

実務対応:

  • デモ口座でフォワードテスト
  • ログを詳細に出力
Print("SL:", sl, " TP:", tp, " Bid:", bid);

3.6 例外を前提とした設計

【結論】
完全にエラーをゼロにするのではなく、「発生しても安全に処理する設計」が重要です。

推奨設計:

  • OrderSend失敗時にリトライ
  • 条件を再計算して再発注
  • ログに原因を記録

例:

if(!OrderSend(request, result))
{
    Print("Order failed, retrying...");
    Sleep(500);
    // 再計算して再送信
}

重要な視点:

  • invalid stopsは「環境依存エラー」
  • 完全排除ではなく「制御」が本質

4. よくある原因パターン

【結論】
invalid stopsの原因は多く見えますが、実務ではほぼ「価格距離」「価格方向」「価格精度」の3系統に整理できます。
原因をパターン化して理解すると、ログ解析と修正が一気に速くなります。

4.1 ストップレベルを考慮していない

【結論】
もっとも多い原因は、ブローカーが要求する最小距離を満たしていないことです。

MQL5では、SLやTPを現在価格の近くに置きすぎると、注文自体が拒否されます。
この最小距離はブローカーごと、銘柄ごとに異なり、同じEAでも環境が変わると急にinvalid stopsが出ることがあります。

典型例:

double sl = bid - 10 * _Point;
double tp = bid + 10 * _Point;

このコードは、一見すると正しそうに見えます。
しかし、実際のストップレベルが20ポイントなら、SLもTPも条件違反です。

つまずきやすい点:

  • 自分の口座で一度通ったので他でも通ると思ってしまう
  • 通貨ペアが変わっても同じ値で動くと考えてしまう
  • テスターで問題が出なかったため本番でも安全だと誤認する

対策は明確です。

  • SYMBOL_TRADE_STOPS_LEVELを毎回取得する
  • 固定値ではなく、取得値ベースで距離を計算する
  • 余裕を持たせるならspreadや安全マージンも加味する

4.2 BidとAskの使い分けを間違えている

【結論】
BuyとSellで基準価格が異なるのに、同じ式でSL/TPを計算するとinvalid stopsが起きやすくなります。

基本ルールは次のとおりです。

  • Buy系はBid基準でSL/TPを見る
  • Sell系はAsk基準でSL/TPを見る

初心者がやりがちな失敗は、どちらも現在価格としてBidだけを使うことです。

誤りやすい例:

double sl = bid - minDistance;
double tp = bid + minDistance;

// Sellでもそのまま使ってしまう

この書き方だと、Sell注文では価格方向が崩れます。
その結果、TPが不正方向になったり、必要距離を満たしていないと判定されたりします。

確認ポイント:

  • BuyのSLは現在価格より下
  • BuyのTPは現在価格より上
  • SellのSLは現在価格より上
  • SellのTPは現在価格より下

これは単純ですが、最も重要な基礎です。
価格の方向を誤ると、距離以前に注文条件そのものが不正になります。

4.3 _Pointとpipsを混同している

【結論】
MQL5では「ポイント」と「pips」は同じ意味ではありません。ここを混同すると距離計算がずれます。

たとえば5桁通貨ペアでは、1 pip = 10 points になることがあります。
そのため、「20pipsのつもり」で 20 * _Point と書くと、実際には2pipsしか確保できていないケースがあります。

典型例:

double sl = bid - 20 * _Point;

このコードは、銘柄によっては十分な距離になりません。

よくある失敗:

  • MT4時代の感覚のまま書く
  • USDJPYとEURUSDで同じ距離式を流用する
  • goldや指数など、FX以外のsymbol仕様を無視する

なぜ危険か:

  • 銘柄ごとの価格刻みが異なる
  • 同じロジックでも実距離が変わる
  • invalid stopsだけでなく、意図しない損切り幅にもつながる

対策:

  • 距離の内部単位を「ポイント」に統一する
  • 必要に応じてpip換算関数を用意する
  • symbolごとの差を前提に設計する

4.4 NormalizeDoubleを使っていない

【結論】
SL/TPの見た目が正しくても、内部値に微小な誤差が残っているとinvalid stopsになることがあります。

MQL5の価格計算はdoubleで行われます。
doubleは便利ですが、小数を完全に表現できない場合があります。
そのため、計算結果をそのまま注文に使うと、ブローカー側で「不正な価格精度」とみなされることがあります。

例:

sl = bid - minDistance;
tp = bid + minDistance;

この段階では、内部的に余分な小数が含まれている可能性があります。
そこで必ず以下を行います。

sl = NormalizeDouble(sl, _Digits);
tp = NormalizeDouble(tp, _Digits);

注意点:

  • _Digits を使わず固定桁数にすると、symbol変更時に破綻しやすい
  • NormalizeDoubleだけで全問題が解決するわけではない
  • 先に距離計算、後で正規化、の順番が基本

つまり、NormalizeDoubleは万能ではありませんが、必須の仕上げ処理です。

4.5 スプレッドを無視している

【結論】
spreadを無視すると、静かな相場では通っても、実運用ではinvalid stopsが断続的に発生します。

特に次の時間帯は危険です。

  • 経済指標前後
  • ロールオーバー前後
  • 早朝や流動性が薄い時間帯

このとき、BidとAskの差が急に広がります。
その結果、事前に計算したSL/TPが「発注時点では近すぎる」と判定されることがあります。

対策としては、最低距離にspreadを加味する方法が実務的です。

double spread = ask - bid;
double safeDistance = minDistance + spread;

さらに安全側に寄せるなら、

  • safeDistance = minDistance + spread
  • または safeDistance = minDistance * 1.2

のような設計もあります。

代替手段として、注文時にSL/TPを付けず、約定後に再設定する方法もあります。
ただし、これはフリーズレベルや約定遅延の影響を受けるため、別の注意が必要です。

4.6 固定値ロジックをそのまま流用している

【結論】
他のEA、古いコード、ネット上のサンプルをそのまま使うと、口座条件の違いでinvalid stopsが出やすくなります。

よくあるケース:

  • 「SL=100ポイント」で固定
  • 「TP=200ポイント」で固定
  • symbolごとの条件分岐なし
  • broker差を無視

サンプルコードは学習には便利ですが、実運用前提では不十分なことが多いです。
なぜなら、ブローカーのexecution、spread、stop level、freeze levelは統一されていないからです。

このため、実務では以下の発想が重要です。

  • 固定値ではなく、環境に応じて計算する
  • symbolと口座条件を毎回取得する
  • 注文前チェックを標準化する

これができると、invalid stopsは「たまに出る厄介なエラー」ではなく、「設計で制御できる条件違反」に変わります。

5. invalid stops対策の設計比較

【結論】
invalid stops対策は複数ありますが、実務では「事前チェック型」が最も再現性・安定性・期待値のバランスに優れます。
他手法は状況依存であり、設計意図を理解して使い分ける必要があります。

5.1 主な対策手法の一覧

【結論】
対策は大きく4パターンに分類できます。

手法概要特徴
事前チェック型注文前に条件検証最も安定
後付け設定型約定後にSL/TP設定ECN向け
リトライ型エラー後に再送信応急処置
固定値型SL/TP固定非推奨

この中で「どれを採用するか」で、EAの品質が大きく変わります。


5.2 事前チェック型(推奨)

【結論】
最も安全で再現性が高いのが事前チェック型です。

処理フロー:

  1. stop level取得
  2. spread取得
  3. SL/TP計算
  4. 距離チェック
  5. 正規化
  6. OrderSend

イメージコード:

double minDistance = stopLevel * _Point;
double spread = ask - bid;
double safeDistance = minDistance + spread;

// 距離チェック
if((bid - sl) < safeDistance)
    return;

メリット:

  • invalid stopsを事前に防げる
  • ログが整理される
  • 無駄な注文が減る(execution効率向上)

デメリット:

  • 実装コストがやや高い
  • 初心者には理解が難しい

ただし、長期運用では最も期待値が高くなります。


5.3 後付け設定型(ECN向け)

【結論】
ECN口座では有効ですが、設計難易度が上がります。

処理フロー:

  1. SL/TPなしで注文
  2. 約定確認
  3. PositionModifyで設定

コードイメージ:

// 注文
OrderSend(request, result);

// 約定後に設定
PositionModify(_Symbol, sl, tp);

メリット:

  • ECN口座で安定
  • execution優先の環境に適合

デメリット:

  • フリーズレベルの影響を受ける
  • 約定直後の価格変動に弱い
  • ロジックが複雑化

リスク:

  • SL設定前に急変すると損失拡大

そのため、リスク許容度に応じて採用判断が必要です。


5.4 リトライ型(補助的手法)

【結論】
invalid stops発生後に再試行する方法ですが、根本解決にはなりません。

例:

if(!OrderSend(request, result))
{
    Sleep(500);
    // 再計算して再送信
}

メリット:

  • 実装が簡単
  • 一時的な市場変動に対応

デメリット:

  • エラー前提の設計になる
  • ログが汚れる
  • 無駄な通信が増える

評価:

  • 単独使用は非推奨
  • 事前チェック型と併用が現実的

5.5 固定値型(非推奨)

【結論】
SL/TPを固定値で設定するだけの設計は、現代の環境では破綻しやすいです。

例:

double sl = bid - 100 * _Point;
double tp = bid + 200 * _Point;

問題点:

  • ブローカー差に対応できない
  • スプレッド変動に弱い
  • symbol変更で破綻

短期的には動いても、長期的には不安定です。


5.6 実務での最適設計

【結論】
現実的な最適解は「事前チェック型 + 必要に応じて後付け設定型」の組み合わせです。

設計指針:

  • 基本は事前チェック型
  • ECN口座では後付け併用
  • リトライは例外処理として限定使用

評価軸で比較:

観点事前チェック後付けリトライ
再現性
安定性
実装難易度
リスク

重要な考え方:

  • invalid stopsは「設計で潰す」
  • エラー処理ではなく予防が本質
  • execution環境を前提にロジックを組む

6. invalid stopsを防ぐチェックリスト

【結論】
invalid stopsは「チェック項目を固定化」すれば再発をほぼ防げます。
実務では、注文前に以下の項目を機械的に確認することが重要です。

6.1 必須チェック項目一覧

【結論】
以下の6項目を満たしていれば、invalid stopsはほぼ発生しません。

チェックリスト:

  • ストップレベルを取得している
  • Bid/Askを正しく使い分けている
  • SL/TPの方向が正しい
  • 最小距離以上を確保している
  • NormalizeDoubleで正規化している
  • スプレッドを考慮している

この6つは「最低限の条件」です。
1つでも欠けると、環境によってエラーが発生します。


6.2 注文前チェックの実装テンプレート

【結論】
チェック処理は関数化して使い回すことで、ミスを防げます。

bool IsValidStops(bool isBuy, double sl, double tp)
{
    double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
    double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

    int stopLevel = (int)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
    double minDistance = stopLevel * _Point;

    double spread = ask - bid;
    double safeDistance = minDistance + spread;

    if(isBuy)
    {
        if(sl >= bid) return false;
        if(tp <= bid) return false;
        if((bid - sl) < safeDistance) return false;
        if((tp - bid) < safeDistance) return false;
    }
    else
    {
        if(sl <= ask) return false;
        if(tp >= ask) return false;
        if((sl - ask) < safeDistance) return false;
        if((ask - tp) < safeDistance) return false;
    }

    return true;
}

使い方:

if(!IsValidStops(true, sl, tp))
{
    Print("Invalid stops detected");
    return;
}

ポイント:

  • 「注文前に落とす」設計
  • OrderSend前に必ず通す
  • ロジックの再利用性を確保

6.3 ログ出力によるデバッグ強化

【結論】
invalid stopsはログを見れば原因が特定できます。ログ設計は必須です。

Print("Bid:", bid, " Ask:", ask);
Print("SL:", sl, " TP:", tp);
Print("MinDistance:", minDistance, " Spread:", spread);

重要な観点:

  • 数値を必ず出力する
  • 条件違反の箇所を明確にする
  • 「なぜ失敗したか」を残す

よくある問題:

  • エラーコードだけ見て原因不明
  • ログ不足で再現できない

ログは「後から検証できる状態」を作るためのものです。


6.4 実運用前の確認手順

【結論】
バックテストだけでは不十分で、必ずフォワードテストを行う必要があります。

推奨手順:

  1. ストラテジーテスターで基本動作確認
  2. デモ口座でフォワードテスト
  3. スプレッド拡大時間帯で検証
  4. 実口座で少額テスト

理由:

  • テスターはexecution条件が簡略化されている
  • real環境ではslippageや遅延が発生する

特に重要:

  • 指標時にエラーが出ないか
  • ロールオーバーで問題が出ないか

6.5 再発防止の設計思想

【結論】
invalid stops対策は「コード修正」ではなく「設計ルール化」が重要です。

実務ルール例:

  • SL/TPは必ず関数経由で生成
  • 注文前チェックを必須化
  • 固定値ロジックは禁止
  • symbol依存処理を明示

これにより:

  • 開発者が変わっても品質維持
  • バグの再発防止
  • EA全体の安定性向上

6.6 最小構成チェック(簡易版)

【結論】
最低限、以下だけでも実装すれば大きな事故は防げます。

if((bid - sl) < minDistance || (tp - bid) < minDistance)
{
    Print("Invalid stops");
    return;
}

ただしこれは簡易版です。
実務では必ずフルチェックを推奨します。

7. まとめと実装ポイントの整理

【結論】
invalid stopsは「環境依存の例外」ではなく、「設計で制御できる条件違反」です。
最小距離・価格方向・価格精度の3点を統合的に管理すれば、再現性高く回避できます。

7.1 最重要ポイントの要約

【結論】
以下の5点を守るだけで、invalid stopsの大半は防げます。

  • ストップレベルを必ず取得する
  • Bid/Askを正しく使い分ける
  • 最小距離+スプレッドを確保する
  • NormalizeDoubleで価格を正規化する
  • 注文前チェックを必須化する

これらは単体ではなく「セット」で機能します。
どれか1つ欠けると、環境によって破綻します。


7.2 実装フローの最適形

【結論】
実務では以下の順序で処理を統一すると安定します。

実装フロー:

  1. 現在価格取得(Bid/Ask)
  2. ストップレベル取得
  3. スプレッド取得
  4. SL/TP計算
  5. 正規化(NormalizeDouble)
  6. 距離チェック
  7. OrderSend

この順番を崩すと、微妙なズレが発生します。

特に重要:

  • 計算 → 正規化 → チェック の順序
  • チェック後に値を変更しない

7.3 再現性を高めるための考え方

【結論】
EAの品質は「エラーを出さないこと」ではなく「同じ条件で同じ結果になること」です。

invalid stopsが問題になる理由:

  • 注文失敗でトレード機会を失う
  • バックテストと実運用の乖離が発生する
  • パフォーマンス(PF)が歪む

つまりこれは単なるエラーではなく、期待値に直結する問題です。

対策の本質:

  • execution条件をロジックに組み込む
  • ブローカー依存を前提にする
  • 環境変動(spread・slippage)を考慮する

7.4 よくある誤解

【結論】
invalid stopsは「たまに起きる不可避の問題」ではありません。

誤解例:

  • テスターで問題ないからOK
  • 一部口座で通るから問題ない
  • リトライすれば解決する

実際には:

  • テスターは簡略化環境
  • ブローカーごとに条件が異なる
  • リトライは根本対策ではない

したがって、設計段階で潰すべき問題です。


7.5 実務での最終チェック

【結論】
実運用前に、以下を満たしていれば安全性は高いです。

最終確認:

  • 異なるブローカーでテスト済み
  • デモ口座でフォワード確認済み
  • スプレッド拡大時も正常動作
  • ログで全条件を確認できる

これにより、環境差による不具合を最小化できます。


7.6 実装者への指針

【結論】
invalid stops対策は「例外処理」ではなく「標準機能」として組み込むべきです。

実務ルール:

  • SL/TP生成は必ず関数化する
  • 注文前チェックは全EA共通化する
  • 固定値ロジックは排除する
  • ログ出力を標準化する

このレベルまで整理すると、EAの品質は一段上がります。

8. よくある質問

【結論】
invalid stopsは「ストップ価格がブローカー条件を満たしていない」ときに発生します。
実務では、StopLevel・Bid/Ask・価格精度の確認で大半を防げます。

8.1 invalid stopsとは何ですか

【結論】
invalid stopsとは、設定したストップロス(SL)やテイクプロフィット(TP)が不正で、注文条件に合っていない状態です。

具体的には、次のようなケースで発生します。

  • 現在価格に近すぎる
  • 価格の向きが逆
  • 桁数が合っていない
  • ブローカーの最小距離に違反している

つまり、単なる「MQL5の文法エラー」ではなく、「注文条件エラー」です。

8.2 StopLevelはどこで取得できますか

【結論】
StopLevelは SymbolInfoInteger で取得できます。

基本コードは次のとおりです。

int stopLevel = (int)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
double minDistance = stopLevel * _Point;

注意点:

  • 値はポイント単位で返る
  • 実際の価格差に使うには _Point を掛ける
  • symbolごと、brokerごとに異なる

この値を取らずに固定値でSL/TPを置くと、invalid stopsの原因になります。

8.3 なぜバックテストでは問題ないのに本番でエラーが出るのですか

【結論】
ストラテジーテスターは、実際のexecution環境を完全には再現しないためです。

本番との差は主に次のとおりです。

  • spreadが固定または単純化される
  • slippageが実環境ほど出ない
  • 約定遅延が小さい
  • broker固有の挙動が反映されにくい

そのため、バックテストで通ったコードでも、リアル口座やデモ口座ではinvalid stopsが出ることがあります。

8.4 SLやTPを付けずに注文すれば回避できますか

【結論】
一時的な回避は可能ですが、根本解決ではありません。

たしかに、ECN口座などでは以下の流れが有効な場合があります。

  • まず成行注文だけ出す
  • 約定後にSL/TPを後付けする

ただし、この方法にも注意点があります。

  • フリーズレベルで変更できないことがある
  • 約定直後の価格変動で失敗することがある
  • リスク管理上、保護なし時間が発生する

したがって、これは「代替手段」であり、基本は事前チェック付きの設計が推奨です。

8.5 BuyとSellでSL/TPの計算は何が違いますか

【結論】
基準価格と方向が逆になります。

基本ルールは次のとおりです。

  • Buy
    • SLは現在価格より下
    • TPは現在価格より上
  • Sell
    • SLは現在価格より上
    • TPは現在価格より下

また、実装時には以下の点が重要です。

  • BuyではBid基準で確認する
  • SellではAsk基準で確認する

ここを間違えると、距離が足りていてもinvalid stopsになることがあります。

8.6 NormalizeDoubleは必須ですか

【結論】
はい、価格を注文に使う前にほぼ必須です。

理由は、double計算では内部的に微小な誤差が残ることがあるためです。
見た目は正しくても、broker側では不正な価格と判定されることがあります。

基本形は次のとおりです。

sl = NormalizeDouble(sl, _Digits);
tp = NormalizeDouble(tp, _Digits);

ただし、NormalizeDoubleだけでは不十分です。
StopLevelやspreadを無視していれば、正規化してもinvalid stopsは防げません。

8.7 spreadはどの程度考慮すべきですか

【結論】
最低でも、最小距離に現在のspreadを加味する設計が安全です。

たとえば次のように計算します。

double spread = ask - bid;
double safeDistance = minDistance + spread;

さらに、指標時や早朝など変動が大きい時間帯では、少し余裕を持たせる設計も有効です。

  • minDistance + spread
  • minDistance * 1.2
  • minDistance + spread + α

どこまで余裕を取るかは、手法の性質とリスク許容度によります。

8.8 invalid stopsを最短で直すには何を見ればよいですか

【結論】
最短で直すには、まず「価格方向」「最小距離」「桁数」の3点を確認します。

優先順は次のとおりです。

  • SL/TPの向きが正しいか
  • StopLevel以上の距離があるか
  • NormalizeDoubleしているか
  • Bid/Askを正しく使っているか
  • spread拡大時でも成立するか

この順で確認すると、ほとんどのケースで原因を特定できます。

9. invalid stopsと合わせて見直すべき関連項目

【結論】
invalid stopsは単独の問題ではなく、「注文処理全体の設計不備」の一部です。
同時に他の注文エラーや実行条件(execution条件)も見直すことで、EAの安定性と期待値が大きく向上します。

9.1 not enough money(証拠金不足)

【結論】
invalid stopsと同様に頻発するのが「not enough money」であり、ロットサイズと証拠金管理の問題です。

発生原因:

  • ロットが口座残高に対して大きすぎる
  • レバレッジ設定を考慮していない
  • 複数ポジションで証拠金が圧迫されている

対策:

double freeMargin = AccountInfoDouble(ACCOUNT_FREEMARGIN);

重要ポイント:

  • 注文前に必要証拠金を計算する
  • ロットサイズを動的に調整する
  • DD(ドローダウン)を前提に設計する

invalid stopsと同様、「事前チェック」で防ぐべきエラーです。


9.2 requote(再提示)

【結論】
requoteは価格変動により、提示価格で約定できない場合に発生します。

原因:

  • 市場の急変(指標、ニュース)
  • slippage許容値が小さい
  • execution方式の影響

対策:

request.deviation = 10; // 許容スリッページ

補足:

  • deviationを広げると約定率は上がる
  • ただし価格のズレ(slippage)は増える

これは「約定優先か価格精度優先か」のトレードオフです。


9.3 off quotes / trade disabled

【結論】
市場が停止している、または取引不可の状態でも注文は失敗します。

典型例:

  • 週末
  • ロールオーバー直後
  • broker側の制限

対策:

bool tradeAllowed = (bool)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_MODE);

確認すべき点:

  • 取引可能状態か
  • セッション時間内か
  • シンボルが有効か

これを無視すると、EAが「動いているのに取引しない」状態になります。


9.4 slippage(スリッページ)の設計

【結論】
slippageは避けるものではなく、「制御する対象」です。

影響:

  • 約定価格がズレる
  • SL/TP距離が変化する
  • invalid stopsの間接原因になる

設計方針:

  • 固定値ではなく、相場状況に応じて調整
  • 指標時は許容値を広げる
  • 平常時は狭める

例:

request.deviation = dynamicDeviation;

ここを固定にすると、約定率と価格精度のバランスが崩れます。


9.5 execution方式の違い

【結論】
execution方式(Market / Instant / Exchange)により、注文挙動は変わります。

主な違い:

方式特徴
Market Execution即時約定、価格変動あり
Instant Execution価格固定、requote発生
Exchange Execution取引所ベース

invalid stopsとの関係:

  • Marketは価格ズレ → 距離不足になりやすい
  • Instantは価格固定 → requoteになりやすい

対策:

  • 口座仕様に合わせた設計
  • 同一EAでも環境別チューニング

9.6 ロットサイズとリスク管理

【結論】
注文エラー対策は「ロット設計」とセットで考える必要があります。

理由:

  • ロットが大きいほどエラーの影響が大きい
  • DD時に証拠金不足と連鎖する
  • execution失敗が期待値に直結する

実務設計:

  • 口座残高に対する%ベース
  • ボラティリティに応じた調整
  • 最大ポジション数の制限

例:

double riskPercent = 0.01; // 1%

9.7 統合的な注文管理設計

【結論】
invalid stops対策は「注文管理モジュール」の一部として統合するべきです。

統合設計例:

  • SL/TP生成モジュール
  • 注文前チェックモジュール
  • エラーハンドリングモジュール
  • ログ出力モジュール

これにより:

  • バグの局所化
  • 再利用性向上
  • EA全体の安定性向上

重要な視点:

  • 個別対応ではなく構造対応
  • エラーを「設計で吸収」する

9.8 実務における優先順位

【結論】
すべてを一度に対応する必要はなく、優先順位を付けることが重要です。

推奨順:

  1. invalid stops(基本条件)
  2. not enough money(資金管理)
  3. slippage / requote(execution)
  4. 取引可否チェック(環境)

この順番で整備すると、効率的に安定性が向上します。

10. 安定したEAを構築するための注文処理テンプレート

【結論】
EAの安定性は「注文処理のテンプレート化」で決まります。
invalid stopsを含むすべての注文エラーは、統一された処理フローで大幅に削減できます。

10.1 注文処理の基本構造

【結論】
注文処理は「取得 → 計算 → 検証 → 実行 → ログ」の5段階で設計します。

標準フロー:

  1. 市場情報取得(Bid / Ask / spread)
  2. パラメータ取得(StopLevel / Digits)
  3. SL/TP計算
  4. 注文前チェック
  5. OrderSend実行
  6. 結果ログ出力

この構造を崩さないことが最重要です。


10.2 テンプレートコード(基本形)

【結論】
以下のテンプレートをベースにすれば、invalid stopsを含む多くのエラーを回避できます。

bool ExecuteOrder(bool isBuy)
{
    double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
    double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
    double price = isBuy ? ask : bid;

    int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
    int stopLevel = (int)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);

    double minDistance = stopLevel * _Point;
    double spread = ask - bid;
    double safeDistance = minDistance + spread;

    double sl, tp;

    if(isBuy)
    {
        sl = bid - safeDistance;
        tp = bid + safeDistance;
    }
    else
    {
        sl = ask + safeDistance;
        tp = ask - safeDistance;
    }

    sl = NormalizeDouble(sl, digits);
    tp = NormalizeDouble(tp, digits);

    // チェック
    if(!IsValidStops(isBuy, sl, tp))
    {
        Print("Invalid stops detected");
        return false;
    }

    MqlTradeRequest request;
    MqlTradeResult result;
    ZeroMemory(request);

    request.action = TRADE_ACTION_DEAL;
    request.symbol = _Symbol;
    request.volume = 0.1;
    request.type = isBuy ? ORDER_TYPE_BUY : ORDER_TYPE_SELL;
    request.price = price;
    request.sl = sl;
    request.tp = tp;
    request.deviation = 10;

    if(!OrderSend(request, result))
    {
        Print("OrderSend failed: ", result.retcode);
        return false;
    }

    return true;
}

重要ポイント:

  • すべての処理を1つの流れに統合
  • エラー発生前に検証
  • ログを残す

10.3 モジュール分割による設計

【結論】
処理を分割することで、保守性と再利用性が大幅に向上します。

推奨構成:

  • GetMarketInfo()
  • CalculateStops()
  • ValidateStops()
  • ExecuteTrade()

例:

double CalculateSL(bool isBuy, double distance)
{
    double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
    double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

    if(isBuy)
        return bid - distance;
    else
        return ask + distance;
}

メリット:

  • ロジックの分離
  • テストしやすい
  • バグの影響範囲が限定される

10.4 ログ設計のテンプレート

【結論】
ログは「再現性確保」のための必須要素です。

Print("Symbol:", _Symbol);
Print("Bid:", bid, " Ask:", ask);
Print("SL:", sl, " TP:", tp);
Print("StopLevel:", stopLevel, " Spread:", spread);

設計ポイント:

  • 必ず数値を出す
  • 失敗条件もログに残す
  • 時系列で追えるようにする

これにより、問題発生時の原因特定が高速化します。


10.5 拡張設計(実務向け)

【結論】
実務では、さらに以下の要素を追加します。

拡張要素:

  • 動的ロット計算(リスクベース)
  • スプレッドフィルター(閾値以上は取引停止)
  • 時間フィルター(指標・ロールオーバー回避)
  • リトライ処理(例外時のみ)

例:

if(spread > maxSpread)
{
    Print("Spread too high");
    return false;
}

これにより:

  • 無駄なトレードを削減
  • リスク低減
  • 安定運用が可能

10.6 実務での評価基準

【結論】
EAの注文処理は「動くか」ではなく「安定して動き続けるか」で評価します。

評価指標:

  • エラー発生率
  • 約定成功率
  • ログの可読性
  • 再現性(バックテスト vs 実運用)

特に重要:

  • invalid stopsがゼロに近い状態
  • 注文失敗時も制御されている

10.7 最終的な設計思想

【結論】
注文処理は「個別対応」ではなく「標準化されたインフラ」として構築するべきです。

重要な考え方:

  • エラーは発生する前に防ぐ
  • 環境依存を前提にする
  • execution条件をロジックに組み込む

このレベルで設計できると、EAの品質は大きく向上します。