feat: opt-in no-forecast method= (min_variance / risk_parity / max_diversification) (fixes #7)#26
Merged
Merged
Conversation
…versification) recommend_weights, recommend, and walk_forward_recommendation gain an opt-in method= — "optimize" (default, objective-based, uses expected returns) or the no-forecast allocators min_variance / risk_parity / max_diversification that allocate from the covariance alone, sidestepping the fragile expected-return estimate (the dominant source of optimizer error). Dispatched via a shared _optimize_weights helper. The default reproduces prior behavior; the chosen method and a uses_return_forecast flag are recorded in evidence, and the rationale states the no-forecast assumption plainly. risk_parity and max_diversification use their own risk-based allocation and do not honor bounds/constraints/profile (documented); min_variance honors them via the shared SLSQP path. Closes #7. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011a6EibcjDDv3Ff1Fb5esS1
Reviewer's GuideAdds an opt-in allocation method switch to the recommendation path, introducing no-forecast allocators (min_variance, risk_parity, max_diversification) wired through a shared helper, while preserving default behavior and enriching rationale/evidence metadata and tests. Sequence diagram for allocation method selection in recommendsequenceDiagram
actor User
participant recommend
participant recommend_weights
participant _optimize_weights
participant find_optimal_portfolio
participant optimize_risk_parity
participant optimize_max_diversification
User->>recommend: recommend(prices, method)
recommend->>recommend_weights: recommend_weights(prices, method)
recommend_weights->>_optimize_weights: _optimize_weights(method, objective, mean, cov, bounds, constraints, risk_free_rate)
alt method == optimize
_optimize_weights->>find_optimal_portfolio: find_optimal_portfolio(objective, mean, cov, bounds, constraints, risk_free_rate)
else method == min_variance
_optimize_weights->>find_optimal_portfolio: find_optimal_portfolio(objective_min_variance, mean, cov, bounds, constraints, risk_free_rate)
else method == risk_parity
_optimize_weights->>optimize_risk_parity: optimize_risk_parity(cov)
else method == max_diversification
_optimize_weights->>optimize_max_diversification: optimize_max_diversification(cov)
else [unknown method]
_optimize_weights-->>recommend_weights: ValueError
end
_optimize_weights-->>recommend_weights: weights
recommend_weights-->>recommend: RecommendedWeights(weights, objective, rationale, evidence)
recommend-->>User: Recommendation(target_weights, trades, alerts, verdict, validation)
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've left some high level feedback:
- For
risk_parityandmax_diversification, consider explicitly warning or erroring whenbounds/constraints/profileare supplied, since they’re currently silently ignored and that may surprise callers. - The
RecommendedWeights.objectivefield now carriesmethodfor no-forecast paths; if consumers expect this to represent the objective function, consider adding a separateallocator/methodfield on the dataclass and keepingobjectivestrictly tied to the optimization objective.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- For `risk_parity` and `max_diversification`, consider explicitly warning or erroring when `bounds`/`constraints`/`profile` are supplied, since they’re currently silently ignored and that may surprise callers.
- The `RecommendedWeights.objective` field now carries `method` for no-forecast paths; if consumers expect this to represent the objective function, consider adding a separate `allocator`/`method` field on the dataclass and keeping `objective` strictly tied to the optimization objective.Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
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.
Closes #7.
What
Adds an opt-in
method=torecommend_weights/recommend/walk_forward_recommendationselecting the allocator:"optimize"(default) — objective-based, uses expected returns (current behavior)"min_variance","risk_parity","max_diversification"— no-forecast: allocate from the covariance alone, sidestepping the fragile expected-return estimate (TODO.md §1b: the mean is the dominant source of optimizer error)Dispatched via a shared
_optimize_weightshelper, reusing the already-shipped engines (optimize_risk_parity,optimize_max_diversification, andobjective_min_variancevia the SLSQP path). Composes withcov_method=from #6.The rationale plainly states the no-forecast assumption;
evidencerecordsmethodand auses_return_forecastflag.Default =
"optimize"(deliberate)The issue suggested considering a no-forecast default. I kept
"optimize"as the default so the change is byte-for-byte behavior-preserving under the 2.x API/output-stability promise (a test assertsrecommend_weights(prices)equalsmethod="optimize"weight-for-weight). Flipping the default is a good candidate for a major version — easy to change if you'd prefer.Constraint handling (documented)
min_variancehonorsbounds/constraints/profilevia the shared SLSQP path.risk_parityandmax_diversificationuse their own risk-based allocation and do not honor those constraints — documented in the docstring.OOS comparison (acceptance criterion)
Walk-forward on the synthetic GBM fixture (AAA/BBB/CCC, ~5y, 1y window / 3m step, 15 windows):
Even on synthetic GBM the thesis shows through: the forecast-based optimizer inflates in-sample (0.78) then degrades 41% out-of-sample, while the no-forecast allocators don't overfit —
risk_parity/max_diversificationreach a higher OOS Sharpe (~0.49) than the optimizer's OOS (0.46) with ~0% degradation. (Reproduced offline; not committed.)Tests
test/test_recommend_method.py— default==optimize (behavior-preserving), each no-forecast method valid + flaggeduses_return_forecast=False, rationale states the assumption, composes withcov_method, invalid method raisesValueError. Full suite: 423 passed, coverage 87.0%;ruff check src/clean.🤖 Generated with Claude Code
https://claude.ai/code/session_011a6EibcjDDv3Ff1Fb5esS1
Summary by Sourcery
Add an opt-in allocation method switch to the recommendation pipeline to support no-forecast allocators while preserving existing default behavior.
New Features:
methodparameter onrecommend_weights,recommend, andwalk_forward_recommendationto choose between objective-based optimization and no-forecast allocators (min_variance, risk_parity, max_diversification).Enhancements:
_optimize_weightshelper and record the chosen method plus auses_return_forecastflag in recommendation evidence and rationale.Tests:
methodbehavior, validity and metadata of no-forecast allocators, interaction with robust covariance estimators, and error handling for invalid methods.