在当前阶段,该函数库已包含了创建和跟踪多周期指标缓冲区的功能。 现在我们需要添加在多品种模式下工作的功能,以便我们能够处理进一步的任务,这些任务瞄准的是开发用于自定义程序的函数库工具。
我之前对缓冲区对象类的工作已经提供了这种功能,只是需要进行一些精练。 因此,今天我将做最后的准备,着手简化多品种、多周期标准指标的开发。
为达此目的,我需要改进计算缓冲区对象类,以便它能够依据标准指标句柄接收其数组所需的数组数据。 这个函数库已有能力做到这一点,但我即将实现的一些小幅附加极大地促进了这项任务,并允许在当前图表上显示任何品种/周期之处的标准指标数据。
在随后的文章中,我欲把当前概念应用在操控来自任意品种/周期的标准指标数据的类,并简化创建多品种、多周期标准指标的任务。
基本抽象缓冲区对象类包含当前指标缓冲区之后创建的后续指标缓冲区的数组索引值。 在当前对象中,我们必须根据当前缓冲区对象的类型,及其绘图样式对后续指标缓冲区的索引进行简单的计算,同时计算中还考虑到“两级别之间填充颜色”绘图样式的指标缓冲区未提供颜色缓冲区。
为了简化计算后续缓冲区索引的任务,并且考虑各种因素不能影响计算的清晰度,我们可以引入另一个类成员变量,其中包含构造缓冲区对象所有占用数组的数量。 创建每个缓冲区对象(抽象缓冲区类后代)时,在子类构造函数的参数里传递所需的数值给受保护的基准对象类构造函数,并指定严格设置的数值。
这看起来很棘手,但实际上一切都非常简单:当创建一个新的指标缓冲对象时,我们已将缓冲对象类构造函数中的一些值传递给其父类构造函数。 甚而,我们会多传递一个数值 – 即构造每个缓冲区对象所需数组的数量。
打开抽象指标缓冲区基本对象类的文件 \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。 将新创建的指标另存为 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 函数库中的时间序列(第三十五部分):柱线对象和品种时间序列列表本社区仅针对特定人员开放
查看需注册登录并通过风险意识测评
5秒后跳转登录页面...
移动端课程