外汇EA编写教程:mql5向导:按计算出的价格下订单、止损和止损。标准库扩展

介绍

MQL5标准库是开发需要严格系统的大型项目的有用辅助工具。在会话模式下,MQL5向导在几分钟内将现成的部件组装到广泛的计划中的能力是不可低估的。mql5向导自动收集EA部件,并根据它们的句柄自动在EA中声明模块参数。当集成了大量不同的模块时,这种自动化可以节省大量的时间和日常操作。

这听起来不错,但有一个明显的缺点——向导在标准类基础上创建的交易系统的能力是有限的。本文提出了一种显著扩展所创建的EA功能的通用方法。当实现此方法时,它可以与向导和标准模块保持相同的兼容性。

这种方法的思想是使用面向对象编程的继承和多态机制,或者在生成EA时创建类而不是标准类代码。这样,向导和标准库尔德人的所有优势都可以被利用,其结果是开发的EA具有所需的能力。为了实现这一目标,代码的数量减少了一点,只有四个字符串。

本文的实际目的是提高企业产生EA的能力,以所需的价格订货,止损止损,止损止损,而不仅仅是从当前价格到规定的距离。

类似的想法也在文章“MQL5指南:如何教EA以任何价格注册”中讨论。该方案的明显缺点是“强制”改变事务信号模块的参数。这种方法不能传递性地集成大量的模块一起工作,并且使用向导进行优化也没有意义。

在继承自标准库的类中以任何价格实现命令、停止和停止将在后面详细研究。也就是说,模块之间不可能有任何冲突。我希望本文中提供的示例能够激励读者编写自己的代码来改进标准框架,并允许用户实现库扩展。

1。标准决策算法

由MQL5向导生成的EA基于专家类实例。指向对象cexpertsignal类的指针在此类中声明。此外,为了简洁起见,本文中的对象将调用主信号。主信号包含一个定向从属滤波器(信号模块是cexpertsignal类的继承者)。

如果没有位置和订单,则EA将在新的实时报价到达时参考主信号检查打开时间。主信号逐个对下级滤波器进行轮询,并根据得到的预测值计算加权平均预测(方向)。如果其值超过阈值(主信号的M_阈值_打开参数值),则订单参数和bool类型条件检查结果将传递给EA。如果满足这些条件,要么按市价开仓,要么按一定距离挂单(见图例)。1)。止损只能放在固定的距离。市场价格的开盘、停盘和停盘距离在EA设置中指定,分别存储在主信号的M 57997;级、M级和M取级变量中。

因此,如果满足这两个条件,则下订单:

  1. 目前的品种还没有开放。
  2. 加权平均预测值的绝对值超过了阈值,说明趋势非常强烈。

图例. 1. 入场的决策形态

传说。1。录取决定表

当前图例中的决策窗体。1显然限制了MQL5向导的应用程序区域。由于波动性的存在,固定价值止损策略在长期交易中很少有效。当系统采用挂单时,通常需要将其放入动态计算的价格中。

2。改进的决策算法

从价格计算和订单放置的角度来看,这是一个死胡同,因为信号模块不能生成除预测值以外的任何东西,并且主信号不设计为与价格一起工作。在这方面,它的建议是:

  • 引入一个新的信号模块,我们打算称之为价格模块,它可以生成订单参数。
  • 主信号被训练来处理这些参数,也就是说,选择最好的参数并将它们传递给EA。

修改后的算法(图例2)可以处理提单以外的请求。算法的本质是将定义的趋势(加权平均预测)与入口点(价格)分开。这意味着在决策时,已经定义了所选的最佳交易方向,并从所选适当方向的价格模块中获得了一组订单参数。如果有几个类似的集合可用,则接受模块中具有最大重量的集合(选择参数值m_weight)。如果方向已确定,但当前没有可用的入口点,则EA处于非活动状态。

图例. 2. 修改的入场决策形态

传说。2。修改后的录取决定表

为了下新订单,必须满足以下要求:

  1. 目前,品种和其他订单没有空缺。
  2. 加权平均预测值的绝对值超过阈值。
  3. 为订单找到至少一个开盘价。

图例中的算法。2可以处理许多可能的入口点,按方向过滤它们,并在EA中选择最佳入口点。

三。开发改进的EA类和信号模块

库扩展基于两个类:cexpertsignaladvanced和expertadvanced,分别继承自cexpertsignal和expert。

所有增加新功能的措施都旨在改变不同EA模块之间的数据交换接口。例如,为了实现图例的形态学算法。2、需要组织主信号(cexpertsignaladvanced)与价格模块(该类的后代)的交互。当数据改变时,更新下订单意味着EA(cexpertadvanced like)与主信号交互。

所以在这个阶段,我们将在Legend中实现表单。2、参数变化时打开仓库,并组织已下订单的更新(例如,出现更好的入口点时)。让我们看看cexpertsignaladvanced类。

3.1。CExpert信号高级

该类将取代其在主信号中的前辈角色,成为价格模块的基础,因为其前辈是信号模块的基础。

class CExpertSignalAdvanced : public CExpertSignal
  {
protected:
   //---data members for storing parameters of the orders being placed
   double            m_order_open_long;         //opening price of the order to buy
   double            m_order_stop_long;         //Stop Loss of the order to buy
   double            m_order_take_long;         //Take Profit of the order to buy
   datetime          m_order_expiration_long;   //expiry time of the order to buy
   double            m_order_open_short;        //opening price of the order to sell
   double            m_order_stop_short;        //Stop Loss of the order to sell
   double            m_order_take_short;        //Take Profit of the order to sell
   datetime          m_order_expiration_short;  //expiry time of the order to sell
   //---             
   int               m_price_module;            //index of the first price module in the m_filters array
public:
                     CExpertSignalAdvanced();
                    ~CExpertSignalAdvanced();
   virtual void      CalcPriceModuleIndex() {m_price_module=m_filters.Total();}
   virtual bool      CheckOpenLong(double &price,double &sl,double &tp,datetime &expiration);
   virtual bool      CheckOpenShort(double &price,double &sl,double &tp,datetime &expiration);
   virtual double    Direction(void);		//calculating weighted average forecast based on the data received from signal modules
   virtual double    Prices(void);		//updating of parameters of the orders being placed according to the data received from price modules
   virtual bool      OpenLongParams(double &price,double &sl,double &tp,datetime &expiration);
   virtual bool      OpenShortParams(double &price,double &sl,double &tp,datetime &expiration);
   virtual bool      CheckUpdateOrderLong(COrderInfo *order_ptr,double &open,double &sl,double &tp,datetime &ex);
   virtual bool      CheckUpdateOrderShort(COrderInfo *order_ptr,double &open,double &sl,double &tp,datetime &ex);
   double            getOpenLong()              { return m_order_open_long;         }
   double            getOpenShort()             { return m_order_open_short;        }
   double            getStopLong()              { return m_order_stop_long;         }
   double            getStopShort()             { return m_order_stop_short;        }
   double            getTakeLong()              { return m_order_take_long;         }
   double            getTakeShort()             { return m_order_take_short;        }
   datetime          getExpLong()               { return m_order_expiration_long;   }
   datetime          getExpShort()              { return m_order_expiration_short;  }
   double            getWeight()                { return m_weight;                  }
  };

在cexpertsignaladvanced类中,已声明了保存order参数的数据成员。这些变量的值在prices()方法中更新。这些变量充当缓冲区。

已声明参数m_price_module。它将价格模块的索引存储在cexpertsignal中声明的数组m_过滤器中。此数组包含指向信号模块的指针。标准模块(过滤器)的指针保存在数组的开头。所以,从M U价格模块指数开始,轮到价格模块了。为了避免改变时间序列的度量和初始化方法,我们决定将数组中的所有内容都保存下来。此外,64个模块可以通过一个数组保存,这通常是足够的。

此外,在cexpertsignaladvanced类中声明了帮助方法,以获取受保护数据成员的值。它们的名称以get开头(请参见类声明)。

3.1.1构造函数

类cexpertsignaladvanced的构造函数初始化类中声明的变量:

CExpertSignalAdvanced::CExpertSignalAdvanced()
  {
   m_order_open_long=EMPTY_VALUE;
   m_order_stop_long=EMPTY_VALUE;
   m_order_take_long=EMPTY_VALUE;
   m_order_expiration_long=0;
   m_order_open_short=EMPTY_VALUE;
   m_order_stop_short=EMPTY_VALUE;
   m_order_take_short=EMPTY_VALUE;
   m_order_expiration_short=0;
   m_price_module=-1;
  }

计算价格模块索引()

方法calcpriceModuleIndex()分配m价模块中当前数组中的元素数,这等于稍后添加的模块索引。添加第一个价格模块时调用此方法。函数体在类声明中。

virtual void      CalcPriceModuleIndex() {m_price_module=m_filters.Total();}

3.1.3.checkopenlong(…)和checkopenshort(…)

方法checkopenlong(…)是从cexpert类的实例调用的,其工作方式如下所述:

  1. 检查包含的价格模块。如果不是,则调用父类同名的方法。
  2. 从方向()法得到加权平均预测(方向);
  3. 通过将定向值与M_门限开度中的零值和门限值进行比较,验证了入路条件。
  4. order参数值由prices()方法更新,并由openlongparams(…)函数传递给ea。保存此函数的结果。
  5. 返回保存的结果。
bool CExpertSignalAdvanced::CheckOpenLong(double &price,double &sl,double &tp,datetime &expiration)
  {
//--- if price modules were not found, call the method of the basic class CExpertSignal
   if(m_price_module<0)
      return(CExpertSignal::CheckOpenLong(price,sl,tp,expiration));

   bool   result   =false;
   double direction=Direction();
//--- prohibitive signal
   if(direction==EMPTY_VALUE)
      return(false);
//--- check for exceeding the threshold
   if(direction>=m_threshold_open)
     {
      Prices();
      result=OpenLongParams(price,sl,tp,expiration);//there's a signal if m_order_open_long!=EMPTY_VALUE
     }
//--- return the result
   return(result);
  }

checkopenshort(…)的工作方式和工作方式相同,这就是为什么我们不去研究它。

3.1.4方向()

方法方向()查询滤波器,计算加权平均预测。此方法与其父cexpertsignal中同名的方法非常相似,但有例外。在一个循环中,我们不能引用m_过滤器数组中的所有元素,而只能引用那些从0到低于m_价格的索引。一切都与cexpertsignal::direction()相似。

double CExpertSignalAdvanced::Direction(void)
  {
   long   mask;
   double direction;
   double result=m_weight*(LongCondition()-ShortCondition());
   int    number=(result==0.0)? 0 : 1;      // number of queried modules
//--- loop by filters
   for(int i=0;i<m_price_module;i++)
     {
      //--- mask for bitmaps (variables, containing flags)
      mask=((long)1)<<i;
      //--- checking for a flag of ignoring a filter signal
      if((m_ignore&mask)!=0)
         continue;
      CExpertSignal *filter=m_filters.At(i);
      //--- checking for a pointer
      if(filter==NULL)
         continue;
      direction=filter.Direction();
      //--- prohibitive signal
      if(direction==EMPTY_VALUE)
         return(EMPTY_VALUE);
      if((m_invert&mask)!=0)
         result-=direction;
      else
         result+=direction;
      number++;
     }
//--- averaging the sum of weighted forecasts
   if(number!=0)
      result/=number;
//--- return the result
   return(result);
  }

3.1.5。价格()

方法prices()将m_filters数组的第二部分从m_price_模块指定的索引遍历到末尾。查询价格模块,通过函数openlongparams(…)和openshortparams(…)更新类变量的值。循环之前,参数值被清除。

在循环中,如果当前价格模块的权重(M_weight)大于上一个查询模块提供的值,则重写参数值。因此,要么保留空参数值(如果未找到任何值),要么参数是调用方法时获得的最佳权重。

double CExpertSignalAdvanced::Prices(void)
  {
   m_order_open_long=EMPTY_VALUE;
   m_order_stop_long=EMPTY_VALUE;
   m_order_take_long=EMPTY_VALUE;
   m_order_expiration_long=0;
   m_order_open_short=EMPTY_VALUE;
   m_order_stop_short=EMPTY_VALUE;
   m_order_take_short=EMPTY_VALUE;
   m_order_expiration_short=0;
   int    total=m_filters.Total();
   double last_weight_long=0;
   double last_weight_short=0;
//--- cycle for price modules
   for(int i=m_price_module;i<total;i++)
     {   
      CExpertSignalAdvanced *prm=m_filters.At(i);
      if(prm==NULL)
         continue;
//--- ignore the current module if it has returned EMPTY_VALUE
      if(prm.Prices()==EMPTY_VALUE)continue;
      double weight=prm.getWeight();
      if(weight==0.0)continue;
//--- select non-empty values from modules with the greatest weight
      if(weight>last_weight_long && prm.getExpLong()>TimeCurrent())
         if(prm.OpenLongParams(m_order_open_long,m_order_stop_long,m_order_take_long,m_order_expiration_long))
            last_weight_long=weight;
      if(weight>last_weight_short && prm.getExpShort()>TimeCurrent())
         if(prm.OpenShortParams(m_order_open_short,m_order_stop_short,m_order_take_short,m_order_expiration_short))
            last_weight_short=weight;
     }
   return(0);
  }

3.1.6。&nbsp;openlong参数(…)和openshort参数(…)

在cexpertsignaladvanced类中,方法openlongparams(…)传递要购买的订单参数值,引用类变量中的输入参数。

此方法与其父类稍有不同。它根据市场价格和主信号中指定的缩进量来计算所需的参数。现在,您只需要传递准备好的参数。如果开盘价正确(不等于空值),则方法返回true,否则返回false。

bool CExpertSignalAdvanced::OpenLongParams(double &price,double &sl,double &tp,datetime &expiration)
  {
   if(m_order_open_long!=EMPTY_VALUE)
     {
      price=m_order_open_long;
      sl=m_order_stop_long;
      tp=m_order_take_long;
      expiration=m_order_expiration_long;
      return(true);
     }
   return(false);
  }

我们将不讨论openshortparams(…),因为它的工作方式和使用方式相同。

checkupdate orderlong(…)和checkupdate ordershort(…)

方法checkUpdateOrderLong(…)和checkUpdateOrderShort(…)在cexpertAdvanced类中调用。它们用于根据最终价格更新已下订单。

我们将更深入地研究checkupdate orderlong(…)方法。调用方法prices(…)时,将更新第一个价格级别,然后执行数据更新检查以消除可能的修改错误。最后,调用openlongparams(…)方法来传递更新的数据并返回结果。

bool CExpertSignalAdvanced::CheckUpdateOrderLong(COrderInfo *order_ptr,double &open,double &sl,double &tp,datetime &ex)
  {
   Prices();   //update prices
//--- check for changes
   double point=m_symbol.Point();
   if(   MathAbs(order_ptr.PriceOpen() - m_order_open_long)>point
      || MathAbs(order_ptr.StopLoss()  - m_order_stop_long)>point
      || MathAbs(order_ptr.TakeProfit()- m_order_take_long)>point
      || order_ptr.TimeExpiration()!=m_order_expiration_long)
      return(OpenLongParams(open,sl,tp,ex));
//--- update is not required   
   return (false);
  }

checkupdate ordershort(…)的操作和应用方法相同,因此不打算研究它。

3.2。高级专家

EA类中的更改只关注根据主信号中的价格更新修改已下订单。类cexpertadvanced声明如下所示。

class CExpertAdvanced : public CExpert
  {
protected:
   virtual bool      CheckTrailingOrderLong();
   virtual bool      CheckTrailingOrderShort();
   virtual bool      UpdateOrder(double price,double sl,double tp,datetime ex);
public:
                     CExpertAdvanced();
                    ~CExpertAdvanced();
  };

正如我们所看到的,方法很少,构造和析构函数都是空的。

检查尾随订单长()和尾随订单短()。

方法checktrailingorderlong()重写基类中同名的方法,并调用主信号的方法checkupdateorderlong(…)以查找必须修改的顺序。如果需要修改,将调用方法updateOrder(…)并返回结果。

bool CExpertAdvanced::CheckTrailingOrderLong(void)
  {
   CExpertSignalAdvanced *signal_ptr=m_signal;
//--- check for the opportunity to modify the order to buy
   double price,sl,tp;
   datetime ex;
   if(signal_ptr.CheckUpdateOrderLong(GetPointer(m_order),price,sl,tp,ex))
      return(UpdateOrder(price,sl,tp,ex));
//--- return with no actions taken
   return(false);
  }

方法checktrailingordershort()类似,使用的方式相同。

3.2.2.UpdateOrder()

函数updateOrder()开始检查相关的价格(非空值)。如果为空,则删除订单,否则根据收到的参数修改订单。

bool CExpertAdvanced::UpdateOrder(double price,double sl,double tp,datetime ex)
  {
   ulong  ticket=m_order.Ticket();
   if(price==EMPTY_VALUE)
      return(m_trade.OrderDelete(ticket));
//--- modify the order, return the result
   return(m_trade.OrderModify(ticket,price,sl,tp,m_order.TypeTime(),ex));
  }

后续完成标准类的开发。

4。开发价格模块

我们已经有了建立EA的基础。准确地说,我们的课程已经准备好了。现在,只剩下由模块生成的那些级别需要编写。

开发价格模块的过程类似于编写交易信号模块。它们之间唯一的区别是价格(),它负责模块内的价格更新,需要覆盖,不同于信号模块中的长条件()、短条件()或方向()。理想情况下,读者应该对信号模块的开发有一个清晰的概念。文章“六步创造你自己的交易机器人!”而“基于定制指标生成交易信号”可能会有所帮助。

以几个价格模块代码为例。

4.1。基于“增量曲折线”指数的价格模块

索引增量的锯齿线通过指定最后的峰值数目来绘制级别。如果价格与这些水平交叉,这意味着趋势可能会逆转。

价格模块的目的是从索引缓冲区中获取入口级别,找到最接近的局部极值并放置停止点,计算停止乘,以在设置中指定的系数处获得停止点。

图例. 3. 描绘按照增量之字折线指标操作,使用订单买入的例子。

传说。三。描述一个使用订单购买按照递增之字形指数进行操作的例子。

图3。显示的价格级别由价格模块生成。在触发订单之前,停止和停止根据以下最小更新而变化。

4.1.1模块描述符

// wizard description start
//+------------------------------------------------------------------+
//| Description of the class                                         |
//| Title=DeltaZZ Price Module                                       |
//| Type=SignalAdvanced                                              |
//| Name=DeltaZZ Price Module                                        |
//| ShortName=DeltaZZ_PM                                             |
//| Class=CPriceDeltaZZ                                              |
//| Page=not used                                                    |
//| Parameter=setAppPrice,int,1, Applied price: 0 - Close, 1 - H/L   |
//| Parameter=setRevMode,int,0, Reversal mode: 0 - Pips, 1 - Percent |
//| Parameter=setPips,int,300,Reverse in pips                        |
//| Parameter=setPercent,double,0.5,Reverse in percent               |
//| Parameter=setLevels,int,2,Peaks number                           |
//| Parameter=setTpRatio,double,1.6,TP:SL ratio                      |
//| Parameter=setExpBars,int,10,Expiration after bars number         |
//+------------------------------------------------------------------+
// wizard description end

描述符中的前五个参数是设置所用指示器所必需的。随后的系数用于从当前价格模块计算基于止损的止损,以及订单的到期列时间。

4.1.2类声明

class CPriceDeltaZZ : public CExpertSignalAdvanced
  {
protected:
   CiCustom          m_deltazz;           //object of the DeltaZZ indicator
   //--- module settings
   int               m_app_price;
   int               m_rev_mode;
   int               m_pips;
   double            m_percent;
   int               m_levels;
   double            m_tp_ratio;          //tp:sl ratio
   int               m_exp_bars;          //lifetime of the orders in bars
   //--- method of indicator initialization
   bool              InitDeltaZZ(CIndicators *indicators);
   //--- helper methods
   datetime          calcExpiration() { return(TimeCurrent()+m_exp_bars*PeriodSeconds(m_period)); }
   double            getBuySL();          //function for searching latest minimum of ZZ for buy SL
   double            getSellSL();         //function for searching latest maximum of ZZ for sell SL
public:
                     CPriceDeltaZZ();
                    ~CPriceDeltaZZ();
   //--- methods of changing module settings
   void              setAppPrice(int ap)           { m_app_price=ap; }
   void              setRevMode(int rm)            { m_rev_mode=rm;  }
   void              setPips(int pips)             { m_pips=pips;    }
   void              setPercent(double perc)       { m_percent=perc; }
   void              setLevels(int rnum)           { m_levels=rnum;  }
   void              setTpRatio(double tpr)        { m_tp_ratio=tpr; }
   void              setExpBars(int bars)          { m_exp_bars=bars;}
   //--- method of checking correctness of settings
   virtual bool      ValidationSettings(void);
   //--- method of creating indicators
   virtual bool      InitIndicators(CIndicators *indicators);
   //--- main method of price module updating the output data
   virtual double    Prices();
  }; 

保护数据-指示对象cicustom类型,并根据描述符中指定的类型在模块中声明参数。

4.1.3.构造函数

构造函数用默认值初始化类参数。然后,根据EA的输入参数,当模块包含在主信号中时,这些值再次更新。

CPriceDeltaZZ::CPriceDeltaZZ() : m_app_price(1),
                                 m_rev_mode(0),
                                 m_pips(300),
                                 m_percent(0.5),
                                 m_levels(2),
                                 m_tp_ratio(1),
                                 m_exp_bars(10)
  {
  }

4.1.4.验证设置()

验证设置()是检查输入参数的重要方法。如果模块参数值无效,则结果为假,并在日志中输出错误消息。

bool CPriceDeltaZZ::ValidationSettings(void)
  {
//--- checking for settings of additional filters
   if(!CExpertSignal::ValidationSettings())
      return(false);
//--- initial data check
   if(m_app_price<0 || m_app_price>1)
     {
      printf(__FUNCTION__+": Applied price must be 0 or 1");
      return(false);
     }
   if(m_rev_mode<0 || m_rev_mode>1)
     {
      printf(__FUNCTION__+": Reversal mode must be 0 or 1");
      return(false);
     }
   if(m_pips<10)
     {
      printf(__FUNCTION__+": Number of pips in a ray must be at least 10");
      return(false);
     }
   if(m_percent<=0)
     {
      printf(__FUNCTION__+": Percent must be greater than 0");
      return(false);
     }
   if(m_levels<1)
     {
      printf(__FUNCTION__+": Ray Number must be at least 1");
      return(false);
     }
   if(m_tp_ratio<=0)
     {
      printf(__FUNCTION__+": TP Ratio must be greater than zero");
      return(false);
     }
   if(m_exp_bars<0)
     {
      printf(__FUNCTION__+": Expiration must be zero or positive value");
      return(false);
     }
//--- parameter check passed
   return(true);
  } 

4.1.5。初始指示器(…)

方法initIndicators(…)调用基类中同名的方法,并通过initDeltazz(…)方法初始化当前模块。

bool CPriceDeltaZZ::InitIndicators(CIndicators *indicators)
  {
//--- initialization of indicator filters
   if(!CExpertSignal::InitIndicators(indicators))
      return(false);
//--- creating and initializing of custom indicator
   if(!InitDeltaZZ(indicators))
      return(false);
//--- success
   return(true);
  }

4.1.6。initteltazz(…)

方法initDeltazz(…)将自定义索引对象添加到集合中,并创建一个新索引“incremental zigzag line”。

bool CPriceDeltaZZ::InitDeltaZZ(CIndicators *indicators)
  {
//--- adds to collection
   if(!indicators.Add(GetPointer(m_deltazz)))
     {
      printf(__FUNCTION__+": error adding object");
      return(false);
     }
//--- specifies indicator parameters
   MqlParam parameters[6];
//---
   parameters[0].type=TYPE_STRING;
   parameters[0].string_value="deltazigzag.ex5";
   parameters[1].type=TYPE_INT;
   parameters[1].integer_value=m_app_price;
   parameters[2].type=TYPE_INT;
   parameters[2].integer_value=m_rev_mode;
   parameters[3].type=TYPE_INT;
   parameters[3].integer_value=m_pips;
   parameters[4].type=TYPE_DOUBLE;
   parameters[4].double_value=m_percent;
   parameters[5].type=TYPE_INT;
   parameters[5].integer_value=m_levels;
//--- object initialization
   if(!m_deltazz.Create(m_symbol.Name(),m_period,IND_CUSTOM,6,parameters))
     {
      printf(__FUNCTION__+": error initializing object");
      return(false);
     }
//--- number of the indicator buffers
   if(!m_deltazz.NumBuffers(5)) return(false);
//--- ок
   return(true);
  }

方法验证设置()、initDeltazz(…)和initIndicators(…)成功完成后,模块已初始化并准备就绪。

4.1.7。价格()

此方法是价格模块的基础。这是更新订单参数的更好地方。它们的值将被传送到主信号。方法返回操作double类型的结果。这主要是为了实现未来的发展。方法价格()的结果可以写出一些特殊的情况和事件,以便主信号对它们进行相应的处理。目前,只打算处理返回值空的_值。收到结果后,主信号忽略模块建议的参数。

模块中方法价格()的运算算法:

  1. 采购订单和销售订单的开盘价分别从指数的第3和第4个缓冲区中获得。
  2. 订单的最终参数为零。
  3. 检查购买价格。如果有,则通过getbuysl()方法识别停止价格,根据停止值计算停止位置和打开价格,并计算订单到期时间。
  4. 检查单价。如果检测到,则通过getsellsl()方法找到停止价格,根据停止值计算停止价格和打开价格,并计算订单到期时间。
  5. 退出。
因为 “增量之字折线” 指标方面的一些操作,买和卖的价位存在条件是互相排斥的。缓存区 3 和 4 省缺以圆点绘制 (参看图例. 3)。

double CPriceDeltaZZ::Prices(void)
  {
   double openbuy =m_deltazz.GetData(3,0);//receive the last value from buffer 3
   double opensell=m_deltazz.GetData(4,0);//receive the last value from buffer 4
//--- clear parameter values
   m_order_open_long=EMPTY_VALUE;
   m_order_stop_long=EMPTY_VALUE;
   m_order_take_long=EMPTY_VALUE;
   m_order_expiration_long=0;
   m_order_open_short=EMPTY_VALUE;
   m_order_stop_short=EMPTY_VALUE;
   m_order_take_short=EMPTY_VALUE;
   m_order_expiration_short=0;
   int digits=m_symbol.Digits();
//--- check for the prices to buy
   if(openbuy>0)//if buffer 3 is not empty
     {
      m_order_open_long=NormalizeDouble(openbuy,digits);
      m_order_stop_long=NormalizeDouble(getBuySL(),digits);
      m_order_take_long=NormalizeDouble(m_order_open_long + m_tp_ratio*(m_order_open_long - m_order_stop_long),digits);
      m_order_expiration_long=calcExpiration();
     }
//--- check for the prices to sell
   if(opensell>0)//if buffer 4 is not empty
     {
      m_order_open_short=NormalizeDouble(opensell,digits);
      m_order_stop_short=NormalizeDouble(getSellSL(),digits);
      m_order_take_short=NormalizeDouble(m_order_open_short - m_tp_ratio*(m_order_stop_short - m_order_open_short),digits);
      m_order_expiration_short=calcExpiration();
     }
   return(0);
  }

4.1.8。getbuysl()和getsellsl()。

方法:getbuysl()和getsellsl()搜索本地的最小值和最大值作为止损位置。对应缓冲区中的最后一个非零值-每个方法中找到的最后一个本地极值。

double CPriceDeltaZZ::getBuySL(void)
  {
   int i=0;
   double sl=0.0;
   while(sl==0.0)
     {
      sl=m_deltazz.GetData(0,i);
      i++;
     }
   return(sl);
  }
double CPriceDeltaZZ::getSellSL(void)
  {
   int i=0;
   double sl=0.0;
   while(sl==0.0)
     {
      sl=m_deltazz.GetData(1,i);
      i++;
     }
   return(sl);
  }

4.2。基于内柱的价格模块

内筒是交易中广泛使用的一种模型或形式,不需要指标,所以称为价格行为。内圆柱线是最大和最小值在前一个极限范围内的圆柱线。内栏表示价格变动可能开始逆转。

在图4中,内柱被一个红色椭圆包围。当检测到一个内缸时,价格模块根据所处理的内缸的极值生成一个订单期初价格,用于分解买卖。

开盘价是反向订单的止损头寸。停止点的计算方法与上面讨论的模块相同-将停止点乘以设置中指定的系数。开盘价和止损用红色横线表示,止损价为绿色。

图例. 4. 价格模块的内柱线和价位描绘

传说。4。价格模块的内栏和价格说明

在图4中,没有销售订单,因为趋势是向上的。但是,价格模块在两个方向生成入门级。有些停止位置超出绘图窗口。

与以前的模块不同,此模块的算法很简单,不需要索引初始化。模块中的代码也很少,下面是整个模块的内容。我们不打算对每个函数进行拆分和分析。

#include <Expert/ExpertSignalAdvanced.mqh>
// wizard description start
//+------------------------------------------------------------------+
//| Description of the class                                         |
//| Title=Inside Bar Price Module                                    |
//| Type=SignalAdvanced                                              |
//| Name=Inside Bar Price Module                                     |
//| ShortName=IB_PM                                                  |
//| Class=CPriceInsideBar                                            |
//| Page=not used                                                    |
//| Parameter=setTpRatio,double,2,TP:SL ratio                        |
//| Parameter=setExpBars,int,10,Expiration after bars number         |
//| Parameter=setOrderOffset,int,5,Offset for open and stop loss     |
//+------------------------------------------------------------------+
// wizard description end
//+------------------------------------------------------------------+
//| Class CPriceInsideBar                                            |
//| Purpose: Class of the generator of price levels for orders based |
//|          on the "inside bar" pattern.                            |
//| Is derived from the CExpertSignalAdvanced class.                 |
//+------------------------------------------------------------------+
class CPriceInsideBar : public CExpertSignalAdvanced
  {
protected:
   double            m_tp_ratio;          //tp:sl ratio
   int               m_exp_bars;          //lifetime of the orders in bars
   double            m_order_offset;      //shift of the opening and Stop Loss levels
   datetime          calcExpiration()  { return(TimeCurrent()+m_exp_bars*PeriodSeconds(m_period)); }
public:
                     CPriceInsideBar();
                    ~CPriceInsideBar();
   void              setTpRatio(double ratio){ m_tp_ratio=ratio; }
   void              setExpBars(int bars)    { m_exp_bars=bars;}
   void              setOrderOffset(int pips){ m_order_offset=m_symbol.Point()*pips;}
   bool              ValidationSettings();
   double            Prices();
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CPriceInsideBar::CPriceInsideBar()
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CPriceInsideBar::~CPriceInsideBar()
  {
  }
//+------------------------------------------------------------------+
//| Validation of protected settings                                 |
//+------------------------------------------------------------------+
bool CPriceInsideBar::ValidationSettings(void)
  {
//--- verification of the filter parameters
   if(!CExpertSignal::ValidationSettings())
      return(false);
//--- initial data check
  if(m_tp_ratio<=0)
     {
      printf(__FUNCTION__+": TP Ratio must be greater than zero");
      return(false);
     }
   if(m_exp_bars<0)
     {
      printf(__FUNCTION__+": Expiration must be zero or positive value");
      return(false);
     }
//--- check passed
   return(true);
  }
//+------------------------------------------------------------------+
//| Price levels refreshing                                          |
//+------------------------------------------------------------------+
double CPriceInsideBar::Prices(void)
  {
   double h[2],l[2];
   if(CopyHigh(m_symbol.Name(),m_period,1,2,h)!=2 || CopyLow(m_symbol.Name(),m_period,1,2,l)!=2)
      return(EMPTY_VALUE);
//--- check for inside bar      
   if(h[0] >= h[1] && l[0] <= l[1])
     {
      m_order_open_long=h[0]+m_order_offset;
      m_order_stop_long=l[0]-m_order_offset;
      m_order_take_long=m_order_open_long+(m_order_open_long-m_order_stop_long)*m_tp_ratio;
      m_order_expiration_long=calcExpiration();
      
      m_order_open_short=m_order_stop_long;
      m_order_stop_short=m_order_open_long;
      m_order_take_short=m_order_open_short-(m_order_stop_short-m_order_open_short)*m_tp_ratio;
      m_order_expiration_short=m_order_expiration_long;
      return(0);
     }
   return(EMPTY_VALUE);
  }  
//+------------------------------------------------------------------+

在该模块的方法价格()中,返回空的_值结果,表示主信号没有可用的价格。

4.3。基于外栏的价格模块

外柱线是另一种流行的价格行为,也称为“吞咽”。它被称为外部列,因为它的最大值和最小值与前一列重叠。外栏的出现表明目标价格变动的后续波动有所增加。

在图中。5.外柱用红色椭圆标记。一旦检测到外缸,价格模块将以其极限值生成一个订单开盘价,该开盘价向上和向下分解。

开盘价是反向订单的止损头寸。停止点的计算方法与上面讨论的模块相同-将停止点乘以设置中指定的系数。开盘价和止损用红色横线表示,止损价为绿色。

图例. 5. 价格模块的外柱线和价位描绘

传说。5。价格模块的外栏和价格说明

在图中。5.外柱用红色椭圆标记。横线反映价格模块生成的提单的期初价格。

在这种情况下,只有销售订单,因为趋势是向下的。这个模块的操作与前一个模块的操作基本相似。其代码的唯一区别是prices()方法,其余方法完全相同,名称相同。这是价格代码()。

double CPriceOutsideBar::Prices(void)
{
   double h[2],l[2];
   if(CopyHigh(m_symbol.Name(),m_period,1,2,h)!=2 || CopyLow(m_symbol.Name(),m_period,1,2,l)!=2)
      return(EMPTY_VALUE);
//--- check of outside bar
   if(h[0] <= h[1] && l[0] >= l[1])
   {
      m_order_open_long=h[1]+m_order_offset;
      m_order_stop_long=l[1]-m_order_offset;
      m_order_take_long=m_order_open_long+(m_order_open_long-m_order_stop_long)*m_tp_ratio;
      m_order_expiration_long=calcExpiration();
      
      m_order_open_short=m_order_stop_long;
      m_order_stop_short=m_order_open_long;
      m_order_take_short=m_order_open_short-(m_order_stop_short-m_order_open_short)*m_tp_ratio;
      m_order_expiration_short=m_order_expiration_long;
      return(0);
   }
   return(EMPTY_VALUE);
} 

4.4。模块性能测试

在下一节中,将介绍三个测试模块示例,以检查它们的独立性、在EA中协同工作以及确认系统按设计工作。因此,生成四个EAS。基于动量振荡指数的交易信号模块在H12时间框架内作为每个EA中的滤波器工作。价格模块在H6中工作。

资金管理:固定数量的手和尾不用。所有测试均以欧元/美元价格执行。在MetaQuotes演示服务器模拟帐户上,测试周期从2013.01.01到2014.06.01不等。终端版本:5.00,汇编号965(2014年6月27日)。

为了简化,测试结果只显示在平衡图中。它们足以在每个特定情况下获得EA的行为。

4.4.1基于“增量之字形”指标的测试模块

图例. 6. 基于"增量之字折线" 的价格模块的测试余额图

传说。6。基于增量之字形指数的价格模块测试平衡图

4.4.2基于“内圆柱”形状的测试模块

图例. 7. 基于内柱线的价格模块的测试余额图

传说。7。基于内柱的价格模块测试平衡图

看看传说。7。应该记住,在这个阶段测试的目的是检查代码的性能,而不是获得一个有利可图的策略。

4.4.3基于“外圆柱”形状的测试模块

图例. 8. 基于外柱线的价格模块的测试余额图

传说。8。基于外栏的价格模块测试平衡图

4.4.4。测试开发中价格模块的联合工作

考虑到以往的试验结果,在“增量之字形线”的基础上,分别得到了内柱和外柱价格模块的权重系数分别为1、0.5和0.7。权重系数定义了价格模块的优先级。

图例. 9. 三种价格模块的测试余额图

传说。9。三个价格模块的测试平衡图

在测试的每个阶段,对为价格范围执行的所有操作都进行了仔细的分析。未造成策略错误或偏差。本文附带了所有推荐的过程,您可以自己测试它们。

5。使用说明

我们打算使用三个价格模块和动量振荡器作为滤波器的EA作为样本扩展来研究EA的发展阶段。

在初始阶段,确认头文件“cexpertadvanced.mqh”和“cexpertsignaladvanced.mqh”已保存在metatrader 5终端的mql5/include/expert文件夹中,并且价格模块文件也在mql5/include/expert/mysignal文件夹中。启动EA之前,请确保已编译度量并位于mql5/indicators文件夹中。在这种情况下,它的文件是“deltazigzag.ex5”。在所附的档案中,所有文件都放在适当的地方。您所要做的就是将归档文件提取到metatrader 5终端的文件夹中,并确认合并的目录。

5.1。EA生成

要开始生成EA,请在元编辑器中选择“文件”-&gt;“新建文件”。

图例. 1. 入场的决策形态0

传说。10。使用MQL5向导创建新的EA

在Heart(心脏)窗口中选择“Intelligent program(generation)”,然后按“Next step”(下一步)。

图例. 1. 入场的决策形态1

传说。11。使用mql5考虑生成EA

它将弹出窗口,您可以指定名称。在这种特殊情况下,ea的名称是“test_ea_ao_dzz_ib_ob”,参数符号和时间框架都有默认值。然后单击下一步。

图例. 1. 入场的决策形态2

传奇
。12。交易程序的一般属性

在出现的窗口中,按“添加”逐个添加模块。整个过程如下。

请注意!添加模块时,首先包括所有信号模块(cexpertsignal的后续模块),然后包括价格模块(cexpertsignaladvanced的后续模块)。如果包含模块的顺序被破坏,结果将是不可预测的。

所以我们从动量振荡器开始,包括信号模块。它是本例中唯一的过滤器。

图例. 1. 入场的决策形态3

传说。13。从动量振荡器包括信号模块

在所有的信号模块都打包好之后,它们会随机地包含所需的价格模块。在这个例子中有三个。

图例. 1. 入场的决策形态4

传说。14。包括Deltazz价格模块的信号模块

图例. 1. 入场的决策形态5

传说。15。包括来自内缸价格模块的信号模块

图例. 1. 入场的决策形态6

传说。16。含外栏价格模块信号模块

已添加所有模块。窗口如下:

图例. 1. 入场的决策形态7

传说。17。交易信号模块
包含清单

价格模块的“权重”参数值定义其优先级。

单击“下一步”按钮后,向导建议选择资金管理模块和尾部停止模块。选择其中一个,或保留原始设置,按“下一步”或“准备”。

按“准备好”,我们会得到EA代码。

5.2。编辑和生成代码

所以,我们有代码。

1。在开始处查找字符串

#include <Expert/Expert.mqh>

像这样编辑

#include <Expert/ExpertAdvanced.mqh>

它需要包含一些已开发的类文件。

2。稍后查找字符串

CExpert ExtExpert;

并改为

CExpertAdvanced ExtExpert;

它将根据所需的功能更改EA及其后代的标准库。

三。现在寻找字符串

CExpertSignal *signal=new CExpertSignal;

并改为

CExpertSignalAdvanced *signal=new CExpertSignalAdvanced;

该方法将根据需要的功能改变主信号及其子代的标准库。

4。找到字符串并将第一个价格模块添加到主信号的m_过滤器数组中。在这种情况下,它看起来是这样的:

signal.AddFilter(filter1);

在此之前,我们插入字符串

signal.CalcPriceModuleIndex();

这是识别M_滤波器阵列中指向的信号模块以及价格模块所需的主要信号,其数量可达到索引值。

找到插入指定字符串的正确位置可能有点困难。数字在单词“filter”中用作参考点。这将简化搜索,而不会遗漏正确的位置。mql5向导自动在代码中按顺序命名包含的模块。第一个模块称为过滤器0、第二个过滤器1、第三个过滤器2等等。在我们的例子中只有一个信号模块。所以,第一个包含的价格模块是数字2,我们需要搜索字符串“信号”。add filter(filter 1);“为了添加筛选器,代码中的数字前导为0。如图所示。18:

图例. 1. 入场的决策形态8

传说。18。根据包含顺序在代码中命名模块

5。本部分不是强制性的。通过引入变更,EA参数负责开盘价缩减、止损、止损收益、订单到期和放弃时间。要使代码更紧凑,可以删除以下字符串:

input double Signal_PriceLevel            =0.0;                    // Price level to execute a deal
input double Signal_StopLevel             =50.0;                   // Stop Loss level (in points)
input double Signal_TakeLevel             =50.0;                   // Take Profit level (in points)
input int    Signal_Expiration            =4;                      // Expiration of pending orders (in bars)

删除上述字符串后,我们会遇到编译错误并删除下一组字符串:

   signal.PriceLevel(Signal_PriceLevel);
   signal.StopLevel(Signal_StopLevel);
   signal.TakeLevel(Signal_TakeLevel);
   signal.Expiration(Signal_Expiration);

删除后,编辑器将成功。

编辑评论和解释可以在附在EA的代码中找到,“test-ea-ao-dzz-ib-ob”。可以删除代码字符串和附带的注释。

结论

在本文中,我们已经显著地扩展了MQL5向导的应用领域。现在它可以用来进行优化,无论当前价格如何,都可以按不同的价格下订单,止损止损,止利自动交易系统。

生成的EA可以包含一组价格模块来计算订单发送的参数。从可用集合中选择最合适的参数组。首选设置中指定的首选项。它允许多个不同的、最有效的入口点。这种方法使EA具有选择性。如果方向已知,但入口点不确定,则EA将等待其出现。

标准兼容模块引入搜索价格具有显著的优势,其目的是简化EA开发。虽然目前只有三个模块,但它们的数量无疑将在未来增加。如果您发现本文非常有用,请在评论部分为价格模块的操作建议一个算法。有趣的想法将在代码中实现。

本文还提供了一种在向导中进一步开发和扩展EA功能的方法。继承是引入变更的最佳方式。

没有编程经验的用户不仅可以根据指令编辑代码,还可以根据现有模型创建更高级的EAS。

本文由MetaQuotes Software Corp.翻译自俄语原文
,网址为https://www.mql5.com/ru/articles/987。

附加文件下载zip mql5_library_extension.zip(715.33 kb)、expertsignal advanced.mqh(22.09 kb)、expertadvanced.mqh(6.61 kb)pricedeltazz。mqh(22.93 kb),PriceInsideBar。mqh(9.45 kb),PriceOutsideBar。MQH(9.47 kb)

 

 


MyFxtop迈投(www.myfxtop.com)-靠谱的外汇跟单社区,免费跟随高手做交易!

 

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

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

風險提示

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

邁投公眾號

聯繫我們

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

MyFxtops 邁投