# EA参数优化:从组合爆炸到遗传算法的完全指南
一、组合爆炸:为什么穷举法行不通?
当我们对EA进行参数优化时,组合数量会指数级增长。以典型场景为例:20个开关参数和20个数值参数(各有10种取值)。总组合数计算如下 :
这个数字有多大?
这就是为什么MT4/MT5内置了遗传算法(Genetic Algorithm)优化,而非穷举搜索。
二、遗传算法的核心原理
遗传算法模拟生物进化过程,在巨大的参数空间中高效搜索最优解 。
五个核心步骤:
步骤1:种群初始化
随机生成数百到数千组参数,每组称为一个“个体”。
步骤2:适应度评估
用每组参数运行回测,计算适应度分数。MT4支持以下优化目标 :
步骤3:选择(适者生存)
适应度高的个体有更大概率被选中繁殖,低的被淘汰。
步骤4:交叉(繁殖)
选中的个体交换“基因”(参数值)产生子代。例如,父代A的止损参数与父代B的止盈参数组合。
步骤5:变异(随机扰动)
少量个体的某些参数随机改变,引入多样性,防止陷入局部最优。
上述过程重复数十到数百代。种群平均适应度持续提升,最终收敛到高性能参数组合 。
三、实战优化策略:分层法
策略1:固定策略性参数
优化前,先区分什么是策略逻辑、什么是可调数值。
以网格EA为例:
这能大幅缩减搜索空间 。
策略2:设置合理的优化范围
在MT4测试器中配置边界 :
| 参数 | 起始值 | 步长 | 停止值 |
|------|--------|------|--------|
| 止盈 | 20 | 10 | 100 |
| 止损 | 15 | 5 | 80 |
| 手数 | 0.01 | 0.01 | 0.10 |
单次优化参数不超过5-7个。
策略3:样本外验证(防过拟合核心)
遗传算法找到的参数可能在历史数据上表现完美,但实盘却很差——这是过拟合。解决方案:
1. 将数据分为70%样本内(用于优化)和30%样本外(用于验证)
2. 仅在样本内数据上运行GA优化
3. 将“冠军参数”在样本外数据上测试
4. 如果性能显著下降(>30%),说明EA过拟合了
四、未来函数检测:指标EA化的前提
在将任何指标转换为EA参与优化前,必须确认该指标不含未来函数(即根据当前K线修改历史信号的重绘行为)。
检测方法一:源码审查(白盒)
检查循环中是否有修改历史数组的操作。任何对非0偏移量的赋值都是重绘信号。
检测方法二:肉眼观察(黑盒)
将指标加载到1分钟图上。随着新K线形成,观察历史信号是否会消失或移动。如果过去的买卖箭头会变化,则该指标含未来函数。
> 含未来函数的指标回测表现极佳,但实盘必然亏损。永远不要基于重绘指标优化EA。
五、通过iCustom()加载自定义指标
确认指标无未来函数后,使用`iCustom()`将其集成到EA中 :
```cpp
// 函数原型
double iCustom(
string symbol, // 品种(NULL=当前)
int timeframe, // 周期(0=当前图表)
string name, // 指标文件名(不含.ex4)
... , // 指标参数(如有)
int mode, // 线索引(0起,对应SetIndexBuffer顺序)
int shift // 偏移(0=当前K线,1=前一根)
);
```
实际示例:RSI背离指标
```cpp
// 假设自定义背离指标:
// 线0 = 看涨背离价格位
// 线1 = 看跌背离价格位
// EMPTY_VALUE 表示无信号
double bullishDiv = iCustom(NULL, 0, "Divergence_Indicator", 0, 1);
double bearishDiv = iCustom(NULL, 0, "Divergence_Indicator", 1, 1);
if (bullishDiv != EMPTY_VALUE && OrdersTotal() == 0) {
// 前一根K线出现看涨背离信号
OrderSend(Symbol(), OP_BUY, lotSize, Ask, 3, 0, 0, "Divergence Buy", magic);
}
```
关键决策:当前K线 vs 前一根K线
使用`shift=0`(当前K线)时,信号会随价格波动出现和消失,导致回测中出现幻影信号。为获得稳健的优化结果,建议使用`shift=1`(已完成K线)。
六、OrderSend()下单函数详解
`OrderSend()`是EA执行交易的核心函数 :
```cpp
int OrderSend(
string symbol, // 品种(用Symbol()取当前)
int cmd, // OP_BUY/OP_SELL或挂单类型
double volume, // 手数
double price, // 开仓价(买单用Ask,卖单用Bid)
int slippage, // 允许滑点(通常2-3点)
double stoploss, // 止损价(0=无)
double takeprofit, // 止盈价(0=无)
string comment, // 订单注释
int magic, // EA魔术号(唯一标识)
datetime expiration, // 挂单过期时间
color arrow_color // 图表箭头颜色
);
```
市价单下单示例
```cpp
int start() {
double lotSize = 0.1;
int slippage = 3;
int magicNumber = 20240601;
// 买入订单
int ticket = OrderSend(
Symbol(), // 当前品种
OP_BUY, // 买入
lotSize, // 0.1手
Ask, // 当前卖价
slippage, // 允许3点滑点
Bid - 50 * Point, // 止损:Bid下方50点
Bid + 100 * Point, // 止盈:Bid上方100点
"GA_Optimized_EA", // 注释
magicNumber, // 唯一ID
0, // 无过期
clrGreen // 绿色箭头
);
if (ticket < 0) {
Print("订单失败,错误码:", GetLastError());
}
return 0;
}
```
七、OnTester()自定义优化目标
如需自定义适应度函数,在EA中实现`OnTester()` :
```cpp
double OnTester() {
// 获取回测统计值
double profitFactor = TesterStatistics(STAT_PROFIT_FACTOR);
double maxDrawdown = TesterStatistics(STAT_EQUITY_DD_PERCENT);
double sharpeRatio = TesterStatistics(STAT_SHARPE_RATIO);
// 自定义评分:夏普比率优先,同时惩罚高回撤
double fitness = sharpeRatio * 100;
if (maxDrawdown > 20.0) fitness *= 0.5; // 回撤超20%扣50%分
if (profitFactor < 1.3) fitness *= 0.8; // 盈利因子低于1.3扣20%分
return fitness;
}
```
八、必须避免的优化陷阱
| 陷阱 | 后果 | 预防措施 |
|------|------|----------|
| 过拟合 | 回测完美,实盘亏损 | 样本外验证 |
| 曲线拟合 | 换市场周期就崩 | 多市场环境测试 |
| 忽略交易成本 | 回测盈利,实盘亏损 | 回测中包含点差+手续费 |
| 优化参数过多 | 找到虚假相关性 | 单次≤7个参数 |
| 幸存者偏差 | 只用现存品种回测 | 纳入已退市品种 |
九、推荐的工作流程
1. 编写模块化EA代码,所有可调参数用input修饰
2. 确认无未来函数(如使用自定义指标)
3. 设置合理的参数范围(不让GA搜索极端值)
4. 运行GA优化:种群500-1000,代数50-100
5. 从结果中选出前10组参数
6. 样本外前向测试(用预留的30%数据)
7. 选择一致性最佳的参数组,而非回测利润最高的
8. 蒙特卡洛模拟,检验随机交易顺序下的稳健性
---
参考来源:
1. Eabang. “MT4MT5优化参数的遗传算法.” 2025年8月.
2. MetaTrader 4帮助文档. “设置 — 专家优化.”
3. FxGecko. “MT4编程:如何把指标应用到外汇EA上?” 2023年3月.
4. MQL4教程. “交易操作编程 — 开仓与挂单.”
5. MQL4教程. “常用函数 — Comment(), MessageBox(), PlaySound().”
6. MQL4教程. “程序执行 — EA生命周期.”
```