MQL5で動的な多銘柄多期間の相対力指標(RSI)指標ダッシュボードを作成する
はじめに
この記事では、MetaTrader 5(MT5)用のMetaQuotes Language 5(MQL5)で、動的な複数銘柄、複数期間のRSI (Relative Strength Index)指標ダッシュボードを作成する手順を説明します。この包括的なガイドでは、カスタムRSIダッシュボードの定義、機能、実用的な活用方法、およびMetaQuotes Language 5(MQL5)を使用した開発に必要なステップを詳しく紹介します。
この動的なダッシュボードは、トレーダーにとって強力なツールとなり、複数の銘柄と時間枠にわたるRSI値を統合して提供します。これにより、市場の過熱や売られ過ぎの状況を迅速に把握し、より的確な判断が可能になります。RSIデータを一つのインターフェイスで視覚的に確認できるため、トレーダーは市場の状態を即座に評価し、それに基づいて戦略を調整することができます。
以下の主要分野をカバーします。
- ダッシュボードの初期化:環境の設定、メインボタンの作成、時間枠と銘柄の表示
- リアルタイム更新:ライブの市場データに基づいて更新されるRSI値を動的に計算して表示する機能を実装
- ボタンの作成と更新:ボタンの作成と更新に使用される機能を詳細に説明し、ダッシュボードがユーザーフレンドリーで有益であることを保証
- カスタマイズと実用化:個々の取引ニーズに合わせてダッシュボードをカスタマイズし、取引戦略に組み込む方法
この記事が終わる頃には、MQL5で複数銘柄、複数期間のRSIダッシュボードを構築し、活用する方法を完全に理解できるでしょう。これにより、取引ツールキットを強化し、市場の動向分析能力を向上させることで、より情報に基づいた取引判断を下せるようになります。以下のトピックに基づき進めます。
- 概要と要素の説明
- MQL5での実装
- 結論
ここでは、MetaEditorのベース統合開発環境 (IDE) コーディング環境としてMetaQuotes Language 5 (MQL5)を広範囲に使用し、MetaTrader 5 (MT5) 取引端末でファイルを実行します。従って、上記のバージョンを持っていることが最も重要になります。 それでは、この強力な取引ツールの開発に挑みましょう。
概要と要素の説明
複数の取引銘柄と時間枠にわたるRSI値を統合する包括的なダッシュボードを作成し、トレーダーに市場分析のための強力なツールを提供します。徹底的な理解を保証するために、すべての詳細を概説し、カバーします。開発では、以下の重要な要素に重点を置きます。
- ダッシュボードの初期化
メインボタンの作成:初期化プロセスの最初のステップは、ダッシュボードのベースとベンチマークを示す中央ボタンを作成することです。このボタンはダッシュボードの主要なコントロールとして機能し、ユーザーインターフェースの中心的な要素となります。ダッシュボードの上部に配置され、視認性が高く、簡単にアクセスできるように戦略的に設置されています。
時間枠ボタン:次に、様々な銘柄期間から他の指標データをフィーチャーするために、異なる時間枠を表すボタンを作成します。これらのボタンはメインボタンの横に水平に配置され、各期間のRSI値を表示するように設計されています。各時間枠のボタンには適切な期間の略号が表示され、トレーダーは一目で異なる時間枠のデータを素早く識別し、比較することができます。
- 銘柄ボタン
動的銘柄リスト:市場の包括的なビューを提供するために、ユーザーのMetaTrader 5プラットフォームで利用可能な各取引銘柄のボタンを生成し、気配値表示に追加します。これらのボタンは動的に作成され、メインボタンの下に縦に並べられます。現在アクティブな取引銘柄は、わかりやすい色(ライムや緑など)で強調表示されます。この機能により、トレーダーはアクティブな銘柄を素早く特定し、そのRSI値をリアルタイムで監視することができます。
ベースボタン:銘柄リストの一番下に、全ての時間枠ボタンを合わせた幅のベースボタンを追加します。このボタンにはダッシュボードの名前(もっと正確にはタイトル)が与えられますが、現在の銘柄のシグナルの状態を表示したり、銘柄リストの概要やフッターとして使用することができます。銘柄ボタンとRSI値表示との間に明確な区分けができ、ダッシュボードがよく整理され、ナビゲートしやすくなります。
- リアルタイム更新:
RSIの計算:ダッシュボードが正確で最新の情報を提供できるように、MQL5に組み込まれた関数を使用して、各銘柄と時間枠のRSI値をリアルタイムで計算します。この関数は、選択した期間の終値に基づいてRSIを計算し、市場の勢いを示す重要な指標を提供します。RSI値は配列に格納され、最新の市場データを反映するためにティックごとに更新されます。
動的表示:算出されたRSI値は、対応するボタンに動的に表示されます。視覚的にわかりやすくするため、事前に定義したRSIの閾値に基づく色分けを実施します。RSIの値が売られすぎを示す30を下回ると、ボタンの背景色が緑に変わります。RSIの値が買われすぎを示す70を超えると、背景色が赤に変わります。RSI値が30~70の場合、背景色は白のままで、中立の状態を示します。この動的な表示により、トレーダーは市場の状況を素早く把握し、十分な情報に基づいた取引判断を下すことができます。
プロセス全体を説明するために、最終的に私たちが手に入れようとしているものを以下に示します。
開発プロセス全体を説明するために、各要素を詳細なステップに分け、コードスニペットと説明を提供します。この記事が終わるころには、カスタマイズして自分の取引戦略に統合できる、完全に機能的なRSIダッシュボードを手にしていることでしょう。
MQL5での実装
指標ダッシュボードは、エキスパートアドバイザー(EA)に基づきます。EAを作成するには、MetaTrader 5端末で[ツール]タブをクリックし、MetaQuotes言語エディタをチェックするか、キーボードのF4を押します。または、ツールバーのIDE (Integrated Development Environment)アイコンをクリックします。これにより、自動売買ロボット、テクニカル指標、スクリプト、関数のライブラリを作成できるMetaQuotes言語エディタ環境が開きます。
MetaEditorを開いたら、ツールバーの[ファイル]タブで[新しいファイル]を選択するか、CTRL + Nキーを押して新規ドキュメントを開きます。または、[ツール]タブの新規アイコンをクリックすることもできます。MQLウィザードのポップアップが表示されます。
ウィザードが表示されたら、[EA(テンプレート)]を選択し、[次へ]をクリックします。
EAの一般プロパティで、[名前]フィールドにEAのファイル名を入力します。フォルダが存在しない場合にフォルダを指定または作成するには、EA名の前にバックスラッシュを使用することに注意してください。例えば、ここではデフォルトで「Experts\」となっています。つまり、私たちのEAはExpertsフォルダに作成され、そこで見つけることができます。他のフィールドはごく簡単ですが、ウィザードの一番下にあるリンクから、正確な手順を知ることができます。
希望する EAファイル名を入力した後、[次へ]をクリックし、[完了]をクリックします。この作業をすべて終えたら、指標ダッシュボードをコーディングし、プログラムする準備が整いました。
まず、これから作るボタンのための関数を作る必要があります。これは、類似のオブジェクトを作成するときにプロセス全体を繰り返す必要がなくなり、類似の関数を作成するときに同じ関数を再利用できるようになるため、非常に便利です。また、プロセスが迅速でわかりやすくなり、コードスニペットが短くなるため、多くの時間とスペースを節約できます。
ボタンを作成するために、11個の引数またはパラメータを取る関数を作成します。
//+------------------------------------------------------------------+ //| Function to create a button | //+------------------------------------------------------------------+ bool createButton(string objName, string text, int xD, int yD, int xS, int yS, color clrTxt, color clrBg, int fontSize = 12, color clrBorder = clrNONE, string font = "Arial Rounded MT Bold" ) { ... }
関数のシグネチャがすべてを物語っています。これは「createButton」という名前のブーリアン関数で、成功した場合と失敗した場合に、それぞれtrueかfalseの2つのブーリアンフラグを返すという意味です。そのパラメータを簡単に理解するために、以下にその概要を個別に説明しましょう。
- objName (string):このパラメータはボタンオブジェクトの名前を指定します。各ボタンには、チャート上の他のオブジェクトと区別するために一意な名前をつけなければなりません。この名前は、更新や修正のためにボタンを参照するために使用されます。
- text (string):ボタンに表示されるテキストを定義します。「RSI」、「BUY」などの文字列や、ボタンの目的に関連したラベルであれば何でもよいです。
- xD (int):このパラメータは、チャートの指定されたコーナーからのボタンの水平距離を指定します。単位はピクセルです。
- yD (int):xDと同様に、このパラメータはチャートの指定されたコーナーからのボタンの垂直距離を定義します。
- xS (int):ボタンの幅をピクセル単位で指定します。チャート上に表示されるボタンの幅を決定します。
- yS (int):ボタンの高さをピクセル単位で定義します。チャート上に表示されるボタンの高さを決定します。
- clrTxt (color):このパラメータは、ボタンに表示されるテキストの色を設定します。色は、clrBlack、clrWhite、clrRedなど、MQL5で定義済みの色定数を使って指定できます。
- clrBg (color):ボタンの背景色を指定します。また、定義済みの色定数を使って指定し、ボタンの塗りつぶし色を決定します。
- fontSize (int):このオプションのパラメータは、ボタンのテキストに使用するフォントのサイズを指定します。デフォルトは12です。フォントサイズは、ボタンに表示されるテキストのサイズを決定します。
- clrBorder (color):このオプションのパラメータは、ボタンの境界線の色を設定します。指定がない場合のデフォルトはclrNONEで、これは境界色が適用されないことを意味します。境界色を指定すると、ボタンの視認性と美観を向上させることができます。
- font (string):さらに、このオプションのパラメータは、ボタンのテキストに使用されるフォントの種類を定義します。デフォルトはArial Rounded MT Boldです。フォントはボタンに表示されるテキストのスタイルを決定します。
関数のシグネチャで、いくつかの引数がすでに何らかの値で初期化されていることに気づいたはずです。初期化値は、そのパラメータが関数呼び出し中に無視された場合に割り当てられるデフォルト値を表します。例えば、デフォルトの境界線カラーはnoneです。つまり、関数呼び出し時にカラー値が指定されなければ、矩形ラベルの境界線にはカラーが適用されません。
中括弧({})で囲まれた関数本体の中で、オブジェクト生成プロシージャを定義します。
// Attempt to create the button if (!ObjectCreate(0, objName, OBJ_BUTTON, 0, 0, 0)) { Print(__FUNCTION__, ": failed to create Btn: ERR Code: ", GetLastError()); // Print error message if button creation fails return (false); // Return false if creation fails }
まず、if文を使用してオブジェクトが作成されていないかどうかをチェックします。ObjectCreate関数は、6つの引数を取るブール値を使用します。この関数は、指定された名前、型、初期座標を持つオブジェクトを、指定されたチャートサブウィンドウに作成します。まず、チャートウィンドウを指定します。0は、オブジェクトがメインウィンドウ上に作成されることを意味します。次に、オブジェクト名を指定します。これは、特定のオブジェクトに一意に割り当てられる名前です。作成するオブジェクトのタイプはOBJ_BUTTONで、カスタム指標ダッシュボードを作成設計するためのオブジェクトを意味します。次に、現在のサブウィンドウを0として、サブウィンドウを指定します。最後に、時間と価格の値は、チャートに付けるのではなく、チャートウィンドウの座標に付けるので、ゼロとして提供します。ピクセルはマッピングの設定に使用されます。
オブジェクトの作成に失敗し、最終的にObjectCreate 関数がfalseを返した場合、明らかに先に進む意味がないため、エラーで返します。この場合、エラーコードの横にエラーを操作ログに出力し、falseを返すことでエラーを通知します。そのため、最新のエラーを取得するには、前のエラーをクリアする必要があります。これは、オブジェクト作成ロジックの直前に、MQL5内蔵関数であるResetLastError関数を呼び出すことで実現します。
ResetLastError(); // Reset the last error code
この関数の目的は、最後にエラーが発生した操作のエラーコードを取得する関数GetLastErrorの値をゼロに設定することです。これを呼び出すことで、次の操作に進む前に、以前のエラーコードがクリアされていることを確認します。このステップは、以前のエラー状態に干渉されることなく、新しいエラーを独立して処理することを可能にするため、不可欠です。
この時点まで戻らなければ、オブジェクトを作成したことになるので、オブジェクトのプロパティ更新を続けることができます。内蔵関数ObjectSet...のセットは、対応するオブジェクトプロパティの値を設定します。オブジェクトプロパティは、datetime型、integer型、color型、boolean型、character型のいずれかでなければなりません。
// Set button properties ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // Set X distance ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Set Y distance ObjectSetInteger(0, objName, OBJPROP_XSIZE, xS); // Set X size ObjectSetInteger(0, objName, OBJPROP_YSIZE, yS); // Set Y size ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_RIGHT_UPPER); // Set corner position ObjectSetString(0, objName, OBJPROP_TEXT, text); // Set button text ObjectSetString(0, objName, OBJPROP_FONT, font); // Set font type ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); // Set font size ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Set text color ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBg); // Set background color ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder); // Set border color ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Set background property ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Set button state ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Set if the button is selectable ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Set if the button is selected
最初の特性ロジックに集中しましょう。
ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // Set X distance
ここでは、組み込みのObjectSetInteger関数を使用し、それぞれパラメータを渡す。パラメータは以下の通りです。
- Chart id:チャートの識別子です。0は現在のチャート(チャートID)を指します。このチャート内のオブジェクトのプロパティを調整しています。
- Name:objNameは、矩形ラベルオブジェクトに割り当てられた固有の名前を表します。
- Property id:オブジェクトプロパティのIDであり、その値はENUM_OBJECT_PROPERTY_INTEGER列挙の値のいずれかです。OBJPROP_XDISTANCEは、X距離プロパティを変更することを指定します。
- Property value:プロパティの値です。xDに代入される値は、矩形ラベルの左上隅がチャートの左端から水平方向にどのくらい右(負の場合は左)に位置するかを決定します。
同様に、他のプロパティも同じ形式で設定します。OBJPROP_YDISTANCEは矩形ラベルのY距離プロパティを設定します。yD値は、矩形ラベルの左上隅がチャートの上端から垂直方向にどのくらい離れているかを決定します。言い換えれば、チャート領域内でのラベルの垂直配置を制御します。これは隅からのY距離を設定します。OBJPROP_XSIZEとOBJPROP_YSIZEはそれぞれ矩形の幅と高さを設定します。
オブジェクトを配置するには、OBJPROP_CORNERプロパティを使い、チャートウインドウのどの隅にオブジェクトを配置するかを決めます。
ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_RIGHT_UPPER); // Set corner position
プロパティは4種類しかありません。
- CORNER_LEFT_UPPER:座標の中心はチャートの左上隅
- CORNER_LEFT_LOWER:座標の中心はチャートの左下隅
- CORNER_RIGHT_LOWER:座標の中心はチャートの右下隅
- CORNER_RIGHT_UPPER:座標の中心はチャートの右上隅
図で表現するとこうなります。
その他の特性は単純明快です。わかりやすいようにコメントをつけました。その後、チャートを再描画するだけで、価格相場やチャートイベントの変化を待つことなく、変更が自動的に反映されます。
ChartRedraw(0); // Redraw the chart to reflect the new button
最後に、オブジェクトプロパティの作成と更新が成功したことを示すtrueを返します。
return (true); // Return true if creation is successful
チャートウインドウ上にボタンオブジェクトを作成するための完全な関数コードは以下の通りです。
//+------------------------------------------------------------------+ //| Function to create a button | //+------------------------------------------------------------------+ bool createButton(string objName, string text, int xD, int yD, int xS, int yS, color clrTxt, color clrBg, int fontSize = 12, color clrBorder = clrNONE, string font = "Arial Rounded MT Bold" ) { ResetLastError(); // Reset the last error code // Attempt to create the button if (!ObjectCreate(0, objName, OBJ_BUTTON, 0, 0, 0)) { Print(__FUNCTION__, ": failed to create Btn: ERR Code: ", GetLastError()); // Print error message if button creation fails return (false); // Return false if creation fails } // Set button properties ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // Set X distance ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Set Y distance ObjectSetInteger(0, objName, OBJPROP_XSIZE, xS); // Set X size ObjectSetInteger(0, objName, OBJPROP_YSIZE, yS); // Set Y size ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_RIGHT_UPPER); // Set corner position ObjectSetString(0, objName, OBJPROP_TEXT, text); // Set button text ObjectSetString(0, objName, OBJPROP_FONT, font); // Set font type ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); // Set font size ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Set text color ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBg); // Set background color ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder); // Set border color ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Set background property ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Set button state ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Set if the button is selectable ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Set if the button is selected ChartRedraw(0); // Redraw the chart to reflect the new button return (true); // Return true if creation is successful }
ボタンを作るのに必要な関数ができたので、それを使って指標ダッシュボードを作ってみましょう。オブジェクトの名前が必要になりますが、オブジェクト名の相互作用を簡単に管理するには、マクロを定義する方がずっと簡単です。
// Define button identifiers and properties #define BTN1 "BTN1"
#defineキーワードを使用して、値BTN1を持つBTN1という名前のマクロを定義し、メインボタン名を簡単に保存します。ボタンセクションを作成するたびに名前を繰り返し再入力する必要がなく、大幅に時間を節約し、名前を間違って指定する可能性を減らすことができます。基本的に、マクロはコンパイル時のテキスト置換に使用されます。
同様に、使用する他のマクロも定義します。
#define Desc "Desc " #define Symb "Symb " #define Base "Base " #define RSI "RSI " #define XS1 90 #define XS2 100 #define YS1 25 #define clrW clrWhite #define clrB clrBlack #define clrW_Gray C'230,230,230'
ここでは、さまざまな時間枠を説明するボタンの名前の接頭辞としてマクロ「Desc」を使用し、「Desc0」、「Desc1」などのインデックスや追加文字列を付加することで、これらの説明ボタンに固有の名前を生成できるようにしています。同様に、マクロSymbは、異なる取引銘柄を表すボタンの名前の接頭辞として使われ、Symb0、Symb1などのユニークな識別子を作るのに役立っています。マクロBaseはベースボタン名のプレフィックスとして機能し、ダッシュボードコンポーネントの明確で一貫した命名規則を提供します。RSI関連のボタンを処理するために、マクロRSIを使用し、異なる銘柄や時間枠にまたがってRSI値を表示するボタンの一意の識別子を確保します。
寸法については、XS1は特定のボタンの幅を設定し、XS2とYS1はそれぞれ他のボタンの幅と高さを指定し、GUI要素のサイズを標準化します。コードの中で白と黒の色を簡単に参照できるように、カラーマクロclrWとclrBを定義します。MQL5にはあらかじめ定義されたcolor形式変数があり、これが白にclrWhiteを使用するときに割り当て、参照するものです(web colors)。
カラー形式セクション1:
カラー形式セクション 2:
さらに、ダッシュボード全体で一貫したビジュアルスタイルを保証するために、背景色または境界色として使用するカスタムグレーカラーを clrW_Gray C'230,230,230'として定義します。色をよりコントロールするために、最後のマクロをリテラル形式で表現します。 これは「C'000,000,000」という書式をとり、「000」は0から255までの任意の数字となります。形式はRGB(赤、緑、青)形式を採用します。この3つの値は、それぞれ赤、緑、青の成分を0から255までのスケールで表しています。したがって、230,230,230はほぼ白に近い色合いとなります。
ダッシュボードで使用する特定の銘柄の時間枠や期間を定義する必要があります。したがって、それらを保存する必要があり、保存する最も簡単な方法は、簡単にアクセスできる配列に保存することです。
// Define the timeframes to be used ENUM_TIMEFRAMES periods[] = {PERIOD_M1, PERIOD_M5, PERIOD_H1, PERIOD_H4, PERIOD_D1};
ダッシュボードで使用する時間枠を指定するために、ENUM_TIMEFRAMES型の静的配列を定義します。配列の名前をperiodsとし、以下の特定の期間を含めます。PERIOD_M1(1分)、PERIOD_M5(5分)、PERIOD_H1(1時間)、PERIOD_H4(4時間)、PERIOD_D1(1日)。これらの時間枠をperiods配列に列挙することで、ダッシュボードにそれぞれの異なる時間枠のRSI値が表示されるようにし、複数の時間枠にまたがる市場動向を包括的に見ることができるようにしています。このセットアップによって、配列を繰り返し、計算とボタン作成を指定された期間ごとに一律に適用することができるようになります。配列を定義するために使用されるデータ型変数は、MetaTrader 5 (MT5)で利用可能なすべての時間枠で構成される列挙です。明示的に利用する限り、どのようなものでも利用できます。以下は、使用可能なすべての時間枠のリストです。
最後に、やはりグローバル変数に、指標を保持する指標ハンドルと、使用する様々な時間枠と銘柄の指標データを格納する配列を作成する必要があります。これは以下のコードスニペットで実現できます。
// Global variables int handleName_Id; // Variable to store the handle ID for the RSI indicator double rsi_Data_Val[]; // Array to store the RSI values
RSI指標のハンドルIDを格納するために、handleName_Id という名前のintegerデータ型変数を宣言します。特定の銘柄と時間枠のRSI指標を作成すると、一意のハンドルIDが返されます。このIDは指標ハンドルに保存され、その後の操作で、この特定のRSI指標を参照できるようになります。そこで2つ目の変数配列が登場します。rsi_Data_Valという名前のdouble型の動的配列を定義し、指標から取得したRSI値を格納します。RSIデータを取得すると、その値はこの配列にコピーされます。この配列をグローバル変数にすることで、RSIデータにプログラム全体を通してアクセスできるようになり、リアルタイム更新に値を使用したり、ダッシュボードのボタンに表示したりできるようになります。
ダッシュボードは、まずEAの初期化セクションに作成されるため、初期化イベントハンドラが何をするのかを見てみましょう。
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { ... return(INIT_SUCCEEDED); // Return initialization success }
OnInit関数は、EAの初期化インスタンスで呼び出されるイベントハンドラであり、必要に応じて必要な初期化をおこないます。EAが正しく動作するために必要なすべての初期設定作業を実行するように設計されています。これには、ユーザーインターフェイス要素の作成、変数の初期化、実行時にプログラムが正しく機能するために必要な条件の設定などが含まれる。ここでは、ダッシュボード要素の初期化に使用します。
次に、名前を入力し、パラメータを指定してボタンを作成する関数を呼び出します。
// Create the main button for the pair with specific properties createButton(BTN1, "PAIR", 600, 50, XS1, YS1, clrW, clrGray, 15, clrGray);
ここでは、マクロの定義と同じようにボタン名を BTN1とします。2番目のパラメータは、ボタン近傍に表示する文言やテキストを指定するために使用します。チャートウィンドウの右上隅からX軸(日時の目盛り)までの距離は600ピクセル、Y軸(価格の目盛り)までの距離は50ピクセルです。幅は、後続のボタンへの参照を容易にするために、すでに定義済みのマクロから取得されます。幅はXS1で90ピクセル、高さはYSIで25ピクセルです。テキストカラーは clrW(白)、背景はグレー、フォントサイズは15、ボタンの境界はグレーとします。ピクセルをおおよその範囲にするためには、チャートを0にスケールダウンし、2つのクロスヘア座標間のバーの数を水平スケール上のピクセル数に等しくします。例を挙げれば、こういうことです。
他のパラメータは省略されており、デフォルト値が自動的に適用されることを意味します。つまり、フォント名はArial Rounded MT Boldとなります。コンパイルすると、これが現在の状況です。
すべてのパラメータを以下のようなデフォルト値にしたとしても、結果は常に同じです。
// Create the main button for the pair with specific properties createButton(BTN1, "PAIR", 600, 50, XS1, YS1, clrW, clrGray, 15, clrGray, "Arial Rounded MT Bold");
次に、各定義済み時間枠に対応するRSI(相対力指数)情報を表示するボタンを作成します。作成する要素ごとにボタンを作成する関数を呼び出すことで、静的にこれをおこなうこともできますが、それではコードが非常に長くなり、面倒になるだけなので、監視された反復でボタンを作成するのに役立つ動的な形式を採用します。
// Loop to create buttons for each timeframe with the corresponding RSI label for(int i = 0; i < ArraySize(periods); i++) { createButton(Desc + IntegerToString(i), truncPrds(periods[i]) + " RSI 14", (600 - XS1) + i * -XS2, 50, XS2, YS1, clrW, clrGray, 13, clrGray); }
forループを開始し、periods配列を反復処理します。ArraySize関数を使用して、ループが配列のすべての要素をカバーするようにしています。この関数はシンプルで、引数をひとつだけ取り、選択された配列(ここでは「periods」)の要素数を返すだけです。 ループ内でcreateButton関数を呼び出し、各時間枠のボタンを作成します。ボタンの名前は、マクロ Desc(Descと定義)とIntegerToString関数を使用して文字列に変換されたインデックスiを連結することによって構築され、各ボタンがDesc 0、Desc 1などの一意の名前を持つことを保証します。この関数は、整数型の値を指定された長さの文字列に変換し、得られた文字列を返します。入力パラメータは3つですが、最後の2つはオプションです。最初のパラメータは変換のための番号で、ここではインデックスiです。ボタンのラベルを生成するためにカスタム関数truncPrdsを使用します。この関数は、時間枠の文字列表現をより読みやすい形式(M1、M5など)に切り詰め、このボタンが期間14のRSI値を表示することを示すためにRSI 14を追加します。この関数のコードスニペットは以下の通りです。
// Function to truncate the ENUM_TIMEFRAMES string for display purposes string truncPrds(ENUM_TIMEFRAMES period) { // Extract the timeframe abbreviation from the full ENUM string string prd = StringSubstr(EnumToString(period), 7); return prd; // Return the truncated string }
この関数は、ENUM_TIMEFRAMES型の期間をパラメータとして受け取り、EnumToString 関数を使用してENUM値を文字列表現に変換することから始めます。これは通常、プレフィックスを含む文字列になります。この不要な部分を取り除くために、StringSubstr関数を使って7文字目以降の部分文字列を抽出します。これは効果的に、文字列をユーザーインターフェイスに表示するのに適した、より短く読みやすい形に切り詰めます。最後に、切り捨てられた文字列を返します。この文字列は、ダッシュボードのボタンのラベル付けに使用できる、すっきりと簡潔な時間枠の表現となります。なぜこの関数が必要なのかを理解するために、ここで説明しましょう。
採用されたロジック
Print("BEFORE: ",EnumToString(periods[i])); Print("AFTER: ",truncPrds(periods[i]));
print文
切り捨てられていない期間は切り捨てられた期間よりも長く、アンダースコア文字を含む不要な7文字「PERIOD_」が含まれていることがはっきりとわかります。これは最終的には削除します。
各ボタンのX座標は動的に計算され、初期値600-XS1から始まり、XS2(ボタンの幅)にインデックスiを掛けた値を引くことで調整されます。この配置により、各ボタンが前のボタンの左側に配置され、横並びになります。Y座標はチャートの上端から50ピクセルに固定され、すべての時間枠ボタンで一貫した垂直位置を維持します。ボタンの寸法は、幅(100ピクセル)はマクロ XS2、高さ(25ピクセル)はマクロYS1で定義された値を用いて設定されます。さらに、各ボタンのテキストカラーをclrWhite(白)に、背景色をグレーに、フォントサイズを13に、そして最後に境界色をグレーに設定しました。コンパイルするとこうなります。
例えば、背景の色が気に入らないとします。どちらを使っても構いません。あなたがしなければならないのは、色をあなたの好みの美学に変えることだけです。例えば、背景を青、境界を黒にするには、以下のようにコードを変更します。
createButton(Desc + IntegerToString(i), truncPrds(periods[i]) + " RSI 14", (600 - XS1) + i * -XS2, 50, XS2, YS1, clrW, clrDodgerBlue, 13, clrBlack);
ここでは、背景色をドジャーブルーに、境界色を黒に変更しました。コンパイルするとこうなります。
ボタンがどのようにファンシーになり、審美的に魅力的になったかに注目してください。しかし、記事の一貫性を保つために、叫ばれていないデフォルトの色を使うことにしましょう。この記事の後半で、有効なシグナルを作成し識別する際にシャウトカラーを使用します。
次に、動的な表現形式を採用した、もうひとつの縦型銘柄ボタンシリーズを作成する必要があります。銘柄については、特定の銘柄を配列で定義して視覚化に使用する必要はありません。私たちは、ブローカーによって利用されている銘柄に自動的にアクセスすることができます。これを実現するために、forループを使ってブローカーが提供するすべての銘柄を繰り返し、必要に応じて必要な銘柄を選択します。
// Loop to create buttons for each symbol for(int i = 0; i < SymbolsTotal(true); i++) { ... }
ブローカーから提供された銘柄を取得するには、MQL5に組み込まれている関数SymbolsTotalを使用します。この関数は、利用可能な(気配値表示で選択された、またはすべての)銘柄の数を返します。この関数はブール値のパラメータを1つだけ取り、それをtrueに設定すると、気配値表示で選択されている銘柄の数を返します。値がfalseの場合、全銘柄の総数を返します。これを明確に理解するために、関数の入力パラメータ値がfalseに設定されているときの値を表示してみましょう。
// Loop to create buttons for each symbol for(int i = 0; i < SymbolsTotal(false); i++) { Print("Index ",i,": Symbol = ",SymbolName(i,false)); ... }
forループの中で、選択された値のフラグをfalseに設定し、銘柄リスト全体にアクセスできるようにしています。print文では、別のSymbolName 関数を使い、リスト内の銘柄名を位置別に取得しています。関数の第2パラメータは、気配値表示銘柄の選択基準に基づくリクエストモードを指定します。 この値がtrueの場合、銘柄は気配値表示で選択された銘柄のリストから取り出されます。値がfalseの場合、銘柄は一般的なリストから選ばれる。コンパイルすると、このようになります。
続きます。
すべての銘柄が選択されていることがわかります。この場合、396の銘柄が選択され、出力されます。ここで、チャートにすべての銘柄を表示する例を想像してみましょう。それはやり過ぎでしょう。1つのチャートに収まらないし、収めようとしてもフォントが小さすぎて銘柄が見づらくなるか、チャートが散らかるだけです。さらに、すべての銘柄が役に立つわけでもありません。この際、最優先事項をいくつか決めて、残りは除外することを検討してもいいでしょう。お気に入りのベストペアは気配値表示のセクションで表示するのが前提です。ここにはお気に入りの銘柄を配置しいて、価格相場を見たり、その動きを一目でモニターしたりすることができます。そこで、この気配値表示からディスプレイ銘柄を選ぶことにします。それを可能にするために、2つの関数の値をtrueに設定します。
// Loop to create buttons for each symbol for(int i = 0; i < SymbolsTotal(true); i++) { Print("Index ",i,": Symbol = ",SymbolName(i,true)); ... }
コンパイルするとこうなります。
気配値表示の12個の銘柄は、ツールボックス操作ログに同じ時系列で出力されています。区別しやすく、参照しやすいように、それぞれ赤と黒で強調しています。動的な縦型ボタンを作成するために、このようなロジックを使用します。
createButton(Symb + IntegerToString(i), SymbolName(i, true), 600, (50 + YS1) + i * YS1, XS1, YS1, clrW, clrGray, 11, clrGray);
ここではまず、マクロ Symb(Symbと定義)と、IntegerToString関数を使って文字列に変換されたインデック iを連結することによってボタンの名前を構築し、各ボタンがSymb 0、Symb 1といった一意の識別子を持つようにします。ボタンのラベルを、銘柄名を取得するSymbolName関数を使用して取得したインデックスiの取引銘柄名に設定し、trueパラメータによってその名前が気配値表示リストに含まれるようにします。ボタンのx座標を600ピクセルに設定し、すべての銘柄ボタンを縦に揃えます。Y座標は、(50 + YS1) +iに YS1を掛けたものとして動的に計算されます。これは、ボタンの高さ(25ピクセルであるYS1)にインデックスiを掛けたものを、最初のオフセットである50ピクセルに YS1 を加えたものに加えることによって、各ボタンを順次下方に配置します。XS1(幅90ピクセル)と YS1(高さ25ピクセル)を使ってボタンの寸法を指定します。テキストの色をclrW(白)、背景色をグレー、フォントサイズを11、そして最後に境界色をグレーに設定しました。コンパイルすると、このようになります。
これは、気配値表示に登録されている全銘柄のリストです。銘柄を追加したり削除したりすると、自動的に銘柄が作成されます。いくつかのペア、特に最後の3つのペアを外して、それが事実かどうかを見てみましょう。
それが自動的におこなわれているのがわかるでしょう。さて、削除された銘柄を元に戻し、現在選択されている銘柄を識別し、優先順位をつけ、他の銘柄ボタンと区別するためのロジックを作り続けます。
if (SymbolName(i, true) == _Symbol) { createButton(Symb + IntegerToString(i), "*" + SymbolName(i, true), 600, (50 + YS1) + i * YS1, XS1, YS1, clrB, clrLimeGreen, 11, clrW); } else { createButton(Symb + IntegerToString(i), SymbolName(i, true), 600, (50 + YS1) + i * YS1, XS1, YS1, clrW, clrGray, 11, clrGray); }
ここでは、同じようなテクスチャでボタンを作成するのではなく、ボタンを作成し、現在アクティブな銘柄に対してボタンの外観を区別します。まず、インデックスiの銘柄名が、あらかじめ定義しておいた変数_Symbolで取得した、現在アクティブな銘柄と一致するかどうかを確認します。一致した場合は、アクティブな銘柄を強調するために、明確な外観を持つボタンを作成します。アクティブな銘柄では、文字色をclrB(黒)、背景色をライムグリーン、フォントサイズを11、境界色をclrW(白)に設定します。この明確な配色は、識別しやすいようにアクティブな銘柄を強調します。銘柄がアクティブな銘柄と一致しない場合、デフォルトの関数が作動し、標準の外観スキームでボタンを作成します。これにより、非アクティブな銘柄がアクティブな銘柄とは一貫性があり、視覚的に区別された形で表示されるようになります。コンパイルすると、このようになります。
必要な銘柄ボタンをすべて作成したら、銘柄可視化行列の終わりを示すフッターを設け、そこに要約情報を追加しましょう。次のコードスニペットはその応用です。
// Create the base button for the RSI Dashboard at the end if (i == SymbolsTotal(true) - 1) { createButton(Base + IntegerToString(i), "RSI DashBoard", 600, (50 + YS1) + (i * YS1) + YS1, XS1 + XS2 * ArraySize(periods), YS1, clrW, clrGray, 11, clrGray); }
RSIダッシュボードのベースボタンを作成し、銘柄リストの最後に配置されるようにするには、まず現在のインデックス「i」が銘柄の総数から1を引いた数に等しいかどうかをチェックします。この条件が真であれば、ベースボタンの作成に進む。createButton」関数を使用して、このベースボタンの外観と位置を定義します。ボタンの名前を構成するために、Base文字列をインデックスiと連結し、ユニークな識別子を提供し、そのラベルを「RSI Dashboard」とします。これはあくまで任意の値であり、適切と思われる他の値に変更することもできます。そして、x座標を600ピクセル、y座標を(50 + YS1) + (i * YS1) + YS1 として計算した位置にボタンを配置し、最後の銘柄ボタンのすぐ下に表示されるようにします。ボタンの幅をXS1+XS2にperiods配列の合計要素数を返すArraySize関数を掛けたものと定義し、すべてのタイムフレーム ボタンを合わせた幅に広げます。一方、高さはYS1(25ピクセル)と定義します。ボタンの配色は標準的な外観を採用します。通常、この基本ボタンはRSIダッシュボード全体の明確なラベルとして機能し、銘柄リストの下部に視覚的なアンカーを提供します。マイルストーンの結果は以下の通りです。
最後に、各銘柄と時間枠の組み合わせのRSI値を表示するボタンを作成する必要があります。そのために次のコードスニペットを使用します。
// Loop to create buttons for RSI values for each symbol and timeframe for (int j = 0; j < ArraySize(periods); j++) { createButton(RSI + IntegerToString(j) + " " + SymbolName(i, true), "-/-", (600 - XS1) + (j * -XS2), (50 + YS1) + (i * YS1), XS2 - 1, YS1 - 1, clrB, clrW, 12, clrW); } }
時間枠のインデックスを表す整数カウンタjでループを開始します。各時間枠について、「createButton」関数を呼び出し、現在の銘柄と時間枠のRSI値を表示するボタンを設定します。RSI文字列とインデックスjおよび銘柄名を連結してボタン名を生成し、各ボタンに固有の識別子を確保します。ボタンのラベルを「-/-」に設定し、後で実際のRSI値で更新します。現時点では、銘柄と期間のグリッドレイアウトができることを確認しましょう。ボタンの位置は、x座標が(600 - XS1) + (j * - XS2)で、ボタンの幅を考慮した調整でXS2(100ピクセル)間隔で水平に配置されます。同様に、y座標を(50 + YS1) + (i * YS1)に設定し、銘柄のインデックスに基づいてボタンが垂直に配置されるようにします。ボタンの寸法は、幅がXS2-1、高さがYS1-1です。1の否定は、1ピクセルの境界を残すことを保証し、ボタングリッドの錯覚を起こさせる。次に、ボタンの配色を設定し、文字色をclrB(黒)、背景色をclrW(白)、フォントサイズを12、境界色をclrW(白)としました。この設定により、RSIボタンがグリッドレイアウトで整理され、それぞれが特定の銘柄と時間枠に関連付けられ、異なる期間にわたるRSI値を明確かつ構造的に表示します。コンパイルするとこうなります。
ダッシュボードの一般的なグリッドレイアウトを作成できたので、あとは指標の値を取得してボタンを更新するだけです。その前に、チャート上のどこかを右クリックし、ポップアップウィンドウでオブジェクトリストオプションを選択すれば、作成されたオブジェクトをプレビューすることができます。または、Ctrl + Bを押します。ポップアップで[List all]をクリックすると、作成した要素のリストが表示されます。
これで、チャート上に時系列にオブジェクトを作成し、それぞれにユニークな名前をつけることができました。これにより、あらゆる手段を尽くすことが保証されます。例えば、銘柄では最初の銘柄をSymb 0として、他の銘柄と区別しているのがわかるでしょう。もし、銘柄の名前をそれぞれのオブジェクトに連結していなかったら、すべてのボタンが同じ銘柄名を持つことになり、他のボタンの作成時にエラーとなります。つまり、技術的には1つのボタンだけが作成され、残りは無視されることになります。なんて狡猾なんでしょう。さて、引き続き初期化値を作成しましょう。
// Loop to initialize RSI values and update buttons for(int i = 0; i < SymbolsTotal(true); i++) { for (int j = 0; j < ArraySize(periods); j++) { // Get the handle ID for the RSI indicator for the specific symbol and timeframe handleName_Id = iRSI(SymbolName(i, true), periods[j], 14, PRICE_CLOSE); ArraySetAsSeries(rsi_Data_Val, true); // Set the array to be used as a time series CopyBuffer(handleName_Id, 0, 0, 1, rsi_Data_Val); // Copy the RSI values into the array ... }
ここでは、2つのループを使用して、各取引銘柄と時間枠を繰り返し、RSI値を初期化し、対応するボタンを更新します。まず、気配値表示で取引銘柄を選択するインデックスiを持つ外側のループから始め、このループの中で、選択された各銘柄について、定義された各時間枠を反復する内側のforループを持ちます。これは、例えば、AUDUSDを選択し、定義されたすべての時間枠、すなわち、M1、M5、H1、H4、D1を介して反復することを意味します。ロジックを簡単に理解するために、出力してみましょう。
// Loop to initialize RSI values and update buttons for(int i = 0; i < SymbolsTotal(true); i++) { Print("SELECTED SYMBOL = ",SymbolName(i, true)); for (int j = 0; j < ArraySize(periods); j++) { Print("PERIOD = ",EnumToString(periods[j])); ... }
これが私たちが持っているものです。
次に、銘柄と時間枠の各組み合わせについて、iRSI関数を使ってRSI指標のハンドルを取得します。パラメータは、銘柄、時間枠、RSI期間を14、価格タイプを終値とします。ハンドルhandleName_Idは、単なる整数で、コンピュータのメモリのどこかに定義保存され、RSI指標のデータを操作することができます。デフォルトでは、整数はインデックス10から始まり、それ以外の指標が作成された場合は、10、11、12、13、...と1ずつ増えていきます。説明のために出力してみましょう。
Print("PERIOD = ",EnumToString(periods[j]),": HANDLE ID = ",handleName_Id);
コンパイルすると、このようになります。
ハンドルのIDは10で始まり、12個の銘柄と各銘柄に5期間があるので、合計(12 * 5 = 60)60個の指標ハンドルがあることがわかります。しかし、インデックス付けを10から始めるので、最終的なハンドルIDを得るためには、最初の9つの値を結果に含める必要があります。数学的には、60+9で(60+9=69)69となります。それが正しいかどうか、視覚的な表現で確認してみましょう。
その通りでした。次に、ArraySetAsSeries関数を呼び出して「rsi_Data_Val」配列を時系列として使用するように設定し、フラグをtrueに設定して動作を確認します。この設定により、最新のRSI値が配列の先頭に来るようになります。次に、CopyBuffer関数を使用してRSI値を配列にコピーします。ここで、0はバッファインデックス、0は開始位置、1は取得するデータポイントの数を指定し、rsi_Data_Valはさらなる分析のために取得したデータを格納する先の配列です。データを操作ログに出力して、何が得られるか見てみましょう。ArrayPrint関数を使って、単純な動的配列を表示します。
ArrayPrint(rsi_Data_Val);
以下がその結果です。
素晴らしいです。正しいデータを得まました。指標ウィンドウとデータウィンドウで、取得したデータを確認することができます。これは、私たちがデータを入手し、次に進むことができることを明確に示しています。私たちのすることすべてを確認することの素晴らしさを知ることができます。ダッシュボードに新しいロジックを追加するたびに、コンパイルしてテストを実行し、予想通りの結果が得られることを確認することをお勧めします。あとは指標の値を使ってダッシュボードを更新すれば完了です。
// Update the button with the RSI value and colors update_Button(RSI + IntegerToString(j) + " " + SymbolName(i, true), DoubleToString(rsi_Data_Val[0], 2), clrBlack, clrW_Gray, clrWhite);
ここでは、カスタム関数update_Buttonを使用して、ダッシュボードのボタンをそれぞれの指標データで更新しています。関数のロジックは以下の通りです。
//+------------------------------------------------------------------+ //| Function to update a button | //+------------------------------------------------------------------+ bool update_Button(string objName, string text, color clrTxt = clrBlack, color clrBG = clrWhite, color clrBorder = clrWhite ) { int found = ObjectFind(0, objName); // Find the button by name // Check if the button exists if (found < 0) { ResetLastError(); // Reset the last error code Print("UNABLE TO FIND THE BTN: ERR Code: ", GetLastError()); // Print error message if button is not found return (false); // Return false if button is not found } else { // Update button properties ObjectSetString(0, objName, OBJPROP_TEXT, text); // Set button text ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Set text color ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBG); // Set background color ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder); // Set border color ChartRedraw(0); // Redraw the chart to reflect the updated button return (true); // Return true if update is successful } }
この関数は、ボタンを作るときに使ったものと同じです。違いは、ボタンを作成する代わりに、ObjectFind関数を使ってボタンを見つけることです。成功すれば、オブジェクトが見つかったサブウィンドウの番号(0はチャートのメインウィンドウ)を返します。オブジェクトが見つからない場合、この関数は負の数を返します。したがって、戻り値が0より小さい場合は、オブジェクトが存在しないことを示す負の数であることを意味し、エラーコードを表示してエラーを知らせ、その前に、前に起こりうるエラーをリセットし、falseを返して関数を終了します。もしパスすれば、オブジェクトを見つけ、オブジェクトのプロパティ(テキスト、テキストの配色、背景、境界)を更新します。コンパイルするとこうなります。
素晴らしいです。指標のデータがグリッドに表示されました。たった1行のコードで、データがいかに正確にマッピングされているかをご覧ください。また、現在の銘柄の1時間足の時間枠で、55.27という以前のデータが正しく入力されていることも確認します。さらに派手にするために、買われすぎと売られすぎのレベルに基づいて相場のエントリ条件を定義し、参照しやすく使いやすいように色を変えてみましょう。そこで、固定色を使う代わりに動的カラーを使うことにしましょう。
// Declare variables for button colors color text_clr, bg_clr, border_clr; // Determine button colors based on RSI value if (rsi_Data_Val[0] < 30) { text_clr = clrWhite; bg_clr = clrGreen; border_clr = clrGreen; } else if (rsi_Data_Val[0] > 70) { text_clr = clrWhite; bg_clr = clrRed; border_clr = clrRed; } else { text_clr = clrBlack; bg_clr = clrW_Gray; border_clr = clrWhite; } // Update the button with the RSI value and colors update_Button(RSI + IntegerToString(j) + " " + SymbolName(i, true), DoubleToString(rsi_Data_Val[0], 2), text_clr, bg_clr, border_clr);
まず、ボタンの色として3つの変数を宣言します。テキストの色はtext_clr、背景の色はbg_clr、境界の色はborder_clrです。次に、RSIデータ配列に格納されているRSI値に基づいて、適切な色を決定します。RSIの値が30を下回ると、一般的に売られ過ぎであることを示すので、文字色を白に、背景色を緑に、枠線の色を緑に設定します。
同様に、RSIの値が70を超え、買われすぎを示唆する場合は、文字色を白、背景色を赤、枠線の色を赤に設定し、赤いボタンで潜在的な売り機会を示します。RSI値が30~70の場合、デフォルトの配色が維持され、標準または中立の状態を反映します。最後に、update_Button関数を呼び出し、それぞれのボタンパラメータを渡すことで、ボタンの外観を更新します。これにより、ダッシュボードの各ボタンは現在のRSIの状態を正確に反映し、市況を視覚的に伝えることができます。マイルストーンを視覚的に見るには、こちらをご覧ください。
最終ONINITマイルストーン
完璧です。現在、動的でレスポンシブな指標ダッシュボードを作成し、現在の市況をチャート上に表示し、ハイライトを簡単に参照できるようにしました。
指標ダッシュボードの初期化を担当する完全なソースコードは以下の通りです。
//+------------------------------------------------------------------+ //| ADVANCED IND DASHBOARD.mq5 | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" // Define button identifiers and properties #define BTN1 "BTN1" #define Desc "Desc " #define Symb "Symb " #define Base "Base " #define RSI "RSI " #define XS1 90 #define XS2 100 #define YS1 25 #define clrW clrWhite #define clrB clrBlack #define clrW_Gray C'230,230,230' // Define the timeframes to be used ENUM_TIMEFRAMES periods[] = {PERIOD_M1, PERIOD_M5, PERIOD_H1, PERIOD_H4, PERIOD_D1}; // Function to truncate the ENUM_TIMEFRAMES string for display purposes string truncPrds(ENUM_TIMEFRAMES period) { // Extract the timeframe abbreviation from the full ENUM string string prd = StringSubstr(EnumToString(period), 7); return prd; // Return the truncated string } // Global variables int handleName_Id; // Variable to store the handle ID for the RSI indicator double rsi_Data_Val[]; // Array to store the RSI values //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Create the main button for the pair with specific properties createButton(BTN1, "PAIR", 600, 50, XS1, YS1, clrW, clrGray, 15, clrGray); // Loop to create buttons for each timeframe with the corresponding RSI label for(int i = 0; i < ArraySize(periods); i++) { //Print("BEFORE: ",EnumToString(periods[i])); //Print("AFTER: ",truncPrds(periods[i])); createButton(Desc + IntegerToString(i), truncPrds(periods[i]) + " RSI 14", (600 - XS1) + i * -XS2, 50, XS2, YS1, clrW, clrGray, 13, clrGray); } // Loop to create buttons for each symbol for(int i = 0; i < SymbolsTotal(true); i++) { //Print("Index ",i,": Symbol = ",SymbolName(i,true)); // Check if the symbol is the current symbol being traded if (SymbolName(i, true) == _Symbol) { createButton(Symb + IntegerToString(i), "*" + SymbolName(i, true), 600, (50 + YS1) + i * YS1, XS1, YS1, clrB, clrLimeGreen, 11, clrW); } else { createButton(Symb + IntegerToString(i), SymbolName(i, true), 600, (50 + YS1) + i * YS1, XS1, YS1, clrW, clrGray, 11, clrGray); } // Create the base button for the RSI Dashboard at the end if (i == SymbolsTotal(true) - 1) { createButton(Base + IntegerToString(i), "RSI DashBoard", 600, (50 + YS1) + (i * YS1) + YS1, XS1 + XS2 * ArraySize(periods), YS1, clrW, clrGray, 11, clrGray); } // Loop to create buttons for RSI values for each symbol and timeframe for (int j = 0; j < ArraySize(periods); j++) { createButton(RSI + IntegerToString(j) + " " + SymbolName(i, true), "-/-", (600 - XS1) + (j * -XS2), (50 + YS1) + (i * YS1), XS2 - 1, YS1 - 1, clrB, clrW, 12, clrW); } } // Loop to initialize RSI values and update buttons for(int i = 0; i < SymbolsTotal(true); i++) { //Print("SELECTED SYMBOL = ",SymbolName(i, true)); for (int j = 0; j < ArraySize(periods); j++) { //Print("PERIOD = ",EnumToString(periods[j])); // Get the handle ID for the RSI indicator for the specific symbol and timeframe handleName_Id = iRSI(SymbolName(i, true), periods[j], 14, PRICE_CLOSE); //Print("PERIOD = ",EnumToString(periods[j]),": HANDLE ID = ",handleName_Id); ArraySetAsSeries(rsi_Data_Val, true); // Set the array to be used as a time series CopyBuffer(handleName_Id, 0, 0, 1, rsi_Data_Val); // Copy the RSI values into the array //ArrayPrint(rsi_Data_Val); // Declare variables for button colors color text_clr, bg_clr, border_clr; // Determine button colors based on RSI value if (rsi_Data_Val[0] < 30) { text_clr = clrWhite; bg_clr = clrGreen; border_clr = clrGreen; } else if (rsi_Data_Val[0] > 70) { text_clr = clrWhite; bg_clr = clrRed; border_clr = clrRed; } else { text_clr = clrBlack; bg_clr = clrW_Gray; border_clr = clrWhite; } // Update the button with the RSI value and colors update_Button(RSI + IntegerToString(j) + " " + SymbolName(i, true), DoubleToString(rsi_Data_Val[0], 2), text_clr, bg_clr, border_clr); } } return(INIT_SUCCEEDED); // Return initialization success } //+------------------------------------------------------------------+ //| Function to create a button | //+------------------------------------------------------------------+ bool createButton(string objName, string text, int xD, int yD, int xS, int yS, color clrTxt, color clrBg, int fontSize = 12, color clrBorder = clrNONE, string font = "Arial Rounded MT Bold" ) { ResetLastError(); // Reset the last error code // Attempt to create the button if (!ObjectCreate(0, objName, OBJ_BUTTON, 0, 0, 0)) { Print(__FUNCTION__, ": failed to create Btn: ERR Code: ", GetLastError()); // Print error message if button creation fails return (false); // Return false if creation fails } // Set button properties ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // Set X distance ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Set Y distance ObjectSetInteger(0, objName, OBJPROP_XSIZE, xS); // Set X size ObjectSetInteger(0, objName, OBJPROP_YSIZE, yS); // Set Y size ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_RIGHT_UPPER); // Set corner position ObjectSetString(0, objName, OBJPROP_TEXT, text); // Set button text ObjectSetString(0, objName, OBJPROP_FONT, font); // Set font type ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); // Set font size ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Set text color ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBg); // Set background color ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder); // Set border color ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Set background property ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Set button state ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Set if the button is selectable ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Set if the button is selected ChartRedraw(0); // Redraw the chart to reflect the new button return (true); // Return true if creation is successful } //+------------------------------------------------------------------+ //| Function to update a button | //+------------------------------------------------------------------+ bool update_Button(string objName, string text, color clrTxt = clrBlack, color clrBG = clrWhite, color clrBorder = clrWhite ) { int found = ObjectFind(0, objName); // Find the button by name // Check if the button exists if (found < 0) { ResetLastError(); // Reset the last error code Print("UNABLE TO FIND THE BTN: ERR Code: ", GetLastError()); // Print error message if button is not found return (false); // Return false if button is not found } else { // Update button properties ObjectSetString(0, objName, OBJPROP_TEXT, text); // Set button text ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Set text color ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBG); // Set background color ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder); // Set border color ChartRedraw(0); // Redraw the chart to reflect the updated button return (true); // Return true if update is successful } }
ダッシュボードをすべての要素で作成したとしても、ダッシュボード上のデータが最新のデータを反映するように、ダッシュボードを刻々と更新する必要があります。これはOnTickイベントハンドラで実現されます。
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { ... }
これは、価格相場が変化するたびに呼び出されるvoidイベントハンドラ関数です。指標の値を更新するには、ここで初期化コードの一部を実行し、最新の値を取得する必要があります。
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Loop to update RSI values and buttons on each tick for(int i = 0; i < SymbolsTotal(true); i++) { for (int j = 0; j < ArraySize(periods); j++) { // Get the handle ID for the RSI indicator for the specific symbol and timeframe handleName_Id = iRSI(SymbolName(i, true), periods[j], 14, PRICE_CLOSE); ArraySetAsSeries(rsi_Data_Val, true); // Set the array to be used as a time series CopyBuffer(handleName_Id, 0, 0, 1, rsi_Data_Val); // Copy the RSI values into the array // Declare variables for button colors color text_clr, bg_clr, border_clr; // Determine button colors based on RSI value if (rsi_Data_Val[0] < 30) { text_clr = clrWhite; bg_clr = clrGreen; border_clr = clrGreen; } else if (rsi_Data_Val[0] > 70) { text_clr = clrWhite; bg_clr = clrRed; border_clr = clrRed; } else { text_clr = clrBlack; bg_clr = clrW_Gray; border_clr = clrWhite; } // Update the button with the RSI value and colors update_Button(RSI + IntegerToString(j) + " " + SymbolName(i, true), DoubleToString(rsi_Data_Val[0], 2), text_clr, bg_clr, border_clr); } } }
ここでは、内側と外側の2つのforループを持つ初期化セクションにコードスニペットをコピーアンドペーストし、指標の値を更新するだけです。これにより、ティックごとに最新の取得データに更新されます。こうすることで、ダッシュボードは非常に動的で、ライブ感があり、魅力的なものになります。GIF(Graphics Interchange Format)において、これは私たちが達成したマイルストーンです。
最後に、EAがチャートから削除されたら、作成したオブジェクト(指標ダッシュボード)を取り除く必要があります。これで指標のダッシュボードは破壊され、チャートはきれいになります。したがって、この機能はクリーンで効率的な取引環境を維持するために極めて重要です。これは、EAがチャートから削除されるたびに呼び出されるOnDeinitイベントハンドラ関数で実現されます。
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { // remove all dashboard objects ObjectsDeleteAll(0,BTN1); ObjectsDeleteAll(0,Desc); ObjectsDeleteAll(0,Symb); ObjectsDeleteAll(0,Base); ObjectsDeleteAll(0,RSI); ChartRedraw(0); }
ここでは、ObjectDeleteAll関数を呼び出して、指定したプレフィックス名のオブジェクトをすべて削除しています。この関数は、オブジェクト名のプレフィックスを使用して、指定された型のオブジェクトをすべて削除します。このロジックは極めて単純です。定義された接頭辞ごとに関数を呼び出します。BTN1、Desc、Symb、Base、RSIは、これらの接頭辞に関連するすべてのオブジェクトが削除され、チャートからすべてのボタンとグラフィカル要素が効果的に削除されます。最後に、ChartRedraw関数を呼び出してチャートを更新し、これらのオブジェクトの削除を反映させ、チャートが更新され、プログラムによって作成された要素が残らないようにします。これが今あるものです。
これで、MQL5で完全に機能する指標ダッシュボードを作成できました。指標ダッシュボードの作成を担当したソースコードの全文は以下の通りです。
//+------------------------------------------------------------------+ //| ADVANCED IND DASHBOARD.mq5 | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" // Define button identifiers and properties #define BTN1 "BTN1" #define Desc "Desc " #define Symb "Symb " #define Base "Base " #define RSI "RSI " #define XS1 90 #define XS2 100 #define YS1 25 #define clrW clrWhite #define clrB clrBlack #define clrW_Gray C'230,230,230' // Define the timeframes to be used ENUM_TIMEFRAMES periods[] = {PERIOD_M1, PERIOD_M5, PERIOD_H1, PERIOD_H4, PERIOD_D1}; // Function to truncate the ENUM_TIMEFRAMES string for display purposes string truncPrds(ENUM_TIMEFRAMES period) { // Extract the timeframe abbreviation from the full ENUM string string prd = StringSubstr(EnumToString(period), 7); return prd; // Return the truncated string } // Global variables int handleName_Id; // Variable to store the handle ID for the RSI indicator double rsi_Data_Val[]; // Array to store the RSI values //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Create the main button for the pair with specific properties createButton(BTN1, "PAIR", 600, 50, XS1, YS1, clrW, clrGray, 15, clrGray); // Loop to create buttons for each timeframe with the corresponding RSI label for(int i = 0; i < ArraySize(periods); i++) { //Print("BEFORE: ",EnumToString(periods[i])); //Print("AFTER: ",truncPrds(periods[i])); createButton(Desc + IntegerToString(i), truncPrds(periods[i]) + " RSI 14", (600 - XS1) + i * -XS2, 50, XS2, YS1, clrW, clrGray, 13, clrGray); } // Loop to create buttons for each symbol for(int i = 0; i < SymbolsTotal(true); i++) { //Print("Index ",i,": Symbol = ",SymbolName(i,true)); // Check if the symbol is the current symbol being traded if (SymbolName(i, true) == _Symbol) { createButton(Symb + IntegerToString(i), "*" + SymbolName(i, true), 600, (50 + YS1) + i * YS1, XS1, YS1, clrB, clrLimeGreen, 11, clrW); } else { createButton(Symb + IntegerToString(i), SymbolName(i, true), 600, (50 + YS1) + i * YS1, XS1, YS1, clrW, clrGray, 11, clrGray); } // Create the base button for the RSI Dashboard at the end if (i == SymbolsTotal(true) - 1) { createButton(Base + IntegerToString(i), "RSI DashBoard", 600, (50 + YS1) + (i * YS1) + YS1, XS1 + XS2 * ArraySize(periods), YS1, clrW, clrGray, 11, clrGray); } // Loop to create buttons for RSI values for each symbol and timeframe for (int j = 0; j < ArraySize(periods); j++) { createButton(RSI + IntegerToString(j) + " " + SymbolName(i, true), "-/-", (600 - XS1) + (j * -XS2), (50 + YS1) + (i * YS1), XS2 - 1, YS1 - 1, clrB, clrW, 12, clrW); } } // Loop to initialize RSI values and update buttons for(int i = 0; i < SymbolsTotal(true); i++) { //Print("SELECTED SYMBOL = ",SymbolName(i, true)); for (int j = 0; j < ArraySize(periods); j++) { //Print("PERIOD = ",EnumToString(periods[j])); // Get the handle ID for the RSI indicator for the specific symbol and timeframe handleName_Id = iRSI(SymbolName(i, true), periods[j], 14, PRICE_CLOSE); //Print("PERIOD = ",EnumToString(periods[j]),": HANDLE ID = ",handleName_Id); ArraySetAsSeries(rsi_Data_Val, true); // Set the array to be used as a time series CopyBuffer(handleName_Id, 0, 0, 1, rsi_Data_Val); // Copy the RSI values into the array //ArrayPrint(rsi_Data_Val); // Declare variables for button colors color text_clr, bg_clr, border_clr; // Determine button colors based on RSI value if (rsi_Data_Val[0] < 30) { text_clr = clrWhite; bg_clr = clrGreen; border_clr = clrGreen; } else if (rsi_Data_Val[0] > 70) { text_clr = clrWhite; bg_clr = clrRed; border_clr = clrRed; } else { text_clr = clrBlack; bg_clr = clrW_Gray; border_clr = clrWhite; } // Update the button with the RSI value and colors update_Button(RSI + IntegerToString(j) + " " + SymbolName(i, true), DoubleToString(rsi_Data_Val[0], 2), text_clr, bg_clr, border_clr); } } return(INIT_SUCCEEDED); // Return initialization success } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { // remove all dashboard objects ObjectsDeleteAll(0,BTN1); ObjectsDeleteAll(0,Desc); ObjectsDeleteAll(0,Symb); ObjectsDeleteAll(0,Base); ObjectsDeleteAll(0,RSI); ChartRedraw(0); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Loop to update RSI values and buttons on each tick for(int i = 0; i < SymbolsTotal(true); i++) { for (int j = 0; j < ArraySize(periods); j++) { // Get the handle ID for the RSI indicator for the specific symbol and timeframe handleName_Id = iRSI(SymbolName(i, true), periods[j], 14, PRICE_CLOSE); ArraySetAsSeries(rsi_Data_Val, true); // Set the array to be used as a time series CopyBuffer(handleName_Id, 0, 0, 1, rsi_Data_Val); // Copy the RSI values into the array // Declare variables for button colors color text_clr, bg_clr, border_clr; // Determine button colors based on RSI value if (rsi_Data_Val[0] < 30) { text_clr = clrWhite; bg_clr = clrGreen; border_clr = clrGreen; } else if (rsi_Data_Val[0] > 70) { text_clr = clrWhite; bg_clr = clrRed; border_clr = clrRed; } else { text_clr = clrBlack; bg_clr = clrW_Gray; border_clr = clrWhite; } // Update the button with the RSI value and colors update_Button(RSI + IntegerToString(j) + " " + SymbolName(i, true), DoubleToString(rsi_Data_Val[0], 2), text_clr, bg_clr, border_clr); } } } //+------------------------------------------------------------------+ //| Function to create a button | //+------------------------------------------------------------------+ bool createButton(string objName, string text, int xD, int yD, int xS, int yS, color clrTxt, color clrBg, int fontSize = 12, color clrBorder = clrNONE, string font = "Arial Rounded MT Bold" ) { ResetLastError(); // Reset the last error code // Attempt to create the button if (!ObjectCreate(0, objName, OBJ_BUTTON, 0, 0, 0)) { Print(__FUNCTION__, ": failed to create Btn: ERR Code: ", GetLastError()); // Print error message if button creation fails return (false); // Return false if creation fails } // Set button properties ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // Set X distance ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Set Y distance ObjectSetInteger(0, objName, OBJPROP_XSIZE, xS); // Set X size ObjectSetInteger(0, objName, OBJPROP_YSIZE, yS); // Set Y size ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_RIGHT_UPPER); // Set corner position ObjectSetString(0, objName, OBJPROP_TEXT, text); // Set button text ObjectSetString(0, objName, OBJPROP_FONT, font); // Set font type ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); // Set font size ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Set text color ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBg); // Set background color ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder); // Set border color ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Set background property ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Set button state ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Set if the button is selectable ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Set if the button is selected ChartRedraw(0); // Redraw the chart to reflect the new button return (true); // Return true if creation is successful } //+------------------------------------------------------------------+ //| Function to update a button | //+------------------------------------------------------------------+ bool update_Button(string objName, string text, color clrTxt = clrBlack, color clrBG = clrWhite, color clrBorder = clrWhite ) { int found = ObjectFind(0, objName); // Find the button by name // Check if the button exists if (found < 0) { ResetLastError(); // Reset the last error code Print("UNABLE TO FIND THE BTN: ERR Code: ", GetLastError()); // Print error message if button is not found return (false); // Return false if button is not found } else { // Update button properties ObjectSetString(0, objName, OBJPROP_TEXT, text); // Set button text ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Set text color ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBG); // Set background color ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder); // Set border color ChartRedraw(0); // Redraw the chart to reflect the updated button return (true); // Return true if update is successful } }
結論
結論として、MetaQuotes Language 5 (MQL5)を使用して複数銘柄・複数期間対応のRSI指標ダッシュボードを作成することで、トレーダーに市場分析を支援する強力なツールを提供できます。このダッシュボードは、さまざまな銘柄や時間枠におけるRSI値をリアルタイムで表示し、トレーダーがより迅速かつ情報に基づいた意思決定をおこなえるようサポートします。ダッシュボードの構築プロセスには、コンポーネントの設定、ボタンの作成、MQL5の機能を利用したリアルタイムデータの更新が含まれます。最終的に完成する製品は、機能的でありながら操作が簡単です。
この記事では、MQL5を活用して実用的な取引ツールを作成する方法について解説しました。トレーダーは、このダッシュボードをそのまま使用したり、自分のニーズに合わせてカスタマイズしたりすることで、進化する市場に対応した半自動や手動の取引戦略をサポートできます。
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/15356
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索