自动选择有 "钱途" 的信号

Alexander Fedosov | 5 二月, 2018


概述

由于市场的不断变化, 在金融市场中自动进行交易是开发新款交易机器人的终极目标。然而, 自动交易智能交易系统无法针对市场中的所有情况都做好准备。因此, 最有效的方式仍然是交易机器人与人工干预的共生关系, 即人工控制自动化系统的操作, 并在必要时进行调整。在 交易信号 服务中可以找到这种交互的良好示例。该服务提供了一系列具有不同风险参数或交易动态的交易系统和方法。众多工具可以帮助用户找到期望的信号。但是, 关键任务是判断参数组是否适合不同的交易风格。一个便捷的解决方案是判断交易信号的参数组以及其数值与适度风险和保守的交易风格相适。

交易信号的评级模型

已选择了五个评估标准来全面评估交易信号。它们中的每一个都将按照 20 分制分级, 其中 0 代表风险交易风格, 20 代表保守交易。总的来说, 每个交易信号将按照 100 分制系统进行评估。交易信号的以下特征将作为标准。

1)交易账户的杠杆。

如您所知, 杠杆越高意味着在交易产品价格暴涨/暴跌的情况下, 您的资金大幅亏损的风险就越高。20 分制系统的以下数值将用于杠杆评估: 

  • 20 分 将被赋予杠杆 1:1
  • 0 分 w将被赋予杠杆 1:500 及以上

为了评估一个基于杠杆的信号, 我们将使用下面的直线方程:

其中 Xa = 1, Ya = 20, 并且相应地, Xb = 500, Yb = 0。这是一条穿过两点的直线方程。在我们的示例中, 方程式反映了 20 分制对当前杠杆的依赖性。 

请注意, 这种依赖性仅针对指定评估范围从 0 到 20 有效。这意味着即使使用的杠杆比例高于 1:500 (例如, 1:1000), 其分数也将为 0。

2)账户增长百分比。

若要根据 20 分制评估这个参数, 我们应该考虑两个因素。首先, 增长可能是负值。另外, 比较它的绝对值是不正确的。因此, 我们来介绍下面的规则和假设。

  • 负增长意味着信号目前处于危险之中, 因此将得到 0 分。
  • 替代评估绝对增长, 我们通过将账户增长除以信号生存期来衡量其随时间的变化。
  • 风险是一个主观的概念。每周增长 10% 对一些交易者来说可能是不够的, 而对其他人来说这是一个良好的增长率。不过, 我们需要设定一定的范围。因此, 1% 的增长率将被作为保守信号的参考值, 15% 将被设定为风险交易的门槛。

再次, 我们使用上面的两条直线方程, 其中, Xa = 1, Ya = 20, 且分别 Xb = 15, Yb = 0。获得以下依赖性:

3)最大回撤百分比。

该值直接表征交易风险。我们使用下面的规则来确定极限值。

  • 最高 20% 的回撤将被视为保守交易风格。它的分数将等于 20。
  • 40% 以上的回撤率将被认为是有风险的, 并被给予 0 分。
  • 最大回撤的 20-40% 的区间将基于之前两个点的直线方程进行评估。  

在此方程里, Xa = 20, Ya = 20, Xb = 40, Yb = 0。获得以下依赖性:

4)ROI (投资回报) 百分比

投资回报率在 100% 以上意味着有效资金利用; 低于 100% 的数值意味着投资无效。我们根据 20 分制评估这个成功的指标。

  • ROI 低于 100% 则赋予 0 分。
  • ROI 高于 200% 则赋予 20 分。
  • 100-200 的间隔将根据之前两个点的直线方程进行评估。

在此方程里, Xa = 100, Ya = 0, 而 Xb = 200, Yb = 20。获得以下依赖性:

5)交易信号生存期。

生存期是一个非常重要的特征, 展现信号是否能够长期交易正确。为了基于 20 分制对它进行评估, 我们首先确定时间量, 将其作为可靠性并验证。采用一周作为衡量单位, 我们已经在增长率评估中使用了周。请注意, 这是个人喜好, 任何人都可以有特定的可靠性时间标准。我们的系统使用以下阈值。

  • 生存期低于 4 周的信号 (一个月内完整周数) 将给予 0 分。
  • 生存期 25 周以上 (六个月内的完整周数) 将给予 25 分。
  • 4 至 25 周的间隔将根据上述两点设定的直线方程进行评估。  


信号评估工具的实现

选择 图形库界面 EasyandFastGUI 来实现这个想法。应用程序的结构如图例 1a 和 1b 所示 (参见下文)。它由以下元素组成:

  • 终端中当前账户的可用信号列表。
  • 列表中所选信号按类别详细评估。
  • 当前账户可用交易信号的数量。
  • 在列表中所选交易信号的可视汇总得分。


图例 1a 应用程序的结构 (左侧部分)

以梯度表示的汇总评分并不意味着它的左侧部分 (红色) 反映的是非盈利信号, 绿色部分显示的是潜在的有 "钱途" 的信号。它只是在特定交易信号基础上提供交易风格的可视化。虽然风险交易意味着更高的亏损机会, 但也意味着更高的盈利。


图例 1b 应用程序的结构 (右侧)

在程序实现中, 我们将详细介绍反映应用程序本质的关键方法。第一个方法是 CreateGUI(), 它是所有其它方法的汇总, 负责显示所有可视信息。

  • CreateWindow() 方法创建带有标题的应用程序窗口。它可以被关闭和最小化。
  • CreateTable() 创建一个包含当前账户所有可用信号的表格。
  • CreateStatusBar() 方法创建一个状态栏, 显示可用交易信号的总数。
  • CreatePicture1()CreatePicture2() 分别创建渐变比例和比例指针。
  • CreateTextLabel() 方法设置显示有关所选交易信号评级的详细信息。
  • CreateButton() 方法创建一个按钮, 用于订阅表中选择的信号。
//+------------------------------------------------------------------+
//| 创建程序的图形界面                                                   |
//+------------------------------------------------------------------+
bool CProgram::CreateGUI(void)
  {
//--- 创建面板
   if(!CreateWindow("自动搜索信号"))
      return(false);
//--- 创建表格
   if(!CreateTable(7,100))
      return(false);
//--- 状态栏
   if(!CreateStatusBar(1,26))
      return(false);
//--- 图像
   if(!CreatePicture1(618,40))
      return(false);
   if(!CreatePicture2(610,80))
      return(false);
//--- 文本标签
   if(!CreateTextLabel1(20,20,"杠杆得分: -"))
      return(false);
   if(!CreateTextLabel2(20,40,"增长得分: -"))
      return(false);
   if(!CreateTextLabel3(20,60,"回撤得分: -"))
      return(false);
   if(!CreateTextLabel4(200,20,"投资回报得分: -"))
      return(false);
   if(!CreateTextLabel5(200,40,"生存期得分: -"))
      return(false);
//--- 图标按钮
   if(!CreateButton(440,40,"订阅"))
      return(false);
//--- 用户图形界面创建完成
   CWndEvents::CompletedGUI();
   return(true);
  }
//+-----------------------------------------------------------------


以下方法负责交互: 它可以从表格中的清单里选择交易信号, 并显示该信号有关的信息。它还跟踪点击已创建按钮和表格中所选信号的订阅事件。

//+------------------------------------------------------------------+
//| 事件处理器                                                         |
//+------------------------------------------------------------------+
void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- 列表或清单项的按击事件
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_LIST_ITEM)
     {
      int x=610+3*int(m_table.GetValue(9,m_table.SelectedItem()));
      //---
      CreateTextLabel1(20,20,"杠杆得分: "+IntegerToString(GetLeverageRating(int(m_table.GetValue(2,m_table.SelectedItem())))));
      CreateTextLabel2(20,40,"增长得分: "+IntegerToString(GetGainRating(int(m_table.GetValue(3,m_table.SelectedItem())),int(m_table.GetValue(8,m_table.SelectedItem())))));
      CreateTextLabel3(20,60,"回撤得分: "+IntegerToString(GetDrawDownRating(int(m_table.GetValue(5,m_table.SelectedItem())))));
      CreateTextLabel4(200,20,"投资回报得分: "+IntegerToString(GetROIRating(int(m_table.GetValue(4,m_table.SelectedItem())))));
      CreateTextLabel5(200,40,"生存期得分: "+IntegerToString(GetWeeksRating(int(m_table.GetValue(8,m_table.SelectedItem())))));
      CreatePicture2(x,80);
      //---
      m_button.IsLocked(false);
      Update(true);
     }
//---
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON)
     {
      //--- 如果按钮被按下
      if(lparam==m_button.Id())
        {
         SignalSubscribe(long(m_table.GetValue(6,m_table.SelectedItem())));
        }
     }
  }

接着, 我们来看看实现交易信号评估的方法。首先, 我们需要获得当前账户可用的交易信号列表, 以及为了添加到表格中并用于编制总评价所需的信息。

GetTradingSignals() 方法攫取有关可用交易信号的信息, 并将其添加到数据数组中:

//+------------------------------------------------------------------+
//| 接收有关可用交易信号的信息                                            |
//+------------------------------------------------------------------+
bool CProgram::GetTradingSignals(void)
  {
//--- 请求信号数据库中的信号总数 
   m_signals_total=SignalBaseTotal();
//---
   ArrayResize(m_name,m_signals_total);
   ArrayResize(m_curr,m_signals_total);
   ArrayResize(m_leverage,m_signals_total);
   ArrayResize(m_gain,m_signals_total);
   ArrayResize(m_roi,m_signals_total);
   ArrayResize(m_max_drawdown,m_signals_total);
   ArrayResize(m_pips,m_signals_total);
   ArrayResize(m_subscr,m_signals_total);
   ArrayResize(m_weeks,m_signals_total);
   ArrayResize(m_rating,m_signals_total);
//--- 遍历所有信号 
   for(int i=0;i<m_signals_total;i++)
     {
      //--- 选择一个信号进一步操作 
      if(SignalBaseSelect(i))
        {
         //--- 接收信号属性 
         m_name[i]=SignalBaseGetString(SIGNAL_BASE_NAME);                                                // 信号名称
         m_curr[i]=SignalBaseGetString(SIGNAL_BASE_CURRENCY);                                            // 信号的货币
         m_leverage[i]=SignalBaseGetInteger(SIGNAL_BASE_LEVERAGE);                                       // 杠杆
         m_gain[i]=SignalBaseGetDouble(SIGNAL_BASE_GAIN);                                                // 账户增长 %
         m_roi[i]=SignalBaseGetDouble(SIGNAL_BASE_ROI);                                                  // 投资回报
         m_max_drawdown[i]=SignalBaseGetDouble(SIGNAL_BASE_MAX_DRAWDOWN);                                // 最大回撤
         m_id[i]=SignalBaseGetInteger(SIGNAL_BASE_ID);                                                   // 信号 ID
         m_subscr[i]=SignalBaseGetInteger(SIGNAL_BASE_SUBSCRIBERS);                                      // 订阅者数量
         m_weeks[i]=int((TimeCurrent()-SignalBaseGetInteger(SIGNAL_BASE_DATE_PUBLISHED))/3600/24/7);     // 信号生存时间
         //--- 接收总评分
         m_rating[i]=GetLeverageRating(m_leverage[i])+GetGainRating(m_gain[i],m_weeks[i])+GetDrawDownRating(m_max_drawdown[i])+GetROIRating(m_roi[i])+GetWeeksRating(m_weeks[i]);
        }
      else
        {
         PrintFormat("信号选择错误. 错误代码=%d",GetLastError());
         return(false);
        }
     }
   return (true);
  }
//+------------------------------------------------------------------+


正如您在 上表 中所见, m_rating[] 是数组, 每个交易信号的评分结果被添加到该数组中。因此, 我们来研究在这个计算中使用的方法。这些方法代表的是本文第一部分所提模型的程序实现。

//+------------------------------------------------------------------+
//| 交易杠杆评估                                                       |
//+------------------------------------------------------------------+
int CProgram::GetLeverageRating(long leverage)
  {
   int lev_rating=int(-20.0/499.0*double(leverage)+10000.0/499.0);
   lev_rating=(lev_rating>20)?20:lev_rating;
   return lev_rating;
  }
//+------------------------------------------------------------------+
//| 增长率评估                                                         |
//+------------------------------------------------------------------+
int CProgram::GetGainRating(double gain,int weeks)
  {
   weeks=(weeks==0)?1:weeks;
   int gain_rating=int(-10*(gain/double(weeks)/7.0)+150.0/7.0);
   gain_rating=(gain_rating>20)?20:gain_rating;
   gain_rating=(gain_rating<0)?0:gain_rating;
   gain_rating=(gain<0)?0:gain_rating;
   return gain_rating;
  }
//+------------------------------------------------------------------+
//| 最大回撤评估                                                       |
//+------------------------------------------------------------------+
int CProgram::GetDrawDownRating(double max_drawdown)
  {
   int drawdn_rating=int(-max_drawdown+40);
   drawdn_rating=(drawdn_rating>20)?20:drawdn_rating;
   drawdn_rating=(drawdn_rating<0)?0:drawdn_rating;
   return drawdn_rating;
  }
//+------------------------------------------------------------------+
//| 投资回报评估                                                       |
//+------------------------------------------------------------------+
int CProgram::GetROIRating(double roi)
  {
   int roi_rating=int(0.2*roi-20);
   roi_rating=(roi_rating>20)?20:roi_rating;
   roi_rating=(roi_rating<0)?0:roi_rating;
   return roi_rating;
  }
//+------------------------------------------------------------------+
//| 交易信号生存时间评估                                                 |
//+------------------------------------------------------------------+
int CProgram::GetWeeksRating(int weeks)
  {
   int age_rating=int(20.0*double(weeks)/21.0-80.0/21.0);
   age_rating=(age_rating>20)?20:age_rating;
   age_rating=(age_rating<0)?0:age_rating;
   return age_rating;
  }

然后, 利用 InitializingTable() 方法将所有获得的数据添加到表格中, 并使用 CreateTable() 方法直观显示。

//+------------------------------------------------------------------+
//| 初始化表格                                                         |
//+------------------------------------------------------------------+
void CProgram::InitializingTable(void)
  {
//---
   string columns[10]=
     {
      "信号名称",
      "账户货币",
      "账户杠杆",
      "账户增长, %",
      "投资回报",
      "最大回撤",
      "信号 ID",
      "订阅者数量",
      "生存时间, 周",
      "评分"
     };
//---
   for(int c=0; c<COLUMNS1_TOTAL; c++)
     {
      //--- 设置表头标题
      m_table.SetHeaderText(c,columns[c]);
      //---
      for(int r=0; r<m_signals_total; r++)
        {
         if(c==0)
            m_table.SetValue(c,r,m_name[r]);
         else if(c==1)
            m_table.SetValue(c,r,m_curr[r]);
         else if(c==2)
            m_table.SetValue(c,r,IntegerToString(m_leverage[r]));
         else if(c==3)
            m_table.SetValue(c,r,DoubleToString(m_gain[r],2));
         else if(c==4)
            m_table.SetValue(c,r,DoubleToString(m_roi[r],2));
         else if(c==5)
            m_table.SetValue(c,r,DoubleToString(m_max_drawdown[r],2));
         else if(c==6)
            m_table.SetValue(c,r,IntegerToString(m_id[r]));
         else if(c==7)
            m_table.SetValue(c,r,IntegerToString(m_subscr[r]));
         else if(c==8)
            m_table.SetValue(c,r,IntegerToString(m_weeks[r]));
         else if(c==9)
            m_table.SetValue(c,r,IntegerToString(m_rating[r]));
        }
     }
  }
//+------------------------------------------------------------------+
//| 创建渲染表格                                                       |
//+------------------------------------------------------------------+
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\arrow_up.bmp"
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\arrow_down.bmp"
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\circle_gray.bmp"
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\calendar.bmp"
//---
bool CProgram::CreateTable(const int x_gap,const int y_gap)
  {
#define COLUMNS1_TOTAL 10
//--- 将指针存储到主控件
   m_table.MainPointer(m_window);
//--- 列宽数组
   int width[COLUMNS1_TOTAL];
   ::ArrayInitialize(width,110);
   width[1]=80;
   width[2]=100;
   width[4]=90;
   width[8]=85;
   width[9]=90;
//--- 在列中文本沿 X 轴偏移的数组
   int text_x_offset[COLUMNS1_TOTAL];
   ::ArrayInitialize(text_x_offset,7);
//--- 列中的文本对齐数组
   ENUM_ALIGN_MODE align[COLUMNS1_TOTAL];
   ::ArrayInitialize(align,ALIGN_CENTER);
//---
   GetTradingSignals();
//--- 属性
   m_table.XSize(1000);
   m_table.YSize(470);
   m_table.CellYSize(20);
   m_table.TableSize(COLUMNS1_TOTAL,m_signals_total);
   m_table.TextAlign(align);
   m_table.ColumnsWidth(width);
   m_table.TextXOffset(text_x_offset);
   m_table.LabelXGap(5);
   m_table.LabelYGap(4);
   m_table.IconXGap(7);
   m_table.IconYGap(4);
   m_table.MinColumnWidth(0);
   m_table.ShowHeaders(true);
   m_table.IsSortMode(true);
   m_table.LightsHover(true);
   m_table.SelectableRow(true);
   m_table.IsWithoutDeselect(true);
   m_table.ColumnResizeMode(true);
   m_table.IsZebraFormatRows(clrWhiteSmoke);
   m_table.AutoXResizeMode(true);
   m_table.AutoXResizeRightOffset(7);
   m_table.AutoYResizeBottomOffset(28);
   m_table.HeadersColor(clrSkyBlue);
   m_table.DataType(2,TYPE_INT);
   m_table.DataType(3,TYPE_FLOAT);
   m_table.DataType(4,TYPE_FLOAT);
   m_table.DataType(5,TYPE_FLOAT);
   m_table.DataType(6,TYPE_INT);
   m_table.DataType(7,TYPE_INT);
   m_table.DataType(8,TYPE_INT);
   m_table.DataType(9,TYPE_INT);

//--- 用数据填充表格
   InitializingTable();
//--- 创建控件
   if(!m_table.CreateTable(x_gap,y_gap))
      return(false);
//--- 将该对象添加到对象组的公用数组中
   CWndContainer::AddToElementsArray(0,m_table);
   m_table.SortData(9);
   m_table.SortData(9);
   return(true);
  }

为了更方便地使用交易信号表格中的数据, 我们应该为 显示的单元格分配数据类型。它将在表格列中启用正确的数据排序。为了获得更好的视觉效果, 表格最初按照最后一列进行排序, 即按照复杂的信号评分, 降序排列。排序执行两次: 首次是降序, 第二次和之后则与以前相反顺序执行。

最后提到的一个要素是以渐变比例和指针的形式显示最终评分。这是通过两种方法完成的, 它们的清单提供如下。在上面的 OnEvent() 方法中已经注意到指针位置在刻度上的变化。

//+------------------------------------------------------------------+
//| 创建一个渐变比例                                                    |
//+------------------------------------------------------------------+
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp64\\000.bmp"
#resource "\\Images\\EasyAndFastGUI\\Controls\\ArrowUp_blue.bmp"
//---
bool CProgram::CreatePicture1(const int x_gap,const int y_gap)
  {
//--- 将指针存储到主控件
   m_picture1.MainPointer(m_window);
//--- 属性
   m_picture1.XSize(300);
   m_picture1.YSize(40);
   m_picture1.IconFile("Images\\EasyAndFastGUI\\Icons\\bmp64\\000.bmp");
//--- 创建按钮
   if(!m_picture1.CreatePicture(x_gap,y_gap))
      return(false);
//--- 在基类中添加一个指向元素的指针
   CWndContainer::AddToElementsArray(0,m_picture1);
   return(true);
  }
//+------------------------------------------------------------------+
//| 为比例创建一个指针                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreatePicture2(const int x_gap,const int y_gap)
  {
//--- 将指针存储到主控件
   m_picture2.MainPointer(m_window);
//--- 属性
   m_picture2.XSize(16);
   m_picture2.YSize(16);
   m_picture2.IconFile("Images\\EasyAndFastGUI\\Controls\\ArrowUp_blue.bmp");
//--- 创建按钮
   if(!m_picture2.CreatePicture(x_gap,y_gap))
      return(false);
//--- 在基类中添加一个指向元素的指针
   CWndContainer::AddToElementsArray(0,m_picture2);
   return(true);
  }


潜在地有 "钱途" 信号的选择

在选择一个信号并确定它的 "前景" 时, 我们预期一个优秀的信号未来会带来某种结果。实践表明, 在短时间内展现出高增长率的信号不会长命。这是因为这类信号通常使用高风险的交易方式来获取更高的利润。尽管如此, 我们不应该从有希望的清单中删除伴有高风险交易风格的交易信号, 因为交易者可能有不同的目标。一些交易者计划长期驻留在市场上, 慢慢赚钱。其他交易者的目标是快速盈利, 并准备承担一定的风险。 

因此, 我们在观感上将上述评分系统分为三个类别 (图例 2)。

  • 第一类别。红色区域。来自该区域的信号可能具有较高的盈利能力, 但涉及高风险。
  • 第二类别。黄色区域。这些信号表现出良好的盈利能力, 风险适中。
  • 第三类别。绿色区域。这些信号被认为是潜在的有希望的, 因为它们表现出更高的可靠性, 尽管盈利能力可能不高。

图例 2. 交易信号评分的等级分类

为了分类编程, 我们在 InitializingTable() 方法里添加 以下语句:

//+------------------------------------------------------------------+
//| 初始化表格                                                         |
//+------------------------------------------------------------------+
void CProgram::InitializingTable(void)
  {
//---
   string columns[10]=
     {
      "信号名称",
      "账户货币",
      "账户杠杆",
      "账户增长, %",
      "投资回报",
      "最大回撤",
      "信号 ID",
      "订阅者数量",
      "生存时间, 周",
      "评分"
     };
//---
   for(int c=0; c<COLUMNS1_TOTAL; c++)
     {
      //--- 设置表头标题
      m_table.SetHeaderText(c,columns[c]);

      //---
      for(int r=0; r<m_signals_total; r++)
        {
         if(c==0)
           {
            m_table.SetValue(c,r,m_name[r]);
            if(m_rating[r]<=30)
               m_table.TextColor(c,r,clrCrimson);
            else if(m_rating[r]>30 && m_rating[r]<=66)
               m_table.TextColor(c,r,clrOrange);
            else if(m_rating[r]>66)
               m_table.TextColor(c,r,clrForestGreen);
           }
         else if(c==1)
            m_table.SetValue(c,r,m_curr[r]);
         else if(c==2)
            m_table.SetValue(c,r,IntegerToString(m_leverage[r]));
         else if(c==3)
            m_table.SetValue(c,r,DoubleToString(m_gain[r],2));
         else if(c==4)
            m_table.SetValue(c,r,DoubleToString(m_roi[r],2));
         else if(c==5)
            m_table.SetValue(c,r,DoubleToString(m_max_drawdown[r],2));
         else if(c==6)
            m_table.SetValue(c,r,IntegerToString(m_id[r]));
         else if(c==7)
            m_table.SetValue(c,r,IntegerToString(m_subscr[r]));
         else if(c==8)
            m_table.SetValue(c,r,IntegerToString(m_weeks[r]));
         else if(c==9)
            m_table.SetValue(c,r,IntegerToString(m_rating[r]));
        }
     }
  }

根据收到的评级, 我们采用 TextColor() 方法更改交易信号名称 (第一列) 的颜色。因此, 可以查看信号所属的类别, 而无需分析或检查表格最后一列所包含的评估值。

图例 3 交易信号的颜色指示

信号可以按照表格最后一列进行排序来分组。排序默认启用。最后一个明显的行动是实现订阅信号的机会。这可利用 CreateButton() 方法完成:

//+------------------------------------------------------------------+
//| 创建一个图像按钮                                                    |
//+------------------------------------------------------------------+
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\start.bmp"
//---
bool CProgram::CreateButton(const int x_gap,const int y_gap,const string text)
  {
//--- 将指针存储到主控件
   m_button.MainPointer(m_window);
//--- 属性
   m_button.XSize(120);
   m_button.YSize(22);
   m_button.IconXGap(3);
   m_button.IconYGap(3);
   m_button.FontSize(10);
   m_button.IsCenterText(true);
   m_button.IconFile("Images\\EasyAndFastGUI\\Icons\\bmp16\\start.bmp");
   m_button.IsLocked(true);
//--- 创建控件
   if(!m_button.CreateButton(text,x_gap,y_gap))
      return(false);
//--- 在基类中添加一个指向元素的指针
   CWndContainer::AddToElementsArray(0,m_button);
   return(true);
  }
//+------------------------------------------------------------------+


为了避免上述函数中可能出现的错误, 我们来看看需要什么样的终端设置。

1. 您必须在 MetaTrader 5 中使用您的 mql5 账户登录才能使用终端中的交易信号:

图例4 终端中的身份验证

2. 当您启动应用程序时, 启用 允许更改信号设置 选项。否则使用此应用程序订阅交易信号将返回错误 4014 ("Function is not allowed for call" - 函数不允许调用)。

图例 5 启动期间配置应用程序

3. 最后一个会影响应用程序正常运行的是 signals.dat 文件。如果表格中的数据显示不正确或根本没有显示, 您应该在 C:\Users\用户名\AppData\Roaming\MetaQuotes\Terminal\..\bases\signals 找到并删除它。然后重新启动终端并打开信号选项卡, 如图例 6 所示。一个新的文件将自动生成, 之后您可以启动和使用该应用程序。

图例 6 MetaTrader 5 终端中的信号选项卡

与网站上的 "信号" 服务中显示所有信号不同, 终端中的 "信号" 部分只显示适合当前所选交易账户的信号。 


结束语

在本文中, 我们已经开发了一个简单的评分系统, 基于不同信号特征给出的分值。该系统提供常见的 100 分制交易风格评级。基于这个评级, 所有可用的信号被分为三类, 其 "钱景" 条件不同, 分别是交易风险, 交易账户的生存时间, 和增长百分比。

附件存档包含所有列出的文件, 这些文件位于相应的文件夹中。为了正确操作, 您只需将 MQL5 文件夹保存到终端根目录下即可。该应用程序使用相关 文章 中的图形界面库 EasyAndFastGUI。函数库已略作修改, 也附在这篇文章之后。

本文中使用的程序:

#
 名称
类型
描述
1
AutoSearch.mq5 智能交易系统
 自动选择有 "钱途" 信号的应用程序
2
Program.mqh 代码库  创建应用程序的类
3
MainWindow.mqh 代码库  一组用于创建应用程序的方法