Inventory and risk
Every fill moves your inventory. Read it from the fill stream position deltas and let it drive your next ladder.Skew quotes as inventory builds
Skew quotes as inventory builds
As you accumulate a long position, lower both sides of your ladder slightly so
you are more likely to sell and less likely to buy more (and vice versa when
short). This mean-reverts your inventory toward flat without crossing the
spread.
Shrink size near limits
Shrink size near limits
Reduce quoted quantity — or widen — as your position approaches your
self-imposed risk limit, so you stop adding to a one-sided book.
Use reduceOnly for exits
Use reduceOnly for exits
Tag exit-only orders
reduceOnly so they can only shrink a position, never
flip it. Use POST {API_BASE}/perps/positions/close to flatten fully or
partially.Track your own margin headroom
Track your own margin headroom
OCX enforces portfolio margin server-side: every order passes a margin gate
before acceptance. A rejected order returns an insufficient-margin error —
handle it, don’t retry blindly. Watch your headroom from
GET {API_BASE}/perps/balances and GET {API_BASE}/perps/positions and
de-risk proactively rather than waiting for rejections. Positions that breach
maintenance margin are liquidated by the exchange.The margin gate only rejects on business rules (insufficient margin). Distinguish
those from transport/parse errors in your client — a margin reject is a signal to
de-risk, not to reconnect.
Dead-man switch
Wire a kill-switch that pulls all your quotes the moment your bot can no longer be sure they still reflect a live price — feed loss, an unhandled error, or shutdown. The clean primitive is cancel-all:Detect the trigger
Your reference feed goes stale, the fill stream stops heartbeating, a
place/replace call errors repeatedly, or you receive SIGTERM.
Cancel everything immediately
Fire
cancel-all (unscoped) before doing anything else, so no stale quote can
be hit while you are blind.Reconnect and heartbeat
SSE streams are long-lived and diff server-side. Build your client to survive drops without leaving orphaned quotes.Snapshot then delta
Snapshot then delta
On connect you get a full
snapshot; thereafter fold in delta/update
events. Keep local book, trade, and position state and mutate it in place.Detect gaps with eventSeq
Detect gaps with eventSeq
Private streams carry a monotonically increasing
eventSeq (and the public
book stream an SSE id:). If a sequence jumps, close and reconnect — you will
get a fresh snapshot to re-sync from.Watch the heartbeat
Watch the heartbeat
Idle streams send a comment heartbeat (
:heartbeat / : keepalive) roughly
every 15s. If heartbeats stop arriving, consider the connection dead and
reconnect — do not wait for a TCP timeout.Reconnect with backoff
Reconnect with backoff
On any disconnect, reconnect with exponential backoff and jitter. On
reconnect, re-snapshot before trusting your local state, and re-run your
quoting cycle only once state is consistent.
Idempotency and identifiers
clientOrderId
Attach a
clientOrderId to each order/quote level for idempotent client-side
tracking, so a retried request doesn’t double-place.quoteId
Tag each quoting cycle with a
quoteId to group and replace a whole batch, and
to scope a targeted cancel.Rate limits
The gateway rate-limits per client; authenticated API-key traffic receives a higher budget than anonymous traffic. Rate-limit state is returned in standardX-RateLimit-* response headers — back off when X-RateLimit-Remaining reaches
0, and handle 429 with backoff rather than tight retries.
Key hygiene
Least privilege
Least privilege
Give an executing bot a
trade-scoped key; a monitoring-only process gets
read. A key cannot mint or revoke another key.Pin and expire
Pin and expire
Pin
allowedIps to your egress addresses and set expiresInDays so keys
rotate. Revoke with DELETE {API_BASE}/perps/me/api-keys/{id}; list metadata
(never the secret) with GET {API_BASE}/perps/me/api-keys.Store the secret once
Store the secret once
The plaintext secret is returned only at creation. Keep it in a secret store,
never in code or logs.
A resilient maker loop
Start up
Authenticate, load the market list and your fee tier, connect one fill stream
per market, and re-snapshot state.
Each cycle
Compute fair value from your reference feed → (optionally)
preview →
quotes/bulk with cancelAll: true.