This file describes implemented order-entry mechanics and dated protocol observations, including non-idempotent client IDs and delayed history. It contains no trading strategy or account-specific examples.
TL;DR
- A
client_order_idcorrelates responses; it does not deduplicate a repeated placement. - Measure fills from sent quantity minus remaining, deduplicated trade events, and a fresh position delta. Event
quantitycan be one trade's size. FILLEDwith nonzero remaining can be followed by cancellation.- Cancel one exact exchange UUID using
cancel_order_id_type: 'order_id'. - Resting reduce-only quantities do not shrink with the position. Their behavior after the position disappears is not fully verified.
- At most 20 resting orders per symbol across both sides were accepted in observations on 2026-10-01.
1. Order frame and order types
An add_order frame has params containing symbol, side (BUY/SELL), order_type, order_time_in_force, numeric quantity and price, boolean reduce_only, and client_order_id. Optional take-profit and stop-loss fields are documented. Numeric strings and nonboolean reduce-only values were rejected on 2026-10-01.
| Documented type | Supported time in force | Behavior |
|---|---|---|
| LIMIT | GTC, IOC, FOK | Price-capped matching; GTC remainder rests |
| MARKET | IOC | Immediate matching without a resting remainder |
| ALO | GTC | Add liquidity only; crossing placement cancels |
| Stop | IOC after trigger | Mark-triggered, exposure reducing |
The toolkit's submission API documents its own supported subset. A protocol type listed here is not an assurance that its convenience builder is implemented or tested.
LIMIT GTC and IOC acknowledgements showed the approximately 500 ms speed bump on 2026-10-01. Passive GTC was delayed too. ALO and cancellation responses did not have the same bump. A crossing ALO was acknowledged then cancelled rather than filled. The timing distinction should not be used to infer an order's terminal outcome.
2. IDs, ownership tags, and response fields
Exchange order_id is a UUID string. Keep it unchanged. client_order_id was echoed exactly, including letter case, in events, open orders, fills, and history on 2026-10-01. A neutral ownership tag may encode caller-supplied magic, flag, sequence, and random bits into a 32-character lowercase hexadecimal client ID. It is a client convention; its magic and flags are application choices.
Repeated client IDs created duplicate resting orders on 2026-10-01. This contradicts the rate-limit page's general idempotency advice. Never retry a possibly sent placement just because it uses the same ID.
An order event includes order ID, symbol, status, side, type, time in force, client ID, quantity, price, remaining quantity, update time, and sometimes trade ID. Observed order and open-order rows also carried reduce_only and order_origin. Fill events carried order_origin but did not establish a reduce_only field. No general cumulative fill or average price exists on the order event.
After an execution, event quantity and price can describe that execution rather than the original order. Preserve the submitted quantity separately.
3. IoC event sequences and fill measurement
Observed sequences on 2026-10-01 were:
- Full execution: ACK, FILLED with zero remaining, then a fill event.
- No liquidity: ACK, CANCELLED with the original remaining quantity.
- Partial execution: ACK, FILLED with a remainder, then CANCELLED with that remainder.
Documented IOC_CANCELLED and IOC_PARTIALLY_FILLED variants were not observed. Decode them when present, but never assume they are required. Fill events can arrive before or after order events and can be delivered to another authenticated session of the same account.
Compute sentQuantity - minimumObservedRemaining for correlated fill-bearing order events, bounded by the submitted size. Deduplicate fills by trade ID. Compare evidence with a freshly read signed position change, accounting for direction. A discrepancy is unresolved evidence, not permission to report the largest value.
4. Cancellation and self-trade prevention
Send cancel_order with symbol, exact order_id, and cancel_order_id_type: 'order_id'. Cancelling by client ID can cancel every matching order on that symbol; it is not an exact single-order identity.
CANCELLED confirms terminal cancellation, but a fill may have happened before it. A clean no-fill cancellation requires consistent original and remaining quantities and no conflicting fill evidence. NO_SUCH_ORDER proves absence at the point of lookup, not that the order never filled. Observed unknown-ID responses had zeroed quantities and defaults; only ID and status were useful.
Self-trade prevention cancelled the resting order with CANCELLED_STP, while the incoming IoC continued matching on 2026-10-01. Account-wide cancel_all_orders and cancel_on_disconnect have broader scope than one order. The session rejects these broad commands unless the caller uses an explicitly supported separate workflow. Ordinary reconnects should not enable them.
Documented modification returns MODIFIED with a new exchange order ID. Its reduce-only field must agree with the existing order. Modify, TWAP, and stop-order orchestration are outside the toolkit's basic submit/cancel workflow.
5. Reduce-only, bands, and limits
At placement with no position, reduce-only GTC and ALO were rejected on 2026-10-01. An oversized resting reduce-only order could rest and retain its full displayed remainder after the position shrank or reached zero. A marketable reduce-only GTC and reduce-only IoC were capped to the remaining position during the tested matches. The match behavior of an already resting order when the position is zero or reversed remains unverified.
Do not assume the displayed resting quantity is bounded by current exposure. A fresh position read is required when interpreting reducible quantity or a full-close size. Below-minimum dust and cross-type minimum behavior are not established.
The observed cap is 20 resting orders per symbol, both sides combined. A rejection arrived before ACK. A per-side allowance of 20 would model the venue incorrectly.
For aggressive price bounds, consult markets-and-numbers.md §4. Passive out-of-band orders were accepted; aggressive out-of-band IoCs returned InvalidOrder errors.
6. Unknown-outcome resolution and pending memory
Timeout, disconnect after send, an uncorrelated error, or a server error can leave the outcome unknown. Preserve the original client ID and submitted body, lock the affected IoC symbol or resting placement key, and resolve before placing a replacement.
Evidence can come from complete get_user_orders pages, exact-ID terminal history, or a correlated event. Terminal history includes filled_qty, nullable avg_price, timestamps, and execution state; it can lag by tens of seconds. A history miss proves nothing, including for a request rejected before order creation.
Own-write echo memory bridges an acknowledged placement/cancellation and the next complete order read. Cancelled IDs should not reappear as live solely because a replica lags; unresolved placements should not produce duplicate submits. Expiry windows, lock windows, and resolution polling are explicit caller policies. Elapsed time alone is weaker than terminal evidence.
The optional fill ledger is experimental and opt-in. It can reject obviously lagging snapshots, but intermediate position values, expired history, and external account round trips can defeat freshness or liveness. It is not wired into account reads by default and must not be described as a proof that REST incorporated every execution.
7. Rejections and outcomes
Preserve exchange statuses and error codes. Known families include wire validation, tick/lot bounds, margin, position/open-interest limits, open-order capacity, permissions, liquidation, and rate limits. InvalidOrder is a broad error, not a unique band verdict.
An echoed validation or permission error before any ACK/fill can prove rejection. An error after execution evidence, ServerError, or an unfamiliar code needs unknown-outcome handling. Local budget refusal means nothing was sent; exchange rejection and unknown outcome remain separate.
Pitfalls
| What breaks | Why | Correct approach |
|---|---|---|
| Duplicate placements | Client ID is not idempotency | Resolve before a new intent |
| Filled size is understated or overstated | Event quantity is one trade | Keep sent size and compare remaining/fills/delta |
| Partial IoC marked final on FILLED | Remaining is nonzero | Await terminal evidence |
| Cancellation is counted as no-fill | NO_SUCH_ORDER or prior fills ignored | Reconcile fill evidence |
| Capacity set separately per side | Venue cap combines both sides | Count all resting orders for the symbol |
| Ledger claims guaranteed freshness | Known experimental holes | Explicit opt-in and limitation disclosure |
Open questions / not verified
- Resting reduce-only match behavior at zero/opposite position; reduce-only dust and additional minimums.
- Documented IOC terminal status variants, stop-order visibility in open-order pages, and live market-closed status.
- Live throttle envelopes and multi-level fill event ordering beyond limited observations.
- Optional account-binding probes are experimental and do not establish subaccount event scope.
Sources
Order types; Add order; Cancel order; Get user orders; Historic orders; Enums; QFEX trade schema. Dated exchange observations: 2026-10-01. Echo/lock/ledger semantics describe toolkit mechanisms rather than exchange guarantees.
© markpaper authors. Licensed under CC BY 4.0: when publishing or adapting this material, credit “markpaper — QFEX knowledge base” and link to the original and the license.