从零构建 MQL5 智能交易系统,第 2 部分:无需鼠标点击即可编译与回测
使用 metaeditor64.exe 编译 EA,并通过 INI 文件运行策略测试器:用十秒运行替代十分钟手动设置,同时介绍我们遇到的问题。

在第一部分中,我们介绍了 EA 的结构,以及如何根据止损距离计算持仓规模。现在来讲一个俄语教程很少涉及的话题:无需用鼠标打开 MetaEditor 和策略测试器,也能测试交易想法。
两者的差别很明显。手动操作一轮——打开编辑器、按 F7、切换到终端、打开测试器、设置品种、周期、日期与输入参数、点击开始、等待并查看报告——大约需要十分钟,而且要一直关注。通过命令行执行同一次测试,大约只需十秒,无需干预。正因如此,我们才能在一个晚上尝试一百多种 EA 配置;靠手动操作,很难完成这么多次测试。
从命令行编译
编译器是终端文件夹中的 metaeditor64.exe。无需打开其界面,就能编译文件:
"C:\Program Files\XM Global MT5\metaeditor64.exe" /compile:"...\MQL5\Experts\MyEA.mq5" /log:"%TEMP%\compile.log"
开始前需要了解三个要点。
源文件必须放在终端的数据文件夹内。 不是项目文件夹,而是终端所用数据目录下的 MQL5\Experts。否则,编译器会找不到标准头文件,并报告“找不到 Trade.mqh”,即使该文件确实存在。我们就遇到过这种情况。可以像我们一样,把源文件放进数据文件夹,每次编译前复制过去;如果终端以便携模式运行,也可以添加 /portable 开关。
日志使用 UTF-16 编码。 用默认编码读取,会出现大量空字符。在 PowerShell 中,应明确指定编码:
$result = Get-Content $log -Encoding Unicode | Select-String 'Result' | Select-Object -Last 1
if ($result -notmatch '0 errors') { throw "Build failed: $result" }
退出码并非总能提供有效信息。 更可靠的做法是像上面那样解析日志中的 Result: 0 errors, 0 warnings 行,并在编译失败时停止运行。否则,测试器会运行上一次生成的旧 .ex5 文件,而你可能花上半天,试图解释一份由已经修改过的旧代码产生的结果。
使用 INI 配置运行测试器
终端支持 /config:path.ini 开关。如果配置中包含 [Tester] 节,就会直接进入测试模式。以下是一个可运行的最简配置:
[Tester]
Expert=TradeaproRangeBreakout
Symbol=GOLD#
Period=M15
Model=1
Optimization=0
FromDate=2025.01.01
ToDate=2025.12.31
Deposit=10000
Currency=USD
Leverage=1:100
Report=C:\Temp\report
ReplaceReport=1
ShutdownTerminal=1
Visual=0
关键设置在最后两组配置行中。Visual=0 用于关闭可视化,ShutdownTerminal=1 则在测试完成后关闭终端。如果没有后者,脚本会无限等待,让你无法确定测试是否已经结束。Model=1 使用开盘价;对于基于已收盘 K 线执行操作的策略,这已足够,而且比“每个报价”模式快数倍。
编者注: 原始示例保留了
Model=1,但 MetaTrader 5 将其定义为“1 分钟 OHLC”;“仅开盘价”使用的是Model=2。因此,原文上方的描述与其配置不一致。请参阅官方 MetaTrader 5 配置参考。
EA 输入参数放在单独的配置节中,名称与代码中的 input 变量相同:
[TesterInputs]
RewardRatio=3.0
BreakevenAtR=1.5
MinRangePct=0.15
将文件保存为 UTF-16(在 PowerShell 中为 -Encoding Unicode)。如果使用 UTF-8,终端会悄悄忽略配置,打开常规窗口。不报错,也不执行测试。
从哪里获取结果
在没有可见窗口的情况下运行,最出人意料的是:原本搭建这一流程就是为了获取的 HTML 报告,经常根本不会生成。不能依赖它。
一种可行办法是在 EA 内部计算指标,并在测试结束时调用的 OnDeinit 中将其输出:
void OnDeinit(const int reason)
{
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
double winrate = trades > 0 ? 100.0 * wins / trades : 0;
double pf = grossLoss > 0 ? grossProfit / grossLoss : 0;
PrintFormat("[RESULT] balance=%.2f trades=%d winrate=%.1f PF=%.2f maxDD=%.1f",
balance, trades, winrate, pf, maxDrawdownPct);
}
输出会写入测试代理的日志:
%APPDATA%\MetaQuotes\Tester\<ID>\Agent-127.0.0.1-3000\logs\<date>.log
脚本只需一行就能从中提取结果。此外,输出跳过入场的原因——[SKIP] spread、[SKIP] range、[SKIP] lot——并进行分组统计,也很有帮助:
$skips = $txt | Select-String '\[SKIP\] (\w+)' -AllMatches |
ForEach-Object { $_.Matches[0].Groups[1].Value } | Group-Object | Sort-Object Count -Desc
EA 的初版在四年测试区间内一笔交易都没做时,正是这些信息帮我们找到了原因。报告是空的,但日志显示,区间过滤器把每一天都排除了。没有这些输出,就只能靠猜测排查。
我们遇到的问题
终端启动四秒后就关闭,什么也没做。 原因是它正在安装更新。更新后的首次启动,需要手动处理。
三个月的测试花了九分钟,而不是十秒。 终端登录的账户访问权限已过期,它一直在等待服务器同步历史数据。测试需要终端登录一个有效账户。任何模拟账户都可以;这不会改变历史数据。
关于经纪商:同一策略至少要在两家经纪商处测试。点差、报价精度,甚至交易品种名称都可能不同。一家经纪商将黄金命名为 GOLD#,另一家则叫 XAUUSD;我们的 EA 在第二家报价数据上的盈利因子为 1.41,而在第一家为 1.33。我们在 XM 上进行测试,并在 RoboForex 上交叉核验;开立模拟账户只需几分钟,随后即可获取历史报价。
不要手动编辑 terminal.ini 或终端模板。 我们曾“整理”配置并清空登录字段,结果终端完全无法启动。测试器所需的一切都可以通过 /config,在单独的 INI 文件中提供。无需改动终端配置。
一次运行对应一个终端。 启动前先关闭上一个进程(Stop-Process -Name terminal64),否则新启动的终端可能读取另一轮测试的配置。要并行测试,请安装多个终端副本,并分别使用独立的数据文件夹。每个副本大约占用一吉字节内存,因此只有四吉字节内存的办公笔记本电脑,无法承受超过两个测试同时运行。
找到配置并将 EA 部署到账户后,原来的测试电脑就不再适合作为运行环境:无论是否合上笔记本电脑,终端都需要全天候运行。我们将交易终端放在靠近经纪商服务器的 VPS 上。对突破策略而言,个位数毫秒的延迟会带来明显影响,因为入场恰好发生在价格突破区间边界的时刻。
这套流程能带来什么
完整脚本涵盖编译、配置、启动和日志解析,大约只需一百行 PowerShell,运行方式如下:
.\run-test.ps1 -Symbol GOLD# -From 2025.01.01 -To 2025.12.31 -Inputs @{ RewardRatio = 3; BreakevenAtR = 1.5 }
再套上一层参数循环,你就能获得一张结果表,用数据评估策略。我们针对 2022–2024 区间,对 EA 进行参数网格测试,然后用未参与筛选的 2025–2026 区间检验最佳配置:验证期盈利因子为 1.33,回撤为 3.9%,共 145 笔交易。
这张表最有价值的部分,是最佳结果周围的表现。如果相邻参数组合的结果相近,就形成了一个表现平台,该设置可能反映了某种市场特征。如果只有一个孤立峰值,周围参数组合全都亏损,那就是曲线拟合,转到真实资金账户后往往难以保留其表现。靠手动反复尝试很难看出这种区别,因为几乎没人会手动完成一百次测试。
本次测试使用的 EA 连同源代码一起收录在目录中,其在我们模拟账户上的虚拟资金交易可在实时监测中查看。
第三部分将介绍如何区分有可行性的策略与拟合出来的策略:前向测试、数据拆分,以及为什么漂亮的回测本身说明不了多少问题。
本文中的经纪商与 VPS 链接为推广佣金链接:如果你通过这些链接注册,我们会获得佣金,而你的使用条件与通过直接链接注册相同。我们只提及自己使用的服务。