编程失误让我痛失1.5万美元
ATC 2010选手亲述MQL5 EA错误与教训
访谈背景与主人公交易起点
Andrey Voitenko 是 2010 年自动化交易锦标赛(ATC 2010)的参赛选手,这是他第一次参加此类赛事,但其 Expert Advisor(EA)表现成熟,连续数周位列前十。在参加锦标赛之前,他于 2008 年通过朋友和网络广告接触交易,起初从事体力劳动,后来开户手动交易,经历了爆仓与迷茫。当时 MT4 流行,但他并未重视自动化,直到后来发现人工盯盘容易被干扰、难以严格执行系统,才开始学习 MQL4,并坦诚在 ATC 2010 之前并不完全相信自动化交易能稳定盈利。
他的经历对中文交易者的启示是:从手动转型程序化交易是普遍路径,但真正建立信心需要实盘或接近实盘的验证环境,例如锦标赛这样的公开实时账户。他也通过 MQL5 的 Jobs 服务接单开发 EA,认为该服务能借助第三方仲裁降低自由职业者收款欺诈风险,这对想靠 MQL5 技能变现的开发者具有参考意义。
EA 核心逻辑:水平通道突破策略
Andrey 的 EA 基于水平通道突破原理。具体做法是:先根据某段时间内五分钟 K 线的最高价与最低价计算通道上下轨;当价格深入通道内部时,在上下边界各挂一张挂单(限价或止损单);随后等待其中一张被触发,再通过对冲或平仓锁定利润或接受亏损。该系统在市场从低波动区转入高波动区时表现较好。
选择五分钟周期是他通过测试优化得出的,目的是让 EA 保持较高活跃度。之所以只用挂单而不直接市价成交,是因为挂单的价位清晰体现了通道逻辑,便于锦标赛中审查与追踪,也避免市价单在滑点下的不可控。这种“只做突破、不做回踩”的单纯逻辑,后来成为他人建议改进的基础,例如有人提议在通道较宽时增加边界 pivot 回踩交易。
- 使用 5 分钟 BAR 的 high/low 计算通道边界
- 价格进入通道内部后双向挂单
- 仅等待突破触发,不主动市价入场
- 优化周期选定 5 分钟以提升交易频率
致命错误:移动止损的条件写错
Andrey 在访谈中反复强调,他最大的失误在于 trailing stop(移动止损)的实现。原本并不是传统意义上的 bug,而是“非标准实现”:代码中的触发条件无效,导致止损被立刻拉近到市价附近,使仓位无法承受正常波动,利润来不及展开就被扫损。他后来修正了该部分,发布了第二版 EA,采用经典移动止损算法。
他估算,如果不是这个草率实验,当时账户余额本可增加 1.5 万美元,排名升至第三并有望获得现金奖励。也就是说,一个看似微小的逻辑错误,直接造成了巨额机会成本。对中文开发者而言,这提醒我们:止损模块必须经过历史回测与样本外验证,不能凭感觉在参赛或实盘前临时修改。
资金管理模块的取舍与风险
Andrey 的 EA 其实内置了简单的资金管理:按当前自由保证金的固定百分比计算手数。但在锦标赛中他故意关闭了该模块,从一开始就固定使用 5 手,理由是“慢而稳才能赢”并不适用于追求排名的竞赛。关闭后风险不再随资金浮动,变成固定手数偏激进的模式。
他指出,如果锦标赛启用资金管理并采用允许的最大手数,结果可能完全不同。这反映出竞赛与实盘的目标冲突:竞赛要短期高收益排名,实盘要回撤可控。中文交易者写 EA 时应明确用途,不要将赛用参数直接用于实盘,否则 50% 回撤(他锦标赛初期就遇到)在实盘可能致命。
回测、优化与曲线平滑的真相
赛前测试效果优于预期,因此他在截止前一个月提交后未改代码。优化 criterion 他选的是“最大余额”而非“最小回撤”,因为目标就是锦标赛排名。其权益曲线看似平滑,主要得益于及时获利了结的算法,而非刻意压低回撤。他的 EA 同时挂 SL 与 TP,但因 trailing stop 修改多次,TP 设得极远几乎不触发,实际靠移动止损出场。
约 50% 回撤发生在锦标赛初期余额较小时,他归因于 12 月 6 日价格懒散来回触碰通道边界、反复被扫损;换成第二版经典 trailing 后曲线明显更平滑。这说明:同一策略逻辑,出场模块差异能彻底改变风险特征。中文开发者优化时不能只看 profit factor,必须观察 equity drawdown 时间点。
社区反馈与后续演进方向
由于 EA 开源发布,网友提出多处改进:通道边界 pivot 回踩交易、参数绑定通道宽度而非固定值、更进阶的资本管理模块、以及最费力的“倾斜通道”替代水平通道。Andrey 表示愿做自然演进,未来版本仍免费放官网。他还看好多货币 EA,认为 Manov 的 EA 曲线优美、按最小回撤优化,若手数更大将难逢敌手。
往届冠军本届表现不佳,他认为除自身编程错误外,新晋者准备更充分、动机更强也是原因。对中文读者的价值是:EA 不是一劳永逸,市场如活物,需持续迭代;参与社区 Jobs 与讨论能暴露盲点。
可借鉴的 MQL5 代码思路(伪代码级)
虽然原文未给完整源码,但依据描述可反推关键片段结构。下面示例展示通道计算与挂单、经典 trailing 的骨架,帮助理解错误所在与正确写法。
// 计算 5分钟通道
int bars = iBars(_Symbol, PERIOD_M5);
double hi = iHigh(_Symbol, PERIOD_M5, iHighest(_Symbol, PERIOD_M5, MODE_HIGH, 20, 0));
double lo = iLow(_Symbol, PERIOD_M5, iLowest(_Symbol, PERIOD_M5, MODE_LOW, 20, 0));
// 错误写法:条件无效导致 SL 立刻贴近
// double sl = price - 1*Point; // bug: 未考虑 stops level
// 正确经典 trailing
if(position_profit > 0){
double new_sl = SymbolInfoDouble(_Symbol, SYMBOL_BID) - TrailDist*Point;
if(new_sl > position_sl) trade.PositionModify(ticket, new_sl, TP);
}
给中文 MQL5 开发者的实操清单
总结 Andrey 的教训,我们可落地为开发规范:第一,任何出场逻辑改动必须先走样本外测试;第二,止损距离必须校验 SYMBOL_TRADE_STOPS_LEVEL;第三,锦标赛与实盘参数分离;第四,优化目标依用途定,但都要监控 drawdown;第五,挂单策略需明确边界意义;第六,开源获反馈但需自担误用风险。
- 修改 trailing 前用历史数据重跑
- 用 SymbolInfoInteger 获取最小止损层级
- MM 模块用 input bool 开关
- 优化报告导出 equity 图人工看谷值
- 将 EA 逻辑写注释防后续误改
- 参与 ATC 类竞赛积累实时验证