面向初学者的创建具有多个指标缓冲区的指标(基础篇)
「给指标开多个缓冲区并不难」
在 MT5 里写一个自定义指标,最容易被卡住的地方不是画图逻辑,而是搞不清『指标缓冲区(indicator buffer)』到底怎么分配。简单说,一个缓冲区就是一条独立的数据线,你往里写值,终端才认得要画什么。 很多新手只声明一个缓冲区,结果想同时显示快慢两条均线或一个主图线加一个色带就崩了。正确做法是:在 OnInit 里用 SetIndexBuffer 把数组和缓冲区编号绑定,编号从 0 开始连续排,漏一个号终端就可能报『数组越界』或画不出线。 以双缓冲区指标为例,核心声明长这样:
class="macro">#class="kw">property indicator_separate_window class="macro">#class="kw">property indicator_buffers class="num">2 class="macro">#class="kw">property indicator_plots class="num">2 class=class="str">"cmt">//--- plot Fast class="macro">#class="kw">property indicator_label1 "Fast" class="macro">#class="kw">property indicator_type1 DRAW_LINE class="macro">#class="kw">property indicator_color1 clrRed class=class="str">"cmt">//--- plot Slow class="macro">#class="kw">property indicator_label2 "Slow" class="macro">#class="kw">property indicator_type2 DRAW_LINE class="macro">#class="kw">property indicator_color2 clrBlue class=class="str">"cmt">//--- indicator buffers class="type">class="kw">double FastBuffer[]; class="type">class="kw">double SlowBuffer[];
indicator_buffers 2 告诉终端我要两块缓冲区,indicator_plots 2 表示要画两条线;FastBuffer 和 SlowBuffer 是两个 double 数组,后续在 OnInit 中分别绑定到 0 号和 1 号缓冲区。外汇与贵金属市场波动剧烈、杠杆高风险大,多缓冲区指标只是帮你把信号分层,不代表任何方向判断。
实际验证时,把上面属性块贴进 MQ5 文件头,再在 OnInit 补两句 SetIndexBuffer(0, FastBuffer); SetIndexBuffer(1, SlowBuffer);,编译后拖到 EURUSD 的 M15 图表,终端『导航器—指标』里就能看到红蓝双线占位,证明缓冲区已生效。
class="macro">#class="kw">property indicator_separate_window class="macro">#class="kw">property indicator_buffers class="num">2 class="macro">#class="kw">property indicator_plots class="num">2 class=class="str">"cmt">//--- plot Fast class="macro">#class="kw">property indicator_label1 "Fast" class="macro">#class="kw">property indicator_type1 DRAW_LINE class="macro">#class="kw">property indicator_color1 clrRed class=class="str">"cmt">//--- plot Slow class="macro">#class="kw">property indicator_label2 "Slow" class="macro">#class="kw">property indicator_type2 DRAW_LINE class="macro">#class="kw">property indicator_color2 clrBlue class=class="str">"cmt">//--- indicator buffers class="type">class="kw">double FastBuffer[]; class="type">class="kw">double SlowBuffer[];
从单缓冲区到更复杂的指标结构
之前两篇分别讲了只带一个指标缓冲区的自定义指标写法,那种结构在入门阶段够用,但真实盘面里价格行为很少能压缩成一条线。 MQL5 本身对缓冲区数量没有硬性上限,限制基本只来自你机器剩下的 RAM。也就是说,想叠加多层滤波或同时画多个状态,技术上完全可行,只是要注意内存占用随缓冲区数量线性增长。 对交易者来说,下一步值得在 MT5 里试着给指标挂两个以上的缓冲区,验证一下多线状态下刷新是否还跟手,而不是停留在单线思维。
◍ 把 Aroon 拆成双缓冲区的实操改法
Aroon 的核心公式分两头:BULLS = (1 - (bar - SHIFT(MAX(HIGH(), AroonPeriod)))/AroonPeriod) * 100,BEARS = (1 - (bar - SHIFT(MIN(LOW(), AroonPeriod)))/AroonPeriod) * 100。SHIFT() 取柱索引,MAX()/MIN() 在 AroonPeriod 周期内搜最高/最低价,输出范围是 0~100,所以必须丢进独立窗口而不是主图叠加。 从 SMA_1.mq5 改最省事:MetaEditor 里另存为 Aroon.mq5,前 11 行只换指标名;第 12 行加 #property indicator_separate_window 切到副图;紧接着把 indicator_buffers 和 indicator_plots 都改成 2,再把原绘图标签复制一份、索引 1 改 2、改名 BullsAroon / BearsAroon。 横线定位用三档:30、50、70,直接在代码里加 indicator_level1/2/3 即可,不需要自己画对象。 OnInit() 里第零缓冲改完,把整段剪贴再贴一次,缓冲区编号 0 改 1、数组名和 label 同步换掉,简称也微调。绘制精度上,值域固定 0~100,用 0 位小数就够了,避免副图上密密麻麻的小数。 OnCalculate() 得换第二种头,因为要拿 high[] 和 low[];原第一种形式没有这两数组。MQL5 没有内建“按递减索引搜极值位置”的函数,偷懒办法是从 MT5 自带的 ZigZag.mq5(MQL5\Indicators\Examples)里抠出极值定位函数,贴到全局层。 另一个思路是用 ArrayMaximum()/ArrayMinimum(),但它们按递增搜,得先用 ArraySetAsSeries() 把缓冲区和价格数组倒序,再把主循环方向反过来、改掉首变量算法。下面这段头文件改动就是副图+双缓冲的落地起点,复制进 MT5 编译就能看到灰绿/红双线跑起来。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Aroon.mq5 | class=class="str">"cmt">//| Copyright class="num">2010, MetaQuotes Software Corp. | class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//---- 版权 class="macro">#class="kw">property copyright "class="num">2010, MetaQuotes Software Corp." class=class="str">"cmt">//---- 作者网站的链接 class="macro">#class="kw">property link "[MQL5官方文档] class=class="str">"cmt">//----版本号 class="macro">#class="kw">property version "class="num">1.00" class=class="str">"cmt">//---- 在独立窗口中绘制指标 class="macro">#class="kw">property indicator_separate_window class=class="str">"cmt">//---- 使用 class="num">2 个缓存 class="macro">#class="kw">property indicator_buffers class="num">2 class=class="str">"cmt">//---- class="num">2 种图形用于绘制 class="macro">#class="kw">property indicator_plots class="num">2 class=class="str">"cmt">//+----------------------------------------------+ class=class="str">"cmt">//| 牛市力量指标参数 | class=class="str">"cmt">//+----------------------------------------------+ class=class="str">"cmt">//----绘制类型 = 线 class="macro">#class="kw">property indicator_type1 DRAW_LINE class=class="str">"cmt">//---- 绘制颜色 = 灰色 class="macro">#class="kw">property indicator_color1 Lime class=class="str">"cmt">//----线形 = 实线 class="macro">#class="kw">property indicator_style1 STYLE_SOLID class=class="str">"cmt">//---- 线宽 = class="num">1 class="macro">#class="kw">property indicator_width1 class="num">1 class=class="str">"cmt">//---- BullsAroon 指标的标签 class="macro">#class="kw">property indicator_label1 "BullsAroon" class=class="str">"cmt">//+----------------------------------------------+ class=class="str">"cmt">//| 熊市力量指标参数 | class=class="str">"cmt">//+----------------------------------------------+ class=class="str">"cmt">//----绘制类型 = 线 class="macro">#class="kw">property indicator_type2 DRAW_LINE class=class="str">"cmt">//---- 绘制颜色 = 红色 class="macro">#class="kw">property indicator_color2 Red class=class="str">"cmt">//----线形 = 实线 class="macro">#class="kw">property indicator_style2 STYLE_SOLID class=class="str">"cmt">//---- 线宽 = class="num">1 class="macro">#class="kw">property indicator_width2 class="num">1 class=class="str">"cmt">//----BearsAroon 指标的标签 class="macro">#class="kw">property indicator_label2 "BearsAroon" class=class="str">"cmt">//+----------------------------------------------+ class=class="str">"cmt">//| 横向水平 | class=class="str">"cmt">//+----------------------------------------------+ class="macro">#class="kw">property indicator_level1 class="num">70.0 class="macro">#class="kw">property indicator_level2 class="num">50.0 class="macro">#class="kw">property indicator_level3 class="num">30.0
「把 Aroon 双线塞进自定义指标缓存」
要在 MT5 里手搓一个 Aroon 双线指标,核心是先声明两个动态数组 BullsAroonBuffer 和 BearsAroonBuffer,再用 SetIndexBuffer 把它们分别绑到索引 0 和 1 上,否则图表只会画一根线甚至报错。 输入参数给了 AroonPeriod=9 和 AroonShift=0,意味着指标默认回看 9 根 K 线、不做水平偏移;PlotIndexSetInteger 里的 PLOT_DRAW_BEGIN 也设成 AroonPeriod,所以前 8 根柱形不会出值,这是避免样本不足导致除零或异常线的硬约束。 OnCalculate 开头那句 if(rates_total < AroonPeriod-1) return(0) 直接把总柱数小于 8 的情况挡掉,实盘加载到分钟图早期或历史过短的品种时会立刻生效。外汇与贵金属波动剧烈,这类周期类指标仅作趋势强度参考,信号失效概率不低,须配合仓位控制。 下面这段是初始化与计算骨架的原文,逐行拆解看注释即可照抄验证。
class="macro">#class="kw">property indicator_levelcolor Gray class="macro">#class="kw">property indicator_levelstyle STYLE_DASHDOTDOT class=class="str">"cmt">//+----------------------------------------------+ class=class="str">"cmt">//| 指标输入参数 | class=class="str">"cmt">//+----------------------------------------------+ input class="type">int AroonPeriod = class="num">9; class=class="str">"cmt">// 周期 input class="type">int AroonShift = class="num">0; class=class="str">"cmt">// 指标的水平柱形偏移 class=class="str">"cmt">//--- 声明用于指标缓存的动态数组 class="type">class="kw">double BullsAroonBuffer[]; class="type">class="kw">double BearsAroonBuffer[]; class=class="str">"cmt">//--- 设置动态数组 BullsAroonBuffer 作为指标缓存 SetIndexBuffer(class="num">0, BullsAroonBuffer, INDICATOR_DATA); class=class="str">"cmt">//--- 指标 class="num">1(AroonShift) 的水平偏移 PlotIndexSetInteger(class="num">0, PLOT_SHIFT, AroonShift); class=class="str">"cmt">//---绘制指标 class="num">1(AroonPeriod) 的开始 PlotIndexSetInteger(class="num">0, PLOT_DRAW_BEGIN, AroonPeriod); class=class="str">"cmt">//--- 数据窗口中显示的标签 PlotIndexSetString(class="num">0, PLOT_LABEL, "BearsAroon"); class=class="str">"cmt">//--- 设置动态数组 BullsAroonBuffer 作为指标缓存 SetIndexBuffer(class="num">1, BearsAroonBuffer, INDICATOR_DATA); class=class="str">"cmt">//--- 指标 class="num">2(AroonShift) 的水平偏移 PlotIndexSetInteger(class="num">1, PLOT_SHIFT, AroonShift); class=class="str">"cmt">//--- 绘制指标 class="num">2(AroonPeriod) 的开始 PlotIndexSetInteger(class="num">1, PLOT_DRAW_BEGIN, AroonPeriod); class=class="str">"cmt">//--- 数据窗口中显示的标签 PlotIndexSetString(class="num">1, PLOT_LABEL, "BullsAroon"); class=class="str">"cmt">//--- 初始化用于指标简称的变量 class="type">class="kw">string shortname; StringConcatenate(shortname, "Aroon(", AroonPeriod, ", ", AroonShift, ")"); class=class="str">"cmt">//---确定指标值的绘制精度 IndicatorSetInteger(INDICATOR_DIGITS, class="num">0); class="type">int OnCalculate( const class="type">int rates_total, class=class="str">"cmt">// 当前价格变动时的所有柱形 const class="type">int prev_calculated, class=class="str">"cmt">// 先前价格变动时的所有柱形 const class="type">class="kw">datetime& time[], const class="type">class="kw">double& open[], const class="type">class="kw">double& high[], class=class="str">"cmt">// 用于指标计算的最高价格数组 const class="type">class="kw">double& low[], class=class="str">"cmt">//用于指标计算的最低价格数组 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">//--- 检查柱形的数量 if (rates_total < AroonPeriod - class="num">1) class="kw">return(class="num">0); class=class="str">"cmt">//--- 声明本地变量 class="type">int first, bar; class="type">class="kw">double BULLS, BEARS; class=class="str">"cmt">//--- 主循环的开始位索引