ctx.order
Places an order and returns once the exchange returns the order ID. By default, the order is placed through the exchange's REST API; when the option via is set to ws, the order is placed through the exchange's WebSocket trading API. Subsequent order status changes and fills are pushed through the private stream subscribed via ctx.subscribe. No subscription is required before calling; it can be called from both the main thread and threads created by threading.Thread(). The version that does not wait for the result is ctx.orderAsync.
ctx.order(ex, symbol, side, px, qty)
ctx.order(ex, symbol, side, px, qty, options)Examples
-
Places a maker-only (post-only) limit buy order for one contract at a price 1% below the best bid, then cancels it after 5 seconds. If placing or canceling the order fails, an error object is returned instead of an exception being thrown.
javascriptfunction main() { var t = exchange.GetTicker("BTC_USDT") if (!t) { return } var id = ctx.order(exchange, "BTC_USDT", "buy", _N(t.Buy * 0.99, 2), 0.001, {postOnly: true}) if (id instanceof Error) { Log("order failed:", id.message) return } Log("order id:", id) Sleep(5000) var r = ctx.cancel(exchange, "BTC_USDT", id) if (r instanceof Error) { Log("cancel failed:", r.message) // e.g. the order has already been filled } }pythondef main(): t = exchange.GetTicker("BTC_USDT") if not t: return oid = ctx.order(exchange, "BTC_USDT", "buy", _N(t["Buy"] * 0.99, 2), 0.001, {"postOnly": True}) if isinstance(oid, CtxError): Log("order failed:", oid) return Log("order id:", oid) Sleep(5000) r = ctx.cancel(exchange, "BTC_USDT", oid) if isinstance(r, CtxError): Log("cancel failed:", r) # e.g. the order has already been filledrustfn main() { let Ok(t) = exchange.GetTicker("BTC_USDT") else { return }; // Options are passed as a JSON string; pass "{}" when there are no options let px = _N(t.Buy * 0.99, 2); let id = match ctx::order(exchange, "BTC_USDT", "buy", px, 0.001, r#"{"postOnly":true}"#) { Ok(id) => id, Err(e) => { Log!("order failed:", e); return; } }; Log!("order id:", id); Sleep(5000); if let Err(e) = ctx::cancel(exchange, "BTC_USDT", &id) { Log!("cancel failed:", e); // e.g. the order has already been filled } } -
Place an IOC buy order over WebSocket with a client order ID, then receive the order's fill reports and final state from the private stream. If the order state is unknown, do not resubmit the order; reconcile it by its client order ID instead.
javascriptfunction main() { var acct = ctx.subscribe(exchange, {channel: "private"}) var t = exchange.GetTicker("BTC_USDT") if (!t) { return } var cid = "demo" + Date.now() var px = _N(t.Sell * 1.001, 2) var id = ctx.order(exchange, "BTC_USDT", "buy", px, 0.001, {via: "ws", tif: "ioc", clientId: cid}) if (id instanceof Error) { if (id.message.indexOf("order state unknown") === 0) { Log("state unknown, check the order by clientId", cid) // do not place the order again } else { Log("order failed:", id.message) } return } var t0 = Date.now() while (Date.now() - t0 < 10000) { var ev = ctx.poll([acct], 1000) if (!ev) { continue } if (ev.kind === 19 && ev.orderId === id) { Log("fill", ev.tradeId, ev.px, ev.qty, ev.liquidity, "fee:", ev.fee, ev.feeCcy) } else if (ev.kind === 16 && ev.id === id) { if (ev.state === "filled" || ev.state === "canceled") { Log("order done:", ev.state, "filled:", ev.filledQty, "avgPx:", ev.avgPx) break } } } }pythonimport time def main(): acct = ctx.subscribe(exchange, {"channel": "private"}) t = exchange.GetTicker("BTC_USDT") if not t: return cid = "demo%d" % int(time.time() * 1000) opts = {"via": "ws", "tif": "ioc", "clientId": cid} oid = ctx.order(exchange, "BTC_USDT", "buy", _N(t["Sell"] * 1.001, 2), 0.001, opts) if isinstance(oid, CtxError): if str(oid).startswith("order state unknown"): Log("state unknown, check the order by clientId", cid) # do not place the order again else: Log("order failed:", oid) return t0 = time.time() while time.time() - t0 < 10: ev = ctx.poll([acct], 1000) if ev is None: continue if ev.kind == 19 and ev.orderId == oid: Log("fill", ev.tradeId, ev.px, ev.qty, ev.liquidity, "fee:", ev.get("fee"), ev.feeCcy) elif ev.kind == 16 and ev.id == oid and ev.state in ("filled", "canceled"): Log("order done:", ev.state, "filled:", ev.filledQty, "avgPx:", ev.avgPx) breakrustuse sdk::ctx::EventExt; fn main() { let acct = ctx::subscribe(exchange, ctx::SubOpts::private()).unwrap(); let Ok(t) = exchange.GetTicker("BTC_USDT") else { return }; let cid = format!("demo{}", UnixNano() / 1_000_000); let opts = format!(r#"{{"via":"ws","tif":"ioc","clientId":"{}"}}"#, cid); let id = match ctx::order(exchange, "BTC_USDT", "buy", _N(t.Sell * 1.001, 2), 0.001, &opts) { Ok(id) => id, Err(e) if e.contains("order state unknown") => { Log!("state unknown, check the order by clientId", cid); // do not place the order again return; } Err(e) => { Log!("order failed:", e); return; } }; let t0 = Unix(); while Unix() - t0 < 10 { match ctx::poll(&[acct.id()], Some(1000)) { ctx::Polled::Event(ev) => { if let Some(f) = ev.fill().filter(|f| f.order_id == id) { let fee = format!("{:?}", f.fee); // None: the exchange did not provide the fee Log!("fill", f.trade_id, f.px, f.qty, f.liquidity, "fee:", fee, f.fee_ccy); } else if let Some(o) = ev.order().filter(|o| o.id == id) { if o.state == "filled" || o.state == "canceled" { Log!("order done:", o.state, "filled:", o.filled_qty, "avgPx:", o.avg_px); break; } } } ctx::Polled::Stopped => break, _ => {} } } }
Returns
| Type | Description |
string / object | On success, returns the exchange's order ID, which matches |
Arguments
| Name | Type | Required | Description |
ex | object / number | Yes | Order account: an exchange object ( |
symbol | string | Yes | Symbol code, e.g. |
side | string | Yes | Trade direction: |
px | number | Yes | Order price. Not used for market orders; |
qty | number | Yes | Order quantity, must be greater than |
options | object | No | Order options |
See Also
Remarks
Fields supported by options:
| Field | Description |
|---|---|
| type | Order type: limit (default, limit order), market (market order) |
| tif | Time in force: gtc (default), ioc, fak, fok, postOnly |
| reduceOnly | Whether the order is reduce-only, default false |
| postOnly | Whether the order is maker-only (post-only), default false |
| extra | Exchange-specific parameters, passed through to the exchange as is |
| clientId | Client order ID, passed through to the exchange as is, up to 64 bytes. When placing orders via WebSocket without specifying one, it is generated automatically in the format required by the exchange |
| via | Order channel: rest (default), ws (the exchange's WebSocket trading API) |
| timeout | Timeout in milliseconds for waiting for the exchange's response; the default value is used when omitted or set to 0 |
options accepts only the fields listed in the table above: if it contains an unknown field (for example, a misspelled reduceonly), no order is placed and an error is returned instead; the field is not ignored. Conditional orders and order modifications do not use this function; see exchange.CreateConditionOrder and exchange.ModifyOrder.
No exception is thrown on failure; all failures are reported through the return value: an Error object is returned for invalid parameters, exchange order rejection, rate limiting, or network failure, and a bogus order ID is never returned. Before using the return value, check it with instanceof Error.
When via is ws, the order is placed through the exchange's WebSocket trading API:
- Available only on exchanges that support WebSocket order placement, currently including Binance Spot and USDT-margined Futures, OKX Spot and Futures, Bybit Spot and Futures (not supported on demo trading), Gate.io Spot and Perpetual Futures, and Bitget Spot and Futures; refer to each exchange's page for details. Unsupported exchanges return an error and do not automatically fall back to placing the order via the REST API.
- Each account uses a dedicated trading connection, established on the first WebSocket order. Every WebSocket order carries a client order ID, which is generated automatically if
clientIdis not specified. - If the trading connection is unavailable before the request is sent (not connected or not logged in), the order is automatically placed via the REST API instead.
- If no response is received after the request is sent (timeout, connection dropped midway, or the exchange reports an unknown execution status), the order is queried once by its client order ID and treated as successful if found; if it still cannot be confirmed, an error whose
messagebegins withorder state unknown (clientId ...)is returned. In this case the order may already have been submitted to the exchange: do not place it again; instead query the order by its client order ID, or wait for confirmation from the order updates on the private stream. - The exchange's order rate limit is usually shared with the REST API; when the limit is exceeded, a rate-limit error is returned immediately, without queuing and without falling back to the REST API.
The call blocks the current thread until the exchange's response is received (one round trip). To send multiple orders at once, or to avoid blocking, use ctx.orderAsync. No subscription is required before placing orders; to receive order status changes and fill information, subscribe to the private channel with ctx.subscribe. For the fields of order updates (kind is 16) and fill updates (kind is 19), see ctx.poll.
In Python it is ctx.order(ex, symbol, side, px, qty, opts=None), where opts is a dict with the same fields; on success it returns the order ID string, and on failure it returns a CtxError object (no exception is thrown), which can be checked with isinstance(r, CtxError). In Rust it is ctx::order(ex, symbol: &str, side: &str, px: f64, qty: f64, opts_json: &str) -> Result<String, String>, with options passed as a JSON string (for example r#"{"via":"ws"}"#), or "{}" when there are no options; on failure it returns Err and does not panic.