连续前行优化 (第三部分): 将机器人适配为自动优化器·综合运用
(3/3)·前两篇的DLL与报告下载对象如何咬合,本文给出可直接套用的交互模板
很多交易者拿到前两篇的DLL和下载类却卡在衔接处,以为要重写整套逻辑。其实只需一个包装层把历史记录转成XML,老EA就能挂上自动优化器。本文收尾整个系列,把这座桥一次搭通。
回测里自动落盘成交历史
在 MT5 策略测试器或优化模式下跑 EA,往往只看到最终权益曲线,原始逐笔成交却难以导出复用。下面这段类把回测区间的起止时间抓住,并在析构时调用历史写入器把数据落盘。 CAutoUploader 在 OnTick 里先判断 MQLInfoInteger(MQL_OPTIMIZATION)==1 或 MQLInfoInteger(MQL_TESTER)==1,即当前处于优化或回测。若 From 为 0,则用 iTime(_Symbol,PERIOD_M1,0) 取 M1 最新 K 线时间赋给 From,之后每次 Tick 都把 Till 刷新成当前 M1 时间,这样能框住回测实际覆盖的时段。 析构函数中同样判断回测/优化环境,构造 CXmlHistoryWriter 并调用 Write(params,From,Till)。这意味着你只要在 Test.mq5 里 #include <History manager/AutoLoader.mqh> 并挂上这个类,回测结束会自动写出 XML 历史,不用手工导报表。 实盘环境(非回测非优化)下 OnTick 与析构均跳过写盘逻辑,避免无意义 IO。外汇与贵金属杠杆品种回测仅具概率参考,真实点差与滑点可能显著偏离。
class="macro">#include <History manager/XmlHistoryWriter.mqh> class CAutoUploader { class="kw">private: class="type">class="kw">datetime From,Till; CCCM *comission_manager; BotParams params[]; class="type">class="kw">string mutexName; class="kw">public: CAutoUploader(CCCM *comission_manager, class="type">class="kw">string mutexName, BotParams ¶ms[]); class="kw">virtual ~CAutoUploader(class="type">void); class="kw">virtual class="type">void OnTick(); }; class="type">void CAutoUploader::OnTick(class="type">void) { if(MQLInfoInteger(MQL_OPTIMIZATION)==class="num">1 || MQLInfoInteger(MQL_TESTER)==class="num">1) { if(From == class="num">0) From = iTime(_Symbol,PERIOD_M1,class="num">0); Till=iTime(_Symbol,PERIOD_M1,class="num">0); } } CAutoUploader::CAutoUploader(CCCM *_comission_manager,class="type">class="kw">string _mutexName,BotParams &_params[]) : comission_manager(_comission_manager), mutexName(_mutexName) { CopyBotParams(params,_params); } CAutoUploader::~CAutoUploader(class="type">void) { if(MQLInfoInteger(MQL_OPTIMIZATION)==class="num">1 || MQLInfoInteger(MQL_TESTER)==class="num">1) { CXmlHistoryWriter historyWriter(mutexName, comission_manager); historyWriter.Write(params,From,Till); } }
◍ 用互斥名把多EA实例锁在同一佣金管理器上
在 MT5 里跑多个 EA 实例时,若各自建佣金管理器会算重成本。这里用 const string my_mutex 定义一个进程级互斥名,把不同图表上的机器人指到同一个 CCCM 实例,回测和实盘都能共享 custom_comission 与 custom_shift 两套参数。 OnInit 里先 _comission_manager_.add(_Symbol,...) 挂当前品种佣金,再用 APPEND_BOT_PARAM 把参数塞进 BotParams 数组,最后 new CRobot(&_comission_manager_, my_mutex, params) 完成绑定。注意 my_mutex 字符串必须跨 EA 完全一致,否则互斥失效。 OnDeinit 只在 MQLInfoInteger(MQL_TESTER)==1 时写报告:用 __FILE__ 拆出前缀,落 XXX Report.csv 与 XXX Deals Report.csv,并显式 delete bot 防内存泄漏。实盘退出不会写文件,验证请走策略测试器。 CRobot 继承 CAutoUploader,构造直接透传佣金管理器、互斥名和参数数组;OnTick 先调父类 CAutoUploader::OnTick() 再做本地逻辑。外汇与贵金属杠杆高,多实例共享状态前务必在测试器跑通再上真金。
class="kw">const class="type">class="kw">string my_mutex = "My Mutex Name for this expert"; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//--- _comission_manager_.add(_Symbol,custom_comission,custom_shift); BotParams params[]; APPEND_BOT_PARAM(custom_comission,params); APPEND_BOT_PARAM(custom_shift,params); bot = new CRobot(&_comission_manager_,my_mutex,params); class=class="str">"cmt">//--- class="kw">return(INIT_SUCCEEDED); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert deinitialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnDeinit(class="kw">const class="type">int reason) { class=class="str">"cmt">//--- if(MQLInfoInteger(MQL_TESTER)==class="num">1) { class="type">class="kw">string arr[]; StringSplit(__FILE__,&class="macro">#x27;.&class="macro">#x27;,arr); class="type">class="kw">string file_name = arr[class="num">0]+" Report.csv"; SaveReportToFile(file_name,&_comission_manager_); WRITE_BOT_PARAM(file_name,custom_comission); class=class="str">"cmt">// Custom commission; WRITE_BOT_PARAM(file_name,custom_shift); class=class="str">"cmt">// Custom shift; WriteDetalesReport(arr[class="num">0]+" Deals Report.csv", &_comission_manager_); } class="kw">delete bot; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert tick function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnTick() { class=class="str">"cmt">//--- bot.OnTick(); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Main Robot class | class=class="str">"cmt">//+------------------------------------------------------------------+ class CRobot : CAutoUploader { class="kw">public: CRobot(CCCM *_comission_manager, class="type">class="kw">string _mutexName, BotParams &_params[]) : CAutoUploader(_comission_manager,_mutexName,_params) {} class="type">void OnTick() class="kw">override; }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Robot logic triggering method | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CRobot::OnTick(class="type">void) { CAutoUploader::OnTick(); Print("This should be the robot logic start"); }
「空小节无内容可提炼」
本小节原文未提供任何技术正文或代码,仅残留一个 HTML 注释片段,无法生成可验证的技术内容。 若需补全此节,请返回带实质说明或 MQL5 代码的原文,蒸馏引擎方可输出硬核块。
把报告桥接层接进你自己的优化器
前三篇把 XML 报告读写、历史对象加载、报告对象生成拆开讲完了,这一节其实就是把中间那层“桥”说清楚:它一边接数据加载机制,一边接报告生成机制,除了落盘 XML,还顺带给了卸载类和可直接套用的机器人模板,老算法也能在自动优化器里和新功能一起跑。 附件压缩包里有两个文件夹,都要解到 MQL5/Include;ReportManager.dll 必须手动放进 MQL5/Libraries,DLL 本体得从前面文章拿。文件清单里 GenericSorter.mqh、DealHistoryGetter.mqh、ReportCreator.mqh、ShortReport.mqh 这几个是接桥时最常改的,先认脸再动代码。 想验证桥接是否通,最快的办法是把 AutoLoader.mqh 挂到你现有 EA 的 OnTesterDeinit 前面,跑一次普通优化,看 Include 目录有没有新 XML 落出来;没有就查 DLL 路径和权限。外汇、贵金属品种做这类历史回放优化波动剧烈,桥接跑通不代表实盘稳健,样本外断片概率仍高。 评论区有人用自定义符号切出“抛出区间”做交叉验证,思路可行但全面枚举才严谨,遗传搜索下帧集不公平,实际损伤多大看测试段占比。先在你的小品种上跑通桥,再谈验证框架,比直接抄别人的统计整合脚本更省时间。