Skip to main content

L3 - Market by Order

The L3 format is designed to represent an order book as a set of individual quotes for each price level.

The keys of the data entry are the symbol, exchange, side, and quote id.

The orders are sorted by price, and quotes within one price level form a queue (if a particular exchange operates with order priority).

L3OrderBook

The following entries can be sent within this level of detalization:

If a TradeEntry is sent in L3 format (with buyerOrderId or sellerOrderId depending on AgressiveSide), it decreases order size with an id equal to the buyer or seller id.

The following scheme describes the process of validation of an incoming message. If the validation rules are not met, the message is rejected and no updates are made to order book by this message.

Market data package validation for level3 data

Package Validation

L3EntryNew is used to provide a snapshot of the order book state. It is considered that the snapshot for a particular exchange is sent completely within one package.

Snapshot entries and update entries should not be grouped in one snapshot package. Trade entries can be grouped with incremental updates. Snapshots for multiple exchanges should be sent in different packages.

Bid and ask entries in each snapshot package should be sorted (from best to worst price, same-priced entries should be in FIFO order).

Fields Validation

  • Side and QuoteId should not be null. These two fields identify entry in L3 Order Book.
  • Size should be > 0, a number, and not null (except for CANCEL entry that can omit Size).
  • Price should be a number and not null. Price should be > 0 by default. Price less or equal to 0 can be enabled in the validator (for example, for spreads or synthetic instruments trading). One exception is CANCEL entry that can omit Price.
  • L3EntryNew should specify a type of insert, which allows managing the order queue. The queue is formed within the price level. The type of insert can be:
    • ADD_BACK - Adds the quote at the end of the queue.
    • ADD_FRONT - Adds the quote in the beginning of the queue.
    • ADD_BEFORE - Adds the quote before the quote with the id specified in the specially provided field InsertBeforeQuoteId.
  • Incremental updates can be:
    • MODIFY - Changes the order while saving its priority in the queue.
    • REPLACE - Changes the order that result in order priority loss (order is moved to the back of same-priced orders queue).
    • CANCEL - Removes the order from the queue.
  • For a snapshot, the type of insert should be filled. For an update, the type of update should be filled.

Conformity with the State of the Book

  • REPLACE can change order price or size.
  • MODIFY cannot change order price.
  • QuoteId must be unique for each quote.
  • MODIFY cannot change order SIDE, but REPLACE can.
  • When inserting before a quote in a queue, the quote, before which we insert this quote, should be on the same price level.

Relation to Trading Actions

The L3 format types reflect the actual trading actions. For example, the following messages are expected in L3 after these trading actions:

  • CancelReplace -> REPLACE
  • Replace -> MODIFY
  • Cancel -> CANCEL
  • NewOrder -> ADD_BACK

Limited order book depth

When market data is limited to top N orders make sure special care must be taken to maintain visible side of order book. For example, when inserting visible order into already full order book make sure to issue synthetic DELETE that hides order pushed out of visibility.

note

In general, when market data is limited to N orders, make sure each incremental update package does NOT break this invariant.

For example, consider a scenario when order book is limited to 3 orders. Imagine the following initial state of bid market side:

EntryIdSideSizePrice
L3EntryNewid0Bid10020.04
L3EntryNewid1Bid3020.02
L3EntryNewid2Bid5020.01

Now imagine we want to insert new bid order with price 20.03. Since such INSERT pushes bid order id2 (priced at 20.01) out of visible order book we expect incremental update package to also contain DELETE entry id2:

EntryIdSideSizePriceAction
L3EntryUpdateid2Bid5020.01DELETE
L3EntryNewid3Bid1020.03

Resulting book:

EntryIdSideSizePrice
L3EntryNewid0Bid10020.04
L3EntryNewid3Bid1020.03
L3EntryNewid1Bid3020.02

Order book examples

Example 1: Inserting a new order at the end of the queue

Consider the following order queue in the order book. An order is identified by its QuoteId (for simplicity, we will only look at the ask side of the book in these examples).

Ask
Position in queueidSizePrice
0id0110.15
1id1210.15
2id2510.15
0id3210.2
1id4410.2

The following message is received:

Package type: Increment

EntryQuoteIdSideSizePriceInsertType
L3EntryNewid6Ask410.15ADD_BACK

Result:

Ask
Position in queueidSizePrice
0id0110.15
1id1210.15
2id2510.15
3id6410.15
0id3210.2
1id4410.2

Example 2: Inserting a new order at the beginning of the queue

Package type: Increment

EntryQuoteIdSideSizePriceInsertType
L3EntryNewid5Ask510.2ADD_FRONT

The resulting order book state:

Ask
Position in queueidSizePrice
0id0110.15
1id1210.15
2id2510.15
3id6410.15
0id5510.2
1id3210.2
2id4410.2

Example 3: Inserting a new order

Package type: Increment

EntryQuoteIdSideSizePriceInsertTypeInsertBeforeQuoteId
L3EntryNewid7Ask1010.2ADD_BEFOREid4

The resulting state:

Ask
Position in queueidSizePrice
0id0110.15
1id1210.15
2id2510.15
3id6410.15
0id5510.2
1id3210.2
2id71010.2
3id4410.2

Example 4: InsertBeforeQuoteId field validation

Package type: Increment

EntryQuoteIdSideSizePriceInsertTypeInsertBeforeQuoteId
L3EntryNewid8Ask1010.25ADD_BEFOREid4

Orders are sorted by price therefore order with greater price is not in the same queue as order with id4 so this update is incorrect.

Example 5: Order update

Consider the following order queue in the order book:

BidAsk
LevelPosition in queueidSizePricePriceSizeid
00id010010.1510.220id1
01id22010.1510.240id3
02id43010.15
10id54010.110.2550id6
11id7210.110.25100id8
20id92010.0510.380id10
30id112010.0010.3550id12
3110.3520id13
40id14909.9510.420id15
41id16909.95

The following message comes:

Package type: Increment

EntryQuoteIdSideSizePriceUpdateType
L3EntryUpdateid6Ask4010.25MODIFY

The resulting order book:

BidAsk
LevelPosition in queueidSizePricePriceSizeid
00id010010.1510.220id1
01id22010.1510.240id3
02id43010.15
10id54010.110.2540id6
11id7210.110.25100id8
20id92010.0510.380id10
30id112010.0010.3550id12
3110.3520id13
40id14909.9510.420id15
41id16909.95

Example 6: Replace in the order

After that a REPLACE update comes:

Package type: Increment

EntryQuoteIdSideSizePriceUpdateType
L3EntryUpdateid6Ask3010.25REPLACE

This results in losing the position in the queue:

BidAsk
LevelPosition in queueidSizePricePriceSizeid
00id010010.1510.220id1
01id22010.1510.240id3
02id43010.15
10id54010.110.25100id8
11id7210.110.2530id6
20id92010.0510.380id10
30id112010.0010.3550id12
3110.3520id13
40id14909.9510.420id15
41id16909.95

Example 7: Cancel for the order

Package type: Increment

EntryQuoteIdSideSizePriceUpdateType
L3EntryUpdateid5CANCEL

No need to specify any other fields except id for this type of update.

BidAsk
LevelPosition in queueidSizePricePriceSizeid
00id010010.1510.220id1
01id22010.1510.240id3
02id43010.15
10id7210.110.25100id8
1110.2530id6
20id92010.0510.380id10
30id112010.0010.3550id12
3110.3520id13
40id14909.9510.420id15
41id16909.95

Example 8: Validation of MODIFY update for the order

Package type: Increment

EntryQuoteIdSideSizePriceUpdateType
L3EntryUpdateid4Bid3010.12MODIFY

This is an invalid update because the price of the update does not coincide with the state of the book for this quote.

Example 9: REPLACE for the order with price change

Package type: Increment

EntryQuoteIdSideSizePriceUpdateType
L3EntryUpdateid4Bid3010.12REPLACE

The result is:

BidAsk
LevelPosition in queueidSizePricePriceSizeid
00id010010.1510.220id1
01id22010.1510.240id3
10id43010.1210.25100id8
1110.2530id6
20id7210.110.380id10
30id92010.0510.3550id12
3110.3520id13
40id112010.0010.420id15
50id14909.95
51id16909.95

Example 10: Replace with side change

Package type: Increment

EntryQuoteIdSideSizePriceUpdateType
L3EntryUpdateid8Bid8010.25REPLACE

The resulting order book is as follows:

BidAsk
LevelPosition in queueidSizePricePriceSizeid
00id010010.1510.220id1
01id22010.1510.240id3
10id43010.1210.2530id6
11id88010.15
20id7210.110.380id10
30id92010.0510.3550id12
3110.3520id13
40id112010.0010.420id15
50id14909.95
51id16909.95

Example 11: Trade processing

Package type: Increment

EntrySizePriceSellerOrderId
TradeEntry2010.2id1

The order with the corresponding id is removed from the order book as its size is equal to the trade size:

BidAsk
LevelPosition in queueidSizePricePriceSizeid
00id010010.1510.240id3
01id22010.15
10id43010.1210.2530id6
11id88010.12
20id7210.110.380id10
30id92010.0510.3550id12
3110.3520id13
40id112010.0010.420id15
50id14909.95
51id16909.95

Example 12: Trade processing

Package type: Increment

EntrySizePriceSellerOrderId
TradeEntry2010.15id0

The order with the corresponding id will have a new size reduced by the trade quantity:

BidAsk
LevelPosition in queueidSizePricePriceSizeid
00id08010.1510.240id3
01id22010.15
10id43010.1210.2530id6
11id88010.12
20id7210.110.380id10
30id92010.0510.3550id12
3110.3520id13
40id112010.0010.420id15
50id14909.95
51id16909.95