第 3 章 · 01 样本协方差


文档摘要

第 3 章 · 01 样本协方差 本节摘要:本节是「风险模型」五节里的第一节,讲最直接也最常被滥用的协方差估计—— 。协方差矩阵 Σ 是均值-方差优化的基石:对角线是每只资产的方差(波动率的平方),非对角线是两两资产的联动程度。 直接用历史收益的样本协方差 年化,无偏但噪声极大。本节拆解它的源码实现、PSD(半正定)修正机制、frequency 参数,以及它最大的隐患——在「资产数接近样本数」时严重失真,优化器会把噪声当成信号放大成错误权重。文档明确建议:不要把它当默认,应优先用收缩估计。 内容来源:原项目源码 (函数 、 )、文档 ,汉化并套用体系化模板。 学习目标 阅读完本节,你应当能够: 写出协方差矩阵的数学含义与对角/非对角元素。 用 算年化样本协方差。

第 3 章 · 01 样本协方差

本节摘要:本节是「风险模型」五节里的第一节,讲最直接也最常被滥用的协方差估计——sample_cov。协方差矩阵 Σ 是均值-方差优化的基石:对角线是每只资产的方差(波动率的平方),非对角线是两两资产的联动程度。sample_cov 直接用历史收益的样本协方差 returns.cov() × 252 年化,无偏但噪声极大。本节拆解它的源码实现、PSD(半正定)修正机制、frequency 参数,以及它最大的隐患——在「资产数接近样本数」时严重失真,优化器会把噪声当成信号放大成错误权重。文档明确建议:不要把它当默认,应优先用收缩估计。

内容来源:原项目源码 pypfopt/risk_models.py(函数 sample_covfix_nonpositive_semidefinite)、文档 docs/RiskModels.rst,汉化并套用体系化模板。

学习目标

阅读完本节,你应当能够:

  1. 写出协方差矩阵的数学含义与对角/非对角元素。
  2. sample_cov 算年化样本协方差。
  3. 解释 PSD(半正定) 要求与自动修正机制。
  4. 复述样本协方差的估计噪声问题(资产数接近样本数时失真)。
  5. 理解作者为何不建议把它当默认。

一、协方差矩阵的直觉

协方差矩阵 Σ 是一个 N×N 对称矩阵(N 是资产数),编码了资产两两之间的联动:

  • 对角线 Σ_ii:资产 i 的方差 = 波动率 σ_i 的平方。
  • 非对角线 Σ_ij:资产 i 与 j 的协方差,= 相关性 × σ_i × σ_j。

它在均值-方差优化里扮演核心角色:

组合方差 = wᵀ Σ w

💡 核心心法:分散化的收益来自「低相关」资产——把不相关甚至负相关的资产组合在一起,组合方差比单只资产方差加权平均还小,这就是「免费午餐」。协方差矩阵就是量化这种联动的工具。估得越准,优化的「免费午餐」越扎实。

二、源码逐行解析

打开 pypfopt/risk_models.py,定位 sample_cov(源码 172-203 行):

def sample_cov(prices, returns_data=False, frequency=252, log_returns=False, **kwargs): if not isinstance(prices, pd.DataFrame): warnings.warn("data is not in a dataframe", RuntimeWarning) prices = pd.DataFrame(prices) if returns_data: returns = prices else: returns = returns_from_prices(prices, log_returns) return fix_nonpositive_semidefinite( returns.cov() * frequency, kwargs.get("fix_method", "spectral") )

简洁明了:

  1. 类型保护:非 DataFrame 转 DataFrame + 警告。
  2. 收益转换:returns_data=False(默认)时调用 returns_from_prices 算日度收益。
  3. 样本协方差:returns.cov()——pandas 内置方法,直接算 N×N 样本协方差矩阵(用样本均值,无偏估计的 (T-1) 分母)。
  4. 年化:* frequency,日度方差乘 252 = 年化方差(协方差同理,因为日度收益近似独立)。
  5. PSD 修正:把结果交给 fix_nonpositive_semidefinite 检查并修复——见下节。

三、PSD 修正机制

协方差矩阵必须是半正定(positive semidefinite, PSD)的——否则 wᵀ Σ w 可能为负,组合方差失去意义,且优化器求逆/Cholesky 分解会失败。

但数值上,样本协方差有时不满足 PSD(尤其资产数 N 接近样本数 T 时)。PyPortfolioOpt 用 fix_nonpositive_semidefinite 兜底,提供两种修正方式:

def fix_nonpositive_semidefinite(matrix, fix_method="spectral"): if _is_positive_semidefinite(matrix): return matrix warnings.warn("The covariance matrix is non positive semidefinite. Amending eigenvalues.") q, V = np.linalg.eigh(matrix) if fix_method == "spectral": q = np.where(q > 0, q, 0) # 负特征值置零 fixed_matrix = V @ np.diag(q) @ V.T # 重构 elif fix_method == "diag": min_eig = np.min(q) fixed_matrix = matrix - 1.1 * min_eig * np.eye(len(matrix)) # 加常数到对角 ...
方法 做法 适用
spectral(默认) 特征值分解,负特征值置零后重构 通用,保秩最小
diag 对角加一个小正数(负特征值绝对值的 1.1 倍) 简单,但改变对角方差

💡 PSD 检测原理:代码里 _is_positive_semidefinite 用 Cholesky 分解尝试,成功即 PSD——这比检查特征值快得多。

四、frequency 与对数收益

参数 默认 含义 备注
prices 价格 DataFrame 必填
returns_data False True 表示输入已是收益 不应是对数收益
frequency 252 年化系数 月度改 12,周度改 52
log_returns False 是否用对数收益算 简单/对数收益数值接近但不等
fix_method "spectral" PSD 修正方法 spectraldiag

⚠️ frequency 必须匹配数据频率:喂日度价格用 252,月度用 12——与 mean_historical_return 一致。年化错误的协方差会让后续优化的「波动率」全错。

五、用法示例

默认调用:

from pypfopt import risk_models S = risk_models.sample_cov(df)

返回 N×N 的 pd.DataFrame,索引与列均为资产代码。UserGuide 给出的示例片段:

GOOG AAPL FB BABA AMZN GE AMD GOOG 0.045529 0.022143 0.006389 0.003720 0.026085 0.015815 0.021761 AAPL 0.022143 0.207037 0.004334 0.002954 0.058200 0.038102 0.084053 FB 0.006389 0.004334 0.029233 0.003770 0.007619 0.003008 0.005804 ...

对角线 GOOG-GOOG = 0.0455 是 GOOG 的年化方差,波动率 sqrt(0.0455) ≈ 21.3%

通过统一入口:

S = risk_models.risk_matrix(df, method="sample_cov")

六、最大的隐患:估计噪声

样本协方差是无偏估计(E(S) = Σ),但在「资产数 N 接近样本数 T」时会严重失真。这是均值-方差优化的著名陷阱。

直觉解释:

  • 样本协方差矩阵有 N(N+1)/2 个独立元素要估。
  • 若 N=100、T=252(一年日度数据),要估 5050 个参数,只用 252 个样本——参数比样本多,矩阵「过拟合」。
  • 极端情况 N > T 时,样本协方差矩阵奇异(秩不足),出现零或负特征值,这就是 PSD 修正被触发的原因。

更糟的是,均值-方差优化器会最大化噪声:

  • 优化器倾向于给「样本估出来方差小、与别人负相关」的资产超大权重。
  • 但这些「最优」特征往往是估计噪声,不是真实结构。
  • 结果:样本内最优,样本外灾难。

⚠️ 官方警告:docs/RiskModels.rstsample_cov 下面明确写:

不应是你的默认选择!请使用收缩估计器代替。

原文用大写强调。原因就是上面的噪声放大问题。

七、何时仍可用样本协方差

尽管不推荐做默认,sample_cov 仍有用武之地:

  1. 样本远多于资产(T ≫ N,如 N=20、T=10 年 = 2520):噪声较小,样本协方差足够。
  2. 教学/快速原型:跑通流程时用最简单的估计。
  3. 作为基准:与收缩估计对比,直观看到「收缩」做了什么——cookbook 2 就是这么用 plotting.plot_covariance 对比两者的热力图。
  4. 喂给收缩估计器:CovarianceShrinkage 内部也先算样本协方差,再向目标收缩。

💡 研究支撑:Kritzman et al. (2010) 等研究表明,最小方差组合(只用 Σ、不喂 μ)样本外常常跑赢最大夏普组合——这进一步说明:若你要简化,优先把 Σ 估好,而不是把 μ 估好。所以第 04 节的收缩估计才是生产默认。

本节要点回顾

  1. 协方差矩阵:N×N 对称矩阵,对角是方差、非对角是协方差;wᵀ Σ w 是组合方差。
  2. 算法:returns.cov() × 252,无偏但噪声大。
  3. PSD 修正:fix_nonpositive_semidefinite 用 spectral(负特征值置零)或 diag(对角加常数)修复非半正定矩阵。
  4. frequency:日度 252、月度 12,必须匹配数据。
  5. 噪声陷阱:资产数 N 接近样本数 T 时严重失真;优化器会最大化噪声成错误权重。
  6. 官方警告:不要做默认,优先用收缩估计(第 04 节);但样本远多于资产、教学、基准对比时仍可用。

下一节,我们看半协方差——一个只惩罚下行偏差、更贴合「风险是亏损」直觉的估计。


发布者: 作者: 灏天文库 转发
评论区 (0)
U