{"slug":"why-market-makers-lose-money","qid":"markout_curve","label":"How much of the spread survives: AAPL markout curve, 1 second to 5 minutes","post_title":"Why Market Makers Lose Money: Adverse Selection","post_url":"/blog/why-market-makers-lose-money#q-markout_curve","columns":["horizon","edge_at_fill_mils","edge_after_mils"],"rows":[{"horizon":"+1s","edge_at_fill_mils":19.66,"edge_after_mils":20.5},{"horizon":"+5s","edge_at_fill_mils":19.66,"edge_after_mils":20.46},{"horizon":"+15s","edge_at_fill_mils":19.72,"edge_after_mils":19.5},{"horizon":"+30s","edge_at_fill_mils":19.78,"edge_after_mils":19.53},{"horizon":"+60s","edge_at_fill_mils":19.69,"edge_after_mils":15.8},{"horizon":"+300s","edge_at_fill_mils":19.68,"edge_after_mils":9.81}],"shape":"ranking","sql":"WITH\n  qs AS (\n    SELECT toUnixTimestamp(toDateTime(sip_timestamp)) AS ts,\n           avg((toFloat64(bid_price) + toFloat64(ask_price)) / 2) AS mid\n    FROM global_markets.cache_stocks_quotes\n    WHERE ticker = 'AAPL'\n      AND sip_timestamp >= '2026-05-14 14:00:00'\n      AND sip_timestamp <  '2026-05-14 17:06:00'\n      AND bid_price > 0 AND ask_price > bid_price\n    GROUP BY ts\n  ),\n  fills AS (\n    SELECT t.ts AS ts, t.px AS px, t.shares AS shares, q.mid AS mid_at_fill,\n           if(t.px > q.mid, 1, -1) AS taker_side\n    FROM (\n      SELECT toUnixTimestamp(toDateTime(sip_timestamp)) AS ts,\n             toFloat64(price) AS px, toUInt64(size) AS shares\n      FROM global_markets.stocks_trades\n      WHERE ticker = 'AAPL'\n        AND sip_timestamp >= '2026-05-14 14:00:00'\n        AND sip_timestamp <  '2026-05-14 17:00:00'\n        AND size > 0\n    ) AS t\n    INNER JOIN qs AS q ON q.ts = t.ts\n    WHERE t.px != q.mid AND abs(t.px / q.mid - 1) < 0.02\n  )\nSELECT\n  concat('+', toString(e.secs), 's')                          AS horizon,\n  round(1000 * avg(e.taker_side * (e.px - e.mid_at_fill)), 2) AS edge_at_fill_mils,\n  round(1000 * avg(e.taker_side * (e.px - f.mid)), 2)         AS edge_after_mils\nFROM (\n  SELECT toUInt32(ts + secs) AS future_ts, secs, px, mid_at_fill, taker_side\n  FROM (SELECT *, arrayJoin([1, 5, 15, 30, 60, 300]) AS secs FROM fills)\n) AS e\nINNER JOIN qs AS f ON f.ts = e.future_ts\nGROUP BY e.secs\nORDER BY e.secs","computed_at":"2026-08-13T16:08:10.851084+00:00","elapsed":0.731299799}