量化交易中文教材

第 08 章 Bootstrap

本章对应 Wasserman 原书第 8 章《The Bootstrap》。Bootstrap 是 Efron (1979) 发明的估计标准误和构造置信区间的方法。它的思想只有一句话——在经验分布 \(\hat F_n\) 上重演一遍抽样——却解决了第 07 章留下的难题:中位数、偏度、相关系数、Sharpe 比率、最大回撤这些统计量,没有现成的标准误公式时怎么办。在量化研究中,bootstrap 可能是使用频率最高的推断工具,也是最容易被误用的工具。本章除原书内容外,补充了金融数据必需的块 bootstrap。

学习目标

  1. 理解 bootstrap 的两步思想:用 \(\mathbb V_{\hat F_n}(T_n)\) 估计 \(\mathbb V_F(T_n)\),再用模拟近似 \(\mathbb V_{\hat F_n}(T_n)\);能说清两层近似误差各自的来源。
  2. 会写 bootstrap 方差估计算法,能对任意统计量(中位数、相关系数、比值、Sharpe 比率)算出 bootstrap 标准误。
  3. 掌握三种 bootstrap 置信区间——正态区间、枢轴区间、百分位区间——的公式和推导,理解枢轴区间中分位数"反转"的原因。
  4. 了解刀切法及其局限,以及 bootstrap 失效的典型场景(参数空间边界、极值统计量)。
  5. 能对有自相关的收益序列使用移动块 bootstrap 和平稳 bootstrap,并理解为什么 IID bootstrap 会低估标准误。

读前导读

这一章在解决什么问题。 第 01 章用 Delta 方法推出了 Sharpe 比率的标准误,推导不短,而且依赖 IID 和矩存在。如果你要的是最大回撤、中位数、Calmar 比率、两个策略 Sharpe 之差的标准误,公式要么推不出来,要么推出来也不可信。Bootstrap 的办法是放弃推公式,改用模拟:既然第 07 章说历史分布 \(\hat F_n\) 是真实分布 \(F\) 的好替身,那就从历史数据里有放回地重新抽出很多条"平行历史",在每一条上重算统计量,看它们之间差多少。这个差距就近似了真实的抽样误差。

你在风险管理里用过的蒙特卡洛 VaR 是"假设一个模型、模拟很多情景";bootstrap 是"不假设模型、直接从历史里重抽情景",这和历史模拟法 VaR 是同一种精神。本章还会讲它的三个陷阱:样本里没有的极端事件抽不出来;极值类统计量会失效;收益有自相关时逐日重抽会把相关性打乱,必须按块抽。

需要先想起来的数学。

  • 大数定律用于模拟。 抽得越多,模拟平均越接近真实期望。蒙特卡洛期权定价也是靠这一点:模拟 10 万条路径取平均 payoff 再贴现。本章 8.2 节就是这个原理。见第 01 章 1.5.5 节与 第 00 册第 07 章 概率中的分析工具。
  • 分位数与"条件在数据上"。 \(\theta^*_\beta\) 是 \(B\) 个 bootstrap 复制值排序后第 \(\beta B\) 个;\(\mathbb E(\cdot\mid X_1,\dots,X_n)\) 的意思是"把原始数据当作固定不变,只对重抽样的随机性求期望"。
  • \(e\) 的极限 \((1-1/n)^n\to e^{-1}\)。 与连续复利 \((1+r/n)^n\to e^r\) 同一个公式,取 \(r=-1\)。用来算"某个观测一次都没被抽中"的概率约 36.8%。见 第 00 册第 04 章 级数与收敛。
  • 单调变换保持分位数。 若 \(m\) 递增,则 \(m(X)\) 的中位数等于 \(m(X\text{ 的中位数})\)。例:收益率的中位数是 5%,则 \(\log(1+r)\) 的中位数就是 \(\log1.05\)。但均值没有这个性质(Jensen)。百分位区间的论证用到它。
  • 自协方差与长期方差。 序列相关时 \(\mathbb V(\bar X)\ne\sigma^2/n\),还要加上各阶自协方差的贡献。这个结论 Newey–West 标准误也用到。见第 01 章 1.3 节的方差公式。

怎么读这一章。 8.1–8.3 是核心,必须读懂"两个世界"的对照和"两层近似"。8.4 的三种区间中,正态区间和百分位区间最常用,枢轴区间的推导值得跟一遍,理解为什么分位数要"反转";8.4.3 的附录论证第一次可以只看结论。例 8.5–8.7 挑一个读即可,例 8.7 的"按受试者整体重抽样"对应量化里"按日期整体重抽样",比较重要。8.5.3 和 8.6.3(块 bootstrap)对金融数据是必读的。


8.1 问题与思想

设 \(T_n=g(X_1,\dots,X_n)\) 是一个统计量(数据的任意函数),我们想知道它的方差 \(\mathbb V_F(T_n)\)。下标 \(F\) 强调:方差依赖于未知的真实分布 \(F\)。

举个简单的例子。\(T_n=\bar X_n\) 时,\(\mathbb V_F(T_n)=\sigma^2/n\),其中 \(\sigma^2=\int(x-\mu)^2\,dF(x)\) 是 \(F\) 的泛函。我们不知道 \(F\),但可以用插入原则:把 \(F\) 换成 \(\hat F_n\),得 \(\hat\sigma^2/n\),\(\hat\sigma^2=\frac1n\sum(X_i-\bar X_n)^2\)。这就是我们一直在用的 \(\widehat{\text{se}}=\hat\sigma/\sqrt n\)。

Bootstrap 把这个想法推广到任意统计量,分两步:

  • 第 1 步(插入):用 \(\mathbb V_{\hat F_n}(T_n)\) 估计 \(\mathbb V_F(T_n)\)。\(\mathbb V_{\hat F_n}(T_n)\) 的意思是:"假如数据真是从 \(\hat F_n\) 抽出来的,\(T_n\) 的方差是多少"。
  • 第 2 步(模拟):用蒙特卡洛模拟近似计算 \(\mathbb V_{\hat F_n}(T_n)\)。

对样本均值,第 1 步就能得到闭式结果 \(\hat\sigma^2/n\)。对中位数、相关系数等复杂统计量,写不出 \(\mathbb V_{\hat F_n}(T_n)\) 的公式,才需要第 2 步。

8.2 模拟:用大数定律算期望

第 2 步依赖一个简单事实。从分布 \(G\) 抽 IID 样本 \(Y_1,\dots,Y_B\),由大数定律

\[\frac1B\sum_{j=1}^BY_j\xrightarrow{P}\int y\,dG(y)=\mathbb E(Y)\qquad(B\to\infty).\]
模拟中 \(B\) 可以任意大,所以模拟平均与真实期望之差可以忽略。一般地 \(\frac1B\sum h(Y_j)\xrightarrow{P}\mathbb E\,h(Y)\),特别地
\[\frac1B\sum_{j=1}^B(Y_j-\bar Y)^2=\frac1B\sum Y_j^2-\Big(\frac1B\sum Y_j\Big)^2\xrightarrow{P}\mathbb E(Y^2)-(\mathbb EY)^2=\mathbb V(Y).\]
所以只要能从某个分布中抽样,就能用模拟值的样本方差近似它的方差。

8.3 Bootstrap 方差估计

8.3.1 两个世界

要模拟 \(T_n\) 在 \(\hat F_n\) 下的分布,就从 \(\hat F_n\) 中抽 \(X_1^*,\dots,X_n^*\),算 \(T_n^*=g(X_1^*,\dots,X_n^*)\)。对照如下:

\[\begin{aligned} \text{真实世界:}&\quad F\ \Longrightarrow\ X_1,\dots,X_n\ \Longrightarrow\ T_n=g(X_1,\dots,X_n)\\ \text{Bootstrap 世界:}&\quad \hat F_n\ \Longrightarrow\ X_1^*,\dots,X_n^*\ \Longrightarrow\ T_n^*=g(X_1^*,\dots,X_n^*) \end{aligned}\]

怎样从 \(\hat F_n\) 抽样?\(\hat F_n\) 在每个数据点上放 \(1/n\) 的质量,所以从 \(\hat F_n\) 抽一个观测,等价于从原始数据中随机挑一个点。抽 \(n\) 个,就是从原数据中有放回地抽 \(n\) 个。有放回是关键:每个 bootstrap 样本里,有的原始观测出现多次,有的一次也没出现(平均约有 \(1-(1-1/n)^n\approx63.2\%\) 的不同观测被抽到)。

白话解释:两个世界的对照是本章的全部要点。真实世界里,我们想知道"如果历史重演,Sharpe 会差多少",但历史只有一条。Bootstrap 世界里,原始样本扮演"总体",原始样本的 Sharpe \(\hat\theta_n\) 扮演"真值",每条重抽的序列扮演"一次可能的历史"。在这个世界里一切都已知,可以无限重演。我们赌的是:bootstrap 世界里"估计值围绕真值的波动"与真实世界里的波动相似。 为什么必须有放回?如果不放回地抽 \(n\) 个,得到的永远是原始样本的一个排列,均值、Sharpe 都不变,毫无波动可言。有放回才能制造出"有的日子出现两次、有的日子没出现"的差异。 63.2% 的来源:某个观测在一次抽取中没被抽到的概率是 \(1-1/n\),\(n\) 次都没被抽到是 \((1-1/n)^n\approx e^{-1}\approx0.368\)。

8.3.2 算法

Bootstrap 方差估计:

  1. 从原数据有放回地抽 \(n\) 个,得 \(X_1^*,\dots,X_n^*\);
  2. 计算 \(T_n^*=g(X_1^*,\dots,X_n^*)\);
  3. 重复第 1–2 步 \(B\) 次,得到 \(T_{n,1}^*,\dots,T_{n,B}^*\);
  4. 计算
    \[v_{boot}=\frac1B\sum_{b=1}^B\Big(T_{n,b}^*-\frac1B\sum_{r=1}^BT_{n,r}^*\Big)^2,\qquad\widehat{\text{se}}_{boot}=\sqrt{v_{boot}}.\]

例 8.1(中位数的标准误)。 原书给出 R 风格的伪代码,写成 Python 是:

import numpy as np
def boot_se_median(x, B=1000, rng=np.random.default_rng(1)):
    n = len(x)
    tboot = [np.median(rng.choice(x, size=n, replace=True)) for _ in range(B)]
    return np.std(tboot, ddof=1)

x = np.random.default_rng(0).standard_normal(400)
print(boot_se_median(x), "理论约", np.sqrt(np.pi / 2 / 400))

输出为 0.0759 理论约 0.0627(已四舍五入)(理论值是正态样本中位数的渐近标准误 \(\sqrt{\pi/(2n)}\);bootstrap 对中位数这种不光滑统计量的标准误估计偏粗糙,但量级正确)。计算量是 \(O(B\times\text{计算一次 }g\text{ 的成本})\)。

8.3.3 两层近似

\[\mathbb V_F(T_n)\ \overset{\text{误差不太小}}{\approx}\ \mathbb V_{\hat F_n}(T_n)\ \overset{\text{误差小}}{\approx}\ v_{boot}.\]

金融直觉:这和蒙特卡洛定价的两种误差是一回事。用 GBM 模型对期权做蒙特卡洛定价,路径数从 1 万加到 100 万,只能消除"模拟噪声";如果波动率参数本身估错了(模型误差),模拟再多次价格也是错的。bootstrap 中 \(B\) 对应路径数,\(\hat F_n\) 对应你喂给模拟器的"模型",后者的误差由数据量 \(n\) 决定。

第一层误差来自用 \(\hat F_n\) 代替 \(F\),由样本量 \(n\) 决定,我们无法控制;第二层是模拟误差,加大 \(B\) 就能消除。一个常见的误解是"bootstrap 次数越多越准"——\(B\) 再大也只能消除第二层误差,第一层误差是数据本身的限度。实践中,估计标准误 \(B\) 取几百到一千就够了;估计置信区间的端点(尾部分位数)需要更多,通常取几千。

例 8.2. 神经数据(第 07 章例 7.2)的偏度插入估计 \(\hat\theta=\frac1n\sum(X_i-\bar X_n)^3/\hat\sigma^3\)。对每个 bootstrap 样本计算偏度,\(B=1000\),得偏度估计的标准误为 0.16。(正态分布的偏度为 0。)


8.4 Bootstrap 置信区间

原书介绍三种方法。设 \(\theta=T(F)\),\(\hat\theta_n=T(\hat F_n)\),\(\hat\theta^*_{n,1},\dots,\hat\theta^*_{n,B}\) 是 bootstrap 复制值,\(\theta^*_\beta\) 表示它们的 \(\beta\) 样本分位数。

8.4.1 方法一:正态区间

\[T_n\pm z_{\alpha/2}\,\widehat{\text{se}}_{boot}.\]

最简单,但只有在 \(T_n\) 的分布接近正态时才准确。它对称,不能反映抽样分布的偏斜,还可能超出参数的取值范围(如相关系数大于 1)。

8.4.2 方法二:枢轴区间

思路。 定义枢轴量(pivot)\(R_n=\hat\theta_n-\theta\),即估计误差,其 CDF 为 \(H(r)=\mathbb P_F(R_n\le r)\)。如果知道 \(H\),令

\[a=\hat\theta_n-H^{-1}\Big(1-\frac\alpha2\Big),\qquad b=\hat\theta_n-H^{-1}\Big(\frac\alpha2\Big),\]
则
\[\mathbb P(a\le\theta\le b)=\mathbb P(\hat\theta_n-b\le R_n\le\hat\theta_n-a)=H\Big(H^{-1}\big(1-\tfrac\alpha2\big)\Big)-H\Big(H^{-1}\big(\tfrac\alpha2\big)\Big)=1-\alpha,\]
这是精确的 \(1-\alpha\) 区间。注意上界 \(b\) 用的是误差的下分位数,下界 \(a\) 用的是误差的上分位数——估计值偏高的可能性越大,真值就越可能在估计值下方,所以要"反转"。

用 bootstrap 估计 \(H\)。 在 bootstrap 世界里,\(\hat\theta_n\) 扮演"真值"的角色,误差是 \(R^*_{n,b}=\hat\theta^*_{n,b}-\hat\theta_n\):

\[\hat H(r)=\frac1B\sum_{b=1}^BI(R^*_{n,b}\le r).\]
\(R^*\) 的 \(\beta\) 分位数 \(r^*_\beta=\theta^*_\beta-\hat\theta_n\),代入得 \(\hat a=\hat\theta_n-r^*_{1-\alpha/2}=2\hat\theta_n-\theta^*_{1-\alpha/2}\),\(\hat b=2\hat\theta_n-\theta^*_{\alpha/2}\)。

\(1-\alpha\) bootstrap 枢轴置信区间:

\[C_n=\Big(2\hat\theta_n-\theta^*_{1-\alpha/2},\ \ 2\hat\theta_n-\theta^*_{\alpha/2}\Big).\]

定理 8.3. 在 \(T(F)\) 满足较弱条件时,\(\mathbb P_F(T(F)\in C_n)\to1-\alpha\)(\(n\to\infty\))。

推导拆解:第一个等号把"\(a\le\theta\le b\)"改写成误差 \(R_n=\hat\theta_n-\theta\) 的范围:三边同时乘 \(-1\)(不等号反向)再加 \(\hat\theta_n\),得 \(\hat\theta_n-b\le R_n\le\hat\theta_n-a\)。代入 \(a,b\) 的定义,\(\hat\theta_n-b=H^{-1}(\alpha/2)\),\(\hat\theta_n-a=H^{-1}(1-\alpha/2)\)。连续 CDF 下,"落在两个分位数之间"的概率就是 \((1-\alpha/2)-\alpha/2=1-\alpha\)。 数值例:样本 Sharpe \(\hat\theta=0.05\),bootstrap 复制值的 2.5% 与 97.5% 分位数分别是 0.00 与 0.14(右偏:向上 0.09、向下 0.05)。bootstrap 世界告诉我们"估计值容易高估 0.09、低估 0.05"。回到真实世界,真值应该在 \(0.05-0.09=-0.04\) 到 \(0.05+0.05=0.10\) 之间,即 \((2\times0.05-0.14,\ 2\times0.05-0.00)\)。百分位区间则直接给 \((0.00,0.14)\)。抽样分布对称时两者一致,偏斜时两者朝相反方向偏移。

8.4.3 方法三:百分位区间

\[C_n=\Big(\theta^*_{\alpha/2},\ \ \theta^*_{1-\alpha/2}\Big),\]

直接取 bootstrap 复制值的分位数。它最直观,而且自动落在参数的取值范围内。

为什么百分位区间合理(原书附录)。 假设存在一个单调变换 \(U=m(T)\) 使 \(U\sim N(\phi,c^2)\),\(\phi=m(\theta)\)。我们不需要知道 \(m\),只需要它存在。单调变换保持分位数,所以 bootstrap 复制值变换后的分位数 \(u^*_\beta=m(\theta^*_\beta)\)。又因 \(U\) 正态,\(u^*_{\alpha/2}\approx U-z_{\alpha/2}c\),\(u^*_{1-\alpha/2}\approx U+z_{\alpha/2}c\)。于是

\[\mathbb P(\theta^*_{\alpha/2}\le\theta\le\theta^*_{1-\alpha/2})=\mathbb P\big(U-cz_{\alpha/2}\le\phi\le U+cz_{\alpha/2}\big)=\mathbb P\Big(-z_{\alpha/2}\le\frac{U-\phi}c\le z_{\alpha/2}\Big)=1-\alpha.\]
精确的正态化变换很少存在,但近似的往往存在(例如相关系数的 Fisher \(z\) 变换),这就是百分位区间常常表现不错的原因。

白话解释:这段论证的逻辑是"换个刻度看问题"。相关系数在 0.9 附近被上限 1 挤压,抽样分布是歪的;但换成 Fisher \(z=\tfrac12\log\frac{1+\rho}{1-\rho}\) 的刻度后,它近似对称正态。在对称正态的刻度上,"估计值 ± 1.96 个标准差"恰好就是 bootstrap 分布的 2.5% 与 97.5% 分位数;再换回原刻度,单调变换不改变"第几个分位数",所以原刻度上直接取 bootstrap 分位数也对。妙处在于你不需要知道这个变换是什么,只要它存在。 读证明时有一处要留意:\(u^*_\beta\approx U\pm z c\) 这一步假设了 bootstrap 世界中变换后的复制值近似服从 \(N(U,c^2)\),即以观测值 \(U\) 为中心——这是 bootstrap 原理本身的假设,原文略过了。

8.4.4 例子

例 8.4(神经数据偏度的 95% 区间)。 正态 \((1.44,2.09)\);枢轴 \((1.48,2.11)\);百分位 \((1.42,2.03)\)。三者都是近似的,精度相近。还有更精确但更复杂的 bootstrap 区间(如 BCa,bias-corrected and accelerated),本书不讨论,见 Efron & Tibshirani (1993)。

例 8.5(胆固醇数据,中位数之差)。 第 07 章例 7.15 的两组数据,目标是两组胆固醇中位数之差。两组要各自有放回重抽样(保持原样本量 \(n_1,n_2\)),每次计算 median(第二组) − median(第一组),\(B=1000\)。结果:点估计 18.5,bootstrap se 7.42;95% 区间——正态 \((3.7,33.3)\),枢轴 \((5.0,34.0)\),百分位 \((5.0,33.3)\)。都不含 0,第二组胆固醇似乎更高,但区间很宽,"高多少"的不确定性很大。

作者提醒:下面两例的样本量很小,小样本下统计方法可能不可靠,仅供教学,结果应持怀疑态度。

例 8.6(Efron 最早的 bootstrap 示例:法学院数据)。 15 所法学院的入学考试平均分 LSAT 与本科平均绩点 GPA:

LSAT 576 635 558 578 666 580 555 661 651 605 653 575 545 572 594
GPA 3.39 3.30 2.81 3.03 3.44 3.07 3.00 3.43 3.36 3.13 3.12 2.74 2.76 2.88 2.96

关心相关系数 \(\theta\),插入估计即样本相关 \(\hat\theta=0.776\);\(B=1000\) 得 \(\widehat{\text{se}}=0.137\)。bootstrap 复制值的直方图左偏(相关系数上限为 1,向上的空间有限)。正态 95% 区间 \(0.78\pm2\widehat{\text{se}}=(0.51,1.00)\),百分位区间 \((0.46,0.96)\)。大样本下两者会更接近。

例 8.7(生物等效性,取自 Efron & Tibshirani 1993)。 药企推出新药时有时需证明生物等效(bioequivalence):新药与现有疗法没有实质差异。8 名受试者分别使用安慰剂、旧贴片、新贴片,测量血液中的激素水平:

受试者 安慰剂 旧 新 \(Z\)=旧−安慰剂 \(Y\)=新−旧
1 9243 17649 16449 8406 −1200
2 9671 12013 14614 2342 2601
3 11792 19979 17274 8187 −2705
4 13357 21816 23798 8459 1982
5 9055 13850 12560 4795 −1290
6 6290 9806 10157 3516 351
7 12412 17208 16570 4796 −638
8 18806 29044 26325 10238 −2719

FDA 的标准是 \(|\theta|\le0.20\),\(\theta=\mathbb E_F(Y)/\mathbb E_F(Z)\)。插入估计 \(\hat\theta=\bar Y/\bar Z=-452.3/6342=-0.0713\);bootstrap se 为 0.105;\(B=1000\) 得 95% 区间 \((-0.24,0.15)\)。区间没有完全落在 \((-0.20,0.20)\) 内,因此在 95% 水平上未能证明生物等效。

注意这里重抽样的单位是受试者:每次抽中某个受试者,就把他的 \((Z,Y)\) 一起带走,保留两者之间的相关性。\(\bar Y/\bar Z\) 这种比值结构与 Beta(协方差/方差)、信息比率完全相同,也可以用第 01 章的多元 Delta 方法求标准误,两者可以相互验证。


8.5 刀切法与 bootstrap 的局限

8.5.1 刀切法

刀切法(jackknife)由 Quenouille (1949) 提出,比 bootstrap 早,计算量小,但适用范围窄。记 \(T_{(-i)}\) 为去掉第 \(i\) 个观测后算出的统计量,\(\bar T_n=\frac1n\sum_iT_{(-i)}\),刀切方差估计为

\[v_{jack}=\frac{n-1}{n}\sum_{i=1}^n\big(T_{(-i)}-\bar T_n\big)^2,\qquad\widehat{\text{se}}_{jack}=\sqrt{v_{jack}}.\]
因子 \(n-1\) 是因为"去掉一个点"的统计量之间几乎相同,波动被压缩了约 \(n-1\) 倍,需要放大回来。

推导拆解:用样本均值验算一遍就清楚了。去掉第 \(i\) 个点后 \(T_{(-i)}=\frac{n\bar X-X_i}{n-1}\),而这些值的平均 \(\bar T_n=\bar X\),所以 \(T_{(-i)}-\bar T_n=-\frac{X_i-\bar X}{n-1}\)——每个偏差只有原数据偏差的 \(1/(n-1)\)。代入:\(v_{jack}=\frac{n-1}n\cdot\frac{1}{(n-1)^2}\sum(X_i-\bar X)^2=\frac{1}{n(n-1)}\sum(X_i-\bar X)^2=\frac{S_n^2}{n}\),正好是均值方差的标准估计。系数 \(\frac{n-1}{n}\) 就是为了让这个特例精确成立而选的。在适当条件下 \(v_{jack}/\mathbb V(T_n)\xrightarrow{as}1\),即相合。但与 bootstrap 不同,刀切法不能相合地估计样本分位数(如中位数)的标准误——中位数对去掉单个点不够"光滑"。刀切法对光滑统计量(均值的光滑函数,如 Sharpe 比率)效果很好,而且结果确定、可复现。

8.5.2 Bootstrap 何时失效

Bootstrap 的有效性依赖于 \(T\) 对 \(F\) 的"光滑性"。原书习题 7 给出一个典型的失效例子:\(X_1,\dots,X_n\sim\text{Uniform}(0,\theta)\),\(\hat\theta=X_{\max}\)。真实世界中 \(\hat\theta\) 是连续变量,\(\mathbb P(\hat\theta=\theta)=0\);但 bootstrap 世界里,只要原样本的最大值被抽中,\(\hat\theta^*\) 就等于 \(\hat\theta\):

\[\mathbb P(\hat\theta^*=\hat\theta)=1-(1-1/n)^n\to1-e^{-1}\approx0.632.\]
bootstrap 分布有一个巨大的点质量,完全不像真实的抽样分布。失效的原因是 \(\theta\) 在支撑的边界上,极值统计量不满足 bootstrap 需要的光滑条件。

对量化的启示:最大回撤、最差单日亏损、"一组策略中最大的 Sharpe"等极值型统计量,直接 bootstrap 可能严重失真。可以考虑子抽样(subsampling,抽 \(m\ll n\) 个不放回)、极值理论,或者至少用模拟检验 bootstrap 在该问题上的覆盖率。

8.5.3 IID 假设:金融数据的块 bootstrap

本章到此为止的 bootstrap 都假设数据 IID。有放回逐点重抽样会彻底打乱时间顺序,从而抹掉所有序列相关。如果收益有正自相关(趋势策略、流动性差的资产、平滑估值的私募基金),真实的均值标准误比 IID 公式大;IID bootstrap 会和 IID 公式一样低估它。

推导拆解:为什么正自相关会放大均值的标准误?由第 01 章的方差公式, \(\mathbb V(\bar X_n)=\frac1{n^2}\Big[\sum_i\mathbb V(X_i)+2\sum_{i<j}\operatorname{Cov}(X_i,X_j)\Big]\)。 IID 时协方差项为 0,得 \(\sigma^2/n\)。AR(1) 时 \(\operatorname{Cov}(X_i,X_{i+k})=\sigma^2\phi^k\),\(n\) 很大时方括号约为 \(n\sigma^2(1+2\phi+2\phi^2+\dots)=n\sigma^2\frac{1+\phi}{1-\phi}\)(用了几何级数 \(\sum_{k\ge1}\phi^k=\frac{\phi}{1-\phi}\))。所以 \(\mathbb V(\bar X_n)\approx\frac{\sigma^2}{n}\cdot\frac{1+\phi}{1-\phi}\),\(\phi=0.3\) 时放大 1.86 倍,标准误放大 \(\sqrt{1.86}\approx1.36\) 倍。IID bootstrap 把日子打乱,相当于强行令所有协方差为 0,所以只能得到 \(\sigma^2/n\)。 金融直觉:这就是私募股权、房地产基金"估值平滑"导致波动率和 Sharpe 失真的同一机制。块 bootstrap 的做法相当于"按连续的月份或季度整段抽取",段内的相关性被保留下来。

补救办法是按块重抽样,保留块内的依赖结构(以下方法不在原书第 8 章范围内,是金融应用的标准补充):

  • 移动块 bootstrap(moving block bootstrap):把序列切成所有长度为 \(L\) 的重叠块,有放回地抽 \(\lceil n/L\rceil\) 个块首尾相接,截取前 \(n\) 个。
  • 平稳 bootstrap(stationary bootstrap,Politis & Romano 1994):块长随机,服从均值为 \(L\) 的几何分布;序列首尾循环相接。这样生成的重抽样序列仍是平稳的,对块长的选择也没那么敏感。

块长 \(L\) 要大到能覆盖主要的相关结构,又要小到有足够多的块可供重组。常见的经验取法是 \(L\propto n^{1/3}\),也有基于数据的自动选择方法(Politis & White 2004)。对于没有自相关但有波动聚集的收益(典型的日收益),IID bootstrap 对均值的标准误影响不大,但对方差、Sharpe 比率等二阶量的标准误仍有偏差,块 bootstrap 更稳妥。


8.6 量化实战

8.6.1 Sharpe 比率:bootstrap、刀切与 Delta 方法

用两年(504 天)\(t(5)\) 厚尾日收益,真实日 Sharpe 0.08(年化约 1.27),计算样本 Sharpe 的三种标准误和三种 bootstrap 区间。

import numpy as np
from scipy import stats
rng = np.random.default_rng(8)

def sharpe(x, axis=-1):
    return x.mean(axis) / x.std(axis, ddof=1)

# 一个策略的 504 天日收益:t(5) 厚尾,日 Sharpe 约 0.08(年化约 1.27)
n, nu = 504, 5
x = 0.0008 + 0.01 * rng.standard_t(nu, n) / np.sqrt(nu / (nu - 2))
th = sharpe(x)

# Bootstrap:B 次有放回重抽样
B = 5000
idx = rng.integers(0, n, size=(B, n))
tb = sharpe(x[idx])
se_boot = tb.std(ddof=1)

# 刀切法
jk = np.array([sharpe(np.delete(x, i)) for i in range(n)])
se_jack = np.sqrt((n - 1) / n * np.sum((jk - jk.mean()) ** 2))

# Delta 方法(含样本偏度、峰度)
sk, ku = stats.skew(x), stats.kurtosis(x, fisher=False)
se_delta = np.sqrt((1 - sk * th + (ku - 1) / 4 * th**2) / n)
print(f"样本日 Sharpe={th:.4f}(年化 {th*np.sqrt(252):.2f})")
print(f"se: bootstrap={se_boot:.4f}  jackknife={se_jack:.4f}  Delta={se_delta:.4f}")

z = stats.norm.ppf(0.975)
lo, hi = np.quantile(tb, [0.025, 0.975])
print(f"95% 正态区间   ({th - z*se_boot:.4f}, {th + z*se_boot:.4f})")
print(f"95% 枢轴区间   ({2*th - hi:.4f}, {2*th - lo:.4f})")
print(f"95% 百分位区间 ({lo:.4f}, {hi:.4f})")
print(f"bootstrap 下 Sharpe<=0 的比例: {np.mean(tb <= 0):.4f}")

输出:

样本日 Sharpe=0.0515(年化 0.82)
se: bootstrap=0.0445  jackknife=0.0453  Delta=0.0448
95% 正态区间   (-0.0357, 0.1387)
95% 枢轴区间   (-0.0367, 0.1383)
95% 百分位区间 (-0.0353, 0.1398)
bootstrap 下 Sharpe<=0 的比例: 0.1198

三种标准误高度一致(0.045 左右),说明对这种"均值的光滑函数",bootstrap、刀切法和 Delta 方法可以互相验证。三种区间也几乎相同,因为抽样分布接近正态。真实年化 Sharpe 1.27 的策略,这次两年样本只算出 0.82,而 95% 区间从负值一直延伸到年化 2.2 左右——两年日数据对 Sharpe 的信息量非常有限。

最后一行的"bootstrap 复制值中 Sharpe ≤ 0 的比例"常被当作"策略无效的概率",这是不对的:它是 bootstrap 分布的一个尾部面积,大致对应一个单侧检验的 p 值,不是"\(\theta\le0\) 的概率"(第 06 章 6.4.2 节,置信不是关于参数的概率;用 bootstrap 标准误做 Wald 检验见本册第 10a 章)。

8.6.2 三种区间的覆盖率:何时出问题

上面只是一个样本。区间方法好不好,要看它在重复抽样下的覆盖率(原书习题 2、3 的思路)。下面对两类策略各做 1000 次"回测",每次 \(n=252\)、\(B=1000\),统计三种 95% 区间套住真实 Sharpe 的比例。

import numpy as np
rng = np.random.default_rng(88)

def sharpe(x, axis=-1):
    return x.mean(axis) / x.std(axis, ddof=1)

def short_vol(size):                     # 第 01 章的负偏收益:1% 概率暴跌
    return np.where(rng.random(size) < 0.01, -0.0984, 0.0016) + rng.normal(0, 0.002, size)

def t5(size):
    return 0.0006 + 0.01 * rng.standard_t(5, size) / np.sqrt(5 / 3)

true_sr = {"t(5)": 0.06, "负偏": 0.0006 / np.sqrt(0.01 * 0.99 * 0.1**2 + 0.002**2)}
n, R, B, z = 252, 1000, 1000, 1.959964
for name, gen in [("t(5)", t5), ("负偏", short_vol)]:
    hits = np.zeros(3)
    for _ in range(R):
        x = gen(n)
        th = sharpe(x)
        tb = sharpe(x[rng.integers(0, n, size=(B, n))])
        se = tb.std(ddof=1)
        lo, hi = np.quantile(tb, [0.025, 0.975])
        s = true_sr[name]
        hits += [abs(th - s) <= z * se, 2*th - hi <= s <= 2*th - lo, lo <= s <= hi]
    print(f"{name}: 名义 95%,实际覆盖率 正态={hits[0]/R:.3f} 枢轴={hits[1]/R:.3f} 百分位={hits[2]/R:.3f}")

输出:

t(5): 名义 95%,实际覆盖率 正态=0.956 枢轴=0.946 百分位=0.957
负偏: 名义 95%,实际覆盖率 正态=0.898 枢轴=0.849 百分位=0.908

对称厚尾收益下,三种区间都接近 95%。对"1% 概率暴跌"的负偏策略,一年数据下三种区间的覆盖率都不足,枢轴区间只有 85%。原因和第 01 章 1.6.2 节一样:约 8% 的样本里一次暴跌都没出现,bootstrap 只能从样本里重抽,样本里没有的暴跌,bootstrap 永远抽不出来。这是 bootstrap 最根本的局限:它只能重现数据里已有的信息。对尾部风险主导的策略,必须用更长的历史、压力情景或参数化的尾部模型来补充。

8.6.3 自相关收益:IID bootstrap 与块 bootstrap

模拟 1000 天 AR(1) 收益(\(\phi=0.3\),正自相关),比较均值标准误的几种估计。理论上,AR(1) 的长期方差使均值标准误放大 \(\sqrt{(1+\phi)/(1-\phi)}\approx1.36\) 倍。

import numpy as np
rng = np.random.default_rng(808)

def ar1(n, phi=0.3, sd=0.01, mu=0.0005):
    e = rng.normal(0, sd * np.sqrt(1 - phi**2), n)   # 平稳方差为 sd^2
    x = np.empty(n); x[0] = rng.normal(0, sd)
    for t in range(1, n):
        x[t] = phi * x[t - 1] + e[t]
    return mu + x

def iid_boot(x, B):
    n = len(x)
    return x[rng.integers(0, n, (B, n))].mean(1)

def moving_block_boot(x, B, L):
    n = len(x); k = int(np.ceil(n / L))
    starts = rng.integers(0, n - L + 1, (B, k))
    idx = (starts[:, :, None] + np.arange(L)).reshape(B, -1)[:, :n]
    return x[idx].mean(1)

def stationary_boot(x, B, L):
    # Politis-Romano:块长服从均值为 L 的几何分布,首尾循环相接
    n = len(x)
    idx = np.empty((B, n), dtype=int)
    idx[:, 0] = rng.integers(0, n, B)
    new_block = rng.random((B, n)) < 1 / L
    jump = rng.integers(0, n, (B, n))
    for t in range(1, n):
        idx[:, t] = np.where(new_block[:, t], jump[:, t], (idx[:, t - 1] + 1) % n)
    return x[idx].mean(1)

n, phi, B, L = 1000, 0.3, 2000, 20
true_se = np.std([ar1(n, phi).mean() for _ in range(4000)])
theory = 0.01 / np.sqrt(n) * np.sqrt((1 + phi) / (1 - phi))
x = ar1(n, phi)
print(f"均值的真实 se: 模拟={true_se*1e4:.2f}bp  理论≈{theory*1e4:.2f}bp")
print(f"IID bootstrap se       = {iid_boot(x, B).std()*1e4:.2f}bp")
print(f"移动块 bootstrap se(L={L}) = {moving_block_boot(x, B, L).std()*1e4:.2f}bp")
print(f"平稳 bootstrap se(L={L})   = {stationary_boot(x, B, L).std()*1e4:.2f}bp")

# bootstrap 失效:Uniform(0,1) 样本的最大值
m = 50
u = rng.uniform(0, 1, m)
mb = u[rng.integers(0, m, (10000, m))].max(1)
print(f"P(θ*=θ̂) 模拟={np.mean(mb == u.max()):.3f}  理论 1-(1-1/n)^n={1-(1-1/m)**m:.3f}")

输出:

均值的真实 se: 模拟=4.27bp  理论≈4.31bp
IID bootstrap se       = 3.16bp
移动块 bootstrap se(L=20) = 4.59bp
平稳 bootstrap se(L=20)   = 4.35bp
P(θ*=θ̂) 模拟=0.631  理论 1-(1-1/n)^n=0.636

IID bootstrap 把标准误低估了约 26%(3.16 vs 4.27),对应的 t 值会被高估约 1.35 倍——一个真实 t 值 1.5 的策略会被报告成 2.0,"显著"了。两种块 bootstrap 都恢复到真实水平附近。最后两行验证了 8.5.2 节的失效例子:\(n=50\) 时 bootstrap 最大值恰好等于样本最大值的概率是 0.636(\(n\to\infty\) 时趋于 0.632)。

8.6.4 Bootstrap 在量化中的其他用途

  • 两策略比较:把例 8.5 的两组重抽样改为同步重抽样(同一天的两个策略收益一起抽),就得到两策略 Sharpe 之差的 bootstrap 区间。同期策略的收益相关,不能像例 8.5 那样各自独立重抽样。
  • 数据窥探检验:同时测试几百个策略后挑出最好的一个,其 Sharpe 必然被高估。White (2000) 的 Reality Check 与 Hansen (2005) 的 SPA 检验用(平稳)bootstrap 构造"最好策略的 Sharpe"在原假设下的分布,是本章方法在策略筛选中的直接延伸(详见本册第 10b 章多重检验与第 11 册)。
  • 组合与模型稳定性:对收益样本 bootstrap 后反复做均值-方差优化,观察权重的波动(Michaud 的再抽样有效前沿 resampled efficiency);或者反复估计因子载荷,看哪些暴露是稳定的。
  • Bootstrap 不是万能的:它不能创造样本里没有的信息(8.6.2 节),不能修复数据窥探带来的选择偏差(除非像 Reality Check 那样把选择过程一起放进重抽样),也不能修复非平稳性(结构突变前后的数据混在一起重抽样,只会得到一个"平均"的、哪个时期都不像的分布)。

本章小结

Bootstrap 是插入原则加蒙特卡洛模拟:用经验分布 \(\hat F_n\) 代替未知的 \(F\),从原数据有放回地重抽样 \(B\) 次,用复制值的波动近似统计量的抽样波动。误差有两层,模拟误差随 \(B\) 增大而消失,用 \(\hat F_n\) 代替 \(F\) 的误差则受样本量限制。三种置信区间各有特点:正态区间最简单但要求近似正态;枢轴区间基于误差分布,要"反转"分位数;百分位区间直接取复制值分位数,在存在近似正态化变换时合理。刀切法计算便宜,但对分位数失效;bootstrap 在参数边界和极值统计量上也会失效。金融收益有序列相关时,必须用块 bootstrap,否则标准误被低估、显著性被高估。

概念 公式 / 要点
Bootstrap 思想 \(\mathbb V_F(T_n)\approx\mathbb V_{\hat F_n}(T_n)\approx v_{boot}\)
重抽样 从原数据有放回抽 \(n\) 个
方差估计 \(v_{boot}=\frac1B\sum_b(T^*_{n,b}-\bar T^*)^2\)
正态区间 \(T_n\pm z_{\alpha/2}\widehat{\text{se}}_{boot}\)
枢轴区间 \((2\hat\theta_n-\theta^*_{1-\alpha/2},\ 2\hat\theta_n-\theta^*_{\alpha/2})\)
百分位区间 \((\theta^*_{\alpha/2},\ \theta^*_{1-\alpha/2})\)
刀切法 \(v_{jack}=\frac{n-1}n\sum_i(T_{(-i)}-\bar T_n)^2\),对分位数不相合
失效例子 Uniform 最大值:\(\mathbb P(\hat\theta^*=\hat\theta)\to1-e^{-1}\approx0.632\)
两组独立样本 各组分别重抽样,保持各自样本量
时间序列 移动块 / 平稳 bootstrap,块长 \(L\propto n^{1/3}\) 量级

练习

基础

  1. 用例 8.6 的法学院数据,计算相关系数的插入估计、bootstrap 标准误和三种 95% 区间(原书习题 1)。哪种区间超出了 \([-1,1]\)?为什么百分位区间不会?
  2. 证明:若原样本没有重复值,不同的 bootstrap 样本(作为多重集合)共有 \(\binom{2n-1}{n}\) 个。(提示:把 \(n\) 个球放进 \(n\) 个桶。)
  3. 设 \(X_1^*,\dots,X_n^*\) 是 bootstrap 样本。求 \(\mathbb E(\bar X_n^*\mid X_1,\dots,X_n)\) 与 \(\mathbb V(\bar X_n^*\mid X_1,\dots,X_n)\)。(答案:\(\bar X_n\) 与 \(\hat\sigma^2/n\),\(\hat\sigma^2=\frac1n\sum(X_i-\bar X_n)^2\)。这说明对样本均值,bootstrap 精确地复现了插入公式。)
  4. 解释为什么刀切法方差公式中有因子 \((n-1)/n\) 乘以平方和,而不是 \(1/(n-1)\)。(提示:对 \(T=\bar X\) 直接计算,\(T_{(-i)}-\bar T_n=-(X_i-\bar X)/(n-1)\)。)
  5. 两个策略在同一时期交易。写出它们 Sharpe 之差的 bootstrap 区间算法,说明为什么不能对两个策略各自独立重抽样。

进阶

  1. 计算机实验(原书习题 2):\(n=50\),\(Y_i\sim N(0,1)\),\(X_i=e^{Y_i}\),目标是 \(X\) 的偏度(真值 \((e+2)\sqrt{e-1}\approx6.18\))。用模拟比较三种 bootstrap 区间的真实覆盖率。预期结果是什么?为什么?(提示:覆盖率会远低于 95%;对数正态的偏度估计受少数极端值支配,\(n=50\) 时插入估计严重偏低。)
  2. 计算机实验(原书习题 3):\(n=25\) 的 \(t_3\) 样本,\(\theta=(q_{0.75}-q_{0.25})/1.34\)(基于四分位距的稳健尺度)。比较三种区间的覆盖率与平均长度,并与刀切法比较。
  3. 原书习题 6:\(X_i\sim N(\mu,1)\),\(\mu=5\),\(n=100\),\(\theta=e^\mu\),\(\hat\theta=e^{\bar X}\)。求 bootstrap se 与 95% 区间,并与第 01 章 Delta 方法的结果 \(\text{se}\approx e^\mu/\sqrt n\) 以及真实抽样分布对比。
  4. 原书习题 7:完整证明 \(\mathbb P(\hat\theta^*=\hat\theta)=1-(1-1/n)^n\),并用模拟画出 \(\hat\theta^*\) 的直方图与真实抽样分布 \(n(\theta-\hat\theta)/\theta\rightsquigarrow\text{Exp}(1)\) 的对比。
  5. 在 8.6.3 节代码中把块长 \(L\) 依次设为 1、5、20、50、200,记录平稳 bootstrap 的标准误。解释 \(L=1\) 与 \(L\) 很大时分别发生了什么。

原书推荐习题:第 8 章习题 2、3(三种区间覆盖率模拟,必做)、7(bootstrap 失效)、1、4、5、6。

原书对照

本章内容 原书章节 PDF 页码
Bootstrap 思想、两步法 第 8 章引言 p.119–120
模拟 8.1 p.120
Bootstrap 方差估计、例 8.1–8.2 8.2 p.120–122
正态、枢轴、百分位区间,例 8.4–8.7 8.3 p.122–127
文献注 8.4 p.127
刀切法、百分位区间的理由 8.5 附录 p.127–128
习题 8.6 p.128–130
块 bootstrap(本教材补充) — —

勘误说明:精读笔记中例 8.6 法学院数据最后一所学校的 GPA 记为 3.96,与原书给出的样本相关系数 0.776 不符;采用 Efron 原始数据的 2.96 时,相关系数恰为 0.776,本章据此更正。