Commit Graph

75 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
3c3dce6b73 feat(backend): quiet-mode logging for backtest runs (Stage D)
Backtest runs were emitting ~25k log lines per 5k-candle backtest at
default 'info' level (~5 logs per candle from rule evaluations). At
year-scale that's 80k+ lines per run — operationally disruptive and
likely the reason the production gate was set conservatively.

Changes:
  - utils/logger.ts: respect LOG_LEVEL env override (no behavior change
    when unset). Add `withLogLevel(level, fn)` helper that swaps the
    logger level for the duration of a function and always restores it
    via finally — safe across throws.
  - backtest/index.ts: wrap runBacktestReplay() in withLogLevel('warn').
    New `logLevel?: string` option on RunBacktestOptions lets callers
    override (e.g. 'info' or 'debug' for engine diagnosis).

Verified:
  - 2,000-candle run: 25,000 → 3 log lines at default
  - 500-candle run with logLevel='info': 3,202 lines (verbose still works)
  - Logger.level correctly restored after both successful runs and
    failed runs that throw (assertBacktestMode rejection test)
  - No regression: logger initial level honors LOG_LEVEL env or 'info'

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:44:30 +00:00
root
4763a9a9d1 feat(devops): admin-only info, public version, dep checks, role hardening
Backend:
- /api/devops/info now requires admin role (was: any authenticated user).
  Exposes env keys, dep checks, and socket counts — admin-only by design.
- New /api/devops/version (public, no auth) returns build SHA/branch/image
  for ops/CI rollback verification.
- Dep checks: live ping for Cosmos (trading_users) and platform-service.
- Service version read dynamically via readServiceVersion(import.meta.url)
  — no more hardcoded '0.1.0'.
- extra: socketIoConnections + tradingApiUrl for runtime debugging.
- saveCurrentUserProfile no longer accepts client-supplied role —
  prevents drift with platform JWT (which is authoritative).

Web:
- DevOps tab is now admin-only (gated behind isAdmin like Bot Config and
  Admin Panel). Both the section list and content render are guarded.
- Service version baked into bundle via Vite `define` (__WEB_SERVICE_VERSION__)
  read from web/package.json — no more hardcoded VERSION constant.
- Bumps @bytelyst/devops dep to ^0.1.2.

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

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-10 05:52:48 +00:00
root
74400fda70 feat: migrate web to Gitea registry, add /api/devops/info, fix role drift
Backend:
- Fix role drift: /api/me/profile now returns JWT role authoritatively (was reading
  drifting role from trading_users). PATCH strips client-supplied role.
- Add /api/devops/info endpoint backed by @bytelyst/devops/server.
- Dockerfile: bake BYTELYST_COMMIT_SHA / BYTELYST_BUILT_AT / etc. as build args.

Web:
- Migrate from vendor/ + .pnpmfile.cjs to Gitea npm registry (consistency with backend).
- Replace file: refs in web/package.json with semver ranges resolved from Gitea.
- Drop vendor/bytelyst/* tree and .pnpmfile.cjs.
- Add DevOpsTab in Settings using @bytelyst/devops/ui (tabbed: Build/Runtime/Config/Deps/Raw).
- Vite alias: restrict @bytelyst/* catch-all to single-segment names so subpath
  imports (@bytelyst/devops/ui) resolve via package exports map.
- Bake BYTELYST_* metadata into the bundle as VITE_BYTELYST_* env.

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

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-10 04:57:09 +00:00
root
1a794d2365 feat(chat): add structured copilot insights 2026-05-07 08:03:08 +00:00
root
8adc27004d feat(chat): add copilot quick links 2026-05-07 07:40:20 +00:00
root
73db534d7d feat(chat): add guided next actions 2026-05-07 07:25:00 +00:00
root
3f73310b4e feat(chat): explain waiting trade signals 2026-05-07 06:59:40 +00:00
root
f3dfe31d1f feat(chat): add runtime copilot explanations 2026-05-07 06:58:11 +00:00
root
a1a63cc945 feat(simple): add long-term hold mode 2026-05-06 17:37:04 +00:00
root
0b526f3499 feat(simple): add setup activity history 2026-05-06 17:23:45 +00:00
root
e853ffc0c5 fix(api): reconcile simple setup state on order sync 2026-05-06 17:07:13 +00:00
root
62804ed4e5 feat(simple): add lifecycle toast notifications 2026-05-06 16:56:01 +00:00
root
d8941c5ad0 fix(api): auto-close dust exit remainders 2026-05-06 16:39:21 +00:00
root
92747b76a7 fix(simple): support concurrent symbol setups 2026-05-06 07:56:03 +00:00
root
e50e906866 fix(simple): dedupe exit retries per scan 2026-05-06 07:36:09 +00:00
root
349cdae4a6 fix(simple): support crypto fallback and scale-in entries 2026-05-06 07:33:52 +00:00
root
e92236b764 fix(simple): rebind armed buys to existing holdings 2026-05-06 07:23:03 +00:00
root
3d505db8d8 feat(simple): add amount sizing and runtime status 2026-05-06 07:04:48 +00:00
root
dad47fc13d fix(simple): prefer available user alpaca creds 2026-05-06 06:40:49 +00:00
root
b690f26a28 fix(simple): refresh user keys for profile sync 2026-05-06 06:35:07 +00:00
root
e01f38c883 fix(simple): allow immediate buy triggers 2026-05-06 06:09:16 +00:00
root
90e733b46c feat(simple): save dip-buy and profit-exit setups 2026-05-06 02:14:32 +00:00
root
0bd46ab43b feat(simple): auto-create dedicated execution profile 2026-05-06 01:29:45 +00:00
root
0f74d7b292 fix(portfolio): tighten bootstrap and manual position handling 2026-05-05 23:31:33 +00:00
root
160920ea07 fix(api): migrate fmp routes to stable endpoints 2026-05-05 23:21:27 +00:00
root
0c0cc93f57 fix(api): require user fmp api key 2026-05-05 23:14:04 +00:00
root
39456473cb feat(settings): add per-user fmp api key 2026-05-05 23:08:31 +00:00
root
adfadd824b fix(api): prefer paper alpaca keys for market data 2026-05-05 22:47:13 +00:00
root
351412423f fix(api): use user alpaca keys for market data 2026-05-05 22:40:06 +00:00
root
3867b6b296 fix(api): return 503 for missing fmp config 2026-05-05 22:20:10 +00:00
root
2db27ef686 fix(auth): handle null platform token verification 2026-05-05 21:19:28 +00:00
e2e189eede fix(C7): mitigate FMP key exposure 2026-05-04 17:07:52 -07:00
1377bf2453 fix(C6): require explicit FMP API key
Remove the silent shared demo-key fallback for FMP-backed research and screener routes, document the required key, and make backend/.env.example trackable so setup guidance has one source of truth.

Refs: docs/AUDIT_REDESIGN.md item C6.

Co-Authored-By: GPT-5 Codex <noreply@openai.com>
2026-05-04 17:01:00 -07:00
7c4b08cdd5 fix(C4): validate news symbol filters
Normalize and limit /api/news symbols before proxying to Alpaca so only bounded, expected symbol characters reach the upstream news endpoint.

Refs: docs/AUDIT_REDESIGN.md item C4.

Co-Authored-By: GPT-5 Codex <noreply@openai.com>
2026-05-04 16:47:24 -07:00
c173aeb87a fix(C3): validate screener sector filters
Reject unsupported /api/screener sector values before building the FMP query so only known sector labels reach the upstream stock screener.

Refs: docs/AUDIT_REDESIGN.md item C3.

Co-Authored-By: GPT-5 Codex <noreply@openai.com>
2026-05-04 16:44:33 -07:00
6aa001a530 fix(C1): refuse unsafe code strategy backtests
Reject inline JavaScript strategy payloads before backtest execution, both at the API boundary and inside runBacktest, so saved profiles and direct internal calls cannot route unsandboxed code into replay handling.

Refs: docs/AUDIT_REDESIGN.md item C1.

Co-Authored-By: GPT-5 Codex <noreply@openai.com>
2026-05-04 16:31:04 -07:00
082800745c fix(C2): cache FMP upstream responses
Add a 30-minute in-memory cache keyed by the complete Financial Modeling Prep upstream URL so repeated profile, metrics, earnings, and screener requests do not burn the free-tier quota. The cache keeps non-OK responses out of storage so transient rate-limit or provider errors can recover on the next request.

Refs: docs/AUDIT_REDESIGN.md item C2.

Co-Authored-By: GPT-5 Codex <noreply@openai.com>
2026-05-04 16:06:47 -07:00
938ed86044 feat: live data wiring (Alpaca/FMP) + strategy builder + screener
Wires the new dashboard to real market data and adds the strategy
builder & screener UIs that were stubbed in the previous commit.

Frontend (web/src/):
- lib/marketApi.ts: authenticated fetch helpers for chart bars,
  market indices, news, and FMP research endpoints
- views/HomeView.tsx: StockChart now fetches live OHLCV via
  fetchChartBars on symbol/period change with loading/error states;
  ResearchCards replaces the static placeholder with live FMP
  profile/metrics/earnings (next-earnings + last 3 historical)
- components/layout/Header.tsx: live SPY/DIA/QQQ price + change%
  via fetchMarketIndices, refreshing every 60s; removed unused
  static sparkline placeholder
- components/strategy/VisualRuleBuilder.tsx: drag-and-drop IF/THEN
  rule composer using @dnd-kit (RSI/MACD/EMA/Price/Volume,
  above/below/crosses, BUY/SELL with shares or % of capital);
  saves via POST /api/profiles
- components/strategy/CodeStrategyEditor.tsx: Monaco editor with
  JS strategy template; "Run Backtest" posts to /api/backtest and
  renders return/win-rate/Sharpe/drawdown plus trade log
- views/ResearchView.tsx: adds "Visual Builder" and "Code Editor"
  sub-tabs alongside Strategies / Signals / Backtesting
- views/ScreenerView.tsx: live FMP screener with market-cap and
  sector filters, sortable columns, click-to-load-symbol routing
- index.css: light theme background; @keyframes spin for loaders
- App.dom.test.tsx: rewritten for router-based AppShell (was
  asserting on the removed tab UI; fixes 5 prior failures)

Backend (backend/src/services/apiServer.ts):
- /api/chart/bars: detects crypto symbols (contains "/") and
  routes to Alpaca v1beta3/crypto/us/bars; equities use
  v2/stocks/{symbol}/bars with iex feed
- (existing) /api/news, /api/market/indices, /api/research/{
  profile,metrics,earnings}, /api/screener proxy endpoints

Build/config:
- web/vite.config.ts: dedupe react / react/jsx-runtime /
  react-router-dom so the vendored react-auth dist resolves the
  same React instance (fixes "Cannot resolve react/jsx-runtime"
  Rollup error)
- web/tsconfig.app.json: exclude shared/platform-clients.ts and
  shared/platform-mobile.ts (mobile-only, missing RN SDK)
- web/package.json: add react-router-dom, @monaco-editor/react,
  @dnd-kit/core, @dnd-kit/sortable, @dnd-kit/utilities

Verification: `npm run build` in web/ → clean (✓ built in 3s);
backend tsc --noEmit → clean. Test suite: 151/155 pass; the 4
remaining failures are pre-existing (3 useTabFeatureFlags module
cache leaks, 1 EntryForm), not introduced here.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 06:16:46 -07:00
f62c3b15ee feat: full web dashboard redesign + 6 new API proxy endpoints
Replaces the 12-tab dashboard with a 3-column layout matching the
investing app mockup (sidebar nav, main chart area, right panel).

Web changes:
- New context/AppContext.tsx — shared botState/auth across all views
- New layout: Sidebar, Header (with market index sparklines), RightPanel
- New views: Home, Portfolio, Research, Markets, Screener, Watchlist, Alerts, Settings
- AppShell wires React Router routes to all views
- App.tsx refactored to use AppContext.Provider + BrowserRouter

Backend changes:
- 6 new proxy endpoints: /api/news, /api/market/indices,
  /api/research/profile, /api/research/metrics,
  /api/research/earnings, /api/screener
- config/index.ts: FMP_API_KEY env var added

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-03 23:50:01 -07:00
4cfb446f57 feat(backend): WebSocket namespaces, audit persistence, tab flags, telemetry
- Add /trading and /admin named Socket.IO namespaces; root namespace kept for
  backward compat; admin namespace rejects non-admins at connect time
- Wire auditRepository.ts: persist TradeAuditEvent to Cosmos audit-events
  container (best-effort); expose GET /api/admin/audit for admin queries
- Add tradingTelemetry singleton (Node.js Map-based storage adapter); init
  and fatal-error tracking wired in index.ts main()
- Add TAB_MARKETPLACE_ENABLED / TAB_MEMBERSHIP_ENABLED config flags; expose
  tabs.* shape in GET /api/feature-flags response
- Fix SupabaseService URL validation (regex check before createClient)
- Wire check:api-contract and check:audit-repository into npm run test
- Switch @bytelyst/* deps to file:../vendor/* references

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 19:35:00 -04:00
aaa516122e feat(backend): wire Azure Key Vault secret resolution at startup
- Add bootstrap.ts as new entry point — resolves Key Vault secrets via
  DefaultAzureCredential before config/index.ts is evaluated, so all
  process.env reads pick up KV values (Azure CLI in dev, Managed Identity
  in prod). Falls back to .env if AZURE_KEYVAULT_URL is not set.
- Define INVTTRDG_SECRETS mappings for Cosmos, Azure OpenAI, product-id
- Add AZURE_OPENAI_ENDPOINT / KEY / DEPLOYMENT to config
- aiClient: prefer AzureOpenAIProvider (AI Foundry) when Azure OpenAI
  config is present; falls back to direct OpenAI if not configured
- Add @azure/identity, @azure/keyvault-secrets, @bytelyst/config deps
- Update dev/start scripts to use bootstrap.ts entry point
- Document AZURE_KEYVAULT_URL and Azure OpenAI vars in .env.example

Key Vault: https://kv-mywisprai.vault.azure.net/
Secrets prefix: invttrdg-*

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 18:28:47 -07:00
c3651f5696 feat(backend): replace custom aiClient with @bytelyst/llm platform package
- Import PerplexityProvider, OpenAIProvider, GeminiProvider from @bytelyst/llm
- Use createFallbackChain() instead of manual axios fallback loop
- Remove axios and @types/axios — no longer needed
- Preserve AIClient class interface (generateAnalysis, getProviderHealth) —
  no changes required in apiServer.ts or AIAnalysisRule.ts
- Fallback order still driven by config.AI.FALLBACK_LIST

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 12:50:34 -07:00
7884639876 refactor(backend): route legacy Postgres access through legacySupabaseClient
Made-with: Cursor
2026-04-04 19:30:15 -07:00
72ec663125 refactor(backend): extract UserConfig to tradingUserTypes
Made-with: Cursor
2026-04-04 19:02:45 -07:00
12cedd12da fix(backend): route alerts admin scoping through isTradingAdmin
Made-with: Cursor
2026-04-04 18:58:06 -07:00
b306f3264e refactor(backend): extract shared trading persistence types from SupabaseService
Made-with: Cursor
2026-04-04 18:56:20 -07:00
774541289a refactor(backend): resolve legacy Supabase client inside capital ledger repository
Made-with: Cursor
2026-04-04 18:53:29 -07:00
f0dd2055bf refactor(backend): resolve legacy Supabase client inside user, profile, and snapshot repositories
Made-with: Cursor
2026-04-04 18:47:23 -07:00