# Auditor response — 2026-04-25 — Step 1 audit complete + re-evaluation: ditch the full replay, propose a minimal polling watcher Step 1 audit complete. Re-evaluation strongly recommends a **lighter path** before any replay infrastructure. --- ## Step 1 audit — findings ### 1.1 Tick capture inventory | Venue | Symbol | Rows (2h36m) | Rate | Daily projection | |---|---|---:|---:|---:| | coinbase | BTC/USD | 12,542 | 1.34/s | ~115k rows/day, ~13 MB | | coinbase | ETH/USD | 11,199 | 1.19/s | ~103k rows/day, ~12 MB | | kraken | BTC/USD | 1,011 | 0.11/s | ~9k rows/day, ~1 MB | | kraken | ETH/USD | 707 | 0.08/s | ~6k rows/day, ~700 KB | - Format: clean JSONL, one quote per line, `{exchange, symbol, bid, ask, bidSize, askSize, ts, receivedAt}`. Replay-ready. - Time-sync: both files cover the same window to the second. Good. - **Limitation:** only ~2.6h of self-captured data exists. Insufficient for 24h or 7-day analysis. A self-replay validates the *pipeline* but cannot answer the viability question. ### 1.2 Engine module boundaries — REPLAY IS STRUCTURALLY EASY Pleasant surprise: - **`FeedBase`** (`engine/src/feeds/feed-base.ts`) already implements the abstraction I'd sketched in the prior Step 2 prompt. EventEmitter with `start/stop/health` semantics, Kraken/Coinbase feeds extend it. **No refactor needed** — just `extends FeedBase`. - **Time module** (`engine/src/time/index.ts`) is small (~50 lines), one source of truth, easy to inject a clock. - **Clock call sites:** 12 in business code, all going through `nowUtcMs()` / `nowMelb()`. One-line change in the time module to swap the clock source. - **Logger** uses `new Date()` directly (line 14 of `logger/index.ts`) — out of band, doesn't need changing. - **Ledger schema** is replay-friendly. `opportunities.id` is the only PK. Replay can write to a separate file. **Verdict:** if we did want a full replay, ~2 days of dev work, not a week. Plumbing is half-built. ### 1.3 Feed base is venue-extensible - Adding Binance / OKX = subclass FeedBase + 2 config lines. Each adapter ~150 lines. - Engine already loops over configured exchanges; nothing hardcoded. ### 1.4 Coinbase WS sizes — minor data quality note `coinbase-feed.ts` has a comment claiming sizes aren't published, but captured ticks show real sizes (e.g. `"bidSize":"0.00107135"`). Either the comment is stale or a code path was added later. Sizes are flowing. Worth clarifying later, not a blocker. ### 1.5 Vendor data research — held Did not deep-dive Tardis/Kaiko/CryptoChassis pricing. **Reason:** if the re-evaluation below is correct, we don't need vendor data for the first viability answer. ### 1.6 Capture coverage gaps - Self-capture: kraken + coinbase only, only since today's restart. - For 4 venues we either (a) build the 2 new feed adapters and self-capture, or (b) skip self-capture entirely. --- ## Re-evaluation: do we need a full replay system? **No, not for the question we're answering right now.** Replay is justified when you need to: - Backtest specific historical periods (e.g., a known volatile day) - Sweep parameters over the same data - Validate rigorously before going live For "is this strategy viable anywhere?" we just need to observe spreads across 4+ venues long enough to see if any pair-direction has a fat right tail. That doesn't need a replay engine. It needs a watcher. ### Three options ranked by speed | Option | What it does | Eng work | Wall-clock to answer | Cost | Confidence | |---|---|---|---|---|---| | **B — REST polling watcher** (zero engine changes) | Standalone script polls each venue's REST `bookTicker` 1Hz, logs JSONL, post-hoc analysis. | 1–2h dev | **2–8h observation** | $0 | Medium-high | | **A — Live WS multi-venue** (add 2 feeds to the existing engine) | New `BinanceFeed`, `OKXFeed` extending `FeedBase`. Engine captures all 4 venues live. Existing `/opportunities/stats` answers it. | 1–2 days dev | **24–48h observation after deploy** | $0 | High | | **Full replay + buy historical data** (original Step 2) | Replay infrastructure + 7 days of vendor L1 ticks for 4 venues. | 2–3 days dev + integration | **3–5 days end-to-end** | $60–$120 | Very high | ### Recommendation: **Option B first, then Option A if needed** Option B answers "could this strategy work somewhere across these 4 venues" in a single afternoon with no dev risk and zero cost. Signal is good enough to: - Eliminate venues clearly: if 6 hours of polling shows max gross spread at e.g. binance↔kraken < 5 bps, that direction is dead. - Identify candidates: if okx↔coinbase shows hour-long windows with > 50 bps spread, that's worth deeper look with Option A. If B identifies promising venue-pairs, **then** Option A for tick-level confirmation. Only if Option A finds qualifying patterns we want to replay against historical periods do we build full replay (Option C). ### Caveats for Option B 1. **USDT vs USD quote currency.** Kraken & Coinbase quote BTC/USD natively. Binance & OKX quote BTC/USDT. USDT trades within ~10 bps of USD. For a viability check at fee tiers of ~100 bps, the noise is small. Just disclose in the report. 2. **1Hz polling sub-samples.** A real arb window at 200ms duration gets a 20% chance of being caught. Frequent windows will be detected; rare instantaneous flickers may be missed. For "is this viable as a sustained strategy," the skew is in the right direction. 3. **REST rate limits.** All within bounds — 8 req/sec across 4 venues is well under all caps. --- ## Minimal-version dev prompt for Option B (DRAFTED, NOT SENT) > **Task: standalone REST polling watcher for cross-venue viability check (no engine changes)** > > Create a single small standalone script outside the existing `engine/` codebase. It does NOT modify the engine, does NOT touch the live ledger, does NOT use any existing modules. > > **Path:** new directory `tools/spread-watcher/` at repo root with `package.json`, `index.ts` (or `.mjs`), `README.md`. > > **What it does:** > 1. Once per second, in parallel, fetches top-of-book from public REST endpoints: > - Kraken: `GET https://api.kraken.com/0/public/Ticker?pair=XBTUSD,ETHUSD` > - Coinbase: `GET https://api.exchange.coinbase.com/products/{BTC,ETH}-USD/ticker` > - Binance: `GET https://api.binance.com/api/v3/ticker/bookTicker?symbols=["BTCUSDT","ETHUSDT"]` > - OKX: `GET https://www.okx.com/api/v5/market/tickers?instType=SPOT` (filter to BTC-USDT, ETH-USDT) > 2. Normalizes responses to one common schema: > `{ ts_ms, exchange, symbol_canonical, quote_ccy, bid, ask, bid_size, ask_size }` > where `symbol_canonical` is `BTC` or `ETH` and `quote_ccy` is `USD` or `USDT`. > 3. Appends each polling tick (one row per venue×symbol) to a daily-rotated JSONL file under `tools/spread-watcher/data/` (e.g. `watcher-2026-04-25.jsonl`). > 4. Handles errors gracefully: any single venue failing must not stop polling of others; log error count per venue per minute. Exponential backoff on persistent 429s. > 5. Runs as a long-lived process. SIGINT/SIGTERM clean shutdown. > > **Plus a separate analysis script:** `tools/spread-watcher/analyze.ts` (or `.mjs`): > - Reads JSONL files for a date range. > - For every (venue_buy, venue_sell, symbol_canonical, ts_bucket=1s), computes: > - `gross_spread_bps = (sell_bid − buy_ask) / mid × 10000` > - `gross_spread_after_basis_bps` (apply USDT/USD basis: assume USDT=USD until we have CEX rate; document the assumption) > - Outputs a markdown report: > - Summary table: per (currency, venue_buy → venue_sell): count, p50/p90/p99/max gross_spread_bps > - Top-50 highest gross_spread observations with timestamps > - Per-hour bucketed max spread chart (text histogram) > - **Ranking of venue-pairs by p99 gross_spread**, since p99 is what determines whether the strategy *ever* fires > > **Constraints:** > - No changes to anything under `engine/`, `dashboard/`, `config/`, `ops/`, or root-level files. > - No DB writes — JSONL only. > - No external deps beyond `node-fetch` (or native fetch). > - One small `package.json` scoped to `tools/spread-watcher/`. > - Output is reproducible: same input JSONL → same report. > > **Definition of done:** > - `cd tools/spread-watcher && npm install && npm run watch` starts the watcher and writes JSONL. > - After ≥1 hour, `npm run analyze -- --from --to ` produces a markdown report to stdout. > - 5 simple unit tests for response parsers (one per venue) using fixture JSON. > - README explains: how to start/stop, how to interpret the report, USDT/USD basis caveat. > > **Out of scope:** DB integration, dashboard UI, websocket connections, integration with the existing engine. --- ## What I need from you 1. **Approve Option B** (REST polling watcher) as the next move? Or skip B and go straight to Option A (live WS for 4 venues)? 2. **Approve the minimal-version dev prompt** above? Any scope changes? 3. **Should I do Tardis/Kaiko vendor pricing research now in parallel** (in case we need it later), or hold it until Option B results? Nothing sent to dev until you say go.