Add a point-in-time optimal cash reserve page - #6
Merged
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a new “Reserve Over Time” workflow to complement the existing Optimizer by computing (in-sample, no-lookahead) the point-in-time optimal cash reserve across expanding-window snapshots, with a Streamlit UI for visualization.
Changes:
- Added
optimal_reserve_over_time()plusReserveSweepPoint/ReserveSweepResultto sweep reserve % over expanding windows deterministically. - Introduced a new Streamlit page (
Reserve Over Time) with reserve trajectory + objective heatmap + snapshot table. - Added test coverage validating shape, determinism, tie-breaking, no-lookahead windowing, and input validation.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| tests/test_optimize.py | Adds unit tests for the new reserve-over-time sweep behavior and contracts. |
| src/portfolio_research_lab/optimizer.py | Implements the expanding-window reserve sweep API and related dataclasses/utilities. |
| src/portfolio_research_lab/init.py | Re-exports the new sweep API and result types from the package top-level. |
| app/pages/3_Reserve_Over_Time.py | Adds a new Streamlit page to run and visualize the reserve sweep over time. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| ) | ||
| min_rows = warmup_years * base.trading_days_per_year | ||
|
|
||
| years = prices.index.map(lambda ts: ts.year) |
Owner
Author
There was a problem hiding this comment.
Kept .map here: the vectorized prices.index.year fails this repo's ty gate (.index is typed as the generic Index, which has no .year attribute), so it needs a type-ignore to compile. This mirrors 2_Optimize.py; added a comment noting why.
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.
What
app/pages/3_Reserve_Over_Time.py: at a series of expanding-window snapshots, it uses only the price history up to each date (no look-ahead), holds the deploy rule + refill fixed, and sweeps the cash reserve to find the one that maximised the chosen objective. It is an in-sample fit at each date (clearly flagged), not an out-of-sample claim.optimal_reserve_over_time()inoptimizer.py(plusReserveSweepPoint/ReserveSweepResult), reusing the existing objective + cash-deploy backtest machinery. Deterministic — no sampler, no seed.Note on behaviour: under the return-only objectives (Excess CAGR / CAGR), the optimal reserve sits at the grid floor (5%) at every snapshot — holding cash only drags a long-rising, price-return-only series. The reserve trade-off appears under the Sharpe and drawdown-capped objectives. This is expected, not a bug.
How to test
Then open Reserve Over Time, press Run sweep, and confirm the line + heatmap render and the headline metric matches the latest snapshot. Switch the objective to Sharpe (vs cash) or Excess CAGR, drawdown-capped to see the reserve vary across time.
Security review
No security-impacting changes.