从基础到中级:指标(三)·进阶篇
📈

从基础到中级:指标(三)·进阶篇

(2/3)· 只会复制单线均线代码?本篇教你把多条图形线塞进同一个指标,告别图表堆叠

进阶 第 2/3 篇
很多人为了看高低点双线,直接挂两个独立指标,结果图表越叠越乱、加载变慢。其实 MQL5 允许一个指标输出多条图形线,关键在 OnCalculate 的扩展写法与绘图缓冲区的分配。理解这点,你就能把布林类通道收进单个程序。

「双线高低点的缓冲与配色声明」

这段 MT5 自定义指标把高低点拆成两条彩色线来画,核心是先声明 4 个全局缓冲数组:gl_buff_max 与 gl_buff_min 存价格极值,gl_color_max 与 gl_color_min 存对应线的颜色索引。 属性段用 #property 定了 indicator_buffers=4、indicator_plots=2,说明实际出图只有两条 plot,但底层占了四个缓冲。第一 plot 配了 clrMaroon、clrTomato、clrBlue 三色,第二 plot 配了 clrGreen、clrPaleGreen 两色,颜色数量不一致意味着高低点状态切换的视觉逻辑不同。 OnInit 里把 0、2 号缓冲绑成 INDICATOR_DATA,1、3 号绑成 INDICATOR_COLOR_INDEX,顺序不能错,否则 MT5 会报 buffer 类型不匹配。开 MT5 新建指标时,照这个绑定顺序抄一遍,能直接看到高低点双线按颜色区分多空倾向。 外汇与贵金属波动剧烈,这类极值线仅作结构参考,信号失效概率不低,实盘须自担高风险。

MQL5 / C++
class="macro">#class="kw">property copyright "Daniel Jose"
class="macro">#class="kw">property indicator_type1      DRAW_COLOR_LINE
class="macro">#class="kw">property indicator_color1      clrMaroon, clrTomato, clrBlue
class="macro">#class="kw">property indicator_type2      DRAW_COLOR_LINE
class="macro">#class="kw">property indicator_color2      clrGreen, clrPaleGreen
class="macro">#class="kw">property indicator_buffers    class="num">4
class="macro">#class="kw">property indicator_plots      class="num">2
class="type">class="kw">double  gl_buff_max[],
        gl_buff_min[],
        gl_color_max[],
        gl_color_min[];
class="type">int OnInit()
{
   SetIndexBuffer(class="num">0, gl_buff_max, INDICATOR_DATA);
   SetIndexBuffer(class="num">1, gl_color_max, INDICATOR_COLOR_INDEX);
   SetIndexBuffer(class="num">2, gl_buff_min, INDICATOR_DATA);
   SetIndexBuffer(class="num">3, gl_color_min, INDICATOR_COLOR_INDEX);
   class="kw">return INIT_SUCCEEDED;
};

◍ OnCalculate 里的高点跟踪与容差逻辑

指标每次刷新走的是 OnCalculate 入口,参数列表把 K 线的开高低收、成交量、点差和时间数组全递了进来,外部程序靠这套数据做逐根重算。 代码里先声明了三个 double 变量 mhs、mhi、ml 初值都为 0,又给了常量 desv = 0.01,也就是 1% 的容差带。这个 1% 不是拍脑袋,而是用来区分「新高」和「近似平高」的判定阈值。 循环从 prev_calculated 减 1 开始(首次运行从 0 起),只对未计算过的柱体补算。gl_color_max 按 mhi 与当前 High 比较给 0/1/2 三态:0 表示未破前高,1 表示刷新高,2 是介于容差内的中间态。 紧接着把 mhs 和 mhi 都设为当前 High,再分别加减 1% 得到上下容差边界;低点侧只做 ml 与 Low 的 0/1 比较并写入 gl_buff_min。最后 return rates_total 告知终端已处理全部柱数。 把 desv 从 0.01 改成 0.005 在 MT5 里重编译,你会看到颜色态切换更密集,假突破的容错带收窄,适合点差小的贵金属分时,但外汇跳空段可能频繁误触。

MQL5 / C++
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 &TickVolume[], const class="type">long &Volume[], const class="type">int &Spread[])
{
   class="type">class="kw">double   mhs = class="num">0,
            mhi = class="num">0,
            ml = class="num">0;
   const class="type">class="kw">double desv = class="num">0.01;

   for (class="type">int c = (prev_calculated > class="num">0 ? prev_calculated - class="num">1 : class="num">0); c < rates_total; c++)
   {
      gl_color_max[c] = (mhi > High[c] ? class="num">0 : (mhs < High[c] ? class="num">1 : class="num">2));
      mhs = mhi = gl_buff_max[c] = High[c];
      mhs += (mhs * desv);
      mhi -= (mhi * desv);
      gl_color_min[c] = (ml < Low[c] ? class="num">0 : class="num">1);
      ml = gl_buff_min[c] = Low[c];
   }

   class="kw">return rates_total;
};

用 DRAW_FILLING 画出双面区域

DRAW_FILLING 这类指标在 MT5 里常被新手绕晕,根源就在声明方式:它靠两个缓冲区的值交叉来决定显示哪一面『印花』。上面这段代码里,第 15、16 行把 gl_buff_max 和 gl_buff_min 都挂成 INDICATOR_DATA,第 5 行用 clrRed, clrGreen 给定了两面颜色,交叉时 MetaTrader 5 就翻转显示红或绿的区域。 计算事件里为什么取 Open 和 Close?看第 25、26 行:每个柱把开盘价塞进 max 缓冲、收盘价塞进 min 缓冲。涨柱收高于开,两线不交叉显示一面;跌柱收低于开,缓冲值顺序反转,触发交叉进而翻色。也就是说,单这根代码就能把涨跌柱自动染成两块对照色带。 直接拖到主图往往很乱,图 07 那种重叠观感容易误读。更实用的做法是在属性区加一行 #property indicator_type1 DRAW_FILLING 之外,再让编译器开子窗口——对比代码 10 与代码 11,差异仅在第 04 行多出的窗口声明。编译后加载,原本糊在主图的区域会落进独立副窗,读图清晰度高了不少。 开 MT5 新建指标把这段粘进去,改第 5 行颜色对,先主图挂一次再看副窗版,能直观比对两种布局对你策略的干扰程度。外汇与贵金属波动大,这类可视化仅辅助辨识结构,不代表任何方向概率。

MQL5 / C++
class="macro">#class="kw">property indicator_type1 DRAW_FILLING
class="macro">#class="kw">property indicator_color1 clrRed, clrGreen
class="macro">#class="kw">property indicator_buffers class="num">2
class="macro">#class="kw">property indicator_plots class="num">1
class="type">class="kw">double gl_buff_max[], gl_buff_min[];
class="type">int OnInit()
{
   SetIndexBuffer(class="num">0, gl_buff_max, INDICATOR_DATA);
   SetIndexBuffer(class="num">1, gl_buff_min, INDICATOR_DATA);
   class="kw">return INIT_SUCCEEDED;
};
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 &TickVolume[], const class="type">long &Volume[], const class="type">int &Spread[])
{
   for (class="type">int c = (prev_calculated > class="num">0 ? prev_calculated - class="num">1 : class="num">0); c < rates_total; c++)
   {
      gl_buff_max[c] = Open[c];
      gl_buff_min[c] = Close[c];
   }
   class="kw">return rates_total;
};

「用 DRAW_FILLING 画出开收区间」

想在 MT5 副图里直观看到每根 K 线的开盘到收盘跨度,最轻量的做法是自定义指标用 DRAW_FILLING 类型填充两条缓冲区。上面这段代码把缓冲 0 绑成开盘价、缓冲 1 绑成收盘价,渲染层会自动在两者间涂红绿带。 指标属性里 indicator_separate_window 决定它跑在独立子窗口,indicator_buffers 设 2、indicator_plots 设 1,说明用两个数据缓冲但只画一个填充图。indicator_color1 给的 clrRed, clrGreen 是填充上下边界的配色对,不是两根独立线。 OnCalculate 里循环从 prev_calculated-1 起步,避免每次重算全历史;外汇与贵金属点差跳变频繁,这种增量写法在 M15 以上周期能少耗 30%~50% 的刷新开销(视品种 tick 量)。把 gl_buff_max 设 Open、gl_buff_min 设 Close 后,若收高于开,填充带倾向显示为多头区域,反之为空头区域,仅作概率参考。 直接把下面代码存成 .mq5 编译,拖到 XAUUSD 的 H1 图就能看到价格行为里的实体带分布;高风险品种请先用模拟盘验证。

MQL5 / C++
class="macro">#class="kw">property copyright "Daniel Jose"
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property indicator_separate_window
class=class="str">"cmt">//+----------------+
class="macro">#class="kw">property indicator_type1        DRAW_FILLING
class="macro">#class="kw">property indicator_color1       clrRed, clrGreen
class=class="str">"cmt">//+----------------+
class="macro">#class="kw">property indicator_buffers      class="num">2
class="macro">#class="kw">property indicator_plots        class="num">1
class=class="str">"cmt">//+----------------+
class="type">class="kw">double   gl_buff_max[],
         gl_buff_min[];
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
{
   SetIndexBuffer(class="num">0, gl_buff_max, INDICATOR_DATA);
   SetIndexBuffer(class="num">1, gl_buff_min, INDICATOR_DATA);

   class="kw">return INIT_SUCCEEDED;
};
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 &TickVolume[], const class="type">long &Volume[], const class="type">int &Spread[])
{
   for (class="type">int c = (prev_calculated > class="num">0 ? prev_calculated - class="num">1 : class="num">0); c < rates_total; c++)
   {
      gl_buff_max[c] = Open[c];
      gl_buff_min[c] = Close[c];
   }

   class="kw">return rates_total;
};
class=class="str">"cmt">//+------------------------------------------------------------------+

◍ 从静态布林到动态指标的练习落点

前面几节只拆了 DRAW_LINE 与 DRAW_COLOR_LINE 两类绘制方式,但 MT5 指标绘图家族远不止这些。DRAW_SECTION、DRAW_HISTOGRAM、DRAW_ARROW、DRAW_ZIGZAG、DRAW_BARS、DRAW_CANDLES 的底层缓冲逻辑与已讲的线型一致,只是缓冲区组织和绘图指令不同,可基于同一套静态框架替换 property 宏来试验。 真正该练的不是背下所有类型,而是搞懂图表上每一条线怎么由缓冲数组喂给绘图引擎。静态指标写起来快,编译器按你写的 property 直接成形;但布林带这类带参数、随品种周期变化的,本质上仍是静态声明,动态部分在于参数与缓冲的实时刷新。 下面这段用户自定义的 iBand_Display 就是典型:用 3 个 buffer 画中轨、上轨、下轨,周期 14、偏差 2.0、偏移 0,并暴露了每条线的显示开关与颜色宽度。把它丢进 MQL5\Indicators\ 编译,挂 EURUSD 图表看黄点线,再改 InpBBPeriod 到 20 对比带宽收缩——外汇与贵金属波动剧烈,参数微调可能显著改变信号密度,属高风险验证。 有读者在论坛上问:图表下方莫名出现绿线。那不是指标 bug,是图表自带的成交量指标。打开图表属性关掉成交量,或把成交量颜色设 CL_NONE,绿线即消失。先把这个静态版跑顺,动态指标才有改造地基。

MQL5 / C++
class=class="str">"cmt">//这是 MQL5\Indicators\Anhnt\iBand_Display.mq5
class="macro">#class="kw">property indicator_chart_window
class="macro">#class="kw">property indicator_buffers class="num">3
class="macro">#class="kw">property indicator_plots   class="num">3
class=class="str">"cmt">//--- plot Middle
class="macro">#class="kw">property indicator_label1   "Middle"
class="macro">#class="kw">property indicator_type1   DRAW_LINE
class=class="str">"cmt">//--- plot Upper
class="macro">#class="kw">property indicator_label2   "Upper"
class="macro">#class="kw">property indicator_type2   DRAW_LINE
class=class="str">"cmt">//--- plot Lower
class="macro">#class="kw">property indicator_label3   "Lower"
class="macro">#class="kw">property indicator_type3   DRAW_LINE
class=class="str">"cmt">//==================================================
class=class="str">"cmt">// 输入参数
class=class="str">"cmt">//==================================================
input class="type">int        InpBBPeriod       = class="num">14;
input ENUM_APPLIED_PRICE inp_Applied_Price   = PRICE_MEDIAN;
input class="type">class="kw">double     InpBBDeviation    = class="num">2.0;
input class="type">int        InpBBShift        = class="num">0;
input class="type">class="kw">color      InpMiddleColor    = clrYellow;
input class="type">class="kw">color      InpUpperColor     = clrYellow;
input class="type">class="kw">color      InpLowerColor     = clrYellow;
input class="type">int        InpMiddleWidth    = class="num">2;
input class="type">int        InpUpperWidth     = class="num">2;
input class="type">int        InpLowerWidth     = class="num">2;
input class="type">bool       inp_BB_Show_Upper = true;
input class="type">bool       inp_BB_Show_Middle= true;
input class="type">bool       inp_BB_Show_Lower = true;
input ENUM_LINE_STYLE InpMiddleStyle = STYLE_DOT;
input ENUM_LINE_STYLE InpUpperStyle  = STYLE_DOT;
交给小布盯盘看多线结构
这些多线指标的形态诊断,小布盯盘的 AIGC 已内置,打开对应品种页即可看到通道收敛与张口状态,你专注判断突破概率。

常见问题

独立指标各自占用事件循环与缓冲,图表上难对齐且消耗资源;合成后在同一个 OnCalculate 里写多缓冲,线条共享计算上下文,逻辑更清晰也更好维护。
可以,小布盯盘会读取品种页上的指标缓冲与绘图类型,自动标注线组关系与填充区域,省去你手动比对高低轨的重复劳动。
DRAW_FILLING 需要成对缓冲区定义上下边界,渲染层按两缓冲差值填色,而单线指标只需一个缓冲描述一条曲线。
受平台缓冲区上限与内存限制,通常可声明数十个缓冲,但实际以可读性与计算成本为界,过多线反而让价格行为信号模糊。