MQL5 EAのObject lifecycleを実コードで解説|OnDeinit cleanupを最小化する理由

EAファンクラブ

MQL5でEAを開発していると、チャート上に表示したラインやラベル、パネル部品などのObject管理が問題になることがあります。

EAを削除した時にラインが残る、時間足変更後に表示が重くなる、再セット後に古いラインを参照してしまう、OnDeinitでObjectDeleteを大量に実行してフリーズする。このような問題は、売買ロジックとは別に、Object lifecycleとして設計する必要があります。

この記事では、MQL5 EAのObject lifecycleを、学習用に一般化したコード断片で解説します。完成EAソース全文、外部シート制御、認証、endpoint、token、URL、実運用set値、中核エントリー条件は公開しません。

扱うのは、OnDeinitで広範囲cleanupをしない理由、起動時に残存Objectを監査するstartup audit、危険な残存Objectを実ロジックから除外するresidual ignore、表示専用Objectと実ロジック参照Objectの分離です。

この記事で扱う範囲

項目この記事で扱う内容扱わない内容
目的EA削除・再セット・時間足変更時にObject起因の不安定化を防ぐ設計売買ロジック、利益を狙う条件、エントリー判断
Object管理visual-only、logic-reference、startup audit、residual ignore管理ラインの具体的な売買条件、TrailやNanpinの中核判定
OnDeinit終了時処理を最小化する考え方OnDeinitで全Objectを強制削除する実装
コード例学習用に簡略化した短い疑似コード完成EAソース全文、実運用コード全文
確認対象Expertsログ、OBJECT_AUDIT、CLEANUP_PLAN、RESIDUAL_IGNORE認証、外部制御、実運用set値、口座管理情報

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");
}

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

このコードの役割

処理役割
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;
}

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

このコードの役割

処理役割
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);
}

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

このコードの役割

処理役割
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は表示補助にする

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

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

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と検証手順を理解するための技術解説です。

FAQ

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で管理する設計が重要です。

関連ページ

ページ確認できること
MQL5ログファースト設計を実コードで解説OBJECT_AUDITなどのログ設計とreason管理
MT5のログ確認方法|Experts・Journalの見方ExpertsログとJournalログの基本確認
MT5開発依頼前に用意する資料まとめ|仕様書・setファイル・ログ・スクリーンショット開発相談前に整理する資料
不具合報告・調査依頼について不具合相談前に送る情報
開発・改修の相談ページを見るEA・インジケーター開発や既存改修の相談導線
よくある質問導入、確認、相談前のFAQ
免責事項投資判断、損益、利用上の注意に関する確認
投資助言を行わない方針売買助言ではなく技術支援として扱う方針

まとめ

MQL5 EAのObject lifecycleでは、OnDeinitで全Objectを消すことよりも、EA本体を異常終了させず、再セット時に残存Objectを安全に扱うことが重要です。

特に、次の4点を分けておくと、長時間稼働や販売後サポートで確認しやすくなります。

  • visual-only:表示専用Object。実ロジックへ使わない
  • logic-reference:実ロジックへ影響し得るObject。残存時は無条件採用しない
  • startup audit:再セット時に残存Objectを監査する
  • residual ignore:現在セッション外のObjectを実ロジックから除外する

EA削除時にObjectが残ること自体よりも、古いObjectを現在の判定へ誤って使うこと、手動ラインや他EAのObjectを消してしまうこと、OnDeinitで重いcleanupをしてEAが不安定になることの方が大きなリスクです。

Object lifecycleをログファーストで設計しておくと、表示残り、再セット、時間足変更、長時間稼働時の問題を、Expertsログとチャート表示の両方から切り分けやすくなります。

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