The 10:00 AM Rule in Stocks, Tested
The 10:00 AM rule says wait out the opening 30 minutes. We tested it on three years of minute bars: how much range and volume the open really puts in.
The 10:00 AM rule in stocks is a waiting rule: leave the first thirty minutes of the session alone and take your first read of the tape once 10:00 a.m. ET has passed. The reasoning that travels with it is that the opening half hour is overnight order imbalance working itself out through wide spreads, rather than a trend that will hold for the day. Two of the claims underneath the rule survive contact with three years of minute by minute data. The clock time does not.
What is the 10:00 AM rule in stocks?
The rule circulates in day-trading circles in several forms. The common version: take no new position before 10:00 a.m. ET, and treat the high and the low of the 09:30 to 10:00 window as reference levels for the rest of the session instead. Other versions use 10:15, or the first fifteen minutes. Nothing in the exchange rulebooks marks 10:00. Regular trading runs 09:30 to 16:00 ET as one continuous session, with no scheduled event at the top of the first hour.
Two pieces of market plumbing sit behind the folklore. Overnight orders do not trade one at a time at 09:30. They are collected and crossed in a single print, the opening auction, and whatever interest does not clear there keeps working into continuous trading. Alongside that, the bid/ask spread, meaning the gap between the highest posted buy price and the lowest posted sell price, starts the day wide and narrows as quotes get refreshed.
That makes the rule measurable. A full session holds 390 one minute bars, six and a half hours of sixty minutes each, the count behind the 390 minute session. Any thirty minute slice is 7.7% of the trading day. If the open were ordinary, it would hold about 7.7% of what the day produces.
How much of the day's range happens in the first 30 minutes?
The first panel takes six heavily traded names over the three calendar years 2023 through 2025 and measures, for every session, the first thirty minutes against the whole regular session. Range means the high to low distance; volume share is measured against regular-session volume only, so premarket and after-hours trading are outside it.
Averaged over every qualifying session in the window, the opening half hour covers 62.2% of the full day high to low range in KO, the highest of the 6 names, and 38.8% in SPY, the lowest. Every one of them sits far above the 7.7% an even split would hand a thirty minute slice. Volume concentrates the same way: KO put 14.2% of its regular-session volume through that half hour, and SPY put 11.3% through it. That is the part of the rule with real support. A disproportionate amount of the day's price territory and turnover is staked out before 10:00.
Where the volume and the range sit across the session
The shape of the whole session says more than the opening figure on its own. This panel splits regular trading into thirteen half hour buckets and asks, for each bucket, what share of the day's volume and what share of the day's high to low range it accounts for. Each bucket's range is measured inside that bucket, so the thirteen range shares overlap and sum past 100%. Read them as coverage, not as a partition.
| et_time | volume_share_pct | range_share_pct |
|---|---|---|
| 09:30 | 15.5 | 54.5 |
| 10:00 | 9.8 | 38.1 |
| 10:30 | 8 | 31.5 |
| 11:00 | 7 | 27.6 |
| 11:30 | 6 | 24.7 |
| 12:00 | 5.4 | 22.6 |
| 12:30 | 4.9 | 21.3 |
| 13:00 | 5 | 21.6 |
| 13:30 | 4.9 | 20.8 |
| 14:00 | 5.4 | 21.7 |
| 14:30 | 5.5 | 20.6 |
| 15:00 | 6.5 | 21 |
| 15:30 | 15.9 | 29.1 |
The exact SQL behind every number
WITH bars AS (
SELECT
ticker,
toDate(toTimeZone(window_start, 'America/New_York')) AS trade_day,
toHour(toTimeZone(window_start, 'America/New_York')) * 60
+ toMinute(toTimeZone(window_start, 'America/New_York')) AS et_minute,
high,
low,
volume
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker IN ('SPY', 'QQQ', 'AAPL', 'MSFT', 'NVDA', 'KO')
AND window_start >= '2023-01-01'
AND window_start < '2026-01-01'
),
regular AS (
SELECT *
FROM bars
WHERE et_minute >= 570 AND et_minute < 960
),
day_totals AS (
SELECT
ticker,
trade_day,
max(high) AS day_high,
min(low) AS day_low,
sum(volume) AS day_volume,
count() AS bar_count
FROM regular
GROUP BY ticker, trade_day
HAVING bar_count >= 300
AND day_high > day_low
AND day_volume > 0
),
half_hours AS (
SELECT
ticker,
trade_day,
intDiv(et_minute - 570, 30) AS bucket,
max(high) AS bucket_high,
min(low) AS bucket_low,
sum(volume) AS bucket_volume
FROM regular
GROUP BY ticker, trade_day, bucket
)
SELECT
formatDateTime(toDateTime((570 + h.bucket * 30) * 60, 'UTC'), '%H:%i') AS et_time,
round(avg(100 * toFloat64(h.bucket_volume) / toFloat64(d.day_volume)), 1) AS volume_share_pct,
round(avg(100 * toFloat64(h.bucket_high - h.bucket_low)
/ toFloat64(d.day_high - d.day_low)), 1) AS range_share_pct
FROM half_hours AS h
INNER JOIN day_totals AS d
ON d.ticker = h.ticker AND d.trade_day = h.trade_day
GROUP BY h.bucket
ORDER BY h.bucketThe 09:30 bucket carries 15.5% of regular-session volume and covers 54.5% of the day's range. By 12:00 the volume share is down to 5.4%, near the low of the session, the midday lull mapped out in why trading volume dies at midday. The last bucket, 15:30, runs back up to 15.9% of volume and 29.1% of the range. The session is U-shaped in both measures, and the opening leg of the U is the one the rule is about.
Is there anything special about 10:00?
Everything above treats the first thirty minutes as a block. The rule makes a sharper claim: that the useful boundary falls at 10:00. Testing that needs minute resolution. This panel averages each clock minute's own high to low range across the six names and every session in the window, quoted in basis points, where one basis point is 0.01% of the price.
| et_time | range_bps | bars_measured |
|---|---|---|
| 09:30 | 33.59 | 4512 |
| 09:31 | 26.31 | 4512 |
| 09:32 | 23.45 | 4512 |
| 09:33 | 21.26 | 4512 |
| 09:34 | 19.86 | 4512 |
| 09:35 | 19.78 | 4512 |
| 09:36 | 18.66 | 4512 |
| 09:37 | 17.39 | 4512 |
| 09:38 | 16.89 | 4512 |
| 09:39 | 16.2 | 4512 |
| 09:40 | 16.96 | 4512 |
| 09:41 | 15.74 | 4512 |
| 09:42 | 15.37 | 4512 |
| 09:43 | 15.13 | 4512 |
| 09:44 | 14.2 | 4512 |
| 09:45 | 16.4 | 4512 |
| 09:46 | 15.27 | 4512 |
| 09:47 | 14.48 | 4512 |
| 09:48 | 14.19 | 4512 |
| 09:49 | 13.72 | 4512 |
The exact SQL behind every number
WITH bars AS (
SELECT
toHour(toTimeZone(window_start, 'America/New_York')) * 60
+ toMinute(toTimeZone(window_start, 'America/New_York')) AS et_minute,
high,
low
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker IN ('SPY', 'QQQ', 'AAPL', 'MSFT', 'NVDA', 'KO')
AND window_start >= '2023-01-01'
AND window_start < '2026-01-01'
)
SELECT
formatDateTime(toDateTime(et_minute * 60, 'UTC'), '%H:%i') AS et_time,
round(avg(10000 * toFloat64(high - low) / toFloat64(low)), 2) AS range_bps,
count() AS bars_measured
FROM bars
WHERE et_minute >= 570
AND et_minute < 660
AND low > 0
GROUP BY et_minute
ORDER BY et_minuteThe curve falls steeply and smoothly. The 09:30 minute averages 33.59 basis points of range. At 09:59 it reads 11.45, the 10:00 minute prints 17.34, and 10:01 prints 13.82. Nothing in the shape of the decay marks 10:00 out from the minutes on either side of it. By 10:59 the average is 8.08 basis points and still drifting lower, which is the other half of the finding: the settling process is not finished at 10:00 either. A quantity that changes continuously has no natural cut point, so 10:00 is a rounding of it, and 09:45 or 10:15 would round it differently with no measurable penalty.
Where the rule has a real mechanical basis
Take the clock time out and what remains is sound. It is about execution quality rather than direction.
- Residual auction imbalance. Interest left unfilled by the opening cross keeps working into the first minutes, so early prints can carry size that has little to do with where the name settles.
- Wide spreads. A round trip pays the spread twice, and paying it at its widest is a cost that has nothing to do with whether the trade idea was any good.
- Slow price discovery in thin names. A stock that prints a few hundred trades a session can take many minutes to find a level, and its first print can sit well away from where it spends the day.
- Overnight information still being priced. Company results, guidance and index changes released outside the session get worked into the price during the first minutes of continuous trading.
The spread piece is the one that can be watched directly. Below is a single pinned session, 10 March 2026, for two of the most heavily traded names on the tape, with the median quoted bid to offer gap in each five minute bucket of the first hour.
| et_time | spy_spread_bps | aapl_spread_bps |
|---|---|---|
| 09:30 | 0.44 | 2.71 |
| 09:35 | 0.44 | 2.31 |
| 09:40 | 0.44 | 2.32 |
| 09:45 | 0.44 | 2.71 |
| 09:50 | 0.44 | 2.32 |
| 09:55 | 0.3 | 2.32 |
| 10:00 | 0.44 | 1.94 |
| 10:05 | 0.44 | 1.93 |
| 10:10 | 0.3 | 1.54 |
| 10:15 | 0.3 | 1.54 |
| 10:20 | 0.3 | 1.54 |
| 10:25 | 0.3 | 1.15 |
The exact SQL behind every number
SELECT
formatDateTime(toStartOfFiveMinutes(toTimeZone(sip_timestamp, 'America/New_York')), '%H:%i') AS et_time,
round(quantileDeterministicIf(0.5)(
10000 * (toFloat64(ask_price) - toFloat64(bid_price))
/ ((toFloat64(ask_price) + toFloat64(bid_price)) / 2),
toUInt64(sequence_number),
ticker = 'SPY'), 2) AS spy_spread_bps,
round(quantileDeterministicIf(0.5)(
10000 * (toFloat64(ask_price) - toFloat64(bid_price))
/ ((toFloat64(ask_price) + toFloat64(bid_price)) / 2),
toUInt64(sequence_number),
ticker = 'AAPL'), 2) AS aapl_spread_bps
FROM global_markets.cache_stocks_quotes
WHERE ticker IN ('SPY', 'AAPL')
AND sip_timestamp >= '2026-03-10 13:29:00'
AND sip_timestamp < '2026-03-10 14:31:00'
AND bid_price > 0
AND ask_price > bid_price
AND sequence_number >= 0
AND (toHour(toTimeZone(sip_timestamp, 'America/New_York')) * 60
+ toMinute(toTimeZone(sip_timestamp, 'America/New_York'))) >= 570
AND (toHour(toTimeZone(sip_timestamp, 'America/New_York')) * 60
+ toMinute(toTimeZone(sip_timestamp, 'America/New_York'))) < 630
GROUP BY et_time
HAVING countIf(ticker = 'SPY') > 0
AND countIf(ticker = 'AAPL') > 0
ORDER BY et_timeIn the 09:30 bucket of that session the median gap measured 0.44 basis points in SPY and 2.71 in AAPL. In the 10:25 bucket the same measures read 0.3 and 1.15. A one cent quote is a different basis-point cost in a $600 instrument than in a $200 one, so compare each series with itself rather than across the two. Both drift lower through the hour, in the same gradual way the range decays, with no bucket where the spread abruptly becomes normal. One pinned session is an illustration of the shape, and the mechanics behind it are covered in why spreads widen at the open.
What the rule is actually a proxy for
Read as a statement about the clock, the 10:00 AM rule is folklore. Read as a habit, it is a crude proxy for a condition that can be measured outright: the spread has come back toward its usual level, and the opening imbalance has worked its way out of the tape. The proxy is crude in both directions. In a name as liquid as SPY on a quiet session, that condition is met within minutes of the open, and a fixed wait to 10:00 sits out the most active stretch of the day. In a thin name, or in a session where a company reported overnight, the condition can still be unmet at 10:00.
The measurable version is available to anyone: quoted spread against its own recent median, and volume against the usual profile for that minute. Rules of thumb like this one, and like the 7 percent sell rule, tend to encode a real observation and then attach a number to it that the data never chose. The observation is worth keeping. The number is worth checking.
FAQ
What is the 10:00 AM rule in stocks?
It is an informal day-trading habit of taking no new position until 10:00 a.m. ET, on the view that the opening half hour is overnight order imbalance clearing rather than a trend. It is a convention passed between traders, not an exchange rule, and the exchanges schedule nothing at that time.
Is the 10:00 AM rule true?
Partly. The opening half hour really does carry an outsized share of the day's volume and price range, which the panels above measure directly. The 10:00 boundary itself is arbitrary: minute by minute volatility decays smoothly across the whole first hour, with no step at the top of the hour.
How much of the daily range happens in the first 30 minutes?
Across six heavily traded names over 2023 through 2025, the 09:30 to 10:00 window covered about 54.5% of the full session high to low range, against the 7.7% a thirty minute slice would hold on an even split. The first panel breaks the figure out name by name.
Why is the bid/ask spread wider right after the open?
Quotes are being reposted around an overnight gap in price, and the interest left over from the opening auction is still working through the book. Both conditions ease within the first minutes of continuous trading, which is what the five minute spread trace above shows.
What time of day is trading volume lowest?
Around midday. Volume share bottoms out near the middle of the session before the closing half hour picks it back up, as the half hour panel here measures and why trading volume dies at midday explores in more detail.
Full data notes
The two long-run panels use a fixed window, 1 January 2023 through 31 December 2025, so the figures on this page are long-run behaviour and do not move with the current month. Regular trading only, 09:30 to 16:00 ET, grouped from the Eastern clock time of each bar. The half hour panel also requires at least 300 regular-session bars in a day, which drops the shortened half-day sessions around holidays and leaves all thirteen buckets comparable. Basis points are 0.01%, computed off each bar's own low. The spread panel is a single pinned session rather than an average, and the median in each bucket is taken over the posted quotes in that bucket, weighted by each quote's own sequence number so the same bucket returns the same median on every re-run.
| symbol | sample_size | avg_bar_count | min_bar_count |
|---|---|---|---|
| KO | 752 | 388.1 | 211 |
| MSFT | 752 | 388.1 | 211 |
| SPY | 752 | 388.1 | 211 |
| AAPL | 752 | 388.1 | 211 |
| QQQ | 752 | 388.1 | 211 |
| NVDA | 752 | 388.1 | 211 |
The exact SQL behind every number
WITH bars AS (
SELECT
ticker,
toDate(toTimeZone(window_start, 'America/New_York')) AS trade_day,
toHour(toTimeZone(window_start, 'America/New_York')) * 60
+ toMinute(toTimeZone(window_start, 'America/New_York')) AS et_minute
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker IN ('SPY', 'QQQ', 'AAPL', 'MSFT', 'NVDA', 'KO')
AND window_start >= '2023-01-01'
AND window_start < '2026-01-01'
),
per_day AS (
SELECT
ticker,
trade_day,
count() AS bar_count
FROM bars
WHERE et_minute >= 570 AND et_minute < 960
GROUP BY ticker, trade_day
)
SELECT
ticker AS symbol,
count() AS sample_size,
round(avg(bar_count), 1) AS avg_bar_count,
min(bar_count) AS min_bar_count
FROM per_day
GROUP BY ticker
ORDER BY sample_size DESCCoverage runs from 752 sessions for the best covered of the 6 names down to 752 for the thinnest, averaging 388.1 priced minutes per session at the top of that list against the 390 a full session can hold.
Every panel here ships with the exact SQL beneath it, so the opening half hour can be re-measured on any name and any window. To run the same test on a ticker you follow, ask for it in plain English on the Strasmore terminal.