Strasmore Research
Deep Dives · Matt ConnorBy Matt Connor ·

Чи завжди фондові gap заповнюються: дані

Чи завжди фондові gap заповнюються? Ні, у фіксованому горизонті це не працює. Дані про fill у ту саму сесію, через 5, 20 і 60 сесій для large caps та SQL-код аналізу.

Чи завжди фондові gap заповнюються? Не в межах будь-якого достатньо короткого для торгівлі горизонту. Це твердження здебільшого зберігає правдоподібність лише тоді, коли ніхто не уточнює часовий проміжок. Gap — це відстань між сьогоднішнім відкриттям і вчорашнім закриттям регулярної сесії. Fill — момент, коли ціна знову проходить через попередню ціну закриття. Уся суперечка зводиться до того, скільки читач готовий чекати.

Що вважається gap, а що — fill

Gap вимірюють між ціною відкриття та попереднім закриттям. Учорашня сесія завершилася на одному рівні, сьогоднішня відкрилася на іншому, і ця різниця є gap. Це не відстань від учорашнього внутрішньоденного максимуму або мінімуму. Це інший і значно легший для досягнення рівень. Підміна одного іншим перетворює «gap» на рух, який у звичайний день закривається ще до обіду. Невеликі gap виникають майже на кожній сесії: торгівля в інших часових поясах, зміни в індексах, новини щодо окремих акцій і коригування через ex-dividend date змінюють справедливу ціну, поки ринок США закритий. У матеріалі Чому акції формують gap overnight пояснюється, звідки береться це нічне переоцінювання.

Fill — це перший момент, коли акція знову торгується через попередню ціну закриття. Але це визначення неповне без установленого дедлайну. На необмеженому горизонті ліквідна акція в певний момент перетне майже будь-який названий читачем рівень. Тому твердження «gap завжди заповнюються» стає нефальсифікованим, а не істинним. Для кожного наведеного нижче показника вказано часовий горизонт.

В усій статті використовується одна вибірка: вісім великих акцій США, дні з gap у період із січня 2021 року до лютого 2026 року, а заповнення перевіряються за одноминутними максимумами та мінімумами регулярної сесії. Gap менше ніж 0,25% вилучено через округлення.

Чи завжди gap в акціях заповнюються в той самий день?

Почнімо з розміру. Згрупуймо всі gap за величиною й перевірмо, чи було попереднє закриття знову досягнуте до завершення тієї самої сесії.

ЗапитЧастка заповнення внутрішньосесійних розривів за розміром, вісім акцій із великою капіталізацією, 2021–2026
Точний SQL-код для кожного числа
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

Найменші gap у вибірці, від 0,25% до 0,5%, закривалися в межах тієї самої сесії у 74.6% випадків із 2381. Gap розміром 4% або більше закривалися в межах тієї самої сесії у 14.8% випадків. Нахил на графіку містить більшу частину висновку: що менший gap, то частіше акція протягом дня повертається через нього. Інакше кажучи, невеликі gap є шумом, а шум часто розвертається.

Скільки часу потрібно для заповнення gap?

Якщо подовжити дедлайн, частка заповнень може лише зрости. Довше вікно додає заповнення і не вилучає вже зафіксовані. Питання полягає в тому, яка частина всіх заповнень відбувається настільки пізно, що вже не має практичної цінності.

ЗапитЧастка заповнених розривів 1%+, залежно від тривалості очікування
Точний SQL-код для кожного числа
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

Серед 2191 gap розміром 1% або більше 34% заповнилися в межах тієї самої сесії, а 88.7% — протягом 60 sessions. Саме другий показник зазвичай цитують, не вказуючи горизонт. Шістдесят сесій — це приблизно три торгові місяці. Для позиції, відкритої в розрахунку на одноденну дислокацію, це довгий період утримання. Gap, який заповнився на 47-й день після того, як акція спершу далеко відійшла в протилежному напрямку, все одно враховується як заповнений у цій статистиці.

Gap, зумовлені шумом, і gap, зумовлені інформацією, — це дві різні групи

Gap у спокійний день зі звичайним обсягом є невеликим коригуванням ціни. Gap після новин про прибутки або пропозиції про придбання відображає зміну оцінки вартості компанії. У такому випадку попередня ціна вже не є рівнем, до якого ринок обов’язково повернеться. Обсяг достатньо добре розділяє ці дві групи: коли весь ринок переоцінює актив, кількість виконаних угод значно перевищує звичайний оборот.

ЗапитЧастка заповнення розривів залежно від обсягу торгів у день розриву
Точний SQL-код для кожного числа
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

Gap розміром 1% або більше за обсягу менш ніж у 1,5 раза від звичайного обороту акції заповнювалися в межах тієї самої сесії у 35.9% випадків. Gap такого самого розміру за обсягу, що у три або більше разів перевищував звичайний, заповнювалися в межах тієї самої сесії у 15.3% випадків, а протягом 20 сесій — у 45.8% випадків із 59. До групи з великим обсягом належать gap після звітів про прибутки та угод злиття або придбання. Усереднений показник для двох груп не описує належним чином жодну з них.

Чому перші хвилини сесії спотворюють статистику заповнення gap

Fill, зафіксований о 9:31, є ненадійним. Ціна відкриття формується на аукціоні — внаслідок одного пакетного перехресного виконання заявок, яке встановлює єдину ціну для всіх заявок, поданих до відкриття. Механіку цього процесу розглянуто в поясненні аукціону відкриття. У хвилини після такого виконання котирувальні спреди є найширшими за день, і одна невелика угода може зафіксувати максимум або мінімум, за яким фактично не торгувався значний обсяг. У матеріалі Спреди розширюються на відкритті пояснюється, чому в цей момент ринок має таку низьку глибину котирувань.

На графіку нижче показано структуру торгів у самій вибірці: середній одноминутний діапазон між максимумом і мінімумом у базисних пунктах (один базисний пункт — це одна сотa відсотка), розподілений за 15-хвилинними часовими інтервалами протягом 2025 року.

ЗапитСередній одนохвилинний діапазон і обсяг за часом доби, 2025
Точний SQL-код для кожного числа
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

У першому часовому інтервалі середній діапазон становить 24.4 б.п. за хвилину проти 6.9 б.п. у 12:30 інтервалі. Обсяг становить відповідно 303.9 тисяч акцій за хвилину проти 69.6 тисяч. Fill, зафіксований у першому інтервалі, ґрунтується на найменш надійних цінах дня. Наведені вище показники для тієї самої сесії включають ці хвилини, що дещо завищує результати. Це застереження стосується кожного показника заповнення протягом одного дня, зокрема й цього.

Що перевірити, перш ніж розглядати gap як торгову можливість

  1. Горизонт. Показник заповнення без установленого дедлайну не є статистикою.
  2. Вибірка. Чи відокремлено gap після новин від gap у спокійні дні та як проведено цю межу.
  3. Метод вимірювання. Чи перевіряли заповнення за фактичними торговими діапазонами та чи враховували перші хвилини сесії.
  4. Компанії, що залишилися у вибірці. Чи містить вибірка лише акції, які досі торгуються. Це непомітно вилучає gap, які так і не заповнилися.

Коректне формулювання цього твердження вужче за популярний слоган. Невеликі gap за звичайного обсягу часто й швидко заповнюються. Великі gap за високого обсягу є переоцінюванням, яке часто зберігається місяцями. Щоб відстежувати акції, які формують gap у реальному часі, використовуйте сторінку найбільші рухи акцій цього тижня. А матеріал як ринки відновлюються після обвалів застосовує такий самий підхід до часового горизонту для просідань індексів.

FAQ

Чи завжди gap в акціях заповнюються?

Ні. У цій вибірці 88.7% gap розміром 1% або більше знову торгувалися через попередню ціну закриття протягом 60 sessions. Отже, значна частина з них залишилася незаповненою приблизно через три торгові місяці. Формулювання «завжди» працює лише без обмеження в часі. Тоді його вже неможливо перевірити.

Скільки часу потрібно для заповнення gap?

Це залежить від gap. У цій вибірці 34% gap розміром 1% або більше заповнилися в межах тієї самої сесії. Решта заповнень припадає на наступні тижні та місяці. Невеликі gap переважно заповнюються протягом дня, а gap за великого обсягу заповнюються найповільніше.

У чому різниця між common gap і breakaway gap?

Термінологія технічного аналізу розділяє gap за контекстом. Common gap формується у звичайний день за звичайного обсягу та без конкретних новин. Breakaway gap виникає за великого обсягу разом із появою нової інформації, часто після виходу акції за межі цінового діапазону, у якому вона тривалий час торгувалася. Поділ за обсягом у панелі вище є вимірюваним відображенням тієї самої різниці.

Чому статистика заповнення gap так сильно відрізняється в різних джерелах?

На показник істотно впливають чотири рішення: горизонт заповнення, вибірка акцій, охоплений період і те, чи вимірюють gap від попереднього закриття, чи від попереднього максимуму та мінімуму. Показник заповнення, у якому не зазначено будь-який із цих чотирьох параметрів, не можна порівнювати з іншим.


Як вимірювали ці показники

Сесії побудовано на основі одноминутних барів у межах регулярних торгових годин за нью-йоркським часом. Ціна відкриття — це відкриття першого бара регулярної сесії, ціна закриття — закриття останнього бара, а денні максимум і мінімум — екстремальні значення цих барів. Кожен gap вимірюють відносно ціни закриття попередньої сесії тієї самої акції.

Горизонти у 5, 20 і 60 сесій включають день формування gap. Дні з gap охоплюють період до кінця лютого 2026 року, а дані торгів — до червня 2026 року. Тому для кожного gap у панелі горизонтів доступні щонайменше 60 наступних сесій. Жоден gap не позначено як незаповнений через нестачу даних.

Звичайний обсяг у третій панелі — це середній обсяг регулярної сесії за 20 сесій, що завершилися за два дні до формування gap. Це виключає з базового показника день формування gap і день перед ним.

До вибірки входять вісім ліквідних акцій із великою капіталізацією. Акції з нижчою ліквідністю формують gap частіше, і вони заповнюються інакше. Тому ці показники не можна переносити на акції з малою капіталізацією без повторного проведення такого самого тесту для них.

Під кожною панеллю наведено точний SQL-запит. Щоб провести такий самий тест для акції, за якою ви стежите, сформулюйте запит звичайною англійською мовою в терміналі Strasmore.

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