Hierarchical Risk Parity (HRP) for Portfolio Optimization
Cluster-based portfolio allocation using hierarchical clustering and graph theory. HRP avoids covariance inversion for more stable diversification.
This paper presents Hierarchical Risk Parity (HRP), a portfolio allocation method that uses hierarchical clustering to structure asset weights without inverting the covariance matrix. By transforming correlations into distance metrics and applying recursive bisection, HRP produces more stable allocations than mean-variance optimization, particularly in high-dimensional or regime-shifting environments. We provide a complete Python implementation and discuss practical advantages over classical approaches.
Key Takeaways
- HRP avoids covariance matrix inversion, eliminating the source of instability that plagues mean-variance optimization in small samples and high dimensions.
- Correlation is converted to a distance metric, and hierarchical clustering groups similar assets before allocating weights via recursive bisection.
- HRP tends to behave well when traditional optimizers overreact to noisy means and covariances, making it practical for real portfolio construction.
- The method treats dependence structure as an object worth modeling directly, which becomes valuable when correlations are unstable.
Introduction
Classical portfolio theory is elegant, but in practical quant workflows it often breaks where the algebra looks strongest. Mean-variance optimization requires estimating expected returns and inverting the covariance matrix. In small samples, high dimensions, or unstable regimes, that process becomes fragile. Tiny changes in input can produce violent changes in weights.
Hierarchical Risk Parity (HRP) avoids direct covariance inversion and uses hierarchical clustering to structure allocation. Assets are not independent points in space — they form dependency clusters: banks, semiconductors, sovereign bonds, energy names, or factor-like groups.
HRP first measures similarity using correlation, then transforms that into a distance metric:
Once the hierarchy is built via clustering, HRP applies two steps: quasi-diagonalization (reorder the covariance matrix so similar assets are adjacent) and recursive bisection. If two clusters have variances \(\sigma_L^2\) and \(\sigma_R^2\), the left cluster receives weight:
Python Implementation
import numpy as np import pandas as pd from scipy.cluster.hierarchy import linkage, leaves_list from scipy.spatial.distance import squareform def correl_dist(corr): return np.sqrt((1 - corr) / 2) def get_cluster_var(cov, cluster_items): sub_cov = cov.loc[cluster_items, cluster_items] ivp = 1 / np.diag(sub_cov) ivp = ivp / ivp.sum() return np.dot(ivp, np.dot(sub_cov, ivp)) def hrp_allocation(returns: pd.DataFrame) -> pd.Series: cov = returns.cov() corr = returns.corr() dist = correl_dist(corr) link = linkage(squareform(dist.values, checks=False), method="single") sort_ix = corr.index[leaves_list(link)] weights = pd.Series(1.0, index=sort_ix) clusters = [list(sort_ix)] while clusters: cluster = clusters.pop(0) if len(cluster) <= 1: continue split = len(cluster) // 2 left, right = cluster[:split], cluster[split:] var_left = get_cluster_var(cov, left) var_right = get_cluster_var(cov, right) alpha = 1 - var_left / (var_left + var_right) weights[left] *= alpha weights[right] *= (1 - alpha) clusters.extend([left, right]) return weights / weights.sum()
HRP's advantage is that it treats dependence structure as an object worth modeling directly. That becomes valuable when correlations are unstable, samples are short, and optimization error matters more than elegant closed forms. It tends to behave well when traditional optimizers overreact to noisy means and covariances.
Assumptions
- Correlation carries the structure worth using. HRP builds its tree from a correlation distance and never estimates expected returns. If you hold genuine return forecasts, discarding them is a real cost, not a free simplification.
- The hierarchy is stable enough to be worth respecting. The tree is estimated from the same noisy sample it is meant to protect against.
- Weights are held fixed out of sample. The comparison below fits weights in sample and does not rebalance, which isolates estimation error from rebalancing effects.
- Long-only by construction. Recursive bisection splits a positive budget, so HRP cannot short. Mean-variance can, and does.
Measured out-of-sample behaviour
Output of compare_mvo.py in the published package: 20 assets,
120 in-sample periods, 400 out-of-sample periods, synthetic data from a fixed
seed. Mean absolute correlation 0.240; covariance condition number 43, high
enough that inversion is visibly unstable.
| Allocator | Vol in | Vol out | Drift % | Max weight | Short | Effective N |
|---|---|---|---|---|---|---|
| HRP (single) | 0.1401 | 0.1419 | +1.3 | 0.106 | 0.000 | 16.29 |
| HRP (ward) | 0.1419 | 0.1425 | +0.4 | 0.110 | 0.000 | 16.73 |
| MinVar | 0.1311 | 0.1489 | +13.6 | 0.229 | -0.112 | 7.37 |
| MinVar long-only | 0.1325 | 0.1460 | +10.2 | 0.206 | 0.000 | 9.54 |
| MinVar shrunk 0.3 | 0.1330 | 0.1432 | +7.7 | 0.169 | -0.019 | 11.78 |
| Equal weight | 0.1474 | 0.1443 | -2.1 | 0.050 | 0.000 | 20.00 |
The finding is drift, not outperformance. Minimum variance achieves the lowest in-sample volatility — it is solving for exactly that — and then gives most of it back: +13.6% out of sample against HRP's +1.3%. The in-sample number is a promise the out-of-sample number does not keep.
One synthetic panel, one seed. Out-of-sample Sharpe was negative for every allocator on this panel, which is why none is quoted as a performance result. This illustrates an estimation-error mechanism; it is not evidence that HRP beats mean-variance in general.
Robustness: what would change the conclusion
Shrinkage closes most of the gap. A Ledoit-Wolf-style shrunk covariance takes minimum variance from +13.6% drift to +7.7%. Comparing HRP against an unconstrained, unshrunk optimiser overstates its advantage, and the shrunk row is included here for exactly that reason.
Equal weight is not embarrassed. The 1/N portfolio drifts -2.1% and is the most diversified allocator in the table. DeMiguel, Garlappi and Uppal (2009) is the standing warning that sophistication has to earn its place against it.
Linkage is a modelling choice. Single and Ward linkage produce different trees and different weights (+1.3% vs +0.4% here). The choice should be disclosed with the result.
One seed is one seed. These numbers come from a single synthetic panel. Change the seed, the asset count or the correlation structure and the magnitudes move; the direction is the part supported by the wider literature, not this table.
Turnover and transaction costs
Concentration is a cost story as much as a risk story. Minimum variance puts 22.9% in a single asset and holds −11.2% short, with an effective breadth of 7.4 names out of 20; HRP holds a 10.6% maximum, no shorts, and an effective 16.3 names. A concentrated, unstable weight vector is the one that generates large rebalancing trades exactly when correlations move.
The comparison above deliberately holds weights fixed out of sample, so no rebalancing cost is included in any figure in this paper. That flatters every allocator, and it flatters the least stable one most. A realistic assessment would apply the spread plus square-root impact model to the turnover each method generates — work this package does not yet do, and the reason no net-of-cost claim appears here.
Limitations
- Sub-optimal in sample by construction. Mean-variance solves for minimum in-sample variance, so any in-sample comparison favours it automatically. HRP's case rests entirely on out-of-sample behaviour, and only out-of-sample tests are informative.
- No return objective. HRP allocates risk and ignores expected returns. Where genuine return forecasts exist, discarding them is a real cost rather than a free simplification.
- Linkage is a free parameter. Single, average and Ward linkage produce different trees and therefore different weights. The choice is a modelling decision that should be disclosed, not a detail.
- Correlation instability. Correlations converge during stress, flattening the cluster tree exactly when diversification matters most. The hierarchy is estimated from the same noisy data it is meant to protect against.
- Regularised mean-variance is a stronger baseline. Much of textbook mean-variance's instability is addressed by shrinkage estimators, constraints or resampling. Comparisons against an unconstrained optimiser overstate HRP's advantage.
References
- López de Prado, M. (2016). “Building Diversified Portfolios that Outperform Out of Sample.” Journal of Portfolio Management 42(4), 59–69.
- Markowitz, H. (1952). “Portfolio Selection.” Journal of Finance 7(1), 77–91.
- Michaud, R. (1989). “The Markowitz Optimization Enigma: Is Optimized Optimal?.” Financial Analysts Journal 45(1), 31–42.
- Ledoit, O. & Wolf, M. (2004). “A Well-Conditioned Estimator for Large-Dimensional Covariance Matrices.” Journal of Multivariate Analysis 88(2), 365–411.
- DeMiguel, V., Garlappi, L. & Uppal, R. (2009). “Optimal Versus Naive Diversification: How Inefficient is the 1/N Portfolio Strategy?.” Review of Financial Studies 22(5), 1915–1953.
QuantMedia research is independent and not peer reviewed. These references are the primary sources the analysis draws on; readers are encouraged to consult them directly rather than relying on this summary.
Research record
- Author
- Cemil Ertürk · QuantMedia Research
- Published
- January 20, 2026
- Last material revision
- August 16, 2026
- Research version
- 1.1
- Topic
- Portfolio Theory
- Code
- Runnable implementation with tests
- Review status
- Independent research. Not peer reviewed.
How to cite this research
Cemil Ertürk. "Hierarchical Risk Parity (HRP) for Portfolio Optimization." QuantMedia, 2026. https://quantmedia.io/paper-hierarchical-risk-parity.html
BibTeX
@misc{erturk2026hierarchical,
author = {Ert{\"u}rk, Cemil},
title = {Hierarchical Risk Parity (HRP) for Portfolio Optimization},
year = {2026},
howpublished = {QuantMedia},
url = {https://quantmedia.io/paper-hierarchical-risk-parity.html},
note = {Accessed: <date>}
}
Code & Reproducibility
A runnable implementation of the allocator described above is published alongside this paper, together with a mean-variance comparison that measures fixed weights out of sample. 13 tests cover it.
| Implementation | quantmedia-research/hierarchical-risk-parity/hrp.py |
|---|---|
| Comparison | compare_mvo.py — HRP vs min-variance, long-only and shrinkage baselines |
| Example output | outputs/comparison.csv, outputs/weights.csv |
| Tests | tests/test_hrp.py — 13 passing |
| Dependencies | numpy, pandas, scipy. No API key, no network access. |
| Reproduces | Out-of-sample volatility drift of +1.3% for HRP against +13.6% for min-variance, narrowing to +7.7% once the covariance is shrunk |
cd quantmedia-research/hierarchical-risk-parity pip install -r requirements.txt python compare_mvo.py
The return panel is synthetic, generated from a fixed seed with a deliberate block-correlation structure. It illustrates the estimation-error mechanism on one panel; it is not evidence that HRP beats mean-variance in general, and the shrinkage row is included precisely so the comparison is not stacked against a straw-man optimiser.
On the code shown above. The snippet inside this paper is a condensed illustration written to be readable in one screen. The package linked here is the canonical implementation: it differs in places — for example it walks the linkage tree explicitly rather than calling leaves_list — and it is the version covered by the tests. Where the two disagree, trust the package.
All research implementations · HRP vs mean-variance explainer