MQL5外部連携EA実務ノート|WebRequest・JSON・Webhookを安全に扱う設計

EAファンクラブ

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を安全に設計・検証・サポートするための技術記事です。

この記事で扱う実務課題

項目この記事で扱う内容扱わない内容
目的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では、少なくとも次の責務を分けて設計します。

責務役割混同すると起きる問題
signalEntry候補を作る外部通信失敗をシグナル不成立と誤解する
executionOrderSend、決済、modifyを実行する通知失敗と注文失敗を混同する
riskロット、保有上限、損失制御を扱う外部状態と内部リスクを混同する
exit既存ポジション管理を行う新規Entry停止時に保護決済まで止めてしまう
auth利用許可や認証状態を扱う認証NG時の停止範囲が曖昧になる
external control外部状態による動作制御を扱う通信失敗時に売買全体の扱いが不明になる
notificationDiscord通知や外部通知を扱う通知失敗が売買失敗に見える
log通信結果、reason、detailを記録する問い合わせ時に原因を追えない

特に重要なのは、notificationとexecutionを分けることです。通知が失敗しても、注文自体は成功している場合があります。逆に、注文が失敗しても通知だけ成功している場合もあります。

WebRequestを使う時に決めること

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層として分けます。通知失敗は、通知失敗としてログに残し、注文実行や決済結果と混同しないようにします。

外部連携でログに残すべき情報

外部連携の不具合調査では、実値ではなく、分類された状態が重要です。

ログ項目残す内容出してはいけない内容
eventFETCH_START、FETCH_OK、FETCH_FAIL、SEND_OK、SEND_FAIL実endpoint、実Webhook URL
reasonTIMEOUT、HTTP_STATUS_NG、INVALID_RESPONSEtoken、APIキー
httpStatusHTTPステータス番号認証ヘッダー実値
scopeNEW_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の補助機能または外部状態取得として設計し、売買判定や注文実行と直接混ぜないことが重要です。

外部制御と認証を混同しない

外部連携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応答の必須項目
  • 外部応答の有効期限
  • ログに残す項目
  • ログに出してはいけない機密情報
  • 開発版と配布版で見せる設定項目の違い
  • 問い合わせ時に送るログとマスク方法

外部連携は、便利な機能である一方、通信失敗、機密情報、サポート対応の設計が必要です。最初に責務分離と失敗時の扱いを決めておくことが重要です。

関連する技術講座・確認ページ

ページ確認できること
技術講座ハブMQL5やMT5開発に関する講座一覧
MQL5デバッグ・ログファースト開発完全ガイドログ確認、デバッグ、原因調査の基本
MQL5ファイル操作・CSVログ出力完全ガイドログ保存、CSV出力、検証記録の基本
MT5開発依頼前に用意する資料まとめ仕様書、setファイル、ログ、スクリーンショットの整理
不具合報告・調査依頼について不具合相談前に送る情報
よくある質問導入、確認、相談前のFAQ
免責事項・リスク説明投資判断、損益、利用上の注意に関する確認

FAQ

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、状態変化時、一定間隔などへ分離する方が安定しやすくなります。

外部制御と認証は同じですか?

違います。外部制御は外部状態に応じた動作制御、認証は利用許可や権限の確認です。ログも停止範囲も分けて設計する必要があります。

実装コードがなくても外部連携設計の相談はできますか?

可能です。連携目的、通信タイミング、失敗時の扱い、通知内容、ログに出す項目、マスクすべき情報が整理されていれば、外部連携設計の相談は進めやすくなります。

関連する開発実務ノート

外部連携を含むEAでは、通信処理だけでなく、EA本体の責務分離、ログ設計、バックテストとの差分、配布前の機密情報管理も重要です。

まとめ

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改修、検証、不具合調査、販売後サポートの品質を上げやすくなります。

ABOUT ME
記事URLをコピーしました