Add ERCOpt: Equal Risk Contribution (Risk Parity) portfolio optimizer#743
Open
hass-nation wants to merge 1 commit into
Open
Add ERCOpt: Equal Risk Contribution (Risk Parity) portfolio optimizer#743hass-nation wants to merge 1 commit into
hass-nation wants to merge 1 commit into
Conversation
Adds ERCOpt to hierarchical_portfolio.py, implementing the Equal Risk
Contribution (ERC) portfolio (Maillard, Roncalli & Teiletche 2010).
ERCOpt finds weights w ≥ 0, sum(w)=1 such that each asset contributes an
equal fraction of total portfolio variance:
w_i · (Σw)_i / (w'Σw) = 1/n for all i
This is also known as the Risk Parity portfolio; when Σ is diagonal it
reduces to inverse-volatility weighting.
The optimizer uses the Spinu (2013) cyclical coordinate descent algorithm,
which solves the exact one-dimensional sub-problem at each coordinate:
Σᵢᵢ·wᵢ² + (Σw − Σᵢᵢ·wᵢ)·wᵢ − 1/n = 0 (positive root)
Weights are NOT normalised between coordinate updates, which is crucial for
convergence — the unconstrained Spinu formulation normalises only after the
full pass over all assets.
API matches HRPOpt exactly:
erc = ERCOpt(returns=returns_df) # or cov_matrix=cov_df
weights = erc.optimize()
erc.portfolio_performance(verbose=True)
Also adds _erc_weights_ccd as a standalone helper (used internally).
29 new tests; 294 existing tests pass (0 regressions).
References
----------
Maillard, S., Roncalli, T., & Teiletche, J. (2010). The Properties of Equally
Weighted Risk Contribution Portfolios. Journal of Portfolio Management, 36(4), 60-70.
Spinu, F. (2013). An Algorithm for Computing Risk Parity Weights. SSRN.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
ERCOpttohierarchical_portfolio.py, implementing the Equal Risk Contribution (ERC) portfolio (Maillard, Roncalli & Teiletche 2010), also known as the Risk Parity portfolio.Motivation
ERC fills a gap between the existing
HRPOpt(hierarchical, cluster-based) and mean-variance optimizers: it is a simple, robust, model-free portfolio where every asset contributes the same fraction of total portfolio variance, requiring only a covariance estimate and no expected returns.ERC has been shown to be more robust out-of-sample than max-Sharpe or min-variance portfolios (Maillard et al. 2010) and is widely used in practice.
Definition
Find w ≥ 0, sum(w) = 1 such that:
When Σ is diagonal this reduces to inverse-volatility weighting:
w_i ∝ 1/σ_i.Solver: Spinu (2013) CCD
At each step, the exact one-dimensional sub-problem is solved for coordinate
i:Crucially, weights are NOT normalised between coordinate updates — normalisation happens once after the full pass. This unconstrained formulation converges reliably for any PD covariance matrix, including those with negative off-diagonal entries (where Roncalli's multiplicative update fails).
API
Matches
HRPOptexactly:Files changed
pypfopt/hierarchical_portfolio.py—_erc_weights_ccdhelper +ERCOptclasspypfopt/__init__.py— exportsERCOpttests/test_erc.py— 29 testsTest plan
clean_weights,portfolio_performancefrom returns and cov-only, error before optimizetest_plotting.pyfailures are pre-existing headless-display failures)sum(weights) = 1and all weights ≥ 0 verifiedRC_i = 1/n * (w'Σw)verified tortol=1e-6References
🤖 Generated with Claude Code