Level2 - Market by Level
Market By Level (or sometimes called Market By Price, or Level 2 detalizatoin) describes a set of active limit orders for a certain instrument maintained by an exchange. The Order Book contains the prices of bids and offers, the size of orders, and sometimes the number of orders on this level. The simplest example of an Order Book is depicted as a table shown in the figure below. The table consists of two parts: the left side shows orders for buying, the right one shows the orders for sale. Each price level indicates its combined volume. The top line of the table yields the best bid and the best ask prices.
The key for this data is the symbol, exchange, side, and level index. This means that there is a unique price entry for this combination of fields.
The following entries can be sent within this level of detalization:
We use the following numbering of price levels: best bid and best ask are attributed to the 0 level, the following prices are at level 1, etc. When a new limit order comes in, it is placed in the book according to its price.
It is possible to pass data to the order book with fixed or non-fixed market depth. Market depth is the number of the levels of the book. It is specified by a Level2 data provider or the exchange itself. Hence, it could be different for different data providers. By design, it is considered that the depth is provided by means of other data messages, or even using some other approach.
If an order book is of fixed depth, market depth should be provided in one way or another, or alternatively, the data provider should send deletes to higher levels if this is the convention. Setting up a correct value for Market Depth is important when creating a book. Failure to do so results in an incorrect simulation:
- If a user specifies market depth greater than the real one, the order book can hold outdated orders.
- If a user specifies market depth smaller than the real one, the order book can lose some new orders.
Levels with orders greater than the depth will be dropped. For example, if the depth is 10 and one more level is added before the 10th, the 10th level is dropped. If DELETE happens after that, we get 9 levels.
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 the order book by this message. The green color of the condition indicates that this is a warning case, and not a reason to consider the message invalid.
Package Validation
Package validation block checks package consistency. It is possible to mix different entries within one package, or entries from different exchanges in one package. But it is not allowed to send snapshots for multiple exchanges within one package. There also should not be snapshot entries combined with update entries in one snapshot message. But trades and updates can be sent in one package. It is considered that a snapshot of the current state of the book for each exchange is sent entirely within one package. There cannot be, for example, a snapshot of each side of the book sent separately in multiple packages.
Trade entry can be sent in this format within a package of incremental updates. They do not affect state of the book.
Bid and ask entries in each snapshot package should be sorted (from best to worst price).
Fields Validation
The following rules are applied to field values:
- Side and size should have valid values. Size should be > 0, a number, and not null.
- 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).
- The level should be less than market depth.
- For an update, the action can be one of the following:
- INSERT: Inserting a new quote on the specified level (specified in the Level property). If this level already exists, every level's index greater than or equal to this will be incremented. (For instance, if level 5 is added then the level index at 5 and greater will be incremented by 1). An L2Entry update with an insert type is considered invalid. L2EntryNew should be sent in this case.
- UPDATE: Updating values on the specified level. Participant id, quote id, and number of orders can be updated.
- DELETE: Deletes the level. The level index after this level is decremented. The side, price, level, and action fields should not be null. DELETE is used to remove quotes because trades do not do that in L2 format. They cannot affect the state of the book (like they do in L3 format). NOTE:
price
andsize
fields are optional in DELETE entry (it identifies what needs to be deleted usinglevel
field which is required).
Conformity with the State of the Book
Sorting:
The design allows sending data to an aggregated or non-aggregated order book. In other words, we can aggregate orders per price level. In the case of an aggregated order book, prices on levels should be strictly ascending for the ask side and strictly descending for the bid side. In the case of a non-aggregated order book, prices on levels should be monotonically ascending for the ask side and monotonically descending for the bid side. (There can be more than one level in a row with the same price, denoting an individual order).
An aggregated Order Book is correct when:
- Bid0 < Ask0
- BidN > BidN+1
- AskN < AskN+1
A non-aggregated Order Book is correct when:
- Bid0 < Ask0
- BidN >= BidN+1
- AskN <= AskN+1
The provider should send packages during processing, during each part of which levels should remain sorted. Therefore, after processing each entry in the package, the order book should be ordered. Otherwise, processing algorithms should have been significantly more complicated.
It is critical that the data is sorted on each side for each exchange, but if the best bid is not less than the best ask, it is considered a warning case. Such data are still valid.
For a non-aggregated orderbook, we allow using QuoteId, but as it was said earlier, QuoteId is not the key of the data. If the QuoteId field value coincides for multiple quotes then this is a warning case. In other words, it is at the data provider's discretion to provide quotes with a unique id.
It is important that values in the fields of the update coincide with the actual values in the order book. For example, the price in an update is the same as the price in an order book for this level. The update cannot change price.
Limited order book depth
When market data is limited to top N levels make sure special care must be taken to maintain visible side of order book. For example, when inserting visible level into already full order book make sure to issue synthetic DELETE that hides level pushed out of visibility.
In general, when market data is limited to N levels, make sure each incremental update package does NOT break this invariant.
For example, consider a scenario when order book is limited to 3 levels. Imagine the following initial state of Bid market side:
Entry | Level | Side | Size | Price |
---|---|---|---|---|
L2EntryNew | 0 | Bid | 100 | 20.04 |
L2EntryNew | 1 | Bid | 30 | 20.02 |
L2EntryNew | 2 | Bid | 50 | 20.01 |
Now imagine we want to insert new bid level with price 20.03. Since such INSERT pushes bid level at 20.01 out of visible order book we expect incremental update package to also contain DELETE Bid at level 2 entry:
Entry | Level | Side | Size | Price | Action |
---|---|---|---|---|---|
L2EntryUpdate | 2 | Bid | 50 | 20.01 | DELETE |
L2EntryNew | 1 | Bid | 10 | 20.03 |
Resulting book:
Entry | Level | Side | Size | Price |
---|---|---|---|---|
L2EntryNew | 0 | Bid | 100 | 20.04 |
L2EntryNew | 1 | Bid | 10 | 20.03 |
L2EntryNew | 2 | Bid | 30 | 20.02 |
Order book examples
Example 1: Snapshot example
Consider a snapshot having 3 levels on the bid side and 1 level on the ask side:
Entry | Level | Side | Size | Price |
---|---|---|---|---|
L2EntryNew | 0 | Bid | 5 | 1.0214 |
L2EntryNew | 1 | Bid | 3 | 1.0213 |
L2EntryNew | 2 | Bid | 4 | 1.0212 |
L2EntryNew | 0 | Ask | 12 | 1.0216 |
The snapshot above reports the following orderbook:
Bid | Ask | |||
---|---|---|---|---|
Level | Size | Price | Price | Size |
0 | 5 | 1.0214 | 1.0216 | 12 |
1 | 3 | 1.0213 | ||
2 | 4 | 1.0212 |
Example 2: Snapshot with equal prices for consequent levels
Consider the following snapshot is received:
Package type: Snapshot
Entry | Level | Side | Size | Price |
---|---|---|---|---|
L2EntryNew | 0 | Bid | 100 | 20.04 |
L2EntryNew | 1 | Bid | 30 | 20.02 |
L2EntryNew | 2 | Bid | 50 | 20.04 |
L2EntryNew | 0 | Ask | 150 | 20.1 |
L2EntryNew | 1 | Ask | 50 | 20.1 |
L2EntryNew | 2 | Ask | 50 | 20.12 |
Though entries on ask side have equal prices for two consequent levels, this is still a valid case (non-aggregated book). This snapshot is invalid because on the bid side, the entries are not sorted.
Example 3: Package validation
Consider the following package is received:
Package type: Snapshot
Entry | Level | Side | Size | Price |
---|---|---|---|---|
L2EntryNew | 0 | Bid | 100 | 20.04 |
L2EntryNew | 1 | Bid | 30 | 20.02 |
L2EntryNew | 2 | Bid | 50 | 20.0 |
L2EntryNew | 0 | Ask | 150 | 20.1 |
L2EntryNew | 1 | Ask | 50 | 20.1 |
L2EntryNew | 2 | Ask | 50 | 20.12 |
TradeEntry | not applicable | not applicable | 50 | 20.12 |
This package is invalid: we do not expect updates grouped with snapshots.
Example 4: Inserting new level
Package type: Increment
Entry | Level | Side | Size | Price |
---|---|---|---|---|
L2EntryNew | 1 | Bid | 5 | 120.53 |
Original orderbook:
Bid | Ask | |||
---|---|---|---|---|
Level | Size | Price | Price | Size |
0 | 5 | 120.55 | 120.6 | 4 |
1 | 2 | 120.5 | 120.65 | 4 |
2 | 7 | 120.45 | 120.7 | 3 |
3 | 2 | 120.4 | 120.75 | 1 |
4 | 1 | 120.35 | 120.8 | 1 |
The resulting orderbook if fixed depth is set to 5:
Bid | Ask | |||
---|---|---|---|---|
Level | Size | Price | Price | Size |
0 | 5 | 120.55 | 120.6 | 4 |
1 | 5 | 120.53 | 120.65 | 4 |
2 | 2 | 120.5 | 120.7 | 3 |
3 | 7 | 120.45 | 120.75 | 1 |
4 | 2 | 120.4 | 120.8 | 1 |
Notice that the last bid level is removed because the insert moves it to 5th level, which exceedes the Order Book size.
However, if the order book depth is not fixed or the depth specified is greater than 5, the result will be as follows:
Bid | Ask | |||
---|---|---|---|---|
Level | Size | Price | Price | Size |
0 | 5 | 120.55 | 120.6 | 4 |
1 | 5 | 120.53 | 120.65 | 4 |
2 | 2 | 120.5 | 120.7 | 3 |
3 | 7 | 120.45 | 120.75 | 1 |
4 | 2 | 120.4 | 120.8 | 1 |
Example 5: Delete for level
Package type: Increment
Entry | Level | Side | Size | Price | Action |
---|---|---|---|---|---|
L2EntryUpdate | 4 | Bid | 2 | 120.4 | DELETE |
The resulting order book if fixed depth is set to 5:
Bid | Ask | |||
---|---|---|---|---|
Level | Size | Price | Price | Size |
0 | 5 | 120.55 | 120.6 | 4 |
1 | 5 | 120.53 | 120.65 | 4 |
2 | 2 | 120.5 | 120.7 | 3 |
3 | 7 | 120.45 | 120.75 | 1 |
4 | 120.8 |
Notice that the last bid level is empty, even though we shifted down the 4th level on the previous step. However, once it was removed, the data from this level was no longer in the order book.
However, if the order book depth is not fixed or the specified depth is greater than 5, the result will be as follows:
Bid | Ask | |||
---|---|---|---|---|
Level | Size | Price | Price | Size |
0 | 5 | 120.55 | 120.6 | 4 |
1 | 5 | 120.53 | 120.65 | 4 |
2 | 2 | 120.5 | 120.7 | 3 |
3 | 7 | 120.45 | 120.75 | 1 |
4 | 1 | 120.35 | 120.8 | 1 |
5 |
Example 6: Conformity with the state
Package type: Increment
Entry | Level | Side | Size | Price | Action |
---|---|---|---|---|---|
L2EntryUpdate | 4 | Bid | 1 | 120.2 | DELETE |
Consider the previous example's state.
This is an invalid update because the price of the update does not coincide with the price on this level.
Example 7: Conformity with the state
Increment
Entry | Level | Side | Size | Price | Action |
---|---|---|---|---|---|
L2EntryUpdate | 3 | Ask | 5 | 120.5 | UPDATE |
This is an invalid update because the price on level 3 does not coincide with the price in the increment message. UPDATE cannot modify the price of the level.
Example 8: Size update
Package type: Increment
Entry | Level | Side | Size | Price | Action |
---|---|---|---|---|---|
L2EntryUpdate | 3 | Ask | 5 | 120.75 | UPDATE |
Result:
Bid | Ask | |||
---|---|---|---|---|
Level | Size | Price | Price | Size |
0 | 5 | 120.55 | 120.6 | 4 |
1 | 5 | 120.53 | 120.65 | 4 |
2 | 2 | 120.5 | 120.7 | 3 |
3 | 7 | 120.45 | 120.75 | 5 |
4 | 120.8 | 1 |
Example 9: Delete for level
Package type: Increment
Entry | Level | Side | Size | Price | Action |
---|---|---|---|---|---|
L2EntryUpdate | 0 | Ask | 4 | 120.6 | DELETE |
In this case, the price and size fields of L2UpdateEntry (if provided) should be equal to those of the corresponding level. Otherwise, this message will be incorrect.
Bid | Ask | |||
---|---|---|---|---|
Level | Size | Price | Price | Size |
0 | 5 | 120.55 | 120.65 | 4 |
1 | 5 | 120.53 | 120.7 | 3 |
2 | 2 | 120.5 | 120.75 | 5 |
3 | 7 | 120.45 | 120.8 | 1 |
4 |