AI 生成的策略过度拟合吗?看 AI 如何解决过度拟合
AI 生成的策略过度拟合吗?看 AI 如何解决过度拟合
上一篇文章发出去之后,大家肯定有这样的疑问:
策略跑了 110 轮进化,试了 420 次参数组合,最后选出夏普 1.33 的那一版。这难道不是过度拟合吗?100 多轮挑出来的最优解,不就是在历史数据上凑答案?
做策略,就是在过度拟合以及欠拟合之间来寻找一个平衡。
110 轮迭代,每轮都在调整参数、加机制、删机制,最后留下来的肯定是历史表现最好的那一组——这正是过度拟合的定义。一个在历史上表现完美的策略,到了未来很可能直接崩盘。
所以这一天我没有继续让 AI 跑进化。我让它做了一件相反的事:如何解决自己策略的过度拟合呢?
1. 什么是过度拟合,为什么 R110-R112 可疑
先说清楚过度拟合长什么样。
最典型的信号是:策略在样本内(in-sample,IS)表现极好,但在样本外(out-of-sample,OOS)断崖式下跌。好比一个学生背过真题集,模拟考满分,但真考换了题就懵了。
量化里,这种”只对历史有效”的策略有几个具体特征:
- 夏普的尖峰:参数稍微动一下,夏普就暴跌(说明最优值是个针尖,不是平台)
- 依赖少数年份:总收益靠某一两年的时间窗口撑着,其他时间平平甚至亏损
- 只适配一种行情:趋势行情赚得盆满钵满,震荡行情全还回去
- IS/OOS 严重分裂:2018 年之前的训练数据上跑得飞起,2018 年之后实战就拉胯
我最担心的是 R110、R111、R112 这三轮。当时 AI 在 R102(即上一篇文章最终选出的那版 baseline)的基础上试了好几个加料版本:
- R110 加了 Donchian 突破入场,夏普从 1.33 掉到 0.11
- R111 加了 breakeven 移动止损,夏普掉到 1.02
- R112 加了金字塔加仓,收益涨了但夏普掉到 1.10
这些是失败实验,看起来没问题——但反过来想:如果这版策略是一个坚实的最优解,为什么周围这么多变种都崩了?是不是它本身就站在悬崖边上?
这个问题必须用数据回答,不能凭感觉。
2. 解决思路:不是加更多机制,而是交叉验证
很多人一听到”过度拟合”,第一反应是”加更多 ML”、“加正则化”、“换更复杂的模型”。
这恰恰是错的方向。
过度拟合的本质,是你在一组数据上反复试错,最后挑出最适配这组数据的参数。解决方法不是让模型更复杂,而是从多个独立维度去验证:这个”最优”是不是只在特定条件下成立。
具体到我这次做的事情,是让 AI 跑了一套 4 维度的交叉验证:
| 维度 | 问的问题 | 通过标准 |
|---|---|---|
| Walk-Forward | 用前半段训练、后半段测试,性能保留多少? | OOS/IS ≥ 0.6 |
| 逐年一致性 | 是不是只靠少数年份拉均值? | ≥ 6 个 OOS 年正收益 |
| 参数微扰 | 参数稍微偏一点,策略会不会崩? | 相邻 Sharpe 差 < 10% |
| Regime 分解 | 是不是只适配某一种行情? | ≥ 3 个 regime 都盈利 |
关键设计:这 4 个维度相互独立,任何一个维度的”作弊”都逃不过另一个维度。
- 你可以在 Walk-Forward 上作弊(比如故意选了一个 OOS 表现好的参数),但你逃不过参数微扰——参数稍微动一下就会暴露
- 你可以靠少数年份拉高平均值,但你逃不过逐年一致性检查
- 你可以只适配一种行情,但你逃不过 Regime 分解
这叫defense in depth——多层防御,单层失守不至于全军覆没。
3. 维度一:Walk-Forward——OOS 保留 IS 的 78%
这是最经典也是最严苛的检查。
把数据切成两段:
- IS(训练集):2010-2017,8 年
- OOS(测试集):2018-2025,8 年
当前这版的参数是在 2010-2025 全量数据上跑出来的,所以”前半段 vs 后半段”是一个公平的分割——它没机会单独适配某一段。
结果:
| 段 | Sharpe |
|---|---|
| IS 2010-2017 | 1.5014 |
| OOS 2018-2025 | 1.1732 |
| 比值 OOS/IS | 0.781 |

0.781 意味着什么?
OOS 完整保留了 IS 78% 的性能。业内一般这么分档:
- ≥ 0.6:PASS,没有明显过度拟合
- 0.4 - 0.6:WARN,轻度可疑
- < 0.4:FAIL,明显过度拟合
0.781 远超 PASS 阈值。这说明它学到的不是”2010-2017 的特例”,而是真实的市场规律——因为同样的规律在它没见过的 2018-2025 上依然成立。
作为对照,一个真正过度拟合的策略,这个比值通常在 0.2-0.4 之间,OOS 会直接腰斩甚至变负。
4. 维度二:逐年 Sharpe——8/9 年正收益
Walk-Forward 只把数据切成两段。但 OOS 的 8 年里,会不会其实是靠某一两个超级好年撑起来的?
所以我让 AI 把 OOS 拆成 9 个年份(2018-2026),单独算每年的 Sharpe:
| Year | Sharpe | 备注 |
|---|---|---|
| 2018 | +0.959 | 中性 |
| 2019 | +1.445 | 好 |
| 2020 | -0.682 | 唯一亏损年(COVID 冲击) |
| 2021 | +1.565 | 好 |
| 2022 | +2.615 | 非常好 |
| 2023 | +1.017 | 中性 |
| 2024 | +0.569 | 弱正 |
| 2025 | +1.618 | 好 |
| 2026 | +0.350 | 弱正(半年) |

8/9 年正收益。
通过标准是 ≥ 6 年正收益,9 年里 8 年都过关,这是非常强的证据。
但更重要的是 2020 年的亏损。
2020 年 3 月,COVID 引发全球资产恐慌,螺纹钢一周内波动率翻倍。趋势策略在这种波动率冲击下被打止损是正常的——任何趋势策略都会在 2020 年吃瘪。
这里有个非常重要的判断:我看到 2020 年 -0.682 的第一反应是”能不能加个规则把它修掉”。但 AI 给出了相反的结论:
不要修。如果你为了”修复 2020”加一条特殊规则,那才是真正的过度拟合——你在用一个特定年份的事件去雕刻参数。一个能在所有极端行情中都盈利的策略,几乎肯定是过拟合的。
健康的策略有亏损年,是 feature 不是 bug。
5. 维度三:参数微扰——11.1% 的平缓下降
这是最能直接检测”针尖最优”的方法。
固定其他所有参数,只动两个核心参数:
st_mult(SuperTrend 倍数):{4.5, 5.0, 5.5}ema_period(趋势 EMA 周期):{155, 162, 170}
跑一个 3×3 的网格,看 Sharpe 在中心点(即当前参数)周围变化有多剧烈:
| st_mult \ ema | 155 | 162 | 170 |
|---|---|---|---|
| 4.5 | 1.170 | 1.184 | 1.100 |
| 5.0 | 1.325 | ★ 1.332 | 1.246 |
| 5.5 | 1.288 | 1.268 | 1.200 |

当前参数在网格中心(★),相邻 8 个点的 Sharpe 全部在 1.10 - 1.33 之间。
通过标准:
- < 10%:PASS,平缓高原
- 10-25%:WARN,轻度敏感
- > 25%:FAIL,悬崖式下降(针尖最优)
中心点的最大相邻差是 11.1%(st_mult 从 5.0 调到 4.5 时 Sharpe 从 1.33 掉到 1.18),落在 WARN 区间。
为什么 WARN 可以接受?
关键看下降形态。如果是悬崖——比如 5.0 时 Sharpe 1.33,4.9 时暴跌到 0.5——那是典型过度拟合。但中心点周围的下降是平缓的:
- st=5.0 → 4.5:Sharpe 1.33 → 1.18,下降 11%
- st=5.0 → 5.5:Sharpe 1.33 → 1.27,下降 5%
这意味着当前参数不是站在针尖上,而是站在一个宽阔的高原上——参数往任何方向偏一点,策略都依然有效。这正是反过度拟合最强的证据之一。
11.1% 的 WARN 提醒我们 st_mult 下限稍敏感(下降不对称),如果未来要做敏感性补强可以关注这一点。但这是一个改进方向,不是回退理由。
6. 维度四:Regime 分解——3/3 全盈利
这是我最喜欢的一个维度。
前面三个维度都是在时间维度上切——切 IS/OOS、切年份、切参数。但市场还有另一个独立的切法:按行情类型切。
先说清楚 Regime 在这里扮演什么角色:它不是一个新的策略,也没有改任何参数。它只是一种新的观察视角——把已经跑完的交易,按开仓那一刻市场所处的状态重新分组,看策略在不同类型的行情下是否都赚钱。
具体做法是用 K-Means 聚类,按 ADX 均值、ATR 百分比均值、自相关、波动率四个特征,把 16 年的 60min K 线聚成 3 类:
| Regime | 特征 | 占比 | 标签 |
|---|---|---|---|
| 0 | ADX 23.6 / Vol 26.6% | 50.2% | 中等趋势 |
| 1 | ADX 34.3 / Vol 27.7% / 自相关 -0.08 | 27.9% | 高波动震荡 |
| 2 | ADX 27.7 / Vol 58.7% | 21.9% | 高波动趋势 |
然后看当前策略在这 3 个 regime 里的 PnL 贡献:
| Regime | Trades | Total PnL |
|---|---|---|
| 0 中等趋势 | 225 | +164,990 |
| 1 高波动震荡 | 69 | +40,237 |
| 2 高波动趋势 | 71 | +39,360 |

3/3 全部盈利。
通过标准是 ≥ 3 个 regime 盈利,结果 3 个都过关。
这意味着什么?
它不是只适配某一种行情的策略。 当前策略在主导趋势行情(regime 0)赚得最多,在高波动震荡(regime 1)也赚,在高波动趋势(regime 2)也赚。这是一种真正的”普适”特征——它捕捉的是跨行情的某种底层规律,而不是某一段特定历史的巧合。
为了更直观,再看几张 regime 维度的图:

上图是 16 年里 regime 标签随时间的分布。可以看到,3 个 regime 是交替出现的——没有任何一个 regime 占据绝对主导,也没有任何一个 regime 集中在某一段特定时间。这进一步排除了”策略只适配特定时间段”的可能。

散点图把每个 trade 按”所在 regime 的波动率”和”实际 PnL”画出来。3 个 regime 的颜色分布在零线以上都明显厚于零线以下——盈利端在所有 regime 中都 extending 到更高的 PnL。
7. 总评:3 PASS + 1 WARN + 0 FAIL
把 4 个维度汇总:
| 检查 | 结果 | 判定 |
|---|---|---|
| Walk-Forward OOS | OOS/IS = 0.781 | PASS |
| 逐年 Sharpe | 8/9 年正收益 | PASS |
| 参数微扰 | 最大差 11.1% | WARN |
| Regime 分解 | 3/3 regime 盈利 | PASS |
3 PASS + 1 WARN + 0 FAIL → 未过度优化。
这版策略可以作为 production baseline,不必回退或继续调参。
这个结论让我松了一口气,但更让我感兴趣的是过程中的一个反讽发现。
8. 反讽:Meta-Labeling 的失败
在跑这套交叉验证之前,我曾经尝试过另一种”解决过度拟合”的思路——加一层 ML。
思路来自 López de Prado 的《Advances in Financial Machine Learning》。原书的建议是:第一层用简单规则生成信号,第二层用 ML 分类器(通常是 LogReg 或 RF)做”二级过滤”——只在 ML 也认为这是一个好信号的时候才下单。这叫 Meta-Labeling。
理论上这个方法很优雅:
- 一级信号负责召回(少漏机会)
- 二级 ML 负责精确(少犯错)
- 合起来既能抓住机会又能过滤假信号
我用当前策略的 607 笔交易做训练集,提取了 18 个特征(regime id、ADX、ATR%、MACD histogram、EMA 偏离度、连亏次数、距离上次交易的天数、小时、星期、月份……),训练了一个 LogReg + StandardScaler 的 pipeline。
离线 cross-validation 看起来很美:
- Precision 0.73,Recall 0.68
- 阈值 0.40 时”看起来”能保留 72.8% 的 PnL,过滤掉大部分亏损交易
然后我把它叠到原策略上做 walk-forward 实测。结果直接打脸:
| 变体 | Sharpe | MaxDD |
|---|---|---|
| R102 baseline | 1.3321 | 9.10% |
| R102 + Meta (thr=0.30) | 1.2959 | 8.67% |
| R102 + Meta (thr=0.40) | 0.85 | 12.5% |

thr=0.30:Sharpe 略降(1.33→1.30),MaxDD 略改善(9.10%→8.67%)。算是平局,但没有明显提升。
thr=0.40(离线 sweep 的”最优”):Sharpe 暴跌到 0.85,MaxDD 反而恶化到 12.5%。离线训练时的”最优阈值”在实战中直接崩了。
为什么会这样?复盘之后发现了几个问题:
1. 离线 sweep 高估了 72.8% 的 PnL 保留率,实际只保留了 83%
离线评估时,我们假设”如果 ML 拒绝了信号,那笔交易就真的不会发生”。但实战中,ML 拒绝信号 → 仓位为 0 → 下一根 bar SuperTrend 还在那个位置 → 同样的信号再次触发 → ML 再判断一次。
结果就是:filter 实际被调用了 1.67 倍(607 笔交易,但 ML 被触发了 1013 次)。原本该过滤掉的”坏信号”,有相当一部分在下一根 bar 重新进入并被放行了。
2. 训练集和测试集的 label 分布漂移
训练集(2010-2017)里”赢的交易”占 64%,但 OOS(2018-2025)由于 2020 年的 COVID 冲击,赢的交易占比下降到 58%。LogReg 学到的”先验概率”在 OOS 上是有偏的。
3. 阈值越严越过度自信
离线 sweep 说 thr=0.40 最好。但 0.40 在 OOS 上意味着——ML 必须非常确信才放行,而 ML 在 OOS 上本来就不够确信(分布漂移),所以大量本该抓住的机会被错杀。
这个反讽让我对”解决过度拟合”这件事有了新的理解:
加复杂度不等于减少过度拟合。 有时候,加一层 ML 反而引入了新的过度拟合源——你在训练集上挑了一个”最优阈值”,但这个阈值本身就是对训练集的 curve-fit。
简单胜过复杂。 这版策略不用 ML,规则透明(SuperTrend + EMA + ADX + MACD + Partial TP),每一行都有经济学解释。它的”反过度拟合”证据来自 4 个独立维度的交叉验证,而不是又一个 ML 模型的 metrics。
9. 那么现在的最终策略是什么样
经过这一轮验证,这版策略的所有参数都没有改动。最终交付的还是上一篇文章里那个策略:
| 参数 | 值 |
|---|---|
| atr_period | 14 |
| st_mult | 5.0 |
| ema_period | 162 |
| adx_threshold | 25.0 |
| macd_filter | 14 / 30 / 6 |
| partial_tp | 4.5×ATR / 90% |
| trail_mult | 6.0 |
| risk_percent | 0.3% |
| max_hold_bars | 210 |
| cooldown_bars | 75 |
16 年实测指标:
| 指标 | 值 |
|---|---|
| Sharpe | 1.3321 |
| Sortino | 1.9093 |
| Total Return | +244.80% |
| Max Drawdown | 9.10% |
| Win Rate | 64.4% |
| Profit Factor | 2.26 |
| 年正收益 | 16/17 |
| 最差年份 | -3.2% (2020) |
净值曲线和回撤与上一篇一致,这里不再重复贴图。唯一不同的是:这次我们有 4 维度交叉验证的证据,证明这组数字不是过度拟合的产物。
10. 这次实验真正说明的事
这次做的事情,本质上是让 AI 证明自己的工作不是 curve-fit。
我没有给它新的优化目标,没有让它继续进化。我给它的是 4 个独立的检验维度,每个维度都有明确的 PASS/WARN/FAIL 标准,没有任何调整空间。
它做出来的不是又一个”看起来更好”的策略,而是一份证据链:
- 用 Walk-Forward 证明:策略在没见过的数据上依然有效
- 用逐年 Sharpe 证明:不是靠少数年份拉均值
- 用参数微扰证明:策略站在宽阔高原上,不是针尖
- 用 Regime 分解证明:不依赖单一行情类型
更重要的是,这套验证本身是可以复现的——任何人拿到策略代码和这 4 个检查脚本,都能跑出同样的结论。这不是观点,是数据。
11. 我从这次实验中学到的事
1. 验证比优化更难,但也更重要
让 AI 跑 110 轮进化很容易,写一个指令就行。但要设计一套严谨的、能排除作弊的交叉验证体系,需要真正想清楚”过度拟合有几种可能的伪装形式”。
这个过程让我意识到:优化是体力活,验证是脑力活。 AI 可以承担前者,但后者目前还是人的核心价值。
2. 加复杂度 ≠ 减少过度拟合
这是 Meta-Labeling 失败给我的最大教训。直觉上”加 ML 应该更鲁棒”,但实际上 ML 模型本身就会引入新的过度拟合源(训练集偏差、阈值选择、分布漂移)。
简单透明的规则 + 严谨的交叉验证 > 复杂黑盒模型 + 自我吹捧的 metrics。
3. WARN 不是失败,是改进方向
策略在参数微扰维度上是 WARN(11.1%),但这不意味着策略不可信。它意味着 st_mult 下限稍敏感——这是一个可操作的改进方向,而不是一个回退理由。
完美的 plateau 是过拟合的另一种伪装。 一个真实有效的策略,总会在某个维度上有”敏感点”——因为真实市场的规律不会完美适配任何参数。
4. 健康的策略有亏损年
2020 年的 -3.2% 是这版策略健康的标志。如果有人给你看一个 16 年零亏损的策略,你应该扭头就走——那 100% 是过度拟合。
12. 下一步
这版策略现在是一个经过验证的 production baseline。我不打算继续动它的参数——动得越多,过度拟合的风险越大。
接下来的工作不在策略本身,而在它没回答的问题:
- 多品种验证:当前策略在 RB88(螺纹钢)上有效。在铁矿石、焦炭、沪铜等其他趋势品种上呢?这是必须做的下一步——一个只在单一品种上有效的策略依然有过度拟合嫌疑。
- 交易成本敏感性:现在的回测用的是合约默认手续费 + 滑点。如果把滑点放大 2 倍、3 倍,策略还赚钱吗?
- 参数稳定性时间维度:现有参数是在 16 年全量数据上挑出来的。如果只用 2010-2015 训练,参数会落在哪?跟现在这版接近吗?
这些问题的答案,决定了它是一个”经过验证的趋势策略”,还是一个”经过深度验证的趋势策略”。
但我现在可以说的是:至少它不是 R110、R112 那种站在悬崖边上的”最优”。它站在一个宽阔的高原上。
这就是这次实验想要证明的事。
参考资料
- 第一篇:一行代码不写,AI 自己进化出夏普 1.33 的策略
- 回测引擎:PowerQuant Backtest(Rust 实现,已开源)
- 验证维度:Walk-Forward / 逐年 Sharpe / 参数微扰 / Regime 分解(K-Means)
- Meta-Labeling 参考:López de Prado, Advances in Financial Machine Learning(实测在本策略上失败)
- 数据:RB88 螺纹钢连续主力,60min K 线,2010-2026
- 结论:3 PASS + 1 WARN + 0 FAIL,当前策略未过度优化,可作为 production baseline