风险平价策略:让资产组合真正“分散”

大家好,我是你们的老朋友。今天我们来聊聊风险平价策略。

说实话,我在刚入行做资产配置那会儿,一直以为“分散投资”就是多买几个品种。股票、债券、商品各来一点,觉得这样就稳了。结果呢?2008年那次市场暴跌,我的组合回撤超过40%。为什么?因为所有资产都在跌,所谓的分散根本没起到作用。

后来我接触到风险平价模型,才恍然大悟——真正的分散,不是资金上的分散,而是风险贡献上的分散

风险平价模型的核心原理

风险平价(Risk Parity)这个概念,最早由桥水基金的Ray Dalio提出。它的核心思想很简单:让组合中每类资产对总风险的贡献度相等

你想想看,传统60/40组合(60%股票+40%债券)里,股票的波动率通常是债券的3-5倍。所以虽然股票只占60%资金,但它贡献了超过90%的风险。一旦股市崩盘,组合就跟着崩。

风险平价要解决的就是这个问题。它通过调整各类资产的杠杆比例,让每类资产的风险贡献度相同。说白了,就是让债券多加点杠杆,让股票少配一点,最终实现风险层面的真正均衡。

核心公式:

风险贡献度 RC_i = w_i × σ_i × ρ_i

其中 w_i 是资产权重,σ_i 是波动率,ρ_i 是资产与组合的相关性

风险平价条件:RC_1 = RC_2 = ... = RC_n

波动率计算:风险平价的基础

做风险平价,第一步就是算波动率。我个人习惯用指数加权移动平均(EWMA),而不是简单的历史波动率。为什么?因为EWMA对近期数据更敏感,能更快捕捉市场变化。

我在项目中遇到过一个问题:用30天滚动窗口算波动率,结果在2020年3月疫情爆发时,波动率飙升得太快,模型来不及调整,导致组合回撤很大。后来我改用EWMA,衰减因子设0.94,效果就好多了。

import numpy as np
import pandas as pd

def ewma_volatility(returns, lambda_=0.94):
    """
    计算EWMA波动率
    lambda_:衰减因子,通常取0.94-0.97
    """
    var = np.zeros(len(returns))
    var[0] = returns.iloc[0] ** 2
    
    for i in range(1, len(returns)):
        var[i] = lambda_ * var[i-1] + (1 - lambda_) * (returns.iloc[i] ** 2)
    
    vol = np.sqrt(var) * np.sqrt(252)  # 年化
    return pd.Series(vol, index=returns.index)

# 示例:计算沪深300的EWMA波动率
hs300_returns = ...  # 你的收益率数据
vol_series = ewma_volatility(hs300_returns)
print(vol_series.tail())

小技巧:衰减因子lambda_的取值很关键。0.94适合日频数据,0.97适合周频数据。我一般会做敏感性测试,看看不同参数下组合表现是否稳定。

杠杆调整:风险平价的关键一步

风险平价模型算出来的权重,往往债券占比很高(因为债券波动率低),股票占比很低。但这样组合的预期收益也会很低。怎么办?加杠杆。

嗯,这里要注意:杠杆不是随便加的。我见过很多新手直接给债券加2倍杠杆,结果遇到利率飙升,债券暴跌,杠杆反而放大了亏损。

正确的做法是:先确定目标波动率,再反推杠杆倍数

def risk_parity_weights(cov_matrix, target_vol=0.10):
    """
    风险平价权重计算
    cov_matrix:协方差矩阵
    target_vol:目标组合波动率(年化)
    """
    n = len(cov_matrix)
    
    # 初始化权重
    w = np.ones(n) / n
    
    # 迭代求解风险平价条件
    for _ in range(100):
        # 计算组合波动率
        port_vol = np.sqrt(w @ cov_matrix @ w)
        
        # 计算边际风险贡献
        mrc = cov_matrix @ w / port_vol
        
        # 计算风险贡献
        rc = w * mrc
        
        # 调整权重,使各资产风险贡献相等
        target_rc = np.mean(rc)
        w = w * (target_rc / rc)
        
        # 归一化
        w = w / np.sum(w)
    
    # 计算杠杆倍数
    leverage = target_vol / port_vol
    
    return w, leverage

# 示例
cov = ...  # 你的协方差矩阵
weights, lev = risk_parity_weights(cov, target_vol=0.12)
print(f"风险平价权重:{weights}")
print(f"建议杠杆倍数:{lev:.2f}倍")

避坑指南:我曾经在2018年给一个客户做风险平价组合,目标波动率设了15%。结果那年A股暴跌,债券也没涨多少,组合波动率直接飙到20%以上。后来我学乖了,目标波动率不要超过长期历史波动率的80%。

实战回测框架:从理论到落地

理论说完了,我们来搭一个完整的回测框架。我个人习惯用向量化回测,速度比循环快10倍以上。

class RiskParityBacktest:
    def __init__(self, returns, window=60, rebalance_freq=21):
        """
        returns:多资产收益率DataFrame
        window:滚动窗口期(交易日)
        rebalance_freq:再平衡频率(交易日)
        """
        self.returns = returns
        self.window = window
        self.rebalance_freq = rebalance_freq
        
    def run(self, target_vol=0.10):
        n = self.returns.shape[1]
        weights = np.zeros((len(self.returns), n))
        
        for i in range(self.window, len(self.returns)):
            # 每rebalance_freq天再平衡一次
            if (i - self.window) % self.rebalance_freq == 0:
                # 计算滚动协方差矩阵
                cov = self.returns.iloc[i-self.window:i].cov() * 252
                
                # 计算风险平价权重
                w, _ = risk_parity_weights(cov.values, target_vol)
                weights[i] = w
            else:
                weights[i] = weights[i-1]
        
        # 计算组合收益
        port_returns = np.sum(weights * self.returns.values, axis=1)
        
        # 计算净值曲线
        nav = (1 + port_returns).cumprod()
        
        return nav, weights
    
    def evaluate(self, nav):
        """评估组合表现"""
        total_return = nav.iloc[-1] - 1
        annual_return = total_return ** (252 / len(nav)) - 1
        annual_vol = nav.pct_change().std() * np.sqrt(252)
        sharpe = annual_return / annual_vol
        max_drawdown = (nav / nav.cummax() - 1).min()
        
        return {
            '总收益': f'{total_return:.2%}',
            '年化收益': f'{annual_return:.2%}',
            '年化波动': f'{annual_vol:.2%}',
            '夏普比率': f'{sharpe:.2f}',
            '最大回撤': f'{max_drawdown:.2%}'
        }

# 使用示例
bt = RiskParityBacktest(returns, window=60, rebalance_freq=21)
nav, weights = bt.run(target_vol=0.10)
metrics = bt.evaluate(nav)
print(metrics)

风险平价策略的核心逻辑图

下面这张图是我自己画的,把风险平价策略的整个流程串起来了。你看一遍就能明白。

风险平价策略核心流程图 多资产收益率数据 EWMA波动率计算(lambda=0.94) 滚动协方差矩阵计算 迭代求解风险平价权重(RC相等) 杠杆调整(目标波动率) 最终组合权重

实战中的几个坑

做风险平价策略,有几个地方特别容易踩坑。我用自己的血泪史给你提个醒:

  • 协方差矩阵不稳定:市场风格切换时,相关性会突变。我建议用60-90天的滚动窗口,太短了噪声大,太长了反应慢。
  • 杠杆成本:加杠杆不是免费的。融资成本、期货升贴水都会侵蚀收益。我一般会在目标波动率上打个8折,留点安全边际。
  • 再平衡频率:太频繁了交易成本高,太低了偏离度大。我个人习惯每月再平衡一次,也就是21个交易日。
  • 极端行情:2020年3月、2022年11月这种极端行情下,波动率会瞬间飙升。我建议设置一个波动率上限,超过就强制减仓。

总结一下:风险平价不是万能药,但它确实能帮你构建一个更稳健的组合。核心就三点:算对波动率、调平风险贡献、控制好杠杆。把这三点做好,你的组合就能在市场波动中睡得着觉。

专注资料整理