回测过拟合怎么看出来?参数最优化之后该做的三件事
你把参数扫了一轮,回测曲线漂亮到让你想直接按 live trading——年化 120%、最大回撤 8%、胜率 62%。 接下来大概率会发生的事:实盘第一个月就吃掉回测三年净利的一半。这不是坏运气,是「过拟合」 (overfitting)留下的收据。
这篇不评论你用哪个策略逻辑——逻辑本身可能没问题,问题往往出在参数怎么挑的、用哪段数据挑的。 给你三件事,都是最优化按下去之后、把策略搬上实盘之前该做的验证,一件都不能省。
先讲清楚:过拟合到底把「什么」配给「什么」
过拟合的机制很直接:你在同一段历史数据上,同时做「选策略」与「调参数」两件事, 最后挑到的那一组,本质上是对这段历史的噪音拿到满分——不是对市场的 结构性行为拿到满分。
把数据想成一张考卷。你先看完考卷再回头挑答题方法,考出来当然全对——但那不能推论你 下一张考卷也会这样。过拟合的核心就是这个:你用未来看过的数据回头挑参数,然后用同一份 数据验证挑得对不对,整个流程里没有任何信息在告诉你这组参数对「没见过的数据」有没有效。
第一关:把数据切开,最优化只能碰 in-sample
最基本、也最常被跳过的动作:数据一开始就切两块。一块给你随便玩—— 参数怎么调、指标怎么加、规则怎么改都可以;另一块锁起来,最优化跑完那一刻才第一次 拿出来看。这块不能参与任何选择,一旦拿出来看过就报废,不能再用。
切法没有唯一正解,看你手上有多少年数据。(本文写作时整理的常见基准)
这里有个很容易骗自己的做法:你把 out-of-sample 拿出来看,发现不理想,回头改参数, 再看一次。这个当下 OOS 就报废了——它已经被最优化流程间接碰过, 跟本来的 in-sample 没有本质差别。看第二次的 OOS 不叫 out-of-sample,叫另一段 in-sample。
想避开这个陷阱,可以再切第三块叫 hold-out:整个开发过程完全不碰, 策略要交付给实盘那天才第一次看。这块小一点没关系(15% 到 20%),它的作用是给你 「这组参数在真的没见过的数据上长什么样」一个诚实的参考。
第二关:walk-forward — 让「最优化」变成一直发生的事
切一次 in-sample/out-of-sample 有个弱点:你只验证了「用前 70% 挑的参数, 在后 30% 上还有效」这一个切点。市场如果换了个模式,你这组挑三年前数据调出来的 参数,可能三年来都在慢慢失效,你只是还没发现。
walk-forward 的想法很单纯:把数据切成很多小段,每段都跑一次「用前面 N 个月调参数、 用接下来 M 个月验证」,然后把「验证段」全部拼起来当成一条连续的样本外曲线。 这条曲线比较接近「真的照这个流程做的话,这几年会拿到什么」。
| 单次切样本外 | walk-forward | ||
|---|---|---|---|
| 验证了几个时间点? | 1 个切点 | 很多个滚动窗口 | |
| 对「市场换模式」的抵抗力 | 低(用一段旧数据挑) | 高(每个窗口都重新挑) | |
| 设定复杂度 | 简单 | 要决定窗口长度与滚动步长 | |
| 运算成本 | 跑 1 次最优化 | 跑 N 次最优化 | |
| 最像实盘的行为 | 不太像 | 接近你会怎么定期重调 |
两种验证方式的取舍。walk-forward 较贵,但更接近「策略要定期重调」的现实。
walk-forward 有个副产品同样重要:每个窗口挑到的「最佳参数」是什么?如果十个窗口挑到十组差很多的值,那不是这个策略敏感——是这个策略没有真的规律, 只是每次都在配当下那段噪音。反过来,十个窗口挑到差不多的参数,那才是真的抓到什么。
第三关:参数敏感度 — 你挑到的是山峰还是山脊?
同样一组回测拿到 Sharpe 2.1,可能是两种很不一样的东西:山峰—— 你刚好挑到那唯一的高点,任何一个参数挪一格数字就掉到 Sharpe 0.5;山脊—— 你周围一片参数都在 Sharpe 1.8~2.1,挪一格还是差不多。第一种是过拟合的教科书范例, 第二种才是可以搬上实盘的东西。
把「参数 vs 绩效」的 3D 热图画出来看是最直观的做法。真的有规律的策略,图看起来会是 一片缓坡;过拟合的策略看起来是几根尖刺。这一步不需要新工具,用你已经在跑的最优化 结果自己画。
顺带一提:数据量够不够支撑你调的参数个数
参数愈多,过拟合愈容易——这句话几乎是常识,但少有人讲具体门槛。这里列一个粗略的 经验值,来自机器学习的一般原则(不是交易独有):每多一个要调的参数,你需要的独立 样本数会加倍成长。把「回测跑过的交易笔数」当成样本数来估:
这是粗略的健康检查值,不是精确门槛。策略愈复杂,数字要往上抓。
如果你的策略跑三年只成交 40 笔,却在调 5 个参数,那不是「策略太挑」——是数据量根本 不够让你分辨「真的有效」与「刚好配到」。这种情况下,唯一有意义的动作是换更高频的 timeframe、或把参数个数砍到 2 个以内。
三关都过了:实盘上线前的最后两步
就算前面三关全过,也不建议直接开正常仓。上线之后的两件事同样关键:
- 先开 1/3 或更小的仓位跑一到两个月。回测会漏算的东西——真实成交延迟、滑点、部分成交、交易所偶尔的拒单——只有实盘会告诉你。
- 纪录每一笔回测 vs 实盘的偏差。同一根 K 棒的信号、实际成交价、以及最终 PnL 有没有系统性偏离回测预期。
- 设定一个停止线:实盘累积亏损超过 X% 或连续 N 笔偏离回测,就先停下来检查——不要等到已经吃完 3 个月才发现不对。
- 要把仓位放大之前,用实盘这一到两个月的数据再跑一次
walk-forward验证。这是最诚实的一份 out-of-sample 数据。
仓位怎么从小开始加大,跟策略逻辑是两回事,属于资金管理的问题。这一段可以另外看 Kelly 公式与仓位大小 那篇,那边有讲「半 Kelly」为什么是实务常用的折衷。
这三关都帮不了你的事
讲完做得到什么,也该讲清楚做不到什么——这是这篇最容易被误解的地方。walk-forward 与参数敏感度都是「同一个市场环境」的验证, 它们对付不了下面这几种东西:
执行成本被低估:多数回测工具预设的手续费、滑点都是理想值, 真实的做市商库存、taker 手续费、部分成交都会啃掉一部分理论净利。
信号稀疏度:一年只触发 10 次的策略,就算 walk-forward 过关, 也可能只是这 10 次刚好对——样本太少,统计上没有意义。
你自己会不会关掉它:一组回测看起来能扛 40% 回撤,实盘跌到 20% 你就 睡不着关掉了——这是行为问题,测不出来。
诚实的一段:把回测数字放在该有的位置
这篇本身没有贴任何具体的策略回测数字,是刻意的——没有标的、期间、参数、样本外设定 一起附上的绩效数字,不管数字多漂亮,读者拿去做决策都是危险的。过去绩效不代表未来表现,这句话在回测结果上尤其成立: 回测是模拟,不是实单纪录。
如果你要在自己的文章、Twitter 或给付费订阅者的信号里放回测结果,最低限度该附上: 标的(例如 BTCUSDT.P)、时间范围(例如 2023-01 至 2025-12)、 参数组(不要只写「经最优化」)、有没有 walk-forward、以及一句「这是模拟结果, 不代表实盘获利」。少一个都会让别人没办法判断这份绩效值不值得跟。
这也是为什么建议在做量化之前先看 散户怎么开始做量化交易 那篇——里面有一整段在讲「年化 500% 不是 overfitting 就是还没崩」,可以当这篇的姐妹篇。
常见问题
walk-forward 的每个窗口都要重新挑参数吗?那不是等于一直在最优化?
为什么参数敏感度要 ±10%,不是 ±20% 或 ±5%?
in-sample 表现太差,能不能回去换策略逻辑,再走一次流程?
什么样的策略最容易过拟合?
工具面上,这三关要怎么跑?
backtesting.py、vectorbt)自己组。先去自己工具的设定里找 「walk-forward」或「rolling window」的选项,没有的话就得把数据导出到别的工具做。Get started
TVSBot 是非托管的 TradingView → 交易所自动下单平台。把你验过的策略接上 webhook,用你自己的 API key,先开 dry-run 或小仓位跑一轮实盘——这是「三关通过之后」该接的下一段。
免费开始