初学者以 MQL5 实现对数字滤波器的实际实施·进阶篇
🔍

初学者以 MQL5 实现对数字滤波器的实际实施·进阶篇

(2/3)· 编译零错误不代表逻辑无误,弄懂 MetaEditor 报错机制才能少走弯路

含代码示例偏理论 第 2/3 篇
很多人以为指标能编译通过就万事大吉,其实 MetaEditor 只抓语法错,逻辑漏洞它一声不吭。故意在代码里制造几个破坏点再编译,比死记手册更快摸清报错规律。

「拿现成SMA模板改出FATL数字滤波器」

想在 MT5 里快速写一个新指标,没必要从空白文件啃语法。拿之前写好的 SMA 指标代码当模板,改算法和变量名就能省掉大量重复劳动。比如 FATL 这种数字滤波器,公式本质是加权求和:滤波器 = SUM(K(i) * CLOSE(i), FilterPeriod),和 SMA = SUM((1/MAPeriod)*CLOSE(i), MAPeriod) 结构几乎一样,只是权重 K(i) 和周期是指定的。 MQL4 里 FATL 用 Close[bar+偏移] 正向取价,但 MQL5 的指标缓冲区计算是反向的,且没有 Close[] 时序数组。所以在 MetaEditor 里用「全部替换」把 Close[bar+表达式] 换成 price[bar-表达式],方向就对了。 实操路径:打开 SMA_1_en.mq5 另存为 FATL_en.mq5,删掉循环里除缓冲区初始化外的代码,把 FATL 公式块贴进去;再把文字 SMA 替换成 FATL,MAPeriod/MAShift 改成 FATLPeriod/FATLShft。FATLPeriod 固定为 39,直接从外部变量和 StringConcatenate() 里删掉,局部变量 iii 也可删,线色改成蓝色加粗即可编译测试。 FATL 指标的成品代码就是后续其他滤波器的母板。以后再写类似指标,只需替换计算公式、把 FATL 字样改成 DIGFILTER、给 DIGFILTERPeriod 赋对应维度。外汇与贵金属市场波动剧烈, Indicators 仅作参考,实盘前务必在策略测试器跑历史数据验证。

MQL5 / C++
  FATL =  class="num">0.4360409450 * Close[bar + class="num">0]
         + class="num">0.3658689069 * Close[bar + class="num">1]
         + class="num">0.2460452079 * Close[bar + class="num">2]
         + class="num">0.1104506886 * Close[bar + class="num">3]
         - class="num">0.0054034585 * Close[bar + class="num">4]
         - class="num">0.0760367731 * Close[bar + class="num">5]
         - class="num">0.0933058722 * Close[bar + class="num">6]
         - class="num">0.0670110374 * Close[bar + class="num">7]
         - class="num">0.0190795053 * Close[bar + class="num">8]
         + class="num">0.0259609206 * Close[bar + class="num">9]
         + class="num">0.0502044896 * Close[bar + class="num">10]
         + class="num">0.0477818607 * Close[bar + class="num">11]
         + class="num">0.0249252327 * Close[bar + class="num">12]
         - class="num">0.0047706151 * Close[bar + class="num">13]
         - class="num">0.0272432537 * Close[bar + class="num">14]
         - class="num">0.0338917071 * Close[bar + class="num">15]
         - class="num">0.0244141482 * Close[bar + class="num">16]
         - class="num">0.0055774838 * Close[bar + class="num">17]
         + class="num">0.0128149838 * Close[bar + class="num">18]
         + class="num">0.0226522218 * Close[bar + class="num">19]
         + class="num">0.0208778257 * Close[bar + class="num">20]
         + class="num">0.0100299086 * Close[bar + class="num">21]
         - class="num">0.0036771622 * Close[bar + class="num">22]
         - class="num">0.0136744850 * Close[bar + class="num">23]
         - class="num">0.0160483392 * Close[bar + class="num">24]
         - class="num">0.0108597376 * Close[bar + class="num">25]
         - class="num">0.0016060704 * Close[bar + class="num">26]
         + class="num">0.0069480557 * Close[bar + class="num">27]
         + class="num">0.0110573605 * Close[bar + class="num">28]

FATL 滤波器的加权系数展开

这段算式把 FATL 快趋势线拆解成 30 根 K 线的线性加权。注意 bar-0 到 bar-3 的系数均为正且递减(0.4360、0.3659、0.2460、0.1105),说明近期收盘价对当前 FATL 值主导权最高。 从 bar-4 开始系数转负并探至 bar-7 的 -0.0933 谷底,随后在正负之间震荡收敛,到 bar-29 只剩 0.0096 的微正权重。这种远端弱、近端强的结构,让 FATL 对最新价格反应灵敏,又滤掉了 30 周期外的噪声。 原文里还有一段以 Close[bar+29] 到 Close[bar+38] 为正向加权(最高 +0.0130)的辅助项,它用的是未来偏移收盘价做平滑参考,实盘不能直接照抄,仅用于理解滤波器设计思路。外汇与贵金属杠杆高,此类加权均线仅作概率参考,不代表方向必然成立。

MQL5 / C++
  FATL =  class="num">0.4360409450 * price[bar - class="num">0]
      + class="num">0.3658689069 * price[bar - class="num">1]
      + class="num">0.2460452079 * price[bar - class="num">2]
      + class="num">0.1104506886 * price[bar - class="num">3]
      - class="num">0.0054034585 * price[bar - class="num">4]
      - class="num">0.0760367731 * price[bar - class="num">5]
      - class="num">0.0933058722 * price[bar - class="num">6]
      - class="num">0.0670110374 * price[bar - class="num">7]
      - class="num">0.0190795053 * price[bar - class="num">8]
      + class="num">0.0259609206 * price[bar - class="num">9]
      + class="num">0.0502044896 * price[bar - class="num">10]
      + class="num">0.0477818607 * price[bar - class="num">11]
      + class="num">0.0249252327 * price[bar - class="num">12]
      - class="num">0.0047706151 * price[bar - class="num">13]
      - class="num">0.0272432537 * price[bar - class="num">14]
      - class="num">0.0338917071 * price[bar - class="num">15]
      - class="num">0.0244141482 * price[bar - class="num">16]
      - class="num">0.0055774838 * price[bar - class="num">17]
      + class="num">0.0128149838 * price[bar - class="num">18]
      + class="num">0.0226522218 * price[bar - class="num">19]
      + class="num">0.0208778257 * price[bar - class="num">20]
      + class="num">0.0100299086 * price[bar - class="num">21]
      - class="num">0.0036771622 * price[bar - class="num">22]
      - class="num">0.0136744850 * price[bar - class="num">23]
      - class="num">0.0160483392 * price[bar - class="num">24]
      - class="num">0.0108597376 * price[bar - class="num">25]
      - class="num">0.0016060704 * price[bar - class="num">26]
      + class="num">0.0069480557 * price[bar - class="num">27]
      + class="num">0.0110573605 * price[bar - class="num">28]
      + class="num">0.0095711419 * price[bar - class="num">29]

◍ FATL指标后半段系数与主循环落地

上面这段加权式接着前半部分继续累加历史价格:bar-30 取正系数 0.0040444064,bar-31 到 bar-34 连续四个负系数(最小 -0.0072003400 在 bar-33),bar-35、36 微弱转正,bar-37 突然拉到 +0.0130129076,最后 bar-38 收在 +0.0040364019。这种远端跳变权重说明滤波器对 37 根前的价格仍保留较强记忆,回测 EURUSD H1 时容易在趋势反转第 3~4 根就钝化。 主循环极简:从 first 跑到 rates_total,每根直接把已算好的 FATL 值写进 ExtLineBuffer[bar],不另做平滑。也就是说指标线本身就是 FATL 序列的裸绘制,想改显示滞后只要动 OnInit 里的 PlotIndexSetInteger(0,PLOT_SHIFT,FATLShift)。 初始化段把缓存绑成 INDICATOR_DATA,画线和宽度写死 Blue / 2px,FATLPeriod=39 同时用作 DRAW_BEGIN,前 38 根不画——开 MT5 把周期改成 21 看前段空白是否缩短,就能验证这个耦合。

MQL5 / C++
   + class="num">0.0040444064 * price[bar - class="num">30]
   - class="num">0.0023824623 * price[bar - class="num">31]
   - class="num">0.0067093714 * price[bar - class="num">32]
   - class="num">0.0072003400 * price[bar - class="num">33]
   - class="num">0.0047717710 * price[bar - class="num">34]
   + class="num">0.0005541115 * price[bar - class="num">35]
   + class="num">0.0007860160 * price[bar - class="num">36]
   + class="num">0.0130129076 * price[bar - class="num">37]
   + class="num">0.0040364019 * price[bar - class="num">38];
class=class="str">"cmt">//---- 指标计算的主循环
  for(bar=first; bar<rates_total; bar++)
   {

     class=class="str">"cmt">//---- 用FATL初始化指标缓存单元
     ExtLineBuffer[bar]=FATL;

}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                 Fatl_en.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_chart_window
class=class="str">"cmt">//---- 一个缓存用于计算和绘制指标
class="macro">#class="kw">property indicator_buffers class="num">1
class=class="str">"cmt">//---- 仅使用一种图形来绘制
class="macro">#class="kw">property indicator_plots   class="num">1
class=class="str">"cmt">//---- 画指标线
class="macro">#class="kw">property indicator_type1   DRAW_LINE
class=class="str">"cmt">//---- 指标线的颜色为蓝色
class="macro">#class="kw">property indicator_color1  Blue
class=class="str">"cmt">//---- 指标线是连续的曲线
class="macro">#class="kw">property indicator_style1  STYLE_SOLID
class=class="str">"cmt">//---- 指标线的宽度为2个像素
class="macro">#class="kw">property indicator_width1  class="num">2
class=class="str">"cmt">//---- 显示指标的标签
class="macro">#class="kw">property indicator_label1  "FATL"
class=class="str">"cmt">//---- 指标的输入参数
class="kw">input class="type">int FATLShift=class="num">0; class=class="str">"cmt">// FATL horizontal shift in bars
class=class="str">"cmt">//---- 声明并初始化一个变量来存储已计算的柱形的数量
class="type">int FATLPeriod=class="num">39;
class=class="str">"cmt">//---- 声明动态数组, 
class=class="str">"cmt">//     将用作指标的缓存
class="type">class="kw">double ExtLineBuffer[];
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 自定义指标初始化函数                                       |
class=class="str">"cmt">//+------------------------------------------------------------------+  
class="type">void OnInit()
 {
class=class="str">"cmt">//----+
class=class="str">"cmt">//---- 将ExtLineBuffer动态数组变换为指标缓存
  SetIndexBuffer(class="num">0,ExtLineBuffer,INDICATOR_DATA);
class=class="str">"cmt">//---- FILTERShift,指标的水平偏移
  PlotIndexSetInteger(class="num">0,PLOT_SHIFT,FATLShift);
class=class="str">"cmt">//----设置指标的开始绘制位置
  PlotIndexSetInteger(class="num">0,PLOT_DRAW_BEGIN,FATLPeriod);
class=class="str">"cmt">//---- 初始化指标简短名称的变量

「FATL指标的名称绑定与加权计算核心」

在 MT5 自定义指标里,先把数据窗口标签和独立窗口短名绑定到动态字符串上,能让多周期加载时不撞名。下面这段代码把『FATL(偏移值)』拼进 shortname,再分别用 PlotIndexSetString 和 IndicatorSetString 写到图表与 tooltip,显示精度取 _Digits+1,空值禁显为 0.0。 代码里的 OnCalculate 是逐根重算入口。若 rates_total 小于 FATLPeriod-1+begin 直接 return(0),说明历史柱不够;首根计算位 first 在 prev_calculated==0 时定为 FATLPeriod-1+begin,若 begin>0 还要用 PlotIndexSetInteger 把绘图起点往后推 begin+FATLPeriod 根。 主循环从 first 扫到 rates_total,FATL 值是 price 数组近 11 根(bar-0 到 bar-10)的固定系数加权:最近三根系数 0.436/0.365/0.246 为正且递减,bar-4 到 bar-7 转负,bar-9 与 bar-10 又回正 0.0259/0.0502。这组权重是 FATL 平滑器的硬参数,改一根都可能让曲线漂移,建议直接抄原系数到 MT5 验证。 外汇与贵金属杠杆高,这类平滑指标只描述已发生价格结构,对后续拐点仅具概率性参考,实盘须自担风险。

MQL5 / C++
  class="type">class="kw">string shortname;
  StringConcatenate(shortname,"FATL(",FATLShift,")");
class=class="str">"cmt">//--- 创建数据窗口显示的标签
  PlotIndexSetString(class="num">0,PLOT_LABEL,shortname);
class=class="str">"cmt">//---创建在独立窗口和工具提示中显示的名称
  IndicatorSetString(INDICATOR_SHORTNAME,shortname);
class=class="str">"cmt">//--- 确定指标值的显示精度
  IndicatorSetInteger(INDICATOR_DIGITS,_Digits+class="num">1);
class=class="str">"cmt">//--- 禁止显示空值
  PlotIndexSetDouble(class="num">0,PLOT_EMPTY_VALUE,class="num">0.0);
}

class="type">int OnCalculate(
                class="kw">const class="type">int rates_total,      class=class="str">"cmt">// 当前报价下的历史柱形数据
                class="kw">const class="type">int prev_calculated,  class=class="str">"cmt">// 先前报价下的历史柱形数据
                class="kw">const class="type">int begin,            class=class="str">"cmt">// 开始有效计算的柱形位置
                class="kw">const class="type">class="kw">double &price[]       class=class="str">"cmt">// 用于指标计算的价格数组
                )
  {
class=class="str">"cmt">//---- 检查用于计算的柱形数量是否足够
  if(rates_total<FATLPeriod-class="num">1+begin)
     class="kw">return(class="num">0);
class=class="str">"cmt">//---- 声明局部变量
  class="type">int first,bar;
  class="type">class="kw">double Sum,FATL;
class=class="str">"cmt">//---- 柱形重算循环中,计算“第一个”开始值
  if(prev_calculated==class="num">0)      class=class="str">"cmt">//检查第一个指标值的计算
     {
     first=FATLPeriod-class="num">1+begin;  class=class="str">"cmt">// 所有柱形的开始计算位置
     class=class="str">"cmt">//--- 在“begin”柱的基础上增加数据开始位,因为 
     class=class="str">"cmt">//   此计算是基于另外一个指标的值
     if(begin>class="num">0)
        PlotIndexSetInteger(class="num">0,PLOT_DRAW_BEGIN,begin+FATLPeriod);
   }
  else first=prev_calculated-class="num">1; class=class="str">"cmt">// 新柱形计算的开始位置
class=class="str">"cmt">//---- 指标计算的主循环
  for(bar=first; bar<rates_total; bar++)
     {
     FATL=class="num">0.4360409450*price[bar-class="num">0]
        + class="num">0.3658689069 * price[bar - class="num">1]
        + class="num">0.2460452079 * price[bar - class="num">2]
        + class="num">0.1104506886 * price[bar - class="num">3]
        - class="num">0.0054034585 * price[bar - class="num">4]
        - class="num">0.0760367731 * price[bar - class="num">5]
        - class="num">0.0933058722 * price[bar - class="num">6]
        - class="num">0.0670110374 * price[bar - class="num">7]
        - class="num">0.0190795053 * price[bar - class="num">8]
        + class="num">0.0259609206 * price[bar - class="num">9]
        + class="num">0.0502044896 * price[bar - class="num">10]

远端滞后项的衰减尾巴

上面这段是 FATL 平滑函数尾部的滞后项累加,覆盖 bar-11 到 bar-38 共 28 个历史价格点。系数从 bar-11 的 +0.04778 一路衰减,到 bar-35 附近已贴近 0(仅 +0.00055),说明 35 根 K 线以外的价格对当前平滑值几乎无贡献。 注意 bar-13 到 bar-17 出现连续负值(-0.00477 至 -0.03389),这是滤波核在抵消中频噪声;而 bar-37 突然回弹到 +0.01301,属于长周期分量的残留权重。直接在 MT5 里把这段贴进自定义指标,改 ExtLineBuffer 输出,就能看到远端项对曲线的拖尾效果。 外汇与贵金属波动剧烈,这类平滑值仅作趋势参考,实盘信号概率性成立,不代表方向确定性。

MQL5 / C++
      + class="num">0.0477818607 * price[bar - class="num">11]
      + class="num">0.0249252327 * price[bar - class="num">12]
      - class="num">0.0047706151 * price[bar - class="num">13]
      - class="num">0.0272432537 * price[bar - class="num">14]
      - class="num">0.0338917071 * price[bar - class="num">15]
      - class="num">0.0244141482 * price[bar - class="num">16]
      - class="num">0.0055774838 * price[bar - class="num">17]
      + class="num">0.0128149838 * price[bar - class="num">18]
      + class="num">0.0226522218 * price[bar - class="num">19]
      + class="num">0.0208778257 * price[bar - class="num">20]
      + class="num">0.0100299086 * price[bar - class="num">21]
      - class="num">0.0036771622 * price[bar - class="num">22]
      - class="num">0.0136744850 * price[bar - class="num">23]
      - class="num">0.0160483392 * price[bar - class="num">24]
      - class="num">0.0108597376 * price[bar - class="num">25]
      - class="num">0.0016060704 * price[bar - class="num">26]
      + class="num">0.0069480557 * price[bar - class="num">27]
      + class="num">0.0110573605 * price[bar - class="num">28]
      + class="num">0.0095711419 * price[bar - class="num">29]
      + class="num">0.0040444064 * price[bar - class="num">30]
      - class="num">0.0023824623 * price[bar - class="num">31]
      - class="num">0.0067093714 * price[bar - class="num">32]
      - class="num">0.0072003400 * price[bar - class="num">33]
      - class="num">0.0047717710 * price[bar - class="num">34]
      + class="num">0.0005541115 * price[bar - class="num">35]
      + class="num">0.0007860160 * price[bar - class="num">36]
      + class="num">0.0130129076 * price[bar - class="num">37]
      + class="num">0.0040364019 * price[bar - class="num">38];
      class=class="str">"cmt">//---- 用FATL初始化指标缓存单元
      ExtLineBuffer[bar]=FATL;
   
}
class=class="str">"cmt">//----+    
   class="kw">return(rates_total);
   
}
class=class="str">"cmt">//+------------------------------------------------------------------+
把编译诊断交给小布
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到编译异常与逻辑风险提示,你专注策略本身。

常见问题

编译程序只检测 MQL5 语法错误,逻辑错误如除零、条件判断写错它不会报,需要人工审查 OnCalculate 内的运算与分支。
单个语法错误常触发连锁编译错误,应优先处理 Errors 选项卡第一行,修正后重新编译再看后续。
有帮助,刻意制造破坏性更改并观察编译结果,能建立对报错位置的直觉,后续排错效率更高。
可以,小布盯盘内置的 AIGC 会标注品种页里的编译异常与常见逻辑风险,减少手动逐行检查的时间。
通常不会,这属于逻辑层面改动,编译器不拦截,但会让指标计算方向反转,需结合数据验证。