RVOL na Wetin? How to Read Relative Volume
RVOL na stock volume divide by im 20-day average. Above one mean trading dey busier than normal. See intraday time-of-day RVOL and wetin count as high.
Relative volume (RVOL) dey measure how busy stock dey trade compared with its own recent normal level: na the session share volume divide by the stock average daily volume, usually based on the last 20 sessions. RVOL of one mean say na normal day. Two mean say shares wey change hands na twice the usual amount. Zero point five show say session dey run at half speed. Every figure for this page come from minute-level US market data, and exact query dey attached to each panel.
Relative volume na wetin? Simple definition
Raw volume by itself no talk much. Five million shares fit mean say mega-cap stock get very quiet day, but for small-cap stock e fit be event wey happen once a year. Relative volume make comparison with the stock’s normal activity clear:
RVOL = volume for the day ÷ stock’s average daily volume (normally the last 20 sessions)
Make we use hypothetical example. If stock dey average 10 million shares per day and e print 25 million by market close, e finish with RVOL of 2.5. (Print na trade wey dem report to the consolidated tape, the market-wide record of executions. “The tape” na trader shorthand for that record.) The 20-session lookback na common convention. But some platforms dey use 10, 30, or 50 days. E roughly represent one calendar month of trading. The window still carry the exchange calendar quirks. Holiday half-day add only part of normal session volume to the average, and market holidays and early closes show where those ones dey happen.
Why traders dey watch relative volume?
RVOL na gauge for market participation, no be gauge for direction. High-RVOL days dey cluster around events wey traders fit identify, like earnings, index additions and headline news. When price move print on heavy volume, crowd dey behind am. But the same move with RVOL at 0.3 fit come from only small number of orders. Activity also dey move with tradability. Across stocks, the most thinly traded names get the widest bid-ask spreads, posted by market makers wey dey quote both sides throughout the day. For any one stock, quotes usually dey somewhat wider during quiet sessions than during the busiest ones. Volume also dey support VWAP, the volume-weighted average price, another major volume-based reference.
How to calculate relative volume, and the time-of-day trap
The full-day calculation na one division: total volume for the session ÷ 20-session average daily volume. E only dey accurate after market close. The trap na to calculate am halfway through the session. By 10:30 a.m., running total naturally still be only part of any full-day figure, so even violent morning activity fit look quiet. Intraday volume dey follow U-shape through the day. Any intraday RVOL must start from this pattern.
Na the pattern be this, measured on SPY, the S&P 500 ETF: median shares traded per minute for each 30-minute clock bucket across the last 30 completed sessions, including extended hours from 4:00 a.m. to 8:00 p.m. Eastern Time.
| ET time | median minute volume k |
|---|---|
| 04:00 | 0.7 |
| 04:30 | 0.5 |
| 05:00 | 0.4 |
| 05:30 | 0.4 |
| 06:00 | 0.4 |
| 06:30 | 0.4 |
| 07:00 | 1.4 |
| 07:30 | 1.5 |
| 08:00 | 1.9 |
| 08:30 | 2.5 |
| 09:00 | 3.6 |
| 09:30 | 95.5 |
| 10:00 | 68.2 |
| 10:30 | 58.9 |
| 11:00 | 49.1 |
| 11:30 | 45 |
| 12:00 | 39.8 |
| 12:30 | 37.8 |
| 13:00 | 37.1 |
| 13:30 | 34.2 |
The exact SQL behind every number
SELECT formatDateTime(toStartOfInterval(toTimeZone(window_start, 'America/New_York'), INTERVAL 30 MINUTE), '%H:%i') AS et_time,
round(quantileDeterministic(0.5)(toFloat64(volume), toUInt64(toUnixTimestamp(window_start))) / 1000, 1) AS median_minute_volume_k
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker = 'SPY'
AND window_start >= now() - INTERVAL 30 DAY
AND toDate(toTimeZone(window_start, 'America/New_York')) < toDate(toTimeZone(now(), 'America/New_York'))
GROUP BY et_time
HAVING et_time >= '04:00' AND et_time < '20:00'
ORDER BY et_timeFor the 9:30 bucket, wey be the first 30 minutes of regular trading, median pace na 95.5 thousand shares per minute. The 9:00 premarket bucket just before am record 3.6 thousand. Pace come reduce to 37.8 thousand for the 12:30 bucket, then e build toward market close: 138.9 thousand for the 3:30 bucket, as trading dey move toward the closing auction, and well above the lunchtime low. After the 4:00 p.m. bell, per-minute pace drop sharply into after-hours trading.
Intraday relative volume: adjust am for time of day
The fix dey change the denominator: compare today running total with the average running total for that same time of day. To do this, you need know the share of normal day volume wey don complete at each time. For here, dem measure am trade by trade from the consolidated tape for SPY last 20 completed sessions:
| ET checkpoint | average percent of day volume wey don done |
|---|---|
| 10:00 | 12 |
| 12:00 | 34.3 |
| 14:00 | 51.1 |
| 15:30 | 64.9 |
| 16:00 | 82.8 |
The exact SQL behind every number
WITH per_min AS (
SELECT toDate(toTimeZone(sip_timestamp, 'America/New_York')) AS et_date,
formatDateTime(toTimeZone(sip_timestamp, 'America/New_York'), '%H:%i') AS et_min,
sum(toFloat64(size)) AS v
FROM global_markets.stocks_trades
WHERE ticker = 'SPY'
AND sip_timestamp >= now() - INTERVAL 45 DAY
AND toDate(toTimeZone(sip_timestamp, 'America/New_York')) < toDate(toTimeZone(now(), 'America/New_York'))
AND NOT hasAny(conditions, [15, 16, 38])
GROUP BY et_date, et_min
),
last20 AS (
SELECT DISTINCT et_date FROM per_min ORDER BY et_date DESC LIMIT 20
)
SELECT checkpoint AS et_checkpoint,
round(avg(share) * 100, 1) AS avg_pct_of_day_volume_done
FROM (
SELECT et_date,
checkpoint,
sumIf(v, et_min < checkpoint) / sum(v) AS share
FROM (
SELECT et_date, et_min, v, arrayJoin(['10:00', '12:00', '14:00', '15:30', '16:00']) AS checkpoint
FROM per_min
WHERE et_date IN (SELECT et_date FROM last20)
)
GROUP BY et_date, checkpoint
)
GROUP BY checkpoint
ORDER BY checkpointBy 10:00 a.m., 30 minutes into regular trading, SPY usually don print just 12% of the final full-day volume. By noon, na 34.3%. By 2:00 p.m., na 51.1%. By 3:30 p.m., e still be only 64.9%. Even for the 4:00 p.m. closing bell, the total reach 82.8%. Everything else dey get reported for or after 16:00. Most of am land within the first few minutes after the bell, when closing auction and other end-of-day trades hit the tape. The after-hours session itself dey far thinner.
This match the cliff for the chart above. Per-minute pace collapse for the bell even as meaningful part of the day total still dey get reported. Time-adjusted morning RVOL divides today cumulative volume by the 20-day average, multiplied by the checkpoint share.
One caveat: this schedule na SPY own. Each stock get its own clock. The worked example below measure one name own.
Example wey show how e work: one real heavy-volume session (MU)
June 2026 na heavy month for Micron. the MU June 2026 deep-dive go through the full tape. Na the biggest-volume session for that month be this one, and we calculate the RVOL in two ways:
| session date | volume by 1030 m | average volume by 1030 m | RVOL 1030 adjusted | RVOL 1030 naive | session volume m | trailing ADV m | RVOL full day |
|---|---|---|---|---|---|---|---|
| 2026-06-25 | 32.8 | 17.3 | 1.9 | 0.64 | 77.2 | 51 | 1.5 |
The exact SQL behind every number
WITH mu_daily AS (
SELECT toDate(toTimeZone(window_start, 'America/New_York')) AS et_date,
sum(toFloat64(volume)) AS day_volume,
sumIf(toFloat64(volume), formatDateTime(toTimeZone(window_start, 'America/New_York'), '%H:%i') < '10:30') AS vol_by_1030
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker = 'MU'
AND window_start >= toDateTime('2026-04-15 00:00:00', 'America/New_York')
AND window_start < toDateTime('2026-07-01 00:00:00', 'America/New_York')
GROUP BY et_date
HAVING day_volume > 1000000
),
biggest AS (
SELECT et_date, day_volume, vol_by_1030
FROM mu_daily
WHERE et_date >= toDate('2026-06-01') AND et_date <= toDate('2026-06-30')
ORDER BY day_volume DESC
LIMIT 1
),
trailing AS (
SELECT sum(day_volume) / 20 AS adv, sum(vol_by_1030) / 20 AS avg_by_1030
FROM (
SELECT day_volume, vol_by_1030
FROM mu_daily
WHERE et_date < (SELECT et_date FROM biggest)
ORDER BY et_date DESC
LIMIT 20
)
)
SELECT formatDateTime((SELECT et_date FROM biggest), '%Y-%m-%d') AS session_date,
round((SELECT vol_by_1030 FROM biggest) / 1e6, 1) AS vol_by_1030_m,
round((SELECT avg_by_1030 FROM trailing) / 1e6, 1) AS avg_vol_by_1030_m,
round((SELECT vol_by_1030 FROM biggest) / (SELECT avg_by_1030 FROM trailing), 1) AS rvol_1030_adjusted,
round((SELECT vol_by_1030 FROM biggest) / (SELECT adv FROM trailing), 2) AS rvol_1030_naive,
round((SELECT day_volume FROM biggest) / 1e6, 1) AS session_volume_m,
round((SELECT adv FROM trailing) / 1e6, 1) AS trailing_adv_m,
round((SELECT day_volume FROM biggest) / (SELECT adv FROM trailing), 1) AS rvol_full_dayMU biggest session for the month happen on 2026-06-25. By 10:30 a.m. that day, 32.8 million shares don trade. The average by that same time across the previous 20 sessions na 17.3 million shares. That one give time-adjusted RVOL of 1.9. If you divide the morning total by the full-day average of 51 million shares instead, the naive reading na 0.64. That one make am look like below-average day, even though the morning pace dey almost two times the normal pace. The session close with 77.2 million shares traded, giving full-day RVOL of 1.5.
That closing figure fit look small beside screenshots of movers wey trade at ten-times-average volume. Whether e really small na matter of distribution, and we go measure that next.
Wetin count as high relative volume?
Trading forums dey throw around threshold like “RVOL above 2 na high.” But na measured version be this: full-day RVOL for every US-listed stock and ETF wey get 20-day average daily volume above 5 million shares, plus full 20 prior sessions of history. We rank dem by percentile across the group for the latest completed session inside our data. One thing about the label: the screen dey count shares, no be dollars. So this high-volume group cover mega-caps and ETFs, reach cheap small-caps wey get plenty churning. High share volume no mean say instrument easy to trade.
| percentile | RVOL |
|---|---|
| p10 | 0.1 |
| p25 | 0.77 |
| p50 (median) | 1.05 |
| p75 | 1.38 |
| p90 | 1.81 |
| p99 | 3.99 |
The exact SQL behind every number
WITH daily AS (
SELECT ticker,
toDate(toTimeZone(window_start, 'America/New_York')) AS et_date,
toFloat64(sum(volume)) AS day_volume
FROM global_markets.delayed_stocks_minute_aggs
WHERE window_start >= now() - INTERVAL 40 DAY
AND toDate(toTimeZone(window_start, 'America/New_York')) < toDate(toTimeZone(now(), 'America/New_York'))
GROUP BY ticker, et_date
),
spy_days AS (
SELECT et_date,
day_volume,
avg(day_volume) OVER (ORDER BY et_date ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING) AS prior_avg
FROM daily
WHERE ticker = 'SPY' AND day_volume > 10000000
),
sessions AS (
SELECT et_date FROM spy_days
),
latest AS (
SELECT max(et_date) AS d FROM spy_days WHERE prior_avg > 0 AND day_volume >= 0.6 * prior_avg
),
prior20 AS (
SELECT et_date FROM sessions WHERE et_date < (SELECT d FROM latest) ORDER BY et_date DESC LIMIT 20
),
rvols AS (
SELECT ticker, day_vol / adv20 AS rvol
FROM (
SELECT ticker,
sumIf(day_volume, et_date = (SELECT d FROM latest)) AS day_vol,
sumIf(day_volume, et_date IN (SELECT et_date FROM prior20)) / 20 AS adv20,
countIf(day_volume > 0 AND et_date IN (SELECT et_date FROM prior20)) AS sessions_traded
FROM daily
WHERE ticker NOT IN ('SPCX')
GROUP BY ticker
HAVING adv20 > 5000000 AND day_vol > 0 AND sessions_traded = 20
)
)
SELECT pair.1 AS percentile,
round(pair.2, 2) AS rvol
FROM (
SELECT arrayJoin(arrayZip(['p10', 'p25', 'p50 (median)', 'p75', 'p90', 'p99'], quantilesExact(0.1, 0.25, 0.5, 0.75, 0.9, 0.99)(rvol))) AS pair
FROM rvols
)
ORDER BY percentileThe median name for this high-volume group traded at 1.05 times its own 20-day average. Nine out of ten names come below 1.81, and only one out of hundred pass 3.99. Put MU’s session from the previous section beside this table, e no longer look modest. Even busy names spend most days near their own average. Multiples of that average dey rare.
The bottom tail matter too. The 10th percentile sit at 0.1. Readings wey low like this usually point to stock wey still get one very large session inside its 20-day average. After a spike, the inflated denominator go hold RVOL down for weeks. The window get memory, for both directions.
One caveat: this na cross-section from only one session. Percentiles fit shift from day to day. Market-wide event days, like index rebalances and option expirations, fit lift the whole curve.
Which stocks get unusual volume? The RVOL leaderboard
Na the same universe, but dem rank am differently. The regular weekly version dey here: unusual volume stocks this week. E show the ten highest full-day RVOL readings for the 2026-10-15 session among names wey dey average more than 5 million shares per day. Na the kind board wey screener go flag as unusual volume. The screen need full 20 prior sessions. New listings no get meaningful 20-day average, so without this rule dem fit fill the board with misleading results.
| ticker | session date | session volume m | ADV 20d m | RVOL |
|---|---|---|---|---|
| CCC | 2026-10-15 | 318 | 9.3 | 34.1 |
| WETO | 2026-10-15 | 111.2 | 6.5 | 17 |
| SDST | 2026-10-15 | 1641.1 | 106.3 | 15.4 |
| DKI | 2026-10-15 | 125.7 | 19.1 | 6.6 |
| WFF | 2026-10-15 | 140.1 | 23.1 | 6.1 |
| ERIC | 2026-10-15 | 39.9 | 8.2 | 4.9 |
| WULF | 2026-10-15 | 105.9 | 26.5 | 4 |
| CRWV | 2026-10-15 | 127.5 | 32.6 | 3.9 |
| HUMA | 2026-10-15 | 20.7 | 5.3 | 3.9 |
| KORU | 2026-10-15 | 77.4 | 21.4 | 3.6 |
The exact SQL behind every number
WITH daily AS (
SELECT ticker,
toDate(toTimeZone(window_start, 'America/New_York')) AS et_date,
toFloat64(sum(volume)) AS day_volume
FROM global_markets.delayed_stocks_minute_aggs
WHERE window_start >= now() - INTERVAL 40 DAY
AND toDate(toTimeZone(window_start, 'America/New_York')) < toDate(toTimeZone(now(), 'America/New_York'))
GROUP BY ticker, et_date
),
spy_days AS (
SELECT et_date,
day_volume,
avg(day_volume) OVER (ORDER BY et_date ROWS BETWEEN 20 PRECEDING AND 1 PRECEDING) AS prior_avg
FROM daily
WHERE ticker = 'SPY' AND day_volume > 10000000
),
sessions AS (
SELECT et_date FROM spy_days
),
latest AS (
SELECT max(et_date) AS d FROM spy_days WHERE prior_avg > 0 AND day_volume >= 0.6 * prior_avg
),
prior20 AS (
SELECT et_date FROM sessions WHERE et_date < (SELECT d FROM latest) ORDER BY et_date DESC LIMIT 20
)
SELECT ticker,
formatDateTime((SELECT d FROM latest), '%Y-%m-%d') AS session_date,
round(day_vol / 1e6, 1) AS session_volume_m,
round(adv20 / 1e6, 1) AS adv_20d_m,
round(day_vol / adv20, 1) AS rvol
FROM (
SELECT ticker,
sumIf(day_volume, et_date = (SELECT d FROM latest)) AS day_vol,
sumIf(day_volume, et_date IN (SELECT et_date FROM prior20)) / 20 AS adv20,
countIf(day_volume > 0 AND et_date IN (SELECT et_date FROM prior20)) AS sessions_traded
FROM daily
WHERE ticker NOT IN ('SPCX')
GROUP BY ticker
HAVING adv20 > 5000000 AND day_vol > 0 AND sessions_traded = 20
)
ORDER BY rvol DESC, ticker
LIMIT 10At the top of the board, CCC print 318 million shares, compared with 20-day average of 9.3 million. That give am RVOL of 34.1. Every row pass its own average. Boards like this dey favor names wey no be household mega-caps. To double a very large average, the stock need extraordinary number of shares. But smaller name fit multiply its trading volume inside one session.
Data notes: how we choose sessions and exclusions
Both ranked tables use the same universe and the same session rule. We only count a candidate session as complete after SPY trading for that date reach at least 60% of SPY own prior-20-session average volume. The newest day data dey arrive with lag. This check stop us from treating a partly loaded day as finished session. Both tables also remove one symbol wey exchanges recently assign to a new listing. Vendor feeds carry the trading history of two different companies under that ticker, so its 20-day average no be meaningful baseline. The SQL exclusion list dey stay aligned with our symbol-identity checks every time we refresh. If any reused symbol get flagged again, we add am the same way.
FAQ
Good RVOL number na wetin?
No official threshold dey. For high-volume US stocks wey we measure for the latest completed session inside our data, the median stock trade at 1.05 times e 20-day average. The 90th percentile na 1.81, while the 99th percentile na 3.99.
RVOL of 2 mean wetin?
The stock don trade two times e average volume for the measured window, usually the trailing 20 sessions. Time of day matter. RVOL of 2 against the average for the same time of day na strong mid-session reading. RVOL of 2 at market close mean say the stock trade at double the normal daily pace.
How una dey calculate relative volume intraday?
Divide today cumulative volume by the average cumulative volume for that same time of day across your lookback window. If you divide mid-morning total by full-day average, you go seriously understate the reading. By 10:00 a.m., SPY don usually complete only 12% of the volume e go eventually trade that day.
Relative volume dey include premarket and after-hours trading?
Platform conventions no be the same. This page dey count the whole Eastern Time calendar day, including extended hours. For SPY, premarket contribution small well. The median na 3.6 thousand shares per minute for the 9:00 bucket, compared with 95.5 thousand just after the open.
High relative volume bullish or bearish?
None of the two. RVOL dey measure participation, no be direction. Heavy tape fit show for stock best days and worst days. Historically, high-RVOL sessions dey happen around earnings, index changes and headline news. The number only show say crowd show up; e no show which direction dem dey support.
Every panel above get the exact SQL wey produce am. Open any one, replace the ticker with your own, then measure RVOL for the names wey you actually trade on the Strasmore terminal.