MQL5:创建自己的指标(基础篇)
◍ 在 MT5 里从零搭一个自定义指标
想在 MT5 里做自己的指标,核心是先理解 indicator 程序的骨架:它不负责下单,只负责在图表上算并画数据。MetaTraotes 官方示例自 2013-08-22 发布以来,累计被查看 15 332 次、评论 43 条,说明这套基础模板仍是多数人上手的第一站。 一个最精简的自定义指标必须声明缓冲区与计算周期。下面这段是 MQL5 指标的最小可用结构,复制进 MetaEditor 新建的 indicator 文件即可编译。 外汇与贵金属市场杠杆高、跳空频繁,自定义指标只描述概率性价格结构,不构成方向保证,实盘前务必在策略测试器跑历史数据。
class="macro">#class="kw">property indicator_separate_window class="macro">#class="kw">property indicator_buffers class="num">1 class="macro">#class="kw">property indicator_plots class="num">1 class="macro">#class="kw">property indicator_label1 "MyBuffer" class="type">class="kw">double MyBuffer[]; class="type">int OnInit() { SetIndexBuffer(class="num">0, MyBuffer, INDICATOR_DATA); class="kw">return(INIT_SUCCEEDED); } class="type">int OnCalculate(class="kw">const class="type">int rates_total, class="kw">const class="type">int prev_calculated, class="kw">const class="type">class="kw">datetime &time[], class="kw">const class="type">class="kw">double &open[], class="kw">const class="type">class="kw">double &high[], class="kw">const class="type">class="kw">double &low[], class="kw">const class="type">class="kw">double &close[], class="kw">const class="type">long &tick_volume[], class="kw">const class="type">long &volume[], class="kw">const class="type">int &spread[]) { for(class="type">int i=class="num">0; i<rates_total; i++) MyBuffer[i]=close[i]; class="kw">return(rates_total); }
指标本质与自定义起点
在 MT5 里,指标不是神秘黑盒,而是一组用数组承载的计算结果。价格数据先进入价格数组,你的算法处理完再写进指标值数组,屏幕上的曲线只是这些数组的可视化。 现成经典指标覆盖不了所有策略逻辑,当你需要把独有算法变成可读的副图或主线,就得写自定义指标。本文这条线只拆最简单的自建路径,不碰复杂缓冲区优化。 外汇与贵金属杠杆高、滑点跳空频繁,自定义指标仅作概率参考,不代表信号必然生效,实盘前务必在策略测试器跑历史数据核对。
「指标长什么样由你定」
在 MT5 里,指标不一定是根线。它可以是带颜色的线条、填充区域,也可以是贴在图表上提示入场时机的特殊标签,甚至把这几类揉在一起用。 这种组合自由度高,意味着你能把动量、区域过滤和信号箭头压进同一个窗口,减少多图表来回切的干扰。 后面我们以 William Blau 的“真实强弱指数(TSI)”为例讲指标创建,它本身就是用双平滑动量构造的经典例子,适合拿来做自定义改造的底子。
◍ TSI 双重平滑怎么算
真实强弱指数(TSI)用双重指数平滑把动量噪声压下去,再归一化到 -100~100 区间,用来判趋势方向和超买超卖。它的核心不是单看动量,而是动量除以绝对动量的平滑比,所以比值越靠近 ±100,方向惯性越强。
| 公式写成 TSI(CLOSE,r,s)=100*EMA(EMA(mtm,r),s)/EMA(EMA( | mtm | ,r),s)。mtm 就是今收减昨收的差值数组, | mtm | 取绝对值,两层 EMA 分别用 r 和 s 两个周期去滚。 |
|---|
默认参数里 r=25、s=13,价格取收盘价。也就是说影响曲线的只有三个东西:长平滑周期 r、短平滑周期 s、以及用的是哪类价格(收盘只是其中一种)。把 r 调大,TSI 会更迟钝但假信号少;s 调小则反应快却容易在贵金属跳空时被甩。 开 MT5 把 r 从 25 改成 40 对比 EURUSD 日线,能直接看到信号滞后约 6~8 根 K 线,外汇高杠杆下这种延迟可能吃掉一段可观浮动盈亏。
用向导起一个 TSI 指标骨架
在 MT5 里做自定义指标,最快的路子不是手写全部属性,而是先让 MQL5 向导把头文件吐出来。选「自定义指标」类型,填程序名,把两个平滑周期 r 和 s 作为参数放进去——默认给 r=25、s=13,这是 TSI 常用的双指数平滑窗口,外汇和贵金属品种上都能直接加载观察。 向导里把显示方式定为独立窗口、蓝色实线,标签写 TSI,点 Done 后生成的草稿已经把 indicator_separate_window、indicator_buffers=1、indicator_plots=1 以及线条颜色样式写死在属性段。你拿到的不是一个能算值的成品,而是一个只映射了缓冲区的空壳,OnCalculate 里还没接任何价格逻辑。 下面这段就是向导产出的头文件和初始化骨架。注意 input 声明的 r、s 是外部可调参数,TSIBuffer 只做了 SetIndexBuffer 绑定,没填数——下一步才是把真实强度指数算进去。外汇贵金属波动大,这类指标仅作辅助,实际信号概率随周期切换明显变化,别当唯一依据。 别把向导产物当完工件 向导只省了属性声明的打字量,OnCalculate 空着就意味着图表上是一条 flat 零线。开 MT5 加载后先确认独立窗口出蓝线,再补计算代码,否则容易误以为指标「失效」。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| True Strength Index.mq5 | class=class="str">"cmt">//| Copyright class="num">2009, MetaQuotes Software Corp. | class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "class="num">2009, MetaQuotes Software Corp." class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class="macro">#class="kw">property indicator_separate_window class="macro">#class="kw">property indicator_buffers class="num">1 class="macro">#class="kw">property indicator_plots class="num">1 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">//--- 输入参数 class="kw">input class="type">int r=class="num">25; class="kw">input class="type">int s=class="num">13; class=class="str">"cmt">//--- 指标缓冲区 class="type">class="kw">double TSIBuffer[]; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 自定义指标初始化函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//--- 指标缓冲区映射关系 SetIndexBuffer(class="num">0,TSIBuffer,INDICATOR_DATA); class=class="str">"cmt">//--- class="kw">return(class="num">0); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 自定义指标迭代函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnCalculate(class="kw">const class="type">int rates_total, class="kw">const class="type">int prev_calculated, class="kw">const class="type">class="kw">datetime& time[], class="kw">const class="type">class="kw">double& open[], class="kw">const class="type">class="kw">double& high[], class="kw">const class="type">class="kw">double& low[], class="kw">const class="type">class="kw">double& close[], class="kw">const class="type">long& tick_volume[], class="kw">const class="type">long& volume[], class="kw">const class="type">int& spread[])
「返回值如何衔接下一次计算」
在自定义指标里,OnCalculate 末尾的 return 值不是摆设。它会被 MT5 在下一次行情刷新时,作为 prev_calculated 参数原样传回,用来判断哪些柱线已经算过、哪些需要增量重算。 上面这段代码直接 return(rates_total),含义是「到目前为止的柱线全部处理完毕」。若你的指标逻辑里没有做增量缓存,每次都从头算到尾,这么写不会出错,但会浪费 CPU;若你做了缓存优化,却返回了错误的数量,就会出现重复计算或漏算。 验证方法:在 OnCalculate 里 Print(prev_calculated) 对比 return 值,观察 MT5 终端里两次调用的衔接关系,外汇与贵金属品种跳空时这一现象更明显,相关交易存在高风险。
{
class=class="str">"cmt">//---
class=class="str">"cmt">//--- 返回值会作为下一次调用的 prev_calculated 参数调用
class="kw">return(rates_total);
}
class=class="str">"cmt">//+------------------------------------------------------------------+