← 返回目录
p3.4动手⏱ 约 18 分钟

多重检验与数据挖掘偏差

P2 全章 49 次检验的总账,以及 p0.1 那个「最优参数」的数学判决

🔎 最后验证 2026-07📚 来源:随机基线、P2 校正、参数搜索模拟与通缩夏普门槛于 2026-07-31 在 ECS 实测(随机种子 20260731),输出见 code/outputs/stdout.txt🧰 statsmodels 0.14.6、scipy 1.18.0、numpy 2.5.1
为什么学这个

从 p2.1 开始,我一边做检验一边记账。现在把账本摊开:

检验次数
p2.1 MACD 金叉6
p2.2 三条口诀18
p2.3 布林带两个方向8
p2.4 三组量价条件12
p2.5 共振五种组合5
合计49

纯随机数据上做 49 次检验,会"发现"多少显著?实测:平均 2.45 个,至少出现 1 个的概率是 92.2%,最多的一次出现了 8 个

这一节做四件事:算随机基线、校正 P2、量化参数搜索的代价,最后回到 p0.1——当时那个夏普 1.28 的"最优参数",低于它自己的噪声门槛 1.55。

我们当时是用样本外结果证明它是噪声的。这一节告诉你:用统计理论,事先就能判定。

💡 打个比方

一个人连续三次猜中硬币正面,你会觉得他有点东西。

但如果房间里有 1000 个人同时在猜,必然有大约 125 个人连中三次

从这 125 个人里挑出一个,给他一个"预测大师"的头衔,再让他继续猜——他会回到 50%

P2 的 49 次检验、p3.3 的 1035 对协整、p0.1 的 591 组参数,都是那个房间。

一、随机基线:49 次检验能"发现"多少

2000 次模拟,每次做 49 个完全无效应的检验(零假设为真时 p 值服从均匀分布):

平均「显著」个数 2.45(理论期望 2.45)
至少 1 个显著的概率 92.2%
至少 3 个显著的概率 44.8%
至少 5 个显著的概率  9.4%
最多的一次出现了 8 个显著

做 49 次检验,"至少出现几个显著"几乎是必然事件。

所以「我找到了一个 p<0.05 的信号」这句话,单独说出来时信息量约等于零。它必须和"我一共试了多少次"一起出现,才有意义。

二、校正 P2:35 个显著,还剩 21 个

把 P2 各节实际报告的 49 个 p 值放在一起校正:

检验总数 49 次
  未校正 p<0.05          35 个显著
  Bonferroni 校正后      21 个显著(阈值 p < 0.00102)
  BH-FDR 校正后          33 个显著

被 Bonferroni 淘汰的 14 个(未校正显著、校正后不显著):

结论原 p 值
p2.1 金叉 1 日0.0027
p2.1 金叉 3 日0.0120
p2.2 RSI>70 60 日0.0272
p2.2 RSI<30 5/10/20 日0.0400 / 0.0069 / 0.0025
p2.2 J<0 1/3 日0.0035 / 0.0351
p2.3 上轨 10 日0.0017
p2.3 下轨 10 日0.0053
p2.4 OBV 背离 5 日0.0017
p2.4 缩量 5 日0.0267
p2.5 ② / ③0.0239 / 0.0015

p2.1 那两颗星没扛住。当时我在课里说"这 2 次显著是真的吗?单看 p 值回答不了"——现在有答案了:扛不住校正。

经 Bonferroni 仍然显著的 21 个(最硬的结论):

  • p2.2 RSI 上穿 70:1/3/5/10/20 日全部存活(p 最小到 0.00001)
  • p2.3 布林上轨:1/5/20 日存活;下轨 1/5 日存活
  • p2.4 OBV 顶背离:1/10/20 日存活;放量上涨 1/5/10/20 日全部存活;缩量 1 日存活
  • p2.5 ①、①+②、①+②+③ 存活

口径说明:这里的存活判定用的是 P2 各节的原始 p 值(未叠加 Newey-West 重叠样本修正)。若先按 p3.2 的做法用 Newey-West 修正后的 p 值再做多重检验,存活集合会更小——例如 OBV 顶背离 20 日(原始 t=4.24、NW 后 2.35)会掉出去。两节口径不同,结论方向一致:重叠越重,可信的越少。

⚠️ 避坑

Bonferroni 和 BH-FDR 差了 12 个,该信哪个?

  • Bonferroni 控制的是"任何一个假阳性的概率",极其保守。做 49 次检验,阈值降到 0.05/49 = 0.00102
  • BH-FDR 控制的是"错误发现的比例",在 49 个里允许约 5% 是错的

用哪个取决于代价:

  • 如果一个假阳性会让你投入真金白银(比如据此上线一个策略)→ 用 Bonferroni
  • 如果这只是研究阶段的筛选、后面还有样本外和实盘小仓验证 → 用 BH-FDR,别把真信号也筛掉

本课的立场:进入 P5 策略开发的,必须过 Bonferroni。因为 P5 之后就是真金白银了。

🔧 动手做:49 个纯噪声检验,平均蒙出几个"显著"?(5 分钟)

python multiple_testing.py。它模拟 2000 次,每次做 49 个完全无效应的检验(对应 P2 全章的检验次数)。

你会看到(实测):每轮平均出现 2.45 个"显著"(纯属运气),至少 1 个显著的概率 92.2%、至少 5 个 9.4%。而 P2 全章 35 个"显著"里,Bonferroni 校正后只剩 21 个。

想明白:显著性会被比较次数吃掉。你检验/搜索得越多,"最优"就越漂亮——但那多半是噪声的最大值(和 p5.4 的参数尖峰是同一件事)。报任何"显著"结果,必须同时报你一共试了多少次,不然那个 p 值毫无意义。

三、参数搜索的代价:搜得越多,"最优"越漂亮

真实夏普为 0 的纯随机数据上,每"组参数"= 一个与收益无关的随机持仓序列,取其中最优:

搜索组数最优夏普(均值)P95最优夏普>1 的概率
1-0.010.510.0%
100.350.780.3%
500.510.922.0%
2000.601.005.3%
10000.731.1314.0%

真实夏普是 0。但只要你肯搜,最优夏普就会稳步上升——搜 1000 组,有 14% 的概率能得到一个夏普 > 1 的"策略"。

"我回测出夏普 1.5"这句话,不说搜索次数就没有意义。

四、通缩夏普比率:把搜索次数折算成门槛

Bailey 与 López de Prado 给出了这个折算:搜索 N 次后,即使全是噪声,最大夏普的期望约为

E[max SR] ≈ σ_SR × [(1-γ)Φ⁻¹(1-1/N) + γΦ⁻¹(1-1/(N·e))]

γ 是欧拉常数。你的夏普必须超过这个门槛,才算携带了信息。

取 σ_SR = 0.5(夏普估计的标准差,典型量级):

搜索次数 N噪声下的期望最大夏普
10.00
100.79
501.14
2001.38
5911.55
10001.63
100001.93

回到 p0.1

第一节我们在 2005–2015 样本内做了 591 组参数网格搜索,冠军是 MA5/55,样本内夏普 1.28

591 组的噪声门槛是 1.55。

1.28 < 1.55。

⚠️ 避坑

这就是 p0.1 那个策略样本外只剩 1.97% 年化的数学原因。

当时我们是怎么知道它不行的?用样本外结果——把参数拿到 2016–2026 跑一遍,发现输给了躺平。那是事后验证。

而现在你知道:当时就能事先判定。样本内夏普 1.28,还没达到"591 次搜索下纯噪声能达到的水平",它就不该被当成一个发现。

这是这一章最实用的一条:在做样本外之前,先用搜索次数算一个门槛。达不到门槛的,连样本外都不必浪费。

五、把它变成习惯

  1. 记账:从第一次检验开始记搜索次数。参数网格、持有期、阈值、标的池筛选——每一个你试过又放弃的选择,都算一次
  2. 报数:任何一个 p 值或夏普,必须与"试了多少次"一起出现
  3. 设门槛:用通缩夏普算出你这次搜索的噪声门槛,再看结果够不够
  4. 分级:研究筛选用 BH-FDR,上线前用 Bonferroni
❓ 测验
你试了 200 组参数,找到一个样本内夏普 1.35 的策略。按本节方法,下一步最该做什么?
✏️ 填空
搜索 N 组参数后,即使全是噪声也能达到的最大夏普水平,称为通缩夏普比率给出的噪声 ___ 。

📌 免责:本课为技术教学,统计与模拟结果均为历史/合成数据演示,不构成投资建议。

✅ 小结

P3 章完结。这一节把整门课前三章的账算清了:

  • 随机基线:49 次检验,纯噪声下平均出现 2.45 个"显著",至少 1 个的概率 92.2%
  • 校正 P2:未校正 35 个显著 → Bonferroni 后 21 个(BH-FDR 33 个);p2.1 金叉那两颗星没扛住
  • 搜索代价:真实夏普为 0 时,搜 1000 组有 14% 概率得到夏普>1 的"策略"
  • 通缩夏普门槛:591 组 → 门槛 1.55;而 p0.1 的冠军夏普 1.28——低于门槛,事先就能判定

四条习惯:记账、报数、设门槛、分级(研究用 BH-FDR,上线用 Bonferroni)。

到这里,P0–P3 的路走完了:知道错在哪(P0)→ 数据可信(P1)→ 因子有评价(P2)→ 结论有统计(P3)

下一章 P4 开始造东西:自研回测引擎。十节,从向量化与事件驱动的取舍开始,一路做到撮合层(T+1、涨跌停)、账户层、双路对账、与 backtrader 对拍,最后打包成一个 pip 可安装的开源库。

那一章会用到这三章的每一条纪律——而它交付的,是这门课的第一个成品。

下一节 → P4 章:自研回测引擎
🔎 来源与核验· 4 条,点开核对
本节每个关键论断都对应一个可追溯的来源 —— 这是本课程"靠谱、不过时"的底线。
「2000 次模拟、每次 49 个无效应检验:平均显著个数 2.45(理论期望 2.45),至少 1/3/5 个显著的概率为 92.2%/44.8%/9.4%,单次最多出现 8 个」
📚 本节 code/multiple_testing.py,2026-07-31 于 ECS 实测(随机种子 20260731),输出见 code/outputs/stdout.txt✓ 已核验 2026-07
「P2 全章 49 次检验:未校正 p<0.05 者 35 个,Bonferroni 校正后 21 个(阈值 0.00102),BH-FDR 校正后 33 个;被 Bonferroni 淘汰 14 个,含 p2.1 金叉 1 日(p=0.0027)与 3 日(p=0.0120)」
📚 同上实测(p 值取自 P2 各节 outputs/stdout.txt)✓ 已核验 2026-07
「参数搜索模拟(2500 天纯随机收益,真实夏普 0):搜索 1/10/50/200/1000 组时最优夏普均值为 -0.01/0.35/0.51/0.60/0.73,P95 为 0.51/0.78/0.92/1.00/1.13,最优夏普>1 的概率为 0.0%/0.3%/2.0%/5.3%/14.0%」
📚 同上实测✓ 已核验 2026-07
「通缩夏普门槛(σ_SR=0.5):N=10/50/200/591/1000/10000 时噪声下期望最大夏普为 0.79/1.14/1.38/1.55/1.63/1.93;p0.1 的 591 组网格冠军样本内夏普 1.28,低于 1.55 的门槛」
📚 同上实测,公式据 Bailey & López de Prado 的 Deflated Sharpe Ratio✓ 已核验 2026-07
σ_SR 取 0.5 为典型量级,实际应按样本长度与收益分布估计;门槛数值随该参数变化。
智图软件的赞赏码
都看到这了,打个赏呗!
接下来 · p4.1
引擎解剖:向量化 vs 事件驱动
继续读下一节 →