MQL5取引ツール(第3回):戦略的取引のための多時間軸スキャナーダッシュボードの構築
はじめに
前回の記事(第2回)では、MetaQuotes Language 5 (MQL5)で取引アシスタントツールを強化し、インタラクティブ性向上のための動的なビジュアルフィードバックを追加しました。今回は、戦略的な意思決定のためにリアルタイムの取引シグナルを提供する多時間軸スキャナーダッシュボードの構築に焦点を当てます。インジケーター駆動のシグナルとクローズボタンを備えたグリッドベースのインターフェースを紹介し、以下のサブトピックを通じてこれらの進展を解説します。
これらのセクションにより、直感的で強力な取引ダッシュボードの作成に向けたガイドとなります。
スキャナーダッシュボードの計画
戦略的な意思決定を支援するため、明確でリアルタイムの取引シグナルを提供する多時間軸スキャナーダッシュボードの作成を目指します。このダッシュボードは、複数の時間軸にわたる買いシグナルと売りシグナルをグリッドレイアウトで表示し、チャートを切り替えることなく市場状況を迅速に把握できるように設計されます。また、クローズボタンを設置し、パネルの簡単な閉鎖を可能にすることで、取引ニーズに応じて柔軟でクリーンなユーザー体験を提供します。
主要なインジケーターとして、相対力指数(RSI: Relative Strength Index)、ストキャスティクス(STOCH)、コモディティチャンネル指数(CCI)、平均方向性指数(ADX: Average Directional Index)、オーサムオシレーター(AO: Awesome Oscillator)を用いたシグナルを組み込みます。これらは、カスタマイズ可能な強度の閾値に基づき、潜在的な取引機会を特定することを目的としています。ただし、使用するインジケーターやプライスアクションデータの選択はユーザー次第です。この構成により、複数の時間軸にわたるトレンドや反転を把握し、短期戦略および長期戦略の両方をサポートします。私たちの目標は、ユーザーフレンドリーでありながら実用的な洞察を提供する、効率的で直感的なツールを作ることです。将来的には、自動アラートや追加インジケーターの導入など、さらなる拡張も見据えています。以下は、私たちが目指すイメージの可視化です。

MQL5での実装
MQL5でプログラムを作成するには、まずプログラムのメタデータを定義し、その後、ダッシュボードのオブジェクトを参照しやすく管理するためのオブジェクト名定数をいくつか定義する必要があります。
//+------------------------------------------------------------------+ //| TimeframeScanner Dashboard EA.mq5 | //| Copyright 2025, Allan Munene Mutiiria. | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, Allan Munene Mutiiria." #property link "https://t.me/Forex_Algo_Trader" #property version "1.00" // Define identifiers and properties for UI elements #define MAIN_PANEL "PANEL_MAIN" //--- Main panel rectangle identifier #define HEADER_PANEL "PANEL_HEADER" //--- Header panel rectangle identifier #define HEADER_PANEL_ICON "PANEL_HEADER_ICON" //--- Header icon label identifier #define HEADER_PANEL_TEXT "PANEL_HEADER_TEXT" //--- Header title label identifier #define CLOSE_BUTTON "BUTTON_CLOSE" //--- Close button identifier #define SYMBOL_RECTANGLE "SYMBOL_HEADER" //--- Symbol rectangle identifier #define SYMBOL_TEXT "SYMBOL_TEXT" //--- Symbol text label identifier #define TIMEFRAME_RECTANGLE "TIMEFRAME_" //--- Timeframe rectangle prefix #define TIMEFRAME_TEXT "TIMEFRAME_TEXT_" //--- Timeframe text label prefix #define HEADER_RECTANGLE "HEADER_" //--- Header rectangle prefix #define HEADER_TEXT "HEADER_TEXT_" //--- Header text label prefix #define RSI_RECTANGLE "RSI_" //--- RSI rectangle prefix #define RSI_TEXT "RSI_TEXT_" //--- RSI text label prefix #define STOCH_RECTANGLE "STOCH_" //--- Stochastic rectangle prefix #define STOCH_TEXT "STOCH_TEXT_" //--- Stochastic text label prefix #define CCI_RECTANGLE "CCI_" //--- CCI rectangle prefix #define CCI_TEXT "CCI_TEXT_" //--- CCI text label prefix #define ADX_RECTANGLE "ADX_" //--- ADX rectangle prefix #define ADX_TEXT "ADX_TEXT_" //--- ADX text label prefix #define AO_RECTANGLE "AO_" //--- AO rectangle prefix #define AO_TEXT "AO_TEXT_" //--- AO text label prefix #define BUY_RECTANGLE "BUY_" //--- Buy rectangle prefix #define BUY_TEXT "BUY_TEXT_" //--- Buy text label prefix #define SELL_RECTANGLE "SELL_" //--- Sell rectangle prefix #define SELL_TEXT "SELL_TEXT_" //--- Sell text label prefix #define WIDTH_TIMEFRAME 90 //--- Width of timeframe and symbol rectangles #define WIDTH_INDICATOR 70 //--- Width of indicator rectangles #define WIDTH_SIGNAL 90 //--- Width of BUY/SELL signal rectangles #define HEIGHT_RECTANGLE 25 //--- Height of all rectangles #define COLOR_WHITE clrWhite //--- White color for text and backgrounds #define COLOR_BLACK clrBlack //--- Black color for borders and text #define COLOR_LIGHT_GRAY C'230,230,230' //--- Light gray color for signal backgrounds #define COLOR_DARK_GRAY C'105,105,105' //--- Dark gray color for indicator backgrounds
まず、#defineディレクティブを使用して、多時間軸スキャナーダッシュボードのユーザーインターフェースのフレームワークを構築します。メインパネルとヘッダーパネルの矩形にはMAIN_PANELやHEADER_PANEL、ヘッダのアイコン、タイトル、クローズボタンにはHEADER_PANEL_ICON、HEADER_PANEL_TEXT、CLOSE_BUTTONといった定数を作成します。
次に、ダッシュボードのグリッド構造用の識別子を定義します。銘柄にはSYMBOL_RECTANGLEとSYMBOL_TEXT、時間軸の行にはTIMEFRAME_RECTANGLEとTIMEFRAME_TEXTというプレフィックスを使用します。列ヘッダーにはHEADER_RECTANGLEとHEADER_TEXT、インジケーターやシグナルセルにはRSI_RECTANGLE、STOCH_RECTANGLE、BUY_RECTANGLEと対応するRSI_TEXT、STOCH_TEXT、BUY_TEXTといったプレフィックスを使います。
サイズはWIDTH_TIMEFRAME(90ピクセル)、WIDTH_INDICATOR(70ピクセル)、WIDTH_SIGNAL(90ピクセル)、HEIGHT_RECTANGLE(25ピクセル)で設定します。色は、テキストや枠線にCOLOR_WHITEとCOLOR_BLACK、シグナル背景にCOLOR_LIGHT_GRAY (C'230,230,230')、インジケーター背景にCOLOR_DARK_GRAY (C'105,105,105')を使用し、均一で見やすいレイアウトを確保します。さらに、プログラム全体で使用するいくつかのグローバル変数を定義する必要があります。
bool panel_is_visible = true; //--- Flag to control panel visibility // Define the timeframes to be used ENUM_TIMEFRAMES timeframes_array[] = {PERIOD_M1, PERIOD_M5, PERIOD_M15, PERIOD_M20, PERIOD_M30, PERIOD_H1, PERIOD_H2, PERIOD_H3, PERIOD_H4, PERIOD_H8, PERIOD_H12, PERIOD_D1, PERIOD_W1}; //--- Array of timeframes for scanning // Global variables for indicator values double rsi_values[]; //--- Array to store RSI values double stochastic_values[]; //--- Array to store Stochastic signal line values double cci_values[]; //--- Array to store CCI values double adx_values[]; //--- Array to store ADX values double ao_values[]; //--- Array to store AO values
ここでは、ブール型変数panel_is_visibleを宣言し、trueに設定します。この変数はダッシュボードがチャート上に表示されるかどうかを決定します。このフラグにより、データ更新が不要な場合などにダッシュボードの表示を切り替えることができます。次に、ENUM_TIMEFRAMES型を使用して配列timeframes_arrayを定義し、PERIOD_M1(1分)からPERIOD_W1(週足)までの期間を列挙します。この配列はダッシュボードが分析する時間軸を指定し、複数の時間軸にわたる市場シグナルを構造的にスキャンできるようにします。不要な場合や変更したい場合は、列挙型を修正してください。
インジケーターのデータを格納するために、double型の配列rsi_values、stochastic_values、cci_values、adx_values、ao_valuesを作成します。これらの配列はそれぞれ相対力指数(RSI)、ストキャス(STOCH)、コモディティチャンネル指数(CCI)、平均方向性指数(ADX)、オーサムオシレーター(AO)の計算値を保持し、各時間軸に対する取引シグナルを効率的に処理・表示できるようにします。次に、取得したチャート銘柄の方向や切り捨てを判定するために使用するヘルパー関数を定義できます。
//+------------------------------------------------------------------+ //| Truncate timeframe enum to display string | //+------------------------------------------------------------------+ string truncate_timeframe_name(int timeframe_index) //--- Function to format timeframe name { string timeframe_string = StringSubstr(EnumToString(timeframes_array[timeframe_index]), 7); //--- Extract timeframe name return timeframe_string; //--- Return formatted name } //+------------------------------------------------------------------+ //| Calculate signal strength for buy/sell | //+------------------------------------------------------------------+ string calculate_signal_strength(double rsi, double stochastic, double cci, double adx, double ao, bool is_buy) //--- Function to compute signal strength { int signal_strength = 0; //--- Initialize signal strength counter if(is_buy && rsi < 40) signal_strength++; //--- Increment for buy if RSI is oversold else if(!is_buy && rsi > 60) signal_strength++; //--- Increment for sell if RSI is overbought if(is_buy && stochastic < 40) signal_strength++; //--- Increment for buy if Stochastic is oversold else if(!is_buy && stochastic > 60) signal_strength++; //--- Increment for sell if Stochastic is overbought if(is_buy && cci < -70) signal_strength++; //--- Increment for buy if CCI is oversold else if(!is_buy && cci > 70) signal_strength++; //--- Increment for sell if CCI is overbought if(adx > 40) signal_strength++; //--- Increment if ADX indicates strong trend if(is_buy && ao > 0) signal_strength++; //--- Increment for buy if AO is positive else if(!is_buy && ao < 0) signal_strength++; //--- Increment for sell if AO is negative if(signal_strength >= 3) return is_buy ? "Strong Buy" : "Strong Sell"; //--- Return strong signal if 3+ conditions met if(signal_strength >= 2) return is_buy ? "Buy" : "Sell"; //--- Return regular signal if 2 conditions met return "Neutral"; //--- Return neutral if insufficient conditions }
ここでは、truncate_timeframe_name関数を定義します。この関数は整数型パラメータtimeframe_indexを受け取り、表示用に時間軸の名前を整形します。内部では、EnumToString関数をtimeframes_array[timeframe_index]に適用した結果から、StringSubstr関数を使って位置7からの部分文字列を抽出し、timeframe_stringに格納します。そしてtimeframe_stringを返すことで、ユーザーに読みやすい時間軸名を提供します。
次に、calculate_signal_strength関数を作成し、インジケーター値に基づいて買いまたは売りのシグナルを判定します。整数型のsignal_strengthを0で初期化し、条件が一致した回数をカウントします。相対力指数(RSI)では、is_buyがtrueかつrsiが40未満(売られ過ぎ)である場合、またはis_buyがfalseかつrsiが60を超える(買われ過ぎ)場合にsignal_strengthを増加させます。同様にストキャスティクス(40未満または60超)、CCI(-70未満または70超)、AO(買いは正、売りは負)を確認し、条件を満たすたびにsignal_strengthを増加させます。
さらに平均方向性指数(ADX)も評価し、adxが40を超える場合は買い・売りいずれのシナリオでも強いトレンドと判断してsignal_strengthを増加させます。signal_strengthが3以上の場合、is_buyがtrueなら「Strong Buy」、falseなら「Strong Sell」を返します。2の場合はBuyまたはSell、1以下の場合はNeutralを返すことで、ダッシュボード用に明確なシグナル分類を可能にします。最後に、オブジェクトを作成するために使用する関数を定義できる状態になります。
//+------------------------------------------------------------------+ //| Create a rectangle for the UI | //+------------------------------------------------------------------+ bool create_rectangle(string object_name, int x_distance, int y_distance, int x_size, int y_size, color background_color, color border_color = COLOR_BLACK) //--- Function to create a rectangle { ResetLastError(); //--- Reset error code if(!ObjectCreate(0, object_name, OBJ_RECTANGLE_LABEL, 0, 0, 0)) { //--- Create rectangle object Print(__FUNCTION__, ": failed to create Rectangle: ERR Code: ", GetLastError()); //--- Log creation failure return(false); //--- Return failure } ObjectSetInteger(0, object_name, OBJPROP_XDISTANCE, x_distance); //--- Set x position ObjectSetInteger(0, object_name, OBJPROP_YDISTANCE, y_distance); //--- Set y position ObjectSetInteger(0, object_name, OBJPROP_XSIZE, x_size); //--- Set width ObjectSetInteger(0, object_name, OBJPROP_YSIZE, y_size); //--- Set height ObjectSetInteger(0, object_name, OBJPROP_CORNER, CORNER_RIGHT_UPPER); //--- Set corner to top-right ObjectSetInteger(0, object_name, OBJPROP_BGCOLOR, background_color); //--- Set background color ObjectSetInteger(0, object_name, OBJPROP_BORDER_COLOR, border_color); //--- Set border color ObjectSetInteger(0, object_name, OBJPROP_BORDER_TYPE, BORDER_FLAT); //--- Set flat border style ObjectSetInteger(0, object_name, OBJPROP_BACK, false); //--- Set to foreground ChartRedraw(0); //--- Redraw chart return(true); //--- Return success } //+------------------------------------------------------------------+ //| Create a text label for the UI | //+------------------------------------------------------------------+ bool create_label(string object_name, string text, int x_distance, int y_distance, int font_size = 12, color text_color = COLOR_BLACK, string font = "Arial Rounded MT Bold") //--- Function to create a label { ResetLastError(); //--- Reset error code if(!ObjectCreate(0, object_name, OBJ_LABEL, 0, 0, 0)) { //--- Create label object Print(__FUNCTION__, ": failed to create Label: ERR Code: ", GetLastError()); //--- Log creation failure return(false); //--- Return failure } ObjectSetInteger(0, object_name, OBJPROP_XDISTANCE, x_distance); //--- Set x position ObjectSetInteger(0, object_name, OBJPROP_YDISTANCE, y_distance); //--- Set y position ObjectSetInteger(0, object_name, OBJPROP_CORNER, CORNER_RIGHT_UPPER); //--- Set corner to top-right ObjectSetString(0, object_name, OBJPROP_TEXT, text); //--- Set label text ObjectSetString(0, object_name, OBJPROP_FONT, font); //--- Set font ObjectSetInteger(0, object_name, OBJPROP_FONTSIZE, font_size); //--- Set font size ObjectSetInteger(0, object_name, OBJPROP_COLOR, text_color); //--- Set text color ObjectSetInteger(0, object_name, OBJPROP_ANCHOR, ANCHOR_CENTER); //--- Center text ChartRedraw(0); //--- Redraw chart return(true); //--- Return success }
オブジェクトの作成を可能にするために、create_rectangle関数を定義します。この関数はobject_name、x_distance、y_distance、x_size、y_size、background_color、border_colorのパラメータを受け取ります。まずResetLastError関数を使用し、ObjectCreate関数でOBJ_RECTANGLE_LABELを作成します。作成に失敗した場合はPrint関数でエラーをログに出力し、falseを返します。
次にObjectSetInteger関数を使用して、位置、サイズ、CORNER_RIGHT_UPPER、background_color、border_color、BORDER_FLATなどの矩形プロパティを設定し、前景に表示されるようにします。ChartRedraw関数を呼び出し、trueを返します。テキスト用には、create_label関数を定義します。パラメータはobject_name、text、x_distance、y_distance、font_size、text_color、fontです。
ResetLastError関数を使用し、ObjectCreate関数でOBJ_LABELを作成します。作成に失敗した場合はエラーをログに出力します。ObjectSetInteger関数で位置、サイズ、色、ANCHOR_CENTERを設定し、ObjectSetString関数でtextとfontを設定します。ChartRedraw関数を呼び出し、trueを返します。これらの関数を用意することで、OnInitイベントハンドラ内で初期パネルオブジェクトを作成し、作業の出発点を整えることができます。
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() //--- Initialize EA { create_rectangle(MAIN_PANEL, 632, 40, 617, 374, C'30,30,30', BORDER_FLAT); //--- Create main panel background create_rectangle(HEADER_PANEL, 632, 40, 617, 27, C'60,60,60', BORDER_FLAT); //--- Create header panel background create_label(HEADER_PANEL_ICON, CharToString(91), 620, 54, 18, clrAqua, "Wingdings"); //--- Create header icon create_label(HEADER_PANEL_TEXT, "TimeframeScanner", 527, 52, 13, COLOR_WHITE); //--- Create header title create_label(CLOSE_BUTTON, CharToString('r'), 32, 54, 18, clrYellow, "Webdings"); //--- Create close button // Create header rectangle and label create_rectangle(SYMBOL_RECTANGLE, 630, 75, WIDTH_TIMEFRAME, HEIGHT_RECTANGLE, clrGray); //--- Create symbol rectangle create_label(SYMBOL_TEXT, _Symbol, 585, 85, 11, COLOR_WHITE); //--- Create symbol label // Create summary and indicator headers (rectangles and labels) string header_names[] = {"BUY", "SELL", "RSI", "STOCH", "CCI", "ADX", "AO"}; //--- Define header titles for(int header_index = 0; header_index < ArraySize(header_names); header_index++) { //--- Loop through headers int x_offset = (630 - WIDTH_TIMEFRAME) - (header_index < 2 ? header_index * WIDTH_SIGNAL : 2 * WIDTH_SIGNAL + (header_index - 2) * WIDTH_INDICATOR) + (1 + header_index); //--- Calculate x position int width = (header_index < 2 ? WIDTH_SIGNAL : WIDTH_INDICATOR); //--- Set width based on header type create_rectangle(HEADER_RECTANGLE + IntegerToString(header_index), x_offset, 75, width, HEIGHT_RECTANGLE, clrGray); //--- Create header rectangle create_label(HEADER_TEXT + IntegerToString(header_index), header_names[header_index], x_offset - width/2, 85, 11, COLOR_WHITE); //--- Create header label } // Create timeframe rectangles and labels, and summary/indicator cells for(int timeframe_index = 0; timeframe_index < ArraySize(timeframes_array); timeframe_index++) { //--- Loop through timeframes // Highlight current timeframe color timeframe_background = (timeframes_array[timeframe_index] == _Period) ? clrLimeGreen : clrGray; //--- Set background color for current timeframe color timeframe_text_color = (timeframes_array[timeframe_index] == _Period) ? COLOR_BLACK : COLOR_WHITE; //--- Set text color for current timeframe create_rectangle(TIMEFRAME_RECTANGLE + IntegerToString(timeframe_index), 630, (75 + HEIGHT_RECTANGLE) + timeframe_index * HEIGHT_RECTANGLE - (1 + timeframe_index), WIDTH_TIMEFRAME, HEIGHT_RECTANGLE, timeframe_background); //--- Create timeframe rectangle create_label(TIMEFRAME_TEXT + IntegerToString(timeframe_index), truncate_timeframe_name(timeframe_index), 585, (85 + HEIGHT_RECTANGLE) + timeframe_index * HEIGHT_RECTANGLE - (1 + timeframe_index), 11, timeframe_text_color); //--- Create timeframe label // Create summary and indicator cells for(int header_index = 0; header_index < ArraySize(header_names); header_index++) { //--- Loop through headers for cells string cell_rectangle_name, cell_text_name; //--- Declare cell name and label variables color cell_background = (header_index < 2) ? COLOR_LIGHT_GRAY : COLOR_BLACK; //--- Set cell background color switch(header_index) { //--- Select cell type case 0: cell_rectangle_name = BUY_RECTANGLE + IntegerToString(timeframe_index); cell_text_name = BUY_TEXT + IntegerToString(timeframe_index); break; //--- Buy cell case 1: cell_rectangle_name = SELL_RECTANGLE + IntegerToString(timeframe_index); cell_text_name = SELL_TEXT + IntegerToString(timeframe_index); break; //--- Sell cell case 2: cell_rectangle_name = RSI_RECTANGLE + IntegerToString(timeframe_index); cell_text_name = RSI_TEXT + IntegerToString(timeframe_index); break; //--- RSI cell case 3: cell_rectangle_name = STOCH_RECTANGLE + IntegerToString(timeframe_index); cell_text_name = STOCH_TEXT + IntegerToString(timeframe_index); break; //--- Stochastic cell case 4: cell_rectangle_name = CCI_RECTANGLE + IntegerToString(timeframe_index); cell_text_name = CCI_TEXT + IntegerToString(timeframe_index); break; //--- CCI cell case 5: cell_rectangle_name = ADX_RECTANGLE + IntegerToString(timeframe_index); cell_text_name = ADX_TEXT + IntegerToString(timeframe_index); break; //--- ADX cell case 6: cell_rectangle_name = AO_RECTANGLE + IntegerToString(timeframe_index); cell_text_name = AO_TEXT + IntegerToString(timeframe_index); break; //--- AO cell } int x_offset = (630 - WIDTH_TIMEFRAME) - (header_index < 2 ? header_index * WIDTH_SIGNAL : 2 * WIDTH_SIGNAL + (header_index - 2) * WIDTH_INDICATOR) + (1 + header_index); //--- Calculate x position int width = (header_index < 2 ? WIDTH_SIGNAL : WIDTH_INDICATOR); //--- Set width based on cell type create_rectangle(cell_rectangle_name, x_offset, (75 + HEIGHT_RECTANGLE) + timeframe_index * HEIGHT_RECTANGLE - (1 + timeframe_index), width, HEIGHT_RECTANGLE, cell_background); //--- Create cell rectangle create_label(cell_text_name, "-/-", x_offset - width/2, (85 + HEIGHT_RECTANGLE) + timeframe_index * HEIGHT_RECTANGLE - (1 + timeframe_index), 10, COLOR_WHITE); //--- Create cell label } } // Initialize indicator arrays ArraySetAsSeries(rsi_values, true); //--- Set RSI array as timeseries ArraySetAsSeries(stochastic_values, true); //--- Set Stochastic array as timeseries ArraySetAsSeries(cci_values, true); //--- Set CCI array as timeseries ArraySetAsSeries(adx_values, true); //--- Set ADX array as timeseries ArraySetAsSeries(ao_values, true); //--- Set AO array as timeseries return(INIT_SUCCEEDED); //--- Return initialization success }
OnInitイベントハンドラ内で、多時間軸スキャナーダッシュボードのユーザーインターフェースを初期化します。まずcreate_rectangle関数を使用して、MAIN_PANELを位置(632,40)、サイズ617x374ピクセル、色C'30,30,30'で描画し、同じ位置に高さ27ピクセル、色C'60,60,60'でHEADER_PANELを描画します。次にcreate_label関数を使用して、HEADER_PANEL_ICONをWingdings文字で位置(620,54)に追加します。MQL5のデフォルト文字を使用し、CharToString関数で文字コードを文字列に変換します。ここでは文字コード91を使用していますが、任意の文字を使用できます。

その後、HEADER_PANEL_TEXTを「TimeframeScanner」として位置(527,52)に作成し、CLOSE_BUTTONを位置(32,54)に作成します。この場合は別のフォントを使用し、文字「r」を文字列としてマッピングします。使用可能なフォントシンボルの可視化も参考にできます。

次に銘柄表示を設定します。create_rectangle関数でSYMBOL_RECTANGLEを位置(630,75)、サイズWIDTH_TIMEFRAME×HEIGHT_RECTANGLE、色グレーで作成し、create_label関数でSYMBOL_TEXTを位置(585,85)に現在の銘柄として配置します。ヘッダについては、header_names配列に「BUY」や「RSI」といったタイトルを定義し、ループでy=75にHEADER_RECTANGLEを作成、xオフセットはWIDTH_SIGNALやWIDTH_INDICATORに基づき、y=85にHEADER_TEXTラベルをcreate_label関数で配置します。
時間軸のグリッドはtimeframes_arrayをループして作成します。create_rectangle関数でTIMEFRAME_RECTANGLEをx=630、yオフセットは75+HEIGHT_RECTANGLE-(1+timeframe_index)として、色timeframe_backgroundで描画します。create_label関数でTIMEFRAME_TEXTをtruncate_timeframe_name関数から取得した名前で配置します。セルについては、BUY_RECTANGLE、RSI_RECTANGLEなどをループでcreate_rectangle関数で作成し、色cell_backgroundを設定、さらにcreate_label関数で「-/-」ラベルを追加します。インジケーター配列(rsi_valuesなど)はArraySetAsSeries関数を使用して時系列データとして設定します。INIT_SUCCEEDEDを返すことで初期化が成功したことを確認し、ダッシュボードのレイアウトとデータ構造を確立します。コンパイルすると、次の結果が得られます。

画像からわかるように、ダッシュボードは準備完了です。あとはインジケーター値を追加して分析に使用するだけです。分析用の関数はすでに用意されているため、データを取得するだけで済みます。そのために、すべての動的更新ロジックを処理する関数を作成します。
//+------------------------------------------------------------------+ //| Update indicator values | //+------------------------------------------------------------------+ void updateIndicators() //--- Update dashboard indicators { for(int timeframe_index = 0; timeframe_index < ArraySize(timeframes_array); timeframe_index++) { //--- Loop through timeframes // Initialize indicator handles int rsi_indicator_handle = iRSI(_Symbol, timeframes_array[timeframe_index], 14, PRICE_CLOSE); //--- Create RSI handle int stochastic_indicator_handle = iStochastic(_Symbol, timeframes_array[timeframe_index], 14, 3, 3, MODE_SMA, STO_LOWHIGH); //--- Create Stochastic handle int cci_indicator_handle = iCCI(_Symbol, timeframes_array[timeframe_index], 20, PRICE_TYPICAL); //--- Create CCI handle int adx_indicator_handle = iADX(_Symbol, timeframes_array[timeframe_index], 14); //--- Create ADX handle int ao_indicator_handle = iAO(_Symbol, timeframes_array[timeframe_index]); //--- Create AO handle // Check for valid handles if(rsi_indicator_handle == INVALID_HANDLE || stochastic_indicator_handle == INVALID_HANDLE || cci_indicator_handle == INVALID_HANDLE || adx_indicator_handle == INVALID_HANDLE || ao_indicator_handle == INVALID_HANDLE) { //--- Check if any handle is invalid Print("Failed to create indicator handle for timeframe ", truncate_timeframe_name(timeframe_index)); //--- Log failure continue; //--- Skip to next timeframe } // Copy indicator values if(CopyBuffer(rsi_indicator_handle, 0, 0, 1, rsi_values) <= 0 || //--- Copy RSI value CopyBuffer(stochastic_indicator_handle, 1, 0, 1, stochastic_values) <= 0 || //--- Copy Stochastic signal line value CopyBuffer(cci_indicator_handle, 0, 0, 1, cci_values) <= 0 || //--- Copy CCI value CopyBuffer(adx_indicator_handle, 0, 0, 1, adx_values) <= 0 || //--- Copy ADX value CopyBuffer(ao_indicator_handle, 0, 0, 1, ao_values) <= 0) { //--- Copy AO value Print("Failed to copy buffer for timeframe ", truncate_timeframe_name(timeframe_index)); //--- Log copy failure continue; //--- Skip to next timeframe } // Update RSI color rsi_text_color = (rsi_values[0] < 30) ? clrBlue : (rsi_values[0] > 70) ? clrRed : COLOR_WHITE; //--- Set RSI text color update_label(RSI_TEXT + IntegerToString(timeframe_index), DoubleToString(rsi_values[0], 2), rsi_text_color); //--- Update RSI label // Update Stochastic (Signal Line only) color stochastic_text_color = (stochastic_values[0] < 20) ? clrBlue : (stochastic_values[0] > 80) ? clrRed : COLOR_WHITE; //--- Set Stochastic text color update_label(STOCH_TEXT + IntegerToString(timeframe_index), DoubleToString(stochastic_values[0], 2), stochastic_text_color); //--- Update Stochastic label // Update CCI color cci_text_color = (cci_values[0] < -100) ? clrBlue : (cci_values[0] > 100) ? clrRed : COLOR_WHITE; //--- Set CCI text color update_label(CCI_TEXT + IntegerToString(timeframe_index), DoubleToString(cci_values[0], 2), cci_text_color); //--- Update CCI label // Update ADX color adx_text_color = (adx_values[0] > 25) ? clrBlue : COLOR_WHITE; //--- Set ADX text color update_label(ADX_TEXT + IntegerToString(timeframe_index), DoubleToString(adx_values[0], 2), adx_text_color); //--- Update ADX label // Update AO color ao_text_color = (ao_values[0] > 0) ? clrGreen : (ao_values[0] < 0) ? clrRed : COLOR_WHITE; //--- Set AO text color update_label(AO_TEXT + IntegerToString(timeframe_index), DoubleToString(ao_values[0], 2), ao_text_color); //--- Update AO label // Update Buy/Sell signals string buy_signal = calculate_signal_strength(rsi_values[0], stochastic_values[0], cci_values[0], adx_values[0], ao_values[0], true); //--- Calculate buy signal string sell_signal = calculate_signal_strength(rsi_values[0], stochastic_values[0], cci_values[0], adx_values[0], ao_values[0], false); //--- Calculate sell signal color buy_text_color = (buy_signal == "Strong Buy") ? COLOR_WHITE : COLOR_WHITE; //--- Set buy text color color buy_background = (buy_signal == "Strong Buy") ? clrGreen : (buy_signal == "Buy") ? clrSeaGreen : COLOR_DARK_GRAY; //--- Set buy background color update_rectangle(BUY_RECTANGLE + IntegerToString(timeframe_index), buy_background); //--- Update buy rectangle update_label(BUY_TEXT + IntegerToString(timeframe_index), buy_signal, buy_text_color); //--- Update buy label color sell_text_color = (sell_signal == "Strong Sell") ? COLOR_WHITE : COLOR_WHITE; //--- Set sell text color color sell_background = (sell_signal == "Strong Sell") ? clrRed : (sell_signal == "Sell") ? clrSalmon : COLOR_DARK_GRAY; //--- Set sell background color update_rectangle(SELL_RECTANGLE + IntegerToString(timeframe_index), sell_background); //--- Update sell rectangle update_label(SELL_TEXT + IntegerToString(timeframe_index), sell_signal, sell_text_color); //--- Update sell label // Release indicator handles IndicatorRelease(rsi_indicator_handle); //--- Release RSI handle IndicatorRelease(stochastic_indicator_handle); //--- Release Stochastic handle IndicatorRelease(cci_indicator_handle); //--- Release CCI handle IndicatorRelease(adx_indicator_handle); //--- Release ADX handle IndicatorRelease(ao_indicator_handle); //--- Release AO handle } }
ダッシュボードの値更新を容易に管理するために、updateIndicators関数を実装し、インジケーター値とシグナルを更新します。timeframes_arrayをtimeframe_indexでループし、各時間軸を処理します。iRSI、iStochastic、iCCI、iADX、iAO関数を使用して、現在の銘柄と時間軸に対応するインジケーターハンドル(例:rsi_indicator_handle)を作成し、14期間のRSIや20期間のCCIなどのパラメータを設定します。インジケーターの設定値はすべてカスタマイズ可能であり、デフォルト値に制限される必要はありません。
次に、rsi_indicator_handleのような任意のハンドルがINVALID_HANDLEに等しい場合、作成に失敗したことを意味します。この場合はPrint関数を使用して、truncate_timeframe_name関数の出力とともにエラーをログに出力し、次の時間軸に進みます。CopyBuffer関数を使用して最新値をrsi_valuesなどの配列に取得し、失敗した場合はエラーをログに残して処理を継続します。インジケーターの表示はupdate_label関数を使って更新します。たとえば、rsi_text_colorはrsi_values[0]に基づいて設定し、30未満なら青、70を超えたら赤、それ以外はCOLOR_WHITEとし、DoubleToString関数で整形した値をRSI_TEXTに反映します。同様の処理をstochastic_values、cci_values、adx_values、ao_valuesにも適用し、色のロジック(例:ao_valuesが正なら緑)を組み込みます。
次に、calculate_signal_strength関数を使用し、rsi_values[0]などを渡してbuy_signalとsell_signalを算出します。buy_backgroundは「Strong Buy」なら緑などの色を設定し、update_rectangle関数でBUY_RECTANGLEを更新、update_label関数でBUY_TEXTを更新します。sell_backgroundとSELL_TEXTについても同様に処理します。最後に、IndicatorRelease関数を使用してrsi_indicator_handleなどのハンドルを解放し、効率的なリソース管理をおこないます。使用したヘルパー関数は以下のように定義されています。
//+------------------------------------------------------------------+ //| Update rectangle background color | //+------------------------------------------------------------------+ bool update_rectangle(string object_name, color background_color)//--- Function to update rectangle color { int found = ObjectFind(0, object_name); //--- Find rectangle object if(found < 0) { //--- Check if object not found ResetLastError(); //--- Reset error code Print("UNABLE TO FIND THE RECTANGLE: ", object_name, ". ERR Code: ", GetLastError()); //--- Log error return(false); //--- Return failure } ObjectSetInteger(0, object_name, OBJPROP_BGCOLOR, background_color); //--- Set background color ChartRedraw(0); //--- Redraw chart return(true); //--- Return success } //+------------------------------------------------------------------+ //| Update label text and color | //+------------------------------------------------------------------+ bool update_label(string object_name, string text, color text_color) //--- Function to update label { int found = ObjectFind(0, object_name); //--- Find label object if(found < 0) { //--- Check if object not found ResetLastError(); //--- Reset error code Print("UNABLE TO FIND THE LABEL: ", object_name, ". ERR Code: ", GetLastError()); //--- Log error return(false); //--- Return failure } ObjectSetString(0, object_name, OBJPROP_TEXT, text); //--- Set label text ObjectSetInteger(0, object_name, OBJPROP_COLOR, text_color); //--- Set text color ChartRedraw(0); //--- Redraw chart return(true); //--- Return success }
update_rectangle関数を定義します。この関数はobject_nameとbackground_colorをパラメータとして受け取り、矩形の外観を変更します。ObjectFind関数を使用して矩形を検索し、結果をfoundに格納します。foundが0未満の場合、そのオブジェクトが存在しないことを意味するため、ResetLastError関数を使用し、Print関数とGetLastErrorでエラーをログに出力し、falseを返します。矩形の背景はObjectSetInteger関数を使用してOBJPROP_BGCOLORをbackground_colorに設定して更新します。ChartRedraw関数でチャートを再描画し、成功時にはtrueを返します。テキスト更新用にはupdate_label関数を定義します。この関数はobject_name、text、text_colorをパラメータとして受け取ります。
ObjectFind関数でラベルの存在を確認し、foundが負の場合はResetLastError関数を使用し、Print関数でエラーをログに出力してfalseを返します。ObjectSetString関数でOBJPROP_TEXTをtextに設定し、ObjectSetInteger関数でOBJPROP_COLORをtext_colorに設定します。ChartRedraw関数を使用してチャートを更新し、trueを返すことで、ラベルの動的更新を可能にします。これでティックごとにupdate関数を呼び出し、ダッシュボードを更新できるようになります。
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() //--- Handle tick events { if (panel_is_visible) { //--- Check if panel is visible updateIndicators(); //--- Update indicators } }
ここでは、OnTickイベントハンドラ内で、パネルが表示されている場合にupdateIndicators関数を呼び出して更新を適用します。最後に、不要になった際には作成したオブジェクトを削除し、それらをチャートから取り除く必要があります。
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) //--- Deinitialize EA { ObjectDelete(0, MAIN_PANEL); //--- Delete main panel ObjectDelete(0, HEADER_PANEL); //--- Delete header panel ObjectDelete(0, HEADER_PANEL_ICON); //--- Delete header icon ObjectDelete(0, HEADER_PANEL_TEXT); //--- Delete header title ObjectDelete(0, CLOSE_BUTTON); //--- Delete close button ObjectsDeleteAll(0, SYMBOL_RECTANGLE); //--- Delete all symbol rectangles ObjectsDeleteAll(0, SYMBOL_TEXT); //--- Delete all symbol labels ObjectsDeleteAll(0, TIMEFRAME_RECTANGLE); //--- Delete all timeframe rectangles ObjectsDeleteAll(0, TIMEFRAME_TEXT); //--- Delete all timeframe labels ObjectsDeleteAll(0, HEADER_RECTANGLE); //--- Delete all header rectangles ObjectsDeleteAll(0, HEADER_TEXT); //--- Delete all header labels ObjectsDeleteAll(0, RSI_RECTANGLE); //--- Delete all RSI rectangles ObjectsDeleteAll(0, RSI_TEXT); //--- Delete all RSI labels ObjectsDeleteAll(0, STOCH_RECTANGLE); //--- Delete all Stochastic rectangles ObjectsDeleteAll(0, STOCH_TEXT); //--- Delete all Stochastic labels ObjectsDeleteAll(0, CCI_RECTANGLE); //--- Delete all CCI rectangles ObjectsDeleteAll(0, CCI_TEXT); //--- Delete all CCI labels ObjectsDeleteAll(0, ADX_RECTANGLE); //--- Delete all ADX rectangles ObjectsDeleteAll(0, ADX_TEXT); //--- Delete all ADX labels ObjectsDeleteAll(0, AO_RECTANGLE); //--- Delete all AO rectangles ObjectsDeleteAll(0, AO_TEXT); //--- Delete all AO labels ObjectsDeleteAll(0, BUY_RECTANGLE); //--- Delete all buy rectangles ObjectsDeleteAll(0, BUY_TEXT); //--- Delete all buy labels ObjectsDeleteAll(0, SELL_RECTANGLE); //--- Delete all sell rectangles ObjectsDeleteAll(0, SELL_TEXT); //--- Delete all sell labels ChartRedraw(0); //--- Redraw chart }
最後に、OnDeinit関数を使用してクリーンアップ処理を実装します。この関数はエキスパートアドバイザー(EA)が削除されたときに実行されます。まずObjectDelete関数を使用して、MAIN_PANELの矩形をはじめ、HEADER_PANEL、HEADER_PANEL_ICON、HEADER_PANEL_TEXT、CLOSE_BUTTONを順に削除し、メインパネルとヘッダのコンポーネントをチャートから確実に消去します。
次にObjectsDeleteAll関数を使用して、すべてのダッシュボードオブジェクトを体系的に削除します。SYMBOL_RECTANGLEとSYMBOL_TEXT、TIMEFRAME_RECTANGLEとTIMEFRAME_TEXT、HEADER_RECTANGLEとHEADER_TEXTに関連する矩形やラベルを削除し、銘柄、時間軸、ヘッダ表示をクリアします。さらに、RSI_RECTANGLE、STOCH_RECTANGLE、CCI_RECTANGLE、ADX_RECTANGLE、AO_RECTANGLEなどのインジケーター関連オブジェクトと、それぞれのRSI_TEXTなどのテキストラベルも削除します。
最後にObjectsDeleteAll関数を使用して、BUY_RECTANGLEとSELL_RECTANGLEオブジェクト、さらにBUY_TEXTとSELL_TEXTラベルを削除し、シグナル関連の要素をすべて取り除きます。ChartRedraw関数を使用してチャートを再描画し、初期化解除後にクリーンな表示状態を確保します。最後に、キャンセルボタンを処理する必要があります。クリックされた際にダッシュボードを閉じ、以後の更新を無効にするようにします。
//+------------------------------------------------------------------+ //| Expert chart event handler | //+------------------------------------------------------------------+ void OnChartEvent(const int event_id, //--- Event ID const long& long_param, //--- Long parameter const double& double_param, //--- Double parameter const string& string_param) //--- String parameter { if (event_id == CHARTEVENT_OBJECT_CLICK) { //--- Check for object click event if (string_param == CLOSE_BUTTON) { //--- Check if close button clicked Print("Closing the panel now"); //--- Log panel closure PlaySound("alert.wav"); //--- Play alert sound panel_is_visible = false; //--- Hide panel ObjectDelete(0, MAIN_PANEL); //--- Delete main panel ObjectDelete(0, HEADER_PANEL); //--- Delete header panel ObjectDelete(0, HEADER_PANEL_ICON); //--- Delete header icon ObjectDelete(0, HEADER_PANEL_TEXT); //--- Delete header title ObjectDelete(0, CLOSE_BUTTON); //--- Delete close button ObjectsDeleteAll(0, SYMBOL_RECTANGLE); //--- Delete all symbol rectangles ObjectsDeleteAll(0, SYMBOL_TEXT); //--- Delete all symbol labels ObjectsDeleteAll(0, TIMEFRAME_RECTANGLE); //--- Delete all timeframe rectangles ObjectsDeleteAll(0, TIMEFRAME_TEXT); //--- Delete all timeframe labels ObjectsDeleteAll(0, HEADER_RECTANGLE); //--- Delete all header rectangles ObjectsDeleteAll(0, HEADER_TEXT); //--- Delete all header labels ObjectsDeleteAll(0, RSI_RECTANGLE); //--- Delete all RSI rectangles ObjectsDeleteAll(0, RSI_TEXT); //--- Delete all RSI labels ObjectsDeleteAll(0, STOCH_RECTANGLE); //--- Delete all Stochastic rectangles ObjectsDeleteAll(0, STOCH_TEXT); //--- Delete all Stochastic labels ObjectsDeleteAll(0, CCI_RECTANGLE); //--- Delete all CCI rectangles ObjectsDeleteAll(0, CCI_TEXT); //--- Delete all CCI labels ObjectsDeleteAll(0, ADX_RECTANGLE); //--- Delete all ADX rectangles ObjectsDeleteAll(0, ADX_TEXT); //--- Delete all ADX labels ObjectsDeleteAll(0, AO_RECTANGLE); //--- Delete all AO rectangles ObjectsDeleteAll(0, AO_TEXT); //--- Delete all AO labels ObjectsDeleteAll(0, BUY_RECTANGLE); //--- Delete all buy rectangles ObjectsDeleteAll(0, BUY_TEXT); //--- Delete all buy labels ObjectsDeleteAll(0, SELL_RECTANGLE); //--- Delete all sell rectangles ObjectsDeleteAll(0, SELL_TEXT); //--- Delete all sell labels ChartRedraw(0); //--- Redraw chart } } }
OnChartEventイベントハンドラ内では、イベントIDがCHARTEVENT_OBJECT_CLICKで、クリックされたオブジェクトがキャンセルボタンである場合に処理を行います。まずPlaySound関数を使用してアラート音を鳴らし、パネルが無効化されることをユーザーに通知します。その後、パネルの表示を無効にし、OnDeinitで使用したのと同じロジックを用いてチャートをクリアし、ダッシュボードを削除します。コンパイルすると、次の結果が得られます。

画像から、インジケーターのデータと売買方向が反映されたダッシュボードが更新されていることが確認できます。残されているのはプロジェクトの実用性をテストすることであり、それは次のセクションで扱います。
バックテスト
テストをおこない、その結果を1枚のGraphics Interchange Format (GIF)ビットマップ画像形式でまとめた可視化を以下に示します。

結論
結論として、MQL5を用いて多時間軸スキャナーダッシュボードを開発しました。構造化されたグリッドレイアウト、リアルタイムのインジケーターシグナル、インタラクティブなクローズボタンを統合することで、戦略的な取引判断の精度を向上させています。これらの機能の設計および実装を示し、堅牢な初期化処理と動的な更新機能を通じて、その有効性を確認しました。このダッシュボードは個々のニーズに応じてカスタマイズ可能であり、複数の時間軸にわたる市場シグナルの監視および迅速な対応能力を大幅に向上させます。
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/18319
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。
MQL5入門(第17回):トレンド反転のためのエキスパートアドバイザーの構築
プライスアクション分析ツールキットの開発(第25回):Dual EMA Fractal Breaker
プライスアクション分析ツールキットの開発(第26回):Pin Bar, Engulfing Patterns and RSI Divergence (Multi-Pattern) Tool
ログレコードをマスターする(第7回):チャートにログを表示する方法
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索