MQL4 イベントハンドラリファレンス|OnInit・OnTick・OnTimer・OnChartEvent完全ガイド

MQL4 イベントハンドラを体系的にまとめました。旧形式のinit()/deinit()/start()とビルド600+で導入されたOnInit()/OnDeinit()/OnTick()の違い、OnTimer()による定期処理、OnChartEvent()によるチャートイベント処理、EA・インジケーター・スクリプトの実行モデルの違い、新バー検出パターン、UninitializeReasonまで、MT4プログラム開発に必要なイベント処理のすべてをコード付きで解説します。

MQL4 イベントハンドラ リファレンスカード
目次

MQL4とMQL5のイベントハンドラの違い

機能MQL4(ビルド600+)MQL5
初期化OnInit() / init()OnInit()
終了処理OnDeinit() / deinit()OnDeinit()
ティック処理OnTick() / start()OnTick()
タイマーOnTimer()OnTimer()
チャートイベントOnChartEvent()OnChartEvent()
計算イベントOnCalculate()(インジケーター)OnCalculate()
取引イベントなしOnTrade() / OnTradeTransaction()
テスターイベントOnTester()OnTester() / OnTesterInit() / OnTesterDeinit()

MQL4ビルド600以降は、MQL5と同じイベントハンドラ名(OnInit/OnDeinit/OnTick)が使えます。旧形式のinit/deinit/startも互換性のために動作しますが、新規開発ではOnXxx形式を推奨します。

旧形式 vs 新形式

ビルド600(2014年)以降のMT4では、MQL5互換のOnInit() / OnDeinit() / OnTick()形式が使えます。旧形式のinit/deinit/startも動作しますが、新形式ではOnDeinitのreason引数OnInitの定数戻り値など、より細かい制御が可能です。新規開発では必ず新形式を使いましょう。

ビルド600(2014年)以降のMT4では、MQL5互換のOnInit() / OnDeinit() / OnTick()形式が使えます。旧形式のinit/deinit/startも動作しますが、新形式ではOnDeinitのreason引数OnInitの定数戻り値など、より細かい制御が可能です。新規開発では必ず新形式を使いましょう。

// 旧形式:戻り値の型が異なる
int init()
  {
   // 初期化処理
   Print("EA初期化(旧形式)");
   return(0);  // 0=成功
  }

int deinit()
  {
   // 終了処理
   Print("EA終了(旧形式)");
   return(0);
  }

int start()
  {
   // ティックごとに実行
   Print("ティック受信(旧形式)");
   return(0);
  }
// 新形式:MQL5と同じ書式
int OnInit()
  {
   // 初期化処理
   Print("EA初期化");
   return(INIT_SUCCEEDED);  // 定数を使用
  }

void OnDeinit(const int reason)
  {
   // 終了処理(理由コードを受け取れる)
   Print("EA終了: reason=", reason);
  }

void OnTick()
  {
   // ティックごとに実行
   // 戻り値なし(void)
  }

OnInit:初期化

EAがチャートにアタッチされた時、時間軸の変更時、再コンパイル時に1回だけ実行されます。

// グローバル変数
int    PipMultiplier;
double PipSize;
string EAName ="MyEA v1.0";

int OnInit()
  {
   // 口座チェック
   if(!IsTradeAllowed())
     {
      Alert(EAName,": 自動売買が許可されていません");
      return(INIT_FAILED);
     }

   // 通貨ペアチェック
   if(Symbol() !="USDJPY" && Symbol() !="GBPJPY")
     {
      Alert(EAName,": 対応通貨ペアではありません");
      return(INIT_PARAMETERS_INCORRECT);
     }

   // 5桁ブローカー判定
   if(Digits == 3 || Digits == 5)
     {
      PipMultiplier = 10;
      PipSize = Point * 10;
     }
   else
     {
      PipMultiplier = 1;
      PipSize = Point;
     }

   // タイマー開始(1秒間隔)
   EventSetTimer(1);

   // チャートコメント
   Comment(EAName," - 稼働中");

   Print(EAName," 初期化完了");
   return(INIT_SUCCEEDED);
  }

OnInitの戻り値

定数説明
INIT_SUCCEEDED0初期化成功
INIT_FAILED1初期化失敗(EA停止)
INIT_PARAMETERS_INCORRECTなしパラメータエラー
INIT_AGENT_NOT_SUITABLEなしテスターエージェント不適合

OnDeinit:終了処理

void OnDeinit(const int reason)
  {
   // タイマー停止
   EventKillTimer();

   // チャート上のオブジェクトを削除
   ObjectsDeleteAll(0,"MyEA_");

   // チャートコメントをクリア
   Comment("");

   // 終了理由をログ出力
   string reasonText;
   switch(reason)
     {
      case REASON_PROGRAM:     reasonText ="プログラムから終了"; break;
      case REASON_REMOVE:      reasonText ="チャートから削除"; break;
      case REASON_RECOMPILE:   reasonText ="再コンパイル"; break;
      case REASON_CHARTCHANGE: reasonText ="通貨ペアまたは時間軸変更"; break;
      case REASON_CHARTCLOSE:  reasonText ="チャートを閉じた"; break;
      case REASON_PARAMETERS:  reasonText ="パラメータ変更"; break;
      case REASON_ACCOUNT:     reasonText ="口座変更"; break;
      case REASON_TEMPLATE:    reasonText ="テンプレート適用"; break;
      case REASON_INITFAILED:  reasonText ="OnInit失敗"; break;
      case REASON_CLOSE:       reasonText ="ターミナル終了"; break;
      default:                 reasonText ="不明(" + IntegerToString(reason) +")";
     }

   Print(EAName," 終了:", reasonText);
  }

UninitializeReason定数

定数説明
REASON_PROGRAM0ExpertRemove()で終了
REASON_REMOVE1チャートから手動削除
REASON_RECOMPILE2プログラムを再コンパイル
REASON_CHARTCHANGE3通貨ペアまたは時間軸を変更
REASON_CHARTCLOSE4チャートを閉じた
REASON_PARAMETERS5入力パラメータを変更
REASON_ACCOUNT6別の口座に切り替え
REASON_TEMPLATE7テンプレートを適用
REASON_INITFAILED8OnInit()が失敗
REASON_CLOSE9ターミナルを閉じた

OnTick:ティック処理

void OnTick()
  {
   // 新バー検出(ティックごとの重複処理を防ぐ)
   static datetime lastBar = 0;
   if(Time[0] == lastBar)
      return;  // 同じバー内では何もしない
   lastBar = Time[0];

   // ここから新バーの処理
   // 1. ポジションチェック
   int posCount = CountMyPositions();

   // 2. 決済判定
   CheckExitConditions();

   // 3. エントリー判定
   if(posCount < MaxPositions)
      CheckEntrySignal();

   // 4. トレーリングストップ
   UpdateTrailingStop();
  }
OnTick内でやってはいけないこと
  • Sleep()の使用: ティック処理がブロックされ、後続のティックが失われる
  • 長時間のループ処理: 他のEAのティック処理にも影響する(MT4はシングルスレッド)
  • 毎ティック同じ計算を繰り返す: 新バー検出で制御し、不要な計算を省く
  • Alert()の多用: ダイアログが表示されるたびに処理が停止する
OnTick内でやってはいけないこと
  • Sleep()の使用: ティック処理がブロックされ、後続のティックが失われる
  • 長時間のループ処理: 他のEAのティック処理にも影響する(MT4はシングルスレッド)
  • 毎ティック同じ計算を繰り返す: 新バー検出で制御し、不要な計算を省く
  • Alert()の多用: ダイアログが表示されるたびに処理が停止する

OnTimer:タイマーイベント

int OnInit()
  {
   // 60秒間隔でタイマーイベントを発生
   EventSetTimer(60);
   return(INIT_SUCCEEDED);
  }

void OnTimer()
  {
   // 定期的な処理
   // - チャートコメントの更新
   // - 損益の定期集計
   // - 外部データの取得

   double equity = AccountEquity();
   double balance = AccountBalance();
   double drawdown = (balance - equity) / balance * 100;

   string info ="";
   info +="残高:" + DoubleToStr(balance, 0) +"n";
   info +="有効証拠金:" + DoubleToStr(equity, 0) +"n";
   info +="DD:" + DoubleToStr(drawdown, 1) +"%n";
   info +="更新:" + TimeToStr(TimeCurrent(), TIME_MINUTES);

   Comment(info);
  }

void OnDeinit(const int reason)
  {
   EventKillTimer();  // タイマー停止を忘れずに
   Comment("");
  }

OnChartEvent:チャートイベント

void OnChartEvent(
   const int     id,       // イベントID
   const long    &lparam,  // long型パラメータ
   const double  &dparam,  // double型パラメータ
   const string  &sparam   // string型パラメータ
);

主要なイベントID

定数説明lparamdparamsparam
CHARTEVENT_KEYDOWNキー押下キーコードなしなし
CHARTEVENT_MOUSE_MOVEマウス移動X座標Y座標マウスフラグ
CHARTEVENT_OBJECT_CLICKオブジェクトクリックX座標Y座標オブジェクト名
CHARTEVENT_OBJECT_CREATEオブジェクト作成なしなしオブジェクト名
CHARTEVENT_OBJECT_DELETEオブジェクト削除なしなしオブジェクト名
CHARTEVENT_CHART_CHANGEチャートサイズ変更なしなしなし
CHARTEVENT_CUSTOMカスタムイベント任意任意任意
int OnInit()
  {
   // ボタンオブジェクトを作成
   ObjectCreate(0,"BuyButton", OBJ_BUTTON, 0, 0, 0);
   ObjectSetInteger(0,"BuyButton", OBJPROP_XDISTANCE, 10);
   ObjectSetInteger(0,"BuyButton", OBJPROP_YDISTANCE, 30);
   ObjectSetInteger(0,"BuyButton", OBJPROP_XSIZE, 100);
   ObjectSetInteger(0,"BuyButton", OBJPROP_YSIZE, 30);
   ObjectSetString(0,"BuyButton", OBJPROP_TEXT,"BUY");
   ObjectSetInteger(0,"BuyButton", OBJPROP_COLOR, clrWhite);
   ObjectSetInteger(0,"BuyButton", OBJPROP_BGCOLOR, clrBlue);

   ObjectCreate(0,"SellButton", OBJ_BUTTON, 0, 0, 0);
   ObjectSetInteger(0,"SellButton", OBJPROP_XDISTANCE, 120);
   ObjectSetInteger(0,"SellButton", OBJPROP_YDISTANCE, 30);
   ObjectSetInteger(0,"SellButton", OBJPROP_XSIZE, 100);
   ObjectSetInteger(0,"SellButton", OBJPROP_YSIZE, 30);
   ObjectSetString(0,"SellButton", OBJPROP_TEXT,"SELL");
   ObjectSetInteger(0,"SellButton", OBJPROP_COLOR, clrWhite);
   ObjectSetInteger(0,"SellButton", OBJPROP_BGCOLOR, clrRed);

   return(INIT_SUCCEEDED);
  }

void OnChartEvent(const int id, const long &lparam,
                   const double &dparam, const string &sparam)
  {
   if(id == CHARTEVENT_OBJECT_CLICK)
     {
      if(sparam =="BuyButton")
        {
         Print("BUYボタンがクリックされました");
         OrderSend(Symbol(), OP_BUY, 0.01, Ask, 3, 0, 0,"Manual Buy", 0, 0, clrBlue);
         ObjectSetInteger(0,"BuyButton", OBJPROP_STATE, false);  // ボタンをリセット
        }
      else if(sparam =="SellButton")
        {
         Print("SELLボタンがクリックされました");
         OrderSend(Symbol(), OP_SELL, 0.01, Bid, 3, 0, 0,"Manual Sell", 0, 0, clrRed);
         ObjectSetInteger(0,"SellButton", OBJPROP_STATE, false);
        }
     }
  }

EA・インジケーター・スクリプトの違い

項目EA(Expert Advisor)インジケータースクリプト
メインイベントOnTick()OnCalculate()OnStart()
実行タイミングティックごとティックごと1回だけ
取引可能はいいいえはい
チャートへの描画オブジェクトのみバッファ+オブジェクトオブジェクトのみ
同時実行チャートに1つ複数可チャートに1つ
OnTimer使用可使用可不可
OnChartEvent使用可使用可不可
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_color1 clrRed

double Buffer[];

int OnInit()
  {
   SetIndexBuffer(0, Buffer);
   SetIndexStyle(0, DRAW_LINE);
   SetIndexLabel(0,"MyIndicator");
   return(INIT_SUCCEEDED);
  }

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
   int limit = rates_total - prev_calculated;
   if(prev_calculated == 0)
      limit = rates_total - 1;

   for(int i = limit; i >= 0; i--)
     {
      Buffer[i] = (high[i] + low[i] + close[i]) / 3.0;
     }

   return(rates_total);
  }
// スクリプト:1回だけ実行される
void OnStart()
  {
   // 全ポジションの情報を表示
   Print("=== 保有ポジション一覧 ===");

   for(int i = OrdersTotal() - 1; i >= 0; i--)
     {
      if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
         continue;

      Print("Ticket=", OrderTicket()," Type=", (OrderType() == OP_BUY ?"BUY" :"SELL")," Lots=", OrderLots()," Profit=", OrderProfit());
     }

   Print("=== 完了 ===");
  }
OnCalculateの最適化
  • prev_calculated引数を活用して、既に計算済みのバーを再計算しない
  • 初回呼び出し時(prev_calculated==0)は全バーを計算、2回目以降は新しいバーだけ計算
  • 戻り値にrates_totalを返すことで、次回呼び出し時のprev_calculatedに反映される
  • インジケーターでは取引関数(OrderSend等)は使用できない
OnCalculateの最適化
  • prev_calculated引数を活用して、既に計算済みのバーを再計算しない
  • 初回呼び出し時(prev_calculated==0)は全バーを計算、2回目以降は新しいバーだけ計算
  • 戻り値にrates_totalを返すことで、次回呼び出し時のprev_calculatedに反映される
  • インジケーターでは取引関数(OrderSend等)は使用できない

イベントハンドラ一覧(MQL5対応表)

MQL4イベントハンドラMQL5対応説明
OnInit() / init()OnInit()初期化
OnDeinit() / deinit()OnDeinit()終了処理
OnTick() / start()OnTick()ティック受信
OnTimer()OnTimer()タイマーイベント
OnChartEvent()OnChartEvent()チャートイベント
OnCalculate()OnCalculate()インジケーター計算
OnTester()OnTester()テスター終了
なしOnTrade()取引イベント
なしOnTradeTransaction()取引トランザクション
なしOnBookEvent()板情報変更

まとめ

MQL4のイベントハンドラはEA、インジケーター、スクリプトそれぞれの実行モデルに合わせて設計されています。実際に試したところ、IsNewBar()で新バーを検出してメイン処理を呼ぶ設計にするだけで、ティックノイズによる誤発注がほぼ解消しました。押さえておきたいポイントは以下の通りです。

  • 新形式(OnInit/OnDeinit/OnTick)を使う:ビルド600+では旧形式より新形式が推奨
  • OnDeinitでは必ずリソースを解放する:タイマー停止、オブジェクト削除、コメントクリア
  • OnTickでは新バー検出を入れる:ティックごとの重複処理を防ぎ、バックテスト精度を向上
  • OnInitの戻り値でエラーを適切に通知する:INIT_FAILEDを返すとEAが自動停止する

次のステップとして、MQLリファレンス総合ガイドでチャート操作やエラーハンドリングと組み合わせたプロ品質のEA開発を学びましょう。

MQL4イベントハンドラ よくある質問

MQL4のイベントハンドラとは何ですか?

特定のイベントが発生したとき自動的に呼び出される関数です。OnInit()でEA起動時の初期化、OnDeinit()で終了処理、OnTick()で新しいティック受信時の処理、OnTimer()でタイマーイベント処理を行います。これらを適切に実装することがEA開発の基本です。

OnInit()とOnDeinit()の役割は?

OnInit()はEAがチャートに追加された時または再コンパイル後に1回呼ばれ、変数初期化・インジケーター設定・タイマー設定を行います。OnDeinit()はEA削除時・チャート変更時・MT4終了時に呼ばれ、タイマー停止・ファイルクローズ・オブジェクト削除などのリソース解放を行います。

OnTick()の正しい使い方は?

OnTick()は新しいティックが来るたびに呼ばれます。重い処理(インジケーター計算、ファイル読み書き)は毎ティック実行しないよう工夫が必要です。IsNewBar()関数で新しいバーの開始を確認してからメイン処理を実行する設計が定番です。

MQL4でタイマーを使う方法は?

OnInit()内でEventSetTimer(seconds)を呼ぶと、指定秒数ごとにOnTimer()が呼ばれます。OnDeinit()ではEventKillTimer()でタイマーを必ず停止します。OnTick()が届かない場面(週末や流動性の低い時間)での定期処理に有効です。

MQL4でカスタムイベントを扱う方法は?

EventChartCustom(chartId, eventCode, lparam, dparam, sparam)でカスタムイベントを送信し、OnChartEvent()で受け取ります。チャートクリックやキーボード入力もOnChartEvent()で処理します。EAでのカスタムイベントはGUI操作や他のEAとの通信に使えます。

MQL4とMQL5のイベントハンドラの違いは?

基本的なOnInit/OnDeinit/OnTick/OnTimerは同じです。MQL5ではOnTrade()/OnTradeTransaction()による取引イベント処理が追加され、OnBookEvent()でDepth of Market変化も扱えます。MQL4にはこれらが存在せず、OnTick()内でポジション状態を監視する実装が必要です。

MQL4リファレンス 関連記事

シストレ.COM編集部|200種類以上のEAをフォワード計測する運営3年の検証メディア。MetaTrader (MT4/MT5) を実運用してきた編集部が監修・執筆しています。

⚠ リスクに関する注意事項

本記事は情報提供を目的としており、特定の金融商品の売買を推奨するものではありません。FX取引はレバレッジ取引の特性上、預託した証拠金以上の損失が生じる可能性があります。取引の際は、ご自身の判断と責任において行ってください。詳しくは特定商取引法に基づく表記をご確認ください。

シストレ.COMで実績のあるEAが使い放題!/
無料会員登録はこちら
FX自動売買でEAを探すなら
シストレ.COM 記事下バナー
実績あるEAが無料
厳格な審査を乗り越えた実績のあるEAが無料で使えます!
自由な口座選び
有料版を購入し柔軟な口座選びが可能!
フォワードテスト公開
全EAのフォワードテスト結果を公開中!
FX初心者も安心
初心者の方も安心して取引を始められます。
多様なEA選択肢
様々な種別のEAをご用意!自分の手法にあった取引が可能です。
信頼のFX会社と提携
人気のFX会社と提携中!様々なFX会社から選べます。
1分で登録完了!EA探すならシストレ.COM!/
無料会員登録はこちら

この記事が気に入ったら
フォローしてね!

お役立ち情報をシェアする
  • URLをコピーしました!

この記事を書いた人

シストレ.COM編集部は、FX自動売買(EA)とテクニカル分析を専門とするインターネットメディア「シストレ.COM」の編集チームです。200本以上のEAのフォワードテスト結果を公開し、実際の運用データに基づいた客観的な情報を発信しています。記事の執筆・監修はMetaTrader(MT4/MT5)での実運用経験を持つスタッフが担当。

目次