Strasmore Research
学习 Matt Connor作者: Matt Connor

高低指数是什么?市场广度指标与计算方法

了解高低指数如何用10日平均值衡量市场广度,查看75和30个新高新低的完整计算,并识别小样本造成的误导。

高低指数是一项市场广度指标,由两个计数构成:它是创纪录高点百分比的10日简单移动平均,而创纪录高点百分比则是新52周高点数量除以新52周高点与新52周低点数量之和,再乘以100。读数高于50,表示创出新高的股票多于创出新低的股票;低于50则相反。公式就是这么简短。下文将用整数量级演示计算过程,并说明样本量缺陷:一个看似明确的读数,可能只建立在少数几只股票之上。

如何计算高低指数?

分两步完成,每一步都能写在一行内。

  1. 单个交易日的创纪录高点百分比:统计股票池中收于新52周高点的股票数量,再统计收于新52周低点的股票数量;用高点数量除以两者之和,最后乘以100。
  2. 高低指数:计算最近10个交易日创纪录高点百分比读数的平均值。

用整数量级举例会更直观。某个交易日出现90个新高和30个新低。高点与低点合计120个,90除以120等于0.75,因此该交易日的创纪录高点百分比为75。再取10个交易日的读数:75、70、65、60、55、50、45、40、35和30。它们的总和为525,525除以10等于52.5。因此,在该交易日自身的创纪录高点百分比为75时,高低指数读数为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,即第一个交易日,高点与低点的数量分别为11。篮子中的其他股票都处于各自52周价格区间内部,没有进入计算。

什么样的高低指数读数才算良好?

50是枢轴位,代表高点和低点大致平衡。传统上,70是强势标志,意味着在完整的10日平均值中,新高数量超过新低数量两倍以上。30则是传统弱势标志,是其镜像水平。当指数处于30至70之间时,说明市场极值表现混合,这也是指数大多数时间所在的区间。

下方面板将同一股票篮子的每日原始创纪录高点百分比与其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的创纪录高点百分比100开盘,当时指数为52.5;以Jul 29100收盘,指数为64。将每个读数分别与70和30标记比较,并把两条线之间的距离理解为平滑处理的代价:在任一交易日,平均值仍包含此前9个交易日的读数。窗口最初的9个交易日未纳入面板,因为计算10日平均值需要10个交易日。

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

需要记住的限制在于:创纪录高点百分比的分母是新高数量加新低数量,仅此而已。一只股票若处于52周价格区间中部,既不在分子中,也不在分母中。该指数无法区分3,000只股票中有900只触及极值的交易日,与只有9只触及极值的交易日;这两种情况都会显示为同一个看似明确的两位数读数。

设想某个拥有6,000只股票的交易所,在8月一个交投清淡的交易日中,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

1 to 3 names39个交易日中,整个读数的基础是40只股票中平均2只触及极值。这些交易日的创纪录高点百分比中位数为100,与50线的距离中位数为50个百分点。10 or more names个交易日中,有1个交易日平均仅有10只股票触及极值;其创纪录高点百分比中位数为70,与中位线相距20个百分点。

算术关系限制了交易清淡的交易日能够显示的读数。当有3只股票触及极值时,可能的读数只有0、33.3、66.7和100。当有2只股票时,可能的读数只有0、50和100。建立在两只股票创出新高、一只股票创出新低之上的66.7,带有一个样本量无法支撑的小数点。解决办法很简单:同时查看新高和新低的原始计数,绝不要单独查看指数。

市场广度:高低指数与涨跌线

两项指标都在回答有多少只股票参与市场走势,但观察周期不同。涨跌线广度统计每只股票相对于昨日收盘价上涨或下跌的情况。其分母是整个股票池,描述的是单个交易日。高低指数只统计处于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

Apr 15,图示区间开端的高低指数为52.5,10日上涨股票占比为52.8;在Jul 29收盘时,两者分别为6452.5,整个区间共包含68个图示交易日。上涨股票占比处于较窄区间。普通交易日中,大约一半股票收涨,取10日平均后,该线仍会靠近尺度中部。高低指数则没有这一锚点。当篮子中连续10个交易日没有股票创出52周低点时,每日读数均为100,平均值也会随之保持在100。

结合参与度指标观察市场广度,结论会更可靠。相对成交量用于判断某只股票今天的成交是否明显高于自身通常水平。本周涨跌幅最大的股票展示实际涨幅或跌幅较大的股票,本周异常成交量股票则展示成交量流向了哪些股票。

高低指数常见问题

高低指数读数高于70意味着什么?

这意味着在最近10个交易日的平均值中,新52周高点数量超过新52周低点数量两倍以上。这是传统的强势市场标志。但它没有说明究竟有多少只股票触及极值,因此仍需同时查看股票数量。

创纪录高点百分比与高低指数有什么区别?

创纪录高点百分比是单个交易日的指标:新高数量除以新高与新低数量之和,再乘以100。高低指数则是该指标的10日简单移动平均。前者描述今天,后者描述过去两周。

高低指数可以是0或100吗?

可以。如果股票池中连续10个交易日没有股票创出新52周低点,每日读数均为100,平均值也为100。如果没有股票创出新高或新低,分数的分母为零,该交易日没有定义明确的读数。

高低指数与涨跌线广度相同吗?

不相同。涨跌线广度以整个股票池为分母,描述单个交易日的涨跌方向。高低指数以处于52周极值的小部分股票为分母,描述价格相对于一年价格区间的位置。

高低指数的分母通常包含多少只股票?

在这里对2026年4月至7月的40只股票篮子进行测算时,1 to 3 names组的交易日中,分母平均包含2只股票。交易所整体的绝对计数会更大,但相对于全部上市股票仍只占较小比例。因此,屏幕上应在指数旁同时显示原始计数。


本页每个计数都来自一条基于收盘价、经过存储并带有版本记录的查询。打开任一面板即可查看其背后的SQL,也可以在Strasmore终端上使用您自己的股票篮子重新构建同一市场广度序列。

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