MQL5ログファースト設計を実コードで解説|event・side・reason・detailで追跡するEA開発
MQL5でEAを開発する時、不具合の原因を後から追えるかどうかは、ロジックそのものと同じくらい重要です。
EAがエントリーしない、注文に失敗する、チャート表示が残る、時間足変更後に挙動が変わる、長時間稼働でログが増えすぎる。こうした問題は、発生時点の状態をExpertsログで追えないと、後から再現条件を整理することが難しくなります。
この記事では、MQL5 EA開発におけるログファースト設計を、学習用に一般化したコード断片を使って解説します。売買判断や収益性を示す記事ではなく、EAを安全に検証・保守するためのログ設計を整理する記事です。
完成EAソース全文、外部シート参照の動作制御ロジック、認証、endpoint、token、URL、実運用set値、中核エントリー条件は公開しません。この記事で扱うのは、ログ設計の考え方と、公開可能な範囲に一般化した短いコード例だけです。
- この記事で扱う範囲
- ログファースト開発とは
- Printをただ増やすだけでは不十分な理由
- event / side / reason / detail の考え方
- ログを出す主な場面
- INITログで確認すること
- ENTRY_SKIPログで確認すること
- ORDER_FAILログで確認すること
- OBJECT_AUDITログで確認すること
- 状態変化時だけログを出す理由
- INIT・SELFCHECK・SNAPを分ける
- 実コード断片1:構造化ログhelper
- 実コード断片2:同じログを出しすぎない抑制例
- 実コード断片3:理由付きログ出力の使い分け
- 注文要求と実行結果を構造化ログへ残す
- ログ設計と責務分離
- 開発版と配布版でログ粒度を分ける
- ログに出してよい情報・出してはいけない情報
- よくある失敗例
- Expertsログで確認するポイント
- 問い合わせ前に送るログ
- 注文ログの失敗例と共有前の確認
- ログファースト設計を開発依頼に活かす
- 公開しない情報
- よくある質問
- 関連する確認ページ
- あわせて確認したいページ
- まとめ
- お問い合わせはEAFC公式LINEへ
- 利用条件・相談窓口
この記事で扱う範囲
| 項目 | この記事で扱う内容 | 扱わない内容 |
|---|---|---|
| 目的 | ExpertsログでEAの状態、見送り理由、失敗理由を追える設計 | 利益を狙う売買ロジック、エントリー条件の公開 |
| コード | 学習用に簡略化したログ関数、ログ抑制、出力例 | 完成EAソース全文、実運用コード全文 |
| ログ | event、side、reason、detailに分けた構造化ログ | 認証情報、token、URL、口座情報を含むログ |
| 確認対象 | INIT、ENTRY_SKIP、ORDER_FAIL、OBJECT_AUDITなどの考え方 | 外部制御値の詳細、実運用set値、中核判定条件 |
| 読者 | MQL5開発者、EA利用者、開発依頼前にログを整理したい人 | 売買判断や推奨設定を知りたい人 |
ログファースト開発とは
ログファースト開発とは、EAが正常に動いた時だけでなく、動かなかった理由、見送った理由、失敗した理由、保護処理が継続している理由を、最初からログで追えるように設計する考え方です。
MQL5では、Print()を使えばExpertsログへ文字を出せます。しかし、単にPrintを増やすだけでは、実運用や長時間検証で必要な情報を追えません。
重要なのは、ログを次のように分解して出すことです。
| 要素 | 意味 | 例 |
|---|---|---|
| layer | どの機能層のログか | INIT、ORDER、ENTRY、OBJECT_AUDIT、RISK |
| side | BUY側かSELL側か、方向なしのログか | BUY、SELL、NONE |
| event | 何が起きたか | START、SKIP、SEND_FAIL、STARTUP_FOUND |
| reason | なぜ起きたか | SPREAD_LIMIT、NO_POSITION、INVALID_HANDLE |
| detail | 確認に必要な補足情報 | symbol、timeframe、retcode、object_kindなど |
このように分けると、ログを見た時に「どの処理で」「どちら側で」「何が起きて」「なぜそうなったか」を追いやすくなります。
Printをただ増やすだけでは不十分な理由
EA開発では、不具合が出るたびにPrintを追加したくなります。しかし、Printを場当たり的に追加すると、次の問題が起きます。
| よくある状態 | 問題点 | 改善方針 |
|---|---|---|
| 同じログが毎tick出る | Expertsログが埋まり、重要な失敗ログが流れる | 状態変化時だけ出す、またはthrottleする |
| 日本語文章だけのログ | 検索、比較、機械的な抽出がしにくい | event、reason、detailを固定キーにする |
| 成功ログだけ多い | 見送り理由や失敗理由が追えない | SKIP、BLOCK、FAILを理由付きで出す |
| 売買判定とログ出力が混ざる | ログ修正が売買挙動へ影響する可能性がある | 判定helperとログhelperを分離する |
| 機密情報がそのまま出る | 問い合わせ時にURL、token、口座情報が漏れる | ログ出力前にマスク・除外する |
ログファースト設計では、Printを増やすこと自体が目的ではありません。検証、保守、問い合わせ対応で使える形に整理して出すことが目的です。
event / side / reason / detail の考え方
構造化ログでは、ログの文章を長くするよりも、固定された項目で揃えることを優先します。
たとえば、次のようなログ形式にすると、後から検索しやすくなります。
DIAG/ORDER: side=BUY event=SEND_FAIL reason=RETCODE_REJECT detail=symbol=XXX retcode=10000
DIAG/ENTRY: side=SELL event=SKIP reason=SPREAD_LIMIT detail=spread=high
DIAG/OBJECT_AUDIT: side=NONE event=STARTUP_FOUND reason=VISUAL_ONLY detail=kind=AVG_LINE
この形式では、ログの前半だけで大まかな種類を把握できます。
- DIAG/ORDER:注文・発注結果に関するログ
- side=BUY:BUY側の処理
- event=SEND_FAIL:注文送信に失敗
- reason=RETCODE_REJECT:失敗理由の分類
- detail=…:調査時に必要な補足
このようにログを分けると、同じEAでも、初期化、エントリー見送り、注文失敗、決済失敗、Object監査、リスク制御などを別々に確認できます。
ログを出す主な場面
EAでログを出す場面は、成功時だけではありません。むしろ、見送りや失敗の理由を残すことが重要です。
| ログ種別 | 目的 | 確認すること |
|---|---|---|
| INIT | EA起動時の準備状態を確認する | バージョン、銘柄、時間足、主要設定、初期化結果 |
| ENTRY_SKIP | 新規エントリーを見送った理由を確認する | spread、時間、リスク、同一バー制御、権限制御など |
| ORDER_FAIL | 注文送信や変更に失敗した理由を確認する | retcode、GetLastError、lot、symbol、action |
| OBJECT_AUDIT | チャート上の残存Objectや表示状態を確認する | 表示専用、実ロジック参照除外、削除対象、無視対象 |
| RUNTIME | 稼働中の状態変化を確認する | 許可状態、停止状態、ガード状態、保護処理の継続 |
| DEINIT | EA終了時の安全性を確認する | 異常終了の有無、終了時に売買処理が走っていないか |
INITログで確認すること
INITログは、EAをチャートへセットした直後の確認に使います。ここで重要なのは、すべての内部情報を出すことではなく、起動状態を判断できる最小限の情報を出すことです。
INITログに入れてよい情報の例です。
- EA名、バージョン
- symbol、timeframe
- 主要機能のON/OFF状態
- ログモード
- Object監査の実行有無
- 初期化成功または安全停止の状態
一方で、次の情報はINITログに出してはいけません。
- 認証token
- endpoint
- Webhook URL
- Google Sheet ID
- 口座番号
- 購入者管理情報
- 実運用set値の詳細
ENTRY_SKIPログで確認すること
EAがエントリーしない時、最初に必要なのは「なぜ見送ったか」です。
よくある失敗は、エントリー条件が成立していないのか、成立したが執行前のガードで止まったのかをログで分けていないことです。
| 状態 | ログで分けるべき理由 |
|---|---|
| シグナル不成立 | ENTRY対象ではない。signal側の確認が必要 |
| スプレッド超過 | signalは候補だが、broker条件で見送り |
| 同一バー重複防止 | 再送防止による見送り |
| 保有上限 | リスク・ポジション管理による見送り |
| 外部許可NG | 新規Entryだけ停止。既存ポジション管理とは分けて確認 |
公開記事では、外部制御の詳細や中核エントリー条件は扱いません。ただし、ログ設計上は「見送り理由をreasonとして残す」ことが重要です。
ORDER_FAILログで確認すること
注文失敗ログではGetLastError()だけでなく、送信したrequestとMqlTradeResult.retcodeを同じ処理単位で追えるようにします。GetLastError()は読み出しても自動でリセットされないため、必要に応じて対象処理の直前でResetLastError()を使い、古いエラー値との混同を避けます。
注文失敗時は、MQL5側の戻り値だけでなく、注文種別、方向、symbol、lot、retcode、GetLastErrorを分けて残します。
注文に失敗した場合、単に「注文失敗」と出すだけでは、次の調査ができません。
- lotが不正だったのか
- AutoTradingがOFFだったのか
- 価格が変わったのか
- サーバー側で拒否されたのか
- 一時的な失敗でretry対象なのか
- そもそも注文対象のsymbolが違うのか
注文結果ログでは、少なくとも「何を送ろうとして」「何が返ってきたか」を追えるようにします。
OBJECT_AUDITログで確認すること
チャート上に描画するEAでは、Objectの残存や再セット時の誤参照が問題になることがあります。
たとえば、平均価格ライン、TPライン、Trailライン、仮想ライン、ラベル、パネル部品などが残っている場合、それを実ロジックへ使うのか、表示専用として無視するのかを分ける必要があります。
Object監査ログでは、次のような観点を残します。
| 監査項目 | 確認内容 |
|---|---|
| STARTUP_FOUND | 起動時に既存Objectを検出した |
| VISUAL_ONLY | 表示専用として扱い、実ロジックへ使わない |
| IGNORE_FOR_LOGIC | 残存Objectを判定ロジックから除外する |
| CLEANUP_PLAN | 削除候補だが、条件を確認してから処理する |
| CLEANUP_SUMMARY | 監査や整理の結果を要約する |
EA削除時にすべてのObjectを無理に消すより、再セット時に監査して、実ロジックへ誤採用しない構造にする方が安全な場合があります。
状態変化時だけログを出す理由
リアルタイムで動くEAでは、OnTickが何度も呼ばれます。条件不成立や待機状態を毎tickログに出すと、Expertsログがすぐに埋まります。
ログ過多になると、重要な失敗ログを見落としやすくなります。そのため、ログは次のように分けると実用的です。
| ログ種別 | 出力方針 |
|---|---|
| 注文成功・注文失敗 | 重要なので原則出す |
| 決済成功・決済失敗 | 重要なので原則出す |
| 設定不正 | 状態変化時、または一定間隔で出す |
| 待機中 | 毎tickではなく抑制する |
| 同じ見送り理由 | 一定秒数以内は抑制する |
| 起動時チェックリスト | 起動時に1回だけ出す |
INIT・SELFCHECK・SNAPを分ける
ログでは、起動情報、前提条件確認、現在状態の記録を同じeventへまとめないことが重要です。
INITにはEA名、バージョン、銘柄、時間足、主要設定を記録します。SELFCHECKには自動売買許可、銘柄仕様、ロット条件、WebRequestなどの前提条件を記録します。SNAPにはポジション数、状態フラグ、counterなど、その時点のruntime状態を記録します。
この3種類を分けることで、初期化失敗、前提条件による停止、稼働中の状態変化を原因を確認しやすくなります。
実コード断片1:構造化ログhelper
以下は、実際のEAで使う考え方を学習用に簡略化したコード例です。完成EAのソース全文ではなく、構造を理解するための抜粋です。
// 学習用に一般化したログ出力helperです。
// 実運用EAの完成ソースではありません。
bool CanWriteDiagLog()
{
return true; // 実際はinputやruntime設定でON/OFFを分けます。
}
string SafeText(string value, string fallback)
{
if(value == "")
return fallback;
// 公開用の考え方:機密情報に該当するdetailは出さない。
if(StringFind(value, "token") >= 0)
return "masked";
return value;
}
void WriteDiagLog(string layer, string side, string eventName, string reason, string detail)
{
if(!CanWriteDiagLog())
return;
string safeLayer = SafeText(layer, "GENERAL");
string safeSide = SafeText(side, "NONE");
string safeEvent = SafeText(eventName, "UNKNOWN");
string safeReason = SafeText(reason, "NO_REASON");
string safeDetail = SafeText(detail, "no detail");
Print("DIAG/", safeLayer,
": side=", safeSide,
" event=", safeEvent,
" reason=", safeReason,
" detail=", safeDetail);
}実際のEAでは、認証、外部制御、口座条件、エラー処理、ログ出力などを分けて設計します。この記事では、公開できる範囲の構造だけを取り上げています。
このコードの役割
| 行・処理 | 役割 |
|---|---|
| CanWriteDiagLog | ログ出力をON/OFFする入口です。開発版、検証版、配布版でログ粒度を変える時に使います。 |
| SafeText | 空文字や機密情報をそのまま出さないための補助です。 |
| WriteDiagLog | layer、side、event、reason、detailを固定形式で出力します。 |
| Expertsログへ1行で出します。ログ形式を揃えることで、後から検索しやすくなります。 |
ここで重要なのは、ログ出力を各処理に直接書き散らさず、helperへ集約することです。helperへ集約すると、ログ形式、マスク処理、出力ON/OFF、ログ抑制を後から調整しやすくなります。
実コード断片2:同じログを出しすぎない抑制例
以下は、同一キーのログを一定秒数だけ抑制する学習用コード例です。完成EAのソース全文ではなく、ログ過多を防ぐ考え方を示すための簡略例です。
// 学習用の簡易throttle例です。
// 実際のEAでは、ログ種類ごとに抑制秒数を分けます。
string g_lastLogKey = "";
datetime g_lastLogTime = 0;
bool ShouldSkipSameLog(string key, int throttleSeconds)
{
datetime nowTime = TimeCurrent();
if(key == g_lastLogKey)
{
if((nowTime - g_lastLogTime) < throttleSeconds)
return true;
}
g_lastLogKey = key;
g_lastLogTime = nowTime;
return false;
}
void WriteDiagLogThrottled(string layer, string side, string eventName, string reason, string detail)
{
string key = layer + "|" + side + "|" + eventName + "|" + reason;
if(ShouldSkipSameLog(key, 300))
return;
WriteDiagLog(layer, side, eventName, reason, detail);
}実際のEAでは、注文失敗や決済失敗のように必ず残したいログと、待機状態のように抑制したいログを分けます。すべてのログを同じ抑制にすると、重要な失敗履歴が欠落する可能性があるためです。
ログ抑制で注意すること
- 注文失敗、決済失敗、初期化失敗は抑制しすぎない
- 待機中、同じ見送り理由、同じ警告は抑制してよい
- 抑制キーにはlayer、side、event、reasonを含める
- detailまでキーに入れると抑制が効きにくい場合がある
- 検証時だけ詳細ログをONにできるようにする
実コード断片3:理由付きログ出力の使い分け
以下は、INIT、ENTRY_SKIP、ORDER_FAIL、OBJECT_AUDITを、同じログhelperで出し分ける学習用コード例です。売買判断や中核エントリー条件は含めていません。
// 学習用の使用例です。
// エントリー条件や外部制御ロジックは含めていません。
void LogInitExample()
{
WriteDiagLog("INIT", "NONE", "START",
"EA_LOADED",
"symbol=" + _Symbol + " tf=" + EnumToString(_Period));
}
void LogEntrySkipExample(bool isBuy, string reason)
{
string side = (isBuy ? "BUY" : "SELL");
WriteDiagLogThrottled("ENTRY", side, "SKIP",
reason,
"scope=new_entry_only");
}
void LogOrderFailExample(bool isBuy, MqlTradeResult &result)
{
string side = (isBuy ? "BUY" : "SELL");
WriteDiagLog("ORDER", side, "SEND_FAIL",
"ORDER_RETCODE",
"retcode=" + IntegerToString((int)result.retcode));
}
void LogObjectAuditExample(string objectKind)
{
WriteDiagLog("OBJECT_AUDIT", "NONE", "STARTUP_FOUND",
"VISUAL_ONLY",
"kind=" + objectKind);
}実際のEAでは、認証、外部制御、口座条件、エラー処理、ログ出力などを分けて設計します。この記事では、公開できる範囲の構造だけを取り上げています。
この例で分かること
- INIT、ENTRY、ORDER、OBJECT_AUDITを同じ形式で出せる
- BUY / SELL / NONEをsideとして分けられる
- SKIP、SEND_FAIL、STARTUP_FOUNDなどのeventを固定できる
- reasonを分けることで、同じSKIPでも理由を追える
- detailは調査に必要な補足だけに絞れる
注文要求と実行結果を構造化ログへ残す
以下のコード例は、個別の処理と確認項目を読むためのものです。複数の例をそのまま一つのEAへ連結する前提ではありません。初期化、値の取得、状態更新、終了処理をそれぞれの例で区別して確認してください。
OrderSend結果ログは注文処理の最終確認に使う
EAの発注処理では、シグナルが成立した後に、スプレッド、ロット、証拠金、SL / TP距離、取引許可、ポジション数、Magic Numberなどを確認し、最後にOrderSendで注文リクエストを送ります。
この時、OrderSendの直前までの条件がすべて正常でも、サーバー側で注文が拒否されることがあります。価格変動、取引時間外、ロット不正、証拠金不足、StopLevel違反、Filling Mode不一致、銘柄取引停止、通信状態などが関係するためです。
そのため、OrderSend結果ログでは、EA側が送ったリクエストと、サーバー側から返った結果を分けて残します。これにより、EAの判定ミスなのか、リクエスト構築ミスなのか、サーバー側の拒否なのか、口座・銘柄仕様の問題なのかを追いやすくなります。
| 確認層 | 確認する内容 | 主なログ |
|---|---|---|
| SIGNAL | BUY / SELLの候補が出たか | SIGNAL_OK / SIGNAL_NG |
| GATE | 稼働時間、許可状態、最大ポジション数など | GATE_PASS / GATE_BLOCK |
| SPECIAL FILTERS | スプレッド、指標、外部停止、ボラティリティなど | FILTER_PASS / FILTER_BLOCK |
| ORDER PRECHECK | ロット、証拠金、SL / TP距離、OrderCheck | ORDER_CHECK / PRECHECK_NG |
| ORDER SEND | MqlTradeRequestを送信した結果 | ORDER_SEND_OK / ORDER_SEND_NG |
| ORDER RESULT | retcode、deal、order、commentの確認 | TRADE_RESULT / RETCODE |
| FOLLOW-UP | ポジション反映、履歴、Magic Number確認 | POSITION_SYNC / HISTORY_CHECK |
OrderSend前後でログを見る順番
OrderSend結果ログは、1行だけを見るのではなく、発注前の条件、送信したrequest、返ってきたresult、反映確認の順番で追うと原因を確認やすくなります。特に、注文が出ない時は「OrderSendまで到達していない」のか、「OrderSendは実行されたがサーバー側で拒否された」のかを最初に分けてください。
実務では、SIGNALやFILTERのログで止まっている場合と、OrderSend後のretcodeで止まっている場合では、見るべき箇所が変わります。前者はEA内部条件、後者はrequest内容、銘柄仕様、口座条件、サーバー応答を確認する流れになります。
| 確認順 | 見るログ | 分けて確認すること |
|---|---|---|
| 1 | SIGNAL / FILTER / GATE | 注文処理へ進む条件を満たしたか |
| 2 | ORDER_CHECK_BEFORE | 送信予定のsymbol、type、volume、price、sl、tpが妥当か |
| 3 | ORDER_CHECK_RESULT | margin、margin_free、retcode、commentで事前確認の結果を見る |
| 4 | ORDER_SEND_BEFORE | 最終的にOrderSendへ渡したMqlTradeRequestを確認する |
| 5 | ORDER_SEND_AFTER | send_ok、retcode、deal、order、comment、last_errorを分けて確認する |
| 6 | POSITION / HISTORY | 約定、未約定注文、ポジション反映、履歴を確認する |
MqlTradeRequestで送信前の条件を記録する
MqlTradeRequestは、OrderSendに渡す注文リクエスト構造体です。注文方向、銘柄、ロット、価格、SL、TP、Magic Number、コメント、Filling Modeなど、EAがサーバーへ送ろうとした内容が入ります。
注文エラー調査では、MqlTradeRequestの値が想定どおりかを確認します。EA側のロット正規化が漏れていないか、SL / TP価格が銘柄のStopLevelに合っているか、注文タイプと価格が一致しているか、Magic Numberが正しいかを確認してください。
| request項目 | 確認する内容 | よくある問題 |
|---|---|---|
| action | TRADE_ACTION_DEAL、TRADE_ACTION_PENDINGなど | 成行注文と指値注文のaction不一致 |
| symbol | 対象銘柄名 | suffix付き銘柄、GOLD / XAUUSD表記差 |
| volume | 発注ロット | volume min / step / maxに合っていない |
| type | ORDER_TYPE_BUY、ORDER_TYPE_SELLなど | BUY / SELL方向の取り違え |
| price | 発注価格 | BUYでBid、SELLでAskを使うなどの方向ミス |
| sl | 損切り価格 | StopLevel違反、価格方向の誤り |
| tp | 利確価格 | StopLevel違反、価格方向の誤り |
| deviation | 許容スリッページ | 値が小さすぎて価格変更に弱い |
| magic | Magic Number | 複数EAや手動取引との識別ができない |
| type_filling | Filling Mode | 銘柄仕様と不一致で注文拒否 |
| comment | 注文コメント | ログ上で注文意図を追えない |
MqlTradeResultで送信後の結果を記録する
MqlTradeResultは、OrderSend実行後にサーバー側から返る結果を受け取る構造体です。ここには、retcode、deal、order、volume、price、bid、ask、commentなどが入ります。
OrderSendの戻り値がtrueでも、result.retcodeを確認しなければ、どのような取引結果になったかを判断できません。OrderSendの戻り値、result.retcode、result.comment、GetLastErrorを混同しないようにしてください。
| result項目 | 確認する内容 | 用途 |
|---|---|---|
| retcode | TRADE_RETCODE系の結果コード | 注文結果の中心確認 |
| deal | 約定deal番号 | 約定済み取引の追跡 |
| order | 注文番号 | 未約定注文や注文履歴の追跡 |
| volume | 結果側の数量 | 部分約定や数量確認 |
| price | 結果側の価格 | 約定価格または処理価格の確認 |
| bid / ask | サーバー側価格 | 価格変更やスプレッド確認 |
| comment | サーバー側コメント | retcodeの補足確認 |
| request_id | リクエスト識別 | 非同期処理や追跡に使う場合がある |
| retcode_external | 外部システム由来のコード | ブローカー・取引所系の補足確認 |
OrderSendの戻り値とretcodeを分ける
OrderSendの戻り値は、取引リクエスト送信処理の成否を示します。一方、MqlTradeResultのretcodeは、取引サーバー側がそのリクエストをどのように処理したかを示します。
この2つを混同すると、「OrderSendがtrueだから注文成功」と誤解する可能性があります。注文処理では、OrderSendの戻り値、result.retcode、result.comment、GetLastErrorを必ず分けてログ化してください。
| 確認対象 | 意味 | 確認上の注意 |
|---|---|---|
| OrderSend戻り値 | リクエスト送信処理の成否 | trueでも約定成功とは限らない |
| result.retcode | サーバー側の取引結果コード | TRADE_RETCODE_DONEなどを確認する |
| result.comment | サーバー側コメント | 拒否理由や補足が出る場合がある |
| GetLastError | 端末側・関数呼び出し側のエラー | retcodeとは別に確認する |
| deal / order | 約定・注文番号 | 成功時の追跡に使う |
| price / bid / ask | 処理価格・サーバー価格 | 価格変更やスリッページ確認に使う |
TRADE_RETCODEは分類して見る
TRADE_RETCODEは、注文結果の確認で最も重要な情報です。すべてのコードを暗記する必要はありませんが、成功系、価格系、証拠金系、ロット系、SL / TP系、取引許可系、通信・サーバー系に分類して見ると原因確認がしやすくなります。
| 分類 | 代表的な状態 | 確認すること |
|---|---|---|
| 成功系 | DONE、PLACED、DONE_PARTIAL | deal、order、volume、priceを確認する |
| 価格系 | REQUOTE、PRICE_CHANGED、PRICE_OFF | Bid / Ask、deviation、約定方式を確認する |
| ロット系 | INVALID_VOLUME | volume min / step / max、ロット正規化を確認する |
| SL / TP系 | INVALID_STOPS | StopLevel、FreezeLevel、価格方向を確認する |
| 証拠金系 | NO_MONEY | OrderCalcMargin、margin_free、ロットを確認する |
| 取引許可系 | TRADE_DISABLED、MARKET_CLOSED | 銘柄trade mode、取引時間、自動売買許可を確認する |
| Filling系 | INVALID_FILL | SYMBOL_FILLING_MODEとrequest.type_fillingを確認する |
| 通信・混雑系 | CONNECTION、TOO_MANY_REQUESTS | 通信状態、連続発注、リトライ制御を確認する |
| 変更・凍結系 | ORDER_CHANGED、FROZEN | 注文変更、FreezeLevel、保留注文状態を確認する |
retcodeを文字列化してログを読みやすくする
retcodeは数値で出すだけでも確認できますが、ログ確認やサポート時には、簡単な文字列化関数を用意しておくと原因確認が速くなります。
string TradeRetcodeToText(const uint retcode)
{
switch(retcode)
{
case TRADE_RETCODE_REQUOTE: return "REQUOTE";
case TRADE_RETCODE_REJECT: return "REJECT";
case TRADE_RETCODE_CANCEL: return "CANCEL";
case TRADE_RETCODE_PLACED: return "PLACED";
case TRADE_RETCODE_DONE: return "DONE";
case TRADE_RETCODE_DONE_PARTIAL: return "DONE_PARTIAL";
case TRADE_RETCODE_ERROR: return "ERROR";
case TRADE_RETCODE_TIMEOUT: return "TIMEOUT";
case TRADE_RETCODE_INVALID: return "INVALID";
case TRADE_RETCODE_INVALID_VOLUME: return "INVALID_VOLUME";
case TRADE_RETCODE_INVALID_PRICE: return "INVALID_PRICE";
case TRADE_RETCODE_INVALID_STOPS: return "INVALID_STOPS";
case TRADE_RETCODE_TRADE_DISABLED: return "TRADE_DISABLED";
case TRADE_RETCODE_MARKET_CLOSED: return "MARKET_CLOSED";
case TRADE_RETCODE_NO_MONEY: return "NO_MONEY";
case TRADE_RETCODE_PRICE_CHANGED: return "PRICE_CHANGED";
case TRADE_RETCODE_PRICE_OFF: return "PRICE_OFF";
case TRADE_RETCODE_INVALID_EXPIRATION:return "INVALID_EXPIRATION";
case TRADE_RETCODE_ORDER_CHANGED: return "ORDER_CHANGED";
case TRADE_RETCODE_TOO_MANY_REQUESTS:return "TOO_MANY_REQUESTS";
case TRADE_RETCODE_NO_CHANGES: return "NO_CHANGES";
case TRADE_RETCODE_SERVER_DISABLES_AT:return "SERVER_DISABLES_AT";
case TRADE_RETCODE_CLIENT_DISABLES_AT:return "CLIENT_DISABLES_AT";
case TRADE_RETCODE_LOCKED: return "LOCKED";
case TRADE_RETCODE_FROZEN: return "FROZEN";
case TRADE_RETCODE_INVALID_FILL: return "INVALID_FILL";
case TRADE_RETCODE_CONNECTION: return "CONNECTION";
case TRADE_RETCODE_ONLY_REAL: return "ONLY_REAL";
case TRADE_RETCODE_LIMIT_ORDERS: return "LIMIT_ORDERS";
case TRADE_RETCODE_LIMIT_VOLUME: return "LIMIT_VOLUME";
default: return "UNKNOWN_RETCODE";
}
}この関数は、retcodeの分類を読みやすくするための補助です。実際の実装では、使用しているMT5ビルドやブローカーの返却内容に応じて、必要なコードを追加してください。
MqlTradeRequestをログに出す
OrderSend前には、MqlTradeRequestの中身をログに出します。これにより、EAが実際にどの条件で注文しようとしたのかを確認できます。
void LogTradeRequest(const string stage,
const MqlTradeRequest &request)
{
PrintFormat("[%s_REQUEST] action=%d symbol=%s type=%d volume=%.2f price=%.5f sl=%.5f tp=%.5f deviation=%d magic=%I64d type_filling=%d comment=%s",
stage,
request.action,
request.symbol,
request.type,
request.volume,
request.price,
request.sl,
request.tp,
request.deviation,
request.magic,
request.type_filling,
request.comment);
}stageには、ORDER_SEND_BEFORE、RETRY_BEFORE、CLOSE_BEFOREなど、どの段階のリクエストか分かる名前を入れると確認しやすくなります。
MqlTradeResultをログに出す
OrderSend後には、MqlTradeResultをログに出します。retcode、retcodeの文字列、deal、order、price、bid、ask、comment、GetLastErrorを同時に残すと、原因確認が進めやすくなります。
void LogTradeResult(const string stage,
const bool send_ok,
const MqlTradeResult &result,
const int last_error)
{
PrintFormat("[%s_RESULT] send_ok=%s retcode=%u retcode_text=%s deal=%I64u order=%I64u volume=%.2f price=%.5f bid=%.5f ask=%.5f comment=%s last_error=%d",
stage,
(send_ok ? "true" : "false"),
result.retcode,
TradeRetcodeToText(result.retcode),
result.deal,
result.order,
result.volume,
result.price,
result.bid,
result.ask,
result.comment,
last_error);
}OrderSendがfalseの場合でも、MqlTradeResultに情報が入ることがあります。send_okだけで分岐してログを省略せず、可能な限りresult全体を出してください。
OrderSend前後をまとめてログ化する例
次の例では、OrderSend前にrequestをログ化し、OrderSend後にresultとGetLastErrorをログ化します。実務では、この前段にスプレッド、ロット、証拠金、StopLevel、OrderCheckなどの確認を入れます。
bool SendTradeRequestWithResultLog(MqlTradeRequest &request,
MqlTradeResult &result)
{
ResetLastError();
LogTradeRequest("ORDER_SEND_BEFORE", request);
bool send_ok = OrderSend(request, result);
int last_error = GetLastError();
LogTradeResult("ORDER_SEND_AFTER", send_ok, result, last_error);
if(!send_ok)
{
PrintFormat("[ORDER_SEND_NG] reason=function_false retcode=%u retcode_text=%s last_error=%d comment=%s",
result.retcode,
TradeRetcodeToText(result.retcode),
last_error,
result.comment);
return false;
}
if(result.retcode != TRADE_RETCODE_DONE &&
result.retcode != TRADE_RETCODE_PLACED &&
result.retcode != TRADE_RETCODE_DONE_PARTIAL)
{
PrintFormat("[ORDER_SEND_NG] reason=retcode_not_success retcode=%u retcode_text=%s comment=%s",
result.retcode,
TradeRetcodeToText(result.retcode),
result.comment);
return false;
}
PrintFormat("[ORDER_SEND_OK] retcode=%u retcode_text=%s deal=%I64u order=%I64u price=%.5f volume=%.2f",
result.retcode,
TradeRetcodeToText(result.retcode),
result.deal,
result.order,
result.price,
result.volume);
return true;
}失敗時はretcode、last_error、requestをセットで読む
OrderSendが失敗した時は、retcodeだけ、GetLastErrorだけ、commentだけを単独で見ないようにします。retcodeは取引サーバー側の結果、last_errorは端末側や関数呼び出し側の情報、requestはEAが実際に送った条件です。3つを並べることで、EA側の入力ミスなのか、銘柄仕様や口座条件なのか、通信やサーバー応答なのかを分けやすくなります。
確認例
| ログ上の症状 | 最初に確認すること | 次に見る情報 |
|---|---|---|
| send_ok=false | GetLastErrorとrequest内容 | symbol、type、volume、price、sl、tp、filling |
| send_ok=trueだがretcodeが成功系でない | result.retcodeとcomment | 価格変更、証拠金、StopLevel、Filling Mode |
| INVALID_VOLUME | volume min / step / max | ロット正規化後の値、口座タイプ、銘柄仕様 |
| INVALID_STOPS | SL / TP方向とStopLevel | FreezeLevel、point、digits、現在価格との距離 |
| NO_MONEY | OrderCalcMarginとmargin_free | ロット、レバレッジ、口座通貨、銘柄条件 |
| INVALID_FILL | SYMBOL_FILLING_MODE | request.type_filling、銘柄ごとの約定方式 |
| MARKET_CLOSED / TRADE_DISABLED | 取引時間とtrade mode | サーバー時間、祝日、銘柄の取引停止状態 |
この確認は、売買判断を行うためではなく、EAの注文処理がどの段階で止まったかを確認するためのものです。ログを見てすぐにロットを上げる、条件を緩める、といった判断に直結させず、まずは原因の分類を行ってください。
この例では、OrderSendの戻り値がtrueでも、成功系retcodeでなければNGとして扱っています。実際の運用では、注文種別、約定方式、保留注文、部分約定、リトライ方針に合わせて成功判定を調整してください。
OrderCheckとOrderSend結果ログを分ける
OrderCheckは、MqlTradeRequestをサーバーへ送る前に確認する関数です。一方、OrderSendは実際に取引リクエストを送信する関数です。OrderCheckが成功しても、OrderSendが成功するとは限りません。
価格変動、取引時間、通信状態、Filling Mode、サーバー側の混雑、銘柄の取引状態などにより、OrderCheck後にOrderSendが失敗する場合があります。そのため、OrderCheckログとOrderSend結果ログは必ず分けてください。
| 段階 | 確認する内容 | ログ例 |
|---|---|---|
| OrderCheck前 | requestの内容が妥当か | ORDER_CHECK_BEFORE_REQUEST |
| OrderCheck後 | retcode、comment、margin、margin_free | ORDER_CHECK_RESULT |
| OrderSend前 | 最終的に送るrequestの内容 | ORDER_SEND_BEFORE_REQUEST |
| OrderSend後 | send_ok、retcode、deal、order、comment | ORDER_SEND_AFTER_RESULT |
| 反映確認 | ポジション・履歴・Magic Number | POSITION_SYNC / HISTORY_CHECK |
OrderCheckの詳細は、MQL5 OrderCheckの使い方も確認してください。
OrderCalcMargin / OrderCalcProfitとの接続
OrderSend結果ログだけでは、注文前に必要証拠金や想定損益をどのように見積もったかは分かりません。発注前チェックでは、OrderCalcMarginで必要証拠金、OrderCalcProfitでSL / TP到達時の想定損益を確認し、その後にOrderCheck、OrderSendへ進む流れが分かりやすくなります。
| 確認項目 | 使用する関数 | ログに残す内容 |
|---|---|---|
| 必要証拠金 | OrderCalcMargin | symbol、type、volume、price、margin |
| SL到達時の想定損失 | OrderCalcProfit | entry、sl、volume、profit、loss_abs |
| TP到達時の想定利益 | OrderCalcProfit | entry、tp、volume、profit |
| 注文リクエスト事前確認 | OrderCheck | retcode、comment、margin、margin_free |
| 注文送信結果 | OrderSend | send_ok、retcode、deal、order、comment |
必要証拠金と想定損益の確認は、MQL5 OrderCalcMargin / OrderCalcProfitの使い方で整理しています。
SymbolInfo系で銘柄仕様を確認する
OrderSend結果ログでINVALID_VOLUME、INVALID_STOPS、INVALID_FILLなどが出る場合、銘柄仕様の確認が必要になります。SymbolInfoDouble / SymbolInfoIntegerで、volume min、volume step、stops level、freeze level、filling mode、trade modeなどを確認してください。
| retcode・症状 | 確認する銘柄仕様 | 代表的な確認 |
|---|---|---|
| INVALID_VOLUME | SYMBOL_VOLUME_MIN / STEP / MAX | ロット正規化 |
| INVALID_STOPS | SYMBOL_TRADE_STOPS_LEVEL | SL / TP距離 |
| FROZEN | SYMBOL_TRADE_FREEZE_LEVEL | 注文変更・決済制限 |
| INVALID_FILL | SYMBOL_FILLING_MODE | type_fillingの対応 |
| TRADE_DISABLED | SYMBOL_TRADE_MODE | 銘柄取引可否 |
| 価格差の違和感 | SYMBOL_POINT / DIGITS / TICK_VALUE | point、digits、損益換算 |
| スプレッド超過 | SYMBOL_SPREAD、Bid / Ask | 発注見送り理由 |
銘柄仕様の確認は、MQL5 SymbolInfoDouble / SymbolInfoIntegerの使い方を確認してください。
CTradeを使う場合も結果ログを省略しない
CTradeクラスを使う場合でも、注文結果ログは必要です。CTradeのBuy、Sell、PositionCloseなどは便利ですが、内部でどのようなリクエストが作られ、どのretcodeが返ったかを確認しないと、注文エラーの原因を追いにくくなります。
CTradeでは、ResultRetcode、ResultRetcodeDescription、ResultDeal、ResultOrder、ResultPrice、ResultVolumeなどをログに出すと確認しやすくなります。
| CTradeで見る項目 | 確認する内容 | 用途 |
|---|---|---|
| ResultRetcode | 取引結果コード | OrderSend結果と同じく中心確認 |
| ResultRetcodeDescription | retcodeの説明 | ログを読みやすくする |
| ResultDeal | deal番号 | 約定確認 |
| ResultOrder | order番号 | 注文履歴確認 |
| ResultPrice | 処理価格 | 約定価格やサーバー価格確認 |
| ResultVolume | 処理数量 | 部分約定や数量確認 |
| CheckResultRetcode | OrderCheck相当の確認 | 発注前確認との接続 |
CTradeとMqlTradeRequestの違いは、MQL5 CTradeとMqlTradeRequestの違いも参考になります。
バックテストでOrderSend結果ログを見る
バックテストで注文が出ない場合、まずシグナルが出ているか、発注前チェックを通過しているか、OrderSendまで到達しているかを確認します。OrderSendログがない場合、どこで止まったかを判断しにくくなります。
| 症状 | 見るログ | 確認する内容 |
|---|---|---|
| 注文が出ない | SIGNAL / GATE / ORDER_SEND | OrderSendまで到達しているか |
| OrderSendに到達しない | FILTER / PRECHECK | スプレッド、ロット、時間条件、ポジション制限 |
| OrderSendがfalse | ORDER_SEND_AFTER_RESULT | last_error、retcode、comment |
| retcodeが成功系でない | TRADE_RETCODE分類 | 価格、証拠金、SL / TP、Filling Mode |
| 取引履歴に出ない | deal / order / history | 約定・注文番号の確認 |
| バックテストだけ動かない | Testerログ / Expertsログ | モデル、データ不足、初期化失敗 |
バックテスト結果の見方は、MT5でバックテスト結果を相談する前に整理することとも接続して確認してください。
デモ口座・リアル口座で結果が違う時の確認
デモ口座とリアル口座では、スプレッド、約定、取引時間、最小ロット、Filling Mode、銘柄名、手数料、約定拒否の扱いが異なる場合があります。同じEA、同じsetファイルでも、OrderSend結果が変わることがあります。
| 差が出る項目 | 確認する内容 | ログで見る場所 |
|---|---|---|
| スプレッド | デモとリアルのBid / Ask差 | request.price、result.bid、result.ask |
| 約定方式 | Filling Modeや約定拒否 | type_filling、retcode |
| 取引時間 | サーバー時間・休場時間 | MARKET_CLOSED、trade mode |
| 最小ロット | volume min / step | request.volume、INVALID_VOLUME |
| 証拠金条件 | レバレッジ、必要証拠金 | OrderCalcMargin、NO_MONEY |
| 銘柄名 | suffix、銘柄仕様差 | request.symbol、SymbolInfoログ |
| 通信状態 | 接続断、サーバー応答 | CONNECTION、TIMEOUT、last_error |
OrderSend結果ログの出力例
以下はログ出力の例です。実際の数値は、銘柄、口座、価格、ロット、ブローカー、相場環境によって変わります。
[ORDER_SEND_BEFORE_REQUEST] action=1 symbol=GOLD type=0 volume=0.10 price=2350.45000 sl=2345.45000 tp=2358.45000 deviation=20 magic=20260603 type_filling=1 comment=EAFC_ENTRY_BUY
[ORDER_SEND_AFTER_RESULT] send_ok=true retcode=10009 retcode_text=DONE deal=123456789 order=987654321 volume=0.10 price=2350.46000 bid=2350.43000 ask=2350.46000 comment=Done last_error=0
[ORDER_SEND_OK] retcode=10009 retcode_text=DONE deal=123456789 order=987654321 price=2350.46000 volume=0.10ログを見る時は、OrderSend前のrequest.priceと、OrderSend後のresult.price、result.bid、result.askを比較します。価格差、スプレッド、deviation、retcodeを合わせて見ると、約定まわりの原因を確認しやすくなります。
ログ設計と責務分離
ログファースト設計では、ログ出力そのものも責務分離します。
| 責務 | 役割 | ログでの確認例 |
|---|---|---|
| signal | 判定候補を作る | SIGNAL、CANDIDATE、NO_SIGNAL |
| execution | 注文・決済を実行する | ORDER、CLOSE、SEND_FAIL、CLOSE_FAIL |
| risk | ロット、損失、上限、保護条件を扱う | RISK_BLOCK、LOT_INVALID、LIMIT_REACHED |
| exit | 建値、トレール、決済条件を扱う | TRAIL、BE、EXIT_SKIP、EXIT_DONE |
| auth | 利用許可や認証状態を扱う | 公開記事では詳細を扱わない。ログにも機密を出さない |
| external control | 外部設定による動作制御を扱う | 公開記事では詳細を扱わない。新規Entry停止と保護継続は分ける |
| UI / Object | チャート表示、パネル、ラインを扱う | OBJECT_AUDIT、DISPLAY_SKIP、CLEANUP_SUMMARY |
特に重要なのは、判定不成立と執行見送りを混同しないことです。
シグナルが成立していない場合と、シグナル候補はあるがスプレッドや保有上限などで見送った場合では、確認すべき場所が違います。ログでは、この違いをreasonとして残す必要があります。
開発版と配布版でログ粒度を分ける
開発・検証向け構成や開発検証版では、原因調査に必要な詳細ログを出します。一方、購入者向けの利用者向け配布版版では、通常利用に必要な状態、停止理由、エラーを中心にし、過剰な内部情報や大量ログを抑えます。
ログ量を変える場合でも、event名とreason名は可能な限り共通化します。これにより、開発版と配布版のログを比較しやすくなります。URL、token、APIキー、Webhook、口座情報などの実値は、どちらの版でも出力しません。
ログに出してよい情報・出してはいけない情報
ログは問い合わせや検証に使うため、必要な情報を残す一方で、機密情報を出してはいけません。
| 分類 | ログに出してよい例 | ログに出してはいけない例 |
|---|---|---|
| 環境 | symbol、timeframe、digits、point | 口座番号、サーバー名、個人情報 |
| 注文結果 | retcode、action、side、lotの分類 | 実運用set値の詳細、推奨ロット表現 |
| 外部連携 | 接続成功/失敗の分類、masked表示 | endpoint、token、Webhook URL、GAS URL、Sheet ID |
| 認証 | OK / NG / masked summary | 認証ロジック、購入者管理情報、許可判定の詳細 |
| Entry | 見送り理由の分類 | 中核エントリー条件、独自優位性のある判定式 |
| Object | kind、event、visual-only、ignore | 他EAや手動Objectを巻き込む詳細削除方針 |
問い合わせ時にログを共有する場合も、URL、token、口座情報、個人情報が含まれていないか確認してください。
よくある失敗例
失敗1:成功ログしか出していない
注文成功や初期化成功だけをログに出していると、EAが動かない時に原因を追えません。見送り、block、failも理由付きで残す必要があります。
失敗2:毎tick同じログを出している
OnTickで毎回同じPrintを出すと、短時間でログが大量になります。待機状態や同じ見送り理由は、状態変化時または一定間隔で出す方が実用的です。
失敗3:ログ文言が毎回違う
「注文できませんでした」「発注失敗」「Order error」のように表記が揺れると、検索や比較が難しくなります。eventやreasonは固定名にします。
失敗4:ログに機密情報を出している
外部連携や認証を使うEAでは、URL、token、Sheet ID、Webhook URLなどをログに出してはいけません。ログは共有される前提で設計します。
失敗5:ログ出力が売買ロジックへ影響している
ログ出力のON/OFFを変更しただけで売買判定や決済挙動が変わる構造は危険です。判定helper、執行helper、ログhelperは分けて設計します。
Expertsログで確認するポイント
EAの状態を確認する時は、Expertsログの中で次の順に見ます。
- INITログでEAが正しく起動しているか確認する
- RUNTIMEまたはCHECKログで主要状態が安全側になっていないか確認する
- ENTRY_SKIPログで新規エントリー見送り理由を確認する
- ORDERログで注文送信結果を確認する
- CLOSE、EXIT、TRAILなどの管理決済ログを確認する
- OBJECT_AUDITログで表示Objectの残存や無視処理を確認する
- DEINIT周辺で異常終了や終了時の余計な売買処理がないか確認する
MT5のExpertsログとJournalログの見方は、基本確認ページも参考にしてください。
問い合わせ前に送るログ
EAの不具合確認や開発相談を行う場合は、次の情報を整理すると原因を確認やすくなります。
| 送る情報 | 内容 | 注意点 |
|---|---|---|
| EA名・バージョン | 対象ファイル名とバージョン | 旧版と混在していないか確認する |
| 発生時刻 | 問題が起きた日時 | PC時刻、サーバー時刻の違いに注意する |
| symbol・timeframe | 対象銘柄と時間足 | チャートとsetの対象を一致させる |
| Expertsログ | INIT、ENTRY_SKIP、ORDER_FAIL、OBJECT_AUDITなど | 発生前後を含める |
| Journalログ | 端末側エラーや接続状態 | Expertsだけで不足する時に確認する |
| setファイル | 使用した設定 | URL、token、口座情報は削除またはマスクする |
| スクリーンショット | チャート、パネル、ログ画面 | 口座番号などが映っていないか確認する |
不具合相談前の整理は、次のページも参考にしてください。
注文ログの失敗例と共有前の確認
よくある失敗パターン
| 失敗パターン | 問題点 | 対策 |
|---|---|---|
| OrderSendの戻り値だけ見る | retcodeを見落として原因が分からない | result.retcodeとcommentを必ず出す |
| GetLastErrorだけ見る | サーバー側retcodeを見落とす | retcodeとlast_errorを分ける |
| requestをログに出さない | 何を送ったか分からない | OrderSend前にrequest全体を出す |
| resultを成功時だけ出す | 失敗時の原因が追えない | 成功・失敗に関係なくresultを出す |
| ロット正規化前の値を送る | INVALID_VOLUMEになりやすい | volume stepで正規化後に送る |
| SL / TP距離を確認しない | INVALID_STOPSになりやすい | StopLevelと価格方向を確認する |
| Filling Modeを固定する | 銘柄仕様と合わない場合がある | SYMBOL_FILLING_MODEを確認する |
| CTradeでログを省略する | 内部結果を追えない | CTradeのResult系を出す |
| リトライを無制限にする | TOO_MANY_REQUESTSや過剰注文になる | 回数と理由を制限する |
注文エラー調査で提出するとよい情報
OrderSend結果を相談する場合は、結果ログだけでなく、発注前の条件と再現情報をセットで整理してください。
| 整理する情報 | 確認内容 |
|---|---|
| EA名・バージョン | 対象EA、更新日、検証版か通常版か |
| 対象銘柄 | symbol、suffix、GOLD / XAUUSD表記 |
| 時間足・稼働条件 | チャート時間足、内部参照時間足、稼働時間 |
| setファイル | ロット、SL / TP、Magic Number、発注条件 |
| OrderCheckログ | retcode、comment、margin、margin_free |
| OrderSend前requestログ | action、symbol、type、volume、price、sl、tp、filling |
| OrderSend後resultログ | send_ok、retcode、deal、order、comment、last_error |
| SymbolInfoログ | point、digits、volume step、stops level、trade mode |
| Expertsログ・Journalログ | 発生時刻前後のログ |
| 再現条件 | バックテスト、デモ口座、リアル口座のどこで発生したか |
ログを共有する時に伏せる情報
調査依頼や問い合わせでログを共有する場合は、原因確認に必要な情報と、公開・共有すべきではない情報を分けます。OrderSendの原因調査では、retcode、comment、symbol、volume、price、sl、tp、filling、margin、timeなどは役立ちますが、口座番号、氏名、メールアドレス、パスワード、APIキー、Webhook URLなどは伏せてください。
| 分類 | 共有してよい情報の例 | 伏せる情報の例 |
|---|---|---|
| 注文条件 | symbol、type、volume、price、sl、tp、deviation | 口座番号、氏名、メールアドレス |
| 結果ログ | send_ok、retcode、comment、deal/orderの一部マスク | 完全な注文番号や個人を特定できる情報 |
| EA設定 | 検証に必要なパラメータ、Magic Numberの一部 | ライセンスキー、認証情報、Webhook URL |
| 環境情報 | バックテスト、デモ、リアルの区分、銘柄、時間帯 | サーバー接続情報やログイン情報 |
OrderSend結果ログの実務チェック表
- OrderSend前にMqlTradeRequestをログ出力している
- OrderSend後にMqlTradeResultをログ出力している
- OrderSendの戻り値とresult.retcodeを分けて確認している
- result.commentとGetLastErrorを両方出している
- deal、order、price、volumeを成功時に確認している
- retcodeを文字列化してログを読みやすくしている
- OrderCheckログとOrderSendログを分けている
- OrderCalcMarginで必要証拠金を確認している
- SymbolInfo系でvolume step、StopLevel、Filling Modeを確認している
- CTrade利用時もResultRetcodeなどを確認している
- バックテスト、デモ口座、リアル口座の差を分けて確認している
- 口座番号、パスワード、APIキー、Webhook URLなどをログ共有時に伏せている
ログファースト設計を開発依頼に活かす
EA開発を依頼する時も、ログ仕様を最初に決めておくと、完成後の確認がしやすくなります。
開発依頼前には、次のようなログ方針を整理しておくと実務上有効です。
- 起動時に何を確認したいか
- エントリー見送り理由をどこまで出したいか
- 注文失敗時にretcodeを出すか
- 決済失敗や変更失敗をどの粒度で出すか
- チャートObjectの残存や再セットを監査するか
- 長時間稼働時にログ過多を防ぐか
- 問い合わせ時に送るログ項目を決めるか
仕様書、setファイル、ログ、スクリーンショットを整理する場合は、次のページも確認してください。
MT5開発依頼前に用意する資料まとめ|仕様書・setファイル・ログ・スクリーンショット
公開しない情報
この記事では、MQL5のログ設計を学ぶために、公開可能な範囲だけを一般化して解説しています。以下は公開しません。
- 完成EAソース全文
- 外部シート参照による動作制御ロジック
- 外部シートCSV取得、解析、時間枠、方向制御ロジック
- 認証、口座許可判定、購入者管理ロジック
- endpoint、token、URL、Google Sheet ID、Webhook URL、GAS URL
- APIキー、認証トークン、口座番号、サーバー名
- 実運用set値
- 中核エントリー条件
- 独自優位性のある売買判定式
- 利益保証、勝率保証、推奨ロット、推奨銘柄、推奨エントリー
本記事は、投資判断や売買指示ではなく、MQL5開発におけるログ設計と検証手順を理解するための技術解説です。
よくある質問
このコードは完成EAとして使えますか?
いいえ。この記事のコード例は、MQL5のログ設計を理解するために簡略化した学習用コードです。完成EAとしてそのまま使うことは想定していません。
実際のEAソース全文は公開されていますか?
公開していません。完成EAソース全文、外部シート制御、認証、endpoint、token、URL、実運用set値、中核エントリー条件は公開対象外です。
EAが動かない時は、どのログを見ればよいですか?
まずINITログで起動状態を確認し、次にENTRY_SKIP、ORDER、CLOSE、OBJECT_AUDITなどを確認します。見送りなのか、注文失敗なのか、表示Objectの問題なのかを分けて見ることが重要です。
ENTRY_SKIPログが出ている場合は、エントリー条件が悪いという意味ですか?
必ずしもそうではありません。ENTRY_SKIPは、新規エントリー候補があっても、スプレッド、保有上限、同一バー制御、リスク制御などで見送る場合にも出ます。reasonを確認してください。
ORDER_FAILログが出た場合は何を確認しますか?
retcode、GetLastError、symbol、lot、注文種別、AutoTradingの状態、証拠金、ブローカー側の制約などを確認します。単に「注文失敗」と見るのではなく、戻り値と理由を分けて確認します。
ログは多いほどよいですか?
いいえ。重要な失敗ログは残すべきですが、同じ待機ログや同じ見送り理由を毎tick出すと、確認しにくくなります。状態変化時だけ出す、または一定間隔で抑制する設計が有効です。
問い合わせ時にログをそのまま送ってよいですか?
送る前に、URL、token、Webhook URL、GAS URL、Google Sheet ID、口座番号、個人情報が含まれていないか確認してください。必要に応じてマスクしたうえで共有してください。
この記事は売買判断や推奨設定を示すものですか?
いいえ。この記事はMQL5開発におけるログ設計の技術解説です。売買判断、推奨ロット、推奨銘柄、利益保証、勝率保証を行うものではありません。
OrderSendがtrueなら注文成功ですか?
必ずしもそうとは限りません。OrderSendの戻り値は送信処理の成否を示します。注文結果はMqlTradeResultのretcode、deal、order、commentを確認してください。
GetLastErrorだけ見れば原因は分かりますか?
GetLastErrorは端末側・関数呼び出し側のエラー確認に使いますが、取引サーバー側の結果はresult.retcodeとresult.commentを見る必要があります。両方をログに出してください。
CTradeを使っている場合もMqlTradeResultを見る必要がありますか?
必要です。CTradeを使う場合も、ResultRetcode、ResultRetcodeDescription、ResultDeal、ResultOrder、ResultPriceなどを確認してください。ラッパーを使っていても、注文結果の確認は省略しない方が安全です。
INVALID_STOPSが出る時は何を確認しますか?
SL / TP価格の方向、StopLevel、FreezeLevel、point、digits、現在価格との距離を確認します。SymbolInfoIntegerやSymbolInfoDoubleで銘柄仕様を取得し、ログに残してください。
NO_MONEYが出る時は何を確認しますか?
ロット、必要証拠金、余剰証拠金、レバレッジ、口座通貨、銘柄仕様を確認します。OrderCalcMarginとOrderCheckのmargin、margin_freeを合わせて見ると確認しやすくなります。
バックテストでは成功するのにリアル口座で失敗することはありますか?
あります。リアル口座では、スプレッド、約定、取引時間、Filling Mode、通信、銘柄仕様、ブローカー制限などが影響します。バックテスト、デモ、リアルでログを分けて確認してください。
ログを問い合わせで共有する時に注意することはありますか?
あります。retcode、comment、requestの主要項目、発生時刻、銘柄、バックテスト・デモ・リアルの区分は原因確認に役立ちます。一方で、口座番号、氏名、メールアドレス、パスワード、APIキー、Webhook URL、ライセンスキーなどは伏せてください。
関連する確認ページ
確認したい内容に応じて、次のページを参照してください。本文中ですでに案内したページは、ここでは重ねて掲載していません。
あわせて確認したいページ
今回の確認内容に関連する補足ページです。必要な項目だけ確認してください。
| 確認目的 | 関連ページ |
|---|---|
| 読後の任意の手続き・一覧入口。本文解決や購入の必須前提にしない | MT4/MT5 技術講座 |
| 「MQL5 ログ設計 実装例」の読後に「MQL5エラーコード辞典」を補足確認 | MQL5エラーコード辞典 |
| 「MQL5 ログ設計 実装例」の読後に「MQL5 Multi-Logic EA設計を実コードで解説」を補足確認 | MQL5 Multi-Logic EA設計を実コードで解説 |
まとめ
MQL5 EAのログ設計では、Printを増やすことよりも、後から追える形でログを設計することが重要です。
特に、次の4点を分けておくと、不具合確認や保守がしやすくなります。
- event:何が起きたか
- side:BUY、SELL、NONEのどれか
- reason:なぜ起きたか
- detail:確認に必要な補足情報
EAが動かない時に重要なのは、単に「動かない」と判断することではなく、起動、判定、見送り、発注、決済、表示、終了処理のどこで止まっているかを分けて確認することです。
ログファースト設計は、開発者だけでなく、EAを使う側、検証する側、サポートを受ける側にも役立ちます。完成EAの中核ロジックを公開しなくても、ログ設計の考え方を理解しておくことで、問題発生時の確認がしやすくなります。
お問い合わせはEAFC公式LINEへ
EAFCサイト内の記事、商品、導入前確認、設定確認、ログ確認、開発依頼前のご相談など、各種お問い合わせはEAFC公式LINEより受け付けています。
※投資助言、売買指示、利益保証、推奨ロット、推奨銘柄の案内は行っておりません。
口座番号、認証情報、Webhook URL、GAS URL、APIキーなどの機密情報は送信しないでください。
利用条件・相談窓口
| 確認内容 | 案内ページ |
|---|---|
| MT5 開発依頼・作成代行|EA・インジケーター新規開発・MT4/MT5既存改修 | 開発・改修の相談ページを見る |
| よくある質問|EA・インジケーター・開発依頼・サポートFAQ | よくある質問 |
| 購入前確認事項 | ご購入前の確認事項 |
| 免責事項・リスク説明 | 免責事項 |
| 投資助言を行わない方針 | 投資助言を行わない方針 |
