[MQL5]TradingView風味の操作でMT5に四角形を描画してみる
TradingView風味にできないかしら?
トレードにはMT5を使っていますが、チャート分析ではTradingViewを使うことが多いので、MT5のチャートにオブジェクト(四角形、トレンドライン、水平線など)を配置する時、
TradingViewと同じような操作感にできないものかと思いました。
たとえば、MT5で四角形を描画する場合は
(1) 四角形アイコンをクリック (2) チャート上のどこかにマウスカーソルを移動 (3) マウスの左ボタンを押下したまま、マウスを移動 (4) マウスの左ボタンを放す
とします。
TradingViewで四角形を描画する場合は
(1) 四角形アイコンをクリック (2) チャート上のどこかをクリック (3) マウスを移動 (4) マウスの左ボタンをクリック
とします。
個人的には、TradingViewの方式の方が楽だと感じています。そこで、TradingView風の操作感で四角形を作成するボタンを作ってみました。
TradingView風味のコード(MT5用)
// このインディケーターが対応している時間軸は M5, M15, H1, H4, D1, W1, MN #property copyright "Copyright 2022. PHILOJUAN" #property link "https://uhoho.hatenablog.jp/" #property strict #property indicator_chart_window #property indicator_plots 0 input color color_1 = clrLimeGreen; // 色(5分足) input int width_1 = 1; // 太さ(5分足) input color color_2 = clrLimeGreen; // 色(15分足) input int width_2 = 1; // 太さ(15分足) input color color_3 = C'85, 85, 255'; // 色(1時間足) input int width_3 = 1; // 太さ(1時間足) input color color_4 = clrDarkOrange; // 色(4時間足) input int width_4 = 1; // 太さ(4時間足) input color color_5 = clrMagenta; // 色(日足) input int width_5 = 2; // 太さ(日足) input color color_6 = clrDeepSkyBlue; // 色(週足) input int width_6 = 2; // 太さ(週足) input color color_7 = clrMediumOrchid; // 色(月足) input int width_7 = 2; // 太さ(月足) input int buttonXdis1 = 450; // ボタンの表示位置(X軸) input int buttonYdis1 = 6; // ボタンの表示位置(Y軸) input color colorOfBorder = clrNONE; // ボタンの縁の色 //-------------------------------------------------------------------- // グローバル変数 (パラメーター設定の対象にしないもの) //-------------------------------------------------------------------- string buttonName = "GBBPPQ_Rec_HHK"; // ボタン名 color colorOfRecs = clrNONE; // オブジェクトの色 int widthOfLine = 1; // Rectangleのラインの太さ string RecName = ""; // Rectangleの名前 int numOfRec = 0; // Rectangleの数 int step = 0; // 処理のステップ string prefix = "RZ98FDBWQQP_"; // オブジェクト名の接頭辞 //------------------------------------------------------------------ // インディケーター挿入時 or timeframe変更直後 //------------------------------------------------------------------ int OnInit() { string shortname = "Rec_button_indicator"; IndicatorSetString(INDICATOR_SHORTNAME, shortname); if(width_1 < 1 || width_2 < 1 || width_3 < 1 || width_4 < 1 || width_5 < 1 || width_6 < 1 || width_7 < 1 || width_1 > 5 || width_2 > 5 || width_3 > 5 || width_4 > 5 || width_5 > 5 || width_6 > 5 || width_7 > 5 ) { Print("Width parameter is wrong"); ChartIndicatorDelete(0, 0,shortname); return INIT_FAILED; } if(buttonXdis1 < 1 || buttonYdis1 < 1) { Print("Position parameter is wrong"); ChartIndicatorDelete(0, 0,shortname); return INIT_FAILED; } CreateButton(buttonName, "Rect", CORNER_LEFT_UPPER, buttonXdis1, buttonYdis1, 60, 15, 7, clrWhite, colorOfBorder, clrGray); SetColorAndWidth(TimeFrameToMinutes(Period())); return(INIT_SUCCEEDED); } //------------------------------------------------------------------ // Tick受信時 (今回は処理なし) //------------------------------------------------------------------ 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); } //------------------------------------------------------------------ // インディケーター除去時 or timeframe変更直前 //------------------------------------------------------------------ void OnDeinit(const int reason) { ObjectDelete(0, buttonName); ChartRedraw(); } //+------------------------------------------------------------------ //| ChartEvent function //+------------------------------------------------------------------ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { datetime time = 0; double price = 0.0; int window = 0; if(id == CHARTEVENT_OBJECT_CLICK) { if(sparam == buttonName && step == 0) step = 1; } if(id == CHARTEVENT_CLICK) { if(step == 1) step = 2; else if(step == 2) { if(!ChartXYToTimePrice(0, (int)lparam, (int)dparam, window, time, price)) Print("ChartXYToTimePrice error. Erro code : ", GetLastError()); RecName = prefix + IntegerToString(numOfRec); // オブジェクト名作成 // 既に同名オブジェクトがある場合への対応 while(ObjectFind(0, RecName) >= 0) { numOfRec++; RecName = prefix + IntegerToString(numOfRec); } MakeRectangle(RecName, time, price, time, price, colorOfRecs, STYLE_SOLID, widthOfLine, false); ChartRedraw(); ChartEventMouseMoveSet(true); // CHARTEVENT_MOUSE_MOVEモードに入る } } if(id == CHARTEVENT_MOUSE_MOVE) { if(step == 2) { // マウスの移動に合わせて、Rectangleのアンカーを変更 if(!ChartXYToTimePrice(0, (int)lparam, (int)dparam, window, time, price)) Print("ChartXYToTimePrice error. Erro code : ", GetLastError()); // ObjectMoveの第3引数はpoint index(アンカーポイント番号) // Rectangleの場合、アンカーポイントは2つなので、point index は 0 か 1 ObjectMove(0, RecName, 1, time, price); ChartRedraw(); // 左クリックをしたら CHARTEVENT_MOUSE_MOVE モードを抜ける // CHARTEVENT_MOUSE_MOVEでのsparamはマウスボタンの状態(1は押下した状態) if(sparam == "1") { ChartEventMouseMoveSet(false); ObjectSetInteger(0, buttonName, OBJPROP_STATE, false); ChartRedraw(); step = 0; } } } } //-------------------------------------------------------------------- // CHART_EVENT_MOUSE_MOVEモードのON / OFF を切り替える関数 //-------------------------------------------------------------------- bool ChartEventMouseMoveSet(const bool value, const long chart_ID = 0) { ResetLastError(); if(!ChartSetInteger(chart_ID, CHART_EVENT_MOUSE_MOVE, 0, value)) { Print(__FUNCTION__ + ", Error Code = ", GetLastError()); return(false); } return(true); } // この関数はMetaEditorのヘルプからそのまま流用(記述箇所は以下のとおり) // 「標準的な定数、列挙と構造体」→「チャート定数」→「チャート操作の例」 //-------------------------------------------------------------------- // Rectangleを描画する関数 //-------------------------------------------------------------------- void MakeRectangle(string objName, // オブジェクト名 datetime rcTime1, // 描画座標1(時間) double rcPrice1, // 描画座標1(価格) datetime rcTime2, // 描画座標2(時間) double rcPrice2, // 描画座標2(価格) color rcColor, // 色 ENUM_LINE_STYLE rcStyle, // 線種 int rcWidth, // 太さ bool rcFill) // 塗りつぶし { if(rcStyle != STYLE_SOLID && rcWidth > 1) { Alert("[MakeRectangle Function] Width is over 1. Style must be solid."); return; } ObjectCreate( 0, objName, OBJ_RECTANGLE, 0, rcTime1, rcPrice1, rcTime2, rcPrice2); ObjectSetInteger(0, objName, OBJPROP_COLOR, rcColor); ObjectSetInteger(0, objName, OBJPROP_STYLE, rcStyle); ObjectSetInteger(0, objName, OBJPROP_WIDTH, rcWidth); ObjectSetInteger(0, objName, OBJPROP_BACK, false); // false 前景 ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, true); ObjectSetInteger(0, objName, OBJPROP_SELECTED, true); ObjectSetInteger(0, objName, OBJPROP_HIDDEN, false); ObjectSetInteger(0, objName, OBJPROP_ZORDER, 0); ObjectSetString( 0, objName, OBJPROP_TOOLTIP, "\n"); ObjectSetInteger(0, objName, OBJPROP_FILL, rcFill); // 塗りつぶし } //-------------------------------------------------------------------- // MQL5の時間軸をint型の「分」に変換する関数 //-------------------------------------------------------------------- // [備考] MQL4と違い、PERIOD_H1以上は分数に対応していない int TimeFrameToMinutes(ENUM_TIMEFRAMES timeframe) { int minutes = 0; if(timeframe == PERIOD_CURRENT) timeframe = _Period; if(timeframe < PERIOD_H1) { minutes = (int)StringToInteger(StringSubstr(EnumToString(timeframe), 8)); // [備考] // MT5の仕様が変わらないなら、if(timeframe < PERIOD_H1) の場合は // minutes = timeframe; // でもOK。 } else if(timeframe < PERIOD_D1) { minutes = 60 * (int)StringToInteger(StringSubstr(EnumToString(timeframe), 8)); } else { switch(timeframe) { case PERIOD_D1: minutes = 1440; break; case PERIOD_W1: minutes = 10080; break; case PERIOD_MN1: minutes = 43200; // 43200分は 30日に相当(MQL4のMN1を踏襲) break; default: minutes = -1; // ERROR } } return minutes; } //-------------------------------------------------------------------- // ボタンを作成する関数 //-------------------------------------------------------------------- void CreateButton(string name, // オブジェクト名 string caption, // ボタン内に表示するテキスト int corner, // 基点(ボタンを配置する時に基準とする場所) int xshift, // 基点からの距離(横方向) int yshift, // 基点からの距離(縦方向) int xsize, // ボタンのサイズ(横) int ysize, // ボタンのサイズ(縦) int font_Size, // ボタン内に表示するテキストのサイズ color textColor, // ボタン内に表示するテキストの色 color bo_color, // ボタンの縁の色 color bg_Color) // ボタンの色 { ObjectCreate( 0, name, OBJ_BUTTON, 0, 0, 0); ObjectSetInteger(0, name, OBJPROP_CORNER, corner); ObjectSetInteger(0, name, OBJPROP_XDISTANCE, xshift); ObjectSetInteger(0, name, OBJPROP_YDISTANCE, yshift); ObjectSetInteger(0, name, OBJPROP_XSIZE, xsize); ObjectSetInteger(0, name, OBJPROP_YSIZE, ysize); ObjectSetInteger(0, name, OBJPROP_BORDER_COLOR, bo_color); ObjectSetInteger(0, name, OBJPROP_BGCOLOR, bg_Color); ObjectSetString (0, name, OBJPROP_TEXT, caption); ObjectSetString (0, name, OBJPROP_FONT, "Arial"); ObjectSetInteger(0, name, OBJPROP_FONTSIZE, font_Size); ObjectSetInteger(0, name, OBJPROP_COLOR, textColor); ObjectSetInteger(0, name, OBJPROP_STATE, false); ObjectSetInteger(0, name, OBJPROP_HIDDEN, true); ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, name, OBJPROP_SELECTED, false); ObjectSetInteger(0, name, OBJPROP_ZORDER, 0); } //-------------------------------------------------------------------- // チャートの時間軸に応じた色と太さをセットする関数 //-------------------------------------------------------------------- // [注意] この関数内ではグローバル変数を使用している void SetColorAndWidth(int timeframe) { if(timeframe <= 5) { // 5分足 colorOfRecs = color_1; widthOfLine = width_1; } else if(timeframe <= 15) { // 15分足 colorOfRecs = color_2; widthOfLine = width_2; } else if(timeframe <= 60) { // 1時間足 colorOfRecs = color_3; widthOfLine = width_3; } else if(timeframe <= 240) { // 4時間足 colorOfRecs = color_4; widthOfLine = width_4; } else if(timeframe <= 1440) { // 日足 colorOfRecs = color_5; widthOfLine = width_5; } else if(timeframe <= 10080) { // 週足 colorOfRecs = color_6; widthOfLine = width_6; } else { // 月足 colorOfRecs = color_7; widthOfLine = width_7; } }
ついでに、MT5風味のコード(MT5用)
メリットは特にないのですが、MT5のツールバーに配置されている四角形作成ボタンを使った場合と同じ操作感をインディケーター(以下「インジ」)で再現してみようと思い、コードを書いてみました。
// このインディケーターが対応している時間軸は M5, M15, H1, H4, D1, W1, MN #property copyright "Copyright 2022. PHILOJUAN" #property link "https://uhoho.hatenablog.jp/" #property strict #property indicator_chart_window #property indicator_plots 0 input color color_1 = clrLimeGreen; // 色(5分足) input int width_1 = 1; // 太さ(5分足) input color color_2 = clrLimeGreen; // 色(15分足) input int width_2 = 1; // 太さ(15分足) input color color_3 = C'85, 85, 255'; // 色(1時間足) input int width_3 = 1; // 太さ(1時間足) input color color_4 = clrDarkOrange; // 色(4時間足) input int width_4 = 1; // 太さ(4時間足) input color color_5 = clrMagenta; // 色(日足) input int width_5 = 2; // 太さ(日足) input color color_6 = clrDeepSkyBlue; // 色(週足) input int width_6 = 2; // 太さ(週足) input color color_7 = clrMediumOrchid; // 色(月足) input int width_7 = 2; // 太さ(月足) input int buttonXdis1 = 450; // ボタンの表示位置(X軸) input int buttonYdis1 = 6; // ボタンの表示位置(Y軸) input color colorOfBorder = clrNONE; // ボタンの縁の色 //-------------------------------------------------------------------- // グローバル変数 (パラメーター設定の対象にしないもの) //-------------------------------------------------------------------- string buttonName = "GBBPPQ_Rec_HHK"; // ボタン名 color colorOfRecs = clrNONE; // オブジェクトの色 int widthOfLine = 1; // ラインの太さ string RecName = ""; // Rectangleの名前 int numOfRec = 0; // Rectangleの数 int step = 0; // 処理のステップ string prefix = "RZ98FDBWQQP_"; // オブジェクト名の接頭辞 //------------------------------------------------------------------ // インディケーター挿入時 or timeframe変更直後 //------------------------------------------------------------------ int OnInit() { string shortname = "Rec_button_indicator"; IndicatorSetString(INDICATOR_SHORTNAME, shortname); if(width_1 < 1 || width_2 < 1 || width_3 < 1 || width_4 < 1 || width_5 < 1 || width_6 < 1 || width_7 < 1 || width_1 > 5 || width_2 > 5 || width_3 > 5 || width_4 > 5 || width_5 > 5 || width_6 > 5 || width_7 > 5 ) { Print("Width parameter is wrong"); ChartIndicatorDelete(0, 0, shortname); return INIT_FAILED; } if(buttonXdis1 < 1 || buttonYdis1 < 1) { Print("Position parameter is wrong"); ChartIndicatorDelete(0, 0, shortname); return INIT_FAILED; } CreateButton(buttonName, "Rect", CORNER_LEFT_UPPER, buttonXdis1, buttonYdis1, 60, 15, 7, clrWhite, colorOfBorder, clrGray); SetColorAndWidth(TimeFrameToMinutes(Period())); return(INIT_SUCCEEDED); } //------------------------------------------------------------------ // Tick受信時 (今回は処理なし) //------------------------------------------------------------------ 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); } //------------------------------------------------------------------ // インディケーター除去時 or timeframe変更直前 //------------------------------------------------------------------ void OnDeinit(const int reason) { ObjectDelete(0, buttonName); ChartRedraw(); } //+------------------------------------------------------------------ //| ChartEvent function //+------------------------------------------------------------------ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { datetime time = 0; double price = 0.0; int window = 0; //-------------------------------------------- // Rectボタンクリック時 //-------------------------------------------- if(id == CHARTEVENT_OBJECT_CLICK) { if(sparam == buttonName && step == 0) { ChartMouseScrollSet(false); // 左ボタンによるチャートスクロールを無効にする ChartEventMouseMoveSet(true); // CHARTEVENT_MOUSE_MOVEモードに入る bool moveIsON = false; ChartEventMouseMoveGet(moveIsON); // CHARTEVENT_MOUSE_MOVEの状態を取得 if(moveIsON) step = 1; } } //-------------------------------------------- // CHARTEVENT_MOUSE_MOVEモード中の処理 //-------------------------------------------- // [備考] このモード中、sparamの値は「マウスボタンの状態」 if(id == CHARTEVENT_MOUSE_MOVE) { // Rectボタンクリック後、次のクリックでRectangleを作成 if(step == 1 && sparam == "1") { if(!ChartXYToTimePrice(0, (int)lparam, (int)dparam, window, time, price)) Print("ChartXYToTimePrice error. Erro code : ", GetLastError()); RecName = prefix + IntegerToString(numOfRec); // オブジェクト名作成 // 既に同名オブジェクトがある場合への対応 while(ObjectFind(0, RecName) >= 0) { numOfRec++; RecName = prefix + IntegerToString(numOfRec); } MakeRectangle(RecName, time, price, time, price, colorOfRecs, STYLE_SOLID, widthOfLine, false); ChartRedraw(); step = 2; } // [if文を整理したくなったら、このメモを読む] // 以下の else if文 2つを step == 2でまとめ、1階層下に sparam == 1 と // sparam == 0 のif文を作るとネストが深くなるので、このままにしておく // 左ボタンを押したままマウスを動かしたらRectangleのアンカーを変更 else if(step == 2 && sparam == "1") { if(!ChartXYToTimePrice(0, (int)lparam, (int)dparam, window, time, price)) Print("ChartXYToTimePrice error. Erro code : ", GetLastError()); // ObjectMoveの第3引数はpoint index(アンカーポイント番号) // Rectangleの場合、アンカーポイントは2つだけなので、point index は 0 と 1 だけ ObjectMove(0, RecName, 1, time, price); ChartRedraw(); } // マウスの左ボタンを離したら全ての作業完了 // [備考] ボタンを離した判定をするのはCHARTEVENT_MOUSE_MOVEモード中なので、ここに記述 else if(step == 2 && sparam == "0") { ChartEventMouseMoveSet(false); ChartMouseScrollSet(true); ObjectSetInteger(0, buttonName, OBJPROP_STATE, false); ChartRedraw(); step = 0; } } } //-------------------------------------------------------------------- // Rectangleを描画する関数 //-------------------------------------------------------------------- void MakeRectangle(string objName, // オブジェクト名 datetime rcTime1, // 描画座標1(時間) double rcPrice1, // 描画座標1(価格) datetime rcTime2, // 描画座標2(時間) double rcPrice2, // 描画座標2(価格) color rcColor, // 色 ENUM_LINE_STYLE rcStyle, // 線種 int rcWidth, // 太さ bool rcFill) // 塗りつぶし { if(rcStyle != STYLE_SOLID && rcWidth > 1) { Alert("[MakeRectangle Function] Width is over 1. Style must be solid."); return; } ObjectCreate( 0, objName, OBJ_RECTANGLE, 0, rcTime1, rcPrice1, rcTime2, rcPrice2); ObjectSetInteger(0, objName, OBJPROP_COLOR, rcColor); ObjectSetInteger(0, objName, OBJPROP_STYLE, rcStyle); ObjectSetInteger(0, objName, OBJPROP_WIDTH, rcWidth); ObjectSetInteger(0, objName, OBJPROP_BACK, false); ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, true); ObjectSetInteger(0, objName, OBJPROP_SELECTED, true); ObjectSetInteger(0, objName, OBJPROP_HIDDEN, false); ObjectSetInteger(0, objName, OBJPROP_ZORDER, 0); ObjectSetString( 0, objName, OBJPROP_TOOLTIP, "\n"); ObjectSetInteger(0, objName, OBJPROP_FILL, rcFill); } //-------------------------------------------------------------------- // MQL5の時間軸をint型の「分」に変換する関数 //-------------------------------------------------------------------- // [備考] MQL4と違い、PERIOD_H1以上は分数に対応していない int TimeFrameToMinutes(ENUM_TIMEFRAMES timeframe) { int minutes = 0; if(timeframe == PERIOD_CURRENT) timeframe = _Period; if(timeframe < PERIOD_H1) { minutes = (int)StringToInteger(StringSubstr(EnumToString(timeframe), 8)); // [備考] // MT5の仕様が変わらないなら、if(timeframe < PERIOD_H1) の場合は // minutes = timeframe; // でもOK。 } else if(timeframe < PERIOD_D1) { minutes = 60 * (int)StringToInteger(StringSubstr(EnumToString(timeframe), 8)); } else { switch(timeframe) { case PERIOD_D1: minutes = 1440; break; case PERIOD_W1: minutes = 10080; break; case PERIOD_MN1: minutes = 43200; // 43200分は 30日に相当(MQL4のMN1を踏襲) break; default: minutes = -1; // ERROR } } return minutes; } //-------------------------------------------------------------------- // ボタンを作成する関数 //-------------------------------------------------------------------- void CreateButton(string name, // オブジェクト名 string caption, // ボタン内に表示するテキスト int corner, // 基点(ボタンを配置する時に基準とする場所) int xshift, // 基点からの距離(横方向) int yshift, // 基点からの距離(縦方向) int xsize, // ボタンのサイズ(横) int ysize, // ボタンのサイズ(縦) int font_Size, // ボタン内に表示するテキストのサイズ color textColor, // ボタン内に表示するテキストの色 color bo_color, // ボタンの縁の色 color bg_Color) // ボタンの色 { ObjectCreate( 0, name, OBJ_BUTTON, 0, 0, 0); ObjectSetInteger(0, name, OBJPROP_CORNER, corner); ObjectSetInteger(0, name, OBJPROP_XDISTANCE, xshift); ObjectSetInteger(0, name, OBJPROP_YDISTANCE, yshift); ObjectSetInteger(0, name, OBJPROP_XSIZE, xsize); ObjectSetInteger(0, name, OBJPROP_YSIZE, ysize); ObjectSetInteger(0, name, OBJPROP_BORDER_COLOR, bo_color); ObjectSetInteger(0, name, OBJPROP_BGCOLOR, bg_Color); ObjectSetString (0, name, OBJPROP_TEXT, caption); ObjectSetString (0, name, OBJPROP_FONT, "Arial"); ObjectSetInteger(0, name, OBJPROP_FONTSIZE, font_Size); ObjectSetInteger(0, name, OBJPROP_COLOR, textColor); ObjectSetInteger(0, name, OBJPROP_STATE, false); ObjectSetInteger(0, name, OBJPROP_HIDDEN, true); ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, name, OBJPROP_SELECTED, false); ObjectSetInteger(0, name, OBJPROP_ZORDER, 0); } //-------------------------------------------------------------------- // チャートの時間軸に応じた色と太さをセットする関数 //-------------------------------------------------------------------- // [注意] この関数内ではグローバル変数を使用している void SetColorAndWidth(int timeframe) { if(timeframe <= 5) { // 5分足 colorOfRecs = color_1; widthOfLine = width_1; } else if(timeframe <= 15) { // 15分足 colorOfRecs = color_2; widthOfLine = width_2; } else if(timeframe <= 60) { // 1時間足 colorOfRecs = color_3; widthOfLine = width_3; } else if(timeframe <= 240) { // 4時間足 colorOfRecs = color_4; widthOfLine = width_4; } else if(timeframe <= 1440) { // 日足 colorOfRecs = color_5; widthOfLine = width_5; } else if(timeframe <= 10080) { // 週足 colorOfRecs = color_6; widthOfLine = width_6; } else { // 月足 colorOfRecs = color_7; widthOfLine = width_7; } } //******************************************************************************** // 以下の3つの関数はMetaEditorのヘルプからそのまま引用(記述箇所は以下のとおり) // 「標準的な定数、列挙と構造体」→「チャート定数」→「チャート操作の例」 //******************************************************************************** //-------------------------------------------------------------------- // CHART_EVENT_MOUSE_MOVEモードのON / OFF を切り替える関数 //-------------------------------------------------------------------- bool ChartEventMouseMoveSet(const bool value, const long chart_ID = 0) { ResetLastError(); if(!ChartSetInteger(chart_ID, CHART_EVENT_MOUSE_MOVE, 0, value)) { Print(__FUNCTION__ + ", Error Code = ", GetLastError()); return(false); } return(true); } //-------------------------------------------------------------------- // マウスの左ボタンによるチャートスクロールを有効化/無効化する関数 //-------------------------------------------------------------------- bool ChartMouseScrollSet(const bool value, const long chart_ID = 0) { ResetLastError(); if(!ChartSetInteger(chart_ID, CHART_MOUSE_SCROLL, 0, value)) { Print(__FUNCTION__ + ", Error Code = ", GetLastError()); return(false); } return(true); } //-------------------------------------------------------------------- // CHART_EVENT_MOUSE_MOVEモードがONかOFFか調べる関数 //-------------------------------------------------------------------- bool ChartEventMouseMoveGet(bool &result, const long chart_ID = 0) { long value; ResetLastError(); if(!ChartGetInteger(chart_ID, CHART_EVENT_MOUSE_MOVE, 0, value)) { Print(__FUNCTION__ + ", Error Code = ", GetLastError()); return(false); } result = value; return(true); }
MT5風味のコードで発生した問題
TradingView風味の操作を再現するよりも、MT5風味の操作を再現する方が少し時間がかかりました。というのも、インジをチャートに入れてから1回目の操作では正常に動くのですが、2回目の操作では正常に動作しなかったからです。
具体的に書きますと
(1) インジをチャートに挿入 (2) Rectボタン(四角形を作成するボタン)をクリック (3) チャート上の任意の地点をクリック (4) そのまま(マウスの左ボタンを押したまま)マウスを移動 (5) マウスの左ボタンを放す(四角形の描画が終了) 以上が1回目の操作です。 そして、2回目の操作に入ります。 (6) Rectボタン(四角形を作成するボタン)をクリック (7) チャート上の任意の地点をクリック
(7)まで来た段階で「あれ?」となりました。四角形が描画されないのです。
調査したところ、2回目のRectボタンクリック時にマウスMOVEモードがONになっていませんでした(CHART_EVENT_MOUSE_MOVEがtrueになっていませんでした)。
しかし、この調査をするために以下の関数をOnChartEvent関数の末尾で実行したところ、2回目以降の操作でもマウスMOVEモードがONになっていることに気づきました。
//-------------------------------------------------------------------- // CHART_EVENT_MOUSE_MOVEがtrueかfalseか調べる関数 //-------------------------------------------------------------------- // この関数はMetaEditorのヘルプに記述されていたものをそのまま引用 bool ChartEventMouseMoveGet(bool &result, const long chart_ID = 0) { long value; ResetLastError(); if(!ChartGetInteger(chart_ID, CHART_EVENT_MOUSE_MOVE, 0, value)) { Print(__FUNCTION__ + ", Error Code = ", GetLastError()); return(false); } result = value; return(true); }
試しにこの関数を除去して動作実験をしたら、やはり2回目以降の操作でマウスMOVEモードがONになりませんでした。どうやら、ChartGetInteger関数でCHART_EVENT_MOUSE_MOVEの状態を確認すると、期待している動作をするようです。
まだ原因は解明できていませんが、マウスMOVEモードがONになっていないのに処理が進んでも意味は無いので、stepを1にする前にこの関数を実行することにしました。それが以下の箇所です。
bool moveIsON = false; ChartEventMouseMoveGet(moveIsON); if(moveIsON) step = 1;