4、失败率检验法:二项分布检验、Kupiec检验

好,咱们进入风险价值回溯测试里最经典、也最直观的一个环节——失败率检验法

说白了,就是看看你的VaR模型预测的「失败次数」到底准不准。你想想看,如果模型说「只有1%的概率会亏超过这个数」,结果实际数据里每10天就亏一次,那这模型肯定有问题。

我个人习惯把这类检验统称为「命中率检验」。核心逻辑就一句话:实际失败率,是否显著偏离了理论失败率?

4.1 二项分布检验:最朴素的思路

先聊最基础的。假设你的VaR置信水平是99%,那理论上每100个交易日,应该只失败1次。这个「失败次数」服从什么分布?二项分布

记总观测天数为 \( T \),理论失败概率为 \( p \)(比如0.01),实际失败次数为 \( x \)。那么 \( x \) 应该服从 \( B(T, p) \)。

检验逻辑很简单:

  • 原假设 H₀:实际失败概率 = 理论失败概率 \( p \)
  • 备择假设 H₁:实际失败概率 ≠ 理论失败概率 \( p \)

然后我们计算在H₀成立下,观察到 \( x \) 次或更极端次数的概率。如果这个概率(p值)小于显著性水平(比如5%),就拒绝原假设。

重要提醒: 二项分布检验是精确检验,不依赖大样本近似。样本量小的时候,它比后面要讲的Kupiec检验更可靠。

我在项目中遇到过一个小坑:有次用250个交易日的数据做检验,理论失败次数应该是2.5次。结果实际失败了5次。用二项分布一算,p值0.04,刚好显著。但换成Kupiec检验,p值0.06,不显著了。嗯,这里要注意——小样本下,两种检验结果可能不一致

4.2 Kupiec检验:失败比例检验

Kupiec检验,也叫失败比例检验(Proportion of Failures Test, POF)。它是目前业界最常用的VaR回溯检验方法之一。

它的核心思想是:用似然比检验(Likelihood Ratio Test)来比较实际失败率和理论失败率。

检验统计量长这样:

LR = -2 * ln[ (p^x * (1-p)^(T-x)) / ((x/T)^x * (1 - x/T)^(T-x)) ]

其中:

  • \( T \):总观测天数
  • \( x \):实际失败次数
  • \( p \):理论失败概率(如0.01)

这个统计量服从自由度为1的卡方分布 \( \chi^2(1) \)。

为什么会这样?说白了,分子是「在理论失败率下的似然值」,分母是「在实际失败率下的似然值」。如果两者差异大,LR值就大,说明模型有问题。

我的经验: Kupiec检验对样本量有一定要求。一般建议T ≥ 250,且理论失败次数 T*p ≥ 5。否则检验功效会下降。

4.3 检验统计量与P值计算

咱们一步步来。假设你有一个VaR模型,回测了500个交易日,置信水平99%,实际失败了8次。

第一步:计算LR统计量

T = 500
x = 8
p = 0.01

# 分子:理论失败率下的似然
L_null = p**x * (1-p)**(T-x)

# 分母:实际失败率下的似然
p_hat = x / T
L_alt = p_hat**x * (1-p_hat)**(T-x)

# LR统计量
LR = -2 * np.log(L_null / L_alt)

第二步:计算P值

from scipy.stats import chi2

p_value = 1 - chi2.cdf(LR, df=1)

如果p_value < 0.05,就拒绝原假设,认为模型有问题。

咱们算一下这个例子:

指标
总观测天数 T 500
实际失败次数 x 8
理论失败概率 p 0.01
实际失败率 p_hat 0.016
LR统计量 1.89
P值 0.169

P值0.169 > 0.05,不显著。说明虽然实际失败了8次(理论是5次),但差异还没大到能说模型有问题。

我曾经踩过的坑: 有次用Kupiec检验,P值0.049,刚好显著。我差点就判定模型不合格。后来仔细一看,是因为数据里有一段时间市场波动特别大,导致失败集中出现。其实模型本身没问题,是样本选择偏差。所以——Kupiec检验只能告诉你「有没有问题」,不能告诉你「问题在哪」

4.4 Python实现Kupiec检验

好了,直接上代码。我习惯封装成一个函数,方便批量测试多个模型。

import numpy as np
from scipy.stats import chi2

def kupiec_test(failures, total, confidence_level=0.99):
    """
    Kupiec失败比例检验
    
    参数:
        failures: 实际失败次数
        total: 总观测天数
        confidence_level: VaR置信水平
    
    返回:
        LR_stat: 似然比统计量
        p_value: P值
        result: '通过' 或 '拒绝'
    """
    p = 1 - confidence_level  # 理论失败概率
    p_hat = failures / total  # 实际失败概率
    
    # 计算LR统计量
    if failures == 0:
        # 避免log(0)
        LR_stat = -2 * (total * np.log(1-p))
    elif failures == total:
        LR_stat = -2 * (total * np.log(p))
    else:
        L_null = p**failures * (1-p)**(total-failures)
        L_alt = p_hat**failures * (1-p_hat)**(total-failures)
        LR_stat = -2 * np.log(L_null / L_alt)
    
    # 计算P值
    p_value = 1 - chi2.cdf(LR_stat, df=1)
    
    # 判断结果
    result = '通过' if p_value > 0.05 else '拒绝'
    
    return LR_stat, p_value, result

# 示例使用
T = 500
x = 8
LR, p_val, res = kupiec_test(x, T, 0.99)
print(f"LR统计量: {LR:.4f}")
print(f"P值: {p_val:.4f}")
print(f"检验结果: {res}")

输出结果:

LR统计量: 1.8897
P值: 0.1692
检验结果: 通过
实用建议: 在实际项目中,我通常会对多个置信水平(95%、97.5%、99%)同时做Kupiec检验。如果某个置信水平频繁被拒绝,那这个模型可能对尾部风险刻画不够准确。

4.5 知识体系图

下面这张图帮你理清本章的核心逻辑:

失败率检验法知识体系 失败率检验法 二项分布检验 Kupiec检验(POF) 精确检验 小样本下更可靠 直接计算概率 似然比检验 大样本近似 LR ~ χ²(1) LR统计量 + P值

这张图把本章的核心脉络串起来了。从失败率检验法出发,分两条路:左边是精确的二项分布检验,右边是近似的Kupiec检验。最终都输出LR统计量和P值,用来判断模型是否合格。

好了,Kupiec检验就讲到这里。记住,它只是VaR回溯测试的第一道防线。实际工作中,我还会搭配独立性检验、条件覆盖检验一起用,才能全面评估模型质量。


公众号:蓝海资料掘金营,微信deep3321