初学者以 MQL5 实现对数字滤波器的实际实施·综合运用
🔧

初学者以 MQL5 实现对数字滤波器的实际实施·综合运用

(3/3)·写得出滤波器代码只是开始,编译报错频发、客户端挂不上指标才是新手真正的拦路虎

进阶 第 3/3 篇
很多初学者以为 MQL5 滤波器写完了就万事大吉,结果一编译满屏报错、指标死活不显示。其实编译器的错误列表有固定排查顺序,先改第一行往往能连带消掉一串假错误。

用 DF.dll 在 MT5 里搭一个通用数字滤波器

想在 MT5 里只靠一个指标就跑通任意数字滤波器,最直接的路子是复用 Sergei Ilyuhin 早年在 MT4 写好的 DF.dll。这个库暴露了 DigitalFilter() 函数,把滤波器系数写进一个长度 1500 的数组,返回值是滤波器实际长度,调用方拿这串系数去 OnCalculate() 里做卷积即可。 导入 DLL 只需三行:#import 声明函数原型,再 #import 收尾。函数接收 10 个输入参数——滤波器类型、两组截断周期 P1/P2、稳态周期 D1/D2、衰减 A1/A2、延迟 Delay 和通带波动 Ripple,其中 array[] 用 double& 引用传回系数。 全局层只声明 FILTERPeriod 和动态数组 FILTERTable[],不初始化。真正麻烦的是 OnInit():如果直接把 FILTERTable[] 当 DigitalFilter() 的缓冲,就得预留 1500 格,但 OnCalculate() 里通常只用 100–200 个。折中办法是在 OnInit() 里开局部 Array[1500],算完用 ArrayCopy 把前 FILTERPeriod 个系数拷进 FILTERTable[],退出后大数组自动销毁,主数组只留必要数据。 把输入参数从 0–3 的数字改成枚举更直观:0 低通(FATL/SATL/KGLP)、1 高通(KGHP)、2 带通(RBCI/KGBP)、3 带阻(KGBS)。MQL5 的 enum 类型加在 input 前,指标对话框里就能下拉选类型而不是填数字。 外汇和贵金属行情高频跳动,DLL 调用涉及第三方模块,加载失败或参数越界会让指标直接罢工——实盘前务必在策略测试器用历史数据验证系数数组非空。完整代码在 DFilter_en.mq5,改完枚举后你就不再依赖外部厂商的滤波器封装,自己调 P1=28、D1=19、A1=40、Ripple=0.08 这组默认值就能看到一条通用滤波线。

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 里跑数字滤波器,先得把 DF.dll 丢进 \MetaTrader 5\MQL5\Libraries\ 目录。它自己不独立运算,还要 bdsp.dll、lapack.dll、mkl_support.dll 三个数学库撑着:32 位系统放 C:\Windows\System32\,64 位系统放 C:\Windows\SysWOW64\。终端里「允许加载 DLL」不勾(Tools→Options→Expert Advisors)就直接编译失败,这是实盘前最容易漏的一步。 滤波器类型用枚举收口比裸 int 干净:LPF 低通(FATL/SATL/KGLP)、HPF 高通(KGHP)、BPF 带通(RBCI/KGBP)、BSF 带阻(KGBS)。低通和高通只吃 P1/D1/A1 三组参数,带通和带阻才需要 P2/D2/A2,且必须满足 D2>P2>P1>D1。 核心卷积就几行:FILTER 先清零,再按 FILTERTable 权重把 price[bar-iii] 累加起来。这段就是 FATL 类均线的本质——用系数表对价格做滑动加权和,周期参数决定平滑度。外汇与贵金属杠杆高,滤波信号只描述概率倾向,不代表方向必现。 加载前确认 SysWOW64 里三个数学库齐活,否则调用会静默报错。开 MT5 把下面代码贴进新指标,改 FType 枚举就能横向比对四类滤波在 XAUUSD 的 1H 表现。

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 里写自定义指标,第一步是把外部 DLL 算出来的数字滤波结果接进绘图缓存。上面这段声明把指标线设成 DarkViolet 实线、宽 2 像素,标签写 DFilter,读者打开数据窗口能直接看到这个名字。 滤波器类型用枚举 FType_ 锁死四种模式:LPF 低通(对应 FATL/SATL/KGLP)、HPF 高通(KGHP)、BPF 带通(RBCI/KGBP)、BSF 带阻(KGBS)。默认 FType=LPF,也就是只保留慢速趋势分量,把高频噪声切掉。 输入参数里 P1=28、D1=19、A1=40 是一组可验证的默认截断周期与衰减(dB),P2/D2/A2 默认全 0 表示二阶带限暂不启用;Ripple=0.08 控制通带波动,Delay 与 FILTERShift 默认 0 即无平移。 核心计算走 #import "DF.dll" 的 DigitalFilter(),它把九个参数加一个 double 数组引用丢进 DLL,返回整型状态码。OnInit 里用 SetIndexBuffer(0,ExtLineBuffer,INDICATOR_DATA) 把动态数组绑成 0 号缓存,再用 PlotIndexSetInteger 设水平偏移与绘制起点,StringConcatenate 拼出 "FILTER(0)" 短名。 [CODE]//---- 指标线的颜色为蓝色 #property indicator_color1 DarkViolet //---- 指标线是连续的曲线 #property indicator_style1 STYLE_SOLID //---- 指标线的宽度为2个像素 #property indicator_width1 2 //---- 显示指标的标签 #property indicator_label1 "DFilter" //---- 声明和初始化数字滤波器类型 enum FType_ //滤波器类型 { LPF, //低通滤波器 (FATL/SATL/KGLP) HPF, //高通滤波器 (KGHP) BPF, //带通滤波器 (RBCI/KGBP) BSF, //带阻滤波器 (KGBS) }; //---- 指标的输入参数 input FType_ FType=LPF; //滤波器类型 //0 - 低通滤波器 (FATL/SATL/KGLP),1 - 高通滤波器 (KGHP), //2 - 带通滤波器 (RBCI / KGBP),3 - 带阻滤波器 (KGBS) input int P1 = 28; //截断周期1,以柱形个数计 input int D1 = 19; //稳态过程截断周期1,以柱形个数计 input int A1 = 40; //延时带1的衰减,单位dB input int P2 = 0; //截断周期2,以柱形个数计 input int D2 = 0; //稳态过程截断周期2,以柱形个数计 input int A2 = 0; //延时带2的衰减,单位dB input int Delay=0; //延迟,以柱形个数计 input double Ripple=0.08; //带宽中的波动,单位dB input int FILTERShift=0; //移动平均的水平方向偏移,以柱形计 //---- 引用DLL #import "DF.dll" int DigitalFilter(int FType,int P1,int D1,int A1,int P2,int D2,int A2,double Ripple,int Delay,double &array[]); #import //---- 声明并初始化一个变量来存储已计算的柱形的数量 int FILTERPeriod; //---- 声明动态数组, // 将用作指标的缓存 double ExtLineBuffer[]; //---- 声明和初始化数组用于数字滤波器的系数 double FILTERTable[]; //+------------------------------------------------------------------+

//自定义指标初始化函数

//+------------------------------------------------------------------+ void OnInit() { //----+ //---- 将ExtLineBuffer动态数组变换为指标缓存 SetIndexBuffer(0,ExtLineBuffer,INDICATOR_DATA); //---- FILTERShift,指标的水平偏移 PlotIndexSetInteger(0,PLOT_SHIFT,FILTERShift); //----设置指标的开始绘制位置 PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,FILTERPeriod); //---- 初始化指标简短名称的变量 string shortname; StringConcatenate(shortname,"FILTER(",FILTERShift,")"); //---- 创建数据窗口显示的标签 PlotIndexSetString(0,PLOT_LABEL,shortname);

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(INDICATOR_SHORTNAME,shortname) 决定副图标题,IndicatorSetInteger(INDICATOR_DIGITS,_Digits+1) 把显示精度比品种默认多留一位,避免小点数被截掉。PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0) 则让空值不绘制,副图不会冒出 0 轴假线。 数字滤波器系数靠 DigitalFilter() 算进 1500 大小的临时 Array,再按返回的 FILTERPeriod 用 ArrayCopy 截进主数组 FILTERTable。若 FILTERPeriod<=0 说明参数组合非法,直接 Print 报错并 return,指标不往下跑。 OnCalculate 里第一道闸是柱数校验:rates_total < FILTERPeriod-1+begin 时返回 0,数据不够不硬算。首次计算 prev_calculated==0 时,first 定位到 FILTERPeriod-1+begin;若 begin>0 还要用 PlotIndexSetInteger 把绘图起点往后推,避免基于其他指标值时前面出空白。 主循环就是标准 FIR 卷积:每个 bar 上用 FILTERTable 系数对 price[bar-iii] 做加权和,写进 ExtLineBuffer[bar]。这套滤波对外汇、贵金属 1 分钟以上周期都可能压掉高频噪声,但杠杆品种跳空时滤波滞后倾向放大,实盘前务必在 MT5 策略测试器用历史数据验证系数稳定性。

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、FATL、DFILTER 代码都跑过一遍后,你应该已经拿到一组可直接挂图验证的指标文件:sma_zh.mq5 约 6.55 KB,fatl_zh.mq5 约 9.74 KB,dfilter_zh.mq5 约 12.45 KB。 这些源码本身不复杂,难点在于 32 位 DLL 在 64 位 MT5 上加载会报错——有用户实测在 2014 年 10 月用 x64 终端报“DF.dll 不是 64 位版本”,只能退回 32 位环境或自己重编。 回看整个路径,如果从单缓冲区 SMA 起步,再到多缓冲区的 FATL,最后接离散滤波器,逻辑是线性递进的;外汇与贵金属杠杆高、滑点跳空频繁,这类自定义指标只辅助判势,信号失效的概率始终存在。 真要落地,就开 MT5 把 zip 里的 mq5 逐个编译,先验证 SMA 在 M1 的均线斜率,再叠 FATL 看相位滞后差,比空读文档来得实在。

把编译排查交给小布盯盘
小布盯盘的 AIGC 已内置 MQL5 常见编译错误模式识别,打开对应品种页就能看到指标加载状态与异常提示,你只需专注滤波逻辑本身。

常见问题

大概率是一个早期错误触发了后续连锁报错。双击 Errors 选项卡第一行定位,修正后重新编译,重复此过程即可。
可以。小布盯盘内置了常见 MQL5 编译错误特征,能在品种页标记指标异常,省去你手动比对报错行的重复劳动。
MetaEditor 只查语法错误,不查逻辑错误。除数为零、循环方向写反这类问题需要你自己在 OnCalculate 里加校验。
需确认 ex5 已生成且放置在正确子目录,滤波器参数与图表周期匹配,否则可能编译通过但图形无输出。