Risk channel
/ws/risk is the primary programmatic surface. It is bidirectional: the server
pushes state, and you can act on the same socket instead of falling back to
REST.
Nothing is pushed until you Subscribe to the
topics you want.
Frames the server pushes
Section titled “Frames the server pushes”| Frame | Carries |
|---|---|
Account-State-Update |
The account’s aggregate state. |
Balance-Update / Margin-Update / Funding-Update / Risk-Update |
Balance, margin, funding, and risk as they change. |
Position-Update / Position-Removed / Position-Snapshot |
Live position state; the snapshot seeds your view. |
Open-Orders-Update |
The working-order book for the account. |
Order-Intent / Order-Intent-Update |
Order lifecycle, from composed to terminal. |
Order-Update / Trade-Update |
Order state and closed round trips, in the external vocabulary. |
Fill |
One frame per execution — never coalesced, so nothing is collapsed away. |
Account-Event |
Lifecycle events on the account. |
Prop-Account-Update |
Challenge progress and status on a prop account. |
Blocking-Update, IP-Update, LoggedOff |
Session-level notices. |
Resync-Required |
Drop local state and re-snapshot. |
There is no server-pushed bracket frame. TP/SL leg state reaches you through
Open-Orders-Update and Order-Intent-Update like any other working order.
Frames you can send
Section titled “Frames you can send”Read requests — Get-Margin, Get-Balance, Get-Risk-Update,
Get-Position-Update, Get-open-orders, Request-Trade-History — plus
Subscribe / Unsubscribe to change topics and scope, and Resume to replay
after a gap.
Write frames mirror the REST OMS:
Order-Sign-Result, Cancel-Order-Intent, Replace-Order-Intent,
Bracket-Insert, Bracket-Modify, Bracket-Cancel, Position-Close. Each is
tier-gated.
There is no place-order frame. New orders start at
POST /api/v1/oms/intents; the socket carries the signature back
(Order-Sign-Result) and then everything that happens to the order afterwards.
Acting on a live order over the socket saves a round trip on a hot path; REST
is fine everywhere else and easier to retry safely.
Position-Close closes the whole position — passing qty is rejected with
partial_close_unsupported — and it accepts only hyperliquid, aster and
polymarket. Flatten anything else through POST /api/v1/oms/flatten.
Sequence numbers — detect a gap
Section titled “Sequence numbers — detect a gap”Every pushed frame that leaves the per-socket outbox carries a monotonic
integer seq, starting at 1 for the life of the connection.
if (frame.seq !== undefined) { if (lastSeq !== null && frame.seq !== lastSeq + 1) { await resnapshot() // a frame was dropped or coalesced away } lastSeq = frame.seq}A gap means one or more frames were dropped or coalesced — the server
merges rapid updates for the same key under load. Re-request the relevant
Get-* snapshot rather than trying to reconstruct the missing step.
Control frames sent outside the outbox — Pong and the Authenticated
handshake — are deliberately unsequenced. Only assert contiguity over frames
that carry seq. A new connection restarts the lane at 1.
Tracking an order to its outcome
Section titled “Tracking an order to its outcome”Subscribe { topics: ["Order-Intent-Changed"] }before you place, so no transition happens while you are unsubscribed.POST /api/v1/oms/intentsreturns an intent id.Order-Intent-Updatecarries the venue order id, then the fills, then the terminal state. The stream is account-scoped, not per-intent — there is nointent.<id>topic — so match each frame on the intent id you were handed.
This is the intended pattern — polling an order endpoint in a loop will always lag the socket and costs you rate limit.