OKX
Founded in 2017, OKX is a cryptocurrency exchange that offers spot, margin, perpetual swap, futures, options, spread, and event contract trading. This integration supports live market data ingest and order execution on OKX.
Overview
This adapter is implemented in Rust and exposed to Python through PyO3 bindings. It does not require external OKX client libraries.
The OKX adapter includes multiple components, which can be used separately or together:
OKXHttpClient: Low-level HTTP API connectivity.OKXWebSocketClient: Low-level WebSocket API connectivity for Rust callers.OKXDataClient: Market data feed manager.OKXExecutionClient: Account management and trade execution gateway.OKXDataClientFactory: Factory for OKX data clients.OKXExecutionClientFactory: Factory for OKX execution clients.
Most users will define a configuration for a live trading node (as shown below), and won't need to work directly with these lower-level components.
Examples
Product support
| Product | Instrument source | Data | Exec | Notes |
|---|---|---|---|---|
| Spot | public/instruments | Yes | Yes | Spot trading pairs. |
| Margin | public/instruments | Yes | Yes | Spot instruments with margin or leverage. |
| Perpetual swaps | public/instruments | Yes | Yes | Linear and inverse contracts. |
| Futures | public/instruments | Yes | Yes | Dated futures contracts. |
| Options | public/instruments | Yes | Yes | Limit-style orders; requires family filters. |
| Spreads | sprd/spreads | Yes | Yes | Snapshots, quotes, trades on business WS. |
| Event contracts | event-contract/* endpoints | Yes | Yes | Parsed as Nautilus BinaryOption. |
Relevant OKX docs:
- Get instruments.
- Get limit price.
- Get Spreads (Public).
- Spread trading place order.
- Event contract series.
Options support: The adapter supports options market data, venue-provided Greeks
(subscribe_option_greeks), and order execution for options instruments. See the
Options trading section below for details and the
Options guide for subscription patterns.
Instrument multipliers: For derivatives (SWAP, FUTURES, OPTION), instrument
multipliers are calculated as the product of OKX's ctMult and ctVal fields. This
keeps position sizing aligned with OKX contract size and value.
Price limits: OKX exposes initPxLmtPct, floatPxLmtPct, and maxPxLmtPct
on public/instruments for spot, margin, swap, and futures instruments. The adapter
preserves non-empty values in the instrument info field as okx_init_px_lmt_pct,
okx_float_px_lmt_pct, and okx_max_px_lmt_pct. These fields describe exchange
band percentages, so they are not parsed as static Nautilus min_price or max_price
values.
Use OKXHttpClient.request_price_limit(instrument_id) when you need the current computed
buy and sell limits from OKX's GET /api/v5/public/price-limit endpoint. OKX documents
the percentage fields as empty for options and event contracts; the adapter leaves their
instrument info unchanged.
OKX finance-product endpoints such as /api/v5/finance/okusd/* are outside the OKX
trading adapter surface.
Instrument updates
The data client loads its instrument cache over REST at connect and subscribes to the OKX instruments WebSocket channel for each configured instrument type. All update paths honor the configured instrument types, families, and contract types, and unchanged definitions are never republished.
| Source | Trigger | Publishes downstream |
|---|---|---|
| Connect load | REST at connect | Full cache, once |
| Instruments channel | Venue push (incremental) | New or changed definitions, InstrumentStatus on each |
| REST reconciliation | update_instruments_interval_mins (default 60) | New or changed definitions only |
Each update first writes the data client, HTTP, and WebSocket caches, then publishes new or
changed definitions as DataEvent::Instrument, so consumers never observe a definition the
caches do not hold. A material change is any serialized field other than ts_event and
ts_init.
- The instruments channel is incremental rather than a snapshot feed: a subscription or reconnect can begin without an initial payload, so reconnect replay alone does not reconcile the instrument cache.
- Set the interval to
0to disable periodic reconciliation; instruments channel updates are always applied. One refresh task runs per connection lifecycle and is cancelled on disconnect, failed-connect teardown, stop, and dispose. Spread instruments are included whenload_spreadsis set. - Instruments that disappear from a REST response are retained in the cache; they may
still back open subscriptions. Suspension, expiry, and delisting arrive as
InstrumentStatusevents through the instruments channel.
Order book subscriptions
Rust and Python v2 support the following subscriptions for L2 market-by-price (L2_MBP) books:
| Subscription | Delivery | Depth |
|---|---|---|
subscribe_book_deltas | OrderBookDeltas on venue updates. | 50 or 400 levels per side; five for spreads. |
subscribe_book_depth | Native OrderBookDepth snapshots. | Up to five levels per side. |
subscribe_book_at_interval | Cached OrderBook at the interval. | All levels retained from the selected channel. |
subscribe_book_depth uses OKX's native books5 snapshots, published on changes at a 100 ms cadence.
depth=None defaults to five levels. Requests in [1, 5] select the best available levels from each
snapshot; larger requests and rpi=True are rejected. Prices, sizes, and venue order counts come
directly from each snapshot. The adapter does not reconstruct depth snapshots from incremental data.
Delta and interval subscriptions retain the existing channel selection: requests in [1, 50] select
the 50-level channel when the configured VIP level permits it, otherwise the public 400-level books
channel. Other depths select a 400-level channel. The rpi parameter selects books-rpi when true.
Channel access remains subject to OKX account permissions.
Within each client, native depth consumers share one requested limit per instrument, including unmanaged subscriptions. A conflicting requested depth is rejected.
Native depth uses a separate feed from deltas and interval books. Unsubscribing either feed leaves
the other active. Spread instruments use their existing shared five-level sprd-books5 snapshot
feed, which remains active until both delta and depth consumers unsubscribe.
A managed book has one update source: deltas or depth. Compatible managed subscriptions share that
source, but managed depth cannot coexist with managed deltas or interval subscriptions for the same
instrument. The data engine rejects conflicting requests before changing the managed book.
Consumers of the same source must agree on client, book type, depth, and subscription parameters.
Different clients may use different configurations only when all consumers of that source are unmanaged.
To receive both deltas and depth, set managed=True on the delta subscription and managed=False
on the depth subscription. Unmanaged depth callbacks then leave the delta-managed book unchanged.
DataTester selects this arrangement when both subscriptions are enabled.
Interval delivery uses the data engine's existing delta subscription and timer. It publishes all retained levels, independently of an unmanaged depth consumer's requested limit. During a connection outage or book recovery, the timer can continue publishing the last cached book. Native depth delivery resumes with a new full snapshot after reconnect; it does not depend on delta recovery. See order book recovery for recovery limits.
Order book recovery
The data client recovers each delta book independently. During recovery, it suppresses incremental updates and replaces the subscription to request a fresh snapshot. Output resumes only after the client accepts a snapshot, which requires the replacement unsubscribe and subscribe requests to have been sent. Accepted snapshots replace all existing price levels; an empty snapshot clears the book.
Recovery triggers
Recovery starts when:
- A sequence gap occurs.
- An initial subscription send fails.
- An initial or post-reconnect snapshot times out.
- The venue rejects a book subscription.
On a sequence gap, the client drops the mismatched batch and suppresses further incremental updates.
book_snapshot_timeout_secs sets the snapshot deadline. For initial subscriptions, the deadline
starts after the subscription is sent, excluding time spent waiting to send.
Reconnecting resets book synchronization on the affected socket. Spread books receive full snapshots on the business socket, so their recovery starts from an initial send failure, a missing initial or post-reconnect snapshot, or a subscription rejection.
Stale-feed checks only log warnings, and skip books that a running recovery owns. They do not start recovery because quiet markets can legitimately have no book changes.
Retry loop and limits
The adapter uses the shared book recovery machinery. Each instrument has one recovery loop. It runs until a fresh snapshot is accepted, or until unsubscribe or shutdown cancels it; recovery never ends in a failed state.
Sending a subscription request keeps the book in recovery until a fresh snapshot is accepted.
- Attempts: Up to eight within the initial budget.
- Initial budget: 180 seconds, including sends, snapshot waits, and retry delays.
- Delay: The first retry is immediate. Later retries use exponential backoff starting at one second, with up to one second of jitter and a ten-second cap.
- After the budget: Attempts continue at an interval that doubles from one minute to fifteen minutes, with up to five seconds of jitter. Each attempt is bounded by one minute, or by the snapshot timeout when that is longer. A non-retryable rejection moves straight to this interval.
A running recovery continues across reconnects with its existing budget. This prevents cancellation between the replacement unsubscribe and subscribe requests. A recovery waiting between attempts after its budget retries on the new connection at once. Replacing a subscription preserves its reconnect intent. Unsubscribe and shutdown cancel recovery.
Persistent failures
When the retry budget runs out, the client logs one error, then a warning for each failed attempt. Once the interval reaches fifteen minutes, a book that keeps failing, such as an instrument the venue no longer serves, sends about eight subscription requests an hour, well under OKX's limit of 480 per hour on each connection. A late snapshot completes recovery at any point. Unsubscribe to stop recovery.
Snapshot correlation limitation
Incremental book channels accept a snapshot only while establishing or recovering synchronization. Once synchronized, the client discards unsolicited snapshots without replacing the book or resetting its sequence. Channels that publish recurring full snapshots continue to accept them.
Book subscription sends wait for a completed transport write on the intended connection. Both sends in a replacement use the same connection; a connection change fails the attempt.
Snapshot acceptance does not correlate subscription acknowledgements with recovery attempts. A delayed snapshot from an earlier subscription can remain queued while a replacement is sent and complete the current recovery when the gate opens. Write confirmation does not eliminate this ambiguity. Recovery also cannot reliably distinguish an unsubscribe error from a subscribe error when the venue response identifies only the book channel and instrument.
Disabling snapshot deadlines
Setting book_snapshot_timeout_secs to 0 disables snapshot deadlines, including initial and
post-reconnect checks. Sequence gaps and subscription rejections still start recovery.
Within the retry budget, a missing snapshot leaves the current attempt waiting until a snapshot is accepted, a rejection arrives, recovery is cancelled, or the 180-second initial budget ends. Attempts after the budget stay bounded as described above.
Live recovery validation
The okx-book-stress harness is a development tool for changes to book synchronization and
recovery. It connects to OKX mainnet public market data, submits no orders, and checks emitted spot,
RPI swap, and spread books against the book stream contract and an independent reconstruction of the
venue feed's best 20 levels.
The harness checks recovery without reconnects, including a dropped replacement snapshot when deadlines are enabled. It then injects sequence gaps, drops and delays snapshots, forces reconnects, and exercises unsubscribe and shutdown during recovery.
From the repository root, run:
CARGO_BUILD_JOBS=16 bash scripts/strip-adapter-env.bash \
cargo test -p nautilus-okx --features examples --test okx-book-stress -- --timeout 10 --rounds 18--scenario selects the run:
churn(default): the fault rounds described above.initial: drops each book's first snapshot, once per round in a fresh session.turnover: unsubscribes and resubscribes books during recovery.boundaries: probes replacement cuts, retry exhaustion into the retry ceiling, and shutdown during a reconnect.
--timeout sets the snapshot timeout in seconds, where 0 disables snapshot deadlines, and
--rounds sets the number of rounds (18 by default).
The harness requires access to the public and business WebSocket endpoints and the public instrument and spread APIs. Automated book lifecycle tests use local mock servers. See Stress harnesses for the shared flags and output format.
Symbology
OKX uses specific symbol conventions for different instrument types. Add the .OKX
suffix when referencing instruments in Nautilus, for example BTC-USDT.OKX.
Symbol format by instrument type
SPOT
Format: {BaseCurrency}-{QuoteCurrency}
Examples:
BTC-USDT- Bitcoin against USDT (Tether)BTC-USDC- Bitcoin against USDCETH-USDT- Ethereum against USDTSOL-USDT- Solana against USDT
To subscribe to spot Bitcoin USD in your strategy:
InstrumentId.from_str("BTC-USDT.OKX") # For USDT-quoted spot
InstrumentId.from_str("BTC-USDC.OKX") # For USDC-quoted spotSWAP (perpetual swaps)
Format: {BaseCurrency}-{QuoteCurrency}-SWAP
Examples:
BTC-USDT-SWAP- Bitcoin perpetual swap (linear, USDT-margined)BTC-USD-SWAP- Bitcoin perpetual swap (inverse, coin-margined)ETH-USDT-SWAP- Ethereum perpetual swap (linear)ETH-USD-SWAP- Ethereum perpetual swap (inverse)
Linear vs inverse contracts:
- Linear (USDT-margined): Uses stablecoins like USDT as margin.
- Inverse (coin-margined): Uses the base cryptocurrency as margin.
FUTURES (dated futures)
Format: {BaseCurrency}-{QuoteCurrency}-{YYMMDD}
Examples:
BTC-USD-261225- Bitcoin futures expiring December 25, 2026ETH-USD-261225- Ethereum futures expiring December 25, 2026BTC-USD-270326- Bitcoin futures expiring March 26, 2027
Futures can be linear or inverse. The adapter derives this from OKX's ctType field.
SPREADS
Format: {Leg1InstrumentId}_{Leg2InstrumentId}
Examples:
BTC-USDT_BTC-USDT-SWAP- Spread between BTC-USDT spot and BTC-USDT perpetual swapETH-USD-SWAP_ETH-USD-261225- Spread between ETH-USD perpetual swap and dated future
Set load_spreads=True on the data client to load live OKX spread instruments from
the OKX Get Spreads (Public)
endpoint. The adapter maps each OKX sprdId to a Nautilus spread instrument ID
with the .OKX venue suffix.
Spread instrument notes:
- Spread market data streams on the OKX business WebSocket: quotes (
sprd-bbo-tbt), trades (sprd-public-trades), and 5-level book snapshots (sprd-books5). Spreads have no incremental book channel. Eachsprd-books5update is delivered asOrderBookDepthto depth subscribers and as snapshot-flaggedOrderBookDeltasto delta subscribers. - The parser represents spot, swap, and futures leg combinations. It also represents option-leg spread definitions when OKX returns them through the same spread endpoint.
- OKX option RFQ and block trading workflows are separate from the Nitro spread order book API and are not routed by this spread path.
OPTIONS
Format: {BaseCurrency}-{QuoteCurrency}-{YYMMDD}-{Strike}-{Type}
Examples:
BTC-USD-261225-100000-C- Bitcoin call option, $100,000 strike, expiring December 25, 2026BTC-USD-261225-100000-P- Bitcoin put option, $100,000 strike, expiring December 25, 2026ETH-USD-261225-4000-C- Ethereum call option, $4,000 strike, expiring December 25, 2026
Where:
C= Call optionP= Put option
EVENTS
OKX event contract instrument IDs use the market ID returned by the OKX instruments API.
The adapter represents these markets as Nautilus BinaryOption instruments.
Example:
BTC-ABOVE-DAILY-261224-1600-65000- Event contract market in theBTC-ABOVE-DAILYseries.
Common questions
Q: How do I know which contract type to use? A: Linear and inverse instruments have distinct symbols. The public Python configs do not expose a contract-type filter, so the adapter loads both for the selected derivative instrument types.
Q: How do I load event contracts?
A: Use OKXInstrumentType.EVENTS. The public Python configs load all discoverable event contract
series and do not expose a series filter.
Retail price improvement (RPI)
Use Retail Price Improvement (RPI) to consume OKX's consolidated organic and RPI depth, place RPI maker orders, or let standard orders take RPI liquidity. The adapter maps these features to existing Nautilus order book, order, and lifecycle types. RPI routing is opt-in, so standard subscriptions and orders remain unchanged.
RPI market data
Pass params={"rpi": True} to subscribe_book_deltas or
request_book_snapshot to use the public books-rpi channel or
GET /api/v5/market/books-rpi. The feed combines organic quantity with RPI quantity that is
available for execution.
Each raw depth level has the wire shape [price, totalQty, nonRpiQty, count]:
| Wire field | Rust type | Meaning |
|---|---|---|
price | Decimal | Price level. |
totalQty | Decimal | Organic and available RPI quantity. |
nonRpiQty | Decimal | Quantity available without RPI taker access. |
count | u64 | Aggregated order count at the price level. |
Nautilus OrderBookDeltas and OrderBook use totalQty as the level quantity. The typed raw
model retains nonRpiQty; the difference between the two quantities is the available RPI
liquidity.
WebSocket snapshots and updates retain seqId and prevSeqId. Emitted deltas carry seqId as
their sequence. The data client checks each update's prevSeqId against the last accepted seqId;
the values do not need to increase by one. A mismatch starts
order book recovery. Emission resumes after an accepted snapshot with
prevSeqId: -1. The adapter applies the same linkage rule to standard incremental OKX book channels when prevSeqId
is present. books-rpi has no checksum.
For WebSocket subscriptions, rpi=True selects books-rpi instead of depth or VIP channel
selection. For REST snapshots, the requested depth becomes sz; OKX defaults to one level per side
and accepts up to 400.
The low-level Rust clients expose:
- WebSocket:
OKXWebSocketClient.subscribe_book_rpiandunsubscribe_book_rpi. - REST:
OKXRawHttpClient.get_rpi_order_bookandOKXHttpClient.request_rpi_book_snapshot.
Public instrument responses expose the venue's RPI spacing thresholds:
| Wire field | Rust type | Instrument info key |
|---|---|---|
rpiMinLevel | Option<u64> | okx_rpi_min_level |
rpiMinPxBand | Option<Decimal> | okx_rpi_min_px_band |
rpiMinLevel counts organic price levels, while rpiMinPxBand measures basis points from the
opposite-side organic best price. The info map stores the price band as its exact decimal string.
The adapter does not reject or round an order from these values because OKX applies the
authoritative instrument and account rules. Use rpi_px_round or handle the venue rejection.
RPI execution
Pass RPI controls through the submit_order, submit_order_list, or modify_order command
params. These controls work with HTTP and private WebSocket execution:
| Parameter | Type | Operations | Behavior |
|---|---|---|---|
rpi | bool | Place and batch place | Sends ordType: rpi; the Nautilus order must be LIMIT. |
rpi_taker_access | bool | Place and amend, single/batch | Lets a standard order take RPI liquidity. |
rpi_px_round | bool | Place and amend, single/batch | Lets OKX round an RPI maker price outward to an eligible level. |
order = strategy.order_factory.limit(
instrument_id=instrument_id,
order_side=OrderSide.SELL,
quantity=instrument.make_qty("250000"),
price=instrument.make_price("0.0001600"),
)
strategy.submit_order(
order,
params={
"rpi": True,
"rpi_px_round": True,
},
)Use rpi_taker_access only with regular limit, market, FOK, or IOC orders. When it is enabled,
OKX applies its taker speed bump to eligible orders, including post-only orders. Use rpi_px_round
only on RPI maker orders. Omit inapplicable controls instead of passing False, because OKX can
reject unsupported combinations. Both controls default to false, and rpi_taker_access is not
inherited during an amendment. Repeat rpi_taker_access=True on every amendment that must retain
access.
The low-level Rust clients expose the same single and batch matrix:
| Operation | REST method | WebSocket method |
|---|---|---|
| Place | place_order | submit_order |
| Batch place | place_orders | batch_submit_orders |
| Amend | amend_order | modify_order |
| Batch amend | amend_orders | batch_modify_orders |
The WebSocket batch amend tuple accepts an optional request ID and serializes it as reqId; it
does not replace the order's client ID.
RPI minimum notional
RPI maker orders must meet both the instrument's minSz and the
RPI minimum notional:
SWAPandFUTURES: 10,000 USD.SPOT: 1,000 USD.EVENTS: exempt from the RPI minimum notional.
OKX rejects an order below the applicable notional threshold with 54051; the execution client emits
OrderRejected for a rejected placement. An amend that includes newSz is checked again, with or
without newPx. A rejected amend leaves the original order active; the adapter emits
OrderModifyRejected and stops tracking the amend as pending. A price-only amend does not trigger
this check. Each sub-order in a batch place or amend request is checked independently.
Orders already on the book when the rule took effect in production on August 18, 2026, are grandfathered.
Non-RPI orders, including orders with rpiTakerAccess: true, are exempt from this notional rule.
An order that meets minSz can still fail the RPI minimum-notional check.
RPI responses and lifecycle
Private order messages parse both ordType: rpi and the migration alias ordType: elp. If an
unfilled RPI placement first appears on the private order channel as state: canceled, with
accFillSz zero or empty, the adapter emits a post-only order rejection without first emitting
acceptance. The fallback reason is RPI order canceled before acceptance. OKX can use this path
when an RPI price fails its spacing rule and rpiPxRound is false. Order reports represent RPI
orders as Nautilus LIMIT orders with post_only=True.
Use get_account_instruments to read the typed OKXRpiPermission value:
Disabledmaps torpi: "0".Enabledmaps torpi: "1"and does not grant permission to place RPI orders.Permittedmaps torpi: "2"and grants permission to place RPI orders.
The public instrument endpoint does not return account permissions. Raw fee responses expose
rpiMaker as an optional Decimal; an empty value means RPI is not applicable.
Responses may contain both RPI and ELP field names during the transition. The adapter prefers rpi
and rpiMaker, reads elp and elpMaker as response aliases, and sends only RPI names. Raw trade
messages describe source: "1" as an RPI order.
RPI exclusions
The adapter deliberately excludes the following:
- It does not expose obsolete
books-elpsubscriptions or emitordType: elp. - It does not treat the published RPI spacing thresholds as authoritative client-side validation.
- It does not apply RPI controls to algo orders. The regular HTTP order path rejects RPI controls for spread orders.
- It does not add generic post-only replay deduplication as part of RPI support.
OKX ignores rpiPxRound for options and event contracts.
See the OKX RPI migration changelog and RPI program guide.
Orders capability
Below are the order types, execution instructions, and time-in-force options supported for linear perpetual swap products on OKX.
WebSocket order identification
OKX WebSocket order operations use instIdCode (a numeric instrument identifier)
instead of the string instId parameter. The adapter resolves instIdCode values
from the instrument definitions fetched during startup and caches them for the
session lifetime. Order submissions fail with a clear error if the required
instIdCode is missing from the cache.
The initial execution connection requires usable instruments from every requested instrument type or family. A failed request or a scope with no usable instruments aborts the connection before WebSockets open, even if another scope succeeds. Pre-open instruments and entries that cannot be parsed do not satisfy this requirement. Options without configured instrument families remain skipped.
USD to USDC spot migration
OKX is consolidating USD and USDC spot books. This is a breaking venue change. Affected
Crypto-USD instruments are replaced by Crypto-USDC instruments. See the
OKX changelog.
| Event | Time |
|---|---|
| Parallel trading opens | 08 UTC on 23 September 2026. |
| USD pairs delisted | 08 UTC on 30 September 2026. |
Instrument IDs
Subscribe to and trade the replacement instrument IDs:
| Before | After |
|---|---|
BTC-USD.OKX | BTC-USDC.OKX |
OKX does not map old USD instId or instIdCode values to the new USDC instruments. The
adapter does not rewrite USD keys in the instrument or instIdCode caches. After
delisting, requests and subscriptions that still use a USD ID may fail or return no data.
Trading quote currency
The default tradeQuoteCcy is the quote currency in instId. Switching only the
instrument ID from Crypto-USD to Crypto-USDC changes the default trading quote from
USD to USDC.
Set spot_trade_quote_ccy on OKXExecutionClientConfig:
spot_trade_quote_ccy | Effect |
|---|---|
Unset (None) | Omits the field; OKX uses the quote currency in instId (USDC on Crypto-USDC). |
"USD" | Keeps trading in USD on a Crypto-USDC instrument. |
The adapter sends tradeQuoteCcy on regular REST and WebSocket spot orders. It does not
send the field on algo or conditional orders.
The adapter rejects the order locally when:
- The configured value is absent from that instrument's
tradeQuoteCcyList. - The list is unknown.
The list is retained from instrument definitions, including
GET /api/v5/account/instruments, and stored on the instrument info map as
okx_trade_quote_ccy_list.
Account activation
Call OKXHttpClient.activate_feature("1") to enable USDC order book trading only after OKX
rejects a Crypto-USDC order with error code 54109, then submit the order again.
Activation is shared between a master account and its sub-accounts, so one successful call
from any of them covers all of them. The adapter never activates accounts implicitly.
Error code 51773 from activate_feature means OKX does not support activation for the
account. It does not mean USDC trading is unavailable; a successful order confirms that
the account can trade the instrument.
Client order ID requirements
OKX requires client order IDs to be alphanumeric (letters and numbers only) and at most
32 characters. Hyphens (-) are rejected, so set the following on your strategy config:
use_hyphens_in_client_order_ids = FalseNautilus client order IDs longer than 32 characters are also rejected. When you need UUID-based
identifiers, combine use_uuid_client_order_ids=True with use_hyphens_in_client_order_ids=False
so the generated value fits within the OKX limit.
Order types
| Order type | Linear perpetual swap | Notes |
|---|---|---|
MARKET | ✓ | Immediate execution at market price. |
MARKET_TO_LIMIT | ✓ | Market order converted to IOC limit. |
LIMIT | ✓ | Execution at specified price or better. |
STOP_MARKET | ✓ | Conditional market order through OKX algo orders. |
STOP_LIMIT | ✓ | Conditional limit order through OKX algo orders. |
MARKET_IF_TOUCHED | ✓ | Conditional market order through OKX algo orders. |
LIMIT_IF_TOUCHED | ✓ | Conditional limit order through OKX algo orders. |
TRAILING_STOP_MARKET | ✓ | Trailing stop market order through OKX advance algo orders. |
Conditional orders: STOP_MARKET, STOP_LIMIT, MARKET_IF_TOUCHED,
LIMIT_IF_TOUCHED, and TRAILING_STOP_MARKET use OKX algo orders. The
TRAILING_STOP_MARKET path uses OKX's advance algo order API (move_order_stop) and
requires the cancel-advance-algos endpoint for cancellation.
Spread orders
OKX spread instruments use a separate spread trading order book and API family. The
execution client routes spread orders by spread instrument ID, for example
ETH-USD-SWAP_ETH-USD-261225.OKX, through the HTTP /api/v5/sprd/* endpoints.
The adapter uses OKX's spread REST endpoints for submit, cancel, mass cancel, order
status, and trade reports. It subscribes to the OKX business WebSocket
sprd-orders channel
for live spread order updates.
OKX sprd-orders WebSocket updates do not include fee fields. The adapter fails closed and discards
the whole update, so it emits neither a fill event nor an order-state update. Startup reconciliation
recovers the order from REST; set open_check_interval_secs to poll open orders continuously.
Historical and reconciliation fill reports from the REST
sprd/trades endpoint
include OKX fee data.
Supported spread order instructions:
LIMITwith GTC time-in-force.LIMITwith IOC time-in-force.LIMITwith post-only execution.
Spread order lists, conditional orders, FOK time-in-force, and modify requests are not supported by the OKX spread trading API path.
Relevant OKX docs:
Execution instructions
| Instruction | Linear perpetual swap | Notes |
|---|---|---|
post_only | ✓ | Only for limit orders. |
reduce_only | ✓ | See the product and position-mode restrictions below. |
The adapter sends OKX's literal reduceOnly field for margin orders in isolated or cross
trade mode and for futures or swap orders in net position mode. In long/short position mode,
OKX does not accept that field. The adapter uses the closing side and posSide combination as
the enforcing venue instruction instead. It rejects reduce-only orders for cash, option, and event
products, and rejects a long/short-mode combination that would increase the selected side. See
OKX's place order documentation.
Time in force
| Time in force | Linear perpetual swap | Notes |
|---|---|---|
GTC | ✓ | Good Till Canceled. |
FOK | ✓ | Fill or Kill. |
IOC | ✓ | Immediate or Cancel. |
GTD | - | No native OKX order time-in-force. |
GTD (Good Till Date) time in force: OKX supports request expiry through expTime,
but that is a request timeout rather than a native order expiry instruction.
If you need GTD functionality, use Nautilus's strategy-managed GTD feature. It handles order expiration by canceling the order at the specified expiry time.
Batch operations
| Operation | Linear perpetual swap | Notes |
|---|---|---|
| Batch Submit | ✓ | Submit multiple orders in single request. |
| Batch Modify | ✓ | Modify multiple orders in single request. |
| Batch Cancel | ✓ | Cancel multiple orders in single request. |
Cancel-all orders
Strategy.cancel_all_orders supports order_side in both strategy-only and cross-strategy mode.
See Cancel-all routing for strategy scope.
With strategy_only=False and an order_side, the adapter selects matching open orders from the cache
across strategies. It sends regular orders through batch cancellation and conditional and spread orders
through their individual-order cancellation APIs. This bypasses venue mass cancellation, including when
the Rust configuration option use_mm_mass_cancel is true.
Side-filtered cancellation excludes orders absent from the cache and orders still in SUBMITTED state.
Without a side filter, ordinary non-spread cancellation also uses cached open orders by default;
spread instruments and the Rust mass-cancel option use venue bulk endpoints.
Rejection reasons
When OKX rejects an order, modify, or cancel request with an error code, the reason on
OrderRejected, OrderModifyRejected, or OrderCancelRejected has the form
OKX error <code>: <message>, for example OKX error 51000: Parameter instId error. A WebSocket
response without a message produces OKX error <code> alone, and one that also carries a
subCode appends it as (subCode=<code>). A conditional order that fails after acceptance
reports only its code, such as OKX error 51008, because OKX sends only a failCode.
Rejections the adapter raises before contacting OKX, such as local validation failures, carry the adapter's own message and no OKX error code.
Position management
| Feature | Linear perpetual swap | Notes |
|---|---|---|
| Query positions | ✓ | Real-time position updates. |
| Position mode | ✓ | Net vs Long/Short mode (see below). |
| Leverage control | - | Not exposed by the execution client. |
| Margin mode | ✓ | Supports isolated and cross modes. |
Position modes
OKX supports two position modes for derivatives trading:
- Net mode (netting): One position per instrument. Buy and sell orders net against each other. This is the default and recommended mode for most traders.
- Long/Short mode (hedging): Separate long and short positions for the same instrument. This mode supports simultaneous long and short exposure.
Position mode applies account-wide. Set it through the OKX web or app interface, or with
OKXHttpClient.set_position_mode; the client configs do not set it. The adapter handles both
modes when reporting positions: in net mode it derives the position side from the signed
quantity, and in long/short mode it uses the posSide reported by OKX.
Trade modes and margin configuration
OKX's unified account system supports different trade modes for spot and derivatives. Configure the account mode first through the OKX web or app interface; the API cannot set it for the first time.
For account mode details, see the OKX Account Mode documentation.
Trade modes overview
The Python execution config selects trade modes as follows:
| Instrument | Trade mode | Configuration |
|---|---|---|
| Spot | cash | Automatic. |
| Derivative | isolated | Default, or margin_mode=OKXMarginMode.ISOLATED. |
| Derivative | cross | margin_mode=OKXMarginMode.CROSS. |
from nautilus_trader.adapters.okx import OKXExecutionClientConfig
from nautilus_trader.adapters.okx import OKXInstrumentType
from nautilus_trader.adapters.okx import OKXMarginMode
from nautilus_trader.model import AccountId
exec_config = OKXExecutionClientConfig(
account_id=AccountId.from_str("OKX-001"),
instrument_types=[OKXInstrumentType.SWAP],
margin_mode=OKXMarginMode.CROSS,
)The public Python config does not expose spot margin selection, so spot orders use cash
mode. In a mixed spot and derivatives client, margin_mode applies to derivatives only.
Manual trade mode override: You can override the trade mode per order with
params={"td_mode": "..."}. This bypasses adapter selection and can lead to order
rejection when the value does not match the instrument type, such as isolated for
spot instruments.
Only use manual override for requirements that cannot be met through configuration.
Order querying
| Feature | Linear perpetual swap | Notes |
|---|---|---|
| Query open orders | ✓ | List all active orders. |
| Query order history | ✓ | Historical order data. |
| Order status updates | ✓ | Real-time order state changes. |
| Trade history | ✓ | Execution and fill reports. |
Contingent orders
| Feature | Linear perpetual swap | Notes |
|---|---|---|
| Order lists | ✓ | Batch via WS; regular orders only. |
| OCO orders | - | Not submitted by OKXExecutionClient. |
| Bracket orders | - | Not submitted by OKXExecutionClient. |
| Conditional orders | ✓ | Stop and limit-if-touched orders. |
The low-level HTTP client models OKX attached TP/SL and OCO payloads, but
OKXExecutionClient does not translate Nautilus OCO or bracket order lists into those payloads.
Conditional order architecture
Conditional orders (OKX algo orders) use a hybrid architecture:
- Submission: HTTP REST API (
/api/v5/trade/order-algo). - Status updates: WebSocket business endpoint (
/ws/v5/business). Stop and touched orders useorders-algo; trailing stops usealgo-advance. - Cancellation: HTTP REST API while the algo parent is active, then the regular order path after a triggered child becomes authoritative.
The orders-algo channel sends updates only, while algo-advance also sends a snapshot on
subscription. The adapter keeps tracked order context across transport reconnects and deduplicates
replayed advance-algo snapshots. REST reconciliation remains responsible for cold-start and
missed-update recovery.
This design ensures:
- Immediate submission acknowledgment through HTTP.
- Real-time status updates through WebSocket.
- Stable order identity while venue authority moves from the algo parent ID to the triggered child order ID.
Supported conditional order types
| Order type | Trigger types | Notes |
|---|---|---|
STOP_MARKET | Last, Mark, Index | Market execution when triggered. |
STOP_LIMIT | Last, Mark, Index | Limit order placement when triggered. |
MARKET_IF_TOUCHED | Last, Mark, Index | Market execution when price touched. |
LIMIT_IF_TOUCHED | Last, Mark, Index | Limit order placement when price touched. |
TRAILING_STOP_MARKET | - | Callback ratio or spread; optional activation price. |
OKX's close_fraction conditional-order parameter is not normalized to the generic
close_position risk contract. Do not add OKX to full_position_exit_venues based on
close_fraction; leave the venue unlisted so ordinary quantity and notional checks apply.
Trigger price types
Stop and touched orders support different trigger price sources:
- Last price (
TriggerType.LAST_PRICE): Uses the last traded price (default). - Mark price (
TriggerType.MARK_PRICE): Uses the mark price. - Index price (
TriggerType.INDEX_PRICE): Uses the underlying index price.
# Example: Stop loss using mark price trigger
stop_order = order_factory.stop_market(
instrument_id=instrument_id,
order_side=OrderSide.SELL,
quantity=Quantity.from_str("0.1"),
trigger_price=Price.from_str("45000.0"),
trigger_type=TriggerType.MARK_PRICE, # Use mark price for trigger
)
strategy.submit_order(stop_order)Risk management
Liquidation and ADL event handling
The OKX adapter detects exchange-initiated risk management events:
- Liquidation warnings: When
instrument_typesincludesMARGIN,SWAP,FUTURES, orOPTION, the execution client subscribes to theliquidation-warningchannel withinstType=ANYand logs a warning when OKX reports a position nearing liquidation. This is an early warning only: the position may already be liquidated by the time the message arrives, and the adapter surfaces it as a log message rather than a strategy-facing event. - Liquidation orders: When the exchange liquidates a position, the adapter detects the liquidation category and logs warnings with order details. These orders continue through the normal order and fill pipeline.
- Auto-deleveraging (ADL): When OKX closes your position to offset a counterparty's liquidation, the adapter detects and logs the ADL event with position details.
Liquidation-order and ADL detection is driven by the category field on the order record. The
recognized values are:
category | Meaning |
|---|---|
full_liquidation | Full position liquidation. |
partial_liquidation | Partial position liquidation. |
adl | Auto-deleveraging close. |
delivery | Contract delivery at expiry. |
normal / other values | Regular order flow. |
Category detection runs on both paths:
- WebSocket
orderschannel (live order and fill updates). - HTTP
GET /api/v5/trade/orders-history(used during reconciliation and cold-start mass status).
Liquidation and ADL events are logged at WARNING level with details including order ID, instrument, and state. Liquidation warnings instead log position side, size, margin ratio, mark price, and margin mode. Monitor these logs as part of your risk management process.
The adapter forwards these exchange-generated orders as OrderStatusReport and FillReport
messages and sends position updates as PositionStatusReport messages. Because the orders are
untracked at dispatch time, this path does not emit strategy-owned order events directly.
Upstream references:
- Order channel and
categoryfield - Liquidation warning channel
- Auto-Deleveraging mechanism
- Liquidation mechanism
Options trading
The OKX adapter supports trading options (OPTION instrument type) with some differences
from other derivatives. OKX options are inverse contracts settled in the underlying
cryptocurrency.
For full API details see the
OKX Options Trading documentation.
Supported order types
Only limit-style orders are supported. OKX does not allow market orders for options.
| Order type | Supported | Notes |
|---|---|---|
LIMIT | ✓ | Standard limit order. |
MARKET | - | Rejected by the adapter before reaching the API. |
MARKET_TO_LIMIT | - | Rejected by the adapter before reaching the API. |
Options support FOK and IOC time-in-force. OKX uses a dedicated op_fok order type for
options FOK orders; the adapter handles this mapping automatically.
Conditional/algo orders (STOP_MARKET, STOP_LIMIT, MARKET_IF_TOUCHED,
LIMIT_IF_TOUCHED, TRAILING_STOP_MARKET) are not supported for options and are denied.
Pricing modes
Options orders can be priced in three mutually exclusive ways. Pass the pricing mode via
order params:
| Mode | Parameter | Description |
|---|---|---|
| Price | (default) | Standard limit price in the contract's currency. |
| USD | px_usd | Price in USD terms. |
| IV | px_vol | Price in implied volatility (1.0 = 100%). |
# Price in USD
order = strategy.order_factory.limit(
instrument_id=InstrumentId.from_str("BTC-USD-261225-50000-C.OKX"),
order_side=OrderSide.BUY,
quantity=Quantity.from_int(1),
price=Price.from_str("0"), # Placeholder; px_usd takes precedence
params={"px_usd": "100.5"},
)
# Price in implied volatility
order = strategy.order_factory.limit(
instrument_id=InstrumentId.from_str("BTC-USD-261225-50000-C.OKX"),
order_side=OrderSide.BUY,
quantity=Quantity.from_int(1),
price=Price.from_str("0"), # Placeholder; px_vol takes precedence
params={"px_vol": "0.55"},
)When modifying an order, the same px_usd or px_vol params can be passed to the modify
command to amend the price in the original pricing mode.
Option Greeks
OKX publishes two parallel greek sets on the opt-summary channel:
- Black-Scholes (
BLACK_SCHOLES): Greeks denominated in USD. Matches the convention used by the Deribit and Bybit adapters. - Price-adjusted (
PRICE_ADJUSTED): Greeks denominated in the underlying coin units. Matches OKX's native contract convention.
By default the adapter emits both on every opt-summary tick. Each emitted OptionGreeks
carries a convention field set to GreeksConvention.BLACK_SCHOLES or
GreeksConvention.PRICE_ADJUSTED, so receivers can branch per message.
To narrow the stream, pass params["greeks_convention"] on subscribe:
- Single string:
"BLACK_SCHOLES"or"PRICE_ADJUSTED"(case-insensitive). - List of strings:
["BLACK_SCHOLES", "PRICE_ADJUSTED"]. - Omitted: adapter emits both.
Unknown entries log a warning and are skipped. If every requested entry is unknown, the adapter falls back to emitting both.
# Default (both conventions, receiver branches)
self.subscribe_option_greeks(instrument_id)
def on_option_greeks(self, greeks: OptionGreeks) -> None:
if greeks.convention == GreeksConvention.BLACK_SCHOLES:
self._handle_bs(greeks)
else:
self._handle_pa(greeks)# Single-convention narrowing
self.subscribe_option_greeks(
instrument_id,
params={"greeks_convention": "PRICE_ADJUSTED"},
)# Explicit list (equivalent to the default when both are listed)
self.subscribe_option_greeks(
instrument_id,
params={"greeks_convention": ["BLACK_SCHOLES", "PRICE_ADJUSTED"]},
)The data engine deduplicates option-greeks subscriptions by instrument_id, so if two actors
on one node subscribe to the same instrument with different single conventions only the first
one reaches the adapter. The second actor gets the first actor's convention set. Workaround:
either actor can subscribe without params (or with the full list) to receive both streams
and filter locally on greeks.convention.
Position Greeks
OKX position payloads include position-level Black-Scholes Greeks (delta_bs, gamma_bs,
theta_bs, and vega_bs). The adapter's standard PositionStatusReport does not expose these
fields. The opt-summary stream described above provides the adapter's exposed per-instrument
Greeks.
Restrictions
- Reduce-only option orders are rejected by the adapter because OKX does not support the instruction for options.
- Position side defaults to
Net.
Configuration
Option discovery requires at least one instrument_families value, for example BTC-USD.
Pass it to OKXDataClientConfig when loading options from Python. The public Python execution
config constructor does not expose this field, so selecting OKXInstrumentType.OPTION only on
OKXExecutionClientConfig skips option loading and logs a warning.
Event contracts
OKX exposes prediction market contracts through instType=EVENTS. The adapter loads
these instruments as Nautilus BinaryOption instruments and preserves OKX metadata
in the instrument info field under the keys series_id, inst_category,
inst_id_code, state, and rule_type.
Loading event contract instruments
Use OKXInstrumentType.EVENTS in the data or execution client config. The adapter requests the
event contract series list, then requests instruments for each series.
from nautilus_trader.adapters.okx import OKXDataClientConfig
from nautilus_trader.adapters.okx import OKXInstrumentType
data_config = OKXDataClientConfig(instrument_types=[OKXInstrumentType.EVENTS])Event contract market data
The low-level HTTP client exposes OKX's public event contract discovery endpoints:
request_event_contract_series.request_event_contract_events.request_event_contract_markets.
The low-level WebSocket client supports the event-contract-markets channel through
subscribe_event_contract_markets and unsubscribe_event_contract_markets. This
channel publishes market status and floor-strike generation updates, has no initial
snapshot, and does not include instId, so the adapter forwards it as raw venue JSON.
OKX's standard market data endpoints return YES-side data for EVENTS. Derive NO-side
prices from YES-side prices when a strategy needs both outcomes.
Event contract trading
Pass the OKX event outcome through order params when submitting event contract orders:
order = strategy.order_factory.limit(
instrument_id=InstrumentId.from_str("BTC-ABOVE-DAILY-261224-1600-65000.OKX"),
order_side=OrderSide.BUY,
quantity=Quantity.from_int(1),
price=Price.from_str("0.42"),
params={"outcome": "yes"},
)
strategy.submit_order(order)OKX requires outcome for EVENTS orders, which the adapter validates before
sending. OKX ignores the obsolete speedBump request parameter, so the adapter
omits it. Remove speed_bump from existing client calls and order params.
Settlement fills arrive with OKX order category delivery. The adapter parses this
category during live order updates and reconciliation.
Upstream references:
Authentication
To use the OKX adapter, create API credentials in your OKX account:
- Log into your OKX account and navigate to the API management page.
- Create a new API key with the required permissions for trading and data access.
- Record your API key, secret key, and passphrase.
You can provide these credentials through environment variables:
export OKX_API_KEY="your_api_key"
export OKX_API_SECRET="your_api_secret"
export OKX_API_PASSPHRASE="your_passphrase"Or pass them directly in the configuration (not recommended for production).
Demo trading
OKX provides a demo trading environment for testing strategies without real funds.
Setting up a demo account
- Log into your OKX account at okx.com.
- Navigate to Trade > Demo Trading.
- Go to Personal Center within Demo Trading.
- Select Demo Trading API and create a new API key.
- Record your demo API key, secret key, and passphrase.
You can provide demo credentials through environment variables:
export OKX_API_KEY="your_demo_api_key"
export OKX_API_SECRET="your_demo_api_secret"
export OKX_API_PASSPHRASE="your_demo_passphrase"Configuration
Set environment=OKXEnvironment.DEMO in your client configuration:
from nautilus_trader.adapters.okx import OKXDataClientConfig
from nautilus_trader.adapters.okx import OKXEnvironment
data_config = OKXDataClientConfig(environment=OKXEnvironment.DEMO)When demo mode is enabled:
- REST API requests reuse the region's live host with the
x-simulated-trading: 1header. - WebSocket connections use demo endpoints (
wspap.okx.comfor the global region).
Demo API keys are separate from production keys. Create API keys for demo trading through the Demo Trading interface. Production API keys do not work in demo mode.
Regional endpoints
OKX serves distinct endpoints per region, and an API key is only valid against the region
where it was registered (using a key against another region's endpoints returns
API key doesn't exist). Set region to select the correct endpoint set:
| Region | Registered on | REST | WebSocket host |
|---|---|---|---|
GLOBAL | www.okx.com | www.okx.com | ws.okx.com |
EEA | my.okx.com | eea.okx.com | wseea.okx.com |
US | app.okx.com | us.okx.com | wsus.okx.com |
Despite its enum name, US also selects the endpoints for Australian accounts registered on
app.okx.com.
region defaults to GLOBAL. For example, an EEA account:
from nautilus_trader.adapters.okx import OKXDataClientConfig
from nautilus_trader.adapters.okx import OKXRegion
data_config = OKXDataClientConfig(region=OKXRegion.EEA)region selects the regional defaults, and combines with environment to pick the demo
hosts (for example wseeapap.okx.com for EEA demo). Explicit base_url_http and
base_url_ws overrides always take precedence over the region defaults.
Funding rates
The adapter receives funding rate data from the
Funding Rate Channel
WebSocket stream. OKX provides both fundingTime and nextFundingTime in each message,
and the adapter computes interval as the difference between these two values.
For historical funding rate requests, the adapter computes the interval from consecutive funding timestamps returned by the Get Funding Rate History endpoint.
Rate limiting
The adapter enforces OKX's per-endpoint quotas while keeping sensible defaults for REST and WebSocket calls.
OKX enforces per-endpoint and per-account quotas. A rate-limited request returns OKX error code
50011; throttle requests on the affected key before retrying.
REST limits
Every request passes through an internal global bucket of 250 requests per second, plus the endpoint-specific bucket below. The endpoint quotas mirror OKX's published limits where available.
| Key / endpoint | Limit (req/sec) | Notes |
|---|---|---|
okx:global | 250 | Adapter-level shared bucket. |
/api/v5/account/set-position-mode | 2 | OKX 5 requests / 2 seconds, rounded down. |
/api/v5/account/activate-feature | 2 | OKX 5 requests / 2 seconds, rounded down. |
/api/v5/account/balance | 5 | OKX 10 requests / 2 seconds. |
/api/v5/account/trade-fee | 2 | OKX 5 requests / 2 seconds, rounded down. |
/api/v5/account/instruments | 10 | OKX 20 requests / 2 seconds. |
/api/v5/account/positions | 5 | OKX 10 requests / 2 seconds. |
/api/v5/account/positions-history | 5 | OKX 10 requests / 2 seconds. |
/api/v5/public/instruments | 10 | OKX 20 requests / 2 seconds. |
/api/v5/public/position-tiers | 5 | OKX 10 requests / 2 seconds. |
/api/v5/public/event-contract/series | 5 | OKX 10 requests / 2 seconds. |
/api/v5/public/event-contract/events | 5 | OKX 10 requests / 2 seconds. |
/api/v5/public/event-contract/markets | 5 | OKX 10 requests / 2 seconds. |
/api/v5/public/opt-summary | 10 | OKX 20 requests / 2 seconds. |
/api/v5/public/price-limit | 10 | OKX 20 requests / 2 seconds. |
/api/v5/public/time | 5 | OKX 10 requests / 2 seconds. |
/api/v5/public/mark-price | 5 | OKX 10 requests / 2 seconds. |
/api/v5/public/funding-rate-history | 5 | OKX 10 requests / 2 seconds. |
/api/v5/market/index-tickers | 10 | OKX 20 requests / 2 seconds. |
/api/v5/market/books | 20 | OKX 40 requests / 2 seconds. |
/api/v5/market/books-rpi | 20 | Adapter bucket; OKX publishes 20 / 2 sec. |
/api/v5/market/candles | 20 | OKX 40 requests / 2 seconds. |
/api/v5/market/history-candles | 10 | OKX 20 requests / 2 seconds. |
/api/v5/market/history-trades | 10 | OKX 20 requests / 2 seconds. |
/api/v5/sprd/spreads | 10 | OKX 20 requests / 2 seconds. |
/api/v5/sprd/order | 10 | OKX 20 requests / 2 seconds. |
/api/v5/sprd/cancel-order | 10 | OKX 20 requests / 2 seconds. |
/api/v5/sprd/mass-cancel | 5 | OKX 10 requests / 2 seconds. |
/api/v5/sprd/orders-pending | 5 | OKX 10 requests / 2 seconds. |
/api/v5/sprd/orders-history | 10 | OKX 20 requests / 2 seconds. |
/api/v5/sprd/trades | 10 | OKX 20 requests / 2 seconds. |
/api/v5/trade/order | 30 | OKX 60 requests / 2 seconds. |
/api/v5/trade/batch-orders | 7 | OKX 300 orders / 2 seconds, rounded down. |
/api/v5/trade/amend-order | 30 | OKX 60 requests / 2 seconds. |
/api/v5/trade/amend-batch-orders | 7 | OKX 300 orders / 2 seconds, rounded down. |
/api/v5/trade/cancel-batch-orders | 7 | OKX 300 orders / 2 seconds, rounded down. |
/api/v5/trade/orders-pending | 30 | OKX 60 requests / 2 seconds. |
/api/v5/trade/orders-history | 20 | OKX 40 requests / 2 seconds. |
/api/v5/trade/fills | 30 | OKX 60 requests / 2 seconds. |
/api/v5/trade/order-algo | 10 | OKX 20 requests / 2 seconds. |
/api/v5/trade/cancel-algos | 1 | OKX 20 orders / 2 seconds. |
/api/v5/trade/cancel-advance-algos | 1 | Conservative bucket, see below. |
/api/v5/trade/amend-algos | 10 | OKX 20 requests / 2 seconds. |
/api/v5/trade/orders-algo-pending | 10 | OKX 20 requests / 2 seconds. |
/api/v5/trade/orders-algo-history | 10 | OKX 20 requests / 2 seconds. |
All keys include the okx:global bucket. URLs are normalized with query strings removed
before rate limiting, so requests with different filters share the same quota.
The adapter's /api/v5/market/books-rpi bucket is 20 requests per second, while OKX publishes
20 requests per 2 seconds. The venue limit remains authoritative, so callers should keep RPI book
snapshot traffic within the published quota.
For order-based batch quotas, the adapter uses request-level buckets that assume full
batch sizes: 20 orders per request for regular batch operations and 10 orders per
request for algo cancels. OKX's public docs do not list a rate limit for
/api/v5/trade/cancel-advance-algos, so the adapter applies a conservative bucket; the HTTP
client calls that endpoint to cancel advance algo orders such as trailing stops.
WebSocket limits
- Connection establishment: 3 requests per second (per IP).
- Subscription operations (subscribe/unsubscribe/login): 480 requests per hour per connection.
Order operation buckets mirror OKX's published limits where available.
| Operation key | Limit (req/sec) | Notes |
|---|---|---|
order | 30 | OKX 60 requests / 2 seconds. |
cancel | 30 | OKX 60 requests / 2 seconds. |
amend | 30 | OKX 60 requests / 2 seconds. |
batch-order | 7 | OKX 300 orders / 2 seconds, rounded down for full batches. |
batch-cancel | 7 | OKX 300 orders / 2 seconds, rounded down for full batches. |
batch-amend | 7 | OKX 300 orders / 2 seconds, rounded down for full batches. |
mass-cancel | 2 | OKX 5 requests / 2 seconds, rounded down. |
algo-order | 10 | OKX 20 requests / 2 seconds. |
algo-cancel | 1 | OKX 20 orders / 2 seconds, rounded down for full batches. |
See the OKX rate limit documentation.
Reconciliation
The OKX adapter applies separate reconciliation policies to current venue state and terminal history:
| Data | Unset lookback | Explicit lookback | OKX source |
|---|---|---|---|
| Pending regular orders | All current orders | All current orders | Regular pending orders |
| Live algo orders | All current orders | All current orders | Algo pending orders |
| Current positions | All current positions | All current positions | Account positions |
| Terminal orders and fills | 3 days | Up to 7 days | Order and trade history |
| Fill lookback <= 3 days | Recent fills | Recent fills | /api/v5/trade/fills |
| Fill lookback > 3 days | Not requested | Extended fills | /api/v5/trade/fills-history |
Values above 7 days are clamped to the longest complete window across the regular order history and spread trade history endpoints used for reconciliation. This is not a limit on all archived data available from OKX.
OKX reports no size for a live algo order placed with close_fraction, because the order closes
the whole position when it triggers. REST order status reports use the size of the position that
OKX links to the order through closeOrderAlgo. Without a linked position, the report keeps a zero
quantity and the adapter logs a warning. Reconciliation does not load an external order with zero
quantity.
Unacknowledged submissions
OKX defines 50004 and 51149 as unknown request outcomes.
The adapter leaves these submissions unresolved, but the default execution policy can still resolve
them locally when reconciliation checks exhaust. OKX does not enable submission retention automatically.
To retain unacknowledged submissions, including those with no response, select the existing engine policy when constructing the node:
use nautilus_live::{
config::LiveExecutionEngineConfig,
execution::submission::SubmissionRecoveryPolicy,
};
let exec_engine = LiveExecutionEngineConfig {
submission_recovery_policy: SubmissionRecoveryPolicy::RetainUnresolved,
..Default::default()
};This is a node execution-engine policy, not an OKX client setting. Recovery queries remain bounded;
exhaustion publishes SubmissionRecoveryExhausted once and preserves the submission identity for
later authoritative evidence without resubmitting the order. See
submission recovery
for confirmation and query-budget rules. Retained submissions still unresolved at the node's
delay_post_stop boundary produce an incomplete-recovery shutdown error
after teardown. The policy does not change timeout resolution for commands on already accepted
orders, provide crash-durable recovery, or prove that positions are flat.
Configuration
Data client
The OKX data client provides the following Python configuration options.
| Option | Default | Description |
|---|---|---|
instrument_types | [OKXInstrumentType.SPOT] | OKX instrument types to load. |
instrument_families | None | Required for options (BTC-USD); filters futures, swaps, and events when set. |
load_spreads | False | Loads live spread instruments. |
base_url_http | None | Override for the OKX REST endpoint. |
base_url_ws_public | None | Override for the public WebSocket URL. |
base_url_ws_business | None | Override for the business WebSocket URL. |
api_key | None | Falls back to OKX_API_KEY when unset. |
api_secret | None | Falls back to OKX_API_SECRET when unset. |
api_passphrase | None | Falls back to OKX_API_PASSPHRASE. |
environment | LIVE | Environment enum (LIVE or DEMO). |
region | GLOBAL | Region enum (GLOBAL, EEA, or US). |
http_timeout_secs | 60 | REST market data request timeout. |
max_retries | 3 | Retry attempts for recoverable REST errors. |
retry_delay_initial_ms | 1,000 | Initial delay before retrying. |
retry_delay_max_ms | 10,000 | Maximum exponential backoff delay. |
update_instruments_interval_mins | 60 | REST instrument cache reconciliation interval in minutes; 0 disables. |
book_stale_check_interval_secs | 5 | Stale book check interval. |
book_stale_threshold_secs | 30 | Idle time before a stale book warning. |
book_snapshot_timeout_secs | 10 | Initial, reconnect, and recovery snapshot wait. |
vip_level | None | Enables higher-depth books by VIP tier. |
proxy_url | None | Optional HTTP and WebSocket proxy URL. |
transport_backend | Sockudo | WebSocket transport backend. |
Set book_stale_check_interval_secs or book_stale_threshold_secs to 0 to disable stale-feed
warnings. Set book_snapshot_timeout_secs to 0 to disable snapshot deadlines, as described in
Order book recovery. Quiet markets can idle without book updates; increase
book_stale_threshold_secs for sparse instruments.
Supported data client instrument_types values are SPOT, MARGIN, SWAP,
FUTURES, OPTION, and EVENTS. See Options trading before selecting
OPTION from Python.
Spread instruments use load_spreads instead of instrument_types because OKX serves them from
/api/v5/sprd/spreads.
Execution client
The OKX execution client provides the following Python configuration options.
| Option | Default | Description |
|---|---|---|
instrument_types | [OKXInstrumentType.SPOT] | Tradable OKX instrument types. |
load_spreads | False | Loads live spread instruments. |
account_id | Required | Nautilus account ID for the client. |
base_url_http | None | Override for the OKX trading REST endpoint. |
base_url_ws_private | None | Override for the private WebSocket URL. |
base_url_ws_business | None | Override for the business WebSocket URL. |
api_key | None | Falls back to OKX_API_KEY when unset. |
api_secret | None | Falls back to OKX_API_SECRET when unset. |
api_passphrase | None | Falls back to OKX_API_PASSPHRASE. |
environment | LIVE | Environment enum (LIVE or DEMO). |
region | GLOBAL | Region enum (GLOBAL, EEA, or US). |
margin_mode | None | Margin mode (ISOLATED or CROSS). |
spot_trade_quote_ccy | None | SPOT tradeQuoteCcy override. Set "USD" to keep USD after migrating to Crypto-USDC. |
http_timeout_secs | 60 | REST trading request timeout. |
max_retries | 3 | Retry attempts for recoverable REST errors. Order submission endpoints are exempt and always send once. |
retry_delay_initial_ms | 1,000 | Initial delay before retrying. |
retry_delay_max_ms | 10,000 | Maximum exponential backoff delay. |
auth_timeout_secs | None | Override WebSocket authentication timeout. |
proxy_url | None | Optional HTTP and WebSocket proxy URL. |
transport_backend | Sockudo | WebSocket transport backend. |
Supported execution client instrument_types values are SPOT, MARGIN, SWAP,
FUTURES, OPTION, and EVENTS. See Options trading before selecting
OPTION from Python.
Spread instruments use OKX spread IDs instead of instrument_types; load them with
load_spreads=True on the data and execution clients before trading them.
See USD to USDC spot migration for spot_trade_quote_ccy.
Manual endpoint overrides
Setting region (see Regional endpoints) selects the correct EEA or
US endpoints automatically, which is the recommended approach. The explicit base_url_*
overrides below remain available for proxies, custom routing, or endpoints not covered by a
region; they take precedence over the region default. The EEA bases are shown as an example.
| Config field | Live base | Demo base | WebSocket path |
|---|---|---|---|
base_url_http | https://eea.okx.com | https://eea.okx.com | |
base_url_ws_public | wss://wseea.okx.com:8443 | wss://wseeapap.okx.com:8443 | /ws/v5/public |
base_url_ws_private | wss://wseea.okx.com:8443 | wss://wseeapap.okx.com:8443 | /ws/v5/private |
base_url_ws_business | wss://wseea.okx.com:8443 | wss://wseeapap.okx.com:8443 | /ws/v5/business |
For WebSocket fields, join the base and path in the same row.
Use base_url_ws_public with data client configs and base_url_ws_private with execution client
configs. When overriding either WebSocket URL, also set base_url_ws_business because the adapter
does not derive a custom business WebSocket URL from the other override.
See the OKX EEA API documentation for the current official endpoint list.
Use OKXDataClientConfig with OKXDataClientFactory and OKXExecutionClientConfig with
OKXExecutionClientFactory. The Python examples show a complete
LiveNode.builder(...) configuration for data and execution clients.
Deterministic simulation testing
OKX is the reference implementation for the
adapter DST contract, which the
adapter developer guide requires of
maintained adapters. This section records the audited OKX slice: the files the static gate covers,
the exclusion rationale for the rest, and the runtime slices the dst tests prove.
Audited files
Audited OKX DST-path production files route state-affecting clock reads and timers through the DST
seams and sort reconnect and bulk-unsubscribe subscription commands. The static gate covers these
files in crates/adapters/okx/src:
- book:
mod.rs,recovery.rs,sync.rs - common:
parse.rs,task.rs - Top level:
config.rs,data.rs,execution.rs - http:
client.rs,models.rs,query.rs - websocket:
client.rs,dispatch.rs,handler.rs,messages.rs,parse.rs,subscription.rs
Static coverage alone does not establish runtime eligibility: these files also serve paths outside a proven runtime slice.
Excluded files
The remaining non-Python OKX production files stay excluded because they carry no DST-path state, clock, RNG, task, or transport surface:
- Module declarations:
lib.rs,common/mod.rs,http/mod.rs,websocket/mod.rs. - Pure venue types:
common/enums.rs,websocket/enums.rs,http/error.rs,websocket/error.rs,common/models.rs. - Pure tables and deterministic mappings:
common/urls.rs(endpoint tables) andcommon/consts.rs(pure predicates, validators, and wire-value and channel resolvers; itsAHashSetis contains-only retry lookup, never iterated). - Deterministic helpers:
common/credential.rs, whose HMAC signs a caller-provided timestamp and whose credential resolution reads only config or declared environment at construction, andcommon/failure.rs, which is pure error classification. - Construction wiring only:
factories.rs. - Test-only or placeholder:
common/testing.rs,http/parse.rs.
check-dst-conventions records this rationale next to ADAPTER_PATHS; re-audit a file if it
gains DST-path runtime logic. The seven files under src/python/ stay excluded by the repo-wide
Python/FFI policy, not by this audit (see
Python and FFI are not in DST scope).
Proven and unproven slices
Focused Madsim tests in crates/adapters/okx/tests/integration/dst.rs prove this slice:
- Subscribe bytes: public WebSocket quotes, trades, and books; business WebSocket bars.
- Reconnect order: multi-instrument quote reconnect in topic order. Reconnect also clears quote
and funding caches in
data.rsso a new generation cannot reuse prior values. - Login frame: key, passphrase, and signature derived from the simulated wall clock.
- Wire fields: single order-submit, amend, and cancel; algo order-submit and cancel; batch order-submit in input order.
Complete request-to-wire-to-domain fresh-process comparison stays in the downstream DST harness. These share the DST facades and convention gate but remain unproven:
- Other public channels: tickers, funding rates, index tickers, option summaries, other book depths, and the other candle granularities.
- Private data streams.
- Mass cancel, batch amend and cancel, and spread orders.
- HTTP report and reconciliation paths.
Simulation test leg
The standard-precision leg runs the integration dst tests under simulation without the crate's
default high-precision feature.
Contributing
For additional features or to contribute to the OKX adapter, please see our contributing guide.
Lighter
Lighter is a decentralized central-limit-order-book exchange for spot and perpetual futures. The venue settles through an Ethereum zero-knowledge rollup...
Polymarket
Founded in 2020, Polymarket is a decentralized prediction market platform that enables traders to speculate on event outcomes by buying and selling outcome...