Strasmore Research
Deep Dives · Matt ConnorBy Matt Connor ·

Why Market Makers Dey Lose Money: Adverse Selection

See why market makers dey lose money from adverse selection, and how markout curves, inventory risk, and queue position costs measure the damage.

Market makers dey lose money from adverse selection: trader wey lift offer often na the person wey don already know say that offer stale. Every fill dey start with small credit, roughly half the spread, and the seconds wey follow decide how much of that credit go remain. The measurement tool na markout, wey be signed price change for fixed horizon after fill. The panels below build real markout curves from trade and quote prints. Our companion piece on how market makers dey make money dey cover the revenue side of the same ledger.

Wetin be adverse selection for trading?

Market maker dey post two prices at once: bid to buy and offer to sell, each for stated size. The gap between dem na the bid ask spread, and the business na arithmetic. Buy for bid, sell for offer, repeat tens of thousands of times for one day, and the average round trip go collect something close to the spread.

That arithmetic assume say both sides go arrive balanced. But dem no balance. Resting quote na standing option wey anybody fit exercise for the moment wey e choose, and traders wey dey pay closest attention dey pick the best moments. Fund wey dey work large parent order go continue to buy while offer dey cheap. System wey don already see correlated instrument move go take the side wey never reprice.

Adverse selection na the name for this imbalance: the fills wey maker receive dey lean toward traders wey want those fills pass. The sign na gap between the spread wey maker quote and the spread wey maker keep.

Wetin be markout for market making?

Markout dey answer one question after every fill: where price dey after fixed number of seconds, measured from the side wey I dey? Suppose maker sell 100 shares for $50.02 while midpoint dey $50.00. That fill start with two cents per share credit, 20 mils for desk units. One mil na one-tenth of one cent. If midpoint move to $50.04 sixty seconds later, the 60 second markout na minus 20 mils. The opening credit don disappear, plus the same amount again.

If you average am across every fill for one window and plot am against the horizon, result na markout curve. The panel below build one for AAPL across three-hour midday window on Thursday, May 14, 2026. Each print dey signed against midpoint of the second wey e print: print above midpoint count as taker buying, so maker dey on sell side.

QueryHow much of the spread dey survive: AAPL markout curve, 1 second to 5 minutes
The exact SQL behind every number
WITH
  qs AS (
    SELECT toUnixTimestamp(toDateTime(sip_timestamp)) AS ts,
           avg((toFloat64(bid_price) + toFloat64(ask_price)) / 2) AS mid
    FROM global_markets.cache_stocks_quotes
    WHERE ticker = 'AAPL'
      AND sip_timestamp >= '2026-05-14 14:00:00'
      AND sip_timestamp <  '2026-05-14 17:06:00'
      AND bid_price > 0 AND ask_price > bid_price
    GROUP BY ts
  ),
  fills AS (
    SELECT t.ts AS ts, t.px AS px, t.shares AS shares, q.mid AS mid_at_fill,
           if(t.px > q.mid, 1, -1) AS taker_side
    FROM (
      SELECT toUnixTimestamp(toDateTime(sip_timestamp)) AS ts,
             toFloat64(price) AS px, toUInt64(size) AS shares
      FROM global_markets.stocks_trades
      WHERE ticker = 'AAPL'
        AND sip_timestamp >= '2026-05-14 14:00:00'
        AND sip_timestamp <  '2026-05-14 17:00:00'
        AND size > 0
    ) AS t
    INNER JOIN qs AS q ON q.ts = t.ts
    WHERE t.px != q.mid AND abs(t.px / q.mid - 1) < 0.02
  )
SELECT
  concat('+', toString(e.secs), 's')                          AS horizon,
  round(1000 * avg(e.taker_side * (e.px - e.mid_at_fill)), 2) AS edge_at_fill_mils,
  round(1000 * avg(e.taker_side * (e.px - f.mid)), 2)         AS edge_after_mils
FROM (
  SELECT toUInt32(ts + secs) AS future_ts, secs, px, mid_at_fill, taker_side
  FROM (SELECT *, arrayJoin([1, 5, 15, 30, 60, 300]) AS secs FROM fills)
) AS e
INNER JOIN qs AS f ON f.ts = e.future_ts
GROUP BY e.secs
ORDER BY e.secs
Run this yourself

The first series na the credit for the exact moment of fill, meaning distance between print and midpoint, averaging 19.66 mils per share. The second series na wetin those same fills worth after midpoint don get time to move: 20.5 mils one second later, and 9.81 mils five minutes later. The distance between both series for any horizon na markout, and e dey come out of the spread wey maker quote.

Curve wey drop quickly and later flatten dey describe maker paying for immediate repricing and keeping the balance. Curve wey continue to slope for the same direction at every horizon dey describe fills wey land for wrong side of price wey continue to move. The first shape na cost of doing business. The second one mean quote need to change.

Which fills be toxic flow?

The word toxic get technical meaning here. Flow toxic when its markout curve dey slope against the maker wey fill am, for every horizon, across large sample. Desks dey sort fills by every label wey dem fit see: print size, venue, order type, counterparty identifier. Size na the label wey public tape carry.

QueryAAPL fills by print size: credit for the fill and value 60 seconds later
The exact SQL behind every number
WITH
  qs AS (
    SELECT toUnixTimestamp(toDateTime(sip_timestamp)) AS ts,
           avg((toFloat64(bid_price) + toFloat64(ask_price)) / 2) AS mid
    FROM global_markets.cache_stocks_quotes
    WHERE ticker = 'AAPL'
      AND sip_timestamp >= '2026-05-14 14:00:00'
      AND sip_timestamp <  '2026-05-14 17:06:00'
      AND bid_price > 0 AND ask_price > bid_price
    GROUP BY ts
  ),
  fills AS (
    SELECT t.ts AS ts, t.px AS px, t.shares AS shares, q.mid AS mid_at_fill,
           if(t.px > q.mid, 1, -1) AS taker_side
    FROM (
      SELECT toUnixTimestamp(toDateTime(sip_timestamp)) AS ts,
             toFloat64(price) AS px, toUInt64(size) AS shares
      FROM global_markets.stocks_trades
      WHERE ticker = 'AAPL'
        AND sip_timestamp >= '2026-05-14 14:00:00'
        AND sip_timestamp <  '2026-05-14 17:00:00'
        AND size > 0
    ) AS t
    INNER JOIN qs AS q ON q.ts = t.ts
    WHERE t.px != q.mid AND abs(t.px / q.mid - 1) < 0.02
  )
SELECT
  multiIf(s.shares < 100, 'under 100 shares',
          s.shares < 500, '100 to 499',
          s.shares < 1000, '500 to 999',
          '1,000 or more')                                    AS size_bucket,
  count()                                                     AS fills_count,
  round(1000 * avg(s.taker_side * (s.px - s.mid_at_fill)), 2) AS edge_at_fill_mils,
  round(1000 * avg(s.taker_side * (s.px - f.mid)), 2)         AS edge_after_60s_mils
FROM (SELECT *, toUInt32(ts + 60) AS future_ts FROM fills) AS s
INNER JOIN qs AS f ON f.ts = s.future_ts
GROUP BY size_bucket
ORDER BY min(s.shares)
Run this yourself

Fills below 100 shares dey start with 19.47 mils per share credit and dey worth 15.22 mils one minute later. Prints of 1,000 shares and above dey start with 36.26 mils and dey worth 21.34 mils for the same horizon. Same instrument, same three hours, different economics per share. Biggest orders often dey avoid public queue altogether, na wetin block trade arrange to do.

Inventory risk, the position wey no fit get hedge on time

Markout horizon na also clock. Maker wey buy 5,000 shares for bid dey hold 5,000 shares, and until e hedge or sell the position, e dey exposed to anything wey price do next. Hedging rarely happen instantly: single stock position fit get offset with correlated ETF or options position, and each one get its own spread to cross. Inventory risk na size of the move wey fit happen inside that window, and dem dey measure am the same way for any name. How far price normally travel for the next minute, and for the next hour?

QueryHow far price dey travel while position dey wait: SPY and NVDA, May 2026
The exact SQL behind every number
WITH
  bars AS (
    SELECT ticker, window_start,
           toDate(toTimeZone(window_start, 'America/New_York')) AS et_date,
           toFloat64(close) AS px
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker IN ('SPY', 'NVDA')
      AND window_start >= '2026-05-01 00:00:00'
      AND window_start <  '2026-06-01 00:00:00'
      AND transactions >= 50
      AND close > 0
  ),
  starts AS (
    SELECT ticker, et_date, px, mins,
           window_start + toIntervalMinute(mins) AS future_start
    FROM (SELECT *, arrayJoin([1, 2, 5, 15, 30, 60]) AS mins FROM bars)
  )
SELECT
  concat(toString(s.mins), ' min')                                AS hold_horizon,
  round(avgIf(abs(f.px / s.px - 1) * 10000, s.ticker = 'SPY'), 1)  AS spy_drift_bps,
  round(avgIf(abs(f.px / s.px - 1) * 10000, s.ticker = 'NVDA'), 1) AS nvda_drift_bps
FROM starts AS s
INNER JOIN bars AS f
  ON f.ticker = s.ticker AND f.et_date = s.et_date AND f.window_start = s.future_start
GROUP BY s.mins
HAVING countIf(s.ticker = 'SPY') > 0 AND countIf(s.ticker = 'NVDA') > 0
ORDER BY s.mins
Run this yourself

Across May 2026, average absolute move for SPY over one minute measure 1.9 basis points. One basis point na one-hundredth of one percent. NVDA measure 5.5 for the same minute. If clock stretch to one hour, the two figures become 14.1 and 40.4. Maker wey fit flatten inside one minute dey manage with the first pair. Maker wey hold for one hour dey manage with the second pair, while the credit collected at fill remain the same either way.

Why market makers dey lose money for the back of the queue

Exchanges dey match resting orders for one price according to when dem arrive: price first, then time. Order for the back of 40,000 share queue at bid go wait for the 40,000 shares ahead of am to clear, and estimating queue position na discipline on its own.

The difficult part na wetin clearing mean. Queue fit empty in two ways. Sellers fit continue to hit bid until the level exhaust, or price fit move down through the level and leave am behind. Order near front go take part for both. Order for back mostly go fill through the second path, just as the level dey become old price. Two makers fit trade same instrument for same price and keep different amount of am. Queue slot no dey show for quoted spread because both makers quote identical price. E dey show for markout curve.

Why widening quote no be free solution

The obvious answer to bad markout curve na to quote wider. Wider quote dey earn more per fill and dey get fewer fills. Both sides of that trade dey inside the same day’s prints, sorted by how far each print land from midpoint.

QueryAAPL prints by distance from midpoint: share of volume and 60 second markout
The exact SQL behind every number
WITH
  qs AS (
    SELECT toUnixTimestamp(toDateTime(sip_timestamp)) AS ts,
           avg((toFloat64(bid_price) + toFloat64(ask_price)) / 2) AS mid
    FROM global_markets.cache_stocks_quotes
    WHERE ticker = 'AAPL'
      AND sip_timestamp >= '2026-05-14 14:00:00'
      AND sip_timestamp <  '2026-05-14 17:06:00'
      AND bid_price > 0 AND ask_price > bid_price
    GROUP BY ts
  ),
  fills AS (
    SELECT t.ts AS ts, t.px AS px, t.shares AS shares, q.mid AS mid_at_fill,
           if(t.px > q.mid, 1, -1) AS taker_side
    FROM (
      SELECT toUnixTimestamp(toDateTime(sip_timestamp)) AS ts,
             toFloat64(price) AS px, toUInt64(size) AS shares
      FROM global_markets.stocks_trades
      WHERE ticker = 'AAPL'
        AND sip_timestamp >= '2026-05-14 14:00:00'
        AND sip_timestamp <  '2026-05-14 17:00:00'
        AND size > 0
    ) AS t
    INNER JOIN qs AS q ON q.ts = t.ts
    WHERE t.px != q.mid AND abs(t.px / q.mid - 1) < 0.02
  )
SELECT
  multiIf(s.cents <= 0.5, '0.5 cents or less',
          s.cents <= 1.0, '0.5 to 1 cent',
          s.cents <= 2.0, '1 to 2 cents',
          'over 2 cents')                                  AS distance_from_mid,
  round(100 * sum(s.shares) / sum(sum(s.shares)) OVER (), 2) AS share_of_volume_pct,
  round(1000 * avg(s.taker_side * (s.px - f.mid)), 2)        AS edge_after_60s_mils
FROM (
  SELECT *, toUInt32(ts + 60) AS future_ts, abs(px - mid_at_fill) * 100 AS cents
  FROM fills
) AS s
INNER JOIN qs AS f ON f.ts = s.future_ts
GROUP BY distance_from_mid
ORDER BY min(s.cents)
Run this yourself

Prints within half a cent of midpoint account for 27.58 percent of shares for the window, and dey worth 0.95 mils per share one minute later. Prints wey land more than two cents away account for 33.19 percent of shares, at 46.86 mils. Quote wey maker post farther from midpoint go get reached by smaller share of the day’s volume.

Fill rate na first cost of stepping back. Exchange rebates na second, because many venues pay maker per share filled, and that income dey scale with shares, no be with quote width. Volume tiers wey set rebate rate dey count monthly, so quiet month go reprice every fill for the next one. Giving up queue slot na last cost, paid on the way out and paid again at the back of the line on the way in. Every maker dey sit somewhere for this curve, and makers wey measure their own markouts most precisely fit stay closest to the touch and still keep the credit. That measurement na the edge wey largest market makers dey defend.

FAQ

Market makers dey lose money for individual trades?

Routinely. Making book na distribution of small gains and small losses, and losing fills dey cluster for moments just before price move. The business dey rest on average across millions of fills.

Wetin be markout for trading?

Markout na change for market price at fixed horizon after fill, signed from the point of view of one side. Positive number mean say that side gain. Equities desks dey quote markouts in mils per share, while options desks dey quote am in ticks per contract.

Wetin toxic order flow mean?

E describe order flow wey markout curve dey persistently slope against the market maker wey fill am. That mean price dey continue to move away after every fill. The label na statistical property of order stream, and e no carry judgment about any single trader.

Why market makers dey widen spreads instead of pulling their quotes?

Wider quote keep maker inside market at price wey dey pay for worse markout curve. Pulling quote no dey earn anything and e surrender queue position together with the quote. Desks dey do both, and the choice depend on how long dem expect quote to stay stale.

How dem dey measure adverse selection?

With markouts. Sign each fill according to the side wey maker dey on, then average midpoint change at fixed horizons afterward. The gap between credit at fill and value at each horizon na adverse selection cost, for the same units as the spread.

:::detailsHow dem build these numbers
Every AAPL panel read two public records: trade tape and quote tape. Each print between 14:00 and 17:00 UTC on May 14, 2026 dey match to average national best bid and offer midpoint for the second wey e print in, then dem sign am. Print above that midpoint count as taker buy, wey put maker on sell side. Prints exactly at midpoint get no side and dem drop am, together with any print more than 2 percent away from midpoint. Quote records where bid meet or cross offer dey exclude, because locked or crossed market no get usable midpoint.

Second-level matching dey approximate quote wey dey active for microsecond of fill, and e smooth the fastest updates. Horizon prices come from the same second-level midpoints. Inventory panel use one-minute bars and keep only bars wey carry at least 50 prints, so sample dey concentrate for busiest hours of the day.
:::


Every panel here carry the SQL wey produce am, so anybody fit inspect the markout math line by line. To rebuild the curve for another name or another day, ask for am in plain English on the Strasmore terminal.