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%),就拒绝原假设。
我在项目中遇到过一个小坑:有次用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值就大,说明模型有问题。
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次),但差异还没大到能说模型有问题。
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
检验结果: 通过
4.5 知识体系图
下面这张图帮你理清本章的核心逻辑:
这张图把本章的核心脉络串起来了。从失败率检验法出发,分两条路:左边是精确的二项分布检验,右边是近似的Kupiec检验。最终都输出LR统计量和P值,用来判断模型是否合格。
好了,Kupiec检验就讲到这里。记住,它只是VaR回溯测试的第一道防线。实际工作中,我还会搭配独立性检验、条件覆盖检验一起用,才能全面评估模型质量。
公众号:蓝海资料掘金营,微信deep3321