Skip to content

Order Routing — Message Reference

All order routing messages share the same 56-byte common header used by market data. The consumer reads the header, dispatches on msg_type, and casts the remaining bytes to the appropriate payload struct.

The provided header library (sorcery/ord_types.h) contains all POD struct definitions and enums. For ring mechanics (poll, has_gap, reset), see Wire protocol — Client library.

Order routing uses two SHM rings per stack-scoped routing session:

  • Request ring (/sorcery-{stack}-ord-req) — client writes, orderd reads
  • Response ring (/sorcery-{stack}-ord-rsp) — orderd writes, client reads

Ring names are stack-scoped. A stack has one request stream and one response stream.

Both rings use the same SPMC ring protocol as market data. Each ring has a single producer.

For multi-strategy systems, introduce a fan-in execution gateway as the sole request-ring producer. Direct multi-writer access to the same request ring is out of contract.

Multi-strategy routing contract

The wire protocol has no strategy_id field. In multi-strategy deployments, routing is defined by external order_id ownership.

Required gateway behavior:

  1. Persist order_id -> strategy owner before publishing NEW.
  2. Route order-scoped responses by order_id ownership.
  3. Route order_id = 0 responses to reconciliation handling only.
  4. On unknown non-zero order_id, fail closed (pause venue submissions) and reconcile.

Consumer pattern

Pin one thread per ring pair. The response ring is the hot path — drain a batch of frames into process-local memory, then dispatch on msg_type. The request ring is the submission path — encode a request into the ring and flush.

#include <sorcery/ord_types.h>  // Header, NewOrder, Fill, OrderAck, ...
#include <sorcery/ring.h>       // Ring, Consumer, Producer, DrainBuffer
#include <sorcery/metadata.h>   // MetadataStore — instrument lookup + price conversion

// Open both rings
auto rsp_ring = sorcery::Ring::open("/sorcery-master-ord-rsp");
auto req_ring = sorcery::Ring::open("/sorcery-master-ord-req");
sorcery::Consumer consumer{rsp_ring};
sorcery::Producer producer{req_ring};
sorcery::DrainBuffer buf;

while (running) {
    // Drain response ring — same pattern as market data
    for (auto& frame : consumer.drain(buf, K)) {

        if (frame.is_gap()) {
            // Ring overflow — reconcile all order state via queries
            reconcile_all_orders();
            continue;
        }

        auto* hdr  = frame.header();
        auto* body = frame.body();

        switch (hdr->msg_type) {
            case sorcery::ord::ORDER_ACK:
                on_order_ack(*hdr, *reinterpret_cast<const sorcery::ord::OrderAckMsg*>(body));
                break;
            case sorcery::ord::FILL:
                on_fill(*hdr, *reinterpret_cast<const sorcery::ord::FillMsg*>(body));
                break;
            case sorcery::ord::ORDER_REJECT:
                on_order_reject(*hdr, *reinterpret_cast<const sorcery::ord::OrderRejectMsg*>(body));
                break;
            case sorcery::ord::MODIFY_ACK:
                on_modify_ack(*hdr, *reinterpret_cast<const sorcery::ord::ModifyAckMsg*>(body));
                break;
            case sorcery::ord::CANCEL_ACK:
                on_cancel_ack(*hdr, *reinterpret_cast<const sorcery::ord::CancelAckMsg*>(body));
                break;
            case sorcery::ord::BALANCES:
                on_balances(*hdr, sorcery::ord::BalancesView{body});
                break;
            // ... remaining types
        }
    }
}

Fixed-size payloads (OrderAck, OrderReject, ModifyAck, ModifyReject, CancelAck, CancelReject, CancelAllAck, Fill, OrderStatus, Status) can be cast directly. Variable-size payloads (Balances, Orders, Positions) are wrapped by view types that compute array offsets and return typed std::spans.

Submitting requests

// Submit a new limit order
sorcery::ord::NewOrder order{};
order.order_id  = next_order_id();
order.px        = 500000;           // price in ticks
order.qty       = 100;              // quantity in steps
order.side      = sorcery::ord::BUY;
order.tif       = sorcery::ord::GTC;
order.flags     = 0;

auto out = producer.get_buffer(sizeof(sorcery::Header) + sizeof(order));
encode_header(out, inst_id, sorcery::ord::NEW, venue);
std::memcpy(out.data() + sizeof(sorcery::Header), &order, sizeof(order));
producer.flush();

Price and quantity conversion

Same as market data. Prices and quantities on the wire are integers in tick/step units. Convert using instrument metadata (see Metadata):

sorcery::MetadataStore store("/sorcery-master-metadata");
auto* inst = store.find_instrument(hdr->inst_id);
double price = inst->to_price(fill.fill_px);  // ticks → real price
double qty   = inst->to_qty(fill.fill_qty);   // steps → real quantity

Enums

msg_type — request ring (u8)

Client → orderd. Valid on the request ring only.

Value Name Payload size
1 NEW 32 bytes (fixed)
2 CANCEL 8 bytes (fixed)
3 MODIFY 16 bytes (fixed)
4 CANCEL_ALL 0 bytes
5 QUERY_BALANCES 0 bytes
6 QUERY_ORDERS 0 bytes
7 QUERY_ORDER 8 bytes (fixed)
8 QUERY_POSITIONS 0 bytes

msg_type — response ring (u8)

orderd → client. Valid on the response ring only.

Value Name Payload size
1 ORDER_ACK 48 bytes (fixed)
2 ORDER_REJECT 16 bytes (fixed)
3 MODIFY_ACK 48 bytes (fixed)
4 MODIFY_REJECT 32 bytes (fixed)
5 CANCEL_ACK 24 bytes (fixed)
6 CANCEL_REJECT 24 bytes (fixed)
7 CANCEL_ALL_ACK 12 bytes (fixed)
8 FILL 80 bytes (fixed)
9 BALANCES variable
10 ORDERS variable
11 ORDER_STATUS 64 bytes (fixed)
12 STATUS 32 bytes (fixed)
13 POSITIONS variable

order_state (u8)

Value Name Terminal Meaning
0 (reserved) Invalid; zero-initialized memory. Consumers MUST treat as error.
1 PENDING_NEW no Sent to exchange, awaiting ack
2 LIVE no Resting on book, no in-flight mutation
3 PENDING_MODIFY no Price modify sent, awaiting response
4 PENDING_CXL no Cancel sent, awaiting response
5 FILLED yes Fully filled
6 REJECTED yes Never accepted by exchange
7 CANCELED yes Cancelled after acceptance

State tracks the mutation lifecycle, not fill progress. Whether an order has partial fills is determined by filled_qty > 0, not by the state value. See Integration guide — State machine for the full transition table.

side (u8)

Value Name
1 BUY
2 SELL

Numeric values match market data BID=1, ASK=2 intentionally. BUY = bidder, SELL = asker.

tif (u8)

Value Name Meaning
1 GTC Good-til-canceled
2 IOC Immediate-or-cancel
3 FOK Fill-or-kill

order_flags (u8, bitfield)

Used in the NEW request payload's flags field.

Bit Name Meaning
0 POST_ONLY Reject if would cross as taker
1 REDUCE_ONLY Only reduce existing position

reject_reason (u8)

Value Name Meaning Source Recoverable
0 UNKNOWN Unmapped exchange error exchange no
1 INTERNAL orderd internal error local no
2 EXCHANGE Generic exchange rejection exchange no
3 INSUFFICIENT_MARGIN Not enough margin/capital exchange yes
4 RATE_LIMITED Exchange rate limit hit exchange yes
5 INVALID_PRICE Price fails validation (tick size, bounds) both no
6 INVALID_QTY Quantity fails validation (step size, min/max) both no
7 INVALID_INSTRUMENT Instrument not active or not found local no
8 POST_ONLY Would cross as taker exchange yes
9 SELF_TRADE Self-trade prevention triggered exchange yes
10 REDUCE_ONLY Would increase position both yes
11 TOO_LATE Order already filled/cancelled exchange no
12 MARKET_CLOSED Market not open for trading exchange yes
13 DUPLICATE_ORDER order_id already in use local no
14 ORDER_NOT_FOUND order_id not found (cancel/modify) local no
15 MODIFY_IN_FLIGHT Another modify/cancel already pending local yes
16 QTY_MODIFY Qty modification not supported (cancel + new) local no
17 DISCONNECTED Not connected to exchange, or unresolved after reconnect reconciliation both yes

Source: local = rejected by orderd before reaching the exchange (response has LOCAL flag set). exchange = rejected by the venue after transmission. both = may originate from orderd local validation, the exchange, or reconciliation synthesis.

DISCONNECTED may appear as a synthetic reconciliation ORDER_REJECT with RECONNECT set and LOCAL unset.

Recoverable means the same request may succeed if retried after the condition resolves. Non-recoverable means the request is fundamentally invalid.

response flags (u16, bitfield)

Used in the response ring header's flags field.

Bit Name Meaning
0 LOCAL Rejected locally by orderd; never sent to exchange
1 RECONNECT Epoch changed; consumer MUST reconcile

Common header

Both rings reuse the same 56-byte common header as market data. The header struct is identical — only the semantic interpretation of certain fields differs by ring direction.

There is no strategy_id/app_id field in this header. Strategy-level routing must be handled outside the wire protocol (typically by order_id namespace ownership).

offset  size  type   field
────────────────────────────────────────────────────────────
0       8     u64    inst_id           instrument ID; 0 for non-instrument-scoped messages (STATUS, QUERY_BALANCES, QUERY_ORDERS scope=all, QUERY_POSITIONS scope=all, CANCEL_ALL scope=all)
8       8     u64    exch_ts           nanos since epoch; exchange event time (response ring); 0 on request ring
16      8     u64    rx_ts             nanos since epoch; orderd receive time (response ring); 0 on request ring
24      8     u64    pub_ts            nanos since epoch; ring publish time (both rings)
32      8     u64    seq               monotone within ring (see Sequencing)
40      4     u32    epoch             per-venue session counter (response ring); 0 on request ring
44      2     u16    schema_ver        wire format version
46      1     u8     msg_type          see per-ring enum above
47      1     u8     venue             venue ID from metadata region
48      2     u16    flags             response flags (response ring); reserved 0 (request ring)
50      2     u16    payload_len       byte length of payload after this header
52      4     u32    _reserved
────────────────────────────────────────────────────────────
                     56 bytes

payload_len is the byte length of the message-specific body that follows the header. The total frame size (as seen in ring framing) is 56 + payload_len.

Request ring field usage

Field Usage
inst_id Instrument for this request; 0 for venue-scoped queries (QUERY_BALANCES, QUERY_ORDERS scope=all, QUERY_POSITIONS scope=all) and CANCEL_ALL scope=all
exch_ts 0 (not applicable)
rx_ts 0 (not applicable)
pub_ts Client's publish timestamp
seq Client request sequence; monotone
epoch 0
msg_type Request type enum (1–8)
venue Target venue
flags Reserved; always 0. Order-specific flags (POST_ONLY, REDUCE_ONLY) are in the NEW payload's flags field.

Response ring field usage

Field Usage
inst_id Instrument for this message; 0 for STATUS and venue-scoped query responses (BALANCES, ORDERS/POSITIONS when the request used inst_id = 0)
exch_ts Exchange timestamp of the event; 0 if venue does not provide
rx_ts When orderd received the exchange message
pub_ts When orderd published to the response ring
seq Response sequence; monotone across all msg_types and instruments
epoch Per-venue session counter; increments on reconnect
msg_type Response type enum (1–13)
venue Source venue
flags Response flags bitfield

Epoch changes require reconciliation

When epoch changes for a venue, all non-terminal orders for that venue are uncertain. See Integration guide — Sequencing for the recovery procedure.


Request messages (client → orderd)

NEW

Submit a new order. order_id MUST be unique across the request stream. inst_id and venue are set in the header.

Payload: 32 bytes. Total frame: 88 bytes.

offset  size  type   field
────────────────────────────────────────
0       8     u64    order_id      client-assigned unique ID
8       8     i64    px            price (ticks)
16      8     i64    qty           quantity (steps)
24      1     u8     side          BUY=1, SELL=2
25      1     u8     tif           GTC=1, IOC=2, FOK=3
26      1     u8     flags         order_flags bitfield (POST_ONLY, REDUCE_ONLY)
27      5            _reserved

orderd validates the request locally before sending to the exchange:

  • order_id must not already exist → DUPLICATE_ORDER
  • inst_id must be ACTIVE in metadata → INVALID_INSTRUMENT
  • px must be a valid multiple of the instrument's price tick → INVALID_PRICE
  • qty must be a valid multiple of the instrument's qty step → INVALID_QTY
  • venue must be connected → DISCONNECTED
  • Flags must be supported by the target venue → REDUCE_ONLY (if venue doesn't support and orderd cannot emulate)

On validation failure, orderd publishes an ORDER_REJECT with the LOCAL flag set in the response header. The request is never sent to the exchange.


CANCEL

Cancel a specific order by order_id.

Payload: 8 bytes. Total frame: 64 bytes.

offset  size  type   field
────────────────────────────────────────
0       8     u64    order_id      which order to cancel

orderd validates locally:

  • order_id must exist and be in a non-terminal state → ORDER_NOT_FOUND
  • Order must not be in PENDING_MODIFY or PENDING_CXL → MODIFY_IN_FLIGHT

Cancel during PENDING_NEW is allowed — orderd transitions the order to PENDING_CXL and sends the cancel to the exchange. See Integration guide — Mutations during PENDING_NEW.


MODIFY

Modify the price of an order. Allowed in LIVE or PENDING_NEW states. Quantity changes are not supported — use cancel + new.

Payload: 16 bytes. Total frame: 72 bytes.

offset  size  type   field
────────────────────────────────────────
0       8     u64    order_id      which order to modify
8       8     i64    new_px        new price (ticks)

orderd validates locally:

  • order_id must exist and be in LIVE or PENDING_NEW → ORDER_NOT_FOUND
  • Order must not be in PENDING_MODIFY or PENDING_CXL → MODIFY_IN_FLIGHT
  • new_px must be a valid tick multiple → INVALID_PRICE
  • Quantity change attempted → QTY_MODIFY

orderd transitions the order to PENDING_MODIFY and sends the amend to the exchange. During PENDING_NEW, the exchange will process the amend after the preceding NEW on the same connection. See Integration guide — Mutations during PENDING_NEW.


CANCEL_ALL

Cancel all open orders. Scope determined by the header:

  • inst_id = 0 → cancel all orders on this venue
  • inst_id = X → cancel all orders for instrument X on this venue

CANCEL_ALL targets a single venue, specified by venue in the header. To cancel across all venues, send one CANCEL_ALL per venue.

Payload: 0 bytes. Total frame: 56 bytes.

orderd iterates all matching open orders, transitions each to PENDING_CXL, and sends individual cancels. The client receives:

  1. Individual CANCEL_ACK for each successfully cancelled order
  2. Individual CANCEL_REJECT for any that fail (e.g., already filled)
  3. A final CANCEL_ALL_ACK summary when all cancel responses have been received (or after the 5-second timeout)

Fills may interleave with the individual CANCEL_ACK stream — a fill arriving between individual cancel responses is normal.


QUERY_BALANCES

Request current balances for the venue specified in the header. inst_id is 0.

Payload: 0 bytes. Total frame: 56 bytes.

orderd queries the exchange and publishes a BALANCES response.


QUERY_ORDERS

Request open orders. Scope determined by the header:

  • inst_id = 0 → all open orders on this venue
  • inst_id = X → open orders for instrument X

Payload: 0 bytes. Total frame: 56 bytes.

orderd queries the exchange and publishes an ORDERS response.


QUERY_ORDER

Check the status of a specific order.

Payload: 8 bytes. Total frame: 64 bytes.

offset  size  type   field
────────────────────────────────────────
0       8     u64    order_id      which order to check

orderd publishes an ORDER_STATUS response with the order's current state.


QUERY_POSITIONS

Request open positions for the venue specified in the header. inst_id is 0 for all positions, or a specific instrument ID for a single position.

Payload: 0 bytes. Total frame: 56 bytes.

orderd queries the exchange and publishes a POSITIONS response. Only applicable to derivatives venues (perp/futures). On spot-only venues, orderd publishes an empty POSITIONS response.


Query correlation and completion contract

Request headers do not carry a query correlation ID. Clients MUST use the following outstanding-request discipline.

Outstanding limits:

  • QUERY_BALANCES: at most one outstanding request per venue.
  • QUERY_ORDERS: at most one outstanding request per (venue, inst_id scope).
  • QUERY_POSITIONS: at most one outstanding request per (venue, inst_id scope).
  • QUERY_ORDER: may be outstanding concurrently for different order_id values.

If a client sends overlapping requests outside this contract, response correlation is undefined.

Completion semantics:

  • QUERY_BALANCES: one BALANCES frame completes the request.
  • QUERY_ORDERS: one ORDERS frame completes the request.
  • QUERY_POSITIONS: one POSITIONS frame completes the request (spot-only venues return n_positions = 0).
  • QUERY_ORDER: one ORDER_STATUS frame with matching order_id completes the request.

Timeout semantics:

  • If no completion frame arrives before client timeout, request outcome is unknown.
  • Client MUST treat the outstanding slot as unresolved until it retries and receives completion.

Use these client timeout defaults unless venue-specific behavior requires overrides:

  • QUERY_ORDER: 500ms
  • QUERY_BALANCES: 1500ms
  • QUERY_ORDERS: 1500ms
  • QUERY_POSITIONS: 1500ms

During reconnect/reconciliation flows, use a wider timeout budget:

  • all query types: 3000ms

Retry policy after timeout:

  • retry with bounded backoff (25ms initial, x2, cap 200ms)
  • keep one outstanding request per constrained scope until completion

Response messages (orderd → client)

ORDER_ACK

New order accepted by the exchange. Order transitions to LIVE — unless a modify or cancel was sent during PENDING_NEW, in which case order_state reflects the in-flight mutation (PENDING_MODIFY or PENDING_CXL).

Payload: 48 bytes. Total frame: 104 bytes.

offset  size  type   field
────────────────────────────────────────
0       8     u64    order_id       client order ID (echo)
8       8     u64    exch_oid       exchange order ID (xxHash64 if string)
16      8     i64    px             confirmed price (ticks)
24      8     i64    qty            confirmed qty (steps)
32      8     i64    filled_qty     cumulative filled (steps); 0 for new order
40      1     u8     order_state    LIVE (2), PENDING_MODIFY (3), or PENDING_CXL (4)
41      1     u8     side           BUY/SELL (echo)
42      6            _pad

ORDER_REJECT

New order rejected. Order transitions to REJECTED (terminal).

Payload: 16 bytes. Total frame: 72 bytes.

offset  size  type   field
────────────────────────────────────────
0       8     u64    order_id       client order ID (echo)
8       1     u8     reject_reason  see reject_reason enum
9       1     u8     order_state    REJECTED (6)
10      6            _pad

When the LOCAL flag is set in the response header, the order was rejected by orderd's local validation and never sent to the exchange.


MODIFY_ACK

Price modification accepted. Order transitions from PENDING_MODIFY to LIVE with the new price confirmed.

Payload: 48 bytes. Total frame: 104 bytes.

offset  size  type   field
────────────────────────────────────────
0       8     u64    order_id       client order ID
8       8     u64    exch_oid       exchange order ID (may change on some venues)
16      8     i64    px             new confirmed price (ticks)
24      8     i64    qty            order qty (steps) — unchanged
32      8     i64    filled_qty     cumulative filled (steps)
40      1     u8     order_state    LIVE (2)
41      7            _pad

On some venues, exch_oid may differ from the original after a modify. orderd handles the mapping internally — the client's order_id is unchanged.


MODIFY_REJECT

Price modification rejected. Order transitions from PENDING_MODIFY to LIVE with original price unchanged.

Payload: 32 bytes. Total frame: 88 bytes.

offset  size  type   field
────────────────────────────────────────
0       8     u64    order_id       client order ID
8       8     i64    px             confirmed price (unchanged original)
16      8     i64    filled_qty     cumulative filled (steps)
24      1     u8     order_state    LIVE (2)
25      1     u8     reject_reason  see reject_reason enum
26      6            _pad

CANCEL_ACK

Cancel confirmed. Order transitions to CANCELED (terminal).

Payload: 24 bytes. Total frame: 80 bytes.

offset  size  type   field
────────────────────────────────────────
0       8     u64    order_id       client order ID
8       8     i64    filled_qty     cumulative filled at cancellation (steps)
16      1     u8     order_state    CANCELED (7)
17      7            _pad

CANCEL_REJECT

Cancel rejected. Order transitions from PENDING_CXL to LIVE — the cancel failed and the order is still resting.

Payload: 24 bytes. Total frame: 80 bytes.

offset  size  type   field
────────────────────────────────────────
0       8     u64    order_id       client order ID
8       8     i64    filled_qty     cumulative filled (steps)
16      1     u8     order_state    LIVE (2)
17      1     u8     reject_reason  see reject_reason enum
18      6            _pad

Not emitted when the NEW itself is rejected during PENDING_NEW. That path emits ORDER_REJECT.


CANCEL_ALL_ACK

Completion signal for a CANCEL_ALL request. Published after all individual CANCEL_ACK / CANCEL_REJECT messages have been sent. orderd applies a timeout (default: 5 seconds) — if individual cancels have not all resolved, CANCEL_ALL_ACK is published with the current counts and timed-out orders in failed_count.

Payload: 12 bytes. Total frame: 68 bytes.

offset  size  type   field
────────────────────────────────────────
0       4     u32    total_count      total orders targeted
4       4     u32    cancelled_count  orders successfully cancelled
8       4     u32    failed_count     orders that failed (filled, rejected, timed out)

total_count = cancelled_count + failed_count.


FILL

A fill (execution) event. Updates the order's filled_qty. If filled_qty == order_qty, order transitions to FILLED (terminal). order_state is the post-fill state.

Payload: 80 bytes. Total frame: 136 bytes.

offset  size  type   field
────────────────────────────────────────
0       8     u64    order_id       client order ID
8       8     u64    exch_oid       exchange order ID
16      8     u64    fill_id        exchange fill ID (xxHash64 if string)
24      8     i64    fill_px        fill price (ticks) — the execution price
32      8     i64    fill_qty       this fill quantity (steps)
40      8     i64    filled_qty     cumulative filled after this fill (steps)
48      8     i64    order_qty      total order qty (steps)
56      8     i64    fee            fee amount (fee asset decimal units; negative = rebate)
64      8     u64    fee_asset_id   fee currency asset_id
72      1     u8     order_state    PENDING_NEW (1), LIVE (2), PENDING_MODIFY (3), PENDING_CXL (4), or FILLED (5)
73      1     u8     side           BUY/SELL
74      1     u8     is_maker       1 = maker, 0 = taker
75      5            _pad

Fee encoding. fee is encoded as an integer in the fee asset's smallest decimal unit, using the decimals field from the asset metadata. Example: if the fee is 0.001 BTC and BTC has decimals = 8, then fee = 100000. Negative values indicate a rebate (e.g., maker rebate on some venues).

fee_asset_id MUST reference a valid asset in the metadata region — orderd ensures the fee asset is registered before publishing the fill. Fees are best-effort at fill time; final fee reconciliation (e.g., VIP rebates applied at settlement) may require out-of-band data.

Fills are the source of truth for fees

The instrument struct fee fields (make_fee_bps, take_fee_bps) in metadata are best-effort estimates. For any strategy where fee accuracy matters, read fees from fill messages and maintain a local cache. The is_maker field tells you which side of the fee schedule applies.

Deduplication. orderd guarantees no duplicate fill_id values on the response ring within a given epoch. Clients do not need to implement fill dedup. The fill_id field remains useful for correlating with exchange records and trade history.

order_id = 0 is reserved for reconciliation orphans (exchange-side orders unknown to the client/gateway mapping). Treat these fills as reconciliation records, not normal strategy callbacks.


BALANCES

Response to QUERY_BALANCES. Contains all balances for the queried venue.

Payload: variable. Minimum 4 bytes.

offset  size  type                    field
────────────────────────────────────────
0       2     u16                     n_balances
2       2     u16                     _pad
4       ...   BalanceEntry[n_balances]

Payload size: 4 + n_balances × 24 bytes.

BalanceEntry (24 bytes)

offset  size  type   field
────────────────────────────────────────
0       8     u64    asset_id       asset identifier
8       8     i64    available      free balance (asset decimal units)
16      8     i64    locked         in-use balance (asset decimal units)

Total balance = available + locked. Both values use the asset's decimals field from metadata for conversion to real values.


ORDERS

Response to QUERY_ORDERS. Contains all open orders matching the query scope.

Payload: variable. Minimum 4 bytes.

offset  size  type                   field
────────────────────────────────────────
0       2     u16                    n_orders
2       2     u16                    _pad
4       ...   OrderEntry[n_orders]

Payload size: 4 + n_orders × 64 bytes.

OrderEntry (64 bytes)

offset  size  type   field
────────────────────────────────────────
0       8     u64    order_id       client order ID
8       8     u64    exch_oid       exchange order ID
16      8     u64    inst_id        instrument ID
24      8     i64    px             current confirmed price (ticks)
32      8     i64    qty            order qty (steps)
40      8     i64    filled_qty     cumulative filled (steps)
48      1     u8     order_state    current state
49      1     u8     side           BUY/SELL
50      1     u8     tif            GTC/IOC/FOK
51      1     u8     flags          order_flags
52      4            _pad
56      8     u64    create_ts      order creation timestamp (nanos since epoch)

Each OrderEntry includes inst_id because the response may contain orders across multiple instruments (when QUERY_ORDERS uses inst_id = 0 in the header).


ORDER_STATUS

Response to QUERY_ORDER. Single order's full state. Same layout as OrderEntry.

Payload: 64 bytes. Total frame: 120 bytes.

order_id = 0 indicates an order discovered during reconciliation that has no client-owned request identity. Route to reconciliation handling only.


POSITIONS

Response to QUERY_POSITIONS. Contains open positions for the queried venue.

Payload: variable. Minimum 4 bytes.

offset  size  type                       field
────────────────────────────────────────
0       2     u16                        n_positions
2       2     u16                        _pad
4       ...   PositionEntry[n_positions]

Payload size: 4 + n_positions × 56 bytes.

PositionEntry (56 bytes)

offset  size  type   field
────────────────────────────────────────
0       8     u64    inst_id           instrument ID
8       8     i64    qty               signed position size (steps); positive=long, negative=short
16      8     i64    entry_px          average entry price (ticks)
24      8     i64    unrealized_pnl    unrealized PnL (quote asset decimal units)
32      8     i64    margin            allocated margin (quote asset decimal units)
40      8     i64    liquidation_px    estimated liquidation price (ticks); 0 if unavailable
48      4     u32    leverage          leverage multiplier (e.g., 10 = 10x)
52      4            _pad

Position sign convention: qty > 0 = long, qty < 0 = short. Convert to real values using the instrument's step size from metadata.


STATUS

Connection health and heartbeat for orderd. Emitted periodically (every 1 second). inst_id is 0 in the header — this message is per-venue, not per-instrument.

Payload: 32 bytes. Total frame: 88 bytes.

offset  size  type   field
────────────────────────────────────────
0       8     u64    last_rx_age_ns    nanos since last exchange message
8       4     u32    reconnect_count
12      4     u32    open_orders       number of tracked open orders
16      4     u32    pending_requests  requests currently in flight to exchange
20      1     u8     conn_state        1=CONNECTED, 2=DISCONNECTED, 3=RECONNECTING
21      3            _pad
24      8     u64    _reserved

If last_rx_age_ns exceeds 5 seconds, the consumer MUST treat the connection as stale and stop submitting new requests on that venue until recovery.

Different from market data STATUS

The order routing STATUS payload tracks open orders and pending requests instead of market data's gap/drop counters and active instruments. Both share the same conn_state enum values.


Venue support matrix

Not every venue supports every feature.

Feature Binance Bybit Coinbase Hyperliquid
NEW yes yes yes yes
CANCEL yes yes yes yes
MODIFY (price) yes yes yes yes
CANCEL_ALL yes yes individual yes
POST_ONLY yes yes yes yes
REDUCE_ONLY yes yes yes
IOC yes yes yes yes
FOK yes yes yes yes
QUERY_BALANCES yes yes yes yes
QUERY_ORDERS yes yes yes yes
QUERY_POSITIONS yes yes empty yes

Venue fallback and reject contract

Scenario Behavior
Coinbase CANCEL_ALL No native endpoint. orderd fans out individual cancels; emits per-order CANCEL_ACK/CANCEL_REJECT, then CANCEL_ALL_ACK.
Coinbase REDUCE_ONLY on NEW Local reject: ORDER_REJECT with reject_reason = REDUCE_ONLY and LOCAL flag set.
Coinbase QUERY_POSITIONS Supported as empty response: POSITIONS with n_positions = 0; no reject frame.
Any venue CANCEL_ALL timeout path CANCEL_ALL_ACK.failed_count includes unresolved or timed-out cancels.