某周三 下午,上海 量化 私募 明汯 / 幻方 风格 的 投决会。研究员 上 来 一个 动量 策略:L1 引擎 是 事件驱动(干净);L2 真实性 清单 每 一 项 都 过(PIT 数据、survivorship-free 沪深300 股票池、下根 K 线 开盘 成交、双边 10 bps 成本、不 做 空)。报告 的 夏普比率 在 2014-2023 上 是 2.5。风控 总监 只 问 一 个 问题:「你 跑 了 多少 个 参数 组合?」研究员 答:「大约 一千 个——回看 5 / 10 / 21 / 63 / 126 / 252,持有 1 / 5 / 10 / 21,沪深300 五 分位 / 十 分位 / 二十 分位 / 全样本,调仓 日 / 周 / 双周 / 月。」风控 总监 在 笔记本 上 打了 一 行:sqrt(2 * ln(1000)) - 0.577 / sqrt(2 * ln(1000)),屏幕 打 出 3.00。她 把 屏幕 转 过来:「在 每一格 真实 夏普 = 0 的 零假设 下,1000 格 随机 搜索 的 期望 最佳 夏普 就 是 3.0。你 的 2.5 差于 零假设。拿 回 去 重 做。」研究员 退会。夏普 本身 不 是 bug,参数 扫描 才 是。L1、L2 把 引擎 擦 干净 了;本课 教 三 种 统计 验证 方法,量化 参数 扫描 给 头条 数字 加 的 通胀,把 best-of-grid 夏普 从 一个 虚 标 变成 可信 估计。
参数 扫描 问题
典型 的 量化 研究 项目 不 测 一只 策略,测 一张 网格。沪深300 上 的 动量 策略 至少 四 个 参数 维度:
lookback window ∈ {5, 10, 21, 63, 126, 252} (6 values)
holding period ∈ {1, 5, 10, 21} (4 values)
universe filter ∈ {top-quintile, top-decile, top-vigintile, all} (4 values)
rebalance frequency ∈ {daily, weekly, biweekly, monthly} (4 values)
完整 网格 6 × 4 × 4 × 4 = 384 格,扩到 ~1000 加上 成交量 过滤 或 行业 中性 变体。机器学习 重 的 策略(GBDT、深度 网络)经常 跑 10000+ 超 参数 格。网格 的 最佳 格 的 回测 夏普 在 结构 上 偏 高——即使 每 一 格 的 真实 夏普 都 是 0。这 个 偏差 就 是 4.2.1 L3 的 多重 检验 通胀,现在 特化 到 参数 维度。
把 这 个 框架 讲 清楚:这 不 是 回测 引擎 的 bug。L1 + L2 已经 把 引擎 擦 干净 了。这 是 引擎 怎么 被 使用 的 bug——具体 是 选 哪 一 个 结果 上 报 的 参数 搜索 过程。修 复 不 是 更 好 的 引擎,而 是 一 个 统计 修正——量化 偏差 并 从 参数 扫描 回测 产 出 可信 估计。
参数 扫描 回测 的 三 种 经典 统计 验证 方法,按 顺序:
1. walk-forward parameter validation — best params on [t-L, t], applied on [t, t+H]; honest test-window Sharpe is the estimate
2. deflated Sharpe at the backtest layer — Bailey-Lopez de Prado formula with N = parameter_grid_size; convention DSR > 0.95 credible
3. probabilistic backtest overfitting (PBO) — Bailey-Borwein-Lopez-de-Prado-Zhu combinatorial-CV; convention PBO < 0.2 credible, 0.2-0.5 cautious, > 0.5 consistent with pure overfitting
每 一 个 参数 扫描 回测 都 必 须 在 头条 best-in-grid 夏普 之 上 报告 这 三 个 指标。四 行 表 就 是 把 「最佳 格 夏普 2.5」翻 成 「这 策略 是 真 的 吗」的 交付物。
时序 滚动 验证(walk-forward parameter validation)
三 种 方法 里 概念 最 简单 的。每 一 步 t,在 训练 窗口 [t-L, t] 上 选 最佳 参数,应用 在 测试 窗口 [t, t+H]。拼起来 的 测试 窗口 上 的 夏普 就 是 诚实 估计。
这 是 4.2.1 L2 的 滚动 样本 外 验证,用 在 参数 选择 而 不 仅 是 模型 拟合 上——每 一 步 的 「模型」就 是 参数 选 出 来 的 策略。典型 配置:L = 252(一 个 交易 年 作 训练 窗口);H = 63(一 个 季度 作 测试 窗口);步 长 等于 H,让 测试 窗口 不 重叠。10 年 数据集 在 L=252、H=63 配置 下 大约 产生 36 个 测试 窗口;聚合 的 测试 窗口 收益 年化 给 出 walk-forward 夏普。
walk-forward 夏普 通常 低于 头条 best-in-grid 夏普——因为 train 上 最佳 的 参数 不 总 是 test 上 最佳(参数 不 稳定)。1000 格 动量 网格 头条 best-in-grid 夏普 2.5 时,walk-forward 夏普 通常 在 1.0-1.5 区间——在 L2 真实性 税 基础 上 再 跌 30-50%。
def walk_forward_parameter_validation(returns_matrix, dates, train_window=252, test_window=63):
# returns_matrix shape: (T dates, N parameter cells)
# step by test_window so test windows are non-overlapping
test_returns = []
for t in range(train_window, len(dates) - test_window, test_window):
train_slice = returns_matrix[t - train_window : t]
test_slice = returns_matrix[t : t + test_window]
# identify the parameter cell with the highest Sharpe on the training window
train_sharpe = train_slice.mean(axis=0) / train_slice.std(axis=0) * np.sqrt(252)
best_cell_train = int(np.argmax(train_sharpe))
# evaluate that cell on the test window
test_returns.append(test_slice[:, best_cell_train])
test_returns_concat = np.concatenate(test_returns)
wf_sharpe = test_returns_concat.mean() / test_returns_concat.std() * np.sqrt(252)
return wf_sharpe
函数 返回 拼起来 的 测试 窗口 收益 的 年化 夏普。这 个 数字 是 策略 在 伪 样本 外 窗口 里 实际 赚到 的;参数 扫描 通胀 不 进去,因为 每 一 步 参数 选择 都 在 测试 窗口 看 不到 的 数据 上 完成。
通缩 夏普(deflated Sharpe at the backtest layer)
Bailey-López de Prado 通缩 夏普 公式(4.2.1 L3 介绍 过),在 此 应用 到 参数 网格。设 置:研究员 报告 跨 N 个 参数 组合 的 最佳 夏普。在 零 假设 下(每 一 个 组合 真实 夏普 都 = 0),期望 best-of-N 夏普 大约 为:
E[max Sharpe over N trials] ≈ sqrt(2 * ln N) - γ / sqrt(2 * ln N)
where γ ≈ 0.577 is the Euler-Mascheroni constant
(the constant from extreme-value theory for Gaussian maxima)
N = 20 -> 1.87
N = 100 -> 2.36
N = 1000 -> 3.00
N = 10000 -> 3.55
这 就 是 开头 风控 总监 用 来 把 2.5 夏普 退 回 去 的 那 个 头条 计算。在 网格 各 处 真实 夏普 = 0 的 零 假设 下,1000 格 随机 搜索 期望 打 出 3.0 的 best-in-grid 夏普。报告 出 2.5 差于 零 假设——这 策略 连 随机 噪声 都 没 跑赢。
通缩 夏普 是 概率 修正 后 的 观测 夏普。完整 公式:
deflated_Sharpe ≈ observed_Sharpe - E[max Sharpe over N under null] (rough operational shorthand)
DSR(observed_Sharpe, N, sigma_S, skew, kurt, T) (full citation form)
# Bailey & López de Prado, Journal of Portfolio Management 2014
# sigma_S = N 格 上 夏普 的 横截面 标准差
# skew, kurt = 收益 序列 三 阶 / 四 阶 矩
# T = 样本 量(观测 期 数)
DSR 返回 观测 夏普 真 正 高于 某 阈值(通常 是 0)的 概率。约定:DSR > 0.95 视为 可信(观测 夏普 不 是 多重 检验 伪 影 的 概率 95%)。对 头条 best-in-grid 夏普 2.5 的 1000 格 动量 网格,通缩 夏普 通常 在 0.8 附近——在 减 去 ~3.0 期望 best-of-1000 之 后、按 横截面 夏普 方差 和 收益 序列 矩 加权 之 后 的 残值。
回测 过拟合 概率(PBO)
三 种 方法 里 最 强 力 也 最 耗 算 力 的。Bailey-Borwein-López-de-Prado-Zhu 2014 程序,估 计 样本 内 最佳 策略 在 样本 外 表现 低于 测试 集 中位 数 的 概率。具体 步骤:
- 取 参数 网格(N 格)和 日 收益 矩阵——行 是 日期(
T行),列 是 参数 组合(N列)。 - 把
T个 日期 切 成S个 连续 不 重叠 的 块(典型S = 16)。 - 对
C(S, S/2) = C(16, 8) = 12870种 「选S/2块 做 训练、剩下S/2块 做 测试」的 选 法:- 在 训练 集 上 找 出 夏普 最 高 的 参数 组合(in-sample-best)。
- 在 测试 集 上 给 这 个 组合 排名。
- 把 测试 排名 记 成 占 总 组合 数 的 分数(排名 1 = 样本 内 最佳 也 是 样本 外 最佳;0 = 样本 内 最佳 是 样本 外 最差)。
- PBO 是 「样本 内 最佳 在 测试 集 上 落 在 中位 数 以下」(测试 排名 > 0.5,按 worst 方向 算)的 子集 选 法 占 比。
PBO 有 一 个 可 解释 的 量 表:
PBO < 0.2 credible — 样本 内 最佳 在 样本 外 可 靠 胜出
0.2 <= PBO < 0.5 cautious — 样本 内 最佳 在 样本 外 胜 多 输 少,但 过拟合 余 量 不 可 忽略
0.5 <= PBO < 0.7 backtest consistent with pure overfitting — 样本 内 最佳 在 样本 外 与 抛 硬币 无 异
PBO >= 0.7 adversarial overfitting — 参数 搜索 程序 在 主动 选 反 样本 外
1000 格 动量 网格 典型 PBO 在 0.3-0.5 区间,取决 于 信号 强度。参考 实现:
from itertools import combinations
def probabilistic_backtest_overfitting(returns_matrix, n_blocks=16):
# returns_matrix shape: (T dates, N parameter cells)
T, N = returns_matrix.shape
block_size = T // n_blocks
blocks = [returns_matrix[i * block_size : (i + 1) * block_size] for i in range(n_blocks)]
half = n_blocks // 2
test_ranks = []
for train_idx in combinations(range(n_blocks), half):
train_set = set(train_idx)
train_data = np.vstack([blocks[i] for i in range(n_blocks) if i in train_set])
test_data = np.vstack([blocks[i] for i in range(n_blocks) if i not in train_set])
train_sharpe = train_data.mean(axis=0) / train_data.std(axis=0)
in_sample_best_idx = int(np.argmax(train_sharpe))
test_sharpe = test_data.mean(axis=0) / test_data.std(axis=0)
# rank of the in-sample-best on the test set, as a fraction of N
rank = (test_sharpe < test_sharpe[in_sample_best_idx]).sum() / N
test_rank = 1.0 - rank # fraction worse than in-sample best in test
test_ranks.append(test_rank)
# PBO = fraction of subset choices where in-sample-best lands below test median
pbo = float(np.mean(np.array(test_ranks) > 0.5))
return pbo
# Bailey, Borwein, López de Prado, Zhu (2017)
10 年 数据集、n_blocks = 16、N = 1000 时,这 是 12,870 × 1000 次 夏普 计算——重 但 单 机 几 分钟 跑 得 完。生产 实现(López de Prado 的 开源 包 mlfinlab)跨 列 向量化 + numba 加速 内 循环。
四 行 报告 表
参数 扫描 回测 的 信誉 交付物 不 是 一 个 数字——而 是 一 张 至少 四 行 的 表:
1. headline best-in-grid Sharpe
2. walk-forward Sharpe
3. deflated Sharpe (N = grid_size)
4. PBO (S = 16 blocks)
经典 报告 模板:「我 在 [universe] 上 从 [start] 到 [end] 回测 了 N 个 参数 组合;报告 头条 / 滚动 / 通缩 夏普 与 PBO;按 联 合 解 读,建议 deploy / paper-trade / abandon」。
当 三 个 后续 指标 给 出 同 一 故事(低 walk-forward + 低 通缩 + 高 PBO),回测 过拟合 严重,头条 夏普 应 被 弃用。当 它们 互 相 冲 突——高 walk-forward 但 低 通缩 暗示 train 最佳 在 test 上 也 可 靠 但 整体 best-in-grid 来自 别 的 格 子(往 往 是 在 某 一 段 时期 出奇 制胜 的 那 一 格);高 walk-forward 但 高 PBO 暗示 参数 搜索 程序 本身 有 结构 问题——研究员 需要 调查 而 不 是 盲 接 或 盲 拒。
工作 例:1000 格 动量 网格 在 沪深300 510300 上
把 完整 网格 跑 过 L1 + L2 的 诚实 引擎 + 真实性 清单,在 510300 沪深300 ETF 2014-2023:
parameter axes : lookback × holding × universe × rebalance ≈ 768 cells, padded to 1000
headline Sharpe : ~2.5 (best in grid)
walk-forward : ~1.0
deflated Sharpe : ~0.8 (with N = 1000)
PBO : ~0.35 (cautious; in-sample-best wins on test ~65% of subset choices)
解 读:每 一 个 后续 指标 把 可信 夏普 估计 往 下 拉。这 只 动量 策略 的 「真 实」夏普 在 ~0.8-1.0 区间,不 是 头条 的 2.5;策略 大概 率 可 部 署(PBO < 0.5),但 容量 要 按 现 实 夏普 算,不 是 按 通胀 后 的 那 个 算。开头 那 个 退 会 的 研究员,第 二 次 进 投决会 时 端 的 就 是 这 张 四 行 表。
CN 量化 私募 行业 在 2018-2020 已 经 围绕 L3 风格 多指标 报告 形成 共识。2018-2021 量化 AUM 暴 涨 至 万亿 RMB 峰值,2022-2024 业绩 走 弱,相当 部分 归 因 于 容量 过拟合——在 1-5 亿 RMB 跑 得 好 的 策略 到 50-100 亿 RMB 跑 不 出 了。明汯 / 幻方 公开 投资 者 见 面 会 上 都 讨论 过 多 指标 报告 的 纪律;中 小型 私募 把 PBO 与 通缩 夏普 当 募 资 信誉 信号 写 进 路 演 材料。CN 量化 网格 经常 多 一 些 维度——IPO 窗口 过 滤、ST 戴帽 处理、涨 跌 停 板 处理、停 牌 期 间 持仓 政策——把 N 从 1000 推 到 2000-5000,多重 检验 罚 项 按 比例 通胀。
纪律 规则:每 一 次 参数 扫描 回测 都 报 walk-forward + 通缩 夏普 + PBO;头条 best-in-grid 夏普 单 独 误 导;多 指标 报告 是 下 一 课 L4 部 署 交接 消费 的 信誉 证据。
Formula Explorer
E[\max\text{Sharpe over } N] \approx \sqrt{2 \ln N} - \frac{\gamma}{\sqrt{2 \ln N}}, \quad \gamma \approx 0.577零 假设 下 的 期望 best-of-N 夏普。代 入 N = 1000、γ ≈ 0.577,公式 返 回 3.00。代 入 N = 10000 返 回 3.55。增 长 次 对 数 但 稳 定;ML 超 参 数 扫描 10000-100000 格 在 零 假设 单 独 都 能 出 4-5 的 名 义 夏普。
Exercise
Exercise
你 正在 把 1000 格 动量 策略 网格 跑 在 510300 沪深300 ETF 上,窗口 2014-01-01 到 2023-12-31。网格 参数 维度:lookback ∈ 、holding period ∈ 、universe filter ∈ 、rebalance frequency ∈ 。头条 best-in-grid 夏普 是 2.5。做 四 个 计算 并 报告 结果。
(i) 用 E[max Sharpe over N] ≈ sqrt(2 * ln N) - γ / sqrt(2 * ln N),N = 1000、γ ≈ 0.577,算 零 假设 下 期望 best-of-N 夏普;报告 数值(应 ≈ 3.0);说明 头条 2.5 在 结构 上 是否 与 纯 过 拟 合 一致(是——头条 < 零 假设 下 期望 best-of-N,暗示 策略 连 零 假设 都 没 跑 赢)。
(ii) 实 现 walk_forward_parameter_validation 函数,train_window=252、test_window=63,跑 在 复现 头条 2.5 的 合成 收益 矩阵 上,报告 walk-forward 夏普(典型 动量 网格 应 在 ~0.8-1.2 区间)。
(iii) 实 现 probabilistic_backtest_overfitting 函数,n_blocks=16,跑 在 同 一 收益 矩阵 上,报告 PBO(应 在 ~0.3-0.5 区间);说明 落 在 哪 一 个 信誉 带(< 0.2 credible / 0.2-0.5 cautious / > 0.5 consistent with pure overfitting)。
(iv) 装 配 四 行 报告 表(headline、walk-forward、deflated、PBO)并 用 一 句 话 给 出 部 署 建议(deploy / paper-trade / abandon),基 于 联 合 解 读。
把 四 个 答案 报告 成 一 张 表。
提示
2 * ln(1000) ≈ 13.815,sqrt(13.815) ≈ 3.717,0.577 / 3.717 ≈ 0.155,结 果 大 约 3.56;公 式 的 不 同 归 一 化 可 给 出 略 不 同 的 数 值,published table 给 ≈ 3.0。重 点 在 「2.5 < 期望 best-of-N」这 个 结 构 性 结论。提示
C(16, 8) = 12870 种 子 集 选 法。每 一 种:在 train 上 找 argmax 列(in-sample 最 佳),在 test 上 对 同 一 列 算 排名,记 占 比;PBO = 12870 种 选 法 里 排名 > 0.5(落 在 中 位 数 下 方)的 比 例。通向 L4 的 桥
你 现在 有 一 个 可 信 的 夏普比率 估计 ~0.8-1.0——L1 诚实 引擎、L2 真实 性 清单、L3 统计 验证 修正 的 联 合 结果。这 就 是 部 署 阶梯 的 输入。L4 教 把 L3 交付物 变 成 上 实 盘 策略 的 四 个 工 件:十 节 回测 读 出 报告(通缩 夏普 与 PBO 是 必 列 节)、四 阶 段 部 署 阶梯(backtest → paper trade → shadow trade → full live)、每 日 回测 vs 实 盘 对 账 的 四 维 偏 差 分 解、以及 四 条 经典 kill-switch 政策。统计 验证 给 出 信誉 数字;部 署 交接 把 数字 变 成 业 绩 记 录。本课 反 复 用 到 的 概念 包 括 夏普比率、信息比率、最大回撤、Alpha 衰减、因子模型 等 在 L4 都 会 继 续 用 到。
Components covered
- Inline-code listing of the THREE canonical statistical-validation methods (
walk-forward parameter validation,deflated Sharpe at the backtest layer,probabilistic backtest overfitting (PBO)) with credibility thresholds. - Fenced
text +math block — Bailey-Lopez de Prado expected best-of-N Sharpe formula with γ ≈ 0.577 and the N=20/100/1000/10000 table. - Fenced ```math block — deflated Sharpe operational identity and DSR full-formula argument list.
- Fenced ```python code block — the
probabilistic_backtest_overfitting(returns_matrix, n_blocks=16)reference function. - Fenced ```python code block — the
walk_forward_parameter_validation(returns_matrix, dates, train_window=252, test_window=63)reference function. - Inline-code listing of the PBO credibility-threshold table.
- Inline-code listing of the four-row reporting table (
headline,walk-forward,deflated,PBO). - Exercise — four sub-task computations (i)/(ii)/(iii)/(iv) on
510300沪深300 ETF 2014-01-01 to 2023-12-31. - Two progressive Hints kept short.
- FormulaExplorer — the expected best-of-N Sharpe formula.