量化交易中文教材

第 10b 章 多重检验与策略过拟合

本章对应 Wasserman 原书第 10 章 10.7 节"多重检验"(PDF p.177–180)。原书只用了 4 页,但对量化研究而言,这 4 页可能是整本书里最值钱的内容:几乎每一个"回测很好、实盘失效"的故事,背后都是多重检验没有校正。所以本章先把原书的 Bonferroni 与 Benjamini–Hochberg 方法讲透(含证明思路),再用较大篇幅把它们落到因子挖掘、参数寻优、策略筛选上,介绍数据挖掘偏差、压缩 Sharpe 比率(Deflated Sharpe Ratio)等量化界的标准做法。凡原书没有的内容都会注明。

学习目标

  1. 能说清楚为什么同时做 \(m\) 个检验时,"至少一次误拒"的概率远大于单个检验的 \(\alpha\),并会计算。
  2. 区分族错误率(FWER)、错误发现比例(FDP)与错误发现率(FDR),知道各自适合什么研究场景。
  3. 会用 Bonferroni、Holm 控制 FWER,会用 Benjamini–Hochberg(及其依赖情形的 Benjamini–Yekutieli 版本)控制 FDR,能手算原书例 10.28,并理解 BH 定理的证明思路。
  4. 理解"从 \(N\) 个候选中选最好的"会系统性地抬高样本内表现,会用预期最大值公式估计这种偏差。
  5. 会计算概率 Sharpe 比率(PSR)与压缩 Sharpe 比率(DSR),理解试验之间相关时"有效试验数"的概念。
  6. 能为自己的量化研究流程设计防过拟合的措施:记录全部试验、样本外保留、多重检验校正、经济逻辑先验。

读前导读

这一章在解决什么问题。 上一章的 p 值都是针对"事先定好的一个检验"。现实中,研究员试了 200 组参数、100 个因子,只把最好看的那个拿出来报告 p 值,这个 p 值就失真了。你在基金评价里一定见过这个现象的另一面:同一家公司旗下几十只基金,总有一两只"连续三年排名前 10%",事后看多半是运气——这叫幸存者偏差,统计上就是本章的多重检验问题。

本章分两部分。前半部分是原书内容:同时做很多检验时,"至少错一次"的概率怎么控制(Bonferroni、Holm),以及换一个更宽松也更实用的目标——"宣布的发现里错的比例"怎么控制(Benjamini–Hochberg)。后半部分是量化专题:从 \(N\) 个策略里挑最好的会把样本内 Sharpe 系统性抬高多少,怎样用压缩 Sharpe 比率(DSR)把这部分"运气"扣掉。

需要先想起来的数学。

  • 并集的概率不超过概率之和(次可加性)。\(\mathbb P(A\cup B)=\mathbb P(A)+\mathbb P(B)-\mathbb P(AB)\le\mathbb P(A)+\mathbb P(B)\)。推广到 \(m\) 个事件也成立,不需要独立。Bonferroni 方法只用这一条。例:两个各 5% 的事件,至少一个发生的概率不超过 10%。
  • 独立事件的"至少一次"。 至少一次 = 1 − 一次都没有,独立时"一次都没有"的概率是各自概率的乘积:\(1-(1-\alpha)^m\)。和信用组合里"至少一家违约"的算法相同。
  • 示性函数与期望的线性。 BH 证明把 FDP 拆成 \(\sum_i\frac{I(\text{第 }i\text{ 个被错拒})}{R}\) 再逐项求期望,用的是"和的期望 = 期望的和"。见 第 00 册第 07 章 概率中的分析工具。
  • 调和级数 \(\sum_{i=1}^m1/i\approx\ln m+0.577\)。 BY 方法的修正因子。\(m=10\) 时约 2.93,\(m=1000\) 时约 7.49,增长很慢。见 第 00 册第 04 章 级数与收敛。
  • 正态分位数 \(\Phi^{-1}\) 与 \(\max\) 的分布。 \(\Phi^{-1}(1-1/N)\) 是"\(N\) 个标准正态里,大约只有一个会超过的水平"。预期最大值公式就是围绕这个量展开的。

怎么读这一章。 10b.1–10b.3 必读,概念简单但非常重要,读完应能熟记 Bonferroni 的 \(t\) 门槛表。10b.4 的 BH 方法要掌握算法(10b.4.2)和直觉(10b.4.3);10b.4.4 的证明第一次可以跳过。10b.5 是选择方法的实用指南。10b.6 是对量化工作价值最高的部分,建议细读 10b.6.1(选择偏差)和 10b.6.2(PSR/DSR),并对照 10b.7.2 的输出体会"\(t=3.55\) 的明星策略其实是纯噪声"。


10b.1 问题:检验做多了,总会"发现"点什么

原书 10.5 节的 DNA 微阵列例子里有 2638 个基因。对每个基因做一次水平 \(\alpha=0.05\) 的检验,每个检验单独误拒的概率是 5%,但至少一个被误拒的概率要高得多。设 \(m\) 个检验相互独立且原假设全为真,则

\[\mathbb P(\text{至少一次误拒})=1-(1-\alpha)^m.\]
\(m=20\) 时约为 \(64\%\),\(m=100\) 时约为 \(99.4\%\)。而误拒个数的期望是 \(m\alpha\):2638 个"无效基因"里平均会有约 132 个 p 值小于 0.05。

推导拆解:两个公式分别用了不同的工具。"至少一次":每个检验"不误拒"的概率是 \(1-\alpha\),独立时全都不误拒的概率是 \((1-\alpha)^m\),取补集。\(m=20\):\(0.95^{20}\approx0.358\),所以至少一次约 64%。"误拒个数的期望":误拒个数 \(=\sum_iI(\text{第 }i\text{ 个被误拒})\),每项期望为 \(\alpha\),由期望的线性得 \(m\alpha\)。注意后者不需要独立——即使因子高度相关,平均误拒数仍是 \(m\alpha\),相关性改变的只是误拒数的波动(要么一个都没有,要么一来一大串)。 金融直觉:这和"100 位基金经理掷硬币选股"的思想实验一样。即使人人无技能,连续 5 年跑赢基准的经理平均也有 \(100\times0.5^5\approx3\) 位,媒体报道的就是这 3 位。

这正是第 10a 章定理 10.14 的直接推论:原假设为真时 p 值服从 Uniform(0,1),抽 \(m\) 次,自然有约 \(m\alpha\) 次落在 \([0,\alpha]\) 里。数据挖掘中要检验的假设可能成千上万甚至上百万个,这个问题无法回避。

量化研究就是一个巨大的多重检验。 一个研究员一年里会尝试多少个"假设"?

  • 因子挖掘:几百上千个候选因子(各种财务比率、价量组合、遗传规划自动生成的公式);
  • 参数寻优:一条均线策略的快慢线长度网格就有上百个组合,再乘上止损、持有期、调仓频率;
  • 隐性的选择:样本区间("从 2010 年开始看")、股票池("剔除 ST、剔除小盘")、信号变换(排名还是 z 分数、是否行业中性)、是否剔除某段"异常"行情……

Gelman 称后者为"分岔小径的花园"(garden of forking paths):即使研究员只正式跑了一次检验,他在看到数据后做出的每一个选择,都相当于暗中做了一次检验。这些隐性试验同样需要计入。


10b.2 错误率的几种定义

考虑 \(m\) 个检验 \(H_{0i}\) vs \(H_{1i}\),p 值为 \(P_1,\dots,P_m\)。设我们拒绝了某些原假设,结果可以分成四格(原书表 10.2):

未拒绝 \(H_0\) 拒绝 \(H_0\) 合计
\(H_0\) 真 \(U\) \(V\)(错误发现) \(m_0\)
\(H_0\) 假 \(T\) \(S\)(正确发现) \(m_1\)
合计 \(m-R\) \(R\) \(m\)

只有 \(m\) 与 \(R\) 能观测到,\(V,S,m_0,m_1\) 都是未知的。

  • 族错误率(family-wise error rate, FWER):\(\mathbb P(V\ge1)\),至少有一个错误发现的概率。
  • 错误发现比例(false discovery proportion, FDP):
    \[\text{FDP}=\begin{cases}V/R,&R>0\\0,&R=0\end{cases}\]
    即"宣布的发现中有多大比例是假的"。它是随机变量。
  • 错误发现率(false discovery rate, FDR):\(\text{FDR}=\mathbb E(\text{FDP})\)。

FWER 要求"一个都不能错",FDR 只要求"错的比例平均不超过 \(\alpha\)"。前者严格得多。

金融直觉:两种错误率对应两种管理目标。FWER 像风控的"零容忍":一年内出一次重大事故就算失败,适合"上线一个实盘策略"这种一次错误就要付真金白银的决定。FDR 像信贷审批的"不良率":允许批出的贷款里有少量坏账,只要坏账比例控制住即可,适合"初筛一批候选因子"这种后面还有复核环节的场景。\(V/R\) 中 \(R=0\) 时定义 FDP 为 0,是因为一个都没宣布就谈不上"宣布的里面错了多少"。


10b.3 控制 FWER:Bonferroni 与 Holm

10b.3.1 Bonferroni 方法

规则。 若 \(P_i<\alpha/m\),拒绝 \(H_{0i}\)。

定理 10.24。 用 Bonferroni 方法,FWER \(\le\alpha\)。

证明。 记 \(R\) 为"至少一个真原假设被拒绝",\(R_i\) 为"第 \(i\) 个真原假设被拒绝"。由概率的次可加性 \(\mathbb P(\bigcup A_i)\le\sum\mathbb P(A_i)\),以及原假设为真时 p 值均匀分布(定理 10.14),

\[\mathbb P(R)\le\sum_{i:\,H_{0i}\text{真}}\mathbb P(R_i)=\sum_{i:\,H_{0i}\text{真}}\frac{\alpha}{m}=\frac{m_0}{m}\alpha\le\alpha.\]

证明只用到了次可加性,不需要检验之间独立——这是 Bonferroni 最大的优点:在任何依赖结构下都成立。

推导拆解:"至少一个真原假设被拒"就是各个 \(R_i\) 的并集 \(\bigcup_iR_i\),所以第一步是次可加性。第二步:第 \(i\) 个原假设为真时 \(P_i\sim\text{Uniform}(0,1)\),被拒即 \(P_i<\alpha/m\),概率恰为 \(\alpha/m\)。第三步:真原假设有 \(m_0\) 个,求和得 \(m_0\alpha/m\),而 \(m_0\le m\)。整个证明里"不等号"只出现在次可加性一处,这一处在检验高度重叠(正相关)时最松:极端情况下 \(m\) 个检验完全相同,并集的概率只有 \(\alpha/m\),而界给的是 \(\alpha\)。

例 10.25。 基因例中 \(\alpha=0.05\),阈值为 \(0.05/2638\approx0.000019\),只有 p 值低于它的基因才宣布显著。

代价:非常保守。 它试图让"哪怕一次错误"都不太可能,于是功效很低。检验之间正相关时(量化中几乎总是如此:因子之间相关、参数相近的策略高度相关),Bonferroni 的界 \(\sum\mathbb P(R_i)\) 远大于 \(\mathbb P(\bigcup R_i)\),保守程度更甚。

Bonferroni 的 \(t\) 值门槛(双侧 \(\alpha=0.05\),用正态近似 \(z_{1-0.025/m}\)):

检验个数 \(m\) 1 10 100 1000 10000
\(\lvert t\rvert\) 门槛 1.96 2.81 3.48 4.06 4.56

这张表值得记住:如果你(或整个行业)试过 100 个因子,\(t=2\) 的"显著"毫无说服力。

10b.3.2 Holm 逐步下降法(原书未讲,补充)

Holm(1979)在不增加任何假设的前提下改进了 Bonferroni:把 p 值从小到大排序 \(P_{(1)}\le\dots\le P_{(m)}\),从最小的开始,若 \(P_{(1)}\le\alpha/m\) 则拒绝并继续;若 \(P_{(2)}\le\alpha/(m-1)\) 则拒绝并继续;……第一次出现 \(P_{(k)}>\alpha/(m-k+1)\) 时停止,之后的全部保留。它同样在任意依赖下控制 FWER,且拒绝集合总是包含 Bonferroni 的拒绝集合。既然严格更好,实践中没有理由用 Bonferroni 而不用 Holm(只是在发现很少时两者几乎一样)。

白话解释:Holm 的思路是"拒掉一个,就少一个需要保护的假设"。第一步面对 \(m\) 个可能为真的原假设,所以门槛是 \(\alpha/m\);一旦最小的 p 值通过了,我们就(以足够把握)认定它为假,剩下只需保护 \(m-1\) 个,门槛可以放宽到 \(\alpha/(m-1)\),依此类推。一旦某一步没通过就停止,后面更大的 p 值全部保留。门槛随步数逐渐放宽,所以拒绝数不会少于 Bonferroni。


10b.4 控制 FDR:Benjamini–Hochberg 方法

10b.4.1 为什么要换目标

在探索性研究里,我们并不要求"一个错误都不能有"。初筛出 30 个候选因子,其中两三个是假的,后续的样本外检验、经济逻辑审查会把它们淘汰;但如果为了绝不犯错只敢保留 3 个,就错过了大量真东西。此时更合理的目标是控制错误发现率:宣布的发现中,假发现的比例平均不超过 \(\alpha\)。

10b.4.2 算法

Benjamini–Hochberg(BH)方法。

  1. 把 p 值排序:\(P_{(1)}<\cdots<P_{(m)}\);
  2. 令 \(\ell_i=\dfrac{i\alpha}{C_mm}\),\(R=\max\{i:P_{(i)}<\ell_i\}\)。p 值独立时 \(C_m=1\);p 值任意依赖时 \(C_m=\sum_{i=1}^m1/i\)(约等于 \(\ln m+0.577\));
  3. 令 \(T=P_{(R)}\),称为 BH 拒绝阈值;
  4. 拒绝所有 \(P_i\le T\) 的 \(H_{0i}\)。

\(C_m=\sum1/i\) 的版本通常称为 Benjamini–Yekutieli(BY)方法(2001),原书把两者写在同一公式里。

几何图像(原书图 10.6)。 把排序后的 p 值画成散点(横轴为秩 \(i\),纵轴为 \(P_{(i)}\)),再画一条过原点、斜率为 \(\alpha/m\) 的直线。BH 找的是最后一个落在直线下方的点,它及它左边的全部拒绝——即使中间有些点在直线上方。这是"逐步上升"(step-up)程序,与 Holm 的"逐步下降"方向相反。

10b.4.3 直觉:BH 是在估计 FDP

设我们拒绝所有 \(P_i\le t\) 的假设。真原假设的 p 值均匀分布,所以其中被拒的个数期望为 \(m_0t\le mt\);实际拒绝了 \(R(t)=\#\{P_i\le t\}\) 个。于是错误发现比例的一个(偏保守的)估计是

\[\widehat{\text{FDP}}(t)=\frac{mt}{R(t)}.\]
在 \(t=P_{(k)}\) 处 \(R(t)=k\),条件 \(\widehat{\text{FDP}}\le\alpha\) 就是 \(P_{(k)}\le k\alpha/m\)——正是 BH 的阈值线。BH 选的是满足"估计的 FDP 不超过 \(\alpha\)"的最大阈值,也就是在 FDR 约束下拒绝最多。

金融直觉:\(\widehat{\text{FDP}}(t)=mt/R(t)\) 是一个"预期坏账 / 实际放款"的比率。分子 \(mt\):假设全部候选都无效,按均匀分布,p 值低于 \(t\) 的平均有 \(mt\) 个,这是"纯运气能产生的发现数"的上限。分母 \(R(t)\):实际观测到 p 值低于 \(t\) 的个数。例:\(m=500\)、\(t=0.01\),纯运气最多产生 5 个;如果你实际看到 40 个 p 值低于 0.01,估计的假发现比例约 \(5/40=12.5\%\);若只看到 6 个,比例高达 83%,这批"发现"基本是噪声。

既然 \(m_0t\le mt\),若能估计真原假设比例 \(\pi_0=m_0/m\),用 \(\hat\pi_0mt\) 代替 \(mt\) 就能更有功效。这就是 Storey 的 q 值方法(原书未讲):用 p 值分布中靠近 1 的部分(几乎全是真原假设)估计 \(\pi_0\)。

10b.4.4 定理与证明思路

定理 10.26(Benjamini & Hochberg, 1995)。 无论多少原假设为真、无论备择下 p 值的分布如何,只要 p 值独立(或用 \(C_m\) 修正),BH 方法满足

\[\text{FDR}=\mathbb E(\text{FDP})\le\frac{m_0}{m}\alpha\le\alpha.\]

证明思路(独立情形;原书未给证明,这里补充标准论证)。 记 \(\mathcal H_0\) 为真原假设的下标集。把 FDP 拆成每个真原假设的贡献:

\[\text{FDP}=\sum_{i\in\mathcal H_0}\frac{I(H_{0i}\text{被拒})}{R\vee1}.\]
固定一个 \(i\in\mathcal H_0\)。令 \(C_k^{(i)}\) 为事件"把 \(P_i\) 换成 0 之后,BH 恰好拒绝 \(k\) 个假设"。它只依赖其他 \(m-1\) 个 p 值,因而与 \(P_i\) 独立;并且对不同 \(k\) 互斥、合起来为必然事件。BH 的结构保证:\(H_{0i}\) 被拒且总拒绝数为 \(k\),当且仅当 \(P_i\le k\alpha/m\) 且 \(C_k^{(i)}\) 发生。于是
\[\mathbb E\Big[\frac{I(H_{0i}\text{被拒})}{R\vee1}\Big]=\sum_{k=1}^m\frac1k\,\mathbb P\Big(P_i\le\frac{k\alpha}m\Big)\mathbb P\big(C_k^{(i)}\big)=\sum_{k=1}^m\frac1k\cdot\frac{k\alpha}{m}\mathbb P\big(C_k^{(i)}\big)=\frac{\alpha}{m}.\]

推导拆解:这个式子每一步用的东西如下。 (1) 第一个等号:被拒时总拒绝数 \(R\) 一定是 1 到 \(m\) 中的某个 \(k\),按 \(k\) 分情况求和(全概率),每种情况下 \(\frac{1}{R\vee1}=\frac1k\);再用"被拒且 \(R=k\) ⇔ \(P_i\le k\alpha/m\) 且 \(C_k^{(i)}\)"把事件拆成两个独立事件的交,概率相乘。 (2) 第二个等号:\(P_i\) 均匀分布,\(\mathbb P(P_i\le k\alpha/m)=k\alpha/m\)。 (3) 第三个等号:\(\frac1k\) 与 \(k\) 抵消,剩下 \(\frac\alpha m\sum_k\mathbb P(C_k^{(i)})\),而 \(C_k^{(i)}\) 对不同 \(k\) 互斥且覆盖所有情况,概率之和为 1。 整个证明最巧的地方就是 BH 的阈值 \(k\alpha/m\) 与 FDP 的分母 \(k\) 恰好抵消,这也解释了阈值线为什么必须是"斜率 \(\alpha/m\) 的直线"。

对 \(m_0\) 个真原假设求和即得 \(\text{FDR}=m_0\alpha/m\)(连续 p 值时等号成立)。依赖情形下第二个等号不再成立,乘上 \(C_m=\sum1/i\) 可以吸收最坏情况的依赖(BY);对实践中常见的"正相关"依赖(如检验统计量多元正态且相关系数非负),Benjamini & Yekutieli(2001)证明原始 BH 仍然有效。

10b.4.5 原书例题

例 10.27(图 10.6)。 6 个排序 p 值:不校正(拒绝 \(P_i<\alpha\))拒绝 4 个;Bonferroni(\(P_i<\alpha/m\))拒绝 0 个;BH 拒绝 2 个。三种方法从宽到严排成一列。

例 10.28。 10 个独立检验的排序 p 值:

\(i\) 1 2 3 4 5 6 7 8 9 10
\(P_{(i)}\) 0.00017 0.00448 0.00671 0.00907 0.01220 0.33626 0.39341 0.53882 0.58125 0.98617
BH 线 \(i\alpha/m\) 0.005 0.010 0.015 0.020 0.025 0.030 0.035 0.040 0.045 0.050
  • 不校正:拒绝前 5 个(都 < 0.05);
  • Bonferroni:阈值 \(0.05/10=0.005\),拒绝前 2 个;
  • Holm:\(0.00017\le0.005\) 拒绝,\(0.00448\le0.05/9=0.00556\) 拒绝,\(0.00671>0.05/8=0.00625\) 停止,拒绝 2 个;
  • BH:\(i=5\) 时 \(0.01220<0.025\) 成立,\(i=6\) 时 \(0.336>0.030\) 不成立,之后也都不成立,故拒绝前 5 个;
  • BY(\(C_{10}=\sum_{i=1}^{10}1/i\approx2.929\)):线为 \(i\times0.00171\),只有 \(i=1\) 满足,拒绝 1 个。

(这些结果用 statsmodels.stats.multitest.multipletests 逐一核对过。)把"10 个检验"换成"10 个候选因子的 p 值",流程原样适用。


10b.5 该控制哪一种错误率

  • FWER(Bonferroni / Holm) 适合"一个假阳性代价就很大"的确认性场合:决定是否把某个策略投入实盘、是否在论文里声称发现了新的定价因子、风控上线一条新的拦截规则。
  • FDR(BH) 适合探索性的筛选:从 2000 个候选因子里挑一批进入下一轮研究、从全市场挑一批"可能有异常成交"的股票做进一步核查。后续还有独立的验证环节,允许初筛里混入一定比例的假货。
  • 都不校正只在你只做了一个事先确定的检验时才合理——这在量化研究里几乎从不成立。

无论选哪种,前提是把所有做过的检验都算进 \(m\),包括失败后放弃的那些。只报告"幸存者"再做校正,等于没校正。


10b.6 数据挖掘偏差与策略过拟合(量化专题)

本节内容超出原书,是把上面的统计工具用于量化研究的标准做法。

10b.6.1 选择偏差:最优者的样本内表现系统性偏高

设有 \(N\) 个候选策略,真实 Sharpe 全为 0。每个策略的样本 Sharpe 估计近似 \(N(0,\sigma_{SR}^2)\),年化时 \(\sigma_{SR}\approx1/\sqrt{\text{年数}}\)(第 01 章的 Sharpe 标准误公式在 \(SR=0\) 时的特例)。选样本内最好的一个,它的 Sharpe 是 \(N\) 个正态变量的最大值。\(N\) 个独立标准正态最大值的期望可近似为

\[\mathbb E\big[\max_{n\le N}Z_n\big]\approx(1-\gamma)\,\Phi^{-1}\!\Big(1-\frac1N\Big)+\gamma\,\Phi^{-1}\!\Big(1-\frac{1}{Ne}\Big),\]
\(\gamma\approx0.5772\) 为 Euler–Mascheroni 常数(Bailey & López de Prado, 2014 使用的近似,来自极值理论)。\(N=10,100,1000\) 时约为 1.57、2.53、3.25。

白话解释:这个公式不必推导,理解它的形状即可。\(\Phi^{-1}(1-1/N)\) 是"\(N\) 个标准正态里平均只有一个能超过的水平",最大值自然在它附近;第二项 \(\Phi^{-1}(1-\frac1{Ne})\) 稍大一些,两者按 \(1-\gamma\) 与 \(\gamma\) 加权,是对极值分布均值的修正。关键性质是增长极慢:\(N\) 从 10 增到 1000(100 倍),预期最大值只从 1.57 增到 3.25(约 2 倍),大致按 \(\sqrt{2\ln N}\) 增长。这意味着多试很多次也只会让"运气最好的那个"小幅提高,但这点提高恰好足以把纯噪声推过 \(t=2\) 甚至 \(t=3\) 的门槛。 年化换算:零假设下年化 Sharpe 估计的标准差约为 \(1/\sqrt{\text{年数}}\),所以 5 年数据、1000 个试验的最优者预期年化 Sharpe \(\approx3.25\times1/\sqrt5\approx1.45\)。

所以:5 年数据、试 1000 个纯噪声策略,最优者的年化 Sharpe 预期约为 \(3.25/\sqrt5\approx1.45\),\(t\) 值约 3.25,单检验 p 值约 0.001。它在样本外的预期 Sharpe 是 0。这就是数据挖掘偏差(data-mining bias)或选择偏差(selection bias)。反过来看,若要求最优者的样本内 Sharpe 不能"仅凭运气"达到,所需的回测长度会随 \(\log N\) 增长——这就是 Bailey 等提出的最小回测长度(minimum backtest length)的思路。

10b.6.2 概率 Sharpe 比率与压缩 Sharpe 比率

概率 Sharpe 比率(Probabilistic Sharpe Ratio, PSR)把第 01 章的 Sharpe 标准误(含偏度、峰度修正)直接变成一个检验:真实 Sharpe 超过基准 \(SR^*\) 的"置信度"

\[\widehat{PSR}(SR^*)=\Phi\!\left(\frac{(\widehat{SR}-SR^*)\sqrt{T-1}}{\sqrt{1-\hat\gamma_3\widehat{SR}+\frac{\hat\gamma_4-1}{4}\widehat{SR}^2}}\right),\]
其中 \(\widehat{SR}\) 是未年化的每期 Sharpe,\(T\) 是期数,\(\hat\gamma_3\) 为偏度,\(\hat\gamma_4\) 为峰度(正态为 3)。\(1-PSR(0)\) 本质上就是一个单侧 p 值。

推导拆解:PSR 只是把第 01 章的 Sharpe 标准误代入一个 Wald 统计量:\(\Phi\) 括号里的分母 \(\sqrt{1-\hat\gamma_3\widehat{SR}+\frac{\hat\gamma_4-1}4\widehat{SR}^2}\big/\sqrt{T-1}\) 正是 \(\operatorname{se}(\widehat{SR})\)(用 \(T-1\) 代替 \(T\) 是小样本的微调),所以括号里就是 \(W=(\widehat{SR}-SR^*)/\widehat{\text{se}}\),\(PSR=\Phi(W)\)。 必须用未年化的 Sharpe,是因为标准误公式是按"每期一个观测"推出来的;偏度、峰度也是日频数据的。直接代入年化 Sharpe 会把 \(\widehat{SR}^2\) 那一项放大约 252 倍,结果完全错误。 DSR 唯一的改动是把基准 \(SR^*\) 从 0 换成 \(SR_0\)——"\(N\) 个零效应试验里预期的最大 Sharpe"。数值例(10b.7.2 的输出):日频最优 Sharpe 约 \(1.59/\sqrt{252}\approx0.100\),\(SR_0\) 约 \(1.49/\sqrt{252}\approx0.094\),差距只有 0.006,乘以 \(\sqrt{T-1}\approx35.5\) 后约 0.2,\(\Phi(0.2)\approx0.59\)——与代码给出的 DSR 0.5874 一致。

压缩 Sharpe 比率(Deflated Sharpe Ratio, DSR;Bailey & López de Prado, 2014)的想法是:既然从 \(N\) 个试验里挑最好的,那么基准就不该是 0,而应是"\(N\) 个零效应试验的预期最大 Sharpe":

\[SR_0=\sqrt{\mathbb V[\{\widehat{SR}_n\}]}\left((1-\gamma)\Phi^{-1}\Big(1-\frac1N\Big)+\gamma\Phi^{-1}\Big(1-\frac1{Ne}\Big)\right),\qquad DSR=\widehat{PSR}(SR_0).\]
\(\mathbb V[\{\widehat{SR}_n\}]\) 是各试验 Sharpe 估计的方差。DSR 同时校正了三件事:试验次数(选择偏差)、样本长度、收益的非正态。实战代码中,一个单检验 p = 0.0004 的"最优策略",PSR(0) 为 0.9998,DSR 只有约 0.59——远达不到通常要求的 0.95。

10b.6.3 试验相关时的"有效试验数"

参数网格上的策略高度相关(快线 30 / 慢线 40 与快线 30 / 慢线 60 的持仓几乎一样),1000 个参数组合远不等于 1000 个独立试验。此时:

  • 用原始 \(N\) 做 Bonferroni 或 DSR 是保守的(高估了运气成分),不会错放过拟合,但会错杀真信号;
  • 可以估计一个"有效独立试验数":例如对试验收益的相关矩阵做主成分分析、按相关性聚类后数簇的个数,或像实战代码那样用"观测到的最优样本内 Sharpe 等于 \(N_{\text{eff}}\) 个独立试验的预期最大值"来反推。不同方法给出的数字差别不小,应当把它当作量级判断而非精确值;
  • 更严谨的方法是直接通过重抽样模拟零假设下"最大统计量"的分布,它自动包含了试验之间的相关结构:White(2000)的 Reality Check、Hansen(2005)的 SPA 检验、Romano & Wolf(2005)的逐步多重检验都属此类。它们的核心都是对"\(\max_n\) 检验统计量"做 Bootstrap(常用平稳 Bootstrap 保留时间依赖)。

10b.6.4 学术界的经验证据

  • Harvey, Liu & Zhu(2016)统计了文献中发表过的数百个"定价因子",按多重检验的思路论证新因子的 \(t\) 值门槛应提高到约 3.0,而不是传统的 2.0。
  • McLean & Pontiff(2016)发现,已发表的横截面异象在样本外和论文发表之后收益明显衰减,一部分原因就是数据挖掘偏差(另一部分是套利资金进入)。
  • Bailey、Borwein、López de Prado 与 Zhu 提出的回测过拟合概率(Probability of Backtest Overfitting, PBO)用组合对称交叉验证(CSCV)估计"样本内最优者在样本外表现低于中位数"的概率。

10b.6.5 研究流程上的防线

统计校正只是最后一道关。更根本的是流程:

  1. 记录所有试验。 维护研究日志:每个尝试过的因子、参数、样本区间、股票池,无论成败。没有 \(m\),就无从校正。
  2. 事先设定,留出样本外。 研究开始前就把最后一段数据(或一组市场)封存,只在最终确认时用一次。反复"偷看"样本外数据,样本外就变成了样本内。滚动前推(walk-forward)验证也遵循同样原则。
  3. 经济逻辑作先验。 有清晰机制(风险补偿、行为偏差、制度约束)的信号,先验成立概率高;纯数据挖掘出的复杂公式先验很低。第 11 章会说明,先验低时,即使 p 值很小,"策略真实有效"的后验概率也可能不高。
  4. 报告效应大小与稳健性。 参数附近的表现是否平滑("参数高原"优于"参数尖峰")、换股票池和区间是否依然成立、扣除成本后的经济显著性。
  5. 对最终候选做多重检验校正。 确认性结论用 Holm 或 DSR;探索性初筛用 BH。

10b.7 量化实战

10b.7.1 因子动物园:五种校正方法的错误率与功效

模拟 500 个候选因子(10 年月度多空收益),其中 50 个真实有效(月度 Sharpe 0.3,年化约 1.04),450 个无效;因子之间通过一个公共成分相关(相关系数 0.3)。重复 300 次,比较不校正、Bonferroni、Holm、BH、BY 的平均拒绝数、错误发现数、FWER、FDR 和功效。

import numpy as np
from scipy import stats
from statsmodels.stats.multitest import multipletests

rng = np.random.default_rng(2024)
m, m1, T = 500, 50, 120          # 500 个候选因子,其中 50 个真有效;每个 10 年月度多空收益
sr_true = 0.30                   # 有效因子的月度 Sharpe(年化约 1.04)
rho = 0.3                        # 因子之间通过一个公共成分相关
methods = {"不校正": None, "Bonferroni": "bonferroni", "Holm": "holm",
           "BH": "fdr_bh", "BY": "fdr_by"}
stats_acc = {k: {"R": [], "V": [], "FDP": [], "anyV": [], "S": []} for k in methods}
reps = 300
for _ in range(reps):
    common = rng.standard_normal((T, 1))
    eps = np.sqrt(rho) * common + np.sqrt(1 - rho) * rng.standard_normal((T, m))
    mu = np.r_[np.full(m1, sr_true), np.zeros(m - m1)]     # 前 50 个为真
    X = mu + eps
    t = X.mean(0) / (X.std(0, ddof=1) / np.sqrt(T))
    p = 2 * stats.t.sf(np.abs(t), T - 1)
    for k, meth in methods.items():
        rej = p < 0.05 if meth is None else multipletests(p, 0.05, method=meth)[0]
        R, V = rej.sum(), rej[m1:].sum()
        a = stats_acc[k]
        a["R"].append(R); a["V"].append(V); a["S"].append(rej[:m1].sum())
        a["FDP"].append(V / max(R, 1)); a["anyV"].append(V > 0)
print(f"{'方法':<10}{'平均拒绝':>8}{'其中错误':>8}{'FWER':>8}{'FDR':>8}{'功效':>8}")
for k, a in stats_acc.items():
    print(f"{k:<10}{np.mean(a['R']):>10.1f}{np.mean(a['V']):>10.1f}{np.mean(a['anyV']):>10.3f}"
          f"{np.mean(a['FDP']):>10.3f}{np.mean(a['S'])/m1:>10.3f}")
print(f"Bonferroni 对应的 |t| 门槛 ≈ {stats.norm.ppf(1-0.025/m):.2f}")

输出:

方法            平均拒绝    其中错误    FWER     FDR      功效
不校正             67.2      22.0     1.000     0.293     0.902
Bonferroni      12.0       0.0     0.020     0.007     0.240
Holm            12.2       0.0     0.020     0.007     0.243
BH              30.5       1.2     0.383     0.039     0.586
BY              16.0       0.1     0.063     0.006     0.318
Bonferroni 对应的 |t| 门槛 ≈ 3.89

读法:

  • 不校正:平均"发现"67 个因子,其中 22 个是假的(450 × 5% ≈ 22),FDR 约 29%——近三分之一的发现是噪声,而且每次模拟都至少有一个假发现(FWER = 1)。
  • Bonferroni / Holm:FWER 只有 2%(远低于 5%,因为因子正相关使其更保守),代价是功效只有 24%,50 个真因子平均只找到 12 个。Holm 比 Bonferroni 略好一点点。
  • BH:FDR 为 3.9%,低于理论上界 \(\frac{m_0}{m}\alpha=0.045\);功效 59%,是 Bonferroni 的 2.4 倍。它的 FWER 达 38%——BH 不保证"一个错都没有",只保证错的比例低。
  • BY:在任意依赖下都成立的代价是非常保守,本例的正相关结构下原始 BH 已经足够。

10b.7.2 从 1000 个噪声策略中挑出"明星":数据挖掘偏差与 DSR

import numpy as np
from scipy import stats

rng = np.random.default_rng(1)
N, T_is, T_oos = 1000, 5 * 252, 2 * 252          # 1000 个候选策略,5 年样本内 + 2 年样本外
# 所有策略真实期望收益都是 0(纯噪声),日波动 1%,带一点厚尾
R = rng.standard_t(5, size=(T_is + T_oos, N)) * 0.01 / np.sqrt(5 / 3)
r_is, r_oos = R[:T_is], R[T_is:]
sr = lambda x: x.mean(0) / x.std(0, ddof=1) * np.sqrt(252)   # 年化 Sharpe
sr_is, sr_oos = sr(r_is), sr(r_oos)
best = np.argmax(sr_is)
print(f"样本内最优策略:年化 SR = {sr_is[best]:.2f},t 值 = {sr_is[best]*np.sqrt(5):.2f},"
      f"单检验 p = {2*stats.norm.sf(sr_is[best]*np.sqrt(5)):.5f}")
print(f"同一策略样本外年化 SR = {sr_oos[best]:.2f};样本内前 10% 策略的样本外平均 SR = "
      f"{sr_oos[sr_is >= np.quantile(sr_is, 0.9)].mean():.2f}")

# 预期最大 Sharpe(Bailey & López de Prado):N 个独立试验在零假设下的最大值近似
g = 0.5772156649                                  # Euler–Mascheroni 常数
def expected_max_z(N):
    return (1 - g) * stats.norm.ppf(1 - 1 / N) + g * stats.norm.ppf(1 - 1 / (N * np.e))
sd_sr = sr_is.std(ddof=1)                         # 各试验 Sharpe 估计的离散程度(年化)
print(f"理论预期最大年化 SR ≈ {sd_sr * expected_max_z(N):.2f}(试验间 SR 标准差 {sd_sr:.3f},"
      f"E[max Z] ≈ {expected_max_z(N):.2f})")

# 压缩 Sharpe 比率(Deflated Sharpe Ratio):用日频、非年化的 SR 计算
x = r_is[:, best]
sr_d = x.mean() / x.std(ddof=1)
skew, kurt = stats.skew(x), stats.kurtosis(x, fisher=False)
sr0_d = (sd_sr / np.sqrt(252)) * expected_max_z(N)
def psr(sr_hat, sr_star, T, skew, kurt):
    return stats.norm.cdf((sr_hat - sr_star) * np.sqrt(T - 1) /
                          np.sqrt(1 - skew * sr_hat + (kurt - 1) / 4 * sr_hat ** 2))
print(f"PSR(基准 0) = {psr(sr_d, 0, T_is, skew, kurt):.4f};"
      f"DSR(基准 = 预期最大 SR) = {psr(sr_d, sr0_d, T_is, skew, kurt):.4f}")
print(f"Bonferroni 校正后 p = {min(1, N * 2*stats.norm.sf(sr_is[best]*np.sqrt(5))):.3f}")

输出:

样本内最优策略:年化 SR = 1.59,t 值 = 3.55,单检验 p = 0.00039
同一策略样本外年化 SR = 0.19;样本内前 10% 策略的样本外平均 SR = 0.01
理论预期最大年化 SR ≈ 1.49(试验间 SR 标准差 0.457,E[max Z] ≈ 3.26)
PSR(基准 0) = 0.9998;DSR(基准 = 预期最大 SR) = 0.5874
Bonferroni 校正后 p = 0.387

读法:1000 个纯噪声策略里挑出的"明星"有 1.59 的年化 Sharpe、\(t=3.55\),单独看是"高度显著",甚至过了 Harvey 等人的 \(t>3\) 门槛。理论公式预测的最大值 1.49 与之接近。样本外它回到 0.19,前 10% 的策略整体样本外平均为 0.01。DSR 只有 0.59,Bonferroni 校正后 p = 0.39——两种校正都正确地拒绝了这个"发现"。这个例子也说明 \(t>3\) 不是万能门槛:门槛应该随你试过的次数变化。

10b.7.3 参数寻优:相关试验与等效试验数

在 7 年随机游走价格上,用 110 组(快线, 慢线)均线交叉参数回测,前 5 年为样本内、后 2 年为样本外,重复 100 条独立路径。

import numpy as np
import pandas as pd

def one_run(rng, T=7 * 252, T_is=5 * 252, warm=260):
    ret = pd.Series(rng.standard_t(5, T) * 0.01 / np.sqrt(5 / 3))  # 随机游走:任何均线择时都无效
    price = (1 + ret).cumprod()
    ma = {w: price.rolling(w).mean() for w in set(range(5, 55, 5)) | set(range(20, 260, 20))}
    cols = {}
    for f in range(5, 55, 5):
        for s in range(20, 260, 20):
            if f < s:
                pos = np.sign(ma[f] - ma[s]).shift(1)                 # 收盘出信号,次日执行
                cols[(f, s)] = (pos * ret).values[warm:]
    P = np.column_stack(list(cols.values()))
    is_, oos = P[:T_is - warm], P[T_is - warm:]
    sr = lambda x: x.mean(0) / x.std(0, ddof=1) * np.sqrt(252)
    s_is, s_oos = sr(is_), sr(oos)
    b = np.argmax(s_is)
    ev = np.linalg.eigvalsh(np.corrcoef(is_.T))
    return s_is[b], s_oos[b], np.median(s_is), ev.sum() ** 2 / (ev ** 2).sum(), P.shape[1]

rng = np.random.default_rng(5)
out = np.array([one_run(rng) for _ in range(100)])
print(f"参数组合数 {int(out[0,4])},重复 100 条随机游走路径:")
print(f"  样本内最优组合的 SR 平均 {out[:,0].mean():.2f},它的样本外 SR 平均 {out[:,1].mean():.2f}"
      f"(样本外 SR>0 的比例 {np.mean(out[:,1]>0):.2f})")
print(f"  样本内全部组合 SR 中位数的平均 {out[:,2].mean():.2f};有效独立试验数(参与率)平均 {out[:,3].mean():.1f}")

# 反推"等效独立试验数":N 个独立零效应试验的预期最大 SR 等于观测到的平均最优 SR
from scipy import stats, optimize
g = 0.5772156649
emax = lambda N: (1 - g) * stats.norm.ppf(1 - 1 / N) + g * stats.norm.ppf(1 - 1 / (N * np.e))
sd0 = 1 / np.sqrt(5)                          # 零假设下 5 年年化 SR 估计的标准差
N_eq = optimize.brentq(lambda N: sd0 * emax(N) - out[:, 0].mean(), 2, 1e4)
print(f"  若 110 个试验相互独立,预期最大 SR ≈ {sd0*emax(110):.2f};按观测值反推等效独立试验数 ≈ {N_eq:.0f}")

输出:

参数组合数 110,重复 100 条随机游走路径:
  样本内最优组合的 SR 平均 0.73,它的样本外 SR 平均 -0.01(样本外 SR>0 的比例 0.52)
  样本内全部组合 SR 中位数的平均 0.01;有效独立试验数(参与率)平均 2.3
  若 110 个试验相互独立,预期最大 SR ≈ 1.15;按观测值反推等效独立试验数 ≈ 11

读法:参数寻优稳定地制造出平均 0.73 的样本内 Sharpe,样本外则是抛硬币(−0.01,正负各半)。由于相邻参数的策略高度相关,最优者的偏差小于"110 个独立试验"的 1.15,相当于约 11 个独立试验;主成分参与率给出的 2.3 则明显偏低——它衡量的是方差集中程度,而不是"最大值"意义上的有效个数。两种估计相差数倍,这正是 10b.6.3 节说的:有效试验数只能作量级判断,严谨做法是对最大统计量做 Bootstrap(Reality Check / SPA)。


本章小结

同时做 \(m\) 个检验时,原假设下 p 值的均匀分布意味着平均会有 \(m\alpha\) 个假发现,至少一个假发现的概率随 \(m\) 迅速趋于 1。控制族错误率(FWER)用 Bonferroni(\(P_i<\alpha/m\),任意依赖下成立)或严格更优的 Holm;控制错误发现率(FDR)用 Benjamini–Hochberg(找最后一个落在 \(i\alpha/m\) 直线下的 p 值),任意依赖时用 \(C_m=\sum1/i\) 修正(BY)。BH 可以理解为"在估计的 FDP 不超过 \(\alpha\) 的前提下拒绝最多",功效远高于 Bonferroni。量化研究本质上是大规模多重检验:因子挖掘、参数寻优和无数隐性选择共同造成数据挖掘偏差,使样本内最优者的表现系统性偏高、样本外回归平庸。应对之道是记录全部试验、封存样本外、用经济逻辑约束搜索空间,并对最终结论做多重检验校正(Holm、DSR、Reality Check / SPA)。\(t>3\) 是一个有用的经验门槛,但合理的门槛应当随试验次数而变。

概念 公式 / 结论
至少一次误拒 \(1-(1-\alpha)^m\)(独立);期望误拒数 \(m_0\alpha\)
FWER \(\mathbb P(V\ge1)\)
FDP / FDR \(V/R\)(\(R>0\));\(\text{FDR}=\mathbb E(\text{FDP})\)
Bonferroni \(P_i<\alpha/m\);FWER \(\le\alpha\),任意依赖
Holm \(P_{(k)}\le\alpha/(m-k+1)\) 逐步下降;FWER \(\le\alpha\),严格优于 Bonferroni
BH \(R=\max\{i:P_{(i)}<i\alpha/m\}\),拒绝 \(P_i\le P_{(R)}\);FDR \(\le m_0\alpha/m\)
BY 阈值除以 \(C_m=\sum_{i=1}^m1/i\);任意依赖
BH 直觉 \(\widehat{\text{FDP}}(t)=mt/R(t)\le\alpha\)
Bonferroni \(t\) 门槛 \(m=10,100,1000\):2.81、3.48、4.06
预期最大值 \(\mathbb E\max Z\approx(1-\gamma)\Phi^{-1}(1-\frac1N)+\gamma\Phi^{-1}(1-\frac1{Ne})\)
PSR \(\Phi\big((\widehat{SR}-SR^*)\sqrt{T-1}/\sqrt{1-\gamma_3\widehat{SR}+\frac{\gamma_4-1}4\widehat{SR}^2}\big)\)
DSR \(PSR(SR_0)\),\(SR_0=\sigma_{SR}\cdot\mathbb E\max Z\)

练习

基础

  1. 一个研究员测试了 20 个相互独立的无效因子,每个用 5% 水平。求至少一个"显著"的概率和"显著"个数的期望。(答案:\(1-0.95^{20}\approx0.64\);1 个。)
  2. 手算例 10.28 中 Holm 与 BY 方法的拒绝个数,并验证 10b.4.5 节的结论。
  3. 原书习题 11:术后恶心药物试验(安慰剂 80 人中 45 例恶心;氯丙嗪 75/26;茶苯海明 85/52;戊巴比妥 100mg 67/35;150mg 85/37)。各药对比安慰剂做 Wald 检验,再用 Bonferroni 和 BH 校正。(答案要点:p 值依次约 0.0057、0.52、0.63、0.10;Bonferroni 阈值 0.0125,只有氯丙嗪显著;BH 同样只拒绝氯丙嗪。氯丙嗪对安慰剂的比值比约 0.41。)
  4. 解释为什么 Bonferroni 在检验正相关时比名义水平更保守。用 10b.7.1 节代码把 rho 改为 0 和 0.8,观察 FWER 的变化。
  5. 某策略 3 年日频回测年化 Sharpe 1.2,研究员承认试过约 200 个参数组合。用 Bonferroni 的思路粗略判断它是否可信。(提示:\(t\approx1.2\sqrt3\approx2.08\),200 次检验的门槛约 3.66。)

进阶

  1. 证明:若 m 个检验的原假设全为真(\(m_0=m\)),则 FDR = FWER。由此说明"BH 控制 FDR"在全零假设下也意味着控制了 FWER(弱控制)。(提示:此时 \(V=R\),FDP \(=I(R>0)\)。)
  2. 补全 10b.4.4 节的证明:说明为什么"\(H_{0i}\) 被拒且 \(R=k\)"等价于"\(P_i\le k\alpha/m\) 且 \(C_k^{(i)}\) 发生"。
  3. 用 10b.7.2 节的代码,把 \(N\) 依次改为 10、100、1000、10000,记录最优策略的样本内 Sharpe,与公式 \(\mathbb E\max Z/\sqrt5\) 比较;再估计要让 1000 个试验中最优者的预期年化 Sharpe 降到 0.5 以下需要多少年数据。(提示:\(3.25/\sqrt Y<0.5\Rightarrow Y>42\)。)
  4. 对 10b.7.3 节的均线网格实现一个简化的 White Reality Check:用平稳 Bootstrap(或简单的分块 Bootstrap)对日收益重抽样,去均值后计算 110 个策略的最大 Sharpe,得到零假设下最大值的分布,求样本内最优策略的 p 值。
  5. 设先验上只有 10% 的候选因子真实有效,单个检验的功效为 50%,水平 5%。求"一个通过检验的因子真实有效"的概率。(答案:\(\frac{0.1\times0.5}{0.1\times0.5+0.9\times0.05}\approx0.53\)。这为第 11 章的贝叶斯视角做铺垫。)

原书推荐习题:第 10 章 11(多药对比 + Bonferroni / FDR,可改编为多因子筛选,必做)。

原书对照

本章内容 原书章节 PDF 页码
多重检验问题、Bonferroni 与定理 10.24、表 10.2、FDP/FDR、BH 方法与定理 10.26、例 10.25、10.27、10.28 10.7 p.177–180
文献注(Benjamini & Hochberg 1995) 10.9 p.181
习题 11 10.11 p.182–185
Holm、Storey q 值、BH 证明、数据挖掘偏差、PSR / DSR、Reality Check / SPA 原书未涉及 —

(PDF 页码 = 原书正文页码 + 17。)