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

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

(3/3)· 从缓冲区索引到多周期映射,46 节实操把函数库时间序列收口为可复用骨架

含代码示例 第 3/3 篇
很多人写跨周期指标时手动对齐柱线时间,结果 M15 图上塞 H1 数值错位到怀疑人生。缓冲区索引和周期属性没理清,每增加一种绘图样式就要重写一大段访问逻辑。本篇把这套脏活交给函数库,你只设时间帧就行。
本章目录
  1. 缓冲区对象的三类属性读写骨架
  2. 缓冲区基类的状态开关与属性 setter
  3. 缓冲区绘制属性的类封装细节
  4. 指标缓冲区的索引与取数接口
  5. 缓冲区初始化与序列填充的底层接口
  6. 从时序对象倒序灌入指标缓冲
  7. 缓冲区构造里的状态触发与配色计数
  8. 缓冲区初始化时的属性绑定与容错
  9. 缓冲区与绘图属性的绑定落点
  10. 缓冲区绘制的几个底层拦截点
  11. 缓冲区着色的安全闸门与文件装配
  12. 缓冲区集合的索引与创建接口
  13. 缓冲区工厂方法的分类与取用
  14. 指标缓冲区集合的初始化与赋值接口
  15. 给自定义图形缓冲写值与染色
  16. 缓冲区集合的清理与初始化接口
  17. 多周期缓冲区的取数与绘图索引
  18. 缓冲区检索与批量初始化的底层接口
  19. 缓冲区集合的初始化与配色逻辑
  20. 箭头缓冲的跨周期清写逻辑
  21. 箭头缓冲与跨周期柱体类型的接口封装
  22. 用标签和时间框架精准抓缓冲区
  23. 缓冲区的取用与新建接口
  24. 缓冲区封装的几种图形与计算入口
  25. 指标缓冲区的初始化与按类型取数接口
  26. 缓冲区读写接口的两种取数路径
  27. 给自定义图形缓冲喂数据的接口族
  28. 往自定义蜡烛缓冲写数据的接口细节
  29. 按图形类别取色与定位缓冲序号
  30. 给九类绘制缓冲单独指定颜色索引
  31. 按索引抹掉缓冲区的图形与配色
  32. 交易类对象的全局参数注入接口
  33. 交易对象的声音与初始化接口
  34. 按索引与时间取棒线类型的双重载
  35. 引擎层缓冲区写入的几组重载接口
  36. 直方图与填充缓冲的写入接口
  37. 给缓冲层灌K线与改色的接口细节
  38. 逐类图形缓冲区的着色与清空接口
  39. 缓冲区清理与集合初始化的底层接口
  40. 把单周期指标改成多周期测试件
  41. 缓冲显示与数据序列化的落地写法
  42. 把 OnCalculate 数组塞进结构体的收尾动作
  43. 把 OnCalculate 数据搬进时间序列结构
  44. 指标主循环里如何按K线方向刷缓冲
  45. 多形态缓冲区的条件写入逻辑
  46. 缓冲区集合类之后要补的指标操作

「缓冲区对象的三类属性读写骨架」

在 MT5 自定义指标里,每个绘图缓冲区(buffer)本质上是一个带类型的属性容器。把整数、浮点、字符串三类属性分开存,是为了避免联合体(union)带来的隐式类型踩踏,也方便后续按属性排序或比对。 下面这段类方法给出了最底层的存取接口:整数属性直接按枚举下标写入 m_long_prop 数组,而双精度与字符串属性需要先经 IndexProp() 做一次映射再落库。 [CODE] 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; [/CODE] 逐行拆解:前 3 行 SetProperty 是写入口,整数走裸数组、双精与字符串走映射数组,差异就在是否调用 IndexProp;紧接着 3 个 GetProperty 是对称读接口,const 限定保证不改动对象状态。 三个 SupportProperty 虚函数默认全返回 true,意味着派生类可以按需关闭某些属性支持——比如某类缓冲不想暴露字符串描述,重写返回 false 即可。Compare 带 mode 参数默认 0,用于列表排序;IsEqual 则做全属性相等判断,常在缓冲去重时调用。 开 MT5 建个继承 CBuffer 的子类,把 SupportProperty(ENUM_BUFFER_PROP_STRING) 改成返回 false,编译后观察调用 GetPropertyDescription 时的行为,能直观验证这套属性门禁机制。外汇与贵金属指标开发属高风险环境,参数误写可能导致图形渲染异常或回测偏差。

MQL5 / C++
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)]; }
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;

◍ 缓冲区基类的状态开关与属性 setter

在 MT5 自定义指标架构里,CBuffer 作为缓冲区基类,先把「名字」和「是否参与计算」这两个最基础的开关封装好。SetName() 只做一件事:把传入的 name 写进成员变量 m_name,方便后续在日志里区分不同缓冲。 SetActStateFlag(bool flag) 与 GetActStateFlag() 成对出现,前者写入 m_act_state_trigger,后者只读返回。这个布尔开关决定该缓冲区是否激活——若置 false,后续继承类在 OnCalculate 里通常会跳过它的绘图与统计,能省下一部分 CPU 开销。 SetSymbol() 和 SetTimeframe() 走的是统一的 SetProperty 通道:前者用 BUFFER_PROP_SYMBOL 绑定具体品种(如 "XAUUSD"),后者用 BUFFER_PROP_TIMEFRAME 绑定周期(如 PERIOD_H1)。也就是说,一个缓冲区可以脱离当前图表,去抓别的符号、别的时间帧的数据,这是做多周期共振指标的关键入口。 外汇与贵金属波动剧烈、杠杆风险高,这类跨周期取数逻辑在实盘前务必用策略测试器跑一遍,确认不同品种点差下的触发延迟在可接受范围。

MQL5 / C++
class=class="str">"cmt">//--- Set the buffer name
   class="type">void         SetName(class="kw">const class="type">class="kw">string name)                                                    { this.m_name=name;   }
class=class="str">"cmt">//--- (class="num">1) Set and(class="num">2) class="kw">return the buffer status class="kw">switch flag
   class="type">void         SetActStateFlag(class="kw">const class="type">bool flag)                                              { this.m_act_state_trigger=flag;     }
   class="type">bool         GetActStateFlag(class="type">void)                                                     class="kw">const { class="kw">return this.m_act_state_trigger;  }

class=class="str">"cmt">//--- Default constructor
                  CBuffer(class="type">void){;}
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(class="num">1) the arrow code, (class="num">2) vertical shift of arrows, (class="num">3) symbol, (class="num">4) timeframe, (class="num">5) buffer activity flag
class=class="str">"cmt">//--- (class="num">6) drawing type, (class="num">7) number of initial bars without drawing, (class="num">8) flag of displaying construction values in DataWindow,
class=class="str">"cmt">//--- (class="num">9) shift of the indicator graphical construction along the time axis, (class="num">10) line style, (class="num">11) line width,
class=class="str">"cmt">//--- (class="num">12) total number of colors, (class="num">13) one drawing class="type">class="kw">color, (class="num">14) class="type">class="kw">color of drawing in the specified class="type">class="kw">color index,
class=class="str">"cmt">//--- (class="num">15) drawing colors from the class="type">class="kw">color array, (class="num">16) empty value, (class="num">17) name of the graphical series displayed in DataWindow
   class="kw">virtual class="type">void  SetArrowCode(class="kw">const class="type">uchar code)                                               { class="kw">return;                                                                                      }
   class="kw">virtual class="type">void  SetArrowShift(class="kw">const class="type">int shift)                                               { class="kw">return;                                                                                      }
   class="type">void          SetSymbol(class="kw">const class="type">class="kw">string symbol)                                               { this.SetProperty(BUFFER_PROP_SYMBOL,symbol);                                                }
   class="type">void          SetTimeframe(class="kw">const ENUM_TIMEFRAMES timeframe)                                { this.SetProperty(BUFFER_PROP_TIMEFRAME,timeframe);                                          }

缓冲区绘制属性的类封装细节

在自定义指标里,每一个数据缓冲区(buffer)都不是裸数组,而是一组带属性的绘图单元。CBuffer 类把 MT5 内核的 PlotIndexSetInteger 等调用包了一层,让开发者用成员函数直接改线型、颜色、偏移。 下面这段是 SetDrawType 的实现核心:若缓冲区类型已是 BUFFER_TYPE_CALCULATE(纯计算缓冲、不参与绘图),函数直接 return,避免无意义的绘图属性写入。 [CODE]void CBuffer::SetDrawType(const ENUM_DRAW_TYPE draw_type) { if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE) return; this.SetProperty(BUFFER_PROP_DRAW_TYPE,draw_type); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_TYPE,draw_type); }[/CODE] 逐行拆解:第1行定义函数,接收绘图类型枚举;第3行判断缓冲是否为计算型,是则退出;第5行把类型写进对象自有属性;第6行调用全局函数,把绘图类型同步到 MT5 图表的对应 plot 索引(BUFFER_PROP_INDEX_PLOT 决定是第几个画布序列)。 其余如 SetShift、SetWidth、SetColor 等,本质都是对 BUFFER_PROP_* 属性或 PlotIndexSetInteger 的封装。实盘调参时,若发现某条线不显示,先确认缓冲类型不是 CALCULATE,再查 INDEX_PLOT 是否越界——这是 MT5 指标调试里高频踩的点。外汇与贵金属杠杆高,指标仅作辅助,信号失效概率不低。

MQL5 / C++
class="type">void CBuffer::SetDrawType(class="kw">const ENUM_DRAW_TYPE draw_type)
  {
   if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE)
      class="kw">return;
   this.SetProperty(BUFFER_PROP_DRAW_TYPE,draw_type);
   ::PlotIndexSetInteger((class="type">int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_TYPE,draw_type);
  }

「指标缓冲区的索引与取数接口」

在 MT5 自定义指标的类封装里,缓冲区并非只是一维数组,它带着绘图序号、基准线序号、配色序号等元数据。下面这组 getter 直接通过 GetProperty 读取缓冲区的内部属性,返回的是 int 或 ENUM_TIMEFRAMES 类型。 IndexPlot 返回该缓冲区对应的绘图线序号(BUFFER_PROP_INDEX_PLOT),IndexBase 返回基准线序号(BUFFER_PROP_INDEX_BASE),IndexColor 返回颜色索引序号(BUFFER_PROP_INDEX_COLOR)。IndexNextBaseBuffer 与 IndexNextPlotBuffer 则给出链式缓冲区的下一段基准/绘图缓冲区序号,做多缓冲叠加指标时会用到。 Timeframe 返回该缓冲区绑定的周期,类型是 ENUM_TIMEFRAMES,说明一个指标对象内不同缓冲区可以挂靠不同周期数据。GetDataTotal(0) 取默认缓冲区的数组长度,GetDataBufferValue(idx, series) 按缓冲区和序列下标取 double 数值,GetColorBufferValueIndex/Color 则分别取颜色索引与颜色值。 写指标时若要把多周期数据画到同一副图,先用 IndexNextPlotBuffer 确认下一绘图缓冲序号,再用 SetBufferValue 填数,否则缓冲区错位会导致 MT5 终端报 Array out of range 而强制卸载指标。

MQL5 / C++
class="type">int IndexPlot(class="type">void) class="kw">const { class="kw">return (class="type">int)this.GetProperty(BUFFER_PROP_INDEX_PLOT); }
class="type">int IndexBase(class="type">void) class="kw">const { class="kw">return (class="type">int)this.GetProperty(BUFFER_PROP_INDEX_BASE); }
class="type">int IndexColor(class="type">void) class="kw">const { class="kw">return (class="type">int)this.GetProperty(BUFFER_PROP_INDEX_COLOR); }
class="type">int IndexNextBaseBuffer(class="type">void) class="kw">const { class="kw">return (class="type">int)this.GetProperty(BUFFER_PROP_INDEX_NEXT_BASE); }
class="type">int IndexNextPlotBuffer(class="type">void) class="kw">const { class="kw">return (class="type">int)this.GetProperty(BUFFER_PROP_INDEX_NEXT_PLOT); }
ENUM_TIMEFRAMES Timeframe(class="type">void) class="kw">const { class="kw">return (ENUM_TIMEFRAMES)this.GetProperty(BUFFER_PROP_TIMEFRAMES); }
class=class="str">"cmt">//--- Return the size of the data buffer array
class="kw">virtual class="type">int GetDataTotal(class="kw">const class="type">uint buffer_index=class="num">0) class="kw">const;
class=class="str">"cmt">//--- Return the value from the specified index of the specified(class="num">1) data, (class="num">2) class="type">class="kw">color index and(class="num">3) class="type">class="kw">color buffer arrays
class="type">class="kw">double GetDataBufferValue(class="kw">const class="type">uint buffer_index,class="kw">const class="type">uint series_index) class="kw">const;
class="type">int GetColorBufferValueIndex(class="kw">const class="type">uint series_index) class="kw">const;
class="type">class="kw">color GetColorBufferValueColor(class="kw">const class="type">uint series_index) class="kw">const;
class=class="str">"cmt">//--- Set the value to the specified index of the specified(class="num">1) data and(class="num">2) class="type">class="kw">color buffer arrays
class="type">void SetBufferValue(class="kw">const class="type">uint buffer_index,class="kw">const class="type">uint series_index,class="kw">const class="type">class="kw">double value);
class="type">void SetBufferColorIndex(class="kw">const class="type">uint series_index,class="kw">const class="type">uchar color_index);

◍ 缓冲区初始化与序列填充的底层接口

在自定义指标的数据缓冲区管理里,CBuffer 类暴露了两组 InitializeAll 重载和两组 FillAsSeries 重载,分别负责把数据缓冲和颜色缓冲刷成指定值或从序列对象灌入数据。 InitializeAll(const double value, const uchar color_index) 用传入的 value 初始化全部数据数组,并按 color_index 初始化颜色数组;若 color_index 超出 ColorsTotal()-1 的范围则回退为 0。无参版本 InitializeAll(void) 则统一用对象自身的 EmptyValue() 清空数据缓冲,颜色缓冲固定填 0。 FillAsSeries 的两个重载让我们能把某根 K 线的属性(如收盘价、成交量,由 ENUM_SORT_BAR_MODE 指定)直接映射到指定 buffer_index,或从外部 double 数组拷贝。注意源码里对 series==NULL 或属性为字符串类型(property > FIRST_BAR_STR_PROP-1)做了早退,避免非法填充。 开 MT5 把下面代码贴进类实现,编译后接一段 Print(GetProperty(BUFFER_PROP_NUM_DATAS)) 就能确认你的缓冲区数量是否和预期一致;外汇与贵金属行情跳动快,缓冲初始化遗漏可能在高频切换周期时露出空值缺口,属高风险操作环节。

MQL5 / C++
class="type">void CBuffer::InitializeAll(class="kw">const class="type">class="kw">double value,class="kw">const class="type">uchar color_index)
  {
   for(class="type">int i=class="num">0;i<this.GetProperty(BUFFER_PROP_NUM_DATAS);i++)
      ::ArrayInitialize(this.DataBuffer[i].Array,value);
   if(this.Status()!=BUFFER_STATUS_FILLING && this.TypeBuffer()!=BUFFER_TYPE_CALCULATE)
      ::ArrayInitialize(this.ColorBufferArray,(color_index>this.ColorsTotal()-class="num">1 ? class="num">0 : color_index));
  }
class="type">void CBuffer::InitializeAll(class="type">void)
  {
   for(class="type">int i=class="num">0;i<this.GetProperty(BUFFER_PROP_NUM_DATAS);i++)
      ::ArrayInitialize(this.DataBuffer[i].Array,this.EmptyValue());
   if(this.Status()!=BUFFER_STATUS_FILLING && this.TypeBuffer()!=BUFFER_TYPE_CALCULATE)
      ::ArrayInitialize(this.ColorBufferArray,class="num">0);
  }
class="type">void CBuffer::FillAsSeries(class="kw">const class="type">int buffer_index,CSeriesDE *series,class="kw">const ENUM_SORT_BAR_MODE class="kw">property)
  {
   if(series==NULL || class="kw">property>FIRST_BAR_STR_PROP-class="num">1)
      class="kw">return;
  }

从时序对象倒序灌入指标缓冲

在自定义指标里把 CBar 集合写进绘图缓冲,核心不是顺时间跑,而是从当前 bar 往历史回退。下面这段逻辑先拿 timeseries 的列表指针,空列表或长度为 0 直接 return,避免后续越界。 循环变量 i 从 total-1 起步,到 WRONG_VALUE 止,同时用 ::IsStopped() 拦掉终端退出信号;每次用 list.At(i) 取柱对象,按 property 枚举区间判断走整型、双精度还是空值分支,再算 n=total-1-i 把“当前序号”翻成缓冲下标。 CBuffer::FillAsSeries 则是另一条路:直接吃一个 double 数组,ArraySize 为 0 就撤,同样倒序把 array[i] 映射到缓冲的 n 位。两个方法都依赖 SetBufferValue(buffer_index,n,value) 落盘,MT5 上你改 buffer_index 就能切换画哪条线。 外汇与贵金属行情跳空频繁,这类倒序拷贝在品种切换时可能漏掉非交易时段 bar,实盘前建议在 EURUSD 的 M1 上跑一遍看缓冲首尾是否对齐。

MQL5 / C++
  CArrayObj *list=series.GetList();
  if(list==NULL || list.Total()==class="num">0)
     class="kw">return;
  class="type">int total=list.Total();
  class="type">int n=class="num">0;
  for(class="type">int i=total-class="num">1;i>WRONG_VALUE && !::IsStopped();i--)
     {
     CBar *bar=list.At(i);
     class="type">class="kw">double value=
        (bar==NULL ? this.EmptyValue():
         class="kw">property<FIRST_BAR_DBL_PROP ? bar.GetProperty((ENUM_BAR_PROP_INTEGER)class="kw">property):
         class="kw">property<FIRST_BAR_STR_PROP ? bar.GetProperty((ENUM_BAR_PROP_DOUBLE)class="kw">property):
         this.EmptyValue()
        );
     n=total-class="num">1-i;
     this.SetBufferValue(buffer_index,n,value);
     }
  }
class="type">void CBuffer::FillAsSeries(class="kw">const class="type">int buffer_index,class="kw">const class="type">class="kw">double &array[])
  {
  class="type">int total=::ArraySize(array);
  if(total==class="num">0)
     class="kw">return;
  class="type">int n=class="num">0;
  for(class="type">int i=total-class="num">1;i>WRONG_VALUE && !::IsStopped();i--)
     {
     n=total-class="num">1-i;
     this.SetBufferValue(buffer_index,n,array[i]);
     }
  }

「缓冲区构造里的状态触发与配色计数」

在自定义指标的集合缓冲区封装里,构造函数一进来就把 m_act_state_trigger 置为 true,这意味着该缓冲区对象在实例化后就处于活动触发状态,后续绘制逻辑会直接读取这个开关。 状态决定绘制类型:当 TypeBuffer 或 Status 任一为假时用 DRAW_NONE;状态为 BUFFER_STATUS_FILLING 时切到 DRAW_FILLING;否则拿 Status 值加 8 强转成 ENUM_DRAW_TYPE。这套映射把内部状态机直接翻译成 MT5 的图形绘制枚举。 颜色索引数那行是容易看漏的:状态大于 BUFFER_STATUS_NONE 才分配索引,且填充态给 2、其他有效态给 1,否则为 0。配合后面 BUFFER_PROP_COLOR 写死 clrRed,你开 MT5 挂上这类指标会看到默认全红,要换色得在构造后覆写该属性。 外汇与贵金属品种波动大、滑点随机,这类底层缓冲区若状态位算错,图形可能整段不画或填充错位,实盘前务必在策略测试器用历史数据跑一遍验证。

MQL5 / C++
class="kw">const class="type">int width,
class="kw">const class="type">class="kw">string label)
{
 this.m_type=COLLECTION_BUFFERS_ID;
 this.m_act_state_trigger=true;
class=class="str">"cmt">//--- Save integer properties
 this.m_long_prop[BUFFER_PROP_STATUS]                       = buffer_status;
 this.m_long_prop[BUFFER_PROP_TYPE]                         = buffer_type;
 ENUM_DRAW_TYPE type=
   (
     !this.TypeBuffer() || !this.Status() ? DRAW_NONE      :
     this.Status()==BUFFER_STATUS_FILLING ? DRAW_FILLING :
     ENUM_DRAW_TYPE(this.Status()+class="num">8)
   );
 this.m_long_prop[BUFFER_PROP_DRAW_TYPE]                   = type;
 this.m_long_prop[BUFFER_PROP_TIMEFRAME]                   = PERIOD_CURRENT;
 this.m_long_prop[BUFFER_PROP_ACTIVE]                      = true;
 this.m_long_prop[BUFFER_PROP_ARROW_CODE]                  = 0x9F;
 this.m_long_prop[BUFFER_PROP_ARROW_SHIFT]                 = class="num">0;
 this.m_long_prop[BUFFER_PROP_DRAW_BEGIN]                  = class="num">0;
 this.m_long_prop[BUFFER_PROP_SHOW_DATA]                   = (buffer_type>BUFFER_TYPE_CALCULATE ? true : class="kw">false);
 this.m_long_prop[BUFFER_PROP_SHIFT]                       = class="num">0;
 this.m_long_prop[BUFFER_PROP_LINE_STYLE]                  = STYLE_SOLID;
 this.m_long_prop[BUFFER_PROP_LINE_WIDTH]                  = width;
 this.m_long_prop[BUFFER_PROP_COLOR_INDEXES]               = (this.Status()>BUFFER_STATUS_NONE ? (this.Status()!=BUFFER_STATUS_FILLING ? class="num">1 : class="num">2) : class="num">0);
 this.m_long_prop[BUFFER_PROP_COLOR]                       = clrRed;
 this.m_long_prop[BUFFER_PROP_NUM_DATAS]                   = num_datas;
 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);

◍ 缓冲区初始化时的属性绑定与容错

在自定义指标缓冲区完成参数配置时,需要先算清相邻缓冲区的索引偏移。当状态为 FILLING 或类型属于 CALCULATE 时,下一个基础索引直接沿用当前颜色索引;否则要额外加 1,避免绘图槽位错位。 真实属性落地时,非计算型缓冲区才写入 EMPTY_VALUE,计算型填 0;字符串属性里 Symbol() 无条件绑定,Label 仅对非计算缓冲区生效,计算缓冲区置 NULL。 数组扩容是硬关卡:DataBuffer 若 ArrayResize 返回 WRONG_VALUE,立刻 Print 报错并带 GetLastError() 码;颜色数组只对 TypeBuffer()>BUFFER_TYPE_CALCULATE 的缓冲区扩容,失败同样打印。DRAW_FILLING 状态会强制塞入 clrBlue(0) 与 clrRed(1) 两个默认色。 最后用循环把 DataBuffer 逐个绑定到指标缓冲槽,索引 = BUFFER_PROP_INDEX_BASE + i。开 MT5 把这段接进你的 CiCustom 派生类,改 BUFFER_STATUS_FILLING 的默认双色,能直接看到填充区配色变化。

MQL5 / C++
this.m_long_prop[BUFFER_PROP_INDEX_NEXT_BASE]                 = this.GetProperty(BUFFER_PROP_INDEX_COLOR)+
                                                                     (this.Status()==BUFFER_STATUS_FILLING || this.TypeBuffer()==BUFFER_TYPE_CALCULATE ? class="num">0 : class="num">1);
this.m_long_prop[BUFFER_PROP_INDEX_NEXT_PLOT]                 = (this.TypeBuffer()>BUFFER_TYPE_CALCULATE ? index_plot+class="num">1 : index_plot);

class=class="str">"cmt">//--- Save real properties
this.m_double_prop[this.IndexProp(BUFFER_PROP_EMPTY_VALUE)] = (this.TypeBuffer()>BUFFER_TYPE_CALCULATE ? EMPTY_VALUE : class="num">0);
class=class="str">"cmt">//--- Save class="type">class="kw">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 ? label : NULL);
class=class="str">"cmt">//--- If failed to change the size of the indicator buffer array, display the appropriate message indicating the class="type">class="kw">string
if(::ArrayResize(this.DataBuffer,(class="type">int)this.GetProperty(BUFFER_PROP_NUM_DATAS))==WRONG_VALUE)
   ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_DRAWING_ARRAY_RESIZE),". ",CMessage::Text(MSG_LIB_SYS_ERROR),": ",(class="type">class="kw">string)::GetLastError());

class=class="str">"cmt">//--- If failed to change the size of the class="type">class="kw">color array(only for a non-calculated buffer), display the appropriate message indicating the class="type">class="kw">string
if(this.TypeBuffer()>BUFFER_TYPE_CALCULATE)
   if(::ArrayResize(this.ArrayColors,(class="type">int)this.ColorsTotal())==WRONG_VALUE)
      ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_COLORS_ARRAY_RESIZE),". ",CMessage::Text(MSG_LIB_SYS_ERROR),": ",(class="type">class="kw">string)::GetLastError());
class=class="str">"cmt">//--- For DRAW_FILLING, fill in the class="type">class="kw">color array with two class="kw">default colors
if(this.Status()==BUFFER_STATUS_FILLING)
   {
   this.SetColor(clrBlue,class="num">0);
   this.SetColor(clrRed,class="num">1);
   }
class=class="str">"cmt">//--- Bind indicator buffers with arrays
class=class="str">"cmt">//--- In a loop by the number of indicator buffers
class="type">int total=::ArraySize(DataBuffer);
for(class="type">int i=class="num">0;i<total;i++)
   {
   class=class="str">"cmt">//--- calculate the index of the next array and
   class=class="str">"cmt">//--- bind the indicator buffer by the calculated index with the dynamic array
   class=class="str">"cmt">//--- located by the i loop index in the DataBuffer array
   class="type">int index=(class="type">int)this.GetProperty(BUFFER_PROP_INDEX_BASE)+i;

缓冲区与绘图属性的绑定落点

在自定义指标初始化阶段,缓冲区必须和指标绘图层显式绑定,否则 MT5 终端不会把它当作可绘制序列。核心动作是先按缓冲区类型调用 SetIndexBuffer:若是数据缓冲就挂到 INDICATOR_DATA,计算缓冲则挂到 INDICATOR_CALCULATIONS,随后立即对数组执行 ArraySetAsSeries(...,true) 以对齐时间序列索引方向。 颜色缓冲只在非填充、非计算型缓冲区下才需要绑定,用 INDICATOR_COLOR_INDEX 挂接并同样设为序列方向。若命中 BUFFER_TYPE_CALCULATE,函数直接 return,因为计算缓冲不参与前端绘制。 绘图整型参数靠一连串 PlotIndexSetInteger 落地:PLOT_DRAW_TYPE 决定线型或箭头,PLOT_ARROW / PLOT_ARROW_SHIFT 控制箭头代号与偏移,PLOT_DRAW_BEGIN 设定最少预热根数(比如设 50 就意味着前 50 根不画),PLOT_LINE_WIDTH 可取 1~5 影响线宽。 双精度与字符串属性收尾:PlotIndexSetDouble 写 PLOT_EMPTY_VALUE(常见 0 或 EMPTY_VALUE 宏),PlotIndexSetString 写 PLOT_LABEL 决定图例文字。开 MT5 把这段接进你的 CiCustom 类,改 PLOT_DRAW_BEGIN 数值就能直观看到预热区长短变化。外汇与贵金属波动剧烈,指标仅辅助判势,实盘仍属高风险。

MQL5 / C++
::SetIndexBuffer(index,this.DataBuffer[i].Array,(this.TypeBuffer()==BUFFER_TYPE_DATA ? INDICATOR_DATA : INDICATOR_CALCULATIONS));
class=class="str">"cmt">//--- Set indexation flag as in the timeseries to all buffer arrays
::ArraySetAsSeries(this.DataBuffer[i].Array,true);
 }
class=class="str">"cmt">//--- Bind the class="type">class="kw">color buffer with the array(only for a non-calculated buffer and not for the filling buffer)
 if(this.Status()!=BUFFER_STATUS_FILLING && this.TypeBuffer()!=BUFFER_TYPE_CALCULATE)
 {
 ::SetIndexBuffer((class="type">int)this.GetProperty(BUFFER_PROP_INDEX_COLOR),this.ColorBufferArray,INDICATOR_COLOR_INDEX);
 ::ArraySetAsSeries(this.ColorBufferArray,true);
 }
class=class="str">"cmt">//--- If this is a calculated buffer, all is done
 if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE)
 class="kw">return;
class=class="str">"cmt">//--- Set integer parameters of the drawn buffer
 ::PlotIndexSetInteger((class="type">int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_TYPE,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_DRAW_TYPE));
 ::PlotIndexSetInteger((class="type">int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_ARROW,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_ARROW_CODE));
 ::PlotIndexSetInteger((class="type">int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_ARROW_SHIFT,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_ARROW_SHIFT));
 ::PlotIndexSetInteger((class="type">int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_BEGIN,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_DRAW_BEGIN));
 ::PlotIndexSetInteger((class="type">int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHOW_DATA,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_SHOW_DATA));
 ::PlotIndexSetInteger((class="type">int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHIFT,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_SHIFT));
 ::PlotIndexSetInteger((class="type">int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_STYLE,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_LINE_STYLE));
 ::PlotIndexSetInteger((class="type">int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_WIDTH,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_LINE_WIDTH));
 this.SetColor((class="type">class="kw">color)this.GetProperty(BUFFER_PROP_COLOR));
class=class="str">"cmt">//--- Set real buffer parameters
 ::PlotIndexSetDouble((class="type">int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_EMPTY_VALUE,this.GetProperty(BUFFER_PROP_EMPTY_VALUE));
class=class="str">"cmt">//--- Set class="type">class="kw">string buffer parameters
 ::PlotIndexSetString((class="type">int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LABEL,this.GetProperty(BUFFER_PROP_LABEL));
}

「缓冲区绘制的几个底层拦截点」

在自定义指标的类封装里,CBuffer 对绘图属性做了几道前置拦截,直接决定 MT5 图表和数据窗口的表现。 SetDrawBegin 遇到 BUFFER_TYPE_CALCULATE 类型会直接 return,这意味着纯计算缓冲不进图表也不进 DataWindow,省掉无谓渲染。其余类型则同时写缓冲区属性与 PLOT_DRAW_BEGIN,从索引 value 处才开始画。 SetColorNumbers 先卡 number>IND_COLORS_TOTAL 与计算型缓冲,越界或计算型一律不处理;未填充状态下默认给 2 个颜色槽,填充后按传入 number 重设并 ArrayResize 颜色数组。 SetBufferValue 的边界处理值得盯:series_index 超了数据总长就钳到末位,但 data_index 算出来小于 0 直接 return,不写数组。外汇与贵金属指标开发属高风险环境,参数误设可能导致图形错位却无报错。 把这几段直接塞进你的指标类里,开 MT5 用不同 buffer 类型调一次 SetDrawBegin(5),能在数据窗口看到前 5 根被跳过。

MQL5 / C++
class="type">void CBuffer::SetDrawBegin(class="kw">const class="type">int value)
  {
   if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE)
      class="kw">return;
   this.SetProperty(BUFFER_PROP_DRAW_BEGIN,value);
   ::PlotIndexSetInteger((class="type">int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_BEGIN,value);
  }
class="type">void CBuffer::SetColorNumbers(class="kw">const class="type">int number)
  {
   if(number>IND_COLORS_TOTAL || this.TypeBuffer()==BUFFER_TYPE_CALCULATE)
      class="kw">return;
   class="type">int n=(this.Status()!=BUFFER_STATUS_FILLING ? number : class="num">2);
   this.SetProperty(BUFFER_PROP_COLOR_INDEXES,n);
   ::ArrayResize(this.ArrayColors,n);
   ::PlotIndexSetInteger((class="type">int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_COLOR_INDEXES,n);
  }
class="type">void CBuffer::SetBufferValue(class="kw">const class="type">uint buffer_index,class="kw">const class="type">uint series_index,class="kw">const class="type">class="kw">double value)
  {
   if(this.GetDataTotal(buffer_index)==class="num">0)
      class="kw">return;
   class="type">int correct_buff_index=this.GetCorrectIndexBuffer(buffer_index);
   class="type">int data_total=this.GetDataTotal(buffer_index);
   class="type">int data_index=((class="type">int)series_index<data_total ? (class="type">int)series_index : data_total-class="num">1);
   if(data_index<class="num">0)
      class="kw">return;
   this.DataBuffer[correct_buff_index].Array[data_index]=value;
  }

◍ 缓冲区着色的安全闸门与文件装配

在自定义指标里给某根 K 线单独指定颜色索引时,先得拦掉非法状态。下面这段逻辑会在缓冲区还没数据、颜色号越界、或缓冲区正处于 FILLING / NONE 状态时直接 return,避免往未就绪的数组写脏数据。

if(this.GetDataTotal(0)==0color_index>this.ColorsTotal()-1this.Status()==BUFFER_STATUS_FILLINGthis.Status()==BUFFER_STATUS_NONE)

return; int data_total=this.GetDataTotal(0); int data_index=((int)series_index<data_total ? (int)series_index : data_total-1); if(::ArraySize(this.ColorBufferArray)==0) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INVALID_PROPERTY_BUFF)); if(data_index<0) return; this.ColorBufferArray[data_index]=color_index; 注意 data_index 被夹在 [0, data_total-1] 区间内:当 series_index 超过已填充数据量时,自动落到最后一根,这能防止历史回看越界。但 data_index<0 的二次拦截不能省,空缓冲或异常索引会直接返回。 整套缓冲区集合靠 CBuffersCollection 类托管,内部用 CListObj 挂接各类 Buffer 对象(箭头、线、区段、柱状、zigzag、填充等)。它持有一个 CTimeSeriesCollection 指针,用来对齐时间序列。 类实现底部那一串 include 是装配清单:从 ListObj.mqh 到 BufferCalculate.mqh、TimeSeriesCollection.mqh 共 11 个头文件。少引一个,编译期就会报 Buffer 子类未定义——开 MT5 自建指标工程时,照这个清单核对引用路径最省事。外汇与贵金属指标开发杠杆高、波动剧烈,回测与实盘差异可能很大,任何缓冲区越界都只会在运行时随机崩 EA,务必在本地先跑最小样例验证。

MQL5 / C++
if(this.GetDataTotal(class="num">0)==class="num">0 || color_index>this.ColorsTotal()-class="num">1 || this.Status()==BUFFER_STATUS_FILLING || this.Status()==BUFFER_STATUS_NONE)
   class="kw">return;
class="type">int data_total=this.GetDataTotal(class="num">0);
class="type">int data_index=((class="type">int)series_index<data_total ? (class="type">int)series_index : data_total-class="num">1);
if(::ArraySize(this.ColorBufferArray)==class="num">0)
   ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INVALID_PROPERTY_BUFF));
if(data_index<class="num">0)
   class="kw">return;
this.ColorBufferArray[data_index]=color_index;

缓冲区集合的索引与创建接口

在 MT5 自定义指标里,多缓冲区的管理通常交给一个集合类。下面这组方法暴露了三个关键索引:最后绘制的缓冲、下一个待绘缓冲、以及基础缓冲的位置,方便在 OnCalculate 里动态定位数据槽。 GetIndexLastPlot 返回最近一次绘制的缓冲序号,GetIndexNextPlot 给出下一个可绘制的空位,GetIndexNextBase 则指向基础数据缓冲。实测中,若指标已绘制 3 条线,NextPlot 通常返回 3(从 0 计),继续 CreateLine 就会占用该槽。 CreateBuffer 按 ENUM_BUFFER_STATUS 状态新建缓冲并挂到内部链表;其包装函数覆盖了箭头、线、段、零轴直方图、双缓冲直方图、zigzag、双价位填色、 bars、蜡烛等 9 种绘制类型。例如 CreateArrow 只是向 CreateBuffer 传入 BUFFER_STATUS_ARROW,省去手写枚举。 PropertyPlotsTotal 与 PropertyBuffersTotal 分别统计已绘制缓冲数和集合内所有数组总数。若你用 2 个直方图加 1 条线,前者返回 3,后者可能返回 4(双缓冲直方图占 2 个数组)。开 MT5 把这段声明塞进类头,编译后就能用 GetList 拿到缓冲对象数组做遍历。

MQL5 / C++
class=class="str">"cmt">//--- Return the index of the(class="num">1) last, (class="num">2) next drawn and(class="num">3) basic buffer
   class="type">int                 GetIndexLastPlot(class="type">void);
   class="type">int                 GetIndexNextPlot(class="type">void);
   class="type">int                 GetIndexNextBase(class="type">void);
class=class="str">"cmt">//--- Create a new buffer object and place it to the collection list
   class="type">bool                CreateBuffer(ENUM_BUFFER_STATUS status);
class=class="str">"cmt">//--- Get data of the necessary timeseries and bars for working with a single buffer bar, and class="kw">return the number of bars
   class="type">int                 GetBarsData(CBuffer *buffer,class="kw">const class="type">int series_index,class="type">int &index_bar_period);
class="kw">public:
class=class="str">"cmt">//--- Return(class="num">1) oneself and(class="num">2) the timeseries list
   CBuffersCollection  *GetObject(class="type">void)             { class="kw">return &this;                    }
   CArrayObj           *GetList(class="type">void)               { class="kw">return &this.m_list;             }
class=class="str">"cmt">//--- Return the number of(class="num">1) drawn buffers, (class="num">2) all arrays used to build all buffers in the collection
   class="type">int                 PropertyPlotsTotal(class="type">void);
   class="type">int                 PropertyBuffersTotal(class="type">void);

class=class="str">"cmt">//--- Create the new buffer(class="num">1) "Drawing with arrows", (class="num">2) "Line", (class="num">3) "Sections", (class="num">4) "Histogram from the zero line", 
class=class="str">"cmt">//--- (class="num">5) "Histogram on two indicator buffers", (class="num">6) "Zigzag", (class="num">7) "Color filling between two levels",
class=class="str">"cmt">//--- (class="num">8) "Display as bars", (class="num">9) "Display as candles", calculated buffer
   class="type">bool                CreateArrow(class="type">void)            { class="kw">return this.CreateBuffer(BUFFER_STATUS_ARROW);    }
   class="type">bool                CreateLine(class="type">void)             { class="kw">return this.CreateBuffer(BUFFER_STATUS_LINE);     }
   class="type">bool                CreateSection(class="type">void)          { class="kw">return this.CreateBuffer(BUFFER_STATUS_SECTION);  }
   class="type">bool                CreateHistogram(class="type">void)        { class="kw">return this.CreateBuffer(BUFFER_STATUS_HISTOGRAM); }
   class="type">bool                CreateHistogram2(class="type">void)       { class="kw">return this.CreateBuffer(BUFFER_STATUS_HISTOGRAM2); }

「缓冲区工厂方法的分类与取用」

在 MT5 自定义指标框架里,绘图缓冲区的创建被封装成一组语义化的工厂方法。下面这五个方法各自对应一种图形状态,调用后内部都走同一个 CreateBuffer,只是传入的缓冲区状态常量不同。 bool CreateZigZag(void) { return this.CreateBuffer(BUFFER_STATUS_ZIGZAG); } bool CreateFilling(void) { return this.CreateBuffer(BUFFER_STATUS_FILLING); } bool CreateBars(void) { return this.CreateBuffer(BUFFER_STATUS_BARS); } bool CreateCandles(void) { return this.CreateBuffer(BUFFER_STATUS_CANDLES); } bool CreateCalculate(void) { return this.CreateBuffer(BUFFER_STATUS_NONE); } 逐行拆解:第1行 CreateZigZag 返回布尔值,内部用 BUFFER_STATUS_ZIGZAG 状态建一个之字形缓冲;第2行 CreateFilling 用 BUFFER_STATUS_FILLING 建填充区缓冲;第3行 CreateBars 用 BUFFER_STATUS_BARS 建柱条缓冲;第4行 CreateCandles 用 BUFFER_STATUS_CANDLES 建蜡烛缓冲;第5行 CreateCalculate 较特殊,状态传 BUFFER_STATUS_NONE,多用于纯计算、不直接绘图的缓冲。 取用缓冲区时有四种检索维度:按图形序列名 GetBufferByLabel、按周期 GetBufferByTimeframe、按 Plot 索引 GetBufferByPlot、按集合列表序号 GetBufferByListIndex。前两种和最后一种在源码中被高亮,说明它们是跨周期/跨标识调用时的高频入口。 按状态批量取对象则用 GetBufferArrow / GetBufferLine / … / GetBufferCalculate 等系列,参数 number 是从 0 计数的同类型缓冲序号——0 代表该类里第一个被创建的缓冲,1、2 依次往后。InitializePlots 则一次性把所有绘图缓冲填成指定值并置好空值色标,省去循环赋初值。 外汇与贵金属行情跳空频繁,缓冲初始化若漏掉空值设置,可能在 H1 切换到 M5 时画出粘连假柱,开 MT5 把这段贴进类里改两个状态常量就能复现。

MQL5 / C++
class="type">bool CreateZigZag(class="type">void)    { class="kw">return this.CreateBuffer(BUFFER_STATUS_ZIGZAG);  }
class="type">bool CreateFilling(class="type">void)   { class="kw">return this.CreateBuffer(BUFFER_STATUS_FILLING); }
class="type">bool CreateBars(class="type">void)      { class="kw">return this.CreateBuffer(BUFFER_STATUS_BARS);    }
class="type">bool CreateCandles(class="type">void)   { class="kw">return this.CreateBuffer(BUFFER_STATUS_CANDLES); }
class="type">bool CreateCalculate(class="type">void) { class="kw">return this.CreateBuffer(BUFFER_STATUS_NONE);   }

class=class="str">"cmt">//--- Return the buffer by(class="num">1) the graphical series name, (class="num">2) timeframe, (class="num">2) Plot index and(class="num">3) collection list object
CBuffer              *GetBufferByLabel(class="kw">const class="type">class="kw">string plot_label);
CBuffer              *GetBufferByTimeframe(class="kw">const ENUM_TIMEFRAMES timeframe);
CBuffer              *GetBufferByPlot(class="kw">const class="type">int plot_index);
CBuffer              *GetBufferByListIndex(class="kw">const class="type">int index_list);
class=class="str">"cmt">//--- Return buffers by their status by the specified serial number
class=class="str">"cmt">//--- (class="num">0 - the very first created buffer with the ХХХ drawing style, class="num">1,class="num">2,N - subsequent ones)
CBufferArrow         *GetBufferArrow(class="kw">const class="type">int number);
CBufferLine          *GetBufferLine(class="kw">const class="type">int number);
CBufferSection       *GetBufferSection(class="kw">const class="type">int number);
CBufferHistogram     *GetBufferHistogram(class="kw">const class="type">int number);
CBufferHistogram2    *GetBufferHistogram2(class="kw">const class="type">int number);
CBufferZigZag        *GetBufferZigZag(class="kw">const class="type">int number);
CBufferFilling       *GetBufferFilling(class="kw">const class="type">int number);
CBufferBars          *GetBufferBars(class="kw">const class="type">int number);
CBufferCandles       *GetBufferCandles(class="kw">const class="type">int number);
CBufferCalculate     *GetBufferCalculate(class="kw">const class="type">int number);

class=class="str">"cmt">//--- Initialize all drawn buffers by a(class="num">1) specified value, (class="num">2) empty value set for the buffer object
class="type">void InitializePlots(class="kw">const class="type">class="kw">double value,class="kw">const class="type">uchar color_index);

◍ 指标缓冲区集合的初始化与赋值接口

在自封装的指标容器类里,缓冲区不是零散管理的,而是统一收进一个 collection 对象。初始化阶段有两个入口:InitializePlots 负责把绘图属性挂到缓冲区对象上,InitializeCalculates 则把计算缓冲区的数值刷成指定值或空值(EMPTY_VALUE),避免历史残留干扰首根 K 线绘制。 颜色与具体图形类型的写入分两条路。SetColors 接收 color 数组,一次性给集合内所有缓冲区的线条、箭头等上色;而针对箭头、线段、区段、零轴直方图、双缓冲直方图、之字转向、填充等 10 类图形,分别提供了 SetBufferXxxValue 方法,按 number 定位缓冲区和 series_index 定位时序位置写入数值与 color_index。 值得注意,这些 Set 方法都带 as_current 参数且默认 false。若置 true,写入会强制落到当前形成中的柱(实时 tick),对黄金 1 分钟这类跳空频繁品种,可能造成未闭合 K 线上的重绘假信号,实盘使用前建议在 MT5 策略测试器用 2023 年 XAUUSD 数据跑一遍验证。

MQL5 / C++
class="type">void InitializePlots(class="type">void);
class=class="str">"cmt">//--- Initialize all calculated buffers by a(class="num">1) specified value, (class="num">2) empty value set for the buffer object
class="type">void InitializeCalculates(class="kw">const class="type">class="kw">double value);
class="type">void InitializeCalculates(class="type">void);
class=class="str">"cmt">//--- Set class="type">class="kw">color values from the passed class="type">class="kw">color array for all indicator buffers within the collection
class="type">void SetColors(class="kw">const class="type">class="kw">color &array_colors[]);

class=class="str">"cmt">//--- Set the value by the timeseries index for the(class="num">1) arrow, (class="num">2) line, (class="num">3) section, (class="num">4) zero line histogram,
class=class="str">"cmt">//--- (class="num">5) two buffer histogram, (class="num">6) zigzag, (class="num">7) filling, (class="num">8) bar, (class="num">9) candle, (class="num">10) calculated buffer
class="type">void SetBufferArrowValue(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">class="kw">double value,class="kw">const class="type">uchar color_index,class="type">bool as_current=class="kw">false);
class="type">void SetBufferLineValue(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">class="kw">double value,class="kw">const class="type">uchar color_index,class="type">bool as_current=class="kw">false);
class="type">void SetBufferSectionValue(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">class="kw">double value,class="kw">const class="type">uchar color_index,class="type">bool as_current=class="kw">false);
class="type">void SetBufferHistogramValue(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">class="kw">double value,class="kw">const class="type">uchar color_index,class="type">bool as_current=class="kw">false);
class="type">void SetBufferHistogram2Value(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">class="kw">double value1,class="kw">const class="type">class="kw">double value2,class="kw">const class="type">uchar color_index,class="type">bool as_current=class="kw">false);
class="type">void SetBufferZigZagValue(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">class="kw">double value1,class="kw">const class="type">class="kw">double value2,class="kw">const class="type">uchar color_index,class="type">bool as_current=class="kw">false);
class="type">void SetBufferFillingValue(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">class="kw">double value1,class="kw">const class="type">class="kw">double value2,class="type">bool as_current=class="kw">false);

给自定义图形缓冲写值与染色

在 MT5 自建指标里,往缓冲写数据并不只有 CopyBuffer 一种路径。通过 CWnd 系或自定义绘图类的成员函数,可以直接按序列索引塞入 OHLC 与颜色索引,省去中间数组倒手。 下面这组函数覆盖了柱状与蜡烛两种 K 线形态的写入:SetBufferBarsValue 与 SetBufferCandlesValue 都接收 number(缓冲编号)、series_index(第几根)、open/high/low/close 四价、color_index 以及 as_current 默认 false。后者为 true 时把该根当作当前未闭合 K 线处理,适合实时刷新。 [CODE] void SetBufferBarsValue(const int number,const int series_index,const double open,const double high,const double low,const double close,const uchar color_index,bool as_current=false); void SetBufferCandlesValue(const int number,const int series_index,const double open,const double high,const double low,const double close,const uchar color_index,bool as_current=false); void SetBufferCalculateValue(const int number,const int series_index,const double value); [/CODE] 逐行拆解:第1行 SetBufferBarsValue 将一根柱状线的开高低收与颜色写入指定缓冲,as_current 控制是否按当前柱。第2行 SetBufferCandlesValue 同理但针对蜡烛样式。第3行 SetBufferCalculateValue 只写单值,用于线、直方图等无 OHLC 的缓冲。 颜色缓冲的编号规则是:0 代表该绘制风格第一个创建的缓冲,1、2 到 N 为后续缓冲。注释里列了 9 类:箭头、线、段、零轴直方图、双缓冲直方图、之字、填充、柱状、蜡烛。 [CODE] void SetBufferArrowColorIndex(const int number,const int series_index,const uchar color_index); void SetBufferLineColorIndex(const int number,const int series_index,const uchar color_index); void SetBufferSectionColorIndex(const int number,const int series_index,const uchar color_index); void SetBufferHistogramColorIndex(const int number,const int series_index,const uchar color_index); void SetBufferHistogram2ColorIndex(const int number,const int series_index,const uchar color_index); void SetBufferZigZagColorIndex(const int number,const int series_index,const uchar color_index); void SetBufferFillingColorIndex(const int number,const int series_index,const uchar color_index); void SetBufferBarsColorIndex(const int number,const int series_index,const uchar color_index); [/CODE] 逐行拆解:以上 8 行分别对应箭头、线、段、直方图、双缓冲直方图、之字、填充、柱状,各自按缓冲编号与序列索引设定 uchar 型 color_index。调用时若 number 超出实际创建数,MT5 可能静默忽略,建议在 OnInit 里用 PlotIndexGetInteger 核对缓冲总数。 外汇与贵金属杠杆高、跳空频繁,这类自绘缓冲在极端行情下可能滞后 1~2 根才重绘,验证时请在策略测试器用 2023 年 3 月瑞郎行情回放观察染色是否错位。

MQL5 / C++
class="type">void SetBufferBarsValue(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">class="kw">double open,class="kw">const class="type">class="kw">double high,class="kw">const class="type">class="kw">double low,class="kw">const class="type">class="kw">double close,class="kw">const class="type">uchar color_index,class="type">bool as_current=class="kw">false);
class="type">void SetBufferCandlesValue(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">class="kw">double open,class="kw">const class="type">class="kw">double high,class="kw">const class="type">class="kw">double low,class="kw">const class="type">class="kw">double close,class="kw">const class="type">uchar color_index,class="type">bool as_current=class="kw">false);
class="type">void SetBufferCalculateValue(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">class="kw">double value);

class="type">void SetBufferArrowColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">uchar color_index);
class="type">void SetBufferLineColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">uchar color_index);
class="type">void SetBufferSectionColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">uchar color_index);
class="type">void SetBufferHistogramColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">uchar color_index);
class="type">void SetBufferHistogram2ColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">uchar color_index);
class="type">void SetBufferZigZagColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">uchar color_index);
class="type">void SetBufferFillingColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">uchar color_index);
class="type">void SetBufferBarsColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">uchar color_index);

「缓冲区集合的清理与初始化接口」

在自定义指标里管理图形对象时,CBuffersCollection 提供了一组按类型清缓冲区的成员函数,覆盖箭头、线、区段、直方图、双缓冲直方图、 zigzag、填充、柱线和蜡烛共 9 类。 每一类清理函数都接收两个 int 参数:number 指定缓冲在集合中的编号,series_index 指定对应时序柱的索引。比如 ClearBufferCandles(number, series_index) 只清掉某一根 K 线上的蜡烛缓冲,而不会动其他图形元素。 SetBufferCandlesColorIndex 则允许直接改写某根蜡烛缓冲的调色板索引(uchar 类型),用来在运行中切换涨跌色而无需重绘整个缓冲。 构造函数 CBuffersCollection() 之后,引擎会在初始化阶段调用 OnInit(CTimeSeriesCollection *timeseries),把时序集合指针喂给 m_timeseries 成员,这一步若漏掉,后续所有 GetList() 都可能返回空。 GetIndexLastPlot 的实现先取内部列表指针,若 list==NULL 直接 return WRONG_VALUE(通常为 -1),这意味着在缓冲尚未建好时调用会拿到非法值,写 EA 时应先判空再绘图。外汇与贵金属杠杆高,这类底层接口误用可能在回测与实盘产生不同渲染结果,务必在 MT5 策略测试器里分步验证。

MQL5 / C++
class="type">void SetBufferCandlesColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">uchar color_index);
class=class="str">"cmt">//--- Clear buffer data by its index in the list in the specified timeseries bar
class="type">void Clear(class="kw">const class="type">int buffer_list_index,class="kw">const class="type">int series_index);
class=class="str">"cmt">//--- Clear data by the timeseries index for the(class="num">1) arrow, (class="num">2) line, (class="num">3) section, (class="num">4) zero line histogram,
class=class="str">"cmt">//--- (class="num">5) histogram on two buffers, (class="num">6) zigzag, (class="num">7) filling, (class="num">8) bars and(class="num">9) candles
class="type">void ClearBufferArrow(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class="type">void ClearBufferLine(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class="type">void ClearBufferSection(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class="type">void ClearBufferHistogram(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class="type">void ClearBufferHistogram2(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class="type">void ClearBufferZigZag(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class="type">void ClearBufferFilling(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class="type">void ClearBufferBars(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class="type">void ClearBufferCandles(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class=class="str">"cmt">//--- Constructor
CBuffersCollection();
class=class="str">"cmt">//--- Get pointers to the timeseries collection(the method is called in the CollectionOnInit() method of the CEngine object)
class="type">void OnInit(CTimeSeriesCollection *timeseries) { this.m_timeseries=timeseries; }
};
class="type">int CBuffersCollection::GetIndexLastPlot(class="type">void)
  {
  CArrayObj *list=this.GetList();
  if(list==NULL)
     class="kw">return WRONG_VALUE;

◍ 多周期缓冲区的取数与绘图索引

在 MT5 自定义指标里,缓冲区往往跨多个时间周期共存。想拿到「当前被绘制、且 Plot 索引最大」的那条缓冲,先调用 FindBufferMax 按 BUFFER_PROP_INDEX_PLOT 在列表里捞;若返回 WRONG_VALUE 说明列表为空,直接给 0 作为首条缓冲的兜底索引。 拿到索引后从集合里取 CBuffer 对象,空指针就回 WRONG_VALUE,否则吐出它的 IndexPlot()。这段逻辑保证了多缓冲指标在 OnInit 阶段不会因空列表崩掉。 GetBarsData 负责把「当前图表一根 bar」映射到「缓冲区周期那根 bar」里。它先用 m_timeseries 分别取 PERIOD_CURRENT 与 buffer.Timeframe() 的序列,再用 GetBarSeriesFirstFromSeriesSecond 按时间归属对齐;返回的 num_bars 由 PeriodSeconds(周期)/PeriodSeconds(当前) 算得,若为 0 则强制返回 1——这意味着 1 分钟图挂 H1 缓冲时,单根 H1 bar 理论含 60 根 M1 bar。 PropertyPlotsTotal 则用 ByBufferProperty 筛 BUFFER_TYPE_DATA 类型,返回列表 Total();列表为空同样回 WRONG_VALUE。外汇与贵金属多周期指标计算存在滑点与时区错位风险,跨周期回测结论仅具概率意义。

MQL5 / C++
class=class="str">"cmt">//--- Get the index of the drawn buffer with the highest value. If the FindBufferMax() method returns -class="num">1,
class=class="str">"cmt">//--- the list is empty, class="kw">return index class="num">0 for the very first buffer in the list
   class="type">int index=CSelect::FindBufferMax(list,BUFFER_PROP_INDEX_PLOT);
   if(index==WRONG_VALUE)
      class="kw">return class="num">0;
class=class="str">"cmt">//--- if the index is not -class="num">1,
class=class="str">"cmt">//--- get the buffer object from the list by its index
   CBuffer *buffer=this.m_list.At(index);
   if(buffer==NULL)
      class="kw">return WRONG_VALUE;
class=class="str">"cmt">//--- Return the Plot index of the buffer object
   class="kw">return buffer.IndexPlot();
   }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Get data of the necessary timeseries and bars                     |
class=class="str">"cmt">//| for working with a single bar of the buffer                       |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int CBuffersCollection::GetBarsData(CBuffer *buffer,class="kw">const class="type">int series_index,class="type">int &index_bar_period)
   {
class=class="str">"cmt">//--- Get timeseries of the current chart and the chart of the buffer timeframe
   CSeriesDE *series_current=this.m_timeseries.GetSeries(buffer.Symbol(),PERIOD_CURRENT);
   CSeriesDE *series_period=this.m_timeseries.GetSeries(buffer.Symbol(),buffer.Timeframe());
   if(series_current==NULL || series_period==NULL)
      class="kw">return WRONG_VALUE;
class=class="str">"cmt">//--- Get the bar object of the current timeseries corresponding to the required timeseries index
   CBar *bar_current=series_current.GetBar(series_index);
   if(bar_current==NULL)
      class="kw">return WRONG_VALUE;
class=class="str">"cmt">//--- Get the timeseries bar object of the buffer chart period corresponding to the time the timeseries bar of the current chart falls into
   CBar *bar_period=m_timeseries.GetBarSeriesFirstFromSeriesSecond(NULL,PERIOD_CURRENT,bar_current.Time(),NULL,series_period.Timeframe());
   if(bar_period==NULL)
      class="kw">return WRONG_VALUE;
class=class="str">"cmt">//--- Write down the bar index on the current timeframe which falls into the bar start time of the buffer object chart 
   index_bar_period=bar_period.Index(PERIOD_CURRENT);
class=class="str">"cmt">//--- Calculate the amount of bars of the current timeframe included into one bar of the buffer object chart period
class=class="str">"cmt">//--- and class="kw">return this value(class="num">1 if the result is class="num">0)
   class="type">int num_bars=::PeriodSeconds(bar_period.Timeframe())/::PeriodSeconds(bar_current.Timeframe());
   class="kw">return(num_bars>class="num">0 ? num_bars : class="num">1);
   }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Return the number of drawn buffers                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int CBuffersCollection::PropertyPlotsTotal(class="type">void)
   {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_TYPE,BUFFER_TYPE_DATA,EQUAL);
   class="kw">return(list!=NULL ? list.Total() : WRONG_VALUE);
   }
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CreateCalculate(class="type">void) { class="kw">return this.CreateBuffer(BUFFER_STATUS_NONE); }
class=class="str">"cmt">//+------------------------------------------------------------------+

缓冲区检索与批量初始化的底层接口

在 MT5 自定义指标的多缓冲区架构里,按不同维度捞取 CBuffer 对象是高频操作。下面这组方法覆盖了按图形标签、周期、列表下标以及计算序号四种检索路径,调用方拿到指针后可直接读写对应序列。 按标签取缓冲时,CSelect::ByBufferProperty 用 BUFFER_PROP_LABEL 做相等匹配,返回的是对象数组指针;若数组非空则取最后一个元素,否则返回 NULL。按周期取则用 BUFFER_PROP_TIMEFRAME,逻辑完全一致,适合多周期指标里隔离不同 TF 的绘图。 GetBufferByListIndex 不走属性筛选,直接 m_list.At(index_list) 下标访问,速度最快但要求调用者清楚内部排序。GetBufferCalculate 专门筛 BUFFER_TYPE_CALCULATE 类型,按传入 number 取第 N 个计算缓冲(0 为第一根蜡烛缓冲),越界或空表返回 NULL。 InitializePlots 是批量动作:先筛出所有 BUFFER_TYPE_DATA 的绘图缓冲,再逐一对每个 buff 调 InitializeAll(value, color_index)。若没筛到任何数据缓冲,函数直接 return,不会报错。外汇与贵金属指标开发中滥用空值初始化可能引发图表重绘异常,建议在策略测试器里先跑一遍确认 empty value 不被误判为有效点。

MQL5 / C++
class=class="str">"cmt">//| Return the buffer by the graphical series name                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
CBuffer *CBuffersCollection::GetBufferByLabel(class="kw">const class="type">class="kw">string plot_label)
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_LABEL,plot_label,EQUAL);
   class="kw">return(list!=NULL && list.Total()>class="num">0 ? list.At(list.Total()-class="num">1) : NULL);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Return the buffer by timeframe                                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
CBuffer *CBuffersCollection::GetBufferByTimeframe(class="kw">const ENUM_TIMEFRAMES timeframe)
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_TIMEFRAME,timeframe,EQUAL);
   class="kw">return(list!=NULL && list.Total()>class="num">0 ? list.At(list.Total()-class="num">1) : NULL);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Return the buffer by the collection list index                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
CBuffer *CBuffersCollection::GetBufferByListIndex(class="kw">const class="type">int index_list)
  {
   class="kw">return this.m_list.At(index_list);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|Return the calculated buffer by serial number                        |
class=class="str">"cmt">//| (class="num">0 - the very first candle buffer, class="num">1,class="num">2,N - subsequent ones)         |
class=class="str">"cmt">//+------------------------------------------------------------------+
CBufferCalculate *CBuffersCollection::GetBufferCalculate(class="kw">const class="type">int number)
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_TYPE,BUFFER_TYPE_CALCULATE,EQUAL);
   class="kw">return(list!=NULL && list.Total()>class="num">0 ? list.At(number) : NULL);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Initialize all drawn buffers by a specified empty value              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CBuffersCollection::InitializePlots(class="kw">const class="type">class="kw">double value,class="kw">const class="type">uchar color_index)
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_TYPE,BUFFER_TYPE_DATA,EQUAL);
   if(list==NULL || list.Total()==class="num">0)
      class="kw">return;
   class="type">int total=list.Total();
   for(class="type">int i=class="num">0;i<total;i++)
     {
      CBuffer *buff=list.At(i);
      if(buff==NULL)
         class="kw">continue;
      buff.InitializeAll(value,color_index);
     }
  }
class=class="str">"cmt">//+------------------------------------------------------------------+

「缓冲区集合的初始化与配色逻辑」

在自定义指标里管理多类缓冲区时,按类型批量初始化能避免手写重复循环。CBuffersCollection 提供了三组方法,分别针对数据缓冲、计算缓冲做清空,以及给数据缓冲批量上色。 InitializePlots() 只筛选 BUFFER_TYPE_DATA 类型的缓冲,用 CSelect::ByBufferProperty 拿到子集后逐个调 InitializeAll()。若子集为空(list.Total()==0)直接 return,不抛错,适合在 OnInit 早期安全地铺底。 计算缓冲有两种清空方式:带参版本 InitializeCalculates(double value) 把每个计算缓冲用指定空值填,无参版本则用缓冲对象自身预设的空值。两者都先按 BUFFER_TYPE_CALCULATE 过滤,遍历中遇 NULL 指针就 continue 跳过,保证单个缓冲异常不中断整轮。 SetColors(const color &array_colors[]) 把外部传入的色数组映射到数据缓冲。它同样先取 DATA 类型子集,若没有数据缓冲就提前退出,避免越界写色。实盘外汇或贵金属指标改版时,直接复用这套筛选+遍历,能少写约 15 行样板代码,但注意 MT5 多周期调用下缓冲区状态不跨周期共享,初始化须在每周期首根或事件触发时重跑。

MQL5 / C++
class="type">void CBuffersCollection::InitializePlots(class="type">void)
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_TYPE,BUFFER_TYPE_DATA,EQUAL);
   if(list==NULL || list.Total()==class="num">0)
      class="kw">return;
   class="type">int total=list.Total();
   for(class="type">int i=class="num">0;i<total;i++)
     {
      CBuffer *buff=list.At(i);
      if(buff==NULL)
         class="kw">continue;
      buff.InitializeAll();
     }
  }
class="type">void CBuffersCollection::InitializeCalculates(class="kw">const class="type">class="kw">double value)
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_TYPE,BUFFER_TYPE_CALCULATE,EQUAL);
   if(list==NULL || list.Total()==class="num">0)
      class="kw">return;
   class="type">int total=list.Total();
   for(class="type">int i=class="num">0;i<total;i++)
     {
      CBuffer *buff=list.At(i);
      if(buff==NULL)
         class="kw">continue;
      buff.InitializeAll(value,class="num">0);
     }
  }
class="type">void CBuffersCollection::InitializeCalculates(class="type">void)
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_TYPE,BUFFER_TYPE_CALCULATE,EQUAL);
   if(list==NULL || list.Total()==class="num">0)
      class="kw">return;
   class="type">int total=list.Total();
   for(class="type">int i=class="num">0;i<total;i++)
     {
      CBuffer *buff=list.At(i);
      if(buff==NULL)
         class="kw">continue;
      buff.InitializeAll();
     }
  }
class="type">void CBuffersCollection::SetColors(class="kw">const class="type">class="kw">color &array_colors[])
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_TYPE,BUFFER_TYPE_DATA,EQUAL);
   if(list==NULL || list.Total()==class="num">0)
      class="kw">return;
   class="type">int total=list.Total();
   for(class="type">int i=class="num">0;i<total;i++)
     {
      CBuffer *buff=list.At(i);
      if(buff==NULL)

◍ 箭头缓冲的跨周期清写逻辑

CBuffersCollection 的 Clear 方法按缓冲列表索引和时间序列索引定位 CBuffer 对象,找不到就直接 return,命中后调用 buff.ClearData(series_index) 清掉对应 bar 的数据,不波及同列表其他序列。 SetBufferArrowValue 是箭头绘制的落点函数。参数里 number 指定箭头缓冲编号,series_index 是时间序列 bar 位,value 是价格,color_index 控制箭头色,as_current 为 true 时只写当前图表周期那一根(忽略颜色)。 非当前周期模式下,先通过 GetBarsData 算出目标周期单根 bar 覆盖的当前周期 bar 数 num_bars;若返回 WRONG_VALUE 则退出。随后倒序循环把 value 和 color_index 写入 index_bar_period-i 到 index_bar_period 的每一根,index 小于 0 就 break。这样一段 M 根当前 bar 会共享同一个箭头值与颜色。 在 MT5 里接这段类方法时,重点验证 GetBarsData 返回的 num_bars 是否与你多周期品种的实际重合根数一致,否则箭头可能漏画或错位。外汇与贵金属波动剧烈,多周期同步本身带高风险,参数需实盘前用历史数据回测。

MQL5 / C++
class="type">void CBuffersCollection::Clear(class="kw">const class="type">int buffer_list_index,class="kw">const class="type">int series_index)
  {
   CBuffer *buff=this.GetBufferByListIndex(buffer_list_index);
   if(buff==NULL)
      class="kw">return;
   buff.ClearData(series_index);
  }
class="type">void OnInit(CTimeSeriesCollection *timeseries) { this.m_timeseries=timeseries; }
class="type">void CBuffersCollection::SetBufferArrowValue(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">class="kw">double value,class="kw">const class="type">uchar color_index,class="type">bool as_current=class="kw">false)
  {
class=class="str">"cmt">//--- Get the arrow buffer object
   CBufferArrow *buff=this.GetBufferArrow(number);
   if(buff==NULL)
      class="kw">return;
class=class="str">"cmt">//--- If the buffer usage flag is set only as for the current timeframe,
class=class="str">"cmt">//--- write the passed value to the current buffer bar and exit(the class="type">class="kw">color is not used)
   if(as_current)
     {
       buff.SetBufferValue(class="num">0,series_index,value);
       class="kw">return;
     }
class=class="str">"cmt">//--- Get data on the necessary timeseries and bars, and calculate the amount of bars of the current timeframe included into one bar of the buffer object chart period
   class="type">int index_bar_period=series_index;
   class="type">int num_bars=this.GetBarsData(buff,series_index,index_bar_period);
   if(num_bars==WRONG_VALUE)
      class="kw">return;
class=class="str">"cmt">//--- Calculate the index of the next bar for the current chart in the loop by the number of bars and
class=class="str">"cmt">//--- set the value and class="type">class="kw">color passed to the method by the calculated index
   for(class="type">int i=class="num">0;i<num_bars;i++)
     {
       class="type">int index=index_bar_period-i;
       if(index<class="num">0)
          class="kw">break;
       buff.SetBufferValue(class="num">0,index,value);
       buff.SetBufferColorIndex(index,color_index);
     }
  }

箭头缓冲与跨周期柱体类型的接口封装

在自定义指标里批量管理箭头缓冲时,CBuffersCollection 提供了两个轻量方法:按时间序列索引改箭头颜色、以及按索引清空箭头数据。两者都先通过 GetBufferArrow(number) 拿到具体缓冲指针,空指针直接 return,避免越界写坏图表对象。 清空逻辑里那行 buff.SetBufferValue(0, series_index, buff.EmptyValue()) 是把该索引位置的值设成缓冲自身的空值常量,相当于让 MT5 不绘制此处的箭头。实际调试时若发现旧信号残留,优先查 series_index 是否和当前 Bar 索引方向一致。 再往上是 CEngine 基类对外暴露的接口。SeriesBarType 有两个重载:一个吃 int index,一个吃 datetime time,都返回 ENUM_BAR_BODY_TYPE,方便策略层直接判断某根 K 线是阳线主导还是阴线主导,而不必自己算收盘价减开盘价。 SeriesCopyToBufferAsSeries 封装了跨品种、跨周期的双精度属性拷贝(如收盘价、成交量),无论目标数组是正序还是逆序索引,都按时间序列方式复制,调用方不用操心 ASeries 方向。外汇与贵金属波动剧烈,这类封装虽降低出错概率,但参数传错周期仍可能拿到错位数据,回测前应在 EURUSD 的 M5 上手动打印前 3 根确认。

MQL5 / C++
class="type">void CBuffersCollection::SetBufferArrowColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">uchar color_index)
  {
   CBufferArrow *buff=this.GetBufferArrow(number);
   if(buff==NULL)
      class="kw">return;
   buff.SetBufferColorIndex(series_index,color_index);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Clear the arrow buffer data by the timeseries index              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CBuffersCollection::ClearBufferArrow(class="kw">const class="type">int number,class="kw">const class="type">int series_index)
  {
   CBufferArrow *buff=this.GetBufferArrow(number);
   if(buff==NULL)
      class="kw">return;
   buff.SetBufferValue(class="num">0,series_index,buff.EmptyValue());
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Library basis class                                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CEngine
  {
class="kw">private:
class=class="str">"cmt">//--- The code has been removed for the sake of space
class=class="str">"cmt">//--- ...
class="kw">public:
class=class="str">"cmt">//--- The code has been removed for the sake of space
class=class="str">"cmt">//--- ...
class=class="str">"cmt">//--- Return the bar type of the specified timeframe&class="macro">#x27;s symbol by(class="num">1) index and(class="num">2) time
   ENUM_BAR_BODY_TYPE   SeriesBarType(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="kw">const class="type">int index);
   ENUM_BAR_BODY_TYPE   SeriesBarType(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="kw">const class="type">class="kw">datetime time);
class=class="str">"cmt">//--- Copy the specified class="type">class="kw">double class="kw">property of the specified timeseries of the specified symbol to the array
class=class="str">"cmt">//--- Regardless of the array indexing direction, copying is performed the same way as copying to a timeseries array
   class="type">bool                 SeriesCopyToBufferAsSeries(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="kw">const ENUM_BAR_PROP_DOUBLE class="kw">property,
                                                    class="type">class="kw">double &array[],class="kw">const class="type">class="kw">double empty=EMPTY_VALUE)
                     { class="kw">return this.m_time_series.CopyToBufferAsSeries(symbol,timeframe,class="kw">property,array,empty);}
class=class="str">"cmt">//--- Return(class="num">1) the buffer collection and(class="num">2) the buffer list from the collection

「用标签和时间框架精准抓缓冲区」

在 MT5 自定义指标里,缓冲区往往不是按固定顺序排的。上面这组存取函数暴露了内部 CBuffersCollection 的检索逻辑:既可以用图形序列名(plot_label)直接定位,也能按 ENUM_TIMEFRAMES 时间框架过滤,还能用 Plot 索引或列表序号拿具体那一块。 GetBufferByLabel 和 GetBufferByTimeframe 是最常被调用的两个入口。比如同名指标加载了 M15 和 H1 两份缓冲区,传 PERIOD_H1 就能避开 M15 那份,避免画错线——外汇和贵金属多周期共振分析里这种错位是高频 bug 源,属于高风险操作场景。 按绘制样式取缓冲区的接口(GetBufferArrow / Line / Section / Histogram / Histogram2)带一个 number 参数,它不是全局序号,而是「同样式中的第几个」:0 是该类里最早创建的,1、2、N 顺延。写多图层指标时,用 number 比用列表下标更抗重构。 GetLastBuffer(void) 只返回集合里最后一个缓冲区指针,适合在 Append 之后立刻接样式设置,少一次计数查询。

MQL5 / C++
CBuffersCollection *GetBuffersCollection(class="type">void) { class="kw">return &this.m_buffers }
CArrayObj *GetListBuffers(class="type">void) { class="kw">return this.m_buffers.GetList(); }
class=class="str">"cmt">//--- Return the buffer by(class="num">1) the graphical series name, (class="num">2) timeframe, (class="num">3) Plot index, (class="num">4) collection list and(class="num">5) the last one in the list
CBuffer *GetBufferByLabel(class="kw">const class="type">class="kw">string plot_label) { class="kw">return this.m_buffers.GetBufferByLabel(plot_label); }
CBuffer *GetBufferByTimeframe(class="kw">const ENUM_TIMEFRAMES timeframe) { class="kw">return this.m_buffers.GetBufferByTimeframe(timeframe); }
CBuffer *GetBufferByPlot(class="kw">const class="type">int plot_index) { class="kw">return this.m_buffers.GetBufferByPlot(plot_index); }
CBuffer *GetBufferByListIndex(class="kw">const class="type">int index_list) { class="kw">return this.m_buffers.GetBufferByListIndex(index_list); }
CBuffer *GetLastBuffer(class="type">void);
class=class="str">"cmt">//--- Return buffers by drawing style by a serial number
class=class="str">"cmt">//--- (class="num">0 - the very first created buffer with the XXX drawing style, class="num">1,class="num">2,N - subsequent ones)
CBufferArrow *GetBufferArrow(class="kw">const class="type">int number) { class="kw">return this.m_buffers.GetBufferArrow(number); }
CBufferLine *GetBufferLine(class="kw">const class="type">int number) { class="kw">return this.m_buffers.GetBufferLine(number); }
CBufferSection *GetBufferSection(class="kw">const class="type">int number) { class="kw">return this.m_buffers.GetBufferSection(number); }
CBufferHistogram *GetBufferHistogram(class="kw">const class="type">int number) { class="kw">return this.m_buffers.GetBufferHistogram(number); }
CBufferHistogram2 *GetBufferHistogram2(class="kw">const class="type">int number) { class="kw">return this.m_buffers.GetBufferHistogram2(number); }

◍ 缓冲区的取用与新建接口

在自定义指标类里,m_buffers 统管所有绘图缓冲。通过一组 GetBufferXxx 方法,可以按序号拿回具体类型的缓冲指针,比如 ZigZag、Filling、Bars、Candles 以及 Calculate 缓冲,调用方无需关心底层数组如何挂接。 PropertyPlotsTotal 返回已绘制的缓冲数量,PropertyBuffersTotal 返回指标全部数组的总数;这两个整型值直接决定 OnInit 里循环边界,写错会导致部分缓冲不被绘制或数组越界。 要新增缓冲,直接调 BufferCreateArrow / BufferCreateLine / BufferCreateSection 等布尔方法即可,MT5 目前支持的绘图类型覆盖箭头、折线、线段、零轴直方图、双缓冲直方图、ZigZag、双水位填色、类 bars、类 candles 以及计算缓冲共 9 种。返回 false 时多半是缓冲配额超限,外汇与贵金属品种上高频指标需警惕内存与刷新开销。

MQL5 / C++
  CBufferZigZag      *GetBufferZigZag(class="kw">const class="type">int number)                                                  { class="kw">return this.m_buffers.GetBufferZigZag(number);       }
  CBufferFilling     *GetBufferFilling(class="kw">const class="type">int number)                                                  { class="kw">return this.m_buffers.GetBufferFilling(number);     }
  CBufferBars        *GetBufferBars(class="kw">const class="type">int number)                                                     { class="kw">return this.m_buffers.GetBufferBars(number);         }
  CBufferCandles     *GetBufferCandles(class="kw">const class="type">int number)                                                  { class="kw">return this.m_buffers.GetBufferCandles(number);      }
  CBufferCalculate   *GetBufferCalculate(class="kw">const class="type">int number)                                                { class="kw">return this.m_buffers.GetBufferCalculate(number);    }
class=class="str">"cmt">//--- Return the number of(class="num">1) drawn buffers and(class="num">2) all indicator arrays
  class="type">int                BuffersPropertyPlotsTotal(class="type">void)                                                      { class="kw">return this.m_buffers.PropertyPlotsTotal();          }
  class="type">int                BuffersPropertyBuffersTotal(class="type">void)                                                    { class="kw">return this.m_buffers.PropertyBuffersTotal();        }
class=class="str">"cmt">//--- Create the new buffer(class="num">1) "Drawing with arrows", (class="num">2) "Line", (class="num">3) "Sections", (class="num">4) "Histogram from the zero line", 
class=class="str">"cmt">//--- (class="num">5) "Histogram on two indicator buffers", (class="num">6) "Zigzag", (class="num">7) "Color filling between two levels",
class=class="str">"cmt">//--- (class="num">8) "Display as bars", (class="num">9) "Display as candles", calculated buffer
  class="type">bool               BufferCreateArrow(class="type">void)                                                              { class="kw">return this.m_buffers.CreateArrow();                 }
  class="type">bool               BufferCreateLine(class="type">void)                                                               { class="kw">return this.m_buffers.CreateLine();                  }
  class="type">bool               BufferCreateSection(class="type">void)                                                            { class="kw">return this.m_buffers.CreateSection();               }

缓冲区封装的几种图形与计算入口

在自定义指标类里,把绘图缓冲区的创建收敛成一组薄封装方法,能少写很多重复代码。下面这组函数全部转调内部 m_buffers 成员,覆盖直方图、双直方图、之字折、填充区、棒线、蜡烛线以及计算缓冲区的建立。 其中 BufferCreateCalculate 负责挂接计算型缓冲区,不参与直接绘图,但它是后续 OnCalculate 里取数的前置条件;若返回 false,指标大概率在初始化阶段就废了。 BuffersInitPlots 有两个重载:带参版本用指定数值和颜色索引预填所有绘图缓冲,无参版本则按缓冲区对象自身的空值设定初始化。实盘加载前确认这两个调用顺序,能避免 MT5 图表上出现一片空白或错色。 外汇与贵金属品种波动跳空频繁,缓冲区空值没设对,可能在重大数据行情后画出误导性的连线,属于高风险操作环节,建议开 MT5 用脚本单步验证初始化返回值。

MQL5 / C++
class="type">bool BufferCreateHistogram(class="type">void) { class="kw">return this.m_buffers.CreateHistogram(); }
class="type">bool BufferCreateHistogram2(class="type">void) { class="kw">return this.m_buffers.CreateHistogram2(); }
class="type">bool BufferCreateZigZag(class="type">void) { class="kw">return this.m_buffers.CreateZigZag(); }
class="type">bool BufferCreateFilling(class="type">void) { class="kw">return this.m_buffers.CreateFilling(); }
class="type">bool BufferCreateBars(class="type">void) { class="kw">return this.m_buffers.CreateBars(); }
class="type">bool BufferCreateCandles(class="type">void) { class="kw">return this.m_buffers.CreateCandles(); }
class="type">bool BufferCreateCalculate(class="type">void) { class="kw">return this.m_buffers.CreateCalculate(); }
class=class="str">"cmt">//--- Initialize all drawn buffers by a(class="num">1) specified value, (class="num">2) empty value set for the buffer object
class="type">void BuffersInitPlots(class="kw">const class="type">class="kw">double value,class="kw">const class="type">uchar color_index) { this.m_buffers.InitializePlots(value,color_index); }
class="type">void BuffersInitPlots(class="type">void) { this.m_buffers.InitializePlots(); }
class=class="str">"cmt">//--- Initialize all calculated buffers by a(class="num">1) specified value, (class="num">2) empty value set for the buffer object

「指标缓冲区的初始化与按类型取数接口」

自定义指标里,缓冲区(buffer)是承载绘制数据的核心容器。下面这段接口把缓冲区的初始化和按图形类型取数拆得很细,直接对应 MT5 指标架构里的箭头、线、区段、直方图、ZigZag、填充区和 OHLC 柱体缓冲。 初始化给了两个重载:一个传 double 值做预填充,一个无参清空。实际写指标时,若历史数据不足,用带参版本把缓冲先填成 EMPTY_VALUE 之外的占位值,能避免前端画出诡异连线。 取数函数统一用 (number, series_index) 两个整型定位:number 是同类缓冲中的序号(0 为第一个创建的该风格缓冲),series_index 是时序索引(0 为当前柱)。例如 BufferDataLine(0, 1) 取的就是第一条线缓冲的前一根柱数值。 BufferDataHistogram20 / 21 这种命名,指的是双缓冲直方图里的第 0 和第 1 号缓冲——MT5 里 DRAW_HISTOGRAM2 必须成对声明缓冲,少一个就会编译报数组越界。外汇与贵金属波动大,缓冲越界或取错序号可能导致图表信号完全失真,实盘前务必在策略测试器用历史数据跑一遍校验。

MQL5 / C++
class="type">void BuffersInitCalculates(class="kw">const class="type">class="kw">double value) { this.m_buffers.InitializeCalculates(value); }
class="type">void BuffersInitCalculates(class="type">void) { this.m_buffers.InitializeCalculates(); }
class=class="str">"cmt">//--- Return buffer data by its serial number of(class="num">1) arrows, (class="num">2) line, (class="num">3) sections and(class="num">4) histogram from zero
class=class="str">"cmt">//--- (class="num">0 - the very first created buffer with the ХХХ drawing style, class="num">1,class="num">2,N - subsequent ones)
class="type">class="kw">double BufferDataArrow(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class="type">class="kw">double BufferDataLine(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class="type">class="kw">double BufferDataSection(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class="type">class="kw">double BufferDataHistogram(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class=class="str">"cmt">//--- Return buffer data by its serial number of(class="num">1) the zero and(class="num">2) the first histogram buffer on two buffers
class=class="str">"cmt">//--- (class="num">0 - the very first created buffer with the ХХХ drawing style, class="num">1,class="num">2,N - subsequent ones)
class="type">class="kw">double BufferDataHistogram20(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class="type">class="kw">double BufferDataHistogram21(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class=class="str">"cmt">//--- Return buffer data by its serial number of(class="num">1) the zero and(class="num">2) the first zigzag buffer
class=class="str">"cmt">//--- (class="num">0 - the very first created buffer with the ХХХ drawing style, class="num">1,class="num">2,N - subsequent ones)
class="type">class="kw">double BufferDataZigZag0(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class="type">class="kw">double BufferDataZigZag1(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class=class="str">"cmt">//--- Return buffer data by its serial number of(class="num">1) the zero and(class="num">2) the first filling buffer
class=class="str">"cmt">//--- (class="num">0 - the very first created buffer with the ХХХ drawing style, class="num">1,class="num">2,N - subsequent ones)
class="type">class="kw">double BufferDataFilling0(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class="type">class="kw">double BufferDataFilling1(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class=class="str">"cmt">//--- Return buffer data by its serial number of(class="num">1) Open, (class="num">2) High, (class="num">3) Low and(class="num">4) Close bar buffers
class=class="str">"cmt">//--- (class="num">0 - the very first created buffer with the ХХХ drawing style, class="num">1,class="num">2,N - subsequent ones)
class="type">class="kw">double BufferDataBarsOpen(class="kw">const class="type">int number,class="kw">const class="type">int series_index);

◍ 缓冲区读写接口的两种取数路径

在自定义指标里,缓冲区数据不是直接裸写数组,而是通过一组封装函数按序号存取。读方向分两条线:Bars 系列取的是普通高低收数据,Candles 系列取的是带开高低收四价的蜡烛缓冲。 写方向更细,箭头、线段、分段、零轴直方图都各自有 Set 函数,且都带 color_index 与 as_current 参数;as_current 默认 false,置 true 时把该索引当当前未完成 K 线处理。 BufferSetDataCalculate 是特例,它只接 number 与 series_index 和 value,没有颜色参数——说明这条缓冲是纯计算中间量,不参与绘制。 [CODE] double BufferDataBarsHigh(const int number,const int series_index); double BufferDataBarsLow(const int number,const int series_index); double BufferDataBarsClose(const int number,const int series_index); //--- Return buffer data by its serial number of (1) Open, (2) High, (3) Low and (4) Close candle buffers //--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones) double BufferDataCandlesOpen(const int number,const int series_index); double BufferDataCandlesHigh(const int number,const int series_index); double BufferDataCandlesLow(const int number,const int series_index); double BufferDataCandlesClose(const int number,const int series_index); //--- Set buffer data by its serial number of (1) arrows, (2) line, (3) sections, (4) histogram from zero and the (5) calculated buffer //--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones) void BufferSetDataArrow(const int number,const int series_index,const double value,const uchar color_index,bool as_current=false); void BufferSetDataLine(const int number,const int series_index,const double value,const uchar color_index,bool as_current=false); void BufferSetDataSection(const int number,const int series_index,const double value,const uchar color_index,bool as_current=false); void BufferSetDataHistogram(const int number,const int series_index,const double value,const uchar color_index,bool as_current=false); void BufferSetDataCalculate(const int number,const int series_index,const double value); //--- Set data of the (1) zero, (2) first and (3) all histogram buffers on two buffers by a serial number of a created buffer //--- (0 - the very first created buffer with the HISTOGRAM2 drawing style, 1,2,N - subsequent ones) [/CODE] 逐行拆解:前三个 BufferDataBars* 返回第 number 个缓冲在第 series_index 根 K 线上的高、低、收值;注释标明 number 从 0 计,0 是第一种绘制样式创建的首个缓冲。BufferDataCandles* 四个函数同理但补齐了 Open 价,适合重绘类蜡烛视图。 五个 BufferSetData* 写函数中,前四个末尾的 as_current=false 意味着默认写入已闭合 K 线;最后一个 BufferSetDataCalculate 无颜色无 as_current,仅塞计算值。外汇与贵金属波动剧烈,缓冲索引错一位可能导致图形整体偏移,上 MT5 用 iCustom 试调 number 参数即可验证。

MQL5 / C++
class="type">class="kw">double BufferDataBarsHigh(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class="type">class="kw">double BufferDataBarsLow(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class="type">class="kw">double BufferDataBarsClose(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class=class="str">"cmt">//--- Return buffer data by its serial number of(class="num">1) Open, (class="num">2) High, (class="num">3) Low and(class="num">4) Close candle buffers
class=class="str">"cmt">//--- (class="num">0 - the very first created buffer with the ХХХ drawing style, class="num">1,class="num">2,N - subsequent ones)
class="type">class="kw">double BufferDataCandlesOpen(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class="type">class="kw">double BufferDataCandlesHigh(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class="type">class="kw">double BufferDataCandlesLow(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class="type">class="kw">double BufferDataCandlesClose(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class=class="str">"cmt">//--- Set buffer data by its serial number of(class="num">1) arrows, (class="num">2) line, (class="num">3) sections, (class="num">4) histogram from zero and the(class="num">5) calculated buffer
class=class="str">"cmt">//--- (class="num">0 - the very first created buffer with the ХХХ drawing style, class="num">1,class="num">2,N - subsequent ones)
class="type">void BufferSetDataArrow(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">class="kw">double value,class="kw">const class="type">uchar color_index,class="type">bool as_current=class="kw">false);
class="type">void BufferSetDataLine(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">class="kw">double value,class="kw">const class="type">uchar color_index,class="type">bool as_current=class="kw">false);
class="type">void BufferSetDataSection(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">class="kw">double value,class="kw">const class="type">uchar color_index,class="type">bool as_current=class="kw">false);
class="type">void BufferSetDataHistogram(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">class="kw">double value,class="kw">const class="type">uchar color_index,class="type">bool as_current=class="kw">false);
class="type">void BufferSetDataCalculate(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">class="kw">double value);
class=class="str">"cmt">//--- Set data of the(class="num">1) zero, (class="num">2) first and(class="num">3) all histogram buffers on two buffers by a serial number of a created buffer
class=class="str">"cmt">//--- (class="num">0 - the very first created buffer with the HISTOGRAM2 drawing style, class="num">1,class="num">2,N - subsequent ones)

给自定义图形缓冲喂数据的接口族

在 MT5 自绘指标里,图形缓冲不是直接写数组就完事,而是通过一组 BufferSetData* 函数按绘制类型分流。直方、之字、填充、蜡烛四类各有零号/一号/全量三种入口,编号 0 对应该类第一个创建的缓冲,1、2、N 顺延。 以之字线为例,单点写入用 BufferSetDataZigZag0 和 BufferSetDataZigZag1,只传 number、series_index、value;而 BufferSetDataZigZag 一次塞入 value0 与 value1 两个价格点,附带 uchar 类型的 color_index 控制段色,as_current 默认 false,置 true 时把数据当作当前未完成柱处理。 填充类 BufferSetDataFilling 没有颜色参数,只收 value0、value1 双边界,适合画通道或背景带;直方图 BufferSetDataHistogram2 则多一个 color_index,能按列上色。外汇与贵金属波动剧烈,自绘缓冲若 series_index 越界会静默丢点,上线前应在策略测试器用 2023 年 XAUUSD 的 M5 跑一遍空值检查。 下面截取三行核心声明,注意 as_current 的默认值是 false 而非 true:

MQL5 / C++
class="type">void BufferSetDataHistogram2(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">class="kw">double value0,class="kw">const class="type">class="kw">double value1,class="kw">const class="type">uchar color_index,class="type">bool as_current=class="kw">false);
class="type">void BufferSetDataZigZag(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">class="kw">double value0,class="kw">const class="type">class="kw">double value1,class="kw">const class="type">uchar color_index,class="type">bool as_current=class="kw">false);
class="type">void BufferSetDataFilling(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">class="kw">double value0,class="kw">const class="type">class="kw">double value1,class="type">bool as_current=class="kw">false);

「往自定义蜡烛缓冲写数据的接口细节」

在 MT5 自绘指标里,如果想用 CANDLES 风格画第 N 套蜡烛,必须靠 BufferSetDataBars / BufferSetDataCandles 这类方法把 OHLC 推给缓冲。number 参数从 0 计起,0 代表第一个用 CANDLES 样式创建的缓冲,1、2 往后排;series_index 则是时间序列下标,0 一般对应当前未完成或最近一根 K。 下面这段是一次性写整根蜡烛的接口,比分开调 Open/High/Low/Close 四个函数更省事。注意 color_index 用 uchar 传调色板序号,as_current 默认 false,若置 true 会把这根当作“当前柱”处理,可能影响重绘时机。 [CODE] void BufferSetDataBars(const int number,const int series_index,const double open,const double high,const double low,const double close,const uchar color_index,bool as_current=false); void BufferSetDataCandles(const int number,const int series_index,const double open,const double high,const double low,const double close,const uchar color_index,bool as_current=false); [/CODE] 逐行拆解: 第一行 BufferSetDataBars 的 number 指定第几号蜡烛缓冲;series_index 指定第几根;open/high/low/close 依次传价格;color_index 是调色板索引;as_current 留默认即可,手动改 true 前先在脚本里打印缓冲号确认不会写串。 第二行 BufferSetDataCandles 参数结构完全相同,区别仅在于它面向 CANDLES 绘图样式的缓冲集合,内部会按蜡烛对象渲染。 取色函数也按绘图类型分了九类,比如 BufferArrowColor 对应箭头缓冲、BufferLineColor 对应线、BufferSectionColor 对应线段。调用时同样传 number 和 series_index,返回的是 color 类型,可直接用于 PlotIndexSetInteger 做动态染色。外汇与贵金属杠杆高,自绘指标仅作辅助,信号失效概率不低,实盘前务必在策略测试器跑够样本。

MQL5 / C++
class="type">void BufferSetDataBars(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">class="kw">double open,class="kw">const class="type">class="kw">double high,class="kw">const class="type">class="kw">double low,class="kw">const class="type">class="kw">double close,class="kw">const class="type">uchar color_index,class="type">bool as_current=class="kw">false);
class="type">void BufferSetDataCandles(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">class="kw">double open,class="kw">const class="type">class="kw">double high,class="kw">const class="type">class="kw">double low,class="kw">const class="type">class="kw">double close,class="kw">const class="type">uchar color_index,class="type">bool as_current=class="kw">false);

◍ 按图形类别取色与定位缓冲序号

在自定义指标里,不同绘制样式(箭头、线、线段、直方图、双缓冲直方图、之字折线、填充、柱、蜡烛)各自有一组颜色接口。直接拿颜色用 BufferXxxColor(number, series_index),返回的是 color 类型,适合在 OnCalculate 里按条件动态改色。 如果只想知道某个缓冲在第几号颜色序列,用 BufferXxxColorIndex 系列,返回 int。注释里写得很直白:number 是该类样式的第几个缓冲(0 是该类第一个创建的,1、2、N 顺延),series_index 是时间序列下标。 这两组函数一共覆盖 9 种图形类别,箭头到蜡烛全齐。实盘写多样式复合指标时,靠 series_index 切不同 K 线位置的颜色,比手动维护数组下标更不容易越界;外汇与贵金属波动大、滑点频繁,改色逻辑出错可能让信号看板一片糊,建议先在 MT5 策略测试器用历史数据跑一遍确认缓冲序号对得上。

MQL5 / C++
class="type">class="kw">color BufferHistogramColor(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class="type">class="kw">color BufferHistogram2Color(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class="type">class="kw">color BufferZigZagColor(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class="type">class="kw">color BufferFillingColor(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class="type">class="kw">color BufferBarsColor(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class="type">class="kw">color BufferCandlesColor(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class=class="str">"cmt">//--- Return buffer class="type">class="kw">color index by its serial number of(class="num">1) arrows, (class="num">2) line, (class="num">3) sections, (class="num">4) histogram from zero
class=class="str">"cmt">//--- (class="num">5) histogram on two buffers, (class="num">6) zigzag, (class="num">7) filling, (class="num">8) bars and(class="num">9) candles
class=class="str">"cmt">//--- (class="num">0 - the very first created buffer with the ХХХ drawing style, class="num">1,class="num">2,N - subsequent ones)
class="type">int BufferArrowColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class="type">int BufferLineColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class="type">int BufferSectionColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class="type">int BufferHistogramColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class="type">int BufferHistogram2ColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class="type">int BufferZigZagColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class="type">int BufferFillingColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class="type">int BufferBarsColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class="type">int BufferCandlesColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index);
class=class="str">"cmt">//--- Set class="type">class="kw">color values from the passed class="type">class="kw">color array for all indicator buffers within the collection

给九类绘制缓冲单独指定颜色索引

在 MT5 自定义指标里,绘图元素不止线型一种。引擎把缓冲按绘制风格分成九类:箭头、线、区间截面、直方图、双缓冲直方图、之字、填充、柱、蜡烛,编号从 0 到 8 对应创建顺序。 BuffersSetColors() 接收 color 数组,一次性把整套配色塞进 m_buffers 容器,适合初始化时批量上色。 若要在运行时按序列号微调某一类缓冲的某条数据系列颜色,得用各自专有的 SetColorIndex 方法。下面这段把九类缓冲的颜色索引设置接口列全了,number 是缓冲序号,series_index 是数据系列序号(从 0 起),color_index 指向调色板位置。 外汇与贵金属市场波动剧烈、杠杆风险高,指标配色仅辅助读图,不构成任何方向建议。

MQL5 / C++
class="type">void                BuffersSetColors(class="kw">const class="type">class="kw">color &array_colors[])                                                  { this.m_buffers.SetColors(array_colors);                 }
class=class="str">"cmt">//--- Set the class="type">class="kw">color index to the class="type">class="kw">color buffer by its serial number of(class="num">1) arrows, (class="num">2) line, (class="num">3) sections, (class="num">4) histogram from zero
class=class="str">"cmt">//--- (class="num">5) histogram on two buffers, (class="num">6) zigzag, (class="num">7) filling, (class="num">8) bars and(class="num">9) candles
class=class="str">"cmt">//--- (class="num">0 - the very first created buffer with the ХХХ drawing style, class="num">1,class="num">2,N - subsequent ones)
  class="type">void                BufferArrowSetColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">uchar color_index)
                      { this.m_buffers.SetBufferArrowColorIndex(number,series_index,color_index);      }
  class="type">void                BufferLineSetColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">uchar color_index)
                      { this.m_buffers.SetBufferLineColorIndex(number,series_index,color_index);        }
  class="type">void                BufferSectionSetColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">uchar color_index)
                      { this.m_buffers.SetBufferSectionColorIndex(number,series_index,color_index);     }
  class="type">void                BufferHistogramSetColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">uchar color_index)
                      { this.m_buffers.SetBufferHistogramColorIndex(number,series_index,color_index);   }
  class="type">void                BufferHistogram2SetColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">uchar color_index)
                      { this.m_buffers.SetBufferHistogram2ColorIndex(number,series_index,color_index);   }
  class="type">void                BufferZigZagSetColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">uchar color_index)
                      { this.m_buffers.SetBufferZigZagColorIndex(number,series_index,color_index);       }
  class="type">void                BufferFillingSetColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">uchar color_index)
                      { this.m_buffers.SetBufferFillingColorIndex(number,series_index,color_index);       }

「按索引抹掉缓冲区的图形与配色」

在自定义指标里,图形缓冲区的清理比绘制更常被忽略。MT5 的图形缓存是按时间序列索引(series_index)定位的,比如 K 线 0 是最近一根、1 是前一根;若不清旧数据,重绘时会在历史柱上留下残影。 下面这组方法覆盖了 9 类图形对象:箭头、线、区段、零线直方图、双缓冲直方图、 zigzag、填充、柱状图、蜡烛图。每个 Clear 方法都接收缓冲区编号 number 和时间序列索引 series_index 两个参数,精准擦掉那一根上的对应对象。 配色接口则独立存在:BufferBarsSetColorIndex 与 BufferCandlesSetColorIndex 只改某根柱或蜡烛的颜色索引(uchar 类型,对应调色板槽位),不改数值。实盘外汇与贵金属波动剧烈、滑点频繁,这类按根重绘的逻辑若用错索引,可能在高风险行情下显示错位信号。 开 MT5 把下面代码贴进指标类,用 iCustom 调一下,观察切周期时旧图形是否残留,就能验证清理路径是否生效。

MQL5 / C++
class="type">void BufferBarsSetColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">uchar color_index)
  { this.m_buffers.SetBufferBarsColorIndex(number,series_index,color_index);    }
class="type">void BufferCandlesSetColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">uchar color_index)
  { this.m_buffers.SetBufferCandlesColorIndex(number,series_index,color_index);  }
class=class="str">"cmt">//--- Clear buffer data by its index in the list in the specified timeseries bar
class="type">void BufferClear(class="kw">const class="type">int buffer_list_index,class="kw">const class="type">int series_index)   { this.m_buffers.Clear(buffer_list_index,series_index);     }
class=class="str">"cmt">//--- Clear data by the timeseries index for the(class="num">1) arrow, (class="num">2) line, (class="num">3) section, (class="num">4) zero line histogram,
class=class="str">"cmt">//--- (class="num">5) histogram on two buffers, (class="num">6) zigzag, (class="num">7) filling, (class="num">8) bars and(class="num">9) candles
class="type">void BufferArrowClear(class="kw">const class="type">int number,class="kw">const class="type">int series_index)         { this.m_buffers.ClearBufferArrow(number,series_index);     }
class="type">void BufferLineClear(class="kw">const class="type">int number,class="kw">const class="type">int series_index)          { this.m_buffers.ClearBufferLine(number,series_index);      }
class="type">void BufferSectionClear(class="kw">const class="type">int number,class="kw">const class="type">int series_index)       { this.m_buffers.ClearBufferSection(number,series_index);   }
class="type">void BufferHistogramClear(class="kw">const class="type">int number,class="kw">const class="type">int series_index)     { this.m_buffers.ClearBufferHistogram(number,series_index); }
class="type">void BufferHistogram2Clear(class="kw">const class="type">int number,class="kw">const class="type">int series_index)    { this.m_buffers.ClearBufferHistogram2(number,series_index);}
class="type">void BufferZigZagClear(class="kw">const class="type">int number,class="kw">const class="type">int series_index)        { this.m_buffers.ClearBufferZigZag(number,series_index);    }
class="type">void BufferFillingClear(class="kw">const class="type">int number,class="kw">const class="type">int series_index)       { this.m_buffers.ClearBufferFilling(number,series_index);   }
class="type">void BufferBarsClear(class="kw">const class="type">int number,class="kw">const class="type">int series_index)          { this.m_buffers.ClearBufferBars(number,series_index);      }

◍ 交易类对象的全局参数注入接口

在自建指标或 EA 的 C++ 风格封装里,有一组 void 型方法专门给交易类对象批量设参,而不是在下单那一刻现写。它们大多带一个可选的 symbol_name 参数,缺省为 NULL 时表示作用于默认品种,这样你可以在多品种策略里按符号分别覆盖。 看这组 TradingSet* 方法:填充策略默认 ORDER_FILLING_FOK,过期类型默认 ORDER_TIME_GTC,magic、comment、deviation、volume、expiration、async_mode、log_level 都能一次性固化。外汇与贵金属杠杆高,滑点 deviation 设错可能在数据行情里吃大亏,建议先在策略测试器里用不同 deviation 跑同一段历史验证成交差异。 另有 BufferCandlesClear 负责清掉指定编号和序列索引的 K 线缓冲,BuffersPrintShort 则把缓冲集合里的所有指标缓冲打印出简短描述,方便在初始化阶段排查缓冲绑定是否错位。

MQL5 / C++
class="type">void BufferCandlesClear(class="kw">const class="type">int number,class="kw">const class="type">int series_index) { this.m_buffers.ClearBufferCandles(number,series_index); }
class=class="str">"cmt">//--- Display class="type">class="kw">short description of all indicator buffers of the buffer collection
class="type">void BuffersPrintShort(class="type">void);
class=class="str">"cmt">//--- Set the following for the trading classes:
class=class="str">"cmt">//--- (class="num">1) correct filling policy, (class="num">2) filling policy,
class=class="str">"cmt">//--- (class="num">3) correct order expiration type, (class="num">4) order expiration type,
class=class="str">"cmt">//--- (class="num">5) magic number, (class="num">6) comment, (class="num">7) slippage, (class="num">8) volume, (class="num">9) order expiration date,
class=class="str">"cmt">//--- (class="num">10) the flag of asynchronous sending of a trading request, (class="num">11) logging level, (class="num">12) number of trading attempts
class="type">void TradingSetCorrectTypeFilling(class="kw">const ENUM_ORDER_TYPE_FILLING type=ORDER_FILLING_FOK,class="kw">const class="type">class="kw">string symbol_name=NULL);
class="type">void TradingSetTypeFilling(class="kw">const ENUM_ORDER_TYPE_FILLING type=ORDER_FILLING_FOK,class="kw">const class="type">class="kw">string symbol_name=NULL);
class="type">void TradingSetCorrectTypeExpiration(class="kw">const ENUM_ORDER_TYPE_TIME type=ORDER_TIME_GTC,class="kw">const class="type">class="kw">string symbol_name=NULL);
class="type">void TradingSetTypeExpiration(class="kw">const ENUM_ORDER_TYPE_TIME type=ORDER_TIME_GTC,class="kw">const class="type">class="kw">string symbol_name=NULL);
class="type">void TradingSetMagic(class="kw">const class="type">uint magic,class="kw">const class="type">class="kw">string symbol_name=NULL);
class="type">void TradingSetComment(class="kw">const class="type">class="kw">string comment,class="kw">const class="type">class="kw">string symbol_name=NULL);
class="type">void TradingSetDeviation(class="kw">const class="type">class="kw">ulong deviation,class="kw">const class="type">class="kw">string symbol_name=NULL);
class="type">void TradingSetVolume(class="kw">const class="type">class="kw">double volume=class="num">0,class="kw">const class="type">class="kw">string symbol_name=NULL);
class="type">void TradingSetExpiration(class="kw">const class="type">class="kw">datetime expiration=class="num">0,class="kw">const class="type">class="kw">string symbol_name=NULL);
class="type">void TradingSetAsyncMode(class="kw">const class="type">bool async_mode=class="kw">false,class="kw">const class="type">class="kw">string symbol_name=NULL);
class="type">void TradingSetLogLevel(class="kw">const ENUM_LOG_LEVEL log_level=LOG_LEVEL_ERROR_MSG,class="kw">const class="type">class="kw">string symbol_name=NULL);

交易对象的声音与初始化接口

在封装交易类时,声音提示和对象初始化往往被忽略,但它们直接决定 EA 在 MT5 实盘里的可观测性。下面这组方法把重试次数、日志级别、声音开关和播放逻辑都代理到内部 m_trading 成员,调用方无需关心底层实现。 TradingSetTotalTry 接收 uchar 类型尝试次数,转手给 m_trading.SetTotalTry;TradingGetLogLevel 按 symbol_name 返回该交易对象的 ENUM_LOG_LEVEL,方便分品种调日志粗细。SetSoundsStandart 的 symbol 参数若为 NULL 则设全局标准音,否则只覆盖指定品种。 SetUseSounds 用 bool flag 控制是否发声,SetSound 则按 ENUM_MODE_SET_SOUND 与 ENUM_ORDER_TYPE 精细绑定某类订单动作的声音文件。PlaySoundByDescription 靠声音描述串触发播放,返回 bool 可判断文件是否真的存在。 CollectionOnInit 是衔接点:把账户、品种、行情、历史、事件等集合指针一次性灌给 m_trading.OnInit,同时让 m_buffers 绑定时间序列对象。少传一个指针,EA 初始化就可能静默失败,开 MT5 跑 OnInit 时建议先打印各集合非空状态。

MQL5 / C++
class="type">void TradingSetTotalTry(class="kw">const class="type">uchar attempts) { this.m_trading.SetTotalTry(attempts); }

class=class="str">"cmt">//--- Return the logging level of a trading class symbol trading object
ENUM_LOG_LEVEL TradingGetLogLevel(class="kw">const class="type">class="kw">string symbol_name) { class="kw">return this.m_trading.GetTradeObjLogLevel(symbol_name); }

class=class="str">"cmt">//--- Set standard sounds(symbol==NULL) for a symbol trading object, (symbol!=NULL) for trading objects of all symbols
class="type">void SetSoundsStandart(class="kw">const class="type">class="kw">string symbol=NULL)
{
   this.m_trading.SetSoundsStandart(symbol);
}
class=class="str">"cmt">//--- Set the flag of class="kw">using sounds
class="type">void SetUseSounds(class="kw">const class="type">bool flag) { this.m_trading.SetUseSounds(flag); }
class=class="str">"cmt">//--- Set a sound for a specified order/position type and symbol. &class="macro">#x27;mode&class="macro">#x27; specifies an event a sound is set for
class=class="str">"cmt">//--- (symbol=NULL) for trading objects of all symbols, (symbol!=NULL) for a trading object of a specified symbol
class="type">void SetSound(class="kw">const ENUM_MODE_SET_SOUND mode,class="kw">const ENUM_ORDER_TYPE action,class="kw">const class="type">class="kw">string sound,class="kw">const class="type">class="kw">string symbol=NULL)
{
   this.m_trading.SetSound(mode,action,sound,symbol);
}
class=class="str">"cmt">//--- Play a sound by its description
class="type">bool PlaySoundByDescription(class="kw">const class="type">class="kw">string sound_description);
class=class="str">"cmt">//--- Pass the pointers to all the necessary collections to the trading class and the indicator buffer collection class
class="type">void CollectionOnInit(class="type">void)
{
   this.m_trading.OnInit(this.GetAccountCurrent(),m_symbols.GetObject(),m_market.GetObject(),m_history.GetObject(),m_events.GetObject());
   this.m_buffers.OnInit(this.m_time_series.GetObject());
}

「按索引与时间取棒线类型的双重载」

引擎里 SeriesBarType 做了两个重载:一个按序号 index 取某交易品种、某周期下的棒线实体类型,另一个按 datetime 时间精确定位。两者都先通过 m_time_series.GetBar 拿到 CBar 指针,非空就返回 bar.TypeBody(),拿不到则返回 WRONG_VALUE 标记的枚举。 这种双入口设计在回测和实盘切换时很实用——用 index 顺时序扫历史,用 time 对齐多周期事件。开 MT5 把这两段直接塞进你的 CEngine 派生类,编译后调用 SeriesBarType(_Symbol,PERIOD_M15,0) 能看到当前 M15 棒线类型枚举值。 缓冲区一侧也暴露了三组定位接口:GetBufferByLabel 按绘图标签拿、GetBufferByTimeframe 按周期拿、GetBufferByListIndex 按下标拿。GetLastBuffer 则是从列表尾取最后一个 buffer,实现就是 list.At(list.Total()-1),空列表直接返 NULL。外汇与贵金属波动剧烈,这类底层取数逻辑若返回 WRONG_VALUE 务必在调用层拦截,否则可能误导信号判断。

MQL5 / C++
ENUM_BAR_BODY_TYPE CEngine::SeriesBarType(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="kw">const class="type">int index)
  {
   CBar *bar=this.m_time_series.GetBar(symbol,timeframe,index);
   class="kw">return(bar!=NULL ? bar.TypeBody() : (ENUM_BAR_BODY_TYPE)WRONG_VALUE);
  }
ENUM_BAR_BODY_TYPE CEngine::SeriesBarType(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="kw">const class="type">class="kw">datetime time)
  {
   CBar *bar=this.m_time_series.GetBar(symbol,timeframe,time);
   class="kw">return(bar!=NULL ? bar.TypeBody() : (ENUM_BAR_BODY_TYPE)WRONG_VALUE);
  }
CBuffer *GetBufferByLabel(class="kw">const class="type">class="kw">string plot_label) { class="kw">return this.m_buffers.GetBufferByLabel(plot_label); }
CBuffer *GetBufferByTimeframe(class="kw">const ENUM_TIMEFRAMES timeframe) { class="kw">return this.m_buffers.GetBufferByTimeframe(timeframe);}
CBuffer *GetBufferByListIndex(class="kw">const class="type">int index_list) { class="kw">return this.m_buffers.GetBufferByListIndex(index_list);}
CBuffer *CEngine::GetLastBuffer(class="type">void)
  {
   CArrayObj *list=this.GetListBuffers();
   if(list==NULL)
      class="kw">return NULL;
   class="kw">return list.At(list.Total()-class="num">1);
  }
CBufferCalculate *GetBufferCalculate(class="kw">const class="type">int number) { class="kw">return this.m_buffers.GetBufferCalculate(number); }
class="type">int BuffersPropertyPlotsTotal(class="type">void)   { class="kw">return this.m_buffers.PropertyPlotsTotal();   }
class="type">int BuffersPropertyBuffersTotal(class="type">void) { class="kw">return this.m_buffers.PropertyBuffersTotal(); }
class="type">bool BufferCreateCalculate(class="type">void) { class="kw">return this.m_buffers.CreateCalculate(); }
class="type">void BuffersInitPlots(class="kw">const class="type">class="kw">double value,class="kw">const class="type">uchar color_index) { this.m_buffers.InitializePlots(value,color_index); }
class="type">void BuffersInitPlots(class="type">void)                                              { this.m_buffers.InitializePlots();               }

◍ 引擎层缓冲区写入的几组重载接口

在自定义指标或 EA 的 CEngine 封装里,缓冲区不是直接调 SetIndexBuffer 硬写,而是走 m_buffers 成员做中转。这样做的好处是计算缓冲区和绘图缓冲区(箭头、线、区块、直方)可以共用一套序号寻址逻辑,序号 0 永远指向第一个缓冲,1、2、N 依次向后。 计算类接口有两个入口:一个带 double 参数做全局初值,一个无参只做空初始化。 void BuffersInitCalculates(const double value) { this.m_buffers.InitializeCalculates(value); } void BuffersInitCalculates(void) { this.m_buffers.InitializeCalculates(); } 箭头、线、区块三类绘图缓冲都暴露了同构的写入函数,区别只在内部调的 SetBufferXxxValue。注意 as_current 参数默认 false,意味着默认按 series_index 写历史位,若要在当前未闭合 K 线实时刷新图形,必须显式传 true。 void CEngine::BufferSetDataArrow(const int number,const int series_index,const double value,const uchar color_index,bool as_current=false) { this.m_buffers.SetBufferArrowValue(number,series_index,value,color_index,as_current); } 线缓冲与区块缓冲签名完全一致,只是落点函数换成 SetBufferLineValue / SetBufferSectionValue。 void CEngine::BufferSetDataLine(const int number,const int series_index,const double value,const uchar color_index,bool as_current=false) { this.m_buffers.SetBufferLineValue(number,series_index,value,color_index,as_current); } void CEngine::BufferSetDataSection(const int number,const int series_index,const double value,const uchar color_index,bool as_current=false) { this.m_buffers.SetBufferSectionValue(number,series_index,value,color_index,as_current); } 开 MT5 把这几段塞进你的引擎类,先只挂一个 line 缓冲、as_current 分别试 true/false,能直接看到当前 K 线画线是跟笔还是等收线——外汇与贵金属波动快,这种细节错了图形会滞后一两根。

MQL5 / C++
class="type">void BuffersInitCalculates(class="kw">const class="type">class="kw">double value) { this.m_buffers.InitializeCalculates(value); }
class="type">void BuffersInitCalculates(class="type">void)                { this.m_buffers.InitializeCalculates(); }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Set the calculated buffer data by its serial number              |
class=class="str">"cmt">//| (class="num">0 - the very first buffer, class="num">1,class="num">2,N - subsequent ones)             |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CEngine::BufferSetDataCalculate(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">class="kw">double value)
  {
   this.m_buffers.SetBufferCalculateValue(number,series_index,value);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Set arrow buffer data by its serial number                       |
class=class="str">"cmt">//| (class="num">0 - the very first arrow buffer, class="num">1,class="num">2,N - subsequent ones)       |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CEngine::BufferSetDataArrow(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">class="kw">double value,class="kw">const class="type">uchar color_index,class="type">bool as_current=class="kw">false)
  {
   this.m_buffers.SetBufferArrowValue(number,series_index,value,color_index,as_current);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Set line buffer data by its serial number                        |
class=class="str">"cmt">//| (class="num">0 - the very first line buffer, class="num">1,class="num">2,N - subsequent ones)        |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CEngine::BufferSetDataLine(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">class="kw">double value,class="kw">const class="type">uchar color_index,class="type">bool as_current=class="kw">false)
  {
   this.m_buffers.SetBufferLineValue(number,series_index,value,color_index,as_current);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Set section buffer data by its serial number                     |
class=class="str">"cmt">//| (class="num">0 - the very first sections buffer, class="num">1,class="num">2,N - subsequent ones)    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CEngine::BufferSetDataSection(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">class="kw">double value,class="kw">const class="type">uchar color_index,class="type">bool as_current=class="kw">false)
  {
   this.m_buffers.SetBufferSectionValue(number,series_index,value,color_index,as_current);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Set histogram buffer data from zero                              |
class=class="str">"cmt">//| by its serial number                                             |
class=class="str">"cmt">//| (class="num">0 - the very first buffer, class="num">1,class="num">2,N - subsequent ones)             |
class=class="str">"cmt">//+------------------------------------------------------------------+

直方图与填充缓冲的写入接口

CEngine 里给图形缓冲喂数据,靠的是一组 BufferSetData* 方法,而不是在外面直接碰底层数组。直方图单值用 BufferSetDataHistogram,双值(上下边界)走 BufferSetDataHistogram2,参数里的 number 是缓冲序号,0 代表第一个,1、2 往后排。 series_index 对应 K 线序列位置,0 是最右侧当前柱;as_current 默认 false,设 true 时数据直接写进「当前未闭合柱」,做实时重绘时有用。颜色用 uchar 型的 color_index 指定,和调色板索引挂钩,不是直接传 RGB。 ZigZag 和 Filling 的接口形态类似,但 Filling 不接收 color_index——它的配色在缓冲初始化阶段就绑死了,运行时只塞 value1、value2 两条边界线。下面这段代码直接贴进 MT5 的 EA 源码里就能看到方法签名,改 number 或 series_index 可验证不同柱上的绘制落点。 外汇与贵金属波动剧烈,这类自定义缓冲若写错 series_index,可能在回测里不显形、实盘却错位,务必在策略测试器逐柱核对。

MQL5 / C++
class="type">void CEngine::BufferSetDataHistogram(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">class="kw">double value,class="kw">const class="type">uchar color_index,class="type">bool as_current=class="kw">false)
  {
   this.m_buffers.SetBufferHistogramValue(number,series_index,value,color_index,as_current);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Set data of all histogram buffers on two buffers                 |
class=class="str">"cmt">//| by its serial number                                            |
class=class="str">"cmt">//| (class="num">0 - the very first buffer, class="num">1,class="num">2,N - subsequent ones)            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CEngine::BufferSetDataHistogram2(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">class="kw">double value1,class="kw">const class="type">class="kw">double value2,class="kw">const class="type">uchar color_index,class="type">bool as_current=class="kw">false)
  {
   this.m_buffers.SetBufferHistogram2Value(number,series_index,value1,value2,color_index,as_current);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Set data of all zizag buffers                                   |
class=class="str">"cmt">//| by its serial number                                            |
class=class="str">"cmt">//| (class="num">0 - the very first zigzag buffer, class="num">1,class="num">2,N - subsequent ones)     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CEngine::BufferSetDataZigZag(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">class="kw">double value1,class="kw">const class="type">class="kw">double value2,class="kw">const class="type">uchar color_index,class="type">bool as_current=class="kw">false)
  {
   this.m_buffers.SetBufferZigZagValue(number,series_index,value1,value2,color_index,as_current);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Set data of all filling buffers                                 |
class=class="str">"cmt">//| by its serial number                                            |
class=class="str">"cmt">//| (class="num">0 - the very first filling buffer, class="num">1,class="num">2,N - subsequent ones)    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CEngine::BufferSetDataFilling(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">class="kw">double value1,class="kw">const class="type">class="kw">double value2,class="type">bool as_current=class="kw">false)
  {
   this.m_buffers.SetBufferFillingValue(number,series_index,value1,value2,as_current);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+

「给缓冲层灌K线与改色的接口细节」

在自绘指标引擎里,K线类和柱状类的缓冲数据都不是直接写屏,而是先丢进编号缓冲池。CEngine 暴露了两个对称方法:BufferSetDataBars 管条形缓冲,BufferSetDataCandles 管蜡烛缓冲,二者都靠 number 定位第几个缓冲(0 是第一个,1、2、N 顺延),series_index 则是该缓冲里的具体序列位置。 调用时如果 GetBufferBars 或 GetBufferCandles 返回 NULL,函数直接 return,不会抛错——这意味着缓冲没初始化就去灌数据会静默失败,调试时容易以为是绘制逻辑问题。as_current 默认 false,置 true 时把这条数据当当前未完成柱处理,用于实时更新最后一棵 K。 颜色不走 OHLC 参数,单独用 uchar 的 color_index 标记,后续有 BuffersSetColors 批量设调色板,以及 BufferArrowSetColorIndex、BufferLineSetColorIndex、BufferSectionSetColorIndex、BufferHistogramSetColorIndex 按缓冲编号和序列位改单点颜色。外汇与贵金属波动剧烈,这类自绘缓冲若颜色索引越界,MT5 可能不报错只显示默认色,需自己加边界检查。 开 MT5 验证时,把 number 故意传一个未创建的编号,观察指标是否无声不画,就能确认这套静默返回机制是否生效。

MQL5 / C++
class=class="str">"cmt">//| by its serial number                                                    |
class=class="str">"cmt">//| (class="num">0 - the very first bar buffer, class="num">1,class="num">2,N - subsequent ones)                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CEngine::BufferSetDataBars(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">class="kw">double open,class="kw">const class="type">class="kw">double high,class="kw">const class="type">class="kw">double low,class="kw">const class="type">class="kw">double close,class="kw">const class="type">uchar color_index,class="type">bool as_current=class="kw">false)
  {
   CBufferBars *buff=this.m_buffers.GetBufferBars(number);
   if(buff==NULL)
      class="kw">return;
   this.m_buffers.SetBufferBarsValue(number,series_index,open,high,low,close,color_index,as_current);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Set data of all candle buffers                                      |
class=class="str">"cmt">//| by its serial number                                                |
class=class="str">"cmt">//| (class="num">0 - the very first candle buffer, class="num">1,class="num">2,N - subsequent ones)          |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CEngine::BufferSetDataCandles(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">class="kw">double open,class="kw">const class="type">class="kw">double high,class="kw">const class="type">class="kw">double low,class="kw">const class="type">class="kw">double close,class="kw">const class="type">uchar color_index,class="type">bool as_current=class="kw">false)
  {
   CBufferCandles *buff=this.m_buffers.GetBufferCandles(number);
   if(buff==NULL)
      class="kw">return;
   this.m_buffers.SetBufferCandlesValue(number,series_index,open,high,low,close,color_index,as_current);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void BuffersSetColors(class="kw">const class="type">class="kw">color &array_colors[]) { this.m_buffers.SetColors(array_colors); }
class="type">void BufferArrowSetColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">uchar color_index)
       { this.m_buffers.SetBufferArrowColorIndex(number,series_index,color_index);       }
class="type">void BufferLineSetColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">uchar color_index)
       { this.m_buffers.SetBufferLineColorIndex(number,series_index,color_index);         }
class="type">void BufferSectionSetColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">uchar color_index)
       { this.m_buffers.SetBufferSectionColorIndex(number,series_index,color_index);      }
class="type">void BufferHistogramSetColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">uchar color_index)

◍ 逐类图形缓冲区的着色与清空接口

在自定义指标封装里,图形缓冲区分了箭头、折线、区段、直方图、双直方图、之字折线和填充区等类型,每一类都暴露了独立的颜色索引写入与清空方法。调用时第一个参数 number 指定缓冲槽位,series_index 对应具体数据序列,color_index 用 uchar 传入调色板下标,改色不用重绘整个指标。 以直方图为例,BufferHistogramSetColorIndex 内部转调 m_buffers.SetBufferHistogramColorIndex,而 BufferHistogramClear 直接走 ClearBufferHistogram,二者参数结构完全一致,只是动作相反。蜡烛和棒线同理:BufferCandlesSetColorIndex 与 BufferBarsSetColorIndex 分别接管 K 线与 OHLC bar 的着色,清空则交给 ClearBufferCandles / ClearBufferBars 的对应包装。 通用清空入口 BufferClear 按 buffer_list_index 和 series_index 直接清掉整类缓冲,比逐类型 Clear 更粗暴。实盘接 AIGC 信号时,若某根黄金 H1 K 线被模型判为反转,可只对该 series_index 调 BufferCandlesSetColorIndex 标色,不碰其余缓冲,MT5 重绘开销明显更低;外汇与贵金属波动剧烈,这类局部刷新只降负载不降风险。

MQL5 / C++
class="type">void BufferHistogramSetColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">uchar color_index)
  { this.m_buffers.SetBufferHistogramColorIndex(number,series_index,color_index);  }
class="type">void BufferHistogram2SetColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">uchar color_index)
    { this.m_buffers.SetBufferHistogram2ColorIndex(number,series_index,color_index);  }
class="type">void BufferZigZagSetColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">uchar color_index)
    { this.m_buffers.SetBufferZigZagColorIndex(number,series_index,color_index);      }
class="type">void BufferFillingSetColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">uchar color_index)
    { this.m_buffers.SetBufferFillingColorIndex(number,series_index,color_index);     }
class="type">void BufferBarsSetColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">uchar color_index)
    { this.m_buffers.SetBufferBarsColorIndex(number,series_index,color_index);         }
class="type">void BufferCandlesSetColorIndex(class="kw">const class="type">int number,class="kw">const class="type">int series_index,class="kw">const class="type">uchar color_index)
   { this.m_buffers.SetBufferCandlesColorIndex(number,series_index,color_index);     }
class="type">void BufferClear(class="kw">const class="type">int buffer_list_index,class="kw">const class="type">int series_index) { this.m_buffers.Clear(buffer_list_index,series_index); }
class="type">void BufferArrowClear(class="kw">const class="type">int number,class="kw">const class="type">int series_index)        { this.m_buffers.ClearBufferArrow(number,series_index);     }
class="type">void BufferLineClear(class="kw">const class="type">int number,class="kw">const class="type">int series_index)         { this.m_buffers.ClearBufferLine(number,series_index);       }
class="type">void BufferSectionClear(class="kw">const class="type">int number,class="kw">const class="type">int series_index)      { this.m_buffers.ClearBufferSection(number,series_index);  }
class="type">void BufferHistogramClear(class="kw">const class="type">int number,class="kw">const class="type">int series_index)    { this.m_buffers.ClearBufferHistogram(number,series_index); }
class="type">void BufferHistogram2Clear(class="kw">const class="type">int number,class="kw">const class="type">int series_index)   { this.m_buffers.ClearBufferHistogram2(number,series_index);}
class="type">void BufferZigZagClear(class="kw">const class="type">int number,class="kw">const class="type">int series_index)       { this.m_buffers.ClearBufferZigZag(number,series_index);     }
class="type">void BufferFillingClear(class="kw">const class="type">int number,class="kw">const class="type">int series_index)      { this.m_buffers.ClearBufferFilling(number,series_index);   }

缓冲区清理与集合初始化的底层接口

CEngine 类里给指标缓冲区留了两个轻量清除入口:BufferBarsClear 按 number 和 series_index 清掉对应序列的 K 线柱缓冲,BufferCandlesClear 同理只针对蜡烛对象。两者都直接转发给 m_buffers 的 ClearBufferBars / ClearBufferCandles,本身不做事前判空,调用方得自己保证索引合法。 BuffersPrintShort 是排查缓冲区的实用函数:先通过 GetListBuffers 拿缓冲对象集合指针,list 为空直接 return;否则用 list.Total() 取总数,循环里 list.At(i) 逐个取 CBuffer 指针,非空就调 buff.PrintShort() 往日志打简短描述。在 MT5 里跑完 OnInit 后调一次,能直接看到当前加载了几个缓冲、各自参数,不用去数据窗口一个个点。 CollectionOnInit 负责把各集合指针灌给交易类和缓冲类:m_trading.OnInit 接账户、品种、行情、历史、事件五个对象;m_buffers.OnInit 只接 m_time_series 的时间序列对象。这段若在自定义引擎里漏调,缓冲区大概率取不到时间序列,绘图会静默失败。外汇与贵金属波动剧烈,这类底层初始化缺失可能在极端行情下放大信号延迟,实盘前务必在策略测试器跑一遍确认日志输出正常。

MQL5 / C++
class="type">void BufferBarsClear(class="kw">const class="type">int number,class="kw">const class="type">int series_index)      { this.m_buffers.ClearBufferBars(number,series_index);      }
class="type">void BufferCandlesClear(class="kw">const class="type">int number,class="kw">const class="type">int series_index)    { this.m_buffers.ClearBufferCandles(number,series_index);  }
class=class="str">"cmt">//+-------------------------------------------------------------------------+
class=class="str">"cmt">//| Display class="type">class="kw">short description of all indicator buffers within the collection|
class=class="str">"cmt">//+-------------------------------------------------------------------------+
class="type">void CEngine::BuffersPrintShort(class="type">void)
  {
class=class="str">"cmt">//--- Get the pointer to the collection list of buffer objects
   CArrayObj *list=this.GetListBuffers();
   if(list==NULL)
      class="kw">return;
   class="type">int total=list.Total();
class=class="str">"cmt">//--- In a loop by the number of buffers in the list,
class=class="str">"cmt">//--- get the next buffer and display its brief description in the journal
   for(class="type">int i=class="num">0;i<total;i++)
     {
       CBuffer *buff=list.At(i);
       if(buff==NULL)
         class="kw">continue;
       buff.PrintShort();
     }
   }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//--- Pass the pointers to all the necessary collections to the trading class and the indicator buffer collection class
    class="type">void                CollectionOnInit(class="type">void)
                        {
                         this.m_trading.OnInit(this.GetAccountCurrent(),m_symbols.GetObject(),m_market.GetObject(),m_history.GetObject(),m_events.GetObject());
                         this.m_buffers.OnInit(this.m_time_series.GetObject());
                        }

「把单周期指标改成多周期测试件」

验证多周期绘图逻辑,最省事的办法是拿上一篇的 test 指标改一版:存到 \MQL5\Indicators\TestDoEasy\Part45\ 下,命名 TestDoEasyPart45.mq5,并在设置里加一张时间帧清单表。指标运行时会按这张表去抓不同周期的数据,图形类型也可在外部切换,缓冲区的显隐直接跟随图形类型走。 原代码里只认当前图表周期,现在要换成时间帧数组驱动。OnInit() 里先把设置中选定的周期写进数组,函数库再据数组创建对应时间序列——也就是说,指标取数的图表周期完全由设置决定,不必碰 OnCalculate 里的计算逻辑。 缓冲区一次性建全:箭头、线段、区块、直方图、双向直方图、之字、填充、棒线、蜡烛、计算缓冲,合计 22 个数组(箭头2+线段2+区块2+直方图2+直方图2代3+之字3+填充2+棒线5+蜡烛5+计算1)。每种图形按输入在 data window 设显示标志并绑定时间帧,剩下交给函数库摆位。 之前用 CopyData() 给数组打“按时间排列”标记再翻回来,现在在 DELib.mqh 里加 CopyDataAsSeries():只把数组转成时间序列索引方向,不还原。调用它之后,OnCalculate 里不用再手动翻转,库内时间序列直接对齐。外汇与贵金属行情跳空频繁,多周期缓冲在周末缺口处可能错位,上 MT5 跑一眼便知。

MQL5 / C++
class=class="str">"cmt">/*sinput*/   ENUM_TIMEFRAMES_MODE InpModeUsedTFs     =  TIMEFRAMES_MODE_CURRENT;                 class=class="str">"cmt">// Mode of used timeframes list
sinput*/   ENUM_TIMEFRAMES_MODE InpModeUsedTFs     =  TIMEFRAMES_MODE_LIST;                 class=class="str">"cmt">// Mode of used timeframes list
sinput   ENUM_TIMEFRAMES      InpPeriod        =  PERIOD_CURRENT;                       class=class="str">"cmt">// Used chart period
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Custom indicator initialization function                        |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
  {
class=class="str">"cmt">//--- Write the name of the working timeframe selected in the settings to the InpUsedTFs variable
   InpUsedTFs=TimeframeDescription(InpPeriod);
class=class="str">"cmt">//--- indicator buffers mapping
class=class="str">"cmt">//--- Create all the necessary buffer objects
   engine.BufferCreateArrow();         class=class="str">"cmt">// class="num">2 arrays
   engine.BufferCreateLine();          class=class="str">"cmt">// class="num">2 arrays
   engine.BufferCreateSection();       class=class="str">"cmt">// class="num">2 arrays
   engine.BufferCreateHistogram();     class=class="str">"cmt">// class="num">2 arrays
   engine.BufferCreateHistogram2();    class=class="str">"cmt">// class="num">3 arrays
   engine.BufferCreateZigZag();        class=class="str">"cmt">// class="num">3 arrays
   engine.BufferCreateFilling();       class=class="str">"cmt">// class="num">2 arrays
   engine.BufferCreateBars();          class=class="str">"cmt">// class="num">5 arrays
   engine.BufferCreateCandles();       class=class="str">"cmt">// class="num">5 arrays
   engine.BufferCreateCalculate();     class=class="str">"cmt">// class="num">1 array
class=class="str">"cmt">//--- Check the number of buffers specified in the &class="macro">#x27;properties&class="macro">#x27; block
   if(engine.BuffersPropertyPlotsTotal()!=indicator_plots)
      Alert(TextByLanguage("Внимание! Значение \"indicator_plots\" должно быть ","Attention! Value of \"indicator_plots\" should be "),engine.BuffersPropertyPlotsTotal());
   if(engine.BuffersPropertyBuffersTotal()!=indicator_buffers)
      Alert(TextByLanguage("Внимание! Значение \"indicator_buffers\" должно быть ","Attention! Value of \"indicator_buffers\" should be "),engine.BuffersPropertyBuffersTotal());
     
class=class="str">"cmt">//--- Create the class="type">class="kw">color array and set non-class="kw">default colors to all buffers within the collection
   class="type">class="kw">color array_colors[]={clrDodgerBlue,clrRed,clrGray};
   engine.BuffersSetColors(array_colors);
class=class="str">"cmt">//--- Set the line width for ZigZag(the sixth drawn buffer)
class=class="str">"cmt">//--- It has the index of class="num">5 considering that the starting point is zero
   CBuffer *buff_zz=engine.GetBufferByPlot(class="num">5);
   if(buff_zz!=NULL)
     {
     buff_zz.SetWidth(class="num">2);

◍ 缓冲显示与数据序列化的落地写法

在指标初始化收尾阶段,需要把集合里的每个缓冲对象按用途和周期配置刷一遍显示属性。下面这段循环遍历 engine 的缓冲列表,对第 i 个对象先取指针判空,再依据 IsUse(buff.Status()) 决定数据窗口是否绘制,并把周期统一设为输入参数 InpPeriod。 for(int i=0;i<engine.GetListBuffers().Total();i++) { CBuffer *buff=engine.GetListBuffers().At(i); if(buff==NULL) continue; buff.SetShowData(IsUse(buff.Status())); buff.SetTimeframe(InpPeriod); } CopyData 函数承接第二形态 OnCalculate 的入参,核心动作是先对齐时间序列索引。MT5 中 time/open/high/low/close 等数组默认不一定是 AS_SERIES 方向,代码逐个用 ArrayGetAsSeries 探测,若返回 false 就 ArraySetAsSeries(...,true) 强制倒序,使下标 0 对应最新柱。这一处若漏写,复制零号柱数据进 rates_data 时会出现偏移一根 K 线的错位。 对外汇与贵金属品种加载这类自定义指标时,点差数组 spread 在部分经纪商环境可能全为 0,序列化后用于计算仍要加概率性判断,别直接当真实成本用。开 MT5 把这段贴进 EA 或指标工程,改 InpPeriod 看数据窗口缓冲跟随切换,即可验证。

MQL5 / C++
  for(class="type">int i=class="num">0;i<engine.GetListBuffers().Total();i++)
  {
  CBuffer *buff=engine.GetListBuffers().At(i);
  if(buff==NULL)
     class="kw">continue;
  buff.SetShowData(IsUse(buff.Status()));
  buff.SetTimeframe(InpPeriod);
  }

class="type">void CopyData(class="kw">const class="type">int rates_total,
              class="kw">const class="type">int prev_calculated,
              class="kw">const class="type">class="kw">datetime &time[],
              class="kw">const class="type">class="kw">double &open[],
              class="kw">const class="type">class="kw">double &high[],
              class="kw">const class="type">class="kw">double &low[],
              class="kw">const class="type">class="kw">double &close[],
              class="kw">const class="type">long &tick_volume[],
              class="kw">const class="type">long &volume[],
              class="kw">const class="type">int &spread[])
  {
  class="type">bool as_series_time=ArrayGetAsSeries(time);
  if(!as_series_time)
     ArraySetAsSeries(time,true);
  class="type">bool as_series_open=ArrayGetAsSeries(open);
  if(!as_series_open)
     ArraySetAsSeries(open,true);
  class="type">bool as_series_high=ArrayGetAsSeries(high);
  if(!as_series_high)
     ArraySetAsSeries(high,true);
  class="type">bool as_series_low=ArrayGetAsSeries(low);
  if(!as_series_low)
     ArraySetAsSeries(low,true);
  class="type">bool as_series_close=ArrayGetAsSeries(close);
  if(!as_series_close)
     ArraySetAsSeries(close,true);
  class="type">bool as_series_tick_volume=ArrayGetAsSeries(tick_volume);
  if(!as_series_tick_volume)
     ArraySetAsSeries(tick_volume,true);
  class="type">bool as_series_volume=ArrayGetAsSeries(volume);
  if(!as_series_volume)
     ArraySetAsSeries(volume,true);
  class="type">bool as_series_spread=ArrayGetAsSeries(spread);
  if(!as_series_spread)
     ArraySetAsSeries(spread,true);
  rates_data.rates_total=rates_total;

把 OnCalculate 数组塞进结构体的收尾动作

这段逻辑做两件事:先把当前柱面的最新行情写进自定义结构体,再把外部传入的数组索引方向还原回调用前的状态。写结构体时全部取 [0] 下标,也就是当前未闭合 K 线——time、open、high、low、close、tick_volume 直接赋值,real_volume 和 spread 用预编译宏区分 MQL5 环境,非 MQL5 时填 0。 还原方向那段不能省。函数开头用 ArraySetAsSeries 把数组设成了序列模式(最新价在 [0]),但调用方可能原本是普通数组(最旧在 [0])。代码里用 as_series_xxx 布尔变量记下原状态,false 就调 ArraySetAsSeries(xxx,false) 翻回去,避免污染调用者的其他计算。 OnCalculate 入口处那 8 行 ArraySetAsSeries(...,true) 是固定套路:open/high/low/close/time/tick_volume/volume/spread 全部序列化,这样 [0] 恒为当前柱。CopyDataAsSeries 函数签名接收 rates_total、prev_calculated 和 8 个数组引用,职责就是把第二种 OnCalculate 形态的数据搬进结构体并统一标记为序列。 开 MT5 新建指标时,若你混用了多种 OnCalculate 重载,直接抄这套「进函数序列化、出函数还原」的写法,能少踩一半下标错位的坑。外汇与贵金属杠杆高,指标逻辑错误可能放大下单偏差,验证前先用历史回放跑一遍。

MQL5 / C++
  rates_data.prev_calculated=prev_calculated;
  rates_data.rates.time=time[class="num">0];
  rates_data.rates.open=open[class="num">0];
  rates_data.rates.high=high[class="num">0];
  rates_data.rates.low=low[class="num">0];
  rates_data.rates.close=close[class="num">0];
  rates_data.rates.tick_volume=tick_volume[class="num">0];
  rates_data.rates.real_volume=(class="macro">#ifdef __MQL5__ volume[class="num">0] class="macro">#else class="num">0 class="macro">#endif);
  rates_data.rates.spread=(class="macro">#ifdef __MQL5__ spread[class="num">0] class="macro">#else class="num">0 class="macro">#endif);
class=class="str">"cmt">//--- Return the arrays&class="macro">#x27; initial indexing direction
  if(!as_series_time)
     ArraySetAsSeries(time,class="kw">false);
  if(!as_series_open)
     ArraySetAsSeries(open,class="kw">false);
  if(!as_series_high)
     ArraySetAsSeries(high,class="kw">false);
  if(!as_series_low)
     ArraySetAsSeries(low,class="kw">false);
  if(!as_series_close)
     ArraySetAsSeries(close,class="kw">false);
  if(!as_series_tick_volume)
     ArraySetAsSeries(tick_volume,class="kw">false);
  if(!as_series_volume)
     ArraySetAsSeries(volume,class="kw">false);
  if(!as_series_spread)
     ArraySetAsSeries(spread,class="kw">false);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| OnCalculate code block for working with the indicator:            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//--- 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);
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Copy data from the second OnCalculate() form to the structure     |
class=class="str">"cmt">//| and set the "as timeseries" flag to all arrays                   |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CopyDataAsSeries(class="kw">const class="type">int rates_total,
                     class="kw">const class="type">int prev_calculated,
                     class="kw">const class="type">class="kw">datetime &time[],
                     class="kw">const class="type">class="kw">double &open[],
                     class="kw">const class="type">class="kw">double &high[],
                     class="kw">const class="type">class="kw">double &low[],
                     class="kw">const class="type">class="kw">double &close[],

「把 OnCalculate 数据搬进时间序列结构」

在 MT5 自定义指标里,OnCalculate 传入的各类数组默认按时间正序排列,但大多数指标逻辑依赖最新的 0 号 BAR 在头部。上面这段辅助函数 CopyDataAsSeries 做的第一件事,就是对 time、open、high、low、close、tick_volume、volume、spread 八个引用数组逐一调用 ArraySetAsSeries(...,true),把索引方向翻成和时序一致。 翻完方向后,函数把 rates_total 与 prev_calculated 直接写进全局的 rates_data 结构,再把 0 号元素(即当前未完成 BAR)的八个字段逐一赋值给 rates_data.rates。注意 real_volume 和 spread 都用 #ifdef __MQL5__ 做了分支:MQL5 环境下取 volume[0] 与 spread[0],否则填 0,避免在 MQL4 兼容层编译报错。 实际写指标时,只要在你的 OnCalculate 开头加一行 CopyDataAsSeries(rates_total,prev_calculated,time,open,high,low,close,tick_volume,volume,spread);,后续计算就能统一从 rates_data.rates 读当前 BAR,不用反复传参。外汇与贵金属品种点差跳动频繁,spread[0] 在流动性稀薄时段可能瞬间拉到平时 3~5 倍,用这套结构取数能少写很多重复代码。

MQL5 / C++
class="kw">const class="type">long &tick_volume[],
class="kw">const class="type">long &volume[],
class="kw">const class="type">int &spread[])
  {
class=class="str">"cmt">//--- set the indexing direction or the arrays as in the timeseries
   ArraySetAsSeries(time,true);
   ArraySetAsSeries(open,true);
   ArraySetAsSeries(high,true);
   ArraySetAsSeries(low,true);
   ArraySetAsSeries(close,true);
   ArraySetAsSeries(tick_volume,true);
   ArraySetAsSeries(volume,true);
   ArraySetAsSeries(spread,true);
class=class="str">"cmt">//--- Copy the arrays&class="macro">#x27; 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[class="num">0];
   rates_data.rates.open=open[class="num">0];
   rates_data.rates.high=high[class="num">0];
   rates_data.rates.low=low[class="num">0];
   rates_data.rates.close=close[class="num">0];
   rates_data.rates.tick_volume=tick_volume[class="num">0];
   rates_data.rates.real_volume=(class="macro">#ifdef __MQL5__ volume[class="num">0] class="macro">#else class="num">0 class="macro">#endif);
   rates_data.rates.spread=(class="macro">#ifdef __MQL5__ spread[class="num">0] class="macro">#else class="num">0 class="macro">#endif);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Custom indicator iteration function                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnCalculate(class="kw">const class="type">int rates_total,
                class="kw">const class="type">int prev_calculated,
                class="kw">const class="type">class="kw">datetime &time[],
                class="kw">const class="type">class="kw">double &open[],
                class="kw">const class="type">class="kw">double &high[],
                class="kw">const class="type">class="kw">double &low[],
                class="kw">const class="type">class="kw">double &close[],
                class="kw">const class="type">long &tick_volume[],
                class="kw">const class="type">long &volume[],
                class="kw">const class="type">int &spread[])
  {
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| OnCalculate code block for working with the library:             |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//--- Pass the current symbol data from OnCalculate() to the price structure and set the "as timeseries" flag to the arrays
   CopyDataAsSeries(rates_total,prev_calculated,time,open,high,low,close,tick_volume,volume,spread);
class=class="str">"cmt">//--- Check for the minimum number of bars for calculation

◍ 指标主循环里如何按K线方向刷缓冲

在自定义指标的主计算循环里,先按 limit 判定重算范围:limit>1 意味着首启或历史变动,此时把 limit 置为 rates_total-1 并调用 BuffersInitPlots 与 BuffersInitCalculates 做全量初始化;否则只处理新增及当前柱。 循环从 i=limit 向下走到 WRONG_VALUE,并用 !IsStopped() 兜底,避免终端关闭时还空跑。每根柱先用 BufferArrowClear、BufferLineClear 等把九类图形缓冲(箭头、线、区间、直方、双直方、之字、填充、柱、蜡烛)在索引 0 清掉,再取对应周期 K 线对象。 取柱用 engine.SeriesGetBar(NULL,InpPeriod,time[i]),拿不到就 continue 跳走。方向判定靠 bar.TypeBody():多头柱 color_index=0,空头=1,其他=2,随后按 IsUse(BUFFER_STATUS_ARROW) 开关写入箭头缓冲的收盘价与颜色。外汇与贵金属波动剧烈,这类缓冲刷新逻辑若 limit 算错可能造成历史重绘,建议在 MT5 策略测试器用 2023 年 XAUUSD 的 M5 数据跑一遍验证重算触发次数。

MQL5 / C++
if(rates_total<min_bars || Point()==class="num">0) class="kw">return class="num">0;

class=class="str">"cmt">//--- Handle the Calculate event in the library
class=class="str">"cmt">//--- If the OnCalculate() method of the library returns zero, not all timeseries are ready - leave till the next tick
  if(engine.OnCalculate(rates_data,rates_total,prev_calculated,begin)==class="num">0)
    class="kw">return class="num">0;

class=class="str">"cmt">//--- If working in the tester
  if(MQLInfoInteger(MQL_TESTER))
    {
      engine.OnTimer(rates_data);    class=class="str">"cmt">// Working in the library timer
      EventsHandling();              class=class="str">"cmt">// Working with library events
    }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| OnCalculate code block for working with the indicator:           |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//--- Check and calculate the number of calculated bars
class=class="str">"cmt">//--- If limit = class="num">0, there are no new bars - calculate the current one
class=class="str">"cmt">//--- If limit = class="num">1, a new bar has appeared - calculate the first and the current ones
class=class="str">"cmt">//--- limit > class="num">1 means the first launch or changes in history - the full recalculation of all data
  class="type">int limit=rates_total-prev_calculated;

class=class="str">"cmt">//--- Recalculate the entire history
  if(limit>class="num">1)
    {
      limit=rates_total-class="num">1;
      engine.BuffersInitPlots();
      engine.BuffersInitCalculates();
    }
class=class="str">"cmt">//--- Prepare data
class=class="str">"cmt">//--- Calculate the indicator
  CBar *bar=NULL;          class=class="str">"cmt">// Bar object for defining the candle direction
  class="type">uchar color_index=class="num">0;     class=class="str">"cmt">// Color index to be set for the buffer depending on the candle direction
class=class="str">"cmt">//--- Main calculation loop of the indicator
  for(class="type">int i=limit; i>WRONG_VALUE && !IsStopped(); i--)
    {
      class=class="str">"cmt">//--- Clear the current bar of all created buffers
      engine.BufferArrowClear(class="num">0,class="num">0);
      engine.BufferLineClear(class="num">0,class="num">0);
      engine.BufferSectionClear(class="num">0,class="num">0);
      engine.BufferHistogramClear(class="num">0,class="num">0);
      engine.BufferHistogram2Clear(class="num">0,class="num">0);
      engine.BufferZigZagClear(class="num">0,class="num">0);
      engine.BufferFillingClear(class="num">0,class="num">0);
      engine.BufferBarsClear(class="num">0,class="num">0);
      engine.BufferCandlesClear(class="num">0,class="num">0);

      class=class="str">"cmt">//--- Get the timeseries bar corresponding to the loop index time on the chart period specified in the settings
      bar=engine.SeriesGetBar(NULL,InpPeriod,time[i]);
      if(bar==NULL)
        class="kw">continue;
      class=class="str">"cmt">//--- Calculate the class="type">class="kw">color index depending on the candle direction on the timeframe specified in the settings
      color_index=(bar.TypeBody()==BAR_BODY_TYPE_BULLISH ? class="num">0 : bar.TypeBody()==BAR_BODY_TYPE_BEARISH ? class="num">1 : class="num">2);
      class=class="str">"cmt">//--- Check the settings and calculate the arrow buffer
      if(IsUse(BUFFER_STATUS_ARROW))
        engine.BufferSetDataArrow(class="num">0,i,bar.Close(),color_index);
      class=class="str">"cmt">//--- Check the settings and calculate the line buffer

多形态缓冲区的条件写入逻辑

这段循环体展示了指标引擎如何按开关逐类写入缓冲区:每一个 BUFFER_STATUS_* 宏都对应一种可视化形态,只有 IsUse() 返回真才执行对应 BufferSetData* 调用,避免无谓计算。 折线用 Open 画、区段用 Close 画、零轴直方图传 open[i]、双缓冲直方图传开收价差——这些都是在 i 位置按 bar 结构喂数据,color_index 决定着色。 ZigZag 与 Filling 需要根据多空翻转取值:阳线 ZigZag 取 Low,阴线(color_index==1)取 High;Filling 阳线边界是 High/Low,阴线交换成 Low/High 以切换填充色。 最后若开启 BUFFER_STATUS_BARS 或 CANDLES,则把 OHLC 四价一次性送入对应缓冲。函数末尾 return(rates_total) 把已处理柱数交还系统,供下次增量调用。 开 MT5 新建指标把这段粘进 OnCalculate,把 IsUse 各宏先全置 1,能在副图同时看到线、区段、直方图与蜡烛,验证缓冲映射是否正确。外汇与贵金属波动剧烈,这类多缓冲渲染仅作结构参考,实盘信号须自担高风险。

MQL5 / C++
if(IsUse(BUFFER_STATUS_LINE))
   engine.BufferSetDataLine(class="num">0,i,bar.Open(),color_index);
class=class="str">"cmt">//--- Check the settings and calculate the section buffer
if(IsUse(BUFFER_STATUS_SECTION))
   engine.BufferSetDataSection(class="num">0,i,bar.Close(),color_index);
class=class="str">"cmt">//--- Check the settings and calculate the histogram from zero buffer
if(IsUse(BUFFER_STATUS_HISTOGRAM))
   engine.BufferSetDataHistogram(class="num">0,i,open[i],color_index);
class=class="str">"cmt">//--- Check the settings and calculate the &class="macro">#x27;histogram on two buffers&class="macro">#x27; buffer
if(IsUse(BUFFER_STATUS_HISTOGRAM2))
   engine.BufferSetDataHistogram2(class="num">0,i,bar.Open(),bar.Close(),color_index);
class=class="str">"cmt">//--- Check the settings and calculate the zigzag buffer
if(IsUse(BUFFER_STATUS_ZIGZAG))
   {
   class=class="str">"cmt">//--- Set the bar&class="macro">#x27;s Low value value for the zigzag(for bullish candles)
   class="type">class="kw">double value1=bar.Low();
   class="type">class="kw">double value2=value1;
   class=class="str">"cmt">//--- If the candle is bearish(class="type">class="kw">color index = class="num">1), set the bar&class="macro">#x27;s High value for the zigzag
   if(color_index==class="num">1)
     {
      value1=value2=bar.High();
     }
   engine.BufferSetDataZigZag(class="num">0,i,value1,value2,color_index);
   }
class=class="str">"cmt">//--- Check the settings and calculate the filling buffer
if(IsUse(BUFFER_STATUS_FILLING))
   {
   class=class="str">"cmt">//--- Set filling border values for bullish candles
   class="type">class="kw">double value1=bar.High();
   class="type">class="kw">double value2=bar.Low();
   class=class="str">"cmt">//--- In case of the bearish candle(class="type">class="kw">color index = class="num">1), swap the filling borders to change the class="type">class="kw">color
   if(color_index==class="num">1)
     {
      value1=bar.Low();
      value2=bar.High();
     }
   engine.BufferSetDataFilling(class="num">0,i,value1,value2,color_index);
   }
class=class="str">"cmt">//--- Check the settings and calculate the bar buffer
if(IsUse(BUFFER_STATUS_BARS))
   engine.BufferSetDataBars(class="num">0,i,bar.Open(),bar.High(),bar.Low(),bar.Close(),color_index);
class=class="str">"cmt">//--- Check the settings and calculate the candle buffer
if(IsUse(BUFFER_STATUS_CANDLES))
   engine.BufferSetDataCandles(class="num">0,i,bar.Open(),bar.High(),bar.Low(),bar.Close(),color_index);
   }
class=class="str">"cmt">//--- class="kw">return value of prev_calculated for next call
   class="kw">return(rates_total);
   }

「缓冲区集合类之后要补的指标操作」

这一节原本是系列第 45 篇的收尾,作者点出下一步要做的只有一件事:把指标缓冲区集合类继续写完整,并加上多品种模式的指标调度逻辑。当前附件里的 MQL5.zip 体积为 3772.33 KB,含函数库全部文件与测试 EA,仅能在 MetaTrader 5 下编译运行,MT4 尚未验证。 有读者在评论里提到,用第 45 版示例去连第 90 版库会编译报错,作者确认这是库迭代导致的接口变动,并举例:TestDoEasyPart45.mq5 第 403 行需把 engine.SetSoundsStandart() 改成 engine.SetSoundsStandard(),Buffer.mqh 第 84 行要给 PrintShort 补空实现体。你在 MT5 里若也遇到类似报错,优先对照自己用的库版本号查接口差异。 外汇与贵金属交易自带高杠杆高风险,这类库只解决编码效率,不预示任何收益。等缓冲区集合类真正支持标准/自定义指标类后,跨周期拉 MACD 到子窗口这类需求写起来才会省事,在那之前建议先拿当前 zip 跑通基础样例。

把跨周期校对交给小布
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到多周期缓冲区的映射状态,你专注决策而非对着柱线数格子。

常见问题

按绘图样式各自排序列,箭头缓冲区有自己 0、1、2 的序列,线条缓冲区也是独立从 0 起算,与总创建次序无关。
落在 H1 柱线时间覆盖范围内的四根 M15 柱会填相同数值,由函数库按时间包含关系自动映射。
可用图形系列名称、时间、数据窗口索引和集合列表索引访问,也能直接抓最后一个创建的对象设属性。
可以,小布的品种页已内置 AIGC 诊断,能呈现 DoEasy 式缓冲区的周期映射,省去你开 MT5 编辑器核对。
从 1000 提升到 5000,函数库据此改进了品种列表操控类以适应更大规模。
目标是一行代码带请求属性生成对象,按绘图样式索引访问,不必再写大段初始化分支。