如何在另一指标的基础上编写一个指标(基础篇)
◍ 在已有指标上叠加新指标的做法
在 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) 即可切掉开头。外汇与贵金属波动剧烈,信号线仅作概率参考,实盘前请自行在策略测试器验证。
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) 决定了信号线前面空多少根不画,改了前面几个周期参数就得同步改这里,否则图表左端会缺一段或错位。
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);