Wire protocol v2

TCP and Unix domain sockets carry the same frames. TLS changes the transport, not the frame layout. All integers are little-endian. Prices use signed i64 and quantities use unsigned u64, both scaled by 10^8. Timestamps and expiry times use Unix nanoseconds.

Frame header

OffsetSizeField
04Payload length, excluding the header (u32)
41Version: 2
51Message type
68Sequence (u64)
148Request ID (u64)
22variablePayload

Read exactly the 22-byte header, then the declared payload. One socket read may contain part of a frame or several frames. Regular inbound payloads are limited to 1024 bytes; batch payloads to 4096 bytes.

Client sequences increase strictly within a connection. Sequence zero is accepted only for its first Handshake. Commands and queries require a nonzero request ID; Ping, Handshake, ResumeSession and Acknowledge do not. Reuse the original request ID and exact payload when retrying a mutation.

Positive server sequences identify the per-session replay stream. Replies passed through the active-session response path, including HandshakeAck, Pong, ReplayGap from ResumeSession and some Error frames, are assigned server sequences and journaled when WAL is enabled. A gap detected during pending delivery instead emits ReplayGap directly with sequence zero, outside the replay journal. Replayed frames retain their original sequence. The server rewrites these headers; a Pong header is not the echoed client sequence (its payload reports the last processed client sequence).

Sequence zero is out-of-band and never advances the receive cursor. SessionResumed is sent with zero before the replay frames; errors sent outside the active-session replay path also use zero. Process every positive-sequence frame in order, including control frames, and advance the cursor only after applying it. Solicited order events mirror the request ID; passive owner events use zero.

Connection and recovery

Start with Handshake to create a session, or ResumeSession to recover one. Do not send order commands before acceptance. Persist the session ID and last applied positive server sequence outside the connection. A session can have only one active connection.

TypeNamePayload, in field order
0x04PingEmpty, or last applied server sequence (u64) as cumulative ACK
0x05HandshakeEmpty, or client version (u8, use 2)
0x06ResumeSessionSession ID (16 bytes), last received/applied server sequence (u64)
0x08AcknowledgeLast applied server sequence (u64)
0x18PongLast processed client sequence (u64): 8 bytes
0x19ErrorCode (u16), message length (u16), UTF-8 message
0x1AHandshakeAckServer version (u8), TLV extensions
0x1BSessionResumedReplayed frame count (u32)
0x20ReplayGapMissing sequence range: from (u64), to (u64)
0x23FlowControlWarningBuffer usage percent (u8), hard-limit flag (u8)

HandshakeAck currently has 41 payload bytes. Each TLV is tag (u16), length (u16), value. Skip unknown tags:

TagValue
1Capabilities (u64)
2Assigned session ID (16 bytes)
3Heartbeat interval in milliseconds (u32)

Capability bits 0–7 indicate session recovery, heartbeat, batch orders, order flags, order query, TLS, mutation deduplication and open-order snapshots respectively. Send heartbeats within the configured timeout.

SessionResumed precedes replay. On ReplayGap, obtain a complete GetOpenOrders snapshot; it restores open order state, not missed settlement events. Reconcile missing executions separately. Do not silently treat a fresh session as recovery of an old one. See client integration and deployment.

Order commands

PlaceOrder (0x01)

OffsetTypeField
0u64Nonzero order ID
8u16Symbol ID
10u8Side: buy 0, sell 1
11u8Order type
12u8Time in force
13i64Limit price; zero for market
21i64Stop price; zero for non-stop
29u64Positive total quantity
37u8Optional flags; absent means zero
38u64Optional GTD expiry; absent means zero
46i64Optional trailing distance; absent means zero
54u64Optional iceberg display quantity; absent means zero

Base payload is 37 bytes; optional fields extend it to 38, 46, 54 or 62 bytes. Include all preceding optional fields when using a later one. GTD requires a nonzero future expiry. Trailing distance must be nonnegative and a positive distance is valid only on stop orders. Positive display quantity is valid only on limit orders. Stop orders require a positive stop price; limit/stop-limit require a positive limit price.

Order types: Limit 0, Market 1, StopLimit 2, StopMarket 3. Time in force: GTC 0, IOC 1, FOK 2, GTD 3. Flags: POST_ONLY 0x01, REDUCE_ONLY 0x02, MAKER_OR_CANCEL 0x04. REDUCE_ONLY is a reserved futures flag; this spot engine does not enforce position reduction. Do not rely on it as an account risk control.

Other commands

TypeNamePayload
0x02CancelOrderOrder ID (u64), symbol ID (u16): 10 bytes
0x03AmendOrderOrder ID (u64), symbol ID (u16), has-price (u8), optional price (i64), has-qty (u8), optional qty (u64): 12–28 bytes
0x07GetOrderStatusOrder ID (u64), symbol ID (u16): 10 bytes
0x09GetOpenOrdersEmpty
0x0BBatchPlaceOrderCount (u8, 1–20), then fixed 38-byte PlaceOrder entries
0x0CBatchCancelOrderCount (u8, 1–50), then 10-byte CancelOrder entries

Amend presence flags are 0 or 1; include at least one changed field. Batch placement requires the flags byte on every entry and cannot carry GTD expiry, trailing distance or iceberg extensions. Batches execute individual items and are not atomic across symbols.

Cancel, amend and status requests are scoped to the requesting session. An order belonging to another session is reported as not found. Durable mutation retries return the original outcome; a retained request ID with a different payload is rejected. Query responses may be regenerated after bounded-cache eviction.

Order events

Fields below are serialized in the listed order, without alignment padding. timestamp is u64, price is i64, quantity is u64, and order/trade IDs are u64.

TypeNameBytesPayload
0x10OrderAccepted16Order ID, timestamp
0x11OrderRejected17Order ID, reason (u8), timestamp
0x12Trade59Trade ID, symbol (u16), price, qty, maker ID, taker ID, maker side (u8), timestamp, trade sequence (u64)
0x13OrderFilled32Order ID, filled qty, price, timestamp
0x14OrderPartiallyFilled40Order ID, filled qty, remaining qty, price, timestamp
0x15OrderCancelled24Order ID, remaining qty, timestamp
0x16OrderAmended32Order ID, new price, new qty, timestamp
0x17StopTriggered24Order ID, trigger price, timestamp
0x1DOrderStatus41Order ID, status (u8), filled qty, remaining qty, price, timestamp
0x1EOrderNotFound8Order ID

One command can emit several events. Continue consuming unsolicited owner events even when no local command is pending. The trade sequence inside Trade is distinct from the frame's server sequence.

Open-order snapshots

TypeNameBytesPayload
0x1FOpenOrdersBegin12Count (u32), snapshot sequence (u64)
0x21OpenOrder60Layout below
0x22OpenOrdersEnd8Same snapshot sequence (u64)

Wait for End with the matching request ID. Validate Begin/End sequence equality and the declared order count before replacing local state. A read timeout is not a valid empty snapshot.

OpenOrder offsets: ID 0 (u64), symbol 8 (u16), side 10, type 11, TIF 12, status 13 (u8 each), price 14 (i64), stop price 22 (i64), total qty 30 (u64), open qty 38 (u64), timestamp 46 (u64), flags 54 (u8), reserved zero bytes 55–59. For an iceberg, open qty includes the hidden remainder. This fixed layout does not include expiry, trailing distance or display size.

Order status values: New 0, PartiallyFilled 1, Filled 2, Cancelled 3, Rejected 4, Triggered 5.

Rejection and error codes

OrderRejected reasons (u8):

CodeReasonCodeReason
0NoLiquidity8SymbolNotFound
1InsufficientLiquidity9TradingHalted
2InvalidPrice10RiskCheckFailed
3InvalidQuantity11WouldTakeMarket
4InvalidLotSize12InternalError
5InvalidTickSize13SelfTradePrevented
6OrderNotFound14InvalidExpiry
7DuplicateOrderId15AmendNotSupported

Protocol Error codes (u16): 1 UnsupportedVersion, 2 SequenceOutOfOrder, 3 HandshakeRequired, 4 InvalidPayload, 5 ServerShuttingDown, 6 Backpressure, 7 HeartbeatTimeout, 8 TlsError, 9 InvalidRequestId, 10 SessionExpired, 11 BufferExhausted, 12 InternalError. Some codes are reserved; their assignment remains part of the wire contract. Handle unknown codes without corrupting framing.

An InternalError is not a successful execution acknowledgment. Reconnect/retry according to the retained mutation identity and recovery contract; do not generate a replacement order merely because delivery failed.