Howwise RSS
Trading Software7 min read

L2 vs L3 Market Data: MBP vs MBO Explained for Quants

Short answer

L2 vs L3 market data differs in granularity: L2 (market by price) aggregates size per price level, while L3 (market by order) streams every individual order by ID. L3 can be aggregated into L2, but L2 can never be turned back into L3.

Key takeaways
  • L2 (MBP) reports total size and order count per price; L3 (MBO) reports each order add, modify, cancel and execute with an order ID.
  • L3 to L2 is lossless aggregation; L2 to L3 is not invertible because order IDs, individual sizes and queue priority are gone.
  • Exact queue position for passive-fill backtests needs MBO data; with MBP you can only apply a probabilistic queue model.
  • Rebuild any book by applying a snapshot, then sequenced deltas, buffering during snapshot fetch and resyncing on any gap.
  • Throttled L2 snapshots (100ms or 1s) miss intra-interval changes and must not be treated as tick-by-tick.

What is the difference between L2 and L3 market data?

L1 is top of book: best bid, best ask, their sizes, and last trade. L2 is market by price (MBP): for each price level you get aggregate size and usually an order count. L3 is market by order (MBO): every individual resting order, identified by a unique order ID, with its side, price and size.

L3 is not 'L2 plus cancels'. It is the raw event stream an exchange matching engine produces: add, modify or replace, cancel, execute (partial or full, tied to an order ID) and delete. L2 is what you get after summing that stream by price. The aggregation throws information away.

Vendor labels are inconsistent. Nasdaq 'Level 2' is a depth-of-book product of limited scope, while TotalView-ITCH is order-level and so is L3-like. Some crypto venues call a 20-level snapshot 'L3'. Always read the message spec: if there is an order ID on each message, it is MBO; if the key is price, it is MBP.

LevelAlso calledKeyContains
L1Top of book, BBONoneBest bid/ask, sizes, last trade
L2MBP, depthPrice levelAggregate size and order count per price, N levels
L3MBO, full order bookOrder IDEvery add, modify, cancel, execute with order ID

Why can't you rebuild L3 from L2?

Take a bid level at 100.00 with three orders in queue order: A=200 at the front, your own order Y=100, then B=200 behind you. L2 shows size 500, count 3. Now the level drops to 300 with count 2.

Either A or B cancelled, because both are 200-lot orders. If A left, you are at the front and fill on the next trade. If B left, you still have 200 ahead of you. L2 shows the identical 500 to 300 change with the same count in both cases, so the queue outcome is invisible.

Trades make it worse. Now use the level in the table below (A=100, B=200, C=200). A 100-lot market sell removes A from the front and drops the level to 400, and a cancel of A produces the same L2 delta. Only the trade feed can disambiguate, and only if you align it exactly with the book update.

Event on level 100.00 (A=100,B=200,C=200)L2 deltaQueue ahead of your new order
A cancels500 to 400, count 3 to 2B+C = 400, same as before for new joiners
C cancels500 to 300, count 3 to 2A+B = 300
Sell of 100 executes against A500 to 400, count 3 to 2B+C = 400
B cancels, then 100 added by D500 to 400 net, count unchangedA, C, D order; B's slot gone
Warning: Simultaneous events inside one L2 update (a cancel and an add at the same price) net out. Hidden and iceberg liquidity adds further ambiguity. Any trade-versus-cancel split from L2 is an inference, not data.

Is L2 delivered as snapshots or incremental updates?

Both exist, depending on venue. Many crypto WebSocket channels send throttled snapshots (for example every 100ms or 1s) of the top N levels. Professional feeds such as CME MDP 3.0 send a snapshot on a recovery channel, then incremental messages that set or change a level with a sequence number.

A throttled snapshot is a sampled view. Everything that happened between samples (a level appearing and vanishing, a burst of cancels) is invisible. Treat it as bars of book state, not ticks.

Incremental L2 usually carries 'set size at price P to X' (absolute) or a signed change (relative). Check which: applying an absolute update as a delta corrupts the book silently.

StyleExampleGap detectionRisk
Throttled snapshotTop-N every 100ms or 1sTimestamps onlyMisses intra-interval events
Snapshot + absolute deltasBinance diff depthUpdate IDs U and uGaps need full resync
Snapshot + sequenced deltasCME MDP 3.0, ITCH-styleSequence numberPacket loss needs recovery feed

Which feed gives which level? (Databento, Nasdaq, CME, Coinbase, Binance)

Many crypto venues expose only L2. Equities and futures exchanges commonly offer order-level feeds, often as a premium product.

FeedLevelNotes
Databento mboL3Every order event with order ID; reconstruct book yourself
Databento mbp-1 / mbp-10L2Book updates with top 1 or 10 levels; tbbo is BBO at each trade; trades is prints only
Nasdaq TotalView-ITCHL3-likeAdd, cancel, delete, execute, replace messages keyed by order reference
CME MDP 3.0L2 and L3Aggregated book (default 10 levels) plus a Market by Order channel on supported products
CoinbaseBoth'level2' channel is aggregated; 'full' channel is order-level
BinanceL2 onlyDiff depth stream with update IDs; no order IDs

How do I rebuild an L2 and L3 book correctly?

The pattern is the same everywhere: get a snapshot, apply later deltas in order, detect gaps, resync. For Binance diff depth, open the stream first and buffer events, fetch the REST depth snapshot (it has lastUpdateId), drop buffered events with u <= lastUpdateId, and the first applied event must satisfy U <= lastUpdateId+1 <= u. After that each event's U must equal the previous u + 1, otherwise resync.

For MBO, keep a dict from order ID to (side, price, size) plus price-level aggregates. An execute reduces remaining size, a replace may change price or size and often loses queue priority, a delete removes the order.

  1. Subscribe to the delta stream and buffer messages.
  2. Fetch the snapshot and record its sequence or update ID.
  3. Discard buffered messages older than the snapshot.
  4. Apply the rest in strict sequence; on any gap, discard the book and restart.
  5. Only trust the book once the first valid message is applied.
from collections import defaultdict

class L2Book:
    def __init__(self):
        self.bids, self.asks = {}, {}
    def set_level(self, side, px, qty):  # absolute size
        book = self.bids if side == 'B' else self.asks
        if qty == 0: book.pop(px, None)
        else: book[px] = qty

class L3Book:
    def __init__(self):
        self.orders = {}  # oid -> [side, px, qty]
        self.levels = {'B': defaultdict(int), 'A': defaultdict(int)}
    def add(self, oid, side, px, qty):
        self.orders[oid] = [side, px, qty]
        self.levels[side][px] += qty
    def reduce(self, oid, qty):  # partial execute or cancel
        side, px, rem = self.orders[oid]
        qty = min(qty, rem)
        self.levels[side][px] -= qty
        self.orders[oid][2] -= qty
        if self.orders[oid][2] == 0: self.delete(oid)
    def delete(self, oid):
        side, px, rem = self.orders.pop(oid)
        self.levels[side][px] -= rem
        if self.levels[side][px] <= 0: del self.levels[side][px]
    def replace(self, oid, new_oid, px, qty):
        side = self.orders[oid][0]
        self.delete(oid)
        self.add(new_oid, side, px, qty)  # new priority
    def to_mbp(self):  # lossless L3 -> L2
        return {s: dict(l) for s, l in self.levels.items()}
Tip: L3 to L2 is a one-way function (to_mbp above). Store MBO if you can afford it and derive MBP on demand.

Why does queue position need L3 for backtests?

Under FIFO price-time priority, a resting limit order fills only after every order ahead of it is executed or cancelled. With MBO you know exactly how many shares are ahead at your arrival, and each cancel ahead of you moves you forward by that order's remaining size. Cancels behind you do nothing.

With MBP you only see the level total. Common approximations assume cancels are uniformly distributed across the queue (advance by size times your fraction ahead) or all cancels happen at the back (conservative) or the front (optimistic). These bracket the truth but change fill rates materially.

A frequent mistake is assuming your passive order sits at the front when a level trades through. Another is ignoring that a modify which increases size usually loses priority on most venues, while a size decrease often keeps it. Rules differ per exchange, so encode them per venue.

SignalL2 (MBP)L3 (MBO)
Exact queue aheadNo, modelledYes
Cancel vs trade attributionInferred with trade feedExplicit
Order lifetimes, cancel-to-trade ratioNoYes
Modify keeps or loses priorityNoYes
Iceberg and layering patternsWeakDetectable
Depth, imbalance, liquidityYesYes
Data volumeSmallerOften several times larger or more

Which should I choose, and what mistakes should I avoid?

Use L2 for depth, spreads, liquidity and imbalance features, and for strategies that cross the spread. Use L3 for passive fill simulation, market-making research, and order-behaviour studies. MBO has far more messages because most are cancels and modifies far from the touch, so expect much higher storage and processing cost and usually higher vendor pricing. Benchmark on one day before committing.

Avoid these errors: treating throttled snapshots as tick data; using L2 to backtest passive fills at queue front; mixing exchange timestamps with receive timestamps; and joining the trade feed to the book without aligning on sequence numbers, which makes trades appear before or after the book change that caused them.

Common questions

What is the difference between market by price and market by order?

Market by price aggregates resting size and order count at each price level. Market by order lists every individual order with its ID, so you can follow each order from add to cancel or fill.

Can you reconstruct L3 from an L2 order book?

No. L2 discards order IDs, individual sizes and time priority, and many different order sets sum to the same level total. You can only build a model-based estimate of queue position, not the real order list.

Is Level 2 market data a snapshot or incremental?

Both exist. Many crypto channels send throttled snapshots, while exchange feeds typically send a snapshot followed by sequenced incremental updates per price level. Check each venue's spec.

Is Nasdaq TotalView Level 3?

TotalView-ITCH is order-level, with add, cancel, execute and replace messages keyed by order reference, so it behaves like L3 or MBO data. The 'Level 2' and 'Level 3' labels are marketing terms and do not map cleanly to MBP and MBO.

Does Binance provide L3 data?

No. Binance's depth stream is L2 only, sending diff updates with first and last update IDs per price level and no order IDs.

Related topics:market-dataorder-booklevel-3databento