主题切换
16.1 风险分解与归因
名词解释:风险归因(Risk Attribution)
风险归因是将投资组合的总体风险分解为不同来源(因子风险、特异性风险、行业风险、国家风险等)的过程。与收益归因关注"赚了多少"不同,风险归因回答"风险从哪里来"和"哪些头寸贡献了最大风险"的问题。
一、风险归因:因子风险 + 特异风险
1.1 因子模型下的风险分解
在多因子模型中,组合的总风险可以精确分解:
其中
1.2 边际风险贡献(MRC)
每个资产对组合风险的边际贡献为:
风险贡献(RC)为权重乘以边际贡献:
所有资产的风险贡献之和等于组合总风险(欧拉定理保证)。
此处有展示代码展开 ▼
python
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
def factor_risk_decomposition(weights: np.ndarray,
factor_exposures: np.ndarray,
factor_cov: np.ndarray,
specific_var: np.ndarray) -> dict:
"""
将组合风险分解为因子风险和特异风险。
参数:
weights: (N,) 资产权重
factor_exposures: (N, K) 因子暴露矩阵
factor_cov: (K, K) 因子协方差矩阵
specific_var: (N,) 各资产的特异方差
返回:
风险分解结果的字典
"""
# 因子风险
portfolio_factor_var = weights @ factor_exposures @ factor_cov @ \
factor_exposures.T @ weights
# 特异风险
D = np.diag(specific_var)
portfolio_specific_var = weights @ D @ weights
# 总风险
total_var = portfolio_factor_var + portfolio_specific_var
total_vol = np.sqrt(total_var)
# 风险贡献分解
cov_matrix = factor_exposures @ factor_cov @ factor_exposures.T + D
portfolio_cov = cov_matrix @ weights
# 边际风险贡献
mrc = portfolio_cov / total_vol
# 风险贡献
rc = weights * mrc
# 百分比风险贡献
rc_pct = rc / total_vol * 100
return {
'total_volatility': total_vol,
'factor_var': portfolio_factor_var,
'specific_var': portfolio_specific_var,
'factor_var_pct': portfolio_factor_var / total_var * 100,
'specific_var_pct': portfolio_specific_var / total_var * 100,
'marginal_risk_contribution': mrc,
'risk_contribution': rc,
'risk_contribution_pct': rc_pct
}
def factor_level_risk_decomp(weights: np.ndarray,
factor_exposures: np.ndarray,
factor_cov: np.ndarray) -> pd.DataFrame:
"""
将因子风险进一步分解到每个因子的贡献。
参数:
weights: (N,) 资产权重
factor_exposures: (N, K) 因子暴露
factor_cov: (K, K) 因子协方差
返回:
各因子的风险贡献DataFrame
"""
portfolio_exposure = weights @ factor_exposures # (K,) 组合因子暴露
# 每个因子的方差贡献和协方差贡献
factor_rc = np.zeros(len(portfolio_exposure))
for k in range(len(portfolio_exposure)):
factor_rc[k] = portfolio_exposure[k] * \
(factor_cov @ portfolio_exposure)[k]
factor_rc_pct = factor_rc / np.sum(factor_rc) * 100
return pd.DataFrame({
'Factor': [f'F{i+1}' for i in range(len(factor_rc))],
'Portfolio_Exposure': portfolio_exposure,
'Risk_Contribution': factor_rc,
'Risk_Contribution_Pct': factor_rc_pct
}).sort_values('Risk_Contribution_Pct', ascending=False)点击展开可浏览运行结果
📘 本段代码定义了 2 个函数/类:函数 `factor_risk_decomposition`(将组合风险分解为因子风险和特异风险)、函数 `factor_level_risk_decomp`(将因子风险进一步分解到每个因子的贡献)。该片段为教学展示(未包含独立运行的输入数据),可在实战练习中结合真实数据调用。
二、Brinson 归因模型
2.1 Brinson 模型的框架
Brinson 归因将组合的超额收益分解为三个来源:
- 配置效应(Allocation Effect):超配/低配某类资产带来的超额收益
- 选择效应(Selection Effect):在某类资产内选股带来的超额收益
- 交互效应(Interaction Effect):配置和选择的交叉影响
2.2 Python 实现
此处有展示代码展开 ▼
python
def brinson_attribution(portfolio_weights: np.ndarray,
benchmark_weights: np.ndarray,
portfolio_sector_returns: np.ndarray,
benchmark_sector_returns: np.ndarray,
sector_names: list = None) -> pd.DataFrame:
"""
Brinson 绩效归因模型。
参数:
portfolio_weights: (S,) 组合在各板块的权重
benchmark_weights: (S,) 基准在各板块的权重
portfolio_sector_returns: (S,) 组合在各板块的收益
benchmark_sector_returns: (S,) 基准在各板块的收益
sector_names: 板块名称列表
返回:
归因结果DataFrame
"""
S = len(portfolio_weights)
if sector_names is None:
sector_names = [f'Sector {i+1}' for i in range(S)]
# 基准总收益
R_B = np.sum(benchmark_weights * benchmark_sector_returns)
R_P = np.sum(portfolio_weights * portfolio_sector_returns)
# 各效应
allocation_effect = (portfolio_weights - benchmark_weights) * \
(benchmark_sector_returns - R_B)
selection_effect = benchmark_weights * \
(portfolio_sector_returns - benchmark_sector_returns)
interaction_effect = (portfolio_weights - benchmark_weights) * \
(portfolio_sector_returns - benchmark_sector_returns)
results = pd.DataFrame({
'Sector': sector_names,
'Portfolio_Weight': portfolio_weights,
'Benchmark_Weight': benchmark_weights,
'Active_Weight': portfolio_weights - benchmark_weights,
'Portfolio_Return': portfolio_sector_returns,
'Benchmark_Return': benchmark_sector_returns,
'Allocation_Effect': allocation_effect,
'Selection_Effect': selection_effect,
'Interaction_Effect': interaction_effect,
'Total_Effect': allocation_effect + selection_effect + interaction_effect
})
# 加总行
total_row = pd.DataFrame({
'Sector': ['TOTAL'],
'Portfolio_Weight': [np.sum(portfolio_weights)],
'Benchmark_Weight': [np.sum(benchmark_weights)],
'Active_Weight': [0],
'Portfolio_Return': [R_P],
'Benchmark_Return': [R_B],
'Allocation_Effect': [np.sum(allocation_effect)],
'Selection_Effect': [np.sum(selection_effect)],
'Interaction_Effect': [np.sum(interaction_effect)],
'Total_Effect': [R_P - R_B]
})
results = pd.concat([results, total_row], ignore_index=True)
# 验证
assert abs(results.iloc[-1]['Total_Effect'] - (R_P - R_B)) < 1e-10, \
"归因加总与超额收益不一致"
return results点击展开可浏览运行结果
📘 本段代码定义了 1 个函数/类:函数 `brinson_attribution`(Brinson 绩效归因模型)。该片段为教学展示(未包含独立运行的输入数据),可在实战练习中结合真实数据调用。
三、Barra 风险模型因子结构
3.1 Barra 模型的因子体系
Barra 是业界最广泛使用的多因子风险模型,其因子体系包括:
| 因子类别 | 因子示例 | 说明 |
|---|---|---|
| 风格因子(Style Factors) | 价值、动量、规模、波动率、质量、成长、杠杆、流动性、分红 | 横截面标准化 |
| 行业因子(Industry Factors) | GICS 行业分类(68个行业) | 虚拟变量 |
| 国家因子(Country Factors) | 各国家/地区 | 全球模型中使用 |
3.2 Barra 风格因子的构建
此处有展示代码展开 ▼
python
def barra_style_factors(fundamentals: pd.DataFrame) -> pd.DataFrame:
"""
简化版 Barra 风格因子的构建。
参数:
fundamentals: 包含公司基本面数据的DataFrame
列包括: size_log, book_to_price, momentum_12m1m,
beta, volatility, roe, eps_growth, leverage, dividend_yield
返回:
标准化后的风格因子暴露矩阵
"""
factors = pd.DataFrame(index=fundamentals.index)
# 1. 规模因子(Size):市值的自然对数
factors['Size'] = fundamentals['size_log']
# 2. 价值因子(Value):账面市值比
factors['Value'] = fundamentals['book_to_price']
# 3. 动量因子(Momentum):12月-1月动量
factors['Momentum'] = fundamentals['momentum_12m1m']
# 4. 波动率因子(Volatility):历史贝塔 + 残差波动率
factors['Volatility'] = 0.6 * fundamentals['beta'] + \
0.4 * fundamentals['residual_volatility']
# 5. 质量因子(Quality):ROE + 盈利增长
factors['Quality'] = 0.5 * fundamentals['roe'] + \
0.5 * fundamentals['eps_growth']
# 6. 杠杆因子(Leverage)
factors['Leverage'] = fundamentals['leverage']
# 7. 成长因子(Growth)
factors['Growth'] = fundamentals['eps_growth']
# 8. 流动性因子(Liquidity)
factors['Liquidity'] = fundamentals['turnover']
# 9. 分红因子(Dividend Yield)
factors['DividendYield'] = fundamentals['dividend_yield']
# 标准化:使每个因子横截面均值为0,标准差为1
for col in factors.columns:
factors[col] = (factors[col] - factors[col].mean()) / \
factors[col].std()
return factors点击展开可浏览运行结果
📘 本段代码定义了 1 个函数/类:函数 `barra_style_factors`(简化版 Barra 风格因子的构建)。该片段为教学展示(未包含独立运行的输入数据),可在实战练习中结合真实数据调用。
四、风险预算分配
4.1 风险预算的概念
风险预算(Risk Budgeting)是将组合的风险按比例分配给各个子组合或策略。与传统的资本权重分配不同,风险预算关注每个组成部分的风险贡献比例。
此处有展示代码展开 ▼
python
def risk_budget_optimization(cov_matrix: np.ndarray,
risk_budgets: np.ndarray,
max_iter: int = 1000,
tol: float = 1e-8) -> np.ndarray:
"""
风险预算优化:找到使风险贡献等于指定预算的权重。
风险贡献 RC_i = w_i * (Σw)_i / σ_P
参数:
cov_matrix: (N, N) 协方差矩阵
risk_budgets: (N,) 目标风险预算比例(总和为1)
max_iter: 最大迭代次数
tol: 收敛容忍度
返回:
最优权重向量
"""
N = len(risk_budgets)
weights = np.ones(N) / N # 初始等权
for iteration in range(max_iter):
# 当前组合
portfolio_std = np.sqrt(weights @ cov_matrix @ weights)
marginal_risk = cov_matrix @ weights / portfolio_std
risk_contribution = weights * marginal_risk
current_budgets = risk_contribution / np.sum(risk_contribution)
# 检查收敛
max_deviation = np.max(np.abs(current_budgets - risk_budgets))
if max_deviation < tol:
break
# 调整权重:风险预算不足的资产增加权重
adjustment = risk_budgets / (current_budgets + 1e-10)
weights = weights * adjustment
# 归一化
weights = weights / np.sum(weights)
return weights点击展开可浏览运行结果
📘 本段代码定义了 1 个函数/类:函数 `risk_budget_optimization`(风险预算优化:找到使风险贡献等于指定预算的权重)。该片段为教学展示(未包含独立运行的输入数据),可在实战练习中结合真实数据调用。
4.2 风险预算的可视化
有效的风险预算管理需要持续监控实际风险贡献与目标预算的偏差:
此处有展示代码展开 ▼
python
def risk_budget_dashboard(current_rc_pct: np.ndarray,
target_budgets: np.ndarray,
labels: list) -> dict:
"""
风险预算监控仪表板:比较实际 vs 目标风险贡献。
返回:
包含偏差分析和预警信号的字典
"""
deviations = current_rc_pct - target_budgets * 100
abs_deviations = np.abs(deviations)
# 预警:偏差超过阈值
threshold = 5 # 5个百分点
alerts = []
for i, dev in enumerate(deviations):
if abs(dev) > threshold:
direction = 'over' if dev > 0 else 'under'
alerts.append(f"{labels[i]}: {direction}-allocated "
f"({dev:+.1f}pp vs budget)")
return {
'current_contribution': dict(zip(labels, current_rc_pct)),
'target_budget': dict(zip(labels, target_budgets * 100)),
'deviations': dict(zip(labels, deviations)),
'max_deviation': np.max(abs_deviations),
'alerts': alerts,
'rebalance_needed': len(alerts) > 0
}点击展开可浏览运行结果
📘 本段代码定义了 1 个函数/类:函数 `risk_budget_dashboard`(风险预算监控仪表板:比较实际 vs 目标风险贡献)。该片段为教学展示(未包含独立运行的输入数据),可在实战练习中结合真实数据调用。
风险分解与归因为量化投资者提供了理解组合风险结构的透镜。接下来我们将讨论一个特别关键的专题:如何量化和管理极端事件带来的尾部风险——这正是极值理论(EVT)所解决的问题。