使用面向对象的方法来编写EA的模式·进阶篇
(2/3)· 多数交易者只跑过发布模式,却不知调试、分析、测试各有独立通道,错配就白干
EA 运行模式在日志里怎么认
在 MT5 里跑 EA,终端日志会用两字母代号把当前运行环境打印出来。上面这段是 EURUSD.e H1 周期下 Test1_Modes_EA 的真实日志切片,初始化阶段(17:30:53.879)显示 Profile mode: yes、Tester mode: no、Optimization mode: no,说明那时只是加载进图表、还没进回测。 等到 17:35:25.397 Core 1 开始跑 2014.11.03 行情,日志里 ER 行出现 Tester mode: yes、EG 行 Profile mode: no,同时 EG 标注 Current mode: MQL_MODE_TESTER——这意味着 EA 已切到单遍回测态,不是优化也不是可视化。 17:48:14.010 又冒出 OH 行 Current mode: MQL_MODE_OPTIMIZATION,此时整段再无 Tester mode: yes,说明同一 EA 被丢进参数优化池了。外汇与贵金属品种波动剧烈、杠杆风险高,这类日志只是帮你确认 EA 到底在哪个沙箱里跑,别把它当信号依据。 实操上,你开 MT5 策略测试器挂个空 EA,把这段两字母前缀(GE/QM/CE/ER/OH 等)对照看,就能在数秒内判断当前是加载、回测还是优化,不用等结果出来才反应过来配错了模式。
「回测与可视化下的运行模式差异」
在 MT5 策略测试器中跑 EA,不同启动方式会直接改变 MQL5 环境报告的运行时模式。上面这组日志来自同一个 Test1_Modes_EA 在 EURUSD.e H1 上的两次执行,时间锚点分别是 17:48:14 与 17:53:51,间隔约 5 分 37 秒。 第一次执行(17:48:14.010)显示 Tester mode: yes、Optimization mode: yes、Visual mode: no,说明那一轮是在优化模式下跑的,没有开图表可视化。第二次执行(17:53:51.485)则变成 Optimization mode: no、Visual mode: yes,且 Current mode 明确打出 MQL_MODE_VISUAL,表明同一 EA 切到单遍回测带可视化后就脱离了优化态。 Profile mode 与 Frame mode 在两轮里都稳定为 no,Debug mode 也始终是 no,这意味着这套测试没有挂性能采样也没有走框架容器。做外汇或贵金属回测时,这种模式下结论的泛化性偏弱,杠杆与滑点假设若失真,实盘倾向出现更大偏差,属典型高风险验证环境。 开 MT5 自己挂这个 EA 跑一遍,对比优化勾选与否的日志输出,就能确认你本地终端的报告字段是否跟这套一致。
◍ 从日志抓 EA 的运行模式切换
在 MT5 策略测试器跑 EURUSD.e 的 H1 周期时,EA 启动首行日志就暴露了环境身份。2014.11.03 00:00:00 的那批记录里,Tester mode 与 Optimization mode 都是 yes,Visual mode 为 no,说明这趟是纯后台优化而非可视化回测。 约 5 分 17 秒后(18:04:27.663),同一 EA 打出第二组状态:Tester mode 变 no、Optimization mode 变 no,而 Frame mode 翻成 yes。这个反转意味着 EA 从测试器环境切到了图表帧模式,常发生在实盘附加或帧函数被调用时。 外汇与贵金属品种的高杠杆特性会放大这类模式误判——若代码里按 Tester 模式写死了点差假设,切到 Frame 模式后可能直接采到实时点差,回测与实盘偏差会显著拉大。 用 MqlMode() 函数可在运行时自诊当前模式,避免逻辑分支踩空。下面这段是模式判定的骨架,核心是先给 curr_mode 赋 WRONG_VALUE 再做环境探测。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 确定当前MQL模式 class=class="str">"cmt">//+------------------------------------------------------------------+ ENUM_MQL_MODE MqlMode(class="type">void) { ENUM_MQL_MODE curr_mode=WRONG_VALUE;
用 MQLInfoInteger 摸清 EA 当前运行环境
写 EA 最怕一件事:同一段代码在回测里跑得好好的,挂实盘就行为异常。根子往往在运行模式不同——调试、分析、可视化测试、优化、普通测试、框架聚合、发布,MT5 给每种环境都留了独立标识。
下面这段逻辑就是靠 MQLInfoInteger 挨个问终端“现在是什么模式”,把结果写进 curr_mode 再返回。优先级从上往下:调试 > 分析 > 可视化 > 优化 > 测试 > 框架聚合,全都不是就归到发布模式。
[CODE]
//--- 如果是调试模式
if(MQLInfoInteger(MQL_DEBUG))
curr_mode=MQL_MODE_DEBUG;
//--- 如果是分析模式
else if(MQLInfoInteger(MQL_PROFILER))
curr_mode=MQL_MODE_PROFILER;
//--- 如果是可视化测试模式
else if(MQLInfoInteger(MQL_VISUAL_MODE))
curr_mode=MQL_MODE_VISUAL;
//--- 如果是优化模式
else if(MQLInfoInteger(MQL_OPTIMIZATION))
curr_mode=MQL_MODE_OPTIMIZATION;
//--- 如果是测试模式
else if(MQLInfoInteger(MQL_TESTER))
curr_mode=MQL_MODE_TESTER;
//--- 如果是时间框架聚集的优化结果模式
else if(MQLInfoInteger(MQL_FRAME_MODE))
curr_mode=MQL_MODE_FRAME;
//--- 如果是发布模式
else
curr_mode=MQL_MODE_RELEASE;
//---
return curr_mode;
}
[/CODE]
逐行看:MQL_DEBUG 为真说明你在 MetaEditor 里点了调试;MQL_PROFILER 对应性能分析器开启;MQL_VISUAL_MODE 是策略测试器勾了“可视化”;MQL_OPTIMIZATION 在跑参数优化时为真;MQL_TESTER 是普通回测;MQL_FRAME_MODE 只在多周期优化结果聚合时出现;都不满足就是实盘发布态。
日志里那行 Current mode: MQL_MODE_PROFILER 是实际跑出来的:EURUSD.e H1 周期下,EA 启动时探到终端正处于分析器模式。外汇与贵金属杠杆高、滑点跳空频繁,实盘和测试环境成交机制不同,靠这个探测把 Print 调试、文件写盘之类重操作只在非实盘模式启用,能少踩很多坑。
class=class="str">"cmt">//--- 如果是调试模式 if(MQLInfoInteger(MQL_DEBUG)) curr_mode=MQL_MODE_DEBUG; class=class="str">"cmt">//--- 如果是分析模式 else if(MQLInfoInteger(MQL_PROFILER)) curr_mode=MQL_MODE_PROFILER; class=class="str">"cmt">//--- 如果是可视化测试模式 else if(MQLInfoInteger(MQL_VISUAL_MODE)) curr_mode=MQL_MODE_VISUAL; class=class="str">"cmt">//--- 如果是优化模式 else if(MQLInfoInteger(MQL_OPTIMIZATION)) curr_mode=MQL_MODE_OPTIMIZATION; class=class="str">"cmt">//--- 如果是测试模式 else if(MQLInfoInteger(MQL_TESTER)) curr_mode=MQL_MODE_TESTER; class=class="str">"cmt">//--- 如果是时间框架聚集的优化结果模式 else if(MQLInfoInteger(MQL_FRAME_MODE)) curr_mode=MQL_MODE_FRAME; class=class="str">"cmt">//--- 如果是发布模式 else curr_mode=MQL_MODE_RELEASE; class=class="str">"cmt">//--- class="kw">return curr_mode; }
「把EA拆成多模式跑,而不是写死一套逻辑」
做EA别只盯最终上线的那一种状态。开发期要调代码、回测期要跑策略测试器的各种模式、实盘才是发布模式,这几段需求差异很大,硬塞在一个流程里只会越改越乱。用面向对象把核心算法锁在基类,不同模式派生不同子类,主逻辑不变、表现随模式切换,是更省心的做法。 基础类 CModeBase 收拢所有通用对象,往下分 CModeRelease(调试父类)和 CModeTester(历史回测)。交易想法很简单:无持仓看信号开仓,有持仓看信号平仓并挂滑动止损;新柱触发,MACD主线在负值区上穿信号线为买、正值区下穿为卖,反向信号或止损离场。外汇和贵金属波动剧烈,这类信号只是概率倾向,实盘前必须用历史数据验过。 基类里 Main()、Open()、Close()、Trail() 是虚方法,基类留空,具体怎么动看作运行模式;CheckOpenSignal() 等信号方法对所有模式共用。下面这段是基类骨架,MACD 数据设成私有,继承类拿不到,要用的东西得在基类先引好。 [CODE] //+------------------------------------------------------------------+
| // | CModeBase 类 |
|---|---|
| // | 目的: MQL-模式基类 |
//+------------------------------------------------------------------+ class CModeBase { //--- === 数据成员 === --- private: //--- macd对象 CiMACD m_macd_obj; double m_macd_main_vals[2]; double m_macd_sig_vals[2]; protected: long m_pos_id; bool m_is_new_bar; uint m_trailing_stop; uint m_trail_step; //--- 交易对象 CSymbolInfo m_symbol_info; CTrade m_trade; CPositionInfo m_pos_info; CDealInfo m_deal_info; //--- mql 模式 ENUM_MQL_MODE m_mql_mode; //--- 新柱形对象 CisNewBar m_new_bar; //--- 当前tick标识 bool m_is_curr_tick_signal; //--- 平仓订单类型 ENUM_ORDER_TYPE m_close_ord_type; //--- === 方法 === --- public: //--- 构造函数/析构函数 void CModeBase(); void ~CModeBase(void){}; //--- 初始化 virtual bool Init(int _fast_ema,int slow_ema,int _sig,ENUM_APPLIED_PRICE _app_price); virtual void Deinit(void){}; //--- 模块 逐行拆解:class CModeBase 定义模式基类;private 里的 CiMACD 及两个长度为2的 double 数组存 MACD 主线与信号线最近两值,但私有化导致子类不能直接复用。protected 暴露持仓ID、新柱标志、滑止损参数、交易与品种信息对象、当前MQL模式枚举等,供派生类共享。public 的构造析构和虚 Init() 留接口,Deinit() 空实现,具体模式再去填肉。 常规测试模式 CModeTester 编译后第一步就是丢进策略测试器。重点不是盈利,是看信号有没有被准确执行。日志里打印开平仓信号和指标值字符串,注意信息在“日志”标签不在“智能交易”标签,量大的话搜关键字或写文件分离。可视化模式 CModeVisual 另开图层:红垂直线标入场、蓝垂直线标出场,MACD子窗口用浅蓝矩形填多单区间、粉矩形填空单区间,无持仓不填色;图表子窗口缩放后矩形高度得重算坐标,不然对不齐。两类样例分别落在 TestMode_tester.mq5 与 TestMode_visual_tester.mq5,开 MT5 直接挂策略测试器就能对照看图。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| CModeBase 类 | class=class="str">"cmt">//| 目的: MQL-模式基类 | class=class="str">"cmt">//+------------------------------------------------------------------+ class CModeBase { class=class="str">"cmt">//--- === 数据成员 === --- class="kw">private: class=class="str">"cmt">//--- macd对象 CiMACD m_macd_obj; class="type">class="kw">double m_macd_main_vals[class="num">2]; class="type">class="kw">double m_macd_sig_vals[class="num">2]; class="kw">protected: class="type">long m_pos_id; class="type">bool m_is_new_bar; class="type">uint m_trailing_stop; class="type">uint m_trail_step; class=class="str">"cmt">//--- 交易对象 CSymbolInfo m_symbol_info; CTrade m_trade; CPositionInfo m_pos_info; CDealInfo m_deal_info; class=class="str">"cmt">//--- mql 模式 ENUM_MQL_MODE m_mql_mode; class=class="str">"cmt">//--- 新柱形对象 CisNewBar m_new_bar; class=class="str">"cmt">//--- 当前tick标识 class="type">bool m_is_curr_tick_signal; class=class="str">"cmt">//--- 平仓订单类型 ENUM_ORDER_TYPE m_close_ord_type; class=class="str">"cmt">//--- === 方法 === --- class="kw">public: class=class="str">"cmt">//--- 构造函数/析构函数 class="type">void CModeBase(); class="type">void ~CModeBase(class="type">void){}; class=class="str">"cmt">//--- 初始化 class="kw">virtual class="type">bool Init(class="type">int _fast_ema,class="type">int slow_ema,class="type">int _sig,ENUM_APPLIED_PRICE _app_price); class="kw">virtual class="type">void Deinit(class="type">void){}; class=class="str">"cmt">//--- 模块