Index Rebalance Day and the Closing Auction
Index rebalance day puts the year's biggest size through the closing auction. How the S&P and Russell rebalances print, with measured volume multiples.
On index rebalance day, the closing auction carries more size than any other session of the quarter. Index funds are measured against the index's own official closing price, and the auction is the one venue that guarantees that price in size. The dates are published months ahead, which makes rebalance day the most crowded and the most predictable print on the calendar.
Why index rebalances concentrate in the closing auction
An index fund's job is to match an index, not to beat it. The index is valued once a day, at the official closing price of every member, so a fund holding the index's exact weights at that print matches the index exactly. A fund that bought at 2:15 p.m. instead owns the stock at a price the index never used, and that difference shows up as tracking error, the gap between a fund's return and its benchmark's.
That puts the closing auction at the center of the day. The auction is a single call at 4:00 p.m. ET: market-on-close orders, limit-on-close orders and eligible continuous-book interest cross at one price, and that price becomes the official close. No other venue can promise it.
A rebalance also arrives as a dated instruction. The index provider announces that a member's share count, float factor or membership changes effective a named session, and every fund tracking that index needs its old weight gone and its new weight on by that session's close. Thousands of funds reading one instruction with one deadline queue into the same few seconds.
The index rebalance calendar: which dates matter
As of October 2026, four recurring events produce most of the outlier closes.
- S&P quarterly rebalance. S&P Dow Jones Indices refreshes share counts and float factors across the S&P 500 and its siblings each quarter, effective after the close of the third Friday in March, June, September and December. The fund trade prints in that Friday's auction, the same session as triple witching expiration.
- Russell reconstitution. FTSE Russell rebuilds the Russell 1000 and Russell 2000 from scratch once a year, effective at the open after the last Friday in June. Recon Friday's close is the auction most market-structure desks point to for the year's largest prints.
- MSCI index reviews. MSCI implements its quarterly reviews as of the close of the last business day of February, May, August and November, with May and November the larger pair. The trade lands in that day's closing auction.
- Single-name index changes. An add or a delete announced between scheduled reviews takes effect before the open of a named session, so the fund trade prints in the session before. Small in aggregate, enormous in the one name changing hands.
The Nasdaq-100 annual reconstitution in December follows the same shape, effective before the open after the third Friday.
How much bigger is the close on index rebalance day?
Rather than assert that it is huge, measure it. The panel below takes three household names across four rebalance sessions, two Russell recon Fridays and two S&P quarterly Fridays, and compares the shares that printed in the 4:00 p.m. minute bar against the median of that same minute over the name's previous twenty sessions.
| label | close_minute_shares_mm | typical_close_shares_mm | close_volume_multiple |
|---|---|---|---|
| MSFT Russell recon Jun-2026 | 16.09 | 0.33 | 48.1 |
| AAPL S&P quarterly Dec-2025 | 19.28 | 0.57 | 33.6 |
| AAPL Russell recon Jun-2026 | 9.85 | 0.33 | 29.7 |
| KO Russell recon Jun-2025 | 0.51 | 0.16 | 3.3 |
| AAPL Russell recon Jun-2025 | 2.5 | 0.78 | 3.2 |
| AAPL S&P quarterly Mar-2026 | 1.4 | 0.47 | 3 |
| MSFT S&P quarterly Dec-2025 | 0.92 | 0.34 | 2.7 |
| MSFT S&P quarterly Mar-2026 | 0.43 | 0.26 | 1.7 |
| MSFT Russell recon Jun-2025 | 0.52 | 0.31 | 1.7 |
| KO S&P quarterly Dec-2025 | 0.44 | 0.28 | 1.5 |
| KO S&P quarterly Mar-2026 | 0.17 | 0.22 | 0.8 |
| KO Russell recon Jun-2026 | 0 | 0.11 | 0 |
The exact SQL behind every number
WITH close_minute AS
(
SELECT
ticker,
toDate(toTimeZone(window_start, 'America/New_York')) AS session_date,
toFloat64(sum(volume)) AS close_volume
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker IN ('AAPL', 'MSFT', 'KO')
AND window_start >= '2025-05-01'
AND window_start < '2026-06-29'
AND toHour(toTimeZone(window_start, 'America/New_York')) = 16
AND toMinute(toTimeZone(window_start, 'America/New_York')) = 0
GROUP BY ticker, session_date
),
ranked AS
(
SELECT
ticker,
session_date,
close_volume,
row_number() OVER (PARTITION BY ticker ORDER BY session_date) AS n
FROM close_minute
),
events AS
(
SELECT
ticker,
close_volume,
n,
multiIf(session_date = '2025-06-27', 'Russell recon Jun-2025',
session_date = '2025-12-19', 'S&P quarterly Dec-2025',
session_date = '2026-03-20', 'S&P quarterly Mar-2026',
'Russell recon Jun-2026') AS event
FROM ranked
WHERE session_date IN ('2025-06-27', '2025-12-19', '2026-03-20', '2026-06-26')
)
SELECT
concat(e.ticker, ' ', e.event) AS label,
round(e.close_volume / 1e6, 2) AS close_minute_shares_mm,
round(quantileDeterministic(0.5)(b.close_volume, b.n) / 1e6, 2) AS typical_close_shares_mm,
round(e.close_volume / quantileDeterministic(0.5)(b.close_volume, b.n), 1) AS close_volume_multiple
FROM events AS e
INNER JOIN ranked AS b ON b.ticker = e.ticker
WHERE b.n >= e.n - 20
AND b.n <= e.n - 1
GROUP BY e.ticker, e.event, e.close_volume
ORDER BY close_volume_multiple DESCThe widest row is MSFT Russell recon Jun-2026, at 48.1x its own twenty-session median: 16.09 million shares in one minute against a typical 0.33 million. The twelve readings do not all point the same way. At the soft end of the panel, KO Russell recon Jun-2026 measured 0x, a closing minute that carried a fraction of that name's usual closing-minute size rather than a multiple of it, which is what a thin or absent 16:00 bar looks like in a minute-bar feed. Read the panel row by row rather than as one number.
Which sessions in a month carry the biggest close?
Zoom out to a full month and the concentration is easy to place. The next panel tracks, session by session through June 2026, the share of each name's regular-session volume that printed in the closing minute.
| session | session_label | spy_close_share_pct | aapl_close_share_pct |
|---|---|---|---|
| 2026-06-01 | Jun 1 | 0.44 | 2.01 |
| 2026-06-02 | Jun 2 | 0.43 | 0.84 |
| 2026-06-03 | Jun 3 | 1.04 | 1.15 |
| 2026-06-04 | Jun 4 | 0.34 | 1.19 |
| 2026-06-05 | Jun 5 | 0.56 | 0.64 |
| 2026-06-08 | Jun 8 | 0.42 | 0.54 |
| 2026-06-09 | Jun 9 | 0.63 | 0.44 |
| 2026-06-10 | Jun 10 | 1.01 | 1.09 |
| 2026-06-11 | Jun 11 | 0.75 | 0.83 |
| 2026-06-12 | Jun 12 | 0.6 | 0.46 |
| 2026-06-15 | Jun 15 | 1.1 | 0.92 |
| 2026-06-16 | Jun 16 | 0.62 | 1.67 |
| 2026-06-17 | Jun 17 | 0.35 | 1.18 |
| 2026-06-18 | Jun 18 | 0.64 | 2.53 |
| 2026-06-22 | Jun 22 | 0.42 | 0.84 |
| 2026-06-23 | Jun 23 | 0.51 | 0.87 |
| 2026-06-24 | Jun 24 | 0.81 | 0.71 |
| 2026-06-25 | Jun 25 | 1.03 | 0.58 |
| 2026-06-26 | Jun 26 | 1.47 | 9.4 |
| 2026-06-29 | Jun 29 | 0.93 | 5.51 |
The exact SQL behind every number
WITH bars AS
(
SELECT
ticker,
toDate(toTimeZone(window_start, 'America/New_York')) AS session_date,
toHour(toTimeZone(window_start, 'America/New_York')) * 60
+ toMinute(toTimeZone(window_start, 'America/New_York')) AS et_minute,
toFloat64(volume) AS bar_volume
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker IN ('SPY', 'AAPL')
AND window_start >= '2026-06-01'
AND window_start < '2026-07-01'
)
SELECT
toString(session_date) AS session,
formatDateTime(session_date, '%b %e') AS session_label,
round(100 * sumIf(bar_volume, ticker = 'SPY' AND et_minute = 960)
/ sumIf(bar_volume, ticker = 'SPY'), 2) AS spy_close_share_pct,
round(100 * sumIf(bar_volume, ticker = 'AAPL' AND et_minute = 960)
/ sumIf(bar_volume, ticker = 'AAPL'), 2) AS aapl_close_share_pct
FROM bars
WHERE et_minute >= 570
AND et_minute <= 960
GROUP BY session_date
HAVING sumIf(bar_volume, ticker = 'SPY') > 0
AND sumIf(bar_volume, ticker = 'AAPL') > 0
ORDER BY session_dateOn Jun 26, the last Friday of that month, SPY's closing minute carried 1.47% of its session volume and AAPL's carried 9.4%. On Jun 1, an ordinary session at the start of the month, the same two readings measured 0.44% and 2.01%. Read across the series and a handful of sessions sit well apart from the rest of the month.
What the volume curve looks like on a rebalance session
Inside one rebalance session the curve is lopsided. This panel splits that June recon Friday into half-hour buckets for a single name and puts each bucket's share of the session next to the average share of the same bucket over the twenty sessions before it.
| et_time | recon_day_pct | typical_day_pct |
|---|---|---|
| 09:30 | 9.7 | 15.49 |
| 10:00 | 5.55 | 8.69 |
| 10:30 | 5.4 | 7.29 |
| 11:00 | 4.51 | 6.19 |
| 11:30 | 6.59 | 6.01 |
| 12:00 | 6.04 | 5.42 |
| 12:30 | 4.29 | 5.57 |
| 13:00 | 3.32 | 5.61 |
| 13:30 | 2.92 | 5.66 |
| 14:00 | 4.97 | 5.22 |
| 14:30 | 6.21 | 5.35 |
| 15:00 | 8.65 | 6.36 |
| 15:30 | 22.46 | 15.4 |
| 16:00 | 9.4 | 1.74 |
The exact SQL behind every number
WITH bars AS
(
SELECT
toDate(toTimeZone(window_start, 'America/New_York')) AS session_date,
toHour(toTimeZone(window_start, 'America/New_York')) * 60
+ toMinute(toTimeZone(window_start, 'America/New_York')) AS et_minute,
toFloat64(volume) AS bar_volume
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker = 'AAPL'
AND window_start >= '2026-05-28'
AND window_start < '2026-06-27'
),
buckets AS
(
SELECT
session_date,
if(et_minute = 960, 960, intDiv(et_minute, 30) * 30) AS bucket_min,
sum(bar_volume) AS bucket_volume
FROM bars
WHERE et_minute >= 570
AND et_minute <= 960
GROUP BY session_date, bucket_min
),
day_totals AS
(
SELECT
session_date,
sum(bucket_volume) AS day_volume
FROM buckets
GROUP BY session_date
),
shares AS
(
SELECT
b.session_date AS session_date,
b.bucket_min AS bucket_min,
100 * b.bucket_volume / d.day_volume AS share_pct
FROM buckets AS b
INNER JOIN day_totals AS d ON d.session_date = b.session_date
)
SELECT
formatDateTime(toDateTime(bucket_min * 60, 'UTC'), '%H:%i') AS et_time,
round(avgIf(share_pct, session_date = '2026-06-26'), 2) AS recon_day_pct,
round(avgIf(share_pct, session_date < '2026-06-26'), 2) AS typical_day_pct
FROM shares
GROUP BY bucket_min
HAVING countIf(session_date = '2026-06-26') > 0
AND countIf(session_date < '2026-06-26') > 0
ORDER BY bucket_minThe 16:00 bucket is the closing minute on its own, and it took 9.4% of that session against 1.74% on an ordinary day. The 09:30 bucket, the opening half hour, took 9.7%. The dip through the middle of both curves is the ordinary midday volume lull, unchanged by the rebalance. What changes is the last bucket.
Placing a market-on-close order into a rebalance
A market-on-close order is an instruction to trade at the official close with no price limit. A limit-on-close order adds a cap and participates only if the auction clears at or inside the price named. Both run on a clock, and the clock is the part most readers have never seen.
On a normal session, market-on-close and limit-on-close interest for the primary exchange's auction has to be entered by 3:50 p.m. ET. After that cutoff an existing order can no longer be cancelled, and fresh on-close interest is accepted only inside narrow exchange rules, generally on the side that offsets a published imbalance.
From that same minute the exchange starts publishing the auction book: paired shares, the size left unpaired on one side, and an indicative clearing price. The NYSE closing auction and the Nasdaq Closing Cross differ in their order types and in the role the exchange's assigned market maker plays, and those two pages walk each venue in detail.
The indication is a snapshot of the book at the moment it was published, not a quote for the close. Between 3:50 and 4:00 the offsetting side arrives, and on a rebalance session it arrives in size.
How far can the close print from the 3:50 p.m. indication?
Price tells the same story as volume. This panel measures, for the same names and sessions, the distance between the 3:50 p.m. price and the last print of the closing minute in basis points, one basis point being one hundredth of one percent, alongside each name's median distance over its prior twenty sessions.
| label | event_drift_bps | typical_drift_bps |
|---|---|---|
| AAPL S&P quarterly Dec-2025 | 110.6 | 15.2 |
| KO S&P quarterly Dec-2025 | 37.7 | 6.2 |
| MSFT Russell recon Jun-2025 | 37.4 | 8.1 |
| KO Russell recon Jun-2026 | 33.4 | 8.1 |
| MSFT Russell recon Jun-2026 | 33.3 | 18.8 |
| MSFT S&P quarterly Mar-2026 | 24 | 8.4 |
| AAPL Russell recon Jun-2025 | 21.4 | 9.5 |
| AAPL S&P quarterly Mar-2026 | 16.2 | 17.9 |
| KO S&P quarterly Mar-2026 | 14.7 | 6.8 |
| AAPL Russell recon Jun-2026 | 6 | 17.2 |
| KO Russell recon Jun-2025 | 5.7 | 10.9 |
| MSFT S&P quarterly Dec-2025 | 3.9 | 15 |
The exact SQL behind every number
WITH marks AS
(
SELECT
ticker,
toDate(toTimeZone(window_start, 'America/New_York')) AS session_date,
anyIf(toFloat64(close),
toHour(toTimeZone(window_start, 'America/New_York')) = 15
AND toMinute(toTimeZone(window_start, 'America/New_York')) = 50) AS px_350,
anyIf(toFloat64(close),
toHour(toTimeZone(window_start, 'America/New_York')) = 16
AND toMinute(toTimeZone(window_start, 'America/New_York')) = 0) AS px_close
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker IN ('AAPL', 'MSFT', 'KO')
AND window_start >= '2025-05-01'
AND window_start < '2026-06-29'
AND toHour(toTimeZone(window_start, 'America/New_York')) IN (15, 16)
GROUP BY ticker, session_date
HAVING px_350 > 0
AND px_close > 0
),
drift AS
(
SELECT
ticker,
session_date,
round(10000 * abs(px_close / px_350 - 1), 1) AS drift_bps,
row_number() OVER (PARTITION BY ticker ORDER BY session_date) AS n
FROM marks
),
events AS
(
SELECT
ticker,
drift_bps,
n,
multiIf(session_date = '2025-06-27', 'Russell recon Jun-2025',
session_date = '2025-12-19', 'S&P quarterly Dec-2025',
session_date = '2026-03-20', 'S&P quarterly Mar-2026',
'Russell recon Jun-2026') AS event
FROM drift
WHERE session_date IN ('2025-06-27', '2025-12-19', '2026-03-20', '2026-06-26')
)
SELECT
concat(e.ticker, ' ', e.event) AS label,
e.drift_bps AS event_drift_bps,
round(quantileDeterministic(0.5)(b.drift_bps, b.n), 1) AS typical_drift_bps
FROM events AS e
INNER JOIN drift AS b ON b.ticker = e.ticker
WHERE b.n >= e.n - 20
AND b.n <= e.n - 1
GROUP BY e.ticker, e.event, e.drift_bps
ORDER BY event_drift_bps DESCAAPL S&P quarterly Dec-2025 shows the widest travel in the panel: 110.6 basis points between the 3:50 p.m. price and the closing minute, against a median of 15.2 basis points across that name's previous twenty sessions. A market-on-close order resting in that auction accepted the full distance, whatever it turned out to be.
The two order types price different risks. A market-on-close order accepts price uncertainty and guarantees the fill. A limit-on-close order controls the price and accepts that it may go unfilled, leaving the position open into the next session. Leveraged and inverse funds add their own mechanical flow to the same minute, which the leveraged ETF rebalancing page covers.
How these panels measure the close
The official closing print is stamped 4:00:00 p.m. ET, so the minute bar stamped 16:00 is the cleanest proxy a minute-bar table offers. That bar can also hold other trades reported inside the same minute, which makes these counts a small overstatement of pure auction size. A pinned session can also carry a thin or absent 16:00 bar, which shows up as a multiple at or near zero instead of an elevated one, and the panels publish every row as measured rather than dropping the ones that sit against the headline. Baselines are medians over each name's previous twenty sessions, computed deterministically so a rerun of the same query returns the same figure. Every panel's exact SQL sits underneath it.
FAQ
What is index rebalance day?
It is the session a published index change takes effect. The index provider updates share counts, float factors or membership on a set date, and the funds tracking that index trade their side in that session's closing auction.
Why do index funds trade at the closing auction?
An index is valued at its members' official closing prices, so a fund that trades at the close owns the same prices the index used. Trading at any other time of day opens a gap between the fund's return and the benchmark's, which is tracking error.
Which index rebalance carries the most volume?
The June Russell reconstitution is the session market-structure desks watch for the year's largest auction prints. The S&P quarterly Fridays come next, and they coincide with index futures and options expiration, which stacks two flows into one close.
Can a market-on-close order print far from the 3:50 p.m. indication?
Yes. On-close orders are due by 3:50 p.m. ET and the exchange publishes its first imbalance picture from that minute, but the picture describes the book at publication time. Offsetting interest arriving in the final ten minutes moves the clearing price, and a market-on-close order carries no price limit, so it settles at whatever the auction prints.
Every panel here ships with the SQL that produced it, so you can open one and follow how each multiple was counted. To measure a rebalance close on a name you follow, ask the question in plain English on the Strasmore terminal.