DoEasy 函数库中的时间序列(第四十二部分):抽象指标缓冲区对象类·进阶篇
(2/3)· 当每个指标线都要手写结构体字段时,维护成本会指数级膨胀,本文给出更优雅的基类方案
◍ 指标缓冲区的枚举与整型属性定义
在 MT5 自定义指标里,缓冲区不是裸数组,而是带状态与类型的对象。源码用枚举把「画法状态」和「缓冲类型」先钉死,后面才能用整数属性统一调度。 ENUM_BUFFER_STATUS 区分了 Zigzag 线、 Bars 柱、 Candles 蜡烛三种绘制形态,均限定为 MQL5 环境可用。ENUM_BUFFER_TYPE 则把缓冲区分成计算型(CALCULATE)和上色数据型(DATA),前者跑逻辑,后者直接上色。 整型属性枚举 ENUM_BUFFER_PROP_INTEGER 给出 9 个可控字段:从 BUFFER_PROP_INDEX_PLOT = 0(绘图序号起点)到 BUFFER_PROP_SHOW_DATA(是否在数据窗口露值)。其中 BUFFER_PROP_DRAW_BEGIN 控制前 N 根 bar 不画也不进数据窗口,做多周期拼接时常用。 开 MT5 新建指标,把这几段枚举原样贴进头文件,就能在 OnCalculate 里用 BUFFER_PROP_TIMEFRAME 给副缓冲挂不同周期数据,验证跨周期缓冲是否如预期刷新。
enum ENUM_BUFFER_STATUS { BUFFER_STATUS_ZIGZAG, class=class="str">"cmt">// Zigzag style BUFFER_STATUS_BARS, class=class="str">"cmt">// Display as bars(MQL5) BUFFER_STATUS_CANDLES, class=class="str">"cmt">// Display as candles(MQL5) }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Buffer type | class=class="str">"cmt">//+------------------------------------------------------------------+ enum ENUM_BUFFER_TYPE { BUFFER_TYPE_CALCULATE, class=class="str">"cmt">// Calculated buffer BUFFER_TYPE_DATA, class=class="str">"cmt">// Colored data buffer }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Buffer integer properties | class=class="str">"cmt">//+------------------------------------------------------------------+ enum ENUM_BUFFER_PROP_INTEGER { BUFFER_PROP_INDEX_PLOT = class="num">0, class=class="str">"cmt">// Plotted buffer serial number BUFFER_PROP_STATUS, class=class="str">"cmt">// Buffer status(by drawing style) from the ENUM_BUFFER_STATUS enumeration BUFFER_PROP_TYPE, class=class="str">"cmt">// Buffer type(from the ENUM_BUFFER_TYPE enumeration) BUFFER_PROP_TIMEFRAME, class=class="str">"cmt">// Buffer period data(timeframe) BUFFER_PROP_ACTIVE, class=class="str">"cmt">// Buffer usage flag BUFFER_PROP_ARROW_CODE, class=class="str">"cmt">// Arrow code for DRAW_ARROW style BUFFER_PROP_ARROW_SHIFT, class=class="str">"cmt">// The vertical shift of the arrows for DRAW_ARROW style BUFFER_PROP_DRAW_BEGIN, class=class="str">"cmt">// The number of initial bars that are not drawn and values in DataWindow BUFFER_PROP_SHOW_DATA, class=class="str">"cmt">// Flag of displaying construction values in DataWindow };
「指标缓冲区的属性枚举怎么排」
在 MT5 自定义指标里,每个绘图缓冲区(buffer)的行为都由一组属性控制。这些属性被拆成整数类、实数类和字符串类三套枚举,分别挂在 ENUM_BUFFER_PROP_INTEGER、ENUM_BUFFER_PROP_DOUBLE 等定义下。 整数类属性覆盖了从图形类型 BUFFER_PROP_DRAW_TYPE、时间轴偏移 BUFFER_PROP_SHIFT,到线型线宽、颜色索引、数据缓冲数量与各级索引指针等 19 项(BUFFER_PROP_INTEGER_TOTAL 宏定义为 19),其中 6 项不参与排序(BUFFER_PROP_INTEGER_SKIP = 6)。 实数类目前只定义了一个:BUFFER_PROP_EMPTY_VALUE,值为 BUFFER_PROP_INTEGER_TOTAL 也就是 19,用来标记「不画」的空值位;BUFFER_PROP_DOUBLE_TOTAL 为 1,SKIP 为 0。 字符串类枚举在原文里只留了分段注释框,没有列出具体成员。开 MT5 随便调个内置指标源码搜 ENUM_BUFFER_PROP_INTEGER,就能对着上面这份清单逐条核对缓冲区是怎么被配置的。
enum ENUM_BUFFER_PROP_INTEGER { BUFFER_PROP_DRAW_TYPE, class=class="str">"cmt">// Graphical construction type(from the ENUM_DRAW_TYPE enumeration) BUFFER_PROP_SHIFT, class=class="str">"cmt">// Indicator graphical construction shift by time axis in bars BUFFER_PROP_LINE_STYLE, class=class="str">"cmt">// Line style BUFFER_PROP_LINE_WIDTH, class=class="str">"cmt">// Line width BUFFER_PROP_COLOR_INDEXES, class=class="str">"cmt">// Number of colors BUFFER_PROP_COLOR, class=class="str">"cmt">// Drawing class="type">class="kw">color BUFFER_PROP_NUM_DATAS, class=class="str">"cmt">// Number of data buffers BUFFER_PROP_INDEX_BASE, class=class="str">"cmt">// Base data buffer index BUFFER_PROP_INDEX_COLOR, class=class="str">"cmt">// Color buffer index BUFFER_PROP_INDEX_NEXT, class=class="str">"cmt">// Index of the free array to be assigned as the next indicator buffer }; class="macro">#define BUFFER_PROP_INTEGER_TOTAL(class="num">19) class=class="str">"cmt">// Total number of integer bar properties class="macro">#define BUFFER_PROP_INTEGER_SKIP(class="num">6) class=class="str">"cmt">// Number of buffer properties not used in sorting enum ENUM_BUFFER_PROP_DOUBLE { BUFFER_PROP_EMPTY_VALUE = BUFFER_PROP_INTEGER_TOTAL, class=class="str">"cmt">// Empty value for plotting where nothing will be drawn }; class="macro">#define BUFFER_PROP_DOUBLE_TOTAL(class="num">1) class=class="str">"cmt">// Total number of real buffer properties class="macro">#define BUFFER_PROP_DOUBLE_SKIP(class="num">0) class=class="str">"cmt">// Number of buffer properties not used in sorting
指标缓冲区字符串属性与排序枚举的落地定义
在自建多缓冲指标框架时,字符串类缓冲区属性需要单独占一段枚举空间。下面这段把 symbol 与 DataWindow 显示名塞进 ENUM_BUFFER_PROP_STRING,起点偏移量直接接在整型与双精度属性总数之后,宏 BUFFER_PROP_STRING_TOTAL 写死为 2,意味着字符串属性目前就这两个,后续扩只能改这个常量。 排序维度才是真正有用的部分。ENUM_SORT_BUFFER_MODE 从 SORT_BY_BUFFER_INDEX_PLOT = 0 起排,覆盖了绘图序号、状态、类型、周期、激活标志、箭头代码与偏移、起始不绘柱数、DataWindow 显隐、绘图类型、时间轴平移共 11 个整型排序键;FIRST_BUFFER_DBL_PROP 与 FIRST_BUFFER_STR_PROP 两个宏则按整型/双精度被跳过的数量反推首偏移,方便循环里定位属性段。 实盘意义在于:你可以用这些枚举做缓冲区动态重排,比如按 TIMEFRAME 把多周期缓冲聚到一起,再按 ACTIVE 标志滤掉闲置序列,MT5 里编译后开 DataWindow 拖一下就能看到顺序变化。外汇与贵金属波动剧烈,这类底层结构改动不影响行情风险,但能降低脚本维护成本。
enum ENUM_BUFFER_PROP_STRING { BUFFER_PROP_SYMBOL = (BUFFER_PROP_INTEGER_TOTAL+BUFFER_PROP_DOUBLE_TOTAL), class=class="str">"cmt">// Buffer symbol BUFFER_PROP_LABEL, class=class="str">"cmt">// Name of the graphical indicator series displayed in DataWindow }; class="macro">#define BUFFER_PROP_STRING_TOTAL(class="num">2) class=class="str">"cmt">// Total number of class="type">class="kw">string buffer properties class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Possible buffer sorting criteria | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#define FIRST_BUFFER_DBL_PROP(BUFFER_PROP_INTEGER_TOTAL-BUFFER_PROP_INTEGER_SKIP) class="macro">#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 { class=class="str">"cmt">//--- Sort by integer properties SORT_BY_BUFFER_INDEX_PLOT = class="num">0, class=class="str">"cmt">// Sort by the plotted buffer serial number SORT_BY_BUFFER_STATUS, class=class="str">"cmt">// Sort by buffer drawing style(status) from the ENUM_BUFFER_STATUS enumeration SORT_BY_BUFFER_TYPE, class=class="str">"cmt">// Sort by buffer type(from the ENUM_BUFFER_TYPE enumeration) SORT_BY_BUFFER_TIMEFRAME, class=class="str">"cmt">// Sort by the buffer data period(timeframe) SORT_BY_BUFFER_ACTIVE, class=class="str">"cmt">// Sort by the buffer usage flag SORT_BY_BUFFER_ARROW_CODE, class=class="str">"cmt">// Sort by the arrow code for DRAW_ARROW style SORT_BY_BUFFER_ARROW_SHIFT, class=class="str">"cmt">// Sort by the vertical shift of the arrows for DRAW_ARROW style SORT_BY_BUFFER_DRAW_BEGIN, class=class="str">"cmt">// Sort by the number of initial bars that are not drawn and values in DataWindow SORT_BY_BUFFER_SHOW_DATA, class=class="str">"cmt">// Sort by the flag of displaying construction values in DataWindow SORT_BY_BUFFER_DRAW_TYPE, class=class="str">"cmt">// Sort by graphical construction type(from the ENUM_DRAW_TYPE enumeration) SORT_BY_BUFFER_SHIFT, class=class="str">"cmt">// Sort by the indicator graphical construction shift by time axis in bars };
◍ 缓冲区排序枚举与基类骨架
在自定义指标的多缓冲管理里,先要决定按什么维度给缓冲区排序。下面这组枚举把排序键分成了外观属性与真实属性两类:线型、线宽、颜色索引、绘制色归为视觉类;空值(EMPTY_VALUE)、关联品种、DataWindow 显示名则归为实际属性类。 枚举里 SORT_BY_BUFFER_EMPTY_VALUE 被显式赋值为 FIRST_BUFFER_DBL_PROP,SORT_BY_BUFFER_SYMBOL 从 FIRST_BUFFER_STR_PROP 起算,说明双精度与字符串属性在内部有独立偏移基址,改排序逻辑时不能混用这两组起点。 紧随其后的 CBuffer 类以 public 方式继承 CBaseObj,并前置包含了 ..\..\Objects\BaseObj.mqh。基类里只露了 private: 段开头,意味着缓冲区的核心数据成员将对外部与派生类封闭,具体读写要走类内方法。 开 MT5 新建一个 .mqh,把这段枚举和类声明原样贴入,确认编译不报 'CBaseObj undeclared' 就能验证包含路径是否正确。外汇与贵金属指标开发属高风险环境,缓冲区排序错误可能让图形错位,实盘前务必在策略测试器跑通。
SORT_BY_BUFFER_LINE_STYLE, class=class="str">"cmt">// Sort by the line style SORT_BY_BUFFER_LINE_WIDTH, class=class="str">"cmt">// Sort by the line width SORT_BY_BUFFER_COLOR_INDEXES, class=class="str">"cmt">// Sort by a number of attempts SORT_BY_BUFFER_COLOR, class=class="str">"cmt">// Sort by the drawing class="type">class="kw">color class=class="str">"cmt">//--- Sort by real properties SORT_BY_BUFFER_EMPTY_VALUE = FIRST_BUFFER_DBL_PROP, class=class="str">"cmt">// Sort by the empty value for plotting where nothing will be drawn class=class="str">"cmt">//--- Sort by class="type">class="kw">string properties SORT_BY_BUFFER_SYMBOL = FIRST_BUFFER_STR_PROP, class=class="str">"cmt">// Sort by the buffer symbol SORT_BY_BUFFER_LABEL, class=class="str">"cmt">// Sort by the name of the graphical indicator series displayed in DataWindow }; class="macro">#include "..\..\Objects\BaseObj.mqh" class CBuffer : class="kw">public CBaseObj { class="kw">private:
「指标缓冲区的三类属性与读写定位」
在 MT5 自定义指标里,一个绘图缓冲区(buffer)的属性被拆成整数、实数、字符串三类,分别塞进三个定长数组:m_long_prop、m_double_prop、m_string_prop。数组长度由枚举上限 BUFFER_PROP_INTEGER_TOTAL、BUFFER_PROP_DOUBLE_TOTAL、BUFFER_PROP_STRING_TOTAL 决定,编译期就固定,运行时不能越界扩。 double 和 string 属性在各自数组里的下标,不是直接用枚举值,而是减去前面所有类型的总数。IndexProp 的两个重载就是干这个偏移换算的:double 属性减一次整数总数,string 属性减整数加 double 两次总数。这样同一套 Get/Set 接口能靠函数重载自动落到正确数组。 对外暴露的 DataArray 和 ColorArray 是真正画到图表上的数据缓冲和颜色缓冲,和内部属性数组是两回事。调 SetProperty 写属性时,整数类直接按枚举下标落 m_long_prop,实数和字符串则先过 IndexProp 算出相对位置再写,少了这步偏移就会写串槽。 下面这段是类内声明与实现的核心片段,可在 MT5 的 MQ5 头文件里原样对照验证: 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)]; }
class="type">long m_long_prop[BUFFER_PROP_INTEGER_TOTAL]; class=class="str">"cmt">// Integer properties class="type">class="kw">double m_double_prop[BUFFER_PROP_DOUBLE_TOTAL]; class=class="str">"cmt">// Real properties class="type">class="kw">string m_string_prop[BUFFER_PROP_STRING_TOTAL]; class=class="str">"cmt">// String properties class=class="str">"cmt">//--- Return the index of the array the buffer&class="macro">#x27;s(class="num">1) class="type">class="kw">double and(class="num">2) class="type">class="kw">string properties are located at class="type">int IndexProp(ENUM_BUFFER_PROP_DOUBLE class="kw">property) class="kw">const { class="kw">return(class="type">int)class="kw">property-BUFFER_PROP_INTEGER_TOTAL; } class="type">int IndexProp(ENUM_BUFFER_PROP_STRING class="kw">property) class="kw">const { class="kw">return(class="type">int)class="kw">property-BUFFER_PROP_INTEGER_TOTAL-BUFFER_PROP_DOUBLE_TOTAL; } class=class="str">"cmt">//--- Set the graphical construction type class="type">void SetDrawType(class="type">void); class="kw">public: class=class="str">"cmt">//--- Array of the(class="num">1) drawn indicator buffer and(class="num">2) class="type">class="kw">color buffer class="type">class="kw">double DataArray[]; class="type">class="kw">double ColorArray[]; class=class="str">"cmt">//--- Set buffer&class="macro">#x27;s(class="num">1) integer, (class="num">2) real and(class="num">3) class="type">class="kw">string properties class="type">void SetProperty(ENUM_BUFFER_PROP_INTEGER class="kw">property,class="type">long value) { this.m_long_prop[class="kw">property]=value; } class="type">void SetProperty(ENUM_BUFFER_PROP_DOUBLE class="kw">property,class="type">class="kw">double value) { this.m_double_prop[this.IndexProp(class="kw">property)]=value; } class="type">void SetProperty(ENUM_BUFFER_PROP_STRING class="kw">property,class="type">class="kw">string value) { this.m_string_prop[this.IndexProp(class="kw">property)]=value; } class=class="str">"cmt">//--- Return(class="num">1) integer, (class="num">2) real and(class="num">3) class="type">class="kw">string buffer properties from the properties array class="type">long GetProperty(ENUM_BUFFER_PROP_INTEGER class="kw">property) class="kw">const { class="kw">return this.m_long_prop[class="kw">property]; } class="type">class="kw">double GetProperty(ENUM_BUFFER_PROP_DOUBLE class="kw">property) class="kw">const { class="kw">return this.m_double_prop[this.IndexProp(class="kw">property)]; }
缓冲区对象的属性读取与比较接口
CBuffer 基类把指标缓冲区的三类属性(整型、双精度、字符串)统一收口在 GetProperty 与 GetPropertyDescription 方法里。GetProperty 对字符串属性直接按索引返回 m_string_prop 成员,调用方不需要关心底层枚举到数组下标的映射。 SupportProperty 的三个虚函数默认都返回 true,意味着派生类若不重写,框架会认为该缓冲区支持全部属性。若你写了自定义缓冲区类型却漏掉重载,日志打印和排序逻辑可能误判,开 MT5 挂个 Print(true) 就能看到实际支持状态。 Compare 与 IsEqual 用于列表排序和等值查找:Compare 带 mode 参数可按指定属性比对,IsEqual 则要求所有属性完全一致才返回真。在外汇或贵金属指标里用这类缓冲区做多线管理时,注意不同品种点差和报价精度差异可能导致双精度属性比较出现偏差,概率上会增加误等风险。 DataArray 与 ColorArray 是公开的双精度数组,分别承载绘制数值和颜色值。SetDrawType 负责把缓冲区绑定到具体图形样式,构造函数分无参和受保护带参两种,后者在继承类里传入状态、类型、plot 索引与底层数组索引完成初始化。
class="type">class="kw">string GetProperty(ENUM_BUFFER_PROP_STRING class="kw">property) class="kw">const { class="kw">return this.m_string_prop[this.IndexProp(class="kw">property)]; } class=class="str">"cmt">//--- Get description of buffer&class="macro">#x27;s(class="num">1) integer, (class="num">2) real and(class="num">3) class="type">class="kw">string properties class="type">class="kw">string GetPropertyDescription(ENUM_BUFFER_PROP_INTEGER class="kw">property); class="type">class="kw">string GetPropertyDescription(ENUM_BUFFER_PROP_DOUBLE class="kw">property); class="type">class="kw">string GetPropertyDescription(ENUM_BUFFER_PROP_STRING class="kw">property); class=class="str">"cmt">//--- Return the flag of the buffer supporting the class="kw">property class="kw">virtual class="type">bool SupportProperty(ENUM_BUFFER_PROP_INTEGER class="kw">property) { class="kw">return true; } class="kw">virtual class="type">bool SupportProperty(ENUM_BUFFER_PROP_DOUBLE class="kw">property) { class="kw">return true; } class="kw">virtual class="type">bool SupportProperty(ENUM_BUFFER_PROP_STRING class="kw">property) { class="kw">return true; } class=class="str">"cmt">//--- Compare CBuffer objects by all possible properties(for sorting the lists by a specified buffer object class="kw">property) class="kw">virtual class="type">int Compare(class="kw">const CObject *node,class="kw">const class="type">int mode=class="num">0) class="kw">const; class=class="str">"cmt">//--- Compare CBuffer objects by all properties(to search for equal buffer objects) class="type">bool IsEqual(CBuffer* compared_obj) class="kw">const; class=class="str">"cmt">//--- Default constructor CBuffer(class="type">void){;} class=class="str">"cmt">//class="kw">protected: class=class="str">"cmt">//--- Protected parametric constructor CBuffer(ENUM_BUFFER_STATUS status_buffer,ENUM_BUFFER_TYPE buffer_type,class="kw">const class="type">uint index_plot,class="kw">const class="type">uint index_base_array); class="kw">public: class=class="str">"cmt">//--- Send description of buffer properties to the journal(full_prop=true - all properties, class="kw">false - only supported ones) class="type">void Print(class="kw">const class="type">bool full_prop=class="kw">false); class=class="str">"cmt">//--- Display a class="type">class="kw">short buffer description in the journal(implementation in the descendants) class="kw">virtual class="type">void PrintShort(class="type">void) {;} class=class="str">"cmt">//--- Set the graphical construction type class="type">void SetDrawType(class="type">void); class="kw">public: class=class="str">"cmt">//--- Array of the(class="num">1) drawn indicator buffer and(class="num">2) class="type">class="kw">color buffer class="type">class="kw">double DataArray[]; class="type">class="kw">double ColorArray[];