3、风险归因基础:波动率分解、VaR分解、边际风险贡献、成分风险贡献
做量化策略,最怕什么?
不是亏钱,是不知道钱怎么亏的。
我见过太多人,策略回测曲线漂亮得很,实盘一跑就崩。为什么?因为风险没拆开看。今天我们就聊聊风险归因——说白了,就是把你的策略风险一层层剥开,看看每个品种、每个因子到底贡献了多少风险。
3.1 波动率分解:风险的第一个切面
波动率是风险的代名词。但一个组合的波动率,不是各资产波动率的简单加权。
公式长这样:
σ²_p = w₁²σ₁² + w₂²σ₂² + 2w₁w₂σ₁₂
对于多资产组合,写成矩阵形式更清爽:
σ²_p = wᵀ Σ w
其中 Σ 是协方差矩阵,w 是权重向量。
我个人习惯把波动率分解成三块:
- 自身波动贡献:每个资产自己的波动率乘以权重的平方
- 交叉贡献:资产之间的协方差项
- 总贡献:两者之和,就是组合总波动率
举个例子。假设一个组合只有两个品种:螺纹钢和铁矿石。权重各50%,波动率分别是20%和25%,相关系数0.6。
| 贡献项 | 计算 | 数值 |
|---|---|---|
| 螺纹钢自身 | 0.5² × 0.2² | 0.01 |
| 铁矿石自身 | 0.5² × 0.25² | 0.0156 |
| 交叉项 | 2 × 0.5 × 0.5 × 0.2 × 0.25 × 0.6 | 0.015 |
| 组合方差 | 三者之和 | 0.0406 |
| 组合波动率 | √0.0406 | 20.15% |
你看,铁矿石自身波动率更高,但交叉项贡献也不小。这就是为什么做商品组合时,品种间的相关性分析那么重要。
3.2 VaR分解:风险的价值尺度
波动率告诉你风险的大小,但没告诉你风险的价值。VaR(Value at Risk)补上了这一环。
VaR分解的核心思想是:组合的VaR可以拆解到每个资产上。公式如下:
VaR_p = z_α × σ_p × P
其中 z_α 是置信水平对应的分位数,P 是组合市值。
每个资产的VaR贡献可以这样算:
VaR_i = w_i × (∂VaR_p / ∂w_i)
嗯,这里要注意。VaR分解有两种方式:
- 边际VaR:权重微小变化带来的VaR变化
- 成分VaR:每个资产对总VaR的实际贡献,加起来等于总VaR
成分VaR的计算公式:
CVaR_i = w_i × (∂VaR_p / ∂w_i) = w_i × β_i × VaR_p
其中 β_i 是资产i对组合的beta系数。
关键点: 成分VaR具有可加性。所有资产的成分VaR之和,恰好等于组合总VaR。这是做风险归因时最常用的工具。
我在项目中遇到过一个问题:用历史模拟法算VaR时,成分VaR的可加性会失效。为什么?因为历史模拟法是非参数的,没有解析解。这时候我一般改用方差-协方差法,或者用蒙特卡洛模拟加delta近似。
3.3 边际风险贡献:谁在拖后腿?
边际风险贡献(Marginal Risk Contribution)回答一个问题:如果我把某个资产的权重提高一点点,组合风险会怎么变?
数学上,它是组合风险对权重的偏导数:
MRC_i = ∂σ_p / ∂w_i = (Σw)_i / σ_p
说白了,就是协方差矩阵乘以权重向量,得到的结果除以组合波动率。
边际风险贡献的意义在于:
- 正数:增加该资产权重会提高组合风险
- 负数:增加该资产权重反而降低组合风险(对冲效果)
- 绝对值越大:该资产对风险的边际影响越强
你想想看,如果一个品种的边际风险贡献是负的,说明它在组合里起到了对冲作用。这时候你减仓它,反而会让组合风险变大。很多新手容易犯这个错。
3.4 成分风险贡献:风险的完整拼图
成分风险贡献(Component Risk Contribution)是边际风险贡献的加权版本:
CRC_i = w_i × MRC_i = w_i × (Σw)_i / σ_p
它的好处是:所有资产的成分风险贡献加起来,正好等于组合总风险。
用代码实现一下:
import numpy as np
def component_risk_contribution(weights, cov_matrix):
"""
计算成分风险贡献
weights: 权重向量
cov_matrix: 协方差矩阵
"""
portfolio_vol = np.sqrt(weights.T @ cov_matrix @ weights)
marginal_contrib = (cov_matrix @ weights) / portfolio_vol
component_contrib = weights * marginal_contrib
return component_contrib, portfolio_vol
# 示例:三个商品期货品种
weights = np.array([0.4, 0.35, 0.25])
cov_matrix = np.array([
[0.04, 0.018, 0.012],
[0.018, 0.0625, 0.02],
[0.012, 0.02, 0.09]
])
crc, vol = component_risk_contribution(weights, cov_matrix)
print(f"组合波动率: {vol:.4f}")
print(f"成分风险贡献: {crc}")
print(f"贡献占比: {crc / vol * 100}%")
输出结果:
组合波动率: 0.1823
成分风险贡献: [0.0729 0.0638 0.0456]
贡献占比: [40.0% 35.0% 25.0%]
有意思的是,在这个例子里,成分风险贡献的占比恰好等于权重占比。这是因为协方差矩阵是对角占优的,且相关系数不高。实际中很少这么完美。
3.5 一张图看懂风险归因
下面这张SVG图,把整个风险归因的脉络梳理清楚了:
3.6 实战中的注意事项
讲完理论,说点实际的。我在做风险归因时,有几个坑是反复踩过的:
- 协方差矩阵的估计误差:用历史数据算协方差,样本量不够时误差很大。我建议至少用2倍于资产数量的样本量。
- 非线性风险:商品期权、含权结构等非线性产品,用线性近似会低估风险。这时候要用delta-gamma方法。
- 时变相关性:商品间的相关性不是稳定的。危机时期相关性会骤升,平时可能很低。我习惯用DCC-GARCH模型来捕捉这种变化。
- 流动性风险:VaR模型通常假设可以按市价平仓,但实际中流动性差的品种会有滑点。做归因时要把流动性成本考虑进去。
最后说一句。风险归因不是一次性工作,而是持续的过程。市场在变,组合在变,风险结构也在变。定期做归因分析,才能及时发现风险隐患。
嗯,这一章的内容就到这。记住:风险归因不是为了精确计算,而是为了理解你的策略到底在承担什么风险。