/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 ids —BTC-USD,ETH-USD, … (the value oncrypto.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
How to consume it
- Fetch the curve once —
GET /api/markets/{symbol}/crypto-price-history?range=10m. - Subscribe to
crypto_price_tickfor that market’sinstrumentId. - Append each tick at its own
at, dropping any tick not newer than the last point you hold.
(instrumentId, at) and duplicates are possible
(multiple producer replicas relaying the same candle). De-duplicating by
at makes them harmless.
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 marketsymbols (lower-cased; the ack echoes them lower-cased)- Required scope: none
- Pushes:
crypto_window_opened,crypto_window_closed,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:
crypto_window_closed is sent once, when the settlement binds the
close, and is not re-sent if publishing fails — a window can resolve
without it. When it is published, the same value is
cryptoWindow.closePrice on GET /api/markets/{slug} (final only once
closePriceIsFinal is true); if it was not, closePrice stays
null. Don’t block on the frame: the outcome comes from
market_resolved and cryptoWindow.outcome.
Worked example
BigInt (or a decimal library) — an 18-decimal integer
does not fit a JS number.