技術講座

MQL5長時間稼働・安定化完全ガイド|メモリ・ハンドル・ログ・オブジェクト管理の基本

MQL5長時間稼働・安定化完全ガイド|メモリ・ハンドル・ログ・オブジェクト管理の基本の内容を表す、handle・log・objectの確認用アイキャッチ画像
EAファンクラブ

MQL5でEAやインジケーターを長時間動かす場合、最初は問題なく動いていても、時間の経過とともにチャートが重くなる、パネルが反応しにくくなる、ログが増えすぎる、Objectが残る、再セット後に表示が混ざる、といった問題が出ることがあります。

これらは、単純なコンパイルエラーとは違い、短時間の起動確認だけでは見つけにくい問題です。OnTickに重い処理を置きすぎている、indicator handleを毎回作っている、不要なログを出し続けている、Objectを毎tick再作成している、OnDeinitで重いcleanupを行っている、外部連携の失敗処理が本体処理と混ざっている、といった設計上の原因が関係する場合があります。

この記事では、MQL5のEA・インジケーターを長時間稼働させる時に確認したい、メモリ、indicator handle、IndicatorRelease、ログ抑制、Object管理、OnTimer分離、VPS運用、フリーズ・ブラックアウト時の確認項目を整理します。売買判断、推奨銘柄、推奨ロット、運用成績に関する助言は扱いません。技術的な安定化と問い合わせ前整理に限定します。

この記事で扱う範囲

本記事では、MQL5 EA・インジケーターを長時間動かす時の安定化に関係する実務項目を扱います。対象は、EA、インジケーター、パネルEA、外部連携EA、通知機能付きツール、複数銘柄・複数時間足を扱うツールです。

主に扱う内容

対象この記事での扱い確認ポイント
OnTick負荷tickごとの重い処理を避ける取得回数、ログ量、Object更新
OnTimer分離定期処理をtick処理から分ける外部制御、通知、UIメンテナンス
indicator handlehandle作成と解放を整理する作成場所、再利用、IndicatorRelease
ログ抑制同じログを出し続けない状態変化時、summary、debug切替
Object管理チャートObjectの増加や残存を整理するprefix、差分更新、startup audit
OnDeinit終了時の処理を軽くするtimer停止、handle解放、広範囲削除回避
VPS運用長時間稼働時の確認ログ、スクリーンショット、再起動履歴
問い合わせ前整理再現情報を集めるEA名、バージョン、set、ログ、発生時刻

この記事で扱わないこと

本記事では、売買ロジックの良し悪し、運用成績の評価、具体的なパラメータ調整、特定環境への誘導は扱いません。MQL5プログラムを長時間動かすための構造、ログ、表示、終了処理、確認手順に絞ります。

MT5 EAやインジが重くなる主な原因

EAやインジケーターが重くなる原因は、1つとは限りません。短時間では目立たなくても、長時間稼働や複数チャート運用、VPS環境、バックテスト、外部連携、Object表示を組み合わせると、負荷が蓄積して見える場合があります。

重くなる原因表

原因起きやすい症状確認すること整理方針
OnTickに処理を詰め込みすぎtickが多い時に重くなるOnTick内の処理量OnTimerや新バー時処理へ分ける
indicator handleを毎回作成徐々に重くなる、再初期化が多いiMA、iATR等の呼び出し位置OnInitで作成し再利用する
CopyBufferやCopyRatesが多すぎる複数銘柄・複数時間足で重い取得対象、取得本数、更新頻度必要最小限にする
ログが多すぎるExpertsログが読みにくい、テスターが重いPrintの頻度、同一ログの反復状態変化時とsummaryへ分ける
Objectを毎tick再作成ちらつき、表示遅延、パネル不安定ObjectCreate / ObjectDeleteの頻度差分更新にする
OnDeinitで重いcleanup削除時・時間足変更時に不安定全Object走査、大量削除軽量終了処理にする
外部連携を同期的に詰め込む通信失敗時に本体処理が重いWebRequestや通知処理の位置OnTimerやqueueへ分ける
複数チャートで同じ処理を重複VPS全体が重くなる設置数、同時稼働数役割ごとに設置範囲を整理する

短時間確認だけでは分からない問題

起動直後は軽く見えても、数時間後や翌日に重くなる場合があります。長時間稼働では、ログ量、Object数、handle作成回数、外部連携の失敗回数、通知queue、再描画回数などが影響します。

そのため、長時間稼働確認では、単に「起動したか」だけではなく、時間経過後のExpertsログ、Journalログ、チャート表示、Object残存、CPU使用率、VPSの応答、MT5の操作感も確認します。

OnTickに重い処理を置かない理由

OnTick は、EAが新しいtickを受け取るたびに呼ばれます。tickが多い銘柄や時間帯では短時間に何度も呼ばれるため、ここに重い処理を置くと、処理が追いつきにくくなります。

OnTickに置くべき処理は、価格変化に依存する軽量な確認、現在価格の取得、新バー判定、必要な評価helperの呼び出しなどです。外部通信、全Object走査、全履歴走査、大量ログ、パネル全再構築などは、OnTimerや別helperへ分けることを検討します。

OnTickに置く処理・分ける処理

処理OnTick向きか理由代替先
現在価格取得向いているtickごとに変化するため–
新バー判定向いている軽量で制御しやすい–
軽量な状態確認条件付きで向いている処理量を限定できる場合helperへ分離
外部制御取得通常は不向き通信待ちや失敗処理が混ざるOnTimer
通知送信通常は不向きtransport処理を分けたいnotification queue / OnTimer
全Object再作成不向き描画負荷が大きい差分更新 / OnTimer
履歴全走査不向き長期間稼働で重い範囲限定 / 必要時のみ
詳細ログの連続出力不向きログ過多になる状態変化時ログ

OnTickを軽く保つ最小例

void OnTick()
{
   UpdateTickSnapshot();

   if(IsNewBar())
   {
      EvaluateOnNewBar();
   }

   RunLightRuntimeCheck();
}

この例では、OnTick内に長い処理を直接書かず、役割ごとのhelperを呼び出しています。重い処理を後から見つけやすくするためにも、OnTickは短く保つ方が確認しやすくなります。

ログ出力が多すぎる時の問題

ログは、不具合確認や販売後サポートで重要です。ただし、同じログが毎tick出続けると、必要な情報が埋もれます。バックテストでは、ログ量が多すぎることで確認や実行時間に影響することもあります。

長時間稼働では、ログを「常時出すもの」「状態変化時だけ出すもの」「debug時だけ出すもの」「終了時summaryにするもの」に分けます。

ログ抑制方針表

ログ種別出力タイミング向いている内容注意点
INITログ起動時version、symbol、設定、handle、timer機密値は出さない
STATE_CHANGE状態が変わった時OKからNG、NGからOKなど同じ状態を連続出力しない
SUMMARY一定間隔または終了時成功数、失敗数、skip数短く比較しやすくする
WARN異常候補発生時取得失敗、handle未準備、Object不足reasonを分ける
DEBUG検証時のみ詳細な内部値通常時はOFFにする
SNAP手動操作または問い合わせ時現在状態のまとめ再現情報として使う
DEINIT終了時終了理由、timer停止、summary重い処理と混ぜない

状態変化時だけ出す例

void PrintWhenStateChanged(const string label,
                           const string current)
{
   static string last = "";

   if(current != last)
   {
      Print("STATE_CHANGE: ", label,
            " from=", last,
            " to=", current);
      last = current;
   }
}

この例では、同じ状態が続く間はログを出さず、状態が変わった時だけ出します。実務では、labelごとに前回値を持つなど、機能別に管理します。

indicator handleの管理

不要になったインジケーターハンドルはIndicatorRelease()で解放できます。長時間稼働では、再初期化や条件変更のたびに新しいハンドルを作り続けず、生成・再利用・解放のライフサイクルを明確にしてください。

MQL5では、iMA、iATR、iBands などの関数でindicator handleを作成し、CopyBuffer で値を取得する構成がよく使われます。長時間稼働では、このhandle管理が重要です。

handleを毎tick作成したり、再初期化のたびに不要なhandleを残したりすると、確認しづらい不安定要因になります。基本は、OnInitで作成し、必要な期間は再利用し、終了時や不要になった時に解放する流れです。

handle管理表

場面行うこと確認ログ例
OnInit必要なhandleを作成するHANDLE_INIT: name=MA handle=…
作成失敗時INVALID_HANDLEを確認するHANDLE_FAIL: reason=INVALID_HANDLE
通常稼働作成済みhandleを再利用するBUFFER_LOAD: copied=…
CopyBuffer失敗時取得本数とreasonを記録するBUFFER_FAIL: copied=…
設定変更時必要に応じて再作成するHANDLE_REBUILD: reason=PARAM_CHANGED
OnDeinit不要handleを解放するHANDLE_RELEASE: ok=Y/N

handle作成の最小例

int g_ma_handle = INVALID_HANDLE;

int OnInit()
{
   g_ma_handle = iMA(_Symbol, PERIOD_CURRENT, 20, 0, MODE_SMA, PRICE_CLOSE);

   if(g_ma_handle == INVALID_HANDLE)
   {
      Print("HANDLE_INIT_FAIL: name=MA");
      return INIT_FAILED;
   }

   Print("HANDLE_INIT: name=MA status=OK");
   return INIT_SUCCEEDED;
}

この例では、OnInitでhandleを作成しています。実務では、銘柄、時間足、期間、適用価格などをログに出すと、設定確認がしやすくなります。

IndicatorReleaseの考え方

IndicatorRelease は、不要になったindicator handleを解放するために使います。長時間稼働や再初期化が関係するEA・インジケーターでは、handleの作成と解放の流れを整理しておくことが重要です。

ただし、IndicatorReleaseを呼べばすべて解決するというより、そもそもhandleを無駄に作らない設計が先です。毎tickでhandleを作成し、それを毎回解放するような構造ではなく、必要なhandleを作成して再利用する設計にします。

IndicatorReleaseの最小例

void OnDeinit(const int reason)
{
   if(g_ma_handle != INVALID_HANDLE)
   {
      bool released = IndicatorRelease(g_ma_handle);
      Print("HANDLE_RELEASE: name=MA ok=", released ? "Y" : "N");
      g_ma_handle = INVALID_HANDLE;
   }

   EventKillTimer();
}

この例では、OnDeinitでhandleを解放し、変数を INVALID_HANDLE に戻しています。解放対象、解放結果、終了理由をログに残すと確認しやすくなります。

IndicatorReleaseで確認すること

確認項目内容補足
handleが有効かINVALID_HANDLEではないか無効handleを処理しない
どこで作成したかOnInit、再構築処理など作成場所と解放場所を対応させる
どこで解放するかOnDeinit、再構築前など不要になったタイミングを明確にする
再利用しているか毎tick作成していないか無駄な作成を避ける
ログがあるか作成・失敗・解放を確認できるか問い合わせ時に役立つ

チャートオブジェクトが増えすぎる問題

パネルUI、ライン、ラベル、ボタン、ステータス表示、通知用マークなどを使うEA・インジケーターでは、チャートObjectの管理が重要です。Objectを毎tick作成し続けたり、削除対象を絞らずに増やし続けたりすると、表示が重くなる原因になります。

Objectは、作成・更新・削除・残存時の扱いを仕様化します。特に、表示専用Objectと、実ロジックに影響するObjectを分けることが重要です。

Object管理の基本表

分類例扱い
panel-controlボタン、入力欄、タブパネル管理で作成・更新する
status-display状態ラベル、HELP、SNAP表示差分更新する
visual-only確認用ライン、説明ラベル実ロジック参照には使わない
logic-reference判定に関係し得る管理ライン残存Objectを無条件採用しない
manualユーザーが手動で引いたライン原則削除しない
unknownowner不明Object削除せず監査対象にする

差分更新を基本にする

Objectは、毎回削除して作り直すのではなく、なければ作る、あれば必要なプロパティだけ更新する方式にします。これにより、ちらつきや描画負荷を抑えやすくなります。

void UpsertStatusLabel(const string name,
                       const string text)
{
   if(ObjectFind(0, name) < 0)
   {
      ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
      ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
      ObjectSetInteger(0, name, OBJPROP_XDISTANCE, 10);
      ObjectSetInteger(0, name, OBJPROP_YDISTANCE, 20);
   }

   ObjectSetString(0, name, OBJPROP_TEXT, text);
}

この例では、Objectがなければ作成し、存在する場合は文字だけ更新します。実務では、前回のtextと同じなら更新しない、一定秒数ごとに更新するなど、さらに負荷を抑える設計も考えられます。

OnDeinit cleanupの注意

EAやインジケーターの終了時に、すべてのObjectを削除したくなることがあります。しかし、OnDeinitで全Object走査や大量削除を行うと、削除時、時間足変更時、再コンパイル時、チャート切替時に不安定化する場合があります。

OnDeinitでは、見た目の完全cleanupよりも、異常終了しないことを優先します。timer停止、handle解放、Comment解除などの軽量処理を中心にし、Object整理はprefixや用途を限定します。

Object lifecycle注意表

場面推奨処理避けたい処理
OnInit必要Object作成、残存Object監査理由なく全削除する
通常稼働差分更新、必要箇所だけ更新毎tick全再作成
OnTimer軽量メンテナンス、Object数確認無制限走査
OnChartEventクリック受付、再配置要求重い再構築
OnDeinittimer停止、handle解放、最小cleanup全Object削除、大量ループ
再セット時startup audit、残存Object分類古いObjectの無条件採用

削除対象をprefixで絞る

Objectを削除する場合は、自EAが作成したObjectだけを対象にします。手動ライン、他EA、他インジケーターのObjectを巻き込まないために、Object名prefixを設計しておきます。

bool IsMyObject(const string name)
{
   return (StringFind(name, "EAF_STABLE_") == 0);
}

このようにprefixで対象を絞ると、cleanupやstartup auditの対象を管理しやすくなります。

チャートオブジェクトの生存期間と終了処理を設計する

以下のコード例は、個別の処理と確認項目を読むためのものです。複数の例をそのまま一つのEAへ連結する前提ではありません。初期化、値の取得、状態更新、終了処理をそれぞれの例で区別して確認してください。

EA削除時に起きやすいObject問題

チャート上にラインやラベルを描画するEAでは、削除時や再セット時に次のような問題が起きることがあります。

現象原因になりやすい設計改善方針
EA削除時にMT5が重くなるOnDeinitで全Objectを走査・削除しているOnDeinitの責務を最小化する
ラインが残る削除対象の整理が不足している残ること自体より、再セット時に誤参照しないことを優先する
再セット後に古いラインを使ってしまう残存Objectを現在セッションのObjectと区別していないsession tag、startup audit、residual ignoreを使う
手動ラインや他EAのObjectを消してしまうprefixやownerを見ずにObjectDeleteしているowner prefix、kind、side、sessionを確認して対象を限定する
長時間稼働で表示が重くなる毎tickで全削除・全再作成している固定Objectの差分更新、throttle、OnTimer分離へ寄せる

Object管理では、見た目を完全に整理することよりも、EA本体が異常終了しないこと、売買判定が古いObjectを誤参照しないことを優先します。

OnDeinitで広範囲cleanupをしない理由

EA削除時にチャート上のObjectをすべて消したくなる場合があります。しかし、OnDeinitで広範囲のObject走査や大量削除を行うと、かえって不安定化することがあります。

特に避けたいのは、次のような処理です。

  • OnDeinit内でObjectsTotalを使って全Objectを走査する
  • prefixが曖昧なObjectをまとめて削除する
  • 手動ラインや他EAのObjectまで削除対象に含める
  • 削除失敗時に再試行ループする
  • ポジション状態に依存した重い処理をOnDeinitで行う
  • 削除と再描画を同じ終了処理内で実行する

OnDeinitは、EAの終了時に呼ばれる処理です。ここで重い処理や不安定な処理を詰め込むと、EA削除時のフリーズ、チャートブラックアウト、Abnormal termination、再セット不能などの原因になります。

そのため、OnDeinitでは、Commentのクリア、timer停止、indicator handle解放、軽量な自EAステータスObjectの整理など、最小限の終了処理に寄せます。

Objectが残ることとEAが異常終了することのリスク差

Object管理では、次の2つを分けて考える必要があります。

状態リスク優先度
表示ラインが残る見た目が残る。再セット時に監査できれば致命傷ではない中
EA削除時に異常終了するEAやチャートが不安定化し、再現性も落ちる高
残存ラインを実ロジックへ使う古いObjectを基準に判定してしまう高
手動ラインを消す利用者の作業内容を壊す高
表示専用Objectを残す監査ログで残存扱いにできれば許容しやすい低〜中

重要なのは、Objectが残ることをすべて悪としないことです。残ってもよいObjectと、実ロジックへ誤って使ってはいけないObjectを分け、再セット時に安全側で監査します。

visual-only objectとlogic-reference objectの違い

Object lifecycleを設計する時は、チャートObjectを用途別に分類します。

分類例扱い
visual-only平均価格ライン、TP目安ライン、Trail開始ライン、説明ラベル表示専用。残存しても実ロジックへ使わない
logic-referenceTrail SLライン、仮想ライン、Skipラインなど実ロジックに影響し得るため、残存時は無条件採用しない
panel-controlボタン、入力欄、タブ、ステータス枠パネルlifecycleで管理する
overlay-onlyMA、BB、矢印、補助表示best-effort。失敗してもEA本体を止めない
manual / external手動ライン、他EAが作成したObject原則削除しない

たとえば、平均価格ラインやTP目安ラインは表示補助として扱いやすい一方で、Trail SLや仮想ラインのように実ロジックの参照候補になり得るObjectは、起動前から残っていたものをそのまま採用すると危険です。

startup auditとは

startup auditとは、EAをチャートへセットした時に、すでに残っている管理Objectを確認し、どのObjectを表示専用として扱うか、どのObjectを実ロジックから除外するかをログに残す処理です。

startup auditで確認する主な項目です。

確認項目意味
Object名自EAの管理Objectかどうかを判断する
kindAVG、TP、TRAIL_SL、KASOU_LINE、SKIP_LINEなどの分類
sideBUY側、SELL側、方向なしを分ける
price表示価格を確認する
actionVISUAL_ONLY、IGNORE_FOR_LOGIC、LOG_ONLYなどの扱い
reasonなぜ削除しないか、なぜ無視するか

この監査により、EA削除時に無理にObjectを消さなくても、再セット時に「古いObjectとして検出し、実ロジックには使わない」という安全設計ができます。

residual ignoreとは

residual ignoreとは、再セット前から残っていたObjectを、現在のEAセッションで作られたObjectではないものとして扱い、実ロジックから除外する考え方です。

特に、次のようなObjectはresidual ignoreの対象になりやすいです。

  • Trail SLライン
  • 仮想ライン
  • Skipライン
  • sideやsessionが現在状態と一致しない管理ライン
  • 現在セッションのsession tagが付いていないObject

残存Objectを消すのではなく、色や線種を変えて「古いライン」として表示し、ExpertsログにもRESIDUAL_IGNOREとして残すと、調査時に分かりやすくなります。

current session objectだけ採用する考え方

Objectを実ロジックへ使う場合は、「このObjectは現在セット中のEAが生成したものか」を確認する必要があります。

考え方は次の通りです。

判定扱い
現在セッションのtagがある実ロジック参照候補として扱える
現在セッションのtagがない起動前残存として無視する
visual-only分類表示専用として扱い、実ロジックへ使わない
logic-reference分類current sessionでなければIGNORE_FOR_LOGIC
owner不明削除しない。監査または無視に留める

この設計にすると、EA削除時のcleanupを重くしなくても、再セット時に安全側で状態を作り直せます。

実コード断片1:Object種別を分類する

以下は、実際のEAで使う考え方を学習用に簡略化したコード例です。完成EAのソース全文ではなく、構造を理解するための抜粋です。

// 学習用に一般化したObject分類例です。
// 実運用EAの完成ソースではありません。

string ResolveObjectKind(string name)
{
   if(StringFind(name, "Logic") != 0)
      return "NONE";

   if(StringFind(name, "-Avg") >= 0)
      return "AVG";

   if(StringFind(name, "-TpLine") >= 0)
      return "TP";

   if(StringFind(name, "-TrailStart") >= 0)
      return "TRAIL_START";

   if(StringFind(name, "-TrailSl") >= 0)
      return "TRAIL_SL";

   if(StringFind(name, "-VirtualLine") >= 0)
      return "VIRTUAL_LINE";

   if(StringFind(name, "-Skip") >= 0)
      return "SKIP_LINE";

   return "NONE";
}

bool IsLogicReferenceKind(string kind)
{
   return (kind == "TRAIL_SL" ||
           kind == "VIRTUAL_LINE" ||
           kind == "SKIP_LINE");
}

実際のEAでは、認証、外部制御、口座条件、エラー処理、ログ出力などを分けて設計します。この記事では、公開できる範囲の構造だけを取り上げています。

このコードの役割

処理役割
ResolveObjectKindObject名から管理ラインの種類を分類する
AVG / TP / TRAIL_START主に表示補助として扱いやすい分類
TRAIL_SL / VIRTUAL_LINE / SKIP_LINE実ロジック参照に影響し得る分類
IsLogicReferenceKind残存時に無条件採用してはいけないObjectを判定する

この段階では、まだObjectを削除しません。まず「何のObjectか」を分類し、次に「現在セッションのObjectか」を確認します。

実コード断片2:startup auditで残存Objectを監査する

以下は、起動時に残っているObjectを監査し、危険な残存Objectを実ロジックから除外する考え方を簡略化した例です。

// 学習用に一般化したstartup audit例です。
// 実運用EAの完成ソースではありません。

string g_sessionTag = "";

void InitObjectSessionTag()
{
   g_sessionTag = "SESSION:" + TimeToString(TimeLocal(), TIME_DATE | TIME_SECONDS);
}

void PrintObjectAudit(string side, string eventName, string detail)
{
   Print("DIAG/OBJECT_AUDIT: side=", side,
         " event=", eventName,
         " detail=", detail);
}

void AuditStartupObjects()
{
   int found = 0;
   int ignored = 0;

   for(int i = ObjectsTotal(0, 0, OBJ_HLINE) - 1; i >= 0; i--)
   {
      string name = ObjectName(0, i, 0, OBJ_HLINE);
      string kind = ResolveObjectKind(name);

      if(kind == "NONE")
         continue;

      found++;

      string action = "VISUAL_ONLY";

      if(IsLogicReferenceKind(kind))
      {
         action = "IGNORE_FOR_LOGIC";
         ignored++;
      }

      double price = ObjectGetDouble(0, name, OBJPROP_PRICE);

      PrintObjectAudit("NONE", "STARTUP_FOUND",
                       "name=" + name +
                       " kind=" + kind +
                       " action=" + action +
                       " price=" + DoubleToString(price, _Digits));
   }

   PrintObjectAudit("NONE", "STARTUP_SUMMARY",
                    "found=" + IntegerToString(found) +
                    " ignored=" + IntegerToString(ignored) +
                    " policy=NO_DEINIT_CLEANUP");
}

このコードの役割

処理役割
InitObjectSessionTag現在EAセット用のsession tagを作る
AuditStartupObjects起動時に残存Objectを確認する
STARTUP_FOUND検出したObjectを1件ずつ記録する
IGNORE_FOR_LOGIC実ロジック参照から除外する
STARTUP_SUMMARY検出数と無視数を要約する

この設計では、EA削除時にすべてを消すのではなく、再セット時に残存Objectを見つけて安全側で扱います。

実コード断片3:residual ignoreで古いObjectを使わない

以下は、現在セッションのObjectだけを実ロジックで参照し、それ以外を残存Objectとして無視する考え方の例です。

// 学習用に一般化したresidual ignore例です。
// 実運用EAの完成ソースではありません。

bool IsCurrentSessionObject(string name)
{
   if(ObjectFind(0, name) < 0)
      return false;

   string tag = ObjectGetString(0, name, OBJPROP_TOOLTIP);
   return (tag == g_sessionTag);
}

void StampCurrentSessionObject(string name)
{
   if(ObjectFind(0, name) < 0)
      return;

   ObjectSetString(0, name, OBJPROP_TOOLTIP, g_sessionTag);
}

bool CanUseObjectForLogic(string name)
{
   string kind = ResolveObjectKind(name);

   if(kind == "NONE")
      return false;

   if(!IsLogicReferenceKind(kind))
      return true;

   if(IsCurrentSessionObject(name))
      return true;

   PrintObjectAudit("NONE", "RESIDUAL_IGNORE",
                    "name=" + name +
                    " kind=" + kind +
                    " reason=NOT_CURRENT_SESSION");

   return false;
}

このコードの役割

処理役割
StampCurrentSessionObject現在EAが作成・更新したObjectにsession tagを付ける
IsCurrentSessionObjectそのObjectが現在セッションのものか確認する
CanUseObjectForLogic実ロジックで参照してよいObjectか判定する
RESIDUAL_IGNORE古いObjectを使わず、ログに理由を残す

このようにしておくと、古いTrailラインや仮想ラインが残っていても、現在セッションで作られたObjectでなければ実ロジックから除外できます。

実コード断片4:OnDeinitを最小化する

以下は、OnDeinitで広範囲cleanupを行わず、終了時の責務を最小化する考え方を示した学習用コード例です。

// 学習用に一般化したOnDeinit最小化例です。
// 実運用EAの完成ソースではありません。

void ReleaseIndicatorHandles()
{
   // 実際は使用中のhandleだけを個別に解放します。
}

void DeleteStatusObjectsOnly()
{
   // EAステータス表示など、軽量で自EA所有が明確なObjectだけを対象にします。
}

void OnDeinit(const int reason)
{
   Comment("");

   EventKillTimer();

   DeleteStatusObjectsOnly();

   // 広範囲のObject走査・全削除は行わない。
   // Avg / TP / Trail / Virtual / Skip などは、
   // 再セット時のstartup auditとresidual ignoreで扱う。

   ReleaseIndicatorHandles();

   Print("DIAG/DEINIT: event=MINIMAL_CLEANUP reason=", reason);
}

このコードの役割

処理役割
Comment(“”)チャート左上コメントを消す
EventKillTimertimer処理を止める
DeleteStatusObjectsOnly自EA所有が明確で軽量なステータスObjectだけ整理する
ReleaseIndicatorHandlesインジケーターハンドルを解放する
全Object削除をしない削除時の不安定化を避ける

OnDeinitでは、「消せるものを全部消す」のではなく、「終了時に安全にできることだけを行う」と考える方が安定します。

再セット時に確認するOBJECT_AUDITログ

Object lifecycle設計では、チャート上の見た目だけでなく、Expertsログを確認することが重要です。

再セット時には、次のようなログを確認します。

DIAG/OBJECT_AUDIT: side=NONE event=STARTUP_FOUND detail=name=... kind=TRAIL_SL action=IGNORE_FOR_LOGIC
DIAG/OBJECT_AUDIT: side=NONE event=RESIDUAL_IGNORE detail=name=... kind=SKIP_LINE reason=NOT_CURRENT_SESSION
DIAG/OBJECT_AUDIT: side=NONE event=STARTUP_SUMMARY detail=found=3 ignored=2 policy=NO_DEINIT_CLEANUP
DIAG/OBJECT_AUDIT: side=NONE event=CLEANUP_PLAN detail=action=LOG_ONLY reason=CLEANUP_DISABLED
ログ見るポイント
STARTUP_FOUND残存Objectを検出しているか
VISUAL_ONLY表示専用として扱っているか
IGNORE_FOR_LOGIC実ロジックへ使わないObjectを分けているか
RESIDUAL_IGNORE現在セッション外のObjectを無視しているか
CLEANUP_PLAN削除ではなく計画・ログ確認に留めているか
STARTUP_SUMMARY検出数・無視数・方針が要約されているか

ログで「古いObjectを検出したが、実ロジックには使っていない」と確認できれば、Objectが残っていても安全側に整理できています。

よくある失敗例

失敗1:OnDeinitで全Objectを削除する

最も避けたいのは、OnDeinitでチャート上のObjectを広範囲に走査し、まとめて削除する処理です。自EA以外のObjectを巻き込むリスクがあり、削除時の不安定化にもつながります。

失敗2:Object名だけで現在状態を判断する

Object名が同じでも、前回セット時の残存Objectである可能性があります。名前だけでなく、現在セッションのtagや監査結果を使って判断する必要があります。

失敗3:表示Objectを実ロジックの真実として使う

ラインやラベルは、表示更新の失敗、残存、再描画遅延の影響を受けます。実ロジックの基準はruntime stateで管理し、Objectは表示補助として扱う方が安全です。

失敗4:表示専用Objectとロジック参照Objectを混ぜる

平均価格ラインやTP目安ラインと、Trail SLや仮想ラインを同じ削除・採用方針で扱うと危険です。用途ごとに分類し、扱いを分ける必要があります。

失敗5:削除失敗時に再試行ループする

ObjectDeleteに失敗した場合、OnDeinit内で何度も再試行すると不安定化します。削除失敗はログ化し、次回起動時のauditやmanual確認へ回す方が安全です。

長時間稼働EAでのObject更新方針

長時間稼働を前提にするEAでは、Objectの作り直し回数を減らすことも重要です。

避ける処理推奨する処理
毎tickでObjectDeleteとObjectCreateを繰り返す既存Objectの価格・テキスト・色だけ差分更新する
毎tickでObjectsTotalを全走査するOnTimerや低頻度maintenanceへ分離する
全Objectを同じprefixで雑に扱うowner、kind、side、sessionを分ける
表示失敗でEA本体を止める表示はbest-effortにし、売買処理と分離する
Objectを判定truthとして使うruntime stateを判定truthにし、Objectは表示補助にする

表示は利用者にとって重要ですが、表示失敗がそのまま発注・決済・リスク管理に波及する構造は避けるべきです。

OnTimerへ分離する処理

長時間稼働では、OnTickに処理を集中させず、一定間隔でよい処理をOnTimerへ分けることが有効です。外部制御、認証確認、通知queue、UIメンテナンス、Object数確認、heartbeatログなどはOnTimerに向いています。

OnTimerへ分離する処理表

処理OnTimer向きの理由確認ログ例
外部制御取得tickごとに取得する必要が薄いTIMER/EXTCTRL: state=OK
認証状態確認一定間隔で十分な場合が多いTIMER/AUTH: status=OK
通知queue処理本体処理とtransportを分けられるTIMER/NOTIFY: sent=…
UIメンテナンス毎tick再描画を避けられるTIMER/UI: diff_update=Y
Object監査低頻度で十分な場合が多いTIMER/OBJECT: count=…
heartbeat長時間稼働中の生存確認TIMER/HEARTBEAT: alive=Y
summaryログログ過多を避けられるTIMER/SUMMARY: warn=…

OnTimer分離の最小例

int OnInit()
{
   EventSetTimer(5);
   Print("INIT: timer=5sec");
   return INIT_SUCCEEDED;
}

void OnTimer()
{
   RefreshExternalStateIfNeeded();
   ProcessNotificationQueue();
   UpdatePanelIfChanged();
   PrintHeartbeatIfNeeded();
}

void OnDeinit(const int reason)
{
   EventKillTimer();
}

OnTimerに分けても、処理量が多すぎれば重くなります。周期、処理対象、ログ量、Object更新量を管理してください。

外部連携と長時間稼働

WebRequest、通知、Google Sheets連携、外部認証、外部制御などを含むEAでは、長時間稼働時の確認項目が増えます。通信失敗、stale、設定不備、通知失敗、外部側の応答変化などを、本体処理と分けて扱う必要があります。

外部連携では、Webhook URL、GAS URL、APIキー、認証トークン、口座番号、個人情報などの実値をログや画面に出さないようにします。ログには、状態分類、成功/失敗、reason、最終取得時刻、stale判定などを出します。

外部連携の長時間稼働確認表

確認項目見る内容ログ例
取得状態成功、失敗、stale、invalidEXT_STATE: status=…
最終成功時刻いつ最後に成功したかEXT_LAST_OK: time=…
失敗理由通信失敗、形式不正、応答なしEXT_FAIL: reason=…
通知queue未送信が溜まっていないかNOTIFY_QUEUE: size=…
通知失敗transport failureとpayload failureを分けるNOTIFY_FAIL: reason=…
機密値ログに実値が出ていないかmasked=Y
本体影響外部失敗で本体処理が止まりすぎていないかEXT_POLICY: scope=…

VPSで長時間動かす時の確認

VPSでEAやインジケーターを長時間動かす場合、MQL5コードだけでなく、MT5本体、VPSのリソース、同時稼働チャート数、ログ量、通信状態、Windows更新、再起動履歴なども確認対象になります。

VPS環境では、画面を常に見ていない時間が長くなります。そのため、Expertsログ、Journalログ、定期summary、SNAP、スクリーンショット、発生時刻の記録が重要です。

VPS長時間稼働チェック表

確認項目内容記録すること
MT5起動状態MT5が落ちていないか起動時刻、再起動履歴
EA稼働状態対象EAが動いているかINITログ、heartbeat
チャート数同時稼働が多すぎないか設置チャート一覧
ログ量Experts / Journalが過剰でないか同一ログの反復有無
CPU / メモリVPS全体が重くないかリソース状況
通信状態外部連携がある場合の通信成功 / 失敗 / stale
表示状態パネルやObjectが崩れていないかスクリーンショット
時間発生時刻を記録するPC時刻、サーバー時刻の区別

VPSで確認しやすいログ

ログ名目的出力タイミング
INIT/SUMMARY起動状態の確認EA起動時
TIMER/HEARTBEAT長時間稼働の生存確認一定間隔
STATE_CHANGE状態変化の確認変化時
EXT_STATE外部連携状態取得時、変化時
OBJECT_AUDITObject残存や増加の確認起動時、必要時
DEINIT/SUMMARY終了理由の確認EA終了時

フリーズ・ブラックアウト時の確認

チャートが固まる、黒くなる、パネルが消える、時間足変更で戻る、EA削除時に不安定になる、といった症状では、発生時刻と直前ログが重要です。

フリーズやブラックアウトは、Object更新、描画負荷、OnDeinit cleanup、外部連携、ログ過多、他インジケーターとの競合、VPSリソースなど、複数原因が関係する場合があります。

フリーズ時の確認表

確認項目見る内容提出するとよい情報
発生時刻いつ発生したか日時、チャート時間、PC時刻
直前のExpertsログ同じログの反復、ERROR、WARN発生前後のログ
JournalログMT5側の異常や接続状態同時刻付近のログ
スクリーンショット表示崩れ、黒画面、Object残存チャート全体画像
再現手順時間足変更、EA削除、再セットなど何をした直後か
設置状況同じチャートにあるEA・インジ設置一覧
外部連携通信失敗や通知失敗機密値を除いた状態ログ
VPS状態CPU、メモリ、再起動履歴可能な範囲で記録

時間足変更で戻る場合

時間足変更で表示が戻る場合、パネル再配置、Object再作成、ChartRedraw、OnChartEvent、OnTimerメンテナンス、Object欠損チェックが関係している可能性があります。表示だけの問題なのか、EA内部状態も止まっているのかを分けて確認します。

販売後サポートで必要なログ

販売後サポートや不具合調査では、画面上の状態だけでは判断しにくいことがあります。長時間稼働の問題では、INIT、TIMER、STATE_CHANGE、OBJECT_AUDIT、HANDLE、DEINIT、SNAPのようなログがあると、原因の確認に役立ちます。

問い合わせ前情報表

情報内容理由
EA / インジ名対象ファイル名対象を特定するため
バージョン画面表示やINITログのversion修正版との差分確認
setファイル使用した設定再現条件の確認
対象チャート銘柄、時間足環境差の確認
発生時刻いつ起きたかログ照合のため
ExpertsログEA・インジ側ログ処理状態の確認
JournalログMT5本体側ログ接続やターミナル状態の確認
スクリーンショット表示崩れやObject状態画面状態の確認
再現手順何をした後に起きたか検証再現のため
VPS情報稼働時間、再起動、リソース長時間稼働の確認

SNAPログに入れるとよい項目

項目内容注意点
versionEA・インジの版ファイル名と一致させる
symbol / timeframe対象チャート複数銘柄時は参照銘柄も分ける
timer状態OnTimerの有効状態周期も記録する
handle状態主要handleの作成状態無効handleを確認する
object countObject数自EA prefix範囲に絞る
last reason直近の警告や停止理由短いreasonで統一する
external state外部連携の状態URLやtokenは出さない
log profile通常 / debug / tester等ログ量の確認に使う

安定化チェック表

長時間稼働前には、以下の観点で確認すると、後から問題を原因を確認しやすくなります。すべてを一度に完璧にするのではなく、対象EAやインジケーターの機能に応じて必要項目を確認します。

長時間稼働チェック表

No確認項目期待する状態
1OnInitで設定確認ログが出るversion、symbol、timer、handle状態が分かる
2OnTimerを使う場合、OnDeinitで停止するEventKillTimerが呼ばれる
3indicator handleを毎tick作成していないOnInit作成、再利用が基本
4不要handleを解放しているIndicatorReleaseの対象が整理済み
5同じログが大量反復しない状態変化時ログまたはsummary
6Objectを毎tick全再作成していない差分更新が基本
7Object名prefixがある他EAや手動Objectと分離できる
8OnDeinitで広範囲Object削除をしていない軽量終了処理を優先
9外部連携失敗reasonが分かるfetch error、invalid、staleを分ける
10機密値がログに出ていないURL、token、口座番号などを出さない
11VPS長時間稼働でログ確認できるheartbeatやsummaryがある
12問い合わせ前情報を集められるSNAP、Experts、Journal、スクリーンショット

オブジェクト管理の不具合を報告する前の記録

問い合わせ前に確認するポイント

EA削除時や再セット時に表示が残る、重い、フリーズする場合は、問い合わせ前に次の情報を整理してください。

確認項目内容
発生タイミングEA削除時、時間足変更時、再セット時、MT5再起動時のどれか
残っているObject平均ライン、TPライン、Trailライン、仮想ライン、ラベル、ボタンなど
ExpertsログOBJECT_AUDIT、STARTUP_FOUND、RESIDUAL_IGNORE、CLEANUP_PLANの有無
異常終了Abnormal terminationやチャートブラックアウトの有無
ポジション状態フラット時か、ポジション保有中か
再現手順どの操作で再現するか
スクリーンショットチャート上の残存ObjectとExpertsログの画面

不具合相談前の整理は、次のページも参考にしてください。

不具合報告・調査依頼について

開発依頼前に整理する情報

EA開発を依頼する場合、Object lifecycleの方針を最初に決めておくと、長時間稼働や販売後サポートが楽になります。

  • チャートに表示するObjectの種類
  • 表示専用Objectと実ロジック参照Objectの分類
  • OnDeinitで削除するObjectと残すObject
  • 再セット時にstartup auditを行うか
  • 残存Objectを実ロジックから除外するか
  • cleanupは手動・dry-run・flat時限定にするか
  • Object関連ログをどの粒度で出すか
  • 問い合わせ時にどのログを送ればよいか

仕様書、setファイル、ログ、スクリーンショットを整理する場合は、次のページも確認してください。

MT5開発依頼前に用意する資料まとめ|仕様書・setファイル・ログ・スクリーンショット

公開しない情報

この記事では、MQL5のObject lifecycleを学ぶために、公開可能な範囲だけを一般化して解説しています。以下は公開しません。

  • 完成EAソース全文
  • 外部シート参照による動作制御ロジック
  • 外部シートCSV取得、解析、時間枠、方向制御ロジック
  • 認証、口座許可判定、購入者管理ロジック
  • endpoint、token、URL、Google Sheet ID、Webhook URL、GAS URL
  • APIキー、認証トークン、口座番号、サーバー名
  • 実運用set値
  • 中核エントリー条件
  • 中核Trail / Nanpin判定
  • 独自優位性のある売買判定式
  • 利益保証、勝率保証、推奨ロット、推奨銘柄、推奨エントリー

本記事は、投資判断や売買指示ではなく、MQL5開発におけるObject lifecycleと検証手順を理解するための技術解説です。

よくある質問

EAが重い時は何を確認しますか?

OnTick内の処理量、ログ出力頻度、Object更新回数、indicator handle作成回数、CopyBufferやCopyRatesの回数、外部連携処理、同時稼働チャート数を確認します。

ログを出しすぎると問題になりますか?

問題になる場合があります。同じログが大量に出ると、必要な情報が埋もれ、バックテストや長時間稼働時の確認もしづらくなります。状態変化時ログ、summary、debug切替に分けると確認しやすくなります。

IndicatorReleaseは必ず必要ですか?

indicator handleを作成している場合は、不要になったタイミングで解放する設計が重要です。ただし、毎tickで作成・解放するのではなく、必要なhandleを作成して再利用する設計が基本です。

OnDeinitで全Objectを削除してよいですか?

常に全削除すればよいとは限りません。OnDeinitでは軽量終了処理を優先し、Object削除はprefixや用途で対象を限定します。手動Objectや他EAのObjectを巻き込まないようにします。

VPSで長時間動かす時の注意点は何ですか?

MT5の起動状態、EA稼働状態、ログ量、同時稼働チャート数、CPU・メモリ、通信状態、外部連携状態、スクリーンショット、再起動履歴を確認します。

チャートがフリーズした時は何を送ればよいですか?

EA名、バージョン、setファイル、対象銘柄、時間足、発生時刻、Expertsログ、Journalログ、スクリーンショット、再現手順、同じチャートに入れているEAやインジケーターの情報を整理してください。

OnTimerに分けると安定しますか?

OnTimerへ分けるだけで必ず安定するわけではありません。ただし、tickごとに実行する必要がない処理を分離することで、OnTickの負荷を下げ、ログや外部連携の管理をしやすくできます。

外部連携EAは長時間稼働で注意が必要ですか?

注意が必要です。通信失敗、stale、通知失敗、外部応答の変化、機密値のログ出力、queue滞留などを確認します。外部連携の失敗理由は、本体処理と分けてログ化します。

Objectが残るのは不具合ですか?

仕様によります。表示専用ラインや履歴確認用Objectを残す設計もあります。ただし、再セット時に古いObjectを実ロジックへ無条件で使わないことが重要です。

時間足変更で表示が戻る場合、何が原因ですか?

パネル再配置、Object再作成、ChartRedraw、OnChartEvent、OnTimerメンテナンス、Object欠損チェックが関係している場合があります。表示だけの問題か、内部処理も止まっているかを分けて確認します。

EAを削除した時にラインが残るのは不具合ですか?

必ずしも不具合とは限りません。ポジション保有中や安全性を優先する設計では、ラインを消さずに残す場合があります。重要なのは、再セット時にその残存ラインを実ロジックへ誤って使わないことです。

OnDeinitで全Objectを削除した方がきれいではありませんか?

見た目はきれいになりますが、広範囲cleanupはEA削除時の不安定化や他Objectの巻き込みにつながる場合があります。終了時は最小化し、再セット時に監査する設計の方が安全な場面があります。

residual ignoreとは何ですか?

再セット前から残っていたObjectを、現在セッションのObjectではないものとして扱い、実ロジックから除外する考え方です。ExpertsログではRESIDUAL_IGNOREのような形で確認できるようにします。

Objectが残っていても売買に影響しませんか?

表示専用Objectであれば、売買判定へ使わない設計にできます。Trail SLや仮想ラインのように実ロジックへ影響し得るObjectは、current session確認やresidual ignoreで誤参照を防ぎます。

どのログを確認すればよいですか?

OBJECT_AUDIT、STARTUP_FOUND、RESIDUAL_IGNORE、CLEANUP_PLAN、STARTUP_SUMMARYなどを確認します。Objectが検出されたか、表示専用扱いか、実ロジックから除外されたかを見ます。

このコードは完成EAとして使えますか?

いいえ。この記事のコード例は、Object lifecycleの考え方を理解するために簡略化した学習用コードです。完成EAとしてそのまま使うことは想定していません。

実際のEAソース全文は公開されていますか?

公開していません。完成EAソース全文、外部シート制御、認証、endpoint、token、URL、実運用set値、中核エントリー条件は公開対象外です。

表示Objectの不具合は売買ロジックの不具合と同じですか?

同じではありません。表示Objectの更新失敗と、発注・決済・リスク管理の不具合は分けて確認します。表示系はbest-effort、判定系はruntime stateで管理する設計が重要です。

関連する確認ページ

確認したい内容に応じて、次のページを参照してください。本文中ですでに案内したページは、ここでは重ねて掲載していません。

確認分野関連ページ
トラブル解決MT5 EAログ完全ガイド|Experts・Journalの見方・問い合わせ前の整理
EA運用ガイドMT5 EAのVPS・別口座・別環境移行ガイド|常時稼働・設定・ログ確認
技術講座MQL5開発入門|MetaEditor・EA・インジケーター・イベント関数・コンパイルエラーの基本
技術講座MQL5時系列データ・価格取得完全ガイド|CopyRates・iClose・SymbolInfoの基本
技術講座MQL5デバッグ・ログファースト開発完全ガイド|Print・Expertsログ・再現条件の整理
技術講座MQL5 WebRequest・JSON・外部API実装完全ガイド|Discord通知・Google Sheets連携前に知ること
技術講座MQL5チャートオブジェクト・パネルUI完全ガイド|OBJ_LABEL・BUTTON・OnChartEventの基本
技術講座MQL5イベント処理完全ガイド|OnTimer・OnTradeTransaction・OnChartEventの使い分け
技術辞典MQL5チャートオブジェクト辞典|ObjectCreate・OBJ_LABEL・OBJ_BUTTON・OnChartEvent一覧
開発実務ノートMQL5ログファースト設計を実コードで解説|event・side・reason・detailで追跡するEA開発

あわせて確認したいページ

今回の確認内容に関連する補足ページです。必要な項目だけ確認してください。

確認目的関連ページ
「MQL5 EA 長時間稼働 安定化」の読後に「MQL5 CopyBufferとインジケーターハンドル管理を実コードで解説」を補足確認MQL5 CopyBufferとインジケーターハンドル管理を実コードで解説

まとめ

MQL5のEAやインジケーターを長時間安定して動かすには、OnTickに処理を詰め込みすぎないこと、indicator handleを適切に作成・再利用・解放すること、ログを状態変化時やsummaryへ整理すること、Objectを差分更新すること、OnDeinitで重いcleanupを避けることが重要です。

外部連携や通知を含むEAでは、通信失敗、stale、通知queue、機密値の扱いも確認対象になります。VPSで長時間動かす場合は、EA単体だけでなく、MT5本体、チャート数、ログ量、CPU・メモリ、通信状態、スクリーンショット、再起動履歴も整理してください。

学習内容を確認しても整理が難しい場合は、EA名、バージョン、setファイル、Expertsログ、Journalログ、発生時刻、スクリーンショットを整理したうえで相談してください。

お問い合わせはEAFC公式LINEへ

EAFCサイト内の記事、商品、導入前確認、設定確認、ログ確認、開発依頼前のご相談など、各種お問い合わせはEAFC公式LINEより受け付けています。

※投資助言、売買指示、利益保証、推奨ロット、推奨銘柄の案内は行っておりません。

口座番号、認証情報、Webhook URL、GAS URL、APIキーなどの機密情報は送信しないでください。

利用条件・相談窓口

確認内容案内ページ
MT4/MT5 技術講座|EA・インジケーター・MQL開発を学ぶ技術講座一覧
導入ガイド導入ガイド
商品一覧|EA・インジケーター販売|商品一覧
MT5 開発依頼・作成代行|EA・インジケーター新規開発・MT4/MT5既存改修開発代行ページ
EA・MT5・MQL5 技術辞典|用語・関数・設定を目的別に探すEA・MT5・MQL5 技術辞典|用語・関数・設定を目的別に探す
ABOUT ME
記事URLをコピーしました