理解并有效地使用 MQL5 策略测试器(基础篇)
📘

理解并有效地使用 MQL5 策略测试器(基础篇)

第 1/2 篇

「先把 MT5 策略测试器跑通」

MQL5 自带的策略测试器(Strategy Tester)是验证任何 EA 或指标逻辑的第一现场,不是可选配件。打开 MT5 终端按 Ctrl+R,默认进入的是本地单线程回测,左上方选好品种、时间帧、测试周期与模型(每 tick / 控制点 / 开盘价)后才能点开始。 很多新手卡在「没数据」:MT5 需要先下载对应品种的历史数据。在「符号」窗口右键刷新或调工具—下载历史,否则回测长度会被截断到几根 K 线。一个可验证现象是,同一 EA 在 2020—2023 的 XAUUSD H1 上用每 tick 模型跑,若历史只覆盖 3 个月,夏普和回撤会严重失真,和全量数据结果偏差可能超过 40%。 测试器右侧的「输入参数」面板直接映射 EA 的 extern 变量,改手数、止损点数、均线周期不用重编译。外汇与贵金属杠杆高、滑点跳空频繁,回测盈利只代表历史概率,实盘可能显著偏离,务必用小资金先验证。

◍ 开测前必须啃下的三块硬骨头

在 MT5 里跑策略测试器之前,有几个前置项不理顺,回测结果基本不可信。很多开发者栽在自以为懂了,实际连编译期的报错都没处理干净,这种状态下跑出的权益曲线没有任何参考价值。 排第一的是错误处理。MQL5 程序里任何未捕获的异常都会让测试中断或给出假信号,所以要在关键调用处把错误码(如 GetLastError 返回的非零值)全部兜住。开发阶段犯糊涂太正常,但正式测试前必须清零这类低级坑。 排第二的是调试。MT5 的调试器支持断点逐行执行,能让你在 tick 级观察变量变化,比单纯看日志高效得多。排第三才是策略测试器本身——它比 MT4 的测试引擎多了真实毫秒级成交模拟和 Agent 分布式计算,功能深得多。 需要点明:本文预设读者已掌握 MQL5 语法并能独立写程序。若你还不会写 EA,直接读测试器相关内容收益极低,应先补语言基础。外汇与贵金属品种波动剧烈、杠杆风险高,任何回测优异都不代表实盘概率稳赢。

先认准错误出在哪一阶段

写 MQL5 程序最耗时的不是改 bug,而是不知道报错到底指什么、发生在哪一步。编译器能准确抛出信息,但人若看不懂,排错时间会成倍拉长。 按工作流切分,错误只落在三个节点:敲代码时的编译错误与警告、点运行后的运行时错误、以及程序发交易指令时由服务器回写的交易错误。先把这三类的边界记牢,再谈定位。 在 MT5 的 MQL5 IDE 里,所有报错集中显示在底部“工具箱”窗口。默认没看到的话,走三条路任一即可调出:菜单“查看”→“工具箱”、快捷键 Ctrl+T、或主工具栏的“工具箱”按钮。窗口一开,后续三类错误都有处可查。

「MT5编译器报错的七种高频形态」

写 MQL5 时编译器报错极其正常,但在「错误」选项卡清零前程序绝不会生成 ex5。下面这七类占了新手绝大多数编译失败:分号缺失、意外令牌、未声明标识符、括号不平衡、程序意外结束、全局范围表达式、参数计数错误。 分号预期最简单——行尾漏写 ; 或左括号缺失都会触发。原错误代码 int a=(2+1) 编译即失败,补成分号结尾 int a=(2+1); 后通过。 未声明标识符常出现在直接用变量再赋值:上面片段里 a=5; 之前没声明,编译器会精确指出变量 a 未声明;改成 int a=5; 即消错。 左括号不平衡与程序意外结束本质都是括号配对失败。例如 if (a=5 漏右括号,会同时报「左括号不平衡」和「意外的程序结束」,补 ) 后干净编译。 表达式不允许出现在全局范围,是因为在函数外写了语句。片段中 void OnStart(){ bool a=7; if(a=5) a=5; 缺收尾 } 导致后续代码掉到全局,补 } 归位即可。 错误的参数计数发生在函数实参个数和定义不符。自定义 myVal(int a,int b) 返回两数之和,调用只传 myVal(10) 必报错;传 myVal(10,20) 才匹配。 某些操作符预期则是运算符漏写,如 int a= 7 10; 中间无 +,编译报「某些运算符预期」,改为 int a= 7+10; 解决。 警告不等于错误:片段末行 int a; 声明未使用,仅出「未使用变量 a」警告,不影响出 ex5。双击错误行可跳转到对应代码行,从最上方错误修起往往连带消掉下游报错。

MQL5 / C++
<span class="keyword">class="type">int</span> a=(<span class="number">class="num">2</span>+<span class="number">class="num">1</span>)
<span class="keyword">class="type">int</span> a=(<span class="number">class="num">2</span>+<span class="number">class="num">1</span>);
<span class="keyword">class="type">int</span> a=(<span class="number">class="num">2</span>+<span class="number">class="num">1</span>;
<span class="keyword">class="type">int</span> a=(<span class="number">class="num">2</span>+<span class="number">class="num">1</span>);
a=<span class="number">class="num">5</span>;
<span class="keyword">class="type">int</span> a=<span class="number">class="num">5</span>;
&nbsp;&nbsp; <span class="keyword">class="type">bool</span> a=<span class="number">class="num">7</span>;
&nbsp;&nbsp; <span class="keyword">if</span> (a=<span class="number">class="num">5</span>
&nbsp;&nbsp; a=<span class="number">class="num">5</span>;
&nbsp;&nbsp; <span class="keyword">class="type">bool</span> a=<span class="number">class="num">7</span>;
&nbsp;&nbsp; <span class="keyword">if</span> (a=<span class="number">class="num">5</span>)
&nbsp;&nbsp; a=<span class="number">class="num">5</span>;
<span class="keyword">class="type">void</span> <span class="functions">OnStart</span>()
&nbsp;&nbsp;{
&nbsp;&nbsp; <span class="keyword">class="type">bool</span> a=<span class="number">class="num">7</span>;
&nbsp;&nbsp; <span class="keyword">if</span> (a=<span class="number">class="num">5</span>)
&nbsp;&nbsp; a=<span class="number">class="num">5</span>;
<span class="keyword">class="type">void</span> <span class="functions">OnStart</span>()
&nbsp;&nbsp;{
&nbsp;&nbsp; <span class="keyword">class="type">bool</span> a=<span class="number">class="num">7</span>;
&nbsp;&nbsp; <span class="keyword">if</span> (a=<span class="number">class="num">5</span>)
&nbsp;&nbsp; a=<span class="number">class="num">5</span>;
&nbsp;&nbsp; }
&nbsp;&nbsp; <span class="keyword">class="type">int</span> a=(<span class="number">class="num">7</span>+<span class="number">class="num">5</span>);
&nbsp;&nbsp; <span class="keyword">if</span> (a&lt;<span class="number">class="num">7</span>)
&nbsp;&nbsp; 
&nbsp;&nbsp; a=<span class="number">class="num">7</span>;
&nbsp;&nbsp; }
&nbsp;&nbsp; <span class="keyword">class="type">int</span> a=(<span class="number">class="num">7</span>+<span class="number">class="num">5</span>);
&nbsp;&nbsp; <span class="keyword">if</span> (a&lt;<span class="number">class="num">7</span>)
&nbsp;&nbsp; {
&nbsp;&nbsp; a=<span class="number">class="num">7</span>;
&nbsp;&nbsp; }
<span class="keyword">class="type">void</span> <span class="functions">OnStart</span>()
&nbsp;&nbsp;{
&nbsp;&nbsp; <span class="keyword">class="type">int</span> example=myVal(<span class="number">class="num">10</span>);
&nbsp;&nbsp;}
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="keyword">class="type">int</span> myVal(<span class="keyword">class="type">int</span> a, <span class="keyword">class="type">int</span> b)
&nbsp;&nbsp;{
&nbsp;&nbsp; <span class="keyword">class="kw">return</span> a+b;
&nbsp;&nbsp;}
<span class="keyword">class="type">void</span> <span class="functions">OnStart</span>()
&nbsp;&nbsp;{
&nbsp;&nbsp; <span class="keyword">class="type">int</span> example=myVal(<span class="number">class="num">10</span>,<span class="number">class="num">20</span>);
&nbsp;&nbsp;}
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="keyword">class="type">int</span> myVal(<span class="keyword">class="type">int</span> a, <span class="keyword">class="type">int</span> b)
&nbsp;&nbsp;{
&nbsp;&nbsp; <span class="keyword">class="kw">return</span> a+b;
&nbsp;&nbsp;}
<span class="keyword">class="type">int</span> a= <span class="number">class="num">7</span> <span class="number">class="num">10</span>;
<span class="keyword">class="type">int</span> a= <span class="number">class="num">7</span>+<span class="number">class="num">10</span>;
<span class="keyword">class="type">int</span> a;

常见问题

先在工具-符号里下载对应品种的历史数据,再在测试器选对品种和时间周期,重载后重跑即可。
重点核对点差设置、佣金成本、以及是否勾选了使用真实 tick 数据,这三块漏掉最容易失真。
可以,小布能读取你的回测日志并标记出错阶段,直接告诉你该补数据还是改逻辑,省去翻报错的时间。
通常是变量或函数名拼错、或头文件没包含,检查拼写并确认#include对应库文件即可消除。
看日志里最后打印的订单或循环计数,多半是死循环或订单条件永远不满足,加 Print 断点逐步排查。