使用面向对象的方法来编写EA的模式(基础篇)
「用类封装把EA骨架搭起来」
在 MT5 里写 EA,最怕把所有逻辑塞进 OnTick 一个函数里,改一处崩一片。把订单管理、信号判断、风控分别封装成类,能让策略模块各自独立,后期调参或换信号源都只用动对应类。 MQL5 自 2010 年随 MetaTrader 5 发布就完整支持面向对象,类、继承、多态都能用。下面这段骨架演示了如何用一个 CTrade 派生类接管下单动作,比直接调 OrderSend 更不容易写出脏代码。 实盘里类封装不会直接提升胜率,但能显著降低因逻辑耦合导致的误操作概率;外汇与贵金属杠杆高,任何下单路径的隐藏 bug 都可能放大亏损,隔离职责是基本功而非花活。
class MyTrade : class="kw">public CTrade { class="kw">public: class="type">bool OpenBuy(class="type">class="kw">double lot, class="type">class="kw">string sym) { class="kw">return Buy(lot, sym); } };
◍ EA开发阶段与运行模式的分工
做 MT5 的 EA,先把开发拆成几个阶段看会更顺:需求定义、逻辑原型、运行模式选型、类结构实现。不同阶段用不同编程模式去落地,效率比一把梭写到底要高。 MetaTrader 5 里 EA 的运行模式不止一种,每种都有自己的实现路径和适用场景,后面会逐一拆开。把模式想清楚再写代码,能少走很多弯路,尤其在涉及外汇、贵金属这类高杠杆品种时,逻辑清晰才有办法控制风险。 本文会先理清 EA 开发的阶段组成,再讲 MT5 中 EA 的运行模式及辅助用法,最后用分层类实现把前面的想法落到代码上。你可以边读边开 MT5 的 MetaEditor 对照建工程。
EA 开发不是写段代码就完事
做一套 MT5 自动交易系统,真正吃重的是算法实现与回测验证,而不是界面或参数面板。原文把流程拆成多阶段,其中第五阶段才叫“算法交易”,需要开发者、程序员、分析师协作;若一个人包揽全部角色,才可能走通这条路径,这类人通常就是程序化交易者。 这套流程本身是可迭代的:每一轮开发周期都能对 EA 做修正和扩展,这比一次性写死逻辑更重要。外汇与贵金属品种波动受消息面冲击大,EA 在实盘前必须意识到高风险,任何阶段结论都只是“可能盈利”而非确定。 各阶段要用的工具与知识并不重叠,原文给出一个变量矩阵说明开发者常面对的简化选择维度。只有用高质量代码把已验证盈利的逻辑落地,才够得上第五阶段的标准;低质量实现即便碰巧赚钱,也难称算法交易。
「EA在MT5里的七种运行身份」
MT5 里的 EA 并非只有「挂上图表跑实盘」这一种状态。按程序文件分,有需要源文件+ex5 的调试/分析类,也有仅吃 ex5 的发布类;按数据分,策略测试器里的 6 种全跑历史数据,只有发布模式直面真实报价。成型的可交易程序(*.ex5)应在发布模式运行,其余模式留给测试与开发。 具体拆开看,MQL5 定义了 7 种执行模式:发布(0)、调试(1)、分析(2)、测试(3)、优化(4)、可视化(5)、时间框架集结(6)。除发布外,调试可用 IS_DEBUG_MODE 宏或 MQL_DEBUG 标识,分析用 IS_PROFILE_MODE 或 MQL_PROFILER,测试/优化/可视化/帧模式都没有宏,只能靠 MQLInfoInteger() 配合对应标识符判断。 一个容易踩的坑:优化模式开启时测试模式默认也开;可视化选中时日志会同时标出标准测试。时间框架集结模式更特殊,若在 OnInit() 里查,标识为 0,必须放进 OnTesterInit() / OnTesterPass() / OnTesterDeinit() 才能被 MQL_FRAME_MODE 抓到——我本地代理日志路径是 %Program Files\MetaTrader5\tester\Agent-127.0.0.1-3000\logs,可供你对照。 别把正态当圣经 发布模式下其他所有模式标识均为 0,所以函数里先排除调试/分析再判测试,顺序错了就会把优化误报成纯测试。写 CheckMqlModes() 塞进 OnInit() 跑一遍,日志打印的 yes/no 就是你当前 EA 的真实身份。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| MQL 模式 | class=class="str">"cmt">//+------------------------------------------------------------------+ enum ENUM_MQL_MODE { MQL_MODE_RELEASE=class="num">0, class=class="str">"cmt">// 发布 MQL_MODE_DEBUG=class="num">1, class=class="str">"cmt">// 调试 MQL_MODE_PROFILER=class="num">2, class=class="str">"cmt">// 分析 MQL_MODE_TESTER=class="num">3, class=class="str">"cmt">// 测试 MQL_MODE_OPTIMIZATION=class="num">4, class=class="str">"cmt">// 优化 MQL_MODE_VISUAL=class="num">5, class=class="str">"cmt">// 可视化测试 MQL_MODE_FRAME=class="num">6, class=class="str">"cmt">// 时间框架集结 }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 检验所有MQL程序模式 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CheckMqlModes(class="type">void) { class=class="str">"cmt">//--- 如果是调试模式 if(MQLInfoInteger(MQL_DEBUG)) Print("Debug mode: yes"); else Print("Debug mode: no"); class=class="str">"cmt">//--- 如果是分析模式 if(MQLInfoInteger(MQL_PROFILER)) Print("Profile mode: yes"); else Print("Profile mode: no"); class=class="str">"cmt">//--- 如果是测试模式 if(MQLInfoInteger(MQL_TESTER)) Print("Tester mode: yes"); else Print("Tester mode: no"); class=class="str">"cmt">//--- 如果是优化模式 if(MQLInfoInteger(MQL_OPTIMIZATION)) Print("Optimization mode: yes"); else Print("Optimization mode: no"); class=class="str">"cmt">//--- 如果是可视化测试模式
◍ 用 MQLInfoInteger 区分 EA 运行环境
EA 在 MT5 里跑,不一定是你以为的那个环境。发布版、调试版、性能剖析版,以及是否在回测器、优化器、可视化模式下,行为可能完全不同,靠肉眼看图表判断容易误事。 下面这段直接在 OnStart 里调用 MQLInfoInteger 探环境,能打印当前到底是哪种模式: if(MQLInfoInteger(MQL_VISUAL_MODE)) Print("Visual mode: yes"); else Print("Visual mode: no"); //--- 如果是时间框架聚集的优化结果模式 if(MQLInfoInteger(MQL_FRAME_MODE)) Print("Frame mode: yes"); else Print("Frame mode: no"); 逐行拆:第1行用 MQL_VISUAL_MODE 查是否带可视化(图表上肉眼可见 K 线走动);第3行否就打印 no。第6行 MQL_FRAME_MODE 查是否处于多周期框架聚合的优化结果模式;第8行对应打印。 实跑日志里能看到明确分界:17:20:38 那次 Current mode 是 MQL_MODE_RELEASE,Debug/Profile/Tester/Optimization/Visual/Frame 全为 no;切到 17:27:47 变成 MQL_MODE_DEBUG,仅 Debug mode 翻成 yes,其余仍 no。外汇与贵金属杠杆高,环境误判可能导致实盘逻辑和回测不一致,开 MT5 把这段贴进脚本跑一遍,确认你本地当前是哪一种。
if(MQLInfoInteger(MQL_VISUAL_MODE)) Print("Visual mode: yes"); else Print("Visual mode: no"); class=class="str">"cmt">//--- 如果是时间框架聚集的优化结果模式 if(MQLInfoInteger(MQL_FRAME_MODE)) Print("Frame mode: yes"); else Print("Frame mode: no");