内容
- 概述
- CBuffer 抽象缓冲区类
- 检查指标中缓冲区对象的创建
- 下一步是什么?
概述
在上一篇文章中,我用 DoEasy 函数库的时间序列对象开发了一个指标操作示例。 为了存储和访问缓冲区数据,我创建了拥有所有必要数据的缓冲区结构,从而可正确识别属于图表的品种和时间周期的缓冲区,并以烛条形式标示和绘制指标线。 不过,为每个指标所需的线型相应字段创建结构很不方便,且不切实际。 利用指标缓冲区对象类则要便捷的多,这令我们能够按样式和绘制方法轻松创建任何类型的缓冲区。
我将开始在本文里开发这种工具。
对象结构由基本的指标缓冲区类(所谓的“抽象缓冲区对象”)组成,该类包含所有指标缓冲区固有的所有常规属性,无论其绘图类型如何。 包含特定缓冲区类型纯数据的缓冲区对象类即是从该抽象缓冲区类继承而来。 这些衍生类将精确定义图形类型,并为该指标缓冲区类型提供独有的独立属性。
在本文中,我们将编写抽象指标缓冲区的对象类。 该对象将包含 ENUM_PLOT_PROPERTY_INTEGER , ENUM_PLOT_PROPERTY_DOUBLE 和 ENUM_PLOT_PROPERTY_STRING 枚举,以及一些其他属性:
- 对于指定缓冲区类型:
- 数据缓冲区,
- 计算用缓冲区,
- 缓冲区状态(定义指标线的图形类型)
衍生对象将按缓冲区状态排序 — 为每个状态创建一个抽象缓冲区类的单独衍生类; - 缓冲区操作品种:
- 当前,
- 严格指定;
- 缓冲区时间帧:
- 当前,
- 严格指定;
- 绘制缓冲区的序列号是显示在终端的 DataWindow 中的缓冲区编号(这些编号与分配给该缓冲区的 double 数组的索引并不一致);
- The buffer usage flag — 该指标按照选中/未选中的标志来启用/禁用数据窗口中缓冲区指标线和数值的显示;
- The index of the base data buffer — 绘制缓冲区线的所有数组中,辅助数据所在的第一个数组索引;
- The amount of buffer data — 绘制缓冲区图形的数组实际辅助数据数量。 有必要计算彩色缓冲区索引,以及下一个可分配给基础数据缓冲区的索引;
- 颜色缓冲区索引辅助数据,其数组索引作为颜色缓冲区;
- The free array index for assigning as the next indicator buffer — 可分配给下一个缓冲区对象的数据缓冲区索引。
结果就是,在创建了操控指标缓冲区的所有必要类之后,我们就可以简单地“告诉”我们的程序“创建该类型的缓冲区”,且函数库将创建该缓冲区,以及所有后续的缓冲区,而无需单独声明数组,分配其属性,并与指标缓冲区绑定。 我们只要简单地获取缓冲区列表,就可以从中访问任何先前创建的指标缓冲区和缓冲区数据。
由于 MQL5 提供了创建两种相似的绘图样式的能力 — 单色和彩色,因此该函数库创建的所有缓冲区都会着色。 如果您想要创建单色线,则为缓冲区显示的所有数据设置单一颜色。
当用不同颜色为柱线着色时,将用指定的颜色编号显示每根柱线。
CBuffer 抽象缓冲区类
对于类操作,我们需要描述缓冲区属性的文本消息。
打开 \MQL5\Include\DoEasy\Datas.mqh,并将新消息索引加入其内:
MSG_LIB_TEXT_WAIT, // Wait MSG_LIB_TEXT_END, // End MSG_LIB_TEXT_PERIOD_CURRENT, // Current chart period
...
MSG_LIB_TEXT_TS_TEXT_ATTEMPT, // Attempt: MSG_LIB_TEXT_TS_TEXT_WAIT_FOR_SYNC, // Waiting for data synchronization ... //--- CBuffer MSG_LIB_TEXT_BUFFER_TEXT_INDEX_BASE, // Base data buffer index MSG_LIB_TEXT_BUFFER_TEXT_INDEX_PLOT, // Plotted buffer serial number MSG_LIB_TEXT_BUFFER_TEXT_INDEX_COLOR, // Color buffer index MSG_LIB_TEXT_BUFFER_TEXT_NUM_DATAS, // Number of data buffers MSG_LIB_TEXT_BUFFER_TEXT_INDEX_NEXT, // Index of the free array to be assigned as the next indicator buffer MSG_LIB_TEXT_BUFFER_TEXT_TIMEFRAME, // Buffer (timeframe) data period MSG_LIB_TEXT_BUFFER_TEXT_STATUS, // Buffer status MSG_LIB_TEXT_BUFFER_TEXT_TYPE, // Buffer type MSG_LIB_TEXT_BUFFER_TEXT_ACTIVE, // Active MSG_LIB_TEXT_BUFFER_TEXT_ARROW_CODE, // Arrow code MSG_LIB_TEXT_BUFFER_TEXT_ARROW_SHIFT, // The vertical shift of the arrows MSG_LIB_TEXT_BUFFER_TEXT_DRAW_BEGIN, // The number of initial bars that are not drawn and values in DataWindow MSG_LIB_TEXT_BUFFER_TEXT_DRAW_TYPE, // Graphical construction type MSG_LIB_TEXT_BUFFER_TEXT_SHOW_DATA, // Display construction values in DataWindow MSG_LIB_TEXT_BUFFER_TEXT_SHIFT, // Indicator graphical construction shift by time axis in bars MSG_LIB_TEXT_BUFFER_TEXT_LINE_STYLE, // Line style MSG_LIB_TEXT_BUFFER_TEXT_LINE_WIDTH, // Line width MSG_LIB_TEXT_BUFFER_TEXT_COLOR_NUM, // Number of colors MSG_LIB_TEXT_BUFFER_TEXT_COLOR, // Drawing color MSG_LIB_TEXT_BUFFER_TEXT_EMPTY_VALUE, // Empty value for plotting where nothing will be drawn MSG_LIB_TEXT_BUFFER_TEXT_SYMBOL, // Buffer symbol MSG_LIB_TEXT_BUFFER_TEXT_LABEL, // Name of the graphical indicator series displayed in DataWindow MSG_LIB_TEXT_BUFFER_TEXT_STATUS_NAME, // Indicator buffer with graphical construction type MSG_LIB_TEXT_BUFFER_TEXT_STATUS_NONE, // No drawing MSG_LIB_TEXT_BUFFER_TEXT_STATUS_FILLING, // Color filling between two levels MSG_LIB_TEXT_BUFFER_TEXT_STATUS_LINE, // Line MSG_LIB_TEXT_BUFFER_TEXT_STATUS_HISTOGRAM, // Histogram from the zero line MSG_LIB_TEXT_BUFFER_TEXT_STATUS_ARROW, // Drawing with arrows MSG_LIB_TEXT_BUFFER_TEXT_STATUS_SECTION, // Segments MSG_LIB_TEXT_BUFFER_TEXT_STATUS_HISTOGRAM2, // Histogram on two indicator buffers MSG_LIB_TEXT_BUFFER_TEXT_STATUS_ZIGZAG, // Zigzag MSG_LIB_TEXT_BUFFER_TEXT_STATUS_BARS, // Display as bars MSG_LIB_TEXT_BUFFER_TEXT_STATUS_CANDLES, // Display as candles MSG_LIB_TEXT_BUFFER_TEXT_TYPE_CALCULATE, // Calculated buffer MSG_LIB_TEXT_BUFFER_TEXT_TYPE_DATA, // Colored data buffer MSG_LIB_TEXT_BUFFER_TEXT_STYLE_SOLID, // Solid line MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DASH, // Dashed line MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DOT, // Dotted line MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DASHDOT, // Dot-dash line MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DASHDOTDOT, // Dash - two dots }; //+------------------------------------------------------------------+
及与新添加的索引相对应的消息文本:
{"Ожидание","Wait"}, {"Окончание","End"}, {"Текущий период графика","Current chart period"},
...
{"Попытка: ","Attempt: "}, {"Ожидание синхронизации данных ...","Waiting for data synchronization ..."}, {"Индекс базового буфера данных","Index of Base data buffer"}, {"Порядковый номер рисуемого буфера","Plot buffer sequence number"}, {"Индекс буфера цвета","Color buffer index"}, {"Количество буферов данных","Number of data buffers"}, {"Индекс массива для назначения следующим индикаторным буфером","Array index for assignment as the next indicator buffer"}, {"Период данных буфера (таймфрейм)","Buffer data Period (Timeframe)"}, {"Статус буфера","Buffer status"}, {"Тип буфера","Buffer type"}, {"Активен","Active"}, {"Код стрелки","Arrow code"}, {"Смещение стрелок по вертикали","Vertical shift of arrows"}, {"Количество начальных баров без отрисовки и значений в DataWindow","Number of initial bars without drawing and values in the DataWindow"}, {"Тип графического построения","Type of graphical construction"}, {"Отображение значений построения в окне DataWindow","Display construction values in the DataWindow"}, {"Сдвиг графического построения индикатора по оси времени в барах","Shift of indicator plotting along the time axis in bars"}, {"Стиль линии отрисовки","Drawing line style "}, {"Толщина линии отрисовки","The thickness of the drawing line"}, {"Количество цветов","The number of colors"}, {"Цвет отрисовки","The index of a buffer containing the drawing color"}, {"Пустое значение для построения, для которого нет отрисовки","An empty value for plotting, for which there is no drawing"}, {"Символ буфера","Buffer Symbol"}, {"Имя индикаторной графической серии, отображаемое в окне DataWindow","The name of the indicator graphical series to display in the DataWindow"}, {"Индикаторный буфер с типом графического построения","Indicator buffer with graphic plot type"}, {"Нет отрисовки","No drawing"}, {"Цветовая заливка между двумя уровнями","Color fill between the two levels"}, {"Линия","Line"}, {"Гистограмма от нулевой линии","Histogram from the zero line"}, {"Отрисовка стрелками","Drawing arrows"}, {"Отрезки","Section"}, {"Гистограмма на двух индикаторных буферах","Histogram of the two indicator buffers"}, {"Зигзаг","Zigzag"}, {"Отображение в виде баров","Display as a sequence of bars"}, {"Отображение в виде свечей","Display as a sequence of candlesticks"}, {"Расчётный буфер","Calculated buffer"}, {"Цветной буфер данных","Colored Data buffer"}, {"Сплошная линия","Solid line"}, {"Прерывистая линия","Broken line"}, {"Пунктирная линия","Dotted line"}, {"Штрих-пунктирная линия","Dash-dot line"}, {"Штрих - две точки","Dash - two points"}, }; //+---------------------------------------------------------------------+
创建存储在对象集合中的函数库对象时,会将相应集合的 ID 分配给每个对象。 缓冲区对象也是如此:创建指标缓冲区集合 ID,并将其分配给抽象缓冲区对象。 相应地,从抽象缓冲区继承的每个对象都将拥有一个 ID,代表其所属的缓冲区集合。
为了创建抽象缓冲区对象,我们需要定义和描述其所有属性。 使用这些属性,我们总是能够在缓冲区集合中找到所需的缓冲区对象,以便进一步的操作。
打开 \MQL5\Include\DoEasy\Defines.mqh,并添加指标缓冲区集合 ID:
//--- Collection list IDs #define COLLECTION_HISTORY_ID (0x777A) // Historical collection list ID #define COLLECTION_MARKET_ID (0x777B) // Market collection list ID #define COLLECTION_EVENTS_ID (0x777C) // Event collection list ID #define COLLECTION_ACCOUNT_ID (0x777D) // Account collection list ID #define COLLECTION_SYMBOLS_ID (0x777E) // Symbol collection list ID #define COLLECTION_SERIES_ID (0x777F) // Timeseries collection list ID #define COLLECTION_BUFFERS_ID (0x7780) // Indicator buffer collection list ID //--- Data parameters for file operations
在文件的末尾,添加缓冲区对象状态的枚举及其类型,定义整数型、实数型和字符串型对象属性,并在其集合列表中写入缓冲区对象排序的可能条件:
//+------------------------------------------------------------------+ //| Data for working with indicator buffers | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Abstract buffer status (by drawing style) | //+------------------------------------------------------------------+ enum ENUM_BUFFER_STATUS { BUFFER_STATUS_NONE, // No drawing BUFFER_STATUS_FILLING, // Color filling between two levels (MQL5) BUFFER_STATUS_LINE, // Line BUFFER_STATUS_HISTOGRAM, // Histogram from the zero line BUFFER_STATUS_ARROW, // Drawing with arrows BUFFER_STATUS_SECTION, // Segments BUFFER_STATUS_HISTOGRAM2, // Histogram on two indicator buffers BUFFER_STATUS_ZIGZAG, // Zigzag style BUFFER_STATUS_BARS, // Display as bars (MQL5) BUFFER_STATUS_CANDLES, // Display as candles (MQL5) }; //+------------------------------------------------------------------+ //| Buffer type | //+------------------------------------------------------------------+ enum ENUM_BUFFER_TYPE { BUFFER_TYPE_CALCULATE, // Calculated buffer BUFFER_TYPE_DATA, // Colored data buffer }; //+------------------------------------------------------------------+ //| 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_ARROW_CODE, // Arrow code for DRAW_ARROW style BUFFER_PROP_ARROW_SHIFT, // The vertical shift of the arrows for DRAW_ARROW style 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_DRAW_TYPE, // Graphical construction type (from the ENUM_DRAW_TYPE enumeration) BUFFER_PROP_SHIFT, // Indicator graphical construction shift by time axis in bars BUFFER_PROP_LINE_STYLE, // Line style BUFFER_PROP_LINE_WIDTH, // Line width BUFFER_PROP_COLOR_INDEXES, // Number of colors BUFFER_PROP_COLOR, // Drawing color BUFFER_PROP_NUM_DATAS, // Number of data buffers BUFFER_PROP_INDEX_BASE, // Base 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 //+------------------------------------------------------------------+ //| Buffer real properties | //+------------------------------------------------------------------+ enum ENUM_BUFFER_PROP_DOUBLE { BUFFER_PROP_EMPTY_VALUE = BUFFER_PROP_INTEGER_TOTAL, // Empty value for plotting where nothing will be drawn }; #define BUFFER_PROP_DOUBLE_TOTAL (1) // Total number of real buffer properties #define BUFFER_PROP_DOUBLE_SKIP (0) // Number of buffer properties not used in sorting //+------------------------------------------------------------------+ //| Buffer string properties | //+------------------------------------------------------------------+ enum ENUM_BUFFER_PROP_STRING { BUFFER_PROP_SYMBOL = (BUFFER_PROP_INTEGER_TOTAL+BUFFER_PROP_DOUBLE_TOTAL), // Buffer symbol BUFFER_PROP_LABEL, // Name of the graphical indicator series displayed in DataWindow }; #define BUFFER_PROP_STRING_TOTAL (2) // Total number of string buffer properties //+------------------------------------------------------------------+ //| Possible buffer sorting criteria | //+------------------------------------------------------------------+ #define FIRST_BUFFER_DBL_PROP (BUFFER_PROP_INTEGER_TOTAL-BUFFER_PROP_INTEGER_SKIP) #define FIRST_BUFFER_STR_PROP (BUFFER_PROP_INTEGER_TOTAL-BUFFER_PROP_INTEGER_SKIP+BUFFER_PROP_DOUBLE_TOTAL-BUFFER_PROP_DOUBLE_SKIP) enum ENUM_SORT_BUFFER_MODE { //--- Sort by integer properties SORT_BY_BUFFER_INDEX_PLOT = 0, // Sort by the plotted buffer serial number SORT_BY_BUFFER_STATUS, // Sort by buffer drawing style (status) from the ENUM_BUFFER_STATUS enumeration SORT_BY_BUFFER_TYPE, // Sort by buffer type (from the ENUM_BUFFER_TYPE enumeration) SORT_BY_BUFFER_TIMEFRAME, // Sort by the buffer data period (timeframe) SORT_BY_BUFFER_ACTIVE, // Sort by the buffer usage flag SORT_BY_BUFFER_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_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_DRAW_TYPE, // Sort by graphical construction type (from the ENUM_DRAW_TYPE enumeration) SORT_BY_BUFFER_SHIFT, // Sort by the indicator graphical construction shift by time axis in bars SORT_BY_BUFFER_LINE_STYLE, // Sort by the line style SORT_BY_BUFFER_LINE_WIDTH, // Sort by the line width SORT_BY_BUFFER_COLOR_INDEXES, // Sort by a number of attempts SORT_BY_BUFFER_COLOR, // Sort by the drawing color //--- 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\Objects\ 里,创建 Indicators\ 文件夹,内含 CBuffer 类的 Buffer.mqh 文件。
所有 CBaseObj 库对象的基准对象类用作抽象缓冲区对象类的基类。
在缓冲区类文件中包含该类文件:
//+------------------------------------------------------------------+ //| Buffer.mqh | //| Copyright 2020, MetaQuotes Software Corp. | //| https://mql5.com/zh/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://mql5.com/zh/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "..\..\Objects\BaseObj.mqh" //+------------------------------------------------------------------+ //| Abstract indicator buffer class | //+------------------------------------------------------------------+ class CBuffer : public CBaseObj {
函数库对象的基本方法集对于每个对象都是标准的。 每个独立对象都能以相同的方式操控每个对象的固有属性。 我在讲述函数库伊始就已经研究过对象构造原理。
此处一切也是标准的:三个对象属性数组(整数型、实数型和字符串型的数组),返回实数型和字符串型属性索引的方法,及设置方法,接收并从这些数组中提取指定属性描述的方法。 三个虚拟方法返回指定属性的使用标志,和两个比较方法 — 按指定属性在集合中进行搜索、排序,并比较两个对象是否相等。 两个构造函数 — 在日志中显示所有缓冲区对象属性,及其简述,一个方法用默认参数,另一个含参数:
//+------------------------------------------------------------------+ //| Abstract indicator buffer class | //+------------------------------------------------------------------+ class CBuffer : public CBaseObj { private: long m_long_prop[BUFFER_PROP_INTEGER_TOTAL]; // Integer properties double m_double_prop[BUFFER_PROP_DOUBLE_TOTAL]; // Real properties string m_string_prop[BUFFER_PROP_STRING_TOTAL]; // String properties //--- Return the index of the array the buffer's (1) double and (2) string properties are located at int IndexProp(ENUM_BUFFER_PROP_DOUBLE property) const { return(int)property-BUFFER_PROP_INTEGER_TOTAL; } int IndexProp(ENUM_BUFFER_PROP_STRING property) const { return(int)property-BUFFER_PROP_INTEGER_TOTAL-BUFFER_PROP_DOUBLE_TOTAL; } //--- Set the graphical construction type void SetDrawType(void); public: //--- Array of the (1) drawn indicator buffer and (2) color buffer double DataArray[]; double ColorArray[]; //--- 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; //--- Default constructor CBuffer(void){;} //protected: //--- Protected parametric constructor CBuffer(ENUM_BUFFER_STATUS status_buffer,ENUM_BUFFER_TYPE buffer_type,const uint index_plot,const uint index_base_array); public: //--- Send description of buffer properties to the journal (full_prop=true - all properties, false - only supported ones) void Print(const bool full_prop=false); //--- Display a short buffer description in the journal (implementation in the descendants) virtual void PrintShort(void) {;}
标准数据和方法在此处高亮显示。
受保护的参数型构造函数应在类的 protected 部分中出现,但在本文里,该构造函数将被公开,以便验证创建的对象操作。
这就是为什么 protected:访问修饰符被注释掉的原因。
一个设置缓冲区图形构造类型的私密方法,两个公开数组并未注释掉,其一绑定为指标缓冲区的数据数组,另一个则是颜色数组:
//--- Set the graphical construction type void SetDrawType(void); public: //--- Array of the (1) drawn indicator buffer and (2) color buffer double DataArray[]; double ColorArray[];
根据缓冲区状态(继承的对象与其“绑定”状态),我们将定义缓冲区图形构造的类型。
如果我们仔细查看缓冲区状态的枚举常量
//+------------------------------------------------------------------+ //| Abstract buffer status (by drawing style) | //+------------------------------------------------------------------+ enum ENUM_BUFFER_STATUS { BUFFER_STATUS_NONE, // No drawing BUFFER_STATUS_FILLING, // Color filling between two levels (MQL5) BUFFER_STATUS_LINE, // Line BUFFER_STATUS_HISTOGRAM, // Histogram from the zero line BUFFER_STATUS_ARROW, // Drawing with arrows BUFFER_STATUS_SECTION, // Segments BUFFER_STATUS_HISTOGRAM2, // Histogram on two indicator buffers BUFFER_STATUS_ZIGZAG, // Zigzag style BUFFER_STATUS_BARS, // Display as bars (MQL5) BUFFER_STATUS_CANDLES, // Display as candles (MQL5) }; //+------------------------------------------------------------------+
并按枚举中常数的数量循环,用常数顺序与 ENUM_DRAW_TYPE 枚举中几乎同名的常数顺序进行比较,
for(int i=0;i<18;i++) Print(EnumToString((ENUM_DRAW_TYPE)i)," = ",i); 2020.04.15 12:51:53.725 DRAW_NONE = 0 2020.04.15 12:51:53.725 DRAW_LINE = 1 2020.04.15 12:51:53.725 DRAW_HISTOGRAM = 2 2020.04.15 12:51:53.725 DRAW_ARROW = 3 2020.04.15 12:51:53.725 DRAW_SECTION = 4 2020.04.15 12:51:53.725 DRAW_HISTOGRAM2 = 5 2020.04.15 12:51:53.725 DRAW_ZIGZAG = 6 2020.04.15 12:51:53.725 DRAW_FILLING = 7 2020.04.15 12:51:53.725 DRAW_BARS = 8 2020.04.15 12:51:53.725 DRAW_CANDLES = 9 2020.04.15 12:51:53.725 DRAW_COLOR_LINE = 10 2020.04.15 12:51:53.725 DRAW_COLOR_HISTOGRAM = 11 2020.04.15 12:51:53.725 DRAW_COLOR_ARROW = 12 2020.04.15 12:51:53.725 DRAW_COLOR_SECTION = 13 2020.04.15 12:51:53.725 DRAW_COLOR_HISTOGRAM2 = 14 2020.04.15 12:51:53.725 DRAW_COLOR_ZIGZAG = 15 2020.04.15 12:51:53.725 DRAW_COLOR_BARS = 16 2020.04.15 12:51:53.725 DRAW_COLOR_CANDLES = 17
我们将看到顺序会与图形构造类型相匹配。
区别在于,绘画样式的枚举含有单色和彩色缓冲区的常量,并且有一种样式不需要彩色缓冲区(在两个价位之间填充颜色)。
所有缓冲区均被着色。 为了设置绘图样式,检查传递给 SetDrawType() 方法的缓冲区状态和绘图类型,并根据其设置:不绘图;或设置两个价位间用颜色填充空间;或将状态枚举索引偏移 8 个单位,以便令常数值对应于来自绘图样式枚举的颜色缓冲区常数。
在类主体之外,实现方法:
//+------------------------------------------------------------------+ //| Set the graphical construction type | //+------------------------------------------------------------------+ void CBuffer::SetDrawType(void) { ENUM_DRAW_TYPE type=(!this.TypeBuffer() || !this.Status() ? DRAW_NONE : this.Status()==BUFFER_STATUS_FILLING ? DRAW_FILLING : ENUM_DRAW_TYPE(this.Status()+8)); this.SetProperty(BUFFER_PROP_DRAW_TYPE,type); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_TYPE,type); } //+------------------------------------------------------------------+
如果缓冲区类型为 BUFFER_TYPE_CALCULATE(0) 或 BUFFER_STATUS_NONE(0),则绘图样式设置为“不绘图” 。 如果缓冲区状态为 BUFFER_STATUS_FILLING(用颜色填充),则设置相应的绘图样式。 所有剩余值简单地加 8。 该偏移指示彩色绘图样式的常数。
下面是将 ENUM_BUFFER_STATUS 枚举常量值加 8 的示例,在此情况下,ENUM_DRAW_TYPE 枚举中的值取决于该常量:
enum ENUM_BUFFER_STATUS { BUFFER_STATUS_NONE, // 0 // No drawing BUFFER_STATUS_FILLING, // 1 // Color filling between two levels (MQL5) BUFFER_STATUS_LINE, // 2 +8 = 10 // Line BUFFER_STATUS_HISTOGRAM, // 3 +8 = 11 // Histogram from the zero line BUFFER_STATUS_ARROW, // 4 +8 = 12 // Drawing with arrows BUFFER_STATUS_SECTION, // 5 +8 = 13 // Segments BUFFER_STATUS_HISTOGRAM2, // 6 +8 = 14 // Histogram on two indicator buffers BUFFER_STATUS_ZIGZAG, // 7 +8 = 15 // Zigzag style BUFFER_STATUS_BARS, // 8 +8 = 16 // Display as bars (MQL5) BUFFER_STATUS_CANDLES, // 9 +8 = 17 // Display as candles (MQL5) }; 2020.04.15 12:51:53.725 DRAW_NONE = 0 2020.04.15 12:51:53.725 DRAW_LINE = 1 2020.04.15 12:51:53.725 DRAW_HISTOGRAM = 2 2020.04.15 12:51:53.725 DRAW_ARROW = 3 2020.04.15 12:51:53.725 DRAW_SECTION = 4 2020.04.15 12:51:53.725 DRAW_HISTOGRAM2 = 5 2020.04.15 12:51:53.725 DRAW_ZIGZAG = 6 2020.04.15 12:51:53.725 DRAW_FILLING = 7 2020.04.15 12:51:53.725 DRAW_BARS = 8 2020.04.15 12:51:53.725 DRAW_CANDLES = 9 2020.04.15 12:51:53.725 DRAW_COLOR_LINE = 10 2020.04.15 12:51:53.725 DRAW_COLOR_HISTOGRAM = 11 2020.04.15 12:51:53.725 DRAW_COLOR_ARROW = 12 2020.04.15 12:51:53.725 DRAW_COLOR_SECTION = 13 2020.04.15 12:51:53.725 DRAW_COLOR_HISTOGRAM2 = 14 2020.04.15 12:51:53.725 DRAW_COLOR_ZIGZAG = 15 2020.04.15 12:51:53.725 DRAW_COLOR_BARS = 16 2020.04.15 12:51:53.725 DRAW_COLOR_CANDLES = 17
因此,根据缓冲区状态值设置其绘制样式。
在类的公开部分添加所有其余设置和返回缓冲区属性的方法,以及在日志中显示其属性说明的方法:
public: //--- Set (1) the arrow code, (2) vertical shift of arrows, (3) symbol, (4) timeframe, (5) buffer activity flag //--- (6) number of initial bars without drawing, (7) flag of displaying construction values in DataWindow, //--- (8) shift of the indicator graphical construction along the time axis, (9) line style, (10) line width, //--- (11) number of colors, (12) drawing color, (13) empty value and (14) graphical series name displayed in DataWindow virtual void SetArrowCode(const uchar code) { return; } virtual void SetArrowShift(const int shift) { return; } void SetSymbol(const string symbol) { this.SetProperty(BUFFER_PROP_SYMBOL,symbol); } void SetTimeframe(const ENUM_TIMEFRAMES timeframe) { this.SetProperty(BUFFER_PROP_TIMEFRAME,timeframe); } void SetActive(const bool flag) { this.SetProperty(BUFFER_PROP_ACTIVE,flag); } void SetDrawBegin(const int value); void SetShowData(const bool flag); void SetShift(const int shift); void SetStyle(const ENUM_LINE_STYLE style); void SetWidth(const int width); void SetColorNumbers(const int number); void SetColor(const color colour); void SetEmptyValue(const double value); void SetLabel(const string label); //--- Return (1) the serial number of the drawn buffer, (2) bound array index, (3) color buffer index, //--- (4) index of the first free bound array, (5) buffer data period (timeframe) (6) buffer status, //--- (7) buffer type, (8) buffer usage flag, (9) arrow code, (10) arrow shift for DRAW_ARROW style, //--- (11) Number of initial bars that are not drawn and values in DataWindow, (12) graphical construction type, //--- (13) flag of displaying construction values in DataWindow, (14) indicator graphical construction shift along the time axis, //--- (15) drawing line style, (16) drawing line width, (17) number of colors, (18) drawing color, //--- (19) set empty value, (20) buffer symbol and (21) name of the indicator graphical series displayed in DataWindow int IndexPlot(void) const { return (int)this.GetProperty(BUFFER_PROP_INDEX_PLOT); } int IndexBase(void) const { return (int)this.GetProperty(BUFFER_PROP_INDEX_BASE); } int IndexColor(void) const { return (int)this.GetProperty(BUFFER_PROP_INDEX_COLOR); } int IndexNextBuffer(void) const { return (int)this.GetProperty(BUFFER_PROP_INDEX_NEXT); } ENUM_TIMEFRAMES Timeframe(void) const { return (ENUM_TIMEFRAMES)this.GetProperty(BUFFER_PROP_TIMEFRAME); } ENUM_BUFFER_STATUS Status(void) const { return (ENUM_BUFFER_STATUS)this.GetProperty(BUFFER_PROP_STATUS); } ENUM_BUFFER_TYPE TypeBuffer(void) const { return (ENUM_BUFFER_TYPE)this.GetProperty(BUFFER_PROP_TYPE); } bool IsActive(void) const { return (bool)this.GetProperty(BUFFER_PROP_ACTIVE); } uchar ArrowCode(void) const { return (uchar)this.GetProperty(BUFFER_PROP_ARROW_CODE); } int ArrowShift(void) const { return (int)this.GetProperty(BUFFER_PROP_ARROW_SHIFT); } int DrawBegin(void) const { return (int)this.GetProperty(BUFFER_PROP_DRAW_BEGIN); } ENUM_DRAW_TYPE DrawType(void) const { return (ENUM_DRAW_TYPE)this.GetProperty(BUFFER_PROP_DRAW_TYPE); } bool IsShowData(void) const { return (bool)this.GetProperty(BUFFER_PROP_SHOW_DATA); } int Shift(void) const { return (int)this.GetProperty(BUFFER_PROP_SHIFT); } ENUM_LINE_STYLE LineStyle(void) const { return (ENUM_LINE_STYLE)this.GetProperty(BUFFER_PROP_LINE_STYLE); } int LineWidth(void) const { return (int)this.GetProperty(BUFFER_PROP_LINE_WIDTH); } int NumberColors(void) const { return (int)this.GetProperty(BUFFER_PROP_COLOR_INDEXES); } color Color(void) const { return (color)this.GetProperty(BUFFER_PROP_COLOR); } double EmptyValue(void) const { return this.GetProperty(BUFFER_PROP_EMPTY_VALUE); } string Symbol(void) const { return this.GetProperty(BUFFER_PROP_SYMBOL); } string Label(void) const { return this.GetProperty(BUFFER_PROP_LABEL); } //--- Return descriptions of the (1) buffer status, (2) buffer type, (3) buffer usage flag, (4) flag of displaying construction values in DataWindow, //--- (5) drawing line style, (6) set empty value, (7) graphical construction type and (8) used timeframe string GetStatusDescription(bool draw_type=false)const; string GetTypeBufferDescription(void) const; string GetActiveDescription(void) const; string GetShowDataDescription(void) const; string GetLineStyleDescription(void) const; string GetEmptyValueDescription(void) const; string GetDrawTypeDescription(void) const; string GetTimeframeDescription(void) const; //--- Return the size of the data buffer array int GetDataTotal(void) const { return ::ArraySize(this.DataArray); } }; //+------------------------------------------------------------------+
我们来研究所声明方法和封闭参数型构造函数的实现。
封闭参数型构造函数:
//+------------------------------------------------------------------+ //| Closed parametric constructor | //+------------------------------------------------------------------+ CBuffer::CBuffer(ENUM_BUFFER_STATUS buffer_status,ENUM_BUFFER_TYPE buffer_type,const uint index_plot,const uint index_base_array) { this.m_type=COLLECTION_BUFFERS_ID; //--- Save integer properties this.m_long_prop[BUFFER_PROP_STATUS] = buffer_status; this.m_long_prop[BUFFER_PROP_TYPE] = buffer_type; this.m_long_prop[BUFFER_PROP_TIMEFRAME] = PERIOD_CURRENT; this.m_long_prop[BUFFER_PROP_ACTIVE] = false; this.m_long_prop[BUFFER_PROP_ARROW_CODE] = 0xFB; this.m_long_prop[BUFFER_PROP_ARROW_SHIFT] = 0; this.m_long_prop[BUFFER_PROP_DRAW_BEGIN] = 0; this.m_long_prop[BUFFER_PROP_SHOW_DATA] = (buffer_type>BUFFER_TYPE_CALCULATE ? true : false); this.m_long_prop[BUFFER_PROP_SHIFT] = 0; this.m_long_prop[BUFFER_PROP_LINE_STYLE] = STYLE_SOLID; this.m_long_prop[BUFFER_PROP_LINE_WIDTH] = 1; this.m_long_prop[BUFFER_PROP_COLOR_INDEXES] = 1; this.m_long_prop[BUFFER_PROP_COLOR] = clrRed; this.m_long_prop[BUFFER_PROP_NUM_DATAS] = 1; this.m_long_prop[BUFFER_PROP_INDEX_PLOT] = index_plot; this.m_long_prop[BUFFER_PROP_INDEX_BASE] = index_base_array; this.m_long_prop[BUFFER_PROP_INDEX_COLOR] = this.GetProperty(BUFFER_PROP_INDEX_BASE)+this.GetProperty(BUFFER_PROP_NUM_DATAS); this.m_long_prop[BUFFER_PROP_INDEX_NEXT] = this.GetProperty(BUFFER_PROP_INDEX_COLOR)+1; this.SetDrawType(); //--- Save real properties this.m_double_prop[this.IndexProp(BUFFER_PROP_EMPTY_VALUE)] = EMPTY_VALUE; //--- Save string properties this.m_string_prop[this.IndexProp(BUFFER_PROP_SYMBOL)] = ::Symbol(); this.m_string_prop[this.IndexProp(BUFFER_PROP_LABEL)] = (this.TypeBuffer()>BUFFER_TYPE_CALCULATE ? "Buffer "+(string)this.IndexPlot() : NULL); //--- Bind indicator buffers with arrays ::SetIndexBuffer((int)this.GetProperty(BUFFER_PROP_INDEX_BASE),this.DataArray,INDICATOR_DATA); ::SetIndexBuffer((int)this.GetProperty(BUFFER_PROP_INDEX_COLOR),this.ColorArray,INDICATOR_COLOR_INDEX); //--- Set integer buffer parameters ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_TYPE,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_DRAW_TYPE)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_ARROW,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_ARROW_CODE)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_ARROW_SHIFT,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_ARROW_SHIFT)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_BEGIN,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_DRAW_BEGIN)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHOW_DATA,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_SHOW_DATA)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHIFT,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_SHIFT)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_STYLE,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_LINE_STYLE)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_WIDTH,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_LINE_WIDTH)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_COLOR_INDEXES,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_COLOR_INDEXES)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_COLOR,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_COLOR)); //--- Set real buffer parameters ::PlotIndexSetDouble((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_EMPTY_VALUE,this.GetProperty(BUFFER_PROP_EMPTY_VALUE)); //--- Set string buffer parameters ::PlotIndexSetString((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LABEL,this.GetProperty(BUFFER_PROP_LABEL)); } //+------------------------------------------------------------------+
构造函数的输入传递已创建缓冲区对象的状态和类型,绘制缓冲区索引(市场观察索引),和基本数组索引(基本数组是构造缓冲区的常规数组列表中的第一个数组)。
将属于缓冲区对象集合的类型分配给对象,并用默认值填充整数型、实数型字符串型对象属性。
接着,将指标缓冲区与数组绑定,并为绘制缓冲区设置整数型、实数型和字符串型属性,这些属性来自同类缓冲区对象属性的最新默认填充值。
这些动作足以创建一个指定绘图类型的单色指标缓冲区。 从指标创建此类缓冲区时,无需声明、设置和分配任何数组 — 所有必需的缓冲区对象已经存在,并在缓冲区对象中得以分配(如有必要)。 BUFFER_PROP_INDEX_PLOT 属性包含绘制缓冲区索引,该缓冲区数据的设置和接收来自其指标程序。
我只在这里提一提标准方法,但不会深究。 在前面的所有文章中,我已讲述过其他对象的类似方法。 Besides, I have considered the methods in detail when creating the very first library object:
搜索和排序的比较方法,以及返回两个比较对象相等标志的方法:
//+------------------------------------------------------------------+ //| Class methods | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Compare CBuffer objects by all possible properties | //+------------------------------------------------------------------+ int CBuffer::Compare(const CObject *node,const int mode=0) const { const CBuffer *compared_obj=node; //--- compare integer properties of two buffers if(mode<BUFFER_PROP_INTEGER_TOTAL) { long value_compared=compared_obj.GetProperty((ENUM_BUFFER_PROP_INTEGER)mode); long value_current=this.GetProperty((ENUM_BUFFER_PROP_INTEGER)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } //--- compare real properties of two buffers else if(mode<BUFFER_PROP_INTEGER_TOTAL+BUFFER_PROP_DOUBLE_TOTAL) { double value_compared=compared_obj.GetProperty((ENUM_BUFFER_PROP_DOUBLE)mode); double value_current=this.GetProperty((ENUM_BUFFER_PROP_DOUBLE)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } //--- compare string properties of two buffers else if(mode<BUFFER_PROP_INTEGER_TOTAL+BUFFER_PROP_DOUBLE_TOTAL+BUFFER_PROP_STRING_TOTAL) { string value_compared=compared_obj.GetProperty((ENUM_BUFFER_PROP_STRING)mode); string value_current=this.GetProperty((ENUM_BUFFER_PROP_STRING)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } return 0; } //+------------------------------------------------------------------+ //| Compare CBuffer objects by all properties | //+------------------------------------------------------------------+ bool CBuffer::IsEqual(CBuffer *compared_obj) const { int beg=0, end=BUFFER_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_BUFFER_PROP_INTEGER prop=(ENUM_BUFFER_PROP_INTEGER)i; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; } beg=end; end+=BUFFER_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_BUFFER_PROP_DOUBLE prop=(ENUM_BUFFER_PROP_DOUBLE)i; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; } beg=end; end+=BUFFER_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_BUFFER_PROP_STRING prop=(ENUM_BUFFER_PROP_STRING)i; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; } return true; } //+------------------------------------------------------------------+
显示所有缓冲区对象属性的方法:
//+------------------------------------------------------------------+ //| Display buffer properties in the journal | //+------------------------------------------------------------------+ void CBuffer::Print(const bool full_prop=false) { ::Print("============= ", CMessage::Text(MSG_LIB_PARAMS_LIST_BEG),": ", this.GetTypeBufferDescription(),"[",(string)this.IndexPlot(),"] \"",this.GetStatusDescription(true),"\"", " ==================" ); int beg=0, end=BUFFER_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_BUFFER_PROP_INTEGER prop=(ENUM_BUFFER_PROP_INTEGER)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=BUFFER_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_BUFFER_PROP_DOUBLE prop=(ENUM_BUFFER_PROP_DOUBLE)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=BUFFER_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_BUFFER_PROP_STRING prop=(ENUM_BUFFER_PROP_STRING)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("================== ", CMessage::Text(MSG_LIB_PARAMS_LIST_END),": ", this.GetTypeBufferDescription(),"[",(string)this.IndexPlot(),"] \"",this.GetStatusDescription(true),"\"", " ==================\n" ); } //+------------------------------------------------------------------+
该方法返回整数型、实数型和字符串型缓冲区对象属性的描述:
//+------------------------------------------------------------------+ //| Return description of a buffer's integer property | //+------------------------------------------------------------------+ string CBuffer::GetPropertyDescription(ENUM_BUFFER_PROP_INTEGER property) { return ( property==BUFFER_PROP_INDEX_PLOT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_PLOT)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_NUM_DATAS ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_NUM_DATAS)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_INDEX_NEXT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_NEXT)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_TIMEFRAME ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_TIMEFRAME)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetTimeframeDescription() ) : property==BUFFER_PROP_STATUS ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetStatusDescription() ) : property==BUFFER_PROP_TYPE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_TYPE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetTypeBufferDescription() ) : property==BUFFER_PROP_ACTIVE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_ACTIVE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetActiveDescription() ) : property==BUFFER_PROP_ARROW_CODE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_ARROW_CODE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_ARROW_SHIFT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_ARROW_SHIFT)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_DRAW_BEGIN ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_DRAW_BEGIN)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_DRAW_TYPE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_DRAW_TYPE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetDrawTypeDescription() ) : property==BUFFER_PROP_SHOW_DATA ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_SHOW_DATA)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetShowDataDescription() ) : property==BUFFER_PROP_SHIFT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_SHIFT)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_LINE_STYLE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_LINE_STYLE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetLineStyleDescription() ) : property==BUFFER_PROP_LINE_WIDTH ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_LINE_WIDTH)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_COLOR_INDEXES ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_COLOR_NUM)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_COLOR ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_COLOR)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+ColorToString(this.Color(),true) ) : property==BUFFER_PROP_INDEX_BASE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_BASE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_NUM_DATAS ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_NUM_DATAS)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_INDEX_COLOR ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_COLOR)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_INDEX_NEXT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_NEXT)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+ColorToString(this.Color(),true) ) : "" ); } //+------------------------------------------------------------------+ //| Return description of a buffer's real property | //+------------------------------------------------------------------+ string CBuffer::GetPropertyDescription(ENUM_BUFFER_PROP_DOUBLE property) { return ( property==BUFFER_PROP_EMPTY_VALUE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_EMPTY_VALUE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetEmptyValueDescription() ) : "" ); } //+------------------------------------------------------------------+ //| Return description of a buffer's string property | //+------------------------------------------------------------------+ string CBuffer::GetPropertyDescription(ENUM_BUFFER_PROP_STRING property) { return ( property==BUFFER_PROP_SYMBOL ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_SYMBOL)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.Symbol() ) : property==BUFFER_PROP_LABEL ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_LABEL)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.Label()==NULL || this.Label()=="" ? CMessage::Text(MSG_LIB_PROP_NOT_SET) : this.Label()) ) : "" ); } //+------------------------------------------------------------------+
我们看一下在类的公开部分里添加的其余方法的实现。
返回缓冲区状态描述的方法:
//+------------------------------------------------------------------+ //| Return the buffer status description | //+------------------------------------------------------------------+ string CBuffer::GetStatusDescription(bool draw_type=false) const { string type= ( this.Status()==BUFFER_STATUS_NONE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS_NONE) : this.Status()==BUFFER_STATUS_ARROW ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS_ARROW) : this.Status()==BUFFER_STATUS_BARS ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS_BARS) : this.Status()==BUFFER_STATUS_CANDLES ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS_CANDLES) : this.Status()==BUFFER_STATUS_FILLING ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS_FILLING) : this.Status()==BUFFER_STATUS_HISTOGRAM ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS_HISTOGRAM) : this.Status()==BUFFER_STATUS_HISTOGRAM2 ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS_HISTOGRAM2) : this.Status()==BUFFER_STATUS_LINE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS_LINE) : this.Status()==BUFFER_STATUS_SECTION ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS_SECTION) : this.Status()==BUFFER_STATUS_ZIGZAG ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS_ZIGZAG) : "Unknown" ); return(!draw_type ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS_NAME)+" \""+type+"\"" : type); } //+------------------------------------------------------------------+
由于缓冲区状态还定义了绘图样式,因此该方法接收指定如何返回描述的标志:
— 如果是请求缓冲区状态( draw_type 设置为 false),则按以下形式返回缓冲区状态
缓冲区状态:具有图形构造类型“线形”的指标缓冲区
— 如果是请求图形类型( draw_type 设置为 true),则按以下形式返回图形类型
图形构造类型:曲线
所有返回对象属性描述的方法都非常简单,把它们留给读者自行研究:
//+------------------------------------------------------------------+ //| Return the buffer type description | //+------------------------------------------------------------------+ string CBuffer::GetTypeBufferDescription(void) const { return ( this.TypeBuffer()==BUFFER_TYPE_DATA ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_TYPE_DATA) : this.TypeBuffer()==BUFFER_TYPE_CALCULATE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_TYPE_CALCULATE) : "Unknown" ); } //+------------------------------------------------------------------+ //| Return description of the buffer usage flag | //+------------------------------------------------------------------+ string CBuffer::GetActiveDescription(void) const { return(this.IsActive() ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)); } //+---------------------------------------------------------------------+ //|Return description of displaying construction values in DataWindow | //+---------------------------------------------------------------------+ string CBuffer::GetShowDataDescription(void) const { return(this.IsShowData() ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)); } //+------------------------------------------------------------------+ //| Return description of the drawing line style | //+------------------------------------------------------------------+ string CBuffer::GetLineStyleDescription(void) const { return ( this.LineStyle()==STYLE_SOLID ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STYLE_SOLID) : this.LineStyle()==STYLE_DASH ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DASH) : this.LineStyle()==STYLE_DOT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DOT) : this.LineStyle()==STYLE_DASHDOT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DASHDOT) : this.LineStyle()==STYLE_DASHDOTDOT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DASHDOTDOT) : "Unknown" ); } //+------------------------------------------------------------------+ //| Return description of the set empty value | //+------------------------------------------------------------------+ string CBuffer::GetEmptyValueDescription(void) const { return(this.EmptyValue()<EMPTY_VALUE ? ::DoubleToString(this.EmptyValue(),(this.EmptyValue()==0 ? 1 : 8)) : "EMPTY_VALUE"); } //+------------------------------------------------------------------+ //| Return description of the graphical construction type | //+------------------------------------------------------------------+ string CBuffer::GetDrawTypeDescription(void) const { return this.GetStatusDescription(true); } //+------------------------------------------------------------------+ //| Return description of the used timeframe | //+------------------------------------------------------------------+ string CBuffer::GetTimeframeDescription(void) const { string timeframe=TimeframeDescription(this.Timeframe()); return(this.Timeframe()==PERIOD_CURRENT ? CMessage::Text(MSG_LIB_TEXT_PERIOD_CURRENT)+" ("+timeframe+")" : timeframe); } //+------------------------------------------------------------------+
设置不同缓冲区对象属性的方法:
//+------------------------------------------------------------------+ //| Set the number of initial bars | //| without drawing and values in DataWindow | //+------------------------------------------------------------------+ void CBuffer::SetDrawBegin(const int value) { this.SetProperty(BUFFER_PROP_DRAW_BEGIN,value); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_BEGIN,value); } //+------------------------------------------------------------------+ //| Set the flag of displaying | //| construction values in DataWindow | //+------------------------------------------------------------------+ void CBuffer::SetShowData(const bool flag) { this.SetProperty(BUFFER_PROP_SHOW_DATA,flag); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHOW_DATA,flag); } //+------------------------------------------------------------------+ //| Set the indicator graphical construction shift | //+------------------------------------------------------------------+ void CBuffer::SetShift(const int shift) { this.SetProperty(BUFFER_PROP_SHIFT,shift); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHIFT,shift); } //+------------------------------------------------------------------+ //| Set the line style | //+------------------------------------------------------------------+ void CBuffer::SetStyle(const ENUM_LINE_STYLE style) { this.SetProperty(BUFFER_PROP_LINE_STYLE,style); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_STYLE,style); } //+------------------------------------------------------------------+ //| Set the line width | //+------------------------------------------------------------------+ void CBuffer::SetWidth(const int width) { this.SetProperty(BUFFER_PROP_LINE_WIDTH,width); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_WIDTH,width); } //+------------------------------------------------------------------+ //| Set the number of colors | //+------------------------------------------------------------------+ void CBuffer::SetColorNumbers(const int number) { this.SetProperty(BUFFER_PROP_COLOR_INDEXES,number); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_COLOR_INDEXES,number); } //+------------------------------------------------------------------+ //| Set the drawing color | //+------------------------------------------------------------------+ void CBuffer::SetColor(const color colour) { this.SetProperty(BUFFER_PROP_COLOR,colour); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_COLOR,colour); } //+------------------------------------------------------------------+ //| Set the "empty" value for construction | //| without drawing | //+------------------------------------------------------------------+ void CBuffer::SetEmptyValue(const double value) { this.SetProperty(BUFFER_PROP_EMPTY_VALUE,value); ::PlotIndexSetDouble((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_EMPTY_VALUE,value); } //+------------------------------------------------------------------+ //| Set the drawing color | //+------------------------------------------------------------------+ void CBuffer::SetLabel(const string label) { this.SetProperty(BUFFER_PROP_LABEL,label); ::PlotIndexSetString((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LABEL,label); } //+------------------------------------------------------------------+
首先将传递给该方法的值写入相应的缓冲区对象属性,然后按其索引为绘制缓冲区设置属性。
至此,创建抽象指标缓冲区对象所需要做的全部工作完工。
检查指标中缓冲区对象的创建
为了检查抽象缓冲区对象的操作,借用上一篇文章中的指标,并将其保存在 \MQL5\Indicators\TestDoEasy\Part42\ 之下,命名为 TestDoEasyPart42.mq5。
从指标里删除所有不必要的东西。
我们不需要任何按钮、及按钮按下对应的函数和缓冲区数据填充函数,因此将它们从代码中剔除,仅保留与函数库操作直接相关的元素。
此外,我们还要从指标代码里把 OnCalculate() 复制数据到函数库价格结构的函数挪到函数库服务函数的文件当中。 打开 \MQL5\Include\DoEasy\Services\DELib.mqh,并在其内加入两个函数:
//+------------------------------------------------------------------+ //| Copy data from the first OnCalculate() form to the structure | //+------------------------------------------------------------------+ void CopyData(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { //--- Get the array indexing flag as in the timeseries. If failed, //--- set the indexing direction for the array as in the timeseries bool as_series_price=ArrayGetAsSeries(price); if(!as_series_price) ArraySetAsSeries(price,true); //--- Copy the array zero bar to the OnCalculate() SDataCalculate data structure rates_data.rates_total=rates_total; rates_data.prev_calculated=prev_calculated; rates_data.begin=begin; rates_data.price=price[0]; //--- Return the array's initial indexing direction if(!as_series_price) ArraySetAsSeries(price,false); } //+------------------------------------------------------------------+ //| Copy data from the second OnCalculate() form to the structure | //+------------------------------------------------------------------+ void CopyData(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { //--- Get the array indexing flags as in the timeseries. If failed, //--- set the indexing direction or the arrays as in the timeseries bool as_series_time=ArrayGetAsSeries(time); if(!as_series_time) ArraySetAsSeries(time,true); bool as_series_open=ArrayGetAsSeries(open); if(!as_series_open) ArraySetAsSeries(open,true); bool as_series_high=ArrayGetAsSeries(high); if(!as_series_high) ArraySetAsSeries(high,true); bool as_series_low=ArrayGetAsSeries(low); if(!as_series_low) ArraySetAsSeries(low,true); bool as_series_close=ArrayGetAsSeries(close); if(!as_series_close) ArraySetAsSeries(close,true); bool as_series_tick_volume=ArrayGetAsSeries(tick_volume); if(!as_series_tick_volume) ArraySetAsSeries(tick_volume,true); bool as_series_volume=ArrayGetAsSeries(volume); if(!as_series_volume) ArraySetAsSeries(volume,true); bool as_series_spread=ArrayGetAsSeries(spread); if(!as_series_spread) ArraySetAsSeries(spread,true); //--- Copy the arrays' zero bar to the OnCalculate() SDataCalculate data structure rates_data.rates_total=rates_total; rates_data.prev_calculated=prev_calculated; rates_data.rates.time=time[0]; rates_data.rates.open=open[0]; rates_data.rates.high=high[0]; rates_data.rates.low=low[0]; rates_data.rates.close=close[0]; rates_data.rates.tick_volume=tick_volume[0]; rates_data.rates.real_volume=(#ifdef __MQL5__ volume[0] #else 0 #endif); rates_data.rates.spread=(#ifdef __MQL5__ spread[0] #else 0 #endif); //--- Return the arrays' initial indexing direction if(!as_series_time) ArraySetAsSeries(time,false); if(!as_series_open) ArraySetAsSeries(open,false); if(!as_series_high) ArraySetAsSeries(high,false); if(!as_series_low) ArraySetAsSeries(low,false); if(!as_series_close) ArraySetAsSeries(close,false); if(!as_series_tick_volume) ArraySetAsSeries(tick_volume,false); if(!as_series_volume) ArraySetAsSeries(volume,false); if(!as_series_spread) ArraySetAsSeries(spread,false); } //+------------------------------------------------------------------+
结果就是,我们清理了指标代码,已备当前验证和将来之用。 挪动的这两个函数,是协同函数库操控指标所必须的,而函数库服务文件是它们的最佳放置位置。
指标“头文件”如下:
//+------------------------------------------------------------------+ //| TestDoEasyPart42.mq5 | //| Copyright 2020, MetaQuotes Software Corp. | //| https://mql5.com/zh/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://mql5.com/zh/users/artmedia70" #property version "1.00" //--- includes #include <DoEasy\Engine.mqh> #include <DoEasy\Objects\Indicators\Buffer.mqh> //--- properties #property indicator_chart_window #property indicator_buffers 4 #property indicator_plots 2 //--- classes //--- enums //--- defines //--- structures //--- input variables /*sinput*/ ENUM_SYMBOLS_MODE InpModeUsedSymbols= SYMBOLS_MODE_DEFINES; // 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_LIST; // Mode of used timeframes list sinput string InpUsedTFs = "M1,M5,M15,M30,H1,H4,D1,W1,MN1"; // List of used timeframes (comma - separator) sinput bool InpUseSounds = true; // Use sounds //--- indicator buffers CArrayObj list_buffers; // Temporary list for storing two buffer objects //--- 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 //+------------------------------------------------------------------+
此处,我们命令编译器在单独的窗口中创建指标,并为其设置四个缓冲区(两个绘制和两个彩色)。
其余的缓冲区参数是在创建指标缓冲区对象期间和之后设置的。
添加 CArrayObj 指针的动态数组作为指标缓冲区。 它存储所创建的缓冲区。
当操控缓冲区对象时,我们不需要声明 double 数组,且无需将它们分配为指标缓冲区。 一切都位于所创建缓冲区对象内部,并在指标的 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 two buffer objects CBuffer *buffer0=new CBuffer(BUFFER_STATUS_ARROW,BUFFER_TYPE_DATA, 0, 0); CBuffer *buffer1=new CBuffer(BUFFER_STATUS_LINE,BUFFER_TYPE_DATA, 1, buffer0.IndexNextBuffer()); //--- Set non-default values for the second buffer's "empty" value and color buffer1.SetEmptyValue(0); buffer1.SetColor(clrBlue); //--- Add both buffers to the list of indicator buffers list_buffers.Add(buffer0); list_buffers.Add(buffer1); //--- Print data of the created buffers buffer0.Print(); buffer1.Print(); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
在此,我创建了两个抽象指标缓冲区对象。
第一个具有“绘制箭头”状态。 将绘制缓冲区索引设置为 0,及缓冲区基础数组索引 — 这是第一个缓冲区,此处所有索引都应具有初始值。
创建指标缓冲区集合时,我们将在创建缓冲区对象时放弃指定数组索引 — 所有索引都将在此处自动设置,无需用户干预。
第二个对象的状态为“线形”。 设置编号 1(紧随编号零)作为绘制缓冲区索引。 另外,将第一个缓冲区对象返回的值设置为基础数组索引。 它表示下一个可作为数组索引的空闲值,将其指定为下一个缓冲区的基础数组。
此外,将 0(零)分配给第二个所创建缓冲区作为“空”值,而将线形颜色设置为“蓝色”(以便检查如何为缓冲区对象属性设置这些值)。
将两个缓冲区都添加到先前声明的 list_buffers 列表中,并将两个新创建缓冲区的所有属性发送到日志。
由于这仅是一个测试指标,故于此我们不检查缓冲区对象是否已成功创建和添加到列表之中,且为了快速验证缓冲区对象,我们可以随意忽略对象创建和添加到列表的控制。
我们来“清理”上一篇文章中的 OnCalculate() 应答程序,仅保留测试所需的最基本元素:我们只需检查创建两个缓冲区对象的结果,以及分配它们作为指标缓冲区的正确性。
我们该如何做呢?
当我们打印每个缓冲区的所有属性至日志时,它们全都已在 OnInit() 中创建并检查。 只能在 OnCalculate() 中检查是否已成功将所创建对象分配为指标缓冲区。 为此,只需将对象中用作指标缓冲区的数组大小与品种的柱线数量进行比较。
一旦我们将数组分配为指标缓冲区,执行程序的终端子系统就会将这些数组置于其羽翼之下,为它们分配内存,并管理数组的大小。 因此,我们只需从 list_buffers 列表获取每个对象,并将分配给缓冲区的数组的大小与 OnCalculate() 中的 rates_total 值进行比较。 若它们相等,则表示终端子系统已在其控制下从缓冲区对象中取得了数组。
为避免在每次即时报价时都显示记录,在第一次指标计算期间会验证作为指标缓冲区分配的缓冲区对象数组 ,仅当已计算 limit 值超过前值时才打印:
//+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { //+------------------------------------------------------------------+ //| OnCalculate code block for working with the library: | //+------------------------------------------------------------------+ //--- Pass the current symbol data from OnCalculate() to the price structure CopyData(rates_total,prev_calculated,time,open,high,low,close,tick_volume,volume,spread); //--- Check for the minimum number of bars for calculation if(rates_total<min_bars || Point()==0) return 0; //--- Handle the Calculate event in the library //--- If the OnCalculate() method of the library returns zero, not all timeseries are ready - leave till the next tick if(engine.0) return 0; //--- If working in the tester if(MQLInfoInteger(MQL_TESTER)) { engine.OnTimer(rates_data); // Working in the library timer EventsHandling(); // Working with library events } //+------------------------------------------------------------------+ //| OnCalculate code block for working with the indicator: | //+------------------------------------------------------------------+ //--- Set OnCalculate arrays as timeseries ArraySetAsSeries(open,true); ArraySetAsSeries(high,true); ArraySetAsSeries(low,true); ArraySetAsSeries(close,true); ArraySetAsSeries(time,true); ArraySetAsSeries(tick_volume,true); ArraySetAsSeries(volume,true); ArraySetAsSeries(spread,true); //--- Check and calculate the number of calculated bars //--- If limit = 0, there are no new bars - calculate the current one //--- If limit = 1, a new bar has appeared - calculate the first and the current ones //--- limit > 1 means the first launch or changes in history - the full recalculation of all data int limit=rates_total-prev_calculated; //--- Recalculate the entire history if(limit>1) { //--- In a loop by the number of buffers in the list for(int i=0;i<list_buffers.Total();i++) { //--- get the next buffer and display the type of its graphical construction to the journal //--- together with the double array assigned to the buffer (if all is correct, the size is equal to rates_total) CBuffer *buff=list_buffers.At(i); Print(buff.Label()," type = ",EnumToString(buff.DrawType()),", data total = ",buff.GetDataTotal(),", rates_total=",rates_total); } limit=rates_total-1; } //--- Prepare data //--- Calculate the indicator for(int i=limit; i>WRONG_VALUE && !IsStopped(); i--) { CalculateSeries(i,time[i]); } //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+
可从下边附带的文件中找到测试指标的完整代码。
编译指标,并设置以下参数后,在图表上启动它:
日志显示以下记录:
Account 8550475: Artyom Trishkin (MetaQuotes Software Corp.) 10425.23 USD, 1:100, Hedge, MetaTrader 5 demo --- Initializing "DoEasy" library --- Working with the current symbol only. The number of used symbols: 1 "EURUSD" Working with the specified timeframe list: "M5" "M15" "M30" "H1" EURUSD symbol timeseries: - Timeseries "EURUSD" M5: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3684 - Timeseries "EURUSD" M15: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3042 - Timeseries "EURUSD" M30: Requested: 1000, Actual: 0, Created: 0, On the server: 0 - Timeseries "EURUSD" H1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 6240 Library initialization time: 00:00:00.156 ============= Parameter list start: Colored data buffer[0] "Drawing with arrows" ================== Plotted buffer serial number: 0 Buffer status: Indicator buffer with graphical construction type "Drawing with arrows" Buffer type: Colored data buffer Buffer data period (timeframe): Current chart period (M30) Active: No Arrow code: 251 The vertical shift of the arrows: 0 The number of initial bars that are not drawn and values in DataWindow: 0 Display construction values in DataWindow: Yes Graphical construction type: Drawing with arrows Indicator graphical construction shift by time axis in bars: 0 Line style: Solid line Line width: 1 Number of colors: 1 Drawing color: clrRed Number of data buffers: 1 Base data buffer index: 0 Color buffer index: 1 Index of the array to be assigned as the next indicator buffer: 2 ------ Empty value for plotting where nothing will be drawn: EMPTY_VALUE ------ Buffer symbol: EURUSD Name of the graphical indicator series displayed in DataWindow: Buffer 0 ================== Parameter list end: Colored data buffer[0] "Drawing with arrows" ================== ============= Parameter list start: Colored data buffer[1] "Line" ================== Plotted buffer serial number: 1 Buffer status: Indicator buffer with graphical construction type "Line" Buffer type: Colored data buffer Buffer data period (timeframe): Current chart period (M30) Active: No Arrow code: 251 The vertical shift of the arrows: 0 The number of initial bars that are not drawn and values in DataWindow: 0 Display construction values in DataWindow: Yes Graphical construction type: Line Indicator graphical construction shift by time axis in bars: 0 Line style: Solid line Line width: 1 Number of colors: 1 Drawing color: clrBlue Number of data buffers: 1 Base data buffer index: 2 Color buffer index: 3 Index of the array to be assigned as the next indicator buffer: 4 ------ Empty value for plotting where nothing will be drawn: 0.0 ------ Buffer symbol: EURUSD Name of the graphical indicator series displayed in DataWindow: Buffer 1 ================== Parameter list end: Colored data buffer[1] "Line" ================== "EURUSD" M30 timeseries created successfully: - Timeseries "EURUSD" M30: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5111 Buffer 0 type = DRAW_COLOR_ARROW, data total = 5111, rates_total=5111 Buffer 1 type = DRAW_COLOR_LINE, data total = 5111, rates_total=5111
在有关创建时间序列的函数库消息之后,显示两个已创建缓冲区对象中的所有属性的模块会在 OnInit() 里打印。 接着,从 OnCalculate() 里显示有关每个已创建缓冲区的绘图类型的两条消息,其一是分配作为指标缓冲区的缓冲区对象的数组大小,其二是指标启动之时,在 rates_total 里指定的数值。
如我们所见,数组的大小与 rates_total 匹配。 这意味着所创建缓冲区对象的数组已由终端控制,并将它们用作指标缓冲区。
为了再次确定,只需打开指标属性( Ctrl+I ),并转到“颜色”选项卡:
为两个指标缓冲区均设置了名称和颜色。 除了在缓冲区对象类构造函数中设置的默认名称和颜色以外,未指定名称和颜色。 我们在 OnInit() 中创建第二个缓冲区之后,将其重置为蓝色。
一切操作符合期望。 然而,这仅仅是开始。 为了创建各种类型的指标缓冲区,我们需要为每种图形构造类型创建衍生类,并从指标缓冲区集合中的操控这些类。
下一步是什么?
在下一篇文章中,我们将创建抽象缓冲区类的衍生对象。 函数库将利用这些对象在基于 DoEasy 库的指标程序中创建和使用指标缓冲区。
以下附件是函数库当前版本的所有文件,以及测试 EA 文件,供您测试和下载。
将您的问题、评论和建议留在评论中。
请记住,此处我已经为 MetaTrader 5 开发了 MQL5 测试指标。
附件仅适用于 MetaTrader 5。 当前函数库版本尚未在 MetaTrader 4 里进行测试。
在创建所有类别的指标缓冲区及其集合之后,我将尝试在 MetaTrader 4 中实现一些 MQL5 的功能。
返回内容目录
该系列中的先前文章:
DoEasy 函数库中的时间序列(第三十五部分):柱线对象和品种时间序列列表DoEasy 函数库中的时间序列(第三十六部分):所有用到的品种周期的时间序列对象
DoEasy 函数库中的时间序列(第三十七部分):时间序列集合 - 按品种和周期的时间序列数据库
DoEasy 函数库中的时间序列(第三十八部分):时间序列集合 - 实时更新以及从程序访问数据
DoEasy 函数库中的时间序列(第三十九部分):基于函数库的指标 - 准备数据和时间序列事件
DoEasy 函数库中的时间序列(第四十部分):基于函数库的指标 - 实时刷新数据
DoEasy 函数库中的时间序列(第四十一部分):多品种多周期指标样品