Market Recap: Week of July 20 2026
We look at how SPY move after July options expiration. See the index scoreboard, sector dispersion, weekly breadth, and where the market dollars flow for this period.
5
The week of July 20, 2026 na the first full week wey follow July monthly options expiration, and e run for 5 sessions without any closure inside. SPY close-over-close change for the week reach -0.59%, and when we measure open-to-close against the trailing year of weeks, e rank 43 out of 53. Every number wey dey below come from one stored query, and every window dey pinned to specific dates, so if you run the SQL again, you go still see the same figures.
Di week for di board
Weekly changes start from Friday July 17 regular close reach Friday July 24. Di last column show di week wey pass, so di two dey side-by-side. Rows follow alphabetical order.
| ticker | price wey week start | price wey week end | week change (%) | last week change (%) |
|---|---|---|---|---|
| DIA | 520.76 | 518.79 | -0.38 | -0.95 |
| IWM | 294.09 | 291.2 | -0.98 | -0.63 |
| QQQ | 695.3 | 684.22 | -1.59 | -4.17 |
| SPY | 743.2 | 738.85 | -0.59 | -1.55 |
The exact SQL behind every number
SELECT ticker,
round(argMaxIf(toFloat64(close), window_start, window_start >= '2026-07-17 13:30:00' AND window_start < '2026-07-17 20:00:00'), 2) AS prior_week_close,
round(argMaxIf(toFloat64(close), window_start, window_start >= '2026-07-24 13:30:00' AND window_start < '2026-07-24 20:00:00'), 2) AS week_close,
round((argMaxIf(toFloat64(close), window_start, window_start >= '2026-07-24 13:30:00' AND window_start < '2026-07-24 20:00:00')
/ argMaxIf(toFloat64(close), window_start, window_start >= '2026-07-17 13:30:00' AND window_start < '2026-07-17 20:00:00') - 1) * 100, 2) AS week_change_pct,
round((argMaxIf(toFloat64(close), window_start, window_start >= '2026-07-17 13:30:00' AND window_start < '2026-07-17 20:00:00')
/ argMaxIf(toFloat64(close), window_start, window_start >= '2026-07-10 13:30:00' AND window_start < '2026-07-10 20:00:00') - 1) * 100, 2) AS prior_week_change_pct
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker IN ('SPY', 'QQQ', 'DIA', 'IWM')
AND ((window_start >= '2026-07-10 13:30:00' AND window_start < '2026-07-10 20:00:00')
OR (window_start >= '2026-07-17 13:30:00' AND window_start < '2026-07-17 20:00:00')
OR (window_start >= '2026-07-24 13:30:00' AND window_start < '2026-07-24 20:00:00'))
GROUP BY ticker
ORDER BY tickerFor di week: DIA move -0.38%, IWM -0.98%, QQQ -1.59%, and SPY -0.59% reach one $738.85 close. Di readings for di week before, for di same alphabetical order, na -0.95%, -0.63%, -4.17%, and -1.55%. How far di four indexes dey from each other na di first tin wey scoreboard dey measure, and e no dey small all di time.
The week against the trailing year
One week number no mean much if you no look at the distribution wey dey behind am. This panel dey recompute every trailing week using the same logic, from open-to-close throughout the regular session, and e dey rank this one inside am.
| open-to-close (%) | rank wey best | weeks wey we compare | first week | days wey market open |
|---|---|---|---|---|
| -1.1 | 43 | 53 | 2025-07-21 | 5 |
The exact SQL behind every number
SELECT round(anyIf(ret, wk = toDate('2026-07-20')), 2) AS week_open_to_close_pct,
arrayCount(x -> x > anyIf(ret, wk = toDate('2026-07-20')), groupArrayIf(ret, wk != toDate('2026-07-20'))) + 1 AS rank_best,
count() AS weeks_compared,
toString(min(wk)) AS first_week,
anyIf(sessions_measured, wk = toDate('2026-07-20')) AS sessions_this_week
FROM (
SELECT toStartOfWeek(toDate(toTimeZone(window_start, 'America/New_York')), 1) AS wk,
uniqExact(toDate(toTimeZone(window_start, 'America/New_York'))) AS sessions_measured,
(argMax(toFloat64(close), window_start) / argMin(toFloat64(open), window_start) - 1) * 100 AS ret
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker = 'SPY'
AND window_start >= toDateTime('2025-07-21 00:00:00')
AND window_start < toDateTime('2026-07-25 00:00:00')
AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
GROUP BY wk
HAVING sessions_measured >= 3
)If you measure am like that, SPY return -1.1% and e rank 43 out of 53 weeks wey go back reach 2025-07-21. Make you note the difference for the definition: this figure start from the first regular-session open of the week, but the scoreboard wey dey up so start from the close of the week before. We label both of dem everywhere wey dem appear.
Five sessions, one arc
| date | SPY close | change (%) | SPY shares (m) |
|---|---|---|---|
| 2026-07-20 | 742.1 | -0.15 | 40.1 |
| 2026-07-21 | 748.32 | 0.84 | 25.6 |
| 2026-07-22 | 747.39 | -0.12 | 26.6 |
| 2026-07-23 | 738.24 | -1.22 | 48 |
| 2026-07-24 | 738.85 | 0.08 | 40.1 |
The exact SQL behind every number
SELECT toString(d) AS date,
round(c, 2) AS spy_close,
round((c / prev_c - 1) * 100, 2) AS change_pct,
round(shares_m, 1) AS spy_shares_m
FROM (
SELECT d, c, shares_m,
lagInFrame(c) OVER (ORDER BY d ASC ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS prev_c
FROM (
SELECT toDate(toTimeZone(window_start, 'America/New_York')) AS d,
argMax(toFloat64(close), window_start) AS c,
toFloat64(sum(volume)) / 1e6 AS shares_m
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker = 'SPY'
AND ((window_start >= '2026-07-17 13:30:00' AND window_start < '2026-07-17 20:00:00')
OR (window_start >= '2026-07-20 13:30:00' AND window_start < '2026-07-24 20:00:00'))
AND (toHour(window_start) * 60 + toMinute(window_start)) BETWEEN 810 AND 1199
GROUP BY d
)
)
WHERE d >= '2026-07-20'
ORDER BY dateSession by session, SPY change read -0.15% for Monday, 0.84% for Tuesday, -0.12% for Wednesday, -1.22% for Thursday, and 0.08% for Friday, wey come finish at $738.85 on top 40.1 million shares. We measure Monday figure against the July 17 close, wey be the session wey come just before this period.
Breadth, session by session
Index level na just one number. Breadth dey count how many stocks follow am move, and na that number dey decide weda the green week wide or e narrow.
| date | stocks wey go up | stocks wey go down | stocks wey no move | names wey we check |
|---|---|---|---|---|
| 2026-07-20 | 1268 | 2632 | 45 | 3945 |
| 2026-07-21 | 2516 | 1450 | 41 | 4007 |
| 2026-07-22 | 1595 | 2332 | 33 | 3960 |
| 2026-07-23 | 1232 | 2815 | 30 | 4077 |
| 2026-07-24 | 2273 | 1635 | 42 | 3950 |
The exact SQL behind every number
SELECT toString(d) AS date, advancers, decliners, unchanged, measured_names
FROM (
SELECT d,
countIf(c > prev_c) AS advancers,
countIf(c < prev_c) AS decliners,
countIf(c = prev_c) AS unchanged,
count() AS measured_names
FROM (
SELECT d, c, dv,
lagInFrame(c) OVER (PARTITION BY ticker ORDER BY d ASC ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS prev_c
FROM (
SELECT ticker,
toDate(toTimeZone(window_start, 'America/New_York')) AS d,
argMax(toFloat64(close), window_start) AS c,
sum(toFloat64(close) * toFloat64(volume)) AS dv
FROM global_markets.delayed_stocks_minute_aggs
WHERE ((window_start >= '2026-07-17 13:30:00' AND window_start < '2026-07-17 20:00:00')
OR (window_start >= '2026-07-20 13:30:00' AND window_start < '2026-07-24 20:00:00'))
AND (toHour(window_start) * 60 + toMinute(window_start)) BETWEEN 810 AND 1199
GROUP BY ticker, d
)
)
WHERE d >= '2026-07-20' AND prev_c > 0 AND dv >= 5000000
GROUP BY d
)
ORDER BY dateFor Monday, 1268 out of 3945 measured names rise and 2632 fall. By Friday, the split show 2273 up against 1635 down, out of 3950. We dey exclude names wey no get close for both sides of a session, na why the measured count dey shift small-small everyday.
The same question wey we ask one time, for the whole week:
| stocks wey go up | stocks wey go down | stocks wey no move | names wey we check | liquidity filter comot am | advancer (%) |
|---|---|---|---|---|---|
| 2437 | 3630 | 33 | 6100 | 5099 | 40 |
The exact SQL behind every number
SELECT
countIf(cw > cp AND liquid) AS advancers,
countIf(cw < cp AND liquid) AS decliners,
countIf(cw = cp AND liquid) AS unchanged,
countIf(liquid) AS measured_names,
countIf(NOT liquid) AS dropped_by_liquidity_filter,
round(100.0 * countIf(cw > cp AND liquid) / countIf(liquid), 1) AS advancer_pct
FROM (
SELECT ticker, cp, cw, dv >= 5000000 AS liquid
FROM (
SELECT ticker,
argMaxIf(toFloat64(close), window_start, window_start < '2026-07-18 00:00:00') AS cp,
argMaxIf(toFloat64(close), window_start, window_start >= '2026-07-24 13:30:00') AS cw,
sumIf(toFloat64(close) * toFloat64(volume), window_start >= '2026-07-20 13:30:00') AS dv
FROM global_markets.delayed_stocks_minute_aggs
WHERE ((window_start >= '2026-07-17 13:30:00' AND window_start < '2026-07-17 20:00:00')
OR (window_start >= '2026-07-20 13:30:00' AND window_start < '2026-07-25 00:00:00'))
AND (toHour(window_start) * 60 + toMinute(window_start)) BETWEEN 810 AND 1199
GROUP BY ticker
HAVING cp > 0 AND cw > 0
)
)Across 6100 names wey clear the five-million-dollar regular-hours turnover bar, 2437 close above their July 17 close and 3630 close below, wey give us advancer share of 40%. The liquidity filter comot another 5099 names wey trade under that bar during the week. We count dem here instead of just throway dem.
Di sector scoreboard
Di eleven SPDR sector ETFs, from July 17 close go reach July 24, wey dem rank from best go worst.
| sector | week (%) | points wey e dey behind best |
|---|---|---|
| Energy | 3.33 | 0 |
| Utilities | 2.5 | 0.83 |
| Industrials | 1.78 | 1.55 |
| Materials | 1.47 | 1.86 |
| Real Estate | 1.12 | 2.21 |
| Health Care | 0.91 | 2.42 |
| Technology | 0.18 | 3.15 |
| Financials | 0.13 | 3.2 |
| Staples | -1.27 | 4.6 |
| Communications | -3.96 | 7.29 |
| Consumer Discretionary | -5.2 | 8.53 |
The exact SQL behind every number
SELECT sector, week_pct, round(max(week_pct) OVER () - week_pct, 2) AS points_behind_best
FROM (
SELECT multiIf(ticker = 'XLK', 'Technology', ticker = 'XLC', 'Communications', ticker = 'XLE', 'Energy',
ticker = 'XLF', 'Financials', ticker = 'XLI', 'Industrials', ticker = 'XLB', 'Materials',
ticker = 'XLP', 'Staples', ticker = 'XLRE', 'Real Estate', ticker = 'XLU', 'Utilities',
ticker = 'XLV', 'Health Care', 'Consumer Discretionary') AS sector,
round((cw / cp - 1) * 100, 2) AS week_pct
FROM (
SELECT ticker,
argMaxIf(toFloat64(close), window_start, window_start < '2026-07-18 00:00:00') AS cp,
argMaxIf(toFloat64(close), window_start, window_start >= '2026-07-24 13:30:00') AS cw
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker IN ('XLB', 'XLC', 'XLE', 'XLF', 'XLI', 'XLK', 'XLP', 'XLRE', 'XLU', 'XLV', 'XLY')
AND ((window_start >= '2026-07-17 13:30:00' AND window_start < '2026-07-17 20:00:00')
OR (window_start >= '2026-07-24 13:30:00' AND window_start < '2026-07-24 20:00:00'))
GROUP BY ticker
HAVING cp > 0 AND cw > 0
)
)
ORDER BY week_pct DESCEnergy stand for top of di board at 3.33%. Consumer Discretionary stand for bottom at -5.2%, 8.53 percentage points behind am. Dat gap na di sector dispersion for di week, and e dey important make person look am well: one week wey all eleven sector land within one point of each oda dey very different from one market wey di spread reach double digits.
Where the dollars go
| ticker | week dollar (bn) | leader (%) |
|---|---|---|
| MU | 158.1 | 100 |
| SPY | 133.9 | 84.7 |
| QQQ | 101.5 | 64.2 |
| NVDA | 93.9 | 59.4 |
| SNDK | 81.4 | 51.5 |
| TSLA | 79.1 | 50 |
| AMD | 57.9 | 36.6 |
| AAPL | 54.6 | 34.5 |
The exact SQL behind every number
SELECT ticker, week_dollar_bn, round(100 * week_dollar_bn / max(week_dollar_bn) OVER (), 1) AS pct_of_leader
FROM (
SELECT ticker,
round(sum(toFloat64(close) * toFloat64(volume)) / 1e9, 1) AS week_dollar_bn
FROM global_markets.delayed_stocks_minute_aggs
WHERE window_start >= '2026-07-20 13:30:00' AND window_start < '2026-07-24 20:00:00'
AND (toHour(window_start) * 60 + toMinute(window_start)) BETWEEN 810 AND 1199
AND ticker NOT IN ('SPCX')
GROUP BY ticker
ORDER BY week_dollar_bn DESC
LIMIT 8
)
ORDER BY week_dollar_bn DESCMU print 158.1 billion dollars of regular-hours turnover across the five sessions, e pass SPY wey get 133.9 billion and QQQ wey get 101.5 billion. Number eight for the board, AAPL, trade 34.5% of wetin the leader trade. Turnover dey show where the market spend the week dey argue, and e no mean say the market must gree with the price. The version of this measure for every single name na relative volume.
Di biggest movers for di week
Splits dey change di print but e no dey change di value: reverse split dey create fake four-digit gain, while forward split dey create fake collapse of di same size. Di week carry 26 reverse splits and 12 forward splits, and di two boards wey dey below exclude any name wey im split execute between di July 17 close and di July 24 close, wey be the exact interval wey we take measure di change. Di two boards also require five million dollars of regular-hours turnover.
| ticker | board | week (%) | week dollar (m) | days wey dem trade | dollar max (%) |
|---|---|---|---|---|---|
| STAK | gainers | 383.7 | 251.6 | 5 | 26 |
| ADVB | gainers | 277.1 | 875.7 | 5 | 90.3 |
| WLDS | gainers | 157.2 | 104.6 | 5 | 10.8 |
| ZYBT | gainers | 123.8 | 294.6 | 5 | 30.4 |
| CJMB | gainers | 111.3 | 131.8 | 5 | 13.6 |
| LVWR | gainers | 107.8 | 131.6 | 5 | 13.6 |
| OMH | gainers | 99.8 | 436.6 | 5 | 45 |
| PN | gainers | 97.5 | 89.7 | 5 | 9.3 |
| UTZ | gainers | 95.4 | 969.5 | 5 | 100 |
| GORO | gainers | 92.3 | 50.3 | 5 | 5.2 |
| LBGJ | decliners | -98.8 | 47.7 | 5 | 4.9 |
| SXTC | decliners | -97.4 | 40.6 | 5 | 4.2 |
| WETO | decliners | -86.4 | 8.8 | 5 | 0.9 |
| GVH | decliners | -78.8 | 17.2 | 5 | 1.8 |
| VEEE | decliners | -66.6 | 65.2 | 5 | 6.7 |
| CLBK | decliners | -53.5 | 953.7 | 5 | 98.4 |
| BIYA | decliners | -53.4 | 266.9 | 5 | 27.5 |
| LESL | decliners | -52.2 | 6.8 | 5 | 0.7 |
| QMLS | decliners | -48.7 | 37.2 | 5 | 3.8 |
| VCIG | decliners | -47.8 | 9.5 | 5 | 1 |
The exact SQL behind every number
SELECT ticker, board, week_pct, week_dollar_m, sessions_traded,
round(100 * week_dollar_m / max(week_dollar_m) OVER (), 1) AS pct_of_dollar_max
FROM (
SELECT 'gainers' AS board, ticker, round((cw / cp - 1) * 100, 1) AS week_pct,
round(dv / 1e6, 1) AS week_dollar_m, sessions_traded
FROM (
SELECT ticker,
argMaxIf(toFloat64(close), window_start, window_start < '2026-07-18 00:00:00') AS cp,
argMaxIf(toFloat64(close), window_start, window_start >= '2026-07-24 13:30:00') AS cw,
sumIf(toFloat64(close) * toFloat64(volume), window_start >= '2026-07-20 13:30:00') AS dv,
uniqExactIf(toDate(toTimeZone(window_start, 'America/New_York')), window_start >= '2026-07-20 13:30:00') AS sessions_traded
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker NOT IN ('SPCX')
AND ticker NOT IN (SELECT ticker FROM global_markets.stocks_splits WHERE execution_date > '2026-07-17' AND execution_date <= '2026-07-24')
AND ((window_start >= '2026-07-17 13:30:00' AND window_start < '2026-07-17 20:00:00')
OR (window_start >= '2026-07-20 13:30:00' AND window_start < '2026-07-25 00:00:00'))
AND (toHour(window_start) * 60 + toMinute(window_start)) BETWEEN 810 AND 1199
GROUP BY ticker
HAVING cp > 0 AND cw > 0 AND dv >= 5000000
)
ORDER BY week_pct DESC
LIMIT 10
UNION ALL
SELECT 'decliners' AS board, ticker, round((cw / cp - 1) * 100, 1) AS week_pct,
round(dv / 1e6, 1) AS week_dollar_m, sessions_traded
FROM (
SELECT ticker,
argMaxIf(toFloat64(close), window_start, window_start < '2026-07-18 00:00:00') AS cp,
argMaxIf(toFloat64(close), window_start, window_start >= '2026-07-24 13:30:00') AS cw,
sumIf(toFloat64(close) * toFloat64(volume), window_start >= '2026-07-20 13:30:00') AS dv,
uniqExactIf(toDate(toTimeZone(window_start, 'America/New_York')), window_start >= '2026-07-20 13:30:00') AS sessions_traded
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker NOT IN ('SPCX')
AND ticker NOT IN (SELECT ticker FROM global_markets.stocks_splits WHERE execution_date > '2026-07-17' AND execution_date <= '2026-07-24')
AND ((window_start >= '2026-07-17 13:30:00' AND window_start < '2026-07-17 20:00:00')
OR (window_start >= '2026-07-20 13:30:00' AND window_start < '2026-07-25 00:00:00'))
AND (toHour(window_start) * 60 + toMinute(window_start)) BETWEEN 810 AND 1199
GROUP BY ticker
HAVING cp > 0 AND cw > 0 AND dv >= 5000000
)
ORDER BY week_pct ASC
LIMIT 10
)
ORDER BY board DESC, abs(week_pct) DESCTo enter any of di boards, di move must be real. Di smallest gainer wey show rise 92.3%, and di smallest decliner print -47.8%. For di extremes, STAK move 383.7% on 251.6 million dollars traded, and LBGJ move -98.8% on 47.7 million. Dat decline dey close to the total quoted value of the name, so the board carry im own receipt: LBGJ print regular-hours bars on 5 of the five sessions, and no split execute against am between the two closes wey the board measure.
The quoted spread, session by session
Price na the main tin. The cost to finish one trade na the quoted spread, wey we measure for SPY one session at a time, straight from the raw NBBO tape instead of any summary.
| date | median spread (bps) | quote updates (m) | one sided quote count | crossed quote count |
|---|---|---|---|---|
| 2026-07-20 | 0.27 | 3.89 | 0 | 1084 |
| 2026-07-21 | 0.27 | 2.29 | 0 | 333 |
| 2026-07-22 | 0.27 | 2.51 | 0 | 747 |
| 2026-07-23 | 0.27 | 5.1 | 0 | 5906 |
| 2026-07-24 | 0.27 | 4.4 | 0 | 2777 |
The exact SQL behind every number
SELECT toString(toDate(sip_timestamp)) AS date,
round(quantileExactIf(0.5)(
10000 * (toFloat64(ask_price) - toFloat64(bid_price)) / ((toFloat64(ask_price) + toFloat64(bid_price)) / 2),
bid_price > 0 AND ask_price > 0 AND ask_price >= bid_price), 2) AS median_spread_bps,
round(count() / 1e6, 2) AS quote_updates_m,
countIf(bid_price <= 0 OR ask_price <= 0) AS one_sided_quote_count,
countIf(bid_price > ask_price AND bid_price > 0 AND ask_price > 0) AS crossed_quote_count
FROM global_markets.cache_stocks_quotes
WHERE ticker = 'SPY'
AND sip_timestamp >= '2026-07-20 13:30:00'
AND sip_timestamp < '2026-07-24 20:00:00'
AND (toHour(sip_timestamp) * 60 + toMinute(sip_timestamp)) BETWEEN 810 AND 1199
GROUP BY toDate(sip_timestamp)
HAVING countIf(bid_price > 0 AND ask_price > 0 AND ask_price >= bid_price) > 0
ORDER BY toDate(sip_timestamp)SPY median quoted spread measure 0.27 basis points of mid on Monday and 0.27 on Friday, base on 3.89 million and 4.4 million NBBO updates. The last two columns na the disclosure instead of the finding: 0 one-sided quotes and 1084 crossed quotes dem count out of Monday median instead of make dem just comot am quietly. One crossed quote, wey bid dey higher pass ask, na normal tin wey dey happen for consolidated feed wey dem join together from many venues for nanosecond resolution.
The options week afta monthly expiration
July monthly expiration bin happen for the Friday before this week start, so everi contract wey dey trade for these five sessions na survivor of that event. Expiration timing dey set the rhythm wey the same-day crowd dey follow trade.
| date | contracts (m) | pct 0dte | pct call | pct expiring jul24 |
|---|---|---|---|---|
| 2026-07-20 | 64 | 40.2 | 55.3 | 15.1 |
| 2026-07-21 | 57.3 | 29.5 | 53.9 | 17 |
| 2026-07-22 | 55.8 | 36.1 | 56.4 | 19.9 |
| 2026-07-23 | 66.2 | 26.4 | 53.4 | 26.4 |
| 2026-07-24 | 71.1 | 49 | 53.5 | 49 |
The exact SQL behind every number
SELECT toString(toDate(sip_timestamp)) AS date,
round(toFloat64(sum(size)) / 1e6, 1) AS contracts_m,
round(100.0 * sumIf(size, substring(ticker, length(ticker) - 14, 6) = formatDateTime(toDate(sip_timestamp), '%y%m%d')) / sum(size), 1) AS pct_0dte,
round(100.0 * sumIf(size, substring(ticker, length(ticker) - 8, 1) = 'C') / sum(size), 1) AS pct_call,
round(100.0 * sumIf(size, substring(ticker, length(ticker) - 14, 6) = '260724') / sum(size), 1) AS pct_expiring_jul24
FROM global_markets.options_trades
WHERE sip_timestamp >= '2026-07-20 00:00:00' AND sip_timestamp < '2026-07-25 00:00:00'
GROUP BY toDate(sip_timestamp)
ORDER BY toDate(sip_timestamp)Contract volume bin reach 64 million for Monday and 71.1 million for Friday. The same-day share, wey mean contracts wey dey expire for the same session wey dem trade am, bin read 40.2% for Monday against 49% for Friday, wey be the expiry session for the week. Calls bin take 53.5% of Friday contract volume. And the pull of that Friday bin dey clear from the opening session: 15.1% of Monday volume don already dey inside contracts wey get expiry date for that day. Before expiry wey get that kain heavy open interest, some traders dey watch max pain, the strike price wey the expiring chain go pay holders the least amount, even though whether settlement go truly land near that price na question wey you suppose check instead of just assume.
Rates across the week
| date | yield 2y (%) | yield 10y (%) | yield 30y (%) | spread 2s10s (bp) | chg 10y from prior close (bp) |
|---|---|---|---|---|---|
| 2026-07-17 | 4.18 | 4.55 | 5.06 | 37 | 0 |
| 2026-07-20 | 4.21 | 4.6 | 5.11 | 39 | 5 |
| 2026-07-21 | 4.26 | 4.63 | 5.13 | 37 | 8 |
| 2026-07-22 | 4.31 | 4.67 | 5.15 | 36 | 12 |
| 2026-07-23 | 4.37 | 4.71 | 5.17 | 34 | 16 |
| 2026-07-24 | 4.33 | 4.69 | 5.16 | 36 | 14 |
The exact SQL behind every number
SELECT toString(date) AS date,
round(toFloat64(yield_2_year), 2) AS yield_2y_pct,
round(toFloat64(yield_10_year), 2) AS yield_10y_pct,
round(toFloat64(yield_30_year), 2) AS yield_30y_pct,
round((toFloat64(yield_10_year) - toFloat64(yield_2_year)) * 100) AS spread_2s10s_bp,
round((toFloat64(yield_10_year) - (SELECT toFloat64(any(yield_10_year)) FROM global_markets.treasury_yields WHERE date = '2026-07-17')) * 100) AS chg_10y_from_prior_close_bp
FROM global_markets.treasury_yields
WHERE date >= '2026-07-17' AND date <= '2026-07-24'
ORDER BY dateIf we measure from the July 17 print, the ten-year move na 14 basis points to reach 4.69% for the last print wey dey file, while the two-year dey at 4.33% and the thirty-year dey at 5.16%. The two-to-ten-year spread finish the series at 36 basis points. Treasury file dey run roughly one session behind the tape, so this panel carry 6 prints, and we put the date for each one inside the table.
The shorts: the daily files
FINRA dey publish daily short-sale volume file. Short volume na gross daily flow, e no be position. Sometimes these files dey come truncated, so we must measure coverage before we quote any ratio from dem.
| date | tickers wey dey file | short shares (bn) | pct of fullest file |
|---|---|---|---|
| 2026-07-20 | 15098 | 4.71 | 100 |
| 2026-07-21 | 15102 | 5.28 | 100 |
| 2026-07-22 | 14984 | 4.59 | 99.2 |
| 2026-07-23 | 4816 | 1.74 | 31.9 |
| 2026-07-24 | 15062 | 4.27 | 99.7 |
The exact SQL behind every number
SELECT date, tickers_on_file, short_shares_bn,
round(100.0 * tickers_on_file / max(tickers_on_file) OVER (), 1) AS pct_of_fullest_file
FROM (
SELECT toString(date) AS date,
uniqExact(ticker) AS tickers_on_file,
round(sum(short_shares) / 1e9, 2) AS short_shares_bn
FROM (
SELECT date, ticker, max(short_volume) AS short_shares
FROM global_markets.stocks_short_volume
WHERE date >= '2026-07-20' AND date <= '2026-07-24'
GROUP BY date, ticker
)
GROUP BY date
)
ORDER BY date5 daily files dey on hand for the week. The first one get 15098 tickers and 4.71 billion short-marked shares; the last one get 15062 tickers, or 99.7% of the fullest file's ticker count. Any short-volume claim for one name wey come from file wey dey far below im neighbors no fit verify until dem refile that file.
The calendar wey dey behind the week
| date | filings | form4 | form8k | f424b2 |
|---|---|---|---|---|
| 2026-07-20 | 3013 | 632 | 166 | 663 |
| 2026-07-21 | 3275 | 503 | 250 | 1083 |
| 2026-07-22 | 3461 | 549 | 279 | 569 |
| 2026-07-23 | 3551 | 545 | 369 | 626 |
| 2026-07-24 | 3844 | 644 | 223 | 623 |
The exact SQL behind every number
SELECT toString(filing_date) AS date,
count() AS filings,
countIf(form_type = '4') AS form4,
countIf(form_type = '8-K') AS form8k,
countIf(form_type = '424B2') AS f424b2
FROM global_markets.stocks_sec_edgar_index
WHERE filing_date >= '2026-07-20' AND filing_date <= '2026-07-24'
GROUP BY filing_date
ORDER BY dateThe first session wey dey on file get 3013 filings, 632 of dem na insider Form 4 reports and 166 of dem na 8-Ks. The last one get 3844, with 644 Form 4s and 223 8-Ks. 5 of the sessions for the week get EDGAR daily index on file; that index dey follow im own schedule, wey sometimes dey trail the tape.
| filings for week | days wey filing don dey | ex dividends for week | household ex dividends for week | reverse splits for week | forward splits for week | listings for week | news for week | news publishers for week |
|---|---|---|---|---|---|---|---|---|
| 17144 | 5 | 594 | 1 | 26 | 12 | 4 | 852 | 2 |
The exact SQL behind every number
SELECT
(SELECT count() FROM global_markets.stocks_sec_edgar_index WHERE filing_date >= '2026-07-20' AND filing_date <= '2026-07-24') AS filings_week,
(SELECT uniqExact(filing_date) FROM global_markets.stocks_sec_edgar_index WHERE filing_date >= '2026-07-20' AND filing_date <= '2026-07-24') AS filing_days_on_file,
(SELECT count() FROM global_markets.stocks_dividends WHERE ex_dividend_date >= '2026-07-20' AND ex_dividend_date <= '2026-07-24') AS ex_dividends_week,
(SELECT countIf(ticker IN ('AAPL', 'MSFT', 'JPM', 'KO', 'JNJ', 'XOM', 'CVX', 'PG', 'WMT', 'HD')) FROM global_markets.stocks_dividends WHERE ex_dividend_date >= '2026-07-20' AND ex_dividend_date <= '2026-07-24') AS household_ex_dividends_week,
(SELECT countIf(toFloat64(split_from) > toFloat64(split_to)) FROM global_markets.stocks_splits WHERE execution_date >= '2026-07-20' AND execution_date <= '2026-07-24') AS reverse_splits_week,
(SELECT countIf(toFloat64(split_to) > toFloat64(split_from)) FROM global_markets.stocks_splits WHERE execution_date >= '2026-07-20' AND execution_date <= '2026-07-24') AS forward_splits_week,
(SELECT count() FROM global_markets.stocks_ipos WHERE listing_date >= '2026-07-20' AND listing_date <= '2026-07-24') AS listings_week,
(SELECT count() FROM global_markets.stocks_news WHERE published_utc >= '2026-07-20 04:00:00' AND published_utc < '2026-07-25 04:00:00') AS news_week,
(SELECT uniqExact(JSONExtractString(publisher, 'name')) FROM global_markets.stocks_news WHERE published_utc >= '2026-07-20 04:00:00' AND published_utc < '2026-07-25 04:00:00') AS news_publishers_weekThe remaining paperwork for the week: 17144 SEC filings across 5 indexed days, 594 ex-dividend records, 1 of dem come from the ten household names wey this panel dey check, 26 reverse and 12 forward splits, 4 new listings, and 852 articles from 2 publishers. That last pair dey measure the attention of one feed, e no be the whole world media.
The sessions, wey dem verify
| sessions wey dey week | first session | last session | regular bars for week | holiday rows for week | up sessions | down sessions | next closure date | next closure name |
|---|---|---|---|---|---|---|---|---|
| 5 | 2026-07-20 | 2026-07-24 | 1950 | 0 | 2 | 3 | 2026-09-07 | Labor Day |
The exact SQL behind every number
WITH spy AS (
SELECT d, c, lagInFrame(c) OVER (ORDER BY d ASC ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS p
FROM (
SELECT toDate(toTimeZone(window_start, 'America/New_York')) AS d,
argMax(toFloat64(close), window_start) AS c
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker = 'SPY'
AND ((window_start >= '2026-07-17 13:30:00' AND window_start < '2026-07-17 20:00:00')
OR (window_start >= '2026-07-20 13:30:00' AND window_start < '2026-07-24 20:00:00'))
AND (toHour(window_start) * 60 + toMinute(window_start)) BETWEEN 810 AND 1199
GROUP BY d
)
)
SELECT
uniqExact(toDate(toTimeZone(window_start, 'America/New_York'))) AS sessions_in_week,
toString(min(toDate(toTimeZone(window_start, 'America/New_York')))) AS first_session,
toString(max(toDate(toTimeZone(window_start, 'America/New_York')))) AS last_session,
count() AS regular_bars_in_week,
(SELECT count() FROM global_markets.stocks_market_holidays WHERE date >= '2026-07-20' AND date <= '2026-07-24') AS holiday_rows_in_week,
(SELECT countIf(c > p) FROM spy WHERE d >= '2026-07-20') AS up_sessions,
(SELECT countIf(c < p) FROM spy WHERE d >= '2026-07-20') AS down_sessions,
(SELECT toString(min(date)) FROM global_markets.stocks_market_holidays WHERE date > '2026-07-24' AND date <= '2026-12-31' AND status = 'closed') AS next_closure_date,
(SELECT argMin(name, date) FROM global_markets.stocks_market_holidays WHERE date > '2026-07-24' AND date <= '2026-12-31' AND status = 'closed') AS next_closure_name
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker = 'SPY'
AND window_start >= '2026-07-20 13:30:00' AND window_start < '2026-07-24 20:00:00'
AND (toHour(window_start) * 60 + toMinute(window_start)) BETWEEN 810 AND 11995 sessions, from 2026-07-20 go reach 2026-07-24, with 0 holiday rows inside the window. SPY print 1950 regular-session minute bars across dem. 2 sessions close above the prior close and 3 close below. The next scheduled closure na Labor Day for 2026-09-07.
Wetin dey come
Next week, check our tables for the details:
| closures for next week | ex dividends wey go show next week | household ex div wey go show next week | splits wey go show next week | jul31 expiry % of friday volume | latest short interest settlement wey dem do |
|---|---|---|---|---|---|
| 0 | 934 | 0 | 26 | 14.2 | 2026-07-15 |
The exact SQL behind every number
SELECT
(SELECT count() FROM global_markets.stocks_market_holidays WHERE date >= '2026-07-27' AND date <= '2026-07-31' AND status != 'open') AS closures_next_week,
(SELECT count() FROM global_markets.stocks_dividends WHERE ex_dividend_date >= '2026-07-27' AND ex_dividend_date <= '2026-07-31') AS ex_dividends_next_week,
(SELECT countIf(ticker IN ('AAPL', 'MSFT', 'JPM', 'KO', 'JNJ', 'XOM', 'CVX', 'PG', 'WMT', 'HD')) FROM global_markets.stocks_dividends WHERE ex_dividend_date >= '2026-07-27' AND ex_dividend_date <= '2026-07-31') AS household_ex_div_next_week,
(SELECT count() FROM global_markets.stocks_splits WHERE execution_date >= '2026-07-27' AND execution_date <= '2026-07-31') AS splits_next_week,
(SELECT round(100.0 * sumIf(size, substring(ticker, length(ticker) - 14, 6) = '260731') / sum(size), 1) FROM global_markets.options_trades WHERE sip_timestamp >= '2026-07-24 00:00:00' AND sip_timestamp < '2026-07-25 00:00:00') AS jul31_expiry_pct_of_friday_volume,
(SELECT toString(max(settlement_date)) FROM global_markets.stocks_short_interest WHERE settlement_date <= '2026-07-24') AS latest_short_interest_settlementThe holiday table show 0 closures for the next week. 934 ex-dividend records dey inside, 0 from the household set, join with 26 scheduled splits. For Friday options volume, 14.2% don already dey inside contracts wey go expire the next Friday. The last short-interest settlement date wey we get na 2026-07-15, one file wey dey publish with delay wey long reach to get its own explainer.
FAQ
How the stock market perform for the week of July 20, 2026?
SPY change -0.59% close over close from July 17 go July 24, with QQQ for -1.59%, DIA for -0.38%, and IWM for -0.98%. Among liquid names, 2437 rise and 3630 fall.
Which sector lead for the week of July 20, 2026?
Energy, at 3.33%. The weakest of the eleven SPDR sector ETFs na Consumer Discretionary at -5.2%, 8.53 percentage points behind.
How many trading sessions dey for the week of July 20, 2026?
5, wey run from 2026-07-20 go 2026-07-24, with 0 holiday rows inside the window. The next scheduled closure na Labor Day.
Which ticker trade the most dollars for the week of July 20, 2026?
MU, at 158.1 billion dollars of regular-hours turnover, ahead of SPY at 133.9 billion.
Data notes
All stored timestamps na UTC; regular hours na 13:30-20:00 UTC per session, and week boundaries start from July 17 regular close go reach July 24. Every close-over-close panel for here dey read the last regular-hours minute bar for each side of the comparison, so extended-hours print no fit set weekly close. The trailing-year rank dey use open-to-close weekly returns, so e no go match the close-over-close scoreboard; we label both definitions where dem dey appear. Weekly breadth and the mover boards dey apply five-million-dollar regular-hours turnover filter, and breadth dey publish the count of names wey that filter comot. Mover boards no dey include any name wey e split execute after July 17 close and reach July 24, the exact interval wey dem dey measure the change, wey dey catch split wey dem stamp for the weekend or session; dem no dey exclude leveraged and inverse ETFs, wey fit dominate board for directional week. The bottom of the decliner board fit sit close to total loss of quoted value for the week, and that board dey publish how many of the five sessions each name print inside. The SPY spread panel dey count one-sided and crossed quotes instead of just comot dem, and the median na wetin dem take from the remaining valid quotes. Treasury file dey run about one session behind the tape and the EDGAR daily index dey land on im own schedule, so the rates and filings panels dey report how many days dem actually hold. No implied-volatility index dey here: those series no get license for this warehouse, so volatility na from the tape we dey read am through ranges, same-day options share, and quote behavior.
Metodoloji
- Market data source: consolidated tape.
delayed_stocks_minute_aggsfor prices and volumes,cache_stocks_quotesfor the NBBO panel,options_tradesfor the options week. - Close: the last regular-session minute bar of the session, no be assumed 16:00 print and no be extended-hours print.
- Sessions: we verify am from the holiday table plus observed bars, we no dey assume am from the calendar.
- Time zone handling: WHERE clauses dey use raw UTC literals, and
toTimeZonedey appear only for SELECT lists for ET labels. - Decimals: we dey cast price, size, and volume columns to Float64 before any division or product.
- Deterministic aggregates: exact quantiles throughout, and every ordering or sign claim wey dey the prose, we encode am as a sanity bound.
- Warehouse as-of date: July 26, 2026.
Cross-links: the previous edition of this weekly series, what a bid-ask spread is, when options expire, short interest versus short volume, and the two-to-ten-year spread.
Every query wey dey up so dey run unchanged for the Strasmore terminal if you want re-point a window to a different week.