MQL5三重滤网EA框架开发实战
基于移动平均改造可扩展多周期交易系统
三重滤网策略与本文目标
三重滤网(Triple Screen)策略由Alexander Elder博士提出,核心思想是在不同时间周期上分别确认趋势与入场时机,就像潮汐、波浪和涟漪的关系:大周期定方向,小周期找切入点。网络上对该策略评价两极分化,但真正检验它的方式是用代码实现并做历史回测,而不是听信任何一方的观点。
本文不从头开发EA,而是基于前作《在EA交易中使用指标设置交易条件》的成品修改。原EA已具备止损止盈、跟踪止损、仓位递增、反向反转等完整功能,我们只需调整外部参数、增加时间框架选项并改写信号模块,即可得到一个通用三重滤网框架。后续若想换用其他指标,改动量也极小。
外部参数与指标句柄设计
为支持三个独立滤网,我们声明了三组参数:Screen01/02/03TimeFrame(ENUM_TIMEFRAMES类型,可在下拉框选周期)与对应的IndicatorPeriod。若某周期Period设为0,表示该滤网停用,系统可降级为单屏或双屏。同时删除了原工程的IndicatorSegments等冗余条件,并在Enums.mqh中移除多余指标枚举,因为本框架只用一种指标(示例为均线)。
每个时间范围需要独立的指标句柄与数据数组。句柄在GetIndicatorHandles()中通过iMA()按各自Symbol与周期获取;数组indicator_buffer1~3分别接收对应滤网数值。注意原文代码中存在一处笔误:三个失败提示都判断了Screen01IndicatorHandle,实际应分别对应01/02/03,否则排查句柄错误时会产生误导。
input ENUM_TIMEFRAMES Screen01TimeFrame=PERIOD_W1; // 第一个滤网的时间范围 input int Screen01IndicatorPeriod=14; // 第一个滤网的指标周期数 input ENUM_TIMEFRAMES Screen02TimeFrame=PERIOD_D1; // 第二滤网的时间范围 input int Screen02IndicatorPeriod=24; // 第二滤网的指标周期数 input ENUM_TIMEFRAMES Screen03TimeFrame=PERIOD_H4; // 第三滤网的时间范围 input int Screen03IndicatorPeriod=44; // 第三滤网的指标周期数
int Screen01IndicatorHandle=INVALID_HANDLE; int Screen02IndicatorHandle=INVALID_HANDLE; int Screen03IndicatorHandle=INVALID_HANDLE; double indicator_buffer1[]; double indicator_buffer2[]; double indicator_buffer3[];
最小时间范围的自动判定
EA以最小时间范围作为新柱检查基准(CheckNewBar与GetBarsData均依赖它),但用户在外部参数中可按任意顺序填写周期。因此需在初始化时调用GetMinimumTimeframe(),遍历所有启用的Period,用PeriodSeconds换算为秒并取最小值。该函数覆盖0~3个滤网全场景:全0返回当前周期;仅一个>0则返回它;两个或三个>0则用MathMin比较秒数。
此设计让框架不强制周期大小顺序,提升易用性。MinimumTimeframe变量存结果,在OnInit里赋值,供后续逻辑使用。若日后扩展为四屏,只需重载此函数增加参数分支。
ENUM_TIMEFRAMES GetMinimumTimeframe(ENUM_TIMEFRAMES tf1,int p1, ENUM_TIMEFRAMES tf2,int p2, ENUM_TIMEFRAMES tf3,int p3)
{
int t1=PeriodSeconds(tf1); int t2=PeriodSeconds(tf2); int t3=PeriodSeconds(tf3);
if(p1<=0 && p2<=0 && p3<=0) return(PERIOD_CURRENT);
if(p1>0 && p2<=0 && p3<=0) return(tf1);
if(p2>0 && p1<=0 && p3<=0) return(tf2);
if(p3>0 && p1<=0 && p2<=0) return(tf3);
if(p1>0 && p2>0 && p3<=0) return((MathMin(t1,t2)==t1)?tf1:tf2);
if(p1>0 && p3>0 && p2<=0) return((MathMin(t1,t3)==t1)?tf1:tf3);
if(p2>0 && p3>0 && p1<=0) return((MathMin(t2,t3)==t2)?tf2:tf3);
if(p1>0 && p2>0 && p3>0)
{
ENUM_TIMEFRAMES tm=(int)MathMin(t1,t2)==t1?tf1:tf2;
int tmin=PeriodSeconds(tm);
return((int)MathMin(tmin,t3)==tmin?tm:tf3);
}
return(WRONG_VALUE);
}
指标数据获取与缓冲区管理
GetIndicatorsData()负责把各滤网均线值填入数组。关键点:使用ArraySetAsSeries(...,true)将索引倒序,使[0]为最新未完成柱、[1]为上一完结柱、[2]为再前一柱,符合顺势比较习惯。CopyBuffer若返回条数不足则打印错误并返回false,触发重连句柄逻辑。
NumberOfValues设为3,即只取最近三根柱,足够判断“当前值比前一柱高/低”。若某滤网Period<=0,则跳过对应拷贝,自然不参与信号计算。这种按开关懒加载的方式降低了资源占用。
bool GetIndicatorsData()
{
int NumberOfValues=3;
if((Screen01IndicatorPeriod>0 && Screen01IndicatorHandle==INVALID_HANDLE) ||
(Screen02IndicatorPeriod>0 && Screen02IndicatorHandle==INVALID_HANDLE) ||
(Screen03IndicatorPeriod>0 && Screen03IndicatorHandle==INVALID_HANDLE))
GetIndicatorHandles();
if(Screen01TimeFrame>0 && Screen01IndicatorHandle!=INVALID_HANDLE)
{
ArraySetAsSeries(indicator_buffer1,true);
if(CopyBuffer(Screen01IndicatorHandle,0,0,NumberOfValues,indicator_buffer1)<NumberOfValues)
{ Print("复制数值失败1"); return(false); }
}
// 同理处理 buffer2, buffer3 ...
return(true);
}
信号合成与交易触发逻辑
GetSignal()按启用滤网数量组合条件:单屏时仅看该屏均线方向;多屏时要求所有启用屏同向(如三屏均buffer[1]<buffer[2]才发SELL)。GetTradingSignal()再叠加仓位状态:无仓时直接跟信号;有仓时若反向信号且价格偏离足够(VolumeIncreaseStep*_Point)则加仓或反转。原文评论指出该实现用三根MA比较并非Elder原版“潮汐波浪涟漪”精神,但作为框架演示足够清晰。
ENUM_ORDER_TYPE GetSignal()
{
if(Screen01IndicatorPeriod>0 && Screen02IndicatorPeriod>0 && Screen03IndicatorPeriod>0)
{
if(indicator_buffer1[1]<indicator_buffer1[2] &&
indicator_buffer2[1]<indicator_buffer2[2] &&
indicator_buffer3[1]<indicator_buffer3[2])
return(ORDER_TYPE_SELL);
}
if(Screen01IndicatorPeriod>0 && Screen02IndicatorPeriod>0 && Screen03IndicatorPeriod>0)
{
if(indicator_buffer1[1]>indicator_buffer1[2] &&
indicator_buffer2[1]>indicator_buffer2[2] &&
indicator_buffer3[1]>indicator_buffer3[2])
return(ORDER_TYPE_BUY);
}
return(WRONG_VALUE);
}
初始化与资源释放
OnInit中依次:算最小周期→取句柄→初始化新柱→读仓位属性→画面板。OnDeinit在REASON_REMOVE时删除面板对象并IndicatorRelease三个句柄,防止内存泄漏与指标实例残留。这种成对管理是MQL5 EA规范的基本要求。
int OnInit()
{
MinimumTimeframe=GetMinimumTimeframe(Screen01TimeFrame,Screen01IndicatorPeriod,
Screen02TimeFrame,Screen02IndicatorPeriod,
Screen03TimeFrame,Screen03IndicatorPeriod);
GetIndicatorHandles(); CheckNewBar(); GetPositionProperties(P_ALL); SetInfoPanel();
return(0);
}
void OnDeinit(const int reason)
{
if(reason==REASON_REMOVE)
{
DeleteInfoPanel();
IndicatorRelease(Screen01IndicatorHandle);
IndicatorRelease(Screen02IndicatorHandle);
IndicatorRelease(Screen03IndicatorHandle);
}
}
回测优化与框架扩展
策略测试器需指定三个时间范围最小值(即EA自动算出的最小周期)作为测试精度。优化时作者人工定周期、让其他参数跑遗传或穷举,双核约30分钟。结果显示最大余额回撤小于最大采收率测试,说明框架可产出稳健曲线。附件elderthreescreens.set提供参数范本。
该框架价值不在策略本身优劣,而在“改信号区块即可换系统”的工程思路。您可把iMA换成iRSI、iMACD,或在GetSignal加成交量过滤;也可将Screen数组改为动态数组支持N屏。对于想批量验证想法的交易者,这是极好的起点。