Skip to content

feat: strategy what-if simulation endpoint (#344) - #415

Merged
Abidoyesimze merged 1 commit into
Neurowealth:mainfrom
BernardOnuh:feat/344-strategy-what-if-simulation
Sep 2, 2026
Merged

feat: strategy what-if simulation endpoint (#344)#415
Abidoyesimze merged 1 commit into
Neurowealth:mainfrom
BernardOnuh:feat/344-strategy-what-if-simulation

Conversation

@BernardOnuh

@BernardOnuh BernardOnuh commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Closes #344

Summary

Adds POST /api/v1/strategies/simulate (#344) — a dry-run what-if preview for a hypothetical strategy change before the user commits to it.

The endpoint resolves the caller's effective strategy config (follow → inline → own, risk ceiling tighten-only), then returns:

  • immediate — what the agent would do right now on the caller's live positions (rebalance/hold/blocked) plus a trace that is shape-parity with the persisted DecisionTrace.
  • historical — a non-compounding daily replay over retained ProtocolRate history, side-by-side with a counterfactual "held in the starting protocol" leg. Each rebalance subtracts the same estimateRebalanceCost the live agent uses.

All accrual math uses Prisma.Decimal (never a plain float).

Zero side effects

The endpoint is a pure read — it never writes an OutboxOp, AgentLog, Transaction, RebalanceDecision, User, PublishedStrategy, or Position row. Enforced structurally in the core (src/agent/simulate.ts has no stellar/db/event imports — verified by a source-reading test) and by acceptance tests at the DB-glue layer.

Deliberate decisions (callouts)

  • Amount encoding: the historical/decision legs pass move amounts as value × 10^18 (the src/agent/backtest.ts convention) so the shared estimateRebalanceCost divides back to human units and yields realistic fees. We deliberately do not mirror the latent plain-integer encoding in src/agent/tools/actionTools.ts:265, which would drive network-fee percent toward ~1e15 and block every move. Documented in ASSUMPTIONS.md / STRATEGY_MARKETPLACE.md.
  • No apply path: per scope, this is core-only (no confirmSimulationToken / PUT /api/v1/strategy apply step — that endpoint does not exist yet; strategy changes go through adjustStrategyTool).

Files

New

  • src/agent/simulate.ts — pure simulation core
  • src/strategy/simulation-service.ts — owner-scoped DB glue (reads only caller positions + public rates/risk scores)
  • tests/unit/agent/simulate.test.ts (12 tests)
  • tests/integration/strategies-simulate.integration.test.ts (7 tests)

Changed

  • src/routes/strategies.ts, src/controllers/strategy-controller.ts — the route
  • src/validators/strategy-validators.tsstrategySimulateSchema (180-day cap, follow/inline mutual exclusion)
  • src/config/env.ts, src/middleware/rateLimiter.tssimulateRateLimit (6 req/min)
  • tests/integration/rateLimiter.integration.test.ts — mock block for the new limiter
  • Docs: docs/openapi.yaml, docs/STRATEGY_MARKETPLACE.md, ASSUMPTIONS.md

Verification

  • npm run typecheck
  • npm run lint
  • npm test ✅ (123 suites / 1665 tests pass)

Note: package-lock.json was left out of this PR — it only picked up a dev: true metadata tweak from a local npm install and is unrelated to the feature.

Add POST /api/v1/strategies/simulate — a dry-run what-if preview that
resolves the caller's effective strategy config (follow -> inline ->
own, risk ceiling tighten-only), then returns an immediate engine-parity
decision plus a non-compounding historical replay against retained
ProtocolRate history, side-by-side with a counterfactual hold leg.

All accrual math uses Prisma.Decimal. The module is a pure read with
zero side effects: it never writes an OutboxOp, AgentLog, Transaction,
RebalanceDecision, User, PublishedStrategy or Position row. Enforced
structurally (no stellar/db/event imports in the core) and by test.

Includes a 120s result cache keyed by a config+window simulation token,
a 6 req/min simulate rate limiter, validators, unit + integration tests,
and docs (openapi, STRATEGY_MARKETPLACE, ASSUMPTIONS).
@drips-wave

drips-wave Bot commented Sep 1, 2026

Copy link
Copy Markdown

@BernardOnuh Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@Abidoyesimze
Abidoyesimze merged commit c967ab7 into Neurowealth:main Sep 2, 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 What-If Simulation Endpoint (Dry-Run Before Apply)

3 participants