- 1 1. MQL5のtrade request structureとは何か
- 2 2. MqlTradeRequestの全体構造と必須フィールド
- 3 3. action・type・type_fillingの違いと正しい使い分け
- 4 4. OrderSendを使った実践コード(成行注文・指値注文)
- 5 5. 注文が通らない原因とデバッグ方法(実務対応)
- 6 6. OrderCheckを使った事前検証(注文前チェック)
- 7 7. 再利用しやすく壊れにくい注文処理の設計パターン
- 8 8. ブローカー環境や実口座で失敗しやすい注意点
- 9 9. FAQ(よくある質問と回答方針)
1. MQL5のtrade request structureとは何か
このセクションでは、MQL5における注文処理の中核である「trade request structure(MqlTradeRequest)」の役割と位置づけを理解します。初心者が最初につまずく「なぜ構造体が必要なのか」も含めて整理します。
1.1 trade request structureの基本概念
MQL5では、注文(エントリー・決済など)を実行する際に
MqlTradeRequest(エムキューエルトレードリクエスト)という構造体を使用します。
構造体とは、
→「複数のデータをまとめて扱うための箱」のことです。
つまり、MqlTradeRequestは
👉「注文に必要な情報をすべてまとめて渡すためのデータセット」です。
例えば、注文には以下のような情報が必要です:
- 通貨ペア(例:EURUSD)
- ロット数(取引量)
- 注文タイプ(Buy / Sell)
- 価格
- ストップロス(損切り)
- テイクプロフィット(利確)
MQL5ではこれらをバラバラに渡すのではなく、
1つの構造体にまとめて OrderSend() に渡す設計になっています。
MqlTradeRequest request;
MqlTradeResult result;
OrderSend(request, result);
この設計により、
- 注文情報の整合性が保たれる
- エラー検出がしやすい
- 拡張性が高い(新しい項目が追加可能)
というメリットがあります。
1.2 なぜ構造体で管理されるのか(設計思想)
MQL4では、OrderSend関数に複数の引数を直接渡していました。
しかしMQL5では設計が大きく変わり、
構造体ベースの注文管理に移行しています。
この理由は主に以下です:
■ 理由1:拡張性(将来対応)
金融システムでは、注文条件が増えることが多いです。
構造体であれば項目追加が容易です。
■ 理由2:可読性(コードの明確化)
request.volume = 0.1;
request.price = Ask;
request.type = ORDER_TYPE_BUY;
このように書くことで、
👉「何を設定しているか」が一目で分かります。
■ 理由3:安全性(バグ防止)
引数が多い関数では、順番ミスが起きやすいです。
構造体なら「名前付き」で管理されるため、誤設定が減ります。
1.3 MQL4との違い(重要)
初心者〜中級者が最も混乱するポイントです。
■ MQL4(旧)
OrderSend(Symbol(), OP_BUY, 0.1, Ask, 3, 0, 0);
■ MQL5(新)
MqlTradeRequest request;
request.action = TRADE_ACTION_DEAL;
request.type = ORDER_TYPE_BUY;
request.volume = 0.1;
request.price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
👉 大きな違いは「構造体を使うかどうか」です。
よくあるつまずき・注意点
■ ① とりあえずOrderSendだけ使おうとする
→ MQL5では通用しない
→ 必ずMqlTradeRequestの理解が必要
■ ② コピペで動かそうとする
→ ブローカー環境により失敗する
→ 特に以下に注意:
- type_filling(約定方式)
- 最小ロット
- ストップ距離
■ ③ 変数未初期化
MqlTradeRequest request; // ←初期化不足
👉 推奨
MqlTradeRequest request = {};
このセクションのまとめ(重要ポイント)
- MqlTradeRequestは「注文情報の箱」
- OrderSendは「その箱を渡す関数」
- MQL5は構造体ベースの設計に変わっている
- 初心者はここで必ずつまずくため、理解が最優先
2. MqlTradeRequestの全体構造と必須フィールド
このセクションでは、MqlTradeRequestに含まれる全フィールドと、その中でも「必須で設定すべき項目」を整理します。初心者が迷いやすいポイントを、実務で使う優先度ベースで解説します。
2.1 MqlTradeRequestの構造体一覧
MqlTradeRequestは以下のようなフィールド(メンバ変数)で構成されています。
struct MqlTradeRequest
{
ENUM_TRADE_REQUEST_ACTIONS action;
ulong magic;
ulong order;
string symbol;
double volume;
double price;
double stoplimit;
double sl;
double tp;
ulong deviation;
ENUM_ORDER_TYPE type;
ENUM_ORDER_TYPE_FILLING type_filling;
ENUM_ORDER_TYPE_TIME type_time;
datetime expiration;
string comment;
ulong position;
ulong position_by;
};
■ 主に使うフィールド(実務優先)
| フィールド | 意味 |
|---|---|
| action | 実行する操作(新規注文など) |
| symbol | 通貨ペア |
| volume | ロット数 |
| price | 注文価格 |
| type | 注文タイプ(Buy/Sell) |
| sl | ストップロス |
| tp | テイクプロフィット |
| deviation | 許容スリッページ |
| type_filling | 約定方式 |
👉 実務では「すべて覚える必要はない」
👉 まずは上記9項目を優先して理解
■ あまり使わない(状況依存)
| フィールド | 用途 |
|---|---|
| magic | EA識別番号 |
| order | 既存注文の操作 |
| stoplimit | 指値+逆指値 |
| type_time | 有効期限設定 |
| expiration | 注文期限 |
| position | ポジション指定 |
| position_by | 両建て決済 |
👉 初心者は後回しでOK
2.2 必須項目と任意項目の違い
MQL5では「全部の項目を設定する必要はありません」。
しかし、最低限設定しないと注文が通らない項目があります。
■ 最低限必要な項目(成行注文)
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;
request.type_filling = ORDER_FILLING_FOK;
■ 必須項目まとめ
- action(何をするか)
- symbol(銘柄)
- volume(ロット)
- type(注文種別)
- price(価格)
- type_filling(約定方法)※環境依存
- deviation(スリッページ許容)
■ 任意項目(推奨)
- sl(損切り)
- tp(利確)
- magic(EA識別)
👉 任意だが「実務ではほぼ必須」
2.3 最低限の注文に必要な構成
ここでは、最小限で「確実に通る構成」を示します。
■ 最小構成コード(Buy注文)
MqlTradeRequest request = {};
MqlTradeResult 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;
request.type_filling = ORDER_FILLING_FOK;
OrderSend(request, result);
■ 手順(初心者向け)
- requestを初期化する
- 必須項目を設定する
- OrderSendに渡す
よくあるつまずき・注意点
■ ① type_filling未設定
→ 注文拒否(特に海外ブローカー)
対策:
request.type_filling = ORDER_FILLING_IOC; // 環境により変更
■ ② priceの取得ミス
→ BuyなのにBidを使うなど
対策:
- Buy → Ask
- Sell → Bid
■ ③ volumeエラー
→ 最小ロット未満
対策:
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
■ ④ request未初期化
→ 不定値でエラー
必ず:
MqlTradeRequest request = {};
このセクションのまとめ(重要ポイント)
- MqlTradeRequestは多数のフィールドを持つが、全部は不要
- 必須項目だけでも注文は可能
- type_filling・price・volumeが特に重要
- エラーの多くは「設定不足」か「環境差」
3. action・type・type_fillingの違いと正しい使い分け
このセクションでは、MqlTradeRequestで最も混乱しやすい「action」「type」「type_filling」の違いを整理します。ここを誤ると注文は通らないため、実務上の最重要ポイントです。
3.1 actionとは何か(注文の“目的”を指定)
actionは
👉「このリクエストで何をしたいのか」を定義する項目です。
主に使うのは以下です:
| 定数 | 意味 |
|---|---|
| TRADE_ACTION_DEAL | 成行注文(即時約定) |
| TRADE_ACTION_PENDING | 指値・逆指値注文 |
| TRADE_ACTION_SLTP | SL/TPの変更 |
| TRADE_ACTION_CLOSE_BY | 両建て決済 |
■ 最もよく使う(初心者はこれだけでOK)
request.action = TRADE_ACTION_DEAL;
👉 成行注文(最も基本)
■ 指値・逆指値の場合
request.action = TRADE_ACTION_PENDING;
👉 この場合は type が重要になる
3.2 typeとは何か(注文の“種類”)
typeは
👉「BuyかSellか」「指値か逆指値か」を決める項目です。
■ 成行注文
| 定数 | 意味 |
|---|---|
| ORDER_TYPE_BUY | 成行買い |
| ORDER_TYPE_SELL | 成行売り |
■ 指値注文(価格が有利になったら)
| 定数 | 意味 |
|---|---|
| ORDER_TYPE_BUY_LIMIT | 安くなったら買う |
| ORDER_TYPE_SELL_LIMIT | 高くなったら売る |
■ 逆指値注文(ブレイク狙い)
| 定数 | 意味 |
|---|---|
| ORDER_TYPE_BUY_STOP | 上抜けで買う |
| ORDER_TYPE_SELL_STOP | 下抜けで売る |
■ 例(成行Buy)
request.action = TRADE_ACTION_DEAL;
request.type = ORDER_TYPE_BUY;
👉 actionとtypeはセットで考える
3.3 type_fillingとは何か(約定方式)
ここが最もエラーが出やすいポイントです。
type_fillingは
👉「どのように約定させるか」を指定します。
■ 主な約定方式
| 定数 | 意味 |
|---|---|
| ORDER_FILLING_FOK | 全量約定 or 全キャンセル |
| ORDER_FILLING_IOC | 一部約定OK |
| ORDER_FILLING_RETURN | 未約定分を残す |
■ 実務での使い分け
- 安定重視 → IOC(推奨)
- 厳密な約定 → FOK
- 指値注文 → RETURN(環境依存)
■ 例
request.type_filling = ORDER_FILLING_IOC;
3.4 3つの関係性(重要)
この3つは独立ではなく、組み合わせで意味が決まるのがポイントです。
■ 正しい組み合わせ例(成行Buy)
request.action = TRADE_ACTION_DEAL;
request.type = ORDER_TYPE_BUY;
request.type_filling = ORDER_FILLING_IOC;
■ 指値注文の例
request.action = TRADE_ACTION_PENDING;
request.type = ORDER_TYPE_BUY_LIMIT;
request.type_filling = ORDER_FILLING_RETURN;
👉 この3つが噛み合っていないと
→ 注文拒否(エラー)になります
よくあるつまずき・注意点
■ ① actionとtypeの不一致
例:
action = TRADE_ACTION_DEAL;
type = ORDER_TYPE_BUY_LIMIT;
👉 成行なのに指値 → エラー
■ ② type_fillingがブローカー非対応
→ 「Invalid filling mode」エラー
対策:
int filling = (int)SymbolInfoInteger(_Symbol, SYMBOL_FILLING_MODE);
👉 ブローカー仕様を確認
■ ③ 指値注文なのにprice未設定
→ 注文失敗
■ ④ RETURNを成行で使う
→ 環境によってはエラー
このセクションのまとめ(重要ポイント)
- action=何をするか(目的)
- type=注文の種類
- type_filling=約定方法
- 3つは必ずセットで整合性を取る必要がある
- エラーの大半はこの組み合わせミス
4. OrderSendを使った実践コード(成行注文・指値注文)
このセクションでは、MqlTradeRequestを使って実際に注文を出す「完成形コード」を提示します。成行注文と指値注文の両方を扱い、実務でそのまま使えるレベルまで具体化します。
4.1 成行注文(Buy)の完全コード
まずは最も基本となる「成行Buy」の例です。
MqlTradeRequest request = {};
MqlTradeResult result = {};
// 現在価格取得
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
// リクエスト設定
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = 0.1;
request.type = ORDER_TYPE_BUY;
request.price = ask;
request.sl = ask - 100 * _Point;
request.tp = ask + 100 * _Point;
request.deviation = 10;
request.type_filling = ORDER_FILLING_IOC;
// 注文実行
if(OrderSend(request, result))
{
Print("注文成功: ", result.retcode);
}
else
{
Print("注文失敗: ", result.retcode);
}
■ 手順整理(初心者向け)
- 現在価格を取得(Ask)
- requestに必要項目を設定
- OrderSendを実行
- resultで結果確認
■ 補足(重要)
_Point:最小価格単位(pipsではない点に注意)retcode:注文結果コード(成功・失敗判定)
4.2 成行注文(Sell)の完全コード
Sellの場合は価格がBidになる点に注意します。
MqlTradeRequest request = {};
MqlTradeResult result = {};
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = 0.1;
request.type = ORDER_TYPE_SELL;
request.price = bid;
request.sl = bid + 100 * _Point;
request.tp = bid - 100 * _Point;
request.deviation = 10;
request.type_filling = ORDER_FILLING_IOC;
OrderSend(request, result);
4.3 指値注文(Buy Limit)の完全コード
次に、価格指定で注文する「指値注文」です。
MqlTradeRequest request = {};
MqlTradeResult result = {};
// 指値価格(現在より下)
double price = SymbolInfoDouble(_Symbol, SYMBOL_BID) - 200 * _Point;
request.action = TRADE_ACTION_PENDING;
request.symbol = _Symbol;
request.volume = 0.1;
request.type = ORDER_TYPE_BUY_LIMIT;
request.price = price;
request.sl = price - 100 * _Point;
request.tp = price + 100 * _Point;
request.type_filling = ORDER_FILLING_RETURN;
request.type_time = ORDER_TIME_GTC;
OrderSend(request, result);
■ ポイント
- action = PENDING
- type = LIMIT系
- priceは現在価格より下に設定
4.4 エラーチェックの基本(retcode確認)
OrderSendは「trueでも失敗している場合」があるため、
必ずretcodeを確認します。
if(result.retcode != TRADE_RETCODE_DONE)
{
Print("エラーコード: ", result.retcode);
}
■ よく使うretcode
| コード | 意味 |
|---|---|
| TRADE_RETCODE_DONE | 成功 |
| TRADE_RETCODE_REJECT | 拒否 |
| TRADE_RETCODE_INVALID_VOLUME | ロット不正 |
| TRADE_RETCODE_INVALID_PRICE | 価格不正 |
よくあるつまずき・注意点
■ ① OrderSendがtrueでも失敗している
→ retcode未確認
👉 必ず確認する
■ ② SL/TPが近すぎる
→ ブローカー制限で拒否
対策:
SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
■ ③ 指値価格が不正
→ Buy Limitなのに現在より上など
■ ④ deviationが小さすぎる
→ 約定失敗
■ ⑤ 約定方式ミス
→ IOC/FOKが環境に合っていない
このセクションのまとめ(重要ポイント)
- OrderSendはrequestとresultのセットで使う
- 成行・指値でactionとtypeが変わる
- retcode確認が必須
- エラー原因の多くは「環境依存」
5. 注文が通らない原因とデバッグ方法(実務対応)
このセクションでは、MqlTradeRequestを正しく書いているにも関わらず「注文が通らない」場合の原因を体系的に整理します。実務ではここが最も重要であり、問題の切り分けができるかどうかで開発効率が大きく変わります。
5.1 注文失敗の基本構造(原因の分類)
注文が失敗する原因は大きく3つに分類できます。
■ ① パラメータエラー(設定ミス)
- volume(ロット)
- price(価格)
- SL/TP(距離制限)
■ ② 環境依存エラー(ブローカー仕様)
- type_filling(約定方式)
- 最小ロット
- ストップレベル
■ ③ 状態エラー(タイミング・市場)
- マーケットクローズ
- 価格変動
- 証拠金不足
👉 まずは「どのカテゴリか」を切り分けることが重要
5.2 retcodeを使ったエラー特定
最も重要なのは result.retcode です。
Print("retcode: ", result.retcode);
■ よくあるエラーと対策
| retcode | 原因 | 対策 |
|---|---|---|
| TRADE_RETCODE_INVALID_VOLUME | ロット不正 | 最小ロット確認 |
| TRADE_RETCODE_INVALID_PRICE | 価格不正 | Ask/Bid確認 |
| TRADE_RETCODE_REJECT | 条件不一致 | filling見直し |
| TRADE_RETCODE_NO_MONEY | 証拠金不足 | ロット減らす |
5.3 デバッグの基本手順(実務フロー)
以下の手順で原因を特定します。
■ ステップ1:retcode確認
if(result.retcode != TRADE_RETCODE_DONE)
{
Print("Error: ", result.retcode);
}
■ ステップ2:ログ出力
Print("volume=", request.volume);
Print("price=", request.price);
Print("type=", request.type);
👉 実際に何が送られているか確認
■ ステップ3:環境情報取得
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double stopLevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
■ ステップ4:約定方式の確認
int filling = (int)SymbolInfoInteger(_Symbol, SYMBOL_FILLING_MODE);
Print("filling=", filling);
5.4 典型的なエラーケース
■ ケース1:注文が一切通らない
原因:
- type_filling不一致
対策:
request.type_filling = ORDER_FILLING_IOC;
■ ケース2:SL/TP設定で失敗
原因:
- ストップ距離不足
対策:
int stopLevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
■ ケース3:指値注文が拒否される
原因:
- priceが無効(方向ミス)
例:
- Buy Limitなのに現在価格より上
■ ケース4:ランダムに失敗する
原因:
- 価格変動(スリッページ)
対策:
request.deviation = 20;
5.5 実務での安定化テクニック
■ ① 最小ロットを自動調整
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
request.volume = MathMax(0.1, minLot);
■ ② ストップ距離を考慮
int stopLevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
double sl = request.price - stopLevel * _Point;
■ ③ リトライ処理
for(int i=0; i<3; i++)
{
if(OrderSend(request, result) && result.retcode == TRADE_RETCODE_DONE)
break;
}
よくあるつまずき・注意点
■ ① 「コードは正しいのに動かない」
→ 環境依存(ほぼこれ)
■ ② ストラテジーテスターでは動く
→ 実口座では失敗
理由:
- 約定方式
- スリッページ
- 制限値
■ ③ Printログを見ていない
→ 原因特定できない
■ ④ 一発で成功させようとする
→ 実務ではリトライ前提
このセクションのまとめ(重要ポイント)
- エラーは「設定・環境・市場」の3分類で考える
- retcode確認が最重要
- デバッグはログ+環境取得で行う
- 実運用ではリトライ・調整が必須
6. OrderCheckを使った事前検証(注文前チェック)
このセクションでは、OrderSend() の前に OrderCheck() を使って注文内容を検証する方法を解説します。実務では、いきなり注文送信するより、先にチェックして失敗要因を潰すほうが安定します。特にロット、証拠金、ストップ距離、注文条件の確認に有効です。
6.1 OrderCheckとは何か
OrderCheck() は、
👉 この注文内容で発注可能かを事前に確認する関数です。
OrderSend() は実際に注文を送りますが、OrderCheck() は送信前の審査に近い役割です。
そのため、以下のような問題を先に見つけやすくなります。
- ロット数が不正
- 必要証拠金が足りない
- SL/TPが不正
- 注文価格が条件に合っていない
基本形は以下です。
MqlTradeRequest request = {};
MqlTradeCheckResult check = {};
bool ok = OrderCheck(request, check);
check には、チェック結果のコードや証拠金関連の情報が入ります。
つまり、OrderSend() の結果確認が「事後」なら、OrderCheck() は「事前」です。
6.2 OrderCheckの基本コード
成行Buy前にチェックする例です。
MqlTradeRequest request = {};
MqlTradeCheckResult check = {};
MqlTradeResult result = {};
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = 0.1;
request.type = ORDER_TYPE_BUY;
request.price = ask;
request.sl = ask - 100 * _Point;
request.tp = ask + 100 * _Point;
request.deviation = 10;
request.type_filling = ORDER_FILLING_IOC;
// 事前チェック
if(!OrderCheck(request, check))
{
Print("OrderCheck失敗: ", check.retcode);
return;
}
// チェックOKなら送信
if(OrderSend(request, result))
{
Print("注文送信成功: ", result.retcode);
}
else
{
Print("注文送信失敗: ", result.retcode);
}
■ 実務での基本手順
MqlTradeRequestを組み立てるOrderCheck()で事前確認する- 問題なければ
OrderSend()する - 問題があればログを出して終了する
この流れにしておくと、
「なぜ失敗したのか分からない」状態をかなり減らせます。
6.3 MqlTradeCheckResultで確認できること
MqlTradeCheckResult には、注文可否以外にも実務で役立つ情報が入ります。
主に見る項目は以下です。
| フィールド | 意味 |
|---|---|
| retcode | チェック結果コード |
| balance | 残高 |
| equity | 有効証拠金ベースの資産 |
| margin | 必要証拠金 |
| margin_free | 余剰証拠金 |
| comment | 補足メッセージ |
例えば、必要証拠金を確認したい場合は以下のように出力できます。
Print("retcode=", check.retcode);
Print("margin=", check.margin);
Print("free margin=", check.margin_free);
Print("comment=", check.comment);
これにより、単に「失敗した」ではなく、
「証拠金不足なのか」「条件不正なのか」を分けて判断できます。
6.4 OrderCheckを使うメリット
■ 1. 証拠金不足を先に検出できる
本番口座でありがちな NO_MONEY 系の問題を事前に把握しやすくなります。
■ 2. ロット計算の検証に使える
自動ロット計算をしているEAでは、計算後のロットが本当に通るか確認できます。
■ 3. デバッグ効率が上がる
OrderSend() 後の失敗だけを見るより、原因切り分けがしやすいです。
■ 4. 危険な注文を弾ける
不正なSL/TPや異常ロットを、本送信前に止められます。
6.5 実務での安全な設計パターン
初心者ほど、注文処理を1か所に詰め込みがちです。
しかし実務では、以下の3段階に分けると安定します。
■ 推奨フロー
- リクエストを作る
OrderCheck()で検証する- 問題なければ
OrderSend()する
コードの役割を分離すると、後から修正しやすくなります。
// 1. request作成
// 2. OrderCheck
// 3. OrderSend
この構造にしておくと、
将来的に以下も追加しやすくなります。
- ロット自動調整
- ストップ距離補正
- エラー時の再試行
- ログ保存
よくあるつまずき・注意点
■ ① OrderCheckが通れば必ず約定すると思う
→ そうではありません。OrderCheck() は事前確認です。
実際の送信時には価格変動や市場状態で失敗することがあります。
■ ② OrderCheckを省略して本番投入する
→ デモでは動いても、本番で失敗率が上がりやすいです。
■ ③ check.retcodeを見ていない
→ true / false だけでは不十分です。
必ず retcode と comment を確認します。
■ ④ 証拠金計算を自前だけで済ませる
→ 自前計算も有効ですが、最終的には OrderCheck() で口座条件込みの確認をしたほうが安全です。
このセクションのまとめ(重要ポイント)
OrderCheck()は注文前の事前検証に使うOrderSend()の前に挟むことで失敗率を下げやすいMqlTradeCheckResultで証拠金やエラー理由を確認できる- 実務では「作成 → 検証 → 送信」の3段階設計が安定
7. 再利用しやすく壊れにくい注文処理の設計パターン
このセクションでは、MqlTradeRequest を単発で書いて終わりにせず、EA全体で再利用しやすい構造にする方法を解説します。注文処理は、後から「ロット調整」「SL/TP変更」「エラー対応」を追加しやすい形で作っておくことが重要です。ここを雑に作ると、記事の理解はできても実運用で崩れやすくなります。
7.1 注文処理を1か所に集約する
初心者がやりがちな失敗は、OnTick() の中に直接すべて書くことです。
if(買い条件)
{
MqlTradeRequest request = {};
MqlTradeResult result = {};
// volume, price, sl, tp, OrderSend...
}
この書き方は最初は動きますが、次の問題が出ます。
- BuyとSellで重複コードが増える
- 修正漏れが起きやすい
- デバッグ箇所が散らばる
- 指値注文や決済追加で破綻しやすい
そのため、注文処理は専用関数に切り出すのが基本です。
7.2 最低限の関数分離パターン
まずは「Buyを送る関数」を作るだけでも改善します。
bool SendBuyOrder(double volume, double sl, double tp)
{
MqlTradeRequest request = {};
MqlTradeResult result = {};
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = volume;
request.type = ORDER_TYPE_BUY;
request.price = ask;
request.sl = sl;
request.tp = tp;
request.deviation = 10;
request.type_filling = ORDER_FILLING_IOC;
if(!OrderSend(request, result))
{
Print("Buy注文失敗: ", result.retcode);
return false;
}
if(result.retcode != TRADE_RETCODE_DONE)
{
Print("Buy注文拒否: ", result.retcode);
return false;
}
return true;
}
呼び出し側はシンプルになります。
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double sl = ask - 100 * _Point;
double tp = ask + 100 * _Point;
SendBuyOrder(0.1, sl, tp);
■ この分離のメリット
- 注文ロジックを再利用できる
- 失敗時の処理を統一できる
OnTick()が読みやすくなる- 後で
OrderCheck()を追加しやすい
7.3 Buy/Sellを共通化する設計
さらに一歩進めるなら、Buy/Sellを1つの関数で扱います。
bool SendMarketOrder(ENUM_ORDER_TYPE orderType, double volume, double sl, double tp)
{
MqlTradeRequest request = {};
MqlTradeResult result = {};
double price = 0.0;
if(orderType == ORDER_TYPE_BUY)
price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
else if(orderType == ORDER_TYPE_SELL)
price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
else
return false;
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = volume;
request.type = orderType;
request.price = price;
request.sl = sl;
request.tp = tp;
request.deviation = 10;
request.type_filling = ORDER_FILLING_IOC;
if(!OrderSend(request, result))
{
Print("注文送信失敗: ", result.retcode);
return false;
}
if(result.retcode != TRADE_RETCODE_DONE)
{
Print("注文拒否: ", result.retcode);
return false;
}
return true;
}
これなら、呼び出し側は方向だけ変えれば済みます。
SendMarketOrder(ORDER_TYPE_BUY, 0.1, slBuy, tpBuy);
SendMarketOrder(ORDER_TYPE_SELL, 0.1, slSell, tpSell);
7.4 リクエスト生成と送信を分ける
実務では、さらに
「requestを作る処理」と「送る処理」を分ける
と保守性が上がります。
■ 役割分担の考え方
- 生成:注文内容を組み立てる
- 検証:
OrderCheck()する - 送信:
OrderSend()する
例えば以下のような分離です。
void BuildBuyRequest(MqlTradeRequest &request, double volume, double sl, double tp)
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = volume;
request.type = ORDER_TYPE_BUY;
request.price = ask;
request.sl = sl;
request.tp = tp;
request.deviation = 10;
request.type_filling = ORDER_FILLING_IOC;
}
送信側:
bool ExecuteRequest(MqlTradeRequest &request)
{
MqlTradeResult result = {};
if(!OrderSend(request, result))
{
Print("送信失敗: ", result.retcode);
return false;
}
if(result.retcode != TRADE_RETCODE_DONE)
{
Print("約定失敗: ", result.retcode);
return false;
}
return true;
}
この設計だと、後から以下を入れやすくなります。
OrderCheck()の追加- ロット補正
- 許容スリッページの変更
- ログ出力の標準化
- 銘柄ごとの分岐
7.5 実務で入れておきたい最低限の防御
注文関数には、最低限以下を入れておくと安定しやすいです。
■ 1. volumeチェック
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
■ 2. stop距離チェック
int stopLevel = (int)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
■ 3. OrderCheck追加
MqlTradeCheckResult check = {};
OrderCheck(request, check);
■ 4. retcodeログ出力
Print("retcode=", result.retcode);
これを毎回手作業で書くのではなく、共通関数にまとめるのがポイントです。
よくあるつまずき・注意点
■ ① OnTickに全部書く
→ 小規模のうちは動いても、すぐ保守不能になります。
■ ② Buy用・Sell用を完全コピペで増やす
→ 後から修正点が増えると事故率が上がります。
■ ③ エラー処理を呼び出し元ごとに変える
→ ログの形式がバラバラになり、解析しにくくなります。
■ ④ request生成と送信を分けない
→ OrderCheck() や条件補正を入れにくいです。
■ ⑤ 将来の拡張を想定しない
→ 指値注文、分割決済、複数銘柄対応で崩れます。
このセクションのまとめ(重要ポイント)
- 注文処理は関数化して1か所に集約する
- Buy/Sellの重複コードはできるだけ共通化する
- 「生成」「検証」「送信」を分離すると壊れにくい
- 実務ではロット・ストップ距離・retcode確認を共通化する
8. ブローカー環境や実口座で失敗しやすい注意点
このセクションでは、MqlTradeRequest のコード自体は正しく見えても、ブローカー仕様・口座種別・実行環境の差によって注文が失敗する典型パターンを整理します。MQL5の注文処理は、言語仕様だけで完結せず、取引環境の制約も前提にする必要があります。
8.1 ブローカーごとに約定条件が違う
MQL5では同じコードでも、ブローカーによって通る注文と通らない注文があります。特に差が出やすいのは以下です。
type_filling(約定方式)- 最小ロット・ロット刻み
SYMBOL_TRADE_STOPS_LEVEL(最小ストップ距離)- 対応している注文種別
- 取引時間
例えば、ORDER_FILLING_IOC は通るが ORDER_FILLING_RETURN は拒否される、といった差があります。
そのため、固定値を決め打ちするより、まず銘柄情報を取得して確認するほうが安全です。
long fillingMode = SymbolInfoInteger(_Symbol, SYMBOL_FILLING_MODE);
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
int stopLevel = (int)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
8.2 デモ口座では動くのに本番で失敗する理由
これは非常によくあります。主な理由は次の通りです。
- デモは約定しやすく、価格変動の影響が軽い
- 本番は流動性(注文の通りやすさ)が変化する
- スプレッド拡大時に
priceやsl/tpが不適切になる - ニュース時に
deviationが足りない - 証拠金条件が厳しくなる
つまり、テスター・デモ・本番は同一ではありません。
テスターで成功したコードをそのまま本番投入するのではなく、最低でも以下を入れておくべきです。
OrderCheck()による事前確認retcodeのログ出力- ロットとストップ距離の補正
- スプレッド異常時の発注回避
8.3 VPS・複数EA運用で起きる実務上の問題
VPS(仮想サーバー)や複数EA運用では、単体検証では見えない問題が出ます。
■ よくある問題
- 複数EAが同時に発注して想定外のポジションを持つ
magic未設定で他EAの注文と区別できない- 再起動後に状態管理がずれる
- 通信遅延で価格ズレが増える
対策として、少なくとも magic は設定しておくべきです。
request.magic = 10001;
また、ポジション管理側でも magic や symbol を条件に絞って扱わないと、別EAの注文を誤って変更・決済する危険があります。
8.4 実口座前に確認したいチェック項目
実口座投入前は、次を最低限確認してください。
- 最小ロットとロット刻み
- 対応する
type_filling - 最小ストップ距離
- その銘柄の取引時間
- スプレッド拡大時の挙動
magicによる識別retcodeの記録
よくあるつまずき・注意点
■ ① テスター成功=本番成功だと思う
→ これは危険です。実口座は約定条件が別です。
■ ② ブローカー差を無視する
→ 同じEAでも業者変更で失敗率が変わります。
■ ③ magic を使わない
→ 複数EA運用で事故の原因になります。
■ ④ 固定のSL/TP幅を全銘柄に使う
→ 銘柄ごとのボラティリティ差を無視すると不適切になります。
このセクションのまとめ(重要ポイント)
- MQL5の注文処理は、コードだけでなく取引環境にも依存する
- ブローカー差・口座差・本番差を前提に設計する必要がある
- 実務では
magic、OrderCheck()、環境取得を組み合わせると安定しやすい - テスターで終わらせず、実口座前の確認工程を必ず入れる
9. FAQ(よくある質問と回答方針)
Q1. MqlTradeRequestは必ず使わないといけませんか?
回答方針:
はい、MQL5では必須です。MQL4のように直接OrderSendへ複数引数を渡す方式は使えず、注文はすべてMqlTradeRequestを通じて行います。
Q2. type_fillingはどれを使えばいいですか?
回答方針:
環境依存です。まずは ORDER_FILLING_IOC を試し、エラーが出る場合は SymbolInfoInteger(..., SYMBOL_FILLING_MODE) で対応方式を確認します。ブローカーごとに異なります。
Q3. OrderSendがtrueなのに注文が通っていません
回答方針:OrderSend() の戻り値ではなく result.retcode を確認してください。trueは「送信成功」であり、「約定成功」ではありません。TRADE_RETCODE_DONE を確認する必要があります。
Q4. SL/TPを設定するとエラーになります
回答方針:
最小ストップ距離(SYMBOL_TRADE_STOPS_LEVEL)を下回っている可能性があります。銘柄ごとに制限があるため、値を取得して調整する必要があります。
Q5. デモでは動くのに本番で失敗します
回答方針:
約定条件・スプレッド・流動性の違いが原因です。本番では deviation を広げる、OrderCheck() を使う、ログを確認するなどの対策が必要です。
Q6. volumeエラーが出る原因は何ですか?
回答方針:
最小ロット未満、またはロット刻みに合っていない可能性があります。SYMBOL_VOLUME_MIN と SYMBOL_VOLUME_STEP を取得して補正します。
Q7. 指値注文が通らないのはなぜですか?
回答方針:
価格方向が間違っている可能性があります。
- Buy Limit → 現在価格より下
- Sell Limit → 現在価格より上
条件が逆だと拒否されます。
Q8. OrderCheckは必ず使うべきですか?
回答方針:
必須ではありませんが、実務では強く推奨されます。証拠金不足や条件エラーを事前に検出できるため、特に自動売買では安定性が大きく向上します。