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).
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.
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.
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:
Entry | Id | Side | Size | Price |
---|---|---|---|---|
L3EntryNew | id0 | Bid | 100 | 20.04 |
L3EntryNew | id1 | Bid | 30 | 20.02 |
L3EntryNew | id2 | Bid | 50 | 20.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:
Entry | Id | Side | Size | Price | Action |
---|---|---|---|---|---|
L3EntryUpdate | id2 | Bid | 50 | 20.01 | DELETE |
L3EntryNew | id3 | Bid | 10 | 20.03 |
Resulting book:
Entry | Id | Side | Size | Price |
---|---|---|---|---|
L3EntryNew | id0 | Bid | 100 | 20.04 |
L3EntryNew | id3 | Bid | 10 | 20.03 |
L3EntryNew | id1 | Bid | 30 | 20.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 queue | id | Size | Price |
0 | id0 | 1 | 10.15 |
1 | id1 | 2 | 10.15 |
2 | id2 | 5 | 10.15 |
0 | id3 | 2 | 10.2 |
1 | id4 | 4 | 10.2 |
The following message is received:
Package type: Increment
Entry | QuoteId | Side | Size | Price | InsertType |
---|---|---|---|---|---|
L3EntryNew | id6 | Ask | 4 | 10.15 | ADD_BACK |
Result:
Ask | |||
---|---|---|---|
Position in queue | id | Size | Price |
0 | id0 | 1 | 10.15 |
1 | id1 | 2 | 10.15 |
2 | id2 | 5 | 10.15 |
3 | id6 | 4 | 10.15 |
0 | id3 | 2 | 10.2 |
1 | id4 | 4 | 10.2 |
Example 2: Inserting a new order at the beginning of the queue
Package type: Increment
Entry | QuoteId | Side | Size | Price | InsertType |
---|---|---|---|---|---|
L3EntryNew | id5 | Ask | 5 | 10.2 | ADD_FRONT |
The resulting order book state:
Ask | |||
---|---|---|---|
Position in queue | id | Size | Price |
0 | id0 | 1 | 10.15 |
1 | id1 | 2 | 10.15 |
2 | id2 | 5 | 10.15 |
3 | id6 | 4 | 10.15 |
0 | id5 | 5 | 10.2 |
1 | id3 | 2 | 10.2 |
2 | id4 | 4 | 10.2 |
Example 3: Inserting a new order
Package type: Increment
Entry | QuoteId | Side | Size | Price | InsertType | InsertBeforeQuoteId |
---|---|---|---|---|---|---|
L3EntryNew | id7 | Ask | 10 | 10.2 | ADD_BEFORE | id4 |
The resulting state:
Ask | |||
---|---|---|---|
Position in queue | id | Size | Price |
0 | id0 | 1 | 10.15 |
1 | id1 | 2 | 10.15 |
2 | id2 | 5 | 10.15 |
3 | id6 | 4 | 10.15 |
0 | id5 | 5 | 10.2 |
1 | id3 | 2 | 10.2 |
2 | id7 | 10 | 10.2 |
3 | id4 | 4 | 10.2 |
Example 4: InsertBeforeQuoteId field validation
Package type: Increment
Entry | QuoteId | Side | Size | Price | InsertType | InsertBeforeQuoteId |
---|---|---|---|---|---|---|
L3EntryNew | id8 | Ask | 10 | 10.25 | ADD_BEFORE | id4 |
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:
Bid | Ask | ||||||
---|---|---|---|---|---|---|---|
Level | Position in queue | id | Size | Price | Price | Size | id |
0 | 0 | id0 | 100 | 10.15 | 10.2 | 20 | id1 |
0 | 1 | id2 | 20 | 10.15 | 10.2 | 40 | id3 |
0 | 2 | id4 | 30 | 10.15 | |||
1 | 0 | id5 | 40 | 10.1 | 10.25 | 50 | id6 |
1 | 1 | id7 | 2 | 10.1 | 10.25 | 100 | id8 |
2 | 0 | id9 | 20 | 10.05 | 10.3 | 80 | id10 |
3 | 0 | id11 | 20 | 10.00 | 10.35 | 50 | id12 |
3 | 1 | 10.35 | 20 | id13 | |||
4 | 0 | id14 | 90 | 9.95 | 10.4 | 20 | id15 |
4 | 1 | id16 | 90 | 9.95 |
The following message comes:
Package type: Increment
Entry | QuoteId | Side | Size | Price | UpdateType |
---|---|---|---|---|---|
L3EntryUpdate | id6 | Ask | 40 | 10.25 | MODIFY |
The resulting order book:
Bid | Ask | ||||||
---|---|---|---|---|---|---|---|
Level | Position in queue | id | Size | Price | Price | Size | id |
0 | 0 | id0 | 100 | 10.15 | 10.2 | 20 | id1 |
0 | 1 | id2 | 20 | 10.15 | 10.2 | 40 | id3 |
0 | 2 | id4 | 30 | 10.15 | |||
1 | 0 | id5 | 40 | 10.1 | 10.25 | 40 | id6 |
1 | 1 | id7 | 2 | 10.1 | 10.25 | 100 | id8 |
2 | 0 | id9 | 20 | 10.05 | 10.3 | 80 | id10 |
3 | 0 | id11 | 20 | 10.00 | 10.35 | 50 | id12 |
3 | 1 | 10.35 | 20 | id13 | |||
4 | 0 | id14 | 90 | 9.95 | 10.4 | 20 | id15 |
4 | 1 | id16 | 90 | 9.95 |
Example 6: Replace in the order
After that a REPLACE update comes:
Package type: Increment
Entry | QuoteId | Side | Size | Price | UpdateType |
---|---|---|---|---|---|
L3EntryUpdate | id6 | Ask | 30 | 10.25 | REPLACE |
This results in losing the position in the queue:
Bid | Ask | ||||||
---|---|---|---|---|---|---|---|
Level | Position in queue | id | Size | Price | Price | Size | id |
0 | 0 | id0 | 100 | 10.15 | 10.2 | 20 | id1 |
0 | 1 | id2 | 20 | 10.15 | 10.2 | 40 | id3 |
0 | 2 | id4 | 30 | 10.15 | |||
1 | 0 | id5 | 40 | 10.1 | 10.25 | 100 | id8 |
1 | 1 | id7 | 2 | 10.1 | 10.25 | 30 | id6 |
2 | 0 | id9 | 20 | 10.05 | 10.3 | 80 | id10 |
3 | 0 | id11 | 20 | 10.00 | 10.35 | 50 | id12 |
3 | 1 | 10.35 | 20 | id13 | |||
4 | 0 | id14 | 90 | 9.95 | 10.4 | 20 | id15 |
4 | 1 | id16 | 90 | 9.95 |
Example 7: Cancel for the order
Package type: Increment
Entry | QuoteId | Side | Size | Price | UpdateType |
---|---|---|---|---|---|
L3EntryUpdate | id5 | CANCEL |
No need to specify any other fields except id for this type of update.
Bid | Ask | ||||||
---|---|---|---|---|---|---|---|
Level | Position in queue | id | Size | Price | Price | Size | id |
0 | 0 | id0 | 100 | 10.15 | 10.2 | 20 | id1 |
0 | 1 | id2 | 20 | 10.15 | 10.2 | 40 | id3 |
0 | 2 | id4 | 30 | 10.15 | |||
1 | 0 | id7 | 2 | 10.1 | 10.25 | 100 | id8 |
1 | 1 | 10.25 | 30 | id6 | |||
2 | 0 | id9 | 20 | 10.05 | 10.3 | 80 | id10 |
3 | 0 | id11 | 20 | 10.00 | 10.35 | 50 | id12 |
3 | 1 | 10.35 | 20 | id13 | |||
4 | 0 | id14 | 90 | 9.95 | 10.4 | 20 | id15 |
4 | 1 | id16 | 90 | 9.95 |
Example 8: Validation of MODIFY update for the order
Package type: Increment
Entry | QuoteId | Side | Size | Price | UpdateType |
---|---|---|---|---|---|
L3EntryUpdate | id4 | Bid | 30 | 10.12 | MODIFY |
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
Entry | QuoteId | Side | Size | Price | UpdateType |
---|---|---|---|---|---|
L3EntryUpdate | id4 | Bid | 30 | 10.12 | REPLACE |
The result is:
Bid | Ask | ||||||
---|---|---|---|---|---|---|---|
Level | Position in queue | id | Size | Price | Price | Size | id |
0 | 0 | id0 | 100 | 10.15 | 10.2 | 20 | id1 |
0 | 1 | id2 | 20 | 10.15 | 10.2 | 40 | id3 |
1 | 0 | id4 | 30 | 10.12 | 10.25 | 100 | id8 |
1 | 1 | 10.25 | 30 | id6 | |||
2 | 0 | id7 | 2 | 10.1 | 10.3 | 80 | id10 |
3 | 0 | id9 | 20 | 10.05 | 10.35 | 50 | id12 |
3 | 1 | 10.35 | 20 | id13 | |||
4 | 0 | id11 | 20 | 10.00 | 10.4 | 20 | id15 |
5 | 0 | id14 | 90 | 9.95 | |||
5 | 1 | id16 | 90 | 9.95 |
Example 10: Replace with side change
Package type: Increment
Entry | QuoteId | Side | Size | Price | UpdateType |
---|---|---|---|---|---|
L3EntryUpdate | id8 | Bid | 80 | 10.25 | REPLACE |
The resulting order book is as follows:
Bid | Ask | ||||||
---|---|---|---|---|---|---|---|
Level | Position in queue | id | Size | Price | Price | Size | id |
0 | 0 | id0 | 100 | 10.15 | 10.2 | 20 | id1 |
0 | 1 | id2 | 20 | 10.15 | 10.2 | 40 | id3 |
1 | 0 | id4 | 30 | 10.12 | 10.25 | 30 | id6 |
1 | 1 | id8 | 80 | 10.15 | |||
2 | 0 | id7 | 2 | 10.1 | 10.3 | 80 | id10 |
3 | 0 | id9 | 20 | 10.05 | 10.35 | 50 | id12 |
3 | 1 | 10.35 | 20 | id13 | |||
4 | 0 | id11 | 20 | 10.00 | 10.4 | 20 | id15 |
5 | 0 | id14 | 90 | 9.95 | |||
5 | 1 | id16 | 90 | 9.95 |
Example 11: Trade processing
Package type: Increment
Entry | Size | Price | SellerOrderId |
---|---|---|---|
TradeEntry | 20 | 10.2 | id1 |
The order with the corresponding id is removed from the order book as its size is equal to the trade size:
Bid | Ask | ||||||
---|---|---|---|---|---|---|---|
Level | Position in queue | id | Size | Price | Price | Size | id |
0 | 0 | id0 | 100 | 10.15 | 10.2 | 40 | id3 |
0 | 1 | id2 | 20 | 10.15 | |||
1 | 0 | id4 | 30 | 10.12 | 10.25 | 30 | id6 |
1 | 1 | id8 | 80 | 10.12 | |||
2 | 0 | id7 | 2 | 10.1 | 10.3 | 80 | id10 |
3 | 0 | id9 | 20 | 10.05 | 10.35 | 50 | id12 |
3 | 1 | 10.35 | 20 | id13 | |||
4 | 0 | id11 | 20 | 10.00 | 10.4 | 20 | id15 |
5 | 0 | id14 | 90 | 9.95 | |||
5 | 1 | id16 | 90 | 9.95 |
Example 12: Trade processing
Package type: Increment
Entry | Size | Price | SellerOrderId |
---|---|---|---|
TradeEntry | 20 | 10.15 | id0 |
The order with the corresponding id will have a new size reduced by the trade quantity:
Bid | Ask | ||||||
---|---|---|---|---|---|---|---|
Level | Position in queue | id | Size | Price | Price | Size | id |
0 | 0 | id0 | 80 | 10.15 | 10.2 | 40 | id3 |
0 | 1 | id2 | 20 | 10.15 | |||
1 | 0 | id4 | 30 | 10.12 | 10.25 | 30 | id6 |
1 | 1 | id8 | 80 | 10.12 | |||
2 | 0 | id7 | 2 | 10.1 | 10.3 | 80 | id10 |
3 | 0 | id9 | 20 | 10.05 | 10.35 | 50 | id12 |
3 | 1 | 10.35 | 20 | id13 | |||
4 | 0 | id11 | 20 | 10.00 | 10.4 | 20 | id15 |
5 | 0 | id14 | 90 | 9.95 | |||
5 | 1 | id16 | 90 | 9.95 |