Console

August 3, 2026 · View on GitHub

Status: SPEC + v1.1 + phase 2 implemented (web/console.html + cmd/obwasm; cmd/obdash) · Author: Karthikeyan NG · Last updated: 2026-08-03

Companion documents:

  • DEMO-SPEC.md — the teaching demo (scripted, narrated). This page is the showcase: a running market, every feature live.
  • research-roadmap.md — the signals shown here and what they honestly do and do not predict.
  • THREAT-MODEL.md — the surveillance detectors the console lets you trip on purpose.

1. Why, and why this shape

The question this answers: can someone who has never read the code see everything the library does — and find the exact call that does it — in under a minute?

The reference point is VisualHFT: an open-source desktop app that visualizes real-time market microstructure — depth ladder, order-flow analytics, execution quality, alerts — fed by exchange connectors. It is the right idea for a showcase and the wrong architecture for this project, three times over:

  1. It needs a market to connect to. This library is the market. There is nothing to connect: the engine, the agents that trade against it, the signals, and the surveillance all compile to WebAssembly and run in the page.
  2. It needs an install. The showcase's job is "look, quickly" — a static page on the existing GitHub Pages site, zero install, zero server, zero new dependency.
  3. It shows markets, not code. This console's second job is adaptability: every panel names the library call that produced it, verbatim, so the path from "I see the depth ladder" to engine.Snapshot(10) is one glance, not a repository search.

The decision, therefore: a browser console driven by the WASM engine and the deterministic simulator — the real engine matching, real sim.NoiseTrader agents providing continuous flow, real signals and surveillance code computing what the panels show. Nothing in the page reimplements library logic; the page is a renderer.

The operator dashboard against a live obgw (the true VisualHFT analogue for operators) was phase 2, and is now implemented as cmd/obdash — with one decision worth recording: SSE, not websockets. The dashboard is strictly one-way; EventSource reconnects natively with the retry interval the server names; it is plain HTTP through every proxy an ops network has; and it costs zero dependencies. A websocket buys back none of that for this traffic shape.

obdash is deliberately a sidecar, not a feature of the venue: an ordinary market-data subscriber over the venue's own wire protocol (fresh subscribe on every reconnect — a dashboard owes nothing to its history) plus a reader of the admin /metrics page. obgw gains no code, no port, no attack surface — and the market-data protocol gets what PROTOCOL.md always claimed it supports, a subscriber written from the format alone, living outside the venue's test tree. The page leans on RUNBOOKS.md's two first-look signals: queue depth against capacity with the 75% alert threshold drawn on the meter, and the sequence rate. A disconnected feed or failed scrape is shown as exactly that, never as the last good number — and a venue that has not published an MDStatus since the subscriber joined shows "no MDStatus yet", because status is published on change and a fresh subscriber honestly does not know.

2. Panels, and the call each one names

Every panel header carries the producing call. That mapping is the spec:

PanelWhat it showsThe call it names
Depth laddertop-10 bids/asks, size bars, mid/spread/lastengine.Snapshot(depth)
Tapelast trades, aggressor-colored, sizedMatchResult.Trades, Trade.TakerSide
Pricemid sparkline with last-trade ticksSnapshot.Mid
OFIcumulative order-flow imbalance sparklinesignals.NewOFI().Observe(snap)
CVDcumulative volume delta sparklinesignals.NewCVD().Observe(trades)
Imbalancetop-5 depth imbalance, signed gaugesignals.DepthImbalance(snap, 5)
Kyle λrolling price-impact fit, λ and R²signals.EstimateLambda(flow, dPrice)
Surveillancelive alert feedsurveillance.NewMonitor(...).Observe(ev)
Trade as "you"limit/market entry, resting orders with cancel, ● markers on the ladderengine.Process, engine.OpenOrdersFor, engine.Cancel
Market barmid/spread/last/step and the book digestEngineSnapshot.Digest
Controlsrun/pause/speed/seed, spoof, flood

The spoof and flood buttons are the showcase's teeth. Spoof places layered away-from-touch size under a throwaway account and cancels it seconds later; the SpoofDetector names the account. Flood fires a burst of far-from-touch IOC placements that never rest and never fill — quote stuffing's signature — and the OTRDetector prints its own arithmetic ("30 orders / 0 fills = OTR 30.0, limit 15.0"). The visitor manipulates a market and watches surveillance catch it, in a browser tab, with the shipping detectors — no mock alert, no scripted timeline.

The digest in the market bar makes the determinism claim falsifiable from the page: same seed, same number of steps, same EngineSnapshot.Digest — on any machine, in any browser.

Honesty rules carried over from the research write-ups: the OFI panel says contemporaneous, not predictive where it shows the signal (the study found a ~540× R² gap); the λ panel shows R² beside λ rather than implying a clean constant; the console never claims the noise-trader market contains exploitable signal.

3. The bridge (cmd/obwasm), extended

Existing: obReset, obSubmit, obSnapshot. Added, all returning JSON strings:

  • obStep(n) — advance the simulation n steps: each step the sim.NoiseTrader agents act on a sim.View and their orders go through engine.Process; trades feed CVD/tick-rule/λ buckets and the surveillance monitor; the snapshot feeds OFI. Returns the step count, new trades, and the sequence — the page renders at animation-frame cadence and calls this per frame.
  • obSubmit — unchanged signature, now also returns the order id (so the page can cancel), and user orders flow through the same signal/surveillance path as agent orders. A visitor's spoof is observed exactly like anything else.
  • obCancel(id, user)engine.Cancel, ownership enforced, observed by surveillance as OrderCancelled.
  • obSignals() — current OFI cumulative, CVD, top-5 and best imbalance, rolling λ fit (value, R², points), mid/spread/last.
  • obAlerts(since) — surveillance alerts after index since, so the page drains incrementally.
  • obReset(seed) — rebuild engine, agents, signal state from a seed. Same seed, same market, every time — determinism is a feature the console demonstrates by putting the seed in the UI.

The bridge holds the step loop rather than calling sim.Run because the console needs a market that advances while the page breathes; it still uses the real sim.Agent/sim.NoiseTrader/sim.View types, so the flow is the study harness's flow, not a lookalike.

4. Visual bar

The site's existing tokens (web/style.css — GitHub-dark palette, --bid/--ask greens and reds, light-theme aware) are the console's tokens; the console reads as another page of the same product, not a bolted-on toy. Canvas sparklines, no chart library, no external requests. Numbers are set in the monospace stack. Nothing animates that data did not change.

5. Non-goals (v1 console)

  • No obgw feedshipped as phase 2, see §1 (cmd/obdash).
  • No latency histograms in the page. WASM-in-a-browser timings would be noise presented as measurement; the honest numbers live in BENCHMARKS.md and the console links them instead of faking its own.
  • No strategy PnL / backtest UI. pkg/backtest exists, but a PnL panel invites "the demo strategy makes money" readings the research docs explicitly refuse.
  • No mobile-first layout. It degrades acceptably; a ladder wants a desktop.