← 返回模块
4.2.1.3beta 可读 · 未来付费校验通过内容版本 2026-05-24

多重检验与 p 值黑客行为

4.2.1 · 研究工作流程与纪律 · 量化全流程

一位 头部 量化 私募 基金 经理 周五 走 进 研究 总监 的 办公室 端 着 一 张 幻灯片 —— 五 年 评估 窗口 上 沪深 300 横截面 净 扣 成本 后 夏普 比率 2.0,t 统计量 4.5,样本外 净 值 曲线 漂亮 至极。研究 总监 翻 到 方法 学 那 页。"你 的 N 是 多少?" "我 在 相同 窗口 上 筛 了 大约 100 个 动量 / 反转 / 量价 叠加 变体。" "那 在 每 个 底层 信号 真实 夏普 等 于 零 的 零 假设 下,N=100 的 最大 夏普 期望 是 多少?" 沉默。"按 Bailey-Lopez de Prado 公式 大约 2.36。" "你 观测 到 的 夏普 是?" "2.0。" "比 零 假设 期望 还 低。" 基金 经理 愣 住。在 单 一 检验 解读 下 看 起来 像 4.5σ 大 胜 的 信号,对 筛 选 规模 修正 之后,最 多 是 微弱 证据 —— 100 次 试验 的 最大 值 即 使 每 一 个 底层 信号 都 是 纯 噪声 也 应 该 在 2.36 左右;打 出 2.0 反 而 低 于 噪声 期望。本 课 是 模块 的 统计 核心。L1 安装 试验 计数器;L2 守 住 测试集;L3 把 计数器 转 成 "结果 真 实 性" 的 通缩 概率。

多重 检验 通胀

当 研究员 试 了 N 个 信号 变体 并 报告 最好 那 个 时,N 次 试验 的 最大 观测 夏普 系统 性 偏 高 —— 即 使 每 一 个 底层 信号 真实 夏普 都 是 零。Bailey-Lopez de Prado 分解 给 出 经典 一阶 近似:

E[max Sharpe over N trials] ≈ sqrt(2 * ln N) - γ / sqrt(2 * ln N)

where γ ≈ 0.577 is the Euler-Mascheroni constant.

N=20    → ≈ 1.87
N=100   → ≈ 2.36
N=1000  → ≈ 3.00
N=10000 → ≈ 3.66

KaTeX 形式:

E[maxSRN]2lnNγ2lnN,γ0.577E[\max SR_N] \approx \sqrt{2 \ln N} - \frac{\gamma}{\sqrt{2 \ln N}}, \quad \gamma \approx 0.577

解读:N=50 候选 信号 筛 出 的 夏普 报 2.0,大约 等 于 零 假设 期望,不 是 真 信号 的 强 证据。公式 推 自 极值 理论 —— N 个 标准 正态 变量 的 最大 值 极限 分布 是 Gumbel;sqrt(2 ln N) 是 极限 的 主 阶 项;γ 修正 是 二 阶 项。该 分解 假定 N 次 试验 近似 独立 且 在 零 假设 下 近似 高斯;López de Prado 在 《Advances in Financial Machine Learning》 第八 章 推广 时 放 松 了 两 项 假设,得 到 通缩 Sharpe 比率。

这 里 的 教训 不 是 量化 研究 没 希望 —— 而 是 报告 出 来 的 夏普 必须 结合 筛 选 规模 来 评估。N=1 时 夏普 2.0 是 强 证据;N=50 时 夏普 2.0 在 噪声 地板;N=10000 时 夏普 3.5 在 噪声 地板。L1 的 试验 计数器 是 输入;下 文 的 修正 是 作用 在 它 上 的 函数。

三 项 修正

实战 中 用 的 三 项 修正,按 此 顺序 与 适用 场景:

1. Bonferroni              - multiply per-test p-value by N; conservative; for high-stakes confirmatory single tests
2. Benjamini-Hochberg FDR  - sort p-values, find largest `k` such that `p_(k) ≤ k/N * α`, reject smallest `k`;
                             controls expected proportion of false discoveries; for screening many candidates
3. Deflated Sharpe Ratio (DSR) - Bailey-López de Prado; estimates probability that observed max Sharpe is real
                                  given N + cross-sectional variance + skewness + kurtosis; threshold `DSR > 0.95`;
                                  for Sharpe-based research output

适用 场景 映射 很 重要。Bonferroni 是 保守 下 界 —— 它 把 family-wise error rate(N 次 检验 中 任 一 假阳性 的 概率)控 制 住,代价 是 统计 检验 力。当 单 次 假阳性 代价 灾难 性 时 用 它(10 亿 规模 sleeve 的 单 次 验证 性 检验)。Benjamini-Hochberg FDR 控制 假阳性 的 期望 比例;比 Bonferroni 宽,适用 于 筛 选 多 候选 且 容忍 少 数 假阳性 的 场景(进入 下 一 轮 评审 的 研究 筛 选)。DSR 是 针 对 backtest-overfitting 的 Sharpe-原生 修正;任何 报告 出 的 样本外 Sharpe 都 应 把 它 作 为 头 条 复现 检查。

通缩 Sharpe 比率

Bailey & López de Prado 2014, J. of Portfolio Management 40(5) 给 出 公式:

DSR = Φ(((SR - E[max SR]) * sqrt(T - 1))
        / sqrt(1 - γ_3 * SR + ((γ_4 - 1) / 4) * SR²))

where SR        = observed annualised Sharpe ratio
      T         = number of observations in years
      γ_3       = skewness of the underlying return series
      γ_4       = kurtosis (the full fourth moment)
      E[max SR] = multiple-testing-inflated expectation from the section above
      Φ         = standard-normal CDF

Threshold rule:
  DSR > 0.95 → strong evidence
  0.7 < DSR < 0.95 → suggestive
  DSR < 0.7 → weak

KaTeX 形式:

DSR=Φ((SRE[maxSR])T11γ3SR+γ414SR2)DSR = \Phi\left( \frac{(SR - E[\max SR])\sqrt{T - 1}}{\sqrt{1 - \gamma_3 \cdot SR + \frac{\gamma_4 - 1}{4} SR^2}} \right)

分子 把 观测 夏普 与 给 定 N 次 试验 后 的 零 假设 期望 作 差;分母 在 非 高斯 收益 下 对 夏普 估计量 的 方差 做 尺度 修正(γ_3 * SR 修 偏 态,((γ_4 - 1) / 4) * SR² 修 峰 度)。Φ 把 结果 的 z 分数 转 成 概率。阈值 规则 DSR > 0.95 → strong; 0.7 < DSR < 0.95 → suggestive; DSR < 0.7 → weak 是 行业 标准 分层。

解读:DSR 是 "观测 夏普 是 真 的" 的 概率 —— 即 底层 策略 真实 Sharpe 大 于 零 的 后 验 概率 —— 给 定 N 次 试验、横截面 Sharpe 方差、收益 分布 矩。N=10 下 夏普 2.0 报 DSR 0.92 是 suggestive;同 一 夏普 N=100 时 DSR 可能 跌 到 0.7 以下。

五 种 经典 p-hacking 形 式

五 种 经典 形 式,按 此 顺序 与 对 应 修复:

1. screening over many signals and reporting the best          fix: trial counter + DSR
2. screening over many hyperparameters per signal              fix: compound N includes every hyperparameter trial
3. screening over many universes / windows / horizons          fix: declare universe + window + horizon in pre-registration
4. optional stopping when result is significant                fix: pre-specified evaluation window with no early stopping
5. post-hoc outlier exclusion to make a result significant     fix: outlier rule declared in pre-registration

五 种 形 式 的 解药 一致:pre-registration + trial counter + DSR + reproducibility。每 一 种 p-hacking 都 在 静默 抬高 有效 N;没 有 L1 试验 计数器 就 无 法 应用 L3 修正;没 有 L2 测试集 上锁 就 无 法 信任 试验 计数器;没 有 L4 复现 性 绑 定 就 无 法 审计 整 条 链。

形 式 (1) 是 经典 筛 选-报告 —— 本 课 开 头 的 失败 案例。形 式 (2) 更 隐 蔽:一 个 信号 "变体" 配 10 种 超 参数 组合 实质 是 10 次 试验,不 是 1 次;compound N 是 每 信号 试验 数 与 每 超 参 试验 数 的 乘 积。形 式 (3) 是 "多 版本 研究员":"我 在 沪深 300 上 试 了,在 中证 500 上 试 了,在 中证 1000 上 试 了,5 日 / 21 日 / 63 日 horizon 都 跑 了,总 有 一 个 work。" 修复:在 预登记 文档 里 锁 死 universe 与 horizon;偏离 预登记 即 +1 进 试验 计数器。形 式 (4) 是 提 前 停止 陷阱:研究员 每 月 看 一 次 测试集 累积 评估,越 过 显著性 阈值 即 停;这 在 结构 上 偏 向 上 行。形 式 (5) 是 异 常 值 操作:测试 窗口 中 某 一 日 大 跌 把 夏普 杀 死,分析师 把 它 当 "非 代表 性" 删 掉。修复:异常 值 规则(例如 "日 收益 缩 尾 在 5σ")必须 在 测试集 触碰 之前 写 入 预登记。

代码:通缩 Sharpe 比率

def deflated_sharpe_ratio(observed_sharpe, n_trials, sharpe_cross_sectional_std,
                          return_skewness, return_kurtosis, years_of_data):
    """Bailey & López de Prado 2014.

    Returns a probability in [0, 1]. Caller compares to thresholds:
      DSR > 0.95 → strong
      DSR > 0.7  → suggestive
      else       → weak
    """
    import math
    from scipy.stats import norm
    gamma = 0.577  # Euler-Mascheroni constant
    sr = observed_sharpe
    n = n_trials
    expected_max_sr = math.sqrt(2 * math.log(n)) - gamma / math.sqrt(2 * math.log(n))
    expected_max_sr *= sharpe_cross_sectional_std
    t = years_of_data
    numerator = (sr - expected_max_sr) * math.sqrt(t - 1)
    denominator = math.sqrt(1 - return_skewness * sr
                            + ((return_kurtosis - 1) / 4) * sr ** 2)
    dsr = norm.cdf(numerator / denominator)
    if dsr > 0.95:
        return dsr, 'strong'
    elif dsr > 0.7:
        return dsr, 'suggestive'
    else:
        return dsr, 'weak'

函数 名 deflated_sharpe_ratio、参数 名、公式 系数(gamma = 0.577)、阈值 分 层 if dsr > 0.95: return 'strong'; elif dsr > 0.7: return 'suggestive'; else: return 'weak' 跨 区域 字节 一致。引用:Bailey & López de Prado 2014。

代码:Bonferroni 与 Benjamini-Hochberg

def bonferroni_threshold(alpha=0.05, n_tests=1):
    """Family-wise error-rate bound. Reject when per-test p < alpha / n_tests."""
    return alpha / n_tests


def benjamini_hochberg(p_values, alpha=0.05):
    """Benjamini & Hochberg 1995, JRSS-B 57(1). Controls expected proportion of false discoveries.

    Sort p-values ascending; find largest k such that p_(k) <= k/n * alpha;
    reject indices 0..k-1 in the sorted order. Returns a boolean mask in the
    original order.
    """
    import numpy as np
    p = np.asarray(p_values)
    n = len(p)
    order = np.argsort(p)
    sorted_p = p[order]
    thresholds = (np.arange(1, n + 1) / n) * alpha
    below = sorted_p <= thresholds
    if not below.any():
        return np.zeros(n, dtype=bool)
    k = np.max(np.where(below)[0]) + 1
    mask_sorted = np.zeros(n, dtype=bool)
    mask_sorted[:k] = True
    mask = np.zeros(n, dtype=bool)
    mask[order] = mask_sorted
    return mask

函数 名、参数 与 Benjamini-Hochberg 算法(排序、找 满足 p_(k) ≤ k/n * alpha 的 最 大 k、拒 绝 最 小 的 k 个)跨 区域 字节 一致;引用 Benjamini & Hochberg 1995。

四 项 上下游 串 联

1. L1 experiment log              - the trial counter N is logged here; pre-registration fixes the intended N
2. L2 test-set-touched-once rule  - prevents the effective N from being inflated by repeated test-set touches
3. L4 reproducibility requirement - makes the DSR verifiable by another researcher given the experiment log
4. the discipline cascade         - without L1+L2+L4, L3's correction cannot be applied honestly

孤 报 一 个 没 有 N 与 DSR 的 夏普 是 软 claim,不 是 研究 结果。脱离 上下文 的 夏普比率、相对 中证 500 / 沪深 300 基准 的 信息比率、评估 窗口 内 的 最大回撤、部署 之后 的 Alpha 衰减 —— 只有 试验 计数器 与 修正 站 在 旁 边 时,这 些 指标 才 在 统计 上 可 解读。因子模型 在 价值、质量、动量 上 的 暴露 告诉 你 这 个 信号 是不 是 已 知 因子 的 伪装;针 对 2015 股灾、2018 trade-war、2020 疫情、2022 房地产 drawdown 的 压力测试 确认 它 经 得 起 régime 变化;下游 4.4 的 均值方差优化 与 组合优化 继承 的 是 修正 后 估计 量 的 噪声 结构,不 是 原始 估计 量 的。

工作 实例:夏普 2.0 的 主 张

同事 报 出 510300 沪深 300 ETF 上 best-of-100 动量 变体:五 年 区间 (T=5) 内 年 化 夏普 2.0;100 次 试验 横截面 Sharpe std 0.4;日 收益 偏 度 γ_3 = -0.7,超 额 峰 度 γ_4 - 3 = 7。算 best-of-N 零 假设:sqrt(2 * ln 100) - 0.577 / sqrt(2 * ln 100) ≈ 2.36。观测 2.0 低 于 零 假设 2.36,结果 最 多 是 微弱 证据。Bonferroni:每 一 次 检验 α = 0.05 / 100 = 5e-4。DSR:把 2.0、2.36、T=5、γ_3=-0.7、γ_4-3=7 代 入 公式 → 分子 (2.0 - 2.36) * sqrt(4) ≈ -0.72,分母 sqrt(1 - (-0.7)(2.0) + (7/4)(4)) ≈ sqrt(9.4) ≈ 3.07,z ≈ -0.23,DSR = Φ(-0.23) ≈ 0.41 —— 分 层 weak。推荐 行 动:abandon。N=100 下 的 Sharpe 2.0 不 是 信号;是 经验 Gumbel 最 大 值 在 说话。

Formula Explorer

\text{DSR} = \Phi\left( \frac{(SR - E[\max SR])\sqrt{T - 1}}{\sqrt{1 - \gamma_3 SR + \frac{\gamma_4 - 1}{4} SR^2}} \right)

练习

Exercise

你 正在 审 一 项 同事 报 来 的 研究 结果 —— 510300 沪深 300 ETF 上 best-of-100 动量 信号 变体,五 年 评估 窗口 (T = 5 years) 内 观测 年 化 夏普 = 2.0。100 次 试验 的 横截面 Sharpe 标准 差 = 0.4;底层 日 收益 序列 偏 度 γ_3 = -0.7,超 额 峰 度 γ_4 - 3 = 7。做 四 项 计算 并 在 一 张 表 上 报告 答案。

(i) 计算 零 假设 下 best-of-N 夏普 期望 E[max SR over N=100] = sqrt(2 * ln 100) - 0.577 / sqrt(2 * ln 100) 并 报告 数值。

(ii) 把 观测 夏普 2.0 与 (i) 的 零 假设 期望 作 对 比;说 明 观测 夏普 是否 超 出 零 假设 足够 多 以 被 视 为 证据(经验 规则:observed - null > 0.5 = evidence)。

(iii) 应 用 Bonferroni 修正:报告 N=100 次 检验 整体 α=0.05 下 每 一 次 检验 的 α 阈值。

(iv) 用 López de Prado 公式 算 通缩 Sharpe 比率:DSR = Φ(((2.0 - E[max SR]) * sqrt(5 - 1)) / sqrt(1 - γ_3 * 2.0 + ((γ_4 - 1) / 4) * 4));报告 DSR 数值 并 分 层 strong (> 0.95)、suggestive (0.7-0.95)、weak (< 0.7)。用 一 句话 给 出 推荐 行 动(production / paper-trade / abandon)。

提示
(i) 题 sqrt(2 * ln 100) ≈ 3.035,0.577 / 3.035 ≈ 0.19,3.035 - 0.19 ≈ 2.85;再 乘 横截面 std 0.4 得 上 下文 化 后 的 零 假设 ≈ 2.36 / 有 些 研究员 用 原始 2.85。
提示
(iv) 题 零 假设 ≈ 2.36 而 观测 2.0,分子 是 负数;DSR 必 落 在 weak 带。推荐 行 动 是 abandon —— N=100 下 的 表 观 夏普 低 于 噪声 期望。

Harvey-Liu-Zhu 与 行业 反 思

Harvey-Liu-Zhu 2016 (Review of Financial Studies 29(1)) 在 学术 期刊 跨 截面 收益 文献 上 做 了 一 项 大规模 多重 检验 修正,把 几 百 个 公布 因子 中 的 大多数 重新 划 入 「未 必 真 实」 一 类。论文 的 教训 是:横截面 收益 文献 报告 的 因子 t-统计量 在 没 有 多重 检验 修正 时 系统 性 地 高 估 显著性;他们 建议 把 单 因子 显著性 阈值 从 t > 2 抬 到 t > 3。这 一 篇 论文 重新 校 定 了 整 个 学术 因子 库 的 「真 实 性」 期望,也 让 量化 私募 与 公募 量化 部门 开始 把 DSR 与 Bonferroni 写 进 标准 评审 清单。

中国 A 股 上 的 类似 重新 评估 始 于 2019-2021 年。明汯、幻方、九坤 投资 与 中诚 投资 的 研究 团队 在 内部 复盘 时 发现:早期 公布 的 A 股 因子(包括 多 类 价值 / 反转 / 量价 因子)在 加 上 N 修正 与 DSR 后,「真实 信号 数量」 从 数 十 个 降 到 五 个 以 内。研究 总监 因此 在 标准 PR 模板 里 强制 加 入 试验 计数器、Bonferroni 阈值 与 DSR 报告。

关于 「试 一 个 漂亮 结果」 的 文化 风险

研究 团队 中 最 隐 蔽 的 文化 风险 不 是 单 个 研究员 在 主观 上 想 作 弊,而 是 团队 整 体 在 不 知 觉 中 把 试 验 次 数 与 报告 解 耦:A 研究员 试 50 个 信号 报 最好;B 研究员 在 A 的 残差 上 又 试 50 个 信号 报 最好;C 研究员 把 A 与 B 的 输出 与 自己 的 30 个 候选 拼 起 来 选 最好。从 单 研究员 视角,每 个 项目 的 N 都 不 大;从 团队 视角,有效 N 至少 是 50 × 50 × 30 = 75000。修复:团队 共享 一 个 实验 日志,所有 信号 试 验 加 入 同 一 计数;团队 PR 评审 把 「团队 累计 N」 而 不是 「个人 N」 写 入 DSR。这 一 文化 修复 比 任 一 统计 工具 都 更 重要。

参考 卡

本 课 装 配 的 组件,按 序:

  • Fenced ```math block —— 多重 检验 通胀 公式 与 N 表。
  • Inline-code listing —— 三 项 经典 多重 检验 修正 与 适用 场景。
  • Fenced ```math block —— 通缩 Sharpe 比率 公式 与 阈值 分 层。
  • Inline-code listing —— 五 种 经典 p-hacking 形 式 与 对 应 修复。
  • Fenced ```python code block —— deflated_sharpe_ratio(...)
  • Fenced ```python code block —— bonferroni_threshold(...)benjamini_hochberg(...)
  • Inline-code listing —— 四 项 上下游 串 联。
  • Exercise —— best-of-100 DSR 评估,配 两 条 渐进 Hint。
  • FormulaExplorer —— DSR 公式。

下 一 课

下 一 课 「研究 工具链 与 可 复现性」是 工程 capstone —— 把 L1 实验 日志 做 成 可 审计、把 L2 测试集 上锁 做 成 可 执行、把 L3 DSR 做 成 另 一 研究员 可 复现 的 那 一 层。你 将 学到 六 层 经典 研究 栈(notebook vs 脚本、版本 锁 依赖、随机数 种子、git + 特性 分支 + PR、实验 跟 踪、代码 评审 清单)、八 项 研究 PR artefact 模板、含 不可 伪造 git_commit_sha 列 的 SQLite 实验 日志 schema、以及 从 单 条 命令 复现 结果 所 需 的 四 项 输入。纪律 在 此 闭 合:带 试验 计数器、DSR 与 可 复现 run-id 的 夏普 是 研究 结果;孤 报 的 夏普 是 猜测。