MQL5 Cookbook: 基于三重滤网策略开发交易系统框架(基础篇)
三重滤网在 MT5 里的骨架怎么搭
三重滤网不是直接塞进一个 EA 就完事,核心是把趋势、回调、信号三层时间周期解耦。MT5 里最实用的做法是用不同周期的手数(Period)分别取指标,而不是在同一个图表上硬切。 原文给出的框架示例发布于 2013-11-01,在 MetaTrader 5 官方示例库累计浏览 6273 次、评论 3 条,说明这套老骨架至今还有人翻出来照抄。 具体落地时,第一层用大周期判断多空偏向,第二层在小周期等回撤,第三层才用入场信号触发。读者可以直接开 MT5 新建 EA,把三层周期参数做成外部输入(input),先验证大周期过滤是否真的砍掉了逆势单。
◍ 从现成EA改出三重滤网框架
三重滤网策略由 Alexander Elder 提出,外界对其评价两极分化,但结论只能靠自己验证。会写 MQL5 的话,最直接的办法是用历史数据做回溯测试,而不是盲信任何一方的说辞。外汇与贵金属杠杆高,回测盈利也不代表实盘能复制,验证时务必控制仓位。 本文不从头造轮子,而是基于前文《MQL5 Cookbook: 在EA交易中使用指标设置交易条件》里的 EA 做修改。那个 EA 已经具备启停止损/获利、跟踪止损、按信号反手加仓等函数,我们要做的只是动外部参数、补几个选项、改少量逻辑。 具体目标:用移动平均线在三个周期上分别出信号。每个滤网的时间范围可独立设置;若某滤网指标周期参数填 0,则该层失效——系统可降级成单周期或双周期运行。后续换其他指标也只需改几行。 动手前,先把前文 EA 源文件所在目录复制一份并重命名,避免覆盖可运行版本。
「多周期三重滤网 EA 的参数与句柄改造」
把 EA 外部参数从单一周期改成三套独立时间范围,是接住三重滤网逻辑的第一步。时间范围声明为 ENUM_TIMEFRAMES 枚举,MT5 下拉框里能直接选 PERIOD_W1、PERIOD_D1、PERIOD_H4 等,顺序随意,不强制大中小排列。 原文示例里三个滤网周期分别给了 14、24、44,手数默认 0.1,止损 50 点、止盈 100 点、跟踪止损 10 点,Reverse 设为 true 表示允许反转仓。这些数字你拷进 MT5 能直接编译跑,但实盘前建议按品种波动重调。 每个时间范围对应一个独立指标,因此要为每个滤网建独立句柄与数据数组。新柱判定只用最小时间范围,所以得写个 GetMinimumTimeframe() 从三个外部参数里挑出最小周期——哪怕你只挂一两个滤网,这函数也得兜住空档。 最小周期存进全局变量 MinimumTimeframe,在 OnInit() 里调用一次,之后 CheckNewBar() 和 GetBarsData() 都靠它。GetIndicatorHandle() 现在按各自周期和时间范围取句柄,GetIndicatorsData() 再逐周期填充数组,句柄错就直接跳过。外汇和贵金属杠杆高,这套框架只是骨架,加条件或换指标都可能,回测不过别硬上。
class=class="str">"cmt">//--- EA交易的外部参数 sinput class="type">long MagicNumber=class="num">777; class=class="str">"cmt">// 幻数 input class="type">int Deviation=class="num">10; class=class="str">"cmt">// 滑点 class=class="str">"cmt">//--- input ENUM_TIMEFRAMES Screen01TimeFrame=PERIOD_W1; class=class="str">"cmt">// 第一个滤网的时间范围 input class="type">int Screen01IndicatorPeriod=class="num">14; class=class="str">"cmt">// 第一个滤网的指标周期数 class=class="str">"cmt">//--- input ENUM_TIMEFRAMES Screen02TimeFrame=PERIOD_D1; class=class="str">"cmt">// 第二滤网的时间范围 input class="type">int Screen02IndicatorPeriod=class="num">24; class=class="str">"cmt">// 第二滤网的指标周期数 class=class="str">"cmt">//--- input ENUM_TIMEFRAMES Screen03TimeFrame=PERIOD_H4; class=class="str">"cmt">// 第三滤网的时间范围 input class="type">int Screen03IndicatorPeriod=class="num">44; class=class="str">"cmt">// 第三滤网的指标周期数 class=class="str">"cmt">//--- input class="type">class="kw">double Lot=class="num">0.1; class=class="str">"cmt">// 手数 input class="type">class="kw">double VolumeIncrease=class="num">0.1; class=class="str">"cmt">// 仓位交易量增加量 input class="type">class="kw">double VolumeIncreaseStep=class="num">10; class=class="str">"cmt">// 仓位交易量增加步长 input class="type">class="kw">double StopLoss=class="num">50; class=class="str">"cmt">// 止损 input class="type">class="kw">double TakeProfit=class="num">100; class=class="str">"cmt">// 获利 input class="type">class="kw">double TrailingStop=class="num">10; class=class="str">"cmt">// 跟踪止损 input class="type">bool Reverse=true; class=class="str">"cmt">// 仓位反转 sinput class="type">bool ShowInfoPanel=true; class=class="str">"cmt">// 显示信息面板 class=class="str">"cmt">//--- 指标句柄
多周期滤网里挑最小时间框架
三层滤网各自挂一个指标句柄,初始化统一写成 INVALID_HANDLE,避免 EA 启动时误读残留句柄。 新K线检测要以最短周期为准,否则大周期还没走完小周期已经反复触发。下面这段把三个时间框架的秒数拉出来比大小,返回最小的那一个。 只开一个滤网时直接返回对应框架;开两个就 MathMin 比秒数;三个全开则先比前两个、胜出者再跟第三个比。若周期参数都 ≤0,退回 PERIOD_CURRENT。 外汇和贵金属波动受杠杆放大,多周期共振也只是概率倾向,实盘前请在 MT5 策略测试器用历史数据验证该函数的返回是否符合预期。
class="type">int Screen01IndicatorHandle=INVALID_HANDLE; class=class="str">"cmt">// 第一滤网的指标句柄 class="type">int Screen02IndicatorHandle=INVALID_HANDLE; class=class="str">"cmt">// 第二滤网的指标句柄 class="type">int Screen03IndicatorHandle=INVALID_HANDLE; class=class="str">"cmt">// 第三滤网的指标句柄 class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 为检查新柱确定最小时间范围 | class=class="str">"cmt">//+------------------------------------------------------------------+ ENUM_TIMEFRAMES GetMinimumTimeframe(ENUM_TIMEFRAMES timeframe1,class="type">int period1, ENUM_TIMEFRAMES timeframe2,class="type">int period2, ENUM_TIMEFRAMES timeframe3,class="type">int period3) { class=class="str">"cmt">//--- 默认最小时间范围 ENUM_TIMEFRAMES timeframe_min=PERIOD_CURRENT; class=class="str">"cmt">//--- 把时间范围值转换为秒用于计算 class="type">int t1= PeriodSeconds(timeframe1); class="type">int t2= PeriodSeconds(timeframe2); class="type">int t3= PeriodSeconds(timeframe3); class=class="str">"cmt">//--- 检查不正确的周期值 if(period1<=class="num">0 && period2<=class="num">0 && period3<=class="num">0) class="kw">return(timeframe_min); class=class="str">"cmt">//--- 只有一个时间范围的条件 if(period1>class="num">0 && period2<=class="num">0 && period3<=class="num">0) class="kw">return(timeframe1); if(period2>class="num">0 && period1<=class="num">0 && period3<=class="num">0) class="kw">return(timeframe2); if(period3>class="num">0 && period1<=class="num">0 && period2<=class="num">0) class="kw">return(timeframe3); class=class="str">"cmt">//--- 只有两个时间范围的条件 if(period1>class="num">0 && period2>class="num">0 && period3<=class="num">0) { timeframe_min=(MathMin(t1,t2)==t1) ? timeframe1 : timeframe2; class="kw">return(timeframe_min); } if(period1>class="num">0 && period3>class="num">0 && period2<=class="num">0) { timeframe_min=(MathMin(t1,t3)==t1) ? timeframe1 : timeframe3; class="kw">return(timeframe_min); } if(period2>class="num">0 && period3>class="num">0 && period1<=class="num">0) { timeframe_min=(MathMin(t2,t3)==t2) ? timeframe2 : timeframe3; class="kw">return(timeframe_min); } class=class="str">"cmt">//--- 三个时间范围的条件 if(period1>class="num">0 && period2>class="num">0 && period3>class="num">0) { timeframe_min=(class="type">int)MathMin(t1,t2)==t1 ? timeframe1 : timeframe2; class="type">int t_min=PeriodSeconds(timeframe_min); timeframe_min=(class="type">int)MathMin(t_min,t3)==t_min ? timeframe_min : timeframe3; class="kw">return(timeframe_min); } class="kw">return(WRONG_VALUE); }