3、协方差矩阵估计:历史协方差、指数加权协方差(EWMA)、收缩估计量(Shrinkage)
协方差矩阵,说白了就是资产之间联动关系的「心电图」。你想想看,做风险预算模型,如果连资产之间怎么互相影响都搞不清楚,那后面的配置全是瞎蒙。我见过不少新手,上来就用历史协方差,结果回测漂亮,实盘一跑就崩——为什么?因为市场变了,过去的联动关系早就不作数了。
这一章,咱们就聊聊三种主流的估计方法。每种方法我都踩过坑,有些教训是用真金白银换来的。
3.1 历史协方差:最朴素,也最危险
历史协方差,就是直接拿过去一段时间的收益率数据,算个协方差矩阵。公式很简单:
import numpy as np
def historical_cov(returns):
# returns: T x N 的收益率矩阵
return np.cov(returns, rowvar=False)
嗯,代码就三行。但越简单的东西,坑越多。
- 等权重陷阱:历史协方差给每一天的权重都一样。但你想,3个月前的数据和昨天的数据,对今天的风险影响能一样吗?
- 样本长度选择:用半年数据?噪声太大。用三年数据?结构可能已经变了。我个人习惯,股票组合至少用1年,债券组合用2年。
- 极端值敏感:一次股灾,就能把协方差矩阵拉偏。我见过一个组合,因为包含了2015年股灾的数据,协方差矩阵里全是恐慌情绪,导致后续配置极度保守。
历史协方差最大的问题,就是它假设「过去=未来」。但市场哪会这么听话?
3.2 指数加权协方差(EWMA):给数据「打折」
EWMA的思路很直观:越近的数据越重要,越远的数据越不重要。用指数衰减的方式给每个时间点分配权重。
公式长这样:
def ewma_cov(returns, lambda_=0.94):
"""
lambda_: 衰减因子,通常取0.94(日频数据)
"""
T, N = returns.shape
cov = np.zeros((N, N))
# 权重:最近的数据权重最大
weights = np.array([(1 - lambda_) * lambda_**(T-1-i) for i in range(T)])
weights /= weights.sum() # 归一化
mean = np.average(returns, axis=0, weights=weights)
centered = returns - mean
# 加权协方差
cov = np.dot((centered * weights[:, np.newaxis]).T, centered)
return cov
lambda_ 怎么选?我一般这么干:
- 日频数据:lambda_=0.94(RiskMetrics的经典值)
- 周频数据:lambda_=0.97
- 月频数据:lambda_=0.99
但别死板。有一次我管一个高频策略,lambda_调到0.98才稳定。说白了,你得根据策略的换手率来调。
EWMA的好处是能快速响应市场变化。但坏处也很明显——如果最近几天市场异常波动(比如闪崩),EWMA会过度反应,导致协方差矩阵「抽风」。
3.3 收缩估计量(Shrinkage):中庸之道
历史协方差噪声大,EWMA又太敏感。那有没有折中方案?有,就是收缩估计量。
核心思想:把样本协方差矩阵往一个「先验目标」上拉一拉。这个目标通常是一个结构简单的矩阵,比如等相关系数矩阵或单因子模型矩阵。
def shrinkage_cov(returns, alpha=0.2):
"""
alpha: 收缩强度,0=纯样本,1=纯目标
"""
# 样本协方差
sample_cov = np.cov(returns, rowvar=False)
# 目标矩阵:这里用等相关系数矩阵
# 即所有资产两两之间的相关系数都取平均值
corr = np.corrcoef(returns, rowvar=False)
avg_corr = (corr.sum() - N) / (N * (N - 1)) # 去掉对角线
target_cov = np.ones_like(sample_cov) * avg_corr
# 保持方差不变
np.fill_diagonal(target_cov, np.diag(sample_cov))
# 收缩
shrunk_cov = (1 - alpha) * sample_cov + alpha * target_cov
return shrunk_cov
alpha 怎么选?别拍脑袋。我推荐用 Ledoit-Wolf 方法,它能根据数据自动估计最优收缩强度。Scikit-learn 里直接有现成的:
from sklearn.covariance import LedoitWolf
lw = LedoitWolf()
lw.fit(returns)
shrunk_cov = lw.covariance_
这个方法我用了好几年,基本没出过大问题。
3.4 三种方法对比:什么时候用哪个?
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 历史协方差 | 简单、透明、可解释 | 噪声大、反应慢、等权重 | 数据量大、市场稳定时 |
| EWMA | 反应快、适应性强 | 过度敏感、参数难调 | 高频策略、趋势市场 |
| 收缩估计量 | 稳健、噪声小、自动调参 | 计算稍复杂、目标选择有讲究 | 资产多、数据短、追求稳健 |
我个人习惯:先用 Ledoit-Wolf 打底,如果策略对时效性要求高,再切换到 EWMA。但不管用哪种,一定要做稳定性检验——把协方差矩阵的特征值画出来,看看有没有异常值。
3.5 知识体系总览
下面这张图,帮你把三种方法的关系理清楚:
别纠结哪种方法最好。我一般这么干:
- 先用 Ledoit-Wolf 跑一遍,得到基准结果
- 再用 EWMA 跑一遍,看看差异大不大
- 如果差异超过10%,说明市场结构可能变了,得深挖原因
记住,协方差矩阵只是工具,不是真理。真正重要的是你理解它背后的假设,以及这些假设在当下市场是否成立。