第 3 章 · 05 最小行列式


文档摘要

第 3 章 · 05 最小行列式 本节摘要:本节是「风险模型」的最后一节,讲 的鲁棒协方差估计—— (MCD,Minimum Covariance Determinant)。前面几种估计都假设数据「干净」,但真实金融数据常有异常值(除权跳空、数据错误、极端行情),它们会严重污染样本均值与样本协方差。MCD 由 Rousseeuw 1985 提出,核心思想是:在所有数据点里找出一个协方差行列式最小的子集(默认占总数约 75%),对这个「干净」子集算均值与协方差,从而抗异常值。PyPortfolioOpt 把它封装在 sklearn 的 之上。本节拆解 MCD 的算法直觉、源码实现,以及一个重要提醒:此函数已弃用,v1.5 将移除。

第 3 章 · 05 最小行列式

本节摘要:本节是「风险模型」的最后一节,讲 risk_models.py 的鲁棒协方差估计——min_cov_determinant(MCD,Minimum Covariance Determinant)。前面几种估计都假设数据「干净」,但真实金融数据常有异常值(除权跳空、数据错误、极端行情),它们会严重污染样本均值与样本协方差。MCD 由 Rousseeuw 1985 提出,核心思想是:在所有数据点里找出一个协方差行列式最小的子集(默认占总数约 75%),对这个「干净」子集算均值与协方差,从而抗异常值。PyPortfolioOpt 把它封装在 sklearn 的 fast_mcd 之上。本节拆解 MCD 的算法直觉、源码实现,以及一个重要提醒:此函数已弃用,v1.5 将移除。

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

学习目标

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

  1. 解释 MCD 的抗异常值直觉:在「干净子集」上算协方差。
  2. 描述行列式最小的几何含义。
  3. min_cov_determinant 算鲁棒协方差。
  4. 知道它依赖 scikit-learnfast_mcd
  5. 注意它已弃用,v1.5 将移除——了解替代方案。

一、为什么要鲁棒估计

样本均值与样本协方差都是非鲁棒估计——单个极端异常值就能把均值拉偏、把方差放大。金融数据里的异常值来源:

  • 数据错误:停牌日误填、复权因子算错。
  • 除权跳空:未正确复权造成的虚假跳变。
  • 极端行情:1987 黑色星期一、2008 雷曼、2020 疫情——单日 10%+ 的极端收益。

普通样本协方差对这些异常值毫无防御,优化器会把它们当成信号放大。

二、MCD 的算法直觉

MCD(Minimum Covariance Determinant)由 Rousseeuw 1985 提出,核心思想:

在 N 个数据点里,找出一个大小为 h(约 0.75×N)的子集,使得这个子集的协方差矩阵的行列式最小。对这个子集算均值与协方差,就是鲁棒估计。

为什么用行列式?行列式 det(Σ) 等于协方差椭球的体积——它综合度量了所有方向的离散程度。找「行列式最小」的子集,等价于找「最紧凑的一群点」——它们自然就是「正常」数据点,排除了离群的异常值。

💡 几何直觉:把数据画在 N 维空间里,正常点聚成一个椭球,异常点散在远处。MCD 找的就是「最紧凑的那个椭球」所对应的子集——椭球体积(行列式)最小,意味着这群点离散度最小,最「干净」。

三、源码逐行解析

打开 pypfopt/risk_models.py,定位 min_cov_determinant(源码 330-363 行):

def min_cov_determinant( prices, returns_data=False, frequency=252, random_state=None, log_returns=False, **kwargs, ): # pragma: no cover warnings.warn("min_cov_determinant is deprecated and will be removed in v1.5") if not isinstance(prices, pd.DataFrame): warnings.warn("data is not in a dataframe", RuntimeWarning) prices = pd.DataFrame(prices) if not _check_soft_dependencies(["scikit-learn"], severity="none"): raise ImportError( "scikit-learn is required to use min_cov_determinant. ..." ) from sklearn.covariance import fast_mcd assets = prices.columns if returns_data: X = prices else: X = returns_from_prices(prices, log_returns) X = X.dropna().values raw_cov_array = fast_mcd(X, random_state=random_state)[1] cov = pd.DataFrame(raw_cov_array, index=assets, columns=assets) * frequency return fix_nonpositive_semidefinite(cov, kwargs.get("fix_method", "spectral"))

关键点:

  1. 弃用警告:函数开头第一行就发 UserWarning——min_cov_determinant is deprecated and will be removed in v1.5。这是重要信号:不要在新代码里依赖它。
  2. 依赖检查:需要 scikit-learn,缺失则抛 ImportError
  3. from sklearn.covariance import fast_mcd:用 sklearn 的 FastMCD 算法(Rousseeuw 与 Van Driessen 1999 的快速实现),它用 C 算法在多项式时间内近似求解(精确解是 NP 难的)。
  4. fast_mcd(X)[1]:fast_mcd 返回 (location, covariance) 元组,[1] 取协方差矩阵。location(鲁棒均值)在此处被丢弃。
  5. * frequency 年化 + PSD 修正:与其他风险模型一致。

⚠️ # pragma: no cover:源码里这个注释表示「此函数不在测试覆盖率统计内」——因为它依赖 sklearn 的随机算法,行为难以确定性测试。这也间接说明它在 PyPortfolioOpt 里是「次要」功能。

四、参数速查表

参数 默认 含义 备注
prices 价格 DataFrame 必填
returns_data False True 表示输入已是收益
frequency 252 年化系数 月度改 12
random_state None 随机种子 设定可复现
log_returns False 是否用对数收益
fix_method "spectral" PSD 修正方法

五、用法示例

默认调用(会发弃用警告):

from pypfopt import risk_models S_mcd = risk_models.min_cov_determinant(df)

设定随机种子可复现:

S_mcd = risk_models.min_cov_determinant(df, random_state=42)

直接喂优化器:

from pypfopt import EfficientFrontier, expected_returns mu = expected_returns.capm_return(df) S = risk_models.min_cov_determinant(df, random_state=42) ef = EfficientFrontier(mu, S) ef.max_sharpe()

⚠️ 注意:risk_matrix 统一入口不支持 min_cov_determinant——查 risk_matrix 源码的 method 派发列表,只覆盖 sample_cov、semicovariance、exp_cov、ledoit_wolf*、oracle_approximating,没有 MCD。要调 MCD 必须直接调 min_cov_determinant 函数。

六、MCD vs 其他风险模型

维度 样本协方差 收缩估计 MCD
抗异常值 中(结构先验缓冲) 强(主动剔除)
偏差-方差 高方差 平衡 偏差换方差
计算成本 高(FastMCD 迭代)
随机性 有(需 random_state)
在库里的地位 基线 生产默认 已弃用

MCD 在「数据确实有异常值」的场景下理论很优,但 PyPortfolioOpt 把它列为已弃用,主要原因推测:

  1. FastMCD 计算开销大:N 大时迭代慢,且每次结果因随机种子不同。
  2. 清洗数据更直接:与其用鲁棒估计兜底,不如在数据预处理阶段把异常值识别并修正/剔除。
  3. 收缩估计已足够:Ledoit-Wolf 在多数场景下已足够稳健,MCD 的边际收益有限。

💡 替代方案:若你担心异常值污染,优先做:

  • 数据清洗:用 expected_returns._check_returns 检查 NaN/Inf,手动剔除极端值。
  • 收缩估计:Ledoit-Wolf 的结构先验天然对异常值有缓冲。
  • HRP(第 8 章):层次风险平价绕开协方差矩阵的精确估计,对异常值更鲁棒。
  • 直接用 sklearn:若确需 MCD,可绕开 PyPortfolioOpt 直接用 sklearn.covariance.MinCovDet,再把结果当普通协方差喂给 EfficientFrontier

七、实务建议

总结:MCD 在 PyPortfolioOpt 里是「理论上有,实务上弃」的状态。理解它的抗异常值思想有助于你看懂其他鲁棒统计文献,但新代码请用收缩估计 + 数据清洗的组合方案。

本节要点回顾

  1. MCD 直觉:在所有数据点里找协方差行列式最小(=椭球体积最小)的子集,在「干净子集」上算协方差,抗异常值。
  2. 行列式含义:det(Σ) = 协方差椭球体积,综合度量所有方向离散度。
  3. 实现:封装 sklearn 的 fast_mcd,取返回的协方差([1]),年化 + PSD 修正。
  4. 依赖:需要 scikit-learn;random_state 可固定随机种子复现。
  5. 不在统一入口:risk_matrix 不派发 MCD,必须直接调函数。
  6. 已弃用:v1.5 将移除——新代码请用收缩估计 + 数据清洗,或第 8 章 HRP。

至此,风险模型五节讲完。下一章我们进入优化核心——EfficientFrontier 类与五种均值-方差目标。


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