指标缓冲区对象类:从抽象基类派生独立缓冲区的底层铺垫(基础篇)
(1/3)· 多数自定义指标卡在缓冲区颜色与数组越界,本篇先把对象骨架搭稳
给指标缓冲区建个对象类
在 DoEasy 函数库里,指标缓冲区被设计成抽象缓冲区对象的衍生类,这样每一根指标线都能当作独立对象来管,而不是散落在全局数组里。 原文示例发布于 2020 年 8 月 31 日,作者 Artyom Trishkin,该篇在源站显示阅读量 2 820、评论 4 条,属于系列第 43 部分,定位是给时间序列功能补上缓冲区封装层。 抽象基类负责通用内存与索引逻辑,衍生类才具体对接 MT5 的 indicator buffer 接口;你在 MT5 里打开这类库,会看到缓冲区对象能直接绑定到 plot 上,省掉手写 CopyBuffer 的重复代码。 下一步通常是写测试用例验证对象在跨周期切换时的缓冲区重建行为,这部分留到后面章节展开。
◍ 把抽象缓冲区改造成可着色派生对象
上一篇文章里我们搭了抽象指标缓冲区对象,这一节把它往下派生:每个派生对象都是独立缓冲区,自带图形构造类型。所有缓冲区默认带颜色,单色需求就在创建时只给一种色,运行期也能再扩颜色数量。 在 Defines.mqh 里用宏替换限定最大颜色数为 64,避免后续验证超过 64 种指定色的开销——哪天上限真扩了再改宏就行。抽象类 Buffer.mqh 中,原本公开的实数型数据和颜色索引数组被挪到受保护区,并改用 SDataBuffer 结构数组承载,以支持多数组缓冲区;同时加了索引越界兜底方法,传错索引就指向结构数组末元素。 构造函数收为受保护,只接受派生类调用,新增数据窗口缓冲区数、线宽、描述等属性。SetColor() 之外补了两个方法:一个按索引追加颜色,一个一次性设全部颜色。NumberColors() 改名返回已用颜色数,另加返回绘图所需数组数的方法。 SetDrawType() 里做了关键偏移:非绘制或填充态直接对应枚举,其余图形类型枚举值加 8 映射成彩色绘图样式常数。DRAW_FILLING 永远只用两种颜色,且无颜色缓冲区,所以算颜色索引时这种类型要跳过 +1。 调颜色的实际动作很直接:若用三色(索引 0/1/2),把目标编号写进指标颜色缓冲区就能重绘对应段——写 0 出蓝、写 1 出红。代码里 MSG_LIB_SYS_FAILED_DRAWING_ARRAY_RESIZE 和 MSG_LIB_SYS_FAILED_COLORS_ARRAY_RESIZE 两条新消息就是给数组 resize 失败用的埋点。 清空缓冲用空值填充所有数组避免图形失真;OnInit 取零、OnCalculate 收 rates_total 来拿数组大小。这一系列改造让派生缓冲区对象在 MT5 里可直接套用,复制 Buffer.mqh 改动后编译指标就能验证着色逻辑。
MSG_LIB_SYS_FAILED_CREATE_BAR_OBJ, class=class="str">"cmt">// Failed to create the "Bar" object MSG_LIB_SYS_FAILED_SYNC_DATA, class=class="str">"cmt">// Failed to synchronize data with the server MSG_LIB_SYS_FAILED_DRAWING_ARRAY_RESIZE, class=class="str">"cmt">// Failed to change the array size of drawn buffers MSG_LIB_SYS_FAILED_COLORS_ARRAY_RESIZE, class=class="str">"cmt">// Failed to change the class="type">color array size MSG_LIB_TEXT_BUFFER_TEXT_LINE_WIDTH, class=class="str">"cmt">// Line width
「文本缓冲区的枚举与多语消息映射」
在 MQL5 指标库里,文本绘图缓冲区的参数靠一组枚举常量来定义,例如箭头尺寸、颜色数量、绘图颜色、空值以及 DataWindow 中显示的序列名。这些常量名以 MSG_LIB_TEXT_BUFFER_TEXT_ 开头,配套注释直接写明用途,方便在 CIndicator::InitBuffers 一类函数里逐项赋值。 紧随其后的结构体用俄英双字符串数组承载运行时提示,比如创建 Bar 对象失败、与服务端同步数据失败,以及调整绘图缓冲数组大小失败。注意带底色那几行:箭头图标尺寸、颜色数组改大小失败、indicator_buffers 数量不正确,都属于缓冲区初始化阶段最容易踩的坑。 线型部分枚举了实线、虚线、点线、点划线与双点划线五种风格,对应 STYLE_SOLID 到 STYLE_DASHDOTDOT。实盘加载自定义指标时,若 #property indicator_buffers 声明的缓冲数少于代码实际使用的数量,终端会抛出“Number of indicator buffers is incorrect”并拒绝编译,外汇与贵金属图表的高波动环境下这类错误会直接拖慢策略部署,需提前核对。
MSG_LIB_TEXT_BUFFER_TEXT_ARROW_SIZE, class=class="str">"cmt">// Arrow size MSG_LIB_TEXT_BUFFER_TEXT_COLOR_NUM, class=class="str">"cmt">// Number of colors MSG_LIB_TEXT_BUFFER_TEXT_COLOR, class=class="str">"cmt">// Drawing class="type">color MSG_LIB_TEXT_BUFFER_TEXT_EMPTY_VALUE, class=class="str">"cmt">// Empty value for plotting where nothing will be drawn MSG_LIB_TEXT_BUFFER_TEXT_SYMBOL, class=class="str">"cmt">// Buffer symbol MSG_LIB_TEXT_BUFFER_TEXT_LABEL, class=class="str">"cmt">// Name of the graphical indicator series displayed in DataWindow MSG_LIB_TEXT_BUFFER_TEXT_STATUS_NAME, class=class="str">"cmt">// Indicator buffer with graphical construction type MSG_LIB_TEXT_BUFFER_TEXT_INVALID_PROPERTY_BUFF, class=class="str">"cmt">// Invalid number of indicator buffers in class="macro">#class="kw">property indicator_buffers MSG_LIB_TEXT_BUFFER_TEXT_TYPE_CALCULATE, class=class="str">"cmt">// Calculated buffer MSG_LIB_TEXT_BUFFER_TEXT_TYPE_DATA, class=class="str">"cmt">// Colored data buffer MSG_LIB_TEXT_BUFFER_TEXT_BUFFER, class=class="str">"cmt">// Buffer MSG_LIB_TEXT_BUFFER_TEXT_STYLE_SOLID, class=class="str">"cmt">// Solid line MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DASH, class=class="str">"cmt">// Dashed line MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DOT, class=class="str">"cmt">// Dotted line MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DASHDOT, class=class="str">"cmt">// Dot-dash line MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DASHDOTDOT, class=class="str">"cmt">// Dash - two dots }; {"Не удалось создать объект \"Бар\"","Failed to create object \"Bar\""}, {"Не удалось синхронизировать данные с сервером","Failed to sync data with server"}, {"Не удалось изменить размер массива рисуемых буферов","Failed to resize drawing buffers array"}, {"Не удалось изменить размер массива цветов","Failed to resize class="type">color array"}, {"Толщина линии отрисовки","Thickness of drawing line"}, {"Размер значка стрелки","Arrow icon size"}, {"Количество цветов","Number of colors"}, {"Цвет отрисовки","Index of buffer containing drawing class="type">color"}, {"Пустое значение для построения, для которого нет отрисовки","Empty value for plotting, for which there is no drawing"}, {"Символ буфера","Buffer Symbol"}, {"Имя индикаторной графической серии, отображаемое в окне DataWindow","Name of indicator graphical series to display in DataWindow"}, {"Индикаторный буфер с типом графического построения","Indicator buffer with graphic plot type"}, {"Неправильно указано количество буферов индикатора (class="macro">#class="kw">property indicator_buffers)","Number of indicator buffers is incorrect(class="macro">#class="kw">property indicator_buffers)"}, {"Расчётный буфер","Calculated buffer"}, {"Цветной буфер данных","Colored Data buffer"}, {"Буфер","Buffer"}, {"Сплошная линия","Solid line"}, {"Прерывистая линия","Broken line"}, {"Пунктирная линия","Dotted line"}, {"Штрих-пунктирная линия","Dash-dot line"}
宏定义与缓冲类骨架的底层约定
这段代码片段暴露了 MT5 标准库常用的预处理宏与抽象缓冲类设计。先盯宏区:TIMER_FREQUENCY 被写死成 16 毫秒,意味着库内定时器最小轮询间隔就是这个值,想压到 10 毫秒以内得改宏重编译。 IND_COLORS_TOTAL 定义为 64,是单指标缓冲区可挂颜色数的硬上限。你在写多色均线或成交量直方图时,若动态调色超过 64 种,编译不会报错但运行时颜色索引会回卷,这是实盘里容易忽略的坑。 CBuffer 类把三种属性拆进定长数组:m_long_prop 装整数属性、m_double_prop 装实数、m_string_prop 装字符串,索引靠 IndexProp() 用枚举偏移算。BUFFER_PROP_INTEGER_TOTAL 就是整数属性的计数基准,改了枚举顺序不跟着调宏,取属性会越界。 DFUN_ERR_LINE 根据终端语言拼出错位置:俄语环境补 ', Page ',其他补 ', Line ' 再加 __LINE__。做多语种 EA 日志时,这套宏能直接复用,少写一堆条件判断。
class="macro">#define DFUN_ERR_LINE(__FUNCTION__+(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian" ? ", Page " : ", Line ")+(class="type">class="kw">string)__LINE__+": ") class="macro">#define DFUN(__FUNCTION__+": ") class=class="str">"cmt">// "Function description" class="macro">#define COUNTRY_LANG("Russian") class=class="str">"cmt">// Country language class="macro">#define END_TIME(D&class="macro">#x27;class="num">31.12.class="num">3000 class="num">23:class="num">59:class="num">59&class="macro">#x27;) class=class="str">"cmt">// End date for account history data requests class="macro">#define TIMER_FREQUENCY(class="num">16) class=class="str">"cmt">// Minimal frequency of the library timer in milliseconds class="macro">#define TOTAL_TRY(class="num">5) class=class="str">"cmt">// Default number of trading attempts class="macro">#define IND_COLORS_TOTAL(class="num">64) class=class="str">"cmt">// Maximum possible number of indicator buffer colors class CBuffer : class="kw">public CBaseObj { class="kw">private: 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="type">int IndexProp(ENUM_BUFFER_PROP_DOUBLE class="kw">property) const { class="kw">return(class="type">int)class="kw">property-BUFFER_PROP_INTEGER_TOTAL; } };
◍ 缓冲区对象的底层数组与构造封装
在 MT5 自定义指标里,CBuffer 类把「绘图缓冲区」和「颜色缓冲区」做了对象化封装。它内部用 SDataBuffer 结构(仅含一个 double 动态数组 Array[])承载每个缓冲区的实际数据,外部再通过 DataArray[] 与 ColorArray[] 暴露给绘图引擎。 看这段声明:protected 下的 SDataBuffer DataBuffer[] 是全部缓冲区对象的数组,ColorBufferArray[] 与 ArrayColors[] 分别存颜色值与索引映射。也就是说,一个带彩色的指标线,背后至少挂了 3 类数组,而非表面那一条线。 构造走参数化保护构造函数 CBuffer(...),入参含 status_buffer、buffer_type、index_plot、index_base_array、num_datas、width、label。其中 num_datas 决定数据条数,width 控制线宽,label 是指标子缓冲区名——开 MT5 写指标时,若线宽不生效,优先查 width 有没有被正确传进构造函数。 公开方法里 SetColor 有两个重载:单色铺满用 SetColor(colour),按索引逐点着色用 SetColor(colour, index);批量配色走 SetColors(array_colors[])。SetEmptyValue 设空白值,SetLabel 虚函数可重写。外汇与贵金属指标开发属高风险环境,参数错配可能导致图形不渲染或历史回看异常,建议改完在策略测试器跑一轮验证。
class="type">int IndexProp(ENUM_BUFFER_PROP_STRING class="kw">property) 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=class="str">"cmt">//--- Return the adjusted buffer array index class="type">int GetCorrectIndexBuffer(const class="type">uint buffer_index) const; class="kw">protected: class=class="str">"cmt">//--- Structure for storing buffer object buffer arrays class="kw">struct SDataBuffer { class="type">class="kw">double Array[]; }; class=class="str">"cmt">//--- Array of(class="num">1) all indicator buffer object buffers, (class="num">2) class="type">color buffer, (class="num">3) for storing colors SDataBuffer DataBuffer[]; class="type">class="kw">double ColorBufferArray[]; class="type">int ArrayColors[]; class="kw">public: class="kw">public: class=class="str">"cmt">//--- Array of the(class="num">1) drawn indicator buffer and(class="num">2) class="type">color buffer class="type">class="kw">double DataArray[]; class="type">class="kw">double ColorArray[]; class="kw">protected: class=class="str">"cmt">//--- Protected parametric constructor CBuffer(ENUM_BUFFER_STATUS status_buffer, ENUM_BUFFER_TYPE buffer_type, const class="type">uint index_plot, const class="type">uint index_base_array, const class="type">int num_datas, const class="type">int width, const class="type">class="kw">string label); class="kw">public: class="type">void SetColor(const class="type">color colour); class="type">void SetColor(const class="type">color colour,const class="type">uchar index); class="type">void SetColors(const class="type">color &array_colors[]); class="type">void SetEmptyValue(const class="type">class="kw">double value); class="kw">virtual class="type">void SetLabel(const class="type">class="kw">string label);
「缓冲区对象暴露的只读属性接口」
在自定义指标或 EA 的缓冲区封装类里,这一组 const 方法把 MQL5 内部 21 个 buffer 属性中的前 12 个直接映射成可读接口,调用方不用再碰 GetProperty 的魔法数字。 IndexPlot 返回该 buffer 绑定的图形序列(plot)下标,IndexBase 指向基准缓冲区序号,二者配合能确认当前缓冲是否为主图叠加层还是副图独立线。IndexColor 与 IndexNextBuffer 则分别给出配色索引与链表里下一个 buffer 的位置,遍历多色缓冲时靠后者不会漏。 Timeframe 拿回的是 ENUM_TIMEFRAMES 类型,意味着某个缓冲完全可能跑在与指标主周期不同的帧上——比如主图 M5 却画了 M1 的快慢线,回测时若没核对这个属性,信号错位概率会明显上升。 Status 与 TypeBuffer 给出运行期状态机和缓冲类别(计算型 / 显示型),IsActive 是快速过滤未启用缓冲的开关;ArrowCode 仅在 DRAW_ARROW 风格下有意义,返回 0~255 的 Wingdings 编码。打开 MT5 把这段贴进你的 CiBuffer 派生类,编译后 watch 一下各属性,比翻文档直观得多。
class="type">int IndexPlot(class="type">void) const { class="kw">return (class="type">int)this.GetProperty(BUFFER_PROP_INDEX_PLOT); } class="type">int IndexBase(class="type">void) const { class="kw">return (class="type">int)this.GetProperty(BUFFER_PROP_INDEX_BASE); } class="type">int IndexColor(class="type">void) const { class="kw">return (class="type">int)this.GetProperty(BUFFER_PROP_INDEX_COLOR); } class="type">int IndexNextBuffer(class="type">void) const { class="kw">return (class="type">int)this.GetProperty(BUFFER_PROP_INDEX_NEXT); } ENUM_TIMEFRAMES Timeframe(class="type">void) const { class="kw">return (ENUM_TIMEFRAMES)this.GetProperty(BUFFER_PROP_TIMEFRAME); } ENUM_BUFFER_STATUS Status(class="type">void) const { class="kw">return (ENUM_BUFFER_STATUS)this.GetProperty(BUFFER_PROP_STATUS); } ENUM_BUFFER_TYPE TypeBuffer(class="type">void) const { class="kw">return (ENUM_BUFFER_TYPE)this.GetProperty(BUFFER_PROP_TYPE); } class="type">bool IsActive(class="type">void) const { class="kw">return (class="type">bool)this.GetProperty(BUFFER_PROP_ACTIVE); } class="type">uchar ArrowCode(class="type">void) const { class="kw">return (class="type">uchar)this.GetProperty(BUFFER_PROP_ARROW_CODE); }