Strasmore Research
Deep Dives · Matt ConnorBy Matt Connor · · Updated 2026-08-08

Do Stock Gaps Always Fill? Na Wetin Data Show

Stock gaps no dey always fill for any fixed horizon. See same-session, 5, 20 and 60 session fill rates for large caps, plus the SQL behind am.

Do stock gaps always get filled? No be inside any horizon wey short enough to trade, and the claim mostly survive because nobody dey state one. Gap na the distance between today open and yesterday regular-session close. Fill na the moment price trade back through that previous close. The whole argument depend on how long person dey ready to wait.

Wetín count as gap, and wetín count as fill

Gap dey measure open against previous close. Yesterday session end for one price, today open for another, and the difference na the gap. E no be distance from yesterday intraday high or low. That one na different and much easier target. When person swap one for the other, “gap” quietly become something wey fit close before lunch on any normal day. Small gaps dey show for almost every session. Trading for other time zones, index changes, single-name news, and ex-dividend adjustments all fit move fair price while US market dey shut. Why stocks gap overnight explain where that overnight repricing dey come from.

Fill na the first moment wey stock trade back through the previous closing price. That definition no complete unless person add deadline. For unlimited horizon, liquid stock go cross almost any level person name at some point. That make “gaps always fill” impossible to disprove, instead of making am true. Every number below get stated horizon.

One sample run through the whole page: eight large-cap US stocks, gap days from January 2021 reach February 2026, with fills tested against regular-session one-minute highs and lows. Gaps below 0.25% no enter because of rounding.

Do stock gaps always get filled the same day?

Start with size. Group every gap according to how large e be, then ask whether price touch the previous close again before that same session end.

QuerySame-session gap fill rate by gap size, eight large caps, 2021 to 2026
The exact SQL behind every number
WITH sessions AS
(
    SELECT
        ticker,
        toDate(toTimeZone(window_start, 'America/New_York')) AS d,
        argMin(toFloat64(open), window_start)                AS session_open,
        argMax(toFloat64(close), window_start)               AS session_close,
        toFloat64(max(high))                                 AS session_high,
        toFloat64(min(low))                                  AS session_low
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker IN ('AAPL', 'MSFT', 'NVDA', 'AMZN', 'JPM', 'KO', 'WMT', 'XOM')
      AND window_start >= '2021-01-01'
      AND window_start <  '2026-07-01'
      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
           + toMinute(toTimeZone(window_start, 'America/New_York'))) >= 570
      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
           + toMinute(toTimeZone(window_start, 'America/New_York'))) < 960
    GROUP BY ticker, d
),
gapped AS
(
    SELECT
        d,
        session_open,
        session_high,
        session_low,
        lagInFrame(session_close) OVER (PARTITION BY ticker ORDER BY d ASC
            ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS prior_close
    FROM sessions
),
measured AS
(
    SELECT
        abs(100 * (session_open / prior_close - 1)) AS gap_pct,
        toUInt8(if(session_open > prior_close,
                   session_low  <= prior_close,
                   session_high >= prior_close))    AS filled_same_session
    FROM gapped
    WHERE prior_close > 0
      AND d <= toDate('2026-02-28')
      AND abs(100 * (session_open / prior_close - 1)) >= 0.25
)
SELECT
    multiIf(gap_pct < 0.5, '0.25 to 0.5%',
            gap_pct < 1,   '0.5 to 1%',
            gap_pct < 2,   '1 to 2%',
            gap_pct < 4,   '2 to 4%',
                           '4% or more')         AS gap_bucket,
    count()                                      AS gap_days,
    round(100 * avg(filled_same_session), 1)     AS same_session_fill_pct
FROM measured
GROUP BY gap_bucket
ORDER BY min(gap_pct) ASC
Run this yourself

The smallest gaps for the sample, 0.25% to 0.5%, close that same session 74.6% of the time across 2381 occurrences. Gaps of 4% or more close that same session 14.8% of the time. The downward slope for the chart carry most of the lesson: the smaller the gap, the more often stock dey move back across am during the day. Another way to talk am be say small gaps na noise, and noise dey reverse.

How long gap dey take to fill?

When you extend the deadline, fill rate fit only rise. Longer window add fills and no remove any. The question na how much of the total arrive late enough to become useless.

QueryShare of 1%+ gaps filled, by how long you wait
The exact SQL behind every number
WITH sessions AS
(
    SELECT
        ticker,
        toDate(toTimeZone(window_start, 'America/New_York')) AS d,
        argMin(toFloat64(open), window_start)                AS session_open,
        argMax(toFloat64(close), window_start)               AS session_close,
        toFloat64(max(high))                                 AS session_high,
        toFloat64(min(low))                                  AS session_low
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker IN ('AAPL', 'MSFT', 'NVDA', 'AMZN', 'JPM', 'KO', 'WMT', 'XOM')
      AND window_start >= '2021-01-01'
      AND window_start <  '2026-07-01'
      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
           + toMinute(toTimeZone(window_start, 'America/New_York'))) >= 570
      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
           + toMinute(toTimeZone(window_start, 'America/New_York'))) < 960
    GROUP BY ticker, d
),
paths AS
(
    SELECT
        d,
        session_open,
        session_high,
        session_low,
        lagInFrame(session_close) OVER (PARTITION BY ticker ORDER BY d ASC
            ROWS BETWEEN 1 PRECEDING AND CURRENT ROW)                                                          AS prior_close,
        min(session_low)  OVER (PARTITION BY ticker ORDER BY d ASC ROWS BETWEEN CURRENT ROW AND 4 FOLLOWING)   AS low_5,
        max(session_high) OVER (PARTITION BY ticker ORDER BY d ASC ROWS BETWEEN CURRENT ROW AND 4 FOLLOWING)   AS high_5,
        min(session_low)  OVER (PARTITION BY ticker ORDER BY d ASC ROWS BETWEEN CURRENT ROW AND 19 FOLLOWING)  AS low_20,
        max(session_high) OVER (PARTITION BY ticker ORDER BY d ASC ROWS BETWEEN CURRENT ROW AND 19 FOLLOWING)  AS high_20,
        min(session_low)  OVER (PARTITION BY ticker ORDER BY d ASC ROWS BETWEEN CURRENT ROW AND 59 FOLLOWING)  AS low_60,
        max(session_high) OVER (PARTITION BY ticker ORDER BY d ASC ROWS BETWEEN CURRENT ROW AND 59 FOLLOWING)  AS high_60
    FROM sessions
),
gaps AS
(
    SELECT
        session_open > prior_close AS gap_up,
        prior_close,
        session_low,
        session_high,
        low_5,
        high_5,
        low_20,
        high_20,
        low_60,
        high_60
    FROM paths
    WHERE prior_close > 0
      AND d <= toDate('2026-02-28')
      AND abs(100 * (session_open / prior_close - 1)) >= 1
),
flags AS
(
    SELECT arrayJoin([
        (1, '1 session',   toUInt8(if(gap_up, session_low <= prior_close, session_high >= prior_close))),
        (2, '5 sessions',  toUInt8(if(gap_up, low_5  <= prior_close, high_5  >= prior_close))),
        (3, '20 sessions', toUInt8(if(gap_up, low_20 <= prior_close, high_20 >= prior_close))),
        (4, '60 sessions', toUInt8(if(gap_up, low_60 <= prior_close, high_60 >= prior_close)))
    ]) AS f
    FROM gaps
)
SELECT
    tupleElement(f, 2)                        AS horizon,
    count()                                   AS gaps_measured,
    round(100 * avg(tupleElement(f, 3)), 1)   AS filled_pct
FROM flags
GROUP BY horizon
ORDER BY min(tupleElement(f, 1)) ASC
Run this yourself

Across 2191 gaps of 1% or more, 34% fill inside the same session and 88.7% don fill within 60 sessions. Na the second figure people usually quote, while dem leave out the horizon. Sixty sessions na roughly three trading months. That one long hold for position wey depend on one-day dislocation. Gap wey fill on day 47, after stock first travel far in the opposite direction, still count as filled for that statistic.

Noise gaps and information gaps na two populations

Gap on quiet day with normal volume na small pricing adjustment. Gap on earnings news or takeover approach carry change in wetin company worth, and no old price remain for am to revert to. Volume separate the two fairly well. When market dey reprice, the whole market want trade prints many times above normal turnover.

QueryGap fill rate by how heavy the gap day's volume was
The exact SQL behind every number
WITH sessions AS
(
    SELECT
        ticker,
        toDate(toTimeZone(window_start, 'America/New_York')) AS d,
        argMin(toFloat64(open), window_start)                AS session_open,
        argMax(toFloat64(close), window_start)               AS session_close,
        toFloat64(max(high))                                 AS session_high,
        toFloat64(min(low))                                  AS session_low,
        sum(toFloat64(volume))                               AS session_volume
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker IN ('AAPL', 'MSFT', 'NVDA', 'AMZN', 'JPM', 'KO', 'WMT', 'XOM')
      AND window_start >= '2021-01-01'
      AND window_start <  '2026-07-01'
      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
           + toMinute(toTimeZone(window_start, 'America/New_York'))) >= 570
      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
           + toMinute(toTimeZone(window_start, 'America/New_York'))) < 960
    GROUP BY ticker, d
),
paths AS
(
    SELECT
        d,
        session_open,
        session_high,
        session_low,
        session_volume,
        lagInFrame(session_close) OVER (PARTITION BY ticker ORDER BY d ASC
            ROWS BETWEEN 1 PRECEDING AND CURRENT ROW)                                                         AS prior_close,
        avg(session_volume) OVER (PARTITION BY ticker ORDER BY d ASC ROWS BETWEEN 21 PRECEDING AND 2 PRECEDING) AS normal_volume,
        min(session_low)  OVER (PARTITION BY ticker ORDER BY d ASC ROWS BETWEEN CURRENT ROW AND 19 FOLLOWING) AS low_20,
        max(session_high) OVER (PARTITION BY ticker ORDER BY d ASC ROWS BETWEEN CURRENT ROW AND 19 FOLLOWING) AS high_20
    FROM sessions
),
measured AS
(
    SELECT
        session_volume / normal_volume AS volume_ratio,
        toUInt8(if(session_open > prior_close, session_low <= prior_close, session_high >= prior_close)) AS filled_same_session,
        toUInt8(if(session_open > prior_close, low_20 <= prior_close, high_20 >= prior_close))           AS filled_20_sessions
    FROM paths
    WHERE prior_close > 0
      AND normal_volume > 0
      AND d >= toDate('2021-03-01')
      AND d <= toDate('2026-02-28')
      AND abs(100 * (session_open / prior_close - 1)) >= 1
)
SELECT
    multiIf(volume_ratio < 1.5, 'Under 1.5x normal',
            volume_ratio < 3,   '1.5x to 3x normal',
                                '3x or more')        AS volume_regime,
    count()                                          AS gap_days,
    round(100 * avg(filled_same_session), 1)         AS same_session_fill_pct,
    round(100 * avg(filled_20_sessions), 1)          AS within_20_sessions_fill_pct
FROM measured
WHERE isFinite(volume_ratio)
GROUP BY volume_regime
ORDER BY min(volume_ratio) ASC
Run this yourself

Gaps of 1% or more on volume below 1.5 times the stock normal turnover fill the same session 35.9% of the time. Same-size gaps on three times normal volume or more fill the same session 15.3% of the time, and 45.8% within 20 sessions, across 59 of dem. Na for the heavy-volume group earnings gaps and deal gaps dey sit. If you average the two groups into one number, e no describe either group well.

Why the first minutes of the session dey distort gap statistics

Fill wey get record for 9:31 a.m. dey fragile. Opening price come from an auction, one batched cross wey set one price for every order queued overnight. Our opening auction explainer go through the mechanics. For the minutes after that cross, quoted spreads dey widest for the day, and one small print fit register high or low wey no real size ever trade at. Spreads widen at the open explain why quote dey thin for that time.

The chart below show the pattern for the basket own tape: average one-minute high-low range in basis points (one basis point na one hundredth of a percent), by fifteen-minute clock bucket, through 2025.

QueryAverage one-minute range and volume by time of day, 2025
The exact SQL behind every number
SELECT
    formatDateTime(toStartOfInterval(toTimeZone(window_start, 'America/New_York'), INTERVAL 15 MINUTE), '%H:%i') AS et_time,
    round(10000 * avg(toFloat64(high) / toFloat64(low) - 1), 1) AS avg_range_bps,
    round(avg(toFloat64(volume)) / 1000, 1)                     AS avg_volume_k
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker IN ('AAPL', 'MSFT', 'NVDA', 'AMZN', 'JPM', 'KO', 'WMT', 'XOM')
  AND window_start >= '2025-01-01'
  AND window_start <  '2026-01-01'
  AND toFloat64(low) > 0
  AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
       + toMinute(toTimeZone(window_start, 'America/New_York'))) >= 570
  AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
       + toMinute(toTimeZone(window_start, 'America/New_York'))) < 960
GROUP BY et_time
ORDER BY et_time ASC
Run this yourself

The opening bucket average 24.4 bps of range per minute against 6.9 bps for the 12:30 bucket, on 303.9 thousand shares per minute versus 69.6 thousand. Fill wey get timestamp inside that first bucket dey rely on the least reliable prices of the day. The same-session figures above include those minutes, so dem bias the figures small upward. That caveat apply to every same-day fill statistic, including this one.

Wetín to check before you treat gap as trade

  1. The horizon. Fill rate wey no get deadline no be statistic.
  2. The population. Check whether news gaps separate from quiet ones, and how dem draw that line.
  3. The measurement. Check whether fills test against real traded ranges, and whether the first minutes of the session enter.
  4. The survivors. Check whether sample hold only stocks wey still trade today. That one fit quietly delete gaps wey never fill.

The honest version of the claim narrower than the slogan. Small gaps on normal volume dey revert often and quickly. Large gaps on heavy volume na repricing, and dem often remain for months. For live view of names wey dey gap, the biggest stock movers this week page track the current week. how markets recover from crashes apply the same horizon discipline to index drawdowns.

FAQ

Do stock gaps always get filled?

No. For the sample here, 88.7% of gaps of 1% or more trade back through the previous close within 60 sessions. That leave a real share of dem unfilled after about three trading months. The “always” version only work when person attach no time limit. Once that happen, e stop to be testable.

How long e dey take for gap to fill?

E depend on the gap. Here, 34% of gaps of 1% or more fill inside the same session, while the remaining fills arrive across the following weeks and months. Small gaps concentrate for the same-day bucket, while heavy-volume gaps dey slowest.

Wetin be the difference between common gap and breakaway gap?

Chart vocabulary dey divide gaps according to context. Common gap open on ordinary day with ordinary volume and no specific news. Breakaway gap open on heavy volume alongside new information, often outside price range wey stock don hold for some time. The volume split for panel above na measurable version of that same difference.

Why gap fill statistics dey vary so much between sources?

Four choices fit move the number well: fill horizon, basket of stocks, period covered, and whether gap measure from previous close or previous high and low. Fill rate wey miss any of the four no fit compare with another one.


How dem measure these numbers

Sessions build from one-minute bars inside regular trading hours for New York time. Opening price na open of the first regular-session bar. Close na close of the last bar. Day high and low na the extremes of those bars. Each gap measure against the previous session close of the same ticker.

The 5, 20 and 60 session horizons include the gap day itself. Gap days stop at the end of February 2026, while the tape run through June 2026. So every gap for the horizon panel get at least sixty later sessions available, and none score as unfilled because data no dey.

Normal volume for the third panel na average regular-session volume over the 20 sessions ending two days before the gap. That keep the gap day and the day before am outside their own benchmark.

The basket na eight liquid large caps. Thinner names dey gap more often and fill differently, so these rates no carry over to small caps unless person run the same test on dem.

Every panel here come with the exact SQL underneath. To run the same test on ticker wey you dey follow, ask the question for plain English on the Strasmore terminal.

#gaps#gap fill#opening auction#overnight returns#technical analysis