小红书上有条 56 秒的量化教程,753 赞、1726 收藏。流程是这样:让 AI 列出 10 个「世界上好的量化策略」,写成代码跑回测,挑前三,再从不同角度优化迭代,就是你自己的系统了。
我没拿真行情去试。我造了一个保证谁都赚不到钱的市场:价格纯随机游走,明天涨跌跟过去一点关系都没有。然后在里面把这套流程跑了两万遍。
10 个零本事的策略回测 3 年,第一名的年化夏普平均 0.89,35% 的时候超过 1。拿前三名去下一年的新数据上跑,平均夏普 0.00。
夏普就是「每扛一份波动,换回多少收益」。年化 1 放在散户的回测截图里,已经是能拿出来晒的成绩了。在一个注定没人能赚钱的市场里,挑一挑就有。
更好玩的是,我那天第一次报的 0.89 其实报高了,第 2 章讲为什么。想直接拿公式看第 1 章,想拿去拆别人截图看第 4 章。
1. 运气能送你多少夏普:一个除法
一个零 edge 的策略,回测出来的夏普会围着 0 上下抖。抖多大,只看回测有几年:
单个策略回测夏普的标准差 ≈ 1 ÷ √回测年数
3 年回测,标准差约 0.58。意思是一个毫无本事的策略,3 年回测随手跑出个 0.6,一点都不稀奇。
你一口气试 N 个、只留最好的那个,期望值就是「N 个标准正态里最大那个的期望」再乘上这个标准差:
运气夏普 ≈ E[max_N] ÷ √回测年数
E[max_N] 不用推,查表:
| 一共试了几次 N | E[max_N] |
|---|---|
| 1 | 0 |
| 3 | 0.85 |
| 10 | 1.54 |
| 30 | 2.04 |
| 100 | 2.51 |
代进去对账:10 个策略、3 年,1.54 ÷ √3 = 0.89。两万次模拟跑出来也是 0.89。这就是一个除法。
我把 N 和年数各换几档,模拟和公式并排放:
| N | 回测年数 | 模拟:第一名夏普均值 | 公式 | 第一名 > 1 的比例 | 前三名下一年的夏普 |
|---|---|---|---|---|---|
| 3 | 3 | 0.49 | 0.49 | 12% | 0.00 |
| 10 | 1 | 1.54 | 1.54 | 82% | 0.00 |
| 10 | 3 | 0.89 | 0.89 | 35% | 0.00 |
| 10 | 10 | 0.49 | 0.49 | 1% | 0.00 |
| 30 | 3 | 1.18 | 1.18 | 72% | -0.01 |
| 100 | 3 | 1.45 | 1.45 | 99% | 0.00 |
| 100 | 10 | 0.79 | 0.79 | 8% | 0.00 |
最后一列全是 0,这是当然的,市场本来就没东西可赚。值得看的是前面几列,两件事挺反直觉:
- 多试涨得很慢。 从 10 次加到 100 次,E[max] 才从 1.54 涨到 2.51。反过来说,「我只试了三四个」也安心不了,试 3 个就已经白送 0.85 ÷ √年数。
- 回测拉长比少试管用。 100 个挑一个、回测 10 年,运气夏普 0.79;10 个挑一个、回测 3 年,0.89。前者多试了十倍,反倒更不容易被骗。
2. 我第一次报的 0.89,其实报高了
看完那条视频,我当时随手写下的结论是「10 个随机策略挑第一,夏普平均 0.89」。数没算错,模型偷了个懒:我默认 10 个策略互相独立,各抖各的。
可视频里那 10 个是双均线、动量、海龟这一类,全盯着同一条价格曲线。两条均线交叉规则,周期差一点,大部分日子信号是一样的。这根本算不上 10 次独立抽奖。
今天补了一组。在同一条随机游走价格上跑 10 条真规则:4 组均线交叉、3 个动量、3 个反转。它们日收益两两之间的平均绝对相关是 0.37。结果:
- 第一名样本内夏普平均 0.79(独立模型是 0.89)
- 第一名 > 1 的比例 26%(独立模型 35%)
- 前三名下一年:0.00
0.79 × √3 = 1.38,回到 E[max] 表里找,落在 N = 7 附近(7 次对应 1.35)。10 条规则,大概只相当于抽了 7 次。
然后试视频说的「优化迭代」。一个均线交叉,快线 10 档、慢线 10 档,去掉快线不短于慢线的组合,剩 89 组参数全跑一遍,挑样本内最好的。直觉上这是挑了 89 次,应该很吓人。
结果第一名平均 0.83,34% 超过 1,下一年 0.00。0.83 × √3 = 1.45,约等于独立挑 8 到 9 次。
所以公式要改一个字:里面的 N 是有效试验次数,不是你按了几次回车。参数网格里相邻两格基本是同一个策略,89 次只值八九次。
先别高兴。这反过来意味着你数不出自己的有效 N。我能算出「约 7 次」「约 8 到 9 次」,是因为我在一个知道答案的假市场里跑过。真实回测里没有这个参照。保守的做法只有一个:按原始次数算,它一定是上界。
3. 一年的样本外,救不了你
视频最后一步说「先模拟盘,稳定了再上真钱」。方向是对的,问题出在时长。
把第 1 章的公式倒过来用:样本外只跑一年,单个策略夏普的标准差是 1 ÷ √1 = 1。我模拟里「前三名下一年夏普的平均」本身的标准差是 0.58,刚好等于 1 ÷ √3,三个取平均。对得上。
想在两个标准差上把一个真本事的策略和运气分开,需要的年数是:
需要的样本外年数 ≈ (2 ÷ 真实夏普)²
真实夏普 0.5,要 16 年。真实夏普 1,要 4 年。模拟盘跑一两个月,能证明的只有一件事:下单程序没写错。
还有一层:0.00 是两万次的平均。单拎某一次出来,前三名下一年跑出 +0.58 以上,大约六次里就有一次。那一次的主人会觉得「样本外也验证过了」,然后上真钱。
4. 拆别人的截图:反推他试了多少次
有人发截图:「回测 3 年,年化夏普 1.0」。
把夏普乘回 √年数:1.0 × √3 = 1.73。去 E[max] 表里找,大约 15。意思是在一个完全没 edge 的市场里,独立试 15 个左右的策略,第一名平均就是这个成绩。
几个对照,同一张表算的:
- 1 年回测,夏普 1.0 → 1.0,大约试 4 次就有
- 1 年回测,夏普 2.0 → 2.0,大约 28 次
- 3 年回测,夏普 1.5 → 2.6,大约 130 次
- 10 年回测,夏普 1.0 → 3.16,试 300 次也够不着
最后一条算好消息:长回测上的高夏普,很难靠瞎试撞出来。真正要警惕的组合是短回测 + 高夏普 + 不说自己试了几次。
AI 写策略让这事更麻烦。以前手写 10 个策略要一周,现在让 AI 吐 10 个只要一分钟。试验变便宜了,运气夏普一分钱没打折。
所以看到截图先问两句:回测几年?一共试了多少个,参数组合算不算?第二句答不上来,就按至少几十次算。
5. 这套模拟哪里不能信
- 收益是正态分布,没有肥尾,也没有「大跌扎堆来」的波动聚集。真实行情肥尾,单个策略夏普抖得更凶,运气夏普只会更高。这个我没测。
- 交易成本只试了一档:每单位换手扣 5 个基点。扣完之后,10 条规则的第一名样本内从 0.79 掉到 0.67,前三名下一年从 0.00 变成 -0.10。运气照拿,成本照扣,样本外就转负了。别的成本档没跑。
- 视频真正的流程是「10 个策略 × 每个再调参」,这个组合我没跑。按上面的估法,它的有效 N 落在 7 和 7 × 9 之间,具体多少不知道。
- 这套模拟只证明了一件事:「挑前三」这个程序在零 edge 的世界里分不出好坏。它没有证明那些经典策略在真实市场里没 edge。
- 「10 条规则约等于 7 次」「89 组参数约等于 8 到 9 次」,都是在一种波动率、一种随机游走下估的。换成趋势更强的序列,规则之间相关会变,这两个数也会变。
6. 下次自己回测,按这个顺序
- 先数总试验次数。参数组合算,AI 吐出来又扔掉的草稿也算。
- 用 E[max_N] ÷ √年数 算出运气夏普。你的结果减掉它,剩下的才值得看。
- 回测不够长,就别盯夏普小数点后面。3 年回测,正负 0.58 一个标准差,这个范围里的数都可能是抖出来的。
- 样本外按 (2 ÷ 预期夏普)² 算够年数。不够,就承认它只是在测程序。
- 按真实换手扣成本再跑一遍,扣完转负的直接扔。
- 想知道自己的有效 N,就在随机游走上把整套流程原样跑一遍,看第一名能拿多少。十分钟的事,下面这段改改就能用:
import numpy as np
rng = np.random.default_rng(1)
# 10 个零 edge 策略,3 年日收益,挑样本内夏普最高的
trials, N, days = 20000, 10, 252 * 3
r = rng.normal(0, 0.01, (trials, N, days))
sr = r.mean(-1) / r.std(-1) * 252 ** 0.5
best = sr.max(1)
print("第一名夏普均值", best.mean().round(2), "超过 1 的比例", (best > 1).mean().round(3))
# E[max_N] 表:用来把 夏普 × √年数 反推回 N
z = rng.standard_normal((100000, 300))
emax = np.maximum.accumulate(z, axis=1).mean(0)
for n in (3, 10, 30, 100, 300):
print(n, emax[n - 1].round(2))