Binance USDⓈ-M futures¶
kind = "binance_usdm" trades Binance USDⓈ-M perpetuals in one-way position mode over the WebSocket API, with REST as the fallback.
| Markets | perpetuals: PERPETUAL and TRADIFI_PERPETUAL contracts; delivery contracts are refused |
| Order entry | WebSocket API; REST fallback |
| Environments | production, Demo Trading |
| Keys | HMAC (default) or Ed25519 |
| Replace | order.modify, which loses queue position; the example config uses cancel and new |
| Dead man's switch | countdownCancelAll, on by default (60 s) |
| Account pools, internal transfers | yes (Account pools) |
| Example config | configs/binance-usdm-demo.toml |
Connections¶
| Channel | Endpoint | Purpose |
|---|---|---|
| md | <ws_url>/public/stream?streams=<sym>@depth@100ms/<sym>@bookTicker |
depth diffs, best bid and offer |
ticker, md_ticker_conns > 0 |
<ws_url>/public/stream?streams=<sym>@bookTicker, up to 8 more connections |
best bid and offer; the first copy of each update is used (redundant feeds) |
| trades | <ws_url>/market/stream?streams=<sym>@aggTrade |
aggregate trades |
| mark | <sym>@markPrice@1s on /market |
mark, index, funding (below) |
| user | <ws_private_url>/ws/<listenKey>, default <ws_url>/private |
ORDER_TRADE_UPDATE, ACCOUNT_UPDATE, listenKeyExpired |
| order | <ws_api_url> |
order.place, order.cancel, order.modify; REST fallback POST, DELETE, PUT /fapi/v1/order |
| rest | <rest_url> |
reference data, depth, clock, listenKey, userTrades, openOrders, positionRisk, account checks, kill switch, dead man's switch |
The routed /public, /market and /private paths replaced the plain /ws and /stream URLs, which Binance switched off on 2026-04-23.
| Demo Trading | Host |
|---|---|
| REST | https://demo-fapi.binance.com |
| streams | wss://demo-fstream.binance.com |
| WebSocket API | wss://testnet.binancefuture.com/ws-fapi/v1 |
The listenKey comes from POST /fapi/v1/listenKey and is kept alive with PUT every 30 minutes; it is valid for 60. On listenKeyExpired or a failed keepalive the connector gets a new key, reopens the user connection and reconciles.
Keys¶
HMAC keys sign every request. Ed25519 keys (key_type = "ed25519") log on to the order connection with session.logon, request id "logon". After that, orders go without apiKey and signature, as on Binance Spot. REST requests are signed with Ed25519 per request. USDⓈ-M has no SBE streams.
Market data¶
Book sync¶
The book follows the Spot procedure (Depth sync) with futures rules:
| Rule | Value |
|---|---|
| snapshot | GET /fapi/v1/depth?symbol=S&limit=L, once the stream's first delta is buffered and the snapshot fits the bulk share of the minute's 2400 weight |
snapshot depth L |
depth_limit; else 1000 (weight 20) with up to 10 symbols, 100 (weight 5) above |
| drop | deltas with u below lastUpdateId |
| first applied delta | U <= lastUpdateId <= u |
| later deltas | pu equals the previous u |
| mismatch | ConnectionState{Resyncing} and a new snapshot, at most once per 2 s |
| a delta over 1024 levels a side | keeps the levels nearest the touch |
The trades connection does not report its state to the engine, so a quiet aggTrade stream never clears the book.
Mark, index and funding¶
The connector sends PerpStateMsg from <sym>@markPrice@1s (Mark, index and funding).
| Field | Source |
|---|---|
| mark / index | p / i |
| funding rate, next funding | r, T |
| funding interval | fundingIntervalHours from GET /fapi/v1/fundingInfo at start, 8 h for a symbol it does not list; corrected when T steps by whole hours |
| open interest | none |
Updates come once a second per symbol.
Orders¶
| Engine order | Sent as |
|---|---|
| limit GTC, IOC, FOK | LIMIT with that timeInForce |
| post-only | LIMIT with GTX; a crossing order is rejected with -5022 or expires (OrderExpired) |
post-only with post_only_rpi = true |
timeInForce RPI |
| market | MARKET, no price, no timeInForce |
| reduce-only | reduceOnly=true |
| position side | omitted (BOTH) |
An RPI order rests post-only, hidden from the API depth, and matches only orders from Binance's app or web page. It queues behind every other order at its price. The venue does not modify an RPI order, so pair it with supports_replace = false.
order.modify keeps the venue's clientOrderId and takes the total quantity. The connector sends the new remaining quantity plus the filled quantity. It reports later events under the engine's new client id and counts their fills from the modify. A modify answered CANCELED or EXPIRED becomes a cancel of the original and a reject of the replacement.
Batch endpoints exist only on REST (POST /fapi/v1/batchOrders, 5 orders; DELETE, 10). The connector stays on the WS API and writes an engine batch in one TCP write, so a batch request would save no round trip.
Reconciliation and fills¶
On every user-stream connect and order-channel reconnect, the connector reconciles in this order:
GET /fapi/v1/userTradesper symbol replays the executions since the last one forwarded (Execution replay).GET /fapi/v1/openOrders(weight 40) andGET /fapi/v3/positionRisk(weight 5) become oneReconcileMsgsequence.- The sequence is Begin, one
OpenOrderper order, onePositionper configured instrument (flat when absent), End.
| Replay item | Value |
|---|---|
| query | GET /fapi/v1/userTrades, weight 5, limit 1000 |
| window | fromId = last trade id + 1 once known, else startTime = connect time or the restored watermark |
| older than 7 days | walked a week per query; older than 3 months is clamped; either way the replay is not exact |
| order named | orderId only, mapped through the last 8192 pairings; an unknown one is looked up with GET /fapi/v1/order?orderId= (weight 1), as on Binance Spot |
The minute's replay reads only the symbols with order activity since the symbol's watermark, less the settle margin. Activity is an order sent or an order event received. A quiet symbol's watermark moves as if it had answered empty. Replays before a reconciliation, after a reconnect, a restart or a failure read every symbol, as do all replays in the first two minutes.
Positions between reconciliations¶
The engine books every fill, including liquidations and ADL (autoclose-* client ids). Once no fill has arrived for 1 s, the connector compares each ACCOUNT_UPDATE position with the fills it forwarded. It sends a PositionUpdate only when they differ (position_from_account_update). The two event types are not ordered against each other, so forwarding every position would count fills twice.
Account settings¶
load_reference_data() logs the position mode, each symbol's leverage and margin type, and the margin balance. It refuses an account in hedge mode; fastmm-live and fastmm-gateway exit 3.
| Key | Change |
|---|---|
one_way_mode = true |
switches a hedge-mode account to one-way (POST /fapi/v1/positionSide/dual), only with no position and no open order |
leverage = <n> |
sets every enabled symbol whose leverage differs (POST /fapi/v1/leverage) |
A refused change stops the start. Without these keys no account setting changes.
Balances¶
| Item | Source |
|---|---|
| snapshot | GET /fapi/v3/account (weight 5); the mode from /fapi/v1/multiAssetsMargin and ACCOUNT_CONFIG_UPDATE ai.j |
| stream | ACCOUNT_UPDATE asks for a new snapshot: its B[] has the wallet balance only |
| free / locked / total / equity / maintenance | availableBalance / initialMargin / walletBalance / marginBalance / maintMargin |
| multi-assets mode | free is each asset's maxWithdrawAmount, plus an account row in USD from the total* fields |
| deposits, withdrawals, transfers | each ACCOUNT_UPDATE B[] row with a reason other than ORDER or FUNDING_FEE and a non-zero bc: an AccountFlow event |
A pool member's position report is its own account's; the engine sums the accounts' reports into the instrument's position.
Funding¶
GET /fapi/v1/income?incomeType=FUNDING_FEE (weight 30, every symbol in one query) is the source. Each row on a subscribed symbol becomes a Funding event:
| Field | Source |
|---|---|
| amount | income, negative when paid, in asset |
| time | time |
| id | tranId |
The query runs with every reconciliation's replay and once a minute, from its own watermark. It reads windows of at most 7 days, no further back than 3 months, and up to 20 pages. A failed query runs again 5 s later. An ACCOUNT_UPDATE with reason FUNDING_FEE carries no id, so it only triggers the query a second later.
Internal transfers¶
Transfers use the master account's universal transfer, as on Binance Spot. The endpoint has no futures-to-futures path, so one USDⓈ-M transfer is two steps:
- the sender's
USDT_FUTUREto the receiver'sSPOT, id<id>a; - the receiver's
SPOTto itsUSDT_FUTURE, id<id>b.
A refused second step is sent again from transfer_status. Refused again, the amount stays in the receiver's spot wallet and the transfer is Failed, said in the log.
Dead man's switch¶
POST /fapi/v1/countdownCancelAll?symbol=S&countdownTime=<ms> (signed, IP weight 10) cancels a symbol's orders when the countdown lapses. Sending it again replaces the running timer; countdownTime=0 stops it.
| Item | Value |
|---|---|
| window | dead_mans_switch_ms, default 60000 ms |
| armed | on every subscribed symbol, from the housekeeping timer |
| refresh | every window/3, at most every 500 ms |
| cost | 10 IP weight per symbol per refresh: 120 a minute for four symbols at the default |
| shutdown | disconnect() sends countdownTime=0 |
| lapse | no refresh succeeded for a whole window: the venue kill trips with DeadMansSwitchLost |
The refresh clock moves only when the venue answers. Binance has no WebSocket method for the countdown and no way to read the armed state back. See Dead man's switch for the other venues.
Rate limits¶
Limits come from exchangeInfo.rateLimits, the WS API rateLimits, and the headers X-MBX-USED-WEIGHT-1M and X-MBX-ORDER-COUNT-10S.
| Request | IP weight | Order limits |
|---|---|---|
| place, modify | 0 | 1 against the 10 s and 1-minute windows |
| cancel | 1 | none |
The userTrades and income queries, order lookups and the start's symbolConfig queries wait while the bulk share is spent or the venue asked for a pause. A 429 waits Retry-After, longer for a second 429 within a minute; a 418 stops REST. Windows, pacing and refusal counts work as on Binance Spot.
Errors¶
| Code | Action |
|---|---|
| -5022 | post-only reject |
| -2018, -2019, -2027 | insufficient balance or margin |
| -2022, -4118 | reduce-only reject |
| -4164 | minimum notional |
| -4014, -4023 | tick, lot |
| -1003, -1015, HTTP 429 | cool down |
| HTTP 418 | stop REST |
| -1021, -5028 | resync the clock |
| -1022, -2015, -4109, -4061 (hedge mode) | fatal |
| -2011, -2013, -4116 | reconcile |
| HTTP 503 | execution status unknown: reconcile |
Connection timeouts¶
| Key | Value |
|---|---|
stale_ms |
2000 ms; the Demo config sets 10000 ms |
dead_ms |
at least 45000 ms for market data and 240000 ms for the other connections; the venue pings every 3 minutes |
Unconfirmed¶
| Item | Status |
|---|---|
| private payloads | ORDER_TRADE_UPDATE, ACCOUNT_UPDATE and WS API order responses follow the documentation and the official connector's models; not recorded |
| crossing GTX order on Demo | -5022 or accepted and expired; both are handled |
order.modify |
covered by the scripted fake exchange only |
countdownTime bounds |
none documented for /fapi; dead_mans_switch_ms is only clamped to be non-negative |
| countdown cancel on the user stream | expected to look like any other x: "CANCELED" |
Sources (read 2026-09-26): USDⓈ-M general info, Account Information V3, Mark Price Stream, Get Funding Rate Info, WebSocket change notice.