MQL5長時間稼働EA実務ノート|フリーズ・重い・オブジェクト残存を防ぐ考え方

EAファンクラブ

MQL5 EAは、短時間の動作確認では問題がなくても、長時間稼働させると重くなる、チャートが固まる、Objectが残る、ログが大量に出る、時間足変更後に不安定になる、といった問題が出ることがあります。

特に、VPS上で複数チャートにEAやインジケーターを入れる場合、OnTick処理、Object操作、外部通信、ログ出力、OnDeinit処理の負荷が積み重なります。短い検証では見えなかった設計上の弱点が、長時間運用で表面化することがあります。

長時間稼働EAで重要なのは、処理を増やすことではなく、OnTickで毎回やる処理、OnTimerへ分ける処理、OnDeinitで最小限にする処理、起動時に監査する処理を分けることです。

この記事では、MQL5 EAを長時間安定稼働させるための設計判断を、開発実務ノートとして整理します。完成EAソース全文、外部シート制御ロジック、認証ロジック、endpoint、token、URL実値、実運用set値、中核Entry条件は公開しません。

本記事は、投資判断や売買指示ではなく、MQL5 EA・インジを安定稼働させるための技術記事です。

この記事で扱う実務課題

項目この記事で扱う内容扱わない内容
目的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を長時間安定稼働させるには、売買ロジックだけでなく、OnTick負荷、OnTimer分離、Object管理、OnDeinit最小化、ログ抑制を設計することが重要です。

特に、次の4点を分けると、フリーズ、重さ、Object残存、ログ過多の問題を減らしやすくなります。

  • OnTick:tickごとに本当に必要な処理だけを行う
  • OnTimer:表示更新、Object監査、summaryログ、外部通信などを必要間隔で行う
  • Object lifecycle:Object名prefix、所有範囲、差分更新、残存監査を決める
  • OnDeinit:timer停止、handle解放、軽量cleanupに絞る

また、ログファーストは重要ですが、ログを無制限に出すことではありません。状態変化、失敗理由、summaryを中心にし、バックテストや配布版ではログ粒度を調整することが実務的です。

完成EAの中核ロジックを公開しなくても、長時間稼働に必要な責務分離と負荷対策を理解しておくことで、開発依頼、既存EA改修、検証、不具合調査、販売後サポートの品質を上げやすくなります。

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