What is SBE?
SBE is a compact binary encoding format with fixed-width fields at fixed offsets, in contrast to standard FIX with ASCII-encoded human-readable tag=value pairs. SBE provides:
- Minimizes latency: Binary encoding eliminates text parsing overhead
- Reduces bandwidth: Compact binary representation uses less network bandwidth than text-based protocols
- Enables zero-copy: Messages can be processed without deserialization in some implementations
- Provides type safety: Strong typing ensures data integrity
- Fixed offsets: Fields are at fixed positions, enabling direct memory access
Message Structure
TCP messages
All SBE messages sent over TCP follow a consistent structure:
- Message Header (32 bytes): Contains protocol identification, message type, sequence numbers, and timing information
- Message Body: Contains the specific message data fields
Messages are sent over TCP connections and can be bidirectional - clients send requests and receive responses/updates on the same connection.
Each message starts with the following 32-byte header:
| Field | Name | Type | Length | Description |
|---|
| 1 | protocolId | uint8 | 1 | Constant (= 0xDB) |
| 2 | flags | uint8 | 1 | Bitset of flags:
0x01 = resend |
| 3 | messageLength | uint16 | 2 | Total length of message including this header and body |
| 4 | messageTypeId | uint16 | 2 | Message type ID (e.g., 100 for NewOrderRequest) |
| 5 | version | uint16 | 2 | Message version number |
| 6 | sequenceNum | int64 | 8 | Message sequence number |
| 7 | lastProcessedSeqNum | int64 | 8 | Sequence number of last message received from client when this message was sent |
| 8 | sendTime | int64 | 8 | Time when this message was sent in nanoseconds since epoch |
UDP messages
Incremental, snapshot and retransmit channels share the same basic packet and message structure. Each UDP packet will start with a packet header followed by zero or more messages. Each message within the packet will start with a message header.
All messages have a sequence number, although only the sequence number of the first message in the packet is specified (in the packet header). Thus the next expected sequence number in the next packet is packet sequenceNum plus messageCount.
Heartbeat packets will have a messageCount of 0 with the next expected sequence number. This same sequence number will be repeated with the first real message.
Each UDP packet starts with the following 24-byte packet header:
| Field | Name | Type | Length | Description |
|---|
| 1 | sendTime | int64 | 8 | Time when this message was sent in nanoseconds since epoch |
| 2 | sequenceNum | int64 | 8 | Message sequence number |
| 3 | channelId | int32 | 4 | Channel identifier for product group |
| 4 | type | int16 | 2 | 1=IncrementalUpdate
2=Snapshot
3=Retransmit |
| 5 | messageCount | uint16 | 2 | Number of messages in packet. 0 for heartbeats |
Each message within a packet starts with the following 16-byte header:
| Field | Name | Type | Length | Description |
|---|
| 1 | messageLength | uint16 | 2 | Total length of message including this header and body |
| 2 | messageTypeId | uint16 | 2 | Message type ID |
| 3 | version | uint16 | 2 | Message version number |
| 4 | flags | uint16 | 2 | Bitset of flags: 0=startOfTransaction 1=endOfTransaction |
| 5 | transactTime | int64 | 8 | Timestamp of event in matching engine. Nanoseconds since epoch |
Key Concepts
Message Type ID: Each message type has a unique messageTypeId (uint16) that identifies its purpose. The messageTypeId is located in field 4 of the message header and is used to determine how to parse and process the message body.
For example, a NewOrderRequest message has messageTypeId = 100. When the gateway receives a message with messageTypeId = 100 in the header, it knows to parse the message body as a NewOrderRequest containing fields such as clientOrderId, correlationId, limitPrice, amount, etc.
Message Types: Each message type has a unique messageTypeId that identifies its purpose (e.g., NewOrderRequest (100), NewOrderResponse (200), OrderFilled (300)).
Sequence Numbers: Sequence numbers are assigned and validated like FIX sequence numbers. Every message includes sequence numbers for:
- sequenceNum: Sequence number of the current message
- lastProcessedSeqNum: Sequence number of the last message received from the client (in responses)
Sequence numbers enable:
- Message ordering verification
- Gap detection for missing messages
- Resend requests when gaps are detected (using the resend flag in the message header)
Correlation IDs: Order entry messages (requests, responses, rejects, and OrderFilled) contain an 8-byte integer correlationId. Clients can assign any value to correlationId, which is not validated by the server. However, Deribit recommends monotonically increasing the value. correlationId is used for matching responses to requests and for indirectly correlated messages, such as order fill and system cancel notifications. Messages from server to client use the correlationId of either the corresponding request message from the client or of the last related request. Market data messages (order book updates, trades, reference data) do not contain correlation IDs.
Protocol ID: All messages start with protocolId = 0xDB to identify the Deribit Starbase protocol.
Data Types
All multi-byte fields are encoded in little-endian byte order.
SBE uses standard binary data types:
- int8/int16/int32/int64: Signed integers of various sizes
- uint8/uint16: Unsigned integers
- double: 64-bit floating point (used for prices)
- char: Fixed-length character arrays
In addition, Starbase uses the following custom composite types:
- Decimal72 (also referred to as DFP — Decimal Floating Point): A 9-byte variable-precision quantity encoding defined as an SBE composite of a 64-bit signed integer mantissa and an 8-bit signed integer exponent, where
value = mantissa × 10^exponent. The variable exponent provides a wide range of precision across different underlying assets. It is used for quantities throughout the protocol.
- QuantityMantissa: A 64-bit signed integer (int64) representing the mantissa component of a Decimal72 quantity. Used in market data messages where only the mantissa is transmitted (8 bytes).
Byte Alignment and Message Padding
Messages are laid out with the following alignment rules:
- 8-byte fields start on 8-byte boundaries
- 4-byte fields start on 4-byte boundaries
- 2-byte fields start on 2-byte boundaries
The frame length of all outbound messages to the client are rounded up to the nearest multiple of 8. Clients are encouraged to do the same with inbound messages, although this is not required.
Usage Workflow
- Connect: Establish a TCP connection to the gateway
- Authenticate: Authenticate using your API credentials
- Send Requests: Send binary-encoded request messages
- Receive Responses: Process binary-encoded response and update messages
- Handle Sequence: Monitor sequence numbers and request resends if gaps are detected
Order Expiration
When an order expires (e.g., a day order at the close of a trading day or when an instrument expires), an OrderCanceled message is sent via the unsolicited events channel.
Fill Limits: The maximum number of fills on a single order or mass quote is 2000 fills for single-leg instruments and 400 fills for combo instruments.
Rejection Reason Codes
Reject messages in the Starbase Binary API include a reason field that indicates why the request was rejected. The following table lists all possible rejection reason codes:
| Value | Name |
|---|
0 | SYSTEM_ERROR |
1 | INVALID_INSTRUMENT |
2 | INVALID_FIELD |
3 | INSUFFICIENT_MARGIN |
4 | DUPLICATE_CLIENT_ORDER_ID |
5 | INVALID_QUANTITY |
6 | INVALID_PRICE |
7 | NOT_ALLOWED_BY_MARKET_STATE |
8 | POST_ONLY |
9 | PRICE_BAND |
10 | PERMISSION_ERROR |
11 | PORTFOLIO_NOT_FOUND |
12 | ORDER_NOT_FOUND |
13 | MMP_NOT_CONFIGURED |
14 | MMP_MAX_QUOTE_QTY_EXCEEDED |
15 | MMP_GROUP_FROZEN |
These rejection reason codes are used in the following reject messages:
Cancel Reason Codes
The cancelReason field uses different enumerations depending on context.
Order Response Cancel Reasons
Used in order response messages to indicate whether the order was immediately (partially) canceled upon entry.
| Value | Name | Description |
|---|
-128 | null | Order was not canceled |
1 | selfMatchPrevention | Canceled due to self-match prevention |
2 | timeInForce | Canceled because time-in-force condition was not met |
3 | marketMakerProtection | Canceled because MMP was triggered |
Used in:
OrdersCanceled Cancel Reasons
Used in the OrdersCanceled unsolicited event to indicate why orders were canceled.
| Value | Name | Description |
|---|
1 | timeInForce | Order expired per its time-in-force setting |
2 | liquidation | Order canceled as part of a liquidation |
3 | clientInitiated | Canceled in response to a client mass cancel request or MMP reset |
4 | adminAction | Canceled by exchange administration |
Used in: