Skip to content

16.1 风险分解与归因

名词解释:风险归因(Risk Attribution)

风险归因是将投资组合的总体风险分解为不同来源(因子风险、特异性风险、行业风险、国家风险等)的过程。与收益归因关注"赚了多少"不同,风险归因回答"风险从哪里来"和"哪些头寸贡献了最大风险"的问题。

一、风险归因:因子风险 + 特异风险

1.1 因子模型下的风险分解

在多因子模型中,组合的总风险可以精确分解:

σP2=wTBΣFBTw因子风险+wTDw特异风险

其中 w 是组合权重向量,B 是因子暴露矩阵,ΣF 是因子协方差矩阵,D 是对角特异风险矩阵。

1.2 边际风险贡献(MRC)

每个资产对组合风险的边际贡献为:

MRCi=σPwi=(Σw)iσP

风险贡献(RC)为权重乘以边际贡献:

RCi=wi×MRCi=wi×(Σw)iσP

所有资产的风险贡献之和等于组合总风险(欧拉定理保证)。

PYTHON85 行 · 2.8 KB
📄此处有展示代码85 行 · 2.8 KB展开 ▼
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):配置和选择的交叉影响
RPRB=i(wiPwiB)(RiBRB)+iwiB(RiPRiB)+i(wiPwiB)(RiPRiB)

2.2 Python 实现

PYTHON69 行 · 2.6 KB
📄此处有展示代码69 行 · 2.6 KB展开 ▼
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 风格因子的构建

PYTHON48 行 · 1.7 KB
📄此处有展示代码48 行 · 1.7 KB展开 ▼
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)是将组合的风险按比例分配给各个子组合或策略。与传统的资本权重分配不同,风险预算关注每个组成部分的风险贡献比例

PYTHON40 行 · 1.3 KB
📄此处有展示代码40 行 · 1.3 KB展开 ▼
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 风险预算的可视化

有效的风险预算管理需要持续监控实际风险贡献与目标预算的偏差:

PYTHON29 行 · 1.0 KB
📄此处有展示代码29 行 · 1.0 KB展开 ▼
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)所解决的问题。