第 3 章 · 05 最小行列式 本节摘要:本节是「风险模型」的最后一节,讲 的鲁棒协方差估计—— (MCD,Minimum Covariance Determinant)。前面几种估计都假设数据「干净」,但真实金融数据常有异常值(除权跳空、数据错误、极端行情),它们会严重污染样本均值与样本协方差。MCD 由 Rousseeuw 1985 提出,核心思想是:在所有数据点里找出一个协方差行列式最小的子集(默认占总数约 75%),对这个「干净」子集算均值与协方差,从而抗异常值。PyPortfolioOpt 把它封装在 sklearn 的 之上。本节拆解 MCD 的算法直觉、源码实现,以及一个重要提醒:此函数已弃用,v1.5 将移除。
本节摘要:本节是「风险模型」的最后一节,讲
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、sklearnfast_mcd文档,汉化并套用体系化模板。
阅读完本节,你应当能够:
min_cov_determinant 算鲁棒协方差。fast_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"))
关键点:
UserWarning——min_cov_determinant is deprecated and will be removed in v1.5。这是重要信号:不要在新代码里依赖它。ImportError。from sklearn.covariance import fast_mcd:用 sklearn 的 FastMCD 算法(Rousseeuw 与 Van Driessen 1999 的快速实现),它用 C 算法在多项式时间内近似求解(精确解是 NP 难的)。fast_mcd(X)[1]:fast_mcd 返回 (location, covariance) 元组,[1] 取协方差矩阵。location(鲁棒均值)在此处被丢弃。* 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 |
|---|---|---|---|
| 抗异常值 | 弱 | 中(结构先验缓冲) | 强(主动剔除) |
| 偏差-方差 | 高方差 | 平衡 | 偏差换方差 |
| 计算成本 | 低 | 中 | 高(FastMCD 迭代) |
| 随机性 | 无 | 无 | 有(需 random_state) |
| 在库里的地位 | 基线 | 生产默认 | 已弃用 |
MCD 在「数据确实有异常值」的场景下理论很优,但 PyPortfolioOpt 把它列为已弃用,主要原因推测:
💡 替代方案:若你担心异常值污染,优先做:
- 数据清洗:用
expected_returns._check_returns检查 NaN/Inf,手动剔除极端值。- 收缩估计:Ledoit-Wolf 的结构先验天然对异常值有缓冲。
- HRP(第 8 章):层次风险平价绕开协方差矩阵的精确估计,对异常值更鲁棒。
- 直接用 sklearn:若确需 MCD,可绕开 PyPortfolioOpt 直接用
sklearn.covariance.MinCovDet,再把结果当普通协方差喂给EfficientFrontier。
总结:MCD 在 PyPortfolioOpt 里是「理论上有,实务上弃」的状态。理解它的抗异常值思想有助于你看懂其他鲁棒统计文献,但新代码请用收缩估计 + 数据清洗的组合方案。
det(Σ) = 协方差椭球体积,综合度量所有方向离散度。fast_mcd,取返回的协方差([1]),年化 + PSD 修正。random_state 可固定随机种子复现。risk_matrix 不派发 MCD,必须直接调函数。至此,风险模型五节讲完。下一章我们进入优化核心——
EfficientFrontier类与五种均值-方差目标。