チャート上に「ライン ON/OFF」のようなボタンを置いて、

クリックひとつでインジケーターの表示を切り替えたいと思ったことはないでしょうか。

あるいは、マウスカーソルを動かしただけで、

その位置の価格がラベルに表示されるインジケーターを見て、

「これはどう作るんだろう」と感じたことはないでしょうか。

この記事では、MQL5の`OnChartEvent()`とマウスイベント

(`CHARTEVENT_MOUSE_MOVE`・

`CHARTEVENT_OBJECT_CLICK`)を使い、

`OBJ_BUTTON`のボタンパネルと、

カーソル位置の価格表示を実際に動くコードで作る方法を解説します。

この記事で分かること
・OnChartEvent()がどんなタイミングで何を受け取るか
・マウスイベントを受け取るための事前準備
・OBJ_BUTTONでクリックできるボタンを作る方法
・ChartXYToTimePriceでカーソル位置の価格を取得する方法

OnChartEventとは何か

まず、チャート上のイベントを受け取る土台となる

`OnChartEvent()`から確認します。

呼ばれるタイミング(OnTick・OnCalculateとの違い)

`OnTick()`や`OnCalculate()`は、

新しいティックやバーが来たときに呼ばれます。

これに対して`OnChartEvent()`は、

キー入力・マウスのクリックや移動・オブジェクトの作成や削除など、

チャート上の「出来事」が起きるたびに呼ばれます。

値動きとは関係のないタイミングで発火する点が、最大の違いです。

4つの引数(id・lparam・dparam・sparam)の役割

void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam);

`id`には、何が起きたかを表す`CHARTEVENT_`から始まる定数が入ります。

`lparam`・`dparam`・`sparam`に入る内容は、

`id`の種類によって変わります。

たとえば`CHARTEVENT_OBJECT_CLICK`では、

`sparam`にクリックされたオブジェクトの名前が入り、

`lparam`・`dparam`はクリック座標になります。

マウスイベントを受け取るための事前準備

マウスの移動イベント(`CHARTEVENT_MOUSE_MOVE`)は、

初期状態では発火しません。

`OnInit()`の中で、

次の1行を実行して明示的に有効化する必要があります。

ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);
補足
・クリックイベント(CHARTEVENT_CLICK)自体は既定で発火します
・座標を伴うマウス移動の検知には別設定が必要です
・有効化を忘れるとボタンは反応するのにカーソル追従だけ動きません

OBJ_BUTTONでクリックできるボタンを作る

次に、実際にクリックできるボタンをチャート上に配置します。

ObjectCreateと位置・サイズの指定

`OBJ_BUTTON`はチャート座標ではなく、

画面の左上等を基準にしたスクリーン座標で配置するオブジェクトです。

ObjectCreate(0, "btnToggleLine", OBJ_BUTTON, 0, 0, 0);
ObjectSetInteger(0, "btnToggleLine", OBJPROP_CORNER, CORNER_LEFT_UPPER);
ObjectSetInteger(0, "btnToggleLine", OBJPROP_XDISTANCE, 10);
ObjectSetInteger(0, "btnToggleLine", OBJPROP_YDISTANCE, 20);
ObjectSetInteger(0, "btnToggleLine", OBJPROP_XSIZE, 140);
ObjectSetInteger(0, "btnToggleLine", OBJPROP_YSIZE, 24);
ObjectSetString(0, "btnToggleLine", OBJPROP_TEXT, "ライン ON/OFF");
ObjectSetInteger(0, "btnToggleLine", OBJPROP_BGCOLOR, clrSteelBlue);
ObjectSetInteger(0, "btnToggleLine", OBJPROP_COLOR, clrWhite);

`ObjectCreate`の`time1`・`price1`には、

ダミーで`0`を渡しています。

`OBJ_BUTTON`はスクリーン座標専用のため、この2つの値は使われません。

OBJPROP_STATEでボタンの押下状態を扱う

ボタンが押されているかどうかは、

`OBJPROP_STATE`(bool型)で取得できます。

bool pressed = ObjectGetInteger(0, "btnToggleLine", OBJPROP_STATE);

MQL5のボタンは、クリックすると押し込まれたまま表示が固定されます。

このままだと「押しっぱなし」に見えてしまうため、

判定処理のあとに自分で`false`へ戻す必要があります。

CHARTEVENT_OBJECT_CLICKでボタンのクリックを検知する

ボタンを配置しただけでは何も起きません。

クリックを検知する処理を書きます。

sparamにクリックされたオブジェクト名が入る

`CHARTEVENT_OBJECT_CLICK`が発生すると、

`sparam`にクリックされたオブジェクトの名前がそのまま渡されます。

if(id == CHARTEVENT_OBJECT_CLICK)
  {
   if(sparam == "btnToggleLine")
     {
      lineVisible = !lineVisible;
      ObjectSetInteger(0, "cursorPriceLine", OBJPROP_COLOR,
                        lineVisible ? clrOrangeRed : clrNONE);
      ObjectSetInteger(0, "btnToggleLine", OBJPROP_STATE, false);
      ChartRedraw(0);
     }
  }

`sparam`をボタン名と比較するだけなので、

複数のボタンを同じ`OnChartEvent()`の中で分岐して扱えます。

押しっぱなし状態を防ぐリセット処理

上のコードでは、ボタン判定の直後に

`OBJPROP_STATE`を`false`へ戻すことで、

押しっぱなし状態を解除しています。

注意
・このリセットを忘れると見た目が押しっぱなしになります
・ただしクリック判定自体は毎回きちんと実行されています
・「2回押さないと切り替わらない」という誤解はしないよう注意してください

マウス座標から価格を取得する(ChartXYToTimePrice)

ボタンに加えて、カーソルを動かすだけで価格が分かる仕組みも作ります。

CHARTEVENT_MOUSE_MOVEで座標を受け取る

if(id == CHARTEVENT_MOUSE_MOVE)
  {
   int x = (int)lparam;
   int y = (int)dparam;
   ...
  }

`CHARTEVENT_MOUSE_MOVE`では、`lparam`にX座標、

`dparam`にY座標がピクセル単位で渡されます

(他のイベントとは型の使われ方が異なる点に注意)。

カーソル位置の価格をラベルに表示する

bool ChartXYToTimePrice(long chart_id, int x, int y,
                        int &sub_window, datetime &time, double &price);

座標をそのまま使うのではなく、`ChartXYToTimePrice`に渡すことで、

その座標が指す「時刻」と「価格」に変換できます。

int win; datetime t; double p;
if(ChartXYToTimePrice(0, x, y, win, t, p))
  {
   ObjectSetString(0, "lblCursorPrice", OBJPROP_TEXT,
                    "価格: " + DoubleToString(p, _Digits));
  }

実際に動くコード全体

ボタンで水平線の表示・非表示を切り替えつつ、

マウス移動でカーソル位置の価格を表示するカスタムインジケーターです。

//+------------------------------------------------------------------+
//|                                       MouseButtonPanel.mq5         |
//|                     Copyright 2026, FX-EA System Project Creator |
//|                        https://creator.fx-ea-system-project.com/ |
//+------------------------------------------------------------------+
#property indicator_chart_window
#property indicator_buffers 0
#property indicator_plots   0

bool lineVisible = true;

int OnInit()
  {
   ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);

   ObjectCreate(0, "btnToggleLine", OBJ_BUTTON, 0, 0, 0);
   ObjectSetInteger(0, "btnToggleLine", OBJPROP_CORNER, CORNER_LEFT_UPPER);
   ObjectSetInteger(0, "btnToggleLine", OBJPROP_XDISTANCE, 10);
   ObjectSetInteger(0, "btnToggleLine", OBJPROP_YDISTANCE, 20);
   ObjectSetInteger(0, "btnToggleLine", OBJPROP_XSIZE, 140);
   ObjectSetInteger(0, "btnToggleLine", OBJPROP_YSIZE, 24);
   ObjectSetString(0, "btnToggleLine", OBJPROP_TEXT, "ライン ON/OFF");
   ObjectSetInteger(0, "btnToggleLine", OBJPROP_BGCOLOR, clrSteelBlue);
   ObjectSetInteger(0, "btnToggleLine", OBJPROP_COLOR, clrWhite);

   ObjectCreate(0, "cursorPriceLine", OBJ_HLINE, 0, 0, SymbolInfoDouble(_Symbol, SYMBOL_BID));
   ObjectSetInteger(0, "cursorPriceLine", OBJPROP_COLOR, clrOrangeRed);
   ObjectSetInteger(0, "cursorPriceLine", OBJPROP_STYLE, STYLE_DASH);

   ObjectCreate(0, "lblCursorPrice", OBJ_LABEL, 0, 0, 0);
   ObjectSetInteger(0, "lblCursorPrice", OBJPROP_CORNER, CORNER_LEFT_UPPER);
   ObjectSetInteger(0, "lblCursorPrice", OBJPROP_XDISTANCE, 10);
   ObjectSetInteger(0, "lblCursorPrice", OBJPROP_YDISTANCE, 54);
   ObjectSetString(0, "lblCursorPrice", OBJPROP_TEXT, "価格: -");

   return(INIT_SUCCEEDED);
  }

void OnDeinit(const int reason)
  {
   ObjectDelete(0, "btnToggleLine");
   ObjectDelete(0, "cursorPriceLine");
   ObjectDelete(0, "lblCursorPrice");
  }

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[])
  {
   return(rates_total);
  }

void OnChartEvent(const int id, const long &lparam,
                  const double &dparam, const string &sparam)
  {
   if(id == CHARTEVENT_OBJECT_CLICK && sparam == "btnToggleLine")
     {
      lineVisible = !lineVisible;
      ObjectSetInteger(0, "cursorPriceLine", OBJPROP_COLOR,
                        lineVisible ? clrOrangeRed : clrNONE);
      ObjectSetInteger(0, "btnToggleLine", OBJPROP_STATE, false);
      ChartRedraw(0);
      return;
     }

   if(id == CHARTEVENT_MOUSE_MOVE)
     {
      int x = (int)lparam;
      int y = (int)dparam;
      int win;
      datetime t;
      double p;
      if(ChartXYToTimePrice(0, x, y, win, t, p))
        {
         ObjectSetString(0, "lblCursorPrice", OBJPROP_TEXT,
                          "価格: " + DoubleToString(p, _Digits));
         if(lineVisible)
            ObjectSetDouble(0, "cursorPriceLine", OBJPROP_PRICE, p);
        }
     }
  }

MetaEditorでのコンパイル・実行手順

  1. MetaEditorで新規カスタムインジケーター「MouseButtonPanel」を作成し、上のコードを貼り付けてコンパイルする
  2. MT5のナビゲーターからチャートへドラッグ&ドロップして適用する
  3. チャート左上に「ライン ON/OFF」ボタンと価格ラベルが表示されることを確認する
  4. チャート上でマウスを動かし、価格ラベルの数値が変化することを確認する
  5. ボタンをクリックし、オレンジの点線(カーソル追従ライン)が消えたり表示されたりすることを確認する

よくあるエラーと注意点

ここまでのコードを自分で改造するときに、

つまずきやすいポイントをまとめます。

ボタンを押してもイベントが反応しない

`sparam`の文字列比較は完全一致です。

ボタン名を後から変更したのに、

`OnChartEvent()`側の比較文字列を直していないと、

一生反応しません。

オブジェクト名は定数化して1箇所にまとめておくと事故を防げます。

CHARTEVENT_MOUSE_MOVEを有効にすると重くなる

注意
・マウス移動イベントはカーソルを動かすたびに大量に発火します
・その中でヒストリカルデータの再取得や重い計算をすると動作が重くなります
・座標変換とラベル更新程度の軽い処理に留めてください

MQL4との違い

`OnChartEvent()`自体はMQL4にも存在し、書き方もほぼ同じです。

ただし`ChartXYToTimePrice`はMQL5の関数であり、

MQL4には存在しません。

MQL4で同等の処理をする場合は、`WindowPriceOnDropped()`等、

別の一連の関数を組み合わせる必要があります。

よくある質問

OnChartEventとマウスイベントについて、よく検索される疑問に回答します。

ボタンは何個でも置けますか?

はい、オブジェクト名を変えれば何個でも配置できます。

`OnChartEvent()`内の`sparam`の比較を、

ボタンの数だけ`if`文やswitch文で分岐させれば、

それぞれ別の処理を割り当てられます。

EA(エキスパートアドバイザー)でも同じコードは使えますか?

使えます。

`OnChartEvent()`はインジケーター・EAどちらでも、

実装できるイベント関数です。

EAにボタンを付ければ、「手動決済ボタン」のような操作パネルも同じ仕組みで作れます。

ボタンの見た目(色・サイズ)は自由に変えられますか?

`OBJPROP_BGCOLOR`・`OBJPROP_COLOR`・

`OBJPROP_XSIZE`・`OBJPROP_YSIZE`など、

この記事で使ったプロパティの値を変えるだけで、

色やサイズは自由に調整できます。

【MQL5】OnChartEventとマウスイベントでクリックできるボタンパネルを作る方法:まとめ

`OnChartEvent()`とマウスイベントを組み合わせれば、

チャート上にクリックできるボタンや、カーソルに追従する価格表示を自分で作れます。

ポイントは、`CHART_EVENT_MOUSE_MOVE`の事前有効化、

`CHARTEVENT_OBJECT_CLICK`でのボタン判定とステート解除、

`ChartXYToTimePrice`での座標→価格変換の3点です。

関連記事