Skip to main content

Reconnect Strategy

WebSocket clients must assume that disconnects happen. Correct recovery is more important than a long-lived connection.

Reconnect loop​

Use exponential backoff with jitter. The session frame's reconnectPolicy carries the server's baseBackoffMs, jitterMs and maxBackoffMs (on 2026-09-14 the public endpoint advertised 250 / 500 / 10000 ms); read them from the frame rather than from this table.

AttemptDelay
1baseBackoffMs (250 ms)
2500 ms
31 s
42 s
5+Double until maxBackoffMs, plus up to jitterMs of jitter

Respect a rotate frame: it announces a planned disconnect before rotateByMs and includes the resubscribe journal, so a rotation costs one reconnect and no gap.

Public stream recovery​

  1. Stop applying deltas when prev_seq is not the last seq you applied or the topOfBookChecksum no longer matches.
  2. Resubscribe with fromSeq set to the last good sequence.
  3. If the server answers resume_required, fetch GET /api/v1/public/markets/{marketId}/orderbook?depth=N&book=SPOT|YES|NO (levels are {price_micros, total_qty} with total_qty in base-asset micro-units), drop frames at or below the snapshot sequence, and continue.
  4. Resume normal processing once continuity is restored.

Private stream recovery​

  1. Reconnect, re-authenticate with a fresh spws1… token (issue a new one if the old one expired).
  2. Resubscribe every channel with fromSeq = last processed sequence, sent as a u64 decimal string; on orderUpdates add cursorNamespace: "execution-v1" (error 1004 without it). Keep cursors inside their namespace — never derive one from another by ±1. The server replays retained events and sends snapshot_end.
  3. Deduplicate by (channel, seq); the replay may include events you already handled.
  4. On resume_required (1005) — expected right after a deploy, even with a fresh cursor — rebuild from HTTP as the source of truth — /api/v1/accounts/{account}/orders, /fills, /balances, and /api/v1/bsl/accounts/{account}/executions?cursor= for the execution log — then resubscribe from oldestAvailableSeq or later.
  5. Resume strategy processing only after reconciliation; pause quoting for the account in the meantime.

Replay on resume​

Replay is served ring-first from an in-memory, per-account buffer bounded by an event count and a retention window, with a transparent SQL backfill for older ranges. Sequence numbers and ordering are identical either way; the produce path is database-write-first, the ring only removes the database read from consumer fan-out.

Nonce window after a reconnect​

A reconnect does not change the account's nonce window, but a crash usually loses in-flight state. The recommended market-maker recovery is bootstrap → cancel-all → nonce fence → resume pipelining (see Order Concurrency & Nonces). Do not resume quoting from a stale local nonce counter.

BSL cancel-on-disconnect​

WebSocket reconnects do not cancel orders. Liquidity providers configure cancel-on-disconnect on the lane that carries their orders: FIX and FIXP credentials (cancelOnDisconnect on the credential), BSL session keys (cancelOnDisconnect in the session-key policy), or the explicit POST /api/v1/bsl/orders/cancel-all recovery call for HTTP and wallet-signed Direct TCP flows, which have no automatic sweep.

For a heartbeat-based sweep independent of the socket, arm the dead-man switch (POST /api/v1/bsl/dms, session-key signed; timeoutMs 100–60000). The session-key policy's allowedActions must include "cancel", otherwise arming answers DMS_AUTH_REJECTED; orders opt in per frame via dmsEpoch. Verified 2026-09-13: once heartbeats stop, the opted-in order is cancelled within the timeout. See Dead-Man Switch.

See BSL for market makers.