Connector behaviour¶
Every venue connector follows the rules on this page; each venue's page lists only what differs. The venues and what each supports: Venue connectors.
Threads and rings¶
Each connector runs on its own network thread and writes normalised messages into two rings per venue. Orders and control commands come back on a third.
| Ring | When full |
|---|---|
| market data | drops the delta and forces a book resync |
| order events | spins for a bounded time, then the venue treats the overflow as fatal |
Behind fastmm-gateway the same code runs in the gateway, and the rings are shared memory the strategy process reads.
cancel_all() uses a blocking REST connection of its own, so it works from any thread while the network thread is wedged. Venue::poll() runs after every network-thread iteration.
Order admission¶
| Situation | What is admitted |
|---|---|
| venue-fatal error or REST hard stop | cancels and cancel-all only, on any transport still usable |
uncork() fails |
every order of the batch is rejected with TransportFull, not counted as sent |
| shadow table full | the order or replace goes back as OrderTableFull |
The shadow table holds 7168 orders over every strategy behind the connector: the orders working at the venue and the replaces in flight. A full table logs one error, and one warning when it has room again; VenueStatus::shadows_refused counts the refusals. An untracked order could not be amended, and its replies would carry no instrument.
Requests are charged what their endpoint charges, per venue; the venue pages give the figures.
Account pools¶
pool_of = "<primary>" in [venues.<member>] runs a second account of the same exchange behind one venue (configuration, strategy API).
| The member has its own | The member uses the primary's |
|---|---|
| network thread, keys, order-count windows, budget publication | instruments and symbol ids |
| order and user sessions, start-up open orders and balances | market data: it opens no stream and holds no books |
execution replay, cancel_on_order_channel_loss, kill-switch cancel_all |
A member's status line reads md=down books=0/0 while order and user are live. Every event a member emits carries its own venue id, so the engine keeps each order with its account. The engine sums the members' position reports into the instrument's position.
Binance Spot and Binance USDⓈ-M run as members; any other connector is refused at configuration time. Binance counts request weight per IP, so accounts leaving from one source address share one weight window. A 429 pause or 418 stop answered to one account holds them all.
Internal transfers¶
A pool treasury ([venues.<primary>.treasury]) moves an asset between a pool's accounts. The calls block on a REST connection of their own and run on the treasury's thread.
| Call | Answers |
|---|---|
Venue::transfer(TransferRequest) |
Pending or Done (taken), Failed (refused, nothing moved), Unknown (ask for the state) |
Venue::transfer_status |
the same, or NotFound |
Venue::transfer_account() |
the account's name in a request |
Venue::request_balances() |
asks for a balance snapshot on the network thread |
Binance Spot and USDⓈ-M support transfers, through the master account's universal transfer (Binance Spot). Other connectors refuse them.
Dead man's switch¶
cancel_on_order_channel_loss cancels over REST when the order channel drops, which needs a live process. What survives a SIGKILL, an OOM kill or a dead host is the venue's own switch.
| Venue | Kind | Setting | Default |
|---|---|---|---|
| Binance USDⓈ-M | countdown, per symbol | dead_mans_switch_ms |
60000 ms |
| OKX | countdown, whole account | dead_mans_switch_s, 10 to 120 |
60 s |
| Gate | countdown, whole account | dead_mans_switch_s |
off |
| Hyperliquid | scheduled cancel | dead_mans_switch_s, at least 10 |
off |
| Lighter | scheduled cancel-all | dead_mans_switch_s, 310 s to 15 days |
off |
| Gemini | cancel on disconnect; heartbeat countdown | cancel_on_disconnect, heartbeat |
on; heartbeat off |
| Deribit | cancel on disconnect, per connection | always on | on |
| Bybit | cancel after all private connections drop | dead_mans_switch_s, 3 to 300 |
off |
| Binance Spot, Coinbase, Alpaca | none |
A countdown can lapse while the process lives. When the venue refuses every refresh for a whole window, it has cancelled the orders. The connector then trips the venue kill with DeadMansSwitchLost instead of quoting again (Kill switch and shutdown).
Batch order entry¶
Only the Hyperliquid connector batches order entry; the others send single orders and coalesce an engine batch into one TCP write.
| Venue | Batch the venue offers | Why the connector does not use it |
|---|---|---|
| Binance Spot | none; order lists are contingency orders | |
| Binance USDⓈ-M | REST batchOrders, 5 orders |
REST only: it would move quotes off the WebSocket API |
| Bybit | WebSocket order.create-batch and friends |
asynchronous, half-accepted results, no rate-limit saving on spot |
| OKX | WebSocket batch-orders, 20 orders |
per-order results like Bybit's; cancel-all uses REST cancel-batch-orders |
| Lighter | sendtxbatch, 15 transactions |
each still counts against the limits; failure semantics undocumented |
| Gemini | none | cancel-all uses REST order/cancel/session |
| Coinbase | Advanced Trade batch_cancel; Exchange batches only on FIX |
the kill switch uses batch_cancel |
| Deribit | private/mass_quote |
needs approval and a second order lifecycle; bulk cancels serve the kill switch |
| Hyperliquid | one action, many orders | used: up to 40 orders an action |
Reconciliation¶
A reconciliation asks the venue what the account executed, then what is open, and sends the result as one snapshot: Begin, OpenOrder per order, Position per instrument on derivatives, End.
| When | What |
|---|---|
| a private or order channel reconnects | a reconciliation |
| the engine sees more than three cancel rejects | ControlCommand::Reconcile; the connector calls request_open_orders() |
| the first private connect of a session | a sweep: empty watermark, finds only orders the engine does not know, which the engine cancels |
| Rule | Effect |
|---|---|
| a snapshot reaches the engine only when the whole reply parsed | a truncated reply is dropped, not sent as empty |
Oms::reconcile_end() cancels every order the snapshot does not name |
bounded by the sent_watermark in Begin: later orders are kept |
| an order dropped with quantity still working | counted in EngineStats::unresolved_orders and logged |
| one snapshot at a time | a request made meanwhile is served by one more straight after |
| a failed snapshot | asked again after 5 s; abandoned after 60 s without an answer |
disconnect() has begun |
nothing more is asked |
After each snapshot the connector drops the order shadows the snapshot proves over: an order answered before the request that the venue no longer holds lost its terminal event. VenueStatus::shadows_swept counts them. "Before" is by send sequence, so behind fastmm-gateway every strategy's shadows are judged.
Orders left by an earlier session carry an earlier session epoch in their client ids, so they cannot be confused with this session's.
A warm standby (Hand over a running session) calls defer_private() before connect(). Binance Spot then opens only public market data until enable_private(), and sends, cancels and queries nothing of the account. The other connectors refuse it, and the standby connects only once it holds the lock.
Execution replay¶
A reconciliation first replays what the account executed, because an open-order snapshot cannot report the fill that finished an order. Venue::request_executions(since_venue_ms) runs the venue's trade-history query and sends each execution as an OrderFillMsg flagged kReplayed.
| Step | Detail |
|---|---|
| deduplication | Oms::on_fill drops an execution id, instrument and side it saw among the last 4096 |
| exact replay | Begin carries ReconcileMsg::kExecutionsExact when every stream answered in full |
| watermark | per stream; moves to the end of a complete read, never past the replay's start less 60 s (OKX 5 min) |
| start | at connect(); a restored session passes its watermark with resume_executions() (Recovery at start-up) |
| schedule | before each reconciliation, and once a minute |
| failure | the watermark stays; asked again 5 s later; a query held back by the rate limiter for 180 s fails |
| long ranges | walked in the venue's longest window; a start older than the venue keeps makes the replay not exact |
When a reconciliation was not preceded by an exact replay, the engine counts it in EngineStats::estimated_reconciles. A quantity no fill covered is then booked at the order's own price with no fee.
A fill first seen only as a larger cumulative quantity stays remembered with its order, for the last 4096 ended orders. When the replay later names it, it corrects the price and fee without adding quantity (EngineStats::corrected_fills). The PnL moves by qty * (px - est_px); the average entry price keeps the estimate.
| Counter | Meaning |
|---|---|
pending= on the estimates line of fastmm-top, fastmm_estimates_pending |
orders whose estimate no execution has named yet |
forgotten=, fastmm_estimates_forgotten_total |
estimates whose order left the 4096 records; a later execution of it is booked again |
VenueStatus::execution_queries, executions_fetched, execution_query_errors |
the replays |
What each venue's history query offers:
| Venue | Query | Scope | Names the client order id |
|---|---|---|---|
| Binance Spot | GET /api/v3/myTrades |
symbol | no: looked up by orderId |
| Binance USDⓈ-M | GET /fapi/v1/userTrades |
symbol | no: looked up by orderId |
| Bybit | GET /v5/execution/list |
account | orderLinkId |
| OKX | GET /api/v5/trade/fills, fills-history |
account and instrument type | clOrdId |
| Gate | GET /my_trades_timerange |
account | text |
| Hyperliquid | userFillsByTime |
account | cloid |
| Gemini | POST /v1/mytrades |
symbol | client_order_id |
| Coinbase Advanced Trade | GET .../orders/historical/fills |
account | no: looked up by order_id |
| Coinbase Exchange | GET /fills |
product | no: looked up by order_id |
| Deribit | private/get_user_trades_by_currency_and_time |
currency | label |
| Alpaca | GET /v2/account/activities/FILL |
account | no: looked up by order id |
| Lighter, Nasdaq ITCH | none |
Balances¶
Every connector reports the account's balances as BalanceMsg events on the order ring (Balance check).
| Source | When |
|---|---|
| snapshot | after each reconciliation snapshot, as one kSnapshot ... kSnapshotEnd set; an asset it does not name holds nothing |
| stream | the private stream's balance updates, as single messages |
| refetch | after a fill, at most once a second, on a venue without a balance stream |
Only the assets the engine keeps are forwarded: the base and quote of spot instruments, the settlement asset of derivatives. A failed balance fetch is asked again after 5 s and does not hold up the order snapshot. Each row carries free, locked, total, equity and maintenance; the venue pages map the venue's fields.
Money that enters or leaves an account other than by trades is an AccountFlow event: Binance Spot balanceUpdate, and Binance USDⓈ-M ACCOUNT_UPDATE rows whose reason is not ORDER or FUNDING_FEE. The engine changes nothing for it; the journal keeps it for a backtest (Journal format). Other connectors send none.
Mark, index and funding¶
Connectors of derivatives send the venue's mark price, index price, funding and open interest as PerpStateMsg events on the market-data ring (Perpetuals). The engine values positions at the mark (Valuation).
| Field | Meaning |
|---|---|
fields |
what this message carries; a venue with separate channels sends one message per channel |
funding_rate |
the rate the venue applies at next_funding, per funding_interval; positive: longs pay |
| Venue | Mark | Index | Funding rate | Open interest |
|---|---|---|---|---|
| Binance USDⓈ-M | yes | yes | yes | no |
| OKX | yes | yes | yes | yes |
| Bybit linear | yes | yes | yes | yes |
| Deribit | yes | yes | yes, per 8 h | yes |
| Gemini | yes | no | yes | no |
| spot venues | none |
Gate sends its futures.tickers channel as PerpStateMsg. Funding payments are Funding events; each venue page says where they come from.