Strasmore Research
Deep Dives · Matt ConnorBy Matt Connor ·

Trade Markout na Wetin? Execution Quality Explain

Trade markout na change for mid price after fill. See the sign convention, adverse selection, and markout curve from one second reach five minutes.

Trade markout na the change wey happen for mid price over fixed time after fill, measured from the exact moment wey that fill print. Positive mean say market move for your side after you trade. Negative mean say e move against you, and na this one desks dey call adverse selection. The horizon wey you choose, whether one second or five minutes, dey determine the question wey the number dey answer.

Trade markout na wetin?

Start with mid price: na the halfway point between best bid and best offer, the midpoint of NBBO. Markout takes that mid for chosen horizon after your fill, subtracts reference price, then e apply sign based on the side wey you trade.

**markout at horizon h = side * (mid at t+h - reference price)**, where side na plus one if you buy and minus one if you sell.

Na the sign convention dey cause confusion most times. If you buy and mid later fall, markout go negative: you buy, price go down, and the person wey sell to you get better result for that trade. Na adverse selection written as number. If you sell and mid later fall, markout go positive because the sign change with the side. Every fill get two sides, and one side markout na the other side own with the sign reversed.

The three reference prices

The reference price wey you subtract dey decide wetin the markout dey measure.

  • The fill price. Markout become money: wetin the position worth at t+h compared with wetin you pay. The spread wey you pay or earn dey inside am.
  • The mid at the moment of the fill. This one remove the spread and isolate how market move after you trade. Na the cleanest way to read adverse selection.
  • The quote on the side you traded. If you measure against the touch wey you take, or the touch wey you post, e show how the fill compare with the price wey actually dey available.

Execution quality work dey use the second one for adverse selection and the first one for profit and loss. Both dey show for the first panel below, using one set of fills.

The markout curve and wetin the shape mean

Run the same statistic across many horizons and you go get curve. The shape fit tell you more than any single number.

  • Curve wey drop sharply inside the first second and remain down dey describe flow wey market don pick off: price move against the fill almost immediately.
  • Curve wey continue to fall for minutes dey show flow wey carry real information about where price dey go.
  • Flat curve near zero dey describe benign flow. After five minutes, mid still look like how e look when the fill happen.
  • Curve wey start negative and climb back toward zero na the shape passive maker wey dey collect rebates dey look for. The early dip na the spread plus immediate impact, while the recovery na market coming back.

Choosing horizon na itself decision about wetin you dey measure. Below one second, markout mostly dey read latency and queue position. If you stretch am to one minute, e dey read the short-term information inside the flow. Past five minutes, normal market drift fit start to overwhelm the fill itself.

Real markout curve, fill by fill

The panel below takes every INTC print from the June 10, 2026 regular session, classify each one as buy initiated or sell initiated against the last quote of the second before e print, then average the signed markout at 5 horizons. Two series dey use the same fills: one measured from the mid at fill, and one from the fill price.

QueryINTC markout curve, June 10 2026, measure from two reference bases
The exact SQL behind every number
WITH
    mid_by_second AS
    (
        SELECT
            dateDiff('second', toDateTime('2026-06-10 13:30:00', 'UTC'), sip_timestamp) AS sec,
            argMax((toFloat64(bid_price) + toFloat64(ask_price)) / 2, sip_timestamp)    AS mid
        FROM global_markets.cache_stocks_quotes
        WHERE ticker = 'INTC'
          AND sip_timestamp >= toDateTime('2026-06-10 13:30:00', 'UTC')
          AND sip_timestamp <  toDateTime('2026-06-10 20:00:00', 'UTC')
          AND bid_price > 0
          AND ask_price > bid_price
        GROUP BY sec
    ),
    signed_fills AS
    (
        SELECT
            t.sec        AS sec,
            t.fill_price AS fill_price,
            q.mid        AS ref_mid,
            if(t.fill_price > q.mid, 1, -1) AS side
        FROM
        (
            SELECT
                dateDiff('second', toDateTime('2026-06-10 13:30:00', 'UTC'), sip_timestamp) AS sec,
                sec - 1          AS ref_sec,
                toFloat64(price) AS fill_price
            FROM global_markets.stocks_trades
            WHERE ticker = 'INTC'
              AND sip_timestamp >= toDateTime('2026-06-10 13:30:01', 'UTC')
              AND sip_timestamp <  toDateTime('2026-06-10 19:55:00', 'UTC')
              AND price > 0
              AND size > 0
        ) AS t
        INNER JOIN mid_by_second AS q ON q.sec = t.ref_sec
        WHERE t.fill_price != q.mid
    )
SELECT
    multiIf(f.horizon_s < 60,
            concat(toString(f.horizon_s), ' sec'),
            concat(toString(intDiv(f.horizon_s, 60)), ' min'))              AS horizon,
    round(avg(f.side * (fut.mid - f.ref_mid) / f.ref_mid) * 10000, 3)       AS mid_basis_bps,
    round(avg(f.side * (fut.mid - f.fill_price) / f.fill_price) * 10000, 3) AS fill_basis_bps,
    count()                                                                 AS fill_count
FROM
(
    SELECT
        sec,
        fill_price,
        ref_mid,
        side,
        horizon_s,
        sec + horizon_s AS future_sec
    FROM signed_fills
    ARRAY JOIN [1, 5, 15, 60, 300] AS horizon_s
) AS f
INNER JOIN mid_by_second AS fut ON fut.sec = f.future_sec
GROUP BY f.horizon_s
ORDER BY f.horizon_s
Run this yourself

Across 900593 classified fills, the mid basis series read 3.068 basis points at 1 sec and 2.977 basis points at 5 min. Basis point na one hundredth of one percent of the price, so one fill fit move by rounding error, but a million shares fit mean real money.

The fill basis series open at -0.488 basis points. The gap between the two series at the shortest horizon na effective half spread: how far the print land from the mid. Taker pay that one on entry, while maker earn am. Everything after the first horizon na market movement.

Fill size and the shape of the curve

Adverse selection no dey affect all fills the same way. If you split the same session by print size, small fills and blocks of one thousand shares or more go dey for the same axes.

QueryThe same curve, split by print size: small fills dey face blocks
The exact SQL behind every number
WITH
    mid_by_second AS
    (
        SELECT
            dateDiff('second', toDateTime('2026-06-10 13:30:00', 'UTC'), sip_timestamp) AS sec,
            argMax((toFloat64(bid_price) + toFloat64(ask_price)) / 2, sip_timestamp)    AS mid
        FROM global_markets.cache_stocks_quotes
        WHERE ticker = 'INTC'
          AND sip_timestamp >= toDateTime('2026-06-10 13:30:00', 'UTC')
          AND sip_timestamp <  toDateTime('2026-06-10 20:00:00', 'UTC')
          AND bid_price > 0
          AND ask_price > bid_price
        GROUP BY sec
    ),
    signed_fills AS
    (
        SELECT
            t.sec        AS sec,
            t.fill_size  AS fill_size,
            q.mid        AS ref_mid,
            if(t.fill_price > q.mid, 1, -1) AS side
        FROM
        (
            SELECT
                dateDiff('second', toDateTime('2026-06-10 13:30:00', 'UTC'), sip_timestamp) AS sec,
                sec - 1          AS ref_sec,
                toFloat64(price) AS fill_price,
                size             AS fill_size
            FROM global_markets.stocks_trades
            WHERE ticker = 'INTC'
              AND sip_timestamp >= toDateTime('2026-06-10 13:30:01', 'UTC')
              AND sip_timestamp <  toDateTime('2026-06-10 19:55:00', 'UTC')
              AND price > 0
              AND size > 0
        ) AS t
        INNER JOIN mid_by_second AS q ON q.sec = t.ref_sec
        WHERE t.fill_price != q.mid
    )
SELECT
    multiIf(f.horizon_s < 60,
            concat(toString(f.horizon_s), ' sec'),
            concat(toString(intDiv(f.horizon_s, 60)), ' min'))                                   AS horizon,
    round(avgIf(f.side * (fut.mid - f.ref_mid) / f.ref_mid, f.fill_size < 1000) * 10000, 3)       AS small_fill_bps,
    round(avgIf(f.side * (fut.mid - f.ref_mid) / f.ref_mid, f.fill_size >= 1000) * 10000, 3)      AS block_fill_bps,
    countIf(f.fill_size >= 1000)                                                                 AS block_fill_count
FROM
(
    SELECT
        sec,
        fill_size,
        ref_mid,
        side,
        horizon_s,
        sec + horizon_s AS future_sec
    FROM signed_fills
    ARRAY JOIN [1, 5, 15, 60, 300] AS horizon_s
) AS f
INNER JOIN mid_by_second AS fut ON fut.sec = f.future_sec
GROUP BY f.horizon_s
HAVING countIf(f.fill_size < 1000) > 0
   AND countIf(f.fill_size >= 1000) > 0
ORDER BY f.horizon_s
Run this yourself

At 5 min horizon, prints below one thousand shares average 2.983 basis points, while 11588 prints of one thousand shares or more average 2.495. Two quotes wey fill the same total volume against those two groups no go finish the day for the same place.

How markouts become the spread numbers for execution reports

Markout na the missing term inside standard spread decomposition. Effective spread na wetin taker actually pay: twice the signed distance from mid to print. Split am into two. One part na realized spread, wetin maker keep after market don move. The other part na adverse selection, wey be mid basis markout multiplied by two. The identity exact: effective spread = realized spread + adverse selection.

QueryEffective spread split into realized spread and adverse selection, by half hour
The exact SQL behind every number
WITH
    mid_by_second AS
    (
        SELECT
            dateDiff('second', toDateTime('2026-06-10 13:30:00', 'UTC'), sip_timestamp) AS sec,
            argMax((toFloat64(bid_price) + toFloat64(ask_price)) / 2, sip_timestamp)    AS mid
        FROM global_markets.cache_stocks_quotes
        WHERE ticker = 'INTC'
          AND sip_timestamp >= toDateTime('2026-06-10 13:30:00', 'UTC')
          AND sip_timestamp <  toDateTime('2026-06-10 20:00:00', 'UTC')
          AND bid_price > 0
          AND ask_price > bid_price
        GROUP BY sec
    ),
    signed_fills AS
    (
        SELECT
            t.sec + 60   AS future_sec,
            t.et_time    AS et_time,
            t.fill_price AS fill_price,
            q.mid        AS ref_mid,
            if(t.fill_price > q.mid, 1, -1) AS side
        FROM
        (
            SELECT
                dateDiff('second', toDateTime('2026-06-10 13:30:00', 'UTC'), sip_timestamp) AS sec,
                sec - 1          AS ref_sec,
                toFloat64(price) AS fill_price,
                formatDateTime(toStartOfInterval(toTimeZone(sip_timestamp, 'America/New_York'), toIntervalMinute(30)), '%H:%i') AS et_time
            FROM global_markets.stocks_trades
            WHERE ticker = 'INTC'
              AND sip_timestamp >= toDateTime('2026-06-10 13:30:01', 'UTC')
              AND sip_timestamp <  toDateTime('2026-06-10 19:55:00', 'UTC')
              AND price > 0
              AND size > 0
        ) AS t
        INNER JOIN mid_by_second AS q ON q.sec = t.ref_sec
        WHERE t.fill_price != q.mid
    )
SELECT
    f.et_time                                                                  AS et_time,
    round(avg(2 * f.side * (f.fill_price - f.ref_mid) / f.ref_mid) * 10000, 3) AS effective_spread_bps,
    round(avg(2 * f.side * (f.fill_price - fut.mid) / f.ref_mid) * 10000, 3)   AS realized_spread_bps,
    round(avg(2 * f.side * (fut.mid - f.ref_mid) / f.ref_mid) * 10000, 3)      AS adverse_selection_bps,
    count()                                                                    AS fill_count
FROM signed_fills AS f
INNER JOIN mid_by_second AS fut ON fut.sec = f.future_sec
GROUP BY f.et_time
ORDER BY f.et_time
Run this yourself

For the half hour wey start at 09:30 Eastern, effective spread average 10.651 basis points. Out of this, 8.313 na adverse selection term at a sixty-second horizon, while 2.338 na wetin maker retain. The panel cover 13 half-hour buckets.

Na these components dem dey summarize venue by venue for Rule 605 and 606 execution reports, and na the arithmetic behind price improvement claims wey dey rely on the sub-penny rule and price improvement. Rebate na fixed credit per share. Adverse selection na variable cost per share on fills wey quote actually receive. To weigh one against the other over measured curve, instead of headline rate card, na the check behind maker-taker fees and rebates, and e dey alongside the mechanics for why market makers lose money.

How dem compute these numbers

Every panel dey use INTC quotes and prints from the June 10, 2026 regular session, 09:30 to 16:00 Eastern. Quotes collapse to one mid per second, using the last one printed inside that second. Print classify as buy initiated when e land above the mid of the previous second, and sell initiated when e land below am. Na the standard quote rule. Prints wey sit exactly on that mid dem drop instead of guessing, so midpoint executions no enter the sample. Using the previous second mid as reference stop the reference from peeking forward. Horizons na whole seconds counted from the second of the print, and fill enter a horizon only when quote dey for the target second.

FAQ

Wetin be markout for trading?

Markout na the signed change for mid price over fixed horizon after fill. Positive mean say market move for the direction of your trade after you get filled. Negative mean say e move the other way, and na the definition of adverse selection.

Negative markout always mean say fill bad?

No. Negative markout at one second on a marketable order mostly na the spread wey the order pay, which na the price of getting execution immediately. The more important thing na whether the curve continue to fall at longer horizons or flatten out.

Wetin be the difference between markout and slippage?

Slippage compare fill with decision price or arrival price, so e grade the whole path of an order. Markout compare fill with mid at a horizon after fill, so e grade wetin happen next. One score the parent order; the other score the individual fill.

Which horizon markout suppose use?

E depend on wetin the fill dey for. Sub-second horizons grade latency-sensitive execution, while one minute na common default for adverse selection on liquid names. Publishing the horizon beside the number na wetin make two markouts comparable at all.

How markout relate to effective spread?

Effective spread split into realized spread plus adverse selection component, and that component na mid basis markout multiplied by two. If horizon change, the split between the two go change too. Na why published spread statistic no complete unless e show the horizon.


Every panel here come with the SQL wey produce am. Open one, change the ticker or date, and the same markout curve go rebuild from scratch on the Strasmore terminal.

#execution quality#markouts#adverse selection#transaction cost analysis#market microstructure