MQL5外部連携EA実務ノート|WebRequest・JSON・Webhookを安全に扱う設計
MQL5 EAでは、WebRequest、JSON、Webhookなどを使って、外部サービスと連携する場面があります。
たとえば、外部通知、稼働状態の送信、設定確認、運用メモの取得、Discord通知、検証ログの送信などです。外部連携を使うと便利ですが、設計を間違えると、通信失敗でEA本体が止まる、tokenやURLがログへ出る、通知失敗を売買失敗と誤解する、外部応答遅延でOnTickが重くなる、といった問題が起きます。
外部連携EAで重要なのは、通信処理を売買ロジックへ直接混ぜないことです。signal、execution、risk、exit、auth、external control、notificationを分け、通信失敗時にどの処理を止めるのか、どの処理を継続するのかを明確にします。
この記事では、MQL5 EAでWebRequest、JSON、Webhookを安全に扱うための設計判断を、開発実務ノートとして整理します。完成EAソース全文、外部シート制御ロジック、認証ロジック、endpoint、token、URL実値、実運用set値、中核Entry条件は公開しません。
本記事は、投資判断や売買指示ではなく、外部連携を含むMQL5 EAを安全に設計・検証・サポートするための技術記事です。
- この記事で扱う実務課題
- なぜ外部連携EAでは責務分離が重要なのか
- 外部連携で分けるべき責務
- WebRequestを使う時に決めること
- JSON応答を扱う時の注意点
- Webhook通知を売買処理と分ける理由
- 通信失敗時は安全側で扱う
- 実コード例1:外部通信結果を構造体で扱う
- 実コード例2:外部通信ログは機密情報をマスクする
- 実コード例3:外部状態を新規Entryと既存管理へ分けて扱う
- 実コード例4:通知失敗を売買失敗と分ける
- 外部連携でログに残すべき情報
- 外部通信をOnTickへ直接詰め込まない
- OnTimerで通信・通知・定期記録を分担する
- 外部制御と認証を混同しない
- よくある失敗例
- 導入前・開発依頼前に整理する情報
- 定期処理の依頼前にそろえる条件
- よくある質問
- 関連する確認ページ
- あわせて確認したいページ
- まとめ
- 利用条件・相談窓口
- お問い合わせはEAFC公式LINEへ
この記事で扱う実務課題
| 項目 | この記事で扱う内容 | 扱わない内容 |
|---|---|---|
| 目的 | WebRequest、JSON、WebhookをEAで安全に扱う設計 | 利益を狙う売買ロジック、具体的なEntry条件 |
| 主な対象 | 外部通信、通知、レスポンス確認、通信失敗時の安全側処理 | 完成EAソース全文、実運用コード全文 |
| 設計観点 | 責務分離、ログ設計、タイムアウト、通信抑制、機密情報マスク | 認証ロジック、外部シート制御、実運用set値 |
| コード例 | 一般化した通信結果構造、ログ例、通知分離、通信失敗時の扱い | 実endpoint、token、URL、GAS URL、Webhook URL |
| 読者 | MQL5開発者、外部通知EAを作りたい人、検証担当者、販売前チェック担当者 | 売買判断や推奨設定を知りたい人 |
なぜ外部連携EAでは責務分離が重要なのか
外部連携をEAへ追加すると、EA本体の処理に通信という不安定要素が入ります。
通信は、EA内部だけでは完結しません。回線、MT5のWebRequest許可設定、外部サーバーの応答、タイムアウト、証明書、payload形式、HTTPステータス、サービス側の制限など、多くの要因に影響されます。
| 問題 | 起きる原因 | 分けるべき責務 |
|---|---|---|
| EAが重くなる | OnTick内で毎回通信している | external communication / runtime |
| 通知が届かない | Webhook設定不備、通信失敗、送信抑制 | notification |
| 売買まで止まる | 通知失敗と売買処理を同じ失敗として扱っている | execution / notification |
| ログに機密情報が出る | endpointやtokenをそのままPrintしている | log / secret management |
| 外部停止の範囲が曖昧 | 新規Entry停止と既存ポジション管理を混同している | external control / exit |
| 問い合わせ時に確認できない | HTTP結果、reason、detailがログにない | log design |
外部連携は便利ですが、EA本体の売買判定や決済処理と密結合にすると、通信失敗がそのまま売買不具合のように見えてしまいます。
外部連携で分けるべき責務
外部連携EAでは、少なくとも次の責務を分けて設計します。
| 責務 | 役割 | 混同すると起きる問題 |
|---|---|---|
| signal | Entry候補を作る | 外部通信失敗をシグナル不成立と誤解する |
| execution | OrderSend、決済、modifyを実行する | 通知失敗と注文失敗を混同する |
| risk | ロット、保有上限、損失制御を扱う | 外部状態と内部リスクを混同する |
| exit | 既存ポジション管理を行う | 新規Entry停止時に保護決済まで止めてしまう |
| auth | 利用許可や認証状態を扱う | 認証NG時の停止範囲が曖昧になる |
| external control | 外部状態による動作制御を扱う | 通信失敗時に売買全体の扱いが不明になる |
| notification | Discord通知や外部通知を扱う | 通知失敗が売買失敗に見える |
| log | 通信結果、reason、detailを記録する | 問い合わせ時に原因を追えない |
特に重要なのは、notificationとexecutionを分けることです。通知が失敗しても、注文自体は成功している場合があります。逆に、注文が失敗しても通知だけ成功している場合もあります。
WebRequestを使う時に決めること
WebRequest()は同期関数で、EAとスクリプトから利用できますがインジケーターから直接呼び出すことはできません。またStrategy Testerでは実行されません。外部通信はOnTickの売買判定へ直結させず、timeout・失敗時の既定動作・再試行・ログ・機密情報マスクを別責務として設計します。
MQL5でWebRequestを使う場合、実装前に次の方針を決めておく必要があります。
| 確認項目 | 設計判断 |
|---|---|
| 通信タイミング | OnTickごとか、OnTimerか、状態変化時か |
| 通信頻度 | 秒単位で抑制するか、イベント発生時だけにするか |
| 失敗時の扱い | 新規Entryを止めるか、通知だけ失敗扱いにするか |
| タイムアウト | 応答遅延時にEA本体を止めないか |
| ログ粒度 | HTTP status、reason、masked detailをどこまで出すか |
| 機密情報 | endpoint、token、URLをログや画面に出さないか |
| 外部依存範囲 | 外部通信が失敗しても既存ポジション管理を続けるか |
外部通信は、OnTick内で無制限に呼ばない方が安全です。状態変化時、OnTimer、または一定間隔のsummary処理へ分けることで、EA本体の安定性を保ちやすくなります。
JSON応答を扱う時の注意点
外部サービスからJSON形式の応答を受け取る場合、文字列が返ってきたことと、EAが使える状態であることは別です。
JSON応答では、次のような確認が必要です。
| 確認項目 | 確認する理由 |
|---|---|
| 空応答ではないか | 通信は成功しても中身が空の場合がある |
| 期待するキーがあるか | 形式変更やエラー応答を検出するため |
| 値の型が想定どおりか | true / false、数値、文字列の誤解釈を防ぐため |
| 時刻や有効期限があるか | 古い応答を使わないため |
| 機密情報を含めていないか | ログへ出す前にマスクするため |
| 失敗応答を成功扱いしていないか | HTTP成功でも業務上NGの可能性があるため |
JSONを受け取ったら、そのまま売買判断へ使うのではなく、通信結果、形式確認、値の妥当性、外部状態の適用範囲を分けて扱います。
Webhook通知を売買処理と分ける理由
Webhook通知は、EAの状態や注文結果を外部へ知らせるために便利です。
ただし、通知は売買処理そのものではありません。通知処理と売買処理を混同すると、次のような問題が起きます。
| 混同例 | 起きる問題 |
|---|---|
| 通知成功を注文成功として扱う | 実際のOrderSend結果とずれる |
| 通知失敗でEA全体を止める | 注文や保護決済まで止まる可能性がある |
| Webhook URLをログへ出す | 通知先が漏れる |
| 毎tick通知する | 通知過多、外部サービス制限、ログ過多になる |
| 通知payloadに口座情報を入れる | 個人情報や機密情報の漏えいにつながる |
Webhook通知は、notification層として分け、送信成功・失敗をログで追えるようにします。通知失敗時に売買処理を止めるかどうかは、仕様として明確にします。
通信失敗時は安全側で扱う
外部連携EAでは、通信失敗時の扱いを最初に決めておく必要があります。
安全側の基本方針は、外部状態が不明な場合、無理に新規Entryへ進まないことです。一方で、既存ポジション管理まで止めるかどうかは慎重に判断します。
| 状態 | 新規Entry | 既存ポジション管理 | 理由 |
|---|---|---|---|
| 外部通信OK | 外部状態に従う | 通常どおり継続 | 外部状態が確認できるため |
| 外部通信失敗 | 停止または前回状態の扱いを仕様化 | 原則として保護処理は継続検討 | 新規Entryと保護処理は責務が違うため |
| 外部応答が不正 | 停止候補 | 必要な保護処理は継続検討 | 不正な外部値を使わないため |
| 通知だけ失敗 | 売買可否とは分ける | 売買可否とは分ける | 通知と注文実行は別責務のため |
「通信できないからEA全体を止める」のか、「新規Entryだけ止める」のか、「一定時間だけ前回状態を使う」のかは、EAの目的とリスク方針によって異なります。重要なのは、仕様として曖昧にしないことです。
実コード例1:外部通信結果を構造体で扱う
以下は、設計判断を理解するために一般化した学習用コード例です。完成EAのソース全文ではありません。実endpoint、token、URLは含めていません。
// 学習用に一般化した外部通信結果の例です。
// 実運用EAの完成ソースではありません。
enum ExternalStatus
{
EXT_STATUS_UNKNOWN = 0,
EXT_STATUS_OK,
EXT_STATUS_FAIL,
EXT_STATUS_TIMEOUT,
EXT_STATUS_INVALID_RESPONSE
};
struct ExternalFetchResult
{
ExternalStatus status;
int httpStatus;
string reason;
string maskedDetail;
datetime checkedAt;
};
ExternalFetchResult MakeExternalFail(string reason, string detail)
{
ExternalFetchResult result;
result.status = EXT_STATUS_FAIL;
result.httpStatus = 0;
result.reason = reason;
result.maskedDetail = detail;
result.checkedAt = TimeCurrent();
return result;
}外部通信の結果は、true / falseだけで扱うより、status、httpStatus、reason、maskedDetail、checkedAtを分けると確認しやすくなります。
特に、通信失敗、タイムアウト、形式不正、外部応答NGは別のreasonとして扱うべきです。
実コード例2:外部通信ログは機密情報をマスクする
以下は、設計判断を理解するために一般化した学習用コード例です。完成EAのソース全文ではありません。
// 学習用に一般化した外部通信ログ例です。
// 実運用EAの完成ソースではありません。
string MaskExternalText(string value)
{
string lower = StringToLower(value);
if(StringFind(lower, "token") >= 0)
return "masked";
if(StringFind(lower, "endpoint") >= 0)
return "masked";
if(StringFind(lower, "webhook") >= 0)
return "masked";
return value;
}
void WriteExternalLog(string eventName,
string reason,
const ExternalFetchResult &result)
{
Print("DIAG/EXTERNAL: event=", eventName,
" reason=", reason,
" status=", (int)result.status,
" http=", result.httpStatus,
" detail=", MaskExternalText(result.maskedDetail));
}外部連携ログでは、endpoint、token、Webhook URL、GAS URL、APIキー、口座番号、顧客情報を出さないことが重要です。
ログに必要なのは、実値ではなく、原因調査に使える分類です。たとえば、FETCH_FAIL、TIMEOUT、INVALID_RESPONSE、HTTP_STATUS_NGのように、reasonで整理します。
実コード例3:外部状態を新規Entryと既存管理へ分けて扱う
以下は、設計判断を理解するために一般化した学習用コード例です。完成EAのソース全文ではありません。外部シート制御や認証処理は含めていません。
// 学習用に一般化した外部状態の適用例です。
// 実運用EAの完成ソースではありません。
struct ExternalRuntimeState
{
bool externalReady;
bool allowNewEntry;
bool allowNotification;
string reason;
};
ExternalRuntimeState BuildExternalRuntimeState(const ExternalFetchResult &fetch)
{
ExternalRuntimeState state;
state.externalReady = false;
state.allowNewEntry = false;
state.allowNotification = false;
state.reason = "INIT";
if(fetch.status != EXT_STATUS_OK)
{
state.reason = "EXTERNAL_NOT_READY";
return state;
}
state.externalReady = true;
state.allowNewEntry = true;
state.allowNotification = true;
state.reason = "EXTERNAL_OK";
return state;
}
void ApplyExternalStateToEntry(const ExternalRuntimeState &state)
{
if(!state.allowNewEntry)
{
Print("DIAG/ENTRY: event=SKIP",
" reason=", state.reason,
" scope=NEW_ENTRY_ONLY");
return;
}
Print("DIAG/ENTRY: event=EXTERNAL_GATE_PASS",
" reason=", state.reason);
}この例では、外部状態を新規Entryの許可判断へ反映する考え方を示しています。ただし、既存ポジション管理や保護決済まで同時に止めるとは限りません。
新規Entry停止と既存ポジション管理は、必ず分けて設計します。
実コード例4:通知失敗を売買失敗と分ける
以下は、設計判断を理解するために一般化した学習用コード例です。完成EAのソース全文ではありません。実Webhook URLやpayloadは含めていません。
// 学習用に一般化した通知結果の扱い例です。
// 実運用EAの完成ソースではありません。
struct NotifyResult
{
bool sent;
string reason;
datetime sentAt;
};
NotifyResult SendNotificationSafe(string eventName, string messageSummary)
{
NotifyResult result;
result.sent = false;
result.reason = "NOT_SENT";
result.sentAt = TimeCurrent();
if(eventName == "")
{
result.reason = "EMPTY_EVENT";
return result;
}
// 実際のEAでは、ここで通知送信処理を別責務として扱います。
// 実Webhook URL、token、payload詳細はこの記事では扱いません。
result.sent = true;
result.reason = "NOTIFY_SEND_OK";
return result;
}
void TraceNotifyResult(const NotifyResult &result)
{
if(result.sent)
{
Print("DIAG/NOTIFY: event=SEND_OK",
" reason=", result.reason);
}
else
{
Print("DIAG/NOTIFY: event=SEND_FAIL",
" reason=", result.reason);
}
}通知処理は、notification層として分けます。通知失敗は、通知失敗としてログに残し、注文実行や決済結果と混同しないようにします。
外部連携でログに残すべき情報
外部連携の不具合調査では、実値ではなく、分類された状態が重要です。
| ログ項目 | 残す内容 | 出してはいけない内容 |
|---|---|---|
| event | FETCH_START、FETCH_OK、FETCH_FAIL、SEND_OK、SEND_FAIL | 実endpoint、実Webhook URL |
| reason | TIMEOUT、HTTP_STATUS_NG、INVALID_RESPONSE | token、APIキー |
| httpStatus | HTTPステータス番号 | 認証ヘッダー実値 |
| scope | NEW_ENTRY_ONLY、NOTIFICATION_ONLY、LOG_ONLY | 口座番号、顧客情報 |
| maskedDetail | マスク済みの要約 | GAS URL、Google Sheet ID、Webhook URL |
| checkedAt | 確認時刻 | 個人情報、購入者情報 |
外部連携では、ログが詳しすぎても危険です。問い合わせ時に共有される可能性を前提に、実値ではなく、分類・状態・reasonを残します。
外部通信をOnTickへ直接詰め込まない
外部通信は、OnTickへ直接詰め込まない方が安全です。
OnTickは価格更新ごとに呼ばれます。ここで毎回WebRequestを実行すると、EA全体が重くなり、バックテストやリアル運用でも不安定になりやすくなります。
| 避けたい設計 | 推奨する設計 |
|---|---|
| OnTickごとにWebRequestする | OnTimer、状態変化時、一定間隔へ分離する |
| 通信結果を即OrderSendへ直結する | ExternalRuntimeStateとして整理してからGATEへ渡す |
| 通信失敗でEA全体を停止する | 新規Entry、既存管理、通知の停止範囲を分ける |
| 通知成功を売買成功として扱う | ORDER_RESULTとNOTIFY_RESULTを分ける |
| 外部応答をそのままログへ出す | 必要項目だけマスクして出す |
外部通信は、EAの補助機能または外部状態取得として設計し、売買判定や注文実行と直接混ぜないことが重要です。
OnTimerで通信・通知・定期記録を分担する
以下のコード例は、個別の処理と確認項目を読むためのものです。複数の例をそのまま一つのEAへ連結する前提ではありません。初期化、値の取得、状態更新、終了処理をそれぞれの例で区別して確認してください。
OnTimerとは何か
OnTimerは、Timerイベントが発生した時に呼ばれるイベント関数です。
EAやインジケーターでタイマー処理を使う場合、通常はOnInitでEventSetTimerを呼び、指定した秒数ごとにOnTimerを実行します。
int OnInit()
{
if(!EventSetTimer(10))
{
PrintFormat("[TIMER_SET_NG] seconds=10 last_error=%d", GetLastError());
return INIT_FAILED;
}
Print("[TIMER_SET_OK] seconds=10");
return INIT_SUCCEEDED;
}
void OnTimer()
{
Print("[TIMER_TICK] periodic task");
}
void OnDeinit(const int reason)
{
EventKillTimer();
PrintFormat("[TIMER_KILL] reason=%d", reason);
}OnTimerを使う場合は、開始、実行、終了をログで確認できるようにしてください。
OnTickとOnTimerの役割を分ける
EA開発では、OnTickとOnTimerの責務を混同しないことが重要です。
OnTickは価格変動に応じた売買判定や注文処理に向いています。一方、OnTimerは、一定間隔でよい処理、外部連携、状態監視、ログ抑制、通知再送などに向いています。
| イベント | 主な役割 | 入れやすい処理 |
|---|---|---|
| OnTick | 価格変動に応じた処理 | signal判定、entry gate、発注前チェック、注文処理、決済判定 |
| OnTimer | 一定間隔の処理 | 外部制御取得、通知送信、CSV出力、稼働監視、ログ抑制、状態同期 |
| OnInit | 初期化処理 | input確認、timer設定、handle生成、初期ログ |
| OnDeinit | 終了処理 | timer停止、handle解放、終了ログ |
外部連携や通知処理をOnTickに直接入れると、tickが多い時間帯に処理回数が増えすぎる場合があります。定期処理でよいものはOnTimerへ分離してください。
OnTimerに向いている処理
OnTimerは、毎tickで実行する必要がない処理に向いています。
特に、外部連携、通知、状態監視、ログ抑制、定期スナップショットは、OnTimerへ分けると構造が分かりやすくなります。
| 処理 | OnTimerに向く理由 | 確認ログ |
|---|---|---|
| Discord通知 | tickごとに送る必要がなく、送信間隔を制御しやすい | NOTIFY_SEND / NOTIFY_SKIP |
| Google Sheets連携 | 定期的な状態送信や集計送信に向いている | SHEET_WRITE / SHEET_THROTTLE |
| 外部制御取得 | 外部シートやAPIから一定間隔で設定を読む場合に向いている | EXTERNAL_FETCH / EXTERNAL_APPLY |
| CSV出力 | tickごとではなく、一定間隔やイベント後の記録に向いている | CSV_WRITE / CSV_SKIP |
| 状態監視 | 口座状態、ポジション状態、稼働状態を周期的に確認できる | RUNTIME_SNAPSHOT |
| ログ抑制 | 同じ状態を毎tick出さず、一定間隔で要約できる | LOG_THROTTLE / TIMER_SUMMARY |
ただし、OnTimerに重い処理を入れすぎると、タイマーイベントの処理自体が詰まる場合があります。処理時間、通信失敗時の戻り、再送制御を設計してください。
OnTimerに入れすぎない方がよい処理
OnTimerは便利ですが、すべての処理を移す場所ではありません。
価格変動に即応する処理や、発注・決済の中心処理は、OnTickや専用のexecution処理へ分けた方が確認しやすくなります。
| OnTimerに入れすぎない処理 | 理由 | 推奨する分離 |
|---|---|---|
| 毎tick必要なentry判定 | Timer間隔中の価格変化を見落とす場合がある | OnTick / signal層 |
| 発注処理そのもの | 外部連携や通知と注文責務が混ざる | execution層へ分離 |
| 重いWebRequestの連続実行 | 通信失敗時に処理が詰まりやすい | throttle、timeout、queue管理 |
| 大量CSV出力 | ファイルI/Oが増え、BTや長時間稼働で重くなる | イベント時出力、要約出力 |
| 毎秒大量ログ | Expertsログが膨らみ、確認しづらくなる | ログ間隔、状態変化時のみ出力 |
OnTimerは、EA本体の売買ロジックを置き換えるものではありません。定期処理、外部処理、補助処理を整理するために使います。
EventSetTimerの基本
EventSetTimerは、Timerイベントの発生間隔を秒単位で設定します。
通常はOnInitで一度だけ呼び出し、失敗した場合はGetLastErrorをログに残します。
input int InpTimerSeconds = 10;
bool SetupTimer()
{
if(InpTimerSeconds <= 0)
{
PrintFormat("[TIMER_CONFIG_NG] seconds=%d reason=must_be_positive", InpTimerSeconds);
return false;
}
ResetLastError();
if(!EventSetTimer(InpTimerSeconds))
{
PrintFormat("[TIMER_SET_NG] seconds=%d last_error=%d",
InpTimerSeconds,
GetLastError());
return false;
}
PrintFormat("[TIMER_SET_OK] seconds=%d", InpTimerSeconds);
return true;
}
int OnInit()
{
if(!SetupTimer())
return INIT_FAILED;
return INIT_SUCCEEDED;
}タイマー間隔は短くしすぎないようにしてください。外部連携や通知では、5秒、10秒、30秒、60秒など、用途に応じて十分な間隔を取る方が扱いやすくなります。
EventKillTimerの基本
OnInitでEventSetTimerを使った場合は、OnDeinitでEventKillTimerを呼びます。
EA削除、時間足変更、再コンパイル、チャート終了などの場面で、終了ログとあわせてTimerを停止しておくと確認しやすくなります。
void OnDeinit(const int reason)
{
EventKillTimer();
PrintFormat("[DEINIT_TIMER_KILL] reason=%d", reason);
}OnDeinitでは、Timer停止、handle解放、必要最小限の終了ログに留める設計が安全です。広範囲のObject削除や重い通信処理を終了時に詰め込みすぎないようにしてください。
ミリ秒タイマーの扱い
EventSetMillisecondTimerを使うと、1秒未満の間隔でTimerイベントを発生させることができます。
ただし、短い間隔でOnTimerを呼び出すほど、処理回数が増え、バックテスト時間や端末負荷に影響します。通常の通知、外部連携、状態監視では、秒単位のEventSetTimerで十分な場合が多いです。
| 方式 | 用途 | 注意点 |
|---|---|---|
| EventSetTimer | 秒単位の定期処理 | 通常の通知、外部連携、状態監視に向く |
| EventSetMillisecondTimer | 1秒未満の高頻度処理 | 処理負荷やBT時間が増えやすい |
高頻度タイマーは、必要性が明確な場合だけ使ってください。ログ確認や外部連携目的で安易に短い周期を設定すると、検証しづらくなる場合があります。
外部連携処理をOnTimerへ分ける
WebRequest、Discord通知、Google Sheets連携などは、OnTimerで周期管理すると整理しやすくなります。
OnTick側では、売買判定や内部状態の更新だけを行い、外部へ送る必要がある情報をキューや状態変数として保持します。OnTimer側で、送信間隔、失敗回数、再送可否を確認してから外部連携を実行します。
datetime g_last_external_sync = 0;
input int InpExternalSyncSeconds = 60;
bool CanRunExternalSync()
{
datetime now = TimeCurrent();
if(g_last_external_sync > 0 && (now - g_last_external_sync) < InpExternalSyncSeconds)
{
PrintFormat("[EXTERNAL_SYNC_SKIP] reason=throttle wait_sec=%d",
InpExternalSyncSeconds - (int)(now - g_last_external_sync));
return false;
}
g_last_external_sync = now;
return true;
}
void OnTimer()
{
if(!CanRunExternalSync())
return;
RunExternalSync();
}外部連携は、失敗しても売買ロジックを直接止めない設計にすることが多くあります。ただし、外部シートによる売買停止など、取引制御に関係する場合は、影響範囲を明確にしてください。
WebRequestをOnTimerで扱う時の注意点
WebRequestを使う場合は、URL許可、timeout、HTTPステータス、戻り値、レスポンス内容、失敗時の扱いを分けて確認します。
WebRequestが失敗した時に、EAの注文処理まで止めるのか、外部連携だけ停止するのか、次回OnTimerで再試行するのかを仕様として決めてください。
| 確認項目 | 内容 | ログ例 |
|---|---|---|
| URL許可 | MT5のオプションでWebRequest許可URLに入っているか | WEBREQUEST_URL_CHECK |
| timeout | 通信待ち時間を長くしすぎていないか | WEBREQUEST_TIMEOUT |
| HTTPステータス | 200、400、403、500などを確認する | WEBREQUEST_STATUS |
| レスポンス | JSON、CSV、テキストなどを解析できるか | WEBREQUEST_PARSE |
| 再試行 | 失敗時にすぐ連続送信しないか | WEBREQUEST_RETRY_SKIP |
| 売買への影響 | 外部連携失敗時にentryを止めるかどうか | EXTERNAL_CONTROL_STATE |
外部連携失敗をすべて注文失敗として扱うと原因が分かりにくくなります。通信失敗、解析失敗、外部制御による停止、EA内部のentry gateは分けてログ化してください。
Discord通知をOnTimerで送る考え方
Discord通知は、状態変化やイベント発生時に送ることが多い処理です。
ただし、同じ状態を毎tick送ると通知が多くなりすぎます。OnTimer側で送信間隔や重複通知を制御すると、運用しやすくなります。
datetime g_last_notify_time = 0;
string g_last_notify_key = "";
bool CanNotify(const string notify_key, const int min_interval_sec)
{
datetime now = TimeCurrent();
if(g_last_notify_key == notify_key && (now - g_last_notify_time) < min_interval_sec)
{
PrintFormat("[NOTIFY_SKIP] key=%s reason=duplicate_interval", notify_key);
return false;
}
g_last_notify_key = notify_key;
g_last_notify_time = now;
return true;
}
void SendTimerNotification()
{
string key = "runtime_alive";
if(!CanNotify(key, 300))
return;
Print("[NOTIFY_SEND] key=runtime_alive");
}通知処理では、通知対象、送信間隔、失敗時の再送、Webhook URLの管理、ログの出し方を分けて設計してください。
Google Sheets連携をOnTimerで扱う考え方
Google Sheets連携では、EAの状態、ポジション数、外部制御、稼働状態、検証メモなどを一定間隔で読み書きすることがあります。
このような処理は、tickごとではなく、OnTimerで間隔を決めて実行する方が扱いやすくなります。
| 処理 | OnTimerで確認すること | ログ例 |
|---|---|---|
| 外部制御取得 | 停止フラグ、BUY/SELL許可、稼働時間など | SHEET_FETCH_CONTROL |
| 状態送信 | EA稼働状態、ポジション数、最終更新時刻 | SHEET_WRITE_STATUS |
| エラー記録 | 通信失敗、解析失敗、認証失敗 | SHEET_ERROR |
| 送信間隔制御 | 短時間で連続送信しない | SHEET_THROTTLE |
| 取引への影響 | 外部制御がentry gateに影響するか | EXTERNAL_GATE_STATE |
外部シートを使ってEAの動作制御を行う場合は、OnTimerで取得した制御値をruntime状態へ反映し、OnTick側ではその状態を参照する設計にすると責務を分けやすくなります。
状態スナップショットをOnTimerで出す
OnTimerは、EAの稼働状態を一定間隔でログ化する用途にも向いています。
毎tickログを出すと確認しづらくなるため、状態が変わった時、または一定間隔ごとに要約ログを出す設計が有効です。
void PrintRuntimeSnapshot()
{
int positions = PositionsTotal();
double equity = AccountInfoDouble(ACCOUNT_EQUITY);
double margin = AccountInfoDouble(ACCOUNT_MARGIN);
double margin_free = AccountInfoDouble(ACCOUNT_MARGIN_FREE);
PrintFormat("[RUNTIME_SNAPSHOT] symbol=%s positions=%d equity=%.2f margin=%.2f margin_free=%.2f",
_Symbol,
positions,
equity,
margin,
margin_free);
}
void OnTimer()
{
PrintRuntimeSnapshot();
}実際のEAでは、毎回すべての情報を出すのではなく、ログプロファイルやdebug設定に応じて出力範囲を切り替えると運用しやすくなります。
バックテストでOnTimerを使う時の注意点
バックテストでOnTimerを使う場合、Timerイベントの回数が多くなるとテスト時間が伸びることがあります。
特に、1秒タイマーやミリ秒タイマーで詳細ログ、WebRequest相当の処理、CSV出力、状態スナップショットを多く出すと、検証が重くなります。
| 注意点 | 問題 | 対策 |
|---|---|---|
| 短いTimer周期 | OnTimer実行回数が増える | BT用は周期を長めにする |
| 詳細ログ過多 | Expertsログが膨らむ | NG時だけ詳細、通常時は要約にする |
| CSV出力過多 | ファイルI/Oが増える | イベント時だけ記録する |
| 外部連携相当処理 | 検証速度や再現性に影響する | BT時は外部連携をOFFまたはモック化する |
| ミリ秒タイマー | 実行回数が非常に多くなる | 必要な場合だけ使う |
Strategy Testerでの検証では、OnTimer用のログ設定、外部連携ON/OFF、Timer周期を別inputにしておくと、速度と確認しやすさを調整できます。
OnTimerログの設計
OnTimerのログは、定期処理の開始、スキップ、成功、失敗、次回実行予定を分けると確認しやすくなります。
| ログ名 | 意味 | 確認内容 |
|---|---|---|
| TIMER_SET_OK | Timer設定成功 | interval、mode、EA version |
| TIMER_SET_NG | Timer設定失敗 | interval、GetLastError |
| TIMER_TICK | OnTimer開始 | 現在時刻、処理対象 |
| TIMER_SKIP | 処理見送り | throttle、外部連携OFF、BTモードなど |
| EXTERNAL_SYNC_OK | 外部同期成功 | HTTP status、更新件数、反映状態 |
| EXTERNAL_SYNC_NG | 外部同期失敗 | last_error、HTTP status、理由 |
| TIMER_SUMMARY | 定期要約ログ | positions、state、last_sync、last_notify |
| TIMER_KILL | Timer停止 | deinit reason |
OnTimerログは、毎回同じ量を出すのではなく、通常時は要約、異常時は詳細に切り替えると確認しやすくなります。
ログ出力例
[TIMER_SET_OK] seconds=30 mode=external_sync
[TIMER_TICK] server_time=2026.05.26 10:15:00 task=external_sync
[EXTERNAL_SYNC_SKIP] reason=throttle wait_sec=18
[SHEET_FETCH_CONTROL] status=200 result=ok entry_control=ALLOW
[NOTIFY_SKIP] key=runtime_alive reason=duplicate_interval
[RUNTIME_SNAPSHOT] symbol=GOLD positions=2 equity=10000.50 margin=250.00 margin_free=9750.50
[TIMER_SUMMARY] last_sync=2026.05.26 10:15:00 last_notify=2026.05.26 10:10:00
[TIMER_KILL] reason=1
ログ例の時刻、数値、HTTP status、状態名は確認用です。実際のEAでは、外部連携先やログプロファイルに合わせて調整してください。
よくある失敗パターン
| 失敗パターン | 問題点 | 確認方法 |
|---|---|---|
| EventSetTimerを呼んでいない | OnTimerが実行されない | OnInitでTIMER_SET_OKが出ているか確認する |
| EventKillTimerを忘れる | 終了処理の見通しが悪くなる | OnDeinitでTIMER_KILLを出す |
| Timer周期が短すぎる | ログ過多やBT低速化につながる | BT用・実運用用で周期を分ける |
| WebRequestを毎tick実行する | 通信回数が多くなり、処理が重くなる | OnTimerへ分けてthrottleする |
| 外部連携失敗で売買処理まで止める | 影響範囲が不明確になる | 外部連携失敗とentry gateを分ける |
| 通知を重複送信する | Discordやメール通知が多すぎる | notify keyと最小間隔で制御する |
| BTで外部連携ログを出しすぎる | 検証ログが膨らみ、速度が落ちる | BT時は簡易ログまたはOFFにする |
外部制御と認証を混同しない
外部連携EAでは、外部制御と認証を混同しないことも重要です。
外部制御は、外部状態に応じて動作を変える仕組みです。たとえば、新規Entryを止める、通知だけ止める、特定機能を一時停止するなどです。
一方、認証は、EAの利用許可や利用者権限を確認する仕組みです。
| 区分 | 役割 | 公開記事での扱い |
|---|---|---|
| 外部制御 | 外部状態に応じて動作を変える | 概念のみ扱う。詳細ロジックは公開しない |
| 認証 | 利用許可や購入者状態を確認する | 詳細ロジック、判定条件、tokenは公開しない |
| 通知 | 状態や結果を外部へ送る | 通知成功・失敗の扱いを説明する |
| ログ | 状態を追跡する | 機密情報をマスクしてreason中心にする |
認証NG、外部制御NG、通信失敗、通知失敗は、それぞれ意味が違います。ログ上もAUTH、EXTERNAL、NOTIFY、ORDERを分けて確認できるようにします。
よくある失敗例
失敗1:WebRequest失敗で売買処理全体を止める
通信失敗時に何を止めるかは仕様で分ける必要があります。新規Entryを止めるのか、通知だけ止めるのか、既存ポジション管理まで止めるのかを曖昧にすると危険です。
失敗2:Webhook URLやtokenをログへ出している
endpoint、token、Webhook URL、GAS URL、Google Sheet ID、APIキーをログへ出してはいけません。問い合わせ時にログが共有される前提で、必ずマスクします。
失敗3:通知失敗を注文失敗として扱う
通知と注文は別責務です。通知が失敗してもOrderSendが成功している場合があります。ORDER_RESULTとNOTIFY_RESULTを分ける必要があります。
失敗4:OnTickで毎回外部通信する
OnTickで毎回WebRequestを実行すると、EAが重くなり、通信制限やタイムアウトの原因になります。OnTimerや状態変化時に分ける方が安全です。
失敗5:JSON応答をそのまま信じる
JSON文字列が返っただけでは、EAが使える状態とは限りません。形式、必須項目、値の型、有効期限、失敗応答を確認する必要があります。
失敗6:外部制御と認証を混同する
外部制御は運用状態の制御、認証は利用許可の確認です。意味が違うため、ログも停止範囲も分ける必要があります。
失敗7:通信失敗時のログがない
外部連携の問題は、ログがないと原因を追えません。HTTPステータス、reason、scope、checkedAtをマスク済みで残す設計が必要です。
導入前・開発依頼前に整理する情報
外部連携EAを開発・改修する場合、次の情報を整理しておくと仕様確認がスムーズになります。
- 外部連携の目的
- 通知だけなのか、外部状態取得も行うのか
- WebRequestを使うタイミング
- 通信頻度
- 通信失敗時に止める範囲
- 新規Entryと既存ポジション管理の扱い
- 通知失敗時に売買へ影響させるか
- JSON応答の必須項目
- 外部応答の有効期限
- ログに残す項目
- ログに出してはいけない機密情報
- 開発版と配布版で見せる設定項目の違い
- 問い合わせ時に送るログとマスク方法
外部連携は、便利な機能である一方、通信失敗、機密情報、サポート対応の設計が必要です。最初に責務分離と失敗時の扱いを決めておくことが重要です。
定期処理の依頼前にそろえる条件
開発依頼前に整理したい情報
OnTimerを使った外部連携、通知、CSV出力、状態監視を相談する場合は、次の情報を整理してください。
| 整理する情報 | 確認内容 |
|---|---|
| 対象EA | 新規EA、既存EA改修、通知ツール、外部連携ツールのどれか |
| Timer周期 | 何秒ごとに処理したいか、BT用と実運用用を分けるか |
| 処理内容 | 通知、外部制御取得、Google Sheets書き込み、CSV出力、状態監視など |
| 外部連携先 | Discord、Google Sheets、GAS、外部API、CSVファイルなど |
| 売買への影響 | 外部連携失敗時にentryを止めるか、通知だけ止めるか |
| ログ方針 | 通常時は要約、失敗時は詳細、BT時は軽量化など |
| 再送制御 | 通知やWebRequestを連続送信しない条件 |
| バックテスト対応 | 外部連携をOFFにするか、モック化するか、ログだけ出すか |
| 機密情報 | Webhook URL、GAS URL、APIキー、認証情報を公開ログに出さない設計 |
外部連携や通知の不具合を相談する場合は、Timer設定ログ、WebRequest結果、HTTP status、送信間隔、通知スキップ理由、Expertsログ、Journalログを合わせて確認してください。
よくある質問
WebRequestを使えばEAは外部サービスと自由に連携できますか?
WebRequestを使うことで外部通信は可能になりますが、MT5側の許可設定、通信先の応答、タイムアウト、payload形式、サービス側の制限などを考慮する必要があります。EA本体の売買処理とは責務を分けるべきです。
通信失敗時はEAを止めるべきですか?
目的によります。新規Entryを止めるのか、通知だけ失敗扱いにするのか、既存ポジション管理まで止めるのかを仕様として決める必要があります。一般には、新規Entryと既存ポジション管理は分けて扱う方が安全です。
Webhook通知が失敗した場合、注文も失敗したという意味ですか?
違います。Webhook通知とOrderSendは別責務です。通知が失敗しても注文が成功している場合があります。ORDER_RESULTとNOTIFY_RESULTを分けて確認します。
JSON応答はそのまま売買判断に使ってよいですか?
避けるべきです。空応答、形式不正、必須項目不足、古い応答、失敗応答を確認し、外部状態として整理したうえでGATEやexternal controlへ渡します。
外部連携ログには何を出すべきですか?
event、reason、HTTP status、scope、checkedAt、マスク済みdetailなどを出します。endpoint、token、Webhook URL、GAS URL、APIキー、口座番号、顧客情報は出してはいけません。
OnTickで毎回WebRequestしてもよいですか?
避けた方が安全です。OnTickは呼び出し頻度が高いため、通信処理はOnTimer、状態変化時、一定間隔などへ分離する方が安定しやすくなります。
外部制御と認証は同じですか?
違います。外部制御は外部状態に応じた動作制御、認証は利用許可や権限の確認です。ログも停止範囲も分けて設計する必要があります。
実装コードがなくても外部連携設計の相談はできますか?
可能です。連携目的、通信タイミング、失敗時の扱い、通知内容、ログに出す項目、マスクすべき情報が整理されていれば、外部連携設計の相談は進めやすくなります。
関連する確認ページ
確認したい内容に応じて、次のページを参照してください。本文中ですでに案内したページは、ここでは重ねて掲載していません。
あわせて確認したいページ
今回の確認内容に関連する補足ページです。必要な項目だけ確認してください。
| 確認目的 | 関連ページ |
|---|---|
| 「MQL5 外部連携 EA 設計」の読後に「MT5外部連携完全ガイド」を補足確認 | MT5外部連携完全ガイド |
| 「MQL5 外部連携 EA 設計」の読後に「MQL5ファイル操作・CSVログ出力完全ガイド」を補足確認 | MQL5ファイル操作・CSVログ出力完全ガイド |
まとめ
MQL5 EAでWebRequest、JSON、Webhookを扱う場合、外部連携を売買ロジックへ直接混ぜないことが重要です。
特に、次の4点を分けておくと、通信失敗や通知失敗が起きた時に原因を確認やすくなります。
- external communication:外部通信の成功・失敗・タイムアウトを扱う
- external control:外部状態をEAの動作制御へどう反映するかを扱う
- notification:Webhook通知や外部通知の送信結果を扱う
- execution / exit:注文実行や既存ポジション管理を外部通信と混同しない
外部通信が失敗した時に、新規Entryを止めるのか、既存ポジション管理を継続するのか、通知だけ失敗扱いにするのかは、仕様として明確にする必要があります。
また、endpoint、token、Webhook URL、GAS URL、Google Sheet ID、APIキー、口座番号、顧客情報は、本文・コード例・ログ例・スクリーンショットのいずれにも出してはいけません。
完成EAの外部制御ロジックや認証処理を公開しなくても、外部連携の責務分離、通信失敗時の安全側処理、ログ設計を理解しておくことで、開発依頼、既存EA改修、検証、不具合調査、販売後サポートの品質を上げやすくなります。
利用条件・相談窓口
| 確認内容 | 案内ページ |
|---|---|
| MT4/MT5 技術講座|EA・インジケーター・MQL開発を学ぶ | 技術講座ハブ |
| 不具合報告の方法 | 不具合報告・調査依頼について |
| よくある質問|EA・インジケーター・開発依頼・サポートFAQ | よくある質問 |
| 免責事項・リスク説明 | 免責事項・リスク説明 |
お問い合わせはEAFC公式LINEへ
EAFCサイト内の記事、商品、導入前確認、設定確認、ログ確認、開発依頼前のご相談など、各種お問い合わせはEAFC公式LINEより受け付けています。
※投資助言、売買指示、利益保証、推奨ロット、推奨銘柄の案内は行っておりません。
口座番号、認証情報、Webhook URL、GAS URL、APIキーなどの機密情報は送信しないでください。
