Skip to main content

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.

L2 order book example

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.

Market data package validation for level2 data

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 and size fields are optional in DELETE entry (it identifies what needs to be deleted using level 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.

note

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:

EntryLevelSideSizePrice
L2EntryNew0Bid10020.04
L2EntryNew1Bid3020.02
L2EntryNew2Bid5020.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:

EntryLevelSideSizePriceAction
L2EntryUpdate2Bid5020.01DELETE
L2EntryNew1Bid1020.03

Resulting book:

EntryLevelSideSizePrice
L2EntryNew0Bid10020.04
L2EntryNew1Bid1020.03
L2EntryNew2Bid3020.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:

EntryLevelSideSizePrice
L2EntryNew0Bid51.0214
L2EntryNew1Bid31.0213
L2EntryNew2Bid41.0212
L2EntryNew0Ask121.0216

The snapshot above reports the following orderbook:

BidAsk
LevelSizePricePriceSize
051.02141.021612
131.0213
241.0212

Example 2: Snapshot with equal prices for consequent levels

Consider the following snapshot is received:

Package type: Snapshot

EntryLevelSideSizePrice
L2EntryNew0Bid10020.04
L2EntryNew1Bid3020.02
L2EntryNew2Bid5020.04
L2EntryNew0Ask15020.1
L2EntryNew1Ask5020.1
L2EntryNew2Ask5020.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

EntryLevelSideSizePrice
L2EntryNew0Bid10020.04
L2EntryNew1Bid3020.02
L2EntryNew2Bid5020.0
L2EntryNew0Ask15020.1
L2EntryNew1Ask5020.1
L2EntryNew2Ask5020.12
TradeEntrynot applicablenot applicable5020.12

This package is invalid: we do not expect updates grouped with snapshots.

Example 4: Inserting new level

Package type: Increment

EntryLevelSideSizePrice
L2EntryNew1Bid5120.53

Original orderbook:

BidAsk
LevelSizePricePriceSize
05120.55120.64
12120.5120.654
27120.45120.73
32120.4120.751
41120.35120.81

The resulting orderbook if fixed depth is set to 5:

BidAsk
LevelSizePricePriceSize
05120.55120.64
15120.53120.654
22120.5120.73
37120.45120.751
42120.4120.81

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:

BidAsk
LevelSizePricePriceSize
05120.55120.64
15120.53120.654
22120.5120.73
37120.45120.751
42120.4120.81

Example 5: Delete for level

Package type: Increment

EntryLevelSideSizePriceAction
L2EntryUpdate4Bid2120.4DELETE

The resulting order book if fixed depth is set to 5:

BidAsk
LevelSizePricePriceSize
05120.55120.64
15120.53120.654
22120.5120.73
37120.45120.751
4120.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:

BidAsk
LevelSizePricePriceSize
05120.55120.64
15120.53120.654
22120.5120.73
37120.45120.751
41120.35120.81
5

Example 6: Conformity with the state

Package type: Increment

EntryLevelSideSizePriceAction
L2EntryUpdate4Bid1120.2DELETE

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

EntryLevelSideSizePriceAction
L2EntryUpdate3Ask5120.5UPDATE

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

EntryLevelSideSizePriceAction
L2EntryUpdate3Ask5120.75UPDATE

Result:

BidAsk
LevelSizePricePriceSize
05120.55120.64
15120.53120.654
22120.5120.73
37120.45120.755
4120.81

Example 9: Delete for level

Package type: Increment

EntryLevelSideSizePriceAction
L2EntryUpdate0Ask4120.6DELETE

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.

BidAsk
LevelSizePricePriceSize
05120.55120.654
15120.53120.73
22120.5120.755
37120.45120.81
4