如何在另一指标的基础上编写一个指标(基础篇)
📘

如何在另一指标的基础上编写一个指标(基础篇)

第 1/2 篇

◍ 在已有指标上叠加新指标的做法

在 MT5 里写指标,不一定从零算价格。更常见的需求是:拿一个现成指标的缓冲区结果,再加工出第二条线。这样能省掉重复计算,也方便做价差、比值或平滑。 核心机制是先用 IndicatorCreate() 把目标指标挂进来,拿到它的句柄;之后在 OnCalculate() 里用 CopyBuffer() 把对应缓冲区的数据取到本地数组,再做自己的逻辑。注意句柄创建失败要返回错误,否则后续全是空值。 一个容易踩的坑:被依赖的指标如果参数变了,主指标不会自动重算,需要在 OnChartEvent() 里监听并重置句柄。外汇与贵金属杠杆高、跳空频繁,这种依赖链断开会导致信号滞后,实盘前务必在 MT5 用历史数据回放验证。

在现有指标上做二次开发

MT5 里写指标不一定非要从零起步。除了完全新建自定义指标,你还能基于已有指标再加工,这能省掉大量重复的底層计算代码。 第一种路数是改开源自定义指标:在别人放出源码的指标上加新算法或新标绘,前提是你能拿到 .mq5 源码,闭源 ex5 动不了。 第二种更通用——直接吃终端内置指标,或用 iCustom() / IndicatorCreate() 去挂载一个现成的自定义指标,把它的缓冲区当输入来用。外汇与贵金属杠杆高,回测结论仅代表历史概率,实盘须自行验证。

「给 TSI 指标挂一条可调信号线」

在 MT5 里给现有指标加一条标绘,最稳的做法是从已成熟的 True_Strength_Index_ver3 改起。我们以新名 TSIs 存到 MQL5/Indicators 目录,目标是多画一条红色信号线,平滑周期默认 5、默认指数平滑。 原指标用了 8 个缓冲区、只绘 1 条线。先把 #property indicator_buffers 和 indicator_plots 都加 1,再补一段 2 号绘图的属性:标签 TSISignal、类型 DRAW_LINE、颜色 Red、实线、1 像素。外部参数加 int sp=5 与 ENUM_MA_METHOD sm=MODE_EMA,信号线形态就交给用户后期在界面上调。 数组声明保持逻辑顺序:TSIBuffer 之后直接放 TSISigBuffer,后者用 SetIndexBuffer(1, TSISigBuffer, INDICATOR_DATA) 绑定——索引 1 对应第二绘图,其余计算用缓冲区顺延重排索引。OnCalculate 里主线算完的 for 循环后,调用 MovingAverages.mqh 里的平滑函数:用 switch(sm) 分流 SimpleMAOnBuffer / ExponentialMAOnBuffer / LinearWeightedMAOnBuffer / SmoothedMAOnBuffer。begin 参数不能等于比 TSIBuffer 起始更早的元素,实测取 begin+r+s-1 可避掉左边缘的空白绘线。 编译后附图能看到蓝主线+红信号线;若图表左拉见到未计算区的怪异绘线,在 OnInit() 里 return(0) 前加 PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, begin+r+s-1) 即可切掉开头。外汇与贵金属波动剧烈,信号线仅作概率参考,实盘前请自行在策略测试器验证。

MQL5 / C++
class="macro">#class="kw">property indicator_buffers class="num">8
@class="kw">property indicator_plots class="num">2
class=class="str">"cmt">//---- TSI 绘图
class="macro">#class="kw">property indicator_label1 "TSI"
class="macro">#class="kw">property indicator_type1 DRAW_LINE
class="macro">#class="kw">property indicator_color1 Blue
class="macro">#class="kw">property indicator_style1 STYLE_SOLID
class="macro">#class="kw">property indicator_width1 class="num">1
class=class="str">"cmt">//---- TSISignal 绘图
class="macro">#class="kw">property indicator_label2 "TSISignal" 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="macro">#class="kw">property indicator_width2 class="num">1           class=class="str">"cmt">// 线粗
class="kw">input class="type">int r=class="num">25;
class="kw">input class="type">int s=class="num">13;
class="kw">input class="type">int sp=class="num">5;
class="kw">input ENUM_MA_METHOD sm=MODE_EMA;
class=class="str">"cmt">//--- 指标缓冲区
class="type">class="kw">double TSIBuffer[];
class="type">class="kw">double TSISigBuffer[]; class=class="str">"cmt">// 用于信号线新缓冲区的数组
class="type">class="kw">double MTMBuffer[];
class="type">class="kw">double AbsMTMBuffer[];
class="type">class="kw">double EMA_MTMBuffer[];

◍ 缓冲区绑定与信号平滑的落地写法

TSI 指标在 MT5 里要把输出和中间量分开挂缓冲区:0 号和 1 号是 INDICTOR_DATA,直接画线和信号;2 到 7 号标成 INDICATOR_CALCULATIONS,只参与动量绝对值的双层平滑,不进图表。 上面这段代码里 0 和 1 被 SetIndexBuffer 重复写了两遍,属冗余,实盘编译虽不报错但建议删掉重复行,减少维护时误改。 平滑方式由 sm 变量切换,EMA、LWMA、SMA、SMMA 四种都走 OnBuffer 系列函数,起点统一用 begin+r+s-1 算出来。把 sp 调小,信号线会对 TSI 主线跟得更紧,杂讯也更多;外汇和贵金属波动大,这种跟线参数容易在震荡里连续触发,属高风险环境,仓位要自己控。 PlotIndexSetInteger(1,PLOT_DRAW_BEGIN,r+s+sp) 决定了信号线前面空多少根不画,改了前面几个周期参数就得同步改这里,否则图表左端会缺一段或错位。

MQL5 / C++
class="type">class="kw">double EMA2_MTMBuffer[];
class="type">class="kw">double EMA_AbsMTMBuffer[];
class="type">class="kw">double EMA2_AbsMTMBuffer[];
SetIndexBuffer(class="num">0,TSIBuffer,INDICATOR_DATA);
SetIndexBuffer(class="num">1,TSISigBuffer,INDICATOR_DATA);
SetIndexBuffer(class="num">0,TSIBuffer,INDICATOR_DATA);
SetIndexBuffer(class="num">1,TSISigBuffer,INDICATOR_DATA);
SetIndexBuffer(class="num">2,MTMBuffer,INDICATOR_CALCULATIONS);
SetIndexBuffer(class="num">3,AbsMTMBuffer,INDICATOR_CALCULATIONS);
SetIndexBuffer(class="num">4,EMA_MTMBuffer,INDICATOR_CALCULATIONS);
SetIndexBuffer(class="num">5,EMA2_MTMBuffer,INDICATOR_CALCULATIONS);
SetIndexBuffer(class="num">6,EMA_AbsMTMBuffer,INDICATOR_CALCULATIONS);
SetIndexBuffer(class="num">7,EMA2_AbsMTMBuffer,INDICATOR_CALCULATIONS);
class="macro">#include <MovingAverages.mqh>
const class="type">int rates_total, const class="type">int prev_calculated, const class="type">int begin, const class="type">int period, const class="type">class="kw">double& price[],class="type">class="kw">double& buffer[]
start=begin+r+s-class="num">1;
class="type">int begin2=begin+r+s-class="num">1;
class="kw">switch(sm)
  {
   case MODE_EMA:
      ExponentialMAOnBuffer(rates_total,prev_calculated,begin2,sp,TSIBuffer,TSISigBuffer);
      break;
   case MODE_LWMA:
      LinearWeightedMAOnBuffer(rates_total,prev_calculated,begin2,sp,TSIBuffer,TSISigBuffer);
      break;
   case MODE_SMA:
      SimpleMAOnBuffer(rates_total,prev_calculated,begin2,sp,TSIBuffer,TSISigBuffer);
      break;
   case MODE_SMMA:
      SmoothedMAOnBuffer(rates_total,prev_calculated,begin2,sp,TSIBuffer,TSISigBuffer);
      break;
  }
PlotIndexSetInteger(class="num">1,PLOT_DRAW_BEGIN,r+s+sp);

常见问题

先在代码里多开一个缓冲区绑定到信号线,再用 iMA 或简单平滑函数对 TSI 主线做平滑后写入该缓冲区,参数留外部输入方便调。
保留原指标 OnCalculate 的主计算不动,只新增缓冲区和独立的信号计算段,用外部参数控制是否显示新线。
可以,小布能读取你当前品种的已有指标状态,按你设定的交叉或阈值条件推送提醒,不用自己写完整 EA。
把信号线源数据先做周期更长的移动平均,或调大平滑系数,回看不同品种确认抖动是否降低。
常见是缓冲区没声明全或每根 K 线重复调用重计算,检查数组大小和 OnCalculate 的起始位参数即可。