Strasmore Research
학습 Matt Connor작성자 Matt Connor · 업데이트됨 2026-08-08

하이-로우 인덱스 정의와 계산 방법

하이-로우 인덱스(High-Low Index)는 신고가 비율의 10일 이동평균을 활용한 시장 폭 지표입니다. 본문에서는 둥근 숫자를 활용한 공식 계산법과 거래량이 적은 장세에서 발생할 수 있는 표본 크기의 오류 및 주의사항을 상세히 설명합니다.

하이-로우 인덱스(High-Low Index)의 정의

하이-로우 인덱스는 두 가지 수치를 바탕으로 산출하는 시장 폭(market breadth) 지표입니다. 이 지표는 '신고가 비율(Record High Percent)'의 10일 단순이동평균(SMA)입니다. 신고가 비율은 52주 신고가 종목 수를 52주 신고가 종목 수와 52주 신저가 종목 수의 합으로 나눈 뒤 100을 곱한 값입니다. 지표가 50을 상회하면 신저가보다 신고가를 기록하는 종목이 더 많다는 의미이며, 50을 하회하면 그 반대를 뜻합니다. 공식은 이처럼 간결합니다. 이어지는 내용은 이 산식을 둥근 숫자로 계산한 결과와, 소수의 종목만으로도 결정적인 수치가 도출될 수 있는 표본 크기의 결함에 관한 것입니다.

High-Low Index는 어떻게 계산합니까?

두 단계로 이루어지며, 모두 한 줄로 요약할 수 있습니다.

  1. 당일 Record High Percent(신고가 비율): 특정 유니버스 내에서 52주 신고가를 기록한 종목 수와 52주 신저가를 기록한 종목 수를 각각 집계한 뒤, 신고가 종목 수를 두 수치의 합으로 나누고 100을 곱합니다.
  2. High-Low Index: 최근 10거래일간의 Record High Percent 수치를 평균합니다.

구체적인 숫자를 예로 들어보겠습니다. 어떤 장에서 90개의 신고가와 30개의 신저가가 발생했다고 가정합니다. 신고가와 신저가의 합은 120이며, 90을 120으로 나누면 0.75가 되므로 해당 세션의 Record High Percent는 75가 됩니다. 이제 10거래일간의 수치인 75, 70, 65, 60, 55, 50, 45, 40, 35, 30을 합산하면 525가 되며, 이를 10으로 나누면 52.5가 나옵니다. 즉, 당일 Record High Percent가 75인 날의 High-Low Index는 52.5를 나타냅니다.

이 격차는 이동평균선이 만들어내는 트레이드 오프입니다. 10일간의 데이터는 하룻밤 사이에 100에서 0으로 급변할 수 있는 지표를 매끄럽게 다듬어주지만, 그 대가로 신호가 늦게 나타납니다. 이 지표는 지난 2주간 시장의 폭(breadth)이 어떠했는지를 보여줄 뿐, 오늘 오후에 무슨 일이 일어났는지는 알려주지 않습니다.

52주 신고가란 무엇인가?

주식 가격이 지난 52주 동안 기록한 모든 가격을 상회할 때 52주 신고가를 경신했다고 합니다. 데이터 제공업체마다 기준이 다른데, 장중 고가와 저가를 비교하는 곳이 있는가 하면 종가만을 기준으로 삼는 곳도 있어 같은 거래일이라도 집계 결과가 다를 수 있습니다. 이 페이지의 모든 데이터는 종가를 기준으로 합니다. 52주 신고가 및 신저가에서 해당 목록을 확인할 수 있습니다.

아래 패널은 2026년 4월 1일부터 7월 31일까지 미국 대형주 40개 종목으로 구성된 고정 바스켓을 대상으로 매 거래일 양극단의 수치를 집계한 것입니다. 이 정도 규모의 바스켓은 수작업으로 검증이 가능하며, 이를 통해 이후 설명할 공식의 결함을 명확히 확인할 수 있습니다.

조회일일 52주 신고가 및 신저가: 미국 대형주 40종목, 2026년 4월~7월
모든 수치 뒤에 숨겨진 정확한 SQL
WITH daily_close AS (
    SELECT ticker,
           toDate(toTimeZone(window_start, 'America/New_York')) AS session_date,
           argMax(close, window_start) AS close_px
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker IN ('AAPL','MSFT','NVDA','AMZN','GOOGL','AVGO','JPM','JNJ','XOM','PG',
                     'KO','PEP','WMT','HD','CVX','MRK','PFE','ABBV','CSCO','ORCL',
                     'CRM','ADBE','MCD','NKE','VZ','T','DIS','BA','CAT','GE',
                     'IBM','MMM','UNH','LLY','COST','TGT','SBUX','GS','MS','LIN')
      AND window_start >= toDateTime('2025-03-01 00:00:00')
      AND window_start < toDateTime('2026-08-01 05:00:00')
      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
           + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
    GROUP BY ticker, session_date
),
extremes AS (
    SELECT cur.ticker AS ticker,
           cur.session_date AS session_date,
           cur.close_px AS close_px,
           max(hist.close_px) AS high_52w,
           min(hist.close_px) AS low_52w
    FROM daily_close AS cur
    INNER JOIN daily_close AS hist ON cur.ticker = hist.ticker
    WHERE cur.session_date >= toDate('2026-04-01')
      AND hist.session_date <= cur.session_date
      AND hist.session_date > cur.session_date - 364
    GROUP BY cur.ticker, cur.session_date, cur.close_px
),
daily AS (
    SELECT session_date,
           countIf(close_px >= high_52w) AS new_highs,
           countIf(close_px <= low_52w) AS new_lows,
           countIf(close_px >= high_52w) + countIf(close_px <= low_52w) AS names_at_extreme
    FROM extremes
    GROUP BY session_date
    HAVING names_at_extreme > 0
)
SELECT session_date AS date,
       formatDateTimeInJodaSyntax(session_date, 'MMM d') AS session_label,
       new_highs,
       new_lows
FROM daily
ORDER BY session_date
Run this yourself

77회의 거래일 동안 두 수치가 동시에 상승하는 경우는 드물었습니다. 분석 기간의 마지막 거래일인 Jul 29에는 40개 종목 중 3개가 52주 신고가로 마감했고, 0개는 52주 신저가로 마감했습니다. 첫 거래일인 Apr 1에는 신고가 1개, 신저가 1개로 집계되었습니다. 바스켓 내 나머지 종목들은 각자의 가격 범위 내에 머물러 계산에 포함되지 않았습니다.

적절한 High-Low Index 수치는 얼마인가?

50선은 신고가와 신저가가 균형을 이루는 피벗(pivot) 지점입니다. 통상적인 강세 신호는 70으로, 이는 10일 이동평균 전체에서 신고가 종목 수가 신저가 종목 수보다 두 배 이상 많음을 의미합니다. 30은 그 반대인 약세 신호입니다. 30에서 70 사이 구간은 시장의 극단적인 상황이 혼재되어 있음을 나타내며, 지수는 대부분 이 구간에 머무릅니다.

아래 패널은 동일한 바스켓에 대해 일별 Record High Percent(신고가 종목 비율) 원데이터와 그 10일 이동평균을 함께 나타낸 것입니다. 지연(lag) 현상은 설명보다 차트에서 더 명확하게 확인할 수 있습니다.

조회신고가 비율(Record High Percent, RHP) 및 10일 이동평균(High-Low Index): 40종목 바스켓
모든 수치 뒤에 숨겨진 정확한 SQL
WITH daily_close AS (
    SELECT ticker,
           toDate(toTimeZone(window_start, 'America/New_York')) AS session_date,
           argMax(close, window_start) AS close_px
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker IN ('AAPL','MSFT','NVDA','AMZN','GOOGL','AVGO','JPM','JNJ','XOM','PG',
                     'KO','PEP','WMT','HD','CVX','MRK','PFE','ABBV','CSCO','ORCL',
                     'CRM','ADBE','MCD','NKE','VZ','T','DIS','BA','CAT','GE',
                     'IBM','MMM','UNH','LLY','COST','TGT','SBUX','GS','MS','LIN')
      AND window_start >= toDateTime('2025-03-01 00:00:00')
      AND window_start < toDateTime('2026-08-01 05:00:00')
      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
           + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
    GROUP BY ticker, session_date
),
extremes AS (
    SELECT cur.ticker AS ticker,
           cur.session_date AS session_date,
           cur.close_px AS close_px,
           max(hist.close_px) AS high_52w,
           min(hist.close_px) AS low_52w
    FROM daily_close AS cur
    INNER JOIN daily_close AS hist ON cur.ticker = hist.ticker
    WHERE cur.session_date >= toDate('2026-04-01')
      AND hist.session_date <= cur.session_date
      AND hist.session_date > cur.session_date - 364
    GROUP BY cur.ticker, cur.session_date, cur.close_px
),
daily AS (
    SELECT session_date,
           countIf(close_px >= high_52w) AS new_highs,
           countIf(close_px >= high_52w) + countIf(close_px <= low_52w) AS names_at_extreme
    FROM extremes
    GROUP BY session_date
    HAVING names_at_extreme > 0
),
rhp AS (
    SELECT session_date,
           round(100 * new_highs / names_at_extreme, 1) AS record_high_pct
    FROM daily
),
smoothed AS (
    SELECT session_date,
           record_high_pct,
           round(avg(record_high_pct) OVER (ORDER BY session_date
                                            ROWS BETWEEN 9 PRECEDING AND CURRENT ROW), 1) AS high_low_index_pct,
           count() OVER (ORDER BY session_date
                         ROWS BETWEEN 9 PRECEDING AND CURRENT ROW) AS sessions_in_window
    FROM rhp
)
SELECT session_date AS date,
       formatDateTimeInJodaSyntax(session_date, 'MMM d') AS session_label,
       record_high_pct,
       high_low_index_pct
FROM smoothed
WHERE sessions_in_window = 10
ORDER BY session_date
Run this yourself

차트의 표시 범위는 Apr 15의 Record High Percent 100에서 시작하며, 당시 지수는 52.5였습니다. 종료 시점은 Jul 29100이며, 지수는 64입니다. 각 수치를 70 및 30 기준선과 비교하여 읽고, 두 선 사이의 간격은 평활화(smoothing)에 따른 비용으로 해석하십시오. 즉, 특정 일자의 이동평균은 여전히 이전 아홉 거래일의 데이터를 포함하고 있습니다. 10일 이동평균을 산출하려면 10일간의 데이터가 필요하므로, 해당 기간의 첫 아홉 거래일은 패널에서 제외되었습니다.

분모는 시장의 대부분을 무시합니다

이 지표가 가진 한계는 반드시 기억해야 합니다. 신고가 비율(Record High Percent)의 분모는 신고가 종목 수와 신저가 종목 수의 합일 뿐, 그 외의 정보는 포함하지 않습니다. 52주 범위의 중간에 머물러 있는 주식은 분자나 분모 어디에도 포함되지 않습니다. 이 지수는 3,000개 종목 중 900개가 극단적인 수치를 기록한 장세와 단 9개 종목만이 기록한 장세를 구분하지 못하며, 두 경우 모두 동일하게 확신에 찬 두 자릿수 숫자로 출력(print)합니다.

거래량이 적은 8월의 어느 장세에서 6,000개 종목이 상장된 거래소를 가정해 보겠습니다. 40개 종목이 신고가를 기록하고 20개 종목이 신저가를 기록했다면, 신고가 비율은 40을 60으로 나눈 66.7이 되며 이는 마치 광범위한 상승장처럼 보입니다. 하지만 이는 전체 상장 종목의 1%만을 기반으로 계산된 수치입니다. 나머지 5,940개 종목은 지수 산정에 아무런 영향을 미치지 못했습니다.

40개 종목으로 구성된 바스켓은 이를 수치화하여 화면에 보여줍니다. 분석 기간 내 모든 장세는 극단적인 수치를 기록한 종목 수에 따라 분류됩니다.

조회분모의 축소 효과: 52주 극단값 기록 종목별 세션 분류
모든 수치 뒤에 숨겨진 정확한 SQL
WITH daily_close AS (
    SELECT ticker,
           toDate(toTimeZone(window_start, 'America/New_York')) AS session_date,
           argMax(close, window_start) AS close_px
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker IN ('AAPL','MSFT','NVDA','AMZN','GOOGL','AVGO','JPM','JNJ','XOM','PG',
                     'KO','PEP','WMT','HD','CVX','MRK','PFE','ABBV','CSCO','ORCL',
                     'CRM','ADBE','MCD','NKE','VZ','T','DIS','BA','CAT','GE',
                     'IBM','MMM','UNH','LLY','COST','TGT','SBUX','GS','MS','LIN')
      AND window_start >= toDateTime('2025-03-01 00:00:00')
      AND window_start < toDateTime('2026-08-01 05:00:00')
      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
           + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
    GROUP BY ticker, session_date
),
extremes AS (
    SELECT cur.ticker AS ticker,
           cur.session_date AS session_date,
           cur.close_px AS close_px,
           max(hist.close_px) AS high_52w,
           min(hist.close_px) AS low_52w
    FROM daily_close AS cur
    INNER JOIN daily_close AS hist ON cur.ticker = hist.ticker
    WHERE cur.session_date >= toDate('2026-04-01')
      AND hist.session_date <= cur.session_date
      AND hist.session_date > cur.session_date - 364
    GROUP BY cur.ticker, cur.session_date, cur.close_px
),
daily AS (
    SELECT session_date,
           countIf(close_px >= high_52w) AS new_highs,
           countIf(close_px >= high_52w) + countIf(close_px <= low_52w) AS names_at_extreme
    FROM extremes
    GROUP BY session_date
    HAVING names_at_extreme > 0
)
SELECT multiIf(names_at_extreme <= 3, '1 to 3 names',
               names_at_extreme <= 9, '4 to 9 names',
               '10 or more names') AS sample_size_bucket,
       count() AS readings,
       round(avg(names_at_extreme), 1) AS avg_names_in_denominator,
       round(quantileDeterministic(0.5)(100 * new_highs / names_at_extreme,
                                        cityHash64(session_date)), 1) AS median_record_high_pct,
       round(quantileDeterministic(0.5)(abs(100 * new_highs / names_at_extreme - 50),
                                        cityHash64(session_date)), 1) AS median_distance_from_50
FROM daily
GROUP BY sample_size_bucket
ORDER BY avg_names_in_denominator
Run this yourself

39 버킷에 속한 1 to 3 names 장세에서, 전체 수치는 40개 종목 중 평균 2개를 기반으로 산출되었습니다. 해당 장세들의 신고가 비율 중앙값은 100이었으며, 50선으로부터 중앙값 50 포인트만큼 떨어져 있었습니다. 10 or more names 장세의 경우, 그중 1는 평균 10개 종목을 기반으로 했으며, 중앙값 70을 기록했고 중간 지점으로부터 20 포인트 떨어져 있었습니다.

산술적인 한계로 인해 거래가 부진한 장세에서는 지표가 출력할 수 있는 수치가 제한됩니다. 극단적인 수치를 기록한 종목이 세 개일 경우, 가능한 수치는 0, 33.3, 66.7, 100뿐입니다. 두 개일 경우에는 0, 50, 100만 가능합니다. 두 개의 신고가와 한 개의 신저가로 산출된 66.7이라는 수치는 표본이 뒷받침할 수 없는 소수점 자릿수를 포함하고 있습니다. 이러한 문제를 해결하는 습관은 명확합니다. 지수만 단독으로 보지 말고, 항상 지수 옆에 표시된 신고가 및 신저가 종목의 원시 데이터(raw counts)를 함께 확인해야 합니다.

시장 폭: 고저 지수(High-Low Index)와 등락주선(advance/decline)

두 지표 모두 얼마나 많은 종목이 시장에 참여하고 있는지를 측정합니다. 다만 측정하는 기간의 범위가 다릅니다. 등락주선은 전일 대비 상승하거나 하락한 모든 종목을 집계합니다. 전체 종목을 분모로 하며, 하루 동안의 시장 상황을 나타냅니다. 반면 고저 지수는 52주 신고가나 신저가를 기록한 종목만을 집계합니다. 분모가 작으며, 지난 1년의 역사적 흐름 속에서 현재 가격이 어디에 위치하는지를 보여줍니다. 따라서 당일 시장 전체가 상승하더라도 52주 신고가 근처에 있는 종목은 거의 없을 수 있습니다.

아래 패널은 두 지표를 동일한 바스켓을 기준으로 10거래일 동안 평활화하여 동일한 척도에서 비교합니다.

조회두 가지 시장 폭(breadth) 지표 비교: 10일 High-Low Index 대 10일 상승 종목 비율
모든 수치 뒤에 숨겨진 정확한 SQL
WITH daily_close AS (
    SELECT ticker,
           toDate(toTimeZone(window_start, 'America/New_York')) AS session_date,
           argMax(close, window_start) AS close_px
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker IN ('AAPL','MSFT','NVDA','AMZN','GOOGL','AVGO','JPM','JNJ','XOM','PG',
                     'KO','PEP','WMT','HD','CVX','MRK','PFE','ABBV','CSCO','ORCL',
                     'CRM','ADBE','MCD','NKE','VZ','T','DIS','BA','CAT','GE',
                     'IBM','MMM','UNH','LLY','COST','TGT','SBUX','GS','MS','LIN')
      AND window_start >= toDateTime('2025-03-01 00:00:00')
      AND window_start < toDateTime('2026-08-01 05:00:00')
      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
           + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
    GROUP BY ticker, session_date
),
extremes AS (
    SELECT cur.ticker AS ticker,
           cur.session_date AS session_date,
           cur.close_px AS close_px,
           max(hist.close_px) AS high_52w,
           min(hist.close_px) AS low_52w
    FROM daily_close AS cur
    INNER JOIN daily_close AS hist ON cur.ticker = hist.ticker
    WHERE cur.session_date >= toDate('2026-04-01')
      AND hist.session_date <= cur.session_date
      AND hist.session_date > cur.session_date - 364
    GROUP BY cur.ticker, cur.session_date, cur.close_px
),
daily AS (
    SELECT session_date,
           countIf(close_px >= high_52w) AS new_highs,
           countIf(close_px >= high_52w) + countIf(close_px <= low_52w) AS names_at_extreme
    FROM extremes
    GROUP BY session_date
    HAVING names_at_extreme > 0
),
prior AS (
    SELECT ticker,
           session_date,
           close_px,
           lagInFrame(close_px, 1) OVER (PARTITION BY ticker ORDER BY session_date
                                         ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS prev_close
    FROM daily_close
),
breadth AS (
    SELECT session_date,
           round(100 * countIf(close_px > prev_close) / count(), 1) AS advancing_pct
    FROM prior
    WHERE session_date >= toDate('2026-04-01')
      AND prev_close > 0
    GROUP BY session_date
    HAVING count() >= 20
),
combined AS (
    SELECT d.session_date AS session_date,
           round(100 * d.new_highs / d.names_at_extreme, 1) AS record_high_pct,
           b.advancing_pct AS advancing_pct
    FROM daily AS d
    INNER JOIN breadth AS b ON d.session_date = b.session_date
),
smoothed AS (
    SELECT session_date,
           round(avg(record_high_pct) OVER (ORDER BY session_date
                                            ROWS BETWEEN 9 PRECEDING AND CURRENT ROW), 1) AS high_low_index_pct,
           round(avg(advancing_pct) OVER (ORDER BY session_date
                                          ROWS BETWEEN 9 PRECEDING AND CURRENT ROW), 1) AS advancing_pct_10d,
           count() OVER (ORDER BY session_date
                         ROWS BETWEEN 9 PRECEDING AND CURRENT ROW) AS sessions_in_window
    FROM combined
)
SELECT session_date AS date,
       formatDateTimeInJodaSyntax(session_date, 'MMM d') AS session_label,
       high_low_index_pct,
       advancing_pct_10d
FROM smoothed
WHERE sessions_in_window = 10
ORDER BY session_date
Run this yourself

이 두 지표는 52.5(고저 지수)와 52.8(10일 등락 비율)에서 시작하여 Apr 15에 도달하며, Jul 29에는 6452.5로 마감합니다. 총 68 거래일이 표시되었습니다. 등락 비율은 좁은 범위 내에서 움직입니다. 일반적인 장세에서는 바스켓의 절반 정도가 상승 마감하며, 이를 10일 평균하면 지표는 척도의 중간 부근에 위치하게 됩니다. 고저 지수에는 이러한 중심값이 없습니다. 바스켓 내 어떤 종목도 10거래일 연속으로 52주 신저가를 기록하지 않으면, 매일의 수치는 100이 되며 평균값 또한 100이 됩니다.

시장 폭은 참여도 측정 지표와 함께 볼 때 더 정확하게 해석됩니다. 상대 거래량(Relative volume)은 특정 종목의 금일 거래량이 평소 대비 얼마나 활발한지를 확인합니다. 이번 주 최대 등락 종목(The week's biggest stock movers)은 실제로 가격 변동이 컸던 종목을 보여주며, 이번 주 이례적 거래량 종목(unusual volume stocks this week)은 거래가 집중된 곳을 나타냅니다.

고가-저가 지수(High-Low Index) FAQ

고가-저가 지수가 70을 상회한다는 것은 무엇을 의미합니까?

이는 지난 10거래일 평균을 기준으로 52주 신고가를 기록한 종목 수가 52주 신저가를 기록한 종목 수보다 두 배 이상 많았음을 의미합니다. 이는 전통적으로 강세장을 나타내는 지표입니다. 이 지표 자체는 극단적인 수치에 도달한 종목이 몇 개인지는 알려주지 않으므로, 반드시 해당 수치를 함께 확인해야 합니다.

신고가 비율(Record High Percent)과 고가-저가 지수의 차이는 무엇입니까?

신고가 비율은 단일 세션의 수치로, 신고가 종목 수를 신고가 종목 수와 신저가 종목 수의 합으로 나눈 뒤 100을 곱한 값입니다. 고가-저가 지수는 이 수치를 10일 단순 이동 평균(simple moving average)으로 계산한 것입니다. 전자는 오늘을 설명하고, 후자는 지난 2주간의 흐름을 설명합니다.

고가-저가 지수가 0이나 100이 될 수 있습니까?

그렇습니다. 만약 10거래일 연속으로 52주 신저가를 기록한 종목이 하나도 없다면, 매일의 수치는 100이 되며 평균값 또한 100이 됩니다. 신고가나 신저가를 기록한 종목이 전혀 없는 경우, 분모가 0이 되어 해당 세션의 수치는 정의되지 않습니다.

고가-저가 지수는 상승/하락 종목 수(advance/decline breadth)와 동일합니까?

아닙니다. 상승/하락 종목 수는 전체 종목을 대상으로 하며 단 하루의 방향성을 설명합니다. 고가-저가 지수는 52주 극단값에 위치한 소수 종목만을 대상으로 하며, 1년 치 가격 대비 현재의 위치를 설명합니다.

고가-저가 지수의 분모에는 보통 몇 개의 종목이 포함됩니까?

2026년 4월부터 7월까지 측정된 40개 종목 바스켓의 경우, 1 to 3 names 버킷의 세션들은 분모에 평균 2개의 종목이 포함되었습니다. 거래소 전체를 대상으로 하면 절대적인 종목 수는 더 많아지지만, 여전히 전체 상장 종목 중 일부에 불과합니다. 이것이 바로 원시 데이터(raw counts)를 지수 옆에 함께 표시해야 하는 이유입니다.


이 페이지의 모든 수치는 종가(closing prices)를 기반으로 저장 및 버전 관리되는 쿼리를 통해 산출됩니다. 각 패널을 열어 배후의 SQL을 확인하거나, Strasmore 터미널에서 직접 구성한 바스켓으로 동일한 브레드스(breadth) 시리즈를 재구축할 수 있습니다.

#market breadth#52-week highs#indicators#record high percent