Summary: 大多数EA的回测曲线都是“骗局”。本文教你识别未来函数、通过样本外验证检测过度拟合、正确使用遗传算法,以及蒙特卡洛模拟验证策略稳健性。




# EA回测准确性指南:如何避开最常见的两个陷阱

残酷的真相:漂亮曲线往往是假的



一条完美向上的回测资金曲线,很多时候是谎言。很多在历史数据上看起来极其赚钱的EA,一上实盘就连续亏损。原因几乎总是两个:未来函数(使用了未来信息)或过度拟合(曲线拟合到历史噪音上)。

MQL5官方文档中有一句忠告:“无论你如何精心设计优化标准,优化器总能找到方法利用你测试方法中的任何隐藏漏洞。”

1. 未来函数陷阱



什么是未来函数?



任何在交易决策时引用了“当时还不可知的价格数据”的代码,都是未来函数。

MQL4/MQL5中的常见违规写法:

| 违规代码 | 为什么错误 |
|:---|:---|
| 使用`Close[1]`但当前K线是0 | K线收盘价在开盘时还不知道 |
| `iCustom()`中使用偏移0 | 指标使用了当前未完成的K线收盘价 |
| 在K线0上使用`Highest()` | 使用了当前K线内的未来最高价 |
| 用`Time[0]`作为入场逻辑 | K线0的收盘时间只在K线完成时才知道 |

危险代码 vs 安全代码



危险 – 使用了未来数据:
```cpp
// 绝对不要这样做 – 在当前K线未收盘时用了收盘价
double currentClose = Close[0];
double currentHigh = High[0];
if(currentClose > currentHigh - 10 * Point)
{
OpenBuy(); // 下单时这个收盘价根本不知道
}
```

安全 – 只使用已确认的数据:
```cpp
// 正确 – 只使用已完成K线的数据
double previousClose = Close[1]; // K线-1已经结束
double previousHigh = High[1];
if(previousClose > previousHigh - 10 * Point)
{
OpenBuy(); // 下单时所有数据都是已知的
}

// 实盘中对当前K线做决策时,用Bid/Ask
double currentBid = Bid;
double currentAsk = Ask;
```

检测未来函数的方法



MQL5中可用调试宏:
```cpp
#ifdef __MQL5__
if(MQL5_Testing)
{
Print("警告:正在检查未来函数...");
// 记录任何未经验证就使用Close[0]的情况
}
#endif
```

2. 过度拟合与曲线拟合



200%法则



算法交易中有一个知名原则:如果你的优化参数与默认参数相差超过20-30%,你很可能正在过度拟合。

过度拟合的典型信号:
  • 资金曲线“太完美”(平滑向上、回撤极小)

  • 交易次数非常少(低于100-200笔)

  • 把回测时间段向后平移一个月,表现就崩溃了

  • 参数极其精细(如周期=14.7、偏差=2.13)


  • 推进分析方法(Walk-Forward)



    MetaQuotes推荐的正确验证方法:

    ```
    第1步:将数据分为样本内(IS)和样本外(OOS)
    第2步:在样本内期间进行参数优化
    第3步:用最优参数在样本外期间测试(绝对不能重新优化)
    第4步:如果样本外表现衰减 <30%,参数是稳健的
    第5步:如果样本外表现衰减 >50%,你过度拟合了
    ```

    时间划分示例:
    | 数据区间 | 用途 | 长度 |
    |:---|:---|:---|
    | 2020-2022 | 样本内(优化) | 3年 |
    | 2023-2024 | 样本外(验证) | 2年 |
    | 2025 | 前瞻测试(实盘/模拟) | 1年 |

    3. 遗传算法优化的正确设置



    MT5策略测试器中的遗传算法功能强大,但极易被误用。

    正确的遗传算法参数:



    | 参数 | 推荐值 | 原因 |
    |:---|:---|:---|
    | 初始种群 | 1000-2000 | 避免陷入局部最优 |
    | 进化代数 | 50-100 | 确保充分收敛 |
    | 交叉概率 | 0.7-0.9 | 保持基因多样性 |
    | 变异概率 | 0.01-0.05 | 防止过早收敛 |
    | 收敛容差 | 0.1-0.5% | 无改善时停止 |

    完整的优化适应度函数:



    ```cpp
    //+------------------------------------------------------------------+
    //| EA输入参数(用于GA优化) |
    //+------------------------------------------------------------------+
    input double TakeProfitPoints = 50.0; // 止盈点数(20-200)
    input double StopLossPoints = 30.0; // 止损点数(15-150)
    input int MA_Period = 14; // 均线周期(5-50)
    input double LotSize = 0.01; // 固定手数(0.01-0.10)

    // 在策略测试器中设置优化范围:
    // 止盈点数:最小20,最大200,步长5
    // 止损点数:最小15,最大150,步长5
    // 均线周期:最小5,最大50,步长1

    //+------------------------------------------------------------------+
    //| 优化适应度函数 |
    //+------------------------------------------------------------------+
    double OnTester()
    {
    // 切忌只用单一指标进行优化
    double netProfit = TesterStatistics(STAT_PROFIT);
    double sharpe = TesterStatistics(STAT_SHARPE_RATIO);
    double drawdown = TesterStatistics(STAT_EQUITY_DDREL_PERCENT);
    double trades = TesterStatistics(STAT_TRADES);

    // 交易次数太少的结果直接排除
    if(trades < 50) return -DBL_MAX;

    // 多指标适应度:盈利 + 夏普比率 - 回撤惩罚
    double fitness = (netProfit / 1000) + (sharpe * 100) - (drawdown * 2);

    return fitness;
    }
    ```

    夏普比率的最低要求



    许多开发者只优化利润,这是错误的。 一个稳健的EA应该有:

    | 指标 | 最低可接受值 |
    |:---|:---|
    | 夏普比率 | > 0.7 |
    | 盈利因子 | > 1.5 |
    | 最大回撤 | < 25% |
    | 平均每笔盈利 | > 2倍点差 |
    | 交易次数 | > 500笔(5年数据) |

    4. 建模质量与Tick数据



    90%原则



    MQL5文档明确指出:*“建模质量低于90%意味着测试结果不可信赖。”*

    | 建模质量 | 可靠性 |
    |:---|:---|
    | 99% | 非常高(使用原始逐笔数据) |
    | 90-98% | 可接受 |
    | 80-89% | 存疑 |
    | <80% | 不可靠 – 不要使用 |

    获得高建模质量的步骤:
    1. 下载Tick数据(不只是M1)
    2. 使用每个即时价格模式(非“控制点”或“仅开盘价”)
    3. 确保所选日期范围有完整的Tick历史

    数据验证代码:



    ```cpp
    // 检查当前品种是否有足够的历史数据
    datetime startDate = D'2020.01.01';
    datetime currentDate = TimeCurrent();

    int barsAvailable = Bars(Symbol(), PERIOD_H1, startDate, currentDate);
    if(barsAvailable < 5000)
    {
    Print("警告:数据不足。仅有 ", barsAvailable, " 根K线可用。");
    Print("请通过 工具 > 选项 > 图表 > 图表中最多K线数 下载更多历史数据");
    }
    ```

    5. 蒙特卡洛模拟验证



    优化完成后,运行蒙特卡洛分析测试策略稳健性。原理很简单:随机移除或随机化交易顺序,看绩效是否仍为正。

    手动蒙特卡洛思路:
    ```cpp
    // 回测时记录所有交易
    struct TradeRecord
    {
    datetime openTime;
    double profit;
    int barsHeld;
    };

    // 回测结束后,随机重采样1000次
    // 如果 >90% 的重采样结果是盈利的,则该策略稳健
    ```

    6. 完整验证检查清单



    在信任任何回测结果之前,确认以下项目:

  • [ ] 入场条件中没有使用`Close[0]`、`High[0]`、`Low[0]`

  • [ ] 所有指标的信号均使用偏移 >= 1

  • [ ] 样本内期间交易次数 >= 500笔

  • [ ] 样本外表现衰减 < 30%

  • [ ] 建模质量 >= 90%

  • [ ] 夏普比率 > 0.7

  • [ ] 盈利因子 > 1.5

  • [ ] 最大回撤 < 25%

  • [ ] 至少测试了两种不同市场状态(趋势 + 震荡)

  • [ ] 蒙特卡洛模拟显示 >90% 的重采样结果盈利


  • 总结



    | 陷阱 | 如何发现 | 解决方案 |
    |:---|:---|:---|
    | 未来函数 | 代码审查 | 将`[0]`替换为`[1]`或改用Bid/Ask |
    | 过度拟合 | 样本外测试严重衰减 | 减少参数数量、增加样本内周期 |
    | 建模质量低 | 查看测试报告 | 下载Tick数据、使用“每个即时价格” |
    | 单一指标优化 | 夏普比率<0.7 | 使用多指标适应度函数 |
    | 交易次数不足 | <500笔 | 延长回测周期 |

    只有经受住严格回测验证的策略,才可能在实盘市场中有生存机会。

    ---
    参考来源:
    1. MQL5官方文档 – 策略测试器:建模质量
    2. MQL5官方文档 – OnTester自定义优化函数
    3. MetaQuotes – 遗传算法优化指南(2025)
    4. QuantConnect – 回测陷阱与过度拟合检测
    5. MQL4/MQL5社区论坛 – 未来函数检测方法(2025)
    ```