繁簡切換您正在訪問的是FX168財經網,本網站所提供的內容及信息均遵守中華人民共和國香港特別行政區當地法律法規。

FX168财经网>人物频道>帖子

监视多币种的交易信号(第二部分):应用程序可视部分的实现

作者/量化客 2020-05-21 16:20 0 来源: FX168财经网人物频道

内容目录

  • 概述
  • 设置步骤 1:品种
  • 设置步骤 2:时间帧
  • 设置步骤 3:添加信号
  • 交易信号创建和编辑窗口
  • 交易信号监控器
  • 结束语

概述

在上一阶段,我们开发了多币种交易信号监控器的通用结构。 在这一部分当中,我们将按顺序逐步实现应用程序配置初始化相关的阶段,并将创建构成界面的基本元素交互。


设置步骤 1:品种

根据应用程序结构,在首次启动期间应用程序设置的第一步即创建一个选择品种的界面,之后会搜索依此创建的交易信号。 在上一篇文章的末尾,我们创建了一个应用程序框架,现继续在此基础上操作。 我们继续开发应用程序。 首先,我们将定义实现此应用程序部分所需的主要元素组:

  • 应用程序窗口。
  • 快速选择品种。
  • 输入字段分组。
  • 品种分组的“保存”和“加载”按钮。
  • 所有可用品种的完整列表呈现为复选框,文本标签表示品种名称。
  • “Next” 按钮将切换到设置的第二步:时间帧选择。

早前创建的文件结构如下所示:

图例 1 应用程序文件结构。

首先,打开 SignalMonitor.mq5 应用程序文件,并在其中添加输入参数。 在 MetaTrader 5 终端中直接运行该应用程序时,您能够设置参数。 另外,声明先前所创建 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");
   }
}
//+------------------------------------------------------------------+

 从代码中可以看出,加入了三个输入参数

  • 字号。
  • 应用程序窗口的标头颜色。
  • 应用程序窗口和元素的背景色。

接着,声明 CProgram 的类实例,并命名为 program,和变量 tick_counter(仅用于显示有关应用程序启动时间的信息)。 进而,在 OnInit() 方法中,我们初始化类变量,把应用程序输入参数赋值给它们。 此外还要调用 CreateGUI() 基础方法,它将启动应用程序。

不过,若您尝试立即编译打开的文件,会收到编译错误,示意在 CProgram 类中找不到变量 m_base_font_sizem_backgroundm_captionCreateGUI() 方法。 故此,打开 Program.mqh 文件,在 CProgram 类里实现它们。 首先,加入上述变量和方法,以及应用程序正确初始操作所需的其他方法。 所需元素加入后,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";
}

现在,我们继续创建应用程序的第一个窗口。 为此目的,在类中声明新的 m_step_window 变量,该变量是 CWindow 类的实例。 还要声明创建第一个窗口的方法,并命名为 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 类实例的数组,其维度是三以及创建按钮的通用方法 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() 里调用该方法, 以不同坐标和文本标签值添加三个按钮:

...
//--- 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 添加快速选择品种分组的按钮。

接下来,添加一个输入字段保存所选品种分组的名称,可用两个按钮进行保存和加载:Save(保存)和 Load(加载)。 为此,添加一个创建输入字段的 CTextEdit 类实例,以及另外两个创建按钮的 CButton 类实例。由于保存和加载按钮只是名称不同,创建通用的 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() 类,在应用程序窗口中添加两个按钮和一个输入字段。

//---
   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 为品种分组和“保存/加载”按钮添加一个输入字段。

现在,我们继续进行可视化,并能选择 MetaTrader 5 终端里当前帐户下的所有可用品种。 请注意,如果您显示所有可用品种,则应用程序窗口的高度可能会不足。 一个优秀的解决方案是根据数据自动调整窗口高度。 添加品种总数都相似:添加创建复选框 CCheckBox 的类实例数组,和创建它们的通用方法(因为它们只有名称不同)。

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

未指定 m_checkbox[] 数组的维度,因为事先不知道终端当前所选帐户里有多少个品种。 因此,我们在 CProgram 类的私密部分中创建两个变量,并为它们分配可用品种总数和市场观察里中当前选定的品种数字。

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_buttonCButton 类实例,调用先前创建的 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 对应的品种。
  • 单击 “Crosss” 会清除先前的选择,并在终端中设置一组与 forex.crosss 对应的品种。
  • 当三个按钮都未被按下时,所有选择都将被取消。
<d

它是这样的:

图例 6 基本交互元素的实现。

若要完成可视化实现,需要加入两小段附加内容。 您可以在图例 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);
}

另外,我们需要监视用户的选择。 如果用户尚未选择至少一个品种,则不允许切换到步骤 2。 利用 “Back” 和 “Next” 按钮执行步骤之间的切换。 因此,若要解决该任务,需将三个新方法添加到 CProgram 类的私密部分。 该方法处理三个步骤中每一步选择的信息,从而执行应用程序的初始设置。 另外,添加 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。 若要在三个配置步骤之间设置导航,则在 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();
      }

如果您在此步骤尝试编译项目,则编译器将返回以下错误:创建了三个方法,并已调用,但尚未实现: 

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

为了修复此问题,需在 Program.mqh 文件中创建这些类的实现。 不过,对于 ToStep_1()ToStep_3() 方法,暂时将其留空。 它们将在以后填补。 现在,我们对切换到第二步 ToStep_2() 的方法感兴趣。 添加检查,判断至少选择了一个品种:

//+------------------------------------------------------------------+
//| 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)
{
//---
}

如果用户在未选择品种的情况下不小心按了下一步,则显示警告,示意至少应选择一个品种。


设置步骤 2:时间帧

在应用程序设置的第二步中,用户应选择搜索交易信号的时间帧。 我们在第一篇文章中提到了必需的 UI 元素:

  • 一组快速选择时间帧的按钮。
  • 复选框形式的时间帧列表。
  • 返回到步骤 1 的 “Back(返回)”按钮。

我们借用步骤 1 可视化实现中的现有对象,并针对时间帧的选择进行调整。 转至我们最近正在编辑的 ToStep_2() 方法的主体,并为其添加其他功能。 首先,请记住在步骤 1 中选择的品种,并在 MetaTrader 5 的市场观察中显示它们:

//--- 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 的界面转换为第二个界面:

//--- 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)。 现在,我们需要修改界面,从而能够正确显示所选时间帧集合 “All(全部)”,“Junior(初级)”,“Senior(高级)”。 打开 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);
                  }
            }
         }

在设置的第二步中要实现的最后一个 UI 元素是 “Back(返回)”按钮,该按钮返回到步骤 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,并在 С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() 创建界面后立即执行,在方法主体末尾添加两行来隐藏这些元素

//+------------------------------------------------------------------+
//| 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。 不要忘记在前两个步骤中选择元素,否则应用程序不允许我们进入第三步。

图例 8 实现应用程序设置的第 3 步。

交易信号创建和编辑窗口

与交易信号相关的可视组件位于 SetWindow.mqh 文件中,故此打开它。 现在,它仅包含 #include 命令行连接的包含文件 Program.mqh。 首先,创建一个单独的窗口,这将是创建和设置所有其他元素的基础。 打开 Program.mqh,并在类中声明 m_set_window 变量,该变量是 CWindow 类实例。 还要添加创建窗口的 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 中点击“添加信号”后,将打开另一个创建和编辑窗口。

图例 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);

结果是 UI 元素,它允许从 7 种振荡器类型的标准指标里进行选择。

图例 10 选择指标类型的元素。

接下来,我们研究分为两部分的元素集合:指标设置和信号设置。 从标准集合中选择的所有指标均具有通用设置,例如周期和适用价格。 因此,第一板块需要以下内容:文本标签,周期输入字段,和选择指标计算所用价格的下拉菜单。 在 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 指标设置的实现。

接下来,进入信号编辑的第二板块。 它由标题和八个设置组成:

  • 信号规则。
  • 信号模块中的文本标签值。
  • 文本标签颜色。
  • 背景用法和颜色。
  • 边缘用法和颜色。
  • 信号模块上方工具提示的用法、颜色和值。
  • 图形标签的用法,及其在信号模块中的外观。
  • 选择可用时间帧以便搜索给定信号。

为了给该板块添加标题,在 CreateSetWindow() 主体的末尾添加以下代码(我们之前已创建了一种可视化标题的方法,以其他参数值复用该方法):

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

信号规则由两个元素组成:下拉菜单,和数字值输入字段。 在 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 中选择的数字。
  • 选择 “Value” 按钮后,“Text” 按钮应变成未按下状态,并且文本标签输入字段应被隐藏。
  • 单击颜色选择按钮应打开一个带有调色板的窗口。
  • 取消选中时,调色板选择、工具提示输入字段和图形标签选择应处于未活动状态。

为了实现所选时间帧的输出,我们在基类的私密部分中创建 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);
}

现在,单击添加信号,将以下内容添加到调用编辑窗口的代码里。

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

下一刻,我们进入与 “Value” 和 “Text” 按钮相关的交互设置。 在 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();
      }

此处满足以下条件:如果按下其中一个按钮,则另一个按钮应释放。 如果未按下 “Text”,则隐藏编辑字段。 此处还实现了单击调色板按钮。 我们有四个按钮,并声明了四个元素的数组,因此编写在循环中访问它们。

      //---
      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 元素交互的实现。


交易信号监控器

开发阶段的最后一步是为将来的交易信号监控器创建一个窗口。 我们还应考虑当前版本中已实现的那些基本设置。 在创建之前,我们设置一些任务,以便令读者理解创建元素之目的:

  • 创建第一步中所选信号的文本标签的行。
  • 创建在第二步中所选时间帧的文本标签的标题列。
  • 根据所创建元素的行和列调整窗口大小。 一种自动调整大小。

为了创建时间帧和品种的文本标签,创建两个 CTextLabel 类实例的数组,并在 CProgram 类中为两个方法添加实现。

   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);
}

在继续进行窗口界面可视化之前,我们需要在私密部分中创建两个重要变量,以及两个方法:

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

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 中的数据用于“品种”部分。 但这些数据尚未收集或准备。 我们来修复它。 进入 ToStep_2() 方法,并检查是否已选择了至少一个品种,记住第一步中选择的品种,并保存到我们的数组当中:

//--- 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);
}

在检查项目之前,m_total_signals 变量应在 CProgram 的构造函数里置零。另一个要点是 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(交叉盘)”。
  • 在第二步中,选择 “Senior(高级)”。
  • 在第三步中,单击 "Add Signal(添加信号)"。 
  • 之后,关闭信号创建窗口,然后单击 "Create(创建)”。

图例 14 基本监视器设置

在下一篇文章中,我们将研究一种算法的实现,该算法将在初次启动期间,依据创建的工作条件搜索已配置的交易信号。

结束语

附件档案包含所有列出的文件,这些文件位于相应的文件夹中。 为令其正常工作,您只需将 MQL5 文件夹保存到终端文件夹里。 若要打开 MQL5 文件夹所在的终端根目录,请在 MetaTrader 5 终端中按下 Ctrl+Shift+D 组合键,或利用关联菜单,如下图例 15 中所示。


图例 15. 在 MetaTrader 5 终端根目录中打开 MQL5 文件夹

分享到:
举报财经168客户端下载

全部回复

0/140

投稿 您想发表你的观点和看法?

更多人气分析师

  • 张亦巧

    人气2192文章4145粉丝45

    暂无个人简介信息

  • 王启蒙现货黄金

    人气296文章3215粉丝8

    本人做分析师以来,并专注于贵金属投资市场,尤其是在现货黄金...

  • 指导老师

    人气1864文章4423粉丝52

    暂无个人简介信息

  • 李冉晴

    人气2320文章3821粉丝34

    李冉晴,专业现贷实盘分析师。

  • 梁孟梵

    人气2176文章3177粉丝39

    qq:2294906466 了解群指导添加微信mfmacd

  • 张迎妤

    人气1896文章3305粉丝34

    个人专注于行情技术分析,消息面解读剖析,给予您第一时间方向...

  • 金泰铬J

    人气2328文章3925粉丝51

    投资问答解咨询金泰铬V/信tgtg67即可获取每日的实时资讯、行情...

  • 金算盘

    人气2696文章7761粉丝125

    高级分析师,混过名校,厮杀于股市和期货、证券市场多年,专注...

  • 金帝财神

    人气4760文章8329粉丝119

    本文由资深分析师金帝财神微信:934295330,指导黄金,白银,...

FX168财经

FX168财经学院

FX168财经

FX168北美