多交易品种多周期指标中的颜色缓冲区·进阶篇
(2/3)·单色缓冲区复制易上手,多色索引数组却常让跨周期绘制踩坑,本篇拆开讲清
◍ 多缓冲区的绑定与空算骨架
在 MT5 自定义指标里,一个绘图缓冲(plot)可以挂多组数据数组。上面这段把第 4 个 plot 绑了 2 个数据数组加 1 个颜色索引数组(索引 9/10/11),第 5 个 plot 直接绑了 4 根线(Open/High/Low/Close)加颜色索引,共占 5 个 buffer 槽位(12~16)。这种写法适合把 OHLC 四价或双线组合画在同一个子图里,省去重复建指标的麻烦。 OnCalculate 这里故意写成直接 return(rates_total),没有写任何计算逻辑。也就是说指标加载后每根 K 线都会触发,但缓冲里没填数,图表上什么都不会画——这通常是先搭好 buffer 框架、后续再补绘制代码的占位结构。 外汇与贵金属波动剧烈、杠杆风险高,这类半成品指标绝不能直接挂实盘。开 MT5 按 F4 把代码贴进新指标,先编译确认 buffer 索引不越界(本例最大用到 16,需在 #property indicator_buffers 里至少声明 17 个),再决定往哪根线填价格。
class=class="str">"cmt">//--- Plot buffer class="num">3. Two arrays for construction SetIndexBuffer(class="num">7,Label4Buffer,INDICATOR_DATA); class=class="str">"cmt">// Data buffer SetIndexBuffer(class="num">8,Label4Colors,INDICATOR_COLOR_INDEX); class=class="str">"cmt">// Buffer of class="type">color indexes class=class="str">"cmt">//--- Plot buffer class="num">4. Three arrays for construction SetIndexBuffer(class="num">9,Label5Buffer1,INDICATOR_DATA); class=class="str">"cmt">// Data buffer for line class="num">1 SetIndexBuffer(class="num">10,Label5Buffer2,INDICATOR_DATA); class=class="str">"cmt">// Data buffer for line class="num">2 SetIndexBuffer(class="num">11,Label5Colors,INDICATOR_COLOR_INDEX); class=class="str">"cmt">// Buffer of class="type">color indexes class=class="str">"cmt">//--- Plot buffer class="num">5. Five arrays for construction SetIndexBuffer(class="num">12,Label6Buffer1,INDICATOR_DATA); class=class="str">"cmt">// Data buffer for line class="num">1 Open SetIndexBuffer(class="num">13,Label6Buffer2,INDICATOR_DATA); class=class="str">"cmt">// Data buffer for line class="num">2 High SetIndexBuffer(class="num">14,Label6Buffer3,INDICATOR_DATA); class=class="str">"cmt">// Data buffer for line class="num">3 Low SetIndexBuffer(class="num">15,Label6Buffer4,INDICATOR_DATA); class=class="str">"cmt">// Data buffer for line class="num">4 Close SetIndexBuffer(class="num">16,Label6Colors,INDICATOR_COLOR_INDEX); class=class="str">"cmt">// Buffer of class="type">color indexes } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Custom indicator iteration function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnCalculate(const class="type">int rates_total, const class="type">int prev_calculated, const class="type">class="kw">datetime &time[], const class="type">class="kw">double &open[], const class="type">class="kw">double &high[], const class="type">class="kw">double &low[], const class="type">class="kw">double &close[], const class="type">long &tick_volume[], const class="type">long &volume[], const class="type">int &spread[]) { class=class="str">"cmt">//--- class="kw">return value of prev_calculated for the next call class="kw">return(rates_total); }
「缓冲区与绘图类型的硬约束」
在 MT5 自定义指标里,#property 预处理器指令直接决定了图表上能画什么、怎么画。上面这段声明里 indicator_buffers 设成 17,意味着指标总共开了 17 个数据数组;而 indicator_plots 只有 6,说明其中仅 6 个数组组会被实际渲染成图形序列,其余 11 个只能闷头算中间量。 六种绘图类型对数组数的胃口完全不同。DRAW_LINE 吃掉 1 个数组,DRAW_FILLING 要 2 个(两条线夹出填充区),DRAW_CANDLES 直接吞掉 4 个 OHLC 数组,DRAW_COLOR_LINE 也要 2 个但第二个是颜色索引数组。开缓冲区时数错数组,编译能过、画图必崩。 颜色切换不是装饰。DRAW_FILLING 的 clrRed,clrDeepSkyBlue 会根据上下线谁高自动选色,DRAW_COLOR_LINE 最多挂 64 色,靠颜色索引数组逐根 bar 指认。外汇和贵金属波动快,这种视觉分层对盯盘分辨结构有帮助,但杠杆品种高风险,别把颜色信号当方向背书。 直接把这段声明贴进 MQ5 文件头,改 indicator_buffers 前先算清 6 个 plot 一共占了多少数组——上面例子 1+2+4+2 已用掉 9 个,剩下 8 个留给计算缓冲,账要对得上才能跑。
class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Copyright class="num">2023, MetaQuotes Ltd." class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class="macro">#class="kw">property indicator_chart_window class="macro">#class="kw">property indicator_buffers class="num">17 class=class="str">"cmt">// class="num">17 arrays for indicator calculations in total class="macro">#class="kw">property indicator_plots class="num">6 class=class="str">"cmt">// Out of class="num">17 arrays, class="num">6 graphic series are plotting buffers class=class="str">"cmt">//--- plot Label1 class="macro">#class="kw">property indicator_label1 "Label1" class="macro">#class="kw">property indicator_type1 DRAW_LINE class=class="str">"cmt">// Plot buffer with index class="num">0 is a simple line, requires one array to plot class="macro">#class="kw">property indicator_color1 clrRed class=class="str">"cmt">// Color line: Red class="macro">#class="kw">property indicator_style1 STYLE_SOLID class="macro">#class="kw">property indicator_width1 class="num">1 class=class="str">"cmt">//--- plot Label2 class="macro">#class="kw">property indicator_label2 "Label2" class="macro">#class="kw">property indicator_type2 DRAW_FILLING class=class="str">"cmt">// Plot buffer with index class="num">1 is a colored area drawn between two indicator lines, requires two arrays to plot class="macro">#class="kw">property indicator_color2 clrRed,clrDeepSkyBlue class=class="str">"cmt">// Area class="type">color: wither Red or DeepSkyBlue depending on which line is higher class="macro">#class="kw">property indicator_style2 STYLE_SOLID class="macro">#class="kw">property indicator_width2 class="num">1 class=class="str">"cmt">//--- plot Label3 class="macro">#class="kw">property indicator_label3 "Label3" class="macro">#class="kw">property indicator_type3 DRAW_CANDLES class=class="str">"cmt">// Plot buffer with index class="num">2, display as single-class="type">color candlesticks, requires class="num">4 arrays of OHLC data to plot class="macro">#class="kw">property indicator_color3 clrDarkSalmon class=class="str">"cmt">// Color: DarkSalmon class="macro">#class="kw">property indicator_style3 STYLE_SOLID class="macro">#class="kw">property indicator_width3 class="num">1 class=class="str">"cmt">//--- plot Label4 class="macro">#class="kw">property indicator_label4 "Label4" class="macro">#class="kw">property indicator_type4 DRAW_COLOR_LINE class=class="str">"cmt">// Plot buffer with index class="num">3, class="type">color line, requires two arrays to plot: data array + class="type">color index array class="macro">#class="kw">property indicator_color4 clrRed,clrRoyalBlue class=class="str">"cmt">// Two colors: Red and RoyalBlue. Can have up to class="num">64 colors class="macro">#class="kw">property indicator_style4 STYLE_SOLID class="macro">#class="kw">property indicator_width4 class="num">1 class=class="str">"cmt">//--- plot Label5 class="macro">#class="kw">property indicator_label5 "Label5"
双线直方图与彩K线的缓冲阵列配置
在 MT5 自定义指标里,DRAW_COLOR_HISTOGRAM2 这种绘图类型看着像一根柱,实则要吃三块数组:两条数据线加一个颜色索引。上面那段声明把第 5 号图(index 4)设成了红、绿、原木色三色切换,最多可扩到 64 色,但线宽锁在 1。 第 6 号图(index 5)走的是 DRAW_COLOR_CANDLES,也就是彩色蜡烛。它比双线直方图更吃资源——四条 OHLC 数据数组外加一个颜色索引数组,合计五块缓冲。颜色用了红、蓝、灰三套,同样支持最多 64 色。 输入项里只有一个 InpHidePlotIndex,默认 0,用来控制隐藏某条绘图序列。缓冲声明部分能看到 Label5Buffer1、Label5Buffer2 配 Label5Colors,正好对应前面说的「2 数据数组 + 颜色数组」结构;Label6 那边则是 4 个数据数组加 1 个颜色数组。 开 MT5 新建一个指标把这段声明贴进 mq5 头部,编译后能在数据窗口数出 13 个缓冲队列(Label1 到 Label5Colors 全算上)。外汇和贵金属波动剧烈,这类多缓冲指标在低频品种上可能卡顿,实盘前先在模拟盘验证资源占用。
class="macro">#class="kw">property indicator_type5 DRAW_COLOR_HISTOGRAM2 class=class="str">"cmt">// Plot buffer with index class="num">4, class="type">color histogram between two lines, requires three arrays: class="num">2 data arrays + class="type">color index array class="macro">#class="kw">property indicator_color5 clrRed,clrForestGreen,clrBurlyWood class=class="str">"cmt">// Three colors: Red, ForestGreen and BurlyWood. Can have up to class="num">64 colors class="macro">#class="kw">property indicator_style5 STYLE_SOLID class="macro">#class="kw">property indicator_width5 class="num">1 class=class="str">"cmt">//--- plot Label6 class="macro">#class="kw">property indicator_label6 "Label6" class=class="str">"cmt">// Plot buffer with index class="num">5, class="type">color histogram between two lines, requires class="num">5 arrays: class="num">4 OHLC data arrays + class="type">color index array class="macro">#class="kw">property indicator_type6 DRAW_COLOR_CANDLES class=class="str">"cmt">// Three colors: Red, Blue and Gray. Can have up to class="num">64 colors class="macro">#class="kw">property indicator_color6 clrRed,clrBlue,clrGray class="macro">#class="kw">property indicator_style6 STYLE_SOLID class="macro">#class="kw">property indicator_width6 class="num">1 class=class="str">"cmt">//--- input variables input class="type">uchar InpHidePlotIndex = class="num">0; class=class="str">"cmt">// Hide Plot Index class=class="str">"cmt">//--- indicator buffers class="type">class="kw">double Label1Buffer[]; class=class="str">"cmt">// Plot buffer with index class="num">0 class="type">class="kw">double Label2Buffer1[]; class=class="str">"cmt">// Plot buffer with index class="num">1, array class="num">1 class="type">class="kw">double Label2Buffer2[]; class=class="str">"cmt">// Plot buffer with index class="num">1, array class="num">2 class="type">class="kw">double Label3Buffer1[]; class=class="str">"cmt">// Plot buffer with index class="num">2, array class="num">1 class="type">class="kw">double Label3Buffer2[]; class=class="str">"cmt">// Plot buffer with index class="num">2, array class="num">2 class="type">class="kw">double Label3Buffer3[]; class=class="str">"cmt">// Plot buffer with index class="num">2, array class="num">3 class="type">class="kw">double Label3Buffer4[]; class=class="str">"cmt">// Plot buffer with index class="num">2, array class="num">4 class="type">class="kw">double Label4Buffer[]; class=class="str">"cmt">// Plot buffer with index class="num">3 class="type">class="kw">double Label4Colors[]; class=class="str">"cmt">// Color index array for plot buffer with index class="num">3 class="type">class="kw">double Label5Buffer1[]; class=class="str">"cmt">// Plot buffer with index class="num">4, array class="num">1 class="type">class="kw">double Label5Buffer2[]; class=class="str">"cmt">// Plot buffer with index class="num">4, array class="num">2 class="type">class="kw">double Label5Colors[]; class=class="str">"cmt">// Color index array for plot buffer with index class="num">4
◍ 多数组绘图缓冲的绑定套路
在 MT5 自定义指标里,一个 Plot 可以挂多根线,靠的是把多个数组依次映射到连续索引的缓冲区。上面这段声明了 Label6Buffer1~4 四个 double 数组加一个 Label6Colors 颜色索引数组,准备给索引 5 的 Plot 做四线同绘加变色。 OnInit 里能看到清晰规律:Plot 0 只占缓冲 0;Plot 1 用缓冲 1、2 两根线;Plot 2 用缓冲 3~6 拼出 OHLC 四线;Plot 3 是缓冲 7 数据加缓冲 8 颜色索引;Plot 4 是 9、10 数据加 11 颜色;Plot 5 从缓冲 12 起接着排。总共已用到 13 个缓冲槽(0~12),写指标时数错索引会直接编译报 Array out of range。 hide_index 那行做了个截断:InpHidePlotIndex 大于 5 就锁成 5,避免外部输入越界隐藏不存在的 Plot。外汇和贵金属波动大,这类边界处理能减少实盘加载指标时的闪退概率。 把缓冲分配抄进自己指标,开 MT5 用 MetaEditor 编译看缓冲总数是否和 Plot 数匹配,是验证多线绘制逻辑最快的一步。
class="type">class="kw">double Label6Buffer1[]; class=class="str">"cmt">// Plot buffer with index class="num">5, array class="num">1 class="type">class="kw">double Label6Buffer2[]; class=class="str">"cmt">// Plot buffer with index class="num">5, array class="num">2 class="type">class="kw">double Label6Buffer3[]; class=class="str">"cmt">// Plot buffer with index class="num">5, array class="num">3 class="type">class="kw">double Label6Buffer4[]; class=class="str">"cmt">// Plot buffer with index class="num">5, array class="num">4 class="type">class="kw">double Label6Colors[]; class=class="str">"cmt">// Color index array for plot buffer with index class="num">5 class=class="str">"cmt">//--- global variables class="type">int hide_index=(InpHidePlotIndex>class="num">5 ? class="num">5 : InpHidePlotIndex); class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Custom indicator initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//--- indicator buffers mapping class=class="str">"cmt">//--- Plot buffer class="num">0. One array to plot SetIndexBuffer(class="num">0,Label1Buffer,INDICATOR_DATA); class=class="str">"cmt">// Data buffer class=class="str">"cmt">//--- Plot buffer class="num">1. Two arrays for construction SetIndexBuffer(class="num">1,Label2Buffer1,INDICATOR_DATA); class=class="str">"cmt">// Line class="num">1 data buffer SetIndexBuffer(class="num">2,Label2Buffer2,INDICATOR_DATA); class=class="str">"cmt">// Line class="num">2 data buffer class=class="str">"cmt">//--- Plot buffer class="num">2. Four arrays for construction SetIndexBuffer(class="num">3,Label3Buffer1,INDICATOR_DATA); class=class="str">"cmt">// Data buffer for line class="num">1 Open SetIndexBuffer(class="num">4,Label3Buffer2,INDICATOR_DATA); class=class="str">"cmt">// Data buffer for line class="num">2 High SetIndexBuffer(class="num">5,Label3Buffer3,INDICATOR_DATA); class=class="str">"cmt">// Data buffer for line class="num">3 Low SetIndexBuffer(class="num">6,Label3Buffer4,INDICATOR_DATA); class=class="str">"cmt">// Data buffer for line class="num">4 Close class=class="str">"cmt">//--- Plot buffer class="num">3. Two arrays for construction SetIndexBuffer(class="num">7,Label4Buffer,INDICATOR_DATA); class=class="str">"cmt">// Data buffer SetIndexBuffer(class="num">8,Label4Colors,INDICATOR_COLOR_INDEX); class=class="str">"cmt">// Buffer of class="type">color indexes class=class="str">"cmt">//--- Plot buffer class="num">4. Three arrays for construction SetIndexBuffer(class="num">9,Label5Buffer1,INDICATOR_DATA); class=class="str">"cmt">// Data buffer for line class="num">1 SetIndexBuffer(class="num">10,Label5Buffer2,INDICATOR_DATA); class=class="str">"cmt">// Data buffer for line class="num">2 SetIndexBuffer(class="num">11,Label5Colors,INDICATOR_COLOR_INDEX);class=class="str">"cmt">// Buffer of class="type">color indexes class=class="str">"cmt">//--- Plot buffer class="num">5. Five arrays for construction SetIndexBuffer(class="num">12,Label6Buffer1,INDICATOR_DATA); class=class="str">"cmt">// Data buffer for line class="num">1 Open
「把多线缓冲挂进数据窗口的细节」
这段初始化收尾把第 13~16 号缓冲区绑给了指标绘图:13/14/15 分别是第二根线的 High、Low、Close 数据缓冲,16 号存颜色索引,类型用 INDICATOR_DATA 和 INDICATOR_COLOR_INDEX 区分开。 PlotIndexSetInteger 从 0 到 5 都把 PLOT_SHOW_DATA 设为 true,意味着前 6 条画线会在数据窗口里显示数值;而 hide_index 那一条设成 false,是被刻意藏起来的辅助线,不占数据窗口行。 OnCalculate 这里直接 return rates_total,没做具体计算逻辑,属于占位骨架——你抄这段时若忘了补计算循环,指标会出白线但无数值。外汇与贵金属波动剧烈,这类多缓冲指标在实盘只作参考,信号失效概率不低。 开 MT5 新建指标把这段塞进 OnInit 末尾,把 hide_index 改成 6 看辅助线是否如预期隐藏,就能验证缓冲绑定对不对。
SetIndexBuffer(class="num">13,Label6Buffer2,INDICATOR_DATA); class=class="str">"cmt">// Data buffer for line class="num">2 High SetIndexBuffer(class="num">14,Label6Buffer3,INDICATOR_DATA); class=class="str">"cmt">// Data buffer for line class="num">3 Low SetIndexBuffer(class="num">15,Label6Buffer4,INDICATOR_DATA); class=class="str">"cmt">// Data buffer for line class="num">4 Close SetIndexBuffer(class="num">16,Label6Colors,INDICATOR_COLOR_INDEX);class=class="str">"cmt">// Buffer of class="type">color indexes class=class="str">"cmt">//--- Hode the specified plot buffer in the data window PlotIndexSetInteger(class="num">0,PLOT_SHOW_DATA,true); PlotIndexSetInteger(class="num">1,PLOT_SHOW_DATA,true); PlotIndexSetInteger(class="num">2,PLOT_SHOW_DATA,true); PlotIndexSetInteger(class="num">3,PLOT_SHOW_DATA,true); PlotIndexSetInteger(class="num">4,PLOT_SHOW_DATA,true); PlotIndexSetInteger(class="num">5,PLOT_SHOW_DATA,true); PlotIndexSetInteger(hide_index,PLOT_SHOW_DATA,false); class="kw">return(INIT_SUCCEEDED); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Custom indicator iteration function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnCalculate(const class="type">int rates_total, const class="type">int prev_calculated, const class="type">class="kw">datetime &time[], const class="type">class="kw">double &open[], const class="type">class="kw">double &high[], const class="type">class="kw">double &low[], const class="type">class="kw">double &close[], const class="type">long &tick_volume[], const class="type">long &volume[], const class="type">int &spread[]) { class=class="str">"cmt">//--- class="kw">return value of prev_calculated for the next call class="kw">return(rates_total); } class=class="str">"cmt">//+------------------------------------------------------------------+
用缓冲区索引取代数组记忆负担
把多数组指标缓冲区的访问方式改成「绘图缓冲区索引 + 所需数组索引」二元定位后,外部调用方不再需要背下每个数组挂在第几个指标缓冲区。比如绘图缓冲区 0 要取第一份数据数组,传 (0,0);取第二份传 (0,1);颜色索引数组则只传绘图缓冲区索引 0,由类内部方法处理。 库文件 \MQL5\Include\IndMSTF\IndMSTF.mqh 里把缓冲区结构重做了:五个数组的初始化和Resize统一进私有段,绘制样式在创建时写进结构,并记下源指标数组索引。之前 Calculate() 里手写复制,现在改成调用一个复制全部数组的方法,复制失败会直接返回错误。 颜色缓冲区的复制与单色类似,只是多传一个绘图侧颜色索引数组,同时拷数据数组和颜色数组。由于颜色缓冲区永远排在数据缓冲区之后,计算侧颜色索引 = 数据缓冲区索引 + 1,这个关系写进类结构后,从计算部分取色变得可推算。 指标集合类也同步扩了方法:添加对象时立即设数字、数据、颜色缓冲区的初始空值,还可顺手设水平线数量和数值。以前只返回句柄或 INVALID_HANDLE,现在创建成功还会在图表上设小数位和水平。外汇与贵金属行情跳空频繁,这类多周期多品种库在实盘使用前,建议先在 MT5 策略测试器用历史数据验证缓冲区复制是否漏帧。
class=class="str">"cmt">//--- class="kw">struct class="kw">struct SBuffer class=class="str">"cmt">// Structure of the indicator buffer { class="kw">private: class="type">class="kw">double init_value; class=class="str">"cmt">// Initializing value class="type">uchar init_color_idx; class=class="str">"cmt">// Initializing value of the class="type">color index class="type">int shift; class=class="str">"cmt">// Horizontal shift of the buffer class="type">uint source; class=class="str">"cmt">// Buffer number of the source indicator class="type">bool colored; class=class="str">"cmt">// Flag of the class="type">color buffer ENUM_DRAW_TYPE draw_type; class=class="str">"cmt">// Buffer drawing style class=class="str">"cmt">//--- Changes the size of all arrays class="type">bool ResizeArrays(const class="type">int new_size) { class="type">bool res=true; class="kw">switch(draw_type) { class=class="str">"cmt">//--- One buffer case DRAW_LINE :