第 10 章 · 03 层次风险平价 HRP 本节摘要:本节用层次聚类解决资产配置的一个老大难问题——传统 Markowitz 均值-方差优化对输入极度敏感,协方差矩阵稍微动一动,权重就剧烈翻转,样本外表现灾难。López de Prado 提出的层次风险平价(Hierarchical Risk Parity, HRP) 用层次聚类把「相似的资产」分组,只在组内做替代,大幅减少自由度,提升配置稳健性。本节讲清四件事:为什么均值-方差优化在金融上不稳定;HRP 的四步流程(相关性→距离矩阵→层次聚类→准对角化→顶向下逆方差分配);如何用 scipy 的 与 实现;以及 HRP 与传统均值-方差的样本外对比。读完本节,你能用一段不长的 Python 实现一个稳健得多的资产配置方案。
本节摘要:本节用层次聚类解决资产配置的一个老大难问题——传统 Markowitz 均值-方差优化对输入极度敏感,协方差矩阵稍微动一动,权重就剧烈翻转,样本外表现灾难。López de Prado 提出的层次风险平价(Hierarchical Risk Parity, HRP) 用层次聚类把「相似的资产」分组,只在组内做替代,大幅减少自由度,提升配置稳健性。本节讲清四件事:为什么均值-方差优化在金融上不稳定;HRP 的四步流程(相关性→距离矩阵→层次聚类→准对角化→顶向下逆方差分配);如何用 scipy 的
linkage与quasi_diagonalize实现;以及 HRP 与传统均值-方差的样本外对比。读完本节,你能用一段不长的 Python 实现一个稳健得多的资产配置方案。
内容来源:原项目
13_unsupervised_learning/04_hierarchical_risk_parity/01_hierarchical_risk_parity.ipynb,汉化并套用体系化模板。
⚠️ 学习提示:HRP 解决的是「权重稳定性」,不是「收益预测」。它不预测收益,只用协方差结构分配风险——这恰恰是它的稳健来源,因为收益的预测比波动难得多。
阅读完本节,你应当能够:
linkage 做层次聚类并 quasi_diagonalize 重排。Markowitz 的均值-方差优化数学优美:给定预期收益与协方差,求一组权重在既定风险下收益最大。问题是它的输入敏感性:协方差矩阵的轻微估计误差,会让最优权重剧烈变化——常常给出极端的多空大杠杆组合,样本外一塌糊涂。
根因在于:均值-方差把所有资产视为「完全可替代」——任何两只都能在优化里互相取代,所以协方差轻微变化就触发大调仓。López de Prado 的洞察是:资产之间其实有层次结构(同行业更相似,跨行业更不像),只在「相似」的资产间互相替代才合理。
HRP 用层次聚类把相似的资产聚到一起,然后只在同一簇内做替代,大幅压缩自由度。整个流程围绕协方差矩阵的结构性,而非具体数值,所以对估计误差稳健。
💡 核心心法:HRP 不预测收益,也不算全局最优——它承认「协方差估计本来就不准」,所以放弃求最优,转而求稳健。它的分配逻辑是「按层次结构分摊风险」,有点像「先把风险预算分给大类资产,再在大类内分给具体资产」的直觉,但是用算法自动化做的。
def get_distance_matrix(corr): """从相关系数矩阵算距离,保证 0 <= d[i,j] <= 1""" return np.sqrt((1 - corr) / 2) cov = monthly_returns.cov() corr = monthly_returns.corr() distance_matrix = get_distance_matrix(corr)
相关性 1(完全相关)→ 距离 0;相关性 -1(完全负相关)→ 距离 1。这就把「相关系数」转成符合距离定义的形式,scipy 才能聚类。
from scipy.cluster.hierarchy import linkage from scipy.spatial.distance import pdist, squareform linkage_matrix = linkage(squareform(distance_matrix), 'single')
linkage 函数对距离矩阵做凝聚层次聚类——初始每点一簇,每步合并最近的两簇,直到全部合为一簇。'single' 表示用「簇间最近邻距离」作为合并依据,还有 'complete'、'ward' 等其他 linkage 策略。
sorted_idx = quasi_diagonalize(linkage_matrix) sorted_tickers = corr.index[sorted_idx].tolist()
quasi_diagonalize 沿聚类树递归展开叶子节点,把资产重排,使得相似的资产相邻。重排后的协方差矩阵近似对角阵——相似资产(高相关)聚在主对角线附近,形成块状结构。这一步是 HRP 的精髓,它把「聚类结构」转化成「矩阵块结构」。
可视化对比:
fig, axes = plt.subplots(ncols=2, figsize=(18, 8)) sns.heatmap(corr, center=0, cmap=cmap, ax=axes[0]) axes[0].set_title('Correlation Matrix 原始') sns.heatmap(corr.loc[sorted_tickers, sorted_tickers], center=0, cmap=cmap, ax=axes[1]) axes[1].set_title('Clustered Correlation 准对角化')
左边原始矩阵杂乱无章,右边准对角化后呈现明显的块状对角结构——这就是「层次结构」的可视化。
def get_inverse_var_pf(cov): """逆方差组合:波动小的资产给更多权重""" ivp = 1 / np.diag(cov) return ivp / ivp.sum() def get_cluster_var(cov, cluster_items): """计算簇内组合方差""" cov_ = cov.loc[cluster_items, cluster_items] w_ = get_inverse_var_pf(cov_) return (w_ @ cov_ @ w_).item()
分配逻辑是递归二分:
这套「自顶向下 + 逆方差」的分配,在层次结构上做风险平价——相似资产先在簇内分摊风险,簇之间再按整体风险反比分摊。它不需要计算全局最优,也不依赖点估计的精确性。
notebook 用 1988-2017 的 S&P500 月度收益:
with pd.HDFStore('../../data/assets.h5') as store: sp500_stocks = store['sp500/stocks'].index prices = store['quandl/wiki/prices'].adj_close.unstack('ticker').filter(sp500_stocks) monthly_returns = (prices.loc['1988':'2017'] .resample('M').last().pct_change() .dropna(how='all').dropna(axis=1))
实证对比的关键是「样本外稳健性」:把样本期切成多段,每段用前一段估协方差算权重,在下一段评估。Markowitz 的权重在不同段差异巨大且常含极端杠杆,HRP 的权重则平滑得多,样本外夏普通常更稳定(不一定是最高,但稳健)。
| 维度 | 均值-方差 | HRP |
|---|---|---|
| 输入敏感性 | 极高 | 低 |
| 极端权重 | 常见 | 罕见 |
| 样本外稳健性 | 差 | 好 |
| 收益预测需求 | 需要预测收益 | 只用协方差 |
| 计算复杂度 | 二次规划 | 层次聚类 + 递归 |
HRP 之后,Raffinot(2016)提出了层次聚类资产分配(HCAA),把同样的层次思想扩展到更多分配准则。HRP 的核心洞察——「相似资产才互相替代,从而压缩自由度」——也启发了后续一系列「基于聚类的稳健配置」方法。这些方法都不追求理论最优,而在「输入本就不准」的现实里追求稳健。
⚠️ HRP 的局限:它只用历史协方差,不预测未来。如果未来协方差结构本身发生剧变(如危机期相关性齐涨),HRP 也会失灵。任何基于历史数据的配置方法都难逃这个局限——HRP 只是相对稳健,不是万能。
d = sqrt((1-corr)/2) 把相关系数转成合法距离。下一节,我们进入深度学习——从前馈网络 FNN 开始,用 TensorFlow/Keras 搭一个能预测资产收益的深度网络。