English
preview
オンチャートUIを使用したリスクベースの取引執行EA(第1回):ユーザーインターフェースの設計

オンチャートUIを使用したリスクベースの取引執行EA(第1回):ユーザーインターフェースの設計

MetaTrader 5トレーディング |
20 0
Chacha Ian Maroa
Chacha Ian Maroa

はじめに

ポジションごとにリスクを一定に保ちながら適切なロットサイズを計算するのは、多くのトレーダーにとって困難です。エントリー前にこれらの計算を手動でおこなうのには反復的で時間がかかり、わずかなミスでも望ましくない損失につながる可能性があります。トレーダーには、このプロセスをより迅速かつ信頼性の高いものにする方法が必要です。それもチャート上から直接操作できる形であることが理想です。

本記事では、リスクベース取引執行エキスパートアドバイザー(EA)用のオンチャートコントロールパネルを設計する実践的な方法を紹介します。今回構築するインターフェースは、将来的にロットサイズ計算および注文発注を自動化する完全なシステムの基盤となるものです。この最初の部分では、ツールの視覚的中核となる静的グラフィカルレイアウトの作成に焦点を当てます。

このプロジェクトの目的は、単なるデザイン作成にとどまりません。最終的には実際の取引環境で使用可能なツールを構築することを目指します。また、読者はチャートオブジェクトの適切な配置やスタイリング方法を学び、将来的に独自のEA向けにプロフェッショナルなインターフェースを設計できるようになります。

初めは、読者はMQL5で整理された魅力的なGUIを設計する方法を理解したいと考えているでしょう。本記事の終了時には、チャート上に完全な静的パネルを構築するための知識とサンプルコードを習得できるでしょう。本記事は、ビジュアルデザインから完全な機能実装へと進む全2回の連載の第1回です。


コンセプトと設計

コーディングを始める前に、構築するものの明確なイメージを持つことが重要です。このEA用のGUIは、トレーダーの作業をより簡単かつ迅速にすることを目的としています。ロットサイズの計算と注文発注に必要な主要入力を、単一のオンチャートパネルにまとめます。

GUIデザイン

このインターフェースでは、トレーダーが発注したい注文タイプを選択できます。指値注文や逆指値注文用に建値を入力し、ストップロスおよびテイクプロフィット水準を指定できます。また、1回の取引あたりのリスク割合を設定するフィールドも含まれています。主要なボタンは2つあります。1つ目は、ユーザー入力に基づいて適切なロットサイズを計算するボタンです。2つ目は、ロットサイズを計算したうえで即座にサーバーへ注文を送信するボタンです。

さらに、計算されたロットサイズを表示する小さなセクションも含まれており、ユーザーは注文送信前に結果を確認できます。操作性向上のため、パネルを閉じるボタンと再度開くボタンも用意しています。これにより、取引中にチャート表示を妨げることなく、必要に応じてインターフェースの表示と非表示を切り替えられます。

デザインはシンプルでモダンなレイアウトを採用しています。すべての要素は一直線に整然と配置され、読みやすく操作しやすい構成になっています。カラーテーマは柔らかい明色背景を基調とし、アクセントカラーは最小限に抑え、情報そのものに集中できるようにしています。タイトルは大きめのフォントと異なる色で強調し、ツールの目的を即座に認識できるようにしています。アクションボタンには目立つ色を使用し、次の操作へ自然に誘導します。

一目見ただけで、ユーザーはパネルの機能と操作方法を理解できます。レイアウトは明確な構造と目的を持ち、プロフェッショナルで使いやすいツールとして設計されています。


EA構造の準備

まず作業用ファイルを作成しましょう。MetaEditorを開き、[ファイル]>[新規作成]>[エキスパートアドバイザ(テンプレート)]を選択し、空のEAファイルを作成します。ファイル名はSmartRiskTraderとします。この名前はツールの目的を明確に示しており、リスクベースの取引執行を管理するスマートな取引アシスタントであることを表しています。

ファイルが作成されたら、自動生成されたコードはすべて削除し、以下のソースコードを貼り付けます。

//+------------------------------------------------------------------+
//|                                              SmartRiskTrader.mq5 |
//|          Copyright 2025, MetaQuotes Ltd. Developer is Chacha Ian |
//|                          https://www.mql5.com/ja/users/chachaian |
//+------------------------------------------------------------------+

#property copyright "Copyright 2025, MetaQuotes Ltd. Developer is Chacha Ian"
#property link      "https://www.mql5.com/ja/users/chachaian"
#property version   "1.00"

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   //--- create timer
   EventSetTimer(60);
      
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   //--- destroy timer
   EventKillTimer();
   
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   
}

//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
{
   
}

//+------------------------------------------------------------------+
//| TradeTransaction function                                        |
//+------------------------------------------------------------------+
void OnTradeTransaction(const MqlTradeTransaction &trans,
                        const MqlTradeRequest     &request,
                        const MqlTradeResult      &result)
{

}

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int32_t id,
                  const long    &lparam,
                  const double  &dparam,
                  const string  &sparam)
{

}

//+------------------------------------------------------------------+

このコードは雛形として機能します。EAの完全な機能を構築するための、クリーンな基盤です。主な部分を簡単に見ていきましょう。

  • OnInit:EAが初めてチャートに読み込まれたときに呼ばれる関数です。ここでは、指定した時間間隔ごとに発生するタイマーイベントを設定します。本例では60秒ごとです。通常、EAの初期化処理はこの関数内でおこないます。
  • OnDeinit:EAがチャートから削除されるか、ターミナルが終了するときに実行される関数です。EAで作成したタイマーやリソースをここで解放します。
  • OnTick:新しい価格ティックが到着するたびに呼ばれる関数です。後ほどここで取引ロジックを処理します。
  • OnTimerOnInitで設定したタイマーに基づき、定期的に実行される関数です。パネル更新や取引条件のチェックなど、バックグラウンド処理に適しています。
  • OnTradeTransaction:ポジションを建てる、修正する、決済するなどの取引アクションが発生するたびに呼ばれる関数です。取引活動の監視や応答に利用できます。
  • OnChartEvent:チャート上のボタンクリックなど、ユーザー操作を処理する関数です。オンチャートコントロールパネル構築には不可欠です。

この構造を準備すれば、ステップごとにロジックを組み込むための必要な「フック」が揃ったことになります。次に、チャート上のグラフィカルオブジェクトを作成および管理しやすくするユーティリティ関数を定義しましょう。既存のソースコードのすぐ下に以下の関数を追加します。これらは、ユーザーインターフェース構築時に再利用可能なビルディングブロックとして役立ちます。

//--- UTILITY FUNCTIONS

//+------------------------------------------------------------------+
//| Function to generate a unique object name with a given prefix    |
//+------------------------------------------------------------------+
string GenerateUniqueName(string prefix){
   int attempt = 0;
   string uniqueName;
   while(true)
   {
      uniqueName = prefix + IntegerToString(MathRand() + attempt);
      if(ObjectFind(0, uniqueName) < 0){
         break;
      }
      attempt++;
   }
   return uniqueName;
}              

//--- Reusable GUI elements
//+------------------------------------------------------------------+
//| 1. To create a Rectangular panel                                 |
//+------------------------------------------------------------------+
bool CREATE_OBJ_RECTANGLE_LABEL(
   string objName,
   int xDistance,
   int yDistance,
   int width,
   int height,
   color clrBackground,
   int borderWidth,
   color borderColor            = clrNONE,
   ENUM_BORDER_TYPE borderType  = BORDER_FLAT,
   ENUM_LINE_STYLE  borderStyle = STYLE_SOLID
){

   ResetLastError();
   
   //--- Create a rectangular panel
   if(!ObjectCreate(0, objName, OBJ_RECTANGLE_LABEL, 0, 0, 0)){
      Print("Error while creating a rectangular panel: ", GetLastError());
      return false;
   }
   
   //--- Set values for corresponding object properties
   ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xDistance);
   ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yDistance);
   ObjectSetInteger(0, objName, OBJPROP_XSIZE, width);
   ObjectSetInteger(0, objName, OBJPROP_YSIZE, height);
   ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
   ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBackground);
   ObjectSetInteger(0, objName, OBJPROP_BORDER_TYPE, borderType);
   ObjectSetInteger(0, objName, OBJPROP_STYLE, borderStyle);
   ObjectSetInteger(0, objName, OBJPROP_WIDTH, borderWidth);
   ObjectSetInteger(0, objName, OBJPROP_COLOR, borderColor);
   ObjectSetInteger(0, objName, OBJPROP_BACK, false);
   ObjectSetInteger(0, objName, OBJPROP_STATE, false);
   ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, objName, OBJPROP_SELECTED, false);
   
   ChartRedraw();
   return true;
}
 
//+------------------------------------------------------------------+
//| 2. To create a Button Object                                     |
//+------------------------------------------------------------------+
bool CREATE_OBJ_BUTTON(
   string objName,
   int xDistance,
   int yDistance,
   int width,
   int height,
   string text           = "Activate",
   color textColor       = clrDarkGray,
   int fontSize          = 12,
   int borderWidth       = 0,
   color backgroundColor = clrWhiteSmoke,
   color borderColor     = clrBlack,
   string font           = "Tahoma"
){
   
   ResetLastError();
   
   //--- Create a button object
   if(!ObjectCreate(0, objName, OBJ_BUTTON, 0, 0, 0)){
      Print("Error while creating a button: ", GetLastError());
      return false;
   }
   
   //--- Set values for corresponding object properties
   ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xDistance);
   ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yDistance);
   ObjectSetInteger(0, objName, OBJPROP_XSIZE, width);
   ObjectSetInteger(0, objName, OBJPROP_YSIZE, height);
   ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
   ObjectSetString (0, objName, OBJPROP_TEXT, text);
   ObjectSetInteger(0, objName, OBJPROP_COLOR, textColor);
   ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize);
   ObjectSetString (0, objName, OBJPROP_FONT, font);
   ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, backgroundColor);
   ObjectSetInteger(0, objName, OBJPROP_BORDER_TYPE, BORDER_FLAT);
   ObjectSetInteger(0, objName, OBJPROP_WIDTH, borderWidth);
   ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, borderColor);
   ObjectSetInteger(0, objName, OBJPROP_BACK, false);
   ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, objName, OBJPROP_SELECTED, false);
   
   ChartRedraw();   
   return true;
}

//+------------------------------------------------------------------+
//| 3. To create an Input field                                      |
//+------------------------------------------------------------------+
bool CREATE_OBJ_EDIT(
   string objName,
   int xDistance,
   int yDistance,
   int width,
   int height,
   string text           = "Say sth'...",
   color textColor       = clrGray,
   int fontSize          = 12,
   color backgroundColor = clrWhite,
   color borderColor     = clrBlack,
   string font           = "Tahoma"
){
   
   ResetLastError();
   
   //--- Create an input field
   if(!ObjectCreate(0, objName, OBJ_EDIT, 0, 0, 0)){
      Print("Error while creating a text input: ", GetLastError());
      return false;
   }
   
   //--- Set values for corresponding object properties
   ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xDistance);
   ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yDistance);
   ObjectSetInteger(0, objName, OBJPROP_XSIZE, width);
   ObjectSetInteger(0, objName, OBJPROP_YSIZE, height);
   ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
   ObjectSetString (0, objName, OBJPROP_TEXT, text);
   ObjectSetInteger(0, objName, OBJPROP_COLOR, textColor);
   ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize);
   ObjectSetString (0, objName, OBJPROP_FONT, font);
   ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, backgroundColor);
   ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, borderColor);
   ObjectSetInteger(0, objName, OBJPROP_ALIGN, ALIGN_LEFT);
   ObjectSetInteger(0, objName, OBJPROP_READONLY, false);
   ObjectSetInteger(0, objName, OBJPROP_BACK, false);
   ObjectSetInteger(0, objName, OBJPROP_STATE, false);
   ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, objName, OBJPROP_SELECTED, false);
   
   ChartRedraw();   
   return true;
}

//+------------------------------------------------------------------+
//| 4. To create a Text label                                        |
//+------------------------------------------------------------------+
bool CREATE_OBJ_LABEL(
   string objName,
   int xDistance,
   int yDistance,
   string text           = "Name sth'...",
   color textColor       = clrDarkGray,
   int fontSize          = 12,
   string font           = "Tahoma"
){
   
   ResetLastError();
   
   //--- Create a text label
   if(!ObjectCreate(0, objName, OBJ_LABEL, 0, 0, 0)){
      Print("Error while creating a text label: ", GetLastError());
      return false;
   }
   
   //--- Set values for corresponding object properties
   ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xDistance);
   ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yDistance);
   ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
   ObjectSetString (0, objName, OBJPROP_TEXT, text);
   ObjectSetInteger(0, objName, OBJPROP_COLOR, textColor);
   ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize);
   ObjectSetString (0, objName, OBJPROP_FONT, font);
   ObjectSetInteger(0, objName, OBJPROP_BACK, false);
   ObjectSetInteger(0, objName, OBJPROP_STATE, false);
   ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, objName, OBJPROP_SELECTED, false);
   
   ChartRedraw();   
   return true;
}

次に、これらのユーティリティ関数がそれぞれ何をするかを理解しましょう。

1. GenerateUniqueName

チャート上のすべてのグラフィカルオブジェクトは固有の名前を持つ必要があります。この関数は、接頭辞とランダムな数字を組み合わせて一意の名前を生成します。複数のオブジェクトを動的に作成する際の名前衝突を防ぐ役割があります。

2.  CREATE_OBJ_RECTANGLE_LABEL

矩形のパネルを作成する関数です。ラベルやボタンなど他のGUI要素をまとめる背景やコンテナとして使用します。

3.  CREATE_OBJ_BUTTON

クリック可能なボタンを作成する関数です。テキスト、サイズ、色をカスタマイズ可能で、取引の発注やロットサイズ計算など、ユーザー操作に必須の要素です。

4.  CREATE_OBJ_EDIT

ユーザーが入力値をタイプできる編集可能なテキストボックスを作成する関数です。後ほど、建値やリスク割合などの入力を取得するために使用します。

5.  CREATE_OBJ_LABEL

単純なテキストラベルを作成する関数です。入力フィールドやボタンの横に情報や説明を表示するのに使用します。

これら5つの関数は、チャート上のグラフィカルインターフェースの基盤を形成します。オブジェクト作成を簡素化し、コードをより整理された読みやすいものにします。次のセクションでは、これらを使ってメインのGUIレイアウトを構築します。


ステップバイステップ:ユーザーインターフェースの組み立て

ヘルパー関数が揃ったので、実際のユーザーインターフェースの組み立てを始めましょう。コードをクリーンで整理された状態に保つため、GUIの描画処理全体を担当する単一の関数を作成します。この関数はCREATE_GUIと名付けます。インターフェースをモジュール化することで、ユーザーがUIの閉じるボタンをクリックした際にパネルを破棄して再構築する処理も容易になります。すべての視覚要素を作成する役割を1つの関数にまとめることで、管理やメンテナンスが容易になります。まずは、既存の関数のすぐ下にCREATE_GUI関数を宣言していきましょう。

...

//+------------------------------------------------------------------+
//| Function to render the main GUI                                  |
//+------------------------------------------------------------------+
void CREATE_GUI(){

}

//+------------------------------------------------------------------+

さらに、OnInit関数内でもCREATE_GUIを呼び出します。これにより、インターフェースをステップごとに構築していく過程で、コードの効果を即座にチャート上で確認できます。

...

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   //--- create timer
   EventSetTimer(60);
   
   //--- render the graphical user interface
   CREATE_GUI();
      
   return(INIT_SUCCEEDED);
}

...

次に進む前に、インターフェースがチャート上で正しく表示されるようにする重要なステップがあります。デフォルトでは、MetaTraderのチャートには左上にワンクリック取引ボタンが表示されます。これがカスタムインターフェースと重なると見づらくなる場合があります。これを回避するため、EA起動時にワンクリック取引ボタンを非表示に設定します。

これをおこなうために、ConfigureChartAppearanceというシンプルな関数を用意します。既存のユーティリティ関数のすぐ下に定義し、チャートプロパティを変更してワンクリックボタンを非表示にします。

//+------------------------------------------------------------------+
//| 4. This function configures the chart's appearance.              |                          |
//+------------------------------------------------------------------+
bool ConfigureChartAppearance()
{
   if(!ChartSetInteger(0, CHART_SHOW_ONE_CLICK, false)){
      Print("Error while setting one click buttons, ", GetLastError());
      return false;
   }
   return true;
}

この関数を定義したら、OnInit関数内で呼び出すことで、EAがチャートに読み込まれた際に自動的に実行されるようにします。これにより、GUIが描画される前にチャートのレイアウトが調整されます。

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   ...
   
   //--- Configure chart appearance
   if(!ConfigureChartAppearance()){
      Print("Error while customizing the chart's appearance");
      return(INIT_FAILED);
   }
      
   return(INIT_SUCCEEDED);
}

また、EAがチャートから削除される際に、作成したすべてのグラフィカルオブジェクトを適切に削除することは良い習慣です。これにより、前回のセッションのオブジェクトが残ることを防ぎ、チャートを常にクリーンな状態に保てます。

これをおこなうために、OnDeinit関数内に以下のコードを追加します。この処理により、現在のチャート上のすべてのオブジェクトが削除され、次回の使用時にもクリーンな状態を保てます。ObjectsDeleteAll関数は、現在のチャート上のすべてのオブジェクトを削除し、次回使用する際にクリーンな状態を保ちます。

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   ...
   
   //--- delete all graphical objects
   ObjectsDeleteAll(0);
      
}

まず、パネル上の各オブジェクトには固有の名前を付ける必要があります。これにより、チャート上で簡単に識別および管理できるようになります。この目的のために、オブジェクト名を格納するマクロを使用します。頻繁にアクセスや変更をおこなわないオブジェクトについては、短い接頭辞を定義して名前生成に使用します。次に、EAのプロパティディレクティブを定義しているセクションのすぐ下に、次のコードスニペットを追加します。

...

//+------------------------------------------------------------------+
//| Macros                                                           |
//+------------------------------------------------------------------+
#define SmartRiskTrader   "SmartRiskTrader"

...

インターフェースは、まず背景パネルから作成します。このパネルはGUIのすべての要素を置くベースコンテナの役割を果たします。いわば、ボタンやラベルなどのコントロールを配置するキャンバスです。これをおこなうために、カスタム関数CREATE_OBJ_RECTANGLE_LABELを呼び出します。この関数は、チャート上に矩形ラベルオブジェクトを描画します。

...

//+------------------------------------------------------------------+
//| Function to render the main GUI                                  |
//+------------------------------------------------------------------+
void CREATE_GUI(){

   //--- Background panel
   CREATE_OBJ_RECTANGLE_LABEL(GenerateUniqueName(SmartRiskTrader), 20, 20, 320, 380, clrWhiteSmoke, 1, clrDarkBlue, BORDER_FLAT, STYLE_DASHDOTDOT);

}

...

オブジェクト名が一意になるようにGenerateUniqueName(SmartRiskTrader)を使用し、背景は清潔感のあるホワイトスモーク色、枠線は濃い青で設定します。

この時点で、ソースコードをコンパイルします。コンパイルに成功したら、チャートに切り替えてEAを起動します。すると、指定した位置にきれいな矩形パネルが表示されているはずです。

メインの矩形パネル

これで、GUI作成関数が正しく動作していることが確認できました。インターフェースが複雑になる前に、このように段階的にコードをテストして問題を早期に発見して解決することは非常に有効です。

次に、元の背景パネルの上にもう1つ矩形パネルを追加します。このサブパネルは機能的な役割はなく、あくまで視覚的な装飾として、インターフェースをより整理されたレイヤー構造に見せるためのものです。メインパネルの枠内に収まるよう、少し小さい座標で最初の矩形の上に重ねます。

これを追加すると、コードは以下のような構造になります。

...

//+------------------------------------------------------------------+
//| Function to render the main GUI                                  |
//+------------------------------------------------------------------+
void CREATE_GUI(){

   //--- Background panel
   CREATE_OBJ_RECTANGLE_LABEL(GenerateUniqueName(SmartRiskTrader), 20, 20, 320, 380, clrWhiteSmoke, 1, clrDarkBlue, BORDER_FLAT, STYLE_DASHDOTDOT);
   CREATE_OBJ_RECTANGLE_LABEL(GenerateUniqueName(SmartRiskTrader), 30, 30, 300, 360, clrWhite, 1, clrDarkBlue, BORDER_FLAT, STYLE_SOLID);

}

...

このシンプルな追加により、インターフェースがより洗練されたレイヤー構造になります。コンパイルしてEAを実行し、更新されたデザインを確認してください。

サブパネル

背景パネルが完成したので、次はGUIのヘッダセクションを作成します。この領域にはツールのタイトルを表示し、ユーザーがパネルを閉じられる小さなボタンを配置します。また、ヘッダと残りのコンポーネントを区別するための細い区切り線も追加します。

この作業はステップごとに進め、コンポーネントを1つずつ追加するたびにコンパイルして変更を確認するのが効果的です。

まずは、閉じるボタンを置く小さな矩形を作成するところから始めましょう。

...

//+------------------------------------------------------------------+
//| Function to render the main GUI                                  |
//+------------------------------------------------------------------+
void CREATE_GUI(){

   //--- Background panel
   ...
   CREATE_OBJ_RECTANGLE_LABEL(GenerateUniqueName(SmartRiskTrader), 30, 30, 300, 360, clrWhite, 1, clrDarkBlue, BORDER_FLAT, STYLE_SOLID);
   
   //--- Header Section Components
   CREATE_OBJ_RECTANGLE_LABEL(GenerateUniqueName(SmartRiskTrader), 300, 40, 20, 20, clrWhiteSmoke, 1, clrDarkBlue, BORDER_FLAT, STYLE_SOLID);
   
}

...

この行を追加したら、コードをコンパイルしてEAを実行します。すると、パネルの右上に小さなヘッダボックスが表示されます。

インターフェースの閉じるボタン

次に、そのボックスの中に閉じるボタン([X])を配置します。 これまで作成してきた装飾用コンポーネントとは異なり、このボタンはユーザー操作に反応する要素です。クリックされるとインターフェースを閉じる動作をおこなうため、後でコード内で参照できるよう、一意でわかりやすい名前を付けることが重要です。

...

//+------------------------------------------------------------------+
//| Macros                                                           |
//+------------------------------------------------------------------+
#define SmartRiskTrader   "SmartRiskTrader"
#define BTN_CLOSE_GUI     "BTN_CLOSE_GUI"

...

次に、実際にボタンを表示するコードを追加しましょう。

...

//+------------------------------------------------------------------+
//| Function to render the main GUI                                  |
//+------------------------------------------------------------------+
void CREATE_GUI(){

   ...
   
   //--- Header Section Components
   CREATE_OBJ_RECTANGLE_LABEL(GenerateUniqueName(SmartRiskTrader), 300, 40, 20, 20, clrWhiteSmoke, 1, clrDarkBlue, BORDER_FLAT, STYLE_SOLID);
   CREATE_OBJ_LABEL(BTN_CLOSE_GUI, 305, 40, "X", clrDarkBlue, 12);
   
}

EAをコンパイルすると、パネルの右上に「X」マークが表示されます。 

GUI閉じるボタン

次に、GUIのタイトルラベルを追加します。これにより、パネルがよりプロフェッショナルで情報的な見た目になります。

...

//+------------------------------------------------------------------+
//| Function to render the main GUI                                  |
//+------------------------------------------------------------------+
void CREATE_GUI(){

   ...
   
   CREATE_OBJ_LABEL(GenerateUniqueName(SmartRiskTrader), 40, 37, "Smart Risk Trader", clrDarkBlue, 14, "Comic Sans Ms");
   CREATE_OBJ_LABEL(GenerateUniqueName(SmartRiskTrader), 40, 37, "Smart Risk Trader", clrDarkBlue, 14, "Comic Sans Ms");
   
}

...

コードを再度コンパイルして実行すると、パネルの上部にタイトルが表示され、読みやすいフォントで確認できます。

タイトル

最後に、ヘッダと下のコンポーネントを区切る水平線を作成します。

...

//+------------------------------------------------------------------+
//| Function to render the main GUI                                  |
//+------------------------------------------------------------------+
void CREATE_GUI(){

   ...
   
   CREATE_OBJ_LABEL(GenerateUniqueName(SmartRiskTrader), 40, 37, "Smart Risk Trader", clrDarkBlue, 14, "Comic Sans Ms");
   CREATE_OBJ_RECTANGLE_LABEL(GenerateUniqueName(SmartRiskTrader), 30, 70, 300, 1, clrDarkBlue, 1, clrDarkBlue, BORDER_FLAT);
   
}

...

もう一度コンパイルして、すべてが適切に動作していることを確認します。

ヘッダセクションの区切り

これらの手順を終えると、GUIにはシンプルながらも整ったヘッダセクションが完成します。小さな閉じるボタン、きれいなタイトル、そして下に引かれた区切り線が、パネルに構造と識別性を与えます。

次に、最初の入力フィールドとそのラベルを追加します。この入力フィールドでは、Buy、Sell、Buy Limitなどの注文タイプをユーザーが選択できるようにします。まず、前と同じように、フィールドの内容を説明するテキストラベルを表示します。

続いて、実際に入力フィールドとして機能するボタンを作成します。このボタンは後でプログラムから参照する必要があるため、動的に生成せず固定の名前を割り当てます。そのために、まずマクロを以下のように定義します。これを実現するには、まず次のようにマクロを定義します。

...

//+------------------------------------------------------------------+
//| Macros                                                           |
//+------------------------------------------------------------------+
...

#define BTN_ORDER_TYPES   "BTN_ORDER_TYPES"

...

マクロを定義したら、次にラベルと入力フィールド(この場合はボタンで表現)を作成します。ラベルはフィールドのタイトルとして機能し、ボタンはユーザーがインターフェースとやり取りして希望する注文タイプを選択できるようにします。

...

//+------------------------------------------------------------------+
//| Function to render the main GUI                                  |
//+------------------------------------------------------------------+
void CREATE_GUI(){

   ...
   
   //--- Order Types
   CREATE_OBJ_LABEL(GenerateUniqueName(SmartRiskTrader), 40, 90, "Order Type: ", C'20, 20, 20'); 
   CREATE_OBJ_BUTTON(BTN_ORDER_TYPES, 140, 90, 140, 25, "Select Order Type", C'20, 20, 20', 12, 1, clrWhiteSmoke, clrDarkBlue);  
   
}

...

このコードを追加したら、コンパイルしてEAを実行すると、チャート上に新しい入力フィールドが表示されるはずです。

注文タイプ

次に、注文タイプ用のドロップダウンメニューを作成する前に、いくつかのマクロを定義する必要があります。これらのマクロは、各ドロップダウン要素の固有IDを保持し、後で簡単に識別および操作できるようにするものです。ユーザーのクリックなどの操作に反応する要素なので、動的に生成するのではなく固定の名前を使うことで、イベント処理がシンプルになります。以下に、ドロップダウングループと各注文タイプオプションを定義するマクロを示します。

#define ORDER_TYPE_GROUP  "ORDER_TYPE_GROUP"
#define MARKET_BUY        "ORDER_TYPE_GROUP_MARKET_BUY"
#define MARKET_SELL       "ORDER_TYPE_GROUP_MARKET_SELL"
#define BUY_LIMIT         "ORDER_TYPE_GROUP_BUY_LIMIT"
#define SELL_LIMIT        "ORDER_TYPE_GROUP_SELL_LIMIT"
#define BUY_STOP          "ORDER_TYPE_GROUP_BUY_STOP"
#define SELL_STOP         "ORDER_TYPE_GROUP_SELL_STOP"

マクロを定義したので、次にドロップダウンを構築する関数を作成します。この関数はCREATE_ORDER_TYPE_DROPDOWNと名付け、背景パネルや各注文タイプラベルを含むすべてのドロップダウン要素を描画します。これにより、ユーザーはインターフェース上で整理された見やすい形式で直接注文タイプを選択できるようになります。

...

//+------------------------------------------------------------------+
//| Function to create the order types dropdown                      |
//+------------------------------------------------------------------+
void CREATE_ORDER_TYPE_DROPDOWN(){
   CREATE_OBJ_RECTANGLE_LABEL(GenerateUniqueName(ORDER_TYPE_GROUP), 140, 116, 140, 151, clrWhiteSmoke, 1, clrDarkBlue, BORDER_FLAT);
   CREATE_OBJ_LABEL(MARKET_BUY , 150, 120, "Market Buy ", C'20, 20, 20');
   CREATE_OBJ_LABEL(MARKET_SELL, 150, 145, "Market Sell", C'20, 20, 20');
   CREATE_OBJ_LABEL(BUY_LIMIT  , 150, 170, "Buy Limit  ", C'20, 20, 20');
   CREATE_OBJ_LABEL(SELL_LIMIT , 150, 195, "Sell Limit ", C'20, 20, 20');
   CREATE_OBJ_LABEL(BUY_STOP   , 150, 220, "Buy Stop   ", C'20, 20, 20');
   CREATE_OBJ_LABEL(SELL_STOP  , 150, 245, "Sell Stop  ", C'20, 20, 20');
}

この関数をテストするために、一時的にOnInit関数内で呼び出してみます。これにより、EAを起動したときにチャート上でドロップダウンがどのように表示されるか確認できます。

...

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   //--- create timer
   EventSetTimer(60);
   
   //--- render the graphical user interface
   CREATE_GUI();
   CREATE_ORDER_TYPE_DROPDOWN();
      
   return(INIT_SUCCEEDED);
}

...

ソースコードをコンパイルしてチャートを確認すると、インターフェース上にきれいなドロップダウンメニューが表示されるはずです。

注文タイプのドロップダウン 

正しく動作することを確認したら、この関数呼び出しはコメントアウトしておくことが重要です。後で、ユーザーが[Select Order Type]ボタンをクリックしたときのみドロップダウンを表示するようにします。こうすることで、ドロップダウンがプロフェッショナルなインターフェースのように動的に振る舞うようになります。

...

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   //--- create timer
   EventSetTimer(60);
   
   //--- render the graphical user interface
   CREATE_GUI();
   //CREATE_ORDER_TYPE_DROPDOWN();
      
   return(INIT_SUCCEEDED);
}

...

次に、建値用の入力フィールドとそのラベルを作成します。このフィールドは後でコード内から参照する必要があるため、固定の名前を割り当てて扱いやすくします。以下のようにマクロを定義します。

...

//+------------------------------------------------------------------+
//| Macros                                                           |
//+------------------------------------------------------------------+
...

#define FIELD_ENTRY_PRICE "FIELD_ENTRY_PRICE"

...

マクロを定義したら、CREATE_GUI関数内にラベルと入力フィールドを追加できます。これらの2行のコードにより、[Entry Price]という表記と、ユーザーが値を入力できる編集ボックスが表示されます。

...

//+------------------------------------------------------------------+
//| Function to render the main GUI                                  |
//+------------------------------------------------------------------+
void CREATE_GUI(){

   ...
  
   //--- Entry Price
   CREATE_OBJ_LABEL(GenerateUniqueName(SmartRiskTrader), 40, 125, "Entry Price: ", C'20, 20, 20');
   CREATE_OBJ_EDIT(FIELD_ENTRY_PRICE, 140, 125, 100, 25, "1.14030", C'20, 20, 20', 12, clrWhiteSmoke, clrDarkBlue);
   
}

ソースコードをコンパイルし、EAをチャートに起動します。すると、整然と配置された[Entry Price]フィールドとラベルが表示され、ユーザーが入力できる状態になっています。

[Entry Price]フィールド

[Entry Price]フィールドを追加したので、同じ手順で残りの入力フィールドも作成します。これらのフィールドでは、ユーザーがストップロス、テイクプロフィット、リスクパーセンテージを入力できるようにします。先ほどと同様に、後でコードから参照しやすいよう、各フィールド用のマクロを定義しておきます。

...

//+------------------------------------------------------------------+
//| Macros                                                           |
//+------------------------------------------------------------------+

...

#define FIELD_STOP_LOSS   "FIELD_STOP_LOSS"
#define FIELD_TAKE_PROFIT "FIELD_TAKE_PROFIT"
#define RISK              "RISK"

これらのマクロを定義したら、再びCREATE_GUI関数に戻り、各フィールドのラベルと入力ボックスを作成するコードを追加します。


//+------------------------------------------------------------------+
//| Function to render the main GUI                                  |
//+------------------------------------------------------------------+
void CREATE_GUI(){

   ...
     
   //--- Stop Loss
   CREATE_OBJ_LABEL(GenerateUniqueName(SmartRiskTrader), 40, 160, "Stop Loss: ", C'20, 20, 20');
   CREATE_OBJ_EDIT(FIELD_STOP_LOSS, 140, 160, 100, 25, "1.13302", C'20, 20, 20', 12, clrWhiteSmoke, clrDarkBlue);
   
   //--- Take Profit
   CREATE_OBJ_LABEL(GenerateUniqueName(SmartRiskTrader), 40, 195, "Take Profit: ", C'20, 20, 20');
   CREATE_OBJ_EDIT(FIELD_TAKE_PROFIT, 140, 195, 100, 25, "1.16302", C'20, 20, 20', 12, clrWhiteSmoke, clrDarkBlue);
   
   //--- Risk
   CREATE_OBJ_LABEL(GenerateUniqueName(SmartRiskTrader), 40, 230, "Risk %: ", C'20, 20, 20');
   CREATE_OBJ_EDIT(RISK, 140, 230, 100, 25, "2.0", C'20, 20, 20', 12, clrWhiteSmoke, clrDarkBlue);

}

これらの行を追加したら、ソースコードをコンパイルしてEAをチャートに起動してください。すると、3つの新しい入力フィールドが追加され、ラベル付きで整然と配置されたきれいなレイアウトが表示されます。

その他の入力

次に、ロットサイズの計算や注文送信などの操作を行う実行ボタンを追加します。これらのボタンは後でユーザーがクリックしたことを検出する必要があるため、それぞれに一意でわかりやすい名前を割り当てることが重要です。以下のようにマクロを定義します

//+------------------------------------------------------------------+
//| Macros                                                           |
//+------------------------------------------------------------------+

...

#define BTN_SEND_ORDER    "BTN_SEND_ORDER"
#define RESULTS_TEXT      "RESULTS_TEXT"
#define BTN_GUI_OPEN      "BTN_GUI_OPEN"

マクロを定義したら、CREATE_GUI関数内で実際のボタン要素を作成できます。以下のコードでは、ロットサイズ計算用のボタンと注文送信用のボタンの2つを追加します。

//+------------------------------------------------------------------+
//| Function to render the main GUI                                  |
//+------------------------------------------------------------------+
void CREATE_GUI(){

   ...
    
   //--- Execution Buttons
   CREATE_OBJ_BUTTON(BTN_CALC_LOT, 40, 270, 140, 40, "CALCULATE LOT", clrWhite, 12, 1, clrDarkBlue, clrBlack);
   CREATE_OBJ_BUTTON(BTN_SEND_ORDER, 190, 270, 120, 40, "SEND ORDER", clrWhite, 12, 1, clrDarkGreen, clrBlack);
  
}

ソースコードをコンパイルし、EAをチャートに適用すると、2つの整列されたボタンが表示されます。これにより、後でユーザーはインターフェース上から直接ロットサイズを計算したり注文を送信したりできるようになります。

実行ボタン

ユーザーインターフェースの静的レイアウトを完成させるために、次に計算されたロットサイズが表示される小さな表示領域を作成します。この表示領域は、矩形の背景パネルとその中に配置されたテキストラベルで構成されます。後でプログラムからこのラベルの値を更新する必要があるため、参照しやすいようにマクロで一意の名前を付けておきます。

//+------------------------------------------------------------------+
//| Macros                                                           |
//+------------------------------------------------------------------+

...

#define RESULTS_TEXT      "RESULTS_TEXT"

マクロを定義したら、次に実際の表示要素を作成します。CREATE_GUI関数内で、矩形のコンテナを描画し、その中にテキストラベルを配置するコードを追加します。

//+------------------------------------------------------------------+
//| Function to render the main GUI                                  |
//+------------------------------------------------------------------+
void CREATE_GUI(){

   ...
   
   //--- Execution Results
   CREATE_OBJ_RECTANGLE_LABEL(GenerateUniqueName(SmartRiskTrader), 40, 320, 270, 50, clrWhiteSmoke, 1, clrDarkBlue, BORDER_FLAT);
   CREATE_OBJ_LABEL(RESULTS_TEXT, 60, 333, "Result: Lot Size = 0.23", clrDarkGreen, 14);
   
}

コードをコンパイルし、EAをチャートに適用すると、インターフェース下部にシンプルで見やすい表示領域が現れ、計算されたロットサイズのサンプルテキストが表示されます。

最終的なGUIの外観

この追加により、「Smart Risk Trader」インターフェースの静的デザインが完成しました。 理解を深めやすくするため、現時点でのプロジェクト全体のソースコードも添付しています。MetaEditorで開き、これまで説明した関数を確認し、正しくコンパイルできることを確認してください。次のセクションでは、GUIをインタラクティブにし、ユーザーの操作に反応するようにする方法に焦点を移します。つまり、インターフェースに「命を吹き込む」段階です。


結論

ここまでで、Smart Risk Trader EA用の、きれいで機能的なチャート上GUIを構築することに成功しました。MQL5のグラフィカルオブジェクトを使って、パネル、ラベル、ボタン、入力フィールドを作成する方法、そしてコードをモジュール化して保守性や拡張性を高める方法を理解できたはずです。最初は何もないEAテンプレートから始まりましたが、今ではプロフェッショナルな取引ツールの視覚的に整理された基盤が完成しました。

ステップごとに進めることで、MQL5でGUIを作成する方法だけでなく、MetaTrader 5環境内でこれらの要素がどのように相互作用するかについても貴重な知見を得ることができました。この知識を活かせば、自分のEAやインジケーター用に、より高度でスタイリッシュ、かつ動的なインターフェースを設計および構築できるようになります。

第2回では、各コンポーネントを実際の機能に接続し、Smart Risk Traderを完全にインタラクティブにする方法を学びます。ボタンクリックやドロップダウン選択などのユーザー操作を検知および反応させる手順を含め、インターフェースを本当に「生きたもの」にします。

MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/19932

添付されたファイル |
SmartRiskTrader.mq5 (15.31 KB)
取引戦略の開発:バタフライオシレーター法 取引戦略の開発:バタフライオシレーター法
魅力的な数学概念であるバタフライ曲線を、実践的な取引ツールへと応用する方法を紹介します。バタフライオシレーターを構築し、それを基盤とした基本的な取引戦略を開発します。この戦略は、オシレーター特有の周期的シグナルと移動平均による従来型のトレンド確認を効果的に組み合わせることで、潜在的な市場エントリーポイントを特定するための体系的なアプローチを実現します。
共和分株式による統計的裁定取引(第7回):スコアリングシステム2 共和分株式による統計的裁定取引(第7回):スコアリングシステム2
平均回帰戦略、特に共和分に基づく統計的裁定取引において取引対象となる株式バスケットの選定に使用する、追加の2つのスコアリング基準について解説します。前回の記事では、流動性および共和分ベクトルの強度、ならびに時間足とルックバック期間という戦略的基準を紹介しました。本記事ではそれを補完する形で、共和分ベクトルの安定性および平均回帰に要する時間、いわゆる半減期を取り上げます。また、新しいフィルタを適用したバックテスト結果の考察と、その再現に必要なファイルも提供します。
エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法 エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法
この記事では、MT4において複数のEAの衝突をさける方法を扱います。ターミナルの操作、MQL4の基本的な使い方がわかる人にとって、役に立つでしょう。
長期取引の最適化:包み足と流動性戦略 長期取引の最適化:包み足と流動性戦略
高時間足(W1、D1、MN)に基づいて長期的な分析と取引判断をおこなうEAです。このEAは、短期的な値動きに翻弄されることなく、利確目標に到達するまで自分のトレンドの方向性(バイアス)を頻繁に変えずにポジションを保持できる、忍耐強い長期トレーダー向けに設計されています。