Pricefeed

The pricefeed protocol sends incremental level and trade updates as needed and book snapshots every 10 seconds. When connecting to the pricefeed intra-day, the customer must wait for a book snapshot to apply incremental updates. In other words, after connecting to the pricefeed, the customer should ignore level updates until they receive a book snapshot. Once that happens they can correctly apply level updates. This process is independent of any market state update as the market state message is not sufficient to construct the book. For example if the customer receives a market open message, that could be the market opening from a halted state which means there is existing book state and the book is NOT clear.

Ack IDs are monotonically incremented at the engine but may appear out of sequence, repeated, and gapped in the pricefeed due in part to inside-out sequencing. Use sequence IDs to determine dropped or duplicated messages.

See Examples for some practical examples.

Trade

Byte OffsetByte LengthTypeNameDescription
01charmessageTypeAlways T
18uint64ackIdMatching engine acked sequence
98uint64productIdProduct ID
171chartakerSideTaker side: B = bid, A = ask
188int64pricePrice in ticks
264uint32quantityQuantity
30

Customers are not expected to mutate the book upon receiving a trade message. Each trade message will be accompanied with a corresponding level update which the customer can use to mutate the book. For example, if a Bid is submitted for 10@9015 and a subsequent Ask is submitted for 20@9015 the customer can see the following messages:

{
  "messageType": "Level",
  "ackId": 1,
  "price": 9015,
  "side": "Bid",
  "productId": 4,
  "quantity": 10
}
{
  "messageType": "LastTrade",
  "ackId": 2,
  "price": 9015,
  "productId": 4,
  "takerSide": "Ask",
  "quantity": 10
}
{
  "messageType": "Level",
  "ackId": 3,
  "price": 9015,
  "side": "Bid",
  "productId": 4,
  "quantity": 0
}
{
  "messageType": "Level",
  "ackId": 4,
  "price": 9015,
  "side": "Ask",
  "productId": 4,
  "quantity": 10
}

Note that the previous example also applies to calendar spreads. More explicitly, there are no extra pricefeed messages for calendar spread events. They all operate on the spread product only.

Level

A level update reflects the current quantity at the respective price level. A cleared level is represented by a zero quantity. Trade updates from a match event are sent before the respective level updates. Ack IDs on level messages represent the latest available ack ID when we calculated the book update.

Byte OffsetByte LengthTypeNameDescription
01charmessageTypeAlways L
18uint64ackIdLatest matching engine acked sequence number
98uint64productIdProduct ID
171charsideSide: B = bid, A = ask
188int64pricePrice in ticks
264uint32quantityQuantity
30

Book

Only the top 10 levels on each side are generated. It is important to note that Level updates are NOT sent when a level goes out of scope (i.e. there are 10 or more "better" orders on that side). For example, if there are 10 contiguous bids on price levels 9000 through 9009 and someone bids at 9010, no message will be sent for level 9000. The next book update will not contain level 9000, but the order still exists! Additionally, if level 9000 is deleted we will not send a level update as it is out of scope in that scenario. If it comes back into scope (re-enters the top 10 bids) then we will send level updates again.

Byte OffsetByte LengthTypeNameDescription
01charmessageTypeAlways B
18uint64lastAckIdLast ackId
98uint64productIdProduct ID
174uint32bidsLengthNumber of bytes for bid levels
21bidsLengthLevelsbidLevelsSee Book Level
21 + bidsLength4uint32asksLengthNumber of bytes for ask levels
25 + bidsLengthasksLengthLevelsaskLevelsSee Book Level
25 + bidsLength + asksLength

Book Level

Byte OffsetByte LengthTypeNameDescription
08int64pricePrice in ticks
84uint32quantityQuantity
12

Block Trade

A block trade is very similar to a Trade message except that it does not have takerSide and these messages do not interact with the book.

Byte OffsetByte LengthTypeNameDescription
01charmessageTypeAlways X
18uint64ackIdMatching engine acked sequence
98uint64productIdProduct ID
178int64pricePrice in ticks
254uint32quantityQuantity
29

Examples

Level Goes Out of Scope

Assuming there are 10 contiguous levels already on the book, a book message similar to the following would be received.

{
  "asks": [],
  "bids": [
    [10000, 10],
    [10001, 10],
    [10002, 10],
    [10003, 10],
    [10004, 10],
    [10005, 10],
    [10006, 10],
    [10007, 10],
    [10008, 10],
    [10009, 10]
  ],
  "lastAckId": 7158621609438216263,
  "productId": 12,
  "messageType": "Book"
}

After another bid is submitted for 10@10010, a book message like this would be received.

{
  "asks": [],
  "bids": [
    [10001, 10],
    [10002, 10],
    [10003, 10],
    [10004, 10],
    [10005, 10],
    [10006, 10],
    [10007, 10],
    [10008, 10],
    [10009, 10],
    [10010, 10]
  ],
  "lastAckId": 7158621609438216264,
  "productId": 12,
  "messageType": "Book"
}

Notice how the 10000 level is not in the book snapshot, but is still on the book! If the original order at the 10000 level were modified, no Level update would be received.

Aggressor Order Full Fill

  • Submit bid for 20@10000
  • Submit ask for 10@10000
{
    "ackId": 7158621609438216346,
    "price": 10000,
    "productId": 12,
    "quantity": 20,
    "side": "Bid",
    "messageType": "Level"
}
{
    "ackId": 7158621609438216348,
    "price": 10000,
    "productId": 12,
    "quantity": 10,
    "takerSide": "Ask",
    "messageType": "LastTrade"
}
{
    "ackId": 7158621609438216348,
    "price": 10000,
    "productId": 12,
    "quantity": 10,
    "side": "Bid",
    "messageType": "Level"
}

There is no Level update for the aggressor order as it is fully filled and never rests on the book.

Aggressor Order Partial Fill

  • Submit bid for 10@15000
  • Submit ask for 20@15000
{
    "ackId": 7158621609438216349,
    "price": 15000,
    "productId": 12,
    "quantity": 10,
    "side": "Bid",
    "messageType": "Level"
}
{
    "ackId": 7158621609438216351,
    "price": 15000,
    "productId": 12,
    "quantity": 10,
    "takerSide": "Ask",
    "messageType": "LastTrade"
}
{
    "ackId": 7158621609438216351,
    "price": 15000,
    "productId": 12,
    "quantity": 0,
    "side": "Bid",
    "messageType": "Level"
}
{
    "ackId": 7158621609438216351,
    "price": 15000,
    "productId": 12,
    "quantity": 10,
    "side": "Ask",
    "messageType": "Level"
}

Since the aggressor order was only partially filled, we get a Level update for both the aggressor and an update which clears out the original passive order since it was fully filled.

Start Trading

Trade US Perpetual Futures, Physical Futures, and Options on the Bitcoin Complex®, XRP, ETH, SOL, and more.