English Русский 中文 Español Deutsch Português
トレードシグナルの多通貨監視(パート2):アプリケーションのビジュアル部分の実装

トレードシグナルの多通貨監視(パート2):アプリケーションのビジュアル部分の実装

MetaTrader 5トレーディング | 25 5月 2020, 10:07
1 268 0
Alexander Fedosov
Alexander Fedosov

目次

イントロダクション

前の段階では、多通貨トレードシグナルモニターの一般的な構造を開発しました。 このパートでは、初期アプリケーション構成に関連する段階を順次実装し、インタフェースを構成する要素の基本的な相互作用を作成します。


セットアップステップ 1: シンボル

アプリケーション構造によると、最初の起動時の最初のアプリケーションのセットアップステップは、作成されたトレードシグナルを検索するためにさらに使用するシンボルを選択するためのインターフェイスの作成を意味します。 前回の記事の最後に、このタスクのアプリケーション フレームワークを作成しました。 今回はアプリケーションの開発に進みましょう。 まず、このアプリケーションパーツの実装に必要な要素の主要なグループを定義します。

  • アプリケーション ウィンドウ。
  • シンボルのクイック選択。
  • グループインプットフィールド。
  • シンボル グループの [保存] ボタンと [読み込み] ボタン
  • 使用可能なすべてのシンボルの完全なリストをチェックボックスとして、シンボル名のテキストラベルを付けます。
  • 2 番目のセットアップステップに切り替える次のボタン、つまり、時間枠の選択です。

以前に作成したファイル構造は次のようになります。

図1 アプリケーションファイルの構造

まず、SignalMonitor.mq5アプリケーションファイルを開き、インプットパラメータを追加します。 MetaTrader5ターミナルで直接アプリケーションを実行する際に、パラメータを設定することができます。 また、以前に作成したCProgramクラスのインスタンスを宣言し、変数の一部を初期化します。 ファイルを次のように編集します。

//+------------------------------------------------------------------+
//|                                                SignalMonitor.mq5 |
//|                                Copyright 2019, Alexander Fedosov |
//|                           https://www.mql5.com/en/users/alex2356 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, Alexander Fedosov"
#property link      "https://www.mql5.com/en/users/alex2356"
#property version   "1.00"
//--- Include application class
#include "Program.mqh"
//+------------------------------------------------------------------+
//| Expert Advisor input parameters                                  |
//+------------------------------------------------------------------+
input int                  Inp_BaseFont      =  10;                  // Basic font
input color                Caption           =  C'0,130,225';        // Caption color
input color                Background        =  clrWhiteSmoke;       // Background color
//---
CProgram program;
ulong tick_counter;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(void)
{
//---
   tick_counter=GetTickCount();
//--- Initialize class variables
   program.OnInitEvent();
   program.m_base_font_size=Inp_BaseFont;
   program.m_background=Background;
   program.m_caption=Caption;
//--- Set up the trading panel
   if(!program.CreateGUI())
   {
      Print(__FUNCTION__," > Failed to create graphical interface!");
      return(INIT_FAILED);
   }
//--- Initialization completed successfully
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   program.OnDeinitEvent(reason);
}
//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer(void)
{
   program.OnTimerEvent();
}
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int    id,
                  const long   &lparam,
                  const double &dparam,
                  const string &sparam)
{
   program.ChartEvent(id,lparam,dparam,sparam);
   //---
   if(id==CHARTEVENT_CUSTOM+ON_END_CREATE_GUI)
   {
      Print("End in ",GetTickCount()-tick_counter," ms");
   }
}
//+------------------------------------------------------------------+

 コードからわかるように、3つのインプットパラメータが追加されました:

  • フォント サイズ。
  • アプリケーション ウィンドウのヘッダの色。
  • アプリケーション ウィンドウと要素の背景色。

次に、CProgramクラス・インスタンスの名前付き program とtick_counter変数を宣言します (アプリケーションの起動時刻に情報を表示するためにのみ必要です)。 さらに、OnInit()メソッドでは、アプリケーションインプットパラメータの値を割り当てることによってクラス インスタンス変数を初期化します。 また、アプリを起動するCreateGUI()基本メソッドを呼び出します。

しかし、開いたファイルをコンパイルしようとすると、cProgramクラスで変数m_base_font_sizeが見つからないことを示すコンパイルエラーが表示され、m_backgroundm_caption CreateGUI() メソッドが見つからなかったことを示しています。 したがって、CProgramクラスを実装するには、Program.mqhファイルを開きます。 まず、上記の変数とメソッド、およびアプリケーションの正しい初期操作に必要な他のメソッドを追加します。 必要な要素を追加すると、CProgramは次のようになります。

//+------------------------------------------------------------------+
//| Class for creating an application                                |
//+------------------------------------------------------------------+
class CProgram : public CWndEvents
{
public:
//---
   int               m_base_font_size;
//---
   string            m_base_font;
//---
   color             m_background;
   color             m_caption;
public:
   CProgram(void);
   ~CProgram(void);
   //--- Initialization/deinitialization
   void              OnInitEvent(void);
   void              OnDeinitEvent(const int reason);
   //--- Timer
   void              OnTimerEvent(void);
   //--- Chart event handler
   virtual void      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- Create the graphical interface of the program
   bool              CreateGUI(void);
};

インターフェイスを作成するメソッドの実装はまだ空です。

//+------------------------------------------------------------------+
//| Creates the graphical interface of the program                   |
//+------------------------------------------------------------------+
bool CProgram::CreateGUI(void)
{
//---

//--- Finish the creation of GUI
   CWndEvents::CompletedGUI();
   return(true);
}
//+------------------------------------------------------------------+

また、アプリケーションのフォント名を担当する m_base_font 文字列変数も追加されています。 クラスコンストラクタで初期化されます。

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CProgram::CProgram(void)
{
   m_base_font="Trebuchet MS";
}

では、アプリケーションの最初のウィンドウの作成に進みましょう。 このためには、クラスでCWindowクラスのインスタンスである新しい m_step_window 変数を宣言します。 また、最初のウィンドウを作成するメソッドを宣言し、その名前を CreateStepWindow()にします。 クラスコードの中で、次のようになります。

class CProgram : public CWndEvents
{
public:
//--- Application windows
   CWindow           m_step_window;
...
protected:
   //--- forms
   bool              CreateStepWindow(const string caption_text);

先ほど、初期起動のひとつひとつの構成を担当するインタフェース部分の実装は、StepWindow.mqhインクルードファイルに配置されることを決定しました。 そこで、CreateStepWindow()のメソッドの実装を開始します。

#include "Program.mqh"
//+------------------------------------------------------------------+
//| Creates a form for the selection of symbols                      |
//+------------------------------------------------------------------+
bool CProgram::CreateStepWindow(const string text)
{
//--- Add the pointer to the window array
   CWndContainer::AddWindow(m_step_window);
//--- Properties
   m_step_window.XSize(600);
   m_step_window.YSize(200);
//--- Coordinates
   int x=int(ChartGetInteger(m_chart_id,CHART_WIDTH_IN_PIXELS)-m_step_window.XSize())/2;
   int y=10;
   m_step_window.CaptionHeight(22);
   m_step_window.IsMovable(true);
   m_step_window.CaptionColor(m_caption);
   m_step_window.CaptionColorLocked(m_caption);
   m_step_window.CaptionColorHover(m_caption);
   m_step_window.BackColor(m_background);
   m_step_window.FontSize(m_base_font_size);
   m_step_window.Font(m_base_font);
//--- Creating a form
   if(!m_step_window.CreateWindow(m_chart_id,m_subwin,text,x,y))
      return(false);
   //---
   return(true);
}
//+------------------------------------------------------------------+

CreateGUI() メソッドに以下を追加することを忘れないでください。

//+------------------------------------------------------------------+
//| Creates the graphical interface of the program                   |
//+------------------------------------------------------------------+
bool CProgram::CreateGUI(void)
{
//--- Step 1-3
   if(!CreateStepWindow("Signal Monitor Step 1: Choose Symbols"))
      return(false);
//--- Finish the creation of GUI
   CWndEvents::CompletedGUI();
   return(true);
}
//+------------------------------------------------------------------+

一連のアクションが正しい場合は、SignalMonitor.mq5ファイルをコンパイルしてターミナルで起動した後に、新しく作成されたフォームが表示されます。

図2 アプリケーションの最初のウィンドウ

作成されたウィンドウの最初の要素には、ターミナルから事前定義されたシンボルセットのクイック選択を可能にするボタンのグループが含まれています: forex.all, forex.crosses, forex.major. Program.mqh ファイルで、CButtonクラスのインスタンスの配列を 3 次元に追加し、ボタンを作成するための汎用CreateSymbolSet()メソッドを追加します。

//+------------------------------------------------------------------+
//| Class for creating an application                                |
//+------------------------------------------------------------------+
class CProgram : public CWndEvents
{
public:
//--- Application windows
   CWindow           m_step_window;
//--- Simple buttons
   CButton           m_currency_set[3];
...
   //--- Buttons
   bool              CreateSymbolSet(CButton &button,string text,const int x_gap,const int y_gap);

次に、StepWindow.mqhファイルを開き、上記のメソッド実装を追加します。

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateSymbolSet(CButton &button,string text,const int x_gap,const int y_gap)
{
//---
   color baseclr=C'220,225,235';
   color pressed=C'55,160,250';
//--- Save the window pointer
   button.MainPointer(m_step_window);
//--- Set properties before creation
   button.TwoState(true);
   button.XSize(80);
   button.YSize(30);
   button.LabelXGap(19);
   button.LabelYGap(2);
   button.Font(m_base_font);
   button.FontSize(m_base_font_size);
   button.BackColor(baseclr);
   button.BackColorHover(baseclr);
   button.BackColorPressed(pressed);
   button.BorderColor(baseclr);
   button.BorderColorHover(baseclr);
   button.BorderColorPressed(pressed);
   button.LabelColor(clrBlack);
   button.LabelColorPressed(clrWhite);
   button.IsCenterText(true);
//--- Create a control
   if(!button.CreateButton(text,x_gap,y_gap))
      return(false);
//--- Add a pointer to element to the base
   CWndContainer::AddToElementsArray(0,button);
   return(true);
}
//+------------------------------------------------------------------+

あとは、フォームを作成した後、ウィンドウの基本的なメソッドCreateStepWindow()で座標とテキストラベルの値が異なるこのメソッドを使用して、3 つのボタンを追加するだけです。

...
//--- Creating a form
   if(!m_step_window.CreateWindow(m_chart_id,m_subwin,text,x,y))
      return(false);
//---
   if(!CreateSymbolSet(m_currency_set[0],"ALL",10,30))
      return(false);
   if(!CreateSymbolSet(m_currency_set[1],"Major",10+100,30))
      return(false);
   if(!CreateSymbolSet(m_currency_set[2],"Crosses",10+2*(100),30))
      return(false);
...

コンパイル後の結果は次のようになります。

図3 シンボルグループの素早い選択のボタンを追加します。

次に、選択したシンボル グループの名前のインプットフィールドを追加します。 これを行うには、CTextEditインプットフィールドを作成するためのクラス インスタンスと、ボタンCButtonを作成するためのクラス インスタンスを 2 つ追加します。保存ボタンとロードボタンの名前は異なるため、汎用CreateButton1()メソッドを作成し、インプットフィールドにCreateEditValue()CProgramクラスに追加します。

//+------------------------------------------------------------------+
//| Class for creating an application                                |
//+------------------------------------------------------------------+
class CProgram : public CWndEvents
{
public:
//--- Application windows
   CWindow           m_step_window;
//--- Simple buttons
   CButton           m_currency_set[3];
   CButton           m_load_button;
   CButton           m_save_button;
   //--- Input fields
   CTextEdit         m_text_edit;
...
   bool              CreateButton1(CButton &button,string text,const int x_gap,const int y_gap);
   //--- Input field
   bool              CreateEditValue(CTextEdit &text_edit,const int x_gap,const int y_gap);

StepWindow.mqh ファイルに戻り、作成したメソッドの実装をファイルのトレーリングストップに追加します。

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateEditValue(CTextEdit &text_edit,const int x_gap,const int y_gap)
{
//--- Store the pointer to the main control
   text_edit.MainPointer(m_step_window);
//--- Properties
   text_edit.XSize(110);
   text_edit.YSize(24);
   text_edit.Font(m_base_font);
   text_edit.FontSize(m_base_font_size);
   text_edit.GetTextBoxPointer().XGap(1);
   text_edit.GetTextBoxPointer().XSize(110);
   text_edit.GetTextBoxPointer().DefaultTextColor(clrSilver);
   text_edit.GetTextBoxPointer().DefaultText("Template name");
//--- Create a control
   if(!text_edit.CreateTextEdit("",x_gap,y_gap))
      return(false);
//--- Add an object to the common array of object groups
   CWndContainer::AddToElementsArray(0,text_edit);
   return(true);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateButton1(CButton &button,string text,const int x_gap,const int y_gap)
{
//---
   color baseclr=C'70,180,70';
   color pressed=C'70,170,70';
//--- Save the window pointer
   button.MainPointer(m_step_window);
//--- Set properties before creation
   button.XSize(80);
   button.YSize(30);
   button.Font(m_base_font);
   button.FontSize(m_base_font_size);
   button.BackColor(baseclr);
   button.BackColorHover(baseclr);
   button.BackColorPressed(pressed);
   button.BorderColor(baseclr);
   button.BorderColorHover(baseclr);
   button.BorderColorPressed(pressed);
   button.LabelColor(clrWhite);
   button.LabelColorPressed(clrWhite);
   button.LabelColorHover(clrWhite);
   button.IsCenterText(true);
//--- Create a control
   if(!button.CreateButton(text,x_gap,y_gap))
      return(false);
//--- Add a pointer to element to the base
   CWndContainer::AddToElementsArray(0,button);
   return(true);
}

 次にCreateStepWindow()クラスに戻り、アプリケーションウィンドウに 2 つのボタンとインプットフィールドを追加します。

//---
   if(!CreateEditValue(m_text_edit,300,m_step_window.CaptionHeight()+10))
      return(false);
//---
   if(!CreateButton1(m_load_button,"Load(L)",m_step_window.XSize()-2*(80+10),m_step_window.CaptionHeight()+10))
      return(false);
   if(!CreateButton1(m_save_button,"Save(S)",m_step_window.XSize()-(80+10),m_step_window.CaptionHeight()+10))
      return(false);

再度、SignalMonitor.mq5ファイルをコンパイルします。 結果は次のとおりです。

図4 シンボルグループとSave/Load(保存/読み込み)ボタンのインプットフィールドを追加します。

さて、MetaTrader5ターミナルで選択したアカウントで利用可能なすべてのシンボルを可視化し、選択できるすべてのシンボルを選択する関数に進みましょう。 使用可能なすべてのシンボルを表示する場合、アプリケーション ウィンドウの高さが十分ではありません。 優れた解決策は、データに応じてウィンドウの高さを自動的に調整できるようにすることです。 シンボルの総数を追加する方法は似ていますが、チェックボックス CCheckBoxを作成するためのクラスインスタンスの配列を追加し、作成するユニバーサルメソッドを追加します(名前が異なるだけであるため)。

...
   //--- Checkboxes
   CCheckBox         m_checkbox[];
...
   //--- Checkboxes
   bool              CreateCheckBox(CCheckBox &checkbox,const int x_gap,const int y_gap,const string text);

m_checkbox[] 配列の次元は、ターミナル内の選択したアカウントに存在するシンボルの数が事前にわからないため、指定されていません。 そこで、CProgramクラスのプライベート セクションに 2 つの変数を作成し、使用可能なシンボルの総数と、Market Watch で現在選択されているシンボルの数を割り当てます。

private:
//---
   int               m_symbol_total;
   int               m_all_symbols;

クラスコンストラクタで、必要な値を割り当て、適切な次元をm_checkbox[]配列に設定します。

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CProgram::CProgram(void)
{
   m_base_font="Trebuchet MS";
   m_symbol_total=SymbolsTotal(true);
   m_all_symbols=SymbolsTotal(false);
   ArrayResize(m_checkbox,m_all_symbols);
}

次のメソッド実装を StepWindow.mqh ファイルのトレーリングストップに追加します。

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateCheckBox(CCheckBox &checkbox,const int x_gap,const int y_gap,const string text)
{
//--- Store the pointer to the main control
   checkbox.MainPointer(m_step_window);
//--- Properties
   checkbox.GreenCheckBox(true);
   checkbox.IsPressed(false);
   checkbox.Font(m_base_font);
   checkbox.FontSize(m_base_font_size);
   checkbox.BackColor(m_background);
   checkbox.LabelColorHover(C'55,160,250');
//--- Create a control
   if(!checkbox.CreateCheckBox(text,x_gap,y_gap))
      return(false);
//--- Add a pointer to element to the base
   CWndContainer::AddToElementsArray(0,checkbox);
   return(true);
}

CreateStepWindow() にチェック ボックスを追加します。 以下のコードでは、使用可能なシンボルのリスト全体に 7 つの列があります。 さらに、ウィンドウの高さは、受信した行の数に応じて変更されます。

   //--- Checkboxes
   int k=0;
   for(int j=0; j<=MathCeil(m_all_symbols/7); j++)
   {
      for(int i=0; i<7; i++)
      {
         if(k<m_all_symbols)
            if(!CreateCheckBox(m_checkbox[k],10+80*i,m_step_window.CaptionHeight()+70+j*25,SymbolName(k,false)))
               return(false);
         k++;
      }
   }
   m_step_window.ChangeWindowHeight(m_checkbox[m_all_symbols-1].YGap()+30+30);

結果として得られた追加をコピーします。

図5 利用可能なすべてのシンボルを含むチェックボックスを追加します。

このアプリケーションパーツの直近の要素には、セットアップステップを切り替えるナビゲーションボタンがあります。 クラスは追加できます: m_next_buttonm_back_buttonという名前の CButton クラスの 2 つのインスタンスを追加し、前に作成したCreateButton1()の作成メソッドを使用します。 CreateStepWindow() メソッドに次の項目を追加します。

//---
   if(!CreateButton1(m_back_button,"Back",m_step_window.XSize()-2*(80+10),m_step_window.YSize()-(30+10)))
      return(false);
   if(!CreateButton1(m_next_button,"Next",m_step_window.XSize()-(80+10),m_step_window.YSize()-(30+10)))
      return(false);

ここで、定義済みのシンボルセットを選択できるボタンの操作を設定するだけです。 Program.mqh ファイルに移動し、OnEvent()を見つけて、次のコードを追加します。

//+------------------------------------------------------------------+
//| Chart event handler                                              |
//+------------------------------------------------------------------+
void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
{
//--- Pressing the button event
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON)
   {
      //--- All
      if(lparam==m_currency_set[0].Id() && m_currency_set[0].IsPressed())
      {
         m_currency_set[1].IsPressed(false);
         m_currency_set[2].IsPressed(false);
         m_currency_set[1].Update(true);
         m_currency_set[2].Update(true);
         //---
         for(int i=0; i<m_all_symbols; i++)
         {
            m_checkbox[i].IsPressed(true);
            m_checkbox[i].Update(true);
         }
      }
      //--- Majors
      else if(lparam==m_currency_set[1].Id() && m_currency_set[1].IsPressed())
      {
         m_currency_set[0].IsPressed(false);
         m_currency_set[2].IsPressed(false);
         m_currency_set[0].Update(true);
         m_currency_set[2].Update(true);
         //---
         string pairs[4]= {"EURUSD","GBPUSD","USDCHF","USDJPY"};
         //--- Clear the selection
         for(int i=0; i<m_all_symbols; i++)
         {
            m_checkbox[i].IsPressed(false);
            m_checkbox[i].Update(true);
         }
         //---
         for(int i=0; i<m_all_symbols; i++)
         {
            for(int j=0; j<4; j++)
               if(m_checkbox[i].LabelText()==pairs[j])
               {
                  m_checkbox[i].IsPressed(true);
                  m_checkbox[i].Update(true);
               }
         }
      }
      //--- Crosses
      else if(lparam==m_currency_set[2].Id() && m_currency_set[2].IsPressed())
      {
         m_currency_set[0].IsPressed(false);
         m_currency_set[1].IsPressed(false);
         m_currency_set[0].Update(true);
         m_currency_set[1].Update(true);
         //---
         string pairs[20]=
         {
            "EURUSD","GBPUSD","USDCHF","USDJPY","USDCAD","AUDUSD","AUDNZD","AUDCAD","AUDCHF","AUDJPY",
            "CHFJPY","EURGBP","EURAUD","EURCHF","EURJPY","EURCAD","EURNZD","GBPCHF","GBPJPY","CADCHF"
         };
         //--- Clear the selection
         for(int i=0; i<m_all_symbols; i++)
         {
            m_checkbox[i].IsPressed(false);
            m_checkbox[i].Update(true);
         }
         //---
         for(int i=0; i<m_all_symbols; i++)
         {
            for(int j=0; j<20; j++)
               if(m_checkbox[i].LabelText()==pairs[j])
               {
                  m_checkbox[i].IsPressed(true);
                  m_checkbox[i].Update(true);
               }
         }
      }
      //---
      if((lparam==m_currency_set[0].Id() && !m_currency_set[0].IsPressed())      ||
            (lparam==m_currency_set[1].Id() && !m_currency_set[1].IsPressed())   ||
            (lparam==m_currency_set[2].Id() && !m_currency_set[2].IsPressed())
        )
      {
         //--- Clear the selection
         for(int i=0; i<m_all_symbols; i++)
         {
            m_checkbox[i].IsPressed(false);
            m_checkbox[i].Update(true);
         }
      }
   }
}

この実装の考え方は次のとおりです。

  • ALL をクリックすると、すべてのシンボルが選択されます。
  • メジャー(Major)をクリックすると、以前の選択が削除され、ターミナルの forex.major に対応するシンボルのセットが設定されます。
  • クロス(Cross)をクリックすると、前の選択が削除され、端子のcrosses.majorに対応するシンボルのセットが設定されます。
  • 3 つのボタンがすべて押されていない場合、選択は取り消されます。
<d

次のようになります。

図6 要素の基本的な相互作用の実装

視覚的な実装を完了するには、2 つの小さな追加が必要です。 図5 では、ウィンドウに以前に作成した [戻る] (Back)ボタンが含まれていることがわかります。 しかし、これはステップ1なので、そのようなボタンは存在しないはずです。 非表示にし、ステップ 2 と ステップ3 にのみ表示されます。 CreateGUI() メソッドに次の行を追加します。

bool CProgram::CreateGUI(void)
{
//--- Step 1-3
   if(!CreateStepWindow("Signal Monitor Step 1: Choose Symbols"))
      return(false);
//--- Finish the creation of GUI
   CWndEvents::CompletedGUI();
   m_back_button.Hide();
   return(true);
}

また、ユーザーの選択を監視する必要があります。 ユーザーが少なくとも 1 つのシンボルを選択していない場合は、ステップ 2 に切り替えできません。 ステップ間の切り替えは、「戻る」および「次へ」ボタンを使用して行います。 そのため、このタスクを解決するには、CProgram クラスのプライベート セクションに 3 つの新しいメソッドを追加します。 このメソッドは、3 つの各ステップで選択された情報を処理し、アプリケーションの初期設定を実行します。 また、m_current_step変数を追加します:Back / Nextをクリックすると、アプリは現在のステップになります。

private:
//---
   int               m_symbol_total;
   int               m_all_symbols;
   int               m_current_step;
   //---
   void              ToStep_1(void);
   void              ToStep_2(void);
   void              ToStep_3(void);

その後、クラスコンストラクタで、作成された変数の最初のステップの値、すなわち1を設定します。 3 つの構成ステップ間のナビゲーションを設定するには、OnEvent() のボタンクリックイベントに以下のコードを追加します。

      //--- Navigation
      if(lparam==m_back_button.Id())
      {
         //--- Return to Step 1
         if(m_current_step==2)
            ToStep_1();
         //--- Return to Step 2
         else if(m_current_step==3)
            ToStep_2();
      }
      //--- Go to Step 2
      if(lparam==m_next_button.Id())
      {
         //--- Go to Step 2
         if(m_current_step==1)
            ToStep_2();
         //--- Go to Step 3
         else if(m_current_step==2)
            ToStep_3();
      }

このステップでプロジェクトをコンパイルしようとすると、コンパイラは、3 つのメソッドが作成され、使用されているが実装が含まれていないというエラーを返します。 

function 'CProgram::ToStep_1' must have a body Program.mqh 60 22

この問題を修正するには、クラスの実装を Program.mqh ファイルに作成します。 ただし、ToStep_1()メソッドとToStep_3()メソッドについては、ここでは空白のままにします。 これは後で埋められます。 次に、メソッドが第2ステップToStep_2()に切り替えることに興味があります。 少なくとも 1 つのシンボルが選択されているかどうかを確認します。

//+------------------------------------------------------------------+
//| Go to Step 1                                                     |
//+------------------------------------------------------------------+
void CProgram::ToStep_1(void)
{
//---
}
//+------------------------------------------------------------------+
//| Go to Step 2                                                 |
//+------------------------------------------------------------------+
void CProgram::ToStep_2(void)
{
//--- Check whether at least one symbol is selected
   int cnt=0;
   for(int i=0; i<m_all_symbols; i++)
   {
      if(m_checkbox[i].IsPressed())
         cnt++;
   }
   if(cnt<1)
   {
      MessageBox("No symbols selected!","Warning");
      return;
   }
}
//+------------------------------------------------------------------+
//| Move to Step 3 3                                                 |
//+------------------------------------------------------------------+
void CProgram::ToStep_3(void)
{
//---
}

ユーザーが誤ってシンボルを選択せずに [次へ] を押した場合は、少なくとも 1 つのシンボルを選択する必要があることを示すアラートが表示されます。


セットアップステップ 2: 時間枠

2番目のアプリケーションの設定ステップでは、ユーザーは、トレードシグナルが検索される時間枠を選択する必要があります。 最初の記事では、必要な UI 要素について説明しました。

  • 時間枠の迅速な選択のボタンのグループ。
  • チェックボックスの形での時間枠のリスト。
  • ステップ 1 に戻るための [戻る] ボタン。

ステップ 1 のビジュアル実装から既存のオブジェクトを使用し、時間枠の選択に合わせて調整してみましょう。 最近編集した ToStep_2() メソッドの本文に移動し、追加関数を追加します。 まず、ステップ1でシンボルの選択を覚えて、MetaTrader5のマーケットウォッチで表示します。

//--- Set selected symbols in Market Watch
   for(int i=0; i<m_all_symbols; i++)
   {
      if(m_checkbox[i].IsPressed())
         SymbolSelect(m_checkbox[i].LabelText(),true);
      else
         SymbolSelect(m_checkbox[i].LabelText(),false);
   }

次に、ステップ 1 のインターフェイスを 2 番目のインターフェイスに変換します。

//--- Change header
   m_step_window.LabelText("Signal Monitor Step 2: Choose Timeframes");
   m_step_window.Update(true);
//--- Hide elements of Step 1
   for(int i=0; i<m_all_symbols; i++)
   {
      m_checkbox[i].IsLocked(false);
      m_checkbox[i].IsPressed(false);
      m_checkbox[i].Hide();
   }
   string names[3]= {"All","Junior","Senior"};
//--- Change names of selection buttons
   for(int i=0; i<3; i++)
   {
      m_currency_set[i].LabelText(names[i]);
      m_currency_set[i].IsPressed(false);
      if(m_current_step==3)
         m_currency_set[i].Show();
      m_currency_set[i].Update(true);
   }
//--- Hide block for working with templates
   m_text_edit.Hide();
   m_load_button.Hide();
   m_save_button.Hide();
//--- Show all timeframes
   string timeframe_names[21]=
   {
      "M1","M2","M3","M4","M5","M6","M10","M12","M15","M20","M30",
      "H1","H2","H3","H4","H6","H8","H12","D1","W1","MN"
   };
   for(int i=0; i<21; i++)
   {
      m_checkbox[i].LabelText(timeframe_names[i]);
      m_checkbox[i].Show();
      m_checkbox[i].Update(true);
   }
//--- Show Back button
   m_back_button.Show();
//---
   m_current_step=2;

この実装は簡単です。 最後に、値 2 を m_current_step 変数に割り当てます (ステップ 2)。 さて、変更されたインターフェイスによってすべて、ジュニア、シニア、選択した時間枠セットの正しい表示を提供する必要があります。 Program.mqh を開き、OnEvent()メソッドのコードを修正します。 "ボタンクリック"イベントセクションで変更が必要です。 オブジェクトの観点から見ると、ステップ 1 と 2 のクイック選択ボタンは似ています。 そのため、ボタンクリックイベントで現在の設定ステップを定義する必要があります。

 //--- Step 1
      if(m_current_step==1)
      {
       ...
      }
      //--- Step 2
      else if(m_current_step==2)
      {
         //--- All
         if(lparam==m_currency_set[0].Id() && m_currency_set[0].IsPressed())
         {
            m_currency_set[1].IsPressed(false);
            m_currency_set[2].IsPressed(false);
            m_currency_set[1].Update(true);
            m_currency_set[2].Update(true);
            //---
            for(int i=0; i<21; i++)
            {
               m_checkbox[i].IsPressed(true);
               m_checkbox[i].Update(true);
            }
         }
         //--- Junior Timeframes
         else if(lparam==m_currency_set[1].Id() && m_currency_set[1].IsPressed())
         {
            m_currency_set[0].IsPressed(false);
            m_currency_set[2].IsPressed(false);
            m_currency_set[0].Update(true);
            m_currency_set[2].Update(true);
            //---
            string pairs[11]=
            {
               "M1","M2","M3","M4","M5","M6","M10","M12","M15","M20","M30"
            };
            //--- Clear the selection
            for(int i=0; i<21; i++)
            {
               m_checkbox[i].IsPressed(false);
               m_checkbox[i].Update(true);
            }
            //---
            for(int i=0; i<21; i++)
            {
               for(int j=0; j<11; j++)
                  if(m_checkbox[i].LabelText()==pairs[j])
                  {
                     m_checkbox[i].IsPressed(true);
                     m_checkbox[i].Update(true);
                  }
            }
         }
         //--- Senior Timeframes
         else if(lparam==m_currency_set[2].Id() && m_currency_set[2].IsPressed())
         {
            m_currency_set[0].IsPressed(false);
            m_currency_set[1].IsPressed(false);
            m_currency_set[0].Update(true);
            m_currency_set[1].Update(true);
            //---
            string pairs[10]=
            {
               "H1","H2","H3","H4","H6","H8","H12","D1","W1","MN"
            };
            //--- Clear the selection
            for(int i=0; i<m_all_symbols; i++)
            {
               m_checkbox[i].IsPressed(false);
               m_checkbox[i].Update(true);
            }
            //---
            for(int i=0; i<m_all_symbols; i++)
            {
               for(int j=0; j<10; j++)
                  if(m_checkbox[i].LabelText()==pairs[j])
                  {
                     m_checkbox[i].IsPressed(true);
                     m_checkbox[i].Update(true);
                  }
            }
         }

2 番目のセットアップステップ内で最後に実装される UI 要素は、ステップ 1 に戻るボタンです。 作成されたが空のステップ ToStep_1() によって行われます。 前のインターフェイスを返し、選択ボタンのクリック イベントを処理するための前のラッパを設定します。

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CProgram::ToStep_1(void)
{
//--- Change header
   m_step_window.LabelText("Signal Monitor Step 1: Choose Symbols");
   m_step_window.Update(true);
//--- Hide the Back button
   m_back_button.Hide();
//--- Clear selection
   for(int i=0; i<21; i++)
   {
      m_checkbox[i].IsPressed(false);
      m_checkbox[i].Update(true);
   }
//--- Show elements of Step 1
   for(int i=0; i<m_all_symbols; i++)
   {
      m_checkbox[i].Show();
      m_checkbox[i].LabelText(SymbolName(i,false));
      m_checkbox[i].Update(true);
   }
   string names[3]= {"All","Majors","Crosses"};
//--- Change names of selection buttons
   for(int i=0; i<3; i++)
   {
      m_currency_set[i].IsPressed(false);
      m_currency_set[i].LabelText(names[i]);
      m_currency_set[i].Update(true);
   }
//--- Show block for working with templates
   m_text_edit.Show();
   m_load_button.Show();
   m_save_button.Show();
//--- Set the current setup step
   m_current_step=1;
}

プロジェクトをコンパイルします。 すべてが正しく追加されれば、結果は図7のようになります。

図7 アプリケーション設定のステップ 2 の実装

セットアップステップ 3: シグナルを追加する

次の段階はステップ 3: インターフェイスを追加するシグナルです。 これは簡単で、追加されたシグナルのリストのボタンとヘッダを追加するシグナルで構成されています。 Program.mqh を開き、2 つの新しい変数をСProgramで宣言します。

   CButton           m_add_signal;
   //---
   CTextLabel        m_signal_header;

変数を実装するメソッド:

   bool              CreateIconButton(CButton &button,string text,const int x_gap,const int y_gap);
   //--- Text label
   bool              CreateLabel(CTextLabel &text_label,const int x_gap,const int y_gap,string label_text);

その実装を StepWindow.mqh ファイルのトレーリングストップに追加します。

//+------------------------------------------------------------------+
//| Creates a button with an image                                   |
//+------------------------------------------------------------------+
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\plus.bmp"
bool CProgram::CreateIconButton(CButton &button,string text,const int x_gap,const int y_gap)
{
//---
   color baseclr=C'70,180,70';
   color pressed=C'70,170,70';
//--- Save the window pointer
   button.MainPointer(m_step_window);
//--- Set properties before creation
   button.XSize(110);
   button.YSize(30);
   button.Font(m_base_font);
   button.FontSize(m_base_font_size);
   button.IconXGap(3);
   button.IconYGap(7);
   button.IconFile("Images\\EasyAndFastGUI\\Icons\\bmp16\\plus.bmp");
   button.BackColor(baseclr);
   button.BackColorHover(baseclr);
   button.BackColorPressed(pressed);
   button.BorderColor(baseclr);
   button.BorderColorHover(baseclr);
   button.BorderColorPressed(pressed);
   button.LabelColor(clrWhite);
   button.LabelColorPressed(clrWhite);
   button.LabelColorHover(clrWhite);
   button.IsCenterText(true);
//--- Create a control
   if(!button.CreateButton(text,x_gap,y_gap))
      return(false);
//--- Add a pointer to element to the base
   CWndContainer::AddToElementsArray(0,button);
   return(true);
}
//+------------------------------------------------------------------+
//| Creates the text label                                           |
//+------------------------------------------------------------------+
bool CProgram::CreateLabel(CTextLabel &text_label,const int x_gap,const int y_gap,string label_text)
{
//--- Save the window pointer
   text_label.MainPointer(m_step_window);
//---
   text_label.Font(m_base_font);
   text_label.FontSize(m_base_font_size);
   text_label.XSize(120);
   text_label.BackColor(m_background);
//--- Create the button
   if(!text_label.CreateTextLabel(label_text,x_gap,y_gap))
      return(false);
//--- Add a pointer to element to the base
   CWndContainer::AddToElementsArray(0,text_label);
   return(true);
}
//+------------------------------------------------------------------+

アプリケーション起動時に作成されるように、次のコードをCreateStepWindow()ウィンドウに追加します。

//---
   if(!CreateIconButton(m_add_signal,"Add Signal",10,30))
      return(false);
   if(!CreateLabel(m_signal_header,10,30+30+10,"Signal List"))
      return(false);   

さて、起動時に表示を無効にするには、即ち最初のステップで、CreateGUI()の呼び出しによってインターフェイス作成直後に、メソッド本体の最後に要素を隠す2行を追加します。

//+------------------------------------------------------------------+
//| Creates the graphical interface of the program                   |
//+------------------------------------------------------------------+
bool CProgram::CreateGUI(void)
{
//--- Step 1-3
   if(!CreateStepWindow("Signal Monitor Step 1: Choose Symbols"))
      return(false);
//--- Finish the creation of GUI
   CWndEvents::CompletedGUI();
   m_back_button.Hide();
   m_add_signal.Hide();
   m_signal_header.Hide();
   return(true);
}

次に、前のステップで視覚化をクリアし、作成した要素を表示する、以前に追加された ToStep_3() メソッドを実装します。

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CProgram::ToStep_3(void)
{
//--- Check whether at least one timeframe is selected
   int cnt=0;
   for(int i=0; i<21; i++)
   {
      if(m_checkbox[i].IsPressed())
         cnt++;
   }
   if(cnt<1)
   {
      MessageBox("No timeframes selected!","Warning");
      return;
   }
//---
   m_step_window.LabelText("Signal Monitor Step 3: Create Signals");
   m_step_window.Update(true);
   m_next_button.LabelText("Create");
   m_next_button.Update(true);
//--- Hide elements of Step 2
   for(int i=0; i<21; i++)
   {
      if(i<3)
         m_currency_set[i].Hide();
      m_checkbox[i].Hide();
   }
//---
   m_add_signal.Show();
   m_signal_header.Show();
//---
   m_current_step=3;
}

もう一度プロジェクトをコンパイルし、次のボタンをダブルクリックしてステップ3に進みます。 最初の2つのステップで要素を選択することを忘れないでください、そうしなければアプリケーションは3番目のステップに行きません。

図8 アプリケーション設定のステップ3の実装

トレーディングシグナル作成と編集ウィンドウ

トレーディングシグナルの処理に関連するビジュアルコンポーネントはSetWindow.mqhファイルに配置されるため、開きます。 #includeコマンドラインを介して接続されたインクルードファイルProgram.mqhしかありません。 まず、他のすべての作成要素とセットアップ要素の基本となる別のウィンドウを作成します。 Program.mqhを開き、CWindowクラスのインスタンスであるm_set_window変数をクラスで宣言します。 また、ウィンドウを作成するためのCreateSetWindow()メソッドを追加します。

   CWindow           m_set_window;

   bool              CreateSetWindow(const string caption_text);

その後、SetWindow.mqhに戻り、作成したメソッドを実装します。

//+------------------------------------------------------------------+
//| Creates a window for creating and editing trading signals        |
//+------------------------------------------------------------------+
bool CProgram::CreateSetWindow(const string text)
{
//--- Add the pointer to the window array
   CWndContainer::AddWindow(m_set_window);
//--- Properties
   m_set_window.XSize(568);
   m_set_window.YSize(555);
//--- Coordinates
   int x=int(ChartGetInteger(m_chart_id,CHART_WIDTH_IN_PIXELS)-m_set_window.XSize())/2;
   int y=30;
//---
   m_set_window.CaptionHeight(22);
   m_set_window.IsMovable(true);
   m_set_window.CaptionColor(m_caption);
   m_set_window.CaptionColorLocked(m_caption);
   m_set_window.CaptionColorHover(m_caption);
   m_set_window.BackColor(m_background);
   m_set_window.FontSize(m_base_font_size);
   m_set_window.Font(m_base_font);
   m_set_window.WindowType(W_DIALOG);
//--- Creating a form
   if(!m_set_window.CreateWindow(m_chart_id,m_subwin,text,x,y))
      return(false);
   return(true);
}
//+------------------------------------------------------------------+

次に、新しく作成したウィンドウを既に使用可能な要素にバインドしましょう。 まず、CreateGUI()インターフェイスの作成でメソッド呼び出しを追加します。 このウィンドウは、ステップ 3 の "Add Signal" ボタンをクリックすると開きます。

//+------------------------------------------------------------------+
//| Creates the graphical interface of the program                   |
//+------------------------------------------------------------------+
bool CProgram::CreateGUI(void)
{
//--- Step 1-3
   if(!CreateStepWindow("Signal Monitor Step 1: Choose Symbols"))
      return(false);
//--- Creation and editing window
   if(!CreateSetWindow("Signal Monitor Edit Signal"))
      return(false);
//--- Finish the creation of GUI
   CWndEvents::CompletedGUI();
   m_back_button.Hide();
   m_add_signal.Hide();
   m_signal_header.Hide();
   return(true);
}

OnEvent()でのクリックイベント:

      //--- Click on the "Add Signal" button
      if(lparam==m_add_signal.Id())
      {
         m_set_window.OpenWindow();
      }

プロジェクトをコンパイルし、結果を確認します: ステップ 3 で "Add Signal" の時刻に追加の作成と編集ウィンドウが開きます。

図9 トレードシグナル作成・編集ウィンドウの実装

最初のウィンドウ要素は、トレードシグナルの生成に使用するインジケータのタイプの選択です。 要素追加プロシージャは同じです: クラス インスタンスを作成し、インスタンスを実装するメソッドを作成します。

   //--- Drop-down menu
   CComboBox         m_indicator_type;
   //--- Creates a drop-down method
   bool              CreateIndicatorType(const int x_gap,const int y_gap);

このメソッドの実装は、以前に作成したウィンドウが作成されたのと同じファイルに配置されます。 

//+------------------------------------------------------------------+
//| Creates a drop-down menu with indicator types                    |
//+------------------------------------------------------------------+
bool CProgram::CreateIndicatorType(const int x_gap,const int y_gap)
{
//--- Pass the object to the panel
   m_indicator_type.MainPointer(m_set_window);
//--- Array of the item values in the list view
   string pattern_names[7]=
   {
      "ATR","CCI","DeMarker","Force Ind","WPR","RSI","Momentum"
   };
//--- Set properties before creation
   m_indicator_type.XSize(200);
   m_indicator_type.YSize(26);
   m_indicator_type.LabelYGap(4);
   m_indicator_type.ItemsTotal(7);
   m_indicator_type.Font(m_base_font);
   m_indicator_type.FontSize(m_base_font_size);
   m_indicator_type.BackColor(m_background);
   m_indicator_type.GetButtonPointer().Font(m_base_font);
   m_indicator_type.GetButtonPointer().FontSize(m_base_font_size);
   m_indicator_type.GetButtonPointer().BackColor(clrWhite);
   m_indicator_type.GetButtonPointer().XGap(100);
   m_indicator_type.GetButtonPointer().XSize(100);
   m_indicator_type.GetListViewPointer().Font(m_base_font);
   m_indicator_type.GetListViewPointer().FontSize(m_base_font_size);
   m_indicator_type.GetListViewPointer().ItemYSize(26);
//--- Store the item values in the combo box list view
   for(int i=0; i<7; i++)
      m_indicator_type.SetValue(i,pattern_names[i]);
//--- Get the list pointer
   CListView *lv=m_indicator_type.GetListViewPointer();
//--- Set the list view properties
   lv.LightsHover(true);
   m_indicator_type.SelectItem(5);
//--- Create a control
   if(!m_indicator_type.CreateComboBox("Indicator Type",x_gap,y_gap))
      return(false);
//--- Add an object to the common array of object groups
   CWndContainer::AddToElementsArray(1,m_indicator_type);
   return(true);
}

ここでの唯一の追加は、CreateSetWindow()メソッド本体の最後に、インジケータ型選択オプションCreateIndicatorType()を作成するためのメソッドを呼び出すということです。

...
//--- Creating a form
   if(!m_set_window.CreateWindow(m_chart_id,m_subwin,text,x,y))
      return(false);
//--- Indicator type
   if(!CreateIndicatorType(10,22+10))
      return(false);

この結果は、オシレータタイプの7つの標準インジケータから選択できるUI要素です。

図10 インジケータタイプを選択するための要素。

次に、インジケータ設定とシグナル設定の 2 つのセクションにグループ化された要素のセットを考えてみましょう。 標準セットから選択されたすべてのインジケータには、期間や適用価格などの共通設定があります。 したがって、最初のセクションには、テキストラベル、期間インプットフィールド、およびインジケータ計算に使用する価格を選択するためのドロップダウンメニューが必要です。 必要な変数と作成メソッドを CProgram クラスに追加します。

//--- Text label
   CTextLabel        m_set_header[5];
//--- Input fields
   CTextEdit         m_period_edit;
//--- Drop-down menu
   CComboBox         m_applied_price;
...
   bool              CreateSetLabel(CTextLabel &text_label,const int x_gap,const int y_gap,string label_text);
   bool              CreatePeriodEdit(const int x_gap,const int y_gap);
   bool              CreateAppliedPrice(const int x_gap,const int y_gap);

追加されたメソッドを実装し、呼び出す、 CreateSetWindow()メソッドの本体のトレーリングストップです。 次に、作成された要素が選択したインジケータの種類に応じて使用可能な設定のセットを変更するメカニズムを追加してみましょう。 これを行うには、ドロップダウンメニュー項目をクリックするイベントを含むセクションをOnEvent()に追加し、各インジケータの設定の個々のセットを設定します。

//--- Item selection in the combobox list
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_COMBOBOX_ITEM)
   {
      int index=m_indicator_type.GetListViewPointer().SelectedItemIndex();
      switch(index)
      {
      case  0:
         m_period_edit.LabelText("ATR Period");
         m_applied_price.Hide();
         break;
      case  1:
         m_period_edit.LabelText("CCI Period");
         m_applied_price.Show();
         break;
      case  2:
         m_period_edit.LabelText("DeMarker Period");
         m_applied_price.Hide();
         break;
      case  3:
         m_period_edit.LabelText("Force Index Period");
         m_applied_price.Show();
         break;
      case  4:
         m_period_edit.LabelText("WPR Period");
         m_applied_price.Hide();
         break;
      case  5:
         m_period_edit.LabelText("RSI Period");
         m_applied_price.Show();
         break;
      case  6:
         m_period_edit.LabelText("Momentum Period");
         m_applied_price.Hide();
         break;
      default:
         m_period_edit.LabelText("RSI Period");
         m_applied_price.Hide();
         break;
      }
      m_period_edit.Update(true);
   }

プロジェクトをコンパイルし、結果を表示します。

図11 インジケータ設定の実装

次に、シグナル編集の2番目のセクションに進みます。 ヘッダと 8 つの設定で構成されます。

  • シグナルルール。
  • シグナル ブロック内のテキスト ラベル値。
  • テキスト ラベルの色。
  • 背景と色。
  • 縁取り法と色。
  • シグナルブロックの上のツールチップ、色、値。
  • グラフィックラベルの使用と、シグナルブロックでの外観。
  • 特定のシグナルを検索するために利用可能な時間枠の選択。

このセクションのヘッダを追加するには、CreateSetWindow()本体の最後に次のコードを追加します (ヘッダを視覚化するメソッドを以前作成しました。

//--- Signal settings
   if(!CreateSetLabel(m_set_header[1],10,22+10+4*(25+10),"2.Signal Settings"))
      return(false);

シグナルルールは、ドロップダウンメニューと数値インプットフィールドの2つの要素で構成されます。 クラスインスタンスと実装メソッドを CProgram クラスに追加します。

CTextEdit         m_rule_value;
CComboBox         m_rule_type;
...
bool              CreateRuleValue(const int x_gap,const int y_gap);
bool              CreateRule(const int x_gap,const int y_gap);

その実装を SetWindow.mqh に追加します。CreateSetWindow()メソッドの本体です。

   //--- Condition settings
   if(!CreateRuleValue(130,22+10+5*(25+10)))
      return(false);
   if(!CreateRule(10,22+10+5*(25+10)))
      return(false);

また、各設定を同じ方法で追加します。 CreateSetWindow()メソッドの完全な実装は次のようになります。

//+------------------------------------------------------------------+
//| Creates a window for creating and editing trading signals        |
//+------------------------------------------------------------------+
bool CProgram::CreateSetWindow(const string text)
{
//--- Add the pointer to the window array
   CWndContainer::AddWindow(m_set_window);
//--- Properties
   m_set_window.XSize(568);
   m_set_window.YSize(575);
//--- Coordinates
   int x=int(ChartGetInteger(m_chart_id,CHART_WIDTH_IN_PIXELS)-m_set_window.XSize())/2;
   int y=30;
//---
   m_set_window.CaptionHeight(22);
   m_set_window.IsMovable(true);
   m_set_window.CaptionColor(m_caption);
   m_set_window.CaptionColorLocked(m_caption);
   m_set_window.CaptionColorHover(m_caption);
   m_set_window.BackColor(m_background);
   m_set_window.FontSize(m_base_font_size);
   m_set_window.Font(m_base_font);
   m_set_window.WindowType(W_DIALOG);
//--- Creating a form
   if(!m_set_window.CreateWindow(m_chart_id,m_subwin,text,x,y))
      return(false);
//--- Indicator type
   if(!CreateIndicatorType(10,22+10))
      return(false);
//--- Settings of the selected indicator
   if(!CreateSetLabel(m_set_header[0],10,22+10+26+10,"1.Indicator Settings"))
      return(false);
   if(!CreatePeriodEdit(10,22+10+2*(25+10)))
      return(false);
   if(!CreateAppliedPrice(10,22+10+3*(25+10)))
      return(false);
//--- Signal settings
   if(!CreateSetLabel(m_set_header[1],10,22+10+4*(25+10),"2.Signal Settings"))
      return(false);
//--- Condition settings
   if(!CreateRuleValue(130,22+10+5*(25+10)))
      return(false);
   if(!CreateRule(10,22+10+5*(25+10)))
      return(false);
//--- Label display settings
   if(!CreateSetLabel(m_set_header[2],10,22+10+6*(25+10),"Label"))
      return(false);
   if(!CreateButton2(m_label_button[0],"Value",100,22+7+6*(25+10)))
      return(false);
   if(!CreateButton2(m_label_button[1],"Text",100+80,22+7+6*(25+10)))
      return(false);
//--- Label color display settings
   if(!CreateColorButton(m_color_button[0],10,22+10+7*(25+10),"Label Color"))
      return(false);
   if(!CreateTextBox(180+80+10,22+7+6*(25+10)))
      return(false);
//---
   if(!CreateColorButton(m_color_button[1],25,22+10+8*(25+10),""))
      return(false);
   if(!CreateSetCheckBox(m_set_param[0],10,22+10+8*(25+10),"Use Background"))
      return(false);
   if(!CreateColorButton(m_color_button[2],25,22+10+9*(25+10),""))
      return(false);
   if(!CreateSetCheckBox(m_set_param[1],10,22+10+9*(25+10),"Use Border"))
      return(false);
   if(!CreateColorButton(m_color_button[3],25,22+10+10*(25+10),""))
      return(false);
   if(!CreateSetCheckBox(m_set_param[2],10,22+10+10*(25+10),"Use Tooltip"))
      return(false);
   if(!CreateTooltipText(240,22+10+10*(25+10)))
      return(false);
   if(!CreateSetCheckBox(m_set_param[3],10,22+10+11*(25+10),"Use Image"))
      return(false);
   if(!CreateImageSlider(125,22+10+11*(25+10)))
      return(false);
//--- Timeframe selection
   if(!CreateSetLabel(m_set_header[4],10,22+10+12*(25+10),"Timeframes"))
      return(false);
//---
   y=22+10+13*(25+10);
   int k=0;
   for(int i=0; i<21; i++)
   {
      if(i==11)
      {
         y=22+20+14*(25+10);
         k=0;
      }
      if(!CreateTfButton(m_tf_button[i],40*k+10,y))
         return(false);
      k++;
   }
   return(true);
}

追加の完全なリストとその実装は、以下の添付ファイルで入手できます。 必要なすべてのパーツを追加すると、作成および編集ウィンドウは次のようになります。

図12 シグナル編集ウィンドウのUI要素の実装

図のように、時間枠の選択ボタンは空です。 また、要素の基本的な相互作用を設定する必要があります。

  • 時間枠のボタンには、ステップ 2 で選択した番号のみが表示されます。
  • [値] ボタンを選択すると、[テキスト] ボタンが押されず、テキスト ラベルインプットフィールドが非表示になります。
  • カラー選択ボタンをクリックすると、カラーパレットのあるウィンドウが開きます。
  • オフにすると、パレットの選択、ツールチップインプットフィールド、およびグラフィック ラベル選択が無効になります。

選択した時間枠の出力を実装するには、基本クラスのプライベートセクションにRebulidTimeframes()メソッドを作成し、このメソッドを実装します。

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CProgram::RebuildTimeframes(void)
{
//--- Count the number of selected timeframes
   int cnt=0;
   for(int i=0; i<21; i++)
   {
      if(m_checkbox[i].IsPressed())
         cnt++;
   }
   ArrayResize(m_timeframes,cnt);
   cnt=0;
//--- Remember the selected timeframe to the array
   for(int i=0; i<21; i++)
   {
      if(m_checkbox[i].IsPressed())
      {
         m_timeframes[cnt]=m_checkbox[i].LabelText();
         cnt++;
      }
   }
//---
   for(int i=0; i<cnt; i++)
      m_tf_button[i].IsLocked(false);
//---
   for(int i=0; i<cnt; i++)
   {
      m_tf_button[i].LabelText(m_timeframes[i]);
      m_tf_button[i].Update(true);
   }
   //---
   for(int i=cnt; i<21; i++)
      m_tf_button[i].IsLocked(true);
}

次に、Add Signal をクリックすると、編集ウィンドウを呼び出すコードに次のコードを追加します。

      //--- Click on the "Add Signal" button
      if(lparam==m_add_signal.Id())
      {
         m_set_window.OpenWindow();
         if(m_set_window.IsAvailable())
            RebuildTimeframes();
      }

値とテキストボタンの相互作用の設定に関連する次のモーメントに移りましょう。 OnEvent() で次のコードを追加します。

//---
      if(lparam==m_label_button[0].Id())
      {
         if(m_label_button[0].IsPressed())
         {
            m_label_button[1].IsPressed(false);
            m_label_button[1].Update(true);
         }
         m_text_box.Hide();
      }
      if(lparam==m_label_button[1].Id())
      {
         if(m_label_button[1].IsPressed())
         {
            m_label_button[0].IsPressed(false);
            m_label_button[0].Update(true);
         }
         m_text_box.Show();
      }

ここで、ボタンの 1 つが押された場合、もう 1 つのボタンを押さないという条件が満たされます。 [テキスト(Text)] が押されていない場合は、編集フィールドを非表示にします。 カラーパレットボタンのクリックもここで実装されています。 4つのボタンがあり、4つの要素の配列が宣言されているので、アクセスすることはループで書き込むことができます。

      //---
      for(int i=0; i<4; i++)
      {
         if(lparam==m_color_button[i].Id())
         {
            m_color_picker.ColorButtonPointer(m_color_button[i]);
            return;
         }
      }

直近のやり取りは、チェックボックスが押されていないときに要素をブロックすることです。 OnEvent()でチェックボックスクリックの追跡を追加し、相互作用を実装します。

//--- Click on the checkbox
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_CHECKBOX)
   {
      //---
      for(int i=0; i<3; i++)
      {
         if(lparam==m_set_param[i].Id())
         {
            m_color_button[i+1].IsLocked(!m_set_param[i].IsPressed());
            if(m_set_param[2].IsPressed())
               m_tooltip_text.Show();
            else
               m_tooltip_text.Hide();
         }
      }
      //---
      if(lparam==m_set_param[3].Id())
         m_pictures_slider.IsLocked(!m_set_param[3].IsPressed());
   }

プロジェクトを再度コンパイルし、結果を表示します。

図13 シグナル編集ウィンドウのUI要素の相互作用の実装


トレーディングシグナルモニター

この開発段階の直近のステップは、未来のトレードシグナルモニターのウィンドウを作成することです。 また、現在のバージョンで既に実装されている基本設定も考慮する必要があります。 作成する前に、要素がどのような目的で作成されているかを読者が理解できるよう、タスクを設定しましょう。

  • 最初のステップで選択したシグナルのテキストラベルを使用して行を作成します。
  • 2 番目のステップで選択したタイムフレームのテキストラベルを使用して、見出し列を作成します。
  • 作成した要素の行と列に合わせてウィンドウのサイズを変更します。 自動サイズ変更の一種。

タイムフレームとシンボルのテキストラベルの作成を可能にするには、CTextLabelクラスインスタンスの2つの配列を作成し、CProgramクラスに2つの実装メソッドを追加します。

   CTextLabel        m_timeframe_label[];
   CTextLabel        m_symbol_label[];
   bool              CreateTimeframeLabel(CTextLabel &text_label,const int x_gap,const int y_gap,string label_text);
   bool              CreateSymbolLabel(CTextLabel &text_label,const int x_gap,const int y_gap,string label_text);

次に、作成したメソッドを MainWindow.mqh ファイルに実装します。

//+------------------------------------------------------------------+
//| Creates the text label                                           |
//+------------------------------------------------------------------+
bool CProgram::CreateTimeframeLabel(CTextLabel &text_label,const int x_gap,const int y_gap,string label_text)
{
//--- Save the window pointer
   text_label.MainPointer(m_step_window);
//---
   text_label.Font(m_base_font);
   text_label.FontSize(m_base_font_size);
   text_label.XSize(40);
   text_label.BackColor(m_background);
//--- Create the button
   if(!text_label.CreateTextLabel(label_text,x_gap,y_gap))
      return(false);
//--- Add a pointer to element to the base
   CWndContainer::AddToElementsArray(0,text_label);
   return(true);
}
//+------------------------------------------------------------------+
//| Creates the text label                                           |
//+------------------------------------------------------------------+
bool CProgram::CreateSymbolLabel(CTextLabel &text_label,const int x_gap,const int y_gap,string label_text)
{
//--- Save the window pointer
   text_label.MainPointer(m_step_window);
//---
   text_label.Font(m_base_font);
   text_label.FontSize(m_base_font_size);
   text_label.XSize(100);
   text_label.BackColor(m_background);
//--- Create the button
   if(!text_label.CreateTextLabel(label_text,x_gap,y_gap))
      return(false);
//--- Add a pointer to element to the base
   CWndContainer::AddToElementsArray(0,text_label);
   return(true);
}

ウィンドウ インターフェイスの視覚化を進める前に、プライベート セクションに 2 つの重要な変数と 2 つのメソッドを作成する必要があります。

   int               m_total_signals;
   string            m_symbols[];
   void              ToMonitor(void);
   void              AutoResize(const int x_size,const int y_size);

少なくとも1つのトレードシグナルが作成されたか確認するには、m_total_signals変数が必要です。 このチェックは、モニター・ウィンドウを作成する前に実行されます。 m_symbols[]配列には、最初のセットアップステップから選択したシンボルが含まれます。 ToMonitor()メソッドはモニターインターフェイスの作成を実装し、AutoResize()は作成された要素に応じてサイズウィンドウを調整します。 宣言されたメソッドの実装は次のとおりです。

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CProgram::ToMonitor(void)
{
//--- Check if there is at least one signal
   if(m_total_signals<1)
   {
      MessageBox("No signals created!","Warning");
      return;
   }
//--- Hide Step 3
   m_add_signal.Hide();
   m_signal_header.Hide();
   m_back_button.Hide();
   m_next_button.Hide();
//--- Change window header
   m_step_window.LabelText("Signal Monitor");
   m_step_window.Update(true);
//--- Symbols
   int sy=ArraySize(m_symbols);
   ArrayResize(m_symbol_label,sy);
   for(int i=0; i<sy; i++)
   {
      if(!CreateSymbolLabel(m_symbol_label[i],5,m_step_window.CaptionHeight()+25+i*25,m_symbols[i]))
         return;
      m_symbol_label[i].Update(true);
   }
//--- Timeframes
   int tf=ArraySize(m_timeframes);
   ArrayResize(m_timeframe_label,tf);
//---
   for(int i=0; i<tf; i++)
   {
      if(!CreateTimeframeLabel(m_timeframe_label[i],110+50*i,m_step_window.CaptionHeight()+3,m_timeframes[i]))
         return;
      m_timeframe_label[i].Update(true);
   }
//--- Resize window
   AutoResize(m_timeframe_label[tf-1].XGap()+m_timeframe_label[tf-1].XSize()+5,m_symbol_label[sy-1].YGap()+m_symbol_label[sy-1].YSize()+5);
}

上記のコードからわかるように、m_symbolsからのデータはシンボルセクション(Symbols)で使用します。 しかし、このデータは収集または準備されていません。 修正しましょう。 ToStep_2()メソッドに移動し、少なくとも 1 つのシンボルが選択されているかどうかを確認した後、配列の最初のステップで選択したシンボルを覚えておいてください。

//--- Count the number of selected symbols
   ArrayResize(m_symbols,cnt);
   cnt=0;
//--- Remember the selected timeframe to the array
   for(int i=0; i<m_all_symbols; i++)
   {
      if(m_checkbox[i].IsPressed())
      {
         m_symbols[cnt]=m_checkbox[i].LabelText();
         cnt++;
      }
   }

次に、自動サイズ変更方法を作成します。 

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CProgram::AutoResize(const int x_size,const int y_size)
{
   m_step_window.ChangeWindowWidth(x_size);
   m_step_window.ChangeWindowHeight(y_size);
}

プロジェクトをチェックする前に、CProgram のコンストラクタで m_total_signals 変数をゼロに設定します。 もう 1 つの重要なポイントは、ボタンクリックイベントでOnEvent()メソッドを追加することです。 

      //--- Navigation
      if(lparam==m_back_button.Id())
      {
         //--- Go back
         if(m_current_step==2)
            ToStep_1();
         //--- Return to Step 2
         else if(m_current_step==3)
            ToStep_2();
      }
      //--- Go forward
      if(lparam==m_next_button.Id())
      {
         //--- Go to Step 2
         if(m_current_step==1)
            ToStep_2();
         //--- Go to Step 3
         else if(m_current_step==2)
            ToStep_3();
         //--- Go to Monitor
         else if(m_current_step==3)
            ToMonitor();
      }

ここでは、次のステップにジャンプするボタンをクリックすると、作成されたToMonitor()メソッドの呼び出しを追加します。このボタンは、ステップ 3 で "Create" と呼ばれます。 次に、プロジェクトをコンパイルし、アプリケーションを起動します。

  • 最初のステップで[クロス/Crosses]を選択します。
  • 2 番目のステップで、[Senior] を選択します。
  • 3 番目のステップで、[シグナルの追加/Add Signal] をクリックします。 
  • その後、シグナル作成ウィンドウを閉じ、[作成/Create]をクリックします。

図14 基本的なモニタのセットアップ

次の記事では、最初の起動時に作成された動作条件で設定されたトレードシグナルを検索するアルゴリズムの実装を検討します。

結論

添付されたアーカイブには、一覧表示されているすべてのファイルが含まれます。 これをするには、MQL5 フォルダをターミナルフォルダに保存するだけです。 MQL5フォルダが置かれているターミナルルートディレクトリを開くには、MetaTrader5ターミナルでCtrl+Shift+Dキーの組み合わせを押すか、下図15に示すようにコンテキストメニューを使用します。


図15. MetaTrader5ターミナルルートでMQL5フォルダを開く

MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/7528

添付されたファイル |
MQL5.zip (1626.37 KB)
DoEasyライブラリの時系列(第35部): バーオブジェクトと銘柄の時系列リスト DoEasyライブラリの時系列(第35部): バーオブジェクトと銘柄の時系列リスト
本稿は、簡単で迅速なプログラム開発のためのDoEasyライブラリの作成に関する新しいシリーズの始まりとなります。本稿では、銘柄の時系列データにアクセスして操作するためのライブラリ機能を実装します。メインおよび拡張時系列バーデータを格納するバーオブジェクトを作成し、オブジェクトの検索と並び替えを容易にするために、時系列リストにバーオブジェクトを配置します。
ネットワーク関数の適用、または DLL を使用しない MySQL: パートII - シグナル特性の変化を監視するプログラム ネットワーク関数の適用、または DLL を使用しない MySQL: パートII - シグナル特性の変化を監視するプログラム
前のパートでは、MySQLコネクタの実装をしました。 この記事では、シグナルプロパティを収集するためのサービスを実装し、時間の経過とともに変化を表示するためのプログラムを実装することで、そのアプリケーションを検討します。 実装された例は、ユーザーがシグナルのWebページに表示されないプロパティの変化を観察する必要がある場合、実用的な意味を持ちます。
連続ウォークフォワード最適化(パート4):最適化マネージャ(オートオプティマイザ) 連続ウォークフォワード最適化(パート4):最適化マネージャ(オートオプティマイザ)
この記事の主な目的は、アプリケーションとその機能を操作するメカニズムについて説明することです。 したがって、この記事は、アプリケーションの使用方法に関する説明書としても使うことができます。 アプリケーションの使用法においてありがちな落とし穴と詳細を扱っています。
MetaTraderプログラムを簡単かつ迅速に開発するためのライブラリ(第29部): 未決取引リクエスト - 特定の条件下での注文とポジションの削除と変更 MetaTraderプログラムを簡単かつ迅速に開発するためのライブラリ(第29部): 未決取引リクエスト - 特定の条件下での注文とポジションの削除と変更
本稿では、保留中リクエスト取引の概念の説明を完了し、未決注文を削除する機能と、特定の条件下で注文とポジションを変更する機能を作成します。したがって、シンプルなカスタム戦略、またはユーザ定義の条件でアクティブ化されるEA動作ロジックを開発できるようになります。