Strasmore Research
Deep Dives · Matt ConnorBy Matt Connor ·

How to estimate queue position from L2 data

Queue position dey determine if your passive order go earn spread. See how to calculate am using aggregated book data and when you suppose switch to MBO instead.

Why queue position na the main edge

Passive order dey wait for inside book make another person cross the spread come meet am. Under price-time priority, the matching engine dey rank orders based on price first, then time wey the order reach. If 10,000 shares dey bid for $10.00 and you add 100 join behind, those 10,000 shares must trade or cancel before your own turn go reach. If you cancel and re-enter for the same price, you go go back go join the end of the line.

Na this ranking decide the economics. If you dey near front, you go trade often and collect the spread plus any add-liquidity rebate, wey be the topic of maker-taker fees and rebates. If you dey near back, you go only trade after everybody wey dey front don trade finish; this one dey happen when heavy flow come one side. Fills for back dey cluster for the moments before the price walk through your level. Same price, same order, but different result.

Wetin Level 2 data fit and no fit show

Level 1 na the best bid and offer with the size for each. Level 2, wey dem still dey call market by price, add depth: e be list of price levels with the total size wey dey rest for each one. Both of dem na aggregates, and Level 1 vs Level 2 market data compare dem well. When the bid for $10.00 drop from 10,000 shares go 8,500, the feed go report say 1,500 shares don comot. E no go talk whether dem trade, whether na one order comot or forty, or where for the line dem bin dey.

Executions na the part wey you fit see: the tape dey print every trade with a size, so you fit subtract those ones exactly. The rest na cancellation, and na for cancellation the guessing dey start. The panel wey dey down here count messages for the consolidated top of book against prints for the tape for one liquid stock for one normal session, Wednesday June 10, 2026.

QueryTop of book messages versus prints for AAPL, June 10 2026
The exact SQL behind every number
SELECT
    q.et_time                            AS et_time,
    q.quote_updates                      AS quote_updates,
    t.trades                             AS trades,
    round(q.quote_updates / t.trades, 1) AS updates_per_trade
FROM
(
    SELECT
        formatDateTime(toStartOfHour(toTimeZone(sip_timestamp, 'America/New_York')), '%H:%i') AS et_time,
        count() AS quote_updates
    FROM global_markets.cache_stocks_quotes
    WHERE ticker = 'AAPL'
      AND sip_timestamp >= '2026-06-10 12:00:00'
      AND sip_timestamp <  '2026-06-10 20:00:00'
    GROUP BY et_time
) AS q
INNER JOIN
(
    SELECT
        formatDateTime(toStartOfHour(toTimeZone(sip_timestamp, 'America/New_York')), '%H:%i') AS trade_hour,
        count() AS trades
    FROM global_markets.stocks_trades
    WHERE ticker = 'AAPL'
      AND sip_timestamp >= '2026-06-10 12:00:00'
      AND sip_timestamp <  '2026-06-10 20:00:00'
    GROUP BY trade_hour
) AS t ON q.et_time = t.trade_hour
ORDER BY et_time
Run this yourself

The 08:00 hour dey before the opening bell: the top of book change 0.8 times per print for there, messages wey no reach the number of prints. Inside the regular session, the ratio change. For the 15:00 hour, the top of book change 1.6 times per print, across 233433 messages for that hour alone. Most of the things wey dey happen to a price level for open market na orders wey dey arrive and leave without trading, and each of those events dey move your place for line by an amount wey no aggregated feed dey show.

The uniform cancellation assumption, and where e dey deceive you

The standard way wey people dey calculate am assume say cancellations dey spread evenly through the queue. Make we call x your fractional depth: the shares wey dey ahead of you divided by the total wey dey rest for your price. The uniform model set the chance say any cancelled share dey ahead of you equal to x, wey be p(x) = x. If you sit halfway for a 10,000 share queue, and you watch 1,000 shares cancel without any print, the model go move you 500 places forward.

Real queues no dey like that. An order wey don rest for a while fit belong to person wey ready to wait, but an order wey dem add just now fit be fleeting quote wey go disappear within seconds. Cancellations dey concentrate towards the back of the line, near you and behind you. Out of those 1,000 cancelled shares, maybe 200 dey ahead of you, but the model go give you credit for 500. If you repeat that all day, the simulated queue go move faster pass the real one. The error dey go one way: more fills, for better moments, pass wetin live orders dey collect.

How the queue dey drain

Cancellations dey move you without you seeing am. Trades dey move you visibly, based on the size of the prints.

QueryHow AAPL prints dey break down by trade size, June 10 2026
The exact SQL behind every number
SELECT
    multiIf(size < 100,  'under 100 shares',
            size = 100,  'exactly 100 shares',
            size <= 499, '101 to 499 shares',
            size <= 999, '500 to 999 shares',
                         '1000 shares or more')             AS trade_size_group,
    round(100 * count() / sum(count()) OVER (), 1)          AS share_of_prints_pct,
    round(100 * sum(size) / sum(sum(size)) OVER (), 1)      AS share_of_shares_pct
FROM global_markets.stocks_trades
WHERE ticker = 'AAPL'
  AND sip_timestamp >= '2026-06-10 14:00:00'
  AND sip_timestamp <  '2026-06-10 20:00:00'
GROUP BY trade_size_group
ORDER BY min(size)
Run this yourself

Prints wey dey below a round lot na 90% of the count for this session and 42.5% of the shares. Blocks of a thousand shares or more na 0.2% of prints and 19.9% of the volume. Queues dey drain small-small, so the gap between position 2,000 and position 3,500 na hundreds of prints of waiting. A simulator wey dey give fill after two large trades don skip most of the tape.

The order wey you no fit see

Depth feeds dey truncate. If you receive ten price levels and your order rest for the eleventh, your order dey outside the data: you no know the size ahead of am, and you no know the orders wey dey join behind am. For that point, you dey invent the number instead of estimating am. This situation na normal thing, because an order wey no dey chase the market dey fall away from the inside quickly.

QueryThe best bid and all the price levels wey e touch, AAPL, 15 minute buckets
The exact SQL behind every number
SELECT
    formatDateTime(toStartOfFifteenMinutes(toTimeZone(sip_timestamp, 'America/New_York')), '%H:%i') AS et_time,
    round(avg(toFloat64(bid_price)), 2) AS best_bid,
    uniqExact(bid_price)                AS bid_levels_touched
FROM global_markets.cache_stocks_quotes
WHERE ticker = 'AAPL'
  AND sip_timestamp >= '2026-06-10 14:00:00'
  AND sip_timestamp <  '2026-06-10 20:00:00'
  AND bid_price > 0
GROUP BY et_time
ORDER BY et_time
Run this yourself

For the 10:00 bucket, the best bid average $290.29 and visit 187 different prices inside fifteen minutes. Each of those prices na separate level for a stock wey dem quote for pennies, so a ten-level view cover ten cents of book. An order wey rest for one price through a session like this fit spend long time below the deepest level wey the owner fit see.

Where queue position dey worth the most

Queue position dey worth the most where price improvement no possible. A stock wey dem pin for a one-cent spread no get room to jump the line with a better price: everybody dey stack for the same tick, and arrival time decide the rest. Where the spread wide reach several cents, a trader fit step in front of the whole queue for one penny, and the price decision go override the line.

QueryAverage quoted spread and time wey dem spend for one cent spread, midday June 10 2026
The exact SQL behind every number
SELECT
    ticker                                                AS symbol,
    round(avg(toFloat64(ask_price - bid_price)) * 100, 2) AS avg_spread_cents,
    round(100 * countIf(round(toFloat64(ask_price - bid_price) * 100) <= 1) / count(), 1) AS one_cent_pct
FROM global_markets.cache_stocks_quotes
WHERE ticker IN ('SPY', 'AAPL', 'KO', 'NVDA', 'MSFT', 'BKNG')
  AND sip_timestamp >= '2026-06-10 15:00:00'
  AND sip_timestamp <  '2026-06-10 19:00:00'
  AND bid_price > 0
  AND ask_price > bid_price
GROUP BY ticker
ORDER BY avg_spread_cents
Run this yourself

Across four midday hours of the same session, the tightest of the six names, KO, average 1.18 cents wide and show a one-cent spread on 82.5% of its updates. The widest, MSFT, average 8.46 cents, with a one-cent spread on 1.1% of updates. A queue model wey dem calibrate on the first name no get any useful info for the second one. Orders wey rest between the quotes, wey dem cover for midpoint peg orders, dey form their own line under the same rules.

Four diagnostics for your own backtest

  1. Compare the simulated fill rate with your live fill rate for the same names over the same hours. A simulator wey dey fill 70% of the orders wey fill 40% of the time live dey describe your assumption, no be the market.
  2. Split simulated fills into two piles: those where the price level survive afterwards, and those wey happen only as the level clear completely. A pile wey weight to the second kind mean say the simulator dey give you fills right as the price trade through you.
  3. Bracket the assumption. Rerun with every cancellation taken from the front of the queue, then with every cancellation taken from the back. Those two runs na the honest error bars around any p(x) wey you choose.
  4. Measure how often your price sit outside the depth wey your feed carry. Fills for there na invented. Look-ahead bias in backtesting describe the same failure from another angle: a result wey rest on information wey the strategy never get.

When to stop modelling and buy the order data

Market-by-order data (MBO) carry a message for every individual order, from the moment e arrive to the moment e execute or cancel, each one get its own identifier. Replay that feed and your place for line go be count instead of estimate, once you assume a realistic delay for your own order to reach the venue. E cost pass depth feed and e dey take more space to store.

Diagnostic 3 na the decision rule. If the front and back brackets both make the strategy profitable, a middle assumption go do. If the strategy make money under one bracket and lose under the other, the queue model na the strategy, and buying the order-by-order feed dey cheaper pass to defend a guess.

Pro-rata venues dey change the question

Some futures and options markets dey split an incoming order across the resting orders for a price in proportion to size instead of by arrival order. Time no matter reach like that for there, and quoted size become the lever: if you double your size, your share of each fill go roughly double. The failure mode move with am, towards quoting more size pass wetin you want own. Why options orders do not get filled cover how that one dey look from the retail side of an options book.

FAQ

Wetin be queue position for trading?

Queue position na your rank among the orders wey dey rest for the same price on the same venue. On a price-time priority book, the orders wey dey ahead of you must trade or cancel before your own fit trade.

You fit calculate queue position from Level 2 data?

No be exactly. Level 2 show total size per price level without order identity, so when size disappear without a print, you no fit tell whether those shares sit ahead of you or behind you. You fit estimate the number and bracket the estimate. The exact figure need order-by-order data.

Why backtested fill rates usually dey too high?

The common assumption dey spread cancellations evenly across the queue, while real cancellations dey concentrate towards the back. An even model dey advance your simulated order faster pass the real line, wey dey show as more fills, and better ones, pass wetin live trading dey deliver.

Wetin be MBO data?

Market-by-order data give every individual order its own messages, from arrival through execution or cancel. Na the feed where your place for line dey countable instead of modelled, wey matter most for strategies wey dey live for a one-tick spread.

Queue position dey matter for pro-rata venues?

E no matter reach. Pro-rata allocation dey split an incoming order across resting orders by size, so to arrive first no dey help you much and quoted size dey do the work. To check which allocation model a venue dey use dey come before any queue modelling.


Every panel for here carry the SQL wey produce am. Change the ticker, move the date, and ask the same question about the names wey you dey trade on the Strasmore terminal.