1. MQL5のOrderCheckとは何か
1.1 OrderCheckの役割(注文前検証)
MQL5におけるOrderCheckは、注文を送信する前に、その注文が実行可能かどうかを検証するための関数です。
具体的には、以下のような条件を事前にチェックします。
- 証拠金が足りているか(marginチェック)
- ロットサイズがブローカーの条件に合っているか
- 価格が有効範囲内か
- 市場が取引可能な状態か(取引時間・シンボル状態)
このように、OrderCheckは「注文を出す前の安全装置」として機能します。
特に自動売買(EA)では、注文失敗はそのまま損失機会やロジック崩壊につながるため、事前検証は実務上ほぼ必須です。
1.2 OrderSendとの違い
初心者が最も混乱しやすいポイントが、OrderCheckとOrderSendの違いです。
| 関数 | 役割 |
|---|---|
| OrderCheck | 注文が通るかどうかを事前に確認 |
| OrderSend | 実際に注文を送信する |
重要なのは、OrderCheckは「確認のみ」であり、注文は一切実行されない点です。
典型的な正しい流れは以下です。
MqlTradeRequest request;
MqlTradeCheckResult check;
MqlTradeResult result;
// requestを設定(省略)
if(OrderCheck(request, check))
{
if(check.retcode == TRADE_RETCODE_DONE)
{
OrderSend(request, result);
}
}
このように、
- OrderCheckで問題なし → OrderSend実行
- OrderCheckでNG → 修正またはスキップ
という分岐を行います。
1.3 なぜOrderCheckが重要か(実務視点)
OrderCheckを使う最大の理由は、無駄なOrderSend失敗を防ぐことです。
OrderSendが失敗するケースには以下があります。
- 証拠金不足(NOT_ENOUGH_MONEY)
- ロット不正(INVALID_VOLUME)
- ストップレベル違反
- スプレッド急拡大による価格不正
これらをOrderSendで直接受けると、
- ログが汚れる
- エラー原因の特定が難しい
- EAの挙動が不安定になる
という問題が発生します。
一方でOrderCheckを使えば、
- 事前にエラーコード(retcode)を取得できる
- ロジック内で安全に分岐できる
- VPS運用でも安定する
というメリットがあります。
特に以下のようなケースでは重要度が高くなります。
- 可変ロット(リスク管理連動)
- 複数通貨ペア運用
- 高頻度トレード
- VPS常時稼働
つまずきポイント・注意点
初心者がよくハマるポイントは以下です。
- OrderCheckを使わずに直接OrderSendしてしまう
- OrderCheckがtrueなら成功だと誤解する
- → 実際は
retcodeを確認する必要あり
- → 実際は
- request構造体の初期化不足
- 未設定項目があると正しくチェックされない
- OrderCheck成功=約定保証と勘違い
- 市場は変動するため、実行時に失敗することもある
特に重要なのは、
OrderCheckは「その瞬間の条件での検証」であり、将来の約定を保証するものではない
という点です。
2. OrderCheckの基本構文と使い方
2.1 基本構文
OrderCheckは、注文情報(MqlTradeRequest)をもとに、その注文が実行可能かどうかを事前検証する関数です。
基本構文は以下の通りです。
bool OrderCheck(
const MqlTradeRequest& request, // 注文内容
MqlTradeCheckResult& result // チェック結果
);
request:注文内容(何を・いくら・どこで)result:チェック結果(通るか・エラー理由など)
戻り値(bool)は「関数が正常に実行されたか」を示すだけであり、
注文が通るかどうかは result.retcode を見る必要があります。
2.2 MqlTradeRequestの構造
MqlTradeRequestは、注文内容を定義する構造体です。
最低限必要な項目は以下です。
MqlTradeRequest request;
ZeroMemory(request); // 初期化(重要)
request.action = TRADE_ACTION_DEAL; // 成行注文
request.symbol = _Symbol; // 通貨ペア
request.volume = 0.1; // ロット
request.type = ORDER_TYPE_BUY; // Buy or Sell
request.price = SymbolInfoDouble(_Symbol, SYMBOL_ASK); // 価格
request.deviation= 10; // 許容スリッページ
補足(初心者向け):
volume:ロット(取引量)price:現在価格(BUYならASK、SELLならBID)deviation:価格ズレ許容幅(ポイント)
2.3 MqlTradeCheckResultの読み方
OrderCheckの結果は、MqlTradeCheckResultに格納されます。
特に重要なのは以下の項目です。
MqlTradeCheckResult check;
Print("retcode=", check.retcode);
Print("margin=", check.margin);
Print("comment=", check.comment);
重要フィールド
retcode- 最重要(成功・失敗の判定)
- 例:
TRADE_RETCODE_DONE(成功)
margin- 必要証拠金
comment- エラー内容(文字列)
実際の使用手順(最短理解)
MqlTradeRequest request;
MqlTradeCheckResult check;
ZeroMemory(request);
// 必須項目設定
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = 0.1;
request.type = ORDER_TYPE_BUY;
request.price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
// チェック実行
if(OrderCheck(request, check))
{
if(check.retcode == TRADE_RETCODE_DONE)
{
Print("注文可能");
}
else
{
Print("エラー: ", check.comment);
}
}
つまずきポイント・注意点
以下は実務でも頻出のミスです。
① ZeroMemoryを忘れる
ZeroMemory(request);
→ 未初期化の値が残り、意図しないエラーになる
② retcodeを見ていない
if(OrderCheck(...)) // ←これだけでは不十分
→ 必ず check.retcode を確認する
③ priceの設定ミス
- BUYなのにBIDを使う
- SELLなのにASKを使う
→ INVALID_PRICEの原因
④ volumeがブローカー条件違反
- 最小ロット未満
- ステップ違反
→ INVALID_VOLUMEの原因
⑤ 必須項目不足
- action未設定
- symbol未設定
→ チェック自体が正しく機能しない
特に重要なのは、
OrderCheckは「正しいrequest構造体」を前提に動く
という点です。
構造体の不備はそのままエラー原因になります。
3. OrderCheckの実践コード例(そのまま使えるテンプレ)
3.1 成行注文(Buy)のチェック例
ここでは、最も基本となる「成行Buy注文」のOrderCheckテンプレを示します。
そのままEAに組み込める構成です。
MqlTradeRequest request;
MqlTradeCheckResult check;
// 初期化(必須)
ZeroMemory(request);
// パラメータ設定
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = 0.1;
request.type = ORDER_TYPE_BUY;
request.price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
request.deviation= 10;
// OrderCheck実行
if(OrderCheck(request, check))
{
if(check.retcode == TRADE_RETCODE_DONE)
{
Print("OrderCheck OK:注文可能");
}
else
{
Print("OrderCheck NG:", check.retcode, " / ", check.comment);
}
}
else
{
Print("OrderCheck関数の実行自体が失敗");
}
3.2 OrderSendと組み合わせた実用パターン
実務では、OrderCheck単体ではなく、OrderSendとセットで使うのが基本です。
MqlTradeRequest request;
MqlTradeCheckResult check;
MqlTradeResult result;
ZeroMemory(request);
// 注文設定
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = 0.1;
request.type = ORDER_TYPE_BUY;
request.price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
request.deviation = 10;
// 事前チェック
if(OrderCheck(request, check))
{
if(check.retcode == TRADE_RETCODE_DONE)
{
// 注文実行
if(OrderSend(request, result))
{
Print("注文成功:", result.order);
}
else
{
Print("OrderSend失敗:", result.retcode);
}
}
else
{
Print("OrderCheckエラー:", check.comment);
}
}
この構造により、
- 無駄なOrderSendを回避
- ログの明確化
- EAの安定性向上
が実現できます。
3.3 関数化して再利用する(実務向け)
EA開発では、毎回同じコードを書くのは非効率です。
そのため、OrderCheckを関数化するのが推奨です。
bool CanTrade(double volume)
{
MqlTradeRequest request;
MqlTradeCheckResult check;
ZeroMemory(request);
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = volume;
request.type = ORDER_TYPE_BUY;
request.price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
if(!OrderCheck(request, check))
return false;
if(check.retcode != TRADE_RETCODE_DONE)
{
Print("取引不可:", check.comment);
return false;
}
return true;
}
使用例:
if(CanTrade(0.1))
{
// OrderSendへ
}
つまずきポイント・注意点
① OrderCheck OKでもOrderSend失敗する
- 原因:価格変動、スリッページ、タイミング
- 対策:
- deviationを適切に設定
- リトライ処理を実装
② volume固定で破綻する
- 証拠金不足で突然NGになる
- 対策:
- 残高ベースでロット計算
- OrderCheckでmargin確認
③ エラーログを出していない
Print("NG"); // ←ダメ
Print("NG:", check.retcode, check.comment); // ←必須
④ 通貨ペアごとの条件差を無視
- 最小ロット
- ステップ
- ストップレベル
→ シンボルごとにSymbolInfoで取得すべき
⑤ VPS運用でログを見ない
- エラー放置でロジック崩壊
重要な実務ポイント:
OrderCheckは「通るかどうか」だけでなく、「なぜ通らないか」をログ化するために使う
4. よくあるエラーと原因分析
4.1 NOT_ENOUGH_MONEY(証拠金不足)
OrderCheckで頻出なのが、必要証拠金が足りないため注文できないケースです。
ロットが大きすぎる、口座残高が少ない、あるいは銘柄ごとの証拠金条件が厳しい場合に発生します。
確認の基本は以下です。
MqlTradeRequest request;
MqlTradeCheckResult check;
ZeroMemory(request);
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = 1.0;
request.type = ORDER_TYPE_BUY;
request.price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
if(OrderCheck(request, check))
{
Print("retcode=", check.retcode);
Print("margin=", check.margin);
Print("comment=", check.comment);
}
見るべき点は次の通りです。
check.margin:その注文に必要な証拠金check.comment:失敗理由の補足- ロットが大きすぎないか
対処の基本は、ロットを下げることです。
固定ロットEAでは発生しやすいため、残高連動やリスク率連動のロット計算と組み合わせると安定します。
4.2 INVALID_VOLUME(ロットサイズ不正)
INVALID_VOLUMEは、指定したロットがブローカーの取引条件に合っていない場合に発生します。
典型例は以下です。
- 最小ロット未満
- 最大ロット超過
- ロット刻み(step)違反
- 小数桁が不正
たとえば、最小0.01、刻み0.01の銘柄に対して0.015を指定すると失敗する可能性があります。
事前確認の例です。
double min_lot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double max_lot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double lot_step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
Print("min=", min_lot, " max=", max_lot, " step=", lot_step);
対策は、ロットを取引条件に合わせて丸めることです。
double NormalizeVolume(double volume)
{
double min_lot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double max_lot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double lot_step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
if(volume < min_lot) volume = min_lot;
if(volume > max_lot) volume = max_lot;
volume = MathFloor(volume / lot_step) * lot_step;
return volume;
}
初心者が最もハマりやすいのは、見た目では正しいロットでも、step条件に合っていないケースです。
4.3 INVALID_PRICE(価格不正)
INVALID_PRICEは、注文価格の指定が不適切な場合に発生します。
成行注文で多い原因は、BuyなのにBID、SellなのにASKを入れてしまうことです。
正しい基本は以下です。
- Buy:
SYMBOL_ASK - Sell:
SYMBOL_BID
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
request.type = ORDER_TYPE_BUY;
request.price = ask;
また、以下でも発生しやすくなります。
- 価格取得後に相場が急変した
- 指値・逆指値の価格位置が不適切
- 桁数を考慮していない
価格指定では、必要に応じて_Digitsに合わせた正規化も重要です。
request.price = NormalizeDouble(ask, _Digits);
ただし、価格正規化だけで全て解決するわけではなく、そもそもの注文種別と価格の関係が正しいかを先に確認すべきです。
4.4 MARKET_CLOSED(市場クローズ)
MARKET_CLOSEDは、取引時間外で注文できない状態です。
土日、祝日、銘柄ごとのセッション外、あるいはブローカー側の一時停止で発生します。
このエラーはコードミスというより、環境条件による失敗です。
そのため、無理に再送するのではなく、スキップ処理にするのが基本です。
if(check.retcode == TRADE_RETCODE_MARKET_CLOSED)
{
Print("市場クローズのため注文見送り");
return;
}
注意点として、FX、CFD、先物、株価指数などは、銘柄ごとに取引時間が異なる場合があります。
複数銘柄EAでは特に注意が必要です。
つまずきポイント・注意点
① OrderCheckの戻り値だけ見ている
if(OrderCheck(request, check))
{
// これだけで成功判定しない
}
trueは「関数呼び出しが成立した」だけであり、注文可否はcheck.retcodeで判定します。
② エラー名だけで決め打ちしている
同じINVALID_VOLUMEでも、原因は
- 最小ロット未満
- lot_step違反
- 最大ロット超過
のどれかです。
必ずシンボル条件と入力値を照合してください。
③ commentを記録していない
ログにretcodeだけ残しても、後から解析しにくくなります。
Print("retcode=", check.retcode, " comment=", check.comment);
④ ブローカー差を無視している
同じコードでも、口座種別やブローカー設定により結果は変わります。
特にロット条件、証拠金条件、取引時間は環境差が出やすいです。
5. 実務での使い方(EA設計視点)
5.1 安全な注文フローに組み込む
実務でOrderCheckを使う目的は、単に「関数を呼ぶこと」ではありません。
EAの注文フロー全体を安定化することが本質です。
基本フローは以下です。
- エントリー条件を判定する
- 注文数量・価格・SL/TPを計算する
OrderCheckで事前検証する- 問題なければ
OrderSendする - 失敗時はログを残して次の処理へ進む
コードの骨格は次のようになります。
bool TryBuy(double volume)
{
MqlTradeRequest request;
MqlTradeCheckResult check;
MqlTradeResult result;
ZeroMemory(request);
ZeroMemory(result);
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = volume;
request.type = ORDER_TYPE_BUY;
request.price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
request.deviation = 10;
if(!OrderCheck(request, check))
{
Print("OrderCheck実行失敗");
return false;
}
if(check.retcode != TRADE_RETCODE_DONE)
{
Print("注文見送り retcode=", check.retcode, " comment=", check.comment);
return false;
}
if(!OrderSend(request, result))
{
Print("OrderSend失敗 retcode=", result.retcode);
return false;
}
Print("注文成功 order=", result.order);
return true;
}
この形にしておくと、
「条件判定」 と 「注文実行」 を分離でき、保守しやすくなります。
5.2 ロット計算と連携する
OrderCheckは、固定ロットよりも可変ロット運用で価値が高くなります。
理由は、ロットを計算しても、その値が実際に通るとは限らないためです。
たとえば、
- 残高の2%リスクで計算した
- 証拠金上は不足していた
- 銘柄の最小ロット刻みに合っていなかった
というケースは普通に起こります。
そのため、実務では以下の順序が安全です。
- まず理論上のロットを計算する
- 最小ロット・最大ロット・ステップで補正する
OrderCheckで最終確認する
例:
double CalcVolume()
{
double raw_volume = 0.13; // 例
double min_lot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double max_lot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double lot_step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
if(raw_volume < min_lot) raw_volume = min_lot;
if(raw_volume > max_lot) raw_volume = max_lot;
raw_volume = MathFloor(raw_volume / lot_step) * lot_step;
return raw_volume;
}
このあとにOrderCheckを入れることで、
ロット計算ロジックの最終防波堤として機能します。
5.3 エラー時は「再送」ではなく「分岐」で考える
初心者がやりがちな失敗のひとつが、OrderCheckでNGだったらすぐ再試行する設計です。
しかし実務では、原因別に分岐する方が合理的です。
たとえば、
NOT_ENOUGH_MONEY- ロットを下げるか、そのシグナルを見送る
INVALID_VOLUME- ロット補正ロジックを見直す
MARKET_CLOSED- 次回の取引時間まで待つ
INVALID_PRICE- 最新価格を再取得して再構築する
つまり、OrderCheckは単なる判定関数ではなく、
EAの異常系制御の入口として使うべきです。
if(check.retcode == TRADE_RETCODE_NO_MONEY)
{
Print("証拠金不足のため見送り");
}
else if(check.retcode == TRADE_RETCODE_INVALID_VOLUME)
{
Print("ロット不正のため見直し");
}
else if(check.retcode == TRADE_RETCODE_MARKET_CLOSED)
{
Print("市場クローズ中のため待機");
}
このように分けると、後でログ解析するときも原因を追いやすくなります。
5.4 毎Tickで無駄に呼ばない
OrderCheckは便利ですが、無制限に呼んでよい関数ではありません。
特にOnTick()内で、エントリー条件も満たしていないのに毎回実行すると、無駄な処理が増えます。
悪い例:
void OnTick()
{
// 条件に関係なく毎回OrderCheck
}
良い例:
- まずエントリー条件を満たしたか判定
- ポジション未保有か確認
- その時だけ
OrderCheck
void OnTick()
{
if(!EntrySignal())
return;
if(PositionSelect(_Symbol))
return;
double volume = CalcVolume();
TryBuy(volume);
}
これにより、
- 不要な処理を減らせる
- ログ汚染を防げる
- EAの動作が読みやすくなる
という利点があります。
つまずきポイント・注意点
① OrderCheckを注文ロジックの外に置いている
事前検証は、実際に使うrequest内容そのものに対して行う必要があります。
古い価格や未補正ロットでチェックしても意味がありません。
② NG時の処理が雑
Print("NG");
だけでは実務では足りません。
最低でもretcodeとcommentを残すべきです。
③ 可変ロットなのに最終確認していない
理論ロットと実行可能ロットは一致しないことがあります。
可変ロットEAほどOrderCheckの重要度は上がります。
④ 失敗を異常停止扱いしてしまう
MARKET_CLOSEDや証拠金不足は、環境次第では普通に起こる事象です。
止めるより、見送る設計の方が再現性は高くなります。
6. OrderCheckを使うべきケース / 不要なケース
6.1 OrderCheckを使うべきケース
OrderCheckは便利ですが、全ての場面で機械的に入れればよいわけではありません。
ただし、注文条件が動的に変わるEAでは、優先度が高い関数です。
特に使うべきケースは以下です。
- 可変ロットを使う
- 銘柄ごとに条件が異なる
- 複数通貨ペアを同時運用する
- 証拠金余力が変動しやすい
- SL/TPや注文価格を都度計算する
- VPSで長時間自動運用する
これらのケースでは、理論上は正しい注文でも、実際の取引条件に通らないことが普通にあります。
そのため、OrderCheckを入れておくことで、注文失敗を事前に除外しやすくなります。
たとえば、リスク率からロットを毎回計算するEAでは、残高変動や含み損の影響で必要証拠金が変わります。
このとき、ロット計算だけで完結させると、OrderSend時点で失敗しやすくなります。
double volume = CalcRiskBasedVolume();
if(!CanTrade(volume))
{
Print("今回の条件では注文見送り");
return;
}
このように、注文前の最終判定層として使うのが実務的です。
6.2 毎回は不要なケース
一方で、OrderCheckを毎回必須と考えるのも過剰です。
次のようなケースでは、重要度は相対的に下がります。
- 手動トレード補助の簡易スクリプト
- 固定ロット・固定条件のみの単純EA
- 検証用の最小コード
- 注文失敗時の影響が小さい試験運用
たとえば、0.01ロット固定、1通貨ペア固定、単純な成行注文だけを行うEAでは、
事前条件がほぼ一定のため、OrderSend失敗時の処理だけでも運用できる場合があります。
if(!OrderSend(request, result))
{
Print("注文失敗 retcode=", result.retcode);
}
ただし、この設計はコード量は少ないが、異常原因の事前把握は弱いです。
そのため、簡易EAや学習用コードなら許容されても、実運用では徐々に限界が出やすくなります。
6.3 使うかどうかの判断基準
判断基準はシンプルです。
注文条件が固定か、動的かで考えると整理しやすくなります。
OrderCheckを入れるべき寄り
- 注文数量が毎回変わる
- 証拠金余力に依存する
- ブローカー条件差の影響を受けやすい
- 複数銘柄や複数時間足で使う
省略しても回る可能性がある寄り
- 固定ロット
- 単一銘柄
- 成行のみ
- 学習用・検証用
つまり、再現性と安定性を重視するなら入れる、最小構成を重視するなら省略余地がある、という整理です。
6.4 パフォーマンスとのトレードオフ
OrderCheckを入れると安全性は上がりますが、処理は増えます。
このため、設計では安全性と軽さのバランスを考える必要があります。
ただし、ここで誤解しやすいのは、
「重くなるから使わない方がよい」と短絡しやすい点です。
実際には、問題はOrderCheckそのものではなく、呼ぶタイミングです。
悪い例:
- 毎Tick呼ぶ
- シグナル未成立でも呼ぶ
- 同じ条件で何度も呼ぶ
良い例:
- エントリー条件成立時のみ呼ぶ
- 注文直前に1回だけ呼ぶ
- request内容が変わるときだけ呼ぶ
if(EntrySignal() && !PositionSelect(_Symbol))
{
double volume = CalcVolume();
if(CanTrade(volume))
{
TryBuy(volume);
}
}
このようにすれば、処理増加は限定的です。
実務では、少し軽いが壊れやすいEAより、少し重くても失敗を制御できるEAの方が期待値は高くなりやすいです。
つまずきポイント・注意点
① 「全部に入れるべき」と思い込む
単純スクリプトや学習用コードでは、必ずしも必須ではありません。
設計目的に応じて使い分ける方が合理的です。
② 「不要」と決めつける
固定ロットEAでも、環境差や市場条件で失敗は起こります。
本番運用では、省略コストより障害コストの方が大きくなりやすいです。
③ 軽さだけで判断する
EAの処理時間だけでなく、
- 注文失敗率
- ログ解析性
- 障害時の再現性
まで含めて判断すべきです。
④ 学習用コードを本番にそのまま持ち込む
学習段階では省略しても、本番化する時点でOrderCheckを含む安全設計へ寄せるのが基本です。
7. OrderCheckと関連関数の関係
7.1 OrderCheckとOrderSendの関係
OrderCheckを正しく理解するには、まずOrderSendとの役割分担を整理する必要があります。
両者は似た場面で使われますが、役割は明確に異なります。
OrderCheck:注文前の検証OrderSend:実際の注文送信
この2つは、代替関係ではなく連携関係です。
つまり、どちらか一方で完結するより、Check → Sendの順で使う方が実務では安定します。
典型的な流れは以下です。
MqlTradeRequest request;
MqlTradeCheckResult check;
MqlTradeResult result;
ZeroMemory(request);
ZeroMemory(result);
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = 0.1;
request.type = ORDER_TYPE_BUY;
request.price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
request.deviation = 10;
if(OrderCheck(request, check))
{
if(check.retcode == TRADE_RETCODE_DONE)
{
if(OrderSend(request, result))
{
Print("注文送信成功");
}
else
{
Print("OrderSend失敗 retcode=", result.retcode);
}
}
else
{
Print("OrderCheck NG retcode=", check.retcode, " comment=", check.comment);
}
}
ここで重要なのは、OrderCheckで通ってもOrderSendの成功は保証されないことです。
理由は、価格や市場状態が一瞬で変わるためです。特に成行注文では、この差を常に意識する必要があります。
したがって実務では、以下のように考えるのが正確です。
OrderCheckは「その時点での送信可能性の確認」OrderSendは「実際の市場条件での注文実行」
7.2 MqlTradeRequestとの関係
OrderCheckは単独では何も判断できません。
判断対象になるのは、MqlTradeRequestに入れた注文内容そのものです。
つまり、OrderCheckの精度は、ほぼそのままMqlTradeRequestの作り方に依存します。
よく使う項目は以下です。
action:どの種別の取引かsymbol:対象銘柄volume:ロットtype:Buy / Sell / 指値 / 逆指値price:注文価格sl/tp:損切り / 利確deviation:許容スリッページ
このため、OrderCheckの記事であっても、実質的にはMqlTradeRequestの理解が前提になります。
たとえば、request.typeだけ正しくても、request.priceがBuyに対してBIDなら、当然OrderCheckは失敗しやすくなります。
逆に言えば、OrderCheckで問題が出たときは、まずrequestの中身を疑うべきです。
ログ出力で確認する例です。
Print("symbol=", request.symbol);
Print("volume=", request.volume);
Print("type=", request.type);
Print("price=", request.price);
初心者がハマりやすいのは、OrderCheckの失敗を関数の問題だと思い込み、実際はrequestの設定ミスだったというケースです。
7.3 MqlTradeCheckResultとMqlTradeResultの違い
名前が似ているため混同しやすいですが、MqlTradeCheckResultとMqlTradeResultは別物です。
MqlTradeCheckResultOrderCheckの結果を受ける- 事前検証の情報
MqlTradeResultOrderSendの結果を受ける- 実際の注文送信結果
実務での違いは次の通りです。
MqlTradeCheckResult check;
MqlTradeResult result;
check.retcode- この注文内容が通りそうか
result.retcode- 実際の送信がどうなったか
この2つを分けて記録すると、障害解析がかなりしやすくなります。
Print("check.retcode=", check.retcode, " comment=", check.comment);
Print("result.retcode=", result.retcode, " order=", result.order);
特に本番EAでは、Check段階で落ちたのか、Send段階で落ちたのかを区別できることに意味があります。
7.4 Position系関数との関係
OrderCheckは「これから注文できるか」を見る関数ですが、
実際のEA運用では、すでにポジションを持っているかもセットで確認する必要があります。
ここで関係するのがPositionSelectなどのPosition系関数です。
典型パターンは以下です。
- まず現在ポジションの有無を確認する
- 未保有なら新規注文の可否を
OrderCheckで判定する - 問題なければ
OrderSend
if(PositionSelect(_Symbol))
{
Print("すでにポジション保有中のため新規注文しない");
return;
}
if(CanTrade(0.1))
{
TryBuy(0.1);
}
この流れにしないと、OrderCheck自体は通っても、EA全体としては
多重エントリーや想定外のナンピンを引き起こすことがあります。
つまり、OrderCheckは注文可否の一部であり、EA全体では
- ポジション状態
- 注文条件
- 市場状態
- リスク条件
の一要素として使うのが正しい位置づけです。
つまずきポイント・注意点
① OrderCheckを単独で理解しようとする
OrderCheckは便利ですが、単体で完結する関数ではありません。MqlTradeRequest、OrderSend、PositionSelectとセットで理解する必要があります。
② checkとresultを混同する
check.retcodeとresult.retcodeは別物です。
ここを混ぜると、何段階で失敗したのか分からなくなります。
③ Position確認を省略する
新規注文可能でも、EA設計上は発注すべきでないケースがあります。
ポジション保有確認は別途必要です。
④ requestの再利用で古い値が残る
前回のrequestを流用すると、古いslやtpが残ることがあります。
毎回ZeroMemory(request);で初期化するのが基本です。
8. まとめ(初心者向け最短理解)
8.1 OrderCheckの本質
OrderCheckは、注文前に「その注文が通るか」を確認する関数です。
ただし重要なのは、「関数が成功したか」ではなく、注文が成立可能かどうか(retcode)を見ることです。
最も重要なポイントは次の1行です。
check.retcode == TRADE_RETCODE_DONEなら注文可能
これだけ理解していれば、最低限は運用できます。
8.2 最短の実装フロー
初心者は、まず以下の流れだけ覚えれば十分です。
if(OrderCheck(request, check))
{
if(check.retcode == TRADE_RETCODE_DONE)
{
OrderSend(request, result);
}
}
この構造にすることで、
- 無駄な注文失敗を防げる
- エラー原因を事前に把握できる
- EAの安定性が上がる
という効果が得られます。
8.3 実務での重要ポイント(要点整理)
実務レベルで重要なのは以下の4点です。
① OrderCheckは「事前検証」であり保証ではない
- 通ってもOrderSendで失敗することはある
- 理由:価格変動、スリッページ、タイミング
② requestの正しさがすべて
- volume(ロット)
- price(価格)
- type(Buy/Sell)
→ これがズレていると必ず失敗する
③ retcodeを必ず確認する
if(check.retcode != TRADE_RETCODE_DONE)
→ これを見ないと意味がない
④ エラーは「分岐」で処理する
- 証拠金不足 → ロット調整 or 見送り
- ロット不正 → 計算ロジック修正
- 市場クローズ → 待機
→ 再送ではなく、原因別対応
8.4 よくある失敗(再確認)
初心者が特にやりがちなミスをまとめます。
- OrderCheckを使わずに直接OrderSendする
OrderCheck == trueで成功と判断する- volumeの刻み違反(lot_step無視)
- BUYでBID、SELLでASKを使う
- requestを初期化していない
これらはすべて、実務で頻発する典型的なバグです。
8.5 どこまでやれば十分か
最低限の実装レベルは以下です。
- OrderCheckを入れる
- retcodeを確認する
- NG時にログを出す
ここまでできれば、初心者レベルは脱却です。
その先は、
- 可変ロットとの連携
- エラー別分岐
- ログ解析設計
へ進むことで、実務レベルのEA設計に到達します。
FAQ(よくある質問)
Q1. OrderCheckがtrueなら注文は成功しますか?
A. いいえ。成功しません。OrderCheckの戻り値trueは「関数が正常実行された」という意味であり、注文可否はcheck.retcodeで判断します。さらに、OrderSend時に市場状況が変われば失敗する可能性もあります。
Q2. OrderCheckは必ず使うべきですか?
A. 実運用EAでは強く推奨されます。
固定ロット・単純構造の検証用コードでは省略可能ですが、可変ロットや複数銘柄運用では、注文失敗を減らすためにほぼ必須です。
Q3. OrderCheckとOrderSendの違いは何ですか?
A. 役割が完全に異なります。
- OrderCheck:注文前の検証
- OrderSend:実際の注文実行
Checkは「事前確認」、Sendは「本番実行」と理解してください。
Q4. retcodeはどれを見ればいいですか?
A. 基本は TRADE_RETCODE_DONE です。
これが返っていれば、その時点では注文可能です。それ以外は何らかの問題があるため、check.commentと合わせて原因を確認します。
Q5. OrderCheckが通ったのにOrderSendで失敗するのはなぜですか?
A. 市場はリアルタイムで変動するためです。
価格変動、スリッページ、約定条件の変化により、Check時とSend時で状況が変わることがあります。これは正常な挙動です。
Q6. INVALID_VOLUMEエラーはどう対処すればいいですか?
A. ロットをブローカー条件に合わせて調整します。
- 最小ロット(SYMBOL_VOLUME_MIN)
- 最大ロット(SYMBOL_VOLUME_MAX)
- ロット刻み(SYMBOL_VOLUME_STEP)
これらを取得し、丸め処理を行う必要があります。
Q7. OrderCheckは毎Tick呼んでも問題ありませんか?
A. 推奨されません。
エントリー条件成立時のみ呼ぶのが基本です。毎Tick呼ぶと無駄な処理とログ増加につながります。
Q8. エラー時は再送すべきですか?
A. 原則は再送ではなく分岐処理です。
エラー原因ごとに対応を変えるべきです。
例:
- 証拠金不足 → ロット調整
- 市場クローズ → 待機
- 価格不正 → 再取得
単純な再送は、同じエラーを繰り返す可能性があります。