第 3 章 · 02 半协方差


文档摘要

第 3 章 · 02 半协方差 本节摘要:本节讲 提供的下行风险估计—— 。普通协方差把上涨与下跌一视同仁,但投资者的真实痛感是非对称的:下跌亏钱、上涨赚钱,只有下跌才算「风险」。半方差(semivariance)只对低于某个基准(默认无风险利率)的收益算协方差,更贴近「风险即亏损」的直觉。本节拆解它的数学定义(Estrada 2007 的成对 min 形式)、源码实现里的 、benchmark 参数如何选择,以及它与样本协方差的差异。最后指出它常与第 6 章的 配合——后者把半方差当真正的目标而非启发式近似。 内容来源:原项目源码 (函数 )、文档 ,汉化并套用体系化模板。 学习目标 阅读完本节,你应当能够: 解释半方差的直觉与「风险即亏损」的非对称视角。

第 3 章 · 02 半协方差

本节摘要:本节讲 risk_models.py 提供的下行风险估计——semicovariance。普通协方差把上涨与下跌一视同仁,但投资者的真实痛感是非对称的:下跌亏钱、上涨赚钱,只有下跌才算「风险」。半方差(semivariance)只对低于某个基准(默认无风险利率)的收益算协方差,更贴近「风险即亏损」的直觉。本节拆解它的数学定义(Estrada 2007 的成对 min 形式)、源码实现里的 drops = np.fmin(returns - benchmark, 0)、benchmark 参数如何选择,以及它与样本协方差的差异。最后指出它常与第 6 章的 EfficientSemivariance 配合——后者把半方差当真正的目标而非启发式近似。

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

学习目标

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

  1. 解释半方差的直觉与「风险即亏损」的非对称视角。
  2. 写出 Estrada (2007) 的成对 min 半协方差公式。
  3. semicovariance 算半协方差矩阵,理解 benchmark 默认值。
  4. 区分半协方差矩阵与样本协方差在优化中的角色。
  5. 知道它常与 EfficientSemivariance(第 6 章)搭配。

一、为什么要只看下行

普通方差 σ² 把所有偏离均值的幅度一视同仁:涨 5% 和跌 5% 都贡献相同的「风险」。但这与投资者的真实感受相悖:

  • 上涨是好事:赚钱谁嫌多?
  • 下跌才是风险:亏钱才有痛感。

这就是下行风险(downside risk)的动机——只对不利方向的偏离算风险。半方差(semivariance)是其中最经典的度量,定义为「低于基准的收益的方差」。

💡 核心心法:半方差把「上涨波动」从风险里剔除——因为上涨是收益不是风险。这让风险度量更贴合投资者直觉,尤其适合「亏损厌恶」型(比亏损厌恶更甚,见前景理论)的投资者。

二、数学定义

半方差的定义有多种微妙差异,主要在「成对」如何处理。PyPortfolioOpt 采纳 Estrada (2007) 的建议:

semicov_ij = (1/n) Σ_t min(r_i,t - B, 0) × min(r_j,t - B, 0)

注意是 min(r_i - B, 0) × min(r_j - B, 0),而不是 min(r_i × r_j, B)——前者分别对每只资产做下行截断,再相乘。docs/RiskModels.rst 的原文公式:

(1/n) Σ_i Σ_j min(r_i, B) min(r_j, B)

直观理解:只有当两只资产同时低于基准时,才贡献正的半协方差;只要有一只上涨(贡献 0),该项就为 0。这让半协方差矩阵聚焦「一起跌」的情景——这正是真正的尾部联动风险。

三、源码逐行解析

打开 pypfopt/risk_models.py,定位 semicovariance(源码 206-253 行):

def semicovariance( prices, returns_data=False, benchmark=0.000079, frequency=252, log_returns=False, **kwargs, ): ... if returns_data: returns = prices else: returns = returns_from_prices(prices, log_returns) drops = np.fmin(returns - benchmark, 0) T = drops.shape[0] return fix_nonpositive_semidefinite( (drops.T @ drops) / T * frequency, kwargs.get("fix_method", "spectral") )

关键点:

  1. benchmark 默认 0.000079:文档注释说这是「日度无风险利率」,即 1.02^(1/252) - 1 ≈ 7.9e-5(年化 2% 转日度)。低于它的算「下行」。
  2. drops = np.fmin(returns - benchmark, 0):np.fmin 逐元素取「收益减基准」与 0 的较小值——正收益(高于基准)被截断为 0,负收益(低于基准)保留。这就是「下行截断」。
  3. (drops.T @ drops) / T:矩阵乘法 drops.T @ drops 就是成对的 min(r_i-B,0) × min(r_j-B,0) 之和,除以 T 得均值。这正是 Estrada 公式的向量化实现——简洁而高效。
  4. * frequency 年化 + PSD 修正:与 sample_cov 一致。

💡 为何默认 benchmark 是 0.000079 而非 0:用 0 当基准会把「收益为正但低于无风险」的日子也算作「正常」,低估了下行风险。用无风险利率当基准,意味着「跑不赢无风险」就算下行——这更符合「机会成本」的视角。实务中可按你的无风险利率假设调整。

四、参数速查表

参数 默认 含义 备注
prices 价格 DataFrame 必填
returns_data False True 表示输入已是收益 不应是对数收益
benchmark 0.000079 下行基准(日度无风险) 周期须匹配 frequency
frequency 252 年化系数 改 frequency 务必同步改 benchmark
log_returns False 是否用对数收益
fix_method "spectral" PSD 修正方法

⚠️ benchmark 与 frequency 必须配套:日度数据用日度 benchmark(默认 0.000079);若改成月度(frequency=12),benchmark 也要换成月度无风险利率(如年化 2% 转月度约 0.02/12)。不配套会让「下行」的判定门槛错位。

五、用法示例

默认调用:

from pypfopt import risk_models S_semi = risk_models.semicovariance(df)

cookbook 3 里就用它替代样本协方差:

from pypfopt import risk_models, expected_returns mu = expected_returns.capm_return(prices) S = risk_models.semicovariance(prices)

自定义基准(年化 3% 无风险,转日度):

S_semi = risk_models.semicovariance(df, benchmark=0.03/252)

通过统一入口(支持 semicovariancesemivariance 两种别名):

risk_models.risk_matrix(df, method="semicovariance") risk_models.risk_matrix(df, method="semivariance") # 别名

六、半协方差 vs 样本协方差

数值上半协方差矩阵通常小于样本协方差矩阵(因为只计入下行),且结构不同:

维度 样本协方差 半协方差
上行波动 计入 不计入
下行波动 计入 计入
「一起涨」的联动 计入 不计入
「一起跌」的联动 计入 计入(聚焦)
投资者痛感贴合度

💡 关键差异:半协方差矩阵不是「样本协方差的一半」。它结构不同——「一起涨」的资产对在半协方差里贡献为 0,但在样本协方差里贡献为正。这意味着优化器在两种矩阵下会选出不同的组合:用半协方差的组合更侧重规避「一起跌」的资产。

七、与 EfficientSemivariance 的关系

semicovariance 函数返回的矩阵可以直接喂给 EfficientFrontier,当作普通协方差用:

ef = EfficientFrontier(mu, S_semi) ef.max_sharpe()

docs/GeneralEfficientFrontier.rst 指出,这只是启发式近似——把半协方差当普通协方差做均值-方差优化,并不能得到真正在「均值-半方差空间」上有效的组合。

要得到精确的均值-半方差最优组合,应用第 6 章的 EfficientSemivariance 类——它把半方差写成凸问题(引入 2T+N 个变量),直接在均值-半方差前沿上求解。两者关系:

方法 实现 精度 成本
semicovariance + EfficientFrontier 启发式近似 近似
EfficientSemivariance 凸问题精确求解 精确 高(变量多)

💡 何时用哪个:资产数少、追求快速原型,用 semicovariance + EfficientFrontier 足够;严肃的下行风险优化,用 EfficientSemivariance(第 6 章)。

本节要点回顾

  1. 直觉:只对低于基准的收益算协方差,聚焦「一起跌」的尾部联动——更贴投资者痛感。
  2. 公式(Estrada 2007):(1/n) Σ min(r_i - B, 0) × min(r_j - B, 0)
  3. 源码关键:drops = np.fmin(returns - benchmark, 0),再 drops.T @ drops / T × frequency
  4. 默认 benchmark = 0.000079:日度无风险利率(年化 2% 转日度);改 frequency 必须同步改 benchmark。
  5. 不是样本协方差的一半:结构不同,优化结果也不同。
  6. 与 EfficientSemivariance:前者启发式近似,后者凸问题精确求解(第 6 章)。

下一节,我们看指数协方差——把指数加权思想用到协方差上,对近期联动更敏感。


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