Strasmore Research
Market Recap Matt ConnorBy Matt Connor · Updated 2026-07-26

Market Recap: June 29, 2026, The Day in Numbers

June 29, 2026 in numbers: a growth-led index gain, a four-point sector spread, a memory-stock round trip, the 0DTE tape, penny-tight quotes and rates.

Monday, June 29, 2026 was a growth-led up day with a narrow leadership band: QQQ gained 2.57%, SPY 1.62%, 3968 liquid names rose against 2327 decliners, yet nearly half the sector baskets closed red. Every number is read from a stored query; expand any panel for the SQL. The tick-by-tick layer of the same session: the June 29 microstructure deep dive.

The scoreboard

Every change compares June 29's last regular-session minute bar with Friday June 26's.

QuerySPY / QQQ / DIA / IWM: June 29 vs the June 26 close, regular hours
The exact SQL behind every number
WITH friday AS (
    SELECT ticker, argMax(close, window_start) AS friday_close
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker IN ('SPY', 'QQQ', 'DIA', 'IWM')
      AND window_start >= '2026-06-26 13:30:00' AND window_start < '2026-06-26 20:00:00'
    GROUP BY ticker
),
monday AS (
    SELECT ticker,
           argMin(open, window_start) AS monday_open,
           argMax(close, window_start) AS monday_close,
           max(high) AS day_high,
           min(low) AS day_low,
           round(toFloat64(sum(volume)) / 1e6, 1) AS shares_traded_m
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker IN ('SPY', 'QQQ', 'DIA', 'IWM')
      AND window_start >= '2026-06-29 13:30:00' AND window_start < '2026-06-29 20:00:00'
    GROUP BY ticker
)
SELECT
    m.ticker AS ticker,
    toFloat64(f.friday_close) AS jun26_close,
    toFloat64(m.monday_open) AS jun29_open,
    toFloat64(m.monday_close) AS jun29_close,
    round((toFloat64(m.monday_close) / toFloat64(f.friday_close) - 1) * 100, 2) AS pct_change,
    round(100 * (toFloat64(m.monday_close) / toFloat64(f.friday_close) - 1) / max(toFloat64(m.monday_close) / toFloat64(f.friday_close) - 1) OVER (), 1) AS pct_of_best_change,
    toFloat64(m.day_high) AS day_high,
    toFloat64(m.day_low) AS day_low,
    m.shares_traded_m AS shares_traded_m
FROM monday m
JOIN friday f ON m.ticker = f.ticker
ORDER BY m.ticker
Run this yourself

SPY opened at $736.525 against Friday's $729.09 close and finished at $740.88, under its $741.56 high. QQQ's +2.57% against DIA's +0.81% marks a growth-and-tech day; small-cap IWM added 0.45%.

Was the day unusual?

A percentage move means little without a yardstick. This panel ranks June 29 against the trailing month by absolute close-over-close size.

QueryQQQ and SPY: June 29 ranked against the trailing month of sessions (rank 1 = biggest absolute move)
The exact SQL behind every number
SELECT
    round(anyIf(cc_pct, ticker = 'QQQ' AND d = toDate('2026-06-29')), 2) AS qqq_close_over_close_pct,
    arrayCount(x -> x > abs(anyIf(cc_pct, ticker = 'QQQ' AND d = toDate('2026-06-29'))), groupArrayIf(abs(cc_pct), ticker = 'QQQ' AND d != toDate('2026-06-29'))) + 1 AS qqq_abs_move_rank,
    countIf(ticker = 'QQQ') AS qqq_sessions_compared,
    round(max(if(ticker = 'QQQ', abs(cc_pct), 0)), 2) AS qqq_biggest_move_of_month_pct,
    countIf(ticker = 'QQQ' AND cc_pct > 0) AS qqq_up_sessions,
    round(anyIf(cc_pct, ticker = 'SPY' AND d = toDate('2026-06-29')), 2) AS spy_close_over_close_pct,
    arrayCount(x -> x > abs(anyIf(cc_pct, ticker = 'SPY' AND d = toDate('2026-06-29'))), groupArrayIf(abs(cc_pct), ticker = 'SPY' AND d != toDate('2026-06-29'))) + 1 AS spy_abs_move_rank,
    countIf(ticker = 'SPY') AS spy_sessions_compared,
    round(anyIf(oc_pct, ticker = 'SPY' AND d = toDate('2026-06-29')), 2) AS spy_open_to_close_pct,
    concat(monthName(min(d)), ' ', toString(toDayOfMonth(min(d))), ', ', toString(toYear(min(d)))) AS first_session
FROM (
    SELECT ticker, d,
           (close_px / lagInFrame(close_px) OVER (PARTITION BY ticker ORDER BY d) - 1) * 100 AS cc_pct,
           oc_pct
    FROM (
        SELECT ticker,
               toDate(toTimeZone(window_start, 'America/New_York')) AS d,
               argMax(toFloat64(close), window_start) AS close_px,
               (argMax(toFloat64(close), window_start) / argMin(toFloat64(open), window_start) - 1) * 100 AS oc_pct
        FROM global_markets.delayed_stocks_minute_aggs
        WHERE ticker IN ('SPY', 'QQQ')
          AND window_start >= toDateTime('2026-05-28 00:00:00')
          AND window_start < toDateTime('2026-06-30 00:00:00')
          AND (toHour(window_start) * 60 + toMinute(window_start)) BETWEEN 810 AND 1199
        GROUP BY ticker, d
    )
)
WHERE isFinite(cc_pct)
Run this yourself

Big, not a record: QQQ's 2.57% move ranks 5 of the 21 sessions since May 29, 2026, in a month whose largest single move measured 4.76%; SPY's 1.62% ranks 4 of 21. Where the move came from matters: SPY's open-to-close grind was only 0.59%, so most of the gain arrived in the overnight gap, before a single regular-hours bar printed. QQQ closed higher on 10 of those sessions: a coin-flip month.

Breadth: how wide was the rally?

An advancer is a ticker whose Monday close beat Friday's, among names trading at least $1 million, a filter dropping 5,108 of 11,475 dual-session tickers.

QueryAdvancers vs decliners among tickers with at least $1M traded on June 29
The exact SQL behind every number
WITH per_ticker AS (
    SELECT
        ticker,
        toFloat64(argMaxIf(close, window_start, window_start < '2026-06-27 00:00:00')) AS friday_close,
        toFloat64(argMaxIf(close, window_start, window_start >= '2026-06-29 00:00:00')) AS monday_close,
        sumIf(toFloat64(close) * toFloat64(volume), window_start >= '2026-06-29 00:00:00') AS monday_dollar_volume
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE (window_start >= '2026-06-26 13:30:00' AND window_start < '2026-06-26 20:00:00')
       OR (window_start >= '2026-06-29 13:30:00' AND window_start < '2026-06-29 20:00:00')
    GROUP BY ticker
)
SELECT
    countIf(monday_close > friday_close AND monday_dollar_volume >= 1000000) AS advancers,
    countIf(monday_close < friday_close AND monday_dollar_volume >= 1000000) AS decliners,
    countIf(monday_close = friday_close AND monday_dollar_volume >= 1000000) AS unchanged,
    countIf(monday_dollar_volume >= 1000000) AS liquid_tickers,
    count() AS tickers_traded_both_sessions,
    reverse(arrayStringConcat(extractAll(reverse(toString(count())), '[0-9]{1,3}'), ',')) AS tickers_traded_both_sessions_label,
    count() - countIf(monday_dollar_volume >= 1000000) AS dropped_by_liquidity_filter,
    reverse(arrayStringConcat(extractAll(reverse(toString(count() - countIf(monday_dollar_volume >= 1000000))), '[0-9]{1,3}'), ',')) AS dropped_by_liquidity_filter_label,
    round(100.0 * countIf(monday_close > friday_close AND monday_dollar_volume >= 1000000)
        / countIf(monday_dollar_volume >= 1000000), 1) AS advancer_pct
FROM per_ticker
WHERE friday_close > 0 AND monday_close > 0
Run this yourself

3968 advancers, 2327 decliners, 72 unchanged: 62.3% of the liquid tape rose, counted one name at a time, a wide day.

Sector by sector: the rally was narrower than it looked

Counting names equally is one view; weighting them by company size is another. The eleven SPDR sector ETFs are the shorthand for the second, one market-value-weighted basket per sector of the S&P 500. They disagree sharply with the breadth count.

QueryThe eleven sector baskets: June 29 vs the June 26 close, regular hours
The exact SQL behind every number
WITH per_etf AS (
    SELECT
        ticker,
        toFloat64(argMaxIf(close, window_start, window_start < '2026-06-27 00:00:00')) AS friday_close,
        toFloat64(argMaxIf(close, window_start, window_start >= '2026-06-29 00:00:00')) AS monday_close,
        maxIf(toFloat64(high), window_start >= '2026-06-29 00:00:00') AS day_high,
        minIf(toFloat64(low), window_start >= '2026-06-29 00:00:00') AS day_low,
        round(sumIf(toFloat64(close) * toFloat64(volume), window_start >= '2026-06-29 00:00:00') / 1e6, 0) AS dollar_volume_m
    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-06-26 13:30:00' AND window_start < '2026-06-26 20:00:00')
        OR (window_start >= '2026-06-29 13:30:00' AND window_start < '2026-06-29 20:00:00'))
    GROUP BY ticker
)
SELECT
    sector,
    ticker,
    round((monday_close / friday_close - 1) * 100, 2) AS pct_change,
    round((day_high / day_low - 1) * 100, 2) AS range_pct,
    dollar_volume_m,
    round((monday_close / friday_close - 1) * 100 - min((monday_close / friday_close - 1) * 100) OVER (), 2) AS pct_above_worst_sector
FROM (
    SELECT *,
        multiIf(ticker = 'XLB', 'Materials',
                ticker = 'XLC', 'Communication services',
                ticker = 'XLE', 'Energy',
                ticker = 'XLF', 'Financials',
                ticker = 'XLI', 'Industrials',
                ticker = 'XLK', 'Technology',
                ticker = 'XLP', 'Consumer staples',
                ticker = 'XLRE', 'Real estate',
                ticker = 'XLU', 'Utilities',
                ticker = 'XLV', 'Health care',
                'Consumer discretionary') AS sector
    FROM per_etf
)
ORDER BY pct_change DESC
Run this yourself

Technology led at 2.52%, consumer discretionary followed at 2.37%; materials closed -1.82% and real estate -0.64%. Best minus worst, the day's sector dispersion, measured 4.34 percentage points, with utilities, staples, energy, real estate and materials all red on a day the index rose. Wide by name count, narrow by weight: that is what an index fund's gain can hide.

The day's highlight: memory and storage

Four names traded the same theme with very different outcomes, co-movement and magnitude only; the data does not say why.

QueryThe memory/storage names: change vs Friday's close and intraday range
The exact SQL behind every number
WITH per_name AS (
    SELECT
        ticker,
        toFloat64(argMaxIf(close, window_start, window_start < '2026-06-27 00:00:00')) AS friday_close,
        toFloat64(argMaxIf(close, window_start, window_start >= '2026-06-29 00:00:00')) AS monday_close,
        maxIf(toFloat64(high), window_start >= '2026-06-29 00:00:00') AS day_high,
        minIf(toFloat64(low), window_start >= '2026-06-29 00:00:00') AS day_low,
        argMinIf(window_start, toFloat64(low), window_start >= '2026-06-29 00:00:00') AS low_bar,
        argMaxIf(window_start, toFloat64(high), window_start >= '2026-06-29 00:00:00') AS high_bar
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker IN ('MU', 'SNDK', 'STX', 'WDC')
      AND ((window_start >= '2026-06-26 13:30:00' AND window_start < '2026-06-26 20:00:00')
        OR (window_start >= '2026-06-29 13:30:00' AND window_start < '2026-06-29 20:00:00'))
    GROUP BY ticker
)
SELECT
    ticker,
    round(friday_close, 2) AS jun26_close,
    round(monday_close, 2) AS jun29_close,
    round((monday_close / friday_close - 1) * 100, 2) AS pct_chg,
    round(day_high, 2) AS day_high,
    formatDateTime(toTimeZone(high_bar, 'America/New_York'), '%H:%i') AS day_high_et,
    round(day_low, 2) AS day_low,
    formatDateTime(toTimeZone(low_bar, 'America/New_York'), '%H:%i') AS day_low_et,
    round((day_high / day_low - 1) * 100, 2) AS range_pct
FROM per_name
ORDER BY ticker
Run this yourself

MU is the textbook case of range versus net change: a 12.22% intraday range, a $1023.65 low at 10:18 ET, a $1148.79 high at 15:59, yet a close 1.97% above Friday. Western Digital rose 11.15% and Seagate 8.17%; SanDisk closed -1.9% across a 10.33% range, the cluster's one lower close. A close hides what holders lived through.

Where the money traded

By dollars traded, Micron (MU) towered over everything, index funds included: $58.47 billion against SPY's $33.97 billion. By share count, a different day.

QueryVolume leaders two ways: top 6 by dollars traded, top 4 by shares traded
The exact SQL behind every number
SELECT ticker, leaderboard, dollar_volume_bn, if(dollar_volume_bn < 1, dollar_volume_m, NULL) AS dollar_value_m, shares_m,
    round(100 * if(leaderboard = 'by dollars traded', dollar_volume_bn, shares_m)
        / max(if(leaderboard = 'by dollars traded', dollar_volume_bn, shares_m)) OVER (PARTITION BY leaderboard), 1) AS pct_of_board_leader
FROM (
    SELECT
        'by dollars traded' AS leaderboard,
        ticker,
        round(sum(toFloat64(close) * toFloat64(volume)) / 1e9, 2) AS dollar_volume_bn,
        round(sum(toFloat64(close) * toFloat64(volume)) / 1e6, 0) AS dollar_volume_m,
        round(sum(toFloat64(volume)) / 1e6, 1) AS shares_m
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE window_start >= '2026-06-29 13:30:00' AND window_start < '2026-06-29 20:00:00'
    GROUP BY ticker
    ORDER BY dollar_volume_bn DESC
    LIMIT 6
    UNION ALL
    SELECT
        'by shares traded' AS leaderboard,
        ticker,
        round(sum(toFloat64(close) * toFloat64(volume)) / 1e9, 2) AS dollar_volume_bn,
        round(sum(toFloat64(close) * toFloat64(volume)) / 1e6, 0) AS dollar_volume_m,
        round(sum(toFloat64(volume)) / 1e6, 1) AS shares_m
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE window_start >= '2026-06-29 13:30:00' AND window_start < '2026-06-29 20:00:00'
    GROUP BY ticker
    ORDER BY shares_m DESC
    LIMIT 4
)
ORDER BY leaderboard ASC, if(leaderboard = 'by dollars traded', dollar_volume_bn, shares_m) DESC
Run this yourself

Share-count boards mislead: the share leaders were SOXS, a 3x-leveraged inverse semiconductor ETF (695.3 million shares), and INLF, a penny stock whose 353.8 million shares were worth about $23 million all day. Dollar volume shows where money moved; relative volume shows whether a name's activity is unusual for itself.

In 30-minute New York buckets, June 29 traces the classic volume "smile":

QueryShares traded per 30-minute bucket, regular hours (billions)
The exact SQL behind every number
SELECT
    formatDateTime(toStartOfInterval(toTimeZone(window_start, 'America/New_York'), INTERVAL 30 MINUTE), '%H:%i') AS et_time,
    round(sum(toFloat64(volume)) / 1e9, 2) AS shares_bn,
    round(100 * sum(toFloat64(volume)) / max(sum(toFloat64(volume))) OVER (), 1) AS pct_of_biggest_bucket,
    round(100 * (sum(toFloat64(volume)) / min(sum(toFloat64(volume))) OVER () - 1), 1) AS pct_above_trough
FROM global_markets.delayed_stocks_minute_aggs
WHERE window_start >= '2026-06-29 13:30:00' AND window_start < '2026-06-29 20:00:00'
GROUP BY et_time
ORDER BY et_time
Run this yourself

2.3 billion shares in the opening half hour, a 0.77 billion trough at 13:30, and the biggest bucket, 2.39 billion, in the closing half hour, where closing auctions and index-tracking flows concentrate.

The options tape

Options traded 66.33 million contracts across 11.04 million prints.

QueryOne row for the whole options day: volume, 0DTE, the holiday-shifted week
The exact SQL behind every number
WITH
    (
        SELECT (any(underlying_symbol), any(toFloat64(strike_price)), any(option_type),
                any(toDateOrNull(concat('20', substring(ticker, length(ticker) - 14, 6)))),
                sum(size), count(), round(avg(toFloat64(price)), 3))
        FROM global_markets.options_trades
        WHERE sip_timestamp >= '2026-06-29 00:00:00' AND sip_timestamp < '2026-06-30 00:00:00'
        GROUP BY ticker
        ORDER BY sum(size) DESC
        LIMIT 1
    ) AS top_contract,
    (
        SELECT round(toFloat64(argMax(close, window_start)), 2)
        FROM global_markets.delayed_stocks_minute_aggs
        WHERE ticker = 'SPY' AND window_start >= '2026-06-29 13:30:00' AND window_start < '2026-06-29 20:00:00'
    ) AS spy_regular_close
SELECT
    round(count() / 1e6, 2) AS option_prints_m,
    round(toFloat64(sum(size)) / 1e6, 2) AS contracts_m,
    round(100.0 * sumIf(size, option_type = 'C') / sum(size), 1) AS call_pct_of_volume,
    round(100.0 * sumIf(size, substring(ticker, length(ticker) - 14, 6) = '260629') / sum(size), 1) AS same_day_expiry_pct,
    round(toFloat64(sumIf(size, substring(ticker, length(ticker) - 14, 6) = '260702')) / 1e6, 2) AS thu_jul2_expiry_contracts_m,
    countIf(substring(ticker, length(ticker) - 14, 6) = '260703') AS fri_jul3_expiry_prints,
    countIf(sip_timestamp < '2026-06-29 13:30:00') AS premarket_prints,
    reverse(arrayStringConcat(extractAll(reverse(toString(countIf(sip_timestamp < '2026-06-29 13:30:00'))), '[0-9]{1,3}'), ',')) AS premarket_prints_label,
    countIf(sip_timestamp < '2026-06-29 13:30:00'
        AND underlying_symbol NOT IN ('SPX', 'SPXW', 'XSP', 'RUTW', 'VIX', 'VIXW')) AS premarket_non_index_prints,
    arrayStringConcat(arraySort(groupUniqArrayIf(underlying_symbol, sip_timestamp < '2026-06-29 13:30:00')), ', ') AS premarket_underlyings,
    round(toFloat64(sumIf(size, underlying_symbol = 'SPY')) / 1e6, 2) AS spy_contracts_m,
    round(toFloat64(sumIf(size, underlying_symbol = 'QQQ')) / 1e6, 2) AS qqq_contracts_m,
    top_contract.1 AS top_contract_underlying,
    top_contract.2 AS top_contract_strike,
    top_contract.3 AS top_contract_type,
    top_contract.4 AS top_contract_expiry,
    top_contract.5 AS top_contract_volume,
    reverse(arrayStringConcat(extractAll(reverse(toString(assumeNotNull(top_contract.5))), '[0-9]{1,3}'), ',')) AS top_contract_volume_label,
    round(top_contract.7, 3) AS top_contract_avg_price,
    round(top_contract.2 - spy_regular_close, 2) AS top_strike_minus_spy_close
FROM global_markets.options_trades
WHERE sip_timestamp >= '2026-06-29 00:00:00' AND sip_timestamp < '2026-06-30 00:00:00'
Run this yourself

Calls took 55.7% of contract volume, and 35.8% of everything that traded expired that same Monday, the zero-days-to-expiry (0DTE) share. The busiest single contract anywhere was the same-day SPY $741 call: 788,133 contracts at an average premium of $0.474, with SPY's close landing 0.12 dollars below the strike, the day's most-traded option finished out of the money. SPY traded 12.01 million contracts as an underlying; QQQ 7.32 million.

The panel also counts 40,621 option prints before the 9:30 equity open, every one on a cash-settled index root (RUTW, SPX, SPXW, VIX, VIXW, XSP), with 0 stock or ETF option prints in the same window. That is the rule, not an accident: index options list extended global-trading-hours sessions, options on stocks and ETFs open with the stock market. The stocks trade early, see premarket and after-hours trading, their options do not.

No contract with a Friday, July 3 expiration code printed all day (0 prints), the market is closed that Friday, while the Thursday, July 2 expiry traded 10.73 million.

The quote tape: what the day cost to trade

Every price on this page sits on a quote stream, the National Best Bid and Offer, re-published continuously for every listed name. The gap between best bid and best offer, the bid-ask spread, is what an order pays to cross.

QueryWhat it cost to cross the spread: NBBO updates and median quoted width, regular hours
The exact SQL behind every number
SELECT
    ticker,
    round(count() / 1e6, 2) AS nbbo_updates_m,
    round(quantileExactIf(0.5)((toFloat64(ask_price) - toFloat64(bid_price)) / ((toFloat64(ask_price) + toFloat64(bid_price)) / 2) * 10000, bid_price > 0 AND ask_price >= bid_price), 2) AS median_spread_bps,
    round(quantileExactIf(0.5)((toFloat64(ask_price) - toFloat64(bid_price)) / ((toFloat64(ask_price) + toFloat64(bid_price)) / 2) * 10000, bid_price > 0 AND ask_price >= bid_price AND sip_timestamp >= '2026-06-29 13:30:00' AND sip_timestamp < '2026-06-29 14:00:00'), 2) AS open_30min_spread_bps,
    round(quantileExactIf(0.5)((toFloat64(ask_price) - toFloat64(bid_price)) / ((toFloat64(ask_price) + toFloat64(bid_price)) / 2) * 10000, bid_price > 0 AND ask_price >= bid_price AND sip_timestamp >= '2026-06-29 17:00:00' AND sip_timestamp < '2026-06-29 17:30:00'), 2) AS midday_spread_bps,
    round(quantileExactIf(0.5)((toFloat64(ask_price) - toFloat64(bid_price)) / ((toFloat64(ask_price) + toFloat64(bid_price)) / 2) * 10000, bid_price > 0 AND ask_price >= bid_price AND sip_timestamp >= '2026-06-29 13:30:00' AND sip_timestamp < '2026-06-29 14:00:00')
        - quantileExactIf(0.5)((toFloat64(ask_price) - toFloat64(bid_price)) / ((toFloat64(ask_price) + toFloat64(bid_price)) / 2) * 10000, bid_price > 0 AND ask_price >= bid_price AND sip_timestamp >= '2026-06-29 17:00:00' AND sip_timestamp < '2026-06-29 17:30:00'), 2) AS open_minus_midday_bps,
    countIf(NOT (bid_price > 0 AND ask_price >= bid_price)) AS dropped_invalid_quotes
FROM global_markets.cache_stocks_quotes
WHERE ticker IN ('SPY', 'QQQ', 'MU', 'WDC')
  AND sip_timestamp >= '2026-06-29 13:30:00' AND sip_timestamp < '2026-06-29 20:00:00'
GROUP BY ticker
ORDER BY ticker
Run this yourself

SPY's median quoted width was 0.41 basis points of its mid-price, against 0.84 for QQQ, 4.6 for Micron and 8.2 for Western Digital, the widest of the four. One basis point of a $10,000 order is one dollar, so buying and instantly selling $10,000 of SPY at the quote costs about $0.41; the same round trip in Western Digital costs $8.2. The trade size did not change; the name did.

All four quoted wider in the opening half hour than in the midday control window, SPY 0.41 bps against 0.27, Western Digital 14.33 against 7.55, a 6.78 bp opening premium on the widest name. Churn is the other half: 3.98 million NBBO updates on SPY in regular hours, 4.42 million on QQQ.

Rates: the curve barely moved

Treasuries had a quiet Monday. Yields are daily closes, changed against June 26.

QueryThe Treasury curve, June 29 close vs June 26 (populated maturities only)
The exact SQL behind every number
SELECT
    t.1 AS curve_point,
    round(t.2, 2) AS jun29_yield_pct,
    round((t.2 - t.3) * 100) AS one_day_change_bp
FROM (
    SELECT arrayJoin([
        ('1 month',  toFloat64(mon.yield_1_month),  toFloat64(fri.yield_1_month)),
        ('3 month',  toFloat64(mon.yield_3_month),  toFloat64(fri.yield_3_month)),
        ('1 year',   toFloat64(mon.yield_1_year),   toFloat64(fri.yield_1_year)),
        ('2 year',   toFloat64(mon.yield_2_year),   toFloat64(fri.yield_2_year)),
        ('5 year',   toFloat64(mon.yield_5_year),   toFloat64(fri.yield_5_year)),
        ('10 year',  toFloat64(mon.yield_10_year),  toFloat64(fri.yield_10_year)),
        ('30 year',  toFloat64(mon.yield_30_year),  toFloat64(fri.yield_30_year)),
        ('2s10s spread', toFloat64(mon.yield_10_year - mon.yield_2_year), toFloat64(fri.yield_10_year - fri.yield_2_year))
    ]) AS t
    FROM (SELECT * FROM global_markets.treasury_yields WHERE date = '2026-06-29') AS mon,
         (SELECT * FROM global_markets.treasury_yields WHERE date = '2026-06-26') AS fri
)
Run this yourself

The 10-year closed at 4.38%, unchanged on the day (0 bp), while the 3-month bill added 4 bp to 3.87%. The 2s10s spread, the 10-year minus the 2-year, closed at 0.28 percentage points (-3 bp): still positive, slightly flatter.

The calendar behind the day

QueryJune 29's corporate calendar and information flow, in one row
The exact SQL behind every number
WITH
    (
        SELECT (argMax(d, n), max(n))
        FROM (
            SELECT ex_dividend_date AS d, count() AS n
            FROM global_markets.stocks_dividends
            WHERE ex_dividend_date BETWEEN '2026-06-22' AND '2026-07-02'
            GROUP BY d
        )
    ) AS peak_ex_div,
    (
        SELECT (countIf(form_type = '424B2'), countIf(form_type = '4'), countIf(form_type = '8-K'), count())
        FROM global_markets.stocks_sec_edgar_index
        WHERE filing_date = '2026-06-29'
    ) AS filings,
    (
        SELECT (count(), uniqExact(publisher), countIf(has(tickers, 'NVDA')))
        FROM global_markets.stocks_news
        WHERE toDate(toTimeZone(published_utc, 'America/New_York')) = '2026-06-29'
    ) AS news,
    (
        SELECT max(n)
        FROM (
            SELECT t, count() AS n
            FROM (
                SELECT arrayJoin(tickers) AS t
                FROM global_markets.stocks_news
                WHERE toDate(toTimeZone(published_utc, 'America/New_York')) = '2026-06-29'
            )
            WHERE t NOT IN ('NVDA', 'SPCX')
            GROUP BY t
        )
    ) AS runner_up_articles,
    (
        SELECT (any(split_from), any(split_to), count())
        FROM global_markets.stocks_splits
        WHERE ticker = 'HON' AND execution_date = '2026-06-29'
    ) AS hon_split,
    (
        SELECT (
            round(toFloat64(argMaxIf(close, window_start, window_start < '2026-06-27 00:00:00')), 2),
            round(toFloat64(argMaxIf(close, window_start, window_start >= '2026-06-29 00:00:00')), 2)
        )
        FROM global_markets.delayed_stocks_minute_aggs
        WHERE ticker = 'HON'
          AND ((window_start >= '2026-06-26 13:30:00' AND window_start < '2026-06-26 20:00:00')
            OR (window_start >= '2026-06-29 13:30:00' AND window_start < '2026-06-29 20:00:00'))
    ) AS hon_close
SELECT
    (SELECT count() FROM global_markets.stocks_dividends WHERE ex_dividend_date = '2026-06-29') AS ex_dividend_records_jun29,
    (SELECT count() FROM global_markets.stocks_dividends WHERE ex_dividend_date = '2026-06-29'
        AND ticker IN ('AAPL', 'MSFT', 'NVDA', 'AMZN', 'GOOGL', 'GOOG', 'META', 'TSLA', 'JPM', 'JNJ',
                       'XOM', 'KO', 'PG', 'V', 'MA', 'HD', 'WMT', 'CVX', 'MRK', 'PEP',
                       'SPY', 'QQQ', 'DIA', 'IWM', 'VTI')) AS household_name_ex_dividends,
    length(['AAPL', 'MSFT', 'NVDA', 'AMZN', 'GOOGL', 'GOOG', 'META', 'TSLA', 'JPM', 'JNJ',
            'XOM', 'KO', 'PG', 'V', 'MA', 'HD', 'WMT', 'CVX', 'MRK', 'PEP',
            'SPY', 'QQQ', 'DIA', 'IWM', 'VTI']) AS household_names_checked,
    concat(monthName(peak_ex_div.1), ' ', toString(toDayOfMonth(peak_ex_div.1))) AS busiest_ex_div_day_of_window,
    peak_ex_div.2 AS busiest_ex_div_day_records,
    (SELECT count() FROM global_markets.stocks_splits WHERE execution_date = '2026-06-29') AS splits_executed,
    (SELECT count() FROM global_markets.stocks_ipos WHERE listing_date = '2026-06-29') AS ipos_listed_jun29,
    (SELECT arrayStringConcat(groupArray(ticker), ', ') FROM (
        SELECT ticker FROM global_markets.stocks_ipos WHERE listing_date = '2026-07-01' ORDER BY ticker
    )) AS jul1_ipo_debuts,
    filings.4 AS sec_filings,
    filings.1 AS prospectus_424b2_filings,
    filings.2 AS insider_form4_filings,
    filings.3 AS filings_8k,
    news.1 AS news_articles,
    news.2 AS news_publishers,
    news.3 AS nvda_articles,
    runner_up_articles AS next_most_covered_articles,
    news.3 - runner_up_articles AS nvda_minus_next_most_covered,
    hon_split.3 AS hon_split_records,
    hon_split.1 AS hon_split_from,
    hon_split.2 AS hon_split_to,
    hon_close.1 AS hon_close_jun26,
    hon_close.2 AS hon_close_jun29
Run this yourself

449 dividend records went ex-dividend on June 29, own the stock before its ex-dividend date or the payment is not yours, yet among 25 household names we checked, 0 appeared. The quarter-end wave crested at 746 records on July 1. 23 splits executed and 0 IPOs listed, Wednesday's debuts (BSP, ITG, LIME) already on the calendar.

The SEC logged 6173 filings dated June 29: 1473 structured-product pricing supplements (form 424B2) and 1277 insider-trade reports (Form 4) dwarf the 231 8-Ks that make headlines. Our news feed carried 170 articles from 3 publishers; excluding one ambiguously-tagged reused symbol (dropped in the query), the most-covered name was NVDA at 14 articles against the runner-up's 12.

Data notes

Weird data gets a note, never silent exclusion, these four touch headline numbers.

  • The sector baskets are a declared method. "Sector" means the eleven SPDR sector ETFs, market-value-weighted, the same names every session, not a vendor's per-ticker classification.
  • Dollar volume is a per-minute proxy, close × volume summed per minute bar, near but not identical to the sum of print values.
  • Quoted spreads exclude invalid quotes (one-sided or crossed NBBO records); the panel counts what it dropped (1544 on SPY). A median quoted width is a quoting statistic, not a per-trade cost, orders often execute inside the quote.
  • A lone print can contaminate a minute bar's high or low. QQQ's 11:07 ET bar carries a $709.58 low while no surrounding bar dipped below $715.09, one print $5.51 under the concurrent market. Every high and low above was cross-checked against adjacent bars; QQQ's $705.172 session low passed.
Full data notes
  • Treasury coverage is thinner than the schema. Four advertised maturities (6-month, 3-, 7- and 20-year) have never been populated; the curve shows the seven that exist, plus the 2s10s row.
  • A Honeywell "reverse split" the tape contradicts. The feed carries 1 HON record dated June 29: a reverse split converting 2 old shares into 1 new. HON closed $231.3 Friday and $227.71 Monday, no doubling. We did not apply it.
  • The news count is one vendor's feed, the 3 publishers we carry, not "all market news".
  • The tick-level receipts, crossed quotes, the phantom-volume correction, the truncated FINRA short-volume file (and why short volume is not short interest), sit in the deep dive.

The session, verified

QuerySession check: SPY's observed minute-bar span, and the Friday closure on the tape
The exact SQL behind every number
WITH
    (
        SELECT (
            round(toFloat64(minIf(low, formatDateTime(toTimeZone(window_start, 'America/New_York'), '%H:%i') = '11:07')), 2),
            round(toFloat64(minIf(low, formatDateTime(toTimeZone(window_start, 'America/New_York'), '%H:%i') IN ('11:04', '11:05', '11:06', '11:08', '11:09', '11:10'))), 2)
        )
        FROM global_markets.delayed_stocks_minute_aggs
        WHERE ticker = 'QQQ' AND window_start >= '2026-06-29 13:30:00' AND window_start < '2026-06-29 20:00:00'
    ) AS qqq_lone
SELECT
    (SELECT count() FROM global_markets.delayed_stocks_minute_aggs
     WHERE ticker = 'SPY' AND window_start >= '2026-07-03 00:00:00' AND window_start < '2026-07-04 00:00:00') AS jul3_spy_bars,
    formatDateTime(min(toTimeZone(window_start, 'America/New_York')), '%H:%i') AS first_spy_bar_et,
    formatDateTime(max(toTimeZone(window_start, 'America/New_York')), '%H:%i') AS last_spy_bar_et,
    count() AS spy_minute_bars,
    countIf(window_start >= '2026-06-29 13:30:00' AND window_start < '2026-06-29 20:00:00') AS regular_session_bars,
    qqq_lone.1 AS qqq_1107_lone_low,
    qqq_lone.2 AS qqq_1107_adjacent_bars_low,
    round(qqq_lone.2 - qqq_lone.1, 2) AS qqq_lone_print_below_adjacent
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker = 'SPY' AND window_start >= '2026-06-29 00:00:00' AND window_start < '2026-06-30 00:00:00'
Run this yourself

SPY's bars run 04:00 to 19:59 New York time with exactly 390 regular-window bars, a complete session, verified from the tape (the exchange-calendar dataset carries only upcoming closures). That Friday, July 3, was a full closure, 0 SPY bars printed all day, July 4 landing on a Saturday; the four-session week has its own recap.

FAQ

How did the stock market do on June 29, 2026?

An up day led by growth: QQQ closed 2.57% above Friday, SPY 1.62%, DIA 0.81% and IWM 0.45%, with 62.3% of tickers trading $1m or more finishing higher.

Was June 29, 2026 a big day for the Nasdaq?

Big, not exceptional: QQQ's 2.57% close-over-close move ranked 5 of the trailing month's 21 sessions by absolute size, against a monthly high of 4.76%.

Which sectors led on June 29, 2026?

Technology (2.52%) and consumer discretionary (2.37%) led the eleven SPDR sector baskets; materials was last at -1.82%, a spread of 4.34 percentage points. Utilities, staples, energy and real estate also closed lower.

What share of options volume on June 29, 2026 was 0DTE?

Same-day expiries were 35.8% of the 66.33 million contracts traded; the busiest single contract, a same-day SPY $741 call (788,133 contracts), finished out of the money.

Why do options trade before the stock market opens?

Only index options do. All 40,621 option prints before 9:30 a.m. ET on June 29 sat on cash-settled index roots (RUTW, SPX, SPXW, VIX, VIXW, XSP), which list extended global-trading-hours sessions; stock and ETF option prints in that window numbered 0.

Methodology

  • Timestamps are stored in UTC, converted to New York time inside the queries. "Close" is the last regular-session minute bar, not the auction print; day changes compare June 29 with June 26, and the session was verified from the observed bar span.
  • The trailing-month rank uses regular-hours close-over-close moves, first session dropped (no prior close inside the window). Option expiries are re-parsed from the OCC ticker (the table's own expiry column is broken).
  • Every panel is read once, at authoring time, through the gated read-only path. Warehouse state as of July 13, 2026.

Every panel is a stored query result, chart, table and SQL in one object. Paste any of them into the Strasmore terminal. Next session: June 30.