第 8 章 · 01 层次风险平价 HRP


文档摘要

第 8 章 · 01 层次风险平价 HRP 本节摘要:均值-方差、BL 都依赖协方差可逆,但金融协方差矩阵常常病态(资产高度相关导致近奇异),求逆放大噪声,权重剧烈跳变。Marcos López de Prado 提出的层次风险平价(Hierarchical Risk Parity,HRP)彻底绕开求逆:用相关性算距离矩阵,做层次聚类把相似资产分组,再按树递归地分配风险。HRP 不需要协方差可逆、不需要收益估计,对噪声鲁棒,在样本外表现稳定。本节讲清 类的四步算法——相关性→距离→聚类→递归分配,并与 cookbook 5 配套。读完本节,你能用 HRP 出权重并画 dendrogram 解释聚类结构。 内容来源:源码 、文档 、cookbook ,汉化并套用体系化模板。

第 8 章 · 01 层次风险平价 HRP

本节摘要:均值-方差、BL 都依赖协方差可逆,但金融协方差矩阵常常病态(资产高度相关导致近奇异),求逆放大噪声,权重剧烈跳变。Marcos López de Prado 提出的层次风险平价(Hierarchical Risk Parity,HRP)彻底绕开求逆:用相关性算距离矩阵,做层次聚类把相似资产分组,再按树递归地分配风险。HRP 不需要协方差可逆、不需要收益估计,对噪声鲁棒,在样本外表现稳定。本节讲清 HRPOpt 类的四步算法——相关性→距离→聚类→递归分配,并与 cookbook 5 配套。读完本节,你能用 HRP 出权重并画 dendrogram 解释聚类结构。

内容来源:源码 pypfopt/hierarchical_portfolio.py、文档 docs/OtherOptimizers.rst、cookbook 5-Hierarchical-Risk-Parity.ipynb,汉化并套用体系化模板。

学习目标

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

  1. HRPOpt 出权重,只需历史收益或协方差。
  2. 说清 HRP 的四步算法(相关性→距离→聚类→递归分配)。
  3. 理解为什么 HRP 不需要协方差可逆
  4. plot_dendrogram 可视化资产聚类结构。
  5. 区分 HRP 与均值-方差的适用场景

一、为什么需要 HRP:协方差求逆的痛

均值-方差优化的解析解 w ∝ Σ⁻¹μ 依赖 Σ 可逆。但金融场景下 Σ 经常病态:

  • 资产高度相关(同行业、同地区),Σ 接近奇异。
  • 资产数 > 样本数时,Σ 不可逆。
  • 微小扰动 → Σ⁻¹ 剧烈变化 → 权重跳变。

HRP 的洞见:根本不求逆。改用聚类 + 递归分配,从结构上分散风险,数学上对噪声鲁棒。

💡 核心心法:HRP 不算「最优」权重,它算「结构上分散」的权重。不追求理论最优,换取工程鲁棒——尤其在资产多、样本少、相关性高时,这恰是均值-方差最脆弱的场景。

二、HRP 四步算法

源码 HRPOpt.optimize 把算法拆得很清楚:

def optimize(self, linkage_method="single"): if self.returns is None: cov = self.cov_matrix corr = cov_to_corr(self.cov_matrix).round(6) else: corr, cov = self.returns.corr(), self.returns.cov() # Step 1: 距离矩阵 matrix = np.sqrt(np.clip((1.0 - corr) / 2.0, a_min=0.0, a_max=1.0)) dist = ssd.squareform(matrix, checks=False) # Step 2: 层次聚类 self.clusters = sch.linkage(dist, linkage_method) # Step 3: 准对角化(按聚类顺序排叶节点) sort_ix = HRPOpt._get_quasi_diag(self.clusters) ordered_tickers = corr.index[sort_ix].tolist() # Step 4: 递归分配 hrp = HRPOpt._raw_hrp_allocation(cov, ordered_tickers) weights = collections.OrderedDict(hrp.sort_index()) self.set_weights(weights) return weights

Step 1:从相关性到距离

相关性 0~1 转成「距离」:d = √((1-corr)/2)。完全正相关 → d=0(无法区分);完全负相关 → d=1(最远)。开根号是为了满足三角不等式。

Step 2:层次聚类

用 scipy 的 sch.linkage 默认 single 链接法,自底向上把相似资产合并成一棵树。高度相关的资产聚到同一簇(如 AMD 和 NVDA 同属半导体)。

Step 3:准对角化

sch.to_tree(link).pre_order() 把叶节点按聚类结构排序——相似的资产相邻。这一步把协方差矩阵重排成准对角形态,让相似的资产在矩阵中聚成块。

Step 4:递归二分分配

源码 _raw_hrp_allocation 是 HRP 的灵魂——从顶部开始,把当前簇对半切成两子簇,按「逆方差组合」的方差比分配权重:

@staticmethod def _raw_hrp_allocation(cov, ordered_tickers): w = pd.Series(1.0, index=ordered_tickers) cluster_items = [ordered_tickers] while len(cluster_items) > 0: cluster_items = [...] # 二分 for i in range(0, len(cluster_items), 2): first_cluster = cluster_items[i] second_cluster = cluster_items[i + 1] first_variance = HRPOpt._get_cluster_var(cov, first_cluster) second_variance = HRPOpt._get_cluster_var(cov, second_cluster) alpha = 1 - first_variance / (first_variance + second_variance) w[first_cluster] *= alpha # 方差小的簇拿更多 w[second_cluster] *= 1 - alpha return w

_get_cluster_var 用簇内逆方差权重算组合方差(等价于簇内的最小方差组合):

@staticmethod def _get_cluster_var(cov, cluster_items): cov_slice = cov.loc[cluster_items, cluster_items] weights = 1 / np.diag(cov_slice) # 逆方差 weights /= weights.sum() return np.linalg.multi_dot((weights, cov_slice, weights))

💡 直觉:方差小的子簇「更稳」,分到更多权重;方差大的子簇「更险」,少给。沿树递归到底,叶节点(单个资产)的权重就出来了。整个过程只求簇内方差,从不求 Σ⁻¹

三、HRPOpt 的两种用法

HRP 接受两种输入:历史收益(推荐)或协方差矩阵。

用法一:历史收益(推荐)

from pypfopt import HRPOpt, expected_returns returns = expected_returns.returns_from_prices(prices) hrp = HRPOpt(returns) weights = hrp.optimize() weights = hrp.clean_weights()

用法二:协方差矩阵(无须收益)

hrp = HRPOpt(cov_matrix=S) hrp.optimize()

⚠️ 协方差用法无 returns:此时 portfolio_performance 无法算收益和 Sharpe(源码里 mu=None)。要算收益,还是用历史收益输入。

四、portfolio_performance 与 cookbook 5 完整示例

from pypfopt import HRPOpt, expected_returns, plotting # 准备数据 tickers = ["BLK", "BAC", "AAPL", "TM", "WMT", "JD", "INTU", "MA", "UL", "CVS", "DIS", "AMD", "NVDA", "PBI", "TGT"] prices = yf.download(tickers, period="max")["Close"] rets = expected_returns.returns_from_prices(prices) # HRP 优化 hrp = HRPOpt(rets) hrp.optimize() weights = hrp.clean_weights() pd.Series(weights).plot.pie(figsize=(10, 10)) # 性能 hrp.portfolio_performance(verbose=True) # Expected annual return: 24.5% # Annual volatility: 19.2% # Sharpe Ratio: 1.27

注意 portfolio_performance 的源码——它从 returns 自己算 μ 和 Σ(忽略你外面传的协方差):

def portfolio_performance(self, verbose=False, risk_free_rate=0.0, frequency=252): if self.returns is None: cov = self.cov_matrix mu = None else: cov = self.returns.cov() * frequency mu = self.returns.mean() * frequency return portfolio_performance(self.weights, mu, cov, verbose, risk_free_rate)

五、可视化:plot_dendrogram

HRP 的最大优势之一是可解释性——能画出资产聚类树:

from pypfopt import plotting plotting.plot_dendrogram(hrp)

dendrogram 把每个资产的合并高度画出来:合并越早(高度越低)说明相关性越强。cookbook 5 提到 AMD 和 NVDA(都是半导体)被聚到一起——这种「同行业先聚类」的结构验证了算法的合理性。

💡 dendrogram 的实战价值:不仅用于验证,还可反向——你看着 dendrogram 决定要不要剔除高度相关的冗余资产,精简宇宙。

六、HRP vs 均值-方差

对比维度 均值-方差 HRP
协方差可逆 必须 不需要
收益估计 需要 μ 不需要
算法 凸优化 聚类+递归
约束/目标 灵活(add_constraint) 不支持(全自动化)
样本外 不稳定 稳定
可解释 (dendrogram)
适用 资产少、数据多 资产多、相关性高、数据少

⚠️ HRP 的代价:全自动化,不支持 add_constraint / add_objective,无法加 sector 约束、无法做市场中性。你需要精确约束时,HRP 不是首选。

本节要点回顾

  1. HRP 是「绕开求逆」的优化器:针对协方差病态/不可逆场景,对噪声鲁棒,样本外稳定。
  2. 四步算法:相关性→距离 d=√((1-corr)/2)→层次聚类→准对角化→递归二分分配。
  3. 递归分配:沿聚类树自顶向下,按子簇方差比 alpha=1-σ²₁/(σ²₁+σ²₂) 分权重,只算簇内逆方差组合方差,不求 Σ⁻¹。
  4. 两种输入:历史收益(推荐,能算 Sharpe)或协方差矩阵。
  5. 可视化:plotting.plot_dendrogram(hrp) 画聚类树,验证资产分组合理性。
  6. 代价:不支持约束和目标函数,适合「全自动分散风险」,不适合需要 sector/市场中性约束的场景。

下一节,我们看 CLA 临界线算法——它不求逆也不聚类,而是精确求解带不等式约束的有效前沿拐点。


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