Strasmore Research
Deep Dives · Matt ConnorBy Matt Connor ·

MBO vs MBP Order Book Data Explained

MBO vs MBP order book data: message-by-order event streams versus aggregated depth per price level, what each can answer, and what each costs to run.

MBO vs MBP order book data is one large distinction hiding behind one small label. Two vendors can both sell you something called "Level 2": MBP (market by price) sends the total size resting at each price level, while MBO (market by order) sends every individual order as its own event with its own ID. One is a summary of the book; the other is the ledger the book is built from, and it costs orders of magnitude more traffic to carry.

What MBP and MBO order book data actually contain

MBP, market by price, is aggregated depth. Each update names a side, a price level, the total displayed size resting there, and sometimes the number of orders behind it. A product sold as MBP-10 gives you the ten best price levels on each side, the ladder in a trading platform and the staircase in every depth chart.

MBO, market by order, is an event stream. Each message names one order: its ID, side, price, displayed size, and what just happened to it. Nothing is pre-aggregated. If forty orders rest at the same price, forty separate messages put them there, and you hold all forty in memory to know what that level totals.

Our Level 1 vs Level 2 market data guide covers what the retail tier labels mean at a broker. MBO and MBP are the precise names for what is inside the box when a vendor says "Level 2", and the schema name is the one worth asking about.

The message actions an MBO feed carries

An MBO feed is a taxonomy of actions applied to order IDs. Four carry most of the traffic:

  • Add: a new order joins the book at a price with a new ID.
  • Modify: an existing ID changes price or size. Raising size or moving price sends the order to the back of the queue at the new level; reducing size normally keeps its place.
  • Cancel: an ID leaves the book, in whole or in part.
  • Trade or fill: an aggressive order executes against one or more resting IDs, shrinking or removing them.

MBP carries none of that vocabulary. An MBP update is a statement about a level: this price now holds this much size. Whether the size left through a cancel or through a fill, the update looks identical. The panel below shows that limit on the thinnest possible book, the consolidated top of book, one price level per side and MBP-1 in this naming.

QueryWhat changed between consecutive top-of-book messages (AAPL, 10:00 to 10:30 a.m. ET, June 16, 2026)
The exact SQL behind every number
WITH
    ordered AS
    (
        SELECT
            row_number() OVER (ORDER BY sip_timestamp, sequence_number) AS msg_index,
            bid_price,
            bid_size,
            lagInFrame(bid_price) OVER (ORDER BY sip_timestamp, sequence_number) AS prev_bid_price,
            lagInFrame(bid_size)  OVER (ORDER BY sip_timestamp, sequence_number) AS prev_bid_size
        FROM global_markets.cache_stocks_quotes
        WHERE ticker = 'AAPL'
          AND sip_timestamp >= '2026-06-16 14:00:00'
          AND sip_timestamp <  '2026-06-16 14:30:00'
          AND bid_price > 0
    ),
    classified AS
    (
        SELECT multiIf(
            bid_price != prev_bid_price, 'best bid price changed',
            bid_size  >  prev_bid_size,  'size joined at the best bid',
            bid_size  <  prev_bid_size,  'size left the best bid',
            'bid untouched, ask side updated') AS message_type
        FROM ordered
        WHERE msg_index > 1
    )
SELECT
    message_type,
    count()                                        AS message_count,
    round(100 * count() / sum(count()) OVER (), 1) AS share_pct
FROM classified
GROUP BY message_type
ORDER BY indexOf(['best bid price changed', 'size joined at the best bid', 'size left the best bid', 'bid untouched, ask side updated'], message_type)
Run this yourself

Over that half hour, 17.6% of messages moved the best bid to a different price, 17.4% added size at an unchanged bid, 12% removed size at an unchanged bid, and 53% left the bid alone while the other side moved. Each group is a net statement about a price level. None names an order, and no arithmetic recovers the ID that is missing.

What each schema can and cannot answer

MBP-10 answers the questions phrased as "how much size was there": the shape of the ladder, book imbalance, liquidity resting near the mid, the depth chart itself. Aggregated levels are all any of those need.

MBO answers the questions phrased as "what happened to this order": how much size sat ahead of yours when you joined, how long orders survive before they cancel, and the probability that a passive order at the touch trades before the price moves away. Those quantities do not exist in aggregated form, and summing orders into a level total destroys the sequence that defined them.

Queue position is the sharpest example, and it only carries meaning under price-time priority versus pro-rata matching, where arrival order decides who trades first. What makes it matter is print size: a level is filled by many small executions, not one large one.

QueryTrade size mix in the same window (AAPL, 10:00 to 10:30 a.m. ET, June 16, 2026)
The exact SQL behind every number
SELECT
    multiIf(size < 100,  '1 to 99 shares',
            size < 200,  '100 to 199 shares',
            size < 500,  '200 to 499 shares',
            size < 1000, '500 to 999 shares',
            '1000 or more shares')                 AS trade_size_bucket,
    count()                                        AS trade_count,
    round(100 * count() / sum(count()) OVER (), 1) AS share_pct,
    round(avg(size))                               AS avg_shares
FROM global_markets.stocks_trades
WHERE ticker = 'AAPL'
  AND sip_timestamp >= '2026-06-16 14:00:00'
  AND sip_timestamp <  '2026-06-16 14:30:00'
  AND size > 0
GROUP BY trade_size_bucket
ORDER BY min(size)
Run this yourself

Prints below 100 shares, an odd lot, made up 92.3% of trades in that window, and the largest bucket present, 1000 or more shares, made up 0.1%. Feed prints that size to a level displaying 4,000 shares and an order that joined the queue last can sit through dozens of executions without trading. MBP shows you the 4,000. MBO shows you the line.

Neither schema shows hidden size. An iceberg order displays a small tip and refreshes with a new ID each time the tip fills, so the reserve never appears in any message.

Rebuilding a book from MBO is a state machine

An MBP feed hands you the answer. An MBO feed hands you the inputs and expects you to be exactly right:

  1. Start from a snapshot, or from an empty book plus the venue's clear message.
  2. Apply every add, modify, cancel and fill in strict sequence order, keyed by order ID.
  3. Maintain a second index by price level, since that is what your strategy reads.
  4. Watch the sequence numbers, and re-sync from a fresh snapshot whenever one is missing.

The failure mode is quiet. Drop one cancel and a phantom order sits in your book for the rest of the session, inflating that level, with no exception raised anywhere. MBP degrades far more gently: each update restates a level's total, so a corrupted value gets overwritten within a few messages.

MBO also lives only on direct venue feeds, one book per exchange, which means running and merging several of them. The consolidated tape is a summary by construction, a split covered in SIP versus direct exchange feeds.

What the extra detail costs in bandwidth

Message counts are the honest way to price the difference. The panel below counts consolidated top-of-book messages against actual prints over the same pinned half hour for five household names.

QueryTop-of-book messages against prints, 10:00 to 10:30 a.m. ET, June 16, 2026
The exact SQL behind every number
WITH
    quote_load AS
    (
        SELECT ticker, count() AS quote_messages
        FROM global_markets.cache_stocks_quotes
        WHERE ticker IN ('SPY', 'AAPL', 'NVDA', 'MSFT', 'KO')
          AND sip_timestamp >= '2026-06-16 14:00:00'
          AND sip_timestamp <  '2026-06-16 14:30:00'
        GROUP BY ticker
    ),
    trade_load AS
    (
        SELECT ticker, count() AS trades
        FROM global_markets.stocks_trades
        WHERE ticker IN ('SPY', 'AAPL', 'NVDA', 'MSFT', 'KO')
          AND sip_timestamp >= '2026-06-16 14:00:00'
          AND sip_timestamp <  '2026-06-16 14:30:00'
        GROUP BY ticker
    )
SELECT
    q.ticker                              AS ticker,
    round(q.quote_messages / 1000, 1)     AS quote_messages_thousands,
    round(t.trades / 1000, 2)             AS trades_thousands,
    round(q.quote_messages / t.trades, 1) AS quotes_per_trade_ratio
FROM quote_load AS q
INNER JOIN trade_load AS t ON t.ticker = q.ticker
ORDER BY quotes_per_trade_ratio DESC
Run this yourself

SPY carried the heaviest quote traffic per print, 9.6 messages for every trade and 510 thousand messages in thirty minutes. The spread across the five names is wide: at the bottom of the panel, MSFT ran 0.7 quote messages per print, fewer than one message for every trade. Remember what that column counts: one price level per side, on a feed that has already collapsed every venue into a single best bid and offer. A ten-level depth product multiplies it, and a per-order feed multiplies it again, since each order behind each level on each venue generates its own add, its own modifies, and its own cancel, whether or not it ever trades. The same arithmetic shows up at larger scale in the size of the options quote feed.

Which order book feed does a strategy need?

Most of the work runs on MBP-10. Depth charts, imbalance features, liquidity-at-price measurement, execution cost models, and nearly every research question about how much size was resting where are answerable from aggregated levels, at a fraction of the message volume.

MBO is the requirement when the answer depends on a specific order: queue position, order lifetime, cancel behaviour, passive fill probability at the touch. A strategy that lives or dies on whether it is 200 shares or 20,000 shares deep in the line cannot get there from aggregated data, and it pays for that in licence fees, bandwidth, storage, and the engineering to keep a reconstructed book correct all day.

How these panels were built
  • The feed behind every panel is the consolidated top of book, one price level per side, plus the trade tape. It is not a depth feed and not a per-order feed, so these panels illustrate the message-volume argument rather than sampling MBO itself.
  • Windows are pinned to a fixed past date, 10:00 to 10:30 a.m. ET on June 16, 2026, stored as 14:00 to 14:30 UTC. Pinned windows keep the numbers stable across regenerations.
  • The classification panel labels each message against the previous one in sequence. It cannot separate a cancel from a fill, the exact limitation this post describes.

FAQ

What is the difference between MBO and MBP market data?

MBP, market by price, aggregates displayed size at each price level and sends one update per level. MBO, market by order, sends every individual order with its own ID, along with the add, modify, cancel and fill events that happen to it.

Is Level 2 data the same as MBO data?

Usually not. "Level 2" at a retail broker almost always means aggregated depth, so MBP with five to twenty price levels. A few vendors market a per-order feed under the same tier name, so the schema name, not the tier name, settles what arrives on the wire.

How much bigger is an MBO feed than an MBP feed?

Orders of magnitude, varying by venue and by symbol. The consolidated top of book alone ran at 9.6 messages per print for the busiest name in the panel above. A per-order feed adds every add, modify and cancel behind every level on every venue, including the large majority of orders that never trade.

Can you rebuild an MBP book from MBO data?

Yes, and that is the normal pipeline: apply each order event to a book keyed by order ID, then publish the level totals. The reverse is impossible: once orders are summed into a level total, the individual IDs and their arrival order are gone.


Every panel here ships with the exact SQL beneath it. To count the same messages on a different symbol or session, ask the question in plain English on the Strasmore terminal.