【検証#03】EAを内部から止める方法¶
この記事の3行まとめ
- 🛑 MQL5では
ExpertRemove()一発でEAを安全に停止できる - ⚠️ MQL4時代はハック的な手法が必要だった
- 🛡️ 異常時の「キルスイッチ」実装はリスク管理の基本
はじめに¶
この記事は、「とあるMetaTraderの備忘秘録」様のブログ記事を検証・紹介するシリーズの第3弾です。
元ネタ
EAのプログラム内から無理やりEAを止める方法。
https://fai-fx.hatenadiary.org/entry/20090722/1248226354
(2009年7月22日 公開)
時代背景に関する注意
この記事の元となるブログ記事は2009年頃に公開されたものです。
MQL4当時は必須だったハックも、現在のMQL5では標準関数で安全に実現できる場合があります。
なぜEAを「内部から」止めたいのか¶
EAの稼働を停止したいとき、通常はチャート上で手動で「EA削除」を行います。
しかし、以下のような場面ではプログラム自身が自動的に停止することが求められます:
| シナリオ | 説明 |
|---|---|
| 💸 ドローダウン超過 | 損失が一定割合を超えたら停止 |
| 📉 連敗検知 | N回連続で負けたら様子見 |
| ❌ APIエラー | 注文失敗が続いたら異常と判断 |
| 📅 指標発表前 | 重要経済指標の前に自動停止 |
| 🔧 メンテナンス | 週末クローズ時に自動アンロード |
これらを実現するには、EA自身が「自分を止める」機能を持つ必要があります。
MQL4時代のハック手法¶
2009年当時、MQL4には公式の自己停止関数がありませんでした。 そのため、以下のようなハック的な手法が使われていました。
方法1:致命的エラーを発生させる¶
存在しないDLL関数を呼び出すことで、エラー126(DLL読み込み失敗)を発生させ、 EAを強制終了させる方法です。
// MQL4 - 非推奨のハック
#import "nonexistent.dll"
void FakeFunction();
#import
void OnTick()
{
if(ShouldStop())
{
FakeFunction(); // エラー126 → EA停止
}
}
非推奨
この方法は予期しない副作用を起こす可能性があり、推奨されません。
方法2:Windows APIでチャートを閉じる¶
user32.dll の PostMessageA を使って、チャートウィンドウに終了コマンドを送信する方法です。
// MQL4 - Windows API使用(非推奨)
#import "user32.dll"
int PostMessageA(int hWnd, int Msg, int wParam, int lParam);
#import
#define WM_CLOSE 0x0010
void OnTick()
{
if(ShouldStop())
{
PostMessageA(WindowHandle(_Symbol, Period()), WM_CLOSE, 0, 0);
}
}
非推奨
Windows依存のコードであり、ポータビリティがありません。
MQL5の正攻法:ExpertRemove()¶
MQL5では、公式関数 ExpertRemove() が追加されました。 これを呼び出すだけで、EAを安全かつクリーンにアンロードできます。
基本的な使い方¶
void OnTick()
{
if(ShouldStop())
{
Print("異常検知: EAを停止します");
ExpertRemove();
return;
}
// 通常のロジック...
}
ExpertRemoveの動作¶
| 項目 | 説明 |
|---|---|
| 戻り値 | void(なし) |
| 動作タイミング | 現在の関数終了後にアンロード実行 |
OnDeinit | 正常に呼び出される |
| 保有ポジション | そのまま残る(クローズしない) |
ポジションは自動クローズされない
ExpertRemove() はEAをアンロードするだけで、保有中のポジションはクローズしません。
必要に応じて、停止前に手動でクローズロジックを実行してください。
実践例:キルスイッチの実装¶
以下は、ドローダウン(DD)が一定割合を超えた場合に自動停止するEAのサンプルです。
//+------------------------------------------------------------------+
//| KillSwitch_Sample.mq5 |
//| 「とあるMetaTraderの備忘秘録」様の記事を検証 |
//| https://fai-fx.hatenadiary.org/entry/20090722/1248226354 |
//+------------------------------------------------------------------+
#property copyright "FXおもしろラボ"
#property link "https://fx-omoshiro-lab.com/"
#property version "1.00"
input double MaxDrawdownPercent = 10.0; // 最大許容ドローダウン (%)
input int MaxConsecutiveLosses = 5; // 最大許容連敗数
// グローバル変数
double g_initialBalance;
int g_consecutiveLosses = 0;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
g_initialBalance = AccountInfoDouble(ACCOUNT_BALANCE);
Print("初期残高: ", g_initialBalance);
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// キルスイッチチェック
if(CheckKillSwitch())
{
return; // ExpertRemove後はすぐリターン
}
// ここに通常のトレードロジック...
}
//+------------------------------------------------------------------+
//| キルスイッチ判定 |
//+------------------------------------------------------------------+
bool CheckKillSwitch()
{
// 1. ドローダウンチェック
double currentBalance = AccountInfoDouble(ACCOUNT_BALANCE);
double drawdownPercent = (g_initialBalance - currentBalance) / g_initialBalance * 100;
if(drawdownPercent >= MaxDrawdownPercent)
{
PrintFormat("⚠️ ドローダウン %.2f%% が上限 %.2f%% を超過",
drawdownPercent, MaxDrawdownPercent);
Alert("キルスイッチ発動: ドローダウン超過");
ExpertRemove();
return true;
}
// 2. 連敗チェック
if(g_consecutiveLosses >= MaxConsecutiveLosses)
{
PrintFormat("⚠️ 連敗数 %d が上限 %d を超過",
g_consecutiveLosses, MaxConsecutiveLosses);
Alert("キルスイッチ発動: 連敗超過");
ExpertRemove();
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| トレード結果を記録(OnTradeTransactionから呼び出す想定) |
//+------------------------------------------------------------------+
void RecordTradeResult(bool isProfit)
{
if(isProfit)
g_consecutiveLosses = 0; // 勝ちでリセット
else
g_consecutiveLosses++; // 負けでインクリメント
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Print("EA終了: 理由コード = ", reason);
// reason == REASON_REMOVE の場合、ExpertRemove()による停止
if(reason == REASON_REMOVE)
{
Print("ExpertRemove()による自動停止でした");
}
}
OnDeinitの終了理由コード¶
ExpertRemove() で停止した場合、OnDeinit の引数 reason には REASON_REMOVE が渡されます。
| 定数 | 値 | 説明 |
|---|---|---|
REASON_PROGRAM | 0 | プログラムによる終了(スクリプト) |
REASON_REMOVE | 1 | チャートから削除 / ExpertRemove |
REASON_RECOMPILE | 2 | 再コンパイル |
REASON_CHARTCHANGE | 3 | 通貨ペア・時間足変更 |
REASON_CHARTCLOSE | 4 | チャートを閉じた |
REASON_PARAMETERS | 5 | パラメータ変更 |
REASON_ACCOUNT | 6 | 口座変更 |
まとめ¶
| ポイント | 内容 |
|---|---|
| MQL4時代 | ハック的な手法が必要(DLLエラー、Windows API) |
| MQL5以降 | ExpertRemove() で安全に停止可能 |
| 注意点 | ポジションは自動クローズされない |
| 応用 | キルスイッチ(異常時自動停止)の実装 |
オリジナル記事への謝辞
この記事は「とあるMetaTraderの備忘秘録」様の貴重な知見をもとに、
MQL5での検証と解説を加えたものです。
オリジナル記事に心より感謝いたします。
ソースコードのダウンロード¶
この記事で紹介したコードをダウンロードできます。
ファイルの種類:EA(Expert Advisor)
保存先: MQL5/Experts/ フォルダ
使い方: MT5のナビゲーターから「エキスパートアドバイザ」を展開し、チャートにドラッグ&ドロップで適用
関連用語¶
この記事で登場した用語は用語集でも解説しています。