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%

你看,铁矿石自身波动率更高,但交叉项贡献也不小。这就是为什么做商品组合时,品种间的相关性分析那么重要。

我的经验: 做波动率分解时,我建议用滚动窗口计算协方差矩阵。窗口太短噪声大,太长反应慢。我个人常用60个交易日,对于商品期货来说刚刚好。

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图,把整个风险归因的脉络梳理清楚了:

风险归因基础框架 组合总风险 波动率分解 VaR分解 风险贡献 自身波动贡献 交叉贡献 总波动率 = 两者之和 边际VaR 成分VaR(可加) 增量VaR 边际风险贡献 成分风险贡献 CRC = w × MRC 应用:风险预算、组合优化、压力测试、绩效归因

3.6 实战中的注意事项

讲完理论,说点实际的。我在做风险归因时,有几个坑是反复踩过的:

  1. 协方差矩阵的估计误差:用历史数据算协方差,样本量不够时误差很大。我建议至少用2倍于资产数量的样本量。
  2. 非线性风险:商品期权、含权结构等非线性产品,用线性近似会低估风险。这时候要用delta-gamma方法。
  3. 时变相关性:商品间的相关性不是稳定的。危机时期相关性会骤升,平时可能很低。我习惯用DCC-GARCH模型来捕捉这种变化。
  4. 流动性风险:VaR模型通常假设可以按市价平仓,但实际中流动性差的品种会有滑点。做归因时要把流动性成本考虑进去。
一个小技巧: 做风险归因时,我建议同时做正向和反向测试。正向:从资产到组合,看风险怎么聚合。反向:从组合到资产,看风险怎么分解。两个方向对得上,说明模型没问题。

最后说一句。风险归因不是一次性工作,而是持续的过程。市场在变,组合在变,风险结构也在变。定期做归因分析,才能及时发现风险隐患。

嗯,这一章的内容就到这。记住:风险归因不是为了精确计算,而是为了理解你的策略到底在承担什么风险。

专注资料整理