Orders
Order types, time-in-force policies, and execution flags
This guide covers OBSDN order types, execution behavior, time-in-force policies, and order flags.
The JSON examples below show the wire fields used by POST /orders (PlaceOrderRequest). Required fields like mkt_id, nonce, and sig are omitted from each example for brevity, see Getting Started for the full payload.
Two wire-format rules apply throughout:
- Enums use the full proto name. Send
ORDER_SIDE_BUY, notBUY; short forms are rejected. uint64/int64fields are JSON strings.nonce,exp_ts, andsched_tsexceed JavaScript's safe integer range, so they're quoted on the wire.
Wire Field Reference
| Field | Type | Description |
|---|---|---|
mkt_id | string | Market identifier (e.g., BTC-PERP) |
sd | enum | Side: ORDER_SIDE_BUY, ORDER_SIDE_SELL |
ot | enum | Order type: ORDER_TYPE_LIMIT, ORDER_TYPE_MARKET, ORDER_TYPE_STOP, ORDER_TYPE_TWAP |
sz | string | Decimal size in base asset units |
px | string | Decimal limit price in quote asset units |
tif | enum | Time-in-force: TIME_IN_FORCE_GTC, TIME_IN_FORCE_IOC, TIME_IN_FORCE_FOK, TIME_IN_FORCE_GTT |
po | bool | Post-only |
ro | bool | Reduce-only |
stp | enum | Self-trade prevention: SELF_TRADE_PREVENTION_CANCEL_TAKER, SELF_TRADE_PREVENTION_CANCEL_MAKER, SELF_TRADE_PREVENTION_CANCEL_BOTH |
cl_oid | string | Client-assigned order id (max 32 chars; alphanumeric + -_:) |
nonce | string (uint64) | Unix nanoseconds; included in the EIP-712 payload. Must be within ±5 minutes of server time; otherwise the request returns 400 nonce too far from current timestamp. |
sig | string | EIP-712 signature from the registered signer |
stop_t | enum | Stop subtype: STOP_TYPE_STOP_LOSS, STOP_TYPE_TAKE_PROFIT (STOP only) |
stop_px | string | Decimal trigger price (STOP only) |
stop_px_type | enum | Trigger source: STOP_PRICE_TYPE_LAST, STOP_PRICE_TYPE_MARK (STOP only) |
exp_ts | string (uint64) | Expiration timestamp in nanoseconds (GTT only) |
sched_ts | string (int64) | Scheduled execution timestamp in nanoseconds (TWAP sub-orders only; ignored for other types) |
await | bool | Wait for matching engine confirmation (MARKET/LIMIT only) |
Defaults when fields are omitted
| Field | Default | Notes |
|---|---|---|
ot | ORDER_TYPE_LIMIT | |
tif | TIME_IN_FORCE_GTC | |
stp | SELF_TRADE_PREVENTION_CANCEL_TAKER | Omitting stp does not disable self-trade prevention |
stop_px_type | STOP_PRICE_TYPE_LAST |
Order Types
LIMIT
A limit order rests on the order book at a specified price until filled or canceled.
{
"ot": "ORDER_TYPE_LIMIT",
"sd": "ORDER_SIDE_BUY",
"px": "50000",
"sz": "0.1",
"tif": "TIME_IN_FORCE_GTC"
}- Execution: only fills at the specified price or better
- Book: rests on the order book if not immediately filled
- Use case: when you want to control execution price
MARKET
A market order executes immediately at the best available price.
{
"ot": "ORDER_TYPE_MARKET",
"sd": "ORDER_SIDE_BUY",
"px": "50000",
"sz": "0.1",
"tif": "TIME_IN_FORCE_IOC"
}- Execution: fills immediately against resting orders
- Book: never rests on the order book
px: required; acts as a slippage cap: a buy will not match makers priced above it, a sell will not match makers priced below ittif: must beTIME_IN_FORCE_FOKorTIME_IN_FORCE_IOC;GTCandGTTare not permittedpo: not permitted- Use case: when speed of execution matters more than price
STOP
A stop order activates when the trigger price is reached.
{
"ot": "ORDER_TYPE_STOP",
"sd": "ORDER_SIDE_SELL",
"px": "49000",
"sz": "0.1",
"stop_px": "49500",
"stop_t": "STOP_TYPE_STOP_LOSS",
"stop_px_type": "STOP_PRICE_TYPE_MARK"
}| Field | Description |
|---|---|
stop_px | Price that activates the order |
stop_t | STOP_TYPE_STOP_LOSS or STOP_TYPE_TAKE_PROFIT |
stop_px_type | STOP_PRICE_TYPE_LAST or STOP_PRICE_TYPE_MARK |
Stop Subtypes
| Value | Trigger Condition |
|---|---|
STOP_TYPE_STOP_LOSS | Triggers when price moves against your position |
STOP_TYPE_TAKE_PROFIT | Triggers when price moves in your favor |
Trigger Sources
| Value | Description |
|---|---|
STOP_PRICE_TYPE_LAST | Triggered by the last traded price |
STOP_PRICE_TYPE_MARK | Triggered by mark price |
When stop_px_type is omitted (or STOP_PRICE_TYPE_UNSPECIFIED), last price is used.
TWAP (Algorithmic)
TWAP time-slices a parent order across scheduled sub-orders. Submit TWAP batches via POST /orders/twap: mkt_id, sd, stp, and ro come from the batch envelope; each sub-order in sub_ords is individually signed and carries its own px, sz, nonce, sig, and sched_ts.
- Execution: splits execution over time instead of taking full size at once
- Use case: large orders where minimizing market impact matters more than immediate completion
{
"mkt_id": "BTC-PERP",
"sd": "ORDER_SIDE_BUY",
"stp": "SELF_TRADE_PREVENTION_CANCEL_TAKER",
"ro": false,
"sub_ords": [
{
"px": "50000",
"sz": "0.1",
"nonce": "1700000000000000000",
"sig": "0x...",
"sched_ts": "1700000060000000000"
},
{
"px": "50000",
"sz": "0.1",
"nonce": "1700000000000000001",
"sig": "0x...",
"sched_ts": "1700000120000000000"
}
]
}Batch validation rules (any violation returns 400):
- All sub-orders must share the same
pxandsz(uniform slicing). noncevalues must be strictly increasing across the batch.sched_tsintervals between consecutive sub-orders must be positive, equal, and divisible by 60 seconds.- The first
sched_tsmust be at least 10 seconds in the future. - At most 100 sub-orders per batch.
- At most 200 untriggered conditional orders per market (shared with STOP orders); exceeding this returns 400.
- Aggregate margin for the batch is checked up front; if the account cannot cover it, the entire batch is rejected.
- If the account is currently in liquidation, the batch is rejected.
- Reduce-only batches are validated against the current position side; wrong-side batches are rejected.
- An empty
sub_ordsarray returns 400sub orders cannot be empty.
Time-in-Force (TIF)
Time-in-force determines how long an order remains active.
GTC (Good Til Canceled)
Order remains active until fully filled or manually canceled.
{ "tif": "TIME_IN_FORCE_GTC" }- Default behavior for most orders
- Order stays on the book indefinitely
IOC (Immediate or Cancel)
Fills as much as possible immediately, cancels the rest.
{ "tif": "TIME_IN_FORCE_IOC" }- Partial fills allowed
- Unfilled portion is immediately canceled
- Use case: fill what you can now, don't leave orders on book
FOK (Fill or Kill)
Must fill entirely or not at all.
{ "tif": "TIME_IN_FORCE_FOK" }- No partial fills
- Use case: all-or-nothing execution required
GTT (Good Til Time)
Order expires at a specified time.
{
"tif": "TIME_IN_FORCE_GTT",
"exp_ts": "1735689600000000000"
}exp_tsis Unix nanoseconds, sent as a JSON string- Must be in the future and at most 30 days from now (otherwise the request is rejected)
- Order is canceled automatically at the expiration time
Timing Controls
sched_ts: scheduled activation time in nanoseconds (TWAP sub-orders only). Ignored for other order types.exp_ts: expiration time in nanoseconds (GTT only)await: synchronous processing flag for MARKET/LIMIT submissions; the request waits for matching-engine processing before returning
Example (GTT limit order with synchronous submit):
{
"ot": "ORDER_TYPE_LIMIT",
"sd": "ORDER_SIDE_BUY",
"px": "50000",
"sz": "0.1",
"tif": "TIME_IN_FORCE_GTT",
"exp_ts": "1735689600000000000",
"await": true
}Special Flags
ro (reduce-only)
Only reduces an existing position, never increases it.
{ "ro": true }- Order canceled (returned as
ORDER_STATUS_DONEwith the correspondingdone_rsn) if it would open or increase a position - Use case: closing positions without risk of accidental reversal
po (post-only)
Order must rest on the book (be a maker), never take liquidity.
{ "po": true }- Order canceled (returned as
ORDER_STATUS_DONEwithdone_rsncanceled: post-only) if it would immediately match - Guarantees maker fee
- Use case: market making, fee optimization
stp (Self-Trade Prevention)
Prevents your orders from matching against each other.
{ "stp": "SELF_TRADE_PREVENTION_CANCEL_TAKER" }| Value | Behavior |
|---|---|
SELF_TRADE_PREVENTION_CANCEL_TAKER | Cancel the incoming (taker) order |
SELF_TRADE_PREVENTION_CANCEL_MAKER | Cancel the resting (maker) order |
SELF_TRADE_PREVENTION_CANCEL_BOTH | Cancel both orders |
When stp is omitted or sent as SELF_TRADE_PREVENTION_UNSPECIFIED, the engine applies SELF_TRADE_PREVENTION_CANCEL_TAKER, STP cannot be disabled.
Use case: prevent self-matches when running multiple bots or accounts.
Order Status Lifecycle
LIMIT / MARKET
PENDING -----------> OPEN -----------> DONE
| ^
+------------------------------------+
STOP / TWAP
UNTRIGGERED ------> OPEN -----------> DONE
| | ^
| +----------------+
+------------------------------------+MARKET orders always transition directly from PENDING to DONE (they never rest on the book as OPEN). STOP and TWAP orders begin in UNTRIGGERED and move to OPEN only when triggered or scheduled; they can also go directly to DONE if canceled while untriggered.
Partial fills are not a separate status: filled_sz updates while the order remains OPEN. The order moves to DONE when fully filled or canceled.
| Status | Description |
|---|---|
ORDER_STATUS_PENDING | LIMIT/MARKET order submitted, awaiting matching engine processing |
ORDER_STATUS_OPEN | Active on the order book |
ORDER_STATUS_UNTRIGGERED | Conditional order waiting for trigger (STOP) or scheduled time (TWAP) |
ORDER_STATUS_DONE | Terminal state: fully filled or canceled |
Done Reasons
When an order reaches ORDER_STATUS_DONE, the done_rsn field carries the terminal reason:
done_rsn | Cause |
|---|---|
filled | Order fully executed |
canceled: user | Canceled by the user |
canceled: admin | Canceled by an admin |
canceled: post-only | Post-only order would have crossed the book |
canceled: insufficient liquidity | No matching orders on the opposite side |
canceled: insufficient margin | Portfolio under-margined after matching |
canceled: time in force IOC | Unfilled remainder after IOC matching |
canceled: time in force FOK | Order not fully filled; entire order voided |
canceled: time in force GTT | Order expired at exp_ts |
canceled: stp | Self-trade prevention triggered |
canceled: too many matches | Exceeded 1,000 matches per order |
canceled: reduce-only position already closed | No open position at pre-match time |
canceled: reduce-only invalid side (...) | Order side matches position side |
canceled: reduce-only invalid size (...) | Order size exceeds position size |
canceled: reduce-only exceeds pos (...) | Reduce-only non-IOC order whose size exceeds the open position. IOC reduce-only orders that exceed the position are clipped to position size and continue matching instead of being canceled. |
canceled: reduce-only auto canceled due to position reduction | Resting reduce-only order excess after position reduced |
canceled: tpsl position closed | Parent position closed before stop order triggered |
canceled: parent order canceled unfilled | Parent order (bracket) canceled before fill |
canceled: liquidation | Canceled by liquidation engine |
canceled: adl counterparty has no entry price | ADL counterparty missing entry price |
canceled: adl counterparty unprofitable | ADL counterparty not profitable enough |
canceled: otc counter order reduce-only invalid side | OTC counter order side conflicts with position |
canceled: otc counter order reduce-only invalid size | OTC counter order size exceeds position |
canceled: otc counter party insufficient margin | OTC counterparty lacks margin for the trade |
Bracket Orders
Create a parent entry order with linked take-profit and/or stop-loss children:
POST /orders/group{
"grp_t": "ORDER_GROUP_TYPE_BRACKET",
"ords": [
{
"mkt_id": "BTC-PERP",
"ot": "ORDER_TYPE_LIMIT",
"sd": "ORDER_SIDE_BUY",
"px": "50000",
"sz": "0.1",
"nonce": "1700000000000000000",
"sig": "0x..."
},
{
"mkt_id": "BTC-PERP",
"ot": "ORDER_TYPE_STOP",
"sd": "ORDER_SIDE_SELL",
"px": "52500",
"stop_px": "52000",
"stop_t": "STOP_TYPE_TAKE_PROFIT",
"sz": "0.1",
"ro": true,
"tif": "TIME_IN_FORCE_IOC",
"nonce": "1700000000000000001",
"sig": "0x..."
},
{
"mkt_id": "BTC-PERP",
"ot": "ORDER_TYPE_STOP",
"sd": "ORDER_SIDE_SELL",
"px": "47500",
"stop_px": "48000",
"stop_t": "STOP_TYPE_STOP_LOSS",
"sz": "0.1",
"ro": true,
"tif": "TIME_IN_FORCE_IOC",
"nonce": "1700000000000000002",
"sig": "0x..."
}
],
"await": false
}When the parent fills, the children transition from ORDER_STATUS_UNTRIGGERED to ORDER_STATUS_OPEN. When one child triggers and fills, the other is automatically canceled.
Set await: true to block the response until the parent has been processed by the matching engine (only effective when the parent is ORDER_TYPE_MARKET or ORDER_TYPE_LIMIT).
Bracket validation rules
The backend rejects the request if any of these are violated:
| Rule | |
|---|---|
| Order count | exactly 2 or 3 (parent + 1–2 children) |
Parent ot | ORDER_TYPE_LIMIT, ORDER_TYPE_MARKET, or ORDER_TYPE_STOP |
Parent ro | must be false (or omitted) |
Children ot | must be ORDER_TYPE_STOP |
Children count by stop_t | at most one STOP_TYPE_TAKE_PROFIT and one STOP_TYPE_STOP_LOSS |
Children mkt_id | same as parent |
Children sz | same as parent |
Children sd | opposite of parent |
Children ro | must be true |
Children tif | must be TIME_IN_FORCE_IOC |
TP stop_px | for ORDER_SIDE_BUY parent: above parent px; for ORDER_SIDE_SELL parent: below parent px |
SL stop_px | for ORDER_SIDE_BUY parent: below parent px; for ORDER_SIDE_SELL parent: above parent px |
Order Sides
| Value | Description |
|---|---|
ORDER_SIDE_BUY | Long / buying the asset |
ORDER_SIDE_SELL | Short / selling the asset |
Examples
Limit Buy with Post-Only
{
"ot": "ORDER_TYPE_LIMIT",
"sd": "ORDER_SIDE_BUY",
"px": "50000",
"sz": "0.1",
"tif": "TIME_IN_FORCE_GTC",
"po": true
}Stop-Loss to Close Long Position
{
"ot": "ORDER_TYPE_STOP",
"sd": "ORDER_SIDE_SELL",
"px": "48000",
"sz": "0.1",
"stop_px": "48500",
"stop_t": "STOP_TYPE_STOP_LOSS",
"stop_px_type": "STOP_PRICE_TYPE_MARK",
"ro": true
}Market Order with IOC
{
"ot": "ORDER_TYPE_MARKET",
"sd": "ORDER_SIDE_BUY",
"px": "50000",
"sz": "0.1",
"tif": "TIME_IN_FORCE_IOC"
}Per-Market Order Limits
Each account is subject to the following order limits per market:
| Category | Limit |
|---|---|
| Pending orders | 200 |
| Open (resting) orders | 200 |
| Conditional (stop/TWAP) orders | 200 |
When a limit is reached, new orders for that market are rejected with maximum pending orders (200) reached for {market} (or the equivalent message for open/conditional orders). Cancel existing orders or wait for fills before placing new ones.