# Optimizing latency

Run three sockets and keep account snapshots and market data out of the
order-entry loop.

| Path | Purpose | Client key |
| --- | --- | --- |
| [`/v1/exchange/ws`](/api/exchange-websocket) | Place, modify, cancel | frame `id` |
| [`/v1/exchange/ws`](/api/exchange-websocket#maintenance-announcements) `maintenance` | Venue maintenance announcements | `topic`, then source `venue` |
| [`/v1/ws`](/api/websocket) `orderUpdates` | Order lifecycle | `cloid` |
| [`/v1/ws`](/api/websocket) `userFills` | Confirmed executions | `(venue, fillId)` |
| [`/v1/ws`](/api/websocket) `clearinghouseState` | Market catalog and account state | `data.type` |
| [`/v1/market-data/ws`](/api/market-data-websocket) `bbo`, `l2Book` | Market data | `(venue, coin, time)` |

:::warning\[Hyperliquid only]
The low-latency path is validated for `hyperliquid` venues. Lighter is
supported by the same API without these latency expectations.
:::

## Order entry

1. Send one order per frame and pipeline independent frames without waiting
   for responses. Keep at most eight frames in flight; at the limit Mosaiq
   pauses reading instead of rejecting.
2. Give every order frame a unique increasing `id` and match responses by
   `data.id`. Completion order is not guaranteed.
3. Give every placement a fresh `cloid`: `0x` plus 32 lowercase hex digits,
   not all zero.
4. Send a dependent cancel or modify on the same connection, after its place,
   with the same `cloid`. Ordering is keyed by `(venue, cloid)` and holds
   through the venue handoff without waiting for the acknowledgement.
5. Use a library that answers server Pings with the same payload on all three
   sockets.

On the execution socket, route `topic: "maintenance"` separately from
`channel: "post"` order responses. Maintenance snapshots replace the complete
source array and do not confirm order outcomes. Subscribe again after an
execution-socket reconnect.

A batch is not atomic and its items are answered by index; separate pipelined
frames are correlated by `id`.

## Lifecycle processing

Keep `/v1/ws` subscribed to `orderUpdates` and `userFills` and keep reading it
even when the strategy ignores messages. Write responses and lifecycle events
are independent, so an `orderUpdates` event can arrive before the matching
write response. Key orders by `cloid`, fills by `(venue, fillId)`, accept
duplicates, and make every transition idempotent. A successful write response
is an acknowledgement, not a fill.

`clearinghouseState` sends four snapshots on subscribe, then only the chunks
whose contents changed. Silence means unchanged, not disconnected. Set
`subscription.markets` to the assets your strategy uses to limit market data.
Keep the latest value of each `data.type` and never use these messages to
confirm individual orders. No topic orders events relative to another topic.

## Disconnect recovery

A dropped connection does not say whether the last frame reached Mosaiq. Do
not resend immediately.

1. Reconnect with backoff.
2. Subscribe again and buffer the new `orderUpdates` and `userFills` messages.
3. Backfill [`GET /v1/userFills`](/api/user-fills), following its cursor and
   deduplicating by `(venue, fillId)`.
4. Query [`GET /v1/openOrders?cloid=<cloid>`](/api/open-orders) for each
   ambiguous order.
5. Reconcile before retrying or replacing. A replacement placement needs a
   new `cloid`.

## Network placement

Measure round-trip time from the host that will run the strategy and reuse
established sockets so DNS, TCP, TLS, and authentication stay off the
per-order path. Private VPC peering is available on request during onboarding.
Mosaiq does not guarantee a specific latency; benchmark the full
client-to-venue path from the production topology.
