コンテンツにスキップ

【移植#51】指値注文を通信に使わない — 期限付き停止指令を検証するリモートHALTガード

EURUSDのH1チャート上で期限切れ・別対象・再送を拒否し、6件の自己テストと注文変更なしを確認したRemote Halt Guard

この記事の3行まとめ

  • 指値注文を通信メッセージにせず、HTTPSの短い応答からHALTRESUMEだけを受け取ります
  • 期限切れ、未来時刻、別チャンネル、別対象、古い連番は拒否し、異常な応答で停止状態を解除しません
  • 専用MT5では6つの状態遷移を自己テストし、注文・ポジションを変えずに最終HALTを確認しました

今回の原典は「別PCのEAを止めたい」

投稿日順で次に当たる2010年2月9日の記事は、同じ口座へ接続した別PCのMT4へ、緊急停止の意思を伝える方法を考えています。

元記事「同一口座の他PC間 MT4の通信?」を読む

想定されていたのは、VPSへログインできないときでも、自宅PCから停止命令を出したい場面です。

原典では、約定しないほど離れた指値注文へ特定のマジックナンバーを付け、その注文を別PCのEAが見つけたら停止する案が示されています。

発想の中心は今も大切です。

  • 管理画面へ入れない経路障害にも備える
  • 複数PCへ同じ停止意思を伝える
  • 通常の売買条件と緊急停止を分ける

ただし、注文は通信パケットではありません。

相場が大きく動く、価格桁や銘柄仕様を取り違える、注文コメントが変わる、ブローカー側の制限に触れる、といった条件が重なると、「絶対に約定しない」という前提は崩れます。

そこで今回は、注文を使う部分を採用しません。


注文ゼロの「停止指令受信EA」へ置き換えた

移植版は、複数PCのMT5が同じHTTPSエンドポイントを定期確認するExpert Advisorです。

受け取る命令は2種類だけです。

命令 役割
HALT 端末内の停止フラグを1へラッチする
RESUME 条件を満たす新しい命令に限り、停止フラグを0へ戻す

このEA自身は、次の操作を行いません。

  • 新規注文
  • 注文変更・取消
  • ポジション決済
  • 自動売買ボタンの切り替え
  • MT5やWindowsの終了

役割は、受信内容を検査して、同じ端末のMQL5プログラムから読める停止フラグを更新するところまでです。

実際に新規エントリーを止めるには、稼働中EA側にもフラグ確認を組み込みます。既存ポジションを維持するか、決済するか、損切りを動かすかは戦略ごとに異なるため、この受信EAが勝手に決める設計にはしませんでした。


なぜインジケーターではなくEAなのか

外部のHTTPS応答はWebRequest()で取得します。

WebRequest()は同期処理です。応答を待つ間、そのMQL5プログラムの処理は止まります。また、共通スレッドで動くインジケーターからは呼び出せず、EAまたはスクリプトで使う必要があります。

そこで、価格Tickへ依存しないOnTimer()で定期確認するEAにしました。

MQL
int OnInit()
{
   EventSetTimer(InpPollSeconds);
   return INIT_SUCCEEDED;
}

void OnTimer()
{
   PollHttps();
}

WebRequest()を使うURLは、利用者がMT5の「ツール」→「オプション」→「エキスパートアドバイザ」で許可リストへ追加する必要があります。プログラムから勝手に許可先を増やすことはできません。

この明示許可は少し手間ですが、ダウンロードしたEAが知らない接続先へ通信しないための大事な境界です。


1行8項目だけを受け取る

応答形式は、複雑なJSONではなく、最大512 bytesの1行に固定しました。

Text Only
MQLP51|1|mql-port-51|demo-fleet|102|HALT|発行時刻UTC|失効時刻UTC

時刻はUTCのUnix秒です。

# 項目 検査
1 プロトコル MQLP51と完全一致
2 1と完全一致
3 チャンネル EAの入力値と完全一致
4 対象 EAのInpScopeTagと完全一致
5 連番 前回受理した値より大きい正整数
6 命令 HALTまたはRESUME
7 発行時刻 許容秒数を超えて未来でない
8 失効時刻 現在より新しく、有効期間上限以内

改行を含む応答、項目数違い、数値の後ろに文字が付いた値も拒否します。

「だいたい合っている」応答を善意に補正すると、停止対象を取り違える余地が増えます。そのため、チャンネルや対象名は大文字小文字も含めて完全一致にしました。


停止状態はラッチする

通信に失敗したとき、停止中だったEAが勝手に再開するのは危険です。

今回の状態遷移は、次のようにしました。

現在 受信 結果
RUN 新しい有効なHALT HALTへ移る
HALT 新しい有効なRESUME RUNへ移る
HALT 期限切れ・形式違い・通信失敗 HALTを維持
HALT 同じ連番の再送 HALTを維持
RUN 無効な応答 RUNを維持

初めてHTTPSモードを起動し、保存済み状態がない場合は、初期設定でHALTから始まります。

ネットワークが使えないことを「停止命令なし」と読み替えません。正常なRESUMEを1度受け取ってRUNへ移った後は、その状態を保存しつつ、異常な応答で状態を書き換えない設計です。

停止フラグと最後の連番は、GlobalVariableSet()でクライアント端末のグローバル変数へ保存します。

Text Only
MQLP51.mql-port-51.halt
MQLP51.mql-port-51.sequence

端末グローバル変数は、MQL5ソース内のグローバル変数とは別物です。同じMT5端末内の別プログラムから読めますが、別PCへ直接同期される機能ではありません。

各PCの受信EAが同じHTTPSエンドポイントを確認し、それぞれ自分の端末内フラグを更新する構成です。


稼働EA側は新規エントリーの直前で確認する

たとえば、チャンネル名を固定して使うEAなら、新規注文を組み立てる前に次のように確認できます。

MQL
bool RemoteHaltActive()
{
   const string key = "MQLP51.mql-port-51.halt";
   if(!GlobalVariableCheck(key))
      return true; // 受信EA未確認を安全側に倒す例
   return GlobalVariableGet(key) >= 0.5;
}

void CheckEntry()
{
   if(RemoteHaltActive())
      return;

   // ここから先で通常の新規エントリー判定を行う
}

この例は「新規エントリーを止める」だけです。

既存ポジションの管理まで止めると、損切りやトレーリングも止まる可能性があります。通常は、停止中も既存ポジションの保護処理を続け、新規発注の直前だけを遮断する方が境界を説明しやすくなります。

複数口座や複数戦略を同じ端末で動かす場合は、チャンネル名を分けてください。端末グローバル変数は口座へ自動的にひも付きません。


Bearer tokenは通信先の認証にだけ使う

HTTPSモードでは、次のヘッダーを付けてGETします。

Text Only
Authorization: Bearer <利用者が設定したトークン>
Accept: text/plain
Cache-Control: no-cache

配布ファイルに実トークンは入っていません。画面にもトークンを表示しません。

入力値は次の条件を満たさないと初期化を拒否します。

  • URLがhttps://で始まる
  • Bearer tokenが16〜256文字
  • URLとtokenに改行がない
  • タイムアウトが250〜10,000 ms
  • 確認間隔が1〜300秒
  • 命令の最大有効期間が10〜3,600秒

ただし、MT5の入力値は秘密保管庫ではありません。設定ファイルや画面共有へtokenを残さず、漏えい時に失効できる専用tokenを使ってください。

また、このEAは証明書ピンニングや応答への電子署名を実装していません。TLSとサーバー側のBearer認証を前提にした最小構成です。より高い保証が必要なら、鍵管理、署名、監査ログ、冗長な停止経路を含む別設計が必要です。


6ケースをローカル自己テストした

WebRequest()はストラテジーテスターでは実行されません。さらに、この記事では実在の制御サーバーや認証情報を検証用に用意していません。

そこで配布EAの初期設定をMQLP51_SELF_TESTにし、専用MT5のライブチャート上で、通信を発生させず受信後の検査と状態遷移を確認しました。

実行条件

項目
MT5 Build 6061
チャート EURUSD・H1
通信 なし(ローカル自己テスト)
初期状態 HALT
注文 / ポジション 0 / 0

テストケース

ケース 期待 結果
新しい期限内HALT 受理して停止 合格
期限切れRESUME 拒否して停止維持 合格
別対象RESUME 拒否して停止維持 合格
同じ連番の再送 拒否して停止維持 合格
新しい期限内RESUME 受理して再開 合格
さらに新しいHALT 受理して再停止 合格

実測結果

確認項目 結果
配布EAのコンパイル 0エラー・0警告
状態遷移 6 / 6合格
受理 / 拒否 3 / 3
最終連番 102
最終状態 HALT
注文件数 0 → 0
ポジション件数 0 → 0
実画面キャプチャー 成功

画像はこの自己テストの実画面です。SOURCE: LOCAL SELF-TESTと表示しているとおり、HTTPS到達性、サーバー認証、複数PCへ届く時間は未検証です。

コンパイル成功とローカル状態遷移の合格を、外部停止経路全体の動作保証へ広げてはいけません。


ダウンロードと使い方

51_Remote_Halt_Guard_v1_00.mq5 をダウンロード

まず、初期設定の自己テストで画面とログを確認してください。

実環境へ接続するときは、デモ口座とテスト用EAから始めます。

  1. HTTPSで1行の命令を返す認証付きエンドポイントを用意します
  2. MT5の許可URLへ、その接続先を追加します
  3. EAのInpSourceModeMQLP51_HTTPSへ変更します
  4. URL、専用Bearer token、チャンネル、対象名を設定します
  5. 稼働EAの新規エントリー直前へ停止フラグ確認を追加します
  6. HALT、通信断、期限切れ、再送、RESUMEをデモ環境で順に確認します

主な入力値は次のとおりです。

パラメーター 初期値 役割
InpSourceMode MQLP51_SELF_TEST ローカル検証 / HTTPS
InpEndpointUrl 空欄 HTTPSエンドポイント
InpAuthToken 空欄 専用Bearer token
InpChannel mql-port-51 指令チャンネル
InpScopeTag demo-fleet 対象グループ
InpPollSeconds 5 確認間隔
InpHttpTimeoutMs 3000 1回の待ち時間上限
InpAllowedFutureSkewSec 30 未来時刻の許容
InpMaxCommandLifetimeSec 300 指令の有効期間上限
InpFailSafeOnStartup true 保存状態がない初回を停止から始める

使えないこと・注意点

  • 配布EAだけでは、他のEAの新規発注は止まりません。各EA側のフラグ確認が必要です。
  • 自動売買ボタン、MT5、Windows、VPSを遠隔終了する機能ではありません。
  • 既存ポジションを決済しません。停止時のポジション管理方針は戦略側で決めます。
  • HTTPSエンドポイントやサーバー実装は付属しません。
  • WebRequest()はストラテジーテスターで動きません。
  • HTTPSは同期処理です。タイムアウトを長くしすぎると受信EAの応答が遅くなります。
  • 端末グローバル変数は別PCへ自動同期されません。各PCで受信EAを動かします。
  • 端末グローバル変数は口座へ自動的にひも付きません。チャンネル設計が必要です。
  • 連番を巻き戻すと命令を拒否します。サーバー側で単調増加を維持してください。
  • tokenを公開コード、画像、共有設定へ入れないでください。
  • 自己テスト合格は、実際のHTTPS経路や障害時運用の合格を意味しません。

ロジック評価の結論

原典の強みは、普段使うログイン経路が壊れたときのために、別の停止経路を考えたことです。

一方、売買注文を通信へ流用すると、「停止したいのに注文リスクを増やす」というねじれが生まれます。

今回の移植では、停止意思の受信と売買処理を分離しました。

  • 通信は明示許可したHTTPSだけ
  • 命令は短命なHALT / RESUMEだけ
  • 対象、時刻、連番をすべて検査
  • 無効な応答で停止を解除しない
  • 受信EAは注文APIを使わない
  • 戦略EAは新規エントリー直前でフラグを見る

これで「別PCから止めたい」という目的を、注文をメッセージにする方法から切り離せます。

ただし、本当に緊急停止へ使うなら、コードだけで終わりではありません。制御サーバーの可用性、token失効、時計ずれ、通信断、端末再起動、監視通知、手動復旧を含めた運用テストが必要です。

止める仕組みほど、正常時より「壊れたときに何を維持するか」を先に決める。

今回のHALTラッチで一番移植したかったのは、その考え方です。


前後の記事

前: 【移植#50】12通貨の指標値を1枚へ — 確定足を揃えるトレンド・コンセンサス

次: 【移植#52】HST直書きをやめる — 日足CSVをMT5カスタムシンボルへ安全に取り込む

— SPONSORED —