feat:Strategy Backtesting & Simulation Sandbox - #303
Merged
robertocarlous merged 1 commit intoJul 30, 2026
Merged
Conversation
7 tasks
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.
Add backtesting sandbox for rebalance strategies
Closes #283
Summary
Adds a historical-replay backtesting engine and a
POST /api/agent/backtestendpoint so users can simulate how TargetAllocationStrategy or MaxYieldStrategy
would have performed over a historical window before committing real funds,
and so reviewers can sanity-check new strategies against history.
Problem
Today there's no way to evaluate a strategy's historical behavior — a user
only finds out how a strategy performs by living through it live. There's
also no return/drawdown math anywhere in the codebase to build this on top of.
What's included
src/agent/backtest.ts— pure replay engineProtocolRate/YieldSnapshotwindow and simulates period-over-period rebalancing decisions and
resulting portfolio value
src/stellar/*, enforced by an automated test(not just convention) — a simulation can never accidentally submit a
real transaction
snapshotter.tsso abacktest's "realized APY" and a live position's "realized APY" mean the
same thing
their rate history: [hold-previous-value / exclude-for-period — pick
and state the one implemented]
POST /api/agent/backtestrequireAuth-gated (compute-bound, not anonymous){ strategy, startDate, endDate, startingAmount }startingAmount <= 0at validation{ date, simulatedValue }[]time series + summary stats(final value, max drawdown, realized simple APY)
earliest available date) instead of truncating or zero-filling when the
requested range predates available history
(strategy, startDate, endDate, startingAmount)tuple, since historical rows are immutable once written
TargetAllocationStrategyandMaxYieldStrategyonly in this PR.
GoalTrackingStrategyis intentionally deferred — itrequires simulating required-rate recalculation over time and is the most
complex to get right, so it lands once the engine is proven out on the
simpler strategies.
docs/openapi.yamlupdated with the new endpoint, request/responseschemas, and error responses.
tests/unit/agent/backtest.test.ts— fixed syntheticYieldSnapshot/ProtocolRatefixture with known expected output, plus:src/stellar/*imports inbacktest.tsnot recompute
Out of scope (deferred deliberately)
replay only
GoalTrackingStrategybacktesting — follow-up PR once this engine ismerged and Goal-Based Investing ("Savings Goals") #281 is finalized
Coordination note
Drawdown/return math here is scoped specifically to backtesting. #225
(live portfolio analytics) may need the same drawdown definition — flagging
so we converge on one implementation rather than defining "drawdown" twice.
Happy to extract this into a shared util if #225 lands first.
How to verify locally