DoEasy 函数库中的时间序列(第四十五部分):多周期指标缓冲区·进阶篇
📊

DoEasy 函数库中的时间序列(第四十五部分):多周期指标缓冲区·进阶篇

(2/3)· 还在手动对齐 M15 与 H1 的柱线数据?一篇讲清缓冲区索引与多周期映射

案例拆解 第 2/3 篇
多数人在写多周期指标时,直接把高周期数值硬塞进当前图表缓冲区,导致四根 M15 柱线显示同一值却不知根源。更糟的是混淆不同绘图样式的索引顺序,调试半天找不到哪个箭头对应哪条线。跨周期显示若不从类层处理,后期维护就是灾难。

◍ 指标缓冲区整数属性的枚举骨架

在 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 时对照传参。

MQL5 / C++
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 按线宽排。写自定义指标面板时,若要根据线型或时间框架重排缓冲区,直接引用这些枚举值即可,外汇与贵金属品种波动剧烈、杠杆高,改完请在策略测试器用历史数据验证排序逻辑是否错位。

MQL5 / C++
  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 索引是否和枚举语义一致,外汇与贵金属波动剧烈,指标缓冲区错位可能导致信号滞后,交易属高风险。

MQL5 / C++
  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 这种计算缓冲就不会误改显示参数。

MQL5 / C++
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 终端看清每个缓冲的角色,不必逐行翻代码。 外汇与贵金属品种波动剧烈,这类底层封装仅降低开发出错概率,不预示任何信号胜率,实盘前请在策略测试器用历史数据验证。

MQL5 / C++
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 改错一个,编译器会立刻报数组越界,比看文档直观。

MQL5 / C++
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
把跨周期校验交给小布盯盘
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到当前图表与缓冲区时间帧的匹配状态,你只需专注策略逻辑而非对齐柱线。

常见问题

每种绘图样式有独立索引序列。例如第一个箭头缓冲区索引 0,随后建的线条缓冲区索引也是 0,因为它是该样式中第一个。
落在 H1 柱线时间范围内的每根 M15 柱线会填充同一 H1 数值,通常由类自动处理映射。
可按图形系列名称、时间、数据窗口索引及集合列表索引访问,也能取最后创建的对象直接设属性。
可以,小布盯盘的品种页会标出图表周期与缓冲区 period 属性的差异,省去你手动打印比对。
自构建 2430 起上限从 1000 升至 5000,操控品种列表的类需相应扩展以避免截断。