チャート上に「ライン 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点です。

関連記事