Skip to content

4.5 统计检验

概念详解

统计检验是区分"真正的Alpha"和"数据中偶然模式"的关键工具。一个回测夏普比率2.0的策略,在统计上是否真的显著优于零?这个问题必须通过统计检验来回答。

在量化策略评估中,常用的统计检验方法包括:

  1. t检验:检验策略的平均收益是否显著异于零
  2. Bootstrap检验:通过重抽样估计夏普比率等复杂统计量的分布
  3. DeFlation检验:控制多次测试后的假阳性率(如Deflated Sharpe Ratio)
  4. White's Reality Check:检验最优策略是否仅是数据挖掘的结果
  5. CSCV(组合对称交叉验证):评估回测过拟合概率(PBO)
名词解释:PBO (回测过拟合概率)

衡量因数据挖掘而导致回测结果不可靠的概率。PBO的计算逻辑是:将样本随机切分多次,观察样本内最优策略在样本外的表现。如果样本内最优在样本外经常表现很差,则PBO高,过拟合风险大。PBO越低,策略过拟合风险越小。

名词解释:Bootstrap

通过对原始样本进行有放回的重抽样,构造统计量的分布,从而进行假设检验或置信区间估计。在量化策略评估中,Bootstrap可以估计夏普比率、最大回撤等复杂统计量的置信区间,而不需要假设收益率的分布形式。

名词解释:Deflated Sharpe Ratio (DSR)

传统的夏普比率检验没有考虑"多次测试"的问题。你测试了100个策略并选择了夏普最高的那个,这个最高夏普的统计显著性应该经过多重比较校正。DSR通过"气馁化"(Deflation)来校正选择偏差:在零假设下,多次测试得到的最高夏普的期望值并非0。

数学原理

夏普比率的渐近分布

Lo (2002) 证明,在收益率服从独立同分布且为正态的假设下,年化夏普比率的渐近分布为:

SR^N(SR,1+12SR2T)

其中 T 是年数。这意味着夏普比率为0的策略,其估计值 SR^ 的标准误约为 1/T。对于3年的样本,标准误约为0.58;对于10年的样本,标准误约为0.32。

Bootstrap原理

设我们观测到的日收益率序列为 {r1,r2,...,rn}。Bootstrap通过从该序列中有放回地抽取 n 个观测值生成一个新的Bootstrap样本:

{r1,r2,...,rn}Fn

其中 Fn 是经验分布。对每个Bootstrap样本计算统计量(如夏普比率),重复 B 次(通常 B=1000 或更多),便可获得该统计量的经验分布。

PBO的计算

PBO=1Ss=1S1(λIS(s)1λOOS(s))

其中 S 是CSCV的切分次数,λIS(s) 是第 s 次切分中样本内最优策略的相对排名(归一化到[0,1]),λOOS(s) 是该策略在样本外的相对排名。PBO > 0.5 表明过拟合严重。

Python实战

📌 案例:Bootstrap显著性

PYTHON23 行 · 893 B
📄此处有展示代码23 行 · 893 B展开 ▼
python
import numpy as np

# 已有日收益率序列 returns 和原始夏普比率 original_sharpe
original_sharpe = returns.mean() / returns.std() * np.sqrt(252)

# Mean-Bootstrap:直接对收益率进行Bootstrap
n_boot = 2000
bootstrapped_sharpes = []
for _ in range(n_boot):
    boot_returns = np.random.choice(returns, size=len(returns), replace=True)
    boot_sharpe = boot_returns.mean() / boot_returns.std() * np.sqrt(252)
    bootstrapped_sharpes.append(boot_sharpe)

# 计算p值:Bootstrap夏普大于原始夏普的比例
p_value = np.mean(np.array(bootstrapped_sharpes) > original_sharpe)

# 置信区间
ci_lower = np.percentile(bootstrapped_sharpes, 2.5)
ci_upper = np.percentile(bootstrapped_sharpes, 97.5)

print(f"原始夏普比率: {original_sharpe:.2f}")
print(f"Bootstrap 95% CI: [{ci_lower:.2f}, {ci_upper:.2f}]")
print(f"p-value (H0: Sharpe <= 0, 保守): {p_value:.4f}")
点击展开可浏览运行结果
原始夏普比率: -0.06
Bootstrap 95% CI: [-2.06, 1.94]
p-value (H0: Sharpe <= 0, 保守): 0.4950

📌 案例:时序Bootstrap(Block Bootstrap)

PYTHON34 行 · 1.4 KB
📄此处有展示代码34 行 · 1.4 KB展开 ▼
python
def block_bootstrap(returns, block_size=10, n_boot=1000):
    """
    分块Bootstrap:保留收益率的时序相关性
    标准的i.i.d. Bootstrap低估了真实方差,因为金融收益率存在自相关和波动率聚集
    """
    n = len(returns)
    n_blocks = int(np.ceil(n / block_size))
    boot_sharpes = []

    for _ in range(n_boot):
        # 随机选择起始块
        block_starts = np.random.choice(n - block_size + 1, size=n_blocks)
        boot_sample = []
        for start in block_starts:
            boot_sample.extend(returns[start:start + block_size])
        boot_sample = np.array(boot_sample[:n])  # 截取到原始长度

        boot_sharpe = boot_sample.mean() / boot_sample.std() * np.sqrt(252)
        boot_sharpes.append(boot_sharpe)

    return np.array(boot_sharpes)

# 对比标准Bootstrap和Block Bootstrap
standard_boot = np.array([
    np.random.choice(returns, size=len(returns), replace=True).mean() /
    np.random.choice(returns, size=len(returns), replace=True).std() * np.sqrt(252)
    for _ in range(1000)
])

block_boot = block_bootstrap(returns, block_size=10, n_boot=1000)

print(f"标准Bootstrap - 均值: {standard_boot.mean():.2f}, 标准差: {standard_boot.std():.2f}")
print(f"分块Bootstrap - 均值: {block_boot.mean():.2f}, 标准差: {block_boot.std():.2f}")
print(f"注意:如果收益率存在正自相关,分块Bootstrap的标准差通常更大,置信区间更宽")
点击展开可浏览运行结果
📘 本段代码定义了 1 个函数/类:函数 `block_bootstrap`(分块Bootstrap:保留收益率的时序相关性)。该片段为教学展示(未包含独立运行的输入数据),可在实战练习中结合真实数据调用。

📌 案例:PBO计算(CSCV方法)

PYTHON47 行 · 1.6 KB
📄此处有展示代码47 行 · 1.6 KB展开 ▼
python
def compute_pbo(returns, n_trials=100, test_size=0.5):
    """
    使用组合对称交叉验证(CSCV)计算PBO

    Parameters:
        returns: T x K matrix, K个策略变体在T个时间点的收益率
    """
    T, K = returns.shape
    n_test = int(T * test_size)
    n_train = T - n_test

    is_ranks = []
    oos_ranks = []

    for _ in range(n_trials):
        # 随机划分训练/测试集
        idx = np.random.permutation(T)
        train_idx = idx[:n_train]
        test_idx = idx[n_train:]

        # 计算样本内夏普
        is_sharpe = returns[train_idx].mean(axis=0) / returns[train_idx].std(axis=0) * np.sqrt(252)
        # 计算样本外夏普
        oos_sharpe = returns[test_idx].mean(axis=0) / returns[test_idx].std(axis=0) * np.sqrt(252)

        # 找出样本内最优策略
        best_is_idx = np.argmax(is_sharpe)

        # 排名(归一化到[0,1])
        is_rank = (np.sum(is_sharpe <= is_sharpe[best_is_idx]) - 1) / (K - 1) if K > 1 else 0.5
        oos_rank = np.sum(oos_sharpe <= oos_sharpe[best_is_idx]) / K

        is_ranks.append(is_rank)
        oos_ranks.append(oos_rank)

    # PBO = 样本内排名 > 1 - 样本外排名的频率
    is_ranks = np.array(is_ranks)
    oos_ranks = np.array(oos_ranks)
    pbo = np.mean(is_ranks >= (1 - oos_ranks))

    print(f"PBO: {pbo:.4f}")
    if pbo > 0.5:
        print("⚠️ 过拟合风险较高!样本内最优策略在样本外倾向于表现最差。")
    else:
        print("✅ 过拟合风险可控。样本内最优策略在样本外也有较好的相对排名。")

    return pbo
点击展开可浏览运行结果
📘 本段代码定义了 1 个函数/类:函数 `compute_pbo`(使用组合对称交叉验证(CSCV)计算PBO)。该片段为教学展示(未包含独立运行的输入数据),可在实战练习中结合真实数据调用。

📌 案例:两次t检验和回归t检验

PYTHON41 行 · 1.6 KB
📄此处有展示代码41 行 · 1.6 KB展开 ▼
python
from scipy import stats

def strategy_significance_test(returns, rf_rate=0.03):
    """
    多种统计检验全面评估策略的显著性
    """
    results = {}

    # 1. 单样本t检验:H0: mean(returns) = 0
    t_stat, p_value = stats.ttest_1samp(returns, 0)
    results['t-test_pvalue'] = p_value
    results['t-test_significant'] = p_value < 0.05

    # 2. 方差比率检验:检验收益率是否为随机游走
    # Var(r_{t,t+k}) / (k * Var(r_t)) 应在1附近
    k = 5
    k_period_returns = returns.rolling(k).sum().dropna()
    var_ratio = k_period_returns.var() / (k * returns.var())
    results['variance_ratio'] = var_ratio

    # 3. 符号检验:正收益天数是否显著多于50%
    n_pos = (returns > 0).sum()
    n_total = len(returns)
    sign_p_value = stats.binomtest(n_pos, n_total, p=0.5, alternative='greater').pvalue
    results['sign_test_pvalue'] = sign_p_value

    # 4. 自相关检验:Ljung-Box检验收益率是否存在显著自相关
    from statsmodels.stats.diagnostic import acorr_ljungbox
    lb_result = acorr_ljungbox(returns, lags=[10], return_df=True)
    results['ljung_box_pvalue'] = lb_result['lb_pvalue'].values[0]
    results['has_autocorr'] = results['ljung_box_pvalue'] < 0.05

    # 5. Bootstrap夏普的置信区间
    boot_sharpes = []
    for _ in range(1000):
        bt = np.random.choice(returns, size=len(returns), replace=True)
        boot_sharpes.append(bt.mean() / bt.std() * np.sqrt(252))
    results['sharpe_95ci'] = (np.percentile(boot_sharpes, 2.5),
                               np.percentile(boot_sharpes, 97.5))

    return pd.Series(results)
点击展开可浏览运行结果
📘 本段代码定义了 1 个函数/类:函数 `strategy_significance_test`(多种统计检验全面评估策略的显著性)。该片段为教学展示(未包含独立运行的输入数据),可在实战练习中结合真实数据调用。

常见误区

  1. 只用t检验,不检查假设:标准的t检验假设收益率独立同分布且为正态。实际金融数据通常违背这两个假设(存在波动率聚集和肥尾)。应使用HAC(异方差自相关一致性)标准误。

  2. 忽略多重比较问题:当你从100个策略变体中选出夏普最高的,你不能用单次测试的标准来评估其显著性。需要用Bonferroni校正或Deflated Sharpe Ratio。

  3. 把不显著的p值当作"策略无效":p > 0.05 只意味着数据不足以拒绝"策略收益为零"的零假设,不意味着策略一定无效。可能只是样本量太小(统计功效不足)。

  4. Bootstrap方法选择不当:对存在时序依赖的收益率使用i.i.d. Bootstrap会低估标准误,导致假阳性。应使用Block Bootstrap或Stationary Bootstrap。

  5. 过早在样本外测试:每在测试集上运行一次,测试集就被"污染"一次。测试集应仅被使用有限次数(理想情况下只使用最终一次)。

实战练习

  1. 对你的策略日收益率序列执行完整的统计检验包:

    • t检验(H0: 均值为零)
    • Bootstrap 95%置信区间
    • 计算夏普比率的标准误(基于Lo的公式)
    • 方差比率检验 如果策略在统计上不显著,讨论可能的原因和下一步改进方向。
  2. 模拟多重比较问题:生成100组随机游走收益率序列(均值为零的i.i.d.正态分布),每组有1000个观测值。计算每组的夏普比率,找出最高的那个。然后对"最优"的这组做t检验。观察假阳性的实际频率与名义显著性水平(如5%)的差距。

  3. 在你当前的策略参数寻优流程中加入PBO计算。如果PBO > 0.3,采取什么措施来减少过拟合?

延伸阅读

  • Lo, A. W. (2002). "The Statistics of Sharpe Ratios." Financial Analysts Journal. 夏普比率统计推断的经典论文。
  • White, H. (2000). "A Reality Check for Data Snooping." Econometrica.
  • Hansen, P. R. (2005). "A Test for Superior Predictive Ability." 优于White's Reality Check的改进版。
  • Lopez de Prado, M. (2018). Advances in Financial Machine Learning. Wiley. 第11-14章。
  • Harvey, C. R. & Liu, Y. (2015). "Backtesting." 提出了更高的显著性阈值(t-stat > 3.0 而非传统的 2.0)来应对多重比较。

本章要点

  • 统计检验是区分真实Alpha数据偶然模式的必修课。没有经过统计检验的策略不可信。
  • Bootstrap是一种非参数方法,无需假设分布形态,适用于夏普比率、最大回撤等复杂统计量的推断。
  • PBO(回测过拟合概率)衡量策略选择过程中的过拟合程度。PBO > 0.5 是强烈警告信号。
  • 金融数据的特点(自相关、波动率聚集、肥尾)要求使用更严谨的推断方法:Block Bootstrap、HAC标准误。
  • Deflated Sharpe Ratio 校正了多次测试的选择偏差,是比传统夏普比率的p值更诚实的显著性度量。
  • 统计显著不等于经济显著。一个在统计上显著的策略,扣除成本后可能无利可图。两种显著性需要同时评估。