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>
|
||
|---|---|---|
| .vscode | ||
| .windsurf/workflows | ||
| backend | ||
| docs | ||
| mobile | ||
| scripts | ||
| shared | ||
| web | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| .npmrc | ||
| .npmrc.docker | ||
| docker-compose.dev.yml | ||
| docker-compose.yml | ||
| package.json | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| README.md | ||
| tsconfig.base.json | ||
| vercel.json | ||
ByteLyst Investment Trading
Canonical monorepo for the ByteLyst trading product. Contains the backend trading engine, web dashboard, and Expo mobile app under a single pnpm workspace.
Workspaces
| Package | Path | Description |
|---|---|---|
@bytelyst/trading-backend |
backend/ |
Node.js trading engine, REST API, Socket.IO |
@bytelyst/trading-web |
web/ |
React 19 dashboard (Vite) |
@bytelyst/trading-mobile |
mobile/ |
Expo 54 React Native companion app |
| shared types | shared/ |
Cross-surface constants, interfaces, and helpers |
Core Principles
- Backend-authoritative state — all trading state (orders, positions, capital) lives in the backend; web/mobile only read and display
- Platform-service for auth (platform JWT), kill-switch, telemetry, and feature flags
- Cosmos DB is primary — Azure Cosmos DB is the production control-plane store; Supabase is legacy fallback only
- No duplicated bootstrap — auth, kill-switch, and telemetry bootstrap run once per surface via shared contracts
- Trading domain stays product-owned — strategy logic, risk rules, and execution never move to common-platform packages
Quick Start
pnpm run install:common-plat
cp .env.example .env # root — used by Docker Compose and CI
cp backend/.env.example backend/.env # backend — fill in Cosmos, exchange, and AI credentials
cp web/.env.example web/.env.local # web — Vite build-time API URLs
cp mobile/.env.example mobile/.env.local # mobile — Expo build-time API URLs
pnpm verify # typecheck + test + strict UI audit + build — must be green before any deploy
Common Commands
# Verification (run before every merge / deploy)
pnpm verify # typecheck + test + strict UI audit + build across all surfaces
pnpm lint # backend contract + security guards + web/mobile lint
pnpm smoke:release # auth + kill-switch smoke tests
# Development
pnpm --filter @bytelyst/trading-backend dev # backend hot-reload (tsx)
pnpm --filter @bytelyst/trading-web dev # web Vite dev server
pnpm --filter @bytelyst/trading-mobile dev # Expo dev server
# Docker
pnpm docker:up # production — build images, start backend + web
pnpm docker:dev # dev overlay — hot-reload for backend and web
pnpm docker:down # stop all containers
Backend Verification Scripts
Beyond pnpm verify, the backend has targeted contract and safety checks:
cd backend
npm run check:api-contract # feature-flag shapes, audit events, namespace constants
npm run check:websocket-contract # BotState lifecycle consistency
npm run check:security-guards # tenant isolation
npm run check:tenant-isolation # row-level access scoping
npm run check:strict-capital-guard # capital invariant enforcement
npm run check:lifecycle-regressions # trade lifecycle regression suite
Full list in backend/package.json under scripts.
Architecture
The backend trading loop polls every POLLING_INTERVAL (default 60 s). For each
user → profile → symbol it runs the 7-rule ProStrategyEngine, then routes signals
through AutoTrader → riskEngine → TradeExecutor → exchange connector.
7-Rule Pipeline (backend/src/strategies/rules/):
TrendBiasRule— EMA 50/200 trend filterSessionRule— market hours gatingZoneRule— price proximity to S/R zonesMomentumRule— RSI confirmationEntryTriggerRule— EMA reclaim / pattern detectionRiskManagementRule— ATR-based stop sizingAIAnalysisRule— LLM sentiment (Perplexity → OpenAI → Gemini fallback)
WebSocket namespaces (shared/realtime.ts):
/trading— all authenticated users; user-scoped BotState/admin— admin-only; full cross-user state; non-admins rejected at connect/(root) — backward-compatible default
Persistence — Azure Cosmos DB (primary) for all runtime paths. Supabase is a legacy
fallback for one-off reconciliation scripts only (documented in
docs/BACKEND_LEGACY_SUPABASE_SCRIPTS.md).
Documentation
| Doc | Purpose |
|---|---|
docs/PRD.md |
Product vision, scope, and platform integration boundaries |
docs/ROADMAP.md |
Phase tracker and implementation snapshot |
docs/OPERATIONS.md |
Local dev, Docker, verification, staged cutover, rollback rules |
docs/CONVENTIONS.md |
Naming, directory structure, and import boundary rules |
docs/BACKEND_AUDIT_SCHEMA.md |
TradeAuditEvent schema and event catalogue |
docs/BACKEND_API_DEPRECATION.md |
Full endpoint catalogue, WebSocket namespaces, deprecation policy |
docs/BACKEND_LEGACY_SUPABASE_SCRIPTS.md |
Legacy Supabase script inventory |
docs/CUTOVER_WEB.md |
Stage 2 — internal web adoption checklist |
docs/CUTOVER_MOBILE.md |
Stage 3 — mobile internal beta checklist |
docs/AZURE_INFRASTRUCTURE.md |
Azure resource and Key Vault setup |
Environment Setup
Each surface has its own .env.example. The root .env.example is the comprehensive reference covering all surfaces and is used by Docker Compose and CI:
| File | Usage |
|---|---|
.env.example |
Root — Docker Compose, CI, complete reference for all variables |
backend/.env.example |
Copy to backend/.env — trading engine config, secrets, feature flags |
web/.env.example |
Copy to web/.env.local — Vite build-time vars (API URLs, feature overrides) |
mobile/.env.example |
Copy to mobile/.env.local — Expo build-time vars (API URLs) |
Minimum backend variables to run locally:
| Variable | Required | Description |
|---|---|---|
COSMOS_ENDPOINT / COSMOS_KEY / COSMOS_DATABASE |
Yes | Primary data store (see docs/AZURE_INFRASTRUCTURE.md) |
PLATFORM_API_URL |
Yes | Platform-service base URL |
PLATFORM_AUTH_ENABLED |
— | true for platform JWT (RS256); false for local dev with JWT_SECRET |
PLATFORM_JWT_PUBLIC_KEY or PLATFORM_JWT_JWKS_URL |
Prod | Platform JWT verification key |
JWT_SECRET |
Dev | Legacy/local JWT secret when PLATFORM_AUTH_ENABLED=false |
ALPACA_API_KEY / ALPACA_API_SECRET |
For trading | Exchange credentials |
FMP_API_KEY |
For research/screener | Financial Modeling Prep key required by /api/research/* and /api/screener; the shared demo key is intentionally rejected by the backend |
OPENAI_API_KEY |
For AI rules | Primary LLM provider |
ENABLE_TRADING |
— | Set true to enable live order execution (default false) |
PAPER_TRADING |
— | Set true for paper mode |
AZURE_KEYVAULT_URL |
Prod | Enables auto-resolution of invttrdg-* secrets at startup |
Financial Modeling Prep requires apikey query-string authentication, so the
backend keeps all FMP calls server-side, rejects the shared demo key, caches
FMP responses for 30 minutes, hashes cache keys instead of storing raw URLs, and
uses redacted URL helpers for any future FMP log output.
Shared Dependencies
Package resolution is controlled by BYTELYST_PACKAGE_SOURCE through
.pnpmfile.cjs.
common-plat(default): resolve@bytelyst/*directly from the siblinglearning_ai_common_platcheckout, or fromBYTELYST_COMMON_PLAT_ROOT.vendor: prefervendor/bytelyst/*, then fall back to the siblinglearning_ai_common_platcheckout.
Examples:
pnpm run install:common-plat
pnpm run install:vendor
pnpm run check:packages
Override the sibling repo location with BYTELYST_COMMON_PLAT_ROOT=/path/to/learning_ai_common_plat.
Release Checklist
pnpm verify && pnpm lint && pnpm smoke:release
All three must be green. See docs/OPERATIONS.md for the full go/no-go criteria and
the staged cutover sequence (backend → web → mobile → production).