Repository navigation
Solve every covariance method in correlation units - #16
Merged
Merged
Conversation
thinkjrs
commented
Oct 27, 2026
thinkjrs
commented
Oct 27, 2026
thinkjrs
commented
Oct 27, 2026
thinkjrs
commented
Oct 27, 2026
thinkjrs
commented
Oct 27, 2026
thinkjrs
commented
Oct 27, 2026
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.
Makes every covariance-based method accurate when asset volatilities are many orders of magnitude apart. The
scales4dataset from #15 showed that max diversification wasn't the only method with this problem.The problem
Assets whose volatilities are about 1e8 apart make Σ numerically singular in raw units, even when the correlations are well conditioned. On
scales4, with volatilities from about 1e-2 to 1e-10, the old code gave:SingularCovariancefrom risk parity, where a risk parity portfolio exists;RiskFreeRateTooHighfrom max Sharpe, and weights 99.9% off from long-only max Sharpe.The fix: correlation units
With
Σ = diag(σ) C diag(σ), every solve now works inu = diag(σ)·won the correlation matrixC, which has a unit diagonal and doesn't depend on any asset's units.src/qp.rs):solve_kktandsolve_nonnegativerescale their variables bysqrt(diag K). This covers min variance, mean-variance and their long-only versions, and long-only max Sharpe and max diversification throughlong_only_max_ratio. Max diversification no longer needs its own correlation code. Each constraint column is divided by its largest entry first, so the rescaling can't overflow.C, andΣ⁺x = D⁻¹C⁺D⁻¹x.C, where the risk contributionsuᵢ(Cu)ᵢequalwᵢ(Σw)ᵢ, and the result is divided by the volatilities.C's eigenvalues, so whether a covariance counts as singular doesn't depend on units. The Rayleigh-quotient test moved into anin_null_spacehelper, shared bylong_only_max_ratioand risk parity.Tolerances that depended on units
1e-10 · Σ |wᵢμᵢ|of the min variance return now counts as at it, for the min variance weightsw. The old tolerance used the largest|μᵢ|, so with μ = [0.1, 1e-13] a rate belowr_mvpwas rejected.1e-10 · max(|r_f|, |μᵢ|), its own rounding. The old tolerance used the largest|μᵢ|, so rescaling one asset down could make it look like it had no excess return.Risk parity on rank-one covariances
Working on
Cexposed a risk parity bug. When a long-only zero-variance portfolio exists, rounding could stall Newton on that direction, or let it converge on the rounding left in a singularC. Either way it returned weights instead ofSingularCovariance. Risk parity now checks its result againstC's null space, with the samein_null_spacecutoff as max diversification, before returning it.Behavior change: minimum norm on singular covariances
When Σ is singular, min variance, mean-variance and max Sharpe return the minimum-norm optimum in correlation units instead of in the weights. Which of the tied portfolios is returned then doesn't depend on units, but it can differ from 0.2.4. Two fixtures on
rank_deficient6moved, by up to 0.087. The docs and CHANGELOG say so.Tests
s².PROPTEST_CASES=50000pass.tests/properties.proptest-regressions.C.lstsqon the whole KKT matrix lost about 1e-9 accuracy onscales4.scales4now runs min variance, risk parity, long-only min variance, both mean-variance objectives for both methods, max Sharpe and long-only max Sharpe.Docs
reference/README.mddescribes the correlation-unit references, the Schur complement andscales4.