Architecture
┌──────────────┐ SSE /api/market/stream ┌──────────────┐
Browser ──────▶│ Next.js web │──────── rewrite /api ──────▶│ FastAPI api │──▶ Postgres (candles, trades, book,
(owner) │ :8471 │ │ :8470 │ runs, audit, risk, paper ledger)
└──────────────┘ └──────┬───────┘
│ Redis pub/sub `ktrade:md`, health keys
┌──────────────┐ REST /0/public/* (pairs, OHLC) │
Kraken ───────▶│ ingest worker│────────────────────────────────────┘
REST + WS v2 │ (ktrade worker) WS v2 ticker/book/trade/ohlc → Redis + Postgres
└──────────────┘Event-time semantics. Candles are stored by open time with a closed flag computed from exchange time;
strategies only ever see closed bars; the backtester executes at the next bar's open. WS messages carry the
exchange timestamp and the local received_at; book validity is tracked per update (CRC32 checksum).
Shared interfaces. Strategy.run(df) → target exposure and RiskGateway.evaluate(intent, portfolio, market)
are the same objects used by the backtester, the paper simulator (Phase 4) and the live adapter (Phase 5).
CostModel (fees/spread/slippage/impact/participation/latency) is shared between backtest and paper fills.
Modules (apps/api/ktrade): kraken/ (rest, ws, checksum, models) · ingestion/ (pairs, backfill, stream) ·
market/ (queries, features) · strategy/ (SDK, plugins, registry) · backtest/ (costs, engine, metrics,
validation, runner) · risk/ (gateway) · execution/ (preflight; paper/live to come) · api/ (auth, routes) ·
workers/ (ingest) · db/ (pool, migrations, audit) · cli.py.
Processes in production (PM2 on the MacLustr node): crypto-trade-bot-api (uvicorn), crypto-trade-bot-worker,
crypto-trade-bot-web (next start). Public route: MacLustr Tunnel (Caddy on BHS64) → web :8471 → /api rewrite → :8470.
Scaling path. Ingestion is per-universe; a second worker can own a disjoint universe (Redis channel is shared).
Backtests run in-process via asyncio.create_task; a queue (Redis/RQ) is the next step when runs get long.