Skip to main content
Two pushes serve crypto markets, both on the public /ws/market gateway: Both are display feeds: they are unsigned, ephemeral, and always re-confirmed by the read model on the next REST fetch. Never treat a frame as settlement evidence — the authoritative frozen open is cryptoWindow.openPrice on the market payload.

crypto_price_tick

  • Gateway: /ws/market
  • ids: one or more crypto instrument idsBTC-USD, ETH-USD, … (the value on crypto.instrumentId / event.cryptoSeries.instrumentId). Case-insensitive; the ack echoes them lower-cased.
  • Required scope: none. The market gateway also accepts anonymous handshakes, so a signed-out browser can subscribe.
  • Pushes: crypto_price_tick
Keyed by instrument, not market symbol — every strike, ladder rung and window over the same asset shares one feed, so a page showing ten BTC markets needs exactly one subscription.

How to consume it

  1. Fetch the curve once — GET /api/markets/{symbol}/crypto-price-history?range=10m.
  2. Subscribe to crypto_price_tick for that market’s instrumentId.
  3. Append each tick at its own at, dropping any tick not newer than the last point you hold.
There is no backfill on subscribe — the consumer binds to new messages only, so a reconnecting client is never replayed a stale backlog. Re-fetch the REST curve after a reconnect instead. Ticks are keyed by (instrumentId, at) and duplicates are possible (multiple producer replicas relaying the same candle). De-duplicating by at makes them harmless.
Silence is not zero. Ticks flow only while a producer is active for that instrument: the live feed runs while an Up/Down window is open on the asset, and the candle feed while a long-dated rule’s observation window is open. An idle instrument simply emits nothing — don’t render “no data” as a price change, and don’t infer a market state from tick silence.
The gateway drops ticks that arrive too late to be useful (about 3 minutes for the live feed, 6 for the candle feed) rather than drawing a stale point on a live chart. The next tick covers the gap.

crypto_window_opened

Fires once per Up/Down window, when the oracle freezes the window’s open price — the price the window’s close is measured against. The pin is write-once, so the value in this frame is final. It rides the per-market market_lifecycle channel, so a window’s page needs one subscription for the open pin, the status transitions and the resolution:
  • Gateway: /ws/market
  • ids: one or more market symbols (lower-cased; the ack echoes them lower-cased)
  • Required scope: none
  • Pushes: crypto_window_opened, status_changed, market_disputed, market_resolved
A frame missing price or decimals is dropped by the gateway rather than forwarded — a blank open would be indistinguishable from “not frozen yet”, which is a different state. The other frames on this channel are market-scoped lifecycle events:

Worked example

Scale through BigInt (or a decimal library) — an 18-decimal integer does not fit a JS number.