如何快速创建一个"2010 年自动交易锦标赛"专用"EA 交易"·进阶篇
用 MACD 加轨道线写信号判定函数
下面这段是 EA 里负责出信号的 CheckSignal 函数骨架,把 MACD 与轨道线(Envelope)叠在一起过滤假突破。逻辑不复杂:最近三根 K 线 + 四个 MACD 值 + 上下轨各两个值,凑齐才判断。 函数先拉最近 3 个柱形的价格到 rt[3],若 CopyRates 返回值不是 3,直接打日志并返回 WRONG_VALUE,说明历史数据没加载全。指标方面分别用 CopyBuffer 取 MACD 主缓冲 4 个点、上轨 2 个点、下轨 2 个点,任意一项不足就退出。 买的条件是:前一根(索引 1)开盘低于下轨、收盘站回下轨上方,且 MACD 连续三根走低(macd[1]<macd[2]<macd[3])。卖则反过来,前一根开盘高于上轨、收盘跌回上轨下,MACD 连续三根走高。注意函数靠 bEntry 参数决定返回买还是卖,用于区分开仓与平仓反手。 开 MT5 把这段代码塞进你的 EA 类里,先把 m_hmacd 和 m_henvel 两个句柄在初始化时绑好,否则 CopyBuffer 永远拿不到数。外汇和贵金属波动大,这套组合只过滤信号、不保胜率,实盘前请用历史数据回测验证。
class="type">long CMyEA::CheckSignal(class="type">bool bEntry) { class="type">class="kw">double macd[class="num">4], class=class="str">"cmt">// MACD 指标值数组 env1[class="num">3], class=class="str">"cmt">// 轨道线上轨值数组 env2[class="num">3]; class=class="str">"cmt">// 轨道线下轨值数组 class="type">MqlRates rt[class="num">3]; class=class="str">"cmt">// 最近3个柱形的价格数组 if(CopyRates(m_smb,m_tf,class="num">0,class="num">3,rt)!=class="num">3) class=class="str">"cmt">// 将最近3个柱形的价格值复制到数组中 { Print("CopyRates ",m_smb," history is not loaded"); class="kw">return(WRONG_VALUE); } class=class="str">"cmt">// 将指标值复制到数组 if(CopyBuffer(m_hmacd,class="num">0,class="num">0,class="num">4,macd)<class="num">4 || CopyBuffer(m_henvel,class="num">0,class="num">0,class="num">2,env1)<class="num">2 ||CopyBuffer(m_henvel,class="num">1,class="num">0,class="num">2,env2)<class="num">2) { Print("CopyBuffer - no data"); class="kw">return(WRONG_VALUE); } class=class="str">"cmt">// 如果MACD在增长并且价格从轨道线下轨反弹,买入 if(rt[class="num">1].open<env2[class="num">1] && rt[class="num">1].close>env2[class="num">1] && macd[class="num">1]<macd[class="num">2] && macd[class="num">2]<macd[class="num">3]) class="kw">return(bEntry ?ORDER_TYPE_BUY:ORDER_TYPE_SELL); class=class="str">"cmt">// 买入的条件 class=class="str">"cmt">// 如果MACD在下降并且价格从轨道线上轨向下反弹,卖出 if(rt[class="num">1].open>env1[class="num">1] && rt[class="num">2].close<env1[class="num">1]&& macd[class="num">1]>macd[class="num">2] && macd[class="num">2]>macd[class="num">3]) class="kw">return(bEntry ?ORDER_TYPE_SELL:ORDER_TYPE_BUY); class=class="str">"cmt">// 卖出条件 class="kw">return(WRONG_VALUE); class=class="str">"cmt">// 如果没有信号 } CMyEA ea; class=class="str">"cmt">// 类的实例
「EURUSD 去年回测先跑一版」
在 MT5 策略测试器里把周期设为「去年」、品种选 EURUSD,直接加载带初始化参数的 EA 跑一轮。出来的净值曲线见图 3,整体形态偏平、回撤控制也一般,属于一眼能看出没调透的状态。 这种初版结果其实很正常——初始化参数是写死的经验值,没碰过止损止盈的边界。下一步就该把 Stop Loss 和 Take Profit 丢进优化器扫区间,而不是靠肉眼拍脑袋定。 外汇品种高波动,EURUSD 虽流动性好但去年因子结构有切换,未经优化的 SL/TP 组合在样本外大概率衰减,优化时记得留 out-of-sample 段验证。
◍ 参数寻优让系统从盈亏边缘翻正
在 MT5 策略测试器里,先用 10–500 区间、50 步幅扫 Stop Loss 与 Take Profit。跑完最优解是 SL=160、TP=310:盈利交易占比从调之前的 36% 抬到 67%,净利润 1522.97,系统由亏转平甚至微盈。 接着把轨道线周期按 4 的步幅从 10 拉到 40,方差 0.1 到 1、步幅 0.1 一起寻优。最佳组合周期=22、方差=0.3,净利润冲到 14418.92,盈利交易比 79%。 再把仓位风险系数顶到 0.8,净利润进一步显示 77330.95。外汇与贵金属属高风险品种,这类回测数字只是历史样本,实盘参数漂移可能导致结果显著偏离,上真金前务必用 MT5 重跑一遍验证。
把轨道系统调出四种变体
优化一套基于轨道线的 EA,不必推倒重来。最实际的四个切口是:换趋势指标、换轨道线类型、换时间表、改交易逻辑。外汇与贵金属杠杆高,任何改动都先在策略测试器跑一遍再上实盘,回测不代表未来概率。 趋势指标方面,移动平均线和其“扇形”排列往往比 MACD 更直白。把 MACD 换成简单 MA 后,随附的 Macena.mq5 可直接编译验证。轨道线也不限 Envelopes,Price Channel、Bollinger Bands 或自建 MA 轨道都行,Maboll.mq5 就是 MA 加布林带的样本。 时间表一行就能切:H4 看大周期过滤噪音,M15 抓小周期频次,改 PERIOD_H1 / H4 / M15 这三个常量之一即可。下面这段是轨道线创建与信号检查的核代码片段,注意 CheckSignal 里只取了最近 3 根柱形,rt[1] 代表倒数第二根——这是信号触发锚点。 m_henvel=iEnvelopes(m_smb,PERIOD_H1,m_EnvelPer,0,MODE_SMA,PRICE_CLOSE,m_EnvelDev); // 创建H1轨道线指标 m_henvel=iEnvelopes(m_smb,PERIOD_H4,m_EnvelPer,0,MODE_SMA,PRICE_CLOSE,m_EnvelDev); // 创建H4轨道线指标 m_henvel=iEnvelopes(m_smb,PERIOD_M15,m_EnvelPer,0,MODE_SMA,PRICE_CLOSE,m_EnvelDev); // 创建M15轨道线指标 long CMyEA::CheckSignal(bool bEntry) { double env1[3], // 轨道线上轨值数组 env2[3]; // 轨道线下轨值数组 MqlRates rt[3]; // 最近3个柱形的价格数组 if(CopyRates(m_smb,m_tf,0,3,rt)!=3) // 将最近3个柱形的价格值复制到数组中 { Print("CopyRates ",m_smb," history is not loaded"); return(WRONG_VALUE); } // 将指标值复制到数组
| if(CopyBuffer(m_henvel,0,0,2,env1)<2 | CopyBuffer(m_henvel,1,0,2,env2)<2) |
|---|
{ Print("CopyBuffer - no data"); return(WRONG_VALUE); } // 如果价格从轨道线下轨反弹,买入 if(rt[1].open<env2[1] && rt[1].close>env2[1]) return(bEntry ?ORDER_TYPE_BUY:ORDER_TYPE_SELL); // 买入的条件 // 如果价格从轨道线上轨向下反弹,卖出 if(rt[1].open>env1[1] && rt[2].close<env1[1]) return(bEntry ?ORDER_TYPE_SELL:ORDER_TYPE_BUY); // 卖出条件 return(WRONG_VALUE); // 如果没有信号 } CMyEA ea; // 类的实例 反向逻辑也能加:下轨反弹买、上轨反弹卖,再把日趋势过滤注释掉,系统就变成纯均值回归。代码里 rt[1].open<env2[1] && rt[1].close>env2[1] 就是下轨刺透收上的触发,把 ORDER_TYPE 对错调转就能反着跑。 别把正态当圣经 轨道线标准差假设价格服从正态分布,但贵金属跳空和外汇非农行情常偏离,M15 上轨穿透后继续冲高概率不低,反向单要配硬止损。
m_henvel=iEnvelopes(m_smb,PERIOD_H1,m_EnvelPer,class="num">0,MODE_SMA,PRICE_CLOSE,m_EnvelDev); class=class="str">"cmt">// 创建轨道线指标 m_henvel=iEnvelopes(m_smb,PERIOD_H4,m_EnvelPer,class="num">0,MODE_SMA,PRICE_CLOSE,m_EnvelDev); class=class="str">"cmt">// 创建轨道线指标 m_henvel=iEnvelopes(m_smb,PERIOD_M15,m_EnvelPer,class="num">0,MODE_SMA,PRICE_CLOSE,m_EnvelDev); class=class="str">"cmt">// 创建轨道线指标 class=class="str">"cmt">//------------------------------------------------------------------ 检查信号 class="type">long CMyEA::CheckSignal(class="type">bool bEntry) { class="type">class="kw">double env1[class="num">3], class=class="str">"cmt">// 轨道线上轨值数组 env2[class="num">3]; class=class="str">"cmt">// 轨道线下轨值数组 class="type">MqlRates rt[class="num">3]; class=class="str">"cmt">// 最近3个柱形的价格数组 if(CopyRates(m_smb,m_tf,class="num">0,class="num">3,rt)!=class="num">3) class=class="str">"cmt">// 将最近3个柱形的价格值复制到数组中 { Print("CopyRates ",m_smb," history is not loaded"); class="kw">return(WRONG_VALUE); } class=class="str">"cmt">// 将指标值复制到数组 if(CopyBuffer(m_henvel,class="num">0,class="num">0,class="num">2,env1)<class="num">2 || CopyBuffer(m_henvel,class="num">1,class="num">0,class="num">2,env2)<class="num">2) { Print("CopyBuffer - no data"); class="kw">return(WRONG_VALUE); } class=class="str">"cmt">// 如果MACD在增长并且价格从轨道线下轨反弹,买入 if(rt[class="num">1].open<env2[class="num">1] && rt[class="num">1].close>env2[class="num">1]) class="kw">return(bEntry ?ORDER_TYPE_BUY:ORDER_TYPE_SELL); class=class="str">"cmt">// 买入的条件 class=class="str">"cmt">// 如果MACD在下降并且价格从轨道线上轨向下反弹,卖出 if(rt[class="num">1].open>env1[class="num">1] && rt[class="num">2].close<env1[class="num">1]) class="kw">return(bEntry ?ORDER_TYPE_SELL:ORDER_TYPE_BUY); class=class="str">"cmt">// 卖出条件 class="kw">return(WRONG_VALUE); class=class="str">"cmt">// 如果没有信号 } CMyEA ea; class=class="str">"cmt">// 类的实例 class=class="str">"cmt">//------------------------------------------------------------------ OnInit
「三个月滚动窗口下的EA表现分化」
把同一套 EA 放到去年(2010)做滚动测试,每次固定 3 个月窗口、按月平移,能直接看出资金曲线对起步时点的敏感程度。外汇与贵金属属高风险品种,这种敏感性本身就意味着实盘前必须压低仓位。 实测数据摆出来:1 月 1 日起步的那格盈利 7239.50 美元、胜率 76.92%;但 2 月起步直接亏 6577.50 美元、胜率挂零。最极端的是 6 月 1 日—8 月 30 日那段,净利润 69835.50 美元、胜率 85%,和前面亏损段完全是两个物种。 结论很硬:把这种激进取法(重仓鞅式加仓类逻辑)绑在 EA 上,样本内就已经剧烈摆动,实盘大概率扛不住连续零胜率月。与其调策略,不如先把风险敞口砍下来,用 MT5 策略测试器把间隔改成 1 个月、周期缩到 3 个月自己跑一遍对照。
◍ 把模板跑成自己的系统
这套以最少代码搭出来的 EA 骨架,核心价值在于让你用几天而非几个月把交易想法落到 MT5 里。把 mabol.mq5、macena.mq5 那几个 11KB 上下的源文件丢进 MQL5/Experts 编译,就能直接看信号逻辑怎么跑。 想在实盘前压住过拟合,得把参数放到更长周期——比如把默认 2010 年锦标赛那类短样本换成跨三到五年的 H1 回测,再让优化器扫一遍均线周期与阈值。外汇和贵金属杠杆高,回测漂亮不代表活盘不爆仓,参数漂移到负期望只是时间问题。 有读者当年拿本文原型加了两个函数就跑出不错曲线,但官方从不对这些代码的实盘后果背书。你真要验证,就开 MT5 把 ZIP 里的 expertadvisor__5.mqh 先include 通,再改一处手数规则看净值曲线怎么歪。