Repository navigation
✨ Add long-only mean-variance - #13
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
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.
Adds long-only mean-variance, the first of the 0.2.5 methods built on the active-set QP solver from #11. It takes the same
MeanVarianceConfigasmean_variance_allocation, so both the risk aversion and the target return objectives work without shorting.long_only_mean_variance_allocation(&MeanVarianceConfig)γ: maximizeμᵀw − (γ/2)wᵀΣwsubject to1ᵀw = 1andw ≥ 0. This issolve_nonnegativewithQ = γΣ,c = μand the budget constraint, starting from equal weights. The feasible set is bounded, so there is always an optimum. On a singular covariance wheremean_variance_allocationreturnsUnbounded, this one returns weights.ρ: minimizewᵀΣwsubject to1ᵀw = 1,μᵀw = ρandw ≥ 0. The solver starts from a mix of the lowest- and highest-return assets that hits the target exactly, and the result is renormalized to sum to 1 after zero-clamping, like long-only min variance.When the unconstrained portfolio has no short positions, the two methods agree. When Σ is singular, several long-only portfolios can share the optimum, and the docs point to shrinkage for a unique answer, as they do for long-only min variance.
There's no
_with_configvariant, becauseMeanVarianceConfigalready carries the covariance adjustments, as it does formean_variance_allocation.New error:
CutupError::TargetReturnOutOfRangeNo long-only portfolio can return less than the lowest or more than the highest expected return, so such a target returns
TargetReturnOutOfRange { target_return, lowest, highest }.CutupErroris#[non_exhaustive], so adding a variant isn't breaking.When every expected return is equal, only that return can be targeted. Any portfolio reaches it, so the result is the long-only min variance portfolio. Unconstrained mean-variance returns
DegenerateExpectedReturnshere for a different target. The long-only method returnsTargetReturnOutOfRangeinstead, since the range is a single point.The risk aversion and target return checks moved into a
check_objectivehelper shared by both mean-variance methods.Tests
Unbounded.μ − γΣwis equal on every held asset and no higher on the others.Σwequalsλ₁ + λ₂μᵢon every held asset and is no lower on the others, with the multipliers fitted on the held assets.PROPTEST_CASES=50000pass.γ = 5and custom expected returns;TargetReturnOutOfRangecases.Docs
reference/README.mddescribes the shared support enumeration.compareexample adds a long-only mean-variance row.