引言:传统资产配置的困境与风险平价的崛起

在现代投资管理领域,资产配置被公认为决定投资组合长期表现的最关键因素,其重要性远超个股选择和市场择时。然而,传统的资产配置方法,尤其是基于市值加权或简单等权重的策略,在面对市场极端波动时往往暴露出显著的脆弱性。2008年全球金融危机、2020年新冠疫情期间的市场崩盘,以及近年来地缘政治冲突引发的市场动荡,都清晰地揭示了传统组合的痛点:风险过度集中

传统组合通常面临两大核心难题:

  1. 股票风险主导:在典型的60/40股债组合中,尽管股票仅占60%的权重,但由于其远高于债券的波动性,组合90%以上的风险实际上来源于股票敞口。这意味着投资者在承担巨大波动的同时,并未获得相应的风险分散效益。
  2. 尾部风险失控:当市场危机来临时,股票和债券(尤其是信用债)往往出现相关性飙升,导致”分散化失效”,组合损失远超预期。

正是在这样的背景下,风险平价(Risk Parity)模型应运而生。它并非追求收益最大化,而是致力于风险均衡——让各类资产对组合的风险贡献相等。这种看似简单的理念,实则蕴含着深刻的金融逻辑,为破解传统组合波动难题提供了全新的量化解决方案。本文将深入剖析风险平价模型的理论基础、量化实现路径、实战应用策略,并通过详尽的Python代码示例展示其具体操作,最终探讨如何通过该模型实现真正的稳健收益。


一、传统资产配置的”波动陷阱”:为何我们需要风险平价?

1.1 传统市值加权组合的内在缺陷

市值加权组合(如标普500指数)是被动投资的基石,但其风险结构极不合理。假设一个简单的双资产组合:股票(年化波动率20%)和债券(年化波动率5%),初始权重为60%股票和40%债券。

风险贡献计算

  • 股票风险贡献 = 权重 × 边际风险贡献 ≈ 60% × 20% = 12%
  • 债券风险贡献 = 40% × 5% = 2%

结果:组合总风险约14%,但股票贡献了约85%的风险,债券仅贡献15%。投资者看似分散了资金,实则将绝大部分风险押注在单一资产类别上。

1.2 相关性崩溃:分散化的阿喀琉斯之踵

传统组合依赖资产间的低相关性来平滑波动。然而,在系统性风险爆发时,资产相关性会急剧上升。例如:

  • 2008年危机:股票与公司债的相关性从平时的0.3飙升至0.8以上
  • 2020年3月:黄金、股票、债券同时下跌,传统分散化瞬间失效

这种”同涨同跌”现象使得组合在最需要保护的时候反而失去缓冲。

1.3 风险平价的核心理念:从”资金平衡”到”风险平衡”

风险平价彻底颠覆了传统思维。它不再关注资金如何分配,而是让风险本身实现均衡。在上述双资产例子中,风险平价会调整权重,使得:

  • 股票风险贡献 = 债券风险贡献 = 组合总风险的50%

通过计算可得,这需要将股票权重降至约20%,债券权重提升至80%。虽然股票资金占比大幅下降,但其风险贡献与债券持平,实现了真正的风险分散。


二、风险平价模型的量化基础:从理论到数学表达

2.1 风险贡献的精确度量

风险平价的核心是计算每类资产对组合总风险的边际贡献。这需要引入现代投资组合理论(MPT)中的风险度量框架。

2.1.1 组合方差与协方差矩阵

对于一个包含N类资产的组合,其总方差为: $\( \sigma_p^2 = \sum_{i=1}^{N}\sum_{j=1}^{N} w_i w_j \sigma_i \sigma_j \rho_{ij} \)\( 其中 \)w_i\( 是资产i的权重,\)\sigmai\( 是其波动率,\)\rho{ij}$ 是资产i和j的相关系数。

用矩阵形式表示为: $\( \sigma_p^2 = \mathbf{w}^T \Sigma \mathbf{w} \)\( 其中 \)\mathbf{w}\( 是权重向量,\)\Sigma$ 是协方差矩阵。

2.1.2 边际风险贡献(MRC)

资产i对组合总风险的边际贡献定义为组合风险对该资产权重的偏导数: $\( MRC_i = \frac{\partial \sigma_p}{\partial w_i} = \frac{(\Sigma \mathbf{w})_i}{\sigma_p} \)\( 其中 \)(\Sigma \mathbf{w})_i$ 是协方差矩阵与权重向量乘积的第i个元素。

2.1.3 风险贡献(RC)

资产i的总风险贡献是其边际贡献与权重的乘积: $$ RC_i = w_i \times MRC_i = \frac{w_i (\Sigma \math1. 风险平价模型的量化基础:从理论到数学表达

2.1.1 组合方差与协方差矩阵

对于一个包含N类资产的组合,其总方差为: $\( \sigma_p^2 = \sum_{i=1}^{N}\sum_{j=1}^{N} w_i w_j \sigma_i \sigma_j \rho_{ij} \)\( 其中 \)w_i\( 是资产i的权重,\)\sigmai\( 是其波动率,\)\rho{ij}$ 是资产i和j的相关系数。

用矩阵形式表示为: $\( \sigma_p^2 = \mathbf{w}^T \Sigma \mathbf{w} \)\( 其中 \)\mathbf{w}\( 是权重向量,\)\Sigma$ 是协方差矩阵。

2.1.2 边际风险贡献(MRC)

资产i对组合总风险的边际贡献定义为组合风险对该资产权重的偏导数: $\( MRC_i = \frac{\partial \sigma_p}{\partial w_i} = \frac{(\Sigma \mathbf{w})_i}{\sigma_p} \)\( 其中 \)(\Sigma \mathbf{w})_i$ 是协方差矩阵与权重向量乘积的第i个元素。

2.1.3 风险贡献(RC)

资产i的总风险贡献是其边际贡献与权重的乘积: $\( RC_i = w_i \times MRC_i = \frac{w_i (\Sigma \mathbf{w})_i}{\sigma_p} \)$

关键性质:所有资产的风险贡献之和等于组合总风险: $\( \sum_{i=1}^{N} RC_i = \sigma_p \)$

2.2 风险平价的数学优化目标

风险平价的核心约束是让每类资产的风险贡献相等: $\( RC_i = \frac{\sigma_p}{N}, \quad \forall i = 1,2,...,N \)$

这转化为一个优化问题: $\( \min_{\mathbf{w}} \sum_{i=1}^{N} \left( RC_i - \frac{\sigma_p}{N} \right)^2 \)\( \)\( \text{s.t.} \quad \sum_{i=1}^{N} w_i = 1, \quad w_i \geq 0 \)$

2.3 实际应用中的关键挑战与解决方案

2.3.1 协方差矩阵估计误差

问题:历史协方差矩阵估计存在噪声,尤其在资产数量多时,估计误差会被放大。

解决方案

  • 收缩估计法(Shrinkage Estimator):将样本协方差矩阵向目标矩阵(如单位矩阵或单因子矩阵)收缩
  • 因子模型降维:用少数宏观经济因子解释资产收益变动
  • 滚动窗口与指数加权:使用更近期的数据赋予更高权重

2.3.2 非凸优化问题

问题:风险平价优化是非凸问题,存在多个局部最优解。

解决方案

  • 序列二次规划(SQP)
  • 信赖域算法
  • 解析近似解:在双资产情况下可推导精确解

三、Python实战:从零实现风险平价模型

下面我们将通过完整的Python代码,演示如何构建一个四资产(股票、债券、商品、REITs)的风险平价组合。

3.1 环境准备与数据获取

import numpy as np
import pandas as pd
import yfinance as yf
from scipy.optimize import minimize
import matplotlib.pyplot as plt
import seaborn as sns

# 设置中文字体(根据系统调整)
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

# 下载历史数据(2015-2024)
tickers = {
    '股票': 'SPY',    # 标普500 ETF
    '债券': 'TLT',    # 20年期国债ETF
    '商品': 'GLD',    # 黄金ETF
    'REITs': 'VNQ'    # 房地产信托ETF
}

# 获取数据
data = yf.download(list(tickers.values()), start='2015-01-01', end='2024-12-31')['Adj Close']
data.columns = list(tickers.keys())

# 计算对数收益率
returns = np.log(data / data.shift(1)).dropna()

print("数据概览:")
print(returns.head())
print(f"\n数据形状:{returns.shape}")

3.2 风险贡献计算函数

def calculate_risk_contributions(weights, cov_matrix):
    """
    计算各资产的风险贡献
    :param weights: 权重向量
    :param cov_matrix: 协方差矩阵
    :return: 风险贡献向量
    """
    # 组合波动率
    portfolio_vol = np.sqrt(weights.T @ cov_matrix @ weights)
    
    # 边际风险贡献
    marginal_risk_contrib = (cov_matrix @ weights) / portfolio_vol
    
    # 总风险贡献
    risk_contrib = weights * marginal_risk_contrib
    
    return risk_contrib

def calculate_portfolio_volatility(weights, cov_matrix):
    """计算组合波动率"""
    return np.sqrt(weights.T @ cov_matrix @ weights)

def risk_parity_objective(weights, cov_matrix):
    """
    风险平价目标函数:最小化风险贡献的差异
    """
    rc = calculate_risk_contributions(weights, cov_matrix)
    # 目标:让所有资产的风险贡献相等
    target_rc = np.sum(rc) / len(rc)
    return np.sum((rc - target_rc)**2)

3.3 优化求解风险平价权重

def calculate_risk_parity_weights(cov_matrix, max_iter=1000):
    """
    计算风险平价权重
    :param cov_matrix: 协方差矩阵
    :param max_iter: 最大迭代次数
    :return: 优化后的权重
    """
    n_assets = cov_matrix.shape[0]
    
    # 初始猜测:等权重
    initial_weights = np.ones(n_assets) / n_assets
    
    # 约束条件
    constraints = (
        {'type': 'eq', 'fun': lambda w: np.sum(w) - 1},  # 权重和为1
    )
    
    # 边界条件:不允许做空
    bounds = tuple((0, 1) for _ in range(n_assets))
    
    # 优化
    result = minimize(
        fun=risk_parity_objective,
        x0=initial_weights,
        args=(cov_matrix,),
        method='SLSQP',
        bounds=bounds,
        constraints=constraints,
        options={'maxiter': max_iter, 'ftol': 1e-9}
    )
    
    if not result.success:
        print(f"优化失败:{result.message}")
        return None
    
    return result.x

# 计算协方差矩阵(使用252天滚动窗口)
cov_matrix = returns.rolling(252).cov().dropna()

# 选取最近一期协方差矩阵进行计算
latest_cov = cov_matrix.iloc[-4:]  # 最后4个资产的协方差矩阵
print("最新协方差矩阵:")
print(latest_cov)

3.4 风险平价组合回测

def backtest_risk_parity(returns, rebal_freq=21):
    """
    回测风险平价组合
    :param returns: 收益率数据
    :param rebal_freq: 再平衡频率(交易日)
    :return: 组合净值曲线
    """
    n_periods = len(returns)
    n_assets = returns.shape[1]
    
    # 初始化
    portfolio_value = 1.0
    weights = np.ones(n_assets) / n_assets  # 初始等权重
    nav_series = [1.0]
    
    for i in range(1, n_periods):
        # 每rebal_freq天再平衡
        if i % rebal_freq == 0:
            # 使用过去252天数据计算协方差矩阵
            if i >= 252:
                cov_mat = returns.iloc[i-252:i].cov().values
                weights = calculate_risk_parity_weights(cov_mat)
                if weights is None:
                    weights = np.ones(n_assets) / n_assets
            else:
                weights = np.ones(n_assets) / n_assets
        
        # 计算当日收益
        daily_return = np.dot(weights, returns.iloc[i].values)
        portfolio_value *= (1 + daily_return)
        nav_series.append(portfolio_value)
    
    return pd.Series(nav_series, index=returns.index)

# 执行回测
rp_nav = backtest_risk_parity(returns)

# 对比基准:等权重组合
ew_nav = (1 + returns).cumprod()
ew_nav = ew_nav.mean(axis=1)  # 等权重组合净值

# 对比基准:60/40股债组合
stock_weight = 0.6
bond_weight = 0.4
benchmark_nav = 1 + returns['股票'] * stock_weight + returns['债券'] * bond_weight
benchmark_nav = benchmark_nav.cumprod()

3.5 绩效评估与可视化

def calculate_performance_metrics(nav_series, benchmark_series):
    """计算绩效指标"""
    returns_series = nav_series.pct_change().dropna()
    benchmark_returns = benchmark_series.pct_change().dropna()
    
    # 年化收益率
    annual_return = (nav_series.iloc[-1] / nav_series.iloc[0]) ** (252/len(returns_series)) - 1
    
    # 年化波动率
    annual_vol = returns_series.std() * np.sqrt(252)
    
    # 夏普比率(假设无风险利率2%)
    sharpe_ratio = (annual_return - 0.02) / annual_vol
    
    # 最大回撤
    cumulative = nav_series
    running_max = cumulative.expanding().max()
    drawdown = (cumulative - running_max) / running_max
    max_drawdown = drawdown.min()
    
    # 胜率(相对于基准)
    outperformance = (returns_series - benchmark_returns).gt(0).mean()
    
    return {
        '年化收益率': f"{annual_return:.2%}",
        '年化波动率': f"{annual_vol:.2%}",
        '夏普比率': f"{sharpe_ratio:.2f}",
        '最大回撤': f"{max_drawdown:.2%}",
        '跑赢基准概率': f"{outperformance:.1%}"
    }

# 计算指标
rp_metrics = calculate_performance_metrics(rp_nav, benchmark_nav)
ew_metrics = calculate_performance_metrics(ew_nav, benchmark_nav)
bm_metrics = calculate_performance_metrics(benchmark_nav, benchmark_nav)

print("\n=== 风险平价组合绩效 ===")
for k, v in rp_metrics.items():
    print(f"{k}: {v}")

print("\n=== 等权重组合绩效 ===")
for k, v in ew_metrics.items():
    print(f"{k}: {v}")

print("\n=== 60/40基准组合绩效 ===")
for k, v in bm_metrics.items():
    print(f"{k}: {v}")

# 可视化
plt.figure(figsize=(14, 6))

# 净值对比
plt.subplot(1, 2, 1)
plt.plot(rp_nav.index, rp_nav.values, label='风险平价', linewidth=2)
plt.plot(ew_nav.index, ew_nav.mean(axis=1).values, label='等权重', alpha=0.7)
plt.plot(benchmark_nav.index, benchmark_nav.values, label='60/40基准', alpha=0.7)
plt.title('净值曲线对比')
plt.xlabel('日期')
plt.ylabel('累计净值')
plt.legend()
plt.grid(True, alpha=0.3)

# 风险贡献对比
plt.subplot(1, 2, 2)
# 计算最新一期风险贡献
latest_weights = calculate_risk_parity_weights(latest_cov.values)
rc = calculate_risk_contributions(latest_weights, latest_cov.values)
plt.bar(range(len(rc)), rc)
plt.title('风险平价组合风险贡献')
plt.xlabel('资产类别')
plt.ylabel('风险贡献')
plt.xticks(range(len(rc)), list(tickers.keys()))
plt.grid(True, alpha=0.3, axis='y')

plt.tight_layout()
plt.show()

3.6 代码运行结果分析

运行上述代码后,你将得到:

  1. 净值曲线对比:清晰展示风险平价组合在波动控制上的优势
  2. 绩效指标表格:量化比较三种策略的表现
  3. 风险贡献柱状图:验证风险平价是否真正实现了风险均衡

预期结果:在2015-2024年的回测中,风险平价组合通常会表现出:

  • 更低的波动率(约8-10% vs 12-15%)
  • 更小的最大回撤(约10-15% vs 20-25%)
  • 更优的夏普比率(通常>0.8)
  • 更高的风险贡献均衡度(各资产RC差异%)

四、进阶策略:从基础风险平价到增强型方案

4.1 引入杠杆:风险平价的”放大器”

基础风险平价组合往往因债券权重过高而导致预期收益偏低。通过适度杠杆(如2倍),可以在保持风险均衡的同时提升收益。

杠杆实现代码

def leveraged_risk_parity_weights(cov_matrix, leverage=2.0):
    """
    计算杠杆化风险平价权重
    :param leverage: 杠杆倍数
    """
    base_weights = calculate_risk_parity_weights(cov_matrix)
    if base_weights is None:
        return None
    
    # 杠杆化(需考虑融资成本)
    leveraged_weights = base_weights * leverage
    
    # 约束:权重不能超过1(单资产上限)
    leveraged_weights = np.clip(leveraged_weights, 0, 1)
    
    # 重新归一化(如果超过1)
    if leveraged_weights.sum() > 1:
        leveraged_weights = leveraged_weights / leveraged_weights.sum()
    
    return leveraged_weights

4.2 动态风险预算:应对市场 regime 变化

市场波动率是动态变化的,固定风险预算可能失效。动态风险平价根据市场波动率调整风险敞口。

动态调整逻辑

  • 当市场波动率上升时,降低整体风险预算
  • 当市场波动率下降时,适度增加风险敞口
def dynamic_risk_parity_weights(cov_matrix, current_vol, baseline_vol=0.15):
    """
    动态风险平价:根据当前波动率调整风险预算
    :param current_vol: 当前市场波动率
    :param baseline_vol: 基准波动率(如15%)
    """
    base_weights = calculate_risk_parity_weights(cov_matrix)
    
    # 计算调整系数(反比关系)
    adjustment_factor = baseline_vol / current_vol
    
    # 调整权重(保持总和为1)
    adjusted_weights = base_weights * adjustment_factor
    adjusted_weights /= adjusted_weights.sum()
    
    return adjusted_weights

4.3 引入动量因子:增强收益

在风险平价基础上加入动量因子,可以提升组合收益。具体做法是:对风险贡献进行动量加权。

def momentum_weighted_risk_parity(returns, cov_matrix, momentum_window=63):
    """
    动量加权风险平价
    """
    # 计算动量得分(过去63天收益)
    momentum_scores = returns.iloc[-momentum_window:].mean()
    
    # 基础风险平价权重
    base_weights = calculate_risk_parity_weights(cov_matrix)
    
    # 动量加权(对高动量资产增加风险预算)
    momentum_weights = base_weights * (1 + momentum_scores / momentum_scores.std())
    momentum_weights /= momentum_weights.sum()
    
    return momentum_weights

五、风险平价的局限性与风险管理

5.1 模型固有缺陷

  1. 对协方差矩阵高度敏感:估计误差会直接导致权重失真
  2. 尾部风险未完全消除:在极端市场下,资产相关性可能趋近1,风险平价也会失效
  3. 流动性约束:小规模资产可能无法承载大资金的快速调整

5.2 实战风险管理框架

5.2.1 压力测试与情景分析

def stress_test_risk_parity(cov_matrix, stress_scenarios):
    """
    压力测试:模拟极端市场下的表现
    :param stress_scenarios: 压力情景字典,如{'股债双杀': {'股票': -0.1, '债券': -0.05}}
    """
    base_weights = calculate_risk_parity_weights(cov_matrix)
    results = {}
    
    for scenario_name, returns_shock in stress_scenarios.items():
        # 构造压力下的协方差矩阵(放大波动率)
        stressed_cov = cov_matrix.copy()
        for asset, shock in returns_shock.items():
            idx = list(tickers.keys()).index(asset)
            stressed_cov.iloc[idx, idx] *= 3  # 假设波动率放大3倍
        
        # 重新计算权重
        stressed_weights = calculate_risk_parity_weights(stressed_cov)
        
        # 计算压力下的风险贡献
        rc = calculate_risk_contributions(stressed_weights, stressed_cov.values)
        results[scenario_name] = dict(zip(tickers.keys(), rc))
    
    return results

# 定义压力情景
stress_scenarios = {
    '股债双杀': {'股票': -0.1, '债券': -0.05},
    '通胀飙升': {'股票': -0.08, '债券': -0.1, '商品': 0.05},
    '流动性危机': {'股票': -0.15, '债券': -0.02, 'REITs': -0.12}
}

stress_results = stress_test_risk_parity(latest_cov.values, stress_scenarios)
print("\n压力测试结果:")
for scenario, rc in stress_results.items():
    print(f"\n{scenario}:")
    for asset, value in rc.items():
        print(f"  {asset}: {value:.4f}")

5.2.2 动态再平衡纪律

核心原则

  • 定期再平衡:每月或每季度强制调整回目标权重
  • 阈值再平衡:当某资产风险贡献偏离目标超过±20%时触发调整
  • 交易成本控制:在再平衡时考虑冲击成本,避免过度交易

六、实战案例:2020年3月市场崩盘中的风险平价表现

6.1 市场背景回顾

2020年3月,新冠疫情引发全球市场恐慌:

  • 标普500指数下跌34%
  • 美国国债上涨(避险情绪)
  • 黄金先跌后涨
  • 公司债暴跌

6.2 组合表现对比

传统60/40组合

  • 股票部分损失:-34% × 60% = -20.4%
  • 债券部分收益:+5% × 40% = +2%
  • 总损失:-18.4%

风险平价组合(假设权重:股票20%、债券60%、黄金10%、REITs 10%):

  • 股票:-34% × 20% = -6.8%
  • 债券:+5% × 60% = +3%
  • 黄金:+2% × 10% = +0.2%
  • REITs:-25% × 10% = -2.5%
  • 总损失:-6.1%

关键洞察:风险平价通过大幅降低股票权重,有效控制了尾部损失。虽然债券权重高,但其波动率低,实际风险贡献仍与股票相当。

6.3 事后复盘与优化

2020年3月也暴露了风险平价的弱点:流动性冲击下的再平衡困难。当时许多ETF出现大幅折溢价,实际交易成本极高。

优化方案

  • 引入波动率过滤器:当VIX指数超过40时,暂停再平衡
  • 增加现金缓冲:保留5-10%现金应对极端情况
  • 使用期货替代ETF:提高流动性,降低冲击成本

七、构建稳健收益的完整框架

7.1 四步实施路径

第一步:数据准备与清洗

  • 获取至少5-10年历史数据
  • 处理缺失值和异常值
  • 检验数据平稳性

第二步:协方差矩阵优化

  • 使用收缩估计法
  • 引入因子模型降维
  • 滚动窗口动态更新

第三步:权重计算与优化

  • 实现风险平价核心算法
  • 加入杠杆和动态调整
  • 设置约束条件(行业、个券上限)

第四步:执行与监控

  • 建立再平衡日历
  • 实时监控风险贡献偏离度
  • 定期压力测试

7.2 与传统组合的对比总结

指标 60/40组合 风险平价组合 说明
资金分配 股票60%,债券40% 股票20%,债券60%,其他20% 风险平价债券权重更高
风险贡献 股票85%,债券15% 各资产约25% 风险平价实现均衡
年化波动率 12-15% 8-10% 风险平价更稳健
最大回撤 -20%至-30% -10%至-15% 风险平价回撤更小
夏普比率 0.5-0.7 0.8-1.0 风险平价风险调整后收益更高
尾部风险 风险平价仍需杠杆提升收益

7.3 个人投资者实施建议

对于个人投资者,可采用以下简化方案:

  1. 使用现成ETF:SPY(股票)、TLT(债券)、GLD(黄金)、VNQ(REITs)
  2. 季度再平衡:每季度调整一次,降低交易成本
  3. 杠杆控制:如需增强收益,杠杆不超过1.5倍
  4. 长期坚持:风险平价的优势在3-5年周期中才能充分显现

八、结论:风险平价——从理论到实践的稳健之路

风险平价模型通过风险均衡理念,从根本上解决了传统组合”风险集中”的痛点。它并非追求绝对收益,而是致力于优化风险-adjusted回报,这正是长期投资成功的关键。

核心价值总结

  1. 破解波动难题:通过风险贡献均衡,大幅降低组合波动率和最大回撤
  2. 实现稳健收益:在不同市场环境下提供更平滑的净值曲线
  3. 量化透明:所有决策基于数据和模型,避免主观情绪干扰

未来展望: 随着人工智能和机器学习技术的发展,风险平价模型正朝着智能化方向演进:

  • AI驱动的协方差预测:使用LSTM、Transformer等模型提升预测精度
  • 自适应风险预算:根据市场状态动态调整风险分配
  • 多因子融合:将风险平价与动量、价值等因子结合

对于追求长期稳健增值的投资者而言,风险平价不仅是一种技术工具,更是一种投资哲学——它提醒我们:控制风险,才能驾驭收益


附录:完整代码与数据获取指南

A.1 完整可运行脚本

# 一键运行完整风险平价系统
import numpy as np
import pandas as pd
import yfinance as yf
from scipy.optimize import minimize
import matplotlib.pyplot as plt

class RiskParityPortfolio:
    """风险平价组合类"""
    
    def __init__(self, tickers, start_date, end_date):
        self.tickers = tickers
        self.start_date = start_date
        self.end_date = end_date
        self.data = None
        self.returns = None
        self.cov_matrix = None
        
    def fetch_data(self):
        """获取数据"""
        self.data = yf.download(
            list(self.tickers.values()), 
            start=self.start_date, 
            end=self.end_date
        )['Adj Close']
        self.data.columns = list(self.tickers.keys())
        self.returns = np.log(self.data / self.data.shift(1)).dropna()
        return self
    
    def calculate_weights(self, window=252):
        """计算最新权重"""
        if self.returns is None:
            raise ValueError("请先获取数据")
        
        # 使用滚动窗口计算协方差
        cov_mat = self.returns.iloc[-window:].cov().values
        
        # 风险平价优化
        n_assets = len(self.tickers)
        initial_weights = np.ones(n_assets) / n_assets
        
        constraints = ({'type': 'eq', 'fun': lambda w: np.sum(w) - 1})
        bounds = tuple((0, 1) for _ in range(n_assets))
        
        def objective(w):
            vol = np.sqrt(w.T @ cov_mat @ w)
            rc = w * (cov_mat @ w) / vol
            target = np.sum(rc) / n_assets
            return np.sum((rc - target)**2)
        
        result = minimize(objective, initial_weights, method='SLSQP', 
                         bounds=bounds, constraints=constraints)
        
        return dict(zip(self.tickers.keys(), result.x))
    
    def backtest(self, rebal_freq=21):
        """回测"""
        returns = self.returns
        n_periods = len(returns)
        n_assets = len(self.tickers)
        
        nav = 1.0
        nav_series = [1.0]
        weights = np.ones(n_assets) / n_assets
        
        for i in range(1, n_periods):
            if i % rebal_freq == 0 and i >= 252:
                cov_mat = returns.iloc[i-252:i].cov().values
                # 重新计算权重
                initial_weights = np.ones(n_assets) / n_assets
                constraints = ({'type': 'eq', 'fun': lambda w: np.sum(w) - 1})
                bounds = tuple((0, 1) for _ in range(n_assets))
                def objective(w):
                    vol = np.sqrt(w.T @ cov_mat @ w)
                    rc = w * (cov_mat @ w) / vol
                    target = np.sum(rc) / n_assets
                    return np.sum((rc - target)**2)
                result = minimize(objective, initial_weights, method='SLSQP', 
                                 bounds=bounds, constraints=constraints)
                if result.success:
                    weights = result.x
            
            daily_return = np.dot(weights, returns.iloc[i].values)
            nav *= (1 + daily_return)
            nav_series.append(nav)
        
        return pd.Series(nav_series, index=returns.index)

# 使用示例
if __name__ == "__main__":
    # 定义资产
    tickers = {
        '股票': 'SPY',
        '债券': 'TLT',
        '商品': 'GLD',
        'REITs': 'VNQ'
    }
    
    # 创建实例
    rp = RiskParityPortfolio(tickers, '2015-01-01', '2024-12-31')
    
    # 获取数据
    rp.fetch_data()
    
    # 计算当前权重
    current_weights = rp.calculate_weights()
    print("当前风险平价权重:")
    for asset, weight in current_weights.items():
        print(f"  {asset}: {weight:.2%}")
    
    # 回测
    nav = rp.backtest()
    print(f"\n回测结果:累计收益率 {(nav.iloc[-1]-1):.2%}")
    
    # 可视化
    plt.figure(figsize=(10, 6))
    plt.plot(nav.index, nav.values, linewidth=2)
    plt.title('风险平价组合净值曲线')
    plt.xlabel('日期')
    plt.ylabel('累计净值')
    plt.grid(True, alpha=0.3)
    plt.show()

A.2 数据获取注意事项

  1. 数据源:推荐使用Yahoo Finance(yfinance库)或Wind、Bloomberg等专业数据库
  2. 数据频率:日频数据最适合风险平价计算
  3. 数据长度:至少5年历史数据,理想为10年
  4. 数据清洗:处理除权、除息、停牌等情况

A.3 常见问题解答

Q1: 风险平价是否适合所有市场环境? A: 风险平价在震荡市和熊市中表现优异,但在单边牛市可能跑输纯股票组合。建议作为核心配置(50-70%),搭配其他策略。

Q2: 个人投资者如何低成本实现? A: 使用ETF组合,季度再平衡,避免频繁交易。杠杆可通过融资融券或杠杆ETF实现,但需谨慎。

Q3: 如何处理资产数量较多的情况? A: 当资产超过10个时,建议使用因子模型降维,或采用分层风险平价(Hierarchical Risk Parity)方法。


最终总结:风险平价模型通过量化手段实现了真正的风险分散,为破解传统组合波动难题提供了科学方案。虽然它不是万能的,但在长期投资中,其稳健性可解释性使其成为现代资产配置不可或缺的工具。对于追求睡得着觉的投资的投资者而言,风险平价值得深入研究和实践。