内容
- 概述
- 改进库类
- 测试
- 下一步是什么?
概述
在本文中,我将通过创建利用若干个绘制缓冲区来显示标准指标的功能,来继续在当前品种图表上显示来自任意品种/周期指标数据的主题。 我们已学习了从任意品种/周期图表里获取数据,并将其显示在当前的品种图表上。 并非一切都顺利,但我会逐渐发现并消除所有缺陷。 毕竟,我们正在开始从头学习开发,这并不排除某些问题考虑不周的可能性。 最重要的是要及时发现存在的缺陷,并逐步解决它们,并要有大量的实践观察时间。 在本文章里,我将实现创建多缓冲区标准指标的功能。
在构建函数库的概念上,与单缓冲区指标的主要区别在于,我们需要以某种方式标记绘制和计算缓冲区,以便可在一个通用指标里通过函数库引用它们全部。
在此刻,我们已将标准指标类型写入缓冲区对象和缓冲区标识符的属性之中:
- “Indicator type” 参数表示标准指标,所创建的绘制和计算缓冲对象属于该指标;
- 若有两个相同类型的已创建指标,则 “Indicator identifier” 参数用于指定绘制和计算缓冲区对象属于哪个指标,(例如,不同的均线指标 — 其中一个的 ID 为 1,第二个为 2 ,第三个为 3,依此类推。 它们都拥有相同的 IND_MA 指标类型)
这些参数非常适合于辨别缓冲区对象与单缓冲区标准指标的隶属关系。 在多缓冲区指标中,由于存在多条指标线,故我们还需要定义计算和绘制缓冲区对象所属的标准指标线。 此处我们得出的结论是,必须至少要设置一个参数 - 线型(顶,底,中间,等)。
在这种情况下,我们将能够清晰地区分缓冲区对象与每个已创建指标的隶属关系,并通过曲线类型轻松访问任何已创建指标的必要曲线:
- “Indicator type” 参数表示标准指标,所创建的绘制和计算缓冲对象属于该指标;
- 若有两个相同类型的已创建指标,则 “Indicator identifier” 参数用于指定绘制和计算缓冲区对象属于哪个指标,(例如,不同的均线指标 — 其中一个的 ID 为 1,第二个为 2 ,第三个为 3,依此类推。 它们都拥有相同的 IND_MA 指标类型);
- “Indicator line” 参数指定依据先前两个设置的参数获取特定指标曲线
我们添加另一个参数:“Short indicator name”,它是标准指标所存储名称与显示在品种和周期子窗口中的指标相匹配。 例如,随机振荡的名称如下所示:
图例 1. 标准随机震荡指标
图例 2. 多品种、多周期标准随机振荡指标
改进库类
我们将 \MQL5\Include\DoEasy\Datas.mqh 文件名更改为 \MQL5\Include\DoEasy\Data.mqh(从语言学角度来看更正确),并在函数库里增加新的消息索引常量:
//--- CBuffer MSG_LIB_TEXT_BUFFER_TEXT_INDEX_BASE, // Base data buffer index MSG_LIB_TEXT_BUFFER_TEXT_INDEX_PLOT, // Plotted buffer serial number MSG_LIB_TEXT_BUFFER_TEXT_INDEX_COLOR, // Color buffer index MSG_LIB_TEXT_BUFFER_TEXT_NUM_DATAS, // Number of data buffers MSG_LIB_TEXT_BUFFER_TEXT_INDEX_NEXT_BASE, // Index of the array to be assigned as the next indicator buffer MSG_LIB_TEXT_BUFFER_TEXT_INDEX_NEXT_PLOT, // Index of the next drawn buffer MSG_LIB_TEXT_BUFFER_TEXT_ID, // Indicator buffer ID MSG_LIB_TEXT_BUFFER_TEXT_IND_LINE_MODE, // Indicator line MSG_LIB_TEXT_BUFFER_TEXT_IND_HANDLE, // Handle of an indicator using a buffer MSG_LIB_TEXT_BUFFER_TEXT_IND_TYPE, // Type of an indicator using a buffer MSG_LIB_TEXT_BUFFER_TEXT_TIMEFRAME, // Buffer (timeframe) data period MSG_LIB_TEXT_BUFFER_TEXT_STATUS, // Buffer status MSG_LIB_TEXT_BUFFER_TEXT_TYPE, // Buffer type MSG_LIB_TEXT_BUFFER_TEXT_ACTIVE, // Active MSG_LIB_TEXT_BUFFER_TEXT_ARROW_CODE, // Arrow code MSG_LIB_TEXT_BUFFER_TEXT_ARROW_SHIFT, // The vertical shift of the arrows MSG_LIB_TEXT_BUFFER_TEXT_DRAW_BEGIN, // The number of initial bars that are not drawn and values in DataWindow MSG_LIB_TEXT_BUFFER_TEXT_DRAW_TYPE, // Graphical construction type MSG_LIB_TEXT_BUFFER_TEXT_SHOW_DATA, // Display construction values in DataWindow MSG_LIB_TEXT_BUFFER_TEXT_SHIFT, // Indicator graphical construction shift by time axis in bars MSG_LIB_TEXT_BUFFER_TEXT_LINE_STYLE, // Line style MSG_LIB_TEXT_BUFFER_TEXT_LINE_WIDTH, // Line width MSG_LIB_TEXT_BUFFER_TEXT_ARROW_SIZE, // Arrow size MSG_LIB_TEXT_BUFFER_TEXT_COLOR_NUM, // Number of colors MSG_LIB_TEXT_BUFFER_TEXT_COLOR, // Drawing color MSG_LIB_TEXT_BUFFER_TEXT_EMPTY_VALUE, // Empty value for plotting where nothing will be drawn MSG_LIB_TEXT_BUFFER_TEXT_SYMBOL, // Buffer symbol MSG_LIB_TEXT_BUFFER_TEXT_LABEL, // Name of the graphical indicator series displayed in DataWindow MSG_LIB_TEXT_BUFFER_TEXT_IND_NAME, // Name of an indicator using a buffer MSG_LIB_TEXT_BUFFER_TEXT_IND_NAME_SHORT, // Short name of an indicator using a buffer MSG_LIB_TEXT_BUFFER_TEXT_STATUS_NAME, // Indicator buffer with graphical construction type 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
添加与新加索引对应的文本消息:
{"Индекс базового буфера данных","Index of Base data buffer"}, {"Порядковый номер рисуемого буфера","Plot buffer sequence number"}, {"Индекс буфера цвета","Color buffer index"}, {"Количество буферов данных","Number of data buffers"}, {"Индекс массива для назначения следующим индикаторным буфером","Array index for assignment as the next indicator buffer"}, {"Индекс следующего по счёту рисуемого буфера","Index of the next drawable buffer"}, {"Идентификатор буферов индикатора","Indicator Buffer Id"}, {"Линия индикатора","Indicator line"}, {"Хэндл индикатора, использующего буфер","Indicator handle that uses the buffer"}, {"Тип индикатора, использующего буфер","Indicator type that uses the buffer"}, {"Период данных буфера (таймфрейм)","Buffer data Period (Timeframe)"}, {"Статус буфера","Buffer status"}, {"Тип буфера","Buffer type"}, {"Активен","Active"}, {"Код стрелки","Arrow code"}, {"Смещение стрелок по вертикали","Vertical shift of arrows"}, {"Количество начальных баров без отрисовки и значений в DataWindow","Number of initial bars without drawing and values in DataWindow"}, {"Тип графического построения","Type of graphical construction"}, {"Отображение значений построения в окне DataWindow","Display construction values in DataWindow"}, {"Сдвиг графического построения индикатора по оси времени в барах","Shift of indicator plotting along time axis in bars"}, {"Стиль линии отрисовки","Drawing line style "}, {"Толщина линии отрисовки","Thickness of drawing line"}, {"Размер значка стрелки","Arrow icon size"}, {"Количество цветов","Number of colors"}, {"Цвет отрисовки","Index of buffer containing drawing color"}, {"Пустое значение для построения, для которого нет отрисовки","Empty value for plotting, for which there is no drawing"}, {"Символ буфера","Buffer Symbol"}, {"Имя индикаторной графической серии, отображаемое в окне DataWindow","Name of indicator graphical series to display in DataWindow"}, {"Наименование индикатора, использующего буфер","Name of indicator that uses buffer"}, {"Короткое наименование индикатора, использующего буфер","Short name of indicator that uses buffer"}, {"Индикаторный буфер с типом графического построения","Indicator buffer with graphic plot type"}, {"Неправильно указано количество буферов индикатора (#property indicator_buffers)","Number of indicator buffers incorrect (#property indicator_buffers)"}, {"Достигнуто максимально возможное количество индикаторных буферов","Maximum number of indicator buffers reached"}, {"Нет ни одного объекта-буфера для стандартного индикатора","No buffer object for standard indicator"},
从 \MQL5\Include\DoEasy\ 函数库文件夹中删除旧的 Datas.mqh 文件,因为 Data.mqh 现在可以就能完成其功能。
将使用多缓冲区标准指标的所有必要新数据添加到 \MQL5\Include\DoEasy\Defines.mqh。
首先,在重命名文件的包含字符串里修改名称:
//+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "DataSND.mqh" #include "DataIMG.mqh" #include "Data.mqh" #ifdef __MQL4__ #include "ToMQL4.mqh" #endif //+------------------------------------------------------------------+
将指标曲线类型的新枚举添加到包含指标缓冲区的数据模块当中:
//+------------------------------------------------------------------+ //| Indicator lines | //+------------------------------------------------------------------+ enum ENUM_INDICATOR_LINE_MODE { INDICATOR_LINE_MODE_MAIN, // Main line INDICATOR_LINE_MODE_SIGNAL, // Signal line INDICATOR_LINE_MODE_UPPER, // Upper line INDICATOR_LINE_MODE_MIDDLE, // Middle line INDICATOR_LINE_MODE_LOWER, // Lower line INDICATOR_LINE_MODE_JAWS, // Jaws line INDICATOR_LINE_MODE_TEETH, // Teeth line INDICATOR_LINE_MODE_LIPS, // Lips line INDICATOR_LINE_MODE_DI_PLUS, // +DI line INDICATOR_LINE_MODE_DI_MINUS, // -DI line }; //+------------------------------------------------------------------+
不同的标准指标都有曲线,根据指标开发人员的命名方式,曲线可能有其自己的名称。 在此,我创建了包含常量的枚举,该常量指定指标曲线的名称。 常量值将用于标记已创建的缓冲对象(计算和绘制对象)与某个标准指标线的隶属关系。 因此,当依据常量名称访问缓冲区对象时,我们可以明确获得要处理的所需对象(考虑指标类型,及其 ID)。
稍后,我将为这些常量分配准确的数值,以便减少此处要生成的代码量(因为许多指标完全重复其他同类型指标不同曲线名的计算)。
添加新数值至整数型缓冲区对象属性的枚举当中,并将整数型属性的数量从 23 增加到 24 :
//+------------------------------------------------------------------+ //| Buffer integer properties | //+------------------------------------------------------------------+ enum ENUM_BUFFER_PROP_INTEGER { BUFFER_PROP_INDEX_PLOT = 0, // Plotted buffer serial number BUFFER_PROP_STATUS, // Buffer status (by drawing style) from the ENUM_BUFFER_STATUS enumeration BUFFER_PROP_TYPE, // Buffer type (from the ENUM_BUFFER_TYPE enumeration) BUFFER_PROP_TIMEFRAME, // Buffer period data (timeframe) BUFFER_PROP_ACTIVE, // Buffer usage flag BUFFER_PROP_DRAW_TYPE, // Graphical construction type (from the ENUM_DRAW_TYPE enumeration) BUFFER_PROP_ARROW_CODE, // Arrow code for DRAW_ARROW style BUFFER_PROP_ARROW_SHIFT, // The vertical shift of the arrows for DRAW_ARROW style BUFFER_PROP_LINE_STYLE, // Line style BUFFER_PROP_LINE_WIDTH, // Line width BUFFER_PROP_DRAW_BEGIN, // The number of initial bars that are not drawn and values in DataWindow BUFFER_PROP_SHOW_DATA, // Flag of displaying construction values in DataWindow BUFFER_PROP_SHIFT, // Indicator graphical construction shift by time axis in bars BUFFER_PROP_COLOR_INDEXES, // Number of colors BUFFER_PROP_COLOR, // Drawing color BUFFER_PROP_INDEX_BASE, // Base data buffer index BUFFER_PROP_INDEX_NEXT_BASE, // Index of the array to be assigned as the next indicator buffer BUFFER_PROP_INDEX_NEXT_PLOT, // Index of the next drawn buffer BUFFER_PROP_IND_LINE_MODE, // Indicator line BUFFER_PROP_ID, // ID of multiple buffers of a single indicator BUFFER_PROP_IND_HANDLE, // Handle of an indicator using a buffer BUFFER_PROP_IND_TYPE, // Type of an indicator using a buffer BUFFER_PROP_NUM_DATAS, // Number of data buffers BUFFER_PROP_INDEX_COLOR, // Color buffer index }; #define BUFFER_PROP_INTEGER_TOTAL (24) // Total number of integer bar properties #define BUFFER_PROP_INTEGER_SKIP (2) // Number of buffer properties not used in sorting //+------------------------------------------------------------------+
以相同的方式,添加新的字符串型属性,然后将其个数增加到 4 :
//+------------------------------------------------------------------+ //| Buffer string properties | //+------------------------------------------------------------------+ enum ENUM_BUFFER_PROP_STRING { BUFFER_PROP_SYMBOL = (BUFFER_PROP_INTEGER_TOTAL+BUFFER_PROP_DOUBLE_TOTAL), // Buffer symbol BUFFER_PROP_LABEL, // Name of the graphical indicator series displayed in DataWindow BUFFER_PROP_IND_NAME, // Name of an indicator using a buffer BUFFER_PROP_IND_NAME_SHORT, // Short name of an indicator using a buffer }; #define BUFFER_PROP_STRING_TOTAL (4) // Total number of string buffer properties //+------------------------------------------------------------------+
由于我已向缓冲区对象里添加了两个新属性,因此我还需要添加两个新的条件,以便依据这些属性在其集合列表里搜索和排序缓冲区对象:
//+------------------------------------------------------------------+ //| Possible buffer sorting criteria | //+------------------------------------------------------------------+ #define FIRST_BUFFER_DBL_PROP (BUFFER_PROP_INTEGER_TOTAL-BUFFER_PROP_INTEGER_SKIP) #define FIRST_BUFFER_STR_PROP (BUFFER_PROP_INTEGER_TOTAL-BUFFER_PROP_INTEGER_SKIP+BUFFER_PROP_DOUBLE_TOTAL-BUFFER_PROP_DOUBLE_SKIP) enum ENUM_SORT_BUFFER_MODE { //--- Sort by integer properties SORT_BY_BUFFER_INDEX_PLOT = 0, // Sort by the plotted buffer serial number SORT_BY_BUFFER_STATUS, // Sort by buffer drawing style (status) from the ENUM_BUFFER_STATUS enumeration SORT_BY_BUFFER_TYPE, // Sort by buffer type (from the ENUM_BUFFER_TYPE enumeration) SORT_BY_BUFFER_TIMEFRAME, // Sort by the buffer data period (timeframe) SORT_BY_BUFFER_ACTIVE, // Sort by the buffer usage flag SORT_BY_BUFFER_DRAW_TYPE, // Sort by graphical construction type (from the ENUM_DRAW_TYPE enumeration) SORT_BY_BUFFER_ARROW_CODE, // Sort by the arrow code for DRAW_ARROW style SORT_BY_BUFFER_ARROW_SHIFT, // Sort by the vertical shift of the arrows for DRAW_ARROW style SORT_BY_BUFFER_LINE_STYLE, // Sort by the line style SORT_BY_BUFFER_LINE_WIDTH, // Sort by the line width SORT_BY_BUFFER_DRAW_BEGIN, // Sort by the number of initial bars that are not drawn and values in DataWindow SORT_BY_BUFFER_SHOW_DATA, // Sort by the flag of displaying construction values in DataWindow SORT_BY_BUFFER_SHIFT, // Sort by the indicator graphical construction shift by time axis in bars SORT_BY_BUFFER_COLOR_INDEXES, // Sort by a number of attempts SORT_BY_BUFFER_COLOR, // Sort by the drawing color SORT_BY_BUFFER_INDEX_BASE, // Sort by the basic data buffer index SORT_BY_BUFFER_INDEX_NEXT_BASE, // Sort by the index of the array to be assigned as the next indicator buffer SORT_BY_BUFFER_INDEX_NEXT_PLOT, // Sort by the index of the next drawn buffer SORT_BY_BUFFER_IND_LINE_MODE, // Sort by the indicator line SORT_BY_BUFFER_ID, // Sort by ID of multiple buffers of a single indicator SORT_BY_BUFFER_IND_HANDLE, // Sort by handle of an indicator using a buffer SORT_BY_BUFFER_IND_TYPE, // Sort by type of an indicator using a buffer //--- Sort by real properties SORT_BY_BUFFER_EMPTY_VALUE = FIRST_BUFFER_DBL_PROP, // Sort by the empty value for plotting where nothing will be drawn //--- Sort by string properties SORT_BY_BUFFER_SYMBOL = FIRST_BUFFER_STR_PROP, // Sort by the buffer symbol SORT_BY_BUFFER_LABEL, // Sort by the name of the graphical indicator series displayed in DataWindow SORT_BY_BUFFER_IND_NAME, // Sort by name of an indicator using a buffer SORT_BY_BUFFER_IND_NAME_SHORT, // Sort by a short name of an indicator using a buffer }; //+------------------------------------------------------------------+
在该类的公开部分,在 \MQL5\Include\DoEasy\Objects\Indicators\Buffer.mqh 中编写安装和接收新的缓冲区对象属性的方法:
public: //--- Send description of buffer properties to the journal (full_prop=true - all properties, false - only supported ones) void Print(const bool full_prop=false); //--- Display a short buffer description in the journal (implementation in the descendants) virtual void PrintShort(void) {;} //--- Set (1) the arrow code, (2) vertical shift of arrows, (3) symbol, (4) timeframe, (5) buffer activity flag //--- (6) drawing type, (7) number of initial bars without drawing, (8) flag of displaying construction values in DataWindow, //--- (9) shift of the indicator graphical construction along the time axis, (10) line style, (11) line width, //--- (12) total number of colors, (13) one drawing color, (14) color of drawing in the specified color index, //--- (15) drawing colors from the color array, (16) empty value, (17) name of the graphical series displayed in DataWindow virtual void SetArrowCode(const uchar code) { return; } virtual void SetArrowShift(const int shift) { return; } void SetSymbol(const string symbol) { this.SetProperty(BUFFER_PROP_SYMBOL,symbol); } void SetTimeframe(const ENUM_TIMEFRAMES timeframe) { this.SetProperty(BUFFER_PROP_TIMEFRAME,timeframe); } void SetActive(const bool flag) { this.SetProperty(BUFFER_PROP_ACTIVE,flag); } void SetDrawType(const ENUM_DRAW_TYPE draw_type); void SetDrawBegin(const int value); void SetShowData(const bool flag); void SetShift(const int shift); void SetStyle(const ENUM_LINE_STYLE style); void SetWidth(const int width); void SetColorNumbers(const int number); void SetColor(const color colour); void SetColor(const color colour,const uchar index); void SetColors(const color &array_colors[]); void SetEmptyValue(const double value); virtual void SetLabel(const string label); void SetID(const int id) { this.SetProperty(BUFFER_PROP_ID,id); } void SetIndicatorHandle(const int handle) { this.SetProperty(BUFFER_PROP_IND_HANDLE,handle); } void SetIndicatorType(const ENUM_INDICATOR type) { this.SetProperty(BUFFER_PROP_IND_TYPE,type); } void SetIndicatorName(const string name) { this.SetProperty(BUFFER_PROP_IND_NAME,name); } void SetIndicatorShortName(const string name) { this.SetProperty(BUFFER_PROP_IND_NAME_SHORT,name); } void SetLineMode(const ENUM_INDICATOR_LINE_MODE mode){ this.SetProperty(BUFFER_PROP_IND_LINE_MODE,mode); } //--- Return (1) the serial number of the drawn buffer, (2) bound array index, (3) color buffer index, //--- (4) index of the first free bound array, (5) index of the next drawn buffer, (6) buffer data period, (7) buffer status, //--- (8) buffer type, (9) buffer usage flag, (10) arrow code, (11) arrow shift for DRAW_ARROW style, //--- (12) number of initial bars that are not drawn and values in DataWindow, (13) graphical construction type, //--- (14) flag of displaying construction values in DataWindow, (15) indicator graphical construction shift along the time axis, //--- (16) drawing line style, (17) drawing line width, (18) number of colors, (19) drawing color, (20) number of buffers for construction //--- (21) set empty value, (22) buffer symbol and (23) name of the indicator graphical series displayed in DataWindow //--- (24) buffer ID, (25) indicator handle, (26) standard indicator type, (27) standard indicator name, //--- (28) umber of standard indicator's calculated bars, (29) line type (main, signal, etc) int IndexPlot(void) const { return (int)this.GetProperty(BUFFER_PROP_INDEX_PLOT); } int IndexBase(void) const { return (int)this.GetProperty(BUFFER_PROP_INDEX_BASE); } int IndexColor(void) const { return (int)this.GetProperty(BUFFER_PROP_INDEX_COLOR); } int IndexNextBaseBuffer(void) const { return (int)this.GetProperty(BUFFER_PROP_INDEX_NEXT_BASE); } int IndexNextPlotBuffer(void) const { return (int)this.GetProperty(BUFFER_PROP_INDEX_NEXT_PLOT); } ENUM_TIMEFRAMES Timeframe(void) const { return (ENUM_TIMEFRAMES)this.GetProperty(BUFFER_PROP_TIMEFRAME); } ENUM_BUFFER_STATUS Status(void) const { return (ENUM_BUFFER_STATUS)this.GetProperty(BUFFER_PROP_STATUS); } ENUM_BUFFER_TYPE TypeBuffer(void) const { return (ENUM_BUFFER_TYPE)this.GetProperty(BUFFER_PROP_TYPE); } bool IsActive(void) const { return (bool)this.GetProperty(BUFFER_PROP_ACTIVE); } uchar ArrowCode(void) const { return (uchar)this.GetProperty(BUFFER_PROP_ARROW_CODE); } int ArrowShift(void) const { return (int)this.GetProperty(BUFFER_PROP_ARROW_SHIFT); } int DrawBegin(void) const { return (int)this.GetProperty(BUFFER_PROP_DRAW_BEGIN); } ENUM_DRAW_TYPE DrawType(void) const { return (ENUM_DRAW_TYPE)this.GetProperty(BUFFER_PROP_DRAW_TYPE); } bool IsShowData(void) const { return (bool)this.GetProperty(BUFFER_PROP_SHOW_DATA); } int Shift(void) const { return (int)this.GetProperty(BUFFER_PROP_SHIFT); } ENUM_LINE_STYLE LineStyle(void) const { return (ENUM_LINE_STYLE)this.GetProperty(BUFFER_PROP_LINE_STYLE); } int LineWidth(void) const { return (int)this.GetProperty(BUFFER_PROP_LINE_WIDTH); } int ColorsTotal(void) const { return (int)this.GetProperty(BUFFER_PROP_COLOR_INDEXES); } color Color(void) const { return (color)this.GetProperty(BUFFER_PROP_COLOR); } int BuffersTotal(void) const { return (int)this.GetProperty(BUFFER_PROP_NUM_DATAS); } double EmptyValue(void) const { return this.GetProperty(BUFFER_PROP_EMPTY_VALUE); } string Symbol(void) const { return this.GetProperty(BUFFER_PROP_SYMBOL); } string Label(void) const { return this.GetProperty(BUFFER_PROP_LABEL); } int ID(void) const { return (int)this.GetProperty(BUFFER_PROP_ID); } int IndicatorHandle(void) const { return (int)this.GetProperty(BUFFER_PROP_IND_HANDLE); } ENUM_INDICATOR IndicatorType(void) const { return (ENUM_INDICATOR)this.GetProperty(BUFFER_PROP_IND_TYPE); } string IndicatorName(void) const { return this.GetProperty(BUFFER_PROP_IND_NAME); } string IndicatorShortName(void) const { return this.GetProperty(BUFFER_PROP_IND_NAME_SHORT); } int IndicatorBarsCalculated(void) const { return ::BarsCalculated((int)this.GetProperty(BUFFER_PROP_IND_HANDLE));} ENUM_INDICATOR_LINE_MODE LineMode(void) const { return (ENUM_INDICATOR_LINE_MODE)this.GetProperty(BUFFER_PROP_IND_LINE_MODE);}
在类构造函数中,为新属性设置默认值:
//+------------------------------------------------------------------+ //| 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; this.m_long_prop[BUFFER_PROP_ID] = WRONG_VALUE; this.m_long_prop[BUFFER_PROP_IND_LINE_MODE] = INDICATOR_LINE_MODE_MAIN; this.m_long_prop[BUFFER_PROP_IND_HANDLE] = INVALID_HANDLE; this.m_long_prop[BUFFER_PROP_IND_TYPE] = WRONG_VALUE; 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); this.m_string_prop[this.IndexProp(BUFFER_PROP_IND_NAME)] = NULL; this.m_string_prop[this.IndexProp(BUFFER_PROP_IND_NAME_SHORT)]= 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)); } //+------------------------------------------------------------------+
在返回缓冲区整数型属性的方法定义中实现返回指标线的用途描述:
//+------------------------------------------------------------------+ //| Return description of a buffer's integer property | //+------------------------------------------------------------------+ string CBuffer::GetPropertyDescription(ENUM_BUFFER_PROP_INTEGER property) { return ( property==BUFFER_PROP_INDEX_PLOT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_PLOT)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_STATUS ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetStatusDescription() ) : property==BUFFER_PROP_TYPE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_TYPE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetTypeBufferDescription() ) : property==BUFFER_PROP_TIMEFRAME ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_TIMEFRAME)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetTimeframeDescription() ) : property==BUFFER_PROP_ACTIVE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_ACTIVE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetActiveDescription() ) : property==BUFFER_PROP_DRAW_TYPE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_DRAW_TYPE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetDrawTypeDescription() ) : property==BUFFER_PROP_ARROW_CODE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_ARROW_CODE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_ARROW_SHIFT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_ARROW_SHIFT)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_LINE_STYLE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_LINE_STYLE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetLineStyleDescription() ) : property==BUFFER_PROP_LINE_WIDTH ? (this.Status()==BUFFER_STATUS_ARROW ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_ARROW_SIZE) : CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_LINE_WIDTH))+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_DRAW_BEGIN ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_DRAW_BEGIN)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_SHOW_DATA ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_SHOW_DATA)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetShowDataDescription() ) : property==BUFFER_PROP_SHIFT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_SHIFT)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_COLOR_INDEXES ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_COLOR_NUM)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_INDEX_COLOR ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_COLOR)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_INDEX_BASE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_BASE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_INDEX_NEXT_BASE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_NEXT_BASE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_INDEX_NEXT_PLOT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_NEXT_PLOT)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_ID ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_ID)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_IND_LINE_MODE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_IND_LINE_MODE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::StringSubstr(::EnumToString((ENUM_INDICATOR_LINE_MODE)this.GetProperty(property)),10) ) : property==BUFFER_PROP_IND_HANDLE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_IND_HANDLE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_IND_TYPE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_IND_TYPE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::StringSubstr(::EnumToString((ENUM_INDICATOR)this.GetProperty(property)),4) ) : property==BUFFER_PROP_NUM_DATAS ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_NUM_DATAS)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_COLOR ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_COLOR)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetColorsDescription() ) : "" ); } //+------------------------------------------------------------------+
在返回缓冲区字符串型属性的方法定义中实现返回指标线的简要名称:
//+------------------------------------------------------------------+ //| Return description of a buffer's string property | //+------------------------------------------------------------------+ string CBuffer::GetPropertyDescription(ENUM_BUFFER_PROP_STRING property) { return ( property==BUFFER_PROP_SYMBOL ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_SYMBOL)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.Symbol() ) : property==BUFFER_PROP_LABEL ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_LABEL)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.Label()==NULL || this.Label()=="" ? CMessage::Text(MSG_LIB_PROP_NOT_SET) : "\""+this.Label()+"\"") ) : property==BUFFER_PROP_IND_NAME ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_IND_NAME)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.IndicatorName()==NULL || this.IndicatorName()=="" ? CMessage::Text(MSG_LIB_PROP_NOT_SET) : "\""+this.IndicatorName()+"\"") ) : property==BUFFER_PROP_IND_NAME_SHORT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_IND_NAME_SHORT)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.IndicatorShortName()==NULL || this.IndicatorName()=="" ? CMessage::Text(MSG_LIB_PROP_NOT_SET) : "\""+this.IndicatorShortName()+"\"") ) : "" ); } //+------------------------------------------------------------------+
由于我已在缓冲区对象里添加了两个新属性,因此我应该将它们添加到允许的列表中,因为我们已有了抽象缓冲区基准对象的衍生对象。 这些衍生对象,依次提供了一些虚方法,可返回对象支持的各种属性的标志。 只有将属性添加到允许的属性列表里,才能启用在缓冲区对象集合列表中搜索、选择和排序缓冲区对象的能力。
每种绘制类型都有自己的缓冲区对象,应该对所有这些对象的文件进行改进。 由于所有对象中的所有修改都是相似的,因此,我们以 \MQL5\Include\DoEasy\Objects\Indicators\BufferLine.mqh 中的曲线缓冲区对象为例来研究改进。
所做的修改就是制作了两个虚拟方法,这些方法返回对象的所支持的整数型和字符串型属性的标志:
//+------------------------------------------------------------------+ //| Return 'true' if a buffer supports a passed | //| integer property, otherwise return 'false' | //+------------------------------------------------------------------+ bool CBufferLine::SupportProperty(ENUM_BUFFER_PROP_INTEGER property) { if((property==BUFFER_PROP_ARROW_CODE || property==BUFFER_PROP_ARROW_SHIFT) || ( this.TypeBuffer()==BUFFER_TYPE_CALCULATE && property!=BUFFER_PROP_TYPE && property!=BUFFER_PROP_INDEX_NEXT_BASE && property!=BUFFER_PROP_IND_LINE_MODE && property!=BUFFER_PROP_IND_HANDLE && property!=BUFFER_PROP_IND_TYPE && property!=BUFFER_PROP_ID ) ) return false; return true; } //+------------------------------------------------------------------+ //| Return 'true' if a buffer supports a passed | //| real property, otherwise return 'false' | //+------------------------------------------------------------------+ bool CBufferLine::SupportProperty(ENUM_BUFFER_PROP_DOUBLE property) { if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE) return false; return true; } //+------------------------------------------------------------------+ //| Return 'true' if a buffer supports a passed | //| string property, otherwise return 'false' | //+------------------------------------------------------------------+ bool CBufferLine::SupportProperty(ENUM_BUFFER_PROP_STRING property) { if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE && property!=BUFFER_PROP_IND_NAME_SHORT) return false; return true; } //+------------------------------------------------------------------+
若缓冲区对象是计算缓冲区,那么如果该方法所接收属性未出现在枚举列表中,则该方法返回 false — 该对象不支持此类属性,否则对象支持此类属性,并且该方法返回 true 。
如此修改(或 BufferCalculate.mqh 中的类似修改)在抽象缓冲区对象的衍生类的所有文件里均已实现:BufferArrow.mqh,BufferBars.mqh,BufferCalculate.mqh,BufferCandles.mqh,BufferFilling.mqh,BufferHistogram.mqh,BufferHistogram2.mqh,BufferSection.mqh,BufferZigZag.mqh,和 BufferLine.mqh,这些我都已经研究过。 在附件中找到这些修改。
在上一篇文章里,我创建了众多方法,可用于为多品种、多周期标准指标创建缓冲区对象,这些指标在主图表的子窗口中显示其数据。 在本文中,我将在函数库里补充一些创建标准指标,并在主图表窗口中显示其数据的方法 — 这些方法与已创建的方法没有什么不同。 它们已针对主窗口的所有标准指标而实施。 我将借用移动平均值作为示例来研究这些方法:
//+------------------------------------------------------------------+ //| Create multi-symbol multi-period MA | //+------------------------------------------------------------------+ int CBuffersCollection::CreateMA(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=WRONG_VALUE) { //--- Create the indicator handle and set the default ID int handle=::iMA(symbol,timeframe,ma_period,ma_shift,ma_method,applied_price); int identifier=(id==WRONG_VALUE ? IND_MA : id); color array_colors[1]={clrRed}; 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_MA); buff.SetLineMode(INDICATOR_LINE_MODE_MAIN); buff.SetShowData(true); buff.SetLabel("MA("+symbol+","+TimeframeDescription(timeframe)+": "+(string)ma_period+")"); buff.SetIndicatorName("Moving Average"); 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_MA); buff.SetLineMode(INDICATOR_LINE_MODE_MAIN); buff.SetEmptyValue(EMPTY_VALUE); buff.SetLabel("MA("+symbol+","+TimeframeDescription(timeframe)+": "+(string)ma_period+")"); buff.SetIndicatorName("Moving Average"); } return handle; } //+------------------------------------------------------------------+
在上一篇文章中了解有关方法操作的更多信息。 与之前研究过的类似方法的唯一区别是为缓冲区对象设置新属性 — 单缓冲区指示器的线型设置为 Main。 上一篇文章中研究并实现的在子窗口中创建单缓冲区标准指标的所有方法均已完成了这些附加功能,而且在主窗口中创建单缓冲区标准指标的方法均已添加到缓冲区对象集合类文件 BuffersCollection.mqh 之中 — 这些是 CreateAMA(), CreateDEMA(), CreateFrAMA(), CreateMA(), CreateSAR(), CreateTEMA() 和 CreateVIDYA() 方法。
这些曲线值已经在类构造函数中设置为默认值,但在此,我添加了该属性的无条件设置,以便这些方法与我现在要研究的创建多缓冲区标准指标的方法相对应。
我们拿创建标准平均方向走势指数指标对象的方法为例,介绍创建多缓冲区标准指标的方法:
//+------------------------------------------------------------------+ //| Create multi-symbol multi-period ADX | //+------------------------------------------------------------------+ int CBuffersCollection::CreateADX(const string symbol,const ENUM_TIMEFRAMES timeframe,const int adx_period,const int id=WRONG_VALUE) { //--- Create the indicator handle and set the default ID int handle=::iADX(symbol,timeframe,adx_period); int identifier=(id==WRONG_VALUE ? IND_ADX : 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 ADX line 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_ADX); buff.SetLineMode(INDICATOR_LINE_MODE_MAIN); // This is the main indicator line buff.SetShowData(true); buff.SetIndicatorName("Average Directional Movement Index"); buff.SetIndicatorShortName("ADX("+symbol+","+TimeframeDescription(timeframe)+": "+(string)adx_period+")"); buff.SetLabel(buff.IndicatorShortName()); buff.SetColors(array_colors); //--- Create the line buffer this.CreateLine(); //--- Get the last created (drawn) buffer object and set all the necessary +DI line 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_ADX); buff.SetLineMode(INDICATOR_LINE_MODE_DI_PLUS); // This is a +DI line buff.SetShowData(true); buff.SetIndicatorName("Average Directional Movement Index"); buff.SetIndicatorShortName("ADX("+symbol+","+TimeframeDescription(timeframe)+": "+(string)adx_period+")"); buff.SetLabel("+DI"); array_colors[0]=clrYellowGreen; buff.SetColors(array_colors); buff.SetStyle(STYLE_DOT); //--- Create the line buffer this.CreateLine(); //--- Get the last created (drawn) buffer object and set all the necessary -DI line 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_ADX); buff.SetLineMode(INDICATOR_LINE_MODE_DI_MINUS); // This is a -DI line buff.SetShowData(true); buff.SetIndicatorName("Average Directional Movement Index"); buff.SetIndicatorShortName("ADX("+symbol+","+TimeframeDescription(timeframe)+": "+(string)adx_period+")"); buff.SetLabel("-DI"); array_colors[0]=clrWheat; buff.SetColors(array_colors); buff.SetStyle(STYLE_DOT); //--- Create a calculated ADX line buffer storing standard indicator data this.CreateCalculate(); //--- Get the last created (calculated) buffer object and set all the necessary ADX line 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_ADX); buff.SetLineMode(INDICATOR_LINE_MODE_MAIN); buff.SetEmptyValue(EMPTY_VALUE); buff.SetIndicatorName("Average Directional Movement Index"); buff.SetLabel("ADX("+symbol+","+TimeframeDescription(timeframe)+": "+(string)adx_period+")"); //--- Create a calculated +DI line buffer storing standard indicator data this.CreateCalculate(); //--- Get the last created (calculated) buffer object and set all the necessary +DI line 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_ADX); buff.SetLineMode(INDICATOR_LINE_MODE_DI_PLUS); buff.SetEmptyValue(EMPTY_VALUE); buff.SetIndicatorName("Average Directional Movement Index"); buff.SetLabel("+DI"); //--- Create a calculated -DI line buffer storing standard indicator data this.CreateCalculate(); //--- Get the last created (calculated) buffer object and set all the necessary -DI line 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_ADX); buff.SetLineMode(INDICATOR_LINE_MODE_DI_MINUS); buff.SetEmptyValue(EMPTY_VALUE); buff.SetIndicatorName("Average Directional Movement Index"); buff.SetLabel("-DI"); } return handle; } //+------------------------------------------------------------------+
此处的逻辑与先前创建的方法完全相同。 缓冲区对象的数量取决于每个特定标准指标需绘制的曲线条数。 为标准指标曲线的每个已创建缓冲区对象设置相应的隶属关系,线形数值需从上面添加的 ENUM_INDICATOR_LINE_MODE 枚举中取值。 因此,每个缓冲对象与为其指定的指标曲线相对应。 每条指标线都有两个缓冲对象 — 绘制和计算各一个。 在计算缓冲区中,填充数据来自所创建标准指标的句柄,而绘制缓冲区将计算缓冲区数据显示到主图表。 这就是整个方法的逻辑。
其余方法几乎完全相同,除了指标名称,它们的简称和每条曲线的名称,因为每条标准指标曲线都有其自己的名称,且其说明与该指标如何在数据窗口里显示其曲线名相对应。
以下创建多缓冲区标准指标对象的方法已实现: CreateADX(), CreateADXWilder(), CreateMACD(), CreateRVI(), CreateStochastic(), CreateBands(), CreateEnvelopes() 和 CreateFractals()。 该方法与上述方法相同。 在此赘述毫无意义。 您可以在附件中找到它们。
在上一篇文章里,我已经开始创建准备计算缓冲区数据,清除缓冲区数据,并将值设置为绘制缓冲区的方法。 利用 switch 运算符,依据指标类型指派缓冲区的执行动作。 正如事实证明,仅需少量的改进即可实现如今创建的标准指标对象的处理。
为指定标准指标准备计算缓冲数据的方法:
//+------------------------------------------------------------------+ //| 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_ind=this.GetListBufferByTypeID(std_ind,id); CArrayObj *list0=NULL,*list1=NULL,*list2=NULL; list_ind=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_TYPE,BUFFER_TYPE_CALCULATE,EQUAL); if(list_ind==NULL || list_ind.Total()==0) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_NO_BUFFER_OBJ)); return 0; } CBufferCalculate *buffer=NULL; int copied=WRONG_VALUE; int idx0=0,idx1=1,idx2=2; switch((int)std_ind) { //--- Single-buffer standard indicators case IND_AC : case IND_AD : case IND_AMA : case IND_AO : case IND_ATR : case IND_BEARS : case IND_BULLS : case IND_BWMFI : case IND_CCI : case IND_CHAIKIN : case IND_DEMA : case IND_DEMARKER : case IND_FORCE : case IND_FRAMA : case IND_MA : case IND_MFI : case IND_MOMENTUM : case IND_OBV : case IND_OSMA : case IND_RSI : case IND_SAR : case IND_STDDEV : case IND_TEMA : case IND_TRIX : case IND_VIDYA : case IND_VOLUMES : case IND_WPR : buffer=list_ind.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),0,0,total_copy); return copied; //--- Multi-buffer standard indicators case IND_ALLIGATOR : case IND_GATOR : idx0=0; idx1=1; list0=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_JAWS,EQUAL); buffer=list0.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx0,0,total_copy); if(copied<total_copy) return 0; list1=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_TEETH,EQUAL); buffer=list1.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx1,0,total_copy); if(copied<total_copy) return 0; list2=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LIPS,EQUAL); buffer=list2.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx2,0,total_copy); if(copied<total_copy) return 0; return copied; case IND_BANDS : idx0=1; idx1=0; list0=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_UPPER,EQUAL); buffer=list0.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx0,0,total_copy); if(copied<total_copy) return 0; list1=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LOWER,EQUAL); buffer=list1.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx1,0,total_copy); if(copied<total_copy) return 0; list2=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MIDDLE,EQUAL); buffer=list2.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx2,0,total_copy); if(copied<total_copy) return 0; return copied; case IND_ENVELOPES : case IND_FRACTALS : idx0=0; idx1=1; list0=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_UPPER,EQUAL); buffer=list0.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx0,0,total_copy); if(copied<total_copy) return 0; list1=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LOWER,EQUAL); buffer=list1.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx1,0,total_copy); if(copied<total_copy) return 0; return copied; //--- Multi-buffer standard indicators in a subwindow case IND_ADX : case IND_ADXW : idx0=0; idx1=1; list0=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MAIN,EQUAL); buffer=list0.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx0,0,total_copy); if(copied<total_copy) return 0; list1=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_DI_PLUS,EQUAL); buffer=list1.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx1,0,total_copy); if(copied<total_copy) return 0; list2=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_DI_MINUS,EQUAL); buffer=list2.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx2,0,total_copy); if(copied<total_copy) return 0; return copied; case IND_MACD : case IND_RVI : case IND_STOCHASTIC : idx0=0; idx1=1; list0=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MAIN,EQUAL); buffer=list0.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx0,0,total_copy); if(copied<total_copy) return 0; list1=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SIGNAL,EQUAL); buffer=list1.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx1,0,total_copy); if(copied<total_copy) return 0; return copied; case IND_ICHIMOKU : break; default: break; } return 0; } //+------------------------------------------------------------------+
如您所见,把处理缓冲区对象所需执行的动作放置在单独的处理模块里。 每个模块都针对自己的指标拥有类似的处理方式。 标准指标的各种类型仅有一个动作,但是与其他类型的标准指标不同,它们被发送到单独的处理模块。 对于布林带(Bollinger Bands)指标的情况,我必须交换其缓冲区的索引,因为出于某种原因,其缓冲区索引(上边界,中位线和下边界)与数据窗口中的索引不同(上边界)、下边界、中位线。 因此,我不得不实现另外两个变量 idx0 和 idx1,它们含有每种类型的标准指标线的真实索引。 对于所有其他类型的指标,它们的曲线索引连续出现:0、1 和 2,而在布林带的情况下,前两个索引相反:1、0 和 2。
在上一篇文章中,我已经研究了其余的方法逻辑。 在此,我仅针对新创建的标准指标对象添加处理模块。
依据时间序列索引清除指定标准指标的缓冲区数据的方法:
//+------------------------------------------------------------------+ //| 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_ind=this.GetListBufferByTypeID(std_ind,id); CArrayObj *list0=NULL,*list1=NULL,*list2=NULL; if(list_ind==NULL || list_ind.Total()==0) return; list_ind=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_TYPE,BUFFER_TYPE_DATA,EQUAL); if(list_ind.Total()==0) return; CBuffer *buffer=NULL; switch((int)std_ind) { //--- Single-buffer standard indicators case IND_AC : case IND_AD : case IND_AMA : case IND_AO : case IND_ATR : case IND_BEARS : case IND_BULLS : case IND_BWMFI : case IND_CCI : case IND_CHAIKIN : case IND_DEMA : case IND_DEMARKER : case IND_FORCE : case IND_FRAMA : case IND_MA : case IND_MFI : case IND_MOMENTUM : case IND_OBV : case IND_OSMA : case IND_RSI : case IND_SAR : case IND_STDDEV : case IND_TEMA : case IND_TRIX : case IND_VIDYA : case IND_VOLUMES : case IND_WPR : buffer=list_ind.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); break; //--- Multi-buffer standard indicators case IND_ALLIGATOR: case IND_GATOR : list0=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_JAWS,EQUAL); buffer=list0.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); list1=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_TEETH,EQUAL); buffer=list1.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); list2=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LIPS,EQUAL); buffer=list2.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); break; case IND_ADX : case IND_ADXW : list0=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MAIN,EQUAL); buffer=list0.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); list1=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_DI_PLUS,EQUAL); buffer=list1.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); list2=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_DI_MINUS,EQUAL); buffer=list2.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); break; case IND_BANDS : list0=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_UPPER,EQUAL); buffer=list0.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); list1=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LOWER,EQUAL); buffer=list1.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); list2=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MIDDLE,EQUAL); buffer=list2.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); break; case IND_ENVELOPES : case IND_FRACTALS : list0=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_UPPER,EQUAL); buffer=list0.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); list1=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LOWER,EQUAL); buffer=list1.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); break; case IND_MACD : case IND_RVI : case IND_STOCHASTIC : list0=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MAIN,EQUAL); buffer=list0.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); list1=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SIGNAL,EQUAL); buffer=list1.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); break; case IND_ICHIMOKU : break; default: break; } } //+------------------------------------------------------------------+
就像以前的方法一样,在此,我已针对不同标准指标对象,将类似的处理动作分组在一起。
如果我们仔细观察,可以发现位于不同分组中的许多指标拥有相同的处理逻辑,而整体差异仅在于它们的曲线常量的名称。 我在文章开头已提到了这一点。 为剩下的三个标准指标创建对象之后,它们的曲线最初显示在图表上,并带有平移(短吻鳄,鳄鱼和 Ishimoku),我将为这些目的相同、但常量不同,而曲线索引的设置值相似的方法进行优化。 这将减少方法代码。
依据时间序列索引为当前图表的指定标准指标缓冲区设置数值的方法:
//+------------------------------------------------------------------+ //| Set values for the current chart to the buffers of the specified | //| 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_data0=NULL; CBuffer *buffer_data1=NULL; CBuffer *buffer_data2=NULL; CBuffer *buffer_calc0=NULL; CBuffer *buffer_calc1=NULL; CBuffer *buffer_calc2=NULL; int index_period=0; int series_index_start=0; int num_bars=1,index=0; uchar clr=color_index; long vol0=0,vol1=0; datetime time_period=0; double value00=EMPTY_VALUE, value01=EMPTY_VALUE; double value10=EMPTY_VALUE, value11=EMPTY_VALUE; double value20=EMPTY_VALUE, value21=EMPTY_VALUE; //--- Depending on the standard indicator type switch((int)ind_type) { //--- Single-buffer standard indicators case IND_AC : case IND_AD : case IND_AMA : case IND_AO : case IND_ATR : case IND_BEARS : case IND_BULLS : case IND_BWMFI : case IND_CCI : case IND_CHAIKIN : case IND_DEMA : case IND_DEMARKER : case IND_FORCE : case IND_FRAMA : case IND_MA : case IND_MFI : case IND_MOMENTUM : case IND_OBV : case IND_OSMA : case IND_RSI : case IND_SAR : case IND_STDDEV : case IND_TEMA : case IND_TRIX : case IND_VIDYA : case IND_VOLUMES : case IND_WPR : //--- Get drawn and calculated buffer objects buffer_data0=list_data.At(0); buffer_calc0=list_calc.At(0); if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0) return false; //--- Find the bar index corresponding to the current bar start time index_period=::iBarShift(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),series_time,true); if(index_period==WRONG_VALUE || index_period>buffer_calc0.GetDataTotal()-1) return false; //--- Get the value by the index from the indicator buffer value00=buffer_calc0.GetDataBufferValue(0,index_period); if(buffer_calc0.Symbol()==::Symbol() && buffer_calc0.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_calc0.Symbol(),buffer_calc0.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_calc0.Timeframe())/::PeriodSeconds(PERIOD_CURRENT); if(num_bars==0) num_bars=1; } //--- Take values to calculate colors value01=(series_index_start+num_bars>buffer_data0.GetDataTotal()-1 ? value00 : buffer_data0.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 value00 and value01 values ratio for(int i=0;i<num_bars;i++) { index=series_index_start-i; buffer_data0.SetBufferValue(0,index,value00); if(ind_type!=IND_BWMFI) clr=(color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index); else { vol0=::iVolume(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),index_period); vol1=::iVolume(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),index_period+1); clr= ( value00>value01 && vol0>vol1 ? 0 : value00<value01 && vol0<vol1 ? 1 : value00>value01 && vol0<vol1 ? 2 : value00<value01 && vol0>vol1 ? 3 : 4 ); } buffer_data0.SetBufferColorIndex(index,clr); } return true; //--- Multi-buffer standard indicators case IND_ADX : case IND_ADXW : //--- Get drawn and calculated buffer objects list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MAIN,EQUAL); buffer_data0=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_DI_PLUS,EQUAL); buffer_data1=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_DI_MINUS,EQUAL); buffer_data2=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MAIN,EQUAL); buffer_calc0=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_DI_PLUS,EQUAL); buffer_calc1=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_DI_MINUS,EQUAL); buffer_calc2=list.At(0); if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0) return false; if(buffer_calc1==NULL || buffer_data1==NULL || buffer_calc1.GetDataTotal(0)==0) return false; if(buffer_calc2==NULL || buffer_data2==NULL || buffer_calc2.GetDataTotal(0)==0) return false; //--- Find the bar index corresponding to the current bar start time index_period=::iBarShift(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),series_time,true); if(index_period==WRONG_VALUE || index_period>buffer_calc0.GetDataTotal()-1) return false; //--- Get the value by the index from the indicator buffer value00=buffer_calc0.GetDataBufferValue(0,index_period); value10=buffer_calc1.GetDataBufferValue(0,index_period); value20=buffer_calc2.GetDataBufferValue(0,index_period); if(buffer_calc0.Symbol()==::Symbol() && buffer_calc0.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_calc0.Symbol(),buffer_calc0.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_calc0.Timeframe())/::PeriodSeconds(PERIOD_CURRENT); if(num_bars==0) num_bars=1; } //--- Take values to calculate colors value01=(series_index_start+num_bars>buffer_data0.GetDataTotal()-1 ? value00 : buffer_data0.GetDataBufferValue(0,series_index_start+num_bars)); value11=(series_index_start+num_bars>buffer_data1.GetDataTotal()-1 ? value10 : buffer_data1.GetDataBufferValue(1,series_index_start+num_bars)); value21=(series_index_start+num_bars>buffer_data2.GetDataTotal()-1 ? value20 : buffer_data2.GetDataBufferValue(2,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 value00 and value01 values ratio for(int i=0;i<num_bars;i++) { index=series_index_start-i; buffer_data0.SetBufferValue(0,index,value00); buffer_data1.SetBufferValue(1,index,value10); buffer_data2.SetBufferValue(2,index,value20); buffer_data0.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index); buffer_data1.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value10>value11 ? 0 : value10<value11 ? 1 : 2) : color_index); buffer_data2.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value20>value21 ? 0 : value20<value21 ? 1 : 2) : color_index); } return true; case IND_BANDS : //--- Get drawn and calculated buffer objects list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_UPPER,EQUAL); buffer_data0=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LOWER,EQUAL); buffer_data1=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MIDDLE,EQUAL); buffer_data2=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_UPPER,EQUAL); buffer_calc0=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LOWER,EQUAL); buffer_calc1=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MIDDLE,EQUAL); buffer_calc2=list.At(0); if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0) return false; if(buffer_calc1==NULL || buffer_data1==NULL || buffer_calc1.GetDataTotal(0)==0) return false; if(buffer_calc2==NULL || buffer_data2==NULL || buffer_calc2.GetDataTotal(0)==0) return false; //--- Find the bar index corresponding to the current bar start time index_period=::iBarShift(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),series_time,true); if(index_period==WRONG_VALUE || index_period>buffer_calc0.GetDataTotal()-1) return false; //--- Get the value by the index from the indicator buffer value00=buffer_calc0.GetDataBufferValue(0,index_period); value10=buffer_calc1.GetDataBufferValue(0,index_period); value20=buffer_calc2.GetDataBufferValue(0,index_period); if(buffer_calc0.Symbol()==::Symbol() && buffer_calc0.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_calc0.Symbol(),buffer_calc0.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_calc0.Timeframe())/::PeriodSeconds(PERIOD_CURRENT); if(num_bars==0) num_bars=1; } //--- Take values to calculate colors value01=(series_index_start+num_bars>buffer_data0.GetDataTotal()-1 ? value00 : buffer_data0.GetDataBufferValue(0,series_index_start+num_bars)); value11=(series_index_start+num_bars>buffer_data1.GetDataTotal()-1 ? value10 : buffer_data1.GetDataBufferValue(1,series_index_start+num_bars)); value21=(series_index_start+num_bars>buffer_data2.GetDataTotal()-1 ? value20 : buffer_data2.GetDataBufferValue(2,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 value00 and value01 values ratio for(int i=0;i<num_bars;i++) { index=series_index_start-i; buffer_data0.SetBufferValue(0,index,value00); buffer_data1.SetBufferValue(1,index,value10); buffer_data2.SetBufferValue(2,index,value20); buffer_data0.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index); buffer_data1.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value10>value11 ? 0 : value10<value11 ? 1 : 2) : color_index); buffer_data2.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value20>value21 ? 0 : value20<value21 ? 1 : 2) : color_index); } return true; case IND_ENVELOPES : case IND_FRACTALS : //--- Get drawn and calculated buffer objects list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_UPPER,EQUAL); buffer_data0=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LOWER,EQUAL); buffer_data1=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_UPPER,EQUAL); buffer_calc0=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LOWER,EQUAL); buffer_calc1=list.At(0); if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0) return false; if(buffer_calc1==NULL || buffer_data1==NULL || buffer_calc1.GetDataTotal(0)==0) return false; //--- Find the bar index corresponding to the current bar start time index_period=::iBarShift(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),series_time,true); if(index_period==WRONG_VALUE || index_period>buffer_calc0.GetDataTotal()-1) return false; //--- Get the value by the index from the indicator buffer value00=buffer_calc0.GetDataBufferValue(0,index_period); value10=buffer_calc1.GetDataBufferValue(0,index_period); if(buffer_calc0.Symbol()==::Symbol() && buffer_calc0.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_calc0.Symbol(),buffer_calc0.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_calc0.Timeframe())/::PeriodSeconds(PERIOD_CURRENT); if(num_bars==0) num_bars=1; } //--- Take values to calculate colors value01=(series_index_start+num_bars>buffer_data0.GetDataTotal()-1 ? value00 : buffer_data0.GetDataBufferValue(0,series_index_start+num_bars)); value11=(series_index_start+num_bars>buffer_data1.GetDataTotal()-1 ? value10 : buffer_data1.GetDataBufferValue(1,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 value00 and value01 values ratio for(int i=0;i<num_bars;i++) { index=series_index_start-i; buffer_data0.SetBufferValue(0,index,value00); buffer_data1.SetBufferValue(1,index,value10); buffer_data0.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index); buffer_data1.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value10>value11 ? 0 : value10<value11 ? 1 : 2) : color_index); } return true; case IND_MACD : case IND_RVI : case IND_STOCHASTIC : //--- Get drawn and calculated buffer objects list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MAIN,EQUAL); buffer_data0=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SIGNAL,EQUAL); buffer_data1=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MAIN,EQUAL); buffer_calc0=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SIGNAL,EQUAL); buffer_calc1=list.At(0); if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0) return false; if(buffer_calc1==NULL || buffer_data1==NULL || buffer_calc1.GetDataTotal(0)==0) return false; //--- Find the bar index corresponding to the current bar start time index_period=::iBarShift(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),series_time,true); if(index_period==WRONG_VALUE || index_period>buffer_calc0.GetDataTotal()-1) return false; //--- Get the value by the index from the indicator buffer value00=buffer_calc0.GetDataBufferValue(0,index_period); value10=buffer_calc1.GetDataBufferValue(0,index_period); if(buffer_calc0.Symbol()==::Symbol() && buffer_calc0.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_calc0.Symbol(),buffer_calc0.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_calc0.Timeframe())/::PeriodSeconds(PERIOD_CURRENT); if(num_bars==0) num_bars=1; } //--- Take values to calculate colors value01=(series_index_start+num_bars>buffer_data0.GetDataTotal()-1 ? value00 : buffer_data0.GetDataBufferValue(0,series_index_start+num_bars)); value11=(series_index_start+num_bars>buffer_data1.GetDataTotal()-1 ? value10 : buffer_data1.GetDataBufferValue(1,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 value00 and value01 values ratio for(int i=0;i<num_bars;i++) { index=series_index_start-i; buffer_data0.SetBufferValue(0,index,value00); buffer_data1.SetBufferValue(1,index,value10); buffer_data0.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index); buffer_data1.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value10>value11 ? 0 : value10<value11 ? 1 : 2) : color_index); } return true; case IND_ALLIGATOR: break; case IND_GATOR : break; case IND_ICHIMOKU : break; default: break; } return false; } //+------------------------------------------------------------------+
该方法的构造与上述两种方法相同。 不同的标准指标若处理动作相同,也于此分组。 之后,为相同类型的指示线设置常数值的方法,也要进行优化。
按类型和 ID 返回标准指标缓冲区描述的方法,先前已在函数库 CEngine 主类文件中实现。 我们将其实现转移到缓冲区集合类。 为此,在类主体的最末端,于其他方法之后声明该方法,返回指标简称:
//--- Return the standard indicator buffer description by type and ID string GetLabelByTypeID(const ENUM_INDICATOR ind_type,const int id,const ENUM_INDICATOR_LINE_MODE line_mode=INDICATOR_LINE_MODE_MAIN); //--- Return the standard indicator short name by type and ID string GetIndicatorShortNameByTypeID(const ENUM_INDICATOR ind_type,const int id); //--- Constructor CBuffersCollection(); //--- Get pointers to the timeseries collection (the method is called in the CollectionOnInit() method of the CEngine object) void OnInit(CTimeSeriesCollection *timeseries) { this.m_timeseries=timeseries; } }; //+------------------------------------------------------------------+
我们在类主体之外编写它们的实现:
//+------------------------------------------------------------------+ //| Return the standard indicator buffer description | //| by type and ID | //+------------------------------------------------------------------+ string CBuffersCollection::GetLabelByTypeID(const ENUM_INDICATOR ind_type,const int id,const ENUM_INDICATOR_LINE_MODE line_mode=INDICATOR_LINE_MODE_MAIN) { CArrayObj *list=this.GetListBufferByTypeID(ind_type,id); list=CSelect::ByBufferProperty(list,BUFFER_PROP_IND_LINE_MODE,line_mode,EQUAL); if(list==NULL || list.Total()==0) return ""; CBuffer *buff=list.At(0); if(buff==NULL) return ""; return buff.Label(); } //+------------------------------------------------------------------+ //| Return the standard indicator short name | //| by type and ID | //+------------------------------------------------------------------+ string CBuffersCollection::GetIndicatorShortNameByTypeID(const ENUM_INDICATOR ind_type,const int id) { CArrayObj *list=this.GetListBufferByTypeID(ind_type,id); if(list==NULL || list.Total()==0) return ""; CBuffer *buff=list.At(0); if(buff==NULL) return ""; return buff.IndicatorShortName(); } //+------------------------------------------------------------------+
我已多次研究过这种方法的逻辑,并且我相信于此一切都很清晰明了。 如果您有任何疑问,请随时在评论中提问。
现在,我们需要在函数库 CEngine 主类中实现所有已创建方法的访问。 鉴于我已在上一篇文章中实现了创建标准指标对象的所有方法,因此现在要做的就是修改调用返回指标曲线及其简称的方法:
//--- 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,const ENUM_INDICATOR_LINE_MODE line_mode) { return this.m_buffers.GetLabelByTypeID(ind_type,id,line_mode); } //--- Return the standard indicator short name by type and ID string BufferGetIndicatorShortNameByTypeID(const ENUM_INDICATOR ind_type,const int id) { return this.m_buffers.GetIndicatorShortNameByTypeID(ind_type,id); } //--- Display short description of all indicator buffers of the buffer collection
这些方法返回调用缓冲区集合类的同名方法的结果。
现在,我只需要删除方法的实现,我已从 CEngine 类移到了缓冲区集合类:
//+------------------------------------------------------------------+
//| 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();
}
//+------------------------------------------------------------------+
当前,这些就是创建多缓冲区标准指标对象的所有修改。
测试
为了进行测试,我们借用上一篇文章中的指标,并利用它创建两个新的指标 — 第一个是在子窗口中显示多品种、多周期的标准指标,而第二个是一个在品种图表的主窗口中执行相同的操作。
与上一篇文章中已研究的测试指标相比,指标的逻辑没有任何改变。 我只是在 OnInit() 处理程序中添加l了调用创建必要指标的方法。
把上一篇文章中的指标保存在 \MQL5\Indicators\TestDoEasy\Part49\ 当中,命名为 TestDoEasyPart49_1.mq5。
该指标在当前品种图标的子窗口中创建并显示标准指标。 其 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) { //--- Single-buffer standard indicators in a subwindow 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_BWMFI : success=engine.BufferCreateBWMFI(InpUsedSymbols,InpPeriod,VOLUME_TICK,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; //--- Multi-buffer standard indicators in a subwindow case IND_ADX : success=engine.BufferCreateADX(InpUsedSymbols,InpPeriod,14,1); break; case IND_ADXW : success=engine.BufferCreateADXWilder(InpUsedSymbols,InpPeriod,14,1); break; case IND_MACD : success=engine.BufferCreateMACD(InpUsedSymbols,InpPeriod,12,26,9,PRICE_CLOSE,1); break; case IND_RVI : success=engine.BufferCreateRVI(InpUsedSymbols,InpPeriod,10,1); break; case IND_STOCHASTIC : success=engine.BufferCreateStochastic(InpUsedSymbols,InpPeriod,5,3,3,MODE_SMA,STO_LOWHIGH,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 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 //--- (commented out since the colors have already been set in the methods of creating default standard indicators) //--- (we can always set necessary colors either for all indicators, like here, or for each of them individually) //color array_colors[]={clrGreen,clrRed,clrGray}; //engine.BuffersSetColors(array_colors); //--- 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_AD : case IND_CHAIKIN : case IND_OBV : case IND_VOLUMES : digits=0; break; case IND_AO : case IND_BEARS : case IND_BULLS : case IND_FORCE : case IND_STDDEV : case IND_AMA : case IND_DEMA : case IND_FRAMA : case IND_MA : case IND_TEMA : case IND_VIDYA : case IND_BANDS : case IND_ENVELOPES : case IND_MACD : digits+=1; break; case IND_AC : case IND_OSMA : digits+=2; break; case IND_MOMENTUM : digits=2; 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_MFI : IndicatorSetInteger(INDICATOR_LEVELS,2); IndicatorSetDouble(INDICATOR_LEVELVALUE,0,80); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,20); 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_STOCHASTIC : IndicatorSetInteger(INDICATOR_LEVELS,2); IndicatorSetDouble(INDICATOR_LEVELVALUE,0,80); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,20); digits=2; break; case IND_WPR : IndicatorSetInteger(INDICATOR_LEVELS,2); IndicatorSetDouble(INDICATOR_LEVELVALUE,0,-80); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,-20); digits=2; break; case IND_ATR : break; case IND_SAR : break; case IND_TRIX : break; default: IndicatorSetInteger(INDICATOR_LEVELS,0); break; } //--- Set the short name for the indicator and bit depth string label=engine.BufferGetIndicatorShortNameByTypeID(InpIndType,1); IndicatorSetString(INDICATOR_SHORTNAME,label); IndicatorSetInteger(INDICATOR_DIGITS,digits); //--- CArrayObj *list=engine.GetListBuffers(); int total=list.Total(); for(int i=0;i<total;i++) { CBuffer *buff=list.At(i); if(buff==NULL) continue; buff.Print(); } //return INIT_FAILED; //--- //--- Successful return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
完整的指标代码在下面的文件中提供。
在指标设置中设置 EURUSD 和 4 小时,编译指标,并在 EURUSD H1 上启动它。 因此,在 H1 图表上,显示来自设置中所选的 H4 标准指标数据:
现在,我们来创建在指标图表主窗口中显示标准指标的指标。
将来自上一篇文章中的指标保存到 \MQL5\Indicators\TestDoEasy\Part49\ 当中,命名为 TestDoEasyPart49_2.mq5。
其 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) { //--- Single-buffer standard indicators in the main window case IND_AMA : success=engine.BufferCreateAMA(InpUsedSymbols,InpPeriod,9,2,30,0,PRICE_CLOSE,1); break; case IND_DEMA : success=engine.BufferCreateDEMA(InpUsedSymbols,InpPeriod,14,0,PRICE_CLOSE,1); break; case IND_FRAMA : success=engine.BufferCreateFrAMA(InpUsedSymbols,InpPeriod,14,0,PRICE_CLOSE,1); break; case IND_MA : success=engine.BufferCreateMA(InpUsedSymbols,InpPeriod,10,0,MODE_SMA,PRICE_CLOSE,1); break; case IND_SAR : success=engine.BufferCreateSAR(InpUsedSymbols,InpPeriod,0.02,0.2,1); break; case IND_TEMA : success=engine.BufferCreateTEMA(InpUsedSymbols,InpPeriod,14,0,PRICE_CLOSE,1); break; case IND_VIDYA : success=engine.BufferCreateVIDYA(InpUsedSymbols,InpPeriod,9,12,0,PRICE_CLOSE,1); break; //--- Multi-buffer standard indicators in a subwindow case IND_BANDS : success=engine.BufferCreateBands(InpUsedSymbols,InpPeriod,20,0,2.0,PRICE_CLOSE,1); break; case IND_ENVELOPES : success=engine.BufferCreateEnvelopes(InpUsedSymbols,InpPeriod,14,0,MODE_SMA,PRICE_CLOSE,0.1,1); break; case IND_FRACTALS : success=engine.BufferCreateFractals(InpUsedSymbols,InpPeriod,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 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 //--- (commented out since the colors have already been set in the methods of creating default standard indicators) //--- (we can always set necessary colors either for all indicators, like here, or for each of them individually) //color array_colors[]={clrGreen,clrRed,clrGray}; //engine.BuffersSetColors(array_colors); //--- 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_AD : case IND_CHAIKIN : case IND_OBV : case IND_VOLUMES : digits=0; break; case IND_AO : case IND_BEARS : case IND_BULLS : case IND_FORCE : case IND_STDDEV : case IND_AMA : case IND_DEMA : case IND_FRAMA : case IND_MA : case IND_TEMA : case IND_VIDYA : case IND_BANDS : case IND_ENVELOPES : case IND_MACD : digits+=1; break; case IND_AC : case IND_OSMA : digits+=2; break; case IND_MOMENTUM : digits=2; 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_MFI : IndicatorSetInteger(INDICATOR_LEVELS,2); IndicatorSetDouble(INDICATOR_LEVELVALUE,0,80); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,20); 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_STOCHASTIC : IndicatorSetInteger(INDICATOR_LEVELS,2); IndicatorSetDouble(INDICATOR_LEVELVALUE,0,80); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,20); digits=2; break; case IND_WPR : IndicatorSetInteger(INDICATOR_LEVELS,2); IndicatorSetDouble(INDICATOR_LEVELVALUE,0,-80); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,-20); digits=2; break; case IND_ATR : break; case IND_SAR : break; case IND_TRIX : break; default: IndicatorSetInteger(INDICATOR_LEVELS,0); break; } //--- Set the short name for the indicator and bit depth string label=engine.BufferGetIndicatorShortNameByTypeID(InpIndType,1); IndicatorSetString(INDICATOR_SHORTNAME,label); IndicatorSetInteger(INDICATOR_DIGITS,digits); //--- CArrayObj *list=engine.GetListBuffers(); int total=list.Total(); for(int i=0;i<total;i++) { CBuffer *buff=list.At(i); if(buff==NULL) continue; buff.Print(); } //return INIT_FAILED; //--- //--- Successful return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
完整的指标代码在下面的文件中提供。
在指标设置中设置 EURUSD 和 4 小时,编译指标,并在 EURUSD H1 上启动它。 因此,在 H1 图表上,显示来自设置中所选的 H4 标准指标数据:
下一步是什么?
在下一篇文章中,我将继续改进创建多品种、多周期标准指标的库类,优化方法,并剔除基于函数库主指标程序的非必要代码。
该函数库当前版本的所有文件,以及测试指标文件均附在下面。 供您测试和下载。
将您的问题、评论和建议留在评论中。
请记住,此处我已经为 MetaTrader 5 开发了 MQL5 测试指标。
附件仅适用于 MetaTrader 5。 当前函数库版本尚未在 MetaTrader 4 里进行测试。
在开发和测试指标缓冲区的功能之后,我将尝试在 MetaTrader 4 中实现一些 MQL5 特性。
返回内容目录
该系列中的先前文章:
DoEasy 函数库中的时间序列(第三十五部分):柱线对象和品种时间序列列表DoEasy 函数库中的时间序列(第三十六部分):所有用到的品种周期的时间序列对象
DoEasy 函数库中的时间序列(第三十七部分):时间序列集合 - 按品种和周期的时间序列数据库
DoEasy 函数库中的时间序列(第三十八部分):时间序列集合 - 实时更新以及从程序访问数据
DoEasy 函数库中的时间序列(第三十九部分):基于函数库的指标 - 准备数据和时间序列事件
DoEasy 函数库中的时间序列(第四十部分):基于函数库的指标 - 实时刷新数据
DoEasy 函数库中的时间序列(第四十一部分):多品种多周期指标样品
DoEasy 函数库中的时间序列(第四十二部分):抽象指标缓冲区对象类
DoEasy 函数库中的时间序列(第四十三部分):指标缓冲区对象类
DoEasy 函数库中的时间序列(第四十四部分):指标缓冲区对象集合类
DoEasy 函数库中的时间序列(第四十五部分):多周期指标缓冲区
DoEasy 函数库中的时间序列(第四十七部分):多周期、多品种标准指标