Strasmore Research
Market Recap

Market Recap: Week of June 29, 2026

2026-07-04

The last week of June was a four-session week: the market traded Monday June 29 through Thursday July 2, then closed Friday July 3 for Independence Day — July 4 fell on a Saturday, so the holiday was observed the Friday before. The receipts for that closure, and for everything else here, are in the panels. The week's most notable measured fact sits in the volume table: one memory-chip maker put $190.2 billion through the tape in four days — half again as much as SPY itself. Every number on this page is a stored query result; expand any panel for the exact SQL.

The week on the board

Did the tape recover the prior week's drop, or extend it? The scoreboard says recovered: this panel computes each index ETF's week-over-week change and, for contrast, the week before it — both sides fresh from the same query.

QueryWeek-over-week: the four index ETFs against the prior Friday's close
The exact SQL behind every number
SELECT ticker,
    round(argMaxIf(toFloat64(close), window_start, toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-06-26') AND (toHour(window_start) * 60 + toMinute(window_start)) BETWEEN 810 AND 1199), 2) AS prior_friday_close,
    round(argMaxIf(toFloat64(close), window_start, toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2026-06-29') AND (toHour(window_start) * 60 + toMinute(window_start)) BETWEEN 810 AND 1199), 2) AS week_close,
    round((argMaxIf(toFloat64(close), window_start, toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2026-06-29') AND (toHour(window_start) * 60 + toMinute(window_start)) BETWEEN 810 AND 1199)
         / argMaxIf(toFloat64(close), window_start, toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-06-26') AND (toHour(window_start) * 60 + toMinute(window_start)) BETWEEN 810 AND 1199) - 1) * 100, 1) AS week_change_pct,
    round((argMaxIf(toFloat64(close), window_start, toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-06-26') AND (toHour(window_start) * 60 + toMinute(window_start)) BETWEEN 810 AND 1199)
         / argMaxIf(toFloat64(close), window_start, toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-06-18') AND (toHour(window_start) * 60 + toMinute(window_start)) BETWEEN 810 AND 1199) - 1) * 100, 1) AS prior_week_change_pct
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker IN ('SPY', 'QQQ', 'DIA', 'IWM')
  AND window_start >= toDateTime('2026-06-15 00:00:00') AND window_start < toDateTime('2026-07-03 00:00:00')
GROUP BY ticker
ORDER BY ticker

SPY finished the week at $744.8, up 2.2% from the prior Friday — following a -2.3% week before it. QQQ rose 1% after a -4.6% prior week, DIA gained 2%, and IWM closed at 297.530% on the week, flat while the large-cap indexes rose.

Four sessions, then a holiday

A five-day trading week is an assumption, not a fact — this one had four sessions. The receipt panel checks the closure two ways: the exchange calendar lists July 3 as a full holiday, and the tape shows zero SPY bars that day against 1560 regular-session bars across the four traded days.

QueryThe session receipt: four traded days, one Friday closure, verified from calendar AND tape
The exact SQL behind every number
SELECT
    (SELECT uniqExact(toDate(toTimeZone(window_start, 'America/New_York'))) FROM global_markets.delayed_stocks_minute_aggs
     WHERE ticker = 'SPY' AND window_start >= toDateTime('2026-06-29 00:00:00') AND window_start < toDateTime('2026-07-03 00:00:00')) AS sessions_in_week,
    (SELECT countIf((toHour(window_start) * 60 + toMinute(window_start)) BETWEEN 810 AND 1199) FROM global_markets.delayed_stocks_minute_aggs
     WHERE ticker = 'SPY' AND window_start >= toDateTime('2026-06-29 00:00:00') AND window_start < toDateTime('2026-07-03 00:00:00')) AS regular_bars_week,
    (SELECT count() FROM global_markets.stocks_market_holidays WHERE date = toDate('2026-07-03')) AS jul3_calendar_rows,
    (SELECT any(status) FROM global_markets.stocks_market_holidays WHERE date = toDate('2026-07-03')) AS jul3_status,
    (SELECT count() FROM global_markets.delayed_stocks_minute_aggs
     WHERE ticker = 'SPY' AND window_start >= toDateTime('2026-07-03 00:00:00') AND window_start < toDateTime('2026-07-04 00:00:00')) AS jul3_spy_bars
QuerySession by session: SPY close, change, and full-day volume
The exact SQL behind every number
SELECT toDate(toTimeZone(window_start, 'America/New_York')) AS et_date,
    round(argMaxIf(toFloat64(close), window_start, (toHour(window_start) * 60 + toMinute(window_start)) BETWEEN 810 AND 1199), 2) AS spy_close,
    round((argMaxIf(toFloat64(close), window_start, (toHour(window_start) * 60 + toMinute(window_start)) BETWEEN 810 AND 1199) / any(prev) - 1) * 100, 1) AS change_pct,
    round(toFloat64(sum(volume)) / 1e6, 1) AS shares_m
FROM global_markets.delayed_stocks_minute_aggs
INNER JOIN (
    SELECT d, lagInFrame(c) OVER (ORDER BY d ASC ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS prev
    FROM (
        SELECT toDate(toTimeZone(window_start, 'America/New_York')) AS d, argMaxIf(toFloat64(close), window_start, (toHour(window_start) * 60 + toMinute(window_start)) BETWEEN 810 AND 1199) AS c
        FROM global_markets.delayed_stocks_minute_aggs
        WHERE ticker = 'SPY' AND window_start >= toDateTime('2026-06-26 00:00:00') AND window_start < toDateTime('2026-07-03 00:00:00')
        GROUP BY d
    )
) AS p ON toDate(toTimeZone(window_start, 'America/New_York')) = p.d
WHERE ticker = 'SPY' AND window_start >= toDateTime('2026-06-29 00:00:00') AND window_start < toDateTime('2026-07-03 00:00:00')
GROUP BY et_date
ORDER BY et_date

All four sessions ran the full regular schedule — the 1560 regular bars work out to exactly four 390-minute sessions. The week closed essentially flat (-0.1%) on 2026-07-02, heading into the holiday — the week's gains were made on its first three sessions.

Breadth: how much of the market went up

One index rising can hide a market falling. This panel counts every ticker with a close on both sides of the week, with the excluded set disclosed rather than silently dropped.

QueryAdvancers and decliners on the week, with the liquidity filter disclosed
The exact SQL behind every number
SELECT
    countIf(chg > 0 AND NOT dropped) AS advancers,
    countIf(chg < 0 AND NOT dropped) AS decliners,
    countIf(chg = 0 AND NOT dropped) AS unchanged,
    countIf(dropped) AS dropped_by_liquidity_filter,
    count() AS tickers_with_both_closes
FROM (
    SELECT ticker,
        argMaxIf(toFloat64(close), window_start, toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2026-06-29') AND (toHour(window_start) * 60 + toMinute(window_start)) BETWEEN 810 AND 1199)
      - argMaxIf(toFloat64(close), window_start, toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-06-26') AND (toHour(window_start) * 60 + toMinute(window_start)) BETWEEN 810 AND 1199) AS chg,
        sumIf(toFloat64(close) * toFloat64(volume), toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2026-06-29')) < 1e6 AS dropped
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE window_start >= toDateTime('2026-06-26 00:00:00') AND window_start < toDateTime('2026-07-03 00:00:00')
    GROUP BY ticker
    HAVING countIf(toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-06-26') AND (toHour(window_start) * 60 + toMinute(window_start)) BETWEEN 810 AND 1199) > 0
       AND countIf(toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2026-06-29') AND (toHour(window_start) * 60 + toMinute(window_start)) BETWEEN 810 AND 1199) > 0
)

5047 tickers rose on the week against 3525 that fell (39 unchanged) — the advance was broad, not just index-level. The liquidity filter set aside 3163 tickers that traded under $1M all week; they are counted here, not hidden.

The tape's leaders: one stock above the ETFs

QueryRegular-hours dollar volume, week of June 29 (one reused-symbol listing excluded pending entity verification)
The exact SQL behind every number
SELECT ticker,
    round(sum(toFloat64(close) * toFloat64(volume)) / 1e9, 1) AS dollar_bn,
    round(100 * sum(toFloat64(close) * toFloat64(volume)) / max(sum(toFloat64(close) * toFloat64(volume))) OVER (), 1) AS pct_of_leader
FROM global_markets.delayed_stocks_minute_aggs
WHERE window_start >= toDateTime('2026-06-29 00:00:00') AND window_start < toDateTime('2026-07-03 00:00:00')
  AND (toHour(window_start) * 60 + toMinute(window_start)) BETWEEN 810 AND 1199
  AND ticker NOT IN ('SPCX')
GROUP BY ticker
ORDER BY dollar_bn DESC
LIMIT 8

MU led the entire tape with $190.2 billion in regular-hours dollar volume — SPY, the biggest ETF, did 66.4% of that. A single company out-trading the market's default index fund for a full week is the week's standout measured fact; this page reports the size of the flow, not the reason for it. Basis: June 29–July 2 regular hours; one reused-symbol June listing is excluded pending entity verification — its receipts live in its own deep-dive.

The holiday moved the weeklies

Where does a Friday weekly expiry go when Friday is closed? The options tape answers directly: this panel parses the expiry date out of every contract traded during the week and ranks them.

QueryContracts traded during the week, by expiry date (single-pass scan; batch-generated)
The exact SQL behind every number
SELECT concat('20', substring(expiry_raw, 1, 2), '-', substring(expiry_raw, 3, 2), '-', substring(expiry_raw, 5, 2)) AS expiry,
    round(sum(size) / 1e6, 1) AS contracts_m,
    round(100 * sum(size) / max(sum(size)) OVER (), 1) AS pct_of_biggest
FROM (
    SELECT substring(ticker, length(ticker) - 14, 6) AS expiry_raw, size
    FROM global_markets.options_trades
    WHERE sip_timestamp >= toDateTime64('2026-06-29 00:00:00', 9) AND sip_timestamp < toDateTime64('2026-07-03 00:00:00', 9)
)
GROUP BY expiry
ORDER BY contracts_m DESC
LIMIT 6

The busiest expiry of the week was 2026-07-02 — a Thursday — at 74.1 million contracts, 42.2% ahead of the next date. With July 3 closed, the week's "Friday" expiration settled on Thursday instead; Monday-through-Wednesday daily expiries and the July 17 monthly fill out the table. Expiry dates are re-parsed from each contract's OCC symbol (the tape's own expiry column is unreliable); the mechanics of expiry weeks like this one are in the holiday explainer.

Rates: the 10-year backed up

QueryTreasury yields through the week (July 2 print not yet ingested at generation)
The exact SQL behind every number
SELECT toString(date) AS d,
    round(yield_10_year, 2) AS y10,
    round(yield_2_year, 2) AS y2,
    round((yield_10_year - yield_2_year) * 100, 0) AS spread_2s10s_bp
FROM global_markets.treasury_yields
WHERE date >= toDate('2026-06-26') AND date <= toDate('2026-07-02')
ORDER BY date

The 10-year moved from 4.38% on the prior Friday to 4.48% by 2026-07-01, while the 2s10s spread held near 31 basis points. One disclosure: the July 2 yield print had not yet been ingested when this page was generated (4 rows through July 1) — the panel will carry it on the next regeneration.

The calendar: a quarter ends, a filing day goes missing

QueryThe week's corporate calendar — with the June 30 filing-index gap on display
The exact SQL behind every number
SELECT
    (SELECT count() FROM global_markets.stocks_dividends WHERE ex_dividend_date >= toDate('2026-06-29') AND ex_dividend_date <= toDate('2026-07-02')) AS ex_div_events,
    (SELECT count() FROM global_markets.stocks_splits WHERE execution_date >= toDate('2026-06-29') AND execution_date <= toDate('2026-07-02')) AS splits,
    (SELECT count() FROM global_markets.stocks_ipos WHERE listing_date >= toDate('2026-06-29') AND listing_date <= toDate('2026-07-02')) AS ipos,
    (SELECT uniqExact(accession_number) FROM global_markets.stocks_sec_edgar_index WHERE filing_date = toDate('2026-06-29')) AS filings_jun29,
    (SELECT uniqExact(accession_number) FROM global_markets.stocks_sec_edgar_index WHERE filing_date = toDate('2026-06-30')) AS filings_jun30,
    (SELECT uniqExact(accession_number) FROM global_markets.stocks_sec_edgar_index WHERE filing_date = toDate('2026-07-01')) AS filings_jul1,
    (SELECT uniqExact(accession_number) FROM global_markets.stocks_sec_edgar_index WHERE filing_date = toDate('2026-07-02')) AS filings_jul2,
    (SELECT count() FROM global_markets.stocks_news WHERE published_utc >= toDateTime('2026-06-29 00:00:00') AND published_utc < toDateTime('2026-07-03 00:00:00')) AS news_articles

The week carried 2217 ex-dividend events, 48 splits, and 7 new listings into the quarter turn. The filing columns hold the week's data-quality find: the SEC filing index shows 4439 filings on June 29 and 4282 on July 1, but only 31 on June 30 — the quarter's final day. Prior-year quarter-end days carry thousands. The gap sits in the data feed, not in what companies filed; any June or Q2 filing count that includes June 30 is understated until the index backfills. Treat it as a known gap, disclosed here and in the June and Q2 recaps.

The shorts: one truncated file, three complete ones

QueryFINRA off-exchange short volume by session: coverage and marked-short share
The exact SQL behind every number
SELECT toString(date) AS d,
    uniqExact(ticker) AS tickers_on_file,
    max(ticker) AS alphabetical_end,
    round(100 * sum(toFloat64(short_volume)) / sum(toFloat64(total_volume)), 1) AS marked_short_pct
FROM (
    SELECT date, ticker, any(short_volume) AS short_volume, any(total_volume) AS total_volume
    FROM global_markets.stocks_short_volume
    WHERE date >= toDate('2026-06-29') AND date <= toDate('2026-07-02')
    GROUP BY date, ticker
)
GROUP BY date
ORDER BY date

Off-exchange volume was marked short at roughly 48.8% mid-week — routine market-maker plumbing, not a squeeze signal. The coverage columns carry the caveat: the June 29 file is truncated market-wide (5489 tickers ending at "SSUS" against ~15,000 ending at "ZYME" on complete days), so June 29's 45.8% is computed from a partial file. The June 29 deep-dive carries the full market-wide probe.

Data notes

Full data notes
  • July 3 was a FULL closure, not an early close. July 4, 2026 fell on a Saturday; NYSE and Nasdaq observed the holiday on Friday July 3 (2 calendar rows, status "closed", 0 SPY bars on the day).
  • Short interest ends at the June 15 settlement. The end-of-June bi-monthly settlement had not been published when this page was generated; nothing here uses it, and the June recap carries the disclosure in full. This post will be regenerated when it lands.
  • The June 30 filing-index gap is disclosed inline above; the July 2 treasury print is pending ingestion.
  • The June 29 FINRA short-volume file is truncated market-wide; its ratio is shown with its coverage inline.
  • Volume-leader exclusion: one June listing trades under a reused symbol and is excluded from the leaderboard pending entity verification (its own post carries the receipts).

Methodology

  • The period is June 29 through July 2, 2026 — the four traded sessions of the week; July 3 was a market holiday. Session bounds come from the exchange calendar corroborated by observed bars, never assumed.
  • Timestamps are stored UTC and filtered with raw UTC bounds; regular hours in this (EDT) week are 13:30–20:00 UTC. Closes are the last regular-hours minute bar; dollar volume is minute close times minute volume, summed over regular hours.
  • Week-over-week figures compare against the prior Friday's close, computed fresh in the same query as this week's — never read from a stored value.
  • Generation runs through the gated read-only path; the public page never queries live. Warehouse state as of July 4, 2026.

This is the first edition of the standing weekly recap — next week's edition will cover July 6–10 and link back here. The June 29 daily recap covers the Monday in day-level detail.