请 [注册] 或 [登录]  | 返回主站

量化交易吧 /  数理科学 帖子:3365344 新帖:1

DoEasy 函数库中的时间序列(第四十六部分):多周期、多品种指标缓冲区

爱汇小王子发表于:11 月 3 日 16:00回复(1)

内容

  • 概述
  • 改进缓冲区对象类,从而可操控任何品种
  • 测试:多周期、多品种移动均线
  • 测试:多周期、多品种 MACD
  • 下一步是什么?

概述

在当前阶段,该函数库已包含了创建和跟踪多周期指标缓冲区的功能。 现在我们需要添加在多品种模式下工作的功能,以便我们能够处理进一步的任务,这些任务瞄准的是开发用于自定义程序的函数库工具。

我之前对缓冲区对象类的工作已经提供了这种功能,只是需要进行一些精练。 因此,今天我将做最后的准备,着手简化多品种、多周期标准指标的开发。
为达此目的,我需要改进计算缓冲区对象类,以便它能够依据标准指标句柄接收其数组所需的数组数据。 这个函数库已有能力做到这一点,但我即将实现的一些小幅附加极大地促进了这项任务,并允许在当前图表上显示任何品种/周期之处的标准指标数据。

在随后的文章中,我欲把当前概念应用在操控来自任意品种/周期的标准指标数据的类,并简化创建多品种、多周期标准指标的任务。

改进缓冲区对象类,从而可操控任何品种

基本抽象缓冲区对象类包含当前指标缓冲区之后创建的后续指标缓冲区的数组索引值。 在当前对象中,我们必须根据当前缓冲区对象的类型,及其绘图样式对后续指标缓冲区的索引进行简单的计算,同时计算中还考虑到“两级别之间填充颜色”绘图样式的指标缓冲区未提供颜色缓冲区。

为了简化计算后续缓冲区索引的任务,并且考虑各种因素不能影响计算的清晰度,我们可以引入另一个类成员变量,其中包含构造缓冲区对象所有占用数组的数量。 创建每个缓冲区对象(抽象缓冲区类后代)时,在子类构造函数的参数里传递所需的数值给受保护的基准对象类构造函数,并指定严格设置的数值。

这看起来很棘手,但实际上一切都非常简单:当创建一个新的指标缓冲对象时,我们已将缓冲对象类构造函数中的一些值传递给其父类构造函数。 甚而,我们会多传递一个数值 – 即构造每个缓冲区对象所需数组的数量。

打开抽象指标缓冲区基本对象类的文件 \MQL5\Include\DoEasy\Objects\Indicators\Buffer.mqh,并对其进行必要的修改。

在类的私密部分声明新变量

//+------------------------------------------------------------------+
//| Abstract indicator buffer class                                  |
//+------------------------------------------------------------------+
class CBuffer : public CBaseObj
  {
private:
   long              m_long_prop[BUFFER_PROP_INTEGER_TOTAL];                     // Integer properties
   double            m_double_prop[BUFFER_PROP_DOUBLE_TOTAL];                    // Real properties
   string            m_string_prop[BUFFER_PROP_STRING_TOTAL];                    // String properties
   bool              m_act_state_trigger;                                        // Auxiliary buffer status switch flag
   uchar             m_total_arrays;                                             // Total number of buffer arrays

声明受保护的参数型类构造函数时,在其输入中添加另一个变量。 创建该缓冲区对象时,此变量会把所有缓冲区对象数组的数量传递给类:

//--- Default constructor
                     CBuffer(void){;}
protected:
//--- Protected parametric constructor
                     CBuffer(ENUM_BUFFER_STATUS status_buffer,
                             ENUM_BUFFER_TYPE buffer_type,
                             const uint index_plot,
                             const uint index_base_array,
                             const int num_datas,
                             const uchar total_arrays,
                             const int width,
                             const string label);
public:  

在构造函数实现代码中,将此变量添加到输入列表,并通过它将传递的数值赋给先前声明的私有变量 。 然后,使用该值计算下一个缓冲区对象的基本数组索引计算颜色数组索引时,要检查缓冲区类型。 如果这是一个绘制缓冲区,在计算索引时要把所有数据数组的数量加上基本数组索引,而对于计算缓冲区,由于计算缓冲区没有颜色数组,所以要加零

//+------------------------------------------------------------------+
//| Closed parametric constructor                                    |
//+------------------------------------------------------------------+
CBuffer::CBuffer(ENUM_BUFFER_STATUS buffer_status,
                 ENUM_BUFFER_TYPE buffer_type,
                 const uint index_plot,
                 const uint index_base_array,
                 const int num_datas,
                 const uchar total_arrays,
                 const int width,
                 const string label)
  {
   this.m_type=COLLECTION_BUFFERS_ID;
   this.m_act_state_trigger=true;
   this.m_total_arrays=total_arrays;
//--- Save integer properties
   this.m_long_prop[BUFFER_PROP_STATUS]                        = buffer_status;
   this.m_long_prop[BUFFER_PROP_TYPE]                          = buffer_type;
   ENUM_DRAW_TYPE type=
     (
      !this.TypeBuffer() || !this.Status() ? DRAW_NONE      : 
      this.Status()==BUFFER_STATUS_FILLING ? DRAW_FILLING   : 
      ENUM_DRAW_TYPE(this.Status()+8)
     );
   this.m_long_prop[BUFFER_PROP_DRAW_TYPE]                     = type;
   this.m_long_prop[BUFFER_PROP_TIMEFRAME]                     = PERIOD_CURRENT;
   this.m_long_prop[BUFFER_PROP_ACTIVE]                        = true;
   this.m_long_prop[BUFFER_PROP_ARROW_CODE]                    = 0x9F;
   this.m_long_prop[BUFFER_PROP_ARROW_SHIFT]                   = 0;
   this.m_long_prop[BUFFER_PROP_DRAW_BEGIN]                    = 0;
   this.m_long_prop[BUFFER_PROP_SHOW_DATA]                     = (buffer_type>BUFFER_TYPE_CALCULATE ? true : false);
   this.m_long_prop[BUFFER_PROP_SHIFT]                         = 0;
   this.m_long_prop[BUFFER_PROP_LINE_STYLE]                    = STYLE_SOLID;
   this.m_long_prop[BUFFER_PROP_LINE_WIDTH]                    = width;
   this.m_long_prop[BUFFER_PROP_COLOR_INDEXES]                 = (this.Status()>BUFFER_STATUS_NONE ? (this.Status()!=BUFFER_STATUS_FILLING ? 1 : 2) : 0);
   this.m_long_prop[BUFFER_PROP_COLOR]                         = clrRed;
   this.m_long_prop[BUFFER_PROP_NUM_DATAS]                     = num_datas;
   this.m_long_prop[BUFFER_PROP_INDEX_PLOT]                    = index_plot;
   this.m_long_prop[BUFFER_PROP_INDEX_BASE]                    = index_base_array;
   this.m_long_prop[BUFFER_PROP_INDEX_COLOR]                   = this.GetProperty(BUFFER_PROP_INDEX_BASE)+
                                                                   (this.TypeBuffer()!=BUFFER_TYPE_CALCULATE ? this.GetProperty(BUFFER_PROP_NUM_DATAS) : 0);
   this.m_long_prop[BUFFER_PROP_INDEX_NEXT_BASE]               = index_base_array+this.m_total_arrays;
   this.m_long_prop[BUFFER_PROP_INDEX_NEXT_PLOT]               = (this.TypeBuffer()>BUFFER_TYPE_CALCULATE ? index_plot+1 : index_plot);
   
//--- Save real properties
   this.m_double_prop[this.IndexProp(BUFFER_PROP_EMPTY_VALUE)] = (this.TypeBuffer()>BUFFER_TYPE_CALCULATE ? EMPTY_VALUE : 0);
//--- Save string properties
   this.m_string_prop[this.IndexProp(BUFFER_PROP_SYMBOL)]      = ::Symbol();
   this.m_string_prop[this.IndexProp(BUFFER_PROP_LABEL)]       = (this.TypeBuffer()>BUFFER_TYPE_CALCULATE ? label : NULL);

//--- If failed to change the size of the indicator buffer array, display the appropriate message indicating the string
   if(::ArrayResize(this.DataBuffer,(int)this.GetProperty(BUFFER_PROP_NUM_DATAS))==WRONG_VALUE)
      ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_DRAWING_ARRAY_RESIZE),". ",CMessage::Text(MSG_LIB_SYS_ERROR),": ",(string)::GetLastError());
      
//--- If failed to change the size of the color array (only for a non-calculated buffer), display the appropriate message indicating the string
   if(this.TypeBuffer()>BUFFER_TYPE_CALCULATE)
      if(::ArrayResize(this.ArrayColors,(int)this.ColorsTotal())==WRONG_VALUE)
         ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_COLORS_ARRAY_RESIZE),". ",CMessage::Text(MSG_LIB_SYS_ERROR),": ",(string)::GetLastError());

//--- For DRAW_FILLING, fill in the color array with two default colors
   if(this.Status()==BUFFER_STATUS_FILLING)
     {
      this.SetColor(clrBlue,0);
      this.SetColor(clrRed,1);
     }

//--- Bind indicator buffers with arrays
//--- In a loop by the number of indicator buffers
   int total=::ArraySize(DataBuffer);
   for(int i=0;i<total;i++)
     {
      //--- calculate the index of the next array and
      //--- bind the indicator buffer by the calculated index with the dynamic array
      //--- located by the i loop index in the DataBuffer array
      int index=(int)this.GetProperty(BUFFER_PROP_INDEX_BASE)+i;
      ::SetIndexBuffer(index,this.DataBuffer[i].Array,(this.TypeBuffer()==BUFFER_TYPE_DATA ? INDICATOR_DATA : INDICATOR_CALCULATIONS));
      //--- Set indexation flag as in the timeseries to all buffer arrays
      ::ArraySetAsSeries(this.DataBuffer[i].Array,true);
     }
//--- Bind the color buffer with the array (only for a non-calculated buffer and not for the filling buffer)
   if(this.Status()!=BUFFER_STATUS_FILLING && this.TypeBuffer()!=BUFFER_TYPE_CALCULATE)
     {
      ::SetIndexBuffer((int)this.GetProperty(BUFFER_PROP_INDEX_COLOR),this.ColorBufferArray,INDICATOR_COLOR_INDEX);
      ::ArraySetAsSeries(this.ColorBufferArray,true);
     }
//--- If this is a calculated buffer, all is done
   if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE)
      return;
//--- Set integer parameters of the graphical series
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_TYPE,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_DRAW_TYPE));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_ARROW,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_ARROW_CODE));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_ARROW_SHIFT,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_ARROW_SHIFT));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_BEGIN,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_DRAW_BEGIN));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHOW_DATA,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_SHOW_DATA));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHIFT,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_SHIFT));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_STYLE,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_LINE_STYLE));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_WIDTH,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_LINE_WIDTH));
   this.SetColor((color)this.GetProperty(BUFFER_PROP_COLOR));
//--- Set real parameters of the graphical series
   ::PlotIndexSetDouble((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_EMPTY_VALUE,this.GetProperty(BUFFER_PROP_EMPTY_VALUE));
//--- Set string parameters of the graphical series
   ::PlotIndexSetString((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LABEL,this.GetProperty(BUFFER_PROP_LABEL));
  }
//+------------------------------------------------------------------+


现在抽象缓冲区基准对象的所有子代对象类都在类构造函数的初始化列表中传递构造缓冲区的需用数组数量。

对于箭头缓冲区(\MQL5\Include\DoEasy\Objects\Indicators\BufferArrow.mqh):

//+------------------------------------------------------------------+
//| Buffer with the "Drawing with arrows" drawing style              |
//+------------------------------------------------------------------+
class CBufferArrow : public CBuffer
  {
private:

public:
//--- Constructor
                     CBufferArrow(const uint index_plot,const uint index_base_array) :
                        CBuffer(BUFFER_STATUS_ARROW,BUFFER_TYPE_DATA,index_plot,index_base_array,1,2,1,"Arrows") {}

对于线条缓冲区 (\MQL5\Include\DoEasy\Objects\Indicators\BufferLine.mqh):

//+------------------------------------------------------------------+
//| Buffer of the Line drawing style                                 |
//+------------------------------------------------------------------+
class CBufferLine : public CBuffer
  {
private:

public:
//--- Constructor
                     CBufferLine(const uint index_plot,const uint index_base_array) :
                        CBuffer(BUFFER_STATUS_LINE,BUFFER_TYPE_DATA,index_plot,index_base_array,1,2,1,"Line") {}

对于线段缓冲区 (\MQL5\Include\DoEasy\Objects\Indicators\BufferSection.mqh):

//+------------------------------------------------------------------+
//| Buffer of the Section drawing style                              |
//+------------------------------------------------------------------+
class CBufferSection : public CBuffer
  {
private:

public:
//--- Constructor
                     CBufferSection(const uint index_plot,const uint index_base_array) :
                        CBuffer(BUFFER_STATUS_SECTION,BUFFER_TYPE_DATA,index_plot,index_base_array,1,2,1,"Section") {}

对于从零轴开始的直方图缓冲区 (\MQL5\Include\DoEasy\Objects\Indicators\BufferHistogram.mqh):

//+------------------------------------------------------------------+
//| Buffer of the "Histogram from the zero line" drawing style       |
//+------------------------------------------------------------------+
class CBufferHistogram : public CBuffer
  {
private:

public:
//--- Constructor
                     CBufferHistogram(const uint index_plot,const uint index_base_array) :
                        CBuffer(BUFFER_STATUS_HISTOGRAM,BUFFER_TYPE_DATA,index_plot,index_base_array,1,2,2,"Histogram") {}

对于两个指标缓冲区之间的直方图缓冲区 (\MQL5\Include\DoEasy\Objects\Indicators\BufferHistogram2.mqh):

//+--------------------------------------------------------------------+
//|Buffer of the "Histogram on two indicator buffers" drawing style    |
//+--------------------------------------------------------------------+
class CBufferHistogram2 : public CBuffer
  {
private:

public:
//--- Constructor
                     CBufferHistogram2(const uint index_plot,const uint index_base_array) :
                        CBuffer(BUFFER_STATUS_HISTOGRAM2,BUFFER_TYPE_DATA,index_plot,index_base_array,2,3,8,"Histogram2 0;Histogram2 1") {}

对于之字折线缓冲区 (\MQL5\Include\DoEasy\Objects\Indicators\BufferZigZag.mqh):

//+------------------------------------------------------------------+
//|Buffer of the ZigZag drawing style                                |
//+------------------------------------------------------------------+
class CBufferZigZag : public CBuffer
  {
private:

public:
//--- Constructor
                     CBufferZigZag(const uint index_plot,const uint index_base_array) :
                        CBuffer(BUFFER_STATUS_ZIGZAG,BUFFER_TYPE_DATA,index_plot,index_base_array,2,3,1,"ZigZag 0;ZigZag 1") {}

对于填充缓冲区 (\MQL5\Include\DoEasy\Objects\Indicators\BufferFilling.mqh):

//+------------------------------------------------------------------+
//|Buffer of the "Color filling between two levels" drawing style    |
//+------------------------------------------------------------------+
class CBufferFilling : public CBuffer
  {
private:

public:
//--- Constructor
                     CBufferFilling(const uint index_plot,const uint index_base_array) :
                        CBuffer(BUFFER_STATUS_FILLING,BUFFER_TYPE_DATA,index_plot,index_base_array,2,2,1,"Filling 0;Filling 1") {}

作为绘制柱线的缓冲区 (\MQL5\Include\DoEasy\Objects\Indicators\BufferBars.mqh):

//+------------------------------------------------------------------+
//|Buffer of the Bars drawing style                                  |
//+------------------------------------------------------------------+
class CBufferBars : public CBuffer
  {
private:

public:
//--- Constructor
                     CBufferBars(const uint index_plot,const uint index_base_array) :
                        CBuffer(BUFFER_STATUS_BARS,BUFFER_TYPE_DATA,index_plot,index_base_array,4,5,2,"Bar Open;Bar High;Bar Low;Bar Close") {}

对于绘制蜡烛的缓冲区 (\MQL5\Include\DoEasy\Objects\Indicators\BufferCandles.mqh):

//+------------------------------------------------------------------+
//|Buffer of the Candles drawing style                               |
//+------------------------------------------------------------------+
class CBufferCandles : public CBuffer
  {
private:

public:
//--- Constructor
                     CBufferCandles(const uint index_plot,const uint index_base_array) : 
                        CBuffer(BUFFER_STATUS_CANDLES,BUFFER_TYPE_DATA,index_plot,index_base_array,4,5,1,"Candle Open;Candle High;Candle Low;Candle Close") {}

对于计算缓冲区 (\MQL5\Include\DoEasy\Objects\Indicators\BufferCalculate.mqh):

//+------------------------------------------------------------------+
//| Calculated buffer                                                |
//+------------------------------------------------------------------+
class CBufferCalculate : public CBuffer
  {
private:

public:
//--- Constructor
                     CBufferCalculate(const uint index_plot,const uint index_array) :
                        CBuffer(BUFFER_STATUS_NONE,BUFFER_TYPE_CALCULATE,index_plot,index_array,1,1,0,"Calculate") {}

如此修改令我们在后续创建缓冲区时,不必再为计算索引而检查缓冲区类型和绘图样式,因为每个缓冲区类型总是会用到相同数量的数组 — 在创建缓冲区时,它会传递严格指定的数值。

在计算缓冲区类里,添加新方法,将来自标准指标句柄的数据写至计算缓冲区数组当中:

//+------------------------------------------------------------------+
//| Calculated buffer                                                |
//+------------------------------------------------------------------+
class CBufferCalculate : public CBuffer
  {
private:

public:
//--- Constructor
                     CBufferCalculate(const uint index_plot,const uint index_array) :
                        CBuffer(BUFFER_STATUS_NONE,BUFFER_TYPE_CALCULATE,index_plot,index_array,1,1,0,"Calculate") {}
//--- Supported integer properties of a buffer
   virtual bool      SupportProperty(ENUM_BUFFER_PROP_INTEGER property);
//--- Supported real properties of a buffer
   virtual bool      SupportProperty(ENUM_BUFFER_PROP_DOUBLE property);
//--- Supported string properties of a buffer
   virtual bool      SupportProperty(ENUM_BUFFER_PROP_STRING property);
//--- Display a short buffer description in the journal
   virtual void      PrintShort(void);
   
//--- Set the value to the data buffer array
   void              SetData(const uint series_index,const double value)               { this.SetBufferValue(0,series_index,value);       }
//--- Return the value from the data buffer array
   double            GetData(const uint series_index)                            const { return this.GetDataBufferValue(0,series_index);  }
   
//--- Copy data of the specified indicator to the buffer object array
   int               FillAsSeries(const int indicator_handle,const int buffer_num,const int start_pos,const int count);
   int               FillAsSeries(const int indicator_handle,const int buffer_num,const datetime start_time,const int count);
   int               FillAsSeries(const int indicator_handle,const int buffer_num,const datetime start_time,const datetime stop_time);
   
  };
//+------------------------------------------------------------------+

我们在类主体之外编写它们的实现:

//+------------------------------------------------------------------+
//| Copy data of the specified indicator to the buffer object array  |
//+------------------------------------------------------------------+
int CBufferCalculate::FillAsSeries(const int indicator_handle,const int buffer_num,const int start_pos,const int count)
  {
   return ::CopyBuffer(indicator_handle,buffer_num,start_pos,count,this.DataBuffer[0].Array);
  }
//+------------------------------------------------------------------+
int CBufferCalculate::FillAsSeries(const int indicator_handle,const int buffer_num,const datetime start_time,const int count)
  {
   return ::CopyBuffer(indicator_handle,buffer_num,start_time,count,this.DataBuffer[0].Array);
  }
//+------------------------------------------------------------------+
int CBufferCalculate::FillAsSeries(const int indicator_handle,const int buffer_num,const datetime start_time,const datetime stop_time)
  {
   return ::CopyBuffer(indicator_handle,buffer_num,start_time,stop_time,this.DataBuffer[0].Array);
  }
//+------------------------------------------------------------------+

这三个方法都调用 CopyBuffer() 的三个变体重载函数。 依据相应的指标缓冲区分配的数组用作接收数组。 相应的指标缓冲区是一个数组,按句柄读取指标数据并由方法写入对象数组。

现在我们来实现操控缓冲区对象的多品种模式。 首先,我需要说明在上一篇文章里准备材料时所做的一些假设,在其中我实现了多周期模式。
在指标缓冲区集合类中,我创建了一个方法,为操控单个缓冲区柱线接收所需的时间序列和柱线数据。 此方法含有图表周期上所有必需的数据 — 当前和分配的缓冲区对象,以及该品种上所有必需的数据 — 当前和分配的缓冲区对象。 下面是来自上一篇文章里的方法:

//+------------------------------------------------------------------+
//| Get data of the necessary timeseries and bars                    |
//| for working with a single bar of the buffer                      |
//+------------------------------------------------------------------+
int CBuffersCollection::GetBarsData(CBuffer *buffer,const int series_index,int &index_bar_period)
  {
//--- Get timeseries of the current chart and the chart of the buffer timeframe
   CSeriesDE *series_current=this.m_timeseries.GetSeries(buffer.Symbol(),PERIOD_CURRENT);
   CSeriesDE *series_period=this.m_timeseries.GetSeries(buffer.Symbol(),buffer.Timeframe());
   if(series_current==NULL || series_period==NULL)
      return WRONG_VALUE;
//--- Get the bar object of the current timeseries corresponding to the required timeseries index
   CBar *bar_current=series_current.GetBar(series_index);
   if(bar_current==NULL)
      return WRONG_VALUE;
//--- Get the timeseries bar object of the buffer chart period corresponding to the time the timeseries bar of the current chart falls into
   CBar *bar_period=m_timeseries.GetBarSeriesFirstFromSeriesSecond(NULL,PERIOD_CURRENT,bar_current.Time(),NULL,series_period.Timeframe());
   if(bar_period==NULL)
      return WRONG_VALUE;
//--- Write down the bar index on the current timeframe which falls into the bar start time of the buffer object chart
   index_bar_period=bar_period.Index(PERIOD_CURRENT);
//--- Calculate the amount of bars of the current timeframe included into one bar of the buffer object chart period
//--- and return this value (1 if the result is 0)
   int num_bars=::PeriodSeconds(bar_period.Timeframe())/::PeriodSeconds(bar_current.Timeframe());
   return(num_bars>0 ? num_bars : 1);
  }
//+------------------------------------------------------------------+

在此,我少了两段代码从所需品种中获取数据 — 对于当前图表时间序列,我们从分配给缓冲区对象的品种图表中获取数据。 在第二段代码中,情况正好相反,我们在需要获取缓冲区对象品种之处得到当前图表品种

结果就是,所有调整都归结为两段代码中的两个修正

修正方法的完整清单:

//+------------------------------------------------------------------+
//| Get data of the necessary timeseries and bars                    |
//| for working with a single bar of the buffer                      |
//+------------------------------------------------------------------+
int CBuffersCollection::GetBarsData(CBuffer *buffer,const int series_index,int &index_bar_period)
  {
//--- Get timeseries of the current chart and the chart of the buffer timeframe
   CSeriesDE *series_current=this.m_timeseries.GetSeries(Symbol(),PERIOD_CURRENT);
   CSeriesDE *series_period=this.m_timeseries.GetSeries(buffer.Symbol(),buffer.Timeframe());
   if(series_current==NULL || series_period==NULL)
      return WRONG_VALUE;
//--- Get the bar object of the current timeseries corresponding to the required timeseries index
   CBar *bar_current=series_current.GetBar(series_index);
   if(bar_current==NULL)
      return WRONG_VALUE;
//--- Get the timeseries bar object of the buffer chart period corresponding to the time the timeseries bar of the current chart falls into
   CBar *bar_period=m_timeseries.GetBarSeriesFirstFromSeriesSecond(NULL,PERIOD_CURRENT,bar_current.Time(),buffer.Symbol(),series_period.Timeframe());
   if(bar_period==NULL)
      return WRONG_VALUE;
//--- Write down the bar index on the current timeframe which falls into the bar start time of the buffer object chart
   index_bar_period=bar_period.Index(PERIOD_CURRENT);
//--- Calculate the amount of bars of the current timeframe included into one bar of the buffer object chart period
//--- and return this value (1 if the result is 0)
   int num_bars=::PeriodSeconds(bar_period.Timeframe())/::PeriodSeconds(bar_current.Timeframe());
   return(num_bars>0 ? num_bars : 1);
  }
//+------------------------------------------------------------------+

一切就绪。 现在我们的缓冲区对象也可以在多品种模式下工作了。

我们仍然缺乏一个方法,返回一根品种/周期图表上的柱线索引映射在当前图表的柱线索引。 对于主指标循环期间,在当前图表上正确显示来自另一个品种/周期的数据,该方法是必要的。

最适合这类方法的地方是时间序列集合类 \MQL5\Include\DoEasy\Collections\TimeSeriesCollection.mqh
在其内声明新的方法:

//--- Return the bar object of the specified timeseries of the specified symbol of the specified position (1) by index, (2) by time
//--- bar object of the first timeseries corresponding to the bar open time on the second timeseries (3) by index, (4) by time
   CBar                   *GetBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index,const bool from_series=true);
   CBar                   *GetBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const datetime bar_time);
   CBar                   *GetBarSeriesFirstFromSeriesSecond(const string symbol_first,const ENUM_TIMEFRAMES timeframe_first,const int index,
                                                             const string symbol_second=NULL,const ENUM_TIMEFRAMES timeframe_second=PERIOD_CURRENT);
   CBar                   *GetBarSeriesFirstFromSeriesSecond(const string symbol_first,const ENUM_TIMEFRAMES timeframe_first,const datetime first_bar_time,
                                                             const string symbol_second=NULL,const ENUM_TIMEFRAMES timeframe_second=PERIOD_CURRENT);

//--- Return the bar index on the specified timeframe chart by the current chart's bar index                                |
   int                     IndexBarPeriodByBarCurrent(const int series_index,const string symbol,const ENUM_TIMEFRAMES timeframe);

//--- Return the flag of opening a new bar of the specified timeseries of the specified symbol
   bool                    IsNewBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const datetime time=0);

我们在类主体之外编写其实现:

//+------------------------------------------------------------------+
//| Return the bar index on the specified timeframe chart            |
//| by the current chart's bar index                                 |
//+------------------------------------------------------------------+
int CTimeSeriesCollection::IndexBarPeriodByBarCurrent(const int series_index,const string symbol,const ENUM_TIMEFRAMES timeframe)
  {
   CSeriesDE *series=this.GetSeries(::Symbol(),(ENUM_TIMEFRAMES)::Period());
   if(series==NULL)
      return WRONG_VALUE;
   CBar *bar=series.GetBar(series_index);
   if(bar==NULL)
      return WRONG_VALUE;
   return ::iBarShift(symbol,timeframe,bar.Time());
  }
//+------------------------------------------------------------------+

该方法接收当前图表柱线索引,以及图表的品种周期,对于这些符号,而方法应返回与传递的当前图表索引时间相对应的柱线索引。

进一步,我们得到指向当前图表时间序列的指针按当前时间序列索引获得指向柱线对象的指针,并且使用柱线时间返回所需时间序列上相应柱线的索引

由于计算缓冲区存储的数据与指标依据的指标时间序列完全一致,所以可用该方法得到计算缓冲区数组中与当前图表上指定柱线索引相对应的索引(指标的循环索引在此可作为示例)。 如果我们能够在两个不同的时间序列之间建立这样的映射,那么我们就可以在所需图表上正确地显示这些数据。

为了从自定义程序访问该方法,我们需要从 CEngine 函数库主对象类(\MQL5\Include\DoEasy\Engine.mqh)提供对它的访问:

//--- Clear data by the timeseries index for the (1) arrow, (2) line, (3) section, (4) zero line histogram,
//--- (5) histogram on two buffers, (6) zigzag, (7) filling, (8) bars and (9) candles
   void                 BufferArrowClear(const int number,const int series_index)         { this.m_buffers.ClearBufferArrow(number,series_index);     }
   void                 BufferLineClear(const int number,const int series_index)          { this.m_buffers.ClearBufferLine(number,series_index);      }
   void                 BufferSectionClear(const int number,const int series_index)       { this.m_buffers.ClearBufferSection(number,series_index);   }
   void                 BufferHistogramClear(const int number,const int series_index)     { this.m_buffers.ClearBufferHistogram(number,series_index); }
   void                 BufferHistogram2Clear(const int number,const int series_index)    { this.m_buffers.ClearBufferHistogram2(number,series_index);}
   void                 BufferZigZagClear(const int number,const int series_index)        { this.m_buffers.ClearBufferZigZag(number,series_index);    }
   void                 BufferFillingClear(const int number,const int series_index)       { this.m_buffers.ClearBufferFilling(number,series_index);   }
   void                 BufferBarsClear(const int number,const int series_index)          { this.m_buffers.ClearBufferBars(number,series_index);      }
   void                 BufferCandlesClear(const int number,const int series_index)       { this.m_buffers.ClearBufferCandles(number,series_index);   }

//--- Return the bar index on the specified timeframe chart by the current chart's bar index
   int                  IndexBarPeriodByBarCurrent(const int series_index,const string symbol,const ENUM_TIMEFRAMES timeframe)
                          { return this.m_time_series.IndexBarPeriodByBarCurrent(series_index,symbol,timeframe);  }

//--- Display short description of all indicator buffers of the buffer collection
   void                 BuffersPrintShort(void);

多品种、多周期指标的函数库类开发,以及为测试而进行的改进至此完毕

若要执行测试,创建两个品种、多周期指标 — 移动均线和 MACD,从指定品种/周期获取数据,并在当前图表上绘制它们的数据。 在指标设定中,设置指标参数,和标准指标需要获取数据的图表周期、品种。

测试:多周期、多品种移动均线

为了执行测试,我们取来自前一篇文章的指标,并将其保存到 \MQL5\Indicators\TestDoEasy\Part46\ ,作为 TestDoEasyPart46_1.mq5

该指标在设置中指定品种和周期,并在单独子窗口中显示据其得到的蜡烛。 在同一子窗口中将显示依据指定参数和相同品种/周期的移动均线。

设置在图表子窗口里显示的指标数据输入指标的品种和图表周期,以及移动均线输入。 还有,设置调整 MA 输入参数的全局变量:

//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
//--- includes
#include <DoEasy\Engine.mqh>
//--- properties
#property indicator_separate_window
#property indicator_buffers 8
#property indicator_plots   2

//--- classes

//--- enums

//--- defines

//--- structures

//--- input variables
sinput   string               InpUsedSymbols    =  "GBPUSD";      // Used symbol (one only)
sinput   ENUM_TIMEFRAMES      InpPeriod         =  PERIOD_M30;    // Used chart period
//---
sinput   uint                 InpPeriodMA       =  14;            // MA Period
sinput   int                  InpShiftMA        =  0;             // MA Shift
sinput   ENUM_MA_METHOD       InpMethodMA       =  MODE_SMA;      // MA Method
sinput   ENUM_APPLIED_PRICE   InpPriceMA        =  PRICE_CLOSE;   // MA Applied Price
//---
sinput   bool                 InpUseSounds      =  true;          // Use sounds

//--- indicator buffers
CArrayObj     *list_buffers;                                      // Pointer to the buffer object list
//--- global variables
ENUM_SYMBOLS_MODE    InpModeUsedSymbols=  SYMBOLS_MODE_DEFINES;   // Mode of used symbols list
ENUM_TIMEFRAMES_MODE InpModeUsedTFs    =  TIMEFRAMES_MODE_LIST;   // Mode of used timeframes list
string               InpUsedTFs;                                  // List of used timeframes
CEngine              engine;                                      // CEngine library main object
string               prefix;                                      // Prefix of graphical object names
int                  min_bars;                                    // The minimum number of bars for the indicator calculation
int                  used_symbols_mode;                           // Mode of working with symbols
string               array_used_symbols[];                        // The array for passing used symbols to the library
string               array_used_periods[];                        // The array for passing used timeframes to the library
int                  handle_ma;                                   // МА handle
int                  period_ma;                                   // Moving Average calculation period
//+------------------------------------------------------------------+

在 OnInit() 应答程序里,创建三个缓存区对象 — 第一个用于 МА 曲线,第二个用于所选的品种蜡烛,而第三个用于纯计算,存储得自所选品种/周期的移动均线数据
接下来,为蜡烛缓冲区设置所有四个数据窗口缓冲区数组的说明。 另外,针对 MA 曲线缓冲区需要相同操作。 完成后,创建移动均线指标句柄

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Write the name of the working timeframe selected in the settings to the InpUsedTFs variable
   InpUsedTFs=TimeframeDescription(InpPeriod);
//--- Initialize DoEasy library
   OnInitDoEasy();
   IndicatorSetInteger(INDICATOR_DIGITS,(int)SymbolInfoInteger(InpUsedSymbols,SYMBOL_DIGITS)+1);
//--- Set indicator global variables
   prefix=engine.Name()+"_";
   //--- Get the index of the maximum used timeframe in the array,
   //--- calculate the number of bars of the current period fitting in the maximum used period
   //--- Use the obtained value if it exceeds 2, otherwise use 2
   int index=ArrayMaximum(ArrayUsedTimeframes);
   int num_bars=NumberBarsInTimeframe(ArrayUsedTimeframes[index]);
   min_bars=(index>WRONG_VALUE ? (num_bars>2 ? num_bars : 2) : 2);

//--- Check and remove remaining indicator graphical objects
   if(IsPresentObectByPrefix(prefix))
      ObjectsDeleteAll(0,prefix);

//--- Create the button panel

//--- Check playing a standard sound using macro substitutions
   engine.PlaySoundByDescription(SND_OK);
//--- Wait for 600 milliseconds
   engine.Pause(600);
   engine.PlaySoundByDescription(SND_NEWS);

//--- indicator buffers mapping
//--- Create all the necessary buffer objects
   engine.BufferCreateLine();          // 2 arrays
   engine.BufferCreateCandles();       // 5 arrays
   engine.BufferCreateCalculate();     // 1 array

//--- Check the number of buffers specified in the 'properties' block
   if(engine.BuffersPropertyPlotsTotal()!=indicator_plots)
      Alert(TextByLanguage("Внимание! Значение \"indicator_plots\" должно быть ","Attention! Value of \"indicator_plots\" should be "),engine.BuffersPropertyPlotsTotal());
   if(engine.BuffersPropertyBuffersTotal()!=indicator_buffers)
      Alert(TextByLanguage("Внимание! Значение \"indicator_buffers\" должно быть ","Attention! Value of \"indicator_buffers\" should be "),engine.BuffersPropertyBuffersTotal());
      
//--- Create the color array and set non-default colors to all buffers within the collection
   color array_colors[]={clrDodgerBlue,clrRed,clrGray};
   engine.BuffersSetColors(array_colors);
    
//--- Set МА period
   period_ma=int(InpPeriodMA<2 ? 2 : InpPeriodMA);

//--- In a loop by the list of collection buffer objects,
   for(int i=0;i<engine.GetListBuffers().Total();i++)
     { 
      //--- get the next buffer
      CBuffer *buff=engine.GetListBuffers().At(i);
      if(buff==NULL)
         continue;
      //--- and set its display in the data window depending on its specified usage
      //--- and also a chart period and symbol selected in the settings
      buff.SetShowData(true);
      buff.SetTimeframe(InpPeriod);
      buff.SetSymbol(InpUsedSymbols);
      if(buff.Status()==BUFFER_STATUS_CANDLES)
        {
         string pr=InpUsedSymbols+" "+TimeframeDescription(InpPeriod)+" ";
         string label=pr+"Open;"+pr+"High;"+pr+"Low;"+pr+"Close";
         buff.SetLabel(label);
        }
      if(buff.Status()==BUFFER_STATUS_LINE)
        {
         string label="MA("+(string)period_ma+")";
         buff.SetLabel(label);
        }
     }
     
//--- Display short descriptions of created indicator buffers
   engine.BuffersPrintShort();

//--- Create МА handle
   handle_ma=iMA(InpUsedSymbols,InpPeriod,period_ma,InpShiftMA,InpMethodMA,InpPriceMA);
   if(handle_ma==INVALID_HANDLE)
      return INIT_FAILED;
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

在 OnCalculate() 应答程序的数据准备模块里,把 MA 数据复制到计算缓冲区。

为了避免在每次即时报价上复制整个 MA 数据数组,我们需要记住,计算 “limit” 变量时,它在第一次启动,或更新历史数据时大于 1,当创立新柱线时等于1,其余时间在每次即时报价时为 0。
我们不能基于 “limit” 值复制数据 — 不能复制零根柱线。 这意味着当 'limit' 等于零时,我们也需要复制一根柱线。 在其他情况下,我们复制的数据量与 “limit” 中的指定值相同。 因此,我编排了一种节省资源方式,把相关 MA 数据复制到计算缓冲器:

//--- Prepare data
   CBufferCalculate *buff_calc=engine.GetBufferCalculate(0);
   int total_copy=(limit<2 ? 1 : limit);
   int copied=buff_calc.FillAsSeries(handle_ma,0,0,total_copy);
   if(copied<total_copy)
      return 0;
        

在主指标循环中,首先清除所有指标绘制缓冲区的当前柱线(以清除垃圾值),然后在重新计算选定品种在当前图表上的显示时,重新计算它们的 MA 线和蜡烛

//--- Main calculation loop of the indicator
   for(int i=limit; i>WRONG_VALUE && !IsStopped(); i--)
     {
      //--- Clear the current bar of all created buffers
      engine.BufferLineClear(0,0);
      engine.BufferCandlesClear(0,0);
      
      //--- Get the timeseries bar corresponding to the loop index time on the chart period specified in the settings
      bar=engine.SeriesGetBar(InpUsedSymbols,InpPeriod,time[i]);
      if(bar==NULL)
         continue;
      //--- Calculate the color index depending on the candle direction on the timeframe specified in the settings
      color_index=(bar.TypeBody()==BAR_BODY_TYPE_BULLISH ? 0 : bar.TypeBody()==BAR_BODY_TYPE_BEARISH ? 1 : 2);
      //--- Calculate the MA line buffer
      int index=engine.IndexBarPeriodByBarCurrent(i,InpUsedSymbols,InpPeriod);
      if(index<0)
         continue;
      engine.BufferSetDataLine(0,i,buff_calc.GetData(index),color_index);
      //--- Calculate the candle buffer
      engine.BufferSetDataCandles(0,i,bar.Open(),bar.High(),bar.Low(),bar.Close(),color_index);
     }
//--- return value of prev_calculated for next call

完整的指标代码在下面的文件中提供。

在 EURUSD M15 图表上启动指标,指定 GBPUSD M30 和一条以收盘价计算的简单移动均线,周期为 14,偏移量为 0:


为了进行比较,打开了相同参数的 GBPUSD 图表和移动均线指标。


测试:多周期、多品种 MACD

现在我们来创建一个多品种、多周期 MACD。 将新创建的指标另存为 TestDoEasyPart46_2.mq5

设置 MACD 的输入,以及计算和显示所需的所有缓冲区。 我们需要两个绘制缓冲区:直方图和柱线在当前图表上显示 MACD,两个计算缓冲区用于存储直方图数据,并按设置指定的品种/周期从 MACD 指标获取信号线。

我已尝试在代码注释中详细讲述所有操作和逻辑,因此在此我只提到与之前指标相比的基本变化

//+------------------------------------------------------------------+
//|                                           TestDoEasyPart46_2.mq5 |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
//--- includes
#include <DoEasy\Engine.mqh>
//--- properties
#property indicator_separate_window
#property indicator_buffers 6
#property indicator_plots   2

//--- classes

//--- enums

//--- defines

//--- structures

//--- input variables
sinput   string               InpUsedSymbols    =  "GBPUSD";      // Used symbol (one only)
sinput   ENUM_TIMEFRAMES      InpPeriod         =  PERIOD_M30;    // Used chart period
//---
sinput   uint                 InpPeriodFastEMA  =  12;            // MACD Fast EMA Period
sinput   uint                 InpPeriodSlowEMA  =  26;            // MACD Slow EMA Period
sinput   uint                 InpPeriodSignalMA =  9;             // MACD Signal MA Period
sinput   ENUM_APPLIED_PRICE   InpPriceMACD      =  PRICE_CLOSE;   // MA Applied Price
//---
sinput   bool                 InpUseSounds      =  true;          // Use sounds
//--- indicator buffers
CArrayObj     *list_buffers;                                      // Pointer to the buffer object list
//--- global variables
ENUM_SYMBOLS_MODE    InpModeUsedSymbols=  SYMBOLS_MODE_DEFINES;   // Mode of used symbols list
ENUM_TIMEFRAMES_MODE InpModeUsedTFs    =  TIMEFRAMES_MODE_LIST;   // Mode of used timeframes list
string               InpUsedTFs;                                  // List of used timeframes
CEngine              engine;                                      // CEngine library main object
string               prefix;                                      // Prefix of graphical object names
int                  min_bars;                                    // The minimum number of bars for the indicator calculation
int                  used_symbols_mode;                           // Mode of working with symbols
string               array_used_symbols[];                        // The array for passing used symbols to the library
string               array_used_periods[];                        // The array for passing used timeframes to the library
int                  handle_macd;                                 // МАCD handle
int                  fast_ema_period;                             // Fast EMA calculation period
int                  slow_ema_period;                             // Slow EMA calculation period
int                  signal_period;                               // MACD signal line calculation period
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Write the name of the working timeframe selected in the settings to the InpUsedTFs variable
   InpUsedTFs=TimeframeDescription(InpPeriod);
//--- Initialize DoEasy library
   OnInitDoEasy();
   IndicatorSetInteger(INDICATOR_DIGITS,(int)SymbolInfoInteger(InpUsedSymbols,SYMBOL_DIGITS)+1);
//--- Set indicator global variables
   prefix=engine.Name()+"_";
   //--- calculate the number of bars of the current period fitting in the maximum used period
   //--- Use the obtained value if it exceeds 2, otherwise use 2
   int num_bars=NumberBarsInTimeframe(InpPeriod);
   min_bars=(num_bars>2 ? num_bars : 2);

//--- Check and remove remaining indicator graphical objects
   if(IsPresentObectByPrefix(prefix))
      ObjectsDeleteAll(0,prefix);

//--- Create the button panel

//--- Check playing a standard sound using macro substitutions
   engine.PlaySoundByDescription(SND_OK);
//--- Wait for 600 milliseconds
   engine.Pause(600);
   engine.PlaySoundByDescription(SND_NEWS);

//--- indicator buffers mapping
//--- Create all the necessary buffer objects for constructing MACD
   engine.BufferCreateHistogram();     // 2 arrays
   engine.BufferCreateLine();          // 2 arrays
   engine.BufferCreateCalculate();     // 1 array for MACD histogram data from the specified symbol/period
   engine.BufferCreateCalculate();     // 1 array for MACD signal line from the specified symbol/period

//--- Check the number of buffers specified in the 'properties' block
   if(engine.BuffersPropertyPlotsTotal()!=indicator_plots)
      Alert(TextByLanguage("Внимание! Значение \"indicator_plots\" должно быть ","Attention! Value of \"indicator_plots\" should be "),engine.BuffersPropertyPlotsTotal());
   if(engine.BuffersPropertyBuffersTotal()!=indicator_buffers)
      Alert(TextByLanguage("Внимание! Значение \"indicator_buffers\" должно быть ","Attention! Value of \"indicator_buffers\" should be "),engine.BuffersPropertyBuffersTotal());
      
//--- Create the color array and set non-default colors to all buffers within the collection
   color array_colors[]={clrDodgerBlue,clrRed,clrGray};
   engine.BuffersSetColors(array_colors);

//--- Set МАCD calculation periods
   fast_ema_period=int(InpPeriodFastEMA<1 ? 1 : InpPeriodFastEMA);
   slow_ema_period=int(InpPeriodSlowEMA<1 ? 1 : InpPeriodSlowEMA);
   signal_period=int(InpPeriodSignalMA<1  ? 1 : InpPeriodSignalMA);

//--- Get the histogram buffer (the first drawn buffer)
//--- It has the index of 0 considering that the starting point is zero
   CBufferHistogram *buff_hist=engine.GetBufferHistogram(0);
   if(buff_hist!=NULL)
     {
      //--- Set the line width for the histogram
      buff_hist.SetWidth(3);
      //--- Set the graphical series description for the histogram
      string label="MACD ("+(string)fast_ema_period+","+(string)slow_ema_period+","+(string)signal_period+")";
      buff_hist.SetLabel(label);
      //--- and set display in the data window for the buffer
      //--- and also a chart period and symbol selected in the settings
      buff_hist.SetShowData(true);
      buff_hist.SetTimeframe(InpPeriod);
      buff_hist.SetSymbol(InpUsedSymbols);
     }
//--- Get the signal line buffer (the first drawn buffer)
//--- It has the index of 0 considering that the starting point is zero
   CBufferLine *buff_line=engine.GetBufferLine(0);
   if(buff_line!=NULL)
     {
      //--- Set the signal line width
      buff_line.SetWidth(1);
      //--- Set the graphical series description for the signal line
      string label="Signal";
      buff_line.SetLabel(label);
      //--- and set display in the data window for the buffer
      //--- and also a chart period and symbol selected in the settings
      buff_line.SetShowData(true);
      buff_line.SetTimeframe(InpPeriod);
      buff_line.SetSymbol(InpUsedSymbols);
     }
    
//--- Get the first calculated buffer
//--- It has the index of 0 considering that the starting point is zero
   CBufferCalculate *buff_calc=engine.GetBufferCalculate(0);
   if(buff_calc!=NULL)
     {
      //--- Set the description of the first calculated buffer as the "MACD histogram temporary array""
      buff_calc.SetLabel("MACD_HIST_TMP");
      //--- and set a chart period and symbol selected in the settings for it
      buff_calc.SetTimeframe(InpPeriod);
      buff_calc.SetSymbol(InpUsedSymbols);
     }
//--- Get the second calculated buffer
//--- It has the index of 1 considering that the starting point is zero
   buff_calc=engine.GetBufferCalculate(1);
   if(buff_calc!=NULL)
     {
      //--- Set the description of the second calculated buffer as the "MACD signal line temporary array""
      buff_calc.SetLabel("MACD_SIGN_TMP");
      //--- and set a chart period and symbol selected in the settings for it
      buff_calc.SetTimeframe(InpPeriod);
      buff_calc.SetSymbol(InpUsedSymbols);
     }
    
//--- Display short descriptions of created indicator buffers
   engine.BuffersPrintShort();

//--- Create МАCD handle
   handle_macd=iMACD(InpUsedSymbols,InpPeriod,fast_ema_period,slow_ema_period,signal_period,InpPriceMACD);
   if(handle_macd==INVALID_HANDLE)
      return INIT_FAILED;
//---
   IndicatorSetString(INDICATOR_SHORTNAME,InpUsedSymbols+" "+TimeframeDescription(InpPeriod)+" MACD("+(string)fast_ema_period+","+(string)slow_ema_period+","+(string)signal_period+")");
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Remove indicator graphical objects by an object name prefix
   ObjectsDeleteAll(0,prefix);
   Comment("");
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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[])
  {
//+------------------------------------------------------------------+
//| OnCalculate code block for working with the library:             |
//+------------------------------------------------------------------+
//--- Pass the current symbol data from OnCalculate() to the price structure and set the "as timeseries" flag to the arrays
   CopyDataAsSeries(rates_total,prev_calculated,time,open,high,low,close,tick_volume,volume,spread);

//--- Check for the minimum number of bars for calculation
   if(rates_total<min_bars || Point()==0) return 0;
   
//--- Handle the Calculate event in the library
//--- If the OnCalculate() method of the library returns zero, not all timeseries are ready - leave till the next tick
   if(engine.0)
      return 0;
   
//--- If working in the tester
   if(MQLInfoInteger(MQL_TESTER)) 
     {
      engine.OnTimer(rates_data);   // Working in the library timer
      EventsHandling();             // Working with library events
     }
//+------------------------------------------------------------------+
//| OnCalculate code block for working with the indicator:           |
//+------------------------------------------------------------------+
//--- Check and calculate the number of calculated bars
//--- If limit = 0, there are no new bars - calculate the current one
//--- If limit = 1, a new bar has appeared - calculate the first and the current ones
//--- limit > 1 means the first launch or changes in history - the full recalculation of all data
   int limit=rates_total-prev_calculated;
   
//--- Recalculate the entire history
   if(limit>1)
     {
      limit=rates_total-1;
      engine.BuffersInitPlots();
      engine.BuffersInitCalculates();
     }

//--- Prepare data 
   int total_copy=(limit<2 ? 1 : limit);
//--- Get the first calculated buffer by its number
   CBufferCalculate *buff_calc_hist=engine.GetBufferCalculate(0);
//--- Fill in the first calculated buffer with MACD histogram data
   int copied=buff_calc_hist.FillAsSeries(handle_macd,0,0,total_copy);
   if(copied<total_copy)
      return 0;
//--- Get the second calculated buffer by its number
   CBufferCalculate *buff_calc_sig=engine.GetBufferCalculate(1);
//--- Fill in the second calculated buffer with MACD signal line data
   copied=buff_calc_sig.FillAsSeries(handle_macd,1,0,total_copy);
   if(copied<total_copy)
      return 0;
        
//--- Calculate the indicator
   CBar *bar=NULL;         // Bar object for defining the candle direction
   uchar color_index=0;    // Color index to be set for the buffer depending on the candle direction

//--- Main calculation loop of the indicator
   for(int i=limit; i>WRONG_VALUE && !IsStopped(); i--)
     {
      //--- Clear the current bar of all created buffers
      engine.BufferHistogramClear(0,0);
      engine.BufferLineClear(0,0);
      
      //--- Get the timeseries bar corresponding to the loop index time on the chart period specified in the settings
      bar=engine.SeriesGetBar(InpUsedSymbols,InpPeriod,time[i]);
      if(bar==NULL)
         continue;
      //--- Calculate the color index depending on the candle direction on the timeframe specified in the settings
      color_index=(bar.TypeBody()==BAR_BODY_TYPE_BULLISH ? 0 : bar.TypeBody()==BAR_BODY_TYPE_BEARISH ? 1 : 2);
      //--- Calculate the MACD histogram buffer
      int index=engine.IndexBarPeriodByBarCurrent(i,InpUsedSymbols,InpPeriod);
      if(index<0)
         continue;
      engine.BufferSetDataHistogram(0,i,buff_calc_hist.GetData(index),color_index);
      //--- Calculate MACD signal line buffer
      engine.BufferSetDataLine(0,i,buff_calc_sig.GetData(index),color_index);
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

每根柱线上的直方图和信号线的颜色对应于指定品种/周期的 MACD 蜡烛方向:


该指标在 EURUSD M15 图表上启动,默认设置为基于 GBPUSD M30 的 MACD。 为了更清楚起见,打开相同参数的 GBPUSD M30,以及标准 MACD。

下一步是什么?

在下一篇文章中,函数库将获得提升创建多品种、多周期标准指标的功能。

以下附件是函数库当前版本的所有文件,以及测试 EA 文件,供您测试和下载。
将您的问题、评论和建议留在评论中。
请记住,此处我已经为 MetaTrader 5 开发了 MQL5 测试指标
附件仅适用于 MetaTrader 5。 当前函数库版本尚未在 MetaTrader 4 里进行测试。
在开发和测试指标缓冲区的功能之后,我将尝试在 MetaTrader 4 中实现一些 MQL5 特性。

返回内容目录

该系列中的先前文章:

DoEasy 函数库中的时间序列(第三十五部分):柱线对象和品种时间序列列表
DoEasy 函数库中的时间序列(第三十六部分):所有用到的品种周期的时间序列对象
DoEasy 函数库中的时间序列(第三十七部分):时间序列集合 - 按品种和周期的时间序列数据库
DoEasy 函数库中的时间序列(第三十八部分):时间序列集合 - 实时更新以及从程序访问数据
DoEasy 函数库中的时间序列(第三十九部分):基于函数库的指标 - 准备数据和时间序列事件
DoEasy 函数库中的时间序列(第四十部分):基于函数库的指标 - 实时刷新数据
DoEasy 函数库中的时间序列(第四十一部分):多品种多周期指标样品
DoEasy 函数库中的时间序列(第四十二部分):抽象指标缓冲区对象类
DoEasy 函数库中的时间序列(第四十三部分):指标缓冲区对象类
DoEasy 函数库中的时间序列(第四十四部分):指标缓冲区对象集合类
DoEasy 函数库中的时间序列(第四十五部分):多周期指标缓冲区

全部回复

0/140

量化课程

    移动端课程