{"slug":"do-stock-gaps-get-filled","qid":"fill_by_volume","label":"Gap fill rate by how heavy the gap day's volume was","post_title":"Do Stock Gaps Always Get Filled? The Data","post_url":"/blog/do-stock-gaps-get-filled#q-fill_by_volume","columns":["volume_regime","gap_days","same_session_fill_pct","within_20_sessions_fill_pct"],"rows":[{"volume_regime":"Under 1.5x normal","gap_days":1666,"same_session_fill_pct":35.9,"within_20_sessions_fill_pct":83.7},{"volume_regime":"1.5x to 3x normal","gap_days":376,"same_session_fill_pct":30.1,"within_20_sessions_fill_pct":76.1},{"volume_regime":"3x or more","gap_days":59,"same_session_fill_pct":15.3,"within_20_sessions_fill_pct":45.8}],"shape":"ranking","sql":"WITH sessions AS\n(\n    SELECT\n        ticker,\n        toDate(toTimeZone(window_start, 'America/New_York')) AS d,\n        argMin(toFloat64(open), window_start)                AS session_open,\n        argMax(toFloat64(close), window_start)               AS session_close,\n        toFloat64(max(high))                                 AS session_high,\n        toFloat64(min(low))                                  AS session_low,\n        sum(toFloat64(volume))                               AS session_volume\n    FROM global_markets.delayed_stocks_minute_aggs\n    WHERE ticker IN ('AAPL', 'MSFT', 'NVDA', 'AMZN', 'JPM', 'KO', 'WMT', 'XOM')\n      AND window_start >= '2021-01-01'\n      AND window_start <  '2026-07-01'\n      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60\n           + toMinute(toTimeZone(window_start, 'America/New_York'))) >= 570\n      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60\n           + toMinute(toTimeZone(window_start, 'America/New_York'))) < 960\n    GROUP BY ticker, d\n),\npaths AS\n(\n    SELECT\n        d,\n        session_open,\n        session_high,\n        session_low,\n        session_volume,\n        lagInFrame(session_close) OVER (PARTITION BY ticker ORDER BY d ASC\n            ROWS BETWEEN 1 PRECEDING AND CURRENT ROW)                                                         AS prior_close,\n        avg(session_volume) OVER (PARTITION BY ticker ORDER BY d ASC ROWS BETWEEN 21 PRECEDING AND 2 PRECEDING) AS normal_volume,\n        min(session_low)  OVER (PARTITION BY ticker ORDER BY d ASC ROWS BETWEEN CURRENT ROW AND 19 FOLLOWING) AS low_20,\n        max(session_high) OVER (PARTITION BY ticker ORDER BY d ASC ROWS BETWEEN CURRENT ROW AND 19 FOLLOWING) AS high_20\n    FROM sessions\n),\nmeasured AS\n(\n    SELECT\n        session_volume / normal_volume AS volume_ratio,\n        toUInt8(if(session_open > prior_close, session_low <= prior_close, session_high >= prior_close)) AS filled_same_session,\n        toUInt8(if(session_open > prior_close, low_20 <= prior_close, high_20 >= prior_close))           AS filled_20_sessions\n    FROM paths\n    WHERE prior_close > 0\n      AND normal_volume > 0\n      AND d >= toDate('2021-03-01')\n      AND d <= toDate('2026-02-28')\n      AND abs(100 * (session_open / prior_close - 1)) >= 1\n)\nSELECT\n    multiIf(volume_ratio < 1.5, 'Under 1.5x normal',\n            volume_ratio < 3,   '1.5x to 3x normal',\n                                '3x or more')        AS volume_regime,\n    count()                                          AS gap_days,\n    round(100 * avg(filled_same_session), 1)         AS same_session_fill_pct,\n    round(100 * avg(filled_20_sessions), 1)          AS within_20_sessions_fill_pct\nFROM measured\nWHERE isFinite(volume_ratio)\nGROUP BY volume_regime\nORDER BY min(volume_ratio) ASC","computed_at":"2026-08-06T05:38:12.289286+00:00","elapsed":0.005822059}