内容
- 概述
- 数据准备及改进缓冲区对象
- 缓冲区对象类集合
- 测试利用缓冲区集合创建指标的能力
- 下一步是什么?
概述
自文章第三十九部分开始,我开始研究利用该函数库来创建自定义指标。 目前,我已准备好时间序列对象及其集合,以及指标缓冲区对象 — 基准抽象缓冲区和基于它的指标缓冲区。
在本文中,我将开始指标缓冲区集合的开发,从而在单个程序中快速创建任意数量(最多 512 个)的缓冲区,以及便捷地检索和访问其数据。
<"timeseries collection> — <buffer collection"> 链接允许创建任何多品种和多周期指标。 这就是我从下一篇文章开始要做的。 在此文章中,我将开发和测试指标缓冲区集合,以便利用九种绘图样式之一来创建任意数量的缓冲区。 当前,指标中的最大可能绘制缓冲区数量不能超过 512。 这足以在大量图表上创建任何复杂指标。 所开发的功能是为了简化此类图形结构的创建和维护,从而访问所创建缓冲区的整个过程降低至仅需按绘制样式、创建时的顺序编号、或按集合中的缓冲区索引。
数据准备及改进缓冲区对象
在之前的文章中,我创建了“指标缓冲区”对象,这些对象是基准抽象缓冲区的后代。 我们用辅助方法来补充它们,以辅助方法访问/读取为指标缓冲区分配的 double 数组中的数据。
尽管已存在足够的基本抽象缓冲区方法,但在仅继承其绘图样式的后代对象中创建自定义方法,会在按其状态访问缓冲区时(绘制样式)有助于更方便地访问其 double 数组,并在创建自定义指标时提供更大的灵活性。
我们来添加新的函数库消息。 在 \MQL5\Include\DoEasy\Datas.mqh 里添加新的消息索引:
MSG_LIB_SYS_FAILED_COLORS_ARRAY_RESIZE, // Failed to change the color array size MSG_LIB_SYS_FAILED_ADD_BUFFER, // Failed to add buffer object to the list MSG_LIB_SYS_FAILED_CREATE_BUFFER_OBJ, // Failed to create \"Indicator buffer\" object MSG_LIB_TEXT_YES, // Yes
...
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_STATUS_NONE, // No drawing
以及与新添加索引相对应的消息文本:
{"Не удалось изменить размер массива цветов","Failed to resize color array"}, {"Не удалось добавить объект-буфер в список","Failed to add buffer object to list"}, {"Не удалось создать объект \"Индикаторный буфер\"","Failed to create object \"Indicator buffer\""}, {"Да","Yes"},
...
{"Неправильно указано количество буферов индикатора (#property indicator_buffers)","Number of indicator buffers incorrect (#property indicator_buffers)"}, {"Достигнуто максимально возможное количество индикаторных буферов","Maximum number of indicator buffers reached"}, {"Нет отрисовки","No drawing"},
该指标最多可以使用 512 个缓冲区。
将指定的宏替换添加到 \MQL5\Include\DoEasy\Defines.mqh:
//+------------------------------------------------------------------+ //| Macro substitutions | //+------------------------------------------------------------------+ //--- Describe the function with the error line number #define DFUN_ERR_LINE (__FUNCTION__+(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian" ? ", Page " : ", Line ")+(string)__LINE__+": ") #define DFUN (__FUNCTION__+": ") // "Function description" #define COUNTRY_LANG ("Russian") // Country language #define END_TIME (D'31.12.3000 23:59:59') // End date for account history data requests #define TIMER_FREQUENCY (16) // Minimal frequency of the library timer in milliseconds #define TOTAL_TRY (5) // Default number of trading attempts #define IND_COLORS_TOTAL (64) // Maximum possible number of indicator buffer colors #define IND_BUFFERS_MAX (512) // Maximum possible number of indicator buffers //--- Standard sounds
当然,您可以直接用数值 “512”,但宏替换更方便,因为如果开发人员有一天会增加该数值,那么您就不必在所有文件中搜索引用该值的地方,并更正为新数值。 您只需将该宏的数值更改即可替换。
我们需要按先前确定的属性来搜索和选择缓冲区对象,而对于搜索和排序则不必要。
所有不会在排序中应用的属性始终位于属性枚举列表的最后。 此外,需启用指定排序中未使用属性数量的宏替换。 若要将属性设置为可排序,我们需要将其从属性枚举列表的末尾移到当前允许排序的那些属性附近,并在排序中指定新的未使用属性数量。
当然,我们还应该用新属性(可用于排序的属性)来补充可能的排序标准枚举。 新添加的属性在排序标准枚举列表中的位置应与这些属性在属性枚举列表中的位置相匹配,在其中我们允许将这些属性用于排序。
听起来有点令人困惑,但实际上一切都很简单。 打开 \MQL5\Include\DoEasy\Defines.mqh,并进行必要的修改。
以前,我们按以下顺序编制了缓冲区对象的整数型属性:
//+------------------------------------------------------------------+ //| 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_NUM_DATAS, // Number of data buffers BUFFER_PROP_INDEX_BASE, // Basic data buffer index BUFFER_PROP_INDEX_COLOR, // Color buffer index BUFFER_PROP_INDEX_NEXT, // Index of the free array to be assigned as the next indicator buffer }; #define BUFFER_PROP_INTEGER_TOTAL (19) // Total number of integer bar properties #define BUFFER_PROP_INTEGER_SKIP (6) // Number of buffer properties not used in sorting //+------------------------------------------------------------------+
此处,应有两个属性可用于排序。 为此,将其提高,然后将未使用属性数量从 6 改为 2 :
//+------------------------------------------------------------------+ //| 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, // Basic data buffer index BUFFER_PROP_INDEX_NEXT, // Index of the free array to be assigned as the next indicator buffer BUFFER_PROP_NUM_DATAS, // Number of data buffers BUFFER_PROP_INDEX_COLOR, // Color buffer index }; #define BUFFER_PROP_INTEGER_TOTAL (19) // Total number of integer bar properties #define BUFFER_PROP_INTEGER_SKIP (2) // Number of buffer properties not used in sorting //+------------------------------------------------------------------+
此处,我指定列表末尾只有两个属性不参与排序。
我们把这些新属性添加到排序条件里:
//+------------------------------------------------------------------+ //| 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, // Sort by the index of the free array to be assigned as the next indicator 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 }; //+------------------------------------------------------------------+
如您所见,它们在排序标准枚举列表中的位置与整数型属性在枚举中的位置一致。 标准枚举是强制性条件,因为排序属性的顺序应与对象属性枚举中的顺序匹配。 在函数库讲述的第三部分中曾对此进行了讨论。
由于我们现在讨论的是按缓冲区对象的属性对其进行排序,是时候准备按其属性搜索缓冲区对象的工具了。 我已为存储在集合中的所有函数库对象引入了这种能力。 现在,我们来编写搜索缓冲区对象的方法。
打开 \MQL5\Include\DoEasy\Services\Select.mqh,并在其内包含缓冲区类文件:
//+------------------------------------------------------------------+ //| Select.mqh | //| 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" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include <Arrays\ArrayObj.mqh> #include "..\Objects\Orders\Order.mqh" #include "..\Objects\Events\Event.mqh" #include "..\Objects\Accounts\Account.mqh" #include "..\Objects\Symbols\Symbol.mqh" #include "..\Objects\PendRequest\PendRequest.mqh" #include "..\Objects\Series\SeriesDE.mqh" #include "..\Objects\Indicators\Buffer.mqh" //+------------------------------------------------------------------+
在类主体的最底部(在声明操控时间序列柱线的方法模块之后),添加声明操控指标缓冲区的方法的模块:
//+------------------------------------------------------------------+ //| Methods of working with timeseries bars | //+------------------------------------------------------------------+ //--- Return the list of bars with one out of (1) integer, (2) real and (3) string properties meeting a specified criterion static CArrayObj *ByBarProperty(CArrayObj *list_source,ENUM_BAR_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByBarProperty(CArrayObj *list_source,ENUM_BAR_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByBarProperty(CArrayObj *list_source,ENUM_BAR_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode); //--- Return the bar index in the list with the maximum value of the order's (1) integer, (2) real and (3) string properties static int FindBarMax(CArrayObj *list_source,ENUM_BAR_PROP_INTEGER property); static int FindBarMax(CArrayObj *list_source,ENUM_BAR_PROP_DOUBLE property); static int FindBarMax(CArrayObj *list_source,ENUM_BAR_PROP_STRING property); //--- Return the bar index in the list with the minimum value of the order's (1) integer, (2) real and (3) string properties static int FindBarMin(CArrayObj *list_source,ENUM_BAR_PROP_INTEGER property); static int FindBarMin(CArrayObj *list_source,ENUM_BAR_PROP_DOUBLE property); static int FindBarMin(CArrayObj *list_source,ENUM_BAR_PROP_STRING property); //+------------------------------------------------------------------+ //| Methods of working with indicator buffers | //+------------------------------------------------------------------+ //--- Return the list of buffers with one out of (1) integer, (2) real and (3) string properties meeting a specified criterion static CArrayObj *ByBufferProperty(CArrayObj *list_source,ENUM_BUFFER_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByBufferProperty(CArrayObj *list_source,ENUM_BUFFER_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByBufferProperty(CArrayObj *list_source,ENUM_BUFFER_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode); //--- Return the buffer index in the list with the maximum value of the order's (1) integer, (2) real and (3) string properties static int FindBufferMax(CArrayObj *list_source,ENUM_BUFFER_PROP_INTEGER property); static int FindBufferMax(CArrayObj *list_source,ENUM_BUFFER_PROP_DOUBLE property); static int FindBufferMax(CArrayObj *list_source,ENUM_BUFFER_PROP_STRING property); //--- Return the buffer index in the list with the minimum value of the order's (1) integer, (2) real and (3) string properties static int FindBufferMin(CArrayObj *list_source,ENUM_BUFFER_PROP_INTEGER property); static int FindBufferMin(CArrayObj *list_source,ENUM_BUFFER_PROP_DOUBLE property); static int FindBufferMin(CArrayObj *list_source,ENUM_BUFFER_PROP_STRING property); //--- }; //+------------------------------------------------------------------+
在文件的末尾,在类主体中添加已声明的所有方法:
//+------------------------------------------------------------------+ //| Methods of working with buffer lists | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Return the list of buffers with one integer | //| property meeting the specified criterion | //+------------------------------------------------------------------+ CArrayObj *CSelect::ByBufferProperty(CArrayObj *list_source,ENUM_BUFFER_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode) { if(list_source==NULL) return NULL; CArrayObj *list=new CArrayObj(); if(list==NULL) return NULL; list.FreeMode(false); ListStorage.Add(list); int total=list_source.Total(); for(int i=0; i<total; i++) { CBuffer *obj=list_source.At(i); if(!obj.SupportProperty(property)) continue; long obj_prop=obj.GetProperty(property); if(CompareValues(obj_prop,value,mode)) list.Add(obj); } return list; } //+------------------------------------------------------------------+ //| Return the list of buffers with one real | //| property meeting the specified criterion | //+------------------------------------------------------------------+ CArrayObj *CSelect::ByBufferProperty(CArrayObj *list_source,ENUM_BUFFER_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode) { if(list_source==NULL) return NULL; CArrayObj *list=new CArrayObj(); if(list==NULL) return NULL; list.FreeMode(false); ListStorage.Add(list); for(int i=0; i<list_source.Total(); i++) { CBuffer *obj=list_source.At(i); if(!obj.SupportProperty(property)) continue; double obj_prop=obj.GetProperty(property); if(CompareValues(obj_prop,value,mode)) list.Add(obj); } return list; } //+------------------------------------------------------------------+ //| Return the list of buffers with one string | //| property meeting the specified criterion | //+------------------------------------------------------------------+ CArrayObj *CSelect::ByBufferProperty(CArrayObj *list_source,ENUM_BUFFER_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode) { if(list_source==NULL) return NULL; CArrayObj *list=new CArrayObj(); if(list==NULL) return NULL; list.FreeMode(false); ListStorage.Add(list); for(int i=0; i<list_source.Total(); i++) { CBuffer *obj=list_source.At(i); if(!obj.SupportProperty(property)) continue; string obj_prop=obj.GetProperty(property); if(CompareValues(obj_prop,value,mode)) list.Add(obj); } return list; } //+------------------------------------------------------------------+ //| Return the buffer index in the list | //| with the maximum integer property value | //+------------------------------------------------------------------+ int CSelect::FindBufferMax(CArrayObj *list_source,ENUM_BUFFER_PROP_INTEGER property) { if(list_source==NULL) return WRONG_VALUE; int index=0; CBuffer *max_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CBuffer *obj=list_source.At(i); long obj1_prop=obj.GetProperty(property); max_obj=list_source.At(index); long obj2_prop=max_obj.GetProperty(property); if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i; } return index; } //+------------------------------------------------------------------+ //| Return the buffer index in the list | //| with the maximum real property value | //+------------------------------------------------------------------+ int CSelect::FindBufferMax(CArrayObj *list_source,ENUM_BUFFER_PROP_DOUBLE property) { if(list_source==NULL) return WRONG_VALUE; int index=0; CBuffer *max_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CBuffer *obj=list_source.At(i); double obj1_prop=obj.GetProperty(property); max_obj=list_source.At(index); double obj2_prop=max_obj.GetProperty(property); if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i; } return index; } //+------------------------------------------------------------------+ //| Return the buffer index in the list | //| with the maximum string property value | //+------------------------------------------------------------------+ int CSelect::FindBufferMax(CArrayObj *list_source,ENUM_BUFFER_PROP_STRING property) { if(list_source==NULL) return WRONG_VALUE; int index=0; CBuffer *max_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CBuffer *obj=list_source.At(i); string obj1_prop=obj.GetProperty(property); max_obj=list_source.At(index); string obj2_prop=max_obj.GetProperty(property); if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i; } return index; } //+------------------------------------------------------------------+ //| Return the buffer index in the list | //| with the minimum integer property value | //+------------------------------------------------------------------+ int CSelect::FindBufferMin(CArrayObj* list_source,ENUM_BUFFER_PROP_INTEGER property) { int index=0; CBuffer *min_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CBuffer *obj=list_source.At(i); long obj1_prop=obj.GetProperty(property); min_obj=list_source.At(index); long obj2_prop=min_obj.GetProperty(property); if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i; } return index; } //+------------------------------------------------------------------+ //| Return the buffer index in the list | //| with the minimum real property value | //+------------------------------------------------------------------+ int CSelect::FindBufferMin(CArrayObj* list_source,ENUM_BUFFER_PROP_DOUBLE property) { int index=0; CBuffer *min_obj=NULL; int total=list_source.Total(); if(total== 0) return WRONG_VALUE; for(int i=1; i<total; i++) { CBuffer *obj=list_source.At(i); double obj1_prop=obj.GetProperty(property); min_obj=list_source.At(index); double obj2_prop=min_obj.GetProperty(property); if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i; } return index; } //+------------------------------------------------------------------+ //| Return the buffer index in the list | //| with the minimum string property value | //+------------------------------------------------------------------+ int CSelect::FindBufferMin(CArrayObj* list_source,ENUM_BUFFER_PROP_STRING property) { int index=0; CBuffer *min_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CBuffer *obj=list_source.At(i); string obj1_prop=obj.GetProperty(property); min_obj=list_source.At(index); string obj2_prop=min_obj.GetProperty(property); if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i; } return index; } //+------------------------------------------------------------------+
我已在讲述函数库创建的第三篇文章中详细描述了 CSelect 类。
我们稍微改进一下抽象缓冲区类及其后代。
由于我们要按缓冲区对象属性执行搜索和排序,因此我们将 CSelect 类文件包含到抽象缓冲区类文件之中 \MQL5\Include\DoEasy\Objects\Indicators\Buffer.mqh:
//+------------------------------------------------------------------+ //| Buffer.mqh | //| 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" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "..\..\Services\Select.mqh" #include "..\..\Objects\BaseObj.mqh" //+------------------------------------------------------------------+
现在,在 CBuffer 类中可直观 CSelect 类及其所有后代。
在类的公开部分,编写设置缓冲区对象的自定义名称的方法:
public: //--- Set buffer's (1) integer, (2) real and (3) string properties void SetProperty(ENUM_BUFFER_PROP_INTEGER property,long value) { this.m_long_prop[property]=value; } void SetProperty(ENUM_BUFFER_PROP_DOUBLE property,double value) { this.m_double_prop[this.IndexProp(property)]=value; } void SetProperty(ENUM_BUFFER_PROP_STRING property,string value) { this.m_string_prop[this.IndexProp(property)]=value; } //--- Return (1) integer, (2) real and (3) string buffer properties from the properties array long GetProperty(ENUM_BUFFER_PROP_INTEGER property) const { return this.m_long_prop[property]; } double GetProperty(ENUM_BUFFER_PROP_DOUBLE property) const { return this.m_double_prop[this.IndexProp(property)]; } string GetProperty(ENUM_BUFFER_PROP_STRING property) const { return this.m_string_prop[this.IndexProp(property)]; } //--- Get description of buffer's (1) integer, (2) real and (3) string properties string GetPropertyDescription(ENUM_BUFFER_PROP_INTEGER property); string GetPropertyDescription(ENUM_BUFFER_PROP_DOUBLE property); string GetPropertyDescription(ENUM_BUFFER_PROP_STRING property); //--- Return the flag of the buffer supporting the property virtual bool SupportProperty(ENUM_BUFFER_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_BUFFER_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_BUFFER_PROP_STRING property) { return true; } //--- Compare CBuffer objects by all possible properties (for sorting the lists by a specified buffer object property) virtual int Compare(const CObject *node,const int mode=0) const; //--- Compare CBuffer objects by all properties (to search for equal buffer objects) bool IsEqual(CBuffer* compared_obj) const; //--- Set the buffer name void SetName(const string name) { this.m_name=name; } //--- Default constructor CBuffer(void){;} protected:
为缓冲区设置自定义名称,令我们能够按名称在集合列表中针对其进行后续搜索。
我们已有了按指定时间序列索引返回缓冲区值的方法,以及返回按指定时间序列索引为缓冲区分配的颜色的方法。 然而,我们没有返回指定时间序列索引中缓冲区颜色索引的方法。 实际上,我们并没有为缓冲区设置颜色值。 我们是用设置颜色索引来替代 — 该值指定颜色的序列号(分配给颜色缓冲区的序号之一),用于在指定时间序列位置绘制线条。
我们来修复这个问题。 声明另一个返回缓冲区在指定时间序列位置设置的颜色索引的方法,并把按指定时间序列位置返回缓冲区颜色的方法从 GetColorBufferValue() 重命名为 GetColorBufferValueColor():
//--- Return the size of the data buffer array virtual int GetDataTotal(const uint buffer_index=0) const; //--- Return the value from the specified index of the specified (1) data, (2) color index and (3) color buffer arrays double GetDataBufferValue(const uint buffer_index,const uint series_index) const; int GetColorBufferValueIndex(const uint series_index) const; color GetColorBufferValueColor(const uint series_index) const; //--- Set the value to the specified index of the specified (1) data and (2) color buffer arrays
现在,我们已声明两个操控缓冲区颜色的方法。 其中之一返回颜色,而第二个返回颜色索引。
在类主体之外,实现返回颜色索引的方法,并修复返回缓冲区类的方法实现:
//+------------------------------------------------------------------+ //| Return the color index value from the specified timeseries index | //| of the specified color buffer array | //+------------------------------------------------------------------+ int CBuffer::GetColorBufferValueIndex(const uint series_index) const { int data_total=this.GetDataTotal(0); if(data_total==0) return WRONG_VALUE; int data_index=((int)series_index<data_total ? (int)series_index : data_total-1); return(this.ColorsTotal()==1 ? 0 : (int)this.ColorBufferArray[data_index]); } //+------------------------------------------------------------------+ //| Return the color value from the specified timeseries index | //| of the specified color buffer array | //+------------------------------------------------------------------+ color CBuffer::GetColorBufferValueColor(const uint series_index) const { int data_total=this.GetDataTotal(0); if(data_total==0) return clrNONE; int color_index=this.GetColorBufferValueIndex(series_index); return(color_index>WRONG_VALUE ? (color)this.ArrayColors[color_index] : clrNONE); } //+------------------------------------------------------------------+
以前,我们只有一种方法,而颜色索引是从其内部获得的:
//+------------------------------------------------------------------+ //| Return the value from the specified timeseries index | //| of the specified color buffer array | //+------------------------------------------------------------------+ color CBuffer::GetColorBufferValue(const uint series_index) const { int data_total=this.GetDataTotal(0); if(data_total==0) return clrNONE; int data_index=((int)series_index<data_total ? (int)series_index : data_total-1); int color_index=(this.ColorsTotal()==1 ? 0 : (int)this.ColorBufferArray[data_index]); return (color)this.ArrayColors[color_index]; } //+------------------------------------------------------------------+
现在,该计算已移至单独的 GetColorBufferValueIndex() 方法,且现在于返回柱线颜色的方法里用调用新方法代替索引计算。
当创建箭头缓冲区对象类时,它是作为抽象缓冲区后代(如同其余缓冲区类),我做了一些改进 — 在 CBuffer 类中声明了设置和偏移箭头代码的虚拟方法,但是,我忘了在后代类中实现它们。 我们来修复这个问题。
打开箭头缓冲区对象类的文件 \MQL5\Include\DoEasy\Objects\Indicators\BufferArrow.mqh,并声明这些方法。 另外,添加另外两个方法设置和返回为指标缓冲区数组里分配的数值:
//+------------------------------------------------------------------+ //| Buffer with the "Drawing with arrows" drawing style | //+------------------------------------------------------------------+ class CBufferArrow : public CBuffer { private: public: //--- Constructor CBufferArrow(const uint index_plot,const uint index_base_array) : CBuffer(BUFFER_STATUS_ARROW,BUFFER_TYPE_DATA,index_plot,index_base_array,1,1,"Arrows") {} //--- Supported integer properties of a buffer virtual bool SupportProperty(ENUM_BUFFER_PROP_INTEGER property); //--- Supported real properties of a buffer virtual bool SupportProperty(ENUM_BUFFER_PROP_DOUBLE property); //--- Supported string properties of a buffer virtual bool SupportProperty(ENUM_BUFFER_PROP_STRING property); //--- Display a short buffer description in the journal virtual void PrintShort(void); //--- Set (1) the arrow code, (2) vertical shift of arrows virtual void SetArrowCode(const uchar code); virtual void SetArrowShift(const int shift); //--- Set the value to the data buffer array void SetData(const uint series_index,const double value) { this.SetBufferValue(0,series_index,value); } //--- Return the value from the data buffer array double GetData(const uint series_index) const { return this.GetDataBufferValue(0,series_index); } }; //+------------------------------------------------------------------+
在类主体之外,实现设置和偏移箭头代码的方法:
//+------------------------------------------------------------------+ //| Set the arrow code | //+------------------------------------------------------------------+ void CBufferArrow::SetArrowCode(const uchar code) { this.SetProperty(BUFFER_PROP_ARROW_CODE,code); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_ARROW,code); } //+------------------------------------------------------------------+ //| Set the vertical shift of the arrows | //+------------------------------------------------------------------+ void CBufferArrow::SetArrowShift(const int shift) { this.SetProperty(BUFFER_PROP_ARROW_SHIFT,shift); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_ARROW_SHIFT,shift); } //+------------------------------------------------------------------+
该方法将所传递的数值写入相应的缓冲区对象属性,然后将此属性设置到绘制缓冲区对象。
我们添加两个方法,在缓冲区对象文件和缓冲区对象数组之间设置/返回数值,如此可为单根柱线渲染:曲线( BufferLine.mqh ),线段( BufferSection .mqh )和从零轴开始的直方图( BufferHistogram.mqh ):
//--- Set the value to the data buffer array void SetData(const uint series_index,const double value) { this.SetBufferValue(0,series_index,value); } //--- Return the value from the data buffer array double GetData(const uint series_index) const { return this.GetDataBufferValue(0,series_index); }
为了渲染,添加四个方法,可在缓冲区对象数据数组和含有两个缓冲区对象的文件之间设置/返回数值:两个数据数组上的直方图( BufferHistogram2.mqh ),之字折线( BufferZigZag.mqh ),然后在两个数据数组之间填充( BufferFilling.mqh ):
//--- Set the value to the (1) zero and (2) the first data buffer array void SetData0(const uint series_index,const double value) { this.SetBufferValue(0,series_index,value); } void SetData1(const uint series_index,const double value) { this.SetBufferValue(1,series_index,value); } //--- Return the value from the (1) zero and (2) the first data buffer array double GetData0(const uint series_index) const { return this.GetDataBufferValue(0,series_index); } double GetData1(const uint series_index) const { return this.GetDataBufferValue(1,series_index); }
为了渲染,添加八个方法,可在缓冲区对象数据数组和含有四个缓冲区对象的文件之间设置/返回数值: 柱线 (BufferBars.mqh) 和蜡烛 (BufferCandlts.mqh):
//--- Set (1) Open, (2) High, (3) Low and (4) Close values to the appropriate data buffer array void SetDataOpen(const uint series_index,const double value) { this.SetBufferValue(0,series_index,value); } void SetDataHigh(const uint series_index,const double value) { this.SetBufferValue(1,series_index,value); } void SetDataLow(const uint series_index,const double value) { this.SetBufferValue(2,series_index,value); } void SetDataClose(const uint series_index,const double value) { this.SetBufferValue(3,series_index,value); } //--- Return (1) Open, (2) High, (3) Low and (4) Close value from the appropriate data buffer array double GetDataOpen(const uint series_index) const { return this.GetDataBufferValue(0,series_index); } double GetDataHigh(const uint series_index) const { return this.GetDataBufferValue(1,series_index); } double GetDataLow(const uint series_index) const { return this.GetDataBufferValue(2,series_index); } double GetDataClose(const uint series_index) const { return this.GetDataBufferValue(3,series_index); }
当然,我们可以不用抽象缓冲区对象已编写好的基本方法 SetBufferValue() 和 GetBufferValue() ,这些方法需要指定所要的缓冲区编号。 我会尽力简化最终用户的工作。 因此,我计划实现按应用选择方法的能力。
现在一切准备就绪,可以开发指标缓冲区对象的集合类。
该类包含所有已创建缓冲区对象的列表,并提供创建和获取任何缓冲区的能力,从而可在程序中使用该缓冲区。 与以前的集合类不同,此处我们不必检查是否存在具有相同属性的相同对象。 代之,我们能够用相同的缓冲区把各种事件可视化。
缓冲区对象类集合
在 \MQL5\Include\DoEasy\Collections\ 里,创建 BuffersCollection.mqh 文件,内有基本的标准库 CObject 类。 立即在其中包含函数库基本清单和缓冲区对象的类文件:
//+------------------------------------------------------------------+ //| BuffersCollection.mqh | //| 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" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "ListObj.mqh" #include "..\Objects\Indicators\BufferArrow.mqh" #include "..\Objects\Indicators\BufferLine.mqh" #include "..\Objects\Indicators\BufferSection.mqh" #include "..\Objects\Indicators\BufferHistogram.mqh" #include "..\Objects\Indicators\BufferHistogram2.mqh" #include "..\Objects\Indicators\BufferZigZag.mqh" #include "..\Objects\Indicators\BufferFilling.mqh" #include "..\Objects\Indicators\BufferBars.mqh" #include "..\Objects\Indicators\BufferCandles.mqh" //+------------------------------------------------------------------+ //| Collection of indicator buffers | //+------------------------------------------------------------------+ class CBuffersCollection : public CObject {
我们在类主体里填充必要的内容(幸运的是,其内容不多),并研究其目的:
//+------------------------------------------------------------------+ //| Collection of indicator buffers | //+------------------------------------------------------------------+ class CBuffersCollection : public CObject { private: CListObj m_list; // Buffer object list //--- Return the index of the next (1) drawn and (2) basic buffer int GetIndexNextPlot(void); int GetIndexNextBase(void); //--- Create a new buffer object and place it to the collection list bool CreateBuffer(ENUM_BUFFER_STATUS status); public: //--- Return (1) oneself and (2) the timeseries list CBuffersCollection *GetObject(void) { return &this; } CArrayObj *GetList(void) { return &this.m_list; } //--- Return the number of (1) drawn buffers, (2) all arrays used to build all buffers in the collection int PlotsTotal(void); int BuffersTotal(void); //--- Create the new buffer (1) "Drawing with arrows", (2) "Line", (3) "Sections", (4) "Histogram from the zero line", //--- (5) "Histogram on two indicator buffers", (6) "Zigzag", (7) "Color filling between two levels", //--- (8) "Display as bars", (9) "Display as candles", bool CreateArrow(void) { return this.CreateBuffer(BUFFER_STATUS_ARROW); } bool CreateLine(void) { return this.CreateBuffer(BUFFER_STATUS_LINE); } bool CreateSection(void) { return this.CreateBuffer(BUFFER_STATUS_SECTION); } bool CreateHistogram(void) { return this.CreateBuffer(BUFFER_STATUS_HISTOGRAM); } bool CreateHistogram2(void) { return this.CreateBuffer(BUFFER_STATUS_HISTOGRAM2); } bool CreateZigZag(void) { return this.CreateBuffer(BUFFER_STATUS_ZIGZAG); } bool CreateFilling(void) { return this.CreateBuffer(BUFFER_STATUS_FILLING); } bool CreateBars(void) { return this.CreateBuffer(BUFFER_STATUS_BARS); } bool CreateCandles(void) { return this.CreateBuffer(BUFFER_STATUS_CANDLES); } //--- Return the buffer by the Plot index CBuffer *GetBufferByPlot(const int plot_index); //--- Return buffers by drawing style by a serial number //--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones) CBufferArrow *GetBufferArrow(const int number); CBufferLine *GetBufferLine(const int number); CBufferSection *GetBufferSection(const int number); CBufferHistogram *GetBufferHistogram(const int number); CBufferHistogram2 *GetBufferHistogram2(const int number); CBufferZigZag *GetBufferZigZag(const int number); CBufferFilling *GetBufferFilling(const int number); CBufferBars *GetBufferBars(const int number); CBufferCandles *GetBufferCandles(const int number); //--- Constructor CBuffersCollection(); }; //+------------------------------------------------------------------+
因此,m_list 是我们添加和存储所有已创建缓冲区对象的列表。 它是指向 CObject 类实例的动态指针数组类的后代。
GetIndexNextPlot() 私密方法返回下一个绘制缓冲区的索引,对于为下一个创建指标缓冲区指定索引是必需的,而 GetIndexNextBase() 方法(也是私密)返回下一个基准缓冲区的索引,对于为新创建的缓冲区对象指定可分配实数型数组的索引是必要的。
我来澄清一下。 在为指标创建缓冲区时,我们在数据窗口中指定其编号(绘制缓冲区编号),并将其绑定到 double 数组(基准缓冲区数组的索引)。 为什么“基准”? 绘图缓冲区能够使用多个数组。 仅第一个为指标分配的数组是基准数组。 其他绘图数组的索引等于“基准数组”+N。
因此,对于基于两个数组的三色缓冲区,绘图和基准的索引如下所示:
第一个缓冲区:
- 绘图缓冲区 — 索引 0
- 基准数组 — 索引 0
- 第二个数组的索引 1
- 颜色数组的索引 2
第二个缓冲区:
- 绘图缓冲区 — 索引 1
- 基准数组 — 索引 3
- 第二个数组 — 索引 4
- 颜色数组 — 索引 5
第三个缓冲区:
- 绘图缓冲区 — 索引 2
- 基准数组 — 索引 6
- 第二个数组 — 索引 7
- 颜色数组 — 索引 8
正如我们所见,索引是为绘制缓冲区和每个缓冲区的基准数组单独建立的。 如果指标中应用了多个缓冲区,我们很容易感到困惑。 集合类自动为绘制缓冲区及其数组分配正确的索引。 这意味着我们总可以从程序中引用它们。
CreateBuffer() 方法创建一个新缓冲区,并将其放置到集合列表当中。
GetObject() 和 GetList() 方法返回指向集合类对象的指针,以及集合类的缓冲区对象列表。
PlotsTotal() 和 BuffersTotal() 方法返回集合中已创建绘制缓冲区的数量,以及相应地构建所有绘制缓冲区的已使用数组总数。
创建具有特定绘图样式的缓冲区对象的公开方法:
//--- Create the new buffer (1) "Drawing with arrows", (2) "Line", (3) "Sections", (4) "Histogram from the zero line", //--- (5) "Histogram on two indicator buffers", (6) "Zigzag", (7) "Color filling between two levels", //--- (8) "Display as bars", (9) "Display as candles", bool CreateArrow(void) { return this.CreateBuffer(BUFFER_STATUS_ARROW); } bool CreateLine(void) { return this.CreateBuffer(BUFFER_STATUS_LINE); } bool CreateSection(void) { return this.CreateBuffer(BUFFER_STATUS_SECTION); } bool CreateHistogram(void) { return this.CreateBuffer(BUFFER_STATUS_HISTOGRAM); } bool CreateHistogram2(void) { return this.CreateBuffer(BUFFER_STATUS_HISTOGRAM2); } bool CreateZigZag(void) { return this.CreateBuffer(BUFFER_STATUS_ZIGZAG); } bool CreateFilling(void) { return this.CreateBuffer(BUFFER_STATUS_FILLING); } bool CreateBars(void) { return this.CreateBuffer(BUFFER_STATUS_BARS); } bool CreateCandles(void) { return this.CreateBuffer(BUFFER_STATUS_CANDLES); }
CreateBuffer() 私有方法按指定绘制样式创建缓冲区对象,并返回结果。
GetBufferByPlot() 方法按绘制缓冲区索引返回缓冲区指针。
这些方法按其序列号返回指向缓冲区对象的指针:
//--- Return buffers by drawing style by a serial number //--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones) CBufferArrow *GetBufferArrow(const int number); CBufferLine *GetBufferLine(const int number); CBufferSection *GetBufferSection(const int number); CBufferHistogram *GetBufferHistogram(const int number); CBufferHistogram2 *GetBufferHistogram2(const int number); CBufferZigZag *GetBufferZigZag(const int number); CBufferFilling *GetBufferFilling(const int number); CBufferBars *GetBufferBars(const int number); CBufferCandles *GetBufferCandles(const int number);
按其创建时的顺序编号返回指定绘图样式的对象。
我们在以下示例中对此进行说明:
我用绘制缓冲区索引 0、1、2 和 3创建了四个 BufferArrow() 箭头缓冲区。
接下来,我用绘制缓冲区索引 4、5、6、7 和 8创建了五个 BufferLine() 曲线缓冲区。
现在,我们需要用到第三个箭头缓冲区(索引为 2)和第四个曲线缓冲区(索引为 7)。
为了获得指向第三个箭头缓冲区的指针,我们只需按其序列号(而不是通过索引)即可得到它。 该数字应从零算起。 例如,如果需要第三个箭头缓冲区,则应按以下方式获取它:
CBufferArrow *buffer_arrow=GetBufferArrow(2); // the third arrow buffer (0,1,2)
指向第四个曲线缓冲区的指针如下获得:
CBufferLine *buffer_line=GetBufferLine(3); // the fourth line buffer (0,1,2,3)
现在我们来研究所有所声明方法的实现。
类构造器:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CBuffersCollection::CBuffersCollection() { this.m_list.Clear(); this.m_list.Sort(); this.m_list.Type(COLLECTION_BUFFERS_ID); } //+------------------------------------------------------------------+
清除列表,为列表设置排序标志,并为集合类型设置指标缓冲区集合列表 ID。
该方法返回下一个绘制缓冲区的索引,且方法返回下一个基准缓冲区的索引:
//+------------------------------------------------------------------+ //| Return the index of the next drawn buffer | //+------------------------------------------------------------------+ int CBuffersCollection::GetIndexNextPlot(void) { //--- Return the pointer to the list. If the list is not created for some reason, return -1 CArrayObj *list=this.GetList(); if(list==NULL) return WRONG_VALUE; //--- Get the index of the drawn buffer with the highest value. If the FindBufferMax() method returns -1, //--- the list is empty, return index 0 for the very first buffer in the list int index=CSelect::FindBufferMax(list,BUFFER_PROP_INDEX_PLOT); if(index==WRONG_VALUE) index=0; //--- if the index is not -1, else { //--- get the buffer object from the list by its index CBuffer *buffer=this.m_list.At(index); if(buffer==NULL) return WRONG_VALUE; //--- Return the index following the Plot index of the buffer object index=buffer.IndexPlot()+1; } //--- Return the index value return index; } //+------------------------------------------------------------------+ //| Return the index of the next basic buffer | //+------------------------------------------------------------------+ int CBuffersCollection::GetIndexNextBase(void) { //--- Return the pointer to the list. If the list is not created for some reason, return -1 CArrayObj *list=this.GetList(); if(list==NULL) return WRONG_VALUE; //--- Get the highest index of the next array that can be assigned as an indicator buffer, //--- if the FindBufferMax() method returns -1, //--- the list is empty, return index 0 for the very first buffer in the list int index=CSelect::FindBufferMax(list,BUFFER_PROP_INDEX_NEXT); if(index==WRONG_VALUE) index=0; //--- if the index is not -1, else { //--- get the buffer object from the list by its index CBuffer *buffer=this.m_list.At(index); if(buffer==NULL) return WRONG_VALUE; //--- Return the index of the next array from the buffer object properties index=buffer.IndexNextBuffer(); } //--- Return the index value return index; } //+------------------------------------------------------------------+
这两个方法的逻辑是相同的。 我已在代码的注释中进行了讲述。
该方法创建一个新的缓冲区对象并将其放置在收集列表中:
//+------------------------------------------------------------------+ //| Create a new buffer object and place it to the collection list | //+------------------------------------------------------------------+ bool CBuffersCollection::CreateBuffer(ENUM_BUFFER_STATUS status) { //--- Get the drawn buffer index and the index used to assign the first buffer array as an indicator one int index_plot=this.GetIndexNextPlot(); int index_base=this.GetIndexNextBase(); //--- If any of the indices is not received, return 'false' if(index_plot==WRONG_VALUE || index_base==WRONG_VALUE) return false; //--- If the maximum possible number of indicator buffers has already been reached, inform about it and return 'false' if(this.m_list.Total()==IND_BUFFERS_MAX) { ::Print(CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_MAX_BUFFERS_REACHED)); return false; } //--- Create the buffer drawing style description string descript=::StringSubstr(::EnumToString(status),14); //--- Declare the abstract buffer object CBuffer *buffer=NULL; //--- Create a buffer object depending on the status passed to the method (drawing style) switch(status) { case BUFFER_STATUS_ARROW : buffer=new CBufferArrow(index_plot,index_base); break; case BUFFER_STATUS_LINE : buffer=new CBufferLine(index_plot,index_base); break; case BUFFER_STATUS_SECTION : buffer=new CBufferSection(index_plot,index_base); break; case BUFFER_STATUS_HISTOGRAM : buffer=new CBufferHistogram(index_plot,index_base); break; case BUFFER_STATUS_HISTOGRAM2 : buffer=new CBufferHistogram2(index_plot,index_base); break; case BUFFER_STATUS_ZIGZAG : buffer=new CBufferZigZag(index_plot,index_base); break; case BUFFER_STATUS_FILLING : buffer=new CBufferFilling(index_plot,index_base); break; case BUFFER_STATUS_BARS : buffer=new CBufferBars(index_plot,index_base); break; case BUFFER_STATUS_CANDLES : buffer=new CBufferCandles(index_plot,index_base); break; default: break; } //--- Failed to create a buffer, inform of that and return 'false' if(buffer==NULL) { ::Print(CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_BUFFER_OBJ)," ",descript); return false; } //--- If failed to add a buffer object to the collection list for some reason, //--- inform of that, remove the created buffer object and return 'false' if(!this.m_list.Add(buffer)) { ::Print(CMessage::Text(MSG_LIB_SYS_FAILED_ADD_BUFFER)); delete buffer; return false; } //--- Set a name for the buffer object and return 'true' buffer.SetName("Buffer"+descript+"("+(string)buffer.IndexPlot()+")"); return true; } //+------------------------------------------------------------------+
此处的注释中也讲述了逻辑。 请注意,我们声明了 CBuffer 抽象缓冲区对象。 不过,实际上,我们创建了一个新对象,其图形类型通过状态(状态描述了图形样式)传递给方法。 所有缓冲区对象都是抽象缓冲区对象的后代,因此如此声明和创建对象是允许且方便的。
该方法按其 Plot 索引(在数据窗口中的索引)返回缓冲区:
//+------------------------------------------------------------------+ //| Return the buffer by the Plot index | //+------------------------------------------------------------------+ CBuffer *CBuffersCollection::GetBufferByPlot(const int plot_index) { CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_INDEX_PLOT,plot_index,EQUAL); return(list!=NULL && list.Total()==1 ? list.At(0) : NULL); } //+------------------------------------------------------------------+
CSelect 类能够获取仅包含指定索引的缓冲区对象的列表(列表中只有一个对象)。 从获得的列表中返回缓冲区对象(如果找到)。 如果集合列表中没有这样的对象,则返回 NULL。
返回指定类型的缓冲区对象的方法:
//+------------------------------------------------------------------+ //| Return the "Drawing by arrows" buffer by a serial number | //| (0 - the very first arrow buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ CBufferArrow *CBuffersCollection::GetBufferArrow(const int number) { CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_STATUS,BUFFER_STATUS_ARROW,EQUAL); return(list!=NULL && list.Total()>0 ? list.At(number) : NULL); } //+------------------------------------------------------------------+ //| Return the Line buffer by a serial number | //| (0 - the very first line buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ CBufferLine *CBuffersCollection::GetBufferLine(const int number) { CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_STATUS,BUFFER_STATUS_LINE,EQUAL); return(list!=NULL && list.Total()>0 ? list.At(number) : NULL); } //+------------------------------------------------------------------+ //| Return the Sections buffer by a serial number | //| (0 - the very first sections buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ CBufferSection *CBuffersCollection::GetBufferSection(const int number) { CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_STATUS,BUFFER_STATUS_SECTION,EQUAL); return(list!=NULL && list.Total()>0 ? list.At(number) : NULL); } //+------------------------------------------------------------------+ //| Return the "Histogram from the zero line" buffer by number | //| (0 - the very first buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ CBufferHistogram *CBuffersCollection::GetBufferHistogram(const int number) { CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_STATUS,BUFFER_STATUS_HISTOGRAM,EQUAL); return(list!=NULL && list.Total()>0 ? list.At(number) : NULL); } //+------------------------------------------------------------------+ //| Return the "Histogram on two buffers" buffer by number | //| (0 - the very first buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ CBufferHistogram2 *CBuffersCollection::GetBufferHistogram2(const int number) { CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_STATUS,BUFFER_STATUS_HISTOGRAM2,EQUAL); return(list!=NULL && list.Total()>0 ? list.At(number) : NULL); } //+------------------------------------------------------------------+ //| Return the ZigZag buffer by a serial number | //| (0 - the very first zigzag buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ CBufferZigZag *CBuffersCollection::GetBufferZigZag(const int number) { CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_STATUS,BUFFER_STATUS_ZIGZAG,EQUAL); return(list!=NULL && list.Total()>0 ? list.At(number) : NULL); } //+------------------------------------------------------------------+ //|Return the "Color filling between two levels" buffer by number | //| (0 - the very first filling buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ CBufferFilling *CBuffersCollection::GetBufferFilling(const int number) { CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_STATUS,BUFFER_STATUS_FILLING,EQUAL); return(list!=NULL && list.Total()>0 ? list.At(number) : NULL); } //+------------------------------------------------------------------+ //| Return the "Display as bars" buffer by a serial number | //| (0 - the very first bar buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ CBufferBars *CBuffersCollection::GetBufferBars(const int number) { CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_STATUS,BUFFER_STATUS_BARS,EQUAL); return(list!=NULL && list.Total()>0 ? list.At(number) : NULL); } //+------------------------------------------------------------------+ //|Return the "Display as candles" buffer by a serial number | //| (0 - the very first candle buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ CBufferCandles *CBuffersCollection::GetBufferCandles(const int number) { CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_STATUS,BUFFER_STATUS_CANDLES,EQUAL); return(list!=NULL && list.Total()>0 ? list.At(number) : NULL); }
所有方法都相同,因此我们仅研究其中之一。
仅获取包含含有必要绘图样式的缓冲区对象的列表。
如果得到了列表,且列表不为空,则按指定索引从所获得的列表中返回对象。。
在列表中,对象均按索引的升序排列,因此不需要调整索引。
如果索引超出列表,则 CArrayObj 类的 At() 方法返回 NULL 。
如果未获取列表或该列表为空,返回 NULL 。
这些方法返回绘制缓冲区的数量和所有指标数组的数量:
//+------------------------------------------------------------------+ //| Return the number of drawn buffers | //+------------------------------------------------------------------+ int CBuffersCollection::PlotsTotal(void) { int index=CSelect::FindBufferMax(this.GetList(),BUFFER_PROP_INDEX_PLOT); CBuffer *buffer=this.m_list.At(index); return(buffer!=NULL ? buffer.IndexPlot()+1 : WRONG_VALUE); } //+------------------------------------------------------------------+ //| Returns the number of all indicator arrays | //+------------------------------------------------------------------+ int CBuffersCollection::BuffersTotal(void) { int index=CSelect::FindBufferMax(this.GetList(),BUFFER_PROP_INDEX_NEXT); CBuffer *buffer=this.m_list.At(index); return(buffer!=NULL ? buffer.IndexNextBuffer() : WRONG_VALUE); } //+------------------------------------------------------------------+
这些方法的逻辑相似:我们获取必要属性的最高索引值,并且按所得索引从集合列表中取得缓冲区对象。 如果得到缓冲区,返回它与方法对应的 属性,否则,返回 -1。
指标缓冲区集合类的开发到此结束。
现在,我们需要在基于函数库的程序里提供访问类的方法。 就我而言,这是在 CEngine 库的基准对象类里完成的。
打开 \MQL5\Include\DoEasy\Engine.mqh,并在那里进行必要的修改。 在此,我们只需复制指标缓冲区集合类的已创建方法,并添加辅助方法即可。
首先,包含类文件,并声明缓冲区集合类的对象:
//+------------------------------------------------------------------+ //| Engine.mqh | //| 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" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "Services\TimerCounter.mqh" #include "Collections\HistoryCollection.mqh" #include "Collections\MarketCollection.mqh" #include "Collections\EventsCollection.mqh" #include "Collections\AccountsCollection.mqh" #include "Collections\SymbolsCollection.mqh" #include "Collections\ResourceCollection.mqh" #include "Collections\TimeSeriesCollection.mqh" #include "Collections\BuffersCollection.mqh" #include "TradingControl.mqh" //+------------------------------------------------------------------+ //| Library basis class | //+------------------------------------------------------------------+ class CEngine { private: CHistoryCollection m_history; // Collection of historical orders and deals CMarketCollection m_market; // Collection of market orders and deals CEventsCollection m_events; // Event collection CAccountsCollection m_accounts; // Account collection CSymbolsCollection m_symbols; // Symbol collection CTimeSeriesCollection m_time_series; // Timeseries collection CBuffersCollection m_buffers; // Collection of indicator buffers CResourceCollection m_resource; // Resource list CTradingControl m_trading; // Trading management object CPause m_pause; // Pause object CArrayObj m_list_counters; // List of timer counters
在类的公开部分,编写方法调用和返回同名缓冲区集合类方法的结果,并声明缓冲区集合类的其他方法:
//--- Copy the specified double property of the specified timeseries of the specified symbol to the array //--- Regardless of the array indexing direction, copying is performed the same way as copying to a timeseries array bool SeriesCopyToBufferAsSeries(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_BAR_PROP_DOUBLE property, double &array[],const double empty=EMPTY_VALUE) { return this.m_time_series.CopyToBufferAsSeries(symbol,timeframe,property,array,empty);} //--- Return (1) the buffer collection, (2) the buffer list from the buffer collection and (3) the buffer by the Plot index CBuffersCollection *GetBuffersCollection(void) { return &this.m_buffers; } CArrayObj *GetListBuffers(void) { return this.m_buffers.GetList(); } CBuffer *GetBufferByPlot(const int plot_index) { return this.m_buffers.GetBufferByPlot(plot_index); } //--- Return buffers by drawing style by a serial number //--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones) CBufferArrow *GetBufferArrow(const int number) { return this.m_buffers.GetBufferArrow(number); } CBufferLine *GetBufferLine(const int number) { return this.m_buffers.GetBufferLine(number); } CBufferSection *GetBufferSection(const int number) { return this.m_buffers.GetBufferSection(number); } CBufferHistogram *GetBufferHistogram(const int number) { return this.m_buffers.GetBufferHistogram(number); } CBufferHistogram2 *GetBufferHistogram2(const int number) { return this.m_buffers.GetBufferHistogram2(number); } CBufferZigZag *GetBufferZigZag(const int number) { return this.m_buffers.GetBufferZigZag(number); } CBufferFilling *GetBufferFilling(const int number) { return this.m_buffers.GetBufferFilling(number); } CBufferBars *GetBufferBars(const int number) { return this.m_buffers.GetBufferBars(number); } CBufferCandles *GetBufferCandles(const int number) { return this.m_buffers.GetBufferCandles(number); } //--- Return the number of (1) drawn buffers and (2) all indicator arrays int BufferPlotsTotal(void) { return this.m_buffers.PlotsTotal(); } int BuffersTotal(void) { return this.m_buffers.BuffersTotal(); } //--- Create the new buffer (1) "Drawing with arrows", (2) "Line", (3) "Sections", (4) "Histogram from the zero line", //--- (5) "Histogram on two indicator buffers", (6) "Zigzag", (7) "Color filling between two levels", //--- (8) "Display as bars", (9) "Display as candles", bool BufferCreateArrow(void) { return this.m_buffers.CreateArrow(); } bool BufferCreateLine(void) { return this.m_buffers.CreateLine(); } bool BufferCreateSection(void) { return this.m_buffers.CreateSection(); } bool BufferCreateHistogram(void) { return this.m_buffers.CreateHistogram(); } bool BufferCreateHistogram2(void) { return this.m_buffers.CreateHistogram2(); } bool BufferCreateZigZag(void) { return this.m_buffers.CreateZigZag(); } bool BufferCreateFilling(void) { return this.m_buffers.CreateFilling(); } bool BufferCreateBars(void) { return this.m_buffers.CreateBars(); } bool BufferCreateCandles(void) { return this.m_buffers.CreateCandles(); } //--- Return buffer data by its serial number of (1) arrows, (2) line, (3) sections and (4) histogram from zero //--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones) double BufferDataArrow(const int number,const int series_index); double BufferDataLine(const int number,const int series_index); double BufferDataSection(const int number,const int series_index); double BufferDataHistogram(const int number,const int series_index); //--- Return buffer data by its serial number of (1) the zero and (2) the first histogram buffer on two buffers //--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones) double BufferDataHistogram20(const int number,const int series_index); double BufferDataHistogram21(const int number,const int series_index); //--- Return buffer data by its serial number of (1) the zero and (2) the first zigzag buffer //--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones) double BufferDataZigZag0(const int number,const int series_index); double BufferDataZigZag1(const int number,const int series_index); //--- Return buffer data by its serial number of (1) the zero and (2) the first filling buffer //--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones) double BufferDataFilling0(const int number,const int series_index); double BufferDataFilling1(const int number,const int series_index); //--- Return buffer data by its serial number of (1) Open, (2) High, (3) Low and (4) Close bar buffers //--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones) double BufferDataBarsOpen(const int number,const int series_index); double BufferDataBarsHigh(const int number,const int series_index); double BufferDataBarsLow(const int number,const int series_index); double BufferDataBarsClose(const int number,const int series_index); //--- Return buffer data by its serial number of (1) Open, (2) High, (3) Low and (4) Close candle buffers //--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones) double BufferDataCandlesOpen(const int number,const int series_index); double BufferDataCandlesHigh(const int number,const int series_index); double BufferDataCandlesLow(const int number,const int series_index); double BufferDataCandlesClose(const int number,const int series_index); //--- Set buffer data by its serial number of (1) arrows, (2) line, (3) sections and (4) histogram from zero //--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones) void BufferSetDataArrow(const int number,const int series_index,const double value); void BufferSetDataLine(const int number,const int series_index,const double value); void BufferSetDataSection(const int number,const int series_index,const double value); void BufferSetDataHistogram(const int number,const int series_index,const double value); //--- Set data of the (1) zero, (2) first and (3) all histogram buffers on two buffers by a serial number of a created buffer //--- (0 - the very first created buffer with the HISTOGRAM2 drawing style, 1,2,N - subsequent ones) void BufferSetDataHistogram20(const int number,const int series_index,const double value); void BufferSetDataHistogram21(const int number,const int series_index,const double value); void BufferSetDataHistogram2(const int number,const int series_index,const double value0,const double value1); //--- Set data of the (1) zero, (2) first and (3) all zigzag buffers by a serial number of a created buffer //--- (0 - the very first created buffer with the ZIGZAG drawing style, 1,2,N - subsequent ones) void BufferSetDataZigZag0(const int number,const int series_index,const double value); void BufferSetDataZigZag1(const int number,const int series_index,const double value); void BufferSetDataZigZag(const int number,const int series_index,const double value0,const double value1); //--- Set data of the (1) zero, (2) first and (3) all filling buffers by a serial number of a created buffer //--- (0 - the very first created buffer with the FILLING drawing style, 1,2,N - subsequent ones) void BufferSetDataFilling0(const int number,const int series_index,const double value); void BufferSetDataFilling1(const int number,const int series_index,const double value); void BufferSetDataFilling(const int number,const int series_index,const double value0,const double value1); //--- Set data of the (1) Open, (2) High, (3) Low, (4) Close and (5) all bar buffers by a serial number of a created buffer //--- (0 - the very first created buffer with the BARS drawing style, 1,2,N - subsequent ones) void BufferSetDataBarsOpen(const int number,const int series_index,const double value); void BufferSetDataBarsHigh(const int number,const int series_index,const double value); void BufferSetDataBarsLow(const int number,const int series_index,const double value); void BufferSetDataBarsClose(const int number,const int series_index,const double value); void BufferSetDataBars(const int number,const int series_index,const double open,const double high,const double low,const double close); //--- Set data of the (1) Open, (2) High, (3) Low, (4) Close and (5) all candle buffers by a serial number of a created buffer //--- (0 - the very first created buffer with the CANDLES drawing style, 1,2,N - subsequent ones) void BufferSetDataCandlesOpen(const int number,const int series_index,const double value); void BufferSetDataCandlesHigh(const int number,const int series_index,const double value); void BufferSetDataCandlesLow(const int number,const int series_index,const double value); void BufferSetDataCandlesClose(const int number,const int series_index,const double value); void BufferSetDataCandles(const int number,const int series_index,const double open,const double high,const double low,const double close); //--- Return buffer color by its serial number of (1) arrows, (2) line, (3) sections, (4) histogram from zero //--- (5) histogram on two buffers, (6) zigzag, (7) filling, (8) bars and (9) candles //--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones) color BufferColorArrow(const int number,const int series_index); color BufferColorLine(const int number,const int series_index); color BufferColorSection(const int number,const int series_index); color BufferColorHistogram(const int number,const int series_index); color BufferColorHistogram2(const int number,const int series_index); color BufferColorZigZag(const int number,const int series_index); color BufferColorFilling(const int number,const int series_index); color BufferColorBars(const int number,const int series_index); color BufferColorCandles(const int number,const int series_index); //--- Return buffer color index by its serial number of (1) arrows, (2) line, (3) sections, (4) histogram from zero //--- (5) histogram on two buffers, (6) zigzag, (7) filling, (8) bars and (9) candles //--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones) int BufferColorIndexArrow(const int number,const int series_index); int BufferColorIndexLine(const int number,const int series_index); int BufferColorIndexSection(const int number,const int series_index); int BufferColorIndexHistogram(const int number,const int series_index); int BufferColorIndexHistogram2(const int number,const int series_index); int BufferColorIndexZigZag(const int number,const int series_index); int BufferColorIndexFilling(const int number,const int series_index); int BufferColorIndexBars(const int number,const int series_index); int BufferColorIndexCandles(const int number,const int series_index); //--- Set the color index to the color buffer by its serial number of (1) arrows, (2) line, (3) sections, (4) histogram from zero //--- (5) histogram on two buffers, (6) zigzag, (7) filling, (8) bars and (9) candles //--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones) void BufferSetColorIndexArrow(const int number,const int series_index,const int color_index); void BufferSetColorIndexLine(const int number,const int series_index,const int color_index); void BufferSetColorIndexSection(const int number,const int series_index,const int color_index); void BufferSetColorIndexHistogram(const int number,const int series_index,const int color_index); void BufferSetColorIndexHistogram2(const int number,const int series_index,const int color_index); void BufferSetColorIndexZigZag(const int number,const int series_index,const int color_index); void BufferSetColorIndexFilling(const int number,const int series_index,const int color_index); void BufferSetColorIndexBars(const int number,const int series_index,const int color_index); void BufferSetColorIndexCandles(const int number,const int series_index,const int color_index); //--- Set the following for the trading classes:
这些方法按其绘制样式,和具有相同绘制样式的缓冲区序列号,返回指定缓冲区数据:
//+------------------------------------------------------------------+ //| Return arrow buffer data by its serial number | //| (0 - the very first arrow buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ double CEngine::BufferDataArrow(const int number,const int series_index) { CBufferArrow *buff=this.m_buffers.GetBufferArrow(number); return(buff!=NULL ? buff.GetData(series_index) : EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Return line buffer data by its serial number | //| (0 - the very first line buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ double CEngine::BufferDataLine(const int number,const int series_index) { CBufferLine *buff=this.m_buffers.GetBufferLine(number); return(buff!=NULL ? buff.GetData(series_index) : EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Return section buffer data by its serial number | //| (0 - the very first sections buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ double CEngine::BufferDataSection(const int number,const int series_index) { CBufferSection *buff=this.m_buffers.GetBufferSection(number); return(buff!=NULL ? buff.GetData(series_index) : EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Return histogram buffer data from zero | //| by its serial number | //| (0 - the very first buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ double CEngine::BufferDataHistogram(const int number,const int series_index) { CBufferHistogram *buff=this.m_buffers.GetBufferHistogram(number); return(buff!=NULL ? buff.GetData(series_index) : EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Return histogram zero buffer data on two buffers | //| by its serial number | //| (0 - the very first buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ double CEngine::BufferDataHistogram20(const int number,const int series_index) { CBufferHistogram2 *buff=this.m_buffers.GetBufferHistogram2(number); return(buff!=NULL ? buff.GetData0(series_index) : EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Return histogram first buffer data on two buffers | //| by its serial number | //| (0 - the very first buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ double CEngine::BufferDataHistogram21(const int number,const int series_index) { CBufferHistogram2 *buff=this.m_buffers.GetBufferHistogram2(number); return(buff!=NULL ? buff.GetData1(series_index) : EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Return zigzag zero buffer data | //| by its serial number | //| (0 - the very first zigzag buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ double CEngine::BufferDataZigZag0(const int number,const int series_index) { CBufferZigZag *buff=this.m_buffers.GetBufferZigZag(number); return(buff!=NULL ? buff.GetData0(series_index) : EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Return zigzag first buffer data | //| by its serial number | //| (0 - the very first zigzag buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ double CEngine::BufferDataZigZag1(const int number,const int series_index) { CBufferZigZag *buff=this.m_buffers.GetBufferZigZag(number); return(buff!=NULL ? buff.GetData1(series_index) : EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Return filling zero buffer data | //| by its serial number | //| (0 - the very first filling buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ double CEngine::BufferDataFilling0(const int number,const int series_index) { CBufferFilling *buff=this.m_buffers.GetBufferFilling(number); return(buff!=NULL ? buff.GetData0(series_index) : EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Return filling first buffer data | //| by its serial number | //| (0 - the very first filling buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ double CEngine::BufferDataFilling1(const int number,const int series_index) { CBufferFilling *buff=this.m_buffers.GetBufferFilling(number); return(buff!=NULL ? buff.GetData1(series_index) : EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Return Open data of the bar buffer by its serial number | //| (0 - the very first bar buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ double CEngine::BufferDataBarsOpen(const int number,const int series_index) { CBufferBars *buff=this.m_buffers.GetBufferBars(number); return(buff!=NULL ? buff.GetDataOpen(series_index) : EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Return High data of the bar buffer by its serial number | //| (0 - the very first bar buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ double CEngine::BufferDataBarsHigh(const int number,const int series_index) { CBufferBars *buff=this.m_buffers.GetBufferBars(number); return(buff!=NULL ? buff.GetDataHigh(series_index) : EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Return Low data of the bar buffer by its serial number | //| (0 - the very first bar buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ double CEngine::BufferDataBarsLow(const int number,const int series_index) { CBufferBars *buff=this.m_buffers.GetBufferBars(number); return(buff!=NULL ? buff.GetDataLow(series_index) : EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Return Close data of the bar buffer by its serial number | //| (0 - the very first bar buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ double CEngine::BufferDataBarsClose(const int number,const int series_index) { CBufferBars *buff=this.m_buffers.GetBufferBars(number); return(buff!=NULL ? buff.GetDataClose(series_index) : EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Return Open data of the candle buffer by its serial number | //| (0 - the very first candle buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ double CEngine::BufferDataCandlesOpen(const int number,const int series_index) { CBufferCandles *buff=this.m_buffers.GetBufferCandles(number); return(buff!=NULL ? buff.GetDataOpen(series_index) : EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Return High data of the candle buffer by its serial number | //| (0 - the very first candle buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ double CEngine::BufferDataCandlesHigh(const int number,const int series_index) { CBufferCandles *buff=this.m_buffers.GetBufferCandles(number); return(buff!=NULL ? buff.GetDataHigh(series_index) : EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Return Low data of the candle buffer by its serial number | //| (0 - the very first candle buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ double CEngine::BufferDataCandlesLow(const int number,const int series_index) { CBufferCandles *buff=this.m_buffers.GetBufferCandles(number); return(buff!=NULL ? buff.GetDataLow(series_index) : EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Return Close data of the candle buffer by its serial number | //| (0 - the very first candle buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ double CEngine::BufferDataCandlesClose(const int number,const int series_index) { CBufferCandles *buff=this.m_buffers.GetBufferCandles(number); return(buff!=NULL ? buff.GetDataClose(series_index) : EMPTY_VALUE); } //+------------------------------------------------------------------+
所有方法都相同。 我们研究利用 Candles 绘图样式返回缓冲区对象的 Close 缓冲区值的方法。
该方法从所有已创建缓冲区中提取 Candles 样式的缓冲区序列号(我在上面已经详细研究了具有特定绘图样式的缓冲区编号的含义),以及应该从中获取 Close 缓冲区数据的时间序列索引。
调用缓冲区集合类的 GetBufferCandles() 方法,接收指向所需缓冲区的指针,并且,如果收到缓冲区,则按指定的时间序列索引从其 Close 缓冲区返回数据。 否则,返回 "Empty value"。
该方法与我刚刚讲述的方法恰恰相反。 它们按其绘制样式和序列号将数值设置到相应缓冲区对象的指定时间序列索引:
//+------------------------------------------------------------------+ //| Set arrow buffer data by its serial number | //| (0 - the very first arrow buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataArrow(const int number,const int series_index,const double value) { CBufferArrow *buff=this.m_buffers.GetBufferArrow(number); if(buff==NULL) return; buff.SetData(series_index,value); } //+------------------------------------------------------------------+ //| Set line buffer data by its serial number | //| (0 - the very first line buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataLine(const int number,const int series_index,const double value) { CBufferLine *buff=this.m_buffers.GetBufferLine(number); if(buff==NULL) return; buff.SetData(series_index,value); } //+------------------------------------------------------------------+ //| Set section buffer data by its serial number | //| (0 - the very first sections buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataSection(const int number,const int series_index,const double value) { CBufferSection *buff=this.m_buffers.GetBufferSection(number); if(buff==NULL) return; buff.SetData(series_index,value); } //+------------------------------------------------------------------+ //| Set histogram buffer data from zero | //| by its serial number | //| (0 - the very first buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataHistogram(const int number,const int series_index,const double value) { CBufferHistogram *buff=this.m_buffers.GetBufferHistogram(number); if(buff==NULL) return; buff.SetData(series_index,value); } //+------------------------------------------------------------------+ //| Set histogram zero buffer data on two buffers | //| by its serial number | //| (0 - the very first buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataHistogram20(const int number,const int series_index,const double value) { CBufferHistogram2 *buff=this.m_buffers.GetBufferHistogram2(number); if(buff==NULL) return; buff.SetData0(series_index,value); } //+------------------------------------------------------------------+ //| Set histogram first buffer data on two buffers | //| by its serial number | //| (0 - the very first buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataHistogram21(const int number,const int series_index,const double value) { CBufferHistogram2 *buff=this.m_buffers.GetBufferHistogram2(number); if(buff==NULL) return; buff.SetData1(series_index,value); } //+------------------------------------------------------------------+ //| Set data of all histogram buffers on two buffers | //| by its serial number | //| (0 - the very first buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataHistogram2(const int number,const int series_index,const double value0,const double value1) { CBufferHistogram2 *buff=this.m_buffers.GetBufferHistogram2(number); if(buff==NULL) return; buff.SetData0(series_index,value0); buff.SetData1(series_index,value1); } //+------------------------------------------------------------------+ //| Set zigzag zero buffer data | //| by its serial number | //| (0 - the very first zigzag buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataZigZag0(const int number,const int series_index,const double value) { CBufferZigZag *buff=this.m_buffers.GetBufferZigZag(number); if(buff==NULL) return; buff.SetData0(series_index,value); } //+------------------------------------------------------------------+ //| Set zigzag first buffer data | //| by its serial number | //| (0 - the very first zigzag buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataZigZag1(const int number,const int series_index,const double value) { CBufferZigZag *buff=this.m_buffers.GetBufferZigZag(number); if(buff==NULL) return; buff.SetData1(series_index,value); } //+------------------------------------------------------------------+ //| Set data of all zizag buffers | //| by its serial number | //| (0 - the very first zigzag buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataZigZag(const int number,const int series_index,const double value0,const double value1) { CBufferZigZag *buff=this.m_buffers.GetBufferZigZag(number); if(buff==NULL) return; buff.SetData0(series_index,value0); buff.SetData1(series_index,value1); } //+------------------------------------------------------------------+ //| Set filling zero buffer data | //| by its serial number | //| (0 - the very first filling buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataFilling0(const int number,const int series_index,const double value) { CBufferFilling *buff=this.m_buffers.GetBufferFilling(number); if(buff==NULL) return; buff.SetData0(series_index,value); } //+------------------------------------------------------------------+ //| Set filling first buffer data | //| by its serial number | //| (0 - the very first filling buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataFilling1(const int number,const int series_index,const double value) { CBufferFilling *buff=this.m_buffers.GetBufferFilling(number); if(buff==NULL) return; buff.SetData1(series_index,value); } //+------------------------------------------------------------------+ //| Set data of all filling buffers | //| by its serial number | //| (0 - the very first filling buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataFilling(const int number,const int series_index,const double value0,const double value1) { CBufferFilling *buff=this.m_buffers.GetBufferFilling(number); if(buff==NULL) return; buff.SetData0(series_index,value0); buff.SetData1(series_index,value1); } //+------------------------------------------------------------------+ //| Set buffer data of Open bars | //| by its serial number | //| (0 - the very first bar buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataBarsOpen(const int number,const int series_index,const double value) { CBufferBars *buff=this.m_buffers.GetBufferBars(number); if(buff==NULL) return; buff.SetDataOpen(series_index,value); } //+------------------------------------------------------------------+ //| Set buffer data of High bars | //| by its serial number | //| (0 - the very first bar buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataBarsHigh(const int number,const int series_index,const double value) { CBufferBars *buff=this.m_buffers.GetBufferBars(number); if(buff==NULL) return; buff.SetDataHigh(series_index,value); } //+------------------------------------------------------------------+ //| Set buffer data of Low bars | //| by its serial number | //| (0 - the very first bar buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataBarsLow(const int number,const int series_index,const double value) { CBufferBars *buff=this.m_buffers.GetBufferBars(number); if(buff==NULL) return; buff.SetDataLow(series_index,value); } //+------------------------------------------------------------------+ //| Set buffer data of Close bars | //| by its serial number | //| (0 - the very first bar buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataBarsClose(const int number,const int series_index,const double value) { CBufferBars *buff=this.m_buffers.GetBufferBars(number); if(buff==NULL) return; buff.SetDataClose(series_index,value); } //+------------------------------------------------------------------+ //| Set buffer data of Open candles | //| by its serial number | //| (0 - the very first candle buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataCandlesOpen(const int number,const int series_index,const double value) { CBufferCandles *buff=this.m_buffers.GetBufferCandles(number); if(buff==NULL) return; buff.SetDataOpen(series_index,value); } //+------------------------------------------------------------------+ //| Set buffer data of High candles | //| by its serial number | //| (0 - the very first candle buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataCandlesHigh(const int number,const int series_index,const double value) { CBufferCandles *buff=this.m_buffers.GetBufferCandles(number); if(buff==NULL) return; buff.SetDataHigh(series_index,value); } //+------------------------------------------------------------------+ //| Set buffer data of Low candles | //| by its serial number | //| (0 - the very first candle buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataCandlesLow(const int number,const int series_index,const double value) { CBufferCandles *buff=this.m_buffers.GetBufferCandles(number); if(buff==NULL) return; buff.SetDataLow(series_index,value); } //+------------------------------------------------------------------+ //| Set buffer data of Close candles | //| by its serial number | //| (0 - the very first candle buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataCandlesClose(const int number,const int series_index,const double value) { CBufferCandles *buff=this.m_buffers.GetBufferCandles(number); if(buff==NULL) return; buff.SetDataClose(series_index,value); } //+------------------------------------------------------------------+
所有方法都相同。 我们来研究按 Candles 绘图样式及其序列号得到缓冲区对象的 Close 缓冲区,并按时间序列索引为其设置数值的方法。
调用缓冲区集合类的 GetBufferCandles() 方法,按其序列号获取 Candles 绘图样式的缓冲区对象。
如果获取对象失败,则退出方法。 按时间序列索引获取所需缓冲区对象的 Close 缓冲区,并将传递给方法的数值设置其中。
还有另外两个单独的方法,可按指定时间序列索引同时为 Bars 和 Candles 缓冲区对象的所有缓冲区设置 OHLC 值:
//+------------------------------------------------------------------+ //| Set data of all bar buffers | //| by its serial number | //| (0 - the very first bar buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataBars(const int number,const int series_index,const double open,const double high,const double low,const double close) { CBufferBars *buff=this.m_buffers.GetBufferBars(number); if(buff==NULL) return; buff.SetDataOpen(series_index,open); buff.SetDataHigh(series_index,high); buff.SetDataLow(series_index,low); buff.SetDataClose(series_index,close); } //+------------------------------------------------------------------+ //| Set data of all candle buffers | //| by its serial number | //| (0 - the very first candle buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataCandles(const int number,const int series_index,const double open,const double high,const double low,const double close) { CBufferCandles *buff=this.m_buffers.GetBufferCandles(number); if(buff==NULL) return; buff.SetDataOpen(series_index,open); buff.SetDataHigh(series_index,high); buff.SetDataLow(series_index,low); buff.SetDataClose(series_index,close); } //+------------------------------------------------------------------+
这里的一切都与上述相同。 不过,所有四个缓冲区对象缓冲区的所有数值均会传递给方法,并由其设置。
这些方法返回按绘制样式和序列号得到的特定缓冲区对象的颜色缓冲区时间序列索引指定的颜色:
//+------------------------------------------------------------------+ //| Return the arrow buffer color by its serial number | //| (0 - the very first arrow buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ color CEngine::BufferColorArrow(const int number,const int series_index) { CBufferArrow *buff=this.m_buffers.GetBufferArrow(number); return(buff!=NULL ? buff.GetColorBufferValueColor(series_index) : clrNONE); } //+------------------------------------------------------------------+ //| Return the line buffer color by its serial number | //| (0 - the very first candle line, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ color CEngine::BufferColorLine(const int number,const int series_index) { CBufferLine *buff=this.m_buffers.GetBufferLine(number); return(buff!=NULL ? buff.GetColorBufferValueColor(series_index) : clrNONE); } //+------------------------------------------------------------------+ //| Return the section buffer color by its serial number | //| (0 - the very first sections buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ color CEngine::BufferColorSection(const int number,const int series_index) { CBufferSection *buff=this.m_buffers.GetBufferSection(number); return(buff!=NULL ? buff.GetColorBufferValueColor(series_index) : clrNONE); } //+------------------------------------------------------------------+ //| Return histogram buffer color from zero | //| by its serial number | //| (0 - the very first buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ color CEngine::BufferColorHistogram(const int number,const int series_index) { CBufferHistogram *buff=this.m_buffers.GetBufferHistogram(number); return(buff!=NULL ? buff.GetColorBufferValueColor(series_index) : clrNONE); } //+------------------------------------------------------------------+ //| Return histogram buffer color on two buffers | //| by its serial number | //| (0 - the very first buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ color CEngine::BufferColorHistogram2(const int number,const int series_index) { CBufferHistogram2 *buff=this.m_buffers.GetBufferHistogram2(number); return(buff!=NULL ? buff.GetColorBufferValueColor(series_index) : clrNONE); } //+------------------------------------------------------------------+ //| Return the zigzag buffer color by its serial number | //| (0 - the very first zigzag buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ color CEngine::BufferColorZigZag(const int number,const int series_index) { CBufferZigZag *buff=this.m_buffers.GetBufferZigZag(number); return(buff!=NULL ? buff.GetColorBufferValueColor(series_index) : clrNONE); } //+------------------------------------------------------------------+ //| Return the filling buffer color by its serial number | //| (0 - the very first filling buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ color CEngine::BufferColorFilling(const int number,const int series_index) { CBufferFilling *buff=this.m_buffers.GetBufferFilling(number); return(buff!=NULL ? buff.GetColorBufferValueColor(series_index) : clrNONE); } //+------------------------------------------------------------------+ //| Return the bar buffer color by its serial number | //| (0 - the very first bar buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ color CEngine::BufferColorBars(const int number,const int series_index) { CBufferBars *buff=this.m_buffers.GetBufferBars(number); return(buff!=NULL ? buff.GetColorBufferValueColor(series_index) : clrNONE); } //+------------------------------------------------------------------+ //| Return the candle buffer color by its serial number | //| (0 - the very first candle buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ color CEngine::BufferColorCandles(const int number,const int series_index) { CBufferCandles *buff=this.m_buffers.GetBufferCandles(number); return(buff!=NULL ? buff.GetColorBufferValueColor(series_index) : clrNONE); } //+------------------------------------------------------------------+
此处的一切都相似:按其编号获取所需的缓冲区对象,如果获取该对象成功, 按指定时间序列返回其颜色缓冲区中设置的颜色索引。 否则,返回 "Color not set"。
由于颜色索引值(而非颜色值)实际上是在颜色缓冲区中设置的,因此我们有相应的方法按指定时间序列索引从其颜色缓冲区返回特定缓冲区对象的颜色索引:
//+------------------------------------------------------------------+ //| Return the arrow buffer color index by its serial number | //| (0 - the very first arrow buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ int CEngine::BufferColorIndexArrow(const int number,const int series_index) { CBufferArrow *buff=this.m_buffers.GetBufferArrow(number); return(buff!=NULL ? buff.GetColorBufferValueIndex(series_index) : WRONG_VALUE); } //+------------------------------------------------------------------+ //| Return the line buffer color index by its serial number | //| (0 - the very first candle line, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ int CEngine::BufferColorIndexLine(const int number,const int series_index) { CBufferLine *buff=this.m_buffers.GetBufferLine(number); return(buff!=NULL ? buff.GetColorBufferValueIndex(series_index) : WRONG_VALUE); } //+------------------------------------------------------------------+ //| Return the section buffer color index by its serial number | //| (0 - the very first sections buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ int CEngine::BufferColorIndexSection(const int number,const int series_index) { CBufferSection *buff=this.m_buffers.GetBufferSection(number); return(buff!=NULL ? buff.GetColorBufferValueIndex(series_index) : WRONG_VALUE); } //+------------------------------------------------------------------+ //| Return histogram buffer color index from zero | //| by its serial number | //| (0 - the very first buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ int CEngine::BufferColorIndexHistogram(const int number,const int series_index) { CBufferHistogram *buff=this.m_buffers.GetBufferHistogram(number); return(buff!=NULL ? buff.GetColorBufferValueIndex(series_index) : WRONG_VALUE); } //+------------------------------------------------------------------+ //| Return histogram buffer color index on two buffers | //| by its serial number | //| (0 - the very first buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ int CEngine::BufferColorIndexHistogram2(const int number,const int series_index) { CBufferHistogram2 *buff=this.m_buffers.GetBufferHistogram2(number); return(buff!=NULL ? buff.GetColorBufferValueIndex(series_index) : WRONG_VALUE); } //+------------------------------------------------------------------+ //| Return the zigzag buffer color index by its serial number | //| (0 - the very first zigzag buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ int CEngine::BufferColorIndexZigZag(const int number,const int series_index) { CBufferZigZag *buff=this.m_buffers.GetBufferZigZag(number); return(buff!=NULL ? buff.GetColorBufferValueIndex(series_index) : WRONG_VALUE); } //+------------------------------------------------------------------+ //| Return the filling buffer color index by its serial number | //| (0 - the very first filling buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ int CEngine::BufferColorIndexFilling(const int number,const int series_index) { CBufferFilling *buff=this.m_buffers.GetBufferFilling(number); return(buff!=NULL ? buff.GetColorBufferValueIndex(series_index) : WRONG_VALUE); } //+------------------------------------------------------------------+ //| Return the bar buffer color index by its serial number | //| (0 - the very first bar buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ int CEngine::BufferColorIndexBars(const int number,const int series_index) { CBufferBars *buff=this.m_buffers.GetBufferBars(number); return(buff!=NULL ? buff.GetColorBufferValueIndex(series_index) : WRONG_VALUE); } //+------------------------------------------------------------------+ //| Return the candle buffer color index by its serial number | //| (0 - the very first candle buffer, 1,2,N - subsequent ones) | //+------------------------------------------------------------------+ int CEngine::BufferColorIndexCandles(const int number,const int series_index) { CBufferCandles *buff=this.m_buffers.GetBufferCandles(number); return(buff!=NULL ? buff.GetColorBufferValueIndex(series_index) : WRONG_VALUE); } //+------------------------------------------------------------------+
除了返回颜色索引外,此处的所有内容均与返回颜色的方法相同。
这些都是测试指标缓冲区集合类所需的 CEngine 类的所有改进。
测试利用缓冲区集合创建指标的能力
为了测试缓冲区集合类,我们将借用上一篇文章中的指标,并将其保存到新文件夹 \MQL5\Indicators\TestDoEasy\Part44\,命名为 TestDoEasyPart44.mq5 。
头部整体如下所示:
//+------------------------------------------------------------------+ //| TestDoEasyPart44.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_chart_window #property indicator_buffers 28 #property indicator_plots 10 //--- classes //--- enums //--- defines //--- structures //--- input variables /*sinput*/ ENUM_SYMBOLS_MODE InpModeUsedSymbols= SYMBOLS_MODE_CURRENT; // Mode of used symbols list /*sinput*/ string InpUsedSymbols = "EURUSD,AUDUSD,EURAUD,EURGBP,EURCAD,EURJPY,EURUSD,GBPUSD,NZDUSD,USDCAD,USDJPY"; // List of used symbols (comma - separator) /*sinput*/ ENUM_TIMEFRAMES_MODE InpModeUsedTFs = TIMEFRAMES_MODE_CURRENT; // Mode of used timeframes list /*sinput*/ string InpUsedTFs = "M1,M5,M15,M30,H1,H4,D1,W1,MN1"; // List of used timeframes (comma - separator) sinput ENUM_INPUT_YES_NO InpDrawArrow = INPUT_YES; // Draw Arrow sinput ENUM_INPUT_YES_NO InpDrawLine = INPUT_NO; // Draw Line sinput ENUM_INPUT_YES_NO InpDrawSection = INPUT_NO; // Draw Section sinput ENUM_INPUT_YES_NO InpDrawHistogram = INPUT_NO; // Draw Histogram sinput ENUM_INPUT_YES_NO InpDrawHistogram2 = INPUT_NO; // Draw Histogram2 sinput ENUM_INPUT_YES_NO InpDrawZigZag = INPUT_NO; // Draw ZigZag sinput ENUM_INPUT_YES_NO InpDrawFilling = INPUT_NO; // Draw Filling sinput ENUM_INPUT_YES_NO InpDrawBars = INPUT_NO; // Draw Bars sinput ENUM_INPUT_YES_NO InpDrawCandles = INPUT_YES; // Draw Candles sinput bool InpUseSounds = true; // Use sounds //--- indicator buffers CArrayObj *list_buffers; // Pointer to the buffer object list //--- global variables 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 //+------------------------------------------------------------------+
从 “includes” 模块中删除所有缓冲区对象文件 — 它们已包含在函数库中:
//--- includes #include <DoEasy\Engine.mqh> #include <DoEasy\Objects\Indicators\BufferArrow.mqh> // 1 construction buffer + 1 color buffer #include <DoEasy\Objects\Indicators\BufferLine.mqh> // 1 construction buffer + 1 color buffer #include <DoEasy\Objects\Indicators\BufferSection.mqh> // 1 construction buffer + 1 color buffer #include <DoEasy\Objects\Indicators\BufferHistogram.mqh> // 1 construction buffer + 1 color buffer #include <DoEasy\Objects\Indicators\BufferHistogram2.mqh> // 2 construction buffers + 1 color buffer #include <DoEasy\Objects\Indicators\BufferZigZag.mqh> // 2 construction buffers + 1 color buffer #include <DoEasy\Objects\Indicators\BufferFilling.mqh> // 2 construction buffers + 1 color buffer #include <DoEasy\Objects\Indicators\BufferBars.mqh> // 4 construction buffer + 1 color buffer #include <DoEasy\Objects\Indicators\BufferCandles.mqh> // 4 construction buffer + 1 color buffer //--- In total: 18 construction buffers + 9 color buffers: 27 indicator buffers, of which 9 are drawn ones //--- properties
为编译器设置绘制缓冲区和指标缓冲区的数量:
#property indicator_buffers 28 #property indicator_plots 10
如果指标中预期要用到多个缓冲区,我们可以在初始阶段简单地设置任意值,而不必“掐算”。 首次启动指标时,我们会收到警报,其中包含绘制缓冲区和指标缓冲区的有效数量,这是为了防止我们错误地指定它们的数量。
该函数库意味着按其绘制样式和编号(按创建顺序)访问已创建的指标缓冲区。 从程序直接访问缓冲区属性尚未实现。 到目前为止,我们只能访问缓冲区数组。 如今,我会绕过此限制,而是从集合中接收缓冲区对象列表,并直接从列表中访问缓冲区(任何对象集合都具有这种功能)。 故此,我们将指向 CObject 对象的动态指针数组用作“指标缓冲区”。
在 OnInit() 响应程序中,准备测试所需的缓冲区,并检查是否可以通过两种不同的方式访问它们:
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Initialize DoEasy library OnInitDoEasy(); //--- Set indicator global variables prefix=engine.Name()+"_"; //--- Get the index of the maximum used timeframe in the array, //--- calculate the number of bars of the current period fitting in the maximum used period //--- Use the obtained value if it exceeds 2, otherwise use 2 int index=ArrayMaximum(ArrayUsedTimeframes); int num_bars=NumberBarsInTimeframe(ArrayUsedTimeframes[index]); min_bars=(index>WRONG_VALUE ? (num_bars>2 ? num_bars : 2) : 2); //--- Check and remove remaining indicator graphical objects if(IsPresentObectByPrefix(prefix)) ObjectsDeleteAll(0,prefix); //--- Create the button panel //--- Check playing a standard sound using macro substitutions engine.PlaySoundByDescription(SND_OK); //--- Wait for 600 milliseconds engine.Pause(600); engine.PlaySoundByDescription(SND_NEWS); //--- indicator buffers mapping //--- Create all the necessary buffer objects engine.BufferCreateArrow(); engine.BufferCreateLine(); engine.BufferCreateSection(); engine.BufferCreateHistogram(); engine.BufferCreateHistogram2(); engine.BufferCreateZigZag(); engine.BufferCreateFilling(); engine.BufferCreateBars(); engine.BufferCreateCandles(); engine.BufferCreateArrow(); //--- Check the number of buffers specified in the 'properties' block if(engine.BufferPlotsTotal()!=indicator_plots) Alert(TextByLanguage("Внимание! Значение \"indicator_plots\" должно быть ","Attention! Value of \"indicator_plots\" should be "),engine.BufferPlotsTotal()); if(engine.BuffersTotal()!=indicator_buffers) Alert(TextByLanguage("Внимание! Значение \"indicator_buffers\" должно быть ","Attention! Value of \"indicator_buffers\" should be "),engine.BuffersTotal()); //--- Create color array color array_colors[]={clrDodgerBlue,clrRed,clrGray}; //--- Get the pointer to the collection list of buffer objects and //--- set non-default color values for buffers in a loop according to the list list_buffers=engine.GetListBuffers(); for(int i=0;i<list_buffers.Total();i++) { CBuffer *buff=list_buffers.At(i); buff.SetColors(array_colors); //--- Print data on the next buffer buff.Print(); } //--- Set the line width for ZigZag (the sixth drawn buffer) //--- It has the index of 5 considering that the starting point is zero CBuffer *buff_zz=engine.GetBufferByPlot(5); if(buff_zz!=NULL) { buff_zz.SetWidth(2); } //--- Get the second arrow buffer (created last). //--- The first arrow buffer has the number of 0, while the second one has the number of 1 //--- Set the arrow size of 2 and the code of 161 CBuffer *buff=engine.GetBufferArrow(1); if(buff!=NULL) { buff.SetWidth(2); buff.SetArrowCode(161); } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
现在,创建缓冲区变得极其简单 — 调用函数库里旨在创建一种或另一种类型的指标缓冲区的方法。 该函数库将接管所有数组函数,并且我们可以在创建缓冲区后更改其属性。 我们来立即检查指标头中 #property 里指定的缓冲区数量。 如果我们在指定缓冲区数量时犯错,则会显示相应的警告。 在开发指标时,这种检查很方便。 之后,可以将其从代码中删除。
我们用两种方法来测试访问缓冲区:
首先,利用 GetBufferByPlot() 方法按其绘制缓冲区索引访问之字折线缓冲区,其中将指定绘制缓冲区索引(对于之字折线此处为索引 5),
然后得以访问末尾创建的最后一个箭头缓冲区。 这是第二个箭头缓冲区。 我们利用 GetBufferArrow() 方法访问它。 在该方法中,指定所需箭头缓冲区的序列号(由于计数从零开始,此处为 1)
OnCalculate() 响应程序几乎保持不变,除了使用蜡烛缓冲区对象方法在蜡烛缓冲区和柱线缓冲区里设置数据(将数据依次写入开盘价、最高价、最低价和收盘价)。 此外,一次性将所有 OHLC 值写入柱线缓冲区数组。 因此,我们能够检查所有已创建的操控缓冲区对象的方法操作:
//--- Calculate the indicator for(int i=limit; i>WRONG_VALUE && !IsStopped(); i--) { //--- In a loop by the number of buffers in the list for(int j=0;j<total;j++) { //--- get the next buffer and CBuffer *buff=list_buffers.At(j); //--- clear its current data buff.ClearData(0); //--- If drawing is not used, move on to the next one if(!IsUse(buff.Status())) continue; //--- Depending on the number of buffers, fill them with price data //--- one buffer if(buff.BuffersTotal()==1) buff.SetBufferValue(0,i,close[i]); //--- two buffers - the first one is to store the bar open price, while the second one is to store the bar close price else if(buff.BuffersTotal()==2) { buff.SetBufferValue(0,i,open[i]); buff.SetBufferValue(1,i,close[i]); } //--- four buffers - each buffer is to store OHLC bar prices else if(buff.BuffersTotal()==4) { //--- If this is the candle buffer if(buff.Status()==BUFFER_STATUS_CANDLES) { //--- create the pointer to the candle buffer object by assigning the pointer to the abstract buffer object to it //--- and write the appropriate timeseries data to the "candle" object buffers CBufferCandles *candle=buff; candle.SetDataOpen(i,open[i]); candle.SetDataHigh(i,high[i]); candle.SetDataLow(i,low[i]); candle.SetDataClose(i,close[i]); } //--- If this is the bar buffer, use access to the first (and only) created bar buffer object //--- and write the appropriate timeseries data to the "bars" object buffers in one go else { engine.BufferSetDataBars(0,i,open[i],high[i],low[i],close[i]); } } //--- Set the buffer color depending on the candle direction if(open[i]<close[i]) buff.SetBufferColorIndex(i,0); else if(open[i]>close[i]) buff.SetBufferColorIndex(i,1); else buff.SetBufferColorIndex(i,2); } } //--- return value of prev_calculated for next call
完整的指标代码在下面的文件中提供。
编译指标并在品种图表上启动它。 在设定中设置单一显示箭头缓冲区。 该缓冲区应在图表上显示为点状。 但是,我们稍后还要创建第二个箭头缓冲区。 我们已经访问了 OnInit() 中的第二个箭头缓冲区,从而可更改其代码和图标大小:
//--- Get the second arrow buffer (created last). //--- The first arrow buffer has the number of 0, while the second one has the number of 1 //--- Set the arrow size of 2 and the code of 161 CBuffer *buff=engine.GetBufferArrow(1); if(buff!=NULL) { buff.SetWidth(2); buff.SetArrowCode(161); }
如果我们按编号访问某个缓冲区类型来获取对象,则在图表上应显示两个箭头缓冲区 — 第一个以点状显示,而第二个以大小为 2 的圆圈显示。
我们调用按其 Plot 索引返回缓冲区对象的方法得到之字折线缓冲区,并设置之字折线线宽。 连接之字折线的显示,并确保其线宽与 OnInit() 中的线宽相对应:
//--- Set the line width for ZigZag (the sixth drawn buffer) //--- It has the index of 5 considering that the starting point is zero CBuffer *buff_zz=engine.GetBufferByPlot(5); if(buff_zz!=NULL) { buff_zz.SetWidth(2); }
最后,我们来看一下柱线和蜡烛的显示方式。 如果将价格值写入缓冲区数组的方法操作正常,则柱线图和蜡烛图应正确显示在图表上。
我们来查看一下:
正如我们所见,一切都按预期运行。
下一步是什么?
在下一篇文章里,我们将继续开发指标缓冲区集合类,确保在多品种和多周期模式下指标的操作。
以下附件是函数库当前版本的所有文件,以及测试 EA 文件,供您测试和下载。
将您的问题、评论和建议留在评论中。
请记住,此处我已经为 MetaTrader 5 开发了 MQL5 测试指标。
附件仅适用于 MetaTrader 5。 当前函数库版本尚未在 MetaTrader 4 里进行测试。
创建并测试指标缓冲区集合后,我将尝试在 MetaTrader 4 中实现一些 MQL5 功能。
返回内容目录
该系列中的先前文章:
DoEasy 函数库中的时间序列(第三十五部分):柱线对象和品种时间序列列表
DoEasy 函数库中的时间序列(第三十六部分):所有用到的品种周期的时间序列对象
DoEasy 函数库中的时间序列(第三十七部分):时间序列集合 - 按品种和周期的时间序列数据库
DoEasy 函数库中的时间序列(第三十八部分):时间序列集合 - 实时更新以及从程序访问数据
DoEasy 函数库中的时间序列(第三十九部分):基于函数库的指标 - 准备数据和时间序列事件
DoEasy 函数库中的时间序列(第四十部分):基于函数库的指标 - 实时刷新数据
DoEasy 函数库中的时间序列(第四十一部分):多品种多周期指标样品
DoEasy 函数库中的时间序列(第四十二部分):抽象指标缓冲区对象类
DoEasy 函数库中的时间序列(第四十三部分):指标缓冲区对象类