Exchange Tools

← Back

Exchange Feeds

You can support this site by registering using any of the exchange affiliate links for lifetime 10% off all of your trading fees.

Ticker--
Order Book
PriceSize
PriceSize
Trades
TimePriceSizeSide
Ticker--
Order Book
PriceSize
PriceSize
Trades
TimePriceSizeSide

Architecture Overview

Client-side System Architecture

System architecture diagram showing FE-BE-Exchange data flow with the client-side data collector variation

Data Flow & Normalization

  • After the WASM bundle loads, the browser opens WebSocket connections directly to each exchange's public market data API. The server is not in the data path at all
  • Raw market data arrives in each exchange's proprietary format; the exchange-api crate, compiled into the WASM bundle, normalizes it into StreamData variants entirely client-side
  • Each variant is dispatched into a per-exchange, per-type signal: trades, ticker, and orderbook deltas each drive their own reactive source independently

Reactive State Management

  • Leptos signals are the unit of reactivity: writing to a signal re-runs only the closures that read it, with no virtual DOM diffing or component re-renders
  • Orderbook state is a client-side BTreeMap keyed by price level; delta messages patch it in-place, and full snapshots replace it when the exchange emits one
  • The snapshot frequency and diffing strategy varies by exchange and is defined in each exchange-specific crate within exchange-api; the WASM client applies whichever sequence the exchange sends without needing to know the strategy upfront
  • After each patch a derived signal produces a sorted, depth-capped view that drives the orderbook panel directly, with no intermediate data transformation layer

Connection Handling

  • WebSocket connections to each exchange are opened after hydration and reconnect automatically on unclean drops
  • Each exchange connection is managed independently; a drop or error on one feed does not affect the others
  • The client sends periodic pings on each connection to detect stale feeds from network drops or backgrounded tabs

Tradeoffs

  • Each browser tab opens its own direct WebSocket connections to all exchanges: N open tabs means N×(number of exchanges) concurrent exchange connections with no server-side fanout
  • Exchange APIs may restrict browser origins via CORS or rate-limit by IP; the architecture depends on exchanges permitting direct browser WebSocket access
  • The exchange-api normalization code ships inside the WASM bundle, increasing its size compared to a thin client that only deserializes pre-normalized messages
  • All market state and normalization logic lives in browser memory per tab. No shared server state means no snapshot cache for late-joining clients
  • Zero server egress cost for market data: the server only serves the initial HTML and WASM bundle, after which exchange bandwidth is consumed directly by each client

Upcoming Redesign

  • Phase 1: Frontend Improvements
    • Feed source toggle. A runtime control to switch between server-mediated and direct-exchange (client-side WASM) feed modes. Both architectures are already implemented; surfacing the switch as a first-class UI option lets users choose lower latency (direct) versus lower client resource usage (server-fed).
    • Symbol selection. A UI control to switch the streamed trading pair at runtime, driving the WebSocket subscription and resetting local orderbook state.
    • Unified cross-exchange orderbook. Because all exchanges are normalized to the same StreamData types, bids and asks from multiple exchanges can be merge-sorted into a single book with an exchange column on each level. Price overlaps between exchanges (best ask on A below best bid on B) become immediately visible, laying the groundwork for cross-exchange arbitrage detection.
    • Performance and memory optimization.
      • Configurable ingress and egress rates for both client-side (exchange WebSocket) and server-side (normalizer broadcast) data flows, reducing CPU and memory pressure under high update frequencies. Rate controls exposed as runtime config so they can be tuned without redeployment.
      • Client-side orderbook depth limiting: discard any levels beyond the visible depth immediately on receipt rather than storing them. Important because some exchanges do not support intermediate depths (e.g. Bybit offers depth 1 or 50 but nothing in between), so the WASM client may receive far more data than the UI can display.
      • Robust server-side rate limiting on the normalized data pipeline, with per-symbol and per-consumer throttle controls to prevent a high-frequency pair from crowding out others.
      • In-browser shared data layer using SharedWorker or BroadcastChannel: a single tab holds the exchange WebSocket connections and fans data out to all other tabs on the same origin, eliminating the N-tabs-N-connections problem. Tabs joining later receive the cached snapshot from the worker rather than waiting for the next exchange update.
    • Distributable WebComponent. The orderbook panel, trade feed, delta application logic, and WebSocket handling packaged as a self-contained Custom Element. A wasm-bindgen-exposed mount function initialises the Leptos component into a shadow root (for CSS isolation), and a thin JS wrapper maps element attributes to config. The result is a drop-in bundle: <exchange-feed exchange="bybit" symbol="ETHUSDC">, embeddable on any page without a Leptos host.
  • Phase 2: Decoupled Data Collection
    • Standalone collector service. Move exchange connections out of the web server into a dedicated process. All backend instances subscribe to a single shared feed, eliminating redundant exchange connections as the server tier scales out.
    • Client-side mode as fallback. If the collector service is unavailable, the frontend can transparently fall back to direct-exchange WebSocket connections. The existing client-side WASM architecture makes this possible without a separate code path.
  • Phase 3: Redis Pub/Sub Layer
    • Fanout and decoupling. A Redis node sits between the collector and backends. Backends subscribe to pub/sub channels to receive normalized data without a direct dependency on the collector process.
    • Per-symbol channel granularity. Each trading pair gets its own channel, so a backend only receives updates for the symbols it actually needs rather than the full firehose.
    • Geographic distribution. Deploying Redis instances in multiple regions reduces the data relay hop for backends co-located with those nodes, cutting end-to-end latency for geographically distributed deployments.
  • Phase 4: Kafka & Persistence
    • Event log alongside Redis. Kafka captures the full normalized event history in parallel with the real-time Redis feed. TimescaleDB consumes the Kafka topic for durable time-series storage.
    • Historical data API. A query endpoint over TimescaleDB lets the frontend load past orderbook snapshots, trade history, and OHLCV aggregates — enabling chart overlays and replay directly in the existing UI.
    • Long-term compaction. Tick-level data is progressively rolled up into OHLCV candles at configurable resolutions, keeping raw storage manageable while preserving queryable history for backtesting.
Want to support this site?Showing crypto-only, tracker-free ads helps keep it free. You can change this anytime in settings (⚙).