OKX SDK
Complete reference for ctx.okx — programmatic trading on OKX from worker code.
OKX V5 API
This SDK wraps the OKX V5 unified API for spot, perpetual swap, futures, and options. Live and demo environments supported.
Setup
- Add an OKX trading block to your workspace canvas.
- Open the inspector → API Keys → pick a key (Settings → API Keys → OKX to add new). OKX requires
api_key+api_secret+passphrase. - Connect the block to your Worker block via an edge.
ctx.okxis now available.
def tick(ctx):
t = ctx.okx.get_ticker("BTC-USDT-SWAP")
ctx.log.info(f"BTC perp: {t['data'][0]['last']}")If no OKX block is connected:
RuntimeError: No trading block connected. Connect an OKX block to this worker.Instrument Naming & Modes
OKX uses inst_id strings shaped by category:
| Pattern | Example | Meaning |
|---|---|---|
{COIN}-{COIN} | BTC-USDT | Spot pair |
{COIN}-{COIN}-SWAP | BTC-USDT-SWAP | Perpetual swap |
{COIN}-{COIN}-{EXPIRY} | BTC-USDT-251227 | Dated future |
{COIN}-USD-{EXP}-{STRIKE}-{C|P} | BTC-USD-251227-100000-C | Option |
inst_type parameter (where applicable):
| Value | Meaning |
|---|---|
SPOT | Spot |
MARGIN | Margin |
SWAP | Perpetual swap |
FUTURES | Dated futures |
OPTION | Options |
Account modes (api_mode on key):
| Mode | Endpoint | Notes |
|---|---|---|
live | www.okx.com | Real funds |
demo | www.okx.com + x-simulated-trading: 1 | Paper-trading, real prices |
There's no testnet — OKX uses a header flag for demo mode.
Market Data
get_ticker
Latest ticker.
ctx.okx.get_ticker(inst_id: str = "BTC-USDT") -> dictReturns: {"code", "data": [{"instId", "last", "lastSz", "askPx", "bidPx", "open24h", "high24h", "low24h", "volCcy24h", "vol24h", …}]}
t = ctx.okx.get_ticker("BTC-USDT")
last = float(t["data"][0]["last"])WebSocket cache
PRO/MAX plans return cached ticker data at 0ms latency from the live WS stream — no REST call.
get_orderbook
Order book depth.
ctx.okx.get_orderbook(inst_id: str = "BTC-USDT") -> dictReturns: {"data": [{"asks": [[price, size, ...]], "bids": [[price, size, ...]], "ts"}]}
get_klines
Candlestick OHLCV.
ctx.okx.get_klines(inst_id: str = "BTC-USDT", bar: str = "1H", limit: int = 100) -> dictbar values: "1m" "3m" "5m" "15m" "30m" "1H" "2H" "4H" "6H" "12H" "1D" "1W" "1M".
Returns: {"data": [[ts, open, high, low, close, volume, volCcy, volCcyQuote, confirm]]}.
get_instruments
All instruments for a category.
ctx.okx.get_instruments(inst_type: str = "SWAP") -> dictget_funding_rate
Current funding rate for a perpetual.
ctx.okx.get_funding_rate(inst_id: str = "BTC-USDT-SWAP") -> dictget_mark_price
Mark price for derivatives.
ctx.okx.get_mark_price(inst_type: str = "SWAP", inst_id: str = None) -> dictmp = ctx.okx.get_mark_price(inst_id="BTC-USDT-SWAP")
mark = float(mp["data"][0]["markPx"])get_open_interest
Open interest for derivatives.
ctx.okx.get_open_interest(inst_type: str = "SWAP", inst_id: str = None) -> dictAccount & Positions
get_balance
Account balance for one or all currencies.
ctx.okx.get_balance(ccy: str = None) -> dictWebSocket cache
PRO/MAX: cached from private WS at 0ms.
get_positions
Open positions.
ctx.okx.get_positions(inst_type: str = None) -> dictWebSocket cache
PRO/MAX: cached at 0ms.
get_position_history
Closed position history with entry / exit and realised P&L.
ctx.okx.get_position_history(
inst_type: str = None,
inst_id: str = None,
mgn_mode: str = None, # "isolated" | "cross"
type: str = None, # "1" partial close / "2" full close / "3" liquidation
# "4" auto-deleveraging / "5" settlement
limit: int = 100,
) -> dictget_account_config
Account-level config: position mode, margin mode, account level (Lv1-Lv4).
ctx.okx.get_account_config() -> dictset_position_mode
Switch derivatives mode for the whole account.
ctx.okx.set_position_mode(pos_mode: str = "long_short_mode") -> dict
# pos_mode: "long_short_mode" (hedge) | "net_mode" (one-way)set_leverage
Set leverage on an instrument.
ctx.okx.set_leverage(inst_id: str, lever: str, mgn_mode: str = "cross") -> dict
# mgn_mode: "cross" | "isolated"get_max_size
Max buy / sell size given current leverage + balance.
ctx.okx.get_max_size(
inst_id: str,
td_mode: str = "cross",
ccy: str = None,
leverage = None,
) -> dictm = ctx.okx.get_max_size("BTC-USDT-SWAP", td_mode="cross")
ctx.log.info(f"Max long: {m['data'][0]['maxBuy']}, max short: {m['data'][0]['maxSell']}")Orders
place_order
Place a single order. Auto-routes spot / margin / swap / futures by inst_id.
ctx.okx.place_order(
inst_id: str,
side: str, # "buy" | "sell"
sz: str, # contract count for derivatives, base qty for spot
ord_type: str = "market", # "market" | "limit" | "post_only" | "fok" | "ioc"
px: str = None, # required for non-market types
td_mode: str = "cross", # "cash" (spot) | "cross" | "isolated"
pos_side: str = None, # "long" | "short" (hedge mode only)
reduce_only: bool = False,
) -> dict# Market buy 0.01 BTC perp at cross margin
r = ctx.okx.place_order("BTC-USDT-SWAP", "buy", "1", "market") # 1 contract = 0.01 BTC
# Spot limit
r = ctx.okx.place_order("BTC-USDT", "buy", "0.001", "limit", px="65000", td_mode="cash")cancel_order
Cancel a single order.
ctx.okx.cancel_order(inst_id: str, order_id: str = None, cl_ord_id: str = None) -> dictcancel_all_orders
Cancel ALL pending orders, optionally filtered.
ctx.okx.cancel_all_orders(inst_type: str = None, inst_id: str = None) -> dict# Kill switch — cancel everything
ctx.okx.cancel_all_orders()amend_order
Modify qty / price of an unfilled order.
ctx.okx.amend_order(
inst_id: str,
order_id: str = None,
cl_ord_id: str = None,
new_sz: str = None,
new_px: str = None,
) -> dictget_orders
Active (unfilled) orders.
ctx.okx.get_orders(inst_type: str = None) -> dictWebSocket cache
PRO/MAX: cached at 0ms.
get_order_history
Filled / cancelled order history (last 7 days).
ctx.okx.get_order_history(
inst_type: str = "SWAP",
inst_id: str = None,
state: str = None, # "canceled" | "filled" | "partially_filled"
limit: int = 100,
) -> dictget_fills
Trade fills with fees.
ctx.okx.get_fills(inst_type: str = None, inst_id: str = None) -> dictclose_position
Close a position immediately.
ctx.okx.close_position(inst_id: str, mgn_mode: str = "cross") -> dictAlgo Orders
OKX's algo orders are SDK-level smart orders — placed and managed independently of regular orders. Use them for TP/SL pairs, conditional triggers, trailing stops, iceberg, and TWAP.
place_algo_order
Universal algo-order entry point.
ctx.okx.place_algo_order(
inst_id: str,
side: str, # "buy" | "sell"
sz,
ord_type: str = "conditional",
td_mode: str = "cross",
# ── For "conditional" / "oco" (TP+SL paired) ──
tp_trigger_px = None, # take-profit trigger
tp_ord_px = None, # take-profit limit price (-1 = market)
sl_trigger_px = None, # stop-loss trigger
sl_ord_px = None, # stop-loss limit price (-1 = market)
# ── For "trigger" (price → market/limit) ──
trigger_px = None,
order_px = None, # -1 = market
# ── Common ──
algo_cl_ord_id: str = None,
reduce_only: bool = False,
) -> dictord_type | Use for |
|---|---|
"conditional" | One-way TP or SL |
"oco" | TP and SL paired (one cancels the other) |
"trigger" | Price trigger then market or limit |
"move_order_stop" | Trailing stop |
"iceberg" | Iceberg (visible chunks of a large order) |
"twap" | TWAP (time-weighted average price) |
# OCO: take 5% profit OR stop-loss at -2%
entry = 65000
ctx.okx.place_algo_order(
inst_id="BTC-USDT-SWAP",
side="sell", sz="1",
ord_type="oco",
tp_trigger_px=str(entry * 1.05), tp_ord_px="-1", # market take-profit
sl_trigger_px=str(entry * 0.98), sl_ord_px="-1", # market stop-loss
reduce_only=True,
)cancel_algo_order
Cancel an algo order by algoId.
ctx.okx.cancel_algo_order(algo_id: str, inst_id: str) -> dictget_algo_orders
List active algo orders.
ctx.okx.get_algo_orders(
ord_type: str = "conditional",
inst_type: str = None,
inst_id: str = None,
limit: int = 100,
) -> dictCommon Patterns
Demo mode during development
Set the API-key mode to demo in Settings → API Keys → OKX. All worker calls auto-add the x-simulated-trading: 1 header.
Error handling
def tick(ctx):
try:
positions = ctx.okx.get_positions()
except Exception as e:
ctx.log.error(f"OKX unreachable: {e}")
returnOkxAPIError (extends ExchangeAPIError) is raised for API errors. Network failures raise httpx.HTTPStatusError.
Cloud-Run proxy
PRO+ workers route OKX traffic through a Cloud Run proxy with rotating exit IPs. Transparent — no code changes.
WebSocket caching
PRO+ subscribes to public + private streams in the background. get_ticker, get_orderbook, get_balance, get_positions, get_orders auto-prefer the cache when fresh.
Recipes
OCO entry (TP + SL paired)
def tick(ctx):
pos = ctx.okx.get_positions(inst_type="SWAP")
if any(p["instId"] == "BTC-USDT-SWAP" and float(p["pos"]) != 0
for p in pos.get("data", [])):
return # already in a position
px = float(ctx.okx.get_ticker("BTC-USDT-SWAP")["data"][0]["last"])
# Limit-buy 1 contract
ctx.okx.place_order("BTC-USDT-SWAP", "buy", "1", "limit", px=str(round(px - 50)))
# Pre-stage OCO: TP +3%, SL -1.5%
ctx.okx.place_algo_order(
inst_id="BTC-USDT-SWAP",
side="sell", sz="1",
ord_type="oco",
tp_trigger_px=str(round(px * 1.03, 1)), tp_ord_px="-1",
sl_trigger_px=str(round(px * 0.985, 1)), sl_ord_px="-1",
reduce_only=True,
)
ctx.log.info(f"Entry placed at {px:.2f} with OCO exits")Trailing stop after 2% in profit
def tick(ctx):
pos = ctx.okx.get_positions(inst_type="SWAP")
long = next((p for p in pos.get("data", [])
if p["instId"] == "BTC-USDT-SWAP" and float(p["pos"]) > 0), None)
if not long:
return
entry = float(long["avgPx"])
mark = float(ctx.okx.get_mark_price(inst_id="BTC-USDT-SWAP")["data"][0]["markPx"])
# Activate 1% trailing stop once we're +2%
if mark > entry * 1.02:
existing = ctx.okx.get_algo_orders(ord_type="move_order_stop",
inst_id="BTC-USDT-SWAP")
if existing.get("data"):
return # already active
ctx.okx.place_algo_order(
inst_id="BTC-USDT-SWAP",
side="sell", sz=long["pos"],
ord_type="move_order_stop",
order_px="-1", # -1 = market once triggered
reduce_only=True,
)
ctx.log.info("Trailing stop activated")Cross-instrument funding-rate arb scanner
def tick(ctx):
swaps = ctx.okx.get_instruments(inst_type="SWAP")["data"]
rates = []
for s in swaps[:20]:
if not s["instId"].endswith("-USDT-SWAP"):
continue
try:
r = ctx.okx.get_funding_rate(s["instId"])
rates.append((s["instId"], float(r["data"][0]["fundingRate"])))
except Exception:
pass
rates.sort(key=lambda x: abs(x[1]), reverse=True)
for inst, rate in rates[:5]:
ctx.log.info(f"{inst:25} funding={rate*100:+.4f}%")Reference
| OKX V5 endpoint | SDK method |
|---|---|
GET /api/v5/market/ticker | get_ticker |
GET /api/v5/market/books | get_orderbook |
GET /api/v5/market/candles | get_klines |
GET /api/v5/public/instruments | get_instruments |
GET /api/v5/public/funding-rate | get_funding_rate |
GET /api/v5/public/mark-price | get_mark_price |
GET /api/v5/public/open-interest | get_open_interest |
GET /api/v5/account/balance | get_balance |
GET /api/v5/account/positions | get_positions |
GET /api/v5/account/positions-history | get_position_history |
GET /api/v5/account/config | get_account_config |
POST /api/v5/account/set-position-mode | set_position_mode |
POST /api/v5/account/set-leverage | set_leverage |
GET /api/v5/account/max-size | get_max_size |
POST /api/v5/trade/order | place_order |
POST /api/v5/trade/cancel-order | cancel_order |
POST /api/v5/trade/cancel-batch-orders | cancel_all_orders |
POST /api/v5/trade/amend-order | amend_order |
GET /api/v5/trade/orders-pending | get_orders |
GET /api/v5/trade/orders-history | get_order_history |
GET /api/v5/trade/fills | get_fills |
POST /api/v5/trade/close-position | close_position |
POST /api/v5/trade/order-algo | place_algo_order |
POST /api/v5/trade/cancel-algos | cancel_algo_order |
GET /api/v5/trade/orders-algo-pending | get_algo_orders |