English Русский 中文 Español Português
preview
リスク管理(第2回):グラフィカルインターフェースでのロット計算の実装

リスク管理(第2回):グラフィカルインターフェースでのロット計算の実装

MetaTrader 5 |
22 0
Niquel Mendoza
Niquel Mendoza



はじめに

ようこそ。前回の記事の続きとして、今回は学んだ内容を実際に使って応用する方法を紹介します。さらに、一部の関数を改善し、より効率的に活用できるようにしていきます。作業を簡単にするために、MQL5の強力なコントロールライブラリを活用します。この記事を通して、それぞれの処理の考え方や背後にある意図についても説明していきます。最終的には、正確かつ効率的にロットサイズとストップロス(SL)を計算できるツールを手に入れることができます。また、この過程でGUIの設計やMQL5ライブラリの使い方についても理解を深められます。 


ロット数とストップロス取得関数の改善

まずは、前回作成した関数の改善から始めます。改善の目的は、処理をよりシンプルにし、効率的に動作させることです。具体的には、リアルタイムでエラーを確認できるように、デバッグ用メッセージ(PrintFormatやPrint)を追加し、理想的なロット数やストップロスの距離をより効率的に計算できる新しい関数を作成します。

GetMaxLote関数の改良


この関数は、利用可能な余剰証拠金と銘柄の仕様に基づいて、取引可能な最大ロット数を計算します。

//+----------------------------------------------------------------------------------------------+
//| Calculates the maximum lot size that can be traded based on free margin and symbol specifics |
//+----------------------------------------------------------------------------------------------+
double GetMaxLote(ENUM_ORDER_TYPE type, double DEVIATION = 100, double STOP_LIMIT = 50)
  {
   double VOLUME = 1.0; // Initial volume size
   ENUM_ORDER_TYPE new_type = MarketOrderByOrderType(type);
   double price = PriceByOrderType(_Symbol, type, DEVIATION, STOP_LIMIT); // Price for the given order type
   double volume_step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); // Volume step for the symbol
   double margin = EMPTY_VALUE; // Required margin, initialized as empty

   ResetLastError();
   if(!OrderCalcMargin(new_type, _Symbol, VOLUME, price, margin))
     {
      Print("OrderCalcMargin() failed. Error ", GetLastError());
      return 0; // Exit the function if margin calculation fails
     }
   if(AccountInfoDouble(ACCOUNT_MARGIN_FREE)  <= 0)
     {
      PrintFormat("Free margin of %+.2f is invalid, you cannot open trades right now",AccountInfoDouble(ACCOUNT_MARGIN_FREE));
      return 0;
     }

   double result = MathFloor((AccountInfoDouble(ACCOUNT_MARGIN_FREE) / margin) / volume_step) * volume_step;
   return result; // Return the calculated maximum lot size
  }

実装された改善点

  • デバッグメッセージ:証拠金計算に失敗した場合や余剰証拠金が不足している場合に、ユーザーに通知されるようになりました。
  • 余剰証拠金の検証:余剰証拠金がゼロ以下の場合は計算を行わないようにチェックが追加されました。

GetIdealLot関数


この関数は、1回の取引で許容される最大リスクと現在の市況を基に、理想的なロットサイズを計算します。

//+---------------------------------------------------------------------+
//| Determine the optimal lot size based on risk and current conditions |
//+---------------------------------------------------------------------+
void GetIdealLot(double& nlot, double glot, double max_risk_per_operation, double& new_risk_per_operation, long StopLoss)
  {
   if(StopLoss <= 0)
     {
      Print("[ERROR SL] Stop Loss distance is less than or equal to zero, now correct the stoploss distance: ", StopLoss);
      nlot = 0.0;
      return;
     }

   Print("Max Lot: ", glot, "  |  RiskPerOperation: ", max_risk_per_operation);

   new_risk_per_operation = 0;
   long spread = (long)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD);
   double tick_value = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
   double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);

   double rpo = (glot * (spread + 1 + (StopLoss * tick_value)));

   if(rpo > max_risk_per_operation)
     {
      if(max_risk_per_operation <= 0)
         return;

      double new_lot = (max_risk_per_operation / rpo) * glot;
      new_lot  = MathFloor(new_lot / step) * step;
      new_risk_per_operation =  new_lot * (spread + 1 + (StopLoss * tick_value));
      nlot =  new_lot;
     }
   else
     {
      new_risk_per_operation = rpo;
      nlot = glot;
     }

   if(nlot <= 0)
      PrintFormat("The lot %.2f is invalid, the risk %.2f increases or the sl %i decreases",nlot,max_risk_per_operation,StopLoss);
  }

この関数の改善点

  • ストップロスの検証:計算を進める前に、ストップロス距離が有効かどうかを確認するようになりました。
  • 明確なメッセージ表示:計算されたロットが無効な場合や、リスク調整が必要な場合に、ユーザーに分かりやすく通知されます。

新しいGetLotByRiskPerOperation関数

この関数は、1回の取引あたりのリスク(USD)と注文タイプのみを基に、理想的なロットサイズを計算できるようにします。そのため、ストップロスの距離をあらかじめ指定する必要がなく、リスク管理をより柔軟かつ簡単におこなうことが可能です。

//+--------------------------------------------------------------------+
//| Function to obtain the ideal lot based on your risk per operation  |
//+--------------------------------------------------------------------+
// risk_per_operation in USD, not %

double GetLotByRiskPerOperation(double risk_per_operation, const ENUM_ORDER_TYPE order_type, double DEVIATION = 100, double STOP_LIMIT = 50)
  {
   double VOLUME = 1.0; // Initial volume size
   ENUM_ORDER_TYPE new_type = MarketOrderByOrderType(order_type);
   double price = PriceByOrderType(_Symbol, order_type, DEVIATION, STOP_LIMIT); // Price for the given order type
   double volume_step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); // Volume step for the symbol
   double margin = EMPTY_VALUE; // Required margin, initialized as empty

   ResetLastError();
   if(!OrderCalcMargin(new_type, _Symbol, VOLUME, price, margin))
     {
      Print("OrderCalcMargin() failed. Error ", GetLastError());
      return 0; // Exit the function if margin calculation fails
     }
   double result = MathFloor((risk_per_operation / margin) / volume_step) * volume_step;

   if(result <= 0)
     {
      PrintFormat("The lot %.2f is invalid, the risk %.2f increases",result,risk_per_operation);
      return 0;
     }

   PrintFormat("The ideal lot for %.2f risk per trade is %.2f lots",risk_per_operation,result);

   return result; // Return the calculated maximum lot size
  }
//+------------------------------------------------------------------+

主な特徴

  • 計算の簡略化:USDでのリスクのみを使用し、他の値に依存せず計算できます。
  • デバッグ機能:ロットサイズが無効な場合やリスクが大きすぎる場合に、分かりやすくメッセージを表示します。

CalculateSL関数の改良

この関数は、1回の取引あたりのリスクと選択したロットサイズに基づいて、理想的なストップロス(SL)距離をポイント単位で計算します。

//+-----------------------------------------------------------------------+
//| Calculate the stop loss distance in points based on risk and lot size |
//+-----------------------------------------------------------------------+
long CalculateSL(const ENUM_ORDER_TYPE type, double risk_per_operation, double &chosen_lot, double DEVIATION = 100, double STOP_LIMIT = 50)
  {
   double lot = GetLotByRiskPerOperation(risk_per_operation,type,DEVIATION,STOP_LIMIT);
   chosen_lot = lot;
   long spread = (long)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD);
   double tick_value = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
   double result = ((risk_per_operation / lot) - spread - 1) / tick_value;
   long ideal_sl =  long(MathRound(result));

   if(ideal_sl <= 0)
     {
      PrintFormat("Stop loss %i invalid, please increase the risk per trade %.2f",ideal_sl,risk_per_operation);
      return 0;
     }

   return ideal_sl;
  }

改善点

  • GetLotByRiskPerOperationとの統合:計算されたロットサイズを使用して、ストップロスを決定するようになりました。
  • 計算精度の向上:ストップロスが常に正で有効な値になるようになりました。

これらの改善により、関数はより堅牢になり、デバッグもしやすく、柔軟性も向上しました。エラーメッセージにより問題を素早く特定でき、ロットサイズとストップロスの計算プロセスも簡略化されています。


MQL5のコントロール&パネルライブラリの活用

MQL5のコントロールライブラリを使い始める前に、まずMQL5におけるオブジェクトの基本的な仕組みを理解しておく必要があります。

MQL5のグラフィカルオブジェクト

MQL5では、グラフィカルオブジェクトはチャート上に配置して情報を表示したり、ユーザーとやり取りしたりするための視覚要素です。オブジェクトの位置は、主に以下の2つの座標で決まります。

  • X(水平座標):オブジェクトの水平方向の位置を定義します。
  • Y(垂直座標):オブジェクトの垂直方向の位置を定義します。

これらの座標により、オブジェクトがチャートやパネル上のどこに表示されるかが正確に決まります。

グラフィカルオブジェクトの基本プロパティ


位置に加えて、オブジェクトには以下のような特性があります。

  • 高さ(Height):オブジェクトが占める垂直方向のスペース
  • 幅(Width):オブジェクトが占める水平方向のスペース

これらのプロパティを適切に設定することで、オブジェクトが表示エリアをはみ出さず、インターフェース全体が整理された状態を保てます。たとえば、UIを設計する際には、これらの寸法設定により要素が見える範囲内に収まり、整然と配置されます。

アンカーポイント


アンカーポイントは、X座標とY座標を使用してオブジェクトの位置を決定する際の基準点です。このポイントにより、オブジェクトの位置の解釈が変わります。MQL5では、主に以下の4種類のアンカーポイントがあります。

ENUM_BASE_CORNER

識別子

説明

CORNER_LEFT_UPPER

チャートの左上を基準に座標を計算

CORNER_LEFT_LOWER

チャートの左下を基準に座標を計算

CORNER_RIGHT_LOWER

チャートの右下を基準に座標を計算

CORNER_RIGHT_UPPER

チャートの右上を基準に座標を計算

これら4つのアンカータイプは、図では次のように表すことができます。

BASE-CORNER

注意:通常、デフォルトで最もよく使われるのはCORNER_LEFT_UPPERです。ユースケースや扱いやすさによって変更することもできますが、この記事では上記のアンカーポイントのみを使用して解説します。

MQL5コントロールライブラリ

MQL5コントロールライブラリは、コントロールやパネルなどを作成するためのクラスライブラリです。各コントロールの詳細な説明については、公式ドキュメントを参照してください。

ドキュメントは、MetaEditorでF1キーを押すことで開くことができます。開いたら、コンテンツセクションに進み、「MQL5リファレンス」をクリックしてください。ページの下部までスクロールすると「標準ライブラリ」という項目があります。これをクリックすると、さまざまな用途向けのライブラリの一覧が表示されます。私たちが使用するのは、この中のコントロールライブラリ(パネルとダイアログ)です。

このライブラリを選択すると、各コントロールの使い方や機能、グループ分けを説明した表にアクセスできます。さらに、コントロールクラスの構造や関連性を理解しやすくするために、継承関係とクラスの構造を示した図を用意しました。

CONTROLS-1

図から分かるように、メインクラスはCObjectで、ここからCWndクラスが継承されています。CWndからさらに2つのクラスが派生しています。CWndContainerは複雑なコントロールを対象とし、CWndObjはより単純なコントロールに使用されます。私たちのパネルでは、ケースに応じて両方のタイプのコントロールを使い分けます。しかし、この記事ではまず、CAppDialogコントロールに注目します。このクラスは、私たちが作成しているメインパネルを生成するクラスです。

 CONTROLS-2

このパネルの中では、まず基本的なコントロールを順に追加していきます。テキストを表示するラベルや、注文タイプを選択するためのコンボボックス、変数を保存したり結果を取得したりするボタン、そしてユーザーが手動で値を入力できる編集フィールド(たとえば、1回の取引あたりの希望リスクを入力するためのフィールド)などです。

MQL5でのパネルの設計:計画とグラフィック要素

MQL5でパネルのコーディングを始める前に、まずそのデザインを計画しましょう。最も良い方法は、添付画像のように、PaintやCanvaなどのシンプルなツールを使ってビジュアルスケッチを作成することです。このステップにより、必要な要素がすべて含まれており、適切に配置されていることを確認でき、漏れを防ぎ、実装を容易にします。以下のように作成しました。

パネル画像を分析すると、異なる機能を持つ複数のコントロールが確認できます。これらのコントロールにより、ユーザーはインターフェースと明確かつ機能的にやり取りできます。以下に、主な要素と、それをクラスでどのように使用するかの詳細を示します。

コントロール  関数 パネル内の例
基本オブジェクト
  CLabel パネル上に静的なテキストを表示するために使用。ユーザー向けの説明や指示に用いる。「Risk Per Operation %」「Deviation (Points)」「Stop Limit (Points)」などのラベルグラフィカルオブジェクトOBJ_LABELから派生
  CComboBox ドロップダウンリストからユーザーが選択できるようにする「Get Lot」「Get SL」、それぞれのロットとストップロス計算用の注文タイプを選択可能複合オブジェクト(単一のグラフィカルオブジェクトから派生したものではない)
  CButton 押すとアクションを実行するインタラクティブなボタン。「SL Point」横のボタンで計算や値の確認を実行。また「Get ideal sl」ボタンも含むグラフィカルオブジェクトOBJ_BUTTONから派生
  CEdit ユーザーがデータを入力または手動で編集できるようにする「Risk Per Operation %」「Deviation (Points)」「Stop Limit (Points)」などの入力フィールド
グラフィカルオブジェクトOBJ_EDITに対応


パネルコンポーネント作成用関数(ラベル、ボタンなど)

関数を作成する前に、まずMetaEditorでエキスパートアドバイザー(EA)のテンプレートを作成する必要があります。このファイルは最初は空の状態で構いません。準備が整ったら、グラフィカルインターフェースのコンポーネントを扱うために必要なライブラリを読み込む作業を開始できます。

そのために、ファイルの先頭に以下のコード行を追加します。

#include <Controls\Dialog.mqh>
#include <Controls\Edit.mqh>
#include <Controls\Label.mqh>
#include <Controls\Button.mqh>
#include <Risk Management.mqh>
#include <Controls\ComboBox.mqh>

メインクラスの作成

まず、さまざまなコントロールの寸法を設定するために使用するパラメータを定義します。これらの値を使用すると、ボタン、テキストボックス、ドロップダウンリストなどのインターフェースコンポーネントのサイズを調整できます。

//+------------------------------------------------------------------+
//| defines                                                          |
//+------------------------------------------------------------------+
//--- for edits
#define EDIT_HEIGHT                         (20)      // edit height

//--- for buttons
#define BUTTON_WIDTH                        (80)     // size by X coordinate 
#define BUTTON_HEIGHT                       (20)      // size by Y coordinate 

//--- for combo box
#define COMBO_BOX_WIDTH                        (200)     // size by X coordinate 
#define COMBO_BOX_HEIGHT                       (20)      // size by Y coordinate 

次に、8要素の容量を持つ文字列配列を作成します。この配列は、さまざまな注文タイプをテキスト形式で保存するために使用されます。

string elements_order_type[8] =
  {
   "ORDER_TYPE_BUY",
   "ORDER_TYPE_SELL",
   "ORDER_TYPE_BUY_LIMIT",
   "ORDER_TYPE_SELL_LIMIT",
   "ORDER_TYPE_BUY_STOP",
   "ORDER_TYPE_SELL_STOP",
   "ORDER_TYPE_BUY_STOP_LIMIT",
   "ORDER_TYPE_SELL_STOP_LIMIT"
  };

リスク管理パネルクラスの作成

次に、CAppDialogクラスのpublic部分を継承する新しいクラスを作成します。CAppDialogはパネル用のメインクラスとして使用されるもので、この新しいクラスがリスク管理パネルの基盤となります。

//+-------------------------------------------------------------------+
//| Class CRiskManagementPanel                                        |
//| This class inherits from CAppDialog and will define the panel for |
//| managing risk parameters.                                         |
//+-------------------------------------------------------------------+
class CRiskManagementPanel : public CAppDialog
{
    // Declare private members here.
};

メソッドと属性の追加

次に、クラスにメソッドと属性を追加していきます。前述したように、いくつかのグラフィカルコントロールが必要になります。前の図に示したように、これらのコントロールはクラスのprivateセクションに追加されます。

private:
   CLabel            m_label_risk_per_operation;
   CEdit             m_edit_risk_per_operation;

   CLabel            m_label_deviaiton;
   CEdit             m_edit_deviaiton;

   CLabel            m_label_stop_limit;
   CEdit             m_edit_stop_limit;

   CLabel            m_label_get_lote;
   CComboBox         m_combobox_odertype_get_lot;

   CLabel            m_label_sl;
   CEdit             m_edit_sl;
   CButton           m_buttom_get_lote;
   CLabel            m_label_result_lote;
   CLabel            m_label_the_result_lote;

   CLabel            m_label_get_sl;
   CComboBox         m_combobox_odertype_get_sl;
   CLabel            m_label_lote;
   CButton           m_buttom_get_sl;
   CLabel            m_label_result_sl;
   CLabel            m_label_the_result_sl;

データを格納するprivate変数

コントロールに加えて、ユーザーがEditやComboBoxコントロールを通じて入力したデータを保持するprivate変数が必要です。これらの変数には、1回の取引あたりのリスク、偏差、注文タイプ、ストップロスの値などが格納されます。

    // Variables to store the data entered by the user
    double            deviation;                    // Stores deviation entered by the user
    double            stop_limit;                   // Stores stop limit entered by the user
    double            risk_per_operation;          // Stores risk per operation entered by the user
    long              sl;                           // Stores stop loss value entered by the user
    ENUM_ORDER_TYPE   order_type_get_sl;           // Stores the selected order type for stop loss
    ENUM_ORDER_TYPE   order_type_get_lot;          // Stores the selected order type for lot size

クラスのprivateセクションを完成させるために、グラフィカルコントロールやユーザー操作を扱うための必要な関数をすべて宣言します。これらの関数には、オブジェクト作成、ユーザーがComboBoxで選択項目を変更した際に呼ばれる関数、Labelの値を更新する関数などが含まれます。

private関数の宣言

//--- create labels and buttons
bool CreateAreaClientPanel();

//--- functions to edit labels dynamically
void EditLabelResultSL(string text);
void EditLabelResultLote(string text);

//--- create controls (buttons, labels, edits, combo boxes)
bool CreateEdit(CEdit &m_edit, const string name, const int x1, const int y1, string initial_Text = "");
bool CreateLabel(CLabel &label, const string name, const string text, const int x1, const int y1);
bool CreateButton(CButton &button, const string name, const string text, const int x1, const int y1, int x2_ = BUTTON_WIDTH, int y2_ = BUTTON_HEIGHT);
bool CreateComboBox(CComboBox &combo_box, const string name, string &elements[], string initial_text, const int x1, const int y1);

//--- combo box functions for handling user input
void OnChangeComBoxOrderTypeGetLote();
void OnChangeComBoxOrderTypeGetSL();

publicメソッドの宣言

クラスのpublicセクションでは、パネルとクライアント領域内のコントロールを作成するための2つの関数を追加します。また、チャートイベントを処理するOnEventハンドラと、文字列をENUM_ORDER_TYPEに変換する関数も宣言します。もちろん、コンストラクタとデストラクタもここで宣言されます。

public:
    CRiskManagementPanel(void);
    ~CRiskManagementPanel(void);

    //--- create panel and controls
    virtual bool Create(const long chart, const string name, const int subwin, const int x1, const int y1, const int x2, const int y2);
    
    //--- chart event handler
    virtual bool OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam);

    //--- function to convert string to ENUM_ORDER_TYPE
    static ENUM_ORDER_TYPE StringOrderTypeToEnum(const string OrderType);

これらの宣言により、パネルの作成、コントロールの追加、チャートイベントの管理に必要な準備が整いました。

コントロール追加用メソッドの作成

このセクションでは、リスク管理パネルを構成するラベルボタン編集フィールドコンボボックスなどの基本的なコントロールを作成するために必要なメソッドを詳しく見ていきます。さらに、パネルの動作を正しく保つための重要なメソッドについても説明します。これは、コントロールをクライアント領域に結び付けることで、パネルを移動またはリサイズした際にもコントロールが正しく配置されることを保証するものです。

コントロールのクライアント領域への結び付け

ユーザーがパネルを操作した際に、ボタンやラベルなどのコントロールがパネルと一緒に移動するようにするには、コントロールをパネルのクライアント領域にアンカー(結び付け)する必要があります。これを実現するために、CDialogクラスのAddメソッドを使用します。このメソッドは、コントロールをクライアント領域のオブジェクトリストに登録し、パネルとの同期を保つ役割を果たします。以下に、CDialogでの定義を示します。

//+------------------------------------------------------------------+
//| Add control to the client area (by pointer)                      |
//+------------------------------------------------------------------+
bool CDialog::Add(CWnd *control)
  {
   return(m_client_area.Add(control));
  }
//+------------------------------------------------------------------+
//| Add control to the client area (by reference)                    |
//+------------------------------------------------------------------+
bool CDialog::Add(CWnd &control)
  {
   return(m_client_area.Add(control));
  }

Addメソッドには2つのバージョンがあります。1つはコントロールへのポインタを受け取り、もう1つは参照を受け取ります。どちらのバージョンも、コントロールをm_client_areaコンテナに追加します。このコンテナは、パネルに関連するすべての要素を管理します。

この結び付けを省略すると、コントロールがパネルの移動に追従せず、表示上の不整合や操作上の問題が発生する可能性があります。

基本コントロール作成用メソッド

1. ラベル作成用関数

ラベルは、パネル上に静的なテキスト(見出しや指示など)を表示するために使用されます。ラベルを作成する関数には、以下の処理が含まれます。

  1. コントロールの座標を定義する。
  2. Createメソッドを使ってラベルオブジェクトを作成する。
  3. Textプロパティでラベルのテキストを設定する。
  4. Addメソッドでラベルをクライアント領域に結び付ける。

コード例

//+------------------------------------------------------------------+
//| Function to create labels                                        |
//+------------------------------------------------------------------+
bool CRiskManagementPanel::CreateLabel(CLabel &label,const string name, const string text, const int x1, const int y1)
  {
//--- coordinates
   int x2=x1+50;
   int y2=y1+20;
//--- create
   if(!label.Create(m_chart_id,name+"Label",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!label.Text(text))
      return(false);
   if(!Add(label))
      return(false);

//--- succeed
   return(true);
  }

  • label.Create:指定した座標にグラフィカルコントロールを作成します。
  • label.Text:ラベルに表示するテキストを設定します。
  • Add:ラベルをパネルに追加し、他のコントロールとともに管理できるようにします。

2. ボタン作成用関数

ボタンは、パネル上でユーザーが直接操作できるインタラクティブな要素です。この関数には、テキスト、寸法、開始座標などのパラメータが含まれます。また、デフォルト値を使用してボタンサイズを調整することも可能です。

コード例

//+------------------------------------------------------------------+
//| Function to create buttons                                       |
//+------------------------------------------------------------------+
bool CRiskManagementPanel::CreateButton(CButton &buttom,const string name, const string text, const int x1, const int y1,int x2_= BUTTON_WIDTH,int y2_ = BUTTON_HEIGHT)
  {
   int x2=x1+x2_;
   int y2=y1+y2_;
//--- create
   if(!buttom.Create(m_chart_id,name,m_subwin,x1,y1,x2,y2))
      return(false);
   if(!buttom.Text(text))
      return(false);
   if(!Add(buttom))
      return(false);
//--- succeed
   return(true);
  }

ボタンの寸法

  • x2とy2:ボタンの幅と高さを指定する任意のパラメータです。指定しない場合は、デフォルト値(BUTTON_WIDTHおよびBUTTON_HEIGHT)が使用されます。

使用されるメソッド

  • button.Create:指定した座標にボタンを作成します。
  • button.Text:ボタンに表示されるテキストを設定します。
  • Add:ボタンをパネルの要素リストに登録します。

3. 編集フィールド(Edit)作成用関数

Editコントロールは、ユーザーがパネル上でテキストを入力できる要素です。この関数には、初期値となるテキストを設定するための任意のパラメータが含まれています。

コード例

//+------------------------------------------------------------------+
//| Function to create edits                                         |
//+------------------------------------------------------------------+
bool CRiskManagementPanel::CreateEdit(CEdit &m_edit, const string name, const int x1, const int y1, string initial_Text = "")
  {
//--- coordinates
   int y2=y1+EDIT_HEIGHT;
   int x2 =x1 +100;
//--- create
   if(!m_edit.Create(m_chart_id,name+"Edit",m_subwin,x1,y1,x2,y2))
      return(false);
//--- allow editing the content
   if(!m_edit.ReadOnly(false))
      return(false);
   if(!Add(m_edit))
      return(false);

   m_edit.Text(initial_Text);

//--- succeed
   return(true);
  }

テキストフィールドのサイズ

  • 固定幅100とEDIT_HEIGHTで定義された高さによって、フィールドのサイズが決まります。

使用されるメソッド

  • m_edit.ReadOnly(false):フィールドを編集可能にします。
  • m_edit.Text:フィールドに表示する初期テキストを設定します。

4. コンボボックス作成用関数

コンボボックスは、ユーザーがドロップダウンリストから項目を選択できる複雑なコントロールです。この関数には、初期テキストの設定や複数項目の追加オプションも含まれています。

コード例

//+-------------------------------------------------------------------+
//| Function to create the complex object: combo box                  |
//| This function creates a combo box with multiple selectable items. |
//+-------------------------------------------------------------------+
bool CRiskManagementPanel::CreateComboBox(CComboBox &combo_box, const string name, string &elements[], string initial_text, const int x1, const int y1)
{
   //--- calculate coordinates for the combo box
   int x2 = x1 + COMBO_BOX_WIDTH;
   int y2 = y1 + COMBO_BOX_HEIGHT;

   //--- create the combo box control
   if (!combo_box.Create(m_chart_id, name, m_subwin, x1, y1, x2, y2))
      return (false);

   //--- add items to the combo box
   for (int i = 0; i < ArraySize(elements); i++)
   {
      if (!combo_box.AddItem(elements[i], i))
         return (false);
   }

   //--- select the initial text
   combo_box.SelectByText(initial_text);

   //--- add the combo box to the panel
   if (!Add(combo_box))
      return (false);

   //--- successfully created the combo box
   return (true);
}

コンボボックスの要素

  • elements[]:ドロップダウンリストに表示する項目を格納した文字列の配列
  • combo_box.AddItem:各要素をコンボボックスに追加し、固有のインデックスと関連付けます。

初期選択

  • combo_box.SelectByText(initial_text):コンボボックス作成時に表示される項目を定義します。

使用されるメソッド

  • combo_box.Create:指定した座標にコンボボックスを初期化します。
  • Add:コンボボックスをパネルに追加します。


クライアント領域内のオブジェクト作成

このセクションでは、ユーザーインターフェースのクライアント領域に配置する要素の設計と作成を始めます。目的は、ユーザーがリスクや取引注文に関連する設定を管理・調整できる機能的なスペースを提供することです。以下では、領域の分割方法と各コンポーネントの作成方法を説明します。

クライアントパネルは、主に3つのセクションに分かれています。

CONTROLS-3-S

  1. 一般設定:このセクションでは、ユーザーが1回の取引あたりのリスク、偏差、ストップリミットなど、一般的な設定を編集できます。

  2. ロット計算:パネルの第2セクションは、1回の取引あたりのリスクとストップロス距離(ポイント)に基づくロットサイズの計算に専用されています。ここでは、ユーザーが許容するリスク量を自動で推定します。

  3. ストップロス計算:最後に、パネル下部では、1回の取引あたりの許容損失率に基づくストップロスの計算をおこなうことができます。

bool CRiskManagementPanel::CreateAreaClientPanel(void)
{
   int x1 = 11; // Initial X coordinate
   int y1 = 15; // Initial Y coordinate

   // --- General Section: Risk Per Operation Configuration ---
   if (!CreateLabel(m_label_risk_per_operation, "L-Risk-Per-operation", "Risk per operation %: ", x1, y1))
      return false; // Create the label for risk per operation
   if (!CreateEdit(m_edit_risk_per_operation, "Risk-Per-operation", x1 + 150, y1))
      return false; // Create the editable field for risk per operation
   
   y1 += 30; // Move the Y coordinate down for the next section

   if (!CreateLabel(m_label_deviation, "L-Deviation", "Deviation (Points):", x1, y1))
      return false; // Create the label for deviation
   if (!CreateEdit(m_edit_deviation, "Deviation", x1 + 150, y1, "100"))
      return false; // Create the editable field for deviation
   
   this.deviation = 100; // Default value for deviation

   y1 += 30;

   if (!CreateLabel(m_label_stop_limit, "L-StopLimit", "Stop Limit (Points):", x1, y1))
      return false; // Create the label for stop limit
   if (!CreateEdit(m_edit_stop_limit, "Stop Limit", x1 + 150, y1, "50"))
      return false; // Create the editable field for stop limit
   
   this.stop_limit = 50; // Default value for stop limit

   y1 += 50;

   // --- Lot Calculation Section ---
   if (!CreateLabel(m_label_get_lote, "L-Get-Lote-Title", "Get Lote", x1, y1))
      return false; // Create the label for lot calculation section
   if (!CreateComboBox(m_combobox_order_type_get_lot, "ORDER_TYPE_LOT", elements_order_type, "ORDER_TYPE_BUY", x1 + 60, y1))
      return false; // Create the combo box to select order type for lot calculation

   this.order_type_get_lot = ORDER_TYPE_BUY; // Default order type

   y1 += 30;

   if (!CreateLabel(m_label_sl, "L-SL", "SL Point: ", x1, y1))
      return false; // Create the label for SL point
   if (!CreateEdit(m_edit_sl, "WRITE-SL", x1 + 60, y1))
      return false; // Create the editable field for SL
   if (!CreateButton(m_button_get_lote, "GET-LOTE", "Save", x1 + 160 + 5, y1))
      return false; // Create the button to save the lot calculation

   y1 += 25;

   if (!CreateLabel(m_label_result_lote, "L-Result-Lote", "Ideal Lot: ", x1, y1))
      return false; // Create the label for displaying the ideal lot
   if (!CreateLabel(m_label_the_result_lote, "L-The-Result-lot", "  ", x1 + 65, y1))
      return false; // Create a label to display the calculated ideal lot

   y1 += 50;

   // --- Stop Loss Calculation Section ---
   if (!CreateLabel(m_label_get_sl, "L-Get-SL-Title", "Get SL", x1, y1))
      return false; // Create the label for stop loss calculation section
   if (!CreateComboBox(m_combobox_order_type_get_sl, "ORDER_TYPE_SL", elements_order_type, "ORDER_TYPE_BUY", x1 + 50, y1))
      return false; // Create the combo box to select order type for stop loss calculation

   this.order_type_get_sl = ORDER_TYPE_BUY; // Default order type

   y1 += 30;

   if (!CreateLabel(m_label_lote, "L-LOTE", "Get ideal sl:", x1, y1))
      return false; // Create the label for getting the ideal stop loss
   if (!CreateButton(m_button_get_sl, "GET-SL", "Get", x1 + 90, y1))
      return false; // Create the button to get the stop loss value

   y1 += 25;

   if (!CreateLabel(m_label_result_sl, "L-Result-sl", "Ideal SL:", x1, y1))
      return false; // Create the label for displaying the ideal stop loss
   if (!CreateLabel(m_label_the_result_sl, "L-The-result-sl", "  ", x1 + 65, y1))
      return false; // Create a label to display the calculated ideal stop loss

   return true; // If all components are successfully created
}

パネル作成のメイン関数

Create関数は、ユーザーインターフェース上でグラフィカルパネルを初期化するためのコアメソッドです。この関数は、パネルの設定と構成に必要な主要要素をまとめ、パネルがチャートに正しく関連付けられ、座標や寸法が適切に割り当てられることを保証します。

入力パラメータの詳細

  1. chart (long):

    • パネルを作成するチャートのIDを表します。
    • MQL5ではデフォルトで、現在のチャートのIDは0です。
  2. name (string):

    • パネルを識別する名前です。
    • この名前はユニークで、他の操作でパネルを参照する際に使用されます。
  3. subwin  (int):

    • パネルを配置するチャートのサブウィンドウのインデックスです。
    • メインウィンドウに配置する場合は0を使用し、追加のサブウィンドウの場合は1、2と順に増加する値を使用します。
  4. x1とy1 (int):

    • パネルの初期座標(左上隅)をチャート上で指定します。
  5. x2とy2 (int):

    • パネルの寸法を指定します。
      • ×2:幅
      • y2:高
    • これらの寸法はピクセル単位で示されます。

//+------------------------------------------------------------------+
//| function to create the interface                                 |
//+------------------------------------------------------------------+
bool CRiskManagementPanel::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
   if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
   if(!CreateAreaClientPanel())
      return(false);
//--- succeed
   return(true);
  }



コンボボックスなどの要素を動的に更新する関数

関数作成を続けて、次にインターフェース要素の値を動的に変更する関数を実装します。これには、計算されたロットサイズやストップロス(SL)を表示するラベルや、ComboBoxなどの要素が含まれます。これらの関数により、ラベルの値を素早く更新でき、ユーザーがComboBoxで変更をおこなった際にインターフェースが即座に反応するようになります。

1. ラベル編集用関数


これらの関数は、インターフェース内の結果表示用ラベルのテキストを修正できるようにします。

//+------------------------------------------------------------------+
//| Function to edit the text of the lot size label                   |
//+------------------------------------------------------------------+
void CRiskManagementPanel::EditLabelResultLote(string text)
{
   // This function updates the text of the label that shows the ideal lot size.
   this.m_label_the_result_lote.Text(text); // Set the new text to the label
}
具体的には、計算されたロットサイズやストップロス(SL)を表示するラベルを、計算結果に基づいて更新するための関数です。テキスト値をパラメータとして受け取り、対応するラベルをその値で更新します。

//+------------------------------------------------------------------+
//| Function to edit the text of the stop loss label                 |
//+------------------------------------------------------------------+
void CRiskManagementPanel::EditLabelResultSL(string text)
{
   // This function updates the text of the label that shows the ideal stop loss value.
   this.m_label_the_result_sl.Text(text); // Set the new text to the stop loss label
}

同様に、この関数は理想的なストップロス値を表示するラベルを更新します。また、テキストをパラメータとして受け取り、対応するラベルを更新します。

2. コンボボックスの変更を処理する関数

ユーザーが注文タイプ(買い/売りなど)を決定するComboBoxで新しい値を選択するたびに、これらの関数が呼び出されます。関数は、選択された注文タイプを格納する内部変数を更新する役割を果たします。

//+------------------------------------------------------------------+
//| Function to update the variable that stores                      |
//| the order type to obtain the ideal sl                            |
//+------------------------------------------------------------------+
void CRiskManagementPanel::OnChangeComBoxOrderTypeGetSL(void)
{
   // Iterate through the order types array to find the selected type
   for(int i = 0; i < ArraySize(elements_order_type); i++)
   {
      // If the selected order type matches the one in the array
      if(m_combobox_order_type_get_sl.Select() == elements_order_type[i])
      {
         // Update the order type variable for stop loss
         this.order_type_get_sl = StringOrderTypeToEnum(m_combobox_order_type_get_sl.Select());
         Print("New order type for sl: ", EnumToString(this.order_type_get_sl)); // Log the selected order type
         break;
      }
   }
}

この関数は、ストップロス取得用のComboBoxでユーザーが注文タイプを変更したときに呼び出されます。関数は、注文タイプの配列を順に確認し、ComboBoxで現在選択されている項目を特定します。その後、StringOrderTypeToEnum関数を使って対応するENUM値を取得し、内部変数order_type_get_slを更新します。

//+------------------------------------------------------------------+
//| Function to update the variable that stores                      |
//| the order type to obtain the ideal lot                           |
//+------------------------------------------------------------------+
void CRiskManagementPanel::OnChangeComBoxOrderTypeGetLote(void)
{
   // Iterate through the order types array to find the selected type
   for(int i = 0; i < ArraySize(elements_order_type); i++)
   {
      // If the selected order type matches the one in the array
      if(m_combobox_order_type_get_lot.Select() == elements_order_type[i])
      {
         // Update the order type variable for lot size
         this.order_type_get_lot = StringOrderTypeToEnum(m_combobox_order_type_get_lot.Select());
         Print("New order type for lot: ", EnumToString(this.order_type_get_lot)); // Log the selected order type
         break;
      }
   }
}

前述の関数と同様に、この関数も同じ処理をおこないますが、対象はロットサイズの計算用です。ユーザーが理想ロット計算用のComboBoxで注文タイプを変更すると、この関数は選択された注文タイプをorder_type_get_lot変数に更新します。

3. 文字列をENUM_ORDER_TYPEに変換する追加関数

最後に、この関数は注文タイプを表す文字列(例:「ORDER_TYPE_BUY」)を、対応するENUM_ORDER_TYPEの列挙値に変換します。

//+------------------------------------------------------------------+
//| Function to convert a string into an order type                  |
//+------------------------------------------------------------------+
ENUM_ORDER_TYPE CRiskManagementPanel::StringOrderTypeToEnum(const string OrderType)
{
   // Convert the string order type to its corresponding enum value
   if(OrderType == "ORDER_TYPE_BUY")
      return ORDER_TYPE_BUY;
   if(OrderType == "ORDER_TYPE_SELL")
      return ORDER_TYPE_SELL;
   if(OrderType == "ORDER_TYPE_BUY_LIMIT")
      return ORDER_TYPE_BUY_LIMIT;
   if(OrderType == "ORDER_TYPE_SELL_LIMIT")
      return ORDER_TYPE_SELL_LIMIT;
   if(OrderType == "ORDER_TYPE_BUY_STOP")
      return ORDER_TYPE_BUY_STOP;
   if(OrderType == "ORDER_TYPE_SELL_STOP")
      return ORDER_TYPE_SELL_STOP;
   if(OrderType == "ORDER_TYPE_BUY_STOP_LIMIT")
      return ORDER_TYPE_BUY_STOP_LIMIT;
   if(OrderType == "ORDER_TYPE_SELL_STOP_LIMIT")
      return ORDER_TYPE_SELL_STOP_LIMIT;

   // Return WRONG_VALUE if no match is found
   return WRONG_VALUE;
}


キーボードイベントの処理:OnCharEventの実装

本記事の締めとして、前章でクラス内に宣言したOnChartEventメソッドを定義します。このメソッドでは、前のセクションで作成した関数を実行し、適切なボタンが押されたときにロットサイズやストップロスを計算し、Editフィールドを更新するなどの処理をおこないます。

しかし、この関数を完成させる前に、MQL5コントロールライブラリでのイベントの仕組みを理解しておく必要があります。

重要なのは、多くのプログラミング環境で一般的に使われるCHARTEVENT_OBJECT_CLICKのようなデフォルトイベントではなく、カスタムイベントを使用する点です。これらのイベントは、プロジェクト内のIncludes\Controls\Defines.mqhフォルダにあるDefines.mqhという特定のファイルで定義されています。

Defines.mqhファイルには、コントロールで必要となる列挙型や定義が含まれています。デフォルト値(コントロールやパネルの色など)に加えて、今回のケースで最も重要なのは、ファイル末尾にあるイベント定義です。以下に、これらの構造と使い方を示します。



イベント定義

Defines.mqh内では、コントロールで使用するためのいくつかのカスタムイベントが定義されています。

//+------------------------------------------------------------------+
//| Events                                                           |
//+------------------------------------------------------------------+
#define ON_CLICK                (0)   // clicking on control event
#define ON_DBL_CLICK            (1)   // double clicking on control event
#define ON_SHOW                 (2)   // showing control event
#define ON_HIDE                 (3)   // hiding control event
#define ON_CHANGE               (4)   // changing control event
#define ON_START_EDIT           (5)   // start of editing event
#define ON_END_EDIT             (6)   // end of editing event
#define ON_SCROLL_INC           (7)   // increment of scrollbar event
#define ON_SCROLL_DEC           (8)   // decrement of scrollbar event
#define ON_MOUSE_FOCUS_SET      (9)   // the "mouse cursor entered the control" event
#define ON_MOUSE_FOCUS_KILL     (10)  // the "mouse cursor exited the control" event
#define ON_DRAG_START           (11)  // the "control dragging start" event
#define ON_DRAG_PROCESS         (12)  // the "control is being dragged" event
#define ON_DRAG_END             (13)  // the "control dragging end" event
#define ON_BRING_TO_TOP         (14)  // the "mouse events priority increase" event
#define ON_APP_CLOSE            (100) // "closing the application" event
//+------------------------------------------------------------------+

パネルでのイベントの使用

今回のケースでは、すべてのイベントを扱う必要はなく、3つの主要なイベントに絞って処理します。

  1. ON_CLICK:ボタンなどのコントロールがクリックされたときに発生します。
  2. ON_CHANGE:ComboBoxなど、コントロールの値が変更されたときに発生します。
  3. ON_END_EDIT:Editコントロールなどでユーザーが編集を終了したときに発生します。

OnChartEventでのイベント実装例

Defines.mqhで定義されたイベントは、OnChartEvent関数のidパラメータに対応しています。この関数は、チャートやパネルで発生するイベントを処理します。IDはCHARTEVENT_CUSTOMと組み合わせて、各イベントのユニークな識別子を作成します。

たとえば、ON_CLICKイベントは以下のように使用されます。

if(id == ON_CLICK + CHARTEVENT_CUSTOM && lparam == m_button_get_lote.Id())
{
    // Acción a ejecutar cuando el botón de obtener lote es presionado
    // Aquí se llamaría a la función correspondiente para gestionar la acción del botón
}

OnEvent関数の作成

1. 関数宣言

bool CRiskManagementPanel::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)

OnEvent関数は、複数のパラメータを受け取るイベントハンドラです。

  • id:発生したイベントの識別子
  • lparam:追加パラメータで、通常コントロールやコンポーネントに関する特定情報を含む
  • dparam:数値を渡すために使用される数値(double)パラメータ
  • sparam:テキスト値を渡すために使用される文字列パラメータ

この関数の目的は、テキストフィールドの変更やボタンクリックなどのインターフェースイベントを管理することです。

2. 注文タイプComboBoxの変更検知

ComboBoxは、ユーザーがドロップダウンリストから値を選択できるようにするコントロールです。このステップでは、ユーザーがComboBoxの値を変更したかどうかを確認します。

ロット計算用ComboBoxの場合

if(id == ON_CHANGE + CHARTEVENT_CUSTOM && lparam == m_combobox_order_type_get_lot.Id())
{
    OnChangeComBoxOrderTypeGetLote();
}

ストップロス(SL)用ComboBoxの場合

if(id == ON_CHANGE + CHARTEVENT_CUSTOM && lparam == m_combobox_order_type_get_sl.Id())
{
    OnChangeComBoxOrderTypeGetSL();
}
  • ON_CHANGE + CHARTEVENT_CUSTOM:コントロールの値が変更されたことを示すイベント
  • lparam == m_combobox_order_type_get_lot.Id():正しいComboBoxのイベントであることを確認

条件内で、OnChangeComBoxOrderTypeGetLoteとOnChangeComBoxOrderTypeGetSLが実行され、選択された注文タイプを表す内部ENUM_ORDER_TYPE変数が更新されます。

3. Edit

Editコントロールは、ユーザーが値を手動で入力できるフィールドです。ここでは、各Editフィールドに関連する内部変数をチェックして更新します。

Risk per Operationフィールド

if(id == ON_END_EDIT + CHARTEVENT_CUSTOM && lparam == m_edit_risk_per_operation.Id())
{
    this.risk_per_operation = StringToDouble(m_edit_risk_per_operation.Text());
    this.risk_per_operation = NormalizeDouble((this.risk_per_operation / 100.0) * AccountInfoDouble(ACCOUNT_BALANCE), 2);
    Print("Edit Risk Per Operation: ", this.risk_per_operation);
}

SLフィールド

if(id == ON_END_EDIT + CHARTEVENT_CUSTOM && lparam == m_edit_sl.Id())
{
    this.sl = StringToInteger(m_edit_sl.Text());
    Print("Edit SL: ", this.sl);
}

その他(Deviation、Stop Limit)

if(id == ON_END_EDIT + CHARTEVENT_CUSTOM && lparam == m_edit_deviation.Id())
{
    this.deviation = StringToDouble(m_edit_deviation.Text());
    Print("Edit Deviation: ", this.deviation);
}
if(id == ON_END_EDIT + CHARTEVENT_CUSTOM && lparam == m_edit_stop_limit.Id())
{
    this.stop_limit = StringToDouble(m_edit_stop_limit.Text());
    Print("Edit Stop Limit: ", this.stop_limit);
}
  • ON_END_EDIT + CHARTEVENT_CUSTOM:ユーザーがフィールドの編集を終了したことを示すイベント
  • 各if文では、lparamが対応するコントロールIDと一致するか確認し、一致した場合はユーザーが入力した値で内部変数を更新。必要に応じて値を適切な型(double、intなど)に変換・正規化

4. 理想ロットサイズとストップロスの取得

ユーザーが[Get Lot]または[ideal stop loss]ボタンをクリックしたかどうかを確認し、それぞれの計算を実行します。

[Get Lot]ボタンの場合

if(id == ON_CLICK + CHARTEVENT_CUSTOM && lparam == m_button_get_lote.Id())
{
    Print("Risk Per operation: ", this.risk_per_operation);
    Print("SL in points: ", this.sl);
    Print("Order type get lot: ", EnumToString(this.order_type_get_lot));

    double new_lot;
    double new_risk_per_operation;

    GetIdealLot(new_lot, GetMaxLote(this.order_type_get_lot), this.risk_per_operation, new_risk_per_operation, this.sl);

    PrintFormat("Loss in case the following operation fails, with the parameters: lot %.2f and stop loss of %i points will be %.2f ", new_lot, this.sl, new_risk_per_operation);

    EditLabelResultLote(DoubleToString(new_lot, 2));
    m_button_get_lote.Pressed(false);
}

[Get SL]ボタンの場合

if(id == ON_CLICK + CHARTEVENT_CUSTOM && lparam == m_button_get_sl.Id())
{
    Print("Risk Per operation: ", this.risk_per_operation);
    Print("Order type get sl: ", EnumToString(this.order_type_get_lot));

    double new_lot;
    long new_sl = CalculateSL(this.order_type_get_sl, this.risk_per_operation, new_lot, this.deviation, this.stop_limit);

    PrintFormat("For the risk per operation %.2f the chosen lot is %.2f and the ideal stop loss in points is %i", this.risk_per_operation, new_lot, new_sl);

    EditLabelResultSL(IntegerToString(new_sl));
    m_button_get_sl.Pressed(false);
}
  • ON_CLICK + CHARTEVENT_CUSTOM:クリックイベントが特定のボタン(m_button_get_loteまたはm_button_get_sl)に対応しているか確認します。
  • 計算はGetIdealLot関数とCalculateSL関数を使用して実行され、理想ロットサイズとストップロスが決定されます。
  • 結果はパネルラベル(EditLabelResultLoteおよびEditLabelResultSL)に表示されます。
  • 最後に、ボタンの状態をfalseに設定してボタンを非アクティブにします(m_button_get_lote.Pressed(false))。

5. OnChartEventを返す

イベントを処理した後、関数はイベントを基本クラスCAppDialogに返します。これにより、基本関数は発生した可能性のある他のイベントも処理できるようになります。

return(CAppDialog::OnEvent(id, lparam, dparam, sparam));

この手順は、OnEventで明示的に処理されない他の重要なイベントが失われないようにするために重要です。


パネルの初期化:OnInitイベントと一般設定の使用

このセクションでは、カスタムクラスCRiskManagementPanelを使ってリスク管理パネルを設定します。このパネルは、ロットサイズやストップロス計算の管理に加え、トレーダーに必要なその他の機能もグラフィカルインターフェースで提供します。以下では、このパネルをプログラム内で宣言、初期化、管理する方法を示します。

1. グローバルオブジェクトの宣言


まず、プログラムのグローバルセクションでCRiskManagementPanelオブジェクトを宣言します。これにより、パネルはOnInit、OnDeinit、OnChartEventなど、コード内のさまざまなメソッドからアクセス可能になります。

//+------------------------------------------------------------------+
//| Global Variables                                                 |
//+------------------------------------------------------------------+
CRiskManagementPanel risk_panel; // Declare the panel object globally

2. OnInitイベントでのパネル設定


OnInitイベント内で、Createメソッドを使ってパネルを設定します。このメソッドは、パネルのグラフィカルウィンドウを初期化し、位置とサイズを定義します。パネルの作成に失敗した場合、プログラムはINIT_FAILEDを返し、初期化エラーを示します。その後、パネルはrunメソッドを使用して実行され、ユーザー操作用のインターフェースがアクティブ化されます。

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   //--- Create the risk management panel
   if(!risk_panel.Create(0, "Test Risk Management", 0, 40, 40, 380, 380)) 
      return(INIT_FAILED); // If panel creation fails, return an error code
   
   //--- Run the panel
   risk_panel.Run(); // Start the graphical interface
   
   //--- Return success
   return(INIT_SUCCEEDED);
  }
  1. 0 (chart_id):パネルがチャートのメインウィンドウに作成されることを示します。
  2. "Test Risk Management" (name):パネルウィンドウに表示されるタイトルを定義します。
  3. 0 (subwin):パネルが表示されるサブウィンドウを示します。ボット内でパネルを作成する場合は0を使用します。
  4. 40, 40 (coordinates):パネルの初期位置をピクセル単位で指定します(x=40, y=40)。
  5. 380, 380 (dimensions):パネルの幅と高さをピクセル単位で定義します。

3. OnDeinitイベントでのパネルの破棄


プログラムが終了されたり閉じられたりした際には、パネルが使用していたリソースを解放する必要があります。これには、OnDeinitイベント内でDestroyメソッドを使用します。これにより、将来のプログラムに影響を与えるような残留グラフィカル要素が残らないことが保証されます。

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   //--- Destroy the risk management panel
   risk_panel.Destroy(reason); // Free resources and remove the panel
  }

4. OnChartEventによるイベント処理


OnChartEventメソッドは、パネル上でのユーザー操作(ボタンクリック、テキストフィールドへの入力、ComboBoxの選択変更など)をキャプチャして解釈するための重要なメソッドです。これらのイベントは、CRiskManagementPanelクラスのChartEventメソッドを通じて処理されます。

//+------------------------------------------------------------------+
//| Expert chart event function                                      |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,         // Event ID
                  const long& lparam,   // Event parameter of type long
                  const double& dparam, // Event parameter of type double
                  const string& sparam) // Event parameter of type string
  {
   //--- Pass the chart event to the risk panel for processing
   risk_panel.ChartEvent(id, lparam, dparam, sparam);
  }


パネルのテストと検証

次に、EAを任意の銘柄で実行します。今回の例ではXAU/USD(ゴールド)を使用します。チャートは次のようになります。

 CONTROLS-4

透明性とデバッグのため、私の口座残高は以下の通りです。

CONTROLS-4

現時点で、ポジションはありません。

パネルの使い方は次のとおりです。

1. [Risk Per Operation]フィールドにパーセンテージを入力します(現在の口座残高に適用)。この例では、口座残高の3%を選択します。

 TEST-1

注意:[Deviation (Points)]や[Stop Limit (Points)]のEditフィールドも変更可能です。

2. 計算したい項目を選択します。まずは、ストップロス(ポイント)、1回の取引あたりのリスク、注文タイプに基づいて理想ロットを取得する機能を試します。今回は、買い注文(ORDER_TYPE_BUY)でストップロス500ポイントを選択します。

TEST-2.

[SL Point]行の[Save]ボタンをクリックします。その結果は以下の通りです。

TEST-4

パネルにはロット0.03が表示されます。さらに、[エキスパート]タブには次の内容が表示されます。

TEST-3.

このメッセージには、最大ロットサイズ(0.45)、1回の取引あたりのリスク、計算されたロットとストップロスで取引が失敗した場合の想定損失($15.45)などが含まれます。

3. 次に、1回の取引あたりのリスクに基づくロット計算のメソッドをテストします。[Get ideal sl]ラベルと同じ行の[Get]ボタンをクリックします。

TEST-5

結果はストップロス1856ポイント、さらに

 TEST-6

リスク3.0%に対する理想的なロットは0.01です。


結論

この記事では、ロットサイズとストップロスを計算する「計算機」の実装方法を学びました。これは、あらゆるトレーダーにとって非常に有用なツールです。さらに、MQL5のクラスとライブラリを使ってカスタムパネルを作成する方法も紹介しました。また、継承の使用やEdit、ComboBox、Buttonなどのオブジェクトを含む、MQL5のクラス構造についても詳しく学び、動的で機能的なグラフィカルインターフェースを構築できるようになりました。

このアプローチは、ワークフローの最適化だけでなく、MQL5でカスタムツールを開発する幅広い可能性も開きます。次回の記事では、ここで紹介した概念を統合し、メインクラスの作成に取り組んでいきます。このガイドが皆さんの参考になり、より高度なグラフィカルインターフェースを作成して取引体験を向上させるきっかけになれば幸いです。

本記事で作成および使用されるすべてのファイルは、次の表に示されています。
ファイル名説明
 Risk_Management_Panel.mq5  本記事で開発したリスク管理パネルを含むEAファイル
 Risk_Management.mqh  ロットサイズ計算、ストップロス、その他リスク管理機能を定義したインクルードファイル

MetaQuotes Ltdによりスペイン語から翻訳されました。
元の記事: https://www.mql5.com/es/articles/16985

添付されたファイル |
Risk_Management.mqh (22.79 KB)
市場シミュレーション(第6回):MetaTrader 5からExcelへの情報の転送 市場シミュレーション(第6回):MetaTrader 5からExcelへの情報の転送
多くの人、特にプログラマーではない人は、MetaTrader 5と他のプログラムとの間で情報をやり取りすることは非常に難しいと感じます。その代表的な例がExcelです。多くの人がExcelをリスク管理や運用管理のための手段として利用しています。Excelは非常に優れたプログラムであり、VBAプログラマーでなくても比較的容易に習得できます。ここでは、MetaTrader 5とExcelの間に接続を確立する方法について説明します。方法は非常にシンプルなものです。
市場シミュレーション(第5回):C_Ordersクラスの作成(II) 市場シミュレーション(第5回):C_Ordersクラスの作成(II)
本記事では、Chart Tradeとエキスパートアドバイザー(EA)が連携して、ユーザーが保有しているすべてのポジションを決済する要求をどのように処理するのかを解説します。一見すると単純な処理に思えるかもしれませんが、実際には注意すべきいくつかの複雑な点があります。
Pythonを使用したボラティリティ予測インジケーターの作成 Pythonを使用したボラティリティ予測インジケーターの作成
本記事では、二値分類を使って将来の極端なボラティリティを予測します。さらに、機械学習を活用した極端ボラティリティ予測インジケーターの開発もおこないます。
初級から中級まで:テンプレートとtypename(V) 初級から中級まで:テンプレートとtypename(V)
本記事では、テンプレートの最後の簡単な使用例を探り、コード内でtypenameを使用する利点と必要性についても解説します。最初は少し難しく感じるかもしれませんが、テンプレートやtypenameを後で正しく使うためには、しっかり理解しておくことが重要です。