【MQL4関数】OnChartEvent関数とは?チャート操作を行なったときに実行されるイベント関数

関数

OnChartEvent関数とは?

OnChartEvent関数は、チャート上で発生するさまざまなイベント(マウスクリック、キーボード入力、オブジェクトの操作など)を検知して処理を実行するためのイベントハンドラ関数です。エキスパートアドバイザー(EA)やインジケーターで使用でき、ユーザーのチャート操作に応じたインタラクティブな処理を実装できます。

この関数は、MQL4が自動的に呼び出す定義済みイベント関数の一つで、開発者が直接呼び出すものではありません。チャート上でイベントが発生するたびに、MT4のランタイムがこの関数を自動的に実行します。

基本的な書式

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

引数の説明

引数名 説明
id const int イベントの種類を示すID。CHARTEVENT列挙値が入る
lparam const long& イベントに応じたlong型パラメータ(キーコード、X座標など)
dparam const double& イベントに応じたdouble型パラメータ(Y座標、価格など)
sparam const string& イベントに応じたstring型パラメータ(オブジェクト名など)

戻り値

OnChartEvent関数の戻り値はvoid(なし)です。値を返す必要はありません。

主要なイベントID一覧

id引数に渡されるイベントIDは、CHARTEVENT列挙型で定義されています。代表的なものを以下に示します。

イベントID 説明 lparam dparam sparam
CHARTEVENT_KEYDOWN キーが押された キーコード 繰り返し回数
CHARTEVENT_MOUSE_MOVE マウスが移動した X座標 Y座標 マウスボタンフラグ
CHARTEVENT_OBJECT_CREATE オブジェクトが作成された オブジェクト名
CHARTEVENT_OBJECT_CHANGE オブジェクトが変更された オブジェクト名
CHARTEVENT_OBJECT_DELETE オブジェクトが削除された オブジェクト名
CHARTEVENT_CLICK チャートがクリックされた X座標 Y座標
CHARTEVENT_OBJECT_CLICK オブジェクトがクリックされた X座標 Y座標 オブジェクト名
CHARTEVENT_OBJECT_DRAG オブジェクトがドラッグされた オブジェクト名
CHARTEVENT_OBJECT_ENDEDIT テキスト編集が完了した オブジェクト名
CHARTEVENT_CHART_CHANGE チャートのサイズ等が変更された
CHARTEVENT_CUSTOM カスタムイベントの最初のID カスタム値 カスタム値 カスタム値
CHARTEVENT_CUSTOM_LAST カスタムイベントの最後のID カスタム値 カスタム値 カスタム値

プログラム例1:全イベントをログ出力する基本例

まずは最も基本的な使い方として、チャート上で発生するすべてのイベントをエキスパートログに出力するサンプルです。どのようなイベントが発生しているかを確認する際に便利です。

//+------------------------------------------------------------------+
//| OnChartEvent基本サンプル:全イベントのログ出力                      |
//+------------------------------------------------------------------+
#property strict

//+------------------------------------------------------------------+
//| 初期化関数                                                        |
//+------------------------------------------------------------------+
int OnInit()
{
   // オブジェクト作成・削除イベントを有効にする
   ChartSetInteger(0, CHART_EVENT_OBJECT_CREATE, true);
   ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, true);
   
   Print("=== OnChartEventログ出力サンプル開始 ===");
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| ティック関数(必須だが今回は処理なし)                               |
//+------------------------------------------------------------------+
void OnTick()
{
   // 特に処理なし
}

//+------------------------------------------------------------------+
//| チャートイベント処理関数                                           |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
{
   // イベントIDに応じてイベント名を判定
   string eventName = "";
   
   switch(id)
   {
      case CHARTEVENT_KEYDOWN:
         eventName = "キーダウン";
         // lparamにキーコードが入る
         Print(eventName, " | キーコード=", lparam, 
               " | 文字='", ShortToString((ushort)lparam), "'");
         break;
         
      case CHARTEVENT_CLICK:
         eventName = "チャートクリック";
         // lparamにX座標、dparamにY座標が入る
         Print(eventName, " | X=", lparam, " | Y=", dparam);
         break;
         
      case CHARTEVENT_OBJECT_CLICK:
         eventName = "オブジェクトクリック";
         // sparamにオブジェクト名が入る
         Print(eventName, " | オブジェクト名=", sparam,
               " | X=", lparam, " | Y=", dparam);
         break;
         
      case CHARTEVENT_OBJECT_CREATE:
         eventName = "オブジェクト作成";
         Print(eventName, " | オブジェクト名=", sparam);
         break;
         
      case CHARTEVENT_OBJECT_DELETE:
         eventName = "オブジェクト削除";
         Print(eventName, " | オブジェクト名=", sparam);
         break;
         
      case CHARTEVENT_OBJECT_CHANGE:
         eventName = "オブジェクト変更";
         Print(eventName, " | オブジェクト名=", sparam);
         break;
         
      case CHARTEVENT_OBJECT_DRAG:
         eventName = "オブジェクトドラッグ";
         Print(eventName, " | オブジェクト名=", sparam);
         break;
         
      case CHARTEVENT_CHART_CHANGE:
         eventName = "チャート変更";
         Print(eventName, "(サイズ変更、スクロール等)");
         break;
         
      default:
         // カスタムイベントまたは未分類のイベント
         if(id >= CHARTEVENT_CUSTOM && id <= CHARTEVENT_CUSTOM_LAST)
         {
            Print("カスタムイベント | ID=", id - CHARTEVENT_CUSTOM,
                  " | lparam=", lparam,
                  " | dparam=", dparam,
                  " | sparam=", sparam);
         }
         else
         {
            Print("不明なイベント | ID=", id);
         }
         break;
   }
}

このサンプルを動かすと、チャート上で操作を行うたびにエキスパートタブにイベント情報が出力されます。ChartSetIntegerでオブジェクトの作成・削除イベントを有効にしている点がポイントです。これを設定しないと、CHARTEVENT_OBJECT_CREATEとCHARTEVENT_OBJECT_DELETEイベントは発生しません。

プログラム例2:ボタンクリックで売買を行うパネルEA

チャート上にBuy/Sellボタンを配置し、クリックで注文を出す実用的なサンプルです。GUIベースのトレードパネルの基本構造として応用できます。

//+------------------------------------------------------------------+
//| ボタンクリックで売買を行うパネルEA                                  |
//+------------------------------------------------------------------+
#property strict

// 入力パラメータ
input double LotSize    = 0.01;   // ロットサイズ
input int    Slippage   = 3;      // スリッページ
input int    StopLoss   = 50;     // ストップロス(ポイント)
input int    TakeProfit = 100;    // テイクプロフィット(ポイント)

// ボタン名の定数
#define BTN_BUY    "BtnBuy"
#define BTN_SELL   "BtnSell"
#define BTN_CLOSE  "BtnCloseAll"
#define LBL_INFO   "LblInfo"

//+------------------------------------------------------------------+
//| ボタンを作成する関数                                               |
//+------------------------------------------------------------------+
bool CreateButton(string name, string text, int x, int y, 
                  int width, int height, color bgColor, color textColor)
{
   // ボタンオブジェクトを作成
   if(!ObjectCreate(0, name, OBJ_BUTTON, 0, 0, 0))
   {
      Print("ボタン作成失敗: ", name);
      return false;
   }
   
   // ボタンのプロパティを設定
   ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
   ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
   ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
   ObjectSetInteger(0, name, OBJPROP_XSIZE, width);
   ObjectSetInteger(0, name, OBJPROP_YSIZE, height);
   ObjectSetString(0, name, OBJPROP_TEXT, text);
   ObjectSetInteger(0, name, OBJPROP_COLOR, textColor);
   ObjectSetInteger(0, name, OBJPROP_BGCOLOR, bgColor);
   ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 10);
   ObjectSetInteger(0, name, OBJPROP_BORDER_COLOR, clrBlack);
   
   return true;
}

//+------------------------------------------------------------------+
//| 情報ラベルを作成する関数                                           |
//+------------------------------------------------------------------+
void CreateInfoLabel()
{
   ObjectCreate(0, LBL_INFO, OBJ_LABEL, 0, 0, 0);
   ObjectSetInteger(0, LBL_INFO, OBJPROP_CORNER, CORNER_LEFT_UPPER);
   ObjectSetInteger(0, LBL_INFO, OBJPROP_XDISTANCE, 20);
   ObjectSetInteger(0, LBL_INFO, OBJPROP_YDISTANCE, 150);
   ObjectSetInteger(0, LBL_INFO, OBJPROP_COLOR, clrWhite);
   ObjectSetInteger(0, LBL_INFO, OBJPROP_FONTSIZE, 9);
   ObjectSetString(0, LBL_INFO, OBJPROP_TEXT, "ボタンをクリックしてください");
}

//+------------------------------------------------------------------+
//| 情報ラベルを更新する関数                                           |
//+------------------------------------------------------------------+
void UpdateInfoLabel(string message)
{
   ObjectSetString(0, LBL_INFO, OBJPROP_TEXT, message);
   ChartRedraw(0);
}

//+------------------------------------------------------------------+
//| 初期化関数                                                        |
//+------------------------------------------------------------------+
int OnInit()
{
   // パネルのボタンを作成
   CreateButton(BTN_BUY,   "BUY",       20, 30, 120, 40, clrDodgerBlue,  clrWhite);
   CreateButton(BTN_SELL,  "SELL",       20, 75, 120, 40, clrCrimson,     clrWhite);
   CreateButton(BTN_CLOSE, "CLOSE ALL",  20, 120, 120, 25, clrDimGray,    clrWhite);
   
   // 情報表示ラベルを作成
   CreateInfoLabel();
   
   ChartRedraw(0);
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| 終了処理関数                                                      |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   // 作成したオブジェクトを全て削除
   ObjectDelete(0, BTN_BUY);
   ObjectDelete(0, BTN_SELL);
   ObjectDelete(0, BTN_CLOSE);
   ObjectDelete(0, LBL_INFO);
}

//+------------------------------------------------------------------+
//| ティック関数                                                      |
//+------------------------------------------------------------------+
void OnTick()
{
   // 特に処理なし
}

//+------------------------------------------------------------------+
//| 買い注文を送信する関数                                             |
//+------------------------------------------------------------------+
void ExecuteBuy()
{
   double sl = 0, tp = 0;
   double price = Ask;
   
   // ストップロス・テイクプロフィットを計算
   if(StopLoss > 0)
      sl = price - StopLoss * _Point;
   if(TakeProfit > 0)
      tp = price + TakeProfit * _Point;
   
   int ticket = OrderSend(Symbol(), OP_BUY, LotSize, price, Slippage, sl, tp,
                           "Panel Buy", 0, 0, clrBlue);
   
   if(ticket > 0)
      UpdateInfoLabel("BUY注文成功 Ticket#" + IntegerToString(ticket));
   else
      UpdateInfoLabel("BUY注文失敗 Error=" + IntegerToString(GetLastError()));
}

//+------------------------------------------------------------------+
//| 売り注文を送信する関数                                             |
//+------------------------------------------------------------------+
void ExecuteSell()
{
   double sl = 0, tp = 0;
   double price = Bid;
   
   if(StopLoss > 0)
      sl = price + StopLoss * _Point;
   if(TakeProfit > 0)
      tp = price - TakeProfit * _Point;
   
   int ticket = OrderSend(Symbol(), OP_SELL, LotSize, price, Slippage, sl, tp,
                           "Panel Sell", 0, 0, clrRed);
   
   if(ticket > 0)
      UpdateInfoLabel("SELL注文成功 Ticket#" + IntegerToString(ticket));
   else
      UpdateInfoLabel("SELL注文失敗 Error=" + IntegerToString(GetLastError()));
}

//+------------------------------------------------------------------+
//| 全ポジションを決済する関数                                         |
//+------------------------------------------------------------------+
void CloseAllPositions()
{
   int closedCount = 0;
   
   // 全注文を逆順にループして決済
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
         continue;
      
      // 現在のシンボルのポジションのみ対象
      if(OrderSymbol() != Symbol())
         continue;
      
      bool result = false;
      
      if(OrderType() == OP_BUY)
         result = OrderClose(OrderTicket(), OrderLots(), Bid, Slippage, clrBlue);
      else if(OrderType() == OP_SELL)
         result = OrderClose(OrderTicket(), OrderLots(), Ask, Slippage, clrRed);
      
      if(result)
         closedCount++;
   }
   
   UpdateInfoLabel(IntegerToString(closedCount) + "件のポジションを決済しました");
}

//+------------------------------------------------------------------+
//| チャートイベント処理関数                                           |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
{
   // オブジェクトクリックイベントのみ処理
   if(id == CHARTEVENT_OBJECT_CLICK)
   {
      // クリックされたオブジェクト名で分岐
      if(sparam == BTN_BUY)
      {
         ExecuteBuy();
         // ボタンの押下状態を解除(元に戻す)
         ObjectSetInteger(0, BTN_BUY, OBJPROP_STATE, false);
      }
      else if(sparam == BTN_SELL)
      {
         ExecuteSell();
         ObjectSetInteger(0, BTN_SELL, OBJPROP_STATE, false);
      }
      else if(sparam == BTN_CLOSE)
      {
         CloseAllPositions();
         ObjectSetInteger(0, BTN_CLOSE, OBJPROP_STATE, false);
      }
      
      ChartRedraw(0);
   }
}

このサンプルのポイントは、ボタンクリック後にObjectSetIntegerOBJPROP_STATEをfalseに戻している点です。これを行わないと、ボタンが押されたままの状態で表示され続けます。

プログラム例3:キーボードショートカットで機能を切り替えるインジケーター

キーボード入力(CHARTEVENT_KEYDOWN)を使って、インジケーターの表示モードをキーボードショートカットで切り替えるサンプルです。

//+------------------------------------------------------------------+
//| キーボードショートカットで表示を切り替えるインジケーター              |
//+------------------------------------------------------------------+
#property strict
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_color1 clrDodgerBlue
#property indicator_color2 clrOrangeRed
#property indicator_width1 2
#property indicator_width2 2

// インジケーターバッファ
double FastMABuffer[];   // 短期移動平均
double SlowMABuffer[];   // 長期移動平均

// 表示モード管理
int    DisplayMode = 0;     // 0=両方表示, 1=短期のみ, 2=長期のみ
string ModeName[]  = {"両方表示", "短期MAのみ", "長期MAのみ"};

// 移動平均のパラメータ
input int FastPeriod = 20;   // 短期MA期間
input int SlowPeriod = 50;   // 長期MA期間

// ステータス表示用ラベル名
#define STATUS_LABEL "MA_StatusLabel"

//+------------------------------------------------------------------+
//| ステータスラベルの作成・更新                                        |
//+------------------------------------------------------------------+
void UpdateStatusLabel()
{
   // ラベルがなければ作成
   if(ObjectFind(0, STATUS_LABEL) < 0)
   {
      ObjectCreate(0, STATUS_LABEL, OBJ_LABEL, 0, 0, 0);
      ObjectSetInteger(0, STATUS_LABEL, OBJPROP_CORNER, CORNER_RIGHT_UPPER);
      ObjectSetInteger(0, STATUS_LABEL, OBJPROP_XDISTANCE, 15);
      ObjectSetInteger(0, STATUS_LABEL, OBJPROP_YDISTANCE, 30);
      ObjectSetInteger(0, STATUS_LABEL, OBJPROP_COLOR, clrYellow);
      ObjectSetInteger(0, STATUS_LABEL, OBJPROP_FONTSIZE, 10);
      ObjectSetString(0, STATUS_LABEL, OBJPROP_FONT, "Arial Bold");
      ObjectSetInteger(0, STATUS_LABEL, OBJPROP_ANCHOR, ANCHOR_RIGHT_UPPER);
   }
   
   // 現在のモード名を表示
   string text = "MA表示: " + ModeName[DisplayMode] + " [M:切替 / H:ヘルプ]";
   ObjectSetString(0, STATUS_LABEL, OBJPROP_TEXT, text);
   ChartRedraw(0);
}

//+------------------------------------------------------------------+
//| 表示モードに応じてバッファの表示/非表示を切り替える                   |
//+------------------------------------------------------------------+
void ApplyDisplayMode()
{
   switch(DisplayMode)
   {
      case 0: // 両方表示
         SetIndexStyle(0, DRAW_LINE, STYLE_SOLID, 2, clrDodgerBlue);
         SetIndexStyle(1, DRAW_LINE, STYLE_SOLID, 2, clrOrangeRed);
         break;
         
      case 1: // 短期MAのみ
         SetIndexStyle(0, DRAW_LINE, STYLE_SOLID, 2, clrDodgerBlue);
         SetIndexStyle(1, DRAW_NONE);  // 長期MAを非表示
         break;
         
      case 2: // 長期MAのみ
         SetIndexStyle(0, DRAW_NONE);  // 短期MAを非表示
         SetIndexStyle(1, DRAW_LINE, STYLE_SOLID, 2, clrOrangeRed);
         break;
   }
   
   UpdateStatusLabel();
}

//+------------------------------------------------------------------+
//| 初期化関数                                                        |
//+------------------------------------------------------------------+
int OnInit()
{
   // バッファの設定
   SetIndexBuffer(0, FastMABuffer);
   SetIndexBuffer(1, SlowMABuffer);
   SetIndexLabel(0, "Fast MA(" + IntegerToString(FastPeriod) + ")");
   SetIndexLabel(1, "Slow MA(" + IntegerToString(SlowPeriod) + ")");
   
   // 初期表示モードを適用
   ApplyDisplayMode();
   
   Print("=== MA表示切替インジケーター ===");
   Print("Mキー: 表示モード切替");
   Print("Hキー: ヘルプ表示");
   
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| 終了処理関数                                                      |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   ObjectDelete(0, STATUS_LABEL);
}

//+------------------------------------------------------------------+
//| メイン計算関数                                                    |
//+------------------------------------------------------------------+
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 - SlowPeriod - 1;
   
   // 移動平均を計算
   for(int i = limit; i >= 0; i--)
   {
      FastMABuffer[i] = iMA(NULL, 0, FastPeriod, 0, MODE_EMA, PRICE_CLOSE, i);
      SlowMABuffer[i] = iMA(NULL, 0, SlowPeriod, 0, MODE_EMA, PRICE_CLOSE, i);
   }
   
   return(rates_total);
}

//+------------------------------------------------------------------+
//| チャートイベント処理関数                                           |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
{
   // キーダウンイベントのみ処理
   if(id != CHARTEVENT_KEYDOWN)
      return;
   
   // lparamにキーコードが入る
   switch((int)lparam)
   {
      case 'M':  // Mキー:表示モードを切り替え
         DisplayMode = (DisplayMode + 1) % 3;  // 0→1→2→0とループ
         ApplyDisplayMode();
         Print("表示モードを変更: ", ModeName[DisplayMode]);
         break;
         
      case 'H':  // Hキー:ヘルプを表示
         Print("=== ヘルプ ===");
         Print("M: 表示モード切替(両方→短期のみ→長期のみ)");
         Print("H: このヘルプを表示");
         Print("現在のモード: ", ModeName[DisplayMode]);
         break;
   }
}

CHARTEVENT_KEYDOWNイベントでは、lparamに押されたキーの仮想キーコードが格納されます。英字キーの場合は大文字のASCIIコード('A'〜'Z')で判定できます。このサンプルではMキーで表示モードを順番に切り替え、Hキーでヘルプ情報を出力しています。

プログラム例4:マウス位置のクロスヘア情報を表示するインジケーター

CHARTEVENT_MOUSE_MOVEを使って、マウスカーソルの位置に対応する価格と時刻をリアルタイムに表示するインジケーターです。マウスイベントの活用方法を理解できます。

//+------------------------------------------------------------------+
//| マウス位置のクロスヘア情報表示インジケーター                         |
//+------------------------------------------------------------------+
#property strict
#property indicator_chart_window
#property indicator_buffers 0

// オブジェクト名定数
#define LABEL_PRICE  "CrossHair_Price"
#define LABEL_TIME   "CrossHair_Time"
#define LABEL_BAR    "CrossHair_Bar"
#define LINE_H       "CrossHair_HLine"
#define LINE_V       "CrossHair_VLine"

// 表示ON/OFF
bool ShowCrossHair = true;

//+------------------------------------------------------------------+
//| ラベルを作成する関数                                               |
//+------------------------------------------------------------------+
void CreateLabel(string name, int x, int y, color clr)
{
   if(ObjectFind(0, name) < 0)
   {
      ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
      ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_LOWER);
      ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
      ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
      ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
      ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 10);
      ObjectSetString(0, name, OBJPROP_FONT, "Consolas");
      ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_LEFT_LOWER);
   }
}

//+------------------------------------------------------------------+
//| 全オブジェクトを削除する関数                                       |
//+------------------------------------------------------------------+
void DeleteAllObjects()
{
   ObjectDelete(0, LABEL_PRICE);
   ObjectDelete(0, LABEL_TIME);
   ObjectDelete(0, LABEL_BAR);
   ObjectDelete(0, LINE_H);
   ObjectDelete(0, LINE_V);
}

//+------------------------------------------------------------------+
//| 初期化関数                                                        |
//+------------------------------------------------------------------+
int OnInit()
{
   // マウス移動イベントを有効にする
   ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);
   
   // 情報ラベルを作成
   CreateLabel(LABEL_PRICE, 20, 60, clrLime);
   CreateLabel(LABEL_TIME,  20, 40, clrAqua);
   CreateLabel(LABEL_BAR,   20, 20, clrYellow);
   
   Print("=== クロスヘア情報インジケーター ===");
   Print("Sキーで表示ON/OFF切替");
   
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| 終了処理関数                                                      |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   // マウス移動イベントを無効に戻す
   ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, false);
   DeleteAllObjects();
}

//+------------------------------------------------------------------+
//| メイン計算関数                                                    |
//+------------------------------------------------------------------+
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)
{
   // キーダウンイベント:Sキーで表示切替
   if(id == CHARTEVENT_KEYDOWN)
   {
      if((int)lparam == 'S')
      {
         ShowCrossHair = !ShowCrossHair;
         if(!ShowCrossHair)
         {
            // 非表示時はラベルをクリア
            ObjectSetString(0, LABEL_PRICE, OBJPROP_TEXT, "");
            ObjectSetString(0, LABEL_TIME,  OBJPROP_TEXT, "");
            ObjectSetString(0, LABEL_BAR,   OBJPROP_TEXT, "");
            ObjectDelete(0, LINE_H);
            ObjectDelete(0, LINE_V);
            ChartRedraw(0);
         }
         Print("クロスヘア表示: ", ShowCrossHair ? "ON" : "OFF");
      }
      return;
   }
   
   // マウス移動イベント
   if(id == CHARTEVENT_MOUSE_MOVE && ShowCrossHair)
   {
      // ピクセル座標を取得
      int mouseX = (int)lparam;
      int mouseY = (int)dparam;
      
      // ピクセル座標をチャートの時間・価格に変換
      int      subWindow = 0;
      datetime mouseTime = 0;
      double   mousePrice = 0;
      
      // ChartXYToTimePrice関数でピクセル座標を価格・時刻に変換
      if(ChartXYToTimePrice(0, mouseX, mouseY, subWindow, mouseTime, mousePrice))
      {
         // メインウィンドウ上のみ処理
         if(subWindow == 0)
         {
            // バーインデックスを取得
            int barIndex = iBarShift(NULL, 0, mouseTime);
            
            // ラベルに情報を表示
            ObjectSetString(0, LABEL_PRICE, OBJPROP_TEXT,
                          "価格: " + DoubleToString(mousePrice, _Digits));
            ObjectSetString(0, LABEL_TIME, OBJPROP_TEXT,
                          "時刻: " + TimeToString(mouseTime, TIME_DATE | TIME_MINUTES));
            ObjectSetString(0, LABEL_BAR, OBJPROP_TEXT,
                          "バー: " + IntegerToString(barIndex) +
                          " | O:" + DoubleToString(Open[barIndex], _Digits) +
                          " H:" + DoubleToString(High[barIndex], _Digits) +
                          " L:" + DoubleToString(Low[barIndex], _Digits) +
                          " C:" + DoubleToString(Close[barIndex], _Digits));
            
            // 水平線を描画
            if(ObjectFind(0, LINE_H) < 0)
            {
               ObjectCreate(0, LINE_H, OBJ_HLINE, 0, 0, mousePrice);
               ObjectSetInteger(0, LINE_H, OBJPROP_COLOR, clrGray);
               ObjectSetInteger(0, LINE_H, OBJPROP_STYLE, STYLE_DOT);
               ObjectSetInteger(0, LINE_H, OBJPROP_SELECTABLE, false);
            }
            else
            {
               ObjectSetDouble(0, LINE_H, OBJPROP_PRICE, mousePrice);
            }
            
            // 垂直線を描画
            if(ObjectFind(0, LINE_V) < 0)
            {
               ObjectCreate(0, LINE_V, OBJ_VLINE, 0, mouseTime, 0);
               ObjectSetInteger(0, LINE_V, OBJPROP_COLOR, clrGray);
               ObjectSetInteger(0, LINE_V, OBJPROP_STYLE, STYLE_DOT);
               ObjectSetInteger(0, LINE_V, OBJPROP_SELECTABLE, false);
            }
            else
            {
               ObjectSetInteger(0, LINE_V, OBJPROP_TIME, mouseTime);
            }
            
            ChartRedraw(0);
         }
      }
   }
}

マウス移動イベントを使用するには、ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true)で明示的に有効化する必要があります。ChartXYToTimePrice関数を使うことで、ピクセル座標をチャート上の時刻と価格に変換できます。このサンプルでは変換した情報をラベルに表示し、クロスヘア線も描画しています。

OnChartEvent関数を使う際の注意点

イベントの有効化が必要な場合がある

一部のイベントはデフォルトでは無効になっており、ChartSetIntegerで明示的に有効化する必要があります。

イベント 有効化に必要な設定
CHARTEVENT_MOUSE_MOVE ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true)
CHARTEVENT_OBJECT_CREATE ChartSetInteger(0, CHART_EVENT_OBJECT_CREATE, true)
CHARTEVENT_OBJECT_DELETE ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, true)

スクリプトでは使用できない

OnChartEvent関数はEA(エキスパートアドバイザー)とインジケーターでのみ使用できます。スクリプトではイベントハンドラが呼び出されません。

重い処理を避ける

特にCHARTEVENT_MOUSE_MOVEはマウスを動かすたびに高頻度で発生するため、イベントハンドラ内に重い処理を記述するとチャートの動作が遅くなります。必要最小限の処理にとどめるか、一定間隔でのみ処理を実行するようにしましょう。

ボタンの状態リセット

OBJ_BUTTONオブジェクトをクリックした際は、処理後にOBJPROP_STATEをfalseに設定してボタンの押下状態をリセットする必要があります。これを忘れると、次回クリック時にイベントが正しく発生しない場合があります。

同一チャートでの制約

一つのチャートに複数のEAやインジケーターがアタッチされている場合、それぞれのOnChartEvent関数が同じイベントを受け取ります。オブジェクト名の命名規則に注意し、自分が作成したオブジェクトのみを処理するようにフィルタリングすることが重要です。