requests for signed REST calls and websockets for streaming market data and fills. The guide assumes Python 3.9+ and a WhiteBIT account with a Trading-permission API key (see Prerequisites below).
Prerequisites
- A WhiteBIT account (register)
- An API key with Trading permission (create one)
- Funds in the Trade balance — transfer from Main balance if needed (see Balances & Transfers). Grid strategies require both the quote currency (for buy orders) and the base currency (for sell orders).
- Familiarity with HMAC-SHA512 signing — see Authentication for the contract and First API Call for a worked signing example with troubleshooting
- Python 3.9+ with
requestsandwebsocketspackages installed (the production guidance in the worked example relies onasyncio.to_thread, added in Python 3.9)
Architecture overview
A trading bot operates as a continuous loop:- Market data — receive prices and orderbook updates via WebSocket (or REST polling for lower-frequency strategies)
- Signal generation — apply strategy logic to determine when and what to trade
- Order execution — place orders via REST API (single or bulk)
- Fill monitoring — track order fills and state changes via WebSocket
- Position management — update internal state, replace filled orders, manage risk
Market data ingestion
REST polling
For strategies that do not require sub-second data, poll market data via REST:- cURL
- Python
GET /api/v4/public/ticker— 24h ticker statistics for all marketsGET /api/v4/public/orderbook/{market}— orderbook snapshot with configurable depth
WebSocket streaming
For real-time data, subscribe to WebSocket market streams:Incremental
depth_update deltas chain via past_update_id. If a delta’s
past_update_id does not match the last-seen update_id for this subscription,
a message was missed — resnapshot by unsubscribing and re-subscribing.
Keepalive snapshots (params[0] is true, past_update_id absent) are full
resets, not gap signals — the snapshot update_id may exceed the last delta’s. See
WS Quickstart — State recovery after reconnect
for the full pattern.Order placement
Every private endpoint in this guide is signed with HMAC-SHA512. Thesend_request helper below handles the signing and a thread-safe nonce; the rest of the guide reuses it.
send_request uses a millisecond nonce. If multiple threads call the helper
in the same millisecond, the server rejects the duplicate via its nonceWindow
(±5 seconds). The helper below uses a monotonic counter protected by a lock
— safe under concurrent callers (for example, the kill-switch heartbeat
thread and the strategy thread placing orders simultaneously).authorize handshake. The helper below fetches a token via REST (rate limit: 10 requests / 60 s) and sends the authorize message. Call it once per WebSocket connection, before any private subscribe:
Single order
Place a limit order using the order creation endpoint: Endpoint:POST /api/v4/order/new
- cURL
- Python
For bot integrations, include
clientOrderId in the request to map fills
back to internal strategy state without depending on the server-assigned
orderId. The identifier is preserved across order/modify calls (modify
issues a new orderId but retains the caller-supplied clientOrderId),
which makes it the right key for order reconciliation. See
Client Order ID for usage patterns.Bulk orders
Place up to 20 limit orders in a single request — one round trip instead of up to 20 for multi-order strategies. Endpoint:POST /api/v4/order/bulk
- cURL
- Python
The bulk endpoint accepts up to 20 limit orders per request. All orders in a batch must target the same market pair. Partial failures are possible — always check each order result individually, or set
stopOnFail: true to stop processing at the first failed order.Kill-switch setup
The kill-switch cancels every active order on the configured market after a timeout that the bot must refresh. If the bot crashes or loses connectivity to the API, the timer expires and the API cancels every stale order automatically. Endpoints:POST /api/v4/order/kill-switch— activate or refresh the timerPOST /api/v4/order/kill-switch/status— check current timer state
Activate kill-switch
- cURL
- Python
The kill-switch scope defaults to all order types (spot, margin, futures).
To scope it to just spot orders — for example, when the same account runs
futures positions that must not be canceled by a spot kill-switch — pass
"types": ["spot"] in the request.Heartbeat refresh loop
The bot must refresh the kill-switch before the timer expires. If the bot fails to refresh, all orders cancel automatically. Retry a failed refresh after a short delay instead of waiting the full interval — with a 45-second interval and a 60-second timeout, one skipped refresh expires the timer before the next scheduled attempt.Deactivate kill-switch
- cURL
- Python
WebSocket fill monitoring
Subscribe to account streams to receive real-time notifications for order state changes and trade executions:Error handling
Unattended bots must handle three failure modes: HTTP 429 rate limits, transient HTTP 5xx responses, and HTTP 4xx validation errors that should not be retried.Rate limit backoff
Error classification
See Rate Limits & Error Codes for the complete error reference.
Reconnection and recovery
WebSocket connections drop due to network issues, server maintenance, or idle timeouts. A production bot must handle disconnections without losing state.Ping/pong keepalive
Send periodic ping messages to detect stale connections before a timeout occurs. The server closes idle connections after 60 seconds of inactivity; send a ping every 50 seconds and treat a pong that takes longer than 10 seconds as a dead connection (see WebSocket rate limits). A longer pong wait delays disconnect detection — long enough for the kill-switch timer to expire and cancel the working orders before the bot even notices the drop:The keepalive function above is shown in isolation. In production, integrate
ping/pong handling into the main message loop — running a separate
recv()
consumer conflicts with the primary message loop and causes missed messages.Reconnection with exponential backoff
Auto-resubscription after reconnect
After reconnecting, the bot must re-authenticate (for private channels) and re-subscribe to all channels.State reconciliation
WebSocket streams do not replay messages that arrived during a disconnection. After reconnecting and re-authenticating, reconcile local state against the authoritative server state:reconcile_state(MARKET, grid_orders) after authenticate_ws(ws) and before re-subscribing. For a per-fill audit trail, also query POST /api/v4/trade-account/executed-history to see fills that happened during the disconnect.
Worked example: Grid bot
A grid bot places buy and sell orders at fixed price intervals around a center price. When a buy fills, a sell is placed one grid level above. When a sell fills, a buy is placed one grid level below. The strategy profits from price oscillation within the grid range. The bot assembles the building blocks from the previous sections — the signedsend_request helper, authenticate_ws, the kill-switch heartbeat, and the reconnection loop — around one strategy-specific piece: the fill-replacement trigger.
The lifecycle, mapped to the sections above:
- Center the grid — fetch the market’s
last_pricefromGET /api/v4/public/tickerand snap to the grid spacing. - Arm the kill-switch first — activate the timer and start the heartbeat refresh loop before any order exists, so a crash mid-startup still cleans up.
- Clean slate — cancel leftovers from a previous run with
POST /api/v4/order/cancel/allscoped to the market. The call removes every open spot order on the market, including manually placed ones. - Place the grid — buy levels below center, sell levels above, batched through the bulk endpoint in groups of 20. Track
orderId → {side, price, level}in a local dict (grid_ordersbelow). - Monitor and replace — subscribe to the account streams; each fully executed grid order triggers an opposite order one level away.
- Recover — on disconnect, reconnect with backoff, re-authorize, reconcile against
POST /api/v4/orders(a grid order missing from the server filled or was canceled during the gap), then re-subscribe.
Inside the async message loop, wrap synchronous REST calls in
asyncio.to_thread() — blocking calls (including the backoff sleeps in
send_request_with_retry) delay WebSocket message processing. When
reconciling after a reconnect, confirm a vanished order’s outcome via
POST /api/v4/trade-account/executed-history
before treating the disappearance as a fill.What’s Next
Account Monitoring
Monitor balances, deposits, and order activity across all account types.
Client Order ID
Attach custom identifiers to orders for tracking across systems.
Rate Limits & Errors
Per-endpoint rate limits, error codes, and retry strategies.