Skip to content

feat: Monte Carlo Simulation & Goal Attainment Probability Module - #385

Merged
robertocarlous merged 1 commit into
Neurowealth:mainfrom
dotunv:feat/319-monte-carlo-simulation
Aug 28, 2026
Merged

feat: Monte Carlo Simulation & Goal Attainment Probability Module#385
robertocarlous merged 1 commit into
Neurowealth:mainfrom
dotunv:feat/319-monte-carlo-simulation

Conversation

@dotunv

@dotunv dotunv commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a Monte Carlo simulation module that turns goal feasibility into probability distributions and confidence intervals, replacing the single deterministic "required APY" line with honest uncertainty quantification.

Closes #319


What's New

Pure Monte Carlo Core (src/analytics/montecarlo.ts)

  • Zero-I/O, zero src/stellar imports — pure computation, unit-testable in isolation
  • Two documented sampling modes:
    • Historical bootstrap: resamples from observed daily rate changes. No distributional assumptions imposed.
    • Parametric lognormal: fits mean/σ to returns and draws from a lognormal model. Assumption documented.
  • Seeded PRNG (mulberry32) for reproducible results — same seed produces byte-identical output
  • Convergence diagnostics: converged flag, recommendedIterations, effectiveSampleSize — never presents a noisy 1,000-path answer as fact

Goal Simulation Endpoint (POST /api/v1/goals/:id/simulate)

  • Owner-scoped, authenticated
  • Returns:
    • attainmentProbability — fraction of paths that crossed the target by the target date
    • Terminal value / drawdown / APY percentile bands (p5 / p50 / p95)
    • sensitivityTable — "at X% APY you have Y% chance" across the feasible rate range
    • convergence diagnostics
    • isSimulation: true + model disclaimer on every response
  • Validation: target date must be future, insufficient_history returned explicitly (not a guessed probability)

Simulation Service (src/goals/simulation.ts)

  • Resolves effective strategy config (own vs. followed, riskCeiling merge per Strategy Marketplace / Opt-In Copy-Trading #285)
  • Fetches 90-day historical ProtocolRate data with gap-handling (forward-fill)
  • Redis caching keyed on canonical input hash (5-min TTL)
  • Throws typed errors: GoalNotFoundError, GoalValidationError, InsufficientHistoryError

Files Changed

File Description
src/analytics/montecarlo.ts Pure Monte Carlo engine (684 lines)
src/goals/simulation.ts I/O service layer — strategy resolution, rate loading, caching (311 lines)
src/controllers/goal-simulation-controller.ts Express controller for simulate endpoint (75 lines)
src/validators/simulation-validators.ts Zod validation schemas (47 lines)
src/routes/goals.ts Added POST /:id/simulate route
tests/unit/analytics/montecarlo.test.ts 25 unit tests — determinism, modes, edge cases, convergence, structural guarantee

Acceptance Criteria Checklist

  • Seeded, deterministic Monte Carlo with bootstrap and parametric modes; fixed seed reproduces byte-identical output
  • Outputs: percentile bands, median, drawdown distribution, attainmentProbability, convergence flag
  • Degenerate / non-converged cases reported honestly (zero variance → converged: false)
  • POST /api/v1/goals/:id/simulate returns probability + sensitivity table + isSimulation disclaimer
  • insufficient_history handled with earliestAvailableDate
  • Redis cache keyed on canonical input hash
  • Pure-module import-graph test (zero src/stellar imports in core)
  • Typecheck passes, all 1194 unit tests green

Design Decisions

  1. Pure core / I/O service split: The simulation engine (montecarlo.ts) is import-graph-safe — it can never touch real funds. All I/O (DB, Redis, strategy resolution) lives in simulation.ts.
  2. Reuse over reimplementation: The simulation runs the same daily accrual + rebalance decision loop as the backtest engine (BacktestRequest/StrategyParams shapes reused). A Monte Carlo run is a distribution of backtests, not a parallel implementation.
  3. Honest failure discipline: converged: false, effectiveSampleSize, and insufficient_history are explicit output states — never hidden or faked. Follows the same "stated assumption beats hidden default" pattern as riskFreeRate.
  4. src/routes/backtest.ts note: This file contains a duplicate of the engine code (not mounted as an Express router). The backtest ?simulate=true extension from Monte Carlo Simulation & Goal Attainment Probability Module #319 would require first converting it to a proper router — tracked as a follow-up.

Testing

✅ Typecheck: 0 errors
✅ Unit tests: 1194 passed, 0 failed (81 suites)
✅ New tests: 25 passed — determinism, bootstrap, parametric, edge cases, convergence, structural guarantee
✅ Existing backtest tests: 13 passed, 0 regressions

🤖 Generated with Codebuff
Co-Authored-By: Codebuff noreply@codebuff.com

…Neurowealth#319)

Implement a pure, zero-I/O Monte Carlo simulation engine that turns goal
feasibility and backtest results into probability distributions and
confidence intervals. Two documented sampling modes:

- Historical bootstrap: resamples from observed daily rate changes with
  no distributional assumptions imposed
- Parametric lognormal: fits mean/σ to returns and draws from a lognormal
  model (assumption documented)

Adds POST /api/v1/goals/:id/simulate endpoint (owner-scoped) that returns:
- Attainment probability (fraction of paths crossing the target)
- Terminal value / drawdown / APY percentile bands (p5/p50/p95)
- Required-rate sensitivity table ("at X% APY you have Y% chance")
- Convergence diagnostics (converged flag, recommended iterations)
- isSimulation disclaimer on every response

Key design decisions:
- Pure core (src/analytics/montecarlo.ts) with no stellar/db imports,
  enforced by structural guarantee tests
- I/O service layer (src/goals/simulation.ts) handles strategy resolution,
  rate loading, and Redis caching
- Reuses the same daily accrual + rebalance decision loop as the backtest
  engine (same BacktestRequest/StrategyParams shapes)
- Seeded PRNG (mulberry32) for reproducible results
- Redis cache keyed on canonical input hash (5-min TTL)
- Concurrency limiter protects against CPU abuse

Closes Neurowealth#319

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
@robertocarlous
robertocarlous merged commit 1b0efc9 into Neurowealth:main Aug 28, 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.

Monte Carlo Simulation & Goal Attainment Probability Module

2 participants