Strasmore Research
學習 Matt Connor作者: Matt Connor

High-Low Index 是什麼?市場廣度與計算方式

了解 High-Low Index 如何以 Record High Percent 的10日平均計算,透過整數範例說明公式,並解析市場清淡時樣本數過小造成的誤導。

High-Low Index 是一項市場廣度指標,由兩個計數值組成:其數值為 Record High Percent 的10日簡單移動平均,而 Record High Percent 則是新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,525除以10等於52.5。因此,當日自身的 Record High Percent 為75時,High-Low Index 讀數為52.5。

這個落差就是移動平均所付出的代價。10日記憶能平滑一個可能在一夜之間由100大幅擺動至0的序列,但代價是反應較慢。該指標回答的是過去兩週的市場廣度狀況,而不是今天下午發生了什麼。

什麼算是新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是分界點,代表高點與低點大致平衡。傳統上,70被視為強勢訊號,表示在完整的10日平均期間,新高數量以超過二比一的幅度多於新低。30則是相反的弱勢訊號。當指標介於30至70之間,代表市場極端值混雜,這也是它大部分時間所在的區間。

下方面板以同一籃子股票,將每日原始 Record High Percent 與其10日平均值繪製在一起。延遲效果會直接呈現在圖表上,而不只是透過文字描述。

查詢創新高比例及其10日平均(高低指數),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的標記,並將兩條線之間的距離視為平滑處理的代價:在任何一天,平均值仍包含前9個交易日的讀數。圖表排除觀察區間最初的9個交易日,因為計算10日平均需要10天資料。

分母忽略了市場大部分股票

這是最值得記住的限制。Record High Percent 的分母是新高加新低,除此之外沒有其他股票。一檔位於52週價格區間中段的股票,不會進入分子,也不會進入分母。該指標無法區分3,000檔股票中有900檔觸及極端值的交易日,與只有9檔觸及極端值的交易日;兩者都會顯示同樣看似明確的兩位數讀數。

想像一個有6,000檔股票的交易所,在8月某個交投清淡的交易日中,有40檔股票創新高、20檔創新低。Record High Percent 為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

1 to 3 names39個交易日中,整體讀數平均只建立在40檔股票中的2檔之上。這些交易日的 Record High Percent 中位數為100,與50分界線的距離中位數為50點。10 or more names個交易日中,有1個交易日的極端值股票平均為10檔;這些交易日的 Record High Percent 中位數為70,與中間值相距20點。

計算方式也限制了低參與度交易日能夠出現的讀數。當只有3檔股票處於極端值時,可能出現的讀數只有0、33.3、66.7與100。當只有2檔時,可能出現的讀數只有0、50與100。若66.7是由兩檔新高與一檔新低計算而來,這個小樣本其實無法支撐讀數所呈現的小數精確度。修正方式很簡單:務必同時查看新高與新低的原始計數,不要單獨解讀指標。

市場廣度:High-Low Index 與上漲/下跌家數

兩項指標都在衡量有多少股票參與市場走勢,但觀察期間不同。上漲/下跌家數廣度會計算相對於前一日收盤價上漲或下跌的每一檔股票。其分母是整個樣本範圍,描述的是單一交易日。High-Low Index 只計算處於52週極端值的股票。其分母較小,描述的是價格相對於全年歷史區間的位置。市場可能在當日廣泛上漲,但真正接近52週高點的股票仍然很少。

下方面板以同一籃子股票、同樣的10個交易日,將兩項指標分別平滑處理,因此兩者可在同一尺度上比較。

查詢兩項市場廣度指標,同一籃子股票:10日高低指數與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

圖表區間涵蓋68個交易日。兩項指標在Apr 15分別以 High-Low Index 52.5與10日上漲股占比52.8開頭,並在Jul 29分別以6452.5收尾。上漲股占比通常處於狹窄區間。一般交易日約有一半的股票收高,取10日平均後,該線仍會靠近尺度中間位置。High-Low Index 則沒有這種錨定效果。若連續10個交易日都沒有樣本中的股票創下52週低點,則每日讀數都是100,平均值也會維持在100。

搭配參與度指標,市場廣度的解讀會更完整。相對成交量用來觀察某檔股票今日的成交是否相對自身正常水準異常放大。本週漲跌幅最大的股票顯示哪些股票實際出現較大價格波動;本週成交量異常的股票則顯示成交量流向哪些股票。

High-Low Index 常見問題

High-Low Index 高於70代表什麼?

這表示在最近10個交易日的平均值中,新52週高點數量以超過二比一的幅度多於新52週低點。這是傳統上代表盤勢強勁的訊號。但它無法說明究竟有多少檔股票觸及極端值,因此仍須同時查看股票數量。

Record High Percent 與 High-Low Index 有何不同?

Record High Percent 是單一交易日的數值:新高數除以新高數與新低數之和,再乘以100。High-Low Index 則是該數值的10日簡單移動平均。一項描述今天,另一項描述過去兩週。

High-Low Index 可以是0或100嗎?

可以。若樣本範圍內連續10個交易日都沒有股票創下新52週低點,每日讀數都是100,平均值也會是100。若某個交易日沒有任何股票創新高或創新低,分數的分母就是0,該交易日沒有定義明確的讀數。

High-Low Index 是否等同於上漲/下跌家數廣度?

不是。上漲/下跌家數廣度以整個樣本範圍為分母,描述單一交易日的方向。High-Low Index 則以處於52週極端值的小部分股票為分母,描述價格相對於一年區間的位置。

High-Low Index 的分母通常包含多少檔股票?

在本頁以2026年4月至7月的40檔股票籃子進行計算時,1 to 3 names分組中的交易日,其分母平均包含2檔股票。全交易所的絕對計數通常較大,但相對於所有掛牌股票仍只占少數,因此畫面上應在指標旁同時列出原始計數。


本頁所有計數均來自以收盤價為基礎、具版本控管的儲存查詢。開啟任一面板即可查看背後的 SQL,或在 Strasmore terminal 上以自選股票籃子重建相同的市場廣度序列。

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