MBO, MBP and BBO: order book data, level by level

Market data comes in levels, and every vendor names them differently: L1, L2 and L3, BBO, MBP and MBO, top of book, depth of market, market by order. The content behind the names is the same. This page says what each level holds, which kind of backtest needs which, and why only one of them tells you where an order stands in its queue.

The levels, side by side

LevelAlso calledWhat it holdsWhat a backtest getsManifold-BT
TradesTape, time and sales, tick by tickEvery print: price, size, time, and which side was the aggressorFills in the order prices printed; order flowingest_trades
L1BBO, top of book (NBBO when consolidated across US venues)The best bid and the best ask, with their sizesThe spread a fill really paidattach_quotes
L2MBP, market by price, depth of market (MBP-10: ten levels)The total size resting at each price, a number of levels deepDepth and imbalance signals; a queue estimated from the displayed sizeingest_book
L3MBO, market by order (Nasdaq TotalView-ITCH)Every order: when it entered, each execution, cancellation and replacementThe exact place of an order in its queueingest_mbo

The difference is easiest to see on one real instant. This is the BTCUSDT perpetual book on Bybit, rebuilt from the free archive, as an L1 feed sends it and as L2 shows it:

quote-saver.bycsi.com/orderbook/linear/BTCUSDT/2025-06-02_BTCUSDT_ob500.data.zip
L1, best bid and offer
Ask105,571.5 × 5.052
Bid105,571.4 × 0.586

Two prices and two sizes. The spread is one tick, 0.1 USDT.

L2, the same instant, 10 levels a side
105,573.60.124
105,573.50.007
105,573.40.076
105,572.70.002
105,572.60.001
105,572.40.005
105,572.20.005
105,572.11.153
105,572.00.065
105,571.55.052
105,571.40.586
105,570.00.002
105,569.60.002
105,569.20.001
105,568.50.002
105,567.60.050
105,567.50.002
105,566.60.076
105,566.40.002
105,566.00.098
Bybit's BTCUSDT perpetual book at 2025-06-02 00:15:00 UTC, rebuilt from the public archive: its snapshot, then every update up to that instant. Sizes in BTC. L1 is the top row of each side of L2; the archive keeps 500 levels a side that day, and these are the first 10. Neither says which orders a level is made of, or in what order they arrived: that is L3.

Which level does a backtest need?

It depends on what the strategy does with the market, not on how fast it trades. A strategy that reads closes and holds for hours needs bars, and nothing on this page. One that crosses the spread many times a day needs L1, because a backtest that fills at the close rather than at the bid or the ask hides the cost that decides whether it pays.

A strategy that reads the book, the size waiting on each side or how it leans over the top five levels, needs L2: in Manifold-BT that is bt.book.imbalance(5) or bt.book.bid_size(3) in the same expression language as any other signal.

A strategy that rests passive orders, a market maker or a limit entry, needs the queue. A resting order is filled because everything ahead of it at its price went first, not because the price touched it. L2 lets a backtest estimate that queue; L3 lets it read it.

Why L2 can only estimate a queue

A level-2 book shows each level's total size. When the size at your price drops from ten to eight, the book does not say whether the two left ahead of you, which moves you up, or behind you, which does not. So on L2 Manifold-BT places a new order behind the whole depth shown at its level when it is posted, lets prints at that price work through it, and takes a rate of cancellations ahead as a declared assumption. When the book shows nothing at that level, the queue is unknown: the run uses a declared fallback or refuses by name, and never reads it as empty, which would put the order at the front of a queue nobody saw.

schematic: one price level, four resting orders
The level, order by order. The front of the queue fills first.
A 2.0
B 5.0
You
C 2.0
front10.0 at your price
Then the level shows 8.0, with no trade. On L2, two stories fit:
A 2.0
B 5.0
You
C 2.0
A cancelled: 5.0 ahead of you
A 2.0
B 5.0
You
C 2.0
C cancelled: 7.0 ahead of you
On L3, the feed says which: a cancel of order C.
A 2.0
B 5.0
You
C 2.0
7.0 ahead of you, read from the data
A schematic, not data. The level falls from 10.0 to 8.0 and nothing traded, so an order cancelled. On L2 both stories fit, and a backtest has to assume one: the cautious rule, risk_adverse, puts every unseen cancellation behind you, while power and log split it by a probability that depends on where you stand. On L3 the feed names the order.

On L3 the guess goes away. Every order has an identifier, so an execution or a cancellation of an order ahead takes its size out of the volume ahead, and an execution of an order behind yours means the market went through your place and yours was filled.

A maker that reads its place in the queue

The strategy below joins the best bid and ask with a round lot each and stands aside on a side where more than 20,000 shares wait ahead of it. On a day stored order by order, queue_ahead is that size exactly. The full run, on the Nasdaq sample day of 9 December 2025, is example 32 in the repository.

queue_aware_maker.py
import manifoldbt as bt
from manifoldbt import book

# A day of Nasdaq TotalView-ITCH 5.0, stored order by order (L3)
store = bt.ingest_mbo("S120925-v50.txt.gz", {"AAPL": 1}, "2025-12-09")

# Join the best bid and ask, but stand aside behind a crowded queue.
# On this market, queue_ahead is the size ahead of the quote exactly.
inventory = bt.position()
book_ok = (book.bid_levels() > 0) & (book.ask_levels() > 0)
crowded_bid = bt.queue_ahead("bid") > 20_000
crowded_ask = bt.queue_ahead("ask") > 20_000

strategy = (
    bt.Strategy.create("queue_aware_maker")
    .quote("buy", bt.when(book_ok, book.bid_price_at(1)), 100, tif="GTX",
           enabled=(inventory < 500) & ~crowded_bid)
    .quote("sell", bt.when(book_ok, book.ask_price_at(1)), 100, tif="GTX",
           enabled=(inventory > -500) & ~crowded_ask)
)

Reading the tape, the book and order-by-order days into the engine is part of the Researcher plan.

Where each level comes from

For crypto, trades and L2 are free: Bybit publishes its book by price level, up to 500 levels deep, and its trades, one file per day, and Binance publishes its spot trades. L1 can be read off that same book, so it costs nothing more. The archives, their sizes and how far back they go are listed in free crypto tick data.

The free crypto archives stop at L2: none of them is order by order. For L3, Nasdaq publishes TotalView-ITCH 5.0 sample days, a whole session of every order on its book, which is what the example above reads; Databento sells MBO and MBP-10 for CME futures and US equities.

Frequently asked questions

What is the difference between MBO and MBP data?

MBP, market by price, gives the total size resting at each price level. MBO, market by order, gives every order separately, with an identifier, so you can follow one order from the moment it enters the book to its execution or cancellation. MBP tells you how much is waiting at a price; MBO tells you who is waiting, and in what order.

Is L2 data enough to backtest a market maker?

It is enough to estimate the queue, not to read it. A level-2 book shows a level shrinking, never whose order left: when the size at your price drops, it may be a fill ahead of you or a cancellation behind you. A backtest on L2 has to assume how much of the queue cancels. On L3 data the place of each order is in the data.

Where can I get L3 order book data?

Nasdaq publishes TotalView-ITCH 5.0 sample days, a full day of every order on its book: the file Manifold-BT's order-by-order example runs on is 7.9 GB for one session. Databento sells MBO for CME and US equities. The free crypto archives are not order by order: Bybit publishes its book by price level, which is L2.

What is BBO data?

The best bid and offer: the highest price someone will buy at, the lowest price someone will sell at, and the size at each. It is level 1. In the US, the NBBO is the national best bid and offer consolidated across exchanges. For a backtest, the BBO is what tells you the spread a fill paid instead of assuming one.

Keep reading

Run your first backtest

Install Manifold-BT and reproduce the backtest above in seconds. The Rust core runs years of bars sub-second so you can sweep parameters instead of waiting.

$pip install manifoldbt