回测准确性决定了EA的可靠性。在全历史数据上单次优化会产生虚假的权益曲线,实盘时必然失效。前进窗口优化(WFO)将历史数据分割为样本内训练期和样本外验证期,提供真实的性能预期。
前进分析框架
将历史数据划分为N个重叠窗口。对每个窗口,在样本内期优化参数,然后在随后的样本外期验证。所有窗口的样本外结果构成真实性能估计。
设窗口大小 \( W \),测试期大小 \( T \)。滚动前进分析:
\[
P_{\text{true}} = \frac{1}{M} \sum_{i=1}^{M} P_{\text{OOS}}^{(i)}, \quad M = \left\lfloor \frac{B_{\text{total}} - W}{T} \right\rfloor
\]
其中 \( B_{\text{total}} \) 为总K线数量。
参数稳定性矩阵
最优参数应在不同市场环境下保持稳定。计算每个优化参数的变异系数:
\[
CV(\theta_j) = \frac{\sigma_{\theta_j}}{\mu_{\theta_j}} \times 100\%
\]
若 \( CV > 15\% \),则参数 \( \theta_j \) 缺乏稳定性,需要重新定义参数范围或步长。
MQL4前进分析完整实现
```cpp
// MQL4代码 - 完整前进优化器
input int InSampleBars = 4000; // 训练期K线数
input int OutSampleBars = 1000; // 测试期K线数
input int OptimizationSteps = 5; // 参数步数粒度
struct OptimizationResult {
double ProfitFactor;
double SharpeRatio;
double MaxDrawdownPct;
int MAPeriodOptimal;
int RSIThresholdOptimal;
};
OptimizationResult results[];
void RunWalkForwardOptimization() {
int totalBars = Bars;
int windows = (totalBars - InSampleBars) / OutSampleBars;
ArrayResize(results, windows);
for(int w = 0; w < windows; w++) {
int isStart = w * OutSampleBars;
int isEnd = isStart + InSampleBars;
int oosStart = isEnd;
int oosEnd = oosStart + OutSampleBars;
// 在样本内期进行网格搜索优化
OptimizationResult best = OptimizeOnRange(isStart, isEnd);
// 在样本外期验证
ValidateOnRange(best, oosStart, oosEnd);
results[w] = best;
}
// 计算聚合样本外指标
double avgProfitFactor = 0;
for(int i = 0; i < windows; i++) {
avgProfitFactor += results[i].ProfitFactor;
}
avgProfitFactor /= windows;
Print("前进分析平均利润因子: ", avgProfitFactor);
}
```
检测未来函数
未来函数会窥视未收盘的K线数据。MQL4中的常见违规包括:在`start()`内部使用`iClose(NULL,0,0)`且未等待K线确认,或未正确检查`OrdersTotal()`就引用`OrderOpenPrice()`。
验证规则:任何基于价格的决策必须使用索引>=1的数据。唯一的例外是移动止损修改,仅对出场使用当前价格,绝不用于入场判断。
样本外接受标准
数学过拟合检测
计算参数敏感性的F统计量:
\[
F = \frac{\text{Var}(P(\theta + \delta))}{\text{Var}(P(\theta))} \]
高F值表示微小参数变化引起性能大幅波动,表明参数集不稳定。
参考来源:MQL5文档 - 策略测试器(https://www.mql5.com/zh/docs/tester),《基于证据的技术分析》David Aronson 著,2006年。