内容
- 概述
- 改进库类
- 测试
- 下一步是什么?
概述
在之前一篇文章里,我使用了加速器振荡器标准指标来突显在当前图表上显示依据任意品种/时间帧计算出的标准指标数据的原理和方法。 现在,我们需要收集一些方法,方便我们开发其他标准指标。 我们的所有准备几乎都是为了实现于此。 在本文之中,我将执行测试,从而判别方法中类似的代码构造,以便随后将重复的代码模块移至单独的方法。 这是使用单缓冲区标准指标(我们仅用一个缓冲区来显示数据的指标)所需要做的最简单的事情。
在下面的文章中,我将基于当前文章里增强的方法,以及在下一篇文章中开发的操控多缓冲区标准指标的方法,来定义可以在代码中进行哪些改进,从而可以减少和优化代码。
今天,我将开发一个自定义指标示例,可在设置里选择,并在当前图表的子窗口中显示标准指标。 这是标准指标之一,拥有单一的绘制缓冲区,并在主图表子窗口中显示其数据。 为此,我不得不稍微改进库类。 这将是创建操控其余标准指标的方法当中最重要的准备步骤。
改进库类
首先,我们往 \MQL5\Include\DoEasy\Datas.mqh 里添加新的函数库消息。
添加新消息索引:
MSG_LIB_TEXT_BUFFER_TEXT_INVALID_PROPERTY_BUFF, // Invalid number of indicator buffers (#property indicator_buffers) MSG_LIB_TEXT_BUFFER_TEXT_MAX_BUFFERS_REACHED, // Reached maximum possible number of indicator buffers MSG_LIB_TEXT_BUFFER_TEXT_NO_BUFFER_OBJ, // No buffer object for standard indicator MSG_LIB_TEXT_BUFFER_TEXT_STATUS_NONE, // No drawing
以及与新添加索引对应的消息文本:
{"Неправильно указано количество буферов индикатора (#property indicator_buffers)","Number of indicator buffers incorrect (#property indicator_buffers)"}, {"Достигнуто максимально возможное количество индикаторных буферов","Maximum number of indicator buffers reached"}, {"Нет ни одного объекта-буфера для стандартного индикатора","There is no buffer object for the standard indicator"}, {"Нет отрисовки","No drawing"},
当前所有的改进都与 \MQL5\Include\DoEasy\Collections\BuffersCollection.mqh 中的指标缓冲区集合类有关,当然也与 CEngine 类有关。
在 BuffersCollection.mqh 里,我们需要添加一个单独方法来准备所有已创建标准指标的计算缓冲区的数据,以便函数库在收到程序命令后能够自行执行此操作。 最终这将简化程序代码 — 无需搜索和获取所需的对象。
在类的公开部分里声明方法:
//--- Prepare calculated buffer data of (1) the specified standard indicator and (2) all created standard indicators int PreparingDataBufferStdInd(const ENUM_INDICATOR std_ind,const int id,const int total_copy); bool PreparingDataAllBuffersStdInd(void); //--- Clear buffer data of the specified standard indicator by the timeseries index
并在类的主体之外编写其实现:
//+------------------------------------------------------------------+ //| Prepare the calculated buffer data | //| of all created standard indicators | //+------------------------------------------------------------------+ bool CBuffersCollection::PreparingDataAllBuffersStdInd(void) { CArrayObj *list=this.GetListBuffersWithID(); if(list==NULL || list.Total()==0) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_NO_BUFFER_OBJ)); return false; } bool res=true; int total=list.Total(); for(int i=0;i<total;i++) { CBuffer *buff=list.At(i); if(buff==NULL || buff.TypeBuffer()==BUFFER_TYPE_DATA || buff.IndicatorType()==WRONG_VALUE) continue; CSeriesDE *series=this.m_timeseries.GetSeries(buff.Symbol(),buff.Timeframe()); if(series==NULL) continue; int used_data=(int)series.AvailableUsedData(); int copied=this.PreparingDataBufferStdInd(buff.IndicatorType(),buff.ID(),used_data); if(copied<used_data) res &=false; } return res; } //+------------------------------------------------------------------+
标准指标的每个计算缓冲区对象都有一个 ID,该 ID 根据标准指标类型(ENUM_INDICATOR)自动分配,或者在创建必要的缓冲区对象时,自程序当中手动分配。
我们在此第一件事是获取 ID 不等于 -1 的缓冲区对象列表。
如果列表为空,则显示尚未创建标准指标缓冲区对象的消息,并返回 false 。
接下来,拥有标准指标 ID 的所有缓冲区对象数量,并作为循环次数,逐一获取下一个缓冲区对象。 每个标准指标都至少拥有两个这样的缓冲对象 — 计算和绘制各一个。 我们只需要计算缓冲区,但由于在上一篇文章中我曾研究过的 PreparingDataBufferStdInd() 方法中只选择了计算缓冲区进一步处理,因此我们在当前循环中不会选择它们(我将在后面对此方法进行改进)。 所以,在此我不再重复此选择。
现在,我们需要为品种/周期已创建缓冲区对象定义应有多少根柱线。 我们需要此数值来决定复制多少标准指标的数据给为其创建的缓冲对象。 为此,通过缓冲区对象的品种和时间帧值获得必要的时间序列,然后从其中获取可用的数据量。
之后,调用 PreparingDataBufferStdInd() 方法将必要数量的数据从指标句柄复制到计算用缓冲区对象。
如果复制的数据量少于所需,则 res 变量将置为 false 。 如果至少一个现存缓冲区对象没有复制到所需数量 ,则 res 变量包含 false 。 并从该方法返回该变量值。 如果所有计算缓冲区对象已成功复制所有数据,则该方法返回 true 。
到目前为止,该方法将所有现有数据从指标句柄复制到计算缓冲区。 当然,在每次即时报价上为一个以上的指标复制大量数据是不切实际的。 但由于我当前正在创建使用标准指标的功能,故我目前仍保持该行为不变。 此处的逻辑比速度更重要。 稍后,我将确保仅在必要条件下复制数据(首次启动,历史数据有变化)。 在其他情况下,仅复制所需的数据量(一或两根柱线)。
在上一篇文章中,我已声明了创建标准指标句柄和配套缓冲区的所有方法。 但我还没有实现它们(创建 AC 和 AD 指标的两种方法除外)。 今天,我将实现创建标准指标句柄及其含有单一缓冲区对象的方法,且指标在主图表子窗口中绘制其自身数据。
此外,我希望按默认情况所创建指标的颜色与相应标准指标的颜色能够相互对应。 我们仍然可以为这些缓冲对象自行设置颜色。 为此,我们只需在主循环中计算缓冲区时传递所需颜色的索引即可。
我们来研究针对加速振荡器标准指标的缓冲区创建方法所做的修改:
//+------------------------------------------------------------------+ //| Create multi-symbol multi-period AC | //+------------------------------------------------------------------+ int CBuffersCollection::CreateAC(const string symbol,const ENUM_TIMEFRAMES timeframe,const int id=WRONG_VALUE) { //--- Create the indicator handle and set the default ID int handle=::iAC(symbol,timeframe); int identifier=(id==WRONG_VALUE ? IND_AC : id); color array_colors[3]={clrGreen,clrRed,clrGreen}; CBuffer *buff=NULL; if(handle!=INVALID_HANDLE) { //--- Create the histogram buffer from the zero line this.CreateHistogram(); //--- Get the last created (drawn) buffer object and set all the necessary parameters to it buff=this.GetLastCreateBuffer(); if(buff==NULL) return INVALID_HANDLE; buff.SetSymbol(symbol); buff.SetTimeframe(timeframe); buff.SetID(identifier); buff.SetIndicatorHandle(handle); buff.SetIndicatorType(IND_AC); buff.SetShowData(true); buff.SetLabel("AC("+symbol+","+TimeframeDescription(timeframe)+")"); buff.SetIndicatorName("Accelerator Oscillator"); buff.SetColors(array_colors); //--- Create a calculated buffer storing standard indicator data this.CreateCalculate(); //--- Get the last created (calculated) buffer object and set all the necessary parameters to it buff=this.GetLastCreateBuffer(); if(buff==NULL) return INVALID_HANDLE; buff.SetSymbol(symbol); buff.SetTimeframe(timeframe); buff.SetID(identifier); buff.SetIndicatorHandle(handle); buff.SetIndicatorType(IND_AC); buff.SetEmptyValue(EMPTY_VALUE); buff.SetLabel("AC("+symbol+","+TimeframeDescription(timeframe)+")"); buff.SetIndicatorName("Accelerator Oscillator"); } return handle; } //+------------------------------------------------------------------+
由于加速振荡器r标准指标用两种颜色来显示其数值,因此声明颜色数组的大小为 3,该颜色数组可立即用三种颜色初始化。 为何是三个? 因为索引为 0 的颜色显示指标线的升序值,而索引为 1 的颜色显示指标线的降序值。 但我们还需要一种颜色来显示指标线的两个相邻柱线的数值相等。 在标准的加速器振荡器中,它们借用升值的颜色显示。 因此,我们需要三种颜色。
创建绘制缓冲区对象后,为其设置的绘制颜色来自此数组。
当获取指向最后所创建缓冲区对象的指针时,可能会出差错。 以前,没有考虑到这种情况,这构成了潜在的危险 - 按无效指针进行访问会导致程序崩溃。
因此,在这种情况下,我们需要退出方法,并返回 INVALID_HANDLE。
对于只有一种绘图颜色的标准指标,我打算仅设置一种元素的颜色数组。 这就是创建彩色标准指标的句柄、及其缓冲区对象的方法,与创建单色标准指标方法的不同之处。
例如,以下是创建平均真实范围标准指标的方法:
//+------------------------------------------------------------------+ //| Create multi-symbol multi-period ATR | //+------------------------------------------------------------------+ int CBuffersCollection::CreateATR(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period,const int id=WRONG_VALUE) { //--- Create the indicator handle and set the default ID int handle=::iATR(symbol,timeframe,ma_period); int identifier=(id==WRONG_VALUE ? IND_ATR : id); color array_colors[1]={clrLightSeaGreen}; CBuffer *buff=NULL; if(handle!=INVALID_HANDLE) { //--- Create the line buffer this.CreateLine(); //--- Get the last created (drawn) buffer object and set all the necessary parameters to it buff=this.GetLastCreateBuffer(); if(buff==NULL) return INVALID_HANDLE; buff.SetSymbol(symbol); buff.SetTimeframe(timeframe); buff.SetID(identifier); buff.SetIndicatorHandle(handle); buff.SetIndicatorType(IND_ATR); buff.SetShowData(true); buff.SetLabel("ATR("+symbol+","+TimeframeDescription(timeframe)+": "+(string)ma_period+")"); buff.SetIndicatorName("Average True Range"); buff.SetColors(array_colors); //--- Create a calculated buffer storing standard indicator data this.CreateCalculate(); //--- Get the last created (calculated) buffer object and set all the necessary parameters to it buff=this.GetLastCreateBuffer(); if(buff==NULL) return INVALID_HANDLE; buff.SetSymbol(symbol); buff.SetTimeframe(timeframe); buff.SetID(identifier); buff.SetIndicatorHandle(handle); buff.SetIndicatorType(IND_ATR); buff.SetEmptyValue(EMPTY_VALUE); buff.SetLabel("ATR("+symbol+","+TimeframeDescription(timeframe)+": "+(string)ma_period+")"); buff.SetIndicatorName("Average True Range"); } return handle; } //+------------------------------------------------------------------+
今天,我将实现在子窗口中绘制单缓冲区标准指标的方法,即:
- 加速器振荡器
- 吸筹/派发
- 动量振荡器
- 平均真实范围
- 空头推力
- 多头推力
- Chaikin 振荡器
- 商品通道指数
- DeMarker
- 强力指数
- 动量
- 资金流指数
- 移动平均线振荡器
- 能量潮
- 相对强度指数
- 标准偏差
- 三重指数平均值
- 威廉的百分比范围
- 成交量
上面所列创建多品种、多时段指标的方法均已创建。 在此研究它们没有意义。 它们与所研究的方法相同,并提供在下面的附件之中。
您大概已经注意到,上面列表不包含在子窗口中绘制的市场便利指数指标,且没有绘制缓冲区。 乍看之下,两个条件都满足。 然而,为了计算直方图列的颜色,它还需要另一个计算缓冲区 — 成交量指标缓冲区。 因此,此指标在子窗口中被视为多缓冲区。
PreparingDataBufferStdInd() 方法取用指标句柄中的数据来填充计算缓冲区对象数组。 我在上一篇文章中曾研究过这种方法,尽管当时只实现了填充 AC 指标缓冲区:
//+------------------------------------------------------------------+ //| Prepare the calculated buffer data | //| of the specified standard indicator | //+------------------------------------------------------------------+ int CBuffersCollection::PreparingDataBufferStdInd(const ENUM_INDICATOR std_ind,const int id,const int total_copy) { CArrayObj *list=this.GetListBufferByTypeID(std_ind,id); list=CSelect::ByBufferProperty(list,BUFFER_PROP_TYPE,BUFFER_TYPE_CALCULATE,EQUAL); if(list==NULL || list.Total()==0) return 0; CBufferCalculate *buffer=NULL; int copies=WRONG_VALUE; switch((int)std_ind) { case IND_AC : buffer=list.At(0); if(buffer==NULL) return 0; copies=buffer.FillAsSeries(buffer.IndicatorHandle(),0,0,total_copy); return copies; case IND_AD : break; case IND_ADX : break; case IND_ADXW : break; case IND_ALLIGATOR : break; case IND_AMA : break; case IND_AO : break; case IND_ATR : break; case IND_BANDS : break; case IND_BEARS : break; case IND_BULLS : break; case IND_BWMFI : break; case IND_CCI : break; case IND_CHAIKIN : break; case IND_DEMA : break; case IND_DEMARKER : break; case IND_ENVELOPES : break; case IND_FORCE : break; case IND_FRACTALS : break; case IND_FRAMA : break; case IND_GATOR : break; case IND_ICHIMOKU : break; case IND_MA : break; case IND_MACD : break; case IND_MFI : break; case IND_MOMENTUM : break; case IND_OBV : break; case IND_OSMA : break; case IND_RSI : break; case IND_RVI : break; case IND_SAR : break; case IND_STDDEV : break; case IND_STOCHASTIC : break; case IND_TEMA : break; case IND_TRIX : break; case IND_VIDYA : break; case IND_VOLUMES : break; case IND_WPR : break; default: break; } return 0; } //+------------------------------------------------------------------+
更有趣的是,针对其他单缓冲区指标理应执行完全相同的操作。 switch 运算符在此为我们达成目标提供了帮助,因为它取表达式的数值,并与所有 case 语句变体包括的常量比较,并依据结果将控制权传递给匹配表达式数值的操作符。 每个 case 语句都以 break 或 return 操作符结尾。 如果 case 语句未设置任何操作符,则在必要的情况下处理表达式数值之后,控制权将传递到下一个 case 语句。
因此,与其将类似的必要操作复制到执行相同操作的所有 case 语句,不如简单地将所有这些 case 语句合并一处,此时仅需一个 return 或 break 操作符:
//+------------------------------------------------------------------+ //| Prepare the calculated buffer data | //| of the specified standard indicator | //+------------------------------------------------------------------+ int CBuffersCollection::PreparingDataBufferStdInd(const ENUM_INDICATOR std_ind,const int id,const int total_copy) { CArrayObj *list=this.GetListBufferByTypeID(std_ind,id); list=CSelect::ByBufferProperty(list,BUFFER_PROP_TYPE,BUFFER_TYPE_CALCULATE,EQUAL); if(list==NULL || list.Total()==0) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_NO_BUFFER_OBJ)); return 0; } CBufferCalculate *buffer=NULL; int copies=WRONG_VALUE; switch((int)std_ind) { //--- Single-buffer standard indicators in a subwindow case IND_AC : case IND_AD : case IND_AO : case IND_ATR : case IND_BEARS : case IND_BULLS : case IND_CHAIKIN : case IND_CCI : case IND_DEMARKER : case IND_FORCE : case IND_MOMENTUM : case IND_MFI : case IND_OSMA : case IND_OBV : case IND_RSI : case IND_STDDEV : case IND_TRIX : case IND_VOLUMES : case IND_WPR : buffer=list.At(0); if(buffer==NULL) return 0; copies=buffer.FillAsSeries(buffer.IndicatorHandle(),0,0,total_copy); return copies; case IND_ADX : break; case IND_ADXW : break; case IND_ALLIGATOR : break; case IND_AMA : break; case IND_BANDS : break; case IND_BWMFI : break; case IND_DEMA : break; case IND_ENVELOPES : break; case IND_FRACTALS : break; case IND_FRAMA : break; case IND_GATOR : break; case IND_ICHIMOKU : break; case IND_MA : break; case IND_MACD : break; case IND_RVI : break; case IND_SAR : break; case IND_STOCHASTIC : break; case IND_TEMA : break; case IND_VIDYA : break; default: break; } return 0; } //+------------------------------------------------------------------+
通过清除指定标准指标缓冲区数据的方法进行相同的操作:
//+------------------------------------------------------------------+ //| Clear buffer data of the specified standard indicator | //| by the timeseries index | //+------------------------------------------------------------------+ void CBuffersCollection::ClearDataBufferStdInd(const ENUM_INDICATOR std_ind,const int id,const int series_index) { //--- Get the list of buffer objects by type and ID CArrayObj *list=this.GetListBufferByTypeID(std_ind,id); if(list==NULL || list.Total()==0) return; list=CSelect::ByBufferProperty(list,BUFFER_PROP_TYPE,BUFFER_TYPE_DATA,EQUAL); if(list.Total()==0) return; CBuffer *buffer=NULL; switch((int)std_ind) { //--- Single-buffer standard indicators in a subwindow case IND_AC : case IND_AD : case IND_AO : case IND_ATR : case IND_BEARS : case IND_BULLS : case IND_CHAIKIN : case IND_CCI : case IND_DEMARKER : case IND_FORCE : case IND_MOMENTUM : case IND_MFI : case IND_OSMA : case IND_OBV : case IND_RSI : case IND_STDDEV : case IND_TRIX : case IND_VOLUMES : case IND_WPR : buffer=list.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); break; case IND_ADX : break; case IND_ADXW : break; case IND_ALLIGATOR : break; case IND_AMA : break; case IND_BANDS : break; case IND_BWMFI : break; case IND_DEMA : break; case IND_ENVELOPES : break; case IND_FRACTALS : break; case IND_FRAMA : break; case IND_GATOR : break; case IND_ICHIMOKU : break; case IND_MA : break; case IND_MACD : break; case IND_RVI : break; case IND_SAR : break; case IND_STOCHASTIC : break; case IND_TEMA : break; case IND_VIDYA : break; default: break; } } //+------------------------------------------------------------------+
在为指定标准指标的缓冲数据设置数值的方法中:
//+------------------------------------------------------------------+ //| Set values for the current chart to the specified buffer | //| of the standard indicator by the timeseries index according to | //| the buffer object symbol/period | //+------------------------------------------------------------------+ bool CBuffersCollection::SetDataBufferStdInd(const ENUM_INDICATOR ind_type,const int id,const int series_index,const datetime series_time,const char color_index=WRONG_VALUE) { //--- Get the list of buffer objects by type and ID CArrayObj *list=this.GetListBufferByTypeID(ind_type,id); if(list==NULL || list.Total()==0) { ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_NO_BUFFER_OBJ)); return false; } //--- Get the list of drawn objects with ID CArrayObj *list_data=CSelect::ByBufferProperty(list,BUFFER_PROP_TYPE,BUFFER_TYPE_DATA,EQUAL); list_data=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_TYPE,ind_type,EQUAL); //--- Get the list of calculated buffers with ID CArrayObj *list_calc=CSelect::ByBufferProperty(list,BUFFER_PROP_TYPE,BUFFER_TYPE_CALCULATE,EQUAL); list_calc=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_TYPE,ind_type,EQUAL); //--- Exit if any of the lists is empty if(list_data.Total()==0 || list_calc.Total()==0) return false; //--- Declare the necessary objects and variables CBuffer *buffer_data=NULL; CBuffer *buffer_calc=NULL; int index_period=0; int series_index_start=0; int num_bars=1,index=0; datetime time_period=0; double value0=EMPTY_VALUE, value1=EMPTY_VALUE; //--- Depending on the standard indicator type switch((int)ind_type) { //--- Single-buffer standard indicators case IND_AC : case IND_AD : case IND_AO : case IND_ATR : case IND_BEARS : case IND_BULLS : case IND_CHAIKIN : case IND_CCI : case IND_DEMARKER : case IND_FORCE : case IND_MOMENTUM : case IND_MFI : case IND_OSMA : case IND_OBV : case IND_RSI : case IND_STDDEV : case IND_TRIX : case IND_VOLUMES : case IND_WPR : //--- Get drawn and calculated buffer objects buffer_data=list_data.At(0); buffer_calc=list_calc.At(0); if(buffer_calc==NULL || buffer_data==NULL || buffer_calc.GetDataTotal(0)==0) return false; //--- Find the bar index corresponding to the current bar start time index_period=::iBarShift(buffer_calc.Symbol(),buffer_calc.Timeframe(),series_time,true); if(index_period==WRONG_VALUE || index_period>buffer_calc.GetDataTotal()-1) return false; //--- Get the value by the index from the indicator buffer value0=buffer_calc.GetDataBufferValue(0,index_period); if(buffer_calc.Symbol()==::Symbol() && buffer_calc.Timeframe()==::Period()) { series_index_start=series_index; num_bars=1; } else { //--- Get the bar time the bar with the index_period index falls into on the calculated buffer period and symbol time_period=::iTime(buffer_calc.Symbol(),buffer_calc.Timeframe(),index_period); if(time_period==0) return false; //--- Get the appropriate current chart bar series_index_start=::iBarShift(::Symbol(),::Period(),time_period,true); if(series_index_start==WRONG_VALUE) return false; //--- Calculate the number of bars on the current chart which should be filled with calculated buffer data num_bars=::PeriodSeconds(buffer_calc.Timeframe())/::PeriodSeconds(PERIOD_CURRENT); if(num_bars==0) num_bars=1; } //--- Take values to calculate colors value1=(series_index_start+num_bars>buffer_data.GetDataTotal()-1 ? value0 : buffer_data.GetDataBufferValue(0,series_index_start+num_bars)); //--- In the loop by the number of bars in num_bars, fill in the drawn buffer with the calculated buffer value taken by the index_period index //--- and set the color of the drawn buffer depending on the value0 and value1 values ratio for(int i=0;i<num_bars;i++) { index=series_index_start-i; buffer_data.SetBufferValue(0,index,value0); buffer_data.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value0>value1 ? 0 : value0<value1 ? 1 : 2) : color_index); } return true; case IND_ADX : break; case IND_ADXW : break; case IND_ALLIGATOR : break; case IND_AMA : break; case IND_BANDS : break; case IND_BWMFI : break; case IND_DEMA : break; case IND_ENVELOPES : break; case IND_FRACTALS : break; case IND_FRAMA : break; case IND_GATOR : break; case IND_ICHIMOKU : break; case IND_MA : break; case IND_MACD : break; case IND_RVI : break; case IND_SAR : break; case IND_STOCHASTIC : break; case IND_TEMA : break; case IND_VIDYA : break; default: break; } return false; } //+------------------------------------------------------------------+
在上一篇文章里,我已经研究了所有三种方法。 在本文中,我简单地合并了 case 语句,其中应该创建相同的处理程序。
处理程序的最后需要 return 或 break 运算符,因此处理中的 switch 数值不会传递给其他处理程序的其他 case 语句。
现在,我们需要改进 \MQL5\Include\DoEasy\Engine.mqh 中的 CEngine 函数库主对象类。
在缓冲区集合类中设置了用于创建标准指标和缓冲区对象的方法(一些尚未被研究的方法现在只需返回 INVALID_HANDLE ),我们需要为所有这些方法设置权限,从而访问 CEngine 类中的程序。
在类的公开部分中,编写所有这些方法的调用://--- The methods of creating standard indicators and buffer objects for them //--- Create the standard Accelerator Oscillator indicator bool BufferCreateAC(const string symbol,const ENUM_TIMEFRAMES timeframe,const int id) { return(this.m_buffers.CreateAC(symbol,timeframe,id)!=INVALID_HANDLE); } //--- Create the standard Accumulation/Distribution indicator bool BufferCreateAD(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_APPLIED_VOLUME applied_volume,const int id) { return(this.m_buffers.CreateAD(symbol,timeframe,applied_volume,id)!=INVALID_HANDLE); } //--- Create the standard ADX indicator bool BufferCreateADX(const string symbol,const ENUM_TIMEFRAMES timeframe,const int adx_period,const int id) { return(this.m_buffers.CreateADX(symbol,timeframe,adx_period,id)!=INVALID_HANDLE); } //--- Create the standard ADX Wilder indicator bool BufferCreateADXWilder(const string symbol,const ENUM_TIMEFRAMES timeframe,const int adx_period,const int id) { return(this.m_buffers.CreateADXWilder(symbol,timeframe,adx_period,id)!=INVALID_HANDLE); } //--- Create the standard Alligator indicator bool BufferCreateAlligator(const string symbol,const ENUM_TIMEFRAMES timeframe, const int jaw_period,const int jaw_shift, const int teeth_period,const int teeth_shift, const int lips_period,const int lips_shift, const ENUM_MA_METHOD ma_method,const ENUM_APPLIED_PRICE applied_price,const int id) { return(this.m_buffers.CreateAlligator(symbol,timeframe, jaw_period,jaw_shift, teeth_period,teeth_shift, lips_period,lips_shift, ma_method,applied_price,id)!=INVALID_HANDLE); } //--- Create the standard Adaprive Moving Average indicator bool BufferCreateAMA(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ama_period, const int fast_ema_period, const int slow_ema_period, const int ama_shift, const ENUM_APPLIED_PRICE applied_price, const int id) { return(this.m_buffers.CreateAMA(symbol,timeframe, ama_period, fast_ema_period,slow_ema_period, ama_shift,applied_price,id)!=INVALID_HANDLE); } //--- Create the standard Awesome Oscillator indicator bool BufferCreateAO(const string symbol,const ENUM_TIMEFRAMES timeframe,const int id) { return(this.m_buffers.CreateAO(symbol,timeframe,id)!=INVALID_HANDLE); } //--- Create the standard Average True Range indicator bool BufferCreateATR(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period,const int id) { return(this.m_buffers.CreateATR(symbol,timeframe,ma_period,id)!=INVALID_HANDLE); } //--- Create the standard Bollinger Bands indicator bool BufferCreateBands(const string symbol,const ENUM_TIMEFRAMES timeframe, const int bands_period, const int bands_shift, const double deviation, const ENUM_APPLIED_PRICE applied_price, const int id) { return(this.m_buffers.CreateBands(symbol,timeframe, bands_period,bands_shift, deviation,applied_price,id)!=INVALID_HANDLE); } //--- Create the standard Bears Power indicator bool BufferCreateBearsPower(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period,const int id) { return(this.m_buffers.CreateBearsPower(symbol,timeframe,ma_period,id)!=INVALID_HANDLE); } //--- Create the standard Bulls Power indicator bool BufferCreateBullsPower(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period,const int id) { return(this.m_buffers.CreateBullsPower(symbol,timeframe,ma_period,id)!=INVALID_HANDLE); } //--- Create the standard Chaikin Oscillator indicator bool BufferCreateChaikin(const string symbol,const ENUM_TIMEFRAMES timeframe, const int fast_ma_period, const int slow_ma_period, const ENUM_MA_METHOD ma_method, const ENUM_APPLIED_VOLUME applied_volume, const int id) { return(this.m_buffers.CreateChaikin(symbol,timeframe, fast_ma_period,slow_ma_period, ma_method,applied_volume,id)!=INVALID_HANDLE); } //--- Create the standard Commodity Channel Index indicator bool BufferCreateCCI(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, const ENUM_APPLIED_PRICE applied_price, const int id) { return(this.m_buffers.CreateCCI(symbol,timeframe,ma_period,applied_price,id)!=INVALID_HANDLE); } //--- Create the standard DEMA indicator bool BufferCreateDEMA(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, const int ma_shift, const ENUM_APPLIED_PRICE applied_price, const int id) { return(this.m_buffers.CreateDEMA(symbol,timeframe,ma_period,ma_shift,applied_price,id)!=INVALID_HANDLE); } //--- Create the standard DeMarker indicator bool BufferCreateDeMarker(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period,const int id) { return(this.m_buffers.CreateDeMarker(symbol,timeframe,ma_period,id)!=INVALID_HANDLE); } //--- Create the standard Envelopes indicator bool BufferCreateEnvelopes(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, const int ma_shift, const ENUM_MA_METHOD ma_method, const ENUM_APPLIED_PRICE applied_price, const double deviation, const int id) { return(this.m_buffers.CreateEnvelopes(symbol,timeframe, ma_period,ma_shift,ma_method, applied_price,deviation,id)!=INVALID_HANDLE); } //--- Create the standard Force Index indicator bool BufferCreateForce(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, const ENUM_MA_METHOD ma_method, const ENUM_APPLIED_VOLUME applied_volume, const int id) { return(this.m_buffers.CreateForce(symbol,timeframe,ma_period,ma_method,applied_volume,id)!=INVALID_HANDLE); } //--- Create the standard Fractals indicator bool BufferCreateFractals(const string symbol,const ENUM_TIMEFRAMES timeframe,const int id) { return(this.m_buffers.CreateFractals(symbol,timeframe,id)!=INVALID_HANDLE); } //--- Create the standard FrAMA indicator bool BufferCreateFrAMA(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, const int ma_shift, const ENUM_APPLIED_PRICE applied_price, const int id) { return(this.m_buffers.CreateFrAMA(symbol,timeframe,ma_period,ma_shift,applied_price,id)!=INVALID_HANDLE); } //--- Create the standard Gator indicator bool BufferCreateGator(const string symbol,const ENUM_TIMEFRAMES timeframe, const int jaw_period, const int jaw_shift, const int teeth_period, const int teeth_shift, const int lips_period, const int lips_shift, const ENUM_MA_METHOD ma_method, const ENUM_APPLIED_PRICE applied_price, const int id) { return(this.m_buffers.CreateGator(symbol,timeframe, jaw_period,jaw_shift, teeth_period,teeth_shift, lips_period,lips_shift, ma_method,applied_price,id)!=INVALID_HANDLE); } //--- Create the standard Ichimoku indicator bool BufferCreateIchimoku(const string symbol,const ENUM_TIMEFRAMES timeframe, const int tenkan_sen, const int kijun_sen, const int senkou_span_b, const int id) { return(this.m_buffers.CreateIchimoku(symbol,timeframe,tenkan_sen,kijun_sen,senkou_span_b,id)!=INVALID_HANDLE); } //--- Create the standard BW MFI indicator bool BufferCreateBWMFI(const string symbol,const ENUM_TIMEFRAMES timeframe, const ENUM_APPLIED_VOLUME applied_volume, const int id) { return(this.m_buffers.CreateBWMFI(symbol,timeframe,applied_volume,id)!=INVALID_HANDLE); } //--- Create the standard Momentum indicator bool BufferCreateMomentum(const string symbol,const ENUM_TIMEFRAMES timeframe, const int mom_period, const ENUM_APPLIED_PRICE applied_price, const int id) { return(this.m_buffers.CreateMomentum(symbol,timeframe,mom_period,applied_price,id)!=INVALID_HANDLE); } //--- Create the standard Money Flow Index indicator bool BufferCreateMFI(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, const ENUM_APPLIED_VOLUME applied_volume, const int id) { return(this.m_buffers.CreateMFI(symbol,timeframe,ma_period,applied_volume,id)!=INVALID_HANDLE); } //--- Create the standard Moving Average indicator bool BufferCreateMA(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, const int ma_shift, const ENUM_MA_METHOD ma_method, const ENUM_APPLIED_PRICE applied_price, const int id) { return(this.m_buffers.CreateMA(symbol,timeframe,ma_period,ma_shift,ma_method,applied_price,id)!=INVALID_HANDLE); } //--- Create the standard Moving Average of Oscillator indicator bool BufferCreateOsMA(const string symbol,const ENUM_TIMEFRAMES timeframe, const int fast_ema_period, const int slow_ema_period, const int signal_period, const ENUM_APPLIED_PRICE applied_price, const int id) { return(this.m_buffers.CreateOsMA(symbol,timeframe,fast_ema_period,slow_ema_period,signal_period,applied_price,id)!=INVALID_HANDLE); } //--- Create the standard MACD indicator bool BufferCreateMACD(const string symbol,const ENUM_TIMEFRAMES timeframe, const int fast_ema_period, const int slow_ema_period, const int signal_period, const ENUM_APPLIED_PRICE applied_price, const int id) { return(this.m_buffers.CreateMACD(symbol,timeframe,fast_ema_period,slow_ema_period,signal_period,applied_price,id)!=INVALID_HANDLE); } //--- Create the standard On Balance Volume indicator bool BufferCreateOBV(const string symbol,const ENUM_TIMEFRAMES timeframe, const ENUM_APPLIED_VOLUME applied_volume, const int id) { return(this.m_buffers.CreateOBV(symbol,timeframe,applied_volume,id)!=INVALID_HANDLE); } //--- Create the standard Parabolic SAR indicator bool BufferCreateSAR(const string symbol,const ENUM_TIMEFRAMES timeframe, const double step, const double maximum, const int id) { return(this.m_buffers.CreateSAR(symbol,timeframe,step,maximum,id)!=INVALID_HANDLE); } //--- Create the standard Relative Strength Index indicator bool BufferCreateRSI(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, const ENUM_APPLIED_PRICE applied_price, const int id) { return(this.m_buffers.CreateRSI(symbol,timeframe,ma_period,applied_price,id)!=INVALID_HANDLE); } //--- Create the standard RVI indicator bool BufferCreateRVI(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period,const int id) { return(this.m_buffers.CreateRVI(symbol,timeframe,ma_period,id)!=INVALID_HANDLE); } //--- Create the standard Standart Deviation indicator bool BufferCreateStdDev(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, const int ma_shift, const ENUM_MA_METHOD ma_method, const ENUM_APPLIED_PRICE applied_price, const int id) { return(this.m_buffers.CreateStdDev(symbol,timeframe,ma_period,ma_shift,ma_method,applied_price,id)!=INVALID_HANDLE); } //--- Create the standard Stochastic indicator bool BufferCreateStochastic(const string symbol,const ENUM_TIMEFRAMES timeframe, const int Kperiod, const int Dperiod, const int slowing, const ENUM_MA_METHOD ma_method, const ENUM_STO_PRICE price_field, const int id) { return(this.m_buffers.CreateStochastic(symbol,timeframe,Kperiod,Dperiod,slowing,ma_method,price_field,id)!=INVALID_HANDLE); } //--- Create the standard TEMA indicator bool BufferCreateTEMA(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, const int ma_shift, const ENUM_APPLIED_PRICE applied_price, const int id) { return(this.m_buffers.CreateTEMA(symbol,timeframe,ma_period,ma_shift,applied_price,id)!=INVALID_HANDLE); } //--- Create the standard Triple Exponential Average indicator bool BufferCreateTriX(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, const ENUM_APPLIED_PRICE applied_price, const int id) { return(this.m_buffers.CreateTriX(symbol,timeframe,ma_period,applied_price,id)!=INVALID_HANDLE); } //--- Create the standard William's Percent Range indicator bool BufferCreateWPR(const string symbol,const ENUM_TIMEFRAMES timeframe,const int calc_period,const int id) { return(this.m_buffers.CreateWPR(symbol,timeframe,calc_period,id)!=INVALID_HANDLE); } //--- Create the standard VIDYA indicator bool BufferCreateVIDYA(const string symbol,const ENUM_TIMEFRAMES timeframe, const int cmo_period, const int ema_period, const int ma_shift, const ENUM_APPLIED_PRICE applied_price, const int id) { return(this.m_buffers.CreateVIDYA(symbol,timeframe,cmo_period,ema_period,ma_shift,applied_price,id)!=INVALID_HANDLE); } //--- Create the standard Volumes indicator bool BufferCreateVolumes(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_APPLIED_VOLUME applied_volume,const int id) { return(this.m_buffers.CreateVolumes(symbol,timeframe,applied_volume,id)!=INVALID_HANDLE); } //--- Initialize all drawn buffers by a (1) specified value, (2) empty value set for the buffer object
集合类中的方法返回 int 类型,或更确切地说,返回所创建标准指标的句柄。 在此,我将实现返回 bool 类型值。 这可令最终程序操控方法大大简化。 因此,这些方法取缓冲区集合类的相应方法返回的值与 INVALID_HANDLE 进行比较,并将结果返回。
在类的公开部分中,编写两个方法 — 其一为所有已创建标准指标准备计算缓冲区数据的方法,返回上面我曾研究过的同名缓冲区对象集合类方法的结果,其二声明依据标准指标类型及其 ID 返回缓冲区对象描述的方法:
//--- Prepare data of the calculated buffer of all created standard indicators bool BufferPreparingDataAllBuffersStdInd(void) { return this.m_buffers.PreparingDataAllBuffersStdInd(); } //--- Return the standard indicator buffer description by type and ID string BufferGetLabelByTypeID(const ENUM_INDICATOR ind_type,const int id); //--- Display short description of all indicator buffers of the buffer collection void BuffersPrintShort(void);
在类主体之外实现方法:
//+------------------------------------------------------------------+ //| Return the standard indicator buffer description | //| by type and ID | //+------------------------------------------------------------------+ string CEngine::BufferGetLabelByTypeID(const ENUM_INDICATOR ind_type,const int id) { CArrayObj *list=m_buffers.GetListBufferByTypeID(ind_type,id); if(list==NULL || list.Total()==0) return ""; CBuffer *buff=list.At(0); if(buff==NULL) return ""; return buff.Label(); } //+------------------------------------------------------------------+
此处的一切都很简单:获得相应标准指标类型(传递给该方法)及其 ID 的缓冲区对象列表。
因此,列表中将有两个这样的缓冲对象 — 绘制以及计算各一。 由于两个对象都包含相似的数据,因此无需关心从中接收数据的确切对象。 因此,获取指向列表中第一个缓冲区对象的指针,并返回其描述。
在“新柱线”事件处理模块中实现 OnDoEasyEvent() 方法时,我们稍微调整获取复制的数据量:
//--- Handling timeseries events else if(idx>SERIES_EVENTS_NO_EVENT && idx<SERIES_EVENTS_NEXT_CODE) { //--- "New bar" event if(idx==SERIES_EVENTS_NEW_BAR) { ::Print(DFUN,TextByLanguage("Новый бар на ","New Bar on "),sparam," ",TimeframeDescription((ENUM_TIMEFRAMES)dparam),": ",TimeToString(lparam)); CArrayObj *list=this.m_buffers.GetListBuffersWithID(); if(list!=NULL) { int total=list.Total(); for(int i=0;i<total;i++) { CBuffer *buff=list.At(i); if(buff==NULL) continue; string symbol=sparam; ENUM_TIMEFRAMES timeframe=(ENUM_TIMEFRAMES)dparam; if(buff.TypeBuffer()==BUFFER_TYPE_DATA || buff.IndicatorType()==WRONG_VALUE) continue; if(buff.Symbol()==symbol && buff.Timeframe()==timeframe ) { CSeriesDE *series=this.SeriesGetSeries(symbol,timeframe); if(series==NULL) continue; int count=::fmin((int)series.AvailableUsedData(),::fmin(buff.GetDataTotal(),buff.IndicatorBarsCalculated())); this.m_buffers.PreparingDataBufferStdInd(buff.IndicatorType(),buff.ID(),count); } } } } //--- "Bars skipped" event if(idx==SERIES_EVENTS_MISSING_BARS) { ::Print(DFUN,TextByLanguage("Пропущены бары на ","Missed bars on "),sparam," ",TimeframeDescription((ENUM_TIMEFRAMES)dparam),": ",(string)lparam); } } //--- Handling trading events
以前,在高亮显示的代码中,我们取缓冲区对象数据的总量,和已计算标准指标数据两者的最小值:
int count=::fmin(buff.GetDataTotal(),buff.IndicatorBarsCalculated());
然而,由于在函数库中(并由此在程序中)已将所用时间序列数据的数量设置为 1000 根柱线,因此现在我们将从三个数据源取最小值 — 缓冲区对象数据的总数,已计算标准指标数据,和 默认数据量(1000)。 通常,1000 总是小于其他两个数值,这在批量复制数据时更有利。
函数库类的改进至此结束。
测试
为了测试在子窗口中绘制的单缓冲区多品种、多周期标准指标的创建,我将执行以下操作:
依据设置中的选择,创建一个自定义指标:
- 标准指标建立在所用品种之上
- 标准指标建立在图表所用周期(时间帧)之上
- 创建的单缓冲区多品种、多周期标准指标的类型
所选标准指标的数据显示在当前交易品种/时段主图表的子窗口当中。
我们借用上一篇文章的指标,并将其保存在 \MQL5\Indicators\TestDoEasy\Part48\ 中,命名为 TestDoEasyPart48.mq5。
In the indicator inputs, add the parameter allowing us to select a standard indicator to be displayed:
//| TestDoEasyPart48.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 3 #property indicator_plots 1 //--- classes //--- enums //--- defines //--- structures //--- input variables sinput string InpUsedSymbols = "GBPUSD"; // Used symbol (one only) sinput ENUM_TIMEFRAMES InpPeriod = PERIOD_M30; // Used chart period sinput ENUM_INDICATOR InpIndType = IND_AC; // Type standard indicator //--- sinput bool InpUseSounds = true; // Use sounds //--- indicator buffers //--- 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 //+------------------------------------------------------------------+
只有含一个构造和显示缓冲区,并显示在子窗口中的标准指标才能与自定义指标一同操作。 在 OnInit() 应答程序中,在函数库中创建了相应类型的标准指标。 我们为其余指标实现错误消息:
//+------------------------------------------------------------------+ //| 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(); //--- 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 a selected standard indicator bool success=false; switch(InpIndType) { case IND_AC : success=engine.BufferCreateAC(InpUsedSymbols,InpPeriod,1); break; case IND_AD : success=engine.BufferCreateAD(InpUsedSymbols,InpPeriod,VOLUME_TICK,1); break; case IND_AO : success=engine.BufferCreateAO(InpUsedSymbols,InpPeriod,1); break; case IND_ATR : success=engine.BufferCreateATR(InpUsedSymbols,InpPeriod,14,1); break; case IND_BEARS : success=engine.BufferCreateBearsPower(InpUsedSymbols,InpPeriod,13,1); break; case IND_BULLS : success=engine.BufferCreateBullsPower(InpUsedSymbols,InpPeriod,13,1); break; case IND_CHAIKIN : success=engine.BufferCreateChaikin(InpUsedSymbols,InpPeriod,3,10,MODE_EMA,VOLUME_TICK,1); break; case IND_CCI : success=engine.BufferCreateCCI(InpUsedSymbols,InpPeriod,14,PRICE_TYPICAL,1); break; case IND_DEMARKER : success=engine.BufferCreateDeMarker(InpUsedSymbols,InpPeriod,14,1); break; case IND_FORCE : success=engine.BufferCreateForce(InpUsedSymbols,InpPeriod,13,MODE_SMA,VOLUME_TICK,1); break; case IND_MOMENTUM : success=engine.BufferCreateMomentum(InpUsedSymbols,InpPeriod,14,PRICE_CLOSE,1); break; case IND_MFI : success=engine.BufferCreateMFI(InpUsedSymbols,InpPeriod,14,VOLUME_TICK,1); break; case IND_OSMA : success=engine.BufferCreateOsMA(InpUsedSymbols,InpPeriod,12,26,9,PRICE_CLOSE,1); break; case IND_OBV : success=engine.BufferCreateOBV(InpUsedSymbols,InpPeriod,VOLUME_TICK,1); break; case IND_RSI : success=engine.BufferCreateRSI(InpUsedSymbols,InpPeriod,14,PRICE_CLOSE,1); break; case IND_STDDEV : success=engine.BufferCreateStdDev(InpUsedSymbols,InpPeriod,20,0,MODE_SMA,PRICE_CLOSE,1); break; case IND_TRIX : success=engine.BufferCreateTriX(InpUsedSymbols,InpPeriod,14,PRICE_CLOSE,1); break; case IND_WPR : success=engine.BufferCreateWPR(InpUsedSymbols,InpPeriod,14,1); break; case IND_VOLUMES : success=engine.BufferCreateVolumes(InpUsedSymbols,InpPeriod,VOLUME_TICK,1); break; default: break; } if(!success) { Print(TextByLanguage("Ошибка. Индикатор не создан","Error. Indicator not created")); return INIT_FAILED; } //--- Check the number of buffers specified in the 'properties' block
子窗口中绘制的每个标准指标都有其自己的小数显示位数。 此外,其中一些还绘制了水平线。 我们为每个用到的标准指标创建一个单独的集合,其中我们将指定显示数据小数位容量,并定义水平线,前提是该标准指标具有这些指标:
//--- Display short descriptions of created indicator buffers engine.BuffersPrintShort(); //--- Set levels where they are required and define the data decimal capacity int digits=(int)SymbolInfoInteger(InpUsedSymbols,SYMBOL_DIGITS); switch(InpIndType) { case IND_AC : digits+=2; break; case IND_AD : digits=0; break; case IND_AO : digits+=1; break; case IND_ATR : break; case IND_BEARS : digits+=1; break; case IND_BULLS : digits+=1; break; case IND_CHAIKIN : digits=0; break; case IND_CCI : IndicatorSetInteger(INDICATOR_LEVELS,2); IndicatorSetDouble(INDICATOR_LEVELVALUE,0,100); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,-100); digits=2; break; case IND_DEMARKER : IndicatorSetInteger(INDICATOR_LEVELS,2); IndicatorSetDouble(INDICATOR_LEVELVALUE,0,0.7); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,0.3); digits=3; break; case IND_FORCE : digits+=1; break; case IND_MOMENTUM : digits=2; break; case IND_MFI : IndicatorSetInteger(INDICATOR_LEVELS,2); IndicatorSetDouble(INDICATOR_LEVELVALUE,0,80); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,20); break; case IND_OSMA : digits+=2; break; case IND_OBV : digits=0; break; case IND_RSI : IndicatorSetInteger(INDICATOR_LEVELS,3); IndicatorSetDouble(INDICATOR_LEVELVALUE,0,70); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,50); IndicatorSetDouble(INDICATOR_LEVELVALUE,2,30); digits=2; break; case IND_STDDEV : digits+=1; break; case IND_TRIX : break; case IND_WPR : IndicatorSetInteger(INDICATOR_LEVELS,2); IndicatorSetDouble(INDICATOR_LEVELVALUE,0,-80); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,-20); digits=2; break; case IND_VOLUMES : digits=0; break; default: IndicatorSetInteger(INDICATOR_LEVELS,0); break; } //--- Set the short name for the indicator and bit depth string label=engine.BufferGetLabelByTypeID(InpIndType,1); IndicatorSetString(INDICATOR_SHORTNAME,label); IndicatorSetInteger(INDICATOR_DIGITS,digits); //--- Successful return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
与以前测试的指标相比,OnCalculate() 应答程序已大大简化。 这是因为我已经创建了操控标准指标的必要方法,而我们所需要做的就是准备标准指标数据,并在循环里于当前图表上显示标准指标的已计算数据:
//+------------------------------------------------------------------+ //| 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 engine.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(); engine.BufferPreparingDataAllBuffersStdInd(); } //--- Prepare data //--- Fill in calculated buffers of all created standard indicators with data int bars_total=engine.SeriesGetBarsTotal(InpUsedSymbols,InpPeriod); int total_copy=(limit<min_bars ? min_bars : fmin(limit,bars_total)); if(!engine.BufferPreparingDataAllBuffersStdInd()) return 0; //--- Calculate the indicator //--- Main calculation loop of the indicator for(int i=limit; i>WRONG_VALUE && !IsStopped(); i--) { engine.GetBuffersCollection().SetDataBufferStdInd(InpIndType,1,i,time[i]); } //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+
这就是创建测试指标所需的全部工作。
完整的指标代码在下面的文件中提供。
编译所创建的指标,在设定中设置 GBPUSD M5,并在 EURUSD M1 上启动指标:
该示意图未显示所有可能的指标,但显示了主要内容:该指标依据选定的标准指标,并基于不同于当前图表的数据绘制。 在需要的地方绘制水平线。
下一步是什么?
在下一篇文章中,我将开始实现创建标准指标的方法,并在主图表窗口中显示数据,以及含有多个绘制缓冲区。
该函数库当前版本的所有文件,以及测试指标文件均附在下面。 供您测试和下载。
将您的问题、评论和建议留在评论中。
请记住,此处我已经为 MetaTrader 5 开发了 MQL5 测试指标。
附件仅适用于 MetaTrader 5。 当前函数库版本尚未在 MetaTrader 4 里进行测试。
在开发和测试指标缓冲区的功能之后,我将尝试在 MetaTrader 4 中实现一些 MQL5 特性。
返回内容目录
该系列中的先前文章:
DoEasy 函数库中的时间序列(第三十五部分):柱线对象和品种时间序列列表DoEasy 函数库中的时间序列(第三十六部分):所有用到的品种周期的时间序列对象
DoEasy 函数库中的时间序列(第三十七部分):时间序列集合 - 按品种和周期的时间序列数据库
DoEasy 函数库中的时间序列(第三十八部分):时间序列集合 - 实时更新以及从程序访问数据
DoEasy 函数库中的时间序列(第三十九部分):基于函数库的指标 - 准备数据和时间序列事件
DoEasy 函数库中的时间序列(第四十部分):基于函数库的指标 - 实时刷新数据
DoEasy 函数库中的时间序列(第四十一部分):多品种多周期指标样品
DoEasy 函数库中的时间序列(第四十二部分):抽象指标缓冲区对象类
DoEasy 函数库中的时间序列(第四十三部分):指标缓冲区对象类
DoEasy 函数库中的时间序列(第四十四部分):指标缓冲区对象集合类
DoEasy 函数库中的时间序列(第四十五部分):多周期指标缓冲区
DoEasy 函数库中的时间序列(第四十六部分):多周期、多品种指标缓冲区
DoEasy 函数库中的时间序列(第四十七部分):多周期、多品种标准指标