주식 갭 메우기 현상 데이터 분석
주식의 갭은 항상 메워질까요? 고정된 기간 내에는 그렇지 않습니다. 대형주를 대상으로 당일 및 5, 20, 60 거래일 기준 갭 메우기 비율을 분석하고 이를 도출하기 위한 SQL 쿼리 예시를 함께 제공합니다.
주식의 갭(gap)은 항상 메워질까? 거래 가능한 짧은 기간 내에는 그렇지 않으며, 이 주장은 아무도 구체적인 기간을 명시하지 않을 때만 유지됩니다. 갭이란 당일 시가와 전일 정규장 종가 사이의 차이를 의미하며, 갭 메우기(fill)란 가격이 전일 종가 수준으로 되돌아오는 순간을 뜻합니다. 결국 이 논쟁의 핵심은 투자자가 이를 위해 얼마나 기다릴 의향이 있는지에 달려 있습니다.
갭과 갭 메우기의 정의
갭은 시가와 전일 종가를 비교하여 측정합니다. 전일 세션이 특정 가격에 마감되고 당일 다른 가격에 시가가 형성되면 그 차이가 갭이 됩니다. 이는 전일 장중 고가나 저가로부터의 거리가 아닙니다. 후자는 훨씬 도달하기 쉬운 목표치이며, 이를 갭과 혼동하는 순간 '갭'은 평범한 날 점심시간 전에 메워지는 흔한 현상이 되어버립니다. 작은 갭은 거의 매 세션마다 발생합니다. 타 시간대의 거래, 지수 변경, 개별 종목 뉴스, 배당락(ex-dividend) 조정 등이 미국 시장이 닫힌 사이 적정 가격을 변화시키기 때문입니다. 주식이 밤사이 갭을 만드는 이유에서 이러한 야간 재평가의 원인을 다룹니다.
갭 메우기란 주가가 전일 종가 수준으로 다시 거래되는 첫 순간을 의미합니다. 이 정의는 기한이 명시되지 않으면 불완전합니다. 무제한의 기간을 가정하면 유동성이 있는 주식은 결국 투자자가 언급하는 거의 모든 가격대를 통과하게 되며, 이는 "갭은 항상 메워진다"는 주장을 참이 아닌 반증 불가능한 명제로 만듭니다. 아래의 모든 수치에는 명시된 기간이 적용되어 있습니다.
본문 전체에서 하나의 표본을 사용합니다. 미국 대형주 8개 종목을 대상으로 2021년 1월부터 2026년 2월까지 발생한 갭을 분석했으며, 갭 메우기 여부는 정규장 1분 단위 고가와 저가를 기준으로 테스트했습니다. 0.25% 미만의 갭은 반올림 오차로 간주하여 제외했습니다.
주식 갭은 항상 당일에 메워질까?
규모부터 살펴보겠습니다. 모든 갭을 크기별로 분류한 뒤, 당일 세션이 종료되기 전에 전일 종가에 다시 도달했는지 확인했습니다.
모든 수치 뒤에 숨겨진 정확한 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표본 중 가장 작은 0.25%~0.5% 규모의 갭은 74.6%의 확률로 2381건 중 당일 내에 메워졌습니다. 4% 이상의 갭은 14.8%의 확률로 당일 내에 메워졌습니다. 차트의 하향 곡선이 핵심을 보여줍니다. 갭이 작을수록 장중에 주가가 해당 구간을 다시 넘나드는 빈도가 높으며, 이는 작은 갭은 소음(noise)에 불과하고 소음은 곧 되돌림이 발생한다는 것을 의미합니다.
갭이 메워지는 데 걸리는 시간은?
기한을 연장하면 갭 메우기 비율은 상승할 수밖에 없습니다. 기간이 길어질수록 메워지는 사례가 추가될 뿐 제외되지는 않기 때문입니다. 문제는 전체 사례 중 얼마나 많은 비율이 너무 늦게 메워져 실질적인 의미가 없는가 하는 점입니다.
모든 수치 뒤에 숨겨진 정확한 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)) ASC1% 이상의 갭 2191건 중, 34%는 당일 내에 메워졌고 88.7%는 60 sessions 이내에 메워졌습니다. 두 번째 수치는 보통 기간을 생략한 채 인용되곤 합니다. 60세션은 대략 3개월의 거래일로, 단기적인 가격 불균형을 전제로 한 포지션치고는 매우 긴 보유 기간입니다. 주가가 반대 방향으로 크게 움직인 후 47일째에 메워진 갭도 해당 통계에서는 '메워진 것'으로 간주됩니다.
소음 갭과 정보 갭은 별개의 집단
평범한 거래량과 함께 조용한 날 발생하는 갭은 작은 가격 조정에 불과합니다. 반면 실적 뉴스나 인수합병 소식에 따른 갭은 기업 가치의 변화를 수반하며, 되돌아갈 '과거의 가격'이 존재하지 않습니다. 거래량은 이 두 가지를 비교적 잘 구분해 줍니다. 시장 전체가 거래하고자 하는 재평가는 평소 거래량의 몇 배에 달하는 거래를 유발합니다.
모든 수치 뒤에 숨겨진 정확한 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평소 거래량의 1.5배 미만인 상태에서 발생한 1% 이상의 갭은 35.9%의 확률로 당일 내에 메워졌습니다. 반면 3배 이상의 거래량을 동반한 동일 규모의 갭은 15.3%의 확률로 당일 내에 메워졌으며, 45.8%는 59건 중 20세션 이내에 메워졌습니다. 거래량이 많은 집단에는 실적 갭과 인수합병 갭이 포함됩니다. 두 집단을 평균 내어 하나의 수치로 표현하는 것은 어느 쪽의 특성도 제대로 설명하지 못합니다.
세션 첫 몇 분이 갭 통계를 왜곡하는 이유
오전 9시 31분에 기록된 갭 메우기는 불안정한 현상입니다. 시가는 경매(auction)를 통해 결정되는데, 이는 밤사이 쌓인 모든 주문을 하나의 가격으로 체결하는 일괄 처리 방식입니다. 시가 경매 설명에서 그 메커니즘을 다룹니다. 체결 직후 몇 분 동안은 호가 스프레드(spread)가 하루 중 가장 넓으며, 실제 거래 규모와 상관없이 단 한 번의 소량 체결만으로도 고가나 저가가 기록될 수 있습니다. 시가에 넓어지는 스프레드에서 호가가 왜 그렇게 얇은지 설명합니다.
아래 차트는 분석 대상 종목들의 데이터를 보여줍니다. 2025년까지 15분 단위로 구분한 1분 평균 고가-저가 범위(basis points, 1bp는 0.01%)입니다.
모든 수치 뒤에 숨겨진 정확한 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시가 구간의 분당 평균 범위는 24.4 bps이며, 12:30 구간의 6.9 bps와 비교됩니다. 거래량은 분당 303.9천 주 대 69.6천 주입니다. 첫 구간 내에서 기록된 갭 메우기는 하루 중 가장 신뢰도가 낮은 가격에 근거합니다. 위에서 언급한 당일 메우기 수치에는 이 시간대가 포함되어 있어 수치가 다소 높게 편향되어 있으며, 이 주의사항은 당일 갭 메우기 통계 전체에 적용됩니다.
갭을 거래하기 전 확인해야 할 사항
- 기간: 기한이 명시되지 않은 메우기 비율은 통계가 아닙니다.
- 집단: 뉴스에 의한 갭과 조용한 날의 갭이 구분되었는지, 그리고 그 기준이 무엇인지 확인해야 합니다.
- 측정 방식: 실제 거래 범위를 기준으로 테스트했는지, 세션 첫 몇 분이 포함되었는지 확인해야 합니다.
- 생존자 편향: 현재도 거래되는 종목만 표본에 포함되었는지 확인해야 합니다. 이는 메워지지 않은 갭을 통계에서 조용히 삭제하는 결과를 낳습니다.
이 주장의 정직한 버전은 슬로건보다 훨씬 좁은 의미를 갖습니다. 평범한 거래량에서 발생하는 작은 갭은 자주, 그리고 빠르게 되돌아옵니다. 거래량이 많은 대형 갭은 수개월 동안 유지되는 재평가인 경우가 많습니다. 현재 어떤 종목이 갭을 만들고 있는지 실시간으로 보려면 이번 주 가장 큰 변동을 보인 주식 페이지를, 지수 하락장에서의 회복을 보려면 시장이 폭락 후 회복하는 방법을 참고하십시오.
FAQ
주식의 갭은 항상 메워질까?
아닙니다. 본 표본에서 1% 이상의 갭 중 88.7%만이 60 sessions 이내에 전일 종가를 다시 통과했습니다. 이는 약 3개월의 거래일이 지난 후에도 상당수의 갭이 메워지지 않은 채 남아있음을 의미합니다. "항상"이라는 표현은 시간 제한이 없을 때만 성립하는데, 그 시점부터는 검증이 불가능해집니다.
갭이 메워지는 데 얼마나 걸릴까?
갭의 성격에 따라 다릅니다. 여기서는 1% 이상의 갭 중 34%가 당일 내에 메워졌고, 나머지는 이후 수주나 수개월에 걸쳐 메워졌습니다. 작은 갭은 당일 내에 집중되는 경향이 있고, 거래량이 많은 갭은 가장 느리게 메워집니다.
일반 갭(common gap)과 돌파 갭(breakaway gap)의 차이는 무엇인가?
차트 용어는 맥락에 따라 갭을 구분합니다. 일반 갭은 특별한 뉴스 없이 평범한 거래량으로 평범한 날 발생하는 반면, 돌파 갭은 새로운 정보와 함께 많은 거래량을 동반하며 종종 주가가 오랫동안 유지해온 가격 범위를 벗어나며 발생합니다. 위 패널의 거래량 구분은 이러한 차이를 측정 가능한 형태로 나타낸 것입니다.
갭 메우기 통계가 출처마다 크게 다른 이유는 무엇인가?
네 가지 선택이 수치를 크게 변화시킵니다: 갭 메우기 기간, 종목 구성, 분석 기간, 그리고 갭을 전일 종가 기준으로 측정했는지 아니면 전일 고가/저가 기준으로 측정했는지 여부입니다. 이 네 가지 중 하나라도 빠진 인용된 메우기 비율은 다른 통계와 비교할 수 없습니다.
측정 방법
세션은 뉴욕 시간 기준 정규 거래 시간 내 1분 단위 바(bar)로 구성됩니다. 시가는 정규장 첫 바의 시가, 종가는 마지막 바의 종가이며, 당일 고가와 저가는 해당 바들의 극값입니다. 각 갭은 동일 종목의 전일 세션 종가와 비교하여 측정되었습니다.
5, 20, 60세션 기간에는 갭 발생 당일이 포함됩니다. 갭 발생일은 2026년 2월 말까지이며 데이터는 2026년 6월까지 이어지므로, 기간 패널의 모든 갭은 최소 60세션 이후의 데이터를 확보하고 있으며 데이터 부족으로 인해 메워지지 않은 것으로 간주된 사례는 없습니다.
세 번째 패널의 평소 거래량은 갭 발생 2일 전까지의 20세션 평균 정규장 거래량으로, 갭 발생 당일과 그 전날을 벤치마크에서 제외했습니다.
표본은 유동성이 높은 대형주 8개 종목입니다. 유동성이 낮은 종목은 갭이 더 자주 발생하고 메워지는 방식도 다르므로, 이 비율을 소형주에 그대로 적용할 수는 없습니다.
이곳의 모든 패널은 그 아래에 정확한 SQL을 포함하고 있습니다. 추적 중인 종목에 대해 동일한 테스트를 수행하려면 Strasmore 터미널에서 평이한 영어로 질문하십시오.