Commit Graph

3 Commits

Author SHA1 Message Date
Devin
4fc53703c6 feat(backtest): runtime + per-user feature flags (Option C)
Replaces the build-time VITE_BACKTEST_ENABLED gate with a fully runtime
flow: a global Cosmos-backed default (already shipped in the existing
dynamicConfig system) plus a new per-user override layer. An admin can
now enable backtest for specific users without flipping the global
switch — useful for staged rollout and beta testers.

Resolution order: per-user override > global config > env fallback.
Both /api/feature-flags (FE display) and /api/backtest/run (server
guard) consult the same merge logic.

Backend (backend/src/...):
  ~ services/profileRepository.ts
      + TradingUserFeatureFlags interface
      + featureFlags?: TradingUserFeatureFlags on TradingUserProfile
      + setUserFeatureFlags(userId, { backtestEnabled, ... })
      ~ saveCurrentUserProfile() — strip role + featureFlags from input
        so non-admins can't elevate via PATCH /api/me/profile
      ~ mergeTradingUserProfiles() — preserves explicit flag values only
  ~ services/apiServer.ts
      ~ /api/feature-flags merges per-user override into the response
      + /api/admin/users/:userId/feature-flags  (GET — overrides + effective)
      + /api/admin/users/:userId/feature-flags  (PATCH — admin-only writer)
      ~ /api/backtest/run resolves effective flags before guarding
  ~ backtest/index.ts
      + RunBacktestOptions.skipGlobalFeatureFlagCheck
      ~ runBacktest() honors the override (route already gated stricter)

Frontend (web/src/...):
  ~ backtest/flags.ts — isBacktestBuildEnabled() now returns true.
    Kept as a no-op function so existing callers don't break.
  + lib/userFeatureFlagsApi.ts — typed admin client
  + components/admin/UserFeatureFlagsPanel.tsx
      Tri-state picker per flag (Default / On / Off), Look up by user id,
      Save/Reset, shows the merged "effective" value.
  ~ tabs/ConfigTab.tsx — mounts <UserFeatureFlagsPanel /> below the
    existing global Backtest Access Control section.
  ~ layout-fixes.css §27 — styles for the per-user panel.

Tests:
  + testBacktestEngine: skipGlobalFeatureFlagCheck enables per-user
    override semantics. 12/12 regression checks pass.

Security note: featureFlags + role are explicitly stripped from
saveCurrentUserProfile input. Only the admin-only PATCH endpoint can
set per-user overrides.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-10 19:04:12 +00:00
Devin
4456873ab4 feat(backtest): Alpaca historical data source for equities (Stage C)
Adds BacktestAlpacaSource so saved trade plans for US equities can be
backtested without manual CSV upload. Mirrors the existing Kraken
loader pattern.

Backend:
  + backend/src/backtest/data/alpacaLoader.ts
      loadDatasetFromAlpaca({ symbols, fromTs, toTs, feed, adjustment })
      - Uses the existing @alpacahq/alpaca-trade-api SDK
      - Fetches 15Min bars; normalize.ts aggregates 1h/4h
      - 50-day warm-up lookback so ProEngine has enough EMA/RSI history
      - Throws cleanly with config guidance if ALPACA_API_KEY missing
      - In-memory cache keyed by (symbol, window, feed, adjustment)
  ~ backend/src/backtest/types.ts
      + BacktestAlpacaSource interface
      + 'alpaca' added to BacktestDataSource and BacktestDataSourceType
  ~ backend/src/backtest/data/loadHistoricalData.ts
      Wires 'alpaca' source into the dispatcher

Frontend:
  ~ web/src/backtest/types.ts — adds 'alpaca' to BacktestDataSourceType
  ~ web/src/backtest/components/BacktestConfigurator.tsx
      + 'alpaca' as a SourceType option
      + AUTO_FETCH_SOURCES list — kraken AND alpaca skip the upload-required
        validation
      + 'Alpaca (US equities)' option in the source-picker dropdown
      + Source-picker change handler seeds default IEX/raw Alpaca payload

Tests:
  + testBacktestEngine.ts: new "alpaca data source dispatcher" assertion
    Verifies the type discriminator + error message without hitting
    the network. 11/11 regression checks pass.

Caveats (documented in alpacaLoader inline + ENGINE_READINESS.md §3.4):
  - Free IEX feed has limited symbol coverage (~2016+)
  - SIP feed (paid) needed for full pre-2017 + full-market historical
  - The loader graceful-fails when credentials aren't configured
  - Existing Alpaca live-trading connector unchanged — backtest uses
    its own SDK instance with a different fetch path

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-10 10:59:06 +00:00
Devin
35efa786bd test(backend): backtest engine regression suite (Stage B)
Adds testBacktestEngine.ts — the minimum viable test set called out in
docs/backtest/ENGINE_READINESS.md §3.1. Codifies behaviors verified
during the readiness audit so they don't silently regress.

Coverage (10 assertions, all passing):
  Unit (testable building blocks):
    - aggregateCandles 15m→1h: OHLC preserved, volume summed
    - aggregateCandles 15m→4h: OHLC preserved, volume summed
    - computeSummary: hand-computed PnL/winRate/drawdown match
    - computeSummary: empty inputs → all zeros (no NaN/Infinity)
    - computeSharpe: single-point timeline → 0 (no divide-by-zero)
    - withLogLevel: level restored after success AND after throw

  Integration (full runBacktest):
    - Determinism: same input → byte-identical JSON output
    - Flat-price tape → 0 trades, 0 PnL, 0 drawdown
    - Result shape contract: all documented top-level keys present
    - Empty candle dataset throws explicitly (no silent 0-trade result)

Conforms to the existing testXxx.ts convention used by the other
check:backtest-* scripts. Wired into package.json:
  - "check:backtest-engine": "node --import tsx testBacktestEngine.ts"
  - chained into the top-level "test" script

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-10 10:46:45 +00:00