MQL5長時間稼働EA実務ノート|フリーズ・重い・オブジェクト残存を防ぐ考え方
MQL5 EAは、短時間の動作確認では問題がなくても、長時間稼働させると重くなる、チャートが固まる、Objectが残る、ログが大量に出る、時間足変更後に不安定になる、といった問題が出ることがあります。
特に、VPS上で複数チャートにEAやインジケーターを入れる場合、OnTick処理、Object操作、外部通信、ログ出力、OnDeinit処理の負荷が積み重なります。短い検証では見えなかった設計上の弱点が、長時間運用で表面化することがあります。
長時間稼働EAで重要なのは、処理を増やすことではなく、OnTickで毎回やる処理、OnTimerへ分ける処理、OnDeinitで最小限にする処理、起動時に監査する処理を分けることです。
この記事では、MQL5 EAを長時間安定稼働させるための設計判断を、開発実務ノートとして整理します。完成EAソース全文、外部シート制御ロジック、認証ロジック、endpoint、token、URL実値、実運用set値、中核Entry条件は公開しません。
本記事は、投資判断や売買指示ではなく、MQL5 EA・インジを安定稼働させるための技術記事です。
- この記事で扱う実務課題
- 長時間稼働で問題が出る理由
- OnTickへ重い処理を詰め込まない
- OnTimerへ分ける処理
- 実コード例1:OnTimerへメンテナンス処理を分ける
- Object操作を毎tickで行わない
- Object残存はOnDeinitだけで解決しようとしない
- 実コード例2:Object名prefixで所有範囲を限定する
- OnDeinitは最小化する
- 実コード例3:OnDeinitを軽量にする
- ログ過多はフリーズや検証遅延の原因になる
- 実コード例4:同じログを出しすぎない
- バックテストとリアル稼働でログ方針を分ける
- 複数チャート・複数EAでの負荷を考える
- フリーズ調査で確認する情報
- よくある失敗例
- 導入前・開発依頼前に整理する情報
- 関連する技術講座・確認ページ
- FAQ
- 関連する開発実務ノート
- まとめ
この記事で扱う実務課題
| 項目 | この記事で扱う内容 | 扱わない内容 |
|---|---|---|
| 目的 | EAの長時間稼働で起きる重さ・フリーズ・Object残存を防ぐ設計 | 利益を狙う売買ロジック、具体的なEntry条件 |
| 主な対象 | OnTick負荷、OnTimer分離、Object lifecycle、OnDeinit最小化、ログ抑制 | 完成EAソース全文、実運用コード全文 |
| 設計観点 | VPS運用、複数チャート、放置確認、販売後サポート、バックテスト速度 | 認証ロジック、外部制御、実運用set値 |
| コード例 | 一般化したOnTimer、ログ抑制、Object audit、OnDeinit最小化 | 実売買条件、独自優位性のある判定式 |
| 読者 | MQL5開発者、EA改修依頼前ユーザー、検証担当者、販売前チェック担当者 | 売買判断や推奨設定を知りたい人 |
長時間稼働で問題が出る理由
EAは、コンパイルできることや数分動くことだけでは安定稼働とは言えません。
長時間稼働では、同じ処理が何千回、何万回と繰り返されます。OnTickで重い処理をしている、Objectを毎回作り直している、同じログを出し続けている、外部通信を頻繁に行っている、終了時に広範囲cleanupをしている場合、時間の経過とともに問題が出やすくなります。
| 現象 | 原因の例 | 見るべき責務 |
|---|---|---|
| チャートが重い | OnTickで大量処理、Object再作成、ログ過多 | runtime / UI / log |
| MT5が固まる | 外部通信、広範囲Object走査、削除処理、無限ループに近い処理 | OnTick / OnTimer / Object管理 |
| EA削除時に不安定 | OnDeinitで大量Object削除、重い処理、他Object巻き込み | OnDeinit / cleanup |
| Objectが残る | prefix不統一、所有範囲不明、cleanup方針不足 | Object lifecycle |
| バックテストが遅い | 毎tickログ、大量Object、不要な集計 | BT profile / log / UI |
| 時間足変更後に崩れる | 再初期化、残存Object、handle解放、再描画方針不足 | OnInit / OnDeinit / Object audit |
長時間稼働の安定性は、個別関数の正しさだけではなく、処理の頻度、責務分離、後始末の軽さ、ログ粒度によって大きく変わります。
OnTickへ重い処理を詰め込まない
OnTickは、価格更新のたびに呼ばれます。銘柄や時間帯によっては、非常に高頻度で呼ばれます。
そのため、OnTickに重い処理を詰め込むと、EA全体が重くなります。
| OnTickで避けたい処理 | 理由 | 代替方針 |
|---|---|---|
| 毎tickで全Objectを走査する | Object数が増えるほど重くなる | OnTimerまたは状態変化時に分ける |
| 毎tickでObjectを削除・再作成する | ちらつき、残存、負荷の原因になる | 初回作成後は差分更新にする |
| 毎tickで大量ログを出す | ログが読めず、BTも遅くなる | 状態変化時または一定間隔に抑制する |
| 毎tickで外部通信する | 通信遅延やタイムアウトがEAへ影響する | OnTimer、状態変化時、一定間隔へ分ける |
| 毎tickで重い集計を行う | 複数チャートで負荷が増える | 必要なタイミングだけ計算する |
OnTickには、tickごとに本当に必要な処理だけを残します。状態監視、表示更新、外部通信、Object監査、summaryログなどは、OnTimerや状態変化時の処理へ分けると安定しやすくなります。
OnTimerへ分ける処理
OnTimerは、一定間隔で処理を行うためのイベントです。OnTickほど高頻度ではない処理を分離するのに向いています。
OnTimerへ分けやすい処理は次の通りです。
| 処理 | OnTimerへ分ける理由 |
|---|---|
| ステータス表示更新 | 毎tick更新する必要がない場合が多いため |
| Object監査 | 毎tick走査すると重いため |
| 外部通信 | 通信頻度を制御しやすいため |
| summaryログ | 一定間隔で要約すれば十分なため |
| 軽い自己診断 | 状態確認を定期的に行いやすいため |
| 通知抑制 | 送信間隔や再送条件を管理しやすいため |
ただし、OnTimerへ分ければ何でも安全というわけではありません。OnTimer内でも重い処理を詰め込みすぎると、結局はMT5全体の負荷になります。
実コード例1:OnTimerへメンテナンス処理を分ける
以下は、設計判断を理解するために一般化した学習用コード例です。完成EAのソース全文ではありません。
// 学習用に一般化したOnTimer分離例です。
// 実運用EAの完成ソースではありません。
datetime g_lastMaintenanceTime = 0;
int g_maintenanceIntervalSeconds = 30;
bool ShouldRunMaintenance()
{
datetime nowTime = TimeCurrent();
if(g_lastMaintenanceTime == 0)
{
g_lastMaintenanceTime = nowTime;
return true;
}
if((nowTime - g_lastMaintenanceTime) < g_maintenanceIntervalSeconds)
return false;
g_lastMaintenanceTime = nowTime;
return true;
}
void RunLightMaintenance()
{
Print("DIAG/MAINTENANCE: event=RUN",
" reason=INTERVAL_REACHED");
// 実際のEAでは、軽い表示更新、summaryログ、
// Object監査などを必要最小限に分けて扱います。
}
void OnTimer()
{
if(!ShouldRunMaintenance())
return;
RunLightMaintenance();
}この例では、OnTimer内でもさらに間隔制御を行っています。OnTimerが呼ばれるたびに重い処理を実行するのではなく、必要な間隔で軽いメンテナンスだけを行います。
Object操作を毎tickで行わない
チャートObjectは、パネル表示、ライン表示、ラベル表示などで便利です。
しかし、ObjectCreate、ObjectDelete、ObjectsTotal、ObjectName、ObjectFindなどを大量に使う処理は、設計によってはチャートを重くする原因になります。
| 避けたい設計 | 理由 | 推奨方針 |
|---|---|---|
| 毎tickでObjectを全削除する | ちらつき、重さ、他Object巻き込みの原因 | 自EA所有Objectだけを差分更新する |
| prefixなしでObjectを管理する | 他EAや手動Objectと区別できない | Object名prefixを固定する |
| 表示Objectを判定の真実にする | 残存Objectや表示遅延で判定が崩れる | runtime stateを真実にする |
| OnDeinitで広範囲cleanupする | 削除時に重くなる、不安定化する場合がある | 軽量UIのみ対象にし、残存はstartup auditで扱う |
| Object作成失敗で本体処理を止める | 表示失敗が売買処理へ波及する | 表示系はbest-effortにする |
Objectは表示のための道具です。判定の真実や実行状態は、runtime stateやposition snapshotで管理する方が安全です。
Object残存はOnDeinitだけで解決しようとしない
EA削除時にObjectが残ると、利用者から見ると不具合に見えます。そのため、OnDeinitですべて消したくなる場合があります。
しかし、OnDeinitで広範囲にObjectを走査し、大量削除する設計は慎重に扱う必要があります。
| 方針 | メリット | 注意点 |
|---|---|---|
| OnDeinitで全削除 | 見た目はきれいになりやすい | 他Object巻き込み、EA削除時の重さ、異常終了リスク |
| 自EAの軽量UIだけ削除 | 安全範囲を限定しやすい | 価格ラインなどの扱いを別に決める必要がある |
| 残存Objectはstartup auditで確認 | 次回起動時に安全に監査できる | 残すObjectと無視するObjectの方針が必要 |
| 表示ラインを意図的に残す | 検証や目視確認に使える | 再接続時に誤参照しない設計が必要 |
長時間稼働や複数チャート運用では、「削除時にすべて片付ける」だけでなく、「起動時に残存を監査する」考え方も重要です。
実コード例2:Object名prefixで所有範囲を限定する
以下は、設計判断を理解するために一般化した学習用コード例です。完成EAのソース全文ではありません。
// 学習用に一般化したObject所有判定例です。
// 実運用EAの完成ソースではありません。
string ObjectPrefix()
{
return "EAFC_LONGRUN_";
}
string BuildObjectName(string part)
{
return ObjectPrefix() + part;
}
bool IsOwnObject(string name)
{
return (StringFind(name, ObjectPrefix()) == 0);
}
void TraceObjectAudit(string eventName, string reason, string name)
{
Print("DIAG/OBJECT_AUDIT: event=", eventName,
" reason=", reason,
" name=", name);
}Object名prefixを固定すると、自EAのObjectだけを識別しやすくなります。
他EAや手動Objectに触れないためにも、Object操作の前に所有範囲を確認する設計が重要です。
OnDeinitは最小化する
OnDeinitは、EAが削除された時、時間足変更、チャート変更、再コンパイルなどで呼ばれる終了処理です。
OnDeinitでは、できるだけ軽い処理に限定します。
| OnDeinitで行いやすい処理 | 注意点 |
|---|---|
| Commentを消す | 軽量で安全 |
| EventKillTimerを呼ぶ | timer停止として必要 |
| IndicatorReleaseする | 作成済みhandleの解放 |
| 自EA所有の軽量UIを削除する | prefixで範囲を限定する |
| 終了ログを出す | ログ過多にならない範囲にする |
OnDeinitで外部通信、大量Object走査、重いファイル処理、広範囲cleanupを行うと、EA削除時の不安定化につながる場合があります。
実コード例3:OnDeinitを軽量にする
以下は、設計判断を理解するために一般化した学習用コード例です。完成EAのソース全文ではありません。
// 学習用に一般化したOnDeinit最小化例です。
// 実運用EAの完成ソースではありません。
int g_sampleHandle = INVALID_HANDLE;
void ReleaseHandles()
{
if(g_sampleHandle == INVALID_HANDLE)
return;
IndicatorRelease(g_sampleHandle);
g_sampleHandle = INVALID_HANDLE;
Print("DIAG/DEINIT: event=HANDLE_RELEASE",
" reason=INDICATOR_RELEASE_DONE");
}
void DeleteLightUiObjectsOnly()
{
for(int i = ObjectsTotal(0) - 1; i >= 0; i--)
{
string name = ObjectName(0, i);
if(!IsOwnObject(name))
continue;
ObjectDelete(0, name);
TraceObjectAudit("DELETE", "OWN_LIGHT_UI", name);
}
}
void OnDeinit(const int reason)
{
Comment("");
EventKillTimer();
ReleaseHandles();
DeleteLightUiObjectsOnly();
Print("DIAG/DEINIT: event=MINIMAL_CLEANUP",
" reason=", reason);
}この例では、OnDeinitで行う処理を限定しています。timer停止、handle解放、自EA所有の軽量UI削除に絞り、広範囲cleanupや外部通信は行っていません。
ログ過多はフリーズや検証遅延の原因になる
ログファースト開発は重要ですが、ログを無制限に出すことではありません。
特に、OnTickで毎回同じログを出し続けると、Expertsログが膨大になり、重要なエラーが埋もれます。バックテストではログ出力そのものが検証速度を下げる原因にもなります。
| ログ過多の原因 | 対策 |
|---|---|
| 毎tick同じSKIP理由を出す | reasonが変わった時だけ出す |
| 成功ログを毎回出す | summary化する |
| Object更新ログが多い | CREATE_FAILや異常時中心にする |
| バックテストで詳細ログを出す | BT用ログプロファイルを用意する |
| 通知や外部通信ログが多い | 送信間隔と失敗時ログを分ける |
読めないログは、ログがないのと同じです。長時間稼働EAでは、状態変化、異常、summaryを中心にログを設計します。
実コード例4:同じログを出しすぎない
以下は、設計判断を理解するために一般化した学習用コード例です。完成EAのソース全文ではありません。
// 学習用に一般化したログ抑制例です。
// 実運用EAの完成ソースではありません。
struct StateLogThrottle
{
string lastKey;
datetime lastPrintedAt;
};
StateLogThrottle g_stateLog;
bool CanPrintStateLog(string key, int intervalSeconds)
{
datetime nowTime = TimeCurrent();
if(g_stateLog.lastKey != key)
{
g_stateLog.lastKey = key;
g_stateLog.lastPrintedAt = nowTime;
return true;
}
if((nowTime - g_stateLog.lastPrintedAt) >= intervalSeconds)
{
g_stateLog.lastPrintedAt = nowTime;
return true;
}
return false;
}
void WriteStateSummary(string key, string reason)
{
if(!CanPrintStateLog(key, 60))
return;
Print("DIAG/RUNTIME: event=SUMMARY",
" reason=", reason,
" key=", key);
}この例では、同じkeyのログを一定間隔に抑制しています。
長時間稼働では、状態変化時だけ出す、一定間隔でsummary化する、失敗系だけ詳細にするなど、ログ粒度を調整します。
バックテストとリアル稼働でログ方針を分ける
バックテストでは、詳細ログが多すぎると検証速度が落ちます。一方で、リアル稼働では、サポート時に必要な情報が残っていないと原因調査ができません。
| 環境 | ログ方針 | 目的 |
|---|---|---|
| 開発版 | 詳細ログを多めに出す | 原因調査、検証、改修 |
| バックテスト | 成功系を抑制し、summary中心にする | 検証速度と再現性 |
| リアル稼働 | 重要イベント、失敗理由、状態変化を残す | 運用確認、サポート |
| 配布版 | 利用者が読める粒度に絞る | 販売後サポート、問い合わせ削減 |
ログ名やreason名は、環境ごとに大きく変えない方が比較しやすくなります。出す量は変えても、分類名は揃える方が実務的です。
複数チャート・複数EAでの負荷を考える
1つのチャートでは軽くても、複数チャート、複数EA、複数インジケーターを同じMT5に入れると負荷が増えます。
特に、次のような構成では注意が必要です。
- 複数通貨ペアに同じEAを設置する
- EAと重いインジケーターを同じチャートに置く
- 外部通信EAを複数稼働する
- パネルEAを複数チャートで表示する
- Object数が多いインジケーターと併用する
- ログ詳細モードのまま長時間稼働する
複数チャート運用では、EA単体の軽さだけでなく、MT5全体としての負荷を確認する必要があります。
フリーズ調査で確認する情報
フリーズや重さの調査では、発生前後のログだけでなく、設置状況も重要です。
| 確認項目 | 理由 |
|---|---|
| 発生時刻 | Expertsログ・Journalログの該当箇所を探すため |
| 銘柄・時間足 | tick頻度や表示条件を確認するため |
| 設置EA・インジ一覧 | 併用負荷やObject競合を確認するため |
| Object数 | 表示過多や残存Objectを確認するため |
| ログ量 | 毎tickログやログ過多を確認するため |
| VPS環境 | CPU、メモリ、MT5数、同時稼働状況を確認するため |
| 再現手順 | 時間足変更、EA削除、再セット、外部通信などの契機を確認するため |
「フリーズしました」だけでは原因を特定できません。どのチャートで、どのEA・インジを併用し、どの操作後に、どのログが出ていたかを整理する必要があります。
よくある失敗例
失敗1:OnTickにすべて詰め込む
OnTickは高頻度で呼ばれます。表示更新、Object監査、外部通信、summaryログをすべてOnTickへ入れると、長時間稼働で重くなりやすくなります。
失敗2:毎tickでObjectを作り直す
ObjectDeleteとObjectCreateを毎tick繰り返すと、ちらつき、負荷、残存Object、表示崩れの原因になります。初回作成後は差分更新を基本にします。
失敗3:OnDeinitで広範囲cleanupする
EA削除時に大量Object削除や外部通信を行うと、削除時の不安定化につながる場合があります。OnDeinitは最小化し、残存監査はstartup auditへ分ける設計も検討します。
失敗4:ログを出しすぎる
ログファーストは重要ですが、毎tick同じログを出し続けると読めなくなります。状態変化、失敗、summaryを中心にします。
失敗5:表示失敗を売買失敗として扱う
パネル表示やObject作成失敗は、売買判定やexecutionとは別責務です。表示系の失敗がEA本体へ過度に波及しないようにします。
失敗6:手動Objectや他EAのObjectを消す
prefixなしでObjectDeleteすると、他EAや手動ラインを巻き込む可能性があります。Object所有範囲を明確にします。
失敗7:短時間テストだけで安定判断する
短時間では問題が出なくても、長時間稼働、時間足変更、EA削除、再セット、複数チャート運用で問題が出ることがあります。放置確認と再初期化確認が必要です。
導入前・開発依頼前に整理する情報
長時間稼働を前提にEAやインジを開発・改修する場合、次の情報を整理しておくと仕様確認がスムーズになります。
- 想定する稼働時間
- VPSで稼働するか
- 同時に動かすMT5の数
- 同時に動かすEA・インジの数
- 設置する銘柄と時間足
- パネル表示やObject表示の有無
- 外部通信の有無
- OnTickで毎回必要な処理
- OnTimerへ分けたい処理
- OnDeinitで削除するObjectの範囲
- 残すObjectと消すObjectの違い
- ログ詳細モードと通常モードの違い
- バックテスト時のログ抑制方針
- フリーズ時に提出するログとスクリーンショット
長時間稼働では、売買ロジックだけでなく、表示、ログ、Object、外部通信、終了処理の設計が重要です。最初に運用環境と負荷条件を整理しておくと、後から大きな改修になりにくくなります。
関連する技術講座・確認ページ
| ページ | 確認できること |
|---|---|
| 技術講座ハブ | MQL5やMT5開発に関する講座一覧 |
| MQL5長時間稼働・安定化完全ガイド | 長時間稼働、ログ過多、安定化の基本 |
| MQL5チャートオブジェクト・UI表示完全ガイド | Object表示、パネルUI、チャート表示の基本 |
| MQL5デバッグ・ログファースト開発完全ガイド | ログ確認、デバッグ、原因調査の基本 |
| MT5開発依頼前に用意する資料まとめ | 仕様書、setファイル、ログ、スクリーンショットの整理 |
| 不具合報告・調査依頼について | 不具合相談前に送る情報 |
| よくある質問 | 導入、確認、相談前のFAQ |
| 免責事項・リスク説明 | 投資判断、損益、利用上の注意に関する確認 |
FAQ
短時間の動作確認で問題なければ長時間稼働も大丈夫ですか?
必ずしもそうではありません。OnTick負荷、Object残存、ログ過多、外部通信、EA削除時の処理などは、長時間稼働や複数チャート運用で問題化することがあります。
OnTickには何を書いてはいけませんか?
絶対に禁止というより、毎tickで行う必要がない重い処理は避けるべきです。全Object走査、毎tick外部通信、大量ログ、Object削除・再作成などは、OnTimerや状態変化時へ分ける方が安全です。
OnTimerを使えば必ず軽くなりますか?
必ずではありません。OnTimer内に重い処理を詰め込めば、結局は重くなります。OnTimerは処理頻度を制御するために使い、さらに内部で間隔制御やsummary化を行うと安全です。
OnDeinitではObjectを全部消した方がよいですか?
必ずしもそうではありません。自EA所有が明確な軽量UIは削除候補ですが、広範囲のObject削除は他Object巻き込みや削除時不安定化の原因になる場合があります。残存Objectはstartup auditで扱う設計もあります。
Objectが残るのは必ず不具合ですか?
残す設計の場合もあります。検証用ラインや目視確認用ラインを意図的に残すことがあります。ただし、再セット時に誤参照しないよう、prefix、所有範囲、startup auditが必要です。
ログは多いほどよいですか?
違います。ログは追跡できることが重要です。毎tick同じログを大量に出すと、重要なエラーが埋もれます。状態変化、失敗理由、summaryを中心に設計します。
フリーズ調査では何を送ればよいですか?
発生時刻、銘柄、時間足、設置EA・インジ一覧、Expertsログ、Journalログ、使用setファイル、スクリーンショット、再現手順、VPSやMT5の稼働状況を整理してください。
実装コードがなくても長時間稼働対策の相談はできますか?
可能です。運用環境、設置数、表示Object、外部通信、ログ量、発生状況が整理されていれば、設計方針の相談は進めやすくなります。
関連する開発実務ノート
長時間稼働の安定性を高めるには、ログ設計、チャートUI、バックテスト条件、商品化前チェックも関係します。フリーズや重さを防ぐため、関連する設計観点も確認してください。
- MQL5ログファースト開発実務ノート|不具合調査しやすいEA・インジの作り方
- MQL5パネルEA・チャートUI実務ノート|表示系と判定系を分ける設計
- MQL5バックテスト実務ノート|検証条件・最適化・リアル差分を記録する考え方
- MQL5商品化・配布前チェック実務ノート|Inputs・HELP・Snapshot・manual・UserLive化の基本
まとめ
MQL5 EAを長時間安定稼働させるには、売買ロジックだけでなく、OnTick負荷、OnTimer分離、Object管理、OnDeinit最小化、ログ抑制を設計することが重要です。
特に、次の4点を分けると、フリーズ、重さ、Object残存、ログ過多の問題を減らしやすくなります。
- OnTick:tickごとに本当に必要な処理だけを行う
- OnTimer:表示更新、Object監査、summaryログ、外部通信などを必要間隔で行う
- Object lifecycle:Object名prefix、所有範囲、差分更新、残存監査を決める
- OnDeinit:timer停止、handle解放、軽量cleanupに絞る
また、ログファーストは重要ですが、ログを無制限に出すことではありません。状態変化、失敗理由、summaryを中心にし、バックテストや配布版ではログ粒度を調整することが実務的です。
完成EAの中核ロジックを公開しなくても、長時間稼働に必要な責務分離と負荷対策を理解しておくことで、開発依頼、既存EA改修、検証、不具合調査、販売後サポートの品質を上げやすくなります。

