Repository navigation
v0.5.0 — Phase 5: reporter + dashboard (15/15)
Persistence and dashboard. The trading binary keeps writing in-process; a new pinned-thread reporter ships events to Postgres; a Go sidecar exposes REST + WebSocket; a React + TypeScript frontend renders the panels.
Verification status: 15/15 items in the Phase 5 fix pass are DONE, audited against the source, and all tests run live (including the two Postgres-dependent ones that previously skipped).
What is in
Persistence (C++)
- PgReporter (
src/db/pg_reporter.{hpp,cpp}): consumer thread pinned tocfg.reporter.core. Pimpl over libpqxx so the header is pqxx-free. DSN only fromSPREADARA_PG_DSN, never config, never logged. Empty DSN → dry mode, trading keeps running. - Connection pool of
cfg.reporter.pg_pool_minconnections (default 2), round-robin per flush, lazy reconnect on failure of a single connection. - Batched flush: whole-batch transaction → retry whole-batch on failure → per-event single-row fallback (poison-pill skip). One bad event no longer kills a whole batch — verified live by
PoisonPillRetainsGoodEvents. - Schema (
trades,position_snapshots,system_events,daily_pnl) created idempotently viaCREATE TABLE IF NOT EXISTSon connection 0 at startup. Verified live bySchemaCreationIdempotent.ts_nscolumns store raw nanoseconds-since-epoch asBIGINT; dashboard converts client-side. - Daily P&L coalesces in-memory; flushes the closed-out day's final totals on UTC date rollover (flush BEFORE updating
current_day_— a bug fix caught during review) and on shutdown drain. Test asserts flushed CONTENT, not just count. - Producer wire-up:
OrderManager::inject_fillpushesDbTrade;CircuitBreaker::set_halt_synchronouslypushes criticalDbSystemEvent;RiskManager::pre_trade_checkpushes warnDbSystemEventon every rejection;main.cpppushesDbPositionSnapshot+DbDailyPnlevery second.
Shutdown safety
shutdown_cancel_allinOrderManagercancels live orders and (if|inv| > flatten_threshold) sends a MARKET/IOC flatten viaRestClient::place_market_order. Invoked from the SIGINT handler BEFOREclient.stop()/ioc.stop()so the REST client is still operational. Shares acancel_and_flatten_lockedhelper withon_halt.flattenedlog flag reflects actual market-orderack.ok.
REST client refinements
place_market_orderfor the flatten path (LIMIT + GTX is wrong for halt).- Price →
min_tick, qty →qty_steprounding before formatting (BinancePRICE_FILTER/LOT_SIZE). AmendAck::cancelled_onlysurfaces the half-state when cancel succeeds but the replacement place fails; pushes a criticalSystemEvent. Slot-side wiring intentionally deferred to Phase 6's FIX/REST hybrid — documented at the half-state site so future readers don't mistake it for an oversight.- Suppressed double-log on curl transport failures (
stage=http http=0skipped when curl already loggedstage=curl).
Dashboard backend (Go)
dashboard_backend/: standard librarynet/http+github.com/lib/pqonly (CGO_ENABLED=0). EndpointsGET /api/snapshot,/api/trades,/api/pnl/daily,/api/events,GET /ws. CORS allowlist forlocalhost/127.0.0.1+ configurable production origin.- Manual RFC 6455: handshake via
Hijacker,Sec-WebSocket-Version=13check (426 on mismatch), origin allowlist enforced at upgrade (CORS does not gate WS), ping → pong dispatched via channel so the writer goroutine stays the sole writer on the connection.
Frontend (React + TypeScript)
dashboard/: Vite + React + TypeScript scaffold, plain CSS, dark background, 8 panels (quotes, inventory, P&L, circuit breaker, fill rate, latency, trades, events). WebSocket client with exponential-backoff reconnect. Nonpm installperformed; run it yourself when you want to develop.
Tests
Six C++ binaries (test_orderbook, test_spsc, test_strategy, test_risk, test_execution, test_reporter) pass under -O3 -march=native -Wall -Wextra -Werror. Go tests pass (6 cases including version-reject, origin-reject, push interval). With SPREADARA_PG_DSN exported and the chronocoder Postgres role in place, the two live tests (SchemaCreationIdempotent, PoisonPillRetainsGoodEvents) execute against a real database — not GTEST_SKIP.
One-time local setup for live Postgres tests
sudo -u postgres psql -c "CREATE ROLE chronocoder LOGIN; CREATE DATABASE chronocoder OWNER chronocoder;"
export SPREADARA_PG_DSN="host=/var/run/postgresql dbname=chronocoder user=chronocoder"
Known limitations
daily_pnlcarries cumulative-since-startup totals, not daily deltas.SUM(total) FROM daily_pnlover-counts when used ascum_totalin/api/snapshot. Dashboard math fix is a Phase 6 follow-up.amend_orderslot-side wiring is dead code —RestClient::amend_orderisn't called fromOrderManagerin Phase 5. TheAmendAck::cancelled_onlyplumbing, half-state log, and SystemEvent push are all in place for the Phase 6 FIX/REST hybrid; the missing slot transition is documented at the half-state site.- Phase 7 geo-block still applies to
fapi.binance.comREST from the current egress. WS resync remains the working fallback.
Not in scope yet
No user-data stream wiring fills back into inject_fill, no FIX execution, no live deployment. Phase 6+.