High-Low Index na Wetin? Market Breadth Explained
High-Low Index na 10-day average of Record High Percent. See round-number formula plus why small sample size fit mislead for quiet market.
High-Low Index na market breadth measure wey dey use two counts: na the 10-day simple moving average of Record High Percent, while Record High Percent na new 52-week highs divide by new 52-week highs plus new 52-week lows, then multiply by 100. Reading above 50 mean more stocks dey print new highs pass new lows, while reading below 50 mean na the opposite. Formula short like that. Wetin follow na the arithmetic for round numbers, plus the sample-size problem wey fit make one decisive-looking reading depend on only small number of stocks.
How you calculate High-Low Index?
Two steps, and each one fit enter one line.
- Record High Percent, for one session: count the stocks for your universe wey close at new 52-week high, count the ones wey close at new 52-week low, divide the highs by the sum of both, then multiply by 100.
- High-Low Index: calculate the average of the last 10 daily Record High Percent readings.
Round numbers make am clear. One tape print 90 new highs and 30 new lows. Highs plus lows na 120, and 90 divide by 120 na 0.75, so Record High Percent for that session na 75. Now take readings from ten sessions: 75, 70, 65, 60, 55, 50, 45, 40, 35 and 30. Dem sum to 525, and 525 divide by 10 na 52.5. High-Low Index read 52.5 on a day wey that day own Record High Percent be 75.
Na that gap be the trade-off wey moving average dey create. Ten days of memory smooth one series wey fit move from 100 to 0 overnight, but e go arrive late. Index dey answer where breadth don dey over two weeks. E no dey answer wetin happen this afternoon.
Wetin count as new 52-week high?
Stock make new 52-week high when its price pass every price wey e print for the previous 52 weeks. Data providers no dey agree on the basis: some compare intraday highs and lows, while others compare closing prices only. The two methods fit produce different counts for the same session. Closing prices na the basis everywhere for this page. New 52-week highs and lows explain the list itself.
The panel below count both extremes for each session for fixed basket of 40 large-cap US names, from April 1 to July 31, 2026. Basket wey get that size keep the counts small enough for person to check by hand. Na wetin make the problem with the formula show later.
The exact SQL behind every number
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_dateAcross 77 sessions, the two counts rarely rise together. On Jul 29, the last session for the window, 3 of the 40 names close at 52-week high and 0 close at 52-week low. On Apr 1, the first session, the split be 1 highs against 1 lows. Every other name for the basket remain somewhere inside its own range and no enter the calculation.
Wetin be good High-Low Index reading?
The 50 line na the pivot, where highs and lows balance. The conventional strong marker na 70. At that level, new highs don pass new lows by better than two-to-one across the full 10-day average. The conventional weak marker na 30, wey be the opposite. Between 30 and 70, index dey show say the market extremes mixed. Na there e dey most of the time.
The panel below plot the raw daily Record High Percent against its own 10-day average for the same basket. You fit see the lag for the chart instead of reading about am.
The exact SQL behind every number
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_dateThe plotted range open at Record High Percent of 100 on Apr 15, while index dey 52.5. E close at 100 on Jul 29, with index at 64. Compare each one with the 70 and 30 markers. Also look at the distance between the two lines as the cost of smoothing: on any day, the average still carry nine older sessions. The first nine sessions of the window no dey inside the panel because 10-day average need 10 days.
Denominator ignore most of the market
Na this limitation you need remember. Denominator of Record High Percent na new highs plus new lows, and nothing else. Stock wey dey rest for the middle of its 52-week range no dey inside numerator or denominator. Index no fit distinguish session where 900 out of 3,000 names hit extreme from one where only 9 do am. E go print the same confident two-digit number for both.
Imagine exchange wey get 6,000 names during slow August session: 40 stocks make new highs and 20 make new lows. Record High Percent na 40 divide by 60, or 66.7, wey look like broad advance. But calculation come from only 1% of the listings. The other 5,940 names no get vote.
The 40-name basket put countable version of that matter on screen. For every session inside the window, sort the names into bucket based on how many of dem print any extreme at all.
The exact SQL behind every number
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_denominatorFor the 39 sessions inside the 1 to 3 names bucket, the whole reading depend on average of 2 names out of 40. Those sessions get median Record High Percent of 100 and sit median 50 points away from the 50 line. The 10 or more names sessions, with 1 of dem averaging 10 names, get median 70 and sit 20 points from the middle.
The arithmetic limit wetin thin session fit print. With three names at an extreme, the only possible readings na 0, 33.3, 66.7 and 100. With two names, na only 0, 50 and 100. Reading of 66.7 wey come from two highs and one low carry decimal point wey the sample no fit support. The simple habit wey fix this one na: read the raw counts of highs and lows beside the index. No read the index alone.
Market breadth: High-Low Index vs advance/decline
Both measures dey ask how many stocks dey participate. But dem dey ask am across different horizons. Advance/decline breadth count every name wey finish up or down against yesterday. Its denominator na the full universe, and e describe one day. High-Low Index count only names wey dey 52-week extreme. Its denominator small, and e describe where prices dey against full year of history. Tape fit broadly higher for the day while very few names dey anywhere near 52-week high.
The panel below smooth both measures across the same 10 sessions and the same basket, so dem dey use one scale.
The exact SQL behind every number
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_dateThe pair open the plotted range at 52.5 for High-Low Index and 52.8 for 10-day advancing share on Apr 15. E finish at 64 and 52.5 on Jul 29, across 68 plotted sessions. Advancing share dey inside narrow band. Roughly half of the basket close higher on ordinary session, and when you average ten of those, the line remain near the middle of the scale. High-Low Index no get that kind anchor. When no name for the basket print 52-week low for ten sessions in a row, every daily reading na 100, and the average sef na 100.
Breadth dey make more sense when you put participation measures beside am. Relative volume dey ask whether one name dey trade unusually heavy today compared with its own normal level. The week's biggest stock movers show which names actually move far, while unusual volume stocks this week show where the trading happen.
High-Low Index FAQ
Wetin High-Low Index reading above 70 mean?
E mean say new 52-week highs don pass new 52-week lows by better than two-to-one across the average of the last ten sessions. Na the conventional strong-tape marker. E no talk anything about how many stocks reach extreme at all. Na that number you need check beside am.
Wetin be the difference between Record High Percent and High-Low Index?
Record High Percent na the figure for one session: new highs divide by new highs plus new lows, then multiply by 100. High-Low Index na the 10-day simple moving average of that figure. One describe today. The other describe the last two weeks.
High-Low Index fit be 0 or 100?
Yes. If no stock for the universe make new 52-week low for ten sessions straight, every daily reading na 100 and the average sef na 100. When no stock make either high or low, fraction get zero denominator and that session no get defined reading.
High-Low Index na the same thing as advance/decline breadth?
No. Advance/decline breadth divide by the whole universe and describe direction for one day. High-Low Index divide by the small group of stocks wey dey 52-week extremes and describe position against one year of prices.
How many stocks dey usually inside High-Low Index denominator?
For the 40-name basket measured here from April to July 2026, sessions inside the 1 to 3 names bucket average 2 names for the denominator. Exchange-wide counts bigger in absolute terms, but dem still be thin slice of all listings. Na why raw counts need dey on the screen beside the index.
Every count for this page come from stored, versioned query wey use closing prices. Open any panel to read the SQL behind am, or rebuild the same breadth series on basket of your own for the Strasmore terminal.