外汇EA编写教程:用 MQL5 语言编写的 20 种交易信号

简介

交易者尝试寻找价格变动的规律,努力制定能够通过使用它们来确定有利的买入时机或卖出时机的规则。为了创建一个全自动系统,您需要了解如何告知此类时机 - 交易信号的来临。

信号告知交易者可能的建仓点位,但是并不是所有这些信号都是必须要执行的。其他标准能够筛选掉大多数信号,但是对我们而言这还不足够。本文旨在说明如何用 MQL5 语言编写最流行的交易信号。

1. 我们知道哪些信号?

确定进入市场时机的所有方法都可以分为几种类型:

  • 移动平均线的相交
  • 范围的突破
  • 依据随机动量指标离开超买/超卖区域
  • 通道边界的反弹
  • 通道边界的突破
  • 趋势的改变

2. 如何更好地编写代码?

有很多方式来编写代码;代码可以写在 OnStart()、OnTick() 和其他函数中。您可以在本网站中于说明文档内找到有关它们的更多详细信息,或在嵌入 MetaEditor 的用户指南中找到更多详细信息;但是,这种方法的效率并不是很高,有时您需要多次编写代码的相同部分。这是我们为什么采取另一方式的原因 – 我们将使用能够从程序代码的任何部分调用的自定义函数。

为了便于使用创建的函数,让我们将它们组合在一个单独的组中,作为外部包含文件,并将其命名为t SignalTrade;它将被存储在 …/MQL5/Include 目录中。这样便能轻松地将此模块连接到任何程序。

尽管所有信号看起来都不相同,但是它们在很多方面是相同的。可以从生成信号的函数得到三种情形:

  • 买入信号
  • 卖出信号
  • 无信号

那么,让我们创建一个对应于这三种信号的枚举。

  •  1 – 买入信号
  • -1 – 卖出信号
  •  0 – 无信号

让我们编写一个返回信号的函数的原型。将我们的函数分为可以在其中执行一个或另一个操作的若干部分。在函数的开头声明和初始化存储数据所需的变量。将在以后从创建的指标加载和检查必要的信息。信息检查对避免在处理数据及使用程序时出现不可预知的后果至关重要。

一旦加载并检查了信息,即开始生成信号。生成信号后,退出函数并作为上述值之一返回获得的信号。

int TradeSignal()
  {
   //--- 0表示信号不存在
   int sig=0;
   
   //--- 检查指标句柄

   //--- 如果所有句柄都无效,创建它们

   //--- 如果句柄有效, 从指标中复制数值

   //--- 检查复制的数据

   //--- 如果复制有错, 从函数中退出

   //--- 把数组索引方式设为倒序

   //--- 如果索引方式有错,退出函数

   //--- 检查条件并设置 sig 的值
 
   //--- 返回交易信号
   return(sig);
  }

3. 20 种交易信号示例

我们将使用不同的指标来获取交易信号。在 MQL5 中,使用特殊的函数来调用指标,例如 iMA、iAC、iMACD、iIchimoku 等;它们在客户端的全局缓存中创建相应技术指标的副本。如果具有相同参数的指标的副本已经存在,则不会创建新的副本,但是指向现有副本的链接的计数器将增大。

这些函数返回对应指标副本的句柄。此外,可以使用此句柄,获得按对应的指标计算的数据。可以使用 CopyBuffer() 函数,将对应缓存的数据(技术指标在它们的内部缓存中包含计算得出的数据;视指标类型而定,它们的数量在 1 至 5 的范围内)复制到 MQL5 程序。

不可能在刚创建好指标之后就立即使用指标的数据,因为有时需要计算指标的值;因此最好的方式是在 OnInit() 内创建句柄。iCustom() 函数创建对应的自定义指标;并且如果创建成功,它将返回指标的句柄。自定义指标最多可包含 512 个指标缓存,也可以使用 CopyBuffer() 函数和获得的句柄获取其内容。

对于每个信号,让我们依据我们的原型 TradeSignal() 创建一个函数,并按以下顺序对这些函数编号:TradeSignal_01() – TradeSignal_20()。让我们详细观察使用基于移动平均线的相交的例子构成一个信号的函数的结构;然后,我们将以类似的方式为其他信号编写函数。

3.1. 移动平均线的相交

图 1. 两条移动平均线的相交

图 1. 两条移动平均线的相交

使用 TradeSignal_01() 函数,我们将获得有关两条移动平均线 (МА) 相交的信号:快的一条移动平均线的周期为 8,慢的一条的周期为 16。

在我们的函数中,我们将仅关注相交的事实并将对应信号的值返回给函数。依据我们的规则和原型,函数看起来如下所示:

int TradeSignal_01()
  {
//--- 0说明没有信号
   int sig=0;

//--- 检查指标的句柄
   if(h_ma1==INVALID_HANDLE)//--- 如果句柄无效
     {
      //--- 再次创建它                                                      
      h_ma1=iMA(Symbol(),Period(),8,0,MODE_SMA,PRICE_CLOSE);
      //--- 退出函数
      return(0);
     }
   else //--- 如果句柄有效
     {
      //--- 把指标值复制到数组中
      if(CopyBuffer(h_ma1,0,0,3,ma1_buffer)<3) //--- 如果数组数据少于所需
         //--- 退出函数
         return(0);
      //--- 设置数组索引方式为倒序                                   
      if(!ArraySetAsSeries(ma1_buffer,true))
         //--- 如果索引出错,退出函数
         return(0);
     }

   if(h_ma2==INVALID_HANDLE)//--- 如果句柄是无效的
     {
      //--- 再次创建它                                                      
      h_ma2=iMA(Symbol(),Period(),16,0,MODE_SMA,PRICE_CLOSE);
      //--- 退出函数
      return(0);
     }
   else //--- 如果句柄有效 
     {
      //--- 把指标值复制到数组中
      if(CopyBuffer(h_ma2,0,0,2,ma2_buffer)<2) //--- 如果数据少于所需
         //--- 退出函数
         return(0);
      //--- 设置数组索引方式为倒序                                   
      if(!ArraySetAsSeries(ma1_buffer,true))
         //--- 如果索引出错,退出函数
         return(0);
     }

//--- 检查条件并设置sig的值
   if(ma1_buffer[2]<ma2_buffer[1] && ma1_buffer[1]>ma2_buffer[1])
      sig=1;
   else if(ma1_buffer[2]>ma2_buffer[1] && ma1_buffer[1]<ma2_buffer[1])
      sig=-1;
   else sig=0;

//--- 返回交易信号
   return(sig);
  }

现在,让我们更加详细地观察代码的每一部分。在函数的开头,我们声明用于存储信号类型的局部变量,并且用零进行初始化,表示没有信号出现。

int sig=0;

接着,我们检查句柄的有效性;如果句柄无效,则我们创建句柄并退出函数,因为指标的计算需要一些时间。这样做是为了避免从指标缓存复制数据时出错。

   if(h_ma1==INVALID_HANDLE)//--- 如果句柄无效
     {
      //--- 再次创建它                                                      
      h_ma1=iMA(Symbol(),Period(),8,0,MODE_SMA,PRICE_CLOSE);
      //--- 退出函数
      return(0);
     }

如果句柄有效,则将数据复制到数组。要分析情形,复制快的 MA 的最后三根指标柱的数据和慢的 MA 的最后两根指标柱的数据就足够了。为此,使用以下函数:

int  CopyBuffer(
   int       indicator_handle,     // 指标句柄
   int       buffer_num,           // 指标缓冲区序号
   int       start_pos,            // 从哪里开始 
   int       count,                // 要复制的数量
   double    buffer[]              // 复制数据的目标数组
   );

对它们进行检查:如果存在的数据比需要的少,则意味着复制出错;进一步引用应在其中存储数据的数组将导致错误。为了避免这种情况,我们退出函数。我们还需要设置数组中的索引化,例如在时间序列中;以下函数就是用于此目的:

bool  ArraySetAsSeries(
   void  array[],     // 数组
   bool  set          // true 说明索引顺序为倒序
   );

如果在数组的索引化期间出错,则退出函数,否则我们会得到不正确的结果。

else //--- 如果句柄有效 
     {
      //--- 把指标值复制到数组中
      if(CopyBuffer(h_ma1,0,0,3,ma1_buffer)<3) //--- 如果数据少于所需
         //--- 退出函数
         return(0);
      //--- 设置数组索引类似于时间序列(倒序)                                   
      if(!ArraySetAsSeries(ma1_buffer,true))
         //--- 如果索引出错,退出函数
         return(0);
     }

现在,已经创建完毕指标并获得了所有必需的信息,让我们继续进行主要的步骤 – 生成信号。

为了消除当前指标柱上信号的不稳定,我们仅分析已收盘的第 1 根和第 2 根指标柱。

生成买入信号。为此,采用快的 MA 在第 2 根指标柱上的值,并将其与慢的 MA 在第 1 根指标柱上的值比较;然后将快的 MA 在第 1 根指标柱上的值与慢的 MA 在第 1 根指标柱上的值进行比较。如果快的 MA 在第 2 根指标柱上的值小于慢的 MA 在第 1 根指标柱上的值,并且快的 MA 在第 1 根指标柱上的值大于慢的 MA 在第 1 根指标柱上的值,则表示快的 MA 向上穿越慢的 MA ;这就是我们的买入信号。如果我们的条件成立,则将 1 写入 sig 变量。

卖出信号以类似的方式生成。如果快的 MA 在第 2 根指标柱上的值大于慢的 MA 在第 1 根指标柱上的值,并且快的 MA 在第 1 根指标柱上的值小于慢的 MA 在第 1 根指标柱上的值,则表示快的 MA 从上到下穿越慢的 MA 。如果我们的条件成立,则将 -1 写入 sig 变量。如果两个条件都不成立,则表示没有信号,因此我们将 0 写入 sig 变量。现在,信号已生成,将获得的信号类型返回给我们的函数 TradeSignal_01()

//--- 检查条件并设置sig的值
   if(ma1_buffer[2]<ma2_buffer[1] && ma1_buffer[1]>ma2_buffer[1])
      sig=1;
   else if(ma1_buffer[2]>ma2_buffer[1] && ma1_buffer[1]<ma2_buffer[1])
      sig=-1;
   else sig=0;

//--- 返回交易信号
   return(sig);

3.2. 主线和 MACD 信号线的相交

图 2. MACD 指标的主线和信号线的相交

TradeSignal_02() 函数中,我们将获得有关 MACD 的信号线和主线相交的信息。如果信号线从上到下穿越主线,则为买入信号。如果信号线从下到上穿越主线,则为卖出信号。其他情形将被视为没有信号。

int TradeSignal_02()
  {
   int sig=0;

   if(h_macd==INVALID_HANDLE)
     {
      h_macd=iMACD(Symbol(),Period(),12,26,9,PRICE_CLOSE);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_macd,0,0,2,macd1_buffer)<2)
         return(0);
      if(CopyBuffer(h_macd,1,0,3,macd2_buffer)<3)
         return(0);
      if(!ArraySetAsSeries(macd1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(macd2_buffer,true))
         return(0);
     }

//--- 检查条件并设置 sig 的值
   if(macd2_buffer[2]>macd1_buffer[1] && macd2_buffer[1]<macd1_buffer[1])
      sig=1;
   else if(macd2_buffer[2]<macd1_buffer[1] && macd2_buffer[1]>macd1_buffer[1])
      sig=-1;
   else sig=0;

//--- 返回交易信号
   return(sig);
  }

3.3. 突破价格通道范围

图 3. 突破价格通道的上下边界

TradeSignal_03() 函数中,我们将接收有关突破价格通道的上下边界的信号。

如果价格穿越价格通道的上边界并且价格固定在此边界的上方,则为买入信号。如果价格穿越价格通道的下边界并且价格固定在此边界的下方,则为卖出信号。其他情形将被视为没有信号。

与前两个函数不同,在这里我们需要一个用于存储收盘价的数组。使用以下函数获得它们:

int  CopyClose(
   string           symbol_name,       // 交易品种名称
   ENUM_TIMEFRAMES  timeframe,          // 周期
   int              start_pos,         // 从哪里开始 
   int              count,             // 需要复制的数量
   double           close_array[]      // 复制收盘价格的目标数组
   );

int TradeSignal_03()
  {
   int sig=0;

   if(h_pc==INVALID_HANDLE)
     {
      h_pc=iCustom(Symbol(),Period(),"Price Channel",22);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_pc,0,0,3,pc1_buffer)<3)
         return(0);
      if(CopyBuffer(h_pc,1,0,3,pc2_buffer)<3)
         return(0);
      if(CopyClose(Symbol(),Period(),0,2,Close)<2)
         return(0);
      if(!ArraySetAsSeries(pc1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(pc2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- 检查条件并设置sig的值
   if(Close[1]>pc1_buffer[2])
      sig=1;
   else if(Close[1]<pc2_buffer[2])
      sig=-1;
   else sig=0;

//--- 返回交易信号
   return(sig);
  }

3.4. 突破 ADX 自适应通道的范围

图 4. 突破 ADX 自适应通道的上下边界

使用 TradeSignal_04() 函数,我们将获得有关突破 ADX 自适应通道的上下边界的信息。

如果价格穿越 ADX 自适应通道的上边界并且收盘价固定在此边界的上方,则为买入信号。如果价格穿越价格通道的下边界并且收盘价固定在此边界的下方,则为卖出信号。其他情形将被视为没有信号。

int TradeSignal_04()
  {
   int sig=0;

   if(h_acadx==INVALID_HANDLE)
     {
      h_acadx=iCustom(Symbol(),Period(),"AdaptiveChannelADX",14);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_acadx,0,0,2,acadx1_buffer)<2)
         return(0);
      if(CopyBuffer(h_acadx,1,0,2,acadx2_buffer)<2)
         return(0);
      if(CopyClose(Symbol(),Period(),0,2,Close)<2)
         return(0);
      if(!ArraySetAsSeries(acadx1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(acadx2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- 检查条件并设置sig的值
   if(Close[1]>acadx1_buffer[1])
      sig=1;
   else if(Close[1]<acadx2_buffer[1])
      sig=-1;
   else sig=0;

//--- 返回交易信号
   return(sig);
  }

3.5. 离开随机动量指标的超买/超卖区域

图 5. 穿越随机动量指标的超买和超卖水平

使用 TradeSignal_05(),我们将获得有关随机动量指标离开超买/超卖区域的信号;这些区域的水平将是值为 80 和 20 的水平

我们在动量指标(%K 或 %D)跌到某一水平(通常为 20)) 下方,然后再升到该水平上方时买入。我们在动量指标升到某一水平(通常为 80)上方,然后再跌到该水平下方时卖出。

int TradeSignal_05()
  {
   int sig=0;

   if(h_stoh==INVALID_HANDLE)
     {
      h_stoh=iStochastic(Symbol(),Period(),5,3,3,MODE_SMA,STO_LOWHIGH);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_stoh,0,0,3,stoh_buffer)<3)
         return(0);

      if(!ArraySetAsSeries(stoh_buffer,true))
         return(0);
     }
//--- 检查条件并设置 sig 的值
   if(stoh_buffer[2]<20 && stoh_buffer[1]>20)
      sig=1;
   else if(stoh_buffer[2]>80 && stoh_buffer[1]<80)
      sig=-1;
   else sig=0;

//--- 返回交易信号
   return(sig);
  }

3.6. 离开 RSI 指标的超买/超卖区域

图 6. 穿越 RSI 指标的超买和超卖水平

使用 TradeSignal_06() 函数,我们将获得有关 RSI 指标离开超买/超卖区域的信号;该区域的水平是值为 70 和 30 的水平

我们在 RSI 跌到某一水平(通常为 30)下方,然后再升到该水平上方时买入。我们在 RSI 升到某一水平(通常为 70)上方,然后再跌到该水平下方时卖出。

int TradeSignal_06()
  {
   int sig=0;

   if(h_rsi==INVALID_HANDLE)
     {
      h_rsi=iRSI(Symbol(),Period(),14,PRICE_CLOSE);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_rsi,0,0,3,rsi_buffer)<3)
         return(0);

      if(!ArraySetAsSeries(rsi_buffer,true))
         return(0);
     }
//--- 检查条件并设置 sig 的值
   if(rsi_buffer[2]<30 && rsi_buffer[1]>30)
      sig=1;
   else if(rsi_buffer[2]>70 && rsi_buffer[1]<70)
      sig=-1;
   else sig=0;

//--- 返回交易信号
   return(sig);
 

3.7. 离开 CCI 指标的超买/超卖区域

图 7. 穿越 CCI 指标的超买和超卖水平

使用 TradeSignal_07() 函数,我们将获得有关 CCI 指标离开超买/超卖区域的信号;该区域的水平是值为 100 和 -100 的水平

我们在 CCI 跌到水平 -100 下方,然后再升到该水平上方时买入。我们在 CCI 升到水平 100 上方,然后再跌到该水平下方时卖出。

int TradeSignal_07()
  {
   int sig=0;

   if(h_cci==INVALID_HANDLE)
     {
      h_cci=iCCI(Symbol(),Period(),14,PRICE_TYPICAL);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_cci,0,0,3,cci_buffer)<3)
         return(0);

      if(!ArraySetAsSeries(cci_buffer,true))
         return(0);
     }
//--- 检查条件并设置 sig 的值
   if(cci_buffer[2]<-100 && cci_buffer[1]>-100)
      sig=1;
   else if(cci_buffer[2]>100 && cci_buffer[1]<100)
      sig=-1;
   else sig=0;

//--- 返回交易信号
   return(sig);
  }

3.8. 离开威廉动量指标的超买/超卖区域

图 8. 穿越威廉动量指标的超买和超卖水平

使用 TradeSignal_08() 函数,我们将获得有关威廉动量指标离开超买/超卖区域的信号;该区域的水平是值为 -20 和 -80 的水平

我们在威廉动量指标跌到水平 -80 的下方,然后再升到该水平的上方买入。我们在威廉动量指标升到水平 -20 上方,然后再跌到该水平下方时卖出。

int TradeSignal_08()
  {
   int sig=0;

   if(h_wpr==INVALID_HANDLE)
     {
      h_wpr=iWPR(Symbol(),Period(),14);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_wpr,0,0,3,wpr_buffer)<3)
         return(0);

      if(!ArraySetAsSeries(wpr_buffer,true))
         return(0);
     }
//--- 检查条件并设置 sig 的值
   if(wpr_buffer[2]<-80 && wpr_buffer[1]>-80)
      sig=1;
   else if(wpr_buffer[2]>-20 && wpr_buffer[1]<-20)
      sig=-1;
   else sig=0;

//--- 返回交易信号
   return(sig);
  }

3.9. 布林通道边界反弹

图 9. 布林通道边界的价格反弹

使用 TradeSignal_09() 函数,我们将在价格于布林通道的边界反弹时获得信号。

如果价格在穿越或碰到布林通道的上边界时返回,则为卖出信号。如果价格在穿越或碰到布林通道的下边界时返回,则为买入信号。

int TradeSignal_09()
  {
   int sig=0;

   if(h_bb==INVALID_HANDLE)
     {
      h_bb=iBands(Symbol(),Period(),20,0,2,PRICE_CLOSE);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_bb,1,0,2,bb1_buffer)<2)
         return(0);
      if(CopyBuffer(h_bb,2,0,2,bb2_buffer)<2)
         return(0);
      if(CopyClose(Symbol(),Period(),0,3,Close)<3)
         return(0);
      if(!ArraySetAsSeries(bb1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(bb2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- 检查条件并设置 sig 的值
   if(Close[2]<=bb2_buffer[1] && Close[1]>bb2_buffer[1])
      sig=1;
   else if(Close[2]>=bb1_buffer[1] && Close[1]<bb1_buffer[1])
      sig=-1;
   else sig=0;

//--- 返回交易信号
   return(sig);
  }

3.10. 标准差通道边界反弹

图 10. 标准差通道边界的价格反弹

使用 TradeSignal_10() 函数,我们将在价格于标准差通道的边界反弹时获得信号。

如果价格在穿越或碰到标准差通道的上边界时返回,则为卖出信号。如果价格在穿越或碰到标准差通道的下边界时返回,则为买入信号。

int TradeSignal_10()
  {
   int sig=0;

   if(h_sdc==INVALID_HANDLE)
     {
      h_sdc=iCustom(Symbol(),Period(),"StandardDeviationChannel",14,0,MODE_SMA,PRICE_CLOSE,2.0);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_sdc,0,0,2,sdc1_buffer)<2)
         return(0);
      if(CopyBuffer(h_sdc,1,0,2,sdc2_buffer)<2)
         return(0);
      if(CopyClose(Symbol(),Period(),0,3,Close)<3)
         return(0);
      if(!ArraySetAsSeries(sdc1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(sdc2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- 检查条件并设置 sig 的值
   if(Close[2]<=sdc2_buffer[1] && Close[1]>sdc2_buffer[1])
      sig=1;
   else if(Close[2]>=sdc1_buffer[1] && Close[1]<sdc1_buffer[1])
      sig=-1;
   else sig=0;

//--- 返回交易信号
   return(sig);
  }

3.11. 价格通道边界反弹

图 11. 价格通道边界的价格反弹

使用 TradeSignal_11() 函数,我们将在价格于价格通道的边界反弹时获得信号。

如果价格在穿越或碰到价格通道的上边界时返回,则为卖出信号。如果价格在穿越或碰到价格通道的下边界时返回,则为买入信号。

int TradeSignal_11()
  {
   int sig=0;

   if(h_pc==INVALID_HANDLE)
     {
      h_pc=iCustom(Symbol(),Period(),"Price Channel",22);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_pc,0,0,4,pc1_buffer)<4)
         return(0);
      if(CopyBuffer(h_pc,1,0,4,pc2_buffer)<4)
         return(0);
      if(CopyClose(Symbol(),Period(),0,3,Close)<3)
         return(0);
      if(!ArraySetAsSeries(pc1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(pc2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- 检查条件并设置 sig 的值
   if(Close[1]>pc2_buffer[2] && Close[2]<=pc2_buffer[3])
      sig=1;
   else if(Close[1]<pc1_buffer[2] && Close[2]>=pc1_buffer[3])
      sig=-1;
   else sig=0;

//--- 返回交易信号
   return(sig);
  }

3.12. 包络线通道边界反弹

图 12. 包络线通道边界的价格反弹

使用 TradeSignal_12() 函数,我们将在价格于包络线通道的边界反弹时获得信号。

如果价格在穿越或碰到包络线通道的上边界时返回,则为卖出信号。如果价格在穿越或碰到包络线通道的下边界时返回,则为买入信号。

int TradeSignal_12()
  {
   int sig=0;

   if(h_env==INVALID_HANDLE)
     {
      h_env=iEnvelopes(Symbol(),Period(),28,0,MODE_SMA,PRICE_CLOSE,0.1);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_env,0,0,2,env1_buffer)<2)
         return(0);
      if(CopyBuffer(h_env,1,0,2,env2_buffer)<2)
         return(0);
      if(CopyClose(Symbol(),Period(),0,3,Close)<3)
         return(0);
      if(!ArraySetAsSeries(env1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(env2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- 检查条件并设置 sig 的值
   if(Close[2]<=env2_buffer[1] && Close[1]>env2_buffer[1])
      sig=1;
   else if(Close[2]>=env1_buffer[1] && Close[1]<env1_buffer[1])
      sig=-1;
   else sig=0;

//--- 返回交易信号
   return(sig);
  }

3.13. 突破唐奇安通道

图 13. 突破唐奇安通道的边界

TradeSignal_13() 函数中,我们将在价格穿越唐奇安通道的边界时获得信号。

如果价格穿越唐奇安通道的上边界并且收盘价固定在此边界的上方,则为买入信号。如果价格穿越唐奇安通道的下边界并且收盘价固定在此边界的下方,则为卖出信号。

int TradeSignal_13()
  {
   int sig=0;

   if(h_dc==INVALID_HANDLE)
     {
      h_dc=iCustom(Symbol(),Period(),"Donchian Channels",24,3,-2);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_dc,0,0,3,dc1_buffer)<3)
         return(0);
      if(CopyBuffer(h_dc,1,0,3,dc2_buffer)<3)
         return(0);
      if(CopyClose(Symbol(),Period(),0,2,Close)<2)
         return(0);
      if(!ArraySetAsSeries(dc1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(dc2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- 检查条件并设置 sig 的值
   if(Close[1]>dc1_buffer[2])
      sig=1;
   else if(Close[1]<dc2_buffer[2])
      sig=-1;
   else sig=0;

//--- 返回交易信号
   return(sig);
  }

3.14. 突破银通道

图 14. 突破银通道的边界

TradeSignal_14() 函数中,我们将在价格穿越银通道边界时获得信号。银通道指标绘制 8 条边界,这些边界也可用作支撑位和阻力位。为了获得信号,我们将使用 2 条中间边界。

如果价格穿越银通道的上边界并且收盘价固定在此边界的上方,则为买入信号。如果价格穿越银通道的下边界并且收盘价固定在此边界的下方,则为卖出信号。

int TradeSignal_14()
  {
   int sig=0;

   if(h_sc==INVALID_HANDLE)
     {
      h_sc=iCustom(Symbol(),Period(),"Silver-channels",26,38.2,23.6,0,61.8);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_sc,0,0,2,sc1_buffer)<2)
         return(0);
      if(CopyBuffer(h_sc,1,0,2,sc2_buffer)<2)
         return(0);
      if(CopyClose(Symbol(),Period(),0,3,Close)<3)
         return(0);
      if(!ArraySetAsSeries(sc1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(sc2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- 检查条件并设置 sig 的值
   if(Close[2]<sc1_buffer[1] && Close[1]>sc1_buffer[1])
      sig=1;
   else if(Close[2]>sc2_buffer[1] && Close[1]<sc2_buffer[1])
      sig=-1;
   else sig=0;

//--- 返回交易信号
   return(sig);
  }

3.15. 突破加拉赫通道

图 15. 突破加拉赫通道的边界

TradeSignal_15() 函数中,我们将在价格穿越加拉赫通道的边界时获得信号。加拉赫通道指标是按 10 天的最高价和最低价绘制的。

如果价格穿越加拉赫通道的上边界并且收盘价固定在此边界的上方,则为买入信号。如果价格穿越加拉赫通道的下边界并且收盘价固定在此边界的下方,则为卖出信号。

int TradeSignal_15()
  {
   int sig=0;

   if(h_gc==INVALID_HANDLE)
     {
      h_gc=iCustom(Symbol(),Period(),"PriceChannelGalaher");
      return(0);
     }
   else
     {
      if(CopyBuffer(h_gc,0,0,3,gc1_buffer)<3)
         return(0);
      if(CopyBuffer(h_gc,1,0,3,gc2_buffer)<3)
         return(0);
      if(CopyClose(Symbol(),Period(),0,2,Close)<2)
         return(0);
      if(!ArraySetAsSeries(gc1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(gc2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- 检查条件并设置 sig 的值
   if(Close[1]>gc1_buffer[2])
      sig=1;
   else if(Close[1]<gc2_buffer[2])
      sig=-1;
   else sig=0;

//--- 返回交易信号
   return(sig);
  }

3.16. NRTR 趋势的改变

图 16. 使用 NRTR 指标识别趋势改变

TradeSignal_16() 函数中,我们将在 NTTR 趋势改变时获得信号。

如果 NRTR 指标显示上升趋势,则为买入信号。如果 NRTR 指标显示下降趋势,则为卖出信号。

int TradeSignal_16()
  {
   int sig=0;

   if(h_nrtr==INVALID_HANDLE)
     {
      h_nrtr=iCustom(Symbol(),Period(),"NRTR",40,2.0);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_nrtr,0,0,2,nrtr1_buffer)<2)
         return(0);
      if(CopyBuffer(h_nrtr,1,0,2,nrtr2_buffer)<2)
         return(0);
      if(!ArraySetAsSeries(nrtr1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(nrtr2_buffer,true))
         return(0);
     }
//--- 检查条件并设置 sig 的值
   if(nrtr1_buffer[1]>0)
      sig=1;
   else if(nrtr2_buffer[1]>0)
      sig=-1;
   else sig=0;

//--- 返回交易信号
   return(sig);
  }

3.17. 鳄鱼指标趋势的改变

图 17. 鳄鱼指标趋势的改变

TradeSignal_17() 函数中,我们将在鳄鱼指标趋势改变时获得信号。

如果颚、齿和唇线闭合或缠绕在一起,则鳄鱼即将睡觉或已经在睡觉。当鳄鱼睡觉时,其饥饿程度会增大;因此,睡得越久,醒来时就越饥饿。它醒来的第一件事就是张开大嘴并开始打哈欠。接着,它开始嗅探食物 – 牛肉或熊肉,然后它开始进食。一旦鳄鱼吃饱了,它失去对食物-价格的兴趣(平衡线汇聚在一起);这正是兑现利润的时候。

int TradeSignal_17()
  {
   int sig=0;

   if(h_al==INVALID_HANDLE)
     {
      h_al=iAlligator(Symbol(),Period(),13,0,8,0,5,0,MODE_SMMA,PRICE_MEDIAN);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_al,0,0,2,al1_buffer)<2)
         return(0);
      if(CopyBuffer(h_al,1,0,2,al2_buffer)<2)
         return(0);
      if(CopyBuffer(h_al,2,0,2,al3_buffer)<2)
         return(0);
      if(!ArraySetAsSeries(al1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(al2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(al3_buffer,true))
         return(0);
     }
//--- 检查条件并设置 sig 的值
   if(al3_buffer[1]>al2_buffer[1] && al2_buffer[1]>al1_buffer[1])
      sig=1;
   else if(al3_buffer[1]<al2_buffer[1] && al2_buffer[1]<al1_buffer[1])
      sig=-1;
   else sig=0;

//--- 返回交易信号
   return(sig);
  }

3.18. AMA 趋势的改变

图 18. AMA 趋势的改变

TradeSignal_18() 函数中,我们将在 AMA 趋势改变时获得信号。

如果 AMA 指标显示上升趋势,则为买入信号。如果 AMA 指标显示下降趋势,则为买入信号。

int TradeSignal_18()
  {
   int sig=0;

   if(h_ama==INVALID_HANDLE)
     {
      h_ama=iAMA(Symbol(),Period(),9,2,30,0,PRICE_CLOSE);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_ama,0,0,3,ama_buffer)<3)
         return(0);
      if(!ArraySetAsSeries(ama_buffer,true))
         return(0);
     }
//--- 检查条件并设置 sig 的值
   if(ama_buffer[2]<ama_buffer[1])
      sig=1;
   else if(ama_buffer[2]>ama_buffer[1])
      sig=-1;
   else sig=0;

//--- 返回交易信号
   return(sig);
  }

3.19. 动量振荡指标颜色的改变

图 19. 使用动量振荡指标识别趋势改变

TradeSignal_19() 函数中,我们将在动量动荡指标柱状图的颜色改变时获得信号。

MQL5 的特点之一是能够为指标创建缓存,可以在其中存储为 #property indicator_colorN 属性设定的线条颜色的索引。当动量振荡指标柱状图的颜色是绿色时,则为买入信号。如果颜色变为红色,则为卖出信号。

int TradeSignal_19()
  {
   int sig=0;

   if(h_ao==INVALID_HANDLE)
     {
      h_ao=iAO(Symbol(),Period());
      return(0);
     }
   else
     {
      if(CopyBuffer(h_ao,1,0,20,ao_buffer)<20)
         return(0);
      if(!ArraySetAsSeries(ao_buffer,true))
         return(0);
     }
//--- 检查条件并设置 sig 的值
   if(ao_buffer[1]==0)
      sig=1;
   else if(ao_buffer[1]==1)
      sig=-1;
   else sig=0;

//--- 返回交易信号
   return(sig);
  }

3.20. Ichimoku 趋势的改变

图 20. 使用 Ichimoku 指标识别趋势改变

TradeSignal_20() 函数中,我们将在 Ichimoku 趋势改变时获得信号。为此,我们将分析信号 (Tenkan-sen) 线和基础 ( Kijun-sen) 线的相交。

当信号线从下到上穿越基础线时生成买入信号。从上到下的穿越即为卖出信号。

int TradeSignal_20()
  {
   int sig=0;

   if(h_ich==INVALID_HANDLE)
     {
      h_ich=iIchimoku(Symbol(),Period(),9,26,52);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_ich,0,0,2,ich1_buffer)<2)
         return(0);
      if(CopyBuffer(h_ich,1,0,2,ich2_buffer)<2)
         return(0);
      if(!ArraySetAsSeries(ich1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(ich2_buffer,true))
         return(0);
     }
//--- 检查条件并设置 sig 的值
   if(ich1_buffer[1]>ich2_buffer[1])
      sig=1;
   else if(ich1_buffer[1]<ich2_buffer[1])
      sig=-1;
   else sig=0;

//--- 返回交易信号
   return(sig);
  }

4. 使其成为一个指标

现在,我们已经有准备就绪的指标构建模块,可以开始编写依据所有选择的方法显示信号的指标。使用创建的模板,我们能够实施来自任意指标的信号的接收;正确地对信号条件进行公式化并将其添加到代码就已足够。

让我们编写一个含有不可更改的内置参数的指标。采用箭头的形式在图表的右侧部分上绘制指标信号(箭头向上 – 买入,向下 – 卖出,叉号 – 无信号)。让我们采用标准 Wingdings 字体来绘制箭头。我们还需要其他几个函数,在交易品种的图表上显示信号的相关信息。我们将它们组合在一个单独的代码块中,作为可用于通过向其添加新的函数来编写您自己的程序的库。让我们将这个库命名为 LibFunctions。

在我们将来的指标的头部中,编写文件与信号生成函数的连接、并且导入函数,这些函数是显示信号图形所必需的,以及声明用于存储从指标收到的信号的类型的作用于全局范围内的变量。

//--- 连接所需要的函数库
#include <SignalTrade.mqh>
//--- 从 LibFunctions 库中导入函数
#import "LibFunctions.ex5"
void SetLabel(string nm,string tx,ENUM_BASE_CORNER cn,ENUM_ANCHOR_POINT cr,int xd,int yd,string fn,int fs,double yg,color ct);
string arrow(int sig);
color Colorarrow(int sig);
#import
//+------------------------------------------------------------------+ 
//| 声明用于保存指标信号的变量                                           |
//+------------------------------------------------------------------+ 
int SignalMA;
int SignalMACD;
int SignalPC;
int SignalACADX;
int SignalST;
int SignalRSI;
int SignalCCI;
int SignalWPR;
int SignalBB;
int SignalSDC;
int SignalPC2;
int SignalENV;
int SignalDC;
int SignalSC;
int SignalGC;
int SignalNRTR;
int SignalAL;
int SignalAMA;
int SignalAO;
int SignalICH;

如我先前所述,指标仅被加载到客户端一次,并且创建到该指标的指针(句柄);这是为什么我们在 OnInit() 函数中创建它们的原因,因为此函数仅在程序启动时运行一次。

int OnInit()
  {
//--- 创建指标句柄
   h_ma1=iMA(Symbol(),Period(),8,0,MODE_SMA,PRICE_CLOSE);
   h_ma2=iMA(Symbol(),Period(),16,0,MODE_SMA,PRICE_CLOSE);
   h_macd=iMACD(Symbol(),Period(),12,26,9,PRICE_CLOSE);
   h_pc=iCustom(Symbol(),Period(),"Price Channel",22);
   h_acadx=iCustom(Symbol(),Period(),"AdaptiveChannelADX",14);
   h_stoh=iStochastic(Symbol(),Period(),5,3,3,MODE_SMA,STO_LOWHIGH);
   h_rsi=iRSI(Symbol(),Period(),14,PRICE_CLOSE);
   h_cci=iCCI(Symbol(),Period(),14,PRICE_TYPICAL);
   h_wpr=iWPR(Symbol(),Period(),14);
   h_bb=iBands(Symbol(),Period(),20,0,2,PRICE_CLOSE);
   h_sdc=iCustom(Symbol(),Period(),"StandardDeviationChannel",14,0,MODE_SMA,PRICE_CLOSE,2.0);
   h_env=iEnvelopes(Symbol(),Period(),28,0,MODE_SMA,PRICE_CLOSE,0.1);
   h_dc=iCustom(Symbol(),Period(),"Donchian Channels",24,3,-2);
   h_sc=iCustom(Symbol(),Period(),"Silver-channels",26,38.2,23.6,0,61.8);
   h_gc=iCustom(Symbol(),Period(),"PriceChannelGalaher");
   h_nrtr=iCustom(Symbol(),Period(),"NRTR",40,2.0);
   h_al=iAlligator(Symbol(),Period(),13,0,8,0,5,0,MODE_SMMA,PRICE_MEDIAN);
   h_ama=iAMA(Symbol(),Period(),9,2,30,0,PRICE_CLOSE);
   h_ao=iAO(Symbol(),Period());
   h_ich=iIchimoku(Symbol(),Period(),9,26,52);
   return(0);
  }

所有的主要计算都在 OnCalculate() 函数中进行;我们将在其中放置余下的指标代码。

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---把信号值赋给变量
   SignalMA    = TradeSignal_01();
   SignalMACD  = TradeSignal_02();
   SignalPC    = TradeSignal_03();
   SignalACADX = TradeSignal_04();
   SignalST    = TradeSignal_05();
   SignalRSI   = TradeSignal_06();
   SignalCCI   = TradeSignal_07();
   SignalWPR   = TradeSignal_08();
   SignalBB    = TradeSignal_09();
   SignalSDC   = TradeSignal_10();
   SignalPC2   = TradeSignal_11();
   SignalENV   = TradeSignal_12();
   SignalDC    = TradeSignal_13();
   SignalSC    = TradeSignal_14();
   SignalGC    = TradeSignal_15();
   SignalNRTR  = TradeSignal_16();
   SignalAL    = TradeSignal_17();
   SignalAMA   = TradeSignal_18();
   SignalAO    = TradeSignal_19();
   SignalICH   = TradeSignal_20();

//--- 在图表左上角画图形对象
   int size=((int)ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS)/22);
   int i=0;
   int x=10;
   int y=0;
   int fz=size-4;

   y+=size;
   SetLabel("arrow"+(string)i,arrow(SignalMA),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalMA));
   x+=size;
   SetLabel("label"+(string)i,"Moving Average",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalMACD),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalMACD));
   x+=size;
   SetLabel("label"+(string)i,"MACD",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalPC),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalPC));
   x+=size;
   SetLabel("label"+(string)i,"Price Channell",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalACADX),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalACADX));
   x+=size;
   SetLabel("label"+(string)i,"Adaptive Channel ADX",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalST),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalST));
   x+=size;
   SetLabel("label"+(string)i,"Stochastic Oscillator",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalRSI),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalRSI));
   x+=size;
   SetLabel("label"+(string)i,"RSI",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalCCI),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalCCI));
   x+=size;
   SetLabel("label"+(string)i,"CCI",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalWPR),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalWPR));
   x+=size;
   SetLabel("label"+(string)i,"WPR",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalBB),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalBB));
   x+=size;
   SetLabel("label"+(string)i,"Bollinger Bands",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalSDC),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalSDC));
   x+=size;
   SetLabel("label"+(string)i,"StDevChannel",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalPC2),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalPC2));
   x+=size;
   SetLabel("label"+(string)i,"Price Channell 2",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalENV),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalENV));
   x+=size;
   SetLabel("label"+(string)i,"Envelopes",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalDC),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalDC));
   x+=size;
   SetLabel("label"+(string)i,"Donchian Channels",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalSC),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalSC));
   x+=size;
   SetLabel("label"+(string)i,"Silver-channels",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalGC),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalGC));
   x+=size;
   SetLabel("label"+(string)i,"Galaher Channel",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalNRTR),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalNRTR));
   x+=size;
   SetLabel("label"+(string)i,"NRTR",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalAL),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalAL));
   x+=size;
   SetLabel("label"+(string)i,"Alligator",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalAMA),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalAMA));
   x+=size;
   SetLabel("label"+(string)i,"AMA",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalAO),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalAO));
   x+=size;
   SetLabel("label"+(string)i,"Awesome oscillator",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalICH),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalICH));
   x+=size;
   SetLabel("label"+(string)i,"Ichimoku Kinko Hyo",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);

   return(rates_total);
  }

好了,我们的指标准备就绪了。最后,我们在图表上显示以下画面。

5. 使其成为一个 EA 交易程序

我们能够以类似的方式编写一个在图表上显示指标信号的 EA 交易程序。让我们实施一个含有图形控件的信息系统。有可能通过图形界面选择必要的指标并设定其参数。

我们不讨论图形界面的实施;您可以在“用 MQL5 创建交易活动控制板”一文中找到相关信息。

为了通过我们的图形界面更改指标的设置,让我们改进我们的 SignalTrade.mqh 库,并将其命名为 SignalTradeExp.mqh。

首先,我们需要用于存储指标设置的其他变量。

//--- 移动平均指标的输入参数
int                periodma1=8;
int                periodma2=16;
ENUM_MA_METHOD     MAmethod=MODE_SMA;
ENUM_APPLIED_PRICE MAprice=PRICE_CLOSE;
//--- MACD 指标输入参数
int                FastMACD=12;
int                SlowMACD=26;
int                MACDSMA=9;
ENUM_APPLIED_PRICE MACDprice=PRICE_CLOSE;
//--- Price Channel 指标输入参数
int                PCPeriod=22;
//--- Adaptive Channel ADX 指标输入参数
int                ADXPeriod=14;
//--- 随机振荡指标输入参数
int                SOPeriodK=5;
int                SOPeriodD=3;
int                SOslowing=3;
ENUM_MA_METHOD     SOmethod=MODE_SMA;
ENUM_STO_PRICE     SOpricefield=STO_LOWHIGH;
//--- RSI 指标输入参数
int                RSIPeriod=14;
ENUM_APPLIED_PRICE RSIprice=PRICE_CLOSE;
//--- CCI 指标输入参数
int                CCIPeriod=14;
ENUM_APPLIED_PRICE CCIprice=PRICE_TYPICAL;
//--- WPR 指标输入参数
int                WPRPeriod=14;
//--- Bollinger Bands 指标输入参数
int                BBPeriod=20;
double             BBdeviation=2.0;
ENUM_APPLIED_PRICE BBprice=PRICE_CLOSE;
//--- Standard Deviation Channel 指标输入参数
int                SDCPeriod=14;
double             SDCdeviation=2.0;
ENUM_APPLIED_PRICE SDCprice=PRICE_CLOSE;
ENUM_MA_METHOD     SDCmethod=MODE_SMA;
//--- Price Channel 2 指标输入参数
int                PC2Period=22;
//--- Envelopes 指标输入参数
int                ENVPeriod=14;
double             ENVdeviation=0.1;
ENUM_APPLIED_PRICE ENVprice=PRICE_CLOSE;
ENUM_MA_METHOD     ENVmethod=MODE_SMA;
//--- Donchian Channels 指标输入参数
int                DCPeriod=24;
int                DCExtremes=3;
int                DCMargins=-2;
//--- Silver-channels 指标输入参数
int                SCPeriod=26;
double             SCSilvCh=38.2;
double             SCSkyCh=23.6;
double             SCFutCh=61.8;
//--- NRTR 指标输入参数
int                NRTRPeriod   =  40;
double             NRTRK        =  2.0;
//--- Alligator 指标输入参数
int                ALjawperiod=13;
int                ALteethperiod=8;
int                ALlipsperiod=5;
ENUM_MA_METHOD     ALmethod=MODE_SMMA;
ENUM_APPLIED_PRICE ALprice=PRICE_MEDIAN;
//--- AMA 指标输入参数
int                AMAperiod=9;
int                AMAfastperiod=2;
int                AMAslowperiod=30;
ENUM_APPLIED_PRICE AMAprice=PRICE_CLOSE;
//--- Ichimoku Kinko Hyo 指标输入参数
int                IKHtenkansen=9;
int                IKHkijunsen=26;
int                IKHsenkouspanb=52;

用变量更换指标的常量值。其他的保持不变。

h_ma1=iMA(Symbol(),Period(),periodma1,0,MAmethod,MAprice);

要点在于经济地使用计算机内存;在更改设置时,必须卸载含有旧设置的指标副本,并加载新的副本。可以使用以下函数实现这一点:

bool  IndicatorRelease(
   int       indicator_handle,     // 指标句柄
   );

   if(id==CHARTEVENT_OBJECT_ENDEDIT && sparam=="PIPSetEditMA2")
     {
      periodma2=(int)ObjectGetString(0,"PIPSetEditMA2",OBJPROP_TEXT);
      ObjectSetString(0,"PIPSetEditMA2",OBJPROP_TEXT,(string)periodma2);
      //--- 卸载指标的旧拷贝
      IndicatorRelease(h_ma2);
      //--- 创建指标新拷贝
      h_ma2=iMA(Symbol(),Period(),periodma2,0,MAmethod,MAprice);
      ChartRedraw();
     }

总结

这样,我们学习了如何从指标读取信息以及如何将其传递到 EA 交易程序的方法。通过这种方式,您可以从任何指标获取信号。

  • 应将 SignalTrade.mq5、AdaptiveChannelADX.mq5、Donchian Channels.mq5、NRTR.mq5、Price Channel.mq5、PriceChannelGalaher.mq5、Silver-channels.mq5、StandardDeviationChannel.mq5 指标的文件复制到 …/MQL5/Indicators 文件夹。
  • 应将 SignalTrade.mqh 和 SignalTradeExp.mqh 包含文件复制到 …/MQL5/Include 文件夹。
  • 应将 LibFunctions.mq5 函数库复制到 …/MQL5/Libraries 文件夹。
  • 应将 ExpSignalTrade.mq5 Expert Advisor 复制到 …/MQL5/Experts。

本文译自 MetaQuotes Software Corp. 撰写的俄文原文
原文地址: https://www.mql5.com/ru/articles/130

附加的文件 |

下载ZIP
indicators.zip
(7.98 KB)
expsignaltrade.mq5
(330.98 KB)
libfunctions__1.mq5
(9.48 KB)
signaltrade.mq5
(20.47 KB)
signaltrade.mqh
(36.65 KB)
signaltradeexp.mqh
(42.35 KB)

 

 


MyFxtop迈投-靠谱的外汇跟单社区,免费跟随高手做交易!

 

免责声明:本文系转载自网络,如有侵犯,请联系我们立即删除,另:本文仅代表作者个人观点,与迈投财经无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。

著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。

風險提示

MyFxtops邁投所列信息僅供參考,不構成投資建議,也不代表任何形式的推薦或者誘導行為。MyFxtops邁投非外匯經紀商,不接觸妳的任何資金。 MYFXTOPS不保證客戶盈利,不承擔任何責任。從事外彙和差價合約等金融產品的槓桿交易具有高風險,損失有可能超過本金,請量力而行,入市前需充分了解潛在的風險。過去的交易成績並不代表以後的交易成績。依據各地區法律法規,MyFxtops邁投不向中國大陸、美國、加拿大、朝鮮居民提供服務。

邁投公眾號

聯繫我們

客服QQ:981617007
Email: service@myfxtop.com

MyFxtops 邁投