「少し待ってから次の処理をしたい」
「ユーザーに確認してから動かしたい」
そう思ったときに使いたくなるのが、
SleepとMessageBoxという2つの関数です。
どちらも一見便利ですが、
EAで使うと処理が止まってしまう危険な関数でもあります。
・Sleep関数がカスタムインジケーターで使えない理由
・MessageBoxがEAのティック処理を止めてしまう仕組み
・両関数がストラテジーテスターでは意味を持たないこと
・待機処理・確認処理の安全な書き方
SleepとMessageBoxが「危険な関数」と言われる理由
まず、この2つの関数がなぜ注意を要するのかを確認します。
どちらも処理を「止める」関数
Sleepは指定した時間だけ、
MessageBoxはユーザーがボタンを押すまで、
プログラムの実行をその場で止めます。
EAは24時間自動で動く前提のプログラム
・EAは人がチャートの前にいなくても動き続けることが前提
・処理が止まっている間は新しいティック(価格更新)に反応できない
・止まっている間に相場が大きく動くリスクがある
ChatGPT等が生成したコードに紛れ込みやすい
既刊chatgpt-ea-not-working-checklist-mt4.mdで
触れたとおり、
AIが生成したサンプルコードには、
一般的なプログラミングの作法としてSleepやMessageBoxが
そのまま含まれていることがあります。
Sleep関数のシグネチャと本来の使い道
実際の書式と本来の用途をdocs.mql4.comで確認済みの内容で見ていきます。
Sleep()のシグネチャ(docs.mql4.com確認済み)
void Sleep(int milliseconds);
引数`milliseconds`で指定した時間(ミリ秒)だけ、
実行を一時停止する関数です。
スクリプトでの一回限りの処理には使える
人が実行ボタンを押して1回だけ動かすスクリプトであれば、
処理の合間に少し待つ目的でSleepを使っても
実害は小さいです。
0.1秒ごとにEA停止フラグをチェックする仕組み
Sleep関数には、
0.1秒ごとにEAの停止操作が無いかを確認する仕組みが
組み込まれています。
Sleep関数をEAで使うとどうなるか
EAの中でSleepを使った場合の具体的な問題を確認します。
カスタムインジケーターでは使用不可
・インジケーターはチャート描画と同じインターフェーススレッドで動く
・そこでSleepを呼ぶと画面の表示自体が固まってしまう
・そのためMQL4ではインジケーターからのSleep呼び出しが禁止されている
EAで使うと新しいティックを取りこぼす
//よくある誤用コード(非推奨)
for(int i = 0; i < 5; i++)
{
Sleep(1000);//1秒待つ×5回
Print("waiting...");
}
このコードが実行されている間、
このEAは新しい価格更新に反応できません。
非ブロッキングな待機処理に書き直す
static int lastActionTime = 0;
if(GetTickCount() - lastActionTime > 5000)
{
lastActionTime = GetTickCount();
DoSomething();
}
直前に実行した時刻を変数に記録し、
一定時間経過したかをOnTick()の中で毎回判定する形にすれば、
ティックを取りこぼさずに「待つ」処理を実現できます。
MessageBox関数のシグネチャと戻り値
次に、MessageBox関数の書式を確認します。
MessageBox()のシグネチャ(docs.mql4.com確認済み)
int MessageBox( string text, //表示するメッセージ string caption=NULL, //タイトル(省略時はEA名) int flags=0 //ボタン・アイコンの指定フラグ );
戻り値は、
ユーザーが押したボタンに対応する定数(`IDOK`等)です。
本来は手動確認用のダイアログ
MessageBoxは、
人が画面の前でボタンを押すことを前提にした
確認ダイアログ用の関数です。
カスタムインジケーターでは使用不可
Sleepと同様に、
MessageBoxもインターフェーススレッドの固まりを避けるため
カスタムインジケーターからは呼び出せません。
MessageBoxをEAで使うとどうなるか
EAでの呼び出しが許可されている点が、Sleepとの違いです。
EA自体はMessageBoxを呼び出せる
EAはインターフェーススレッドとは別に動くため、
コンパイル・起動自体はできてしまいます。
クリックするまでそのEAの処理が止まる
・ダイアログが表示されている間、そのEAのOnTick()は先に進まない
・無人で稼働させている場面では誰もクリックできない
・ポジションを持ったまま長時間反応できなくなるリスクがある
通知はPrint・Comment・SendMail等で行う
EAからの通知には、
既刊get-last-error.mdのPrint()・
既刊のComment表示・SendMail()等、
処理を止めない手段を使います。
ストラテジーテスターでは両方とも意味を持たない
バックテストで検証する際の注意点を確認します。
Sleepはテスターでは実行を中断しない
docs.mql4.comで確認した通り、
Sleep関数はストラテジーテスター上では
実際には処理を止めません。
MessageBoxはテスターでは機能しない
同様に、MessageBoxもストラテジーテスター上では
機能しないことが公式リファレンスに明記されています。
テストで問題が見えないまま本番稼働させない
バックテストで何も問題が起きなかったとしても、
本番のリアルタイム実行では
前述の停止リスクがそのまま残っている点に注意してください。
ChatGPT等が生成したコードへの対処
実際にこれらの関数が紛れ込んでいた場合の直し方を確認します。
コード内にSleep・MessageBoxが無いか検索する
MetaEditorの検索機能で
`Sleep(`・`MessageBox(`という文字列を検索し、
使われている箇所をすべて確認します。
用途ごとに置き換え先を選ぶ
・一定時間待ちたいだけ → GetTickCount()を使った非ブロッキング処理
・ユーザーへの通知が目的 → Print・Comment・Alert・SendMail
・起動時の確認のみ → OnInit()内なら実害は小さいが依然非推奨
置き換え後は必ずバックテストと通して確認する
置き換えたあとは、
既刊backtest-live-gap-mt4.mdで触れた点も踏まえ、
バックテストと可能であればデモ口座での実動作を確認します。
よくある質問
Sleep・MessageBoxについて、よく検索される疑問に回答します。
Sleepをインジケーターで使うとどうなりますか?
MQL4の仕様上、
カスタムインジケーターからのSleep呼び出しは許可されていません。
使用した場合はコンパイルエラーまたは実行時エラーになります。
MessageBoxは絶対に使ってはいけませんか?
人が手動で1回実行するスクリプトの確認用途であれば
問題になりにくいです。
無人稼働を前提とするEAのOnTick()内で使うことは避けてください。
Sleepを使わずに「数秒待つ」処理は作れますか?
作れます。
この記事で紹介したGetTickCount()を使った
時刻比較の書き方に置き換えれば、
ティックを取りこぼさずに同じ目的を実現できます。
【MQL4】SleepとMessageBoxをEAで使うと危険な理由(インジケーターでは使用不可・テスターでは無効):まとめ
SleepとMessageBoxはどちらも、
人がその場で操作することを前提にした関数です。
無人稼働が前提のEAにそのまま使うと、
処理が止まったままティックへの反応が遅れるリスクがあります。
GetTickCount()を使った非ブロッキングな書き方に
置き換えることをおすすめします。
コメント