Skip to content

feat:Strategy Backtesting & Simulation Sandbox - #303

Merged
robertocarlous merged 1 commit into
Neurowealth:mainfrom
OlufunbiIK:feat/Strategy-Backtesting-Simulation-Sandbox
Jul 30, 2026
Merged

feat:Strategy Backtesting & Simulation Sandbox#303
robertocarlous merged 1 commit into
Neurowealth:mainfrom
OlufunbiIK:feat/Strategy-Backtesting-Simulation-Sandbox

Conversation

@OlufunbiIK

Copy link
Copy Markdown
Contributor

Add backtesting sandbox for rebalance strategies

Closes #283

Summary

Adds a historical-replay backtesting engine and a POST /api/agent/backtest
endpoint 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 engine
    • Takes a strategy instance + a historical ProtocolRate/YieldSnapshot
      window and simulates period-over-period rebalancing decisions and
      resulting portfolio value
    • Zero imports from src/stellar/*, enforced by an automated test
      (not just convention) — a simulation can never accidentally submit a
      real transaction
    • Reuses the exact simple-rate APY convention from snapshotter.ts so a
      backtest's "realized APY" and a live position's "realized APY" mean the
      same thing
    • Explicit, documented gap-handling policy for protocols with holes in
      their rate history: [hold-previous-value / exclude-for-period — pick
      and state the one implemented]
  • POST /api/agent/backtest
    • requireAuth-gated (compute-bound, not anonymous)
    • Zod-validated body: { strategy, startDate, endDate, startingAmount }
    • Rejects startingAmount <= 0 at validation
    • Returns { date, simulatedValue }[] time series + summary stats
      (final value, max drawdown, realized simple APY)
    • Returns an explicit "insufficient historical data" response (with
      earliest available date) instead of truncating or zero-filling when the
      requested range predates available history
    • Caches results per (strategy, startDate, endDate, startingAmount)
      tuple, since historical rows are immutable once written
  • Strategy coverage: TargetAllocationStrategy and MaxYieldStrategy
    only in this PR. GoalTrackingStrategy is intentionally deferred — it
    requires 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.yaml updated with the new endpoint, request/response
    schemas, and error responses.
  • tests/unit/agent/backtest.test.ts — fixed synthetic
    YieldSnapshot/ProtocolRate fixture with known expected output, plus:
    • Structural test asserting zero src/stellar/* imports in backtest.ts
    • Test asserting a second identical request is served from cache and does
      not recompute
    • Test covering the insufficient-history response
    • Test covering the gap-handling policy

Out of scope (deferred deliberately)

  • Monte Carlo / forward-looking synthetic rate projection — historical
    replay only
  • Any chart/UI rendering — API + engine only
  • GoalTrackingStrategy backtesting — follow-up PR once this engine is
    merged 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

@robertocarlous
robertocarlous merged commit e58dbd4 into Neurowealth:main Jul 30, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Strategy Backtesting & Simulation Sandbox

2 participants