Agent-readable docs index: /llms.txt. Full docs in one file: /llms-full.txt. Download /docs.zip to grep all markdown files locally.

WebSocket

GET /v1/ws upgrades to a WebSocket. Anyone may connect. For your account's channel, pass a session token at connect, as Authorization: Bearer <token> or ?token=<token>.
The first frame on every connection is a hello giving the stream position the gateway is at:
{ "type": "hello", "seq": 1834, "pos": 918274 }

Subscribing

{ "op": "subscribe", "channel": "book:7:859008923130267914" } { "op": "unsubscribe", "channel": "book:7:859008923130267914" }
ChannelWhat it carries
book:<underlyingKey>:<specifier>One contract's book: a snapshot, then every changed level
book:<underlyingKey>Every contract of one underlying: a snapshot of all of them, then every changed level
prints:<underlyingKey>:<specifier>One contract's trades
reference:<underlyingKey>The underlying's reference values and price
account:<address>Your account's events. Needs a session for that address.
Keys and specifiers are decimal. A connection holds up to 256 channels.
The answer to a subscribe is a book snapshot for a book: channel, and otherwise:
{ "type": "subscribed", "channel": "prints:7:859008923130267914", "pos": 918274 }
Errors come back as {"type": "error", "channel": "…", "error": "…"}.

Books

A book:<uk>:<spec> snapshot:
{ "type": "book", "channel": "book:7:859008923130267914", "pos": 918274, "seq": 1834, "bids": [{ "price": 19000, "quantity": 40 }], "asks": [{ "price": 20000, "quantity": 15 }] }
A book:<uk> snapshot holds every contract, by specifier:
{ "type": "book", "channel": "book:7", "pos": 918274, "seq": 1834, "instruments": [ { "specifier": 859008921287065866, "bids": [], "asks": [{ "price": 31000, "quantity": 5 }] } ] }
Then each changed level, carrying its new total. quantity: 0 means the level is empty. On a book:<uk> channel the frame also names the specifier.
{ "type": "level", "channel": "book:7", "pos": 918275, "seq": 1835, "specifier": 859008923130267914, "side": 0, "price": 19500, "quantity": 3 }
side is 0 for bids and 1 for asks. Every level frame after a snapshot has a pos at or after the snapshot's, so applying them in order keeps the book exact.

Events

Trades, reference prices and account events arrive as an event inside a frame:
{ "type": "fill", "channel": "prints:7:859008923130267914", "event": { "type": "fill", "…": "…" } }
Event typeChannelsCarries
fillprints:, account:price, size, side (the maker's), maker, taker, makerNonce, takerNonce, makerFee, takerFee
referencereference:price, valid
insertedaccount:An order rested: orderId, nonce, side, size, price
cancelledaccount:An order was cancelled
expiredaccount:An order's lifetime ended, including the remainder of a zero-lifetime order
refusedaccount:The engine refused an order: nonce, code
orderSettledaccount:A resting order ended at settlement
positionSettledaccount:A position settled: amount
fundsaccount:A deposit, withdrawal or other movement of cash: amount, freeCash
quoteRequestbook:, account:A request for quote on the contract: side, sided (whether a side is named), size. On book: channels without the requester; your own on your account: channel in full
Every event carries seq, ts (engine ms), pos, underlyingKey and specifier where they apply.
On your account: channel:
  • A fill carries your balances after it (makerBalances or takerBalances: freeCash, collateral, security), and effect: how much of the fill closed an existing position, how much opened a new one, and the position after.
  • You see only your own side's balances and effect. prints: channels carry neither.

Positions and recovery

  • pos is a position in the exchange's output stream. It is the same on every gateway, so it orders everything you receive.
  • seq is the sequence number of the input that caused the output.
  • A connection that falls more than 2048 frames behind is closed with code 4008, behind the stream: resubscribe. To recover, reconnect and resubscribe: the snapshots bring you back to an exact state.
  • The gateway pings every 20 seconds.