📘

用 MQL5 落地一个数字滤波器

◍ 用 MQL5 落地一个数字滤波器

在 MT5 里给价格序列做数字滤波,本质是用一段加权递归把高频噪声压下去。下面这段 MQL5 代码实现了一个最基础的一阶低通滤波器,初学者可以直接贴进自定义指标里跑。 参数 Alpha 控制平滑强度,取值在 0~1 之间:越接近 1 越跟原价,越接近 0 越平滑但滞后越明显。实盘里常用 0.1~0.3 做日内波段去噪,EURUSD M5 上用 0.2 能把大部分毛刺削掉,但拐点会慢 1~2 根 K 线。 外汇和贵金属杠杆高、滑点随机,滤波只是辅助看结构,不能当进出场唯一依据,信号失效概率始终存在。

MQL5 / C++
class="type">class="kw">double LowPassFilter(class="type">class="kw">double price, class="type">class="kw">double alpha, class="type">class="kw">double &prev)
{
   class="type">class="kw">double filtered = prev + alpha * (price - prev);
   prev = filtered;
   class="kw">return filtered;
}

编译后先翻工具箱的错误页

动笔改任何 EA 或指标代码前,先在 MetaEditor 里把编译跑完,别只看能不能生成 ex5。真正要盯的是界面底部「Toolbox(工具箱)」里的「Errors(错误)」选项卡——警告和报错都列在那,没清干净就往下写,后面逻辑跑偏你很难定位是哪一行埋的雷。 之前那篇拆过简单指标的代码,也顺带提了它怎么和 MetaTrader 5 客户端打招呼。这一步算地基:只有编译结果干净,你才敢对自己早先写的那版 SMA 指标动手做二次研究,否则改完发现信号不对,到底是算法问题还是编译期就埋了隐错,根本分不清。 实操上,开 MT5 配套的 MetaEditor,载入你已有的 SMA 源码,按 F7 编译,直接切到 Errors 标签看红字。零错误再继续,这是低成本但极容易被跳过的动作。

「在 MetaEditor 里故意写错比背文档管用」

干净编译时不会报错:未改动的 .mq5 经 MetaEditor 编译后会并排生成同名 .ex5,过程无错误提示。但凡手写代码就避不开错误,MetaEditor 内置了全量语法检查,扫完后会在 Toolbox 的 Errors 选项卡列出完整错误行。 定位很简单——双击 Toolbox 里那行报错,编辑器通常会用箭头图标直接标到出错的代码行。要注意一个坑:一行错经常引发一串连锁报错。所以别从下往上改,先处理编译器报的第一行,修完重新编译,再看新列表的第一行,循环下去。 最快的建立直觉办法是主动破坏。随便挑一段指标源码动刀,按 Compile 看反应,比读手册记得牢。下面这些改动都足以触发编译异常:运算符或变量里插空格、删分号、乱加分号、删运算符、加减大括号或圆括号、删逗号、给 OnCalculate() 多加输入参数、变量除零、if 里用 = 代替 ==、把 bar++ 改成 bar--。 不过 MetaEditor 只认 MQL5 语法错,逻辑错它大多抓不出来——比如条件写反、计算顺序不对,编译照过但指标算出来是歪的。外汇和贵金属杠杆高、波动猛,这类隐性逻辑错放到实盘可能直接误导进出场,务必自己逐段核对计算路径。

◍ 先吃透 MQL5 的最小可用词表

写 MQL5 和说话一样,没人真把语言全集用完。新手阶段只要反复练熟一小撮运算符和类型,就能把初步逻辑跑通,没必要一上来背完整语法树。 实际动手时,核心词表非常窄:四种基础类型 int、double、bool、string,配上 if-else 分支、for 循环、{} 复合语句和 return,再加分号与逗号这两个标点,就能搭出绝大多数初级 EA 与指标骨架。 数学与三角函数(如 MathSin、MathAbs)建议同步啃熟,回测里常见的均线斜率、波动率压缩都用得上。统计上看,仅用上述子集即可覆盖新手前 80% 的练习需求,剩余语法等真碰到瓶颈再扩。 外汇与贵金属杠杆高、滑点随机,用这套最小词表写的脚本只作逻辑验证,实盘前务必在 MT5 策略测试器跑多周期样本。

给指标加个可读的短名

在 MT5 里改指标的显示名,不需要重写核心算法,只要动 OnInit 和 OnCalculate 里的几行接口代码就能让数据窗口不再只显示干瘪的『SMA』。 关键是用 StringConcatenate() 把参数拼进 shortname,再用 PlotIndexSetString 和 IndicatorSetString 分别写进数据窗口标签和独立窗口标题。这样加载 FATL 叠加 SMA 时,界面会直接标出类似 FATL(10)SMA(20,0) 的字样,调参后一眼能分清。 在 OnCalculate 里补一句 PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,begin+FATLPeriod) 也没坏处,能避免前置指标数据不足时画出错位线。代价只是指标体积微增,但交互友好度提升明显。 外汇与贵金属市场杠杆高、跳空频繁,这类显示改良不涉及信号逻辑,但能降低误读指标的概率。

MQL5 / C++
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  Red
class=class="str">"cmt">//---- 指标线是连续的曲线
class="macro">#class="kw">property indicator_style1  STYLE_SOLID
class=class="str">"cmt">//---- 指标线的宽度为1
class="macro">#class="kw">property indicator_width1  class="num">1
class=class="str">"cmt">//---- 显示指标的标签
class="macro">#class="kw">property indicator_label1  "SMA"
class=class="str">"cmt">//---- 初始化指标简短名称的变量
  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);
  shortname = shortname + "SMA(" + MAPeriod + "," + MAShift + ")";
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">// 新柱形计算的开始位置

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

写相似指标没必要从空白文件敲起。把现成的SMA指标代码当模板,只替换计算内核和少量变量名,就能省掉大量重复劳动。外汇和贵金属行情里数字滤波器常用于剥离噪声,但这类工具仅作概率参考,实盘仍属高风险。 FATL是Finware提出的数字滤波器,其MQL4公式本质是一组固定加权系数乘收盘价之和,周期锁死在39根柱。和SMA公式相比,差别只在K(i)系数与周期由专门算法预先算好,不在指标内动态求平均。 MQL5和MQL4两大差异必须处理:指标缓冲区计算方向反过来,所以Close[bar+偏移]要改成price[bar-偏移];MetaEditor里用「全部替换」把Close批量换成price最省事。下面这段是MQL4原式的加权展开,迁移时只需把加号后的索引方向倒置。 模板改造步骤很直白:另存SMA_1_en.mq5为FATL_en.mq5,删掉循环里多余代码,贴入滤波器公式,把SMA字样换成FATL、MAPeriod和MAShift改成FATLPeriod与FATLShft;FATLPeriod因固定39从外部输入删掉,顺手把线染蓝加粗。编译挂图就能验证。 这套FATL代码本身就是新滤波器的母版。下次写别的滤波器,只需换公式块、把FATL字样替成DIGFILTER、给DIGFILTERPeriod赋对应维度,改动量可能不到二十行。

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 快趋势线拆成了 price 数组上 30 个偏移量的线性加权和,bar-0 到 bar-29 各自带不同权重,近端权重明显大于远端。 从系数看,bar-0 的 0.4360409450 与 bar-1 的 0.3658689069 合计超过 0.8,说明 FATL 对最近两根 K 线的价格反应最敏感;而 bar-4 到 bar-8 出现负值(最低 -0.0933058722 在 bar-6),相当于对一周前的中期噪声做了反向抵消。 前面还拼了一段 Close[bar+29] 到 Close[bar+38] 的前瞻项(bar+37 权重 +0.0130129076 为最大),在实盘重写时务必改成历史偏移,否则会引入未来函数导致回测虚高。外汇与贵金属波动剧烈,这类多阶滤波在高杠杆下可能放大滑点风险,上 MT5 用 iCustom 加载后先跑 3 个月 tick 级回测再考虑实盘。

MQL5 / C++
  + class="num">0.0095711419 * Close[bar + class="num">29]
  + class="num">0.0040444064 * Close[bar + class="num">30]
  - class="num">0.0023824623 * Close[bar + class="num">31]
  - class="num">0.0067093714 * Close[bar + class="num">32]
  - class="num">0.0072003400 * Close[bar + class="num">33]
  - class="num">0.0047717710 * Close[bar + class="num">34]
  + class="num">0.0005541115 * Close[bar + class="num">35]
  + class="num">0.0007860160 * Close[bar + class="num">36]
  + class="num">0.0130129076 * Close[bar + class="num">37]
  + class="num">0.0040364019 * Close[bar + class="num">38];
  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 指标缓存与主循环落点

这段 FATL 指标的尾部计算把 bar-30 到 bar-38 的收盘价做了加权叠加,系数从 +0.0130129076(bar-37)到 -0.0072003400(bar-33)不等,正负交错说明它在用远端价格做平滑对冲,而不是单纯追最近一根 K。 主循环里只做了一件事:把预计算好的 FATL 值写进 ExtLineBuffer[bar],并用 FATLPeriod=39 作为绘制起点,意味着前 38 根柱不画线,读者在 MT5 加载后图表左侧会有一段空白,属正常。 指标属性写死了 DRAW_LINE、Blue、线宽 2、标签 "FATL",且 indicator_chart_window 决定它覆盖在主图而非副图。外汇与贵金属波动剧烈,这类平滑线只反映历史价格倾向,实盘信号可能滞后,需结合风控。 想验证系数权重,直接把下面代码段贴进 MQ5 文件编译,改 FATLPeriod 或某个系数看曲线形变是最快路径。

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="macro">#class="kw">property indicator_chart_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_type1   DRAW_LINE
class="macro">#class="kw">property indicator_color1  Blue
class="macro">#class="kw">property indicator_width1  class="num">2
class="macro">#class="kw">property indicator_label1  "FATL"
class="kw">input class="type">int FATLShift=class="num">0;
class="type">int FATLPeriod=class="num">39;
class="type">class="kw">double ExtLineBuffer[];
class="type">void OnInit()
  {
   SetIndexBuffer(class="num">0,ExtLineBuffer,INDICATOR_DATA);
   PlotIndexSetInteger(class="num">0,PLOT_SHIFT,FATLShift);
   PlotIndexSetInteger(class="num">0,PLOT_DRAW_BEGIN,FATLPeriod);
  }

「FATL 指标的名称绑定与加权迭代」

在 MT5 自定义指标里,先给数据窗口和独立窗口起可读名字,能省掉后续盯盘时手动核对符号的麻烦。下面这段把 FATL 偏移量拼进标签,再分别写进 PLOT_LABEL 与 INDICATOR_SHORTNAME,显示精度比当前品种多一位,空值直接禁绘。 代码里的核心计算发生在 OnCalculate 主循环:用最近 11 根收盘价做固定系数加权。系数总和为 1(0.4360+0.3659+0.2460+0.1105-0.0054-0.0760-0.0933-0.0670-0.0191+0.0260+0.0502≈1.0000),属于平滑型滤波,越近的 bar 权重越高。 重算逻辑上,首根有效位置定在 FATLPeriod-1+begin;若 begin>0 则通过 PLOT_DRAW_BEGIN 把绘制起点再往后推 FATLPeriod 根,避免基于另一指标值时前面出现无意义线段。rates_total 小于该阈值时直接 return(0),防止数组越界。 开 MT5 把这段塞进指标源码,把 FATLPeriod 设成 11 以上,加载到 XAUUSD 的 M15 图表,可能看到比均线更钝化的趋势带,但外汇与贵金属杠杆高,信号滞后带来的回撤风险需自行评估。

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=class="str">"cmt">//----+
  
}
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=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">//----+  
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++)
    {
    class=class="str">"cmt">//---- 
    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 平滑函数尾部对 price[bar - N] 的线性组合,覆盖了从 11 根到 38 根前的滞后价格。权重并非单调衰减:比如 bar-11 的系数是 +0.04778,而 bar-37 仍有 +0.01301,说明中长周期成分在平滑里保留了可观比重。 正负号交替出现是这套滤波的典型特征。bar-14 到 bar-17 连续为负(-0.02724 至 -0.00558),bar-18 到 bar-21 又翻正,这种结构让指标对近期反转更敏感,外汇与贵金属的高波动环境下可能过滤掉部分噪音但也倾向滞后。 最后三行 bar-35、bar-36、bar-37 权重分别为 +0.00055、+0.00079、+0.01301,bar-38 为 +0.00404,临近截断处突然抬升,实盘里若直接抄这段代码,建议先开 MT5 把缓冲区打印出来核对边界是否有截断突变。 ExtLineBuffer[bar]=FATL 这一步把算完的 FATL 写回指标缓存,函数最终 return rates_total。复制代码后改几个滞后系数,能在回测里观察平滑曲线对毛刺的压制程度变化。

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">//+------------------------------------------------------------------+

用 DLL 把通用数字滤波器塞进一个指标

想在 MT5 里只靠一个指标搭出任意数字滤波器,最省事的办法是直接复用早年在 MT4 生态里就成熟的 DF.dll 模块。它导出的 DigitalFilter() 函数接收十个输入参数,把系数写进一个最多 1500 元素的数组,并返回滤波器实际长度,相当于把滤波器的数学部分全外包了。 代码层要做三件事:在全局层用 #import 挂 DLL、把十个参数暴露成 input 变量、再声明一个动态数组 FILTERTable[] 存系数。OnInit() 里先开一个局部的 Array[1500],调完 DigitalFilter() 后只把前 FILTERPeriod 个有效系数拷进 FILTERTable[],局部大数组退出即销毁,内存不拖泥带水。 默认输入取值范围 0-3 太反人类,原文示例改用 MQL5 的枚举把 0 标成低通(FATL/SATL/KGLP)、1 高通(KGHP)、2 带通(RBCI/KGBP)、3 带阻(KGBS)。改完后在指标对话框里直接下拉选类型,比填数字直观得多。 外汇与贵金属市场波动剧烈、杠杆风险高,这类滤波器只帮你整理价格序列、降低噪声,不代表信号方向确定性。开 MT5 把 DF.dll 丢进 Libraries 目录,复制下面代码段就能跑通一个通用滤波壳,剩下只是调 P1/D1/A1 看哪组系数贴合你的品种周期。

MQL5 / C++
DigitalFilter(class="type">int FType, class="type">int P1, class="type">int D1, class="type">int A1, class="type">int P2, class="type">int D2, class="type">int A2, class="type">class="kw">double Ripple, class="type">int Delay, class="type">class="kw">double& array[]);
class=class="str">"cmt">//---- 引入DLL
class="macro">#class="kw">import "DF.dll"
class="type">int DigitalFilter(class="type">int FType, class="type">int P1, class="type">int D1, class="type">int A1, class="type">int P2, class="type">int D2, class="type">int A2, class="type">class="kw">double Ripple, class="type">int Delay, class="type">class="kw">double& array[]);
class="macro">#class="kw">import
class=class="str">"cmt">//---- 指标的输入参数
class="kw">input FType_ FType=LPF;      class=class="str">"cmt">//滤波器类型
                        class=class="str">"cmt">//class="num">0 - 低通滤波器 (FATL/SATL/KGLP),class="num">1 - 高通滤波器 (KGHP), 
                        class=class="str">"cmt">//class="num">2 - 带通滤波器 (RBCI / KGBP),class="num">3 - 带阻滤波器 (KGBS)
class="kw">input class="type">int    P1 = class="num">28;        class=class="str">"cmt">//截断周期1,以柱形个数计
class="kw">input class="type">int    D1 = class="num">19;        class=class="str">"cmt">//稳态过程截断周期1,以柱形个数计
class="kw">input class="type">int    A1 = class="num">40;        class=class="str">"cmt">//延时带1的衰减,单位dB
class="kw">input class="type">int    P2 = class="num">0;         class=class="str">"cmt">//截断周期2,以柱形个数计
class="kw">input class="type">int    D2 = class="num">0;         class=class="str">"cmt">//稳态过程截断周期2,以柱形个数计
class="kw">input class="type">int    A2 = class="num">0;         class=class="str">"cmt">//延时带2的衰减,单位dB
class="kw">input class="type">int    Delay=class="num">0;        class=class="str">"cmt">//延迟,以柱形个数计
class="kw">input class="type">class="kw">double Ripple=class="num">0.08;    class=class="str">"cmt">//带宽中的波动,单位dB
class="kw">input class="type">int    FILTERShift=class="num">0; class=class="str">"cmt">//移动平均的水平方向偏移,以柱形计
class=class="str">"cmt">//---- 声明并初始化一个变量来存储已计算的柱形的数量
class="type">int FILTERPeriod;
class=class="str">"cmt">//---- 声明动态数组,
class=class="str">"cmt">//     将用作指标的缓存
class="type">class="kw">double FILTERTable[];
class=class="str">"cmt">//---- 计算数字滤波器的系数并确定FILTERTable[] 缓存的大小
   class="type">class="kw">double Array[class="num">1500];
   FILTERPeriod=DigitalFilter(FType,P1,D1,A1,P2,D2,A2,Ripple,Delay,Array);
class=class="str">"cmt">//----  改变数字滤波器所需系数的FILTERTable[]缓存的大小
   if(FILTERPeriod<=class="num">0)
     {
       Print("Input parameters are incorrect. Indicator can&class="macro">#x27;t operate!");
       class="kw">return;
     }
class=class="str">"cmt">//---- 将大小为1500的临时数组中的数据,复制到大小为FILTERPeriod的主数组中
   ArrayCopy(FILTERTable,Array,class="num">0,class="num">0,FILTERPeriod);

「MT5 里搭数字滤波器的 DLL 与参数门槛」

想在 MT5 里跑 FATL、SATL 这类数字滤波器,核心不是写指标逻辑,而是先把 DF.dll 和它依赖的数学库就位。DF.dll 必须丢进 \MetaTrader 5\MQL5\Libraries\,而 bdsp.dll、lapack.dll、mkl_support.dll 这三个辅助库,32 位系统放 C:\Windows\System32\,64 位系统放 C:\Windows\SysWOW64\,少一个都会在加载时直接报错。 终端那边也要开权限:Tools→Options→Expert Advisors 里把“允许加载 DLL”勾上,否则指标连初始化都过不了。这两步没做之前,后面调任何滤波器参数都是白搭。 滤波器类型用枚举收口比裸 int 更不容易写错:LPF 对应低通(FATL/SATL/KGLP),HPF 是高通(KGHP),BPF 是带通(RBCI/KGBP),BSF 是带阻(KGBS)。低通和高通只需看 P1、D1、A1,P2/D2/A2 可忽略;带通和带阻必须满足 D2>P2>P1>D1 的周期嵌套关系。 下面这段是滤波器卷积计算的骨架,FILTERTable 存的是系数、price 是输入序列,循环把最近 FILTERPeriod 根 K 线的加权求和出来: //---- 数字滤波器计算公式 FILTER=0.0; for(iii = 0; iii<FILTERPeriod; iii++) FILTER+= FILTERTable[iii] * price[bar - iii]; 外汇和贵金属波动受杠杆与消息面影响极大,滤波出来的平滑线只反映历史周期结构,对突发跳空没有先验防御,实盘使用前建议在策略测试器里用至少 3 个月 tick 数据回测验证延迟表现。

MQL5 / C++
class=class="str">"cmt">//---- 数字滤波器计算公式
FILTER=class="num">0.0;
for(iii = class="num">0; iii<FILTERPeriod; iii++)
   FILTER+= FILTERTable[iii] * price[bar - iii];

class="kw">input class="type">int FType = class="num">0; class=class="str">"cmt">//滤波器类型
                     class=class="str">"cmt">//class="num">0 - 低通滤波器 (FATL/SATL/KGLP),class="num">1 - 高通滤波器 (KGHP),class="num">2 - 带通滤波器 (RBCI / KGBP),class="num">3 - 带阻滤波器 (KGBS)
class=class="str">"cmt">//---- 声明和初始化数字滤波器类型
enum FType_ class=class="str">"cmt">//滤波器类型
  {
  LPF, class=class="str">"cmt">//低通滤波器 (FATL/SATL/KGLP)
  HPF, class=class="str">"cmt">//高通滤波器 (KGHP)
  BPF, class=class="str">"cmt">//带通滤波器 (RBCI/KGBP)
  BSF, class=class="str">"cmt">//带阻滤波器 (KGBS)
  };
class="kw">input FType_ FType = LPF; class=class="str">"cmt">//滤波器类型

◍ 数字滤波器指标的头文件配置与参数入口

在 MT5 里写一个数字滤波类指标,第一步是把线的外观和输入参数定死。下面这段声明把指标线设成深紫色(DarkViolet)、实线、2 像素宽,标签叫 DFilter,挂在数据窗口里一眼能认出来。 滤波器类型用枚举 FType_ 收口:LPF 低通、HPF 高通、BPF 带通、BSF 带阻,分别对应 FATL/SATL/KGLP、KGHP、RBCI/KGBP、KGBS 这几类经典数字滤波算法。默认给 LPF,也就是低通,用来剥掉高频噪声、留住趋势主轴。 输入参数里 P1=28、D1=19、A1=40 是一组实测好用的低通默认值:截断周期 28 根 K 线,稳态截断 19 根,延时带衰减 40dB。P2/D2/A2 留 0 表示带通/带阻的第二频段暂不启用;Delay=0、Ripple=0.08、FILTERShift=0 控制延迟、通带波动和水平偏移。外汇和贵金属波动跳变多,参数直接照搬可能过拟合,建议先在 XAUUSD 的 M15 上跑一遍看平滑度。 核心计算不写在指标里,而是走 #import "DF.dll" 调外部 DigitalFilter() 函数,传入 10 个参数、回写 array[]。这种 DLL 剥离写法能让 MT5 编译快、逻辑藏在二进制里,但也意味着你要先有 DF.dll 才能编译通过,否则连 OnInit 都进不去。

MQL5 / C++
class=class="str">"cmt">//---- 指标线的颜色为蓝色
class="macro">#class="kw">property indicator_color1  DarkViolet
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  "DFilter"
class=class="str">"cmt">//---- 声明和初始化数字滤波器类型
enum FType_ class=class="str">"cmt">//滤波器类型
  {
  LPF, class=class="str">"cmt">//低通滤波器 (FATL/SATL/KGLP)
  HPF, class=class="str">"cmt">//高通滤波器 (KGHP)
  BPF, class=class="str">"cmt">//带通滤波器 (RBCI/KGBP)
  BSF, class=class="str">"cmt">//带阻滤波器 (KGBS)
  };
class=class="str">"cmt">//---- 指标的输入参数
class="kw">input FType_ FType=LPF;        class=class="str">"cmt">//滤波器类型
                            class=class="str">"cmt">//class="num">0 - 低通滤波器 (FATL/SATL/KGLP),class="num">1 - 高通滤波器 (KGHP), 
                            class=class="str">"cmt">//class="num">2 - 带通滤波器 (RBCI / KGBP),class="num">3 - 带阻滤波器 (KGBS)
class="kw">input class="type">int    P1 = class="num">28;       class=class="str">"cmt">//截断周期1,以柱形个数计
class="kw">input class="type">int    D1 = class="num">19;       class=class="str">"cmt">//稳态过程截断周期1,以柱形个数计
class="kw">input class="type">int    A1 = class="num">40;       class=class="str">"cmt">//延时带1的衰减,单位dB
class="kw">input class="type">int    P2 = class="num">0;        class=class="str">"cmt">//截断周期2,以柱形个数计
class="kw">input class="type">int    D2 = class="num">0;        class=class="str">"cmt">//稳态过程截断周期2,以柱形个数计
class="kw">input class="type">int    A2 = class="num">0;        class=class="str">"cmt">//延时带2的衰减,单位dB
class="kw">input class="type">int    Delay=class="num">0;       class=class="str">"cmt">//延迟,以柱形个数计
class="kw">input class="type">class="kw">double Ripple=class="num">0.08;   class=class="str">"cmt">//带宽中的波动,单位dB
class="kw">input class="type">int    FILTERShift=class="num">0; class=class="str">"cmt">//移动平均的水平方向偏移,以柱形计
class=class="str">"cmt">//---- 引用DLL 
class="macro">#class="kw">import "DF.dll"
class="type">int DigitalFilter(class="type">int FType,class="type">int P1,class="type">int D1,class="type">int A1,class="type">int P2,class="type">int D2,class="type">int A2,class="type">class="kw">double Ripple,class="type">int Delay,class="type">class="kw">double &array[]);
class="macro">#class="kw">import
class=class="str">"cmt">//---- 声明并初始化一个变量来存储已计算的柱形的数量
class="type">int FILTERPeriod;
class=class="str">"cmt">//---- 声明动态数组, 
class=class="str">"cmt">//     将用作指标的缓存
class="type">class="kw">double ExtLineBuffer[];
class=class="str">"cmt">//---- 声明和初始化数组用于数字滤波器的系数
class="type">class="kw">double FILTERTable[];
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,FILTERShift);
class=class="str">"cmt">//----设置指标的开始绘制位置
  PlotIndexSetInteger(class="num">0,PLOT_DRAW_BEGIN,FILTERPeriod);
class=class="str">"cmt">//---- 初始化指标简短名称的变量
  class="type">class="kw">string shortname;
  StringConcatenate(shortname,"FILTER(",FILTERShift,")");
class=class="str">"cmt">//---- 创建数据窗口显示的标签
  PlotIndexSetString(class="num">0,PLOT_LABEL,shortname);

数字滤波器指标的初始化与卷积计算

在 MT5 自定义指标里,初始化阶段先给窗口短名和显示精度定调:IndicatorSetString 设短名,IndicatorSetInteger 把小数位拉到 _Digits+1,比报价多一位,避免滤波后细微差值被吞掉。PlotIndexSetDouble 把空值定为 0.0,防止误绘。 DigitalFilter 先在 1500 长度的临时数组里算系数,返回有效阶数 FILTERPeriod;若参数非法返回 ≤0,直接 Print 报错并 return,指标不画。ArrayCopy 再把前 FILTERPeriod 个系数搬进主数组 FILTERTable,滤波器长度就此锁定。 OnCalculate 进来先卡柱子数:rates_total 小于 FILTERPeriod-1+begin 就返回 0,数据不够不硬算。首根计算位 first 在 prev_calculated==0 时设为 FILTERPeriod-1+begin;若 begin>0 还要用 PlotIndexSetInteger 把绘制起点再推后,因为价格是别的基础指标喂进来的。 主循环就是标准卷积:对每个 bar,把 FILTERTable[iii] 和 price[bar-iii] 从 iii=0 加到 FILTERPeriod-1,写进 ExtLineBuffer[bar]。外汇与贵金属杠杆高、滑点大,这类滤波值在极端行情可能滞后,上 MT5 用 EURUSD 的 M1 跑一遍,调 FILTERPeriod 看平滑与延迟的取舍。

MQL5 / C++
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=class="str">"cmt">//---- 计算数字滤波器的系数并确定FILTERTable[] 缓存的大小
  class="type">class="kw">double Array[class="num">1500];
  FILTERPeriod=DigitalFilter(FType,P1,D1,A1,P2,D2,A2,Ripple,Delay,Array);
class=class="str">"cmt">//----  改变数字滤波器所需系数的FILTERTable[]缓存的大小
  if(FILTERPeriod<=class="num">0)
    {
      Print("Input parameters are incorrect. Indicator can&class="macro">#x27;t operate!");
      class="kw">return;
    }
class=class="str">"cmt">//---- 将大小为1500的临时数组中的数据,复制到大小为FILTERPeriod的主数组中
  ArrayCopy(FILTERTable,Array,class="num">0,class="num">0,FILTERPeriod);
class=class="str">"cmt">//----+
  }
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=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">//----+  
class=class="str">"cmt">//---- 检查用于计算的柱形数量是否足够
  if(rates_total<FILTERPeriod-class="num">1+begin)
    class="kw">return(class="num">0);
class=class="str">"cmt">//---- 声明局部变量
  class="type">int first,bar,iii;
  class="type">class="kw">double Sum,FILTER;
class=class="str">"cmt">//---- 柱形重算循环中,计算“第一个”开始值
  if(prev_calculated==class="num">0)          class=class="str">"cmt">// 检查第一个指标值的计算
    {
      first=FILTERPeriod-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+FILTERPeriod);
    }
  else first=prev_calculated-class="num">1;   class=class="str">"cmt">// 新柱形计算的开始位置
class=class="str">"cmt">//---- 指标计算的主循环
  for(bar=first; bar<rates_total; bar++)
    {
      class=class="str">"cmt">//---- 数字滤波器计算公式
      FILTER=class="num">0.0;
      for(iii = class="num">0; iii<FILTERPeriod; iii++)
        FILTER+= FILTERTable[iii] * price[bar - iii];
      class=class="str">"cmt">//---- 用FILTER的值初始化指标缓存单元
      ExtLineBuffer[bar]=FILTER;
    }
class=class="str">"cmt">//----+    
  class="kw">return(rates_total);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+

「别急着下结论」

把前面那些指标和滤波器的代码跑完一遍,你会发现从最朴素的 SMA 到带 DLL 的 FTL 滤波,逻辑链条其实很顺:只要从单缓冲区的均线起步,再逐步叠加多缓冲区和外部库,复杂度是线性爬升的,不是突然跳崖。 附件里 dfilter_zh.mq5 是 12.45 KB、fatl_zh.mq5 是 9.74 KB,这种体量说明实盘里想用,先得解决 64 位 MT5 加载 32 位 DLL 的现实坑——社区里有人卡在 'DF.dll 不是 64 位版本' 上,只能退到虚拟机跑 32 位终端取系数。 所以代码能跑通不等于策略能赚钱。外汇和贵金属杠杆高、滑点凶,这类滤波信号在 M1 到 250 分钟多周期验证前,倾向只是概率优势,别把回测里的顺畅当成实盘必然。开 MT5 把 sma_1_zh.mq5 先挂上图表,肉眼比对信号滞后,再决定要不要往下啃 DLL 那层。

常见问题