DoEasy 函数库中的时间序列(第四十五部分):多周期指标缓冲区·进阶篇
(2/3)· 还在手动对齐 M15 与 H1 的柱线数据?一篇讲清缓冲区索引与多周期映射
◍ 指标缓冲区整数属性的枚举骨架
在 MT5 自定义指标里,每个绘图缓冲区(buffer)都不是裸数组,而带一组可配置的整数属性。系统用 ENUM_BUFFER_PROP_INTEGER 这个枚举把属性名固化下来,从绘图序号到线宽、偏移、颜色数量全在里面。 下面这段枚举定义了 18 个整数属性常量,其中 BUFFER_PROP_INDEX_NEXT_BASE 是较后期加入的字段,用于指定「下一个要被指派为基础缓冲区的数组索引」,在多层缓冲复用时有用。 读代码时重点看三个容易忽略的项:BUFFER_PROP_DRAW_BEGIN 控制前 N 根 bar 不画也不进 DataWindow;BUFFER_PROP_SHIFT 是按时间轴整体平移绘图;BUFFER_PROP_COLOR_INDEXES 决定该 buffer 能套几种颜色(多色柱线就靠它)。 开 MT5 新建一个空指标,把枚举原样贴进头文件,编译后逐个 Print(BUFFER_PROP_xxx) 就能确认常量值,方便后续写 SetIndexBuffer 时对照传参。
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_DRAW_TYPE, class=class="str">"cmt">// Graphical construction type(from the ENUM_DRAW_TYPE enumeration) 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_LINE_STYLE, class=class="str">"cmt">// Line style BUFFER_PROP_LINE_WIDTH, class=class="str">"cmt">// Line width 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 BUFFER_PROP_SHIFT, class=class="str">"cmt">// Indicator graphical construction shift by time axis in bars 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_INDEX_BASE, class=class="str">"cmt">// Base data buffer index BUFFER_PROP_INDEX_NEXT_BASE, class=class="str">"cmt">// Index of the array to be assigned as the next indicator buffer };
缓冲区枚举与排序宏的底层定义
在 MT5 指标内核里,缓冲区属性用一组枚举值描述,代码末尾的 BUFFER_PROP_INDEX_NEXT_PLOT、BUFFER_PROP_NUM_DATAS、BUFFER_PROP_INDEX_COLOR 分别指向「下一个绘制缓冲区的索引」「数据缓冲区总数」「颜色缓冲区索引」,这些是定位多缓冲布局的坐标。 宏定义给出了硬数字:BUFFER_PROP_INTEGER_TOTAL 固定为 20,代表整数类 bar 属性总量;BUFFER_PROP_INTEGER_SKIP 为 2,表示排序时不参与的整数属性个数。用这两个值可推算 double 与 string 属性的起始偏移:FIRST_BUFFER_DBL_PROP = 20 - 2 = 18,FIRST_BUFFER_STR_PROP 则再叠加上 double 类总数减去跳过数。 排序模式枚举 ENUM_SORT_BUFFER_MODE 从 0 开始,SORT_BY_BUFFER_INDEX_PLOT 按绘制序号排,SORT_BY_BUFFER_STATUS 按 ENUM_BUFFER_STATUS 里的绘制状态排,一直到 SORT_BY_BUFFER_LINE_WIDTH 按线宽排。写自定义指标面板时,若要根据线型或时间框架重排缓冲区,直接引用这些枚举值即可,外汇与贵金属品种波动剧烈、杠杆高,改完请在策略测试器用历史数据验证排序逻辑是否错位。
BUFFER_PROP_INDEX_NEXT_PLOT, class=class="str">"cmt">// Index of the next drawn buffer BUFFER_PROP_NUM_DATAS, class=class="str">"cmt">// Number of data buffers BUFFER_PROP_INDEX_COLOR, class=class="str">"cmt">// Color buffer index }; class="macro">#define BUFFER_PROP_INTEGER_TOTAL(class="num">20) class=class="str">"cmt">// Total number of integer bar properties class="macro">#define BUFFER_PROP_INTEGER_SKIP(class="num">2) class=class="str">"cmt">// Number of buffer properties not used in sorting class=class="str">"cmt">//+------------------------------------------------------------------+ 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_DRAW_TYPE, class=class="str">"cmt">// Sort by graphical construction type(from the ENUM_DRAW_TYPE enumeration) 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_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 };
「指标缓冲区排序枚举与中文映射细节」
在 MT5 指标架构里,缓冲区(buffer)有一整套排序枚举,用来在内部按不同属性归类和检索。上面列出的 SORT_BY_BUFFER_* 枚举覆盖了从起始未绘制柱数、DataWindow 显示标志、时间轴偏移、颜色索引,到基础/下一个缓冲区索引等维度。 其中 SORT_BY_BUFFER_INDEX_NEXT_BASE 与 SORT_BY_BUFFER_INDEX_NEXT_PLOT 是两个容易混淆的项:前者指向「将被指定为下一个指标缓冲区的数组索引」,后者指向「下一个实际绘制的缓冲区索引」。在调试多缓冲指标时,若绘制错位,优先核对这两项赋值。 后面的 MSG_LIB_TEXT_BUFFER_TEXT_* 是一组本地化文本常量,配套俄文与英文描述。例如 MSG_LIB_TEXT_BUFFER_TEXT_INDEX_NEXT_BASE 对应『Array index for assignment as the next indicator buffer』,而 MSG_LIB_TEXT_BUFFER_TEXT_INDEX_NEXT_PLOT 对应『Index of the next drawable buffer』。 开 MT5 按 F4 进 MetaEditor,在自定义指标头文件里搜 SORT_BY_BUFFER_INDEX_NEXT_PLOT,确认你的绘图循环里 next plot 索引是否和枚举语义一致,外汇与贵金属波动剧烈,指标缓冲区错位可能导致信号滞后,交易属高风险。
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_SHIFT, class=class="str">"cmt">// Sort by the indicator graphical construction shift by time axis in bars 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 SORT_BY_BUFFER_INDEX_BASE, class=class="str">"cmt">// Sort by the basic data buffer index SORT_BY_BUFFER_INDEX_NEXT_BASE, class=class="str">"cmt">// Sort by the index of the array to be assigned as the next indicator buffer SORT_BY_BUFFER_INDEX_NEXT_PLOT, class=class="str">"cmt">// Sort by the index of the next drawn buffer 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=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//--- CBuffer MSG_LIB_TEXT_BUFFER_TEXT_INDEX_BASE, class=class="str">"cmt">// Base data buffer index MSG_LIB_TEXT_BUFFER_TEXT_INDEX_PLOT, class=class="str">"cmt">// Plotted buffer serial number MSG_LIB_TEXT_BUFFER_TEXT_INDEX_COLOR, class=class="str">"cmt">// Color buffer index MSG_LIB_TEXT_BUFFER_TEXT_NUM_DATAS, class=class="str">"cmt">// Number of data buffers MSG_LIB_TEXT_BUFFER_TEXT_INDEX_NEXT_BASE, class=class="str">"cmt">// Index of the array to be assigned as the next indicator buffer MSG_LIB_TEXT_BUFFER_TEXT_INDEX_NEXT_PLOT, class=class="str">"cmt">// Index of the next drawn buffer MSG_LIB_TEXT_BUFFER_TEXT_TIMEFRAME, class=class="str">"cmt">// Buffer(timeframe) data period {"Индекс базового буфера данных","Index of Base data buffer"}, {"Порядковый номер рисуемого буфера","Plot buffer sequence number"}, {"Индекс буфера цвета","Color buffer index"}, {"Количество буферов данных","Number of data buffers"}, {"Индекс массива для назначения следующим индикаторным буфером","Array index for assignment as the next indicator buffer"}, {"Индекс следующего по счёту рисуемого буфера","Index of the next drawable buffer"}, {"Период данных буфера (таймфрейм)","Buffer data Period(Timeframe)"},
◍ 用 PrintShort 看清缓冲区的真实身份
在 MT5 自定义指标里,缓冲区(buffer)的排列经常让人迷糊:到底第几个绘图序列对应哪块内存、什么类型?CBufferArrow::PrintShort 这个方法就是用来把这些信息直接打到专家日志里的。 下面这段是 PrintShort 的实现,它把缓冲区索引、基础数组索引、颜色索引和状态描述拼成一行输出: void CMessage::Text 取本地化文案,后面紧跟 (P 绘图号 /B 基础号 /C 颜色号) 的结构,再补上品种与周期。实际跑出来,日志里会出现诸如「Buffer(P0/B0/C1): Drawing with arrows EURUSD H1」这样的行,一眼就能核对缓冲区挂载位置。 从示例日志能看到,P0 到 P8 共 9 个绘图缓冲,分别承载箭头、线、分段、零轴直方图、双缓冲直方图、之字、双水平填色、仿蜡烛与仿 bar;其中 P8 还额外出现 B26/C27 的计算缓冲,说明同一绘图号下可挂多个基础数组。外汇与贵金属品种(如 EURUSD)波动剧烈、杠杆风险高,缓冲区错位可能导致信号绘制异常,开 MT5 后建议先用这类短描述函数自检。 CBufferCalculate 类则专门标记 BUFFER_TYPE_CALCULATE 类型,构造时写死名字「Calculate」且不参与直接绘图,仅作中间计算层——验证指标逻辑时,认准 P8/B26/C27 这种计算缓冲就不会误改显示参数。
class=class="str">"cmt">//| Display class="type">class="kw">short buffer description in the journal | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CBufferArrow::PrintShort(class="type">void) { ::Print( CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_BUFFER),"(P",(class="type">class="kw">string)this.IndexPlot(),"/B",(class="type">class="kw">string)this.IndexBase(),"/C",(class="type">class="kw">string)this.IndexColor(),"): ", this.GetStatusDescription(true)," ",this.Symbol()," ",TimeframeDescription(this.Timeframe()) ); } class=class="str">"cmt">//+------------------------------------------------------------------+
缓冲区类的属性支持与日志输出实现
在自定义指标的多缓冲架构里,CBufferCalculate 用一组 SupportProperty 重载来声明自身能处理哪些元数据属性。整数类属性里,它只对绘图索引、状态、类型、基础索引及下一基础索引返回 true,其余整数项与全部 double、string 属性均返回 false,这意味着该缓冲层不承接任何浮点与文本类的扩展配置。 数据读写被压缩成两个内联函数:SetData 把数值写进 0 号缓冲区的 series_index 位置,GetData 从同一位置取回 double 值。这种写法把数组下标访问封装掉,调用方不必关心底层缓冲区编号。 PrintShort 在日志里打印一段短描述,格式为 [P绘图索引/B基础索引/C颜色索引]: 类型说明。调试多缓冲指标时,直接调一次就能在 MT5 终端看清每个缓冲的角色,不必逐行翻代码。 外汇与贵金属品种波动剧烈,这类底层封装仅降低开发出错概率,不预示任何信号胜率,实盘前请在策略测试器用历史数据验证。
class="kw">virtual class="type">bool SupportProperty(ENUM_BUFFER_PROP_STRING class="kw">property); class=class="str">"cmt">//--- Display a class="type">class="kw">short buffer description in the journal class="kw">virtual class="type">void PrintShort(class="type">void); class=class="str">"cmt">//--- Set the value to the data buffer array class="type">void SetData(class="kw">const class="type">uint series_index,class="kw">const class="type">class="kw">double value) { this.SetBufferValue(class="num">0,series_index,value); } class=class="str">"cmt">//--- Return the value from the data buffer array class="type">class="kw">double GetData(class="kw">const class="type">uint series_index) class="kw">const { class="kw">return this.GetDataBufferValue(class="num">0,series_index); } }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Return &class="macro">#x27;true&class="macro">#x27; if a buffer supports a passed | class=class="str">"cmt">//| integer class="kw">property, otherwise class="kw">return &class="macro">#x27;class="kw">false&class="macro">#x27; | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CBufferCalculate::SupportProperty(ENUM_BUFFER_PROP_INTEGER class="kw">property) { if( class="kw">property==BUFFER_PROP_INDEX_PLOT || class="kw">property==BUFFER_PROP_STATUS || class="kw">property==BUFFER_PROP_TYPE || class="kw">property==BUFFER_PROP_INDEX_BASE || class="kw">property==BUFFER_PROP_INDEX_NEXT_BASE ) class="kw">return true; class="kw">return class="kw">false; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Return &class="macro">#x27;true&class="macro">#x27; if a buffer supports a passed | class=class="str">"cmt">//| real class="kw">property, otherwise class="kw">return &class="macro">#x27;class="kw">false&class="macro">#x27; | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CBufferCalculate::SupportProperty(ENUM_BUFFER_PROP_DOUBLE class="kw">property) { class="kw">return class="kw">false; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Return &class="macro">#x27;true&class="macro">#x27; if a buffer supports a passed | class=class="str">"cmt">//| class="type">class="kw">string class="kw">property, otherwise class="kw">return &class="macro">#x27;class="kw">false&class="macro">#x27; | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CBufferCalculate::SupportProperty(ENUM_BUFFER_PROP_STRING class="kw">property) { class="kw">return class="kw">false; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Display class="type">class="kw">short buffer description in the journal | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CBufferCalculate::PrintShort(class="type">void) { ::Print( CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_BUFFER),"[P",(class="type">class="kw">string)this.IndexPlot(),"/B",(class="type">class="kw">string)this.IndexBase(),"/C",(class="type">class="kw">string)this.IndexColor(),"]: ", this.GetTypeBufferDescription() ); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+
「缓冲区对象的属性容器与索引映射」
在 MT5 自定义指标里,把每个绘图缓冲区的属性收拢到一个类里,能省掉大量重复声明。CBuffer 用三组定长数组分别装整数、双精度、字符串属性:m_long_prop 对应 BUFFER_PROP_INTEGER_TOTAL 个整型槽,m_double_prop 与 m_string_prop 同理,编译期就锁定了内存布局。 索引换算靠两个重载的 IndexProp:双精度属性减去整型总数得到落点,字符串属性再扣掉双精度总数。比如某字符串属性枚举值是 20,而整型+双精度共占 15 个槽,那它实际落在 m_string_prop[4]。 被高亮的 m_act_state_trigger 是个辅助开关,用来标记缓冲区当前激活状态,SetDrawType 会读它决定画线还是画柱。外汇与贵金属行情跳空频繁,这类状态位能避免历史缓冲在重绘时被错画,但指标本身不预测方向,仅降低渲染噪声,实盘仍属高风险环境。 开 MT5 建个空指标把这段类头丢进 Include,故意把 BUFFER_PROP_xxx_TOTAL 改错一个,编译器会立刻报数组越界,比看文档直观。
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">bool m_act_state_trigger; class=class="str">"cmt">// Auxiliary buffer status class="kw">switch flag 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 by buffer type and status class="type">void SetDrawType(class="type">void); class=class="str">"cmt">//--- Return the adjusted buffer array index class="type">int GetCorrectIndexBuffer(class="kw">const class="type">uint buffer_index) class="kw">const; class="kw">protected: class="kw">struct SDataBuffer { class="type">class="kw">double Array[]; }; class=class="str">"cmt">// Structure for storing buffer object buffer arrays SDataBuffer DataBuffer[]; class=class="str">"cmt">// Array of all object indicator buffers class="type">class="kw">double ColorBufferArray[]; class=class="str">"cmt">// Color buffer array class="type">int ArrayColors[]; class=class="str">"cmt">// Array for storing colors class="kw">public: 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