MQL5 EAの基本イベント構造を実コードで解説|OnInit・OnTick・OnDeinitの責務分離

EAファンクラブ

MQL5でEAを作る時、最初に理解しておきたいのがOnInit・OnTick・OnDeinitの役割です。

EAは、チャートへセットされた時、価格が動いた時、削除・再初期化された時で呼ばれるイベント関数が異なります。このイベント構造を理解せずに処理を詰め込むと、初期化失敗、tickごとの処理過多、終了時のcleanup不安定、ログ不足などが起きやすくなります。

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

扱うのは、OnInitで準備すること、OnTickで実行すること、OnDeinitで最小限に片付けること、そしてそれぞれをログで追えるようにする設計です。

この記事で扱う範囲

項目この記事で扱う内容扱わない内容
目的EAの基本イベント構造と責務分離を理解する利益を狙う売買ロジック、具体的なエントリー条件
主な関数OnInit、OnTick、OnDeinit完成EAソース全文、実運用コード全文
設計対象初期化、tick処理、終了処理、ログ、handle解放外部制御、認証、実運用set値
コード例学習用に簡略化した短いコード断片中核エントリー条件、独自優位性のある判定式
確認対象INITログ、TICK処理、DEINITログ、責務分離推奨ロット、推奨銘柄、売買判断

OnInit・OnTick・OnDeinitの役割

MQL5 EAでは、処理の入口がイベント関数として分かれています。

イベント関数主なタイミング主な役割
OnInitEAをチャートへセットした時、再初期化時初期化、handle作成、設定チェック、起動ログ
OnTick価格更新があった時現在状態の取得、判定、見送り、発注候補、管理処理
OnDeinitEA削除、時間足変更、チャート変更、再コンパイル時などtimer停止、handle解放、軽量な終了処理

重要なのは、すべての処理をOnTickへ詰め込まないことです。OnInitで準備できるものは起動時に行い、OnTickはできるだけ軽くし、OnDeinitは安全に終了できる最小処理へ寄せます。

OnInitで行うこと

OnInitは、EAをチャートへセットした時に呼ばれる初期化処理です。

OnInitで行う代表的な処理は次の通りです。

  • EA名、バージョン、symbol、timeframeの確認
  • input値の簡易validation
  • インジケーターハンドルの作成
  • timerを使う場合のEventSetTimer
  • チャートObjectのstartup audit
  • ログモードや表示モードの初期化
  • 初期化成功または安全停止のログ出力

一方で、OnInitに重い売買判定や発注処理を入れるべきではありません。OnInitはあくまで準備と検査の入口です。

OnTickで行うこと

OnTickは、価格が更新されるたびに呼ばれる処理です。

OnTickでは、tickごとに必要な処理だけを行います。代表的には、現在価格の確認、ポジション状態の取得、必要なインジケーター値の取得、entry guard、managed exit、signal評価などです。

ただし、OnTickは呼び出し回数が多いため、次のような処理を毎回行うと重くなります。

  • 毎tickでインジケーターハンドルを作成する
  • 毎tickで全Objectを走査する
  • 毎tickで全Objectを削除・再作成する
  • 毎tickで同じログを大量に出す
  • 毎tickで外部通信や重い集計を行う

OnTickでは、処理をhelperへ分け、ログも状態変化時や失敗時に絞ると確認しやすくなります。

OnDeinitで行うこと

OnDeinitは、EAが終了または再初期化される時に呼ばれます。

OnDeinitでは、できるだけ軽い処理に限定します。

  • Commentを消す
  • timerを止める
  • 作成済みインジケーターハンドルをIndicatorReleaseする
  • 自EA所有が明確な軽量ステータスObjectを整理する
  • 終了ログを出す

OnDeinitで広範囲のObject cleanupを行うと、EA削除時のフリーズ、Abnormal termination、手動ラインや他EAのObject巻き込みにつながる場合があります。チャートObjectの管理は、終了時に全部消すより、再セット時にstartup auditで監査する設計が安全な場面があります。

イベント関数へ処理を詰め込まない理由

OnInit・OnTick・OnDeinitはEAの入口ですが、そこへ全処理を直接書くと保守が難しくなります。

悪い例問題点改善方針
OnTickに判定・発注・決済・ログ・Object更新をすべて書くどこで止まったか追えないResolve / Evaluate / Executeへ分ける
OnInitに重い処理を入れる起動失敗や再初期化時の原因が追いにくいvalidationと準備に限定する
OnDeinitで全Object削除する削除時の不安定化や他Object巻き込みが起きるOnDeinitは最小化し、startup auditへ回す
ログを各所に直接Printする表記揺れ、ログ過多、検索しにくさが出るログhelperへ集約する
値取得失敗とシグナル不成立を混同するEAが動かない理由を切り分けにくい取得失敗、判定不成立、執行見送りを分ける

イベント関数は、全処理を書く場所ではなく、必要なhelperを呼び出す入口として設計すると安定します。

基本構造の全体像

EAのイベント構造は、次のように分けると整理しやすくなります。

役割
Init層初期化、validation、handle作成InitRuntime、CreateHandles、InitSelfCheck
Market層価格、symbol、spread、position状態の取得BuildMarketSnapshot、BuildPositionSnapshot
Signal層判定候補の作成EvaluateSignal、BuildSignalSnapshot
Guard層実行前の安全確認CheckSpread、CheckDuplicate、CheckRisk
Execution層発注・決済・modifySendOrder、ClosePosition、ModifyStop
Exit層建値、トレール、保護決済RunManagedExit、RunSignalExit
Deinit層handle解放、timer停止、軽量終了ReleaseHandles、StopTimers

この記事では売買条件の中身は扱いません。重要なのは、処理の入口と責務を分け、ログで確認できる形にすることです。

実コード断片1:OnInitで初期化を分ける

以下は、OnInitの中へ処理を詰め込まず、初期化helperへ分ける学習用コード例です。完成EAのソース全文ではありません。

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

bool g_runtimeReady = false;
int  g_maHandle = INVALID_HANDLE;

bool InitRuntime()
{
   g_runtimeReady = false;

   Print("DIAG/INIT: event=START",
         " symbol=", _Symbol,
         " timeframe=", EnumToString(_Period));

   return true;
}

bool ValidateInputs()
{
   // 実際はinput値の範囲、ON/OFF整合、危険設定などを確認します。
   Print("DIAG/INIT: event=VALIDATION_OK");
   return true;
}

bool CreateIndicatorHandles()
{
   if(g_maHandle != INVALID_HANDLE)
      return true;

   ResetLastError();

   g_maHandle = iMA(_Symbol,
                    PERIOD_CURRENT,
                    20,
                    0,
                    MODE_EMA,
                    PRICE_CLOSE);

   if(g_maHandle == INVALID_HANDLE)
   {
      Print("DIAG/INIT: event=HANDLE_CREATE_FAIL",
            " reason=INVALID_HANDLE",
            " error=", GetLastError());
      return false;
   }

   Print("DIAG/INIT: event=HANDLE_CREATE_OK",
         " kind=MA");

   return true;
}

int OnInit()
{
   if(!InitRuntime())
      return INIT_FAILED;

   if(!ValidateInputs())
      return INIT_FAILED;

   if(!CreateIndicatorHandles())
      return INIT_FAILED;

   g_runtimeReady = true;

   Print("DIAG/INIT: event=RUNTIME_INIT_OK");

   return INIT_SUCCEEDED;
}

この例では、OnInitを「初期化処理の入口」として扱い、実際の処理はhelperへ分けています。

このコードの役割

処理役割
InitRuntimeruntime状態を初期化し、起動ログを出す
ValidateInputsinput設定の不整合を確認する
CreateIndicatorHandles必要なhandleを作成する
RUNTIME_INIT_OKEAが初期化完了したことを確認する
INIT_FAILED準備できない場合に安全側で起動失敗にする

実コード断片2:OnTickを入口だけにする

以下は、OnTickに全処理を書かず、tickごとの処理をhelperへ分ける例です。中核エントリー条件は含めていません。

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

struct RuntimeSnapshot
{
   bool ready;
   bool newBar;
   string reason;
};

RuntimeSnapshot BuildRuntimeSnapshot()
{
   RuntimeSnapshot snap;
   snap.ready = false;
   snap.newBar = false;
   snap.reason = "INIT";

   if(!g_runtimeReady)
   {
      snap.reason = "RUNTIME_NOT_READY";
      return snap;
   }

   snap.ready = true;
   snap.reason = "OK";

   return snap;
}

void RunManagedExit()
{
   // 実際は建値移動、Trail、保護決済などを別helperで扱います。
}

void RunEntryEvaluation()
{
   // 実際はsignal評価、entry guard、executionを分けて扱います。
   // 中核エントリー条件はこの記事では扱いません。
}

void OnTick()
{
   RuntimeSnapshot snap = BuildRuntimeSnapshot();

   if(!snap.ready)
   {
      Print("DIAG/TICK: event=SKIP",
            " reason=", snap.reason);
      return;
   }

   RunManagedExit();

   RunEntryEvaluation();
}

OnTickでは、まずruntimeが準備できているか確認し、準備できていない場合は理由付きで見送ります。

このコードの役割

処理役割
BuildRuntimeSnapshottick処理に入れる状態か確認する
RUNTIME_NOT_READY初期化未完了時に処理を止める理由
RunManagedExit既存ポジション保護や決済管理を扱う
RunEntryEvaluation新規Entry候補の評価を扱う
OnTick各helperを呼ぶ入口として最小化する

EAでは、新規Entry停止中でも既存ポジション保護は継続したい場合があります。そのため、entry処理とexit処理は分けておくと確認しやすくなります。

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

以下は、OnDeinitで広範囲cleanupを行わず、終了時の処理を最小化する学習用コード例です。

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

void ReleaseIndicatorHandles()
{
   if(g_maHandle != INVALID_HANDLE)
   {
      IndicatorRelease(g_maHandle);
      g_maHandle = INVALID_HANDLE;

      Print("DIAG/DEINIT: event=HANDLE_RELEASE",
            " kind=MA");
   }
}

void DeleteStatusObjectsOnly()
{
   // 自EA所有が明確な軽量ステータスObjectだけを対象にします。
   // 広範囲のObject走査・全削除は行いません。
}

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

   EventKillTimer();

   DeleteStatusObjectsOnly();

   ReleaseIndicatorHandles();

   g_runtimeReady = false;

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

OnDeinitは、終了時に安全にできる処理だけを行います。Object cleanupをすべてここへ詰め込むのではなく、必要に応じて次回OnInit時のstartup auditで確認する設計が安全な場合があります。

このコードの役割

処理役割
Comment(“”)チャート左上コメントを消す
EventKillTimertimer処理を停止する
DeleteStatusObjectsOnly自EA所有が明確な軽量Objectだけ整理する
ReleaseIndicatorHandles作成済みhandleをIndicatorReleaseする
MINIMAL_CLEANUP終了時処理が最小化されていることをログに残す

実コード断片4:OnTimerを分ける考え方

EAによっては、OnTickだけでなくOnTimerを使って、重い処理や定期確認を分けることがあります。

以下は、OnTimerを使う考え方の学習用例です。

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

datetime g_lastSummaryTime = 0;

bool ShouldPrintRuntimeSummary()
{
   datetime nowTime = TimeCurrent();

   if((nowTime - g_lastSummaryTime) < 300)
      return false;

   g_lastSummaryTime = nowTime;
   return true;
}

void PrintRuntimeSummary()
{
   Print("DIAG/RUNTIME: event=SUMMARY",
         " ready=", g_runtimeReady,
         " symbol=", _Symbol);
}

void OnTimer()
{
   if(!g_runtimeReady)
      return;

   if(ShouldPrintRuntimeSummary())
      PrintRuntimeSummary();

   // 外部通信や重い処理を使う場合も、
   // OnTickとは責務を分けて設計します。
}

OnTimerを使う場合でも、外部連携、認証、通知、ログ集計などは責務を分けて設計します。この記事では外部制御の詳細や認証ロジックは扱いません。

ログで確認するポイント

OnInit・OnTick・OnDeinitを責務分離しているEAでは、Expertsログで次の流れを確認できます。

ログ見るポイント
DIAG/INIT: STARTEAが起動処理に入ったか
DIAG/INIT: VALIDATION_OKinputや初期設定の確認が通ったか
DIAG/INIT: HANDLE_CREATE_OKインジケーターハンドルが作成できたか
DIAG/INIT: RUNTIME_INIT_OKruntimeが稼働可能状態になったか
DIAG/TICK: SKIPtick処理が見送られた理由が分かるか
DIAG/RUNTIME: SUMMARY定期的な稼働状態が確認できるか
DIAG/DEINIT: HANDLE_RELEASEhandle解放が行われたか
DIAG/DEINIT: MINIMAL_CLEANUP終了処理が最小化されているか

ログ確認の基本は、次のページも参考にしてください。

MT5のログ確認方法|Experts・Journalの見方

よくある失敗例

失敗1:OnTickへすべて書いてしまう

OnTickに判定、発注、決済、表示、ログ、外部通信をすべて書くと、処理の見通しが悪くなります。原因調査もしにくくなるため、helperへ分ける設計が重要です。

失敗2:OnInitで準備失敗しているのにOnTickが動く

handle作成やvalidationに失敗しているのにOnTickが動くと、不正な状態で処理が進む可能性があります。runtime readyフラグやINIT_FAILEDを使い、安全側で止めます。

失敗3:OnDeinitで全Objectを消そうとする

EA削除時に広範囲cleanupを行うと、フリーズや他Object巻き込みの原因になる場合があります。OnDeinitは最小化し、残存Objectはstartup auditで確認する方が安全な場面があります。

失敗4:handle解放を忘れる

iMA、iRSI、iCustomなどで作成したhandleは、不要になった時にIndicatorReleaseする設計が必要です。解放後はINVALID_HANDLEへ戻しておくと誤使用を防ぎやすくなります。

失敗5:ログが成功時だけになっている

起動成功ログだけでは、動かない原因を追えません。INIT失敗、TICK見送り、DEINIT処理、handle作成失敗などをreason付きで出すと確認しやすくなります。

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

EA開発を依頼する場合、イベント構造の方針を最初に整理しておくと、仕様のすれ違いを減らせます。

  • OnInitでどこまでvalidationするか
  • インジケーターハンドルをOnInitで作るか
  • OnTickではバー確定型かリアルタイム型か
  • OnTickで毎回実行する処理と、状態変化時だけ実行する処理
  • OnTimerを使うか
  • OnDeinitでどこまで片付けるか
  • Object cleanupをOnDeinitで行うか、startup auditへ回すか
  • INIT / TICK / DEINITログをどの粒度で出すか

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

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

公開しない情報

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

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

本記事は、投資判断や売買指示ではなく、MQL5 EA開発におけるOnInit・OnTick・OnDeinitの責務分離を理解するための技術解説です。

FAQ

OnInitでは何をするべきですか?

OnInitでは、初期化、input確認、handle作成、timer設定、起動ログなどを行います。重い売買判定や発注処理はOnInitに入れず、準備と検査に限定する方が安全です。

OnTickでは何をするべきですか?

OnTickでは、現在状態の取得、既存ポジション管理、signal評価、entry guard、execution候補などを扱います。ただし、処理を直接書き込みすぎず、helperへ分けることが重要です。

OnDeinitで全Objectを削除した方がよいですか?

必ずしもそうではありません。広範囲cleanupはEA削除時の不安定化や他Object巻き込みにつながる場合があります。OnDeinitは最小化し、残存Objectは次回起動時のstartup auditで扱う設計が安全な場合があります。

OnTimerは必ず必要ですか?

必須ではありません。ただし、定期ログ、状態監視、外部連携、重い確認処理などをOnTickから分離したい場合は、OnTimerを使う設計が有効です。

runtime readyフラグは必要ですか?

handle作成やvalidationが失敗した状態でOnTick処理へ進まないようにするため、runtime readyのような状態フラグを持つと安全です。

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

いいえ。この記事のコード例は、OnInit・OnTick・OnDeinitの責務分離を理解するために簡略化した学習用コードです。完成EAとしてそのまま使うことは想定していません。

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

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

関連ページ

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

まとめ

MQL5 EAの基本構造では、OnInit・OnTick・OnDeinitの役割を分けることが重要です。

特に、次の4点を分けておくと、原因調査と保守がしやすくなります。

  • OnInit:初期化、validation、handle作成、起動ログ
  • OnTick:状態取得、既存ポジション管理、判定、見送り、実行候補
  • OnDeinit:timer停止、handle解放、軽量終了処理
  • OnTimer:必要に応じて定期ログや重い処理を分離

イベント関数へ処理を詰め込みすぎると、どこで失敗しているのか追いにくくなります。OnInitでは準備、OnTickでは実行入口、OnDeinitでは最小終了処理という形に分けることで、EAの挙動をログから確認しやすくなります。

完成EAの中核ロジックを公開しなくても、イベント構造と責務分離を理解しておくことで、EAが起動しない、値が取れない、tick処理が重い、削除時に不安定になるといった問題を切り分けやすくなります。

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