第三节:协方差矩阵与相关性——风险联动的核心工具
说实话,协方差矩阵这东西,我刚开始做量化的时候觉得它就是个数学符号。直到有一次,我管理的组合在2020年3月市场暴跌时,明明分散了仓位,却所有资产一起跳水。嗯,那才真正意识到——你看到的分散,可能只是假象。
这一节,我们就来拆解协方差矩阵和相关性。它们是你理解资产之间「怎么联动」的基础。说白了,没有它们,风险预算就是瞎算。
3.1 协方差矩阵的计算
先问个问题:为什么我们不用方差,而要用协方差?
因为单看一个资产的波动,你永远不知道它和别的资产是不是「同涨同跌」。协方差就是衡量两个资产一起变动的方向和幅度。
公式长这样:
Cov(Ri, Rj) = E[(Ri - μi)(Rj - μj)]
翻译成人话:把两个资产的收益率,各自减去自己的均值,然后乘起来,再求平均。
我习惯用Python里的numpy.cov直接算。但你要理解它背后的逻辑——正数表示同向变动,负数表示反向变动,零表示没关系。
举个例子,假设我们有三个资产:沪深300、中证500、国债。5天的收益率数据如下:
| 日期 | 沪深300 | 中证500 | 国债 |
|---|---|---|---|
| Day1 | 0.02 | 0.03 | -0.01 |
| Day2 | -0.01 | -0.02 | 0.005 |
| Day3 | 0.015 | 0.01 | -0.002 |
| Day4 | -0.005 | 0.005 | 0.003 |
| Day5 | 0.01 | 0.015 | -0.004 |
用Python算一下:
import numpy as np
returns = np.array([
[0.02, 0.03, -0.01],
[-0.01, -0.02, 0.005],
[0.015, 0.01, -0.002],
[-0.005, 0.005, 0.003],
[0.01, 0.015, -0.004]
])
cov_matrix = np.cov(returns, rowvar=False)
print(cov_matrix)
输出结果是一个3x3的矩阵。对角线是每个资产自己的方差,非对角线就是协方差。你会发现沪深300和中证500的协方差是正的,而它们和国债的协方差是负的。这符合直觉——股票和债券通常跷跷板。
3.2 相关性分析
协方差有个问题:它受量纲影响。比如一个资产收益率是0.01级别,另一个是0.1级别,协方差会被大的那个带偏。这时候就需要相关性——说白了,就是标准化后的协方差。
相关系数公式:
ρij = Cov(Ri, Rj) / (σi * σj)
取值范围在-1到1之间。1代表完全正相关,-1代表完全负相关,0代表不相关。
我个人习惯,在构建组合前,先看相关性矩阵。如果两个资产相关性超过0.8,我会怀疑它们是不是同一个东西。我曾经见过一个组合,里面同时持有沪深300ETF和上证50ETF,相关性高达0.95——那还分散个啥?
用上面的数据算相关性:
corr_matrix = np.corrcoef(returns, rowvar=False)
print(corr_matrix)
结果大概是这样:
| 沪深300 | 中证500 | 国债 | |
|---|---|---|---|
| 沪深300 | 1.00 | 0.87 | -0.62 |
| 中证500 | 0.87 | 1.00 | -0.55 |
| 国债 | -0.62 | -0.55 | 1.00 |
你看,两个股票指数相关性0.87,很高。而国债和它们都是负相关。这就是经典的「股债跷跷板」效应。
3.3 协方差矩阵的估计方法
这里有个坑:你算出来的协方差矩阵,真的准吗?
我踩过这个坑。有一次我用过去3年的日数据算协方差矩阵,结果模型回测很漂亮,实盘却一塌糊涂。为什么?因为市场结构变了,过去的协方差不能代表未来。
所以,我们需要不同的估计方法。常用的有三种:
3.3.1 历史估计法
最简单粗暴。直接用过去N天的收益率数据算协方差。优点是简单,缺点是对极端值敏感,而且假设过去会重复。
# 历史估计:直接用最近252个交易日
hist_cov = np.cov(returns[-252:], rowvar=False)
我建议你至少用1年以上的数据。太短了噪声大,太长了市场结构可能已经变了。
3.3.2 指数加权估计法
这个方法更聪明。它给近期的数据更高的权重,远期的数据权重逐渐衰减。说白了,就是「最近发生的事更重要」。
公式:
Cov_t = λ * Cov_{t-1} + (1-λ) * (R_t * R_t^T)
其中λ是衰减因子,通常取0.94(RiskMetrics的标准做法)。
def ewma_cov(returns, lam=0.94):
cov = np.cov(returns[:2], rowvar=False)
for i in range(2, len(returns)):
r = returns[i].reshape(-1, 1)
cov = lam * cov + (1-lam) * (r @ r.T)
return cov
我个人习惯用指数加权。它在市场波动突变时反应更快。比如2020年3月,历史估计法还在用过去的数据平滑,指数加权已经捕捉到了波动率的飙升。
3.3.3 收缩估计法
这个方法解决了一个实际问题:当资产数量多、数据量少时,样本协方差矩阵会「病态」。比如你有100个资产,但只有50天的数据,算出来的矩阵可能不可逆。
收缩估计法的思路是:把样本协方差矩阵向一个「目标矩阵」收缩。目标矩阵通常是对角矩阵(假设资产不相关)或者常数相关矩阵。
公式:
Σ_shrink = δ * F + (1-δ) * S
其中S是样本协方差矩阵,F是目标矩阵,δ是收缩强度(0到1之间)。
from sklearn.covariance import LedoitWolf
lw = LedoitWolf()
shrink_cov = lw.fit(returns).covariance_
Ledoit-Wolf方法会自动计算最优的收缩强度。我遇到高维数据时,几乎必用这个方法。它虽然有点「作弊」——把矩阵往理想方向拉——但实际效果往往比纯样本估计好得多。
知识体系总览
下面这张图,帮你理清这一节的核心逻辑:
嗯,这一节的内容就到这里。协方差矩阵和相关性,是量化交易里最基础也最容易被忽视的工具。你想想看,如果连资产之间怎么联动都没搞清楚,风险预算就是空中楼阁。
我建议你拿到真实数据后,先画个相关性热力图,再试试三种估计方法。你会发现,不同的方法算出来的组合风险可能差好几个百分点。选对方法,比选对参数更重要。