Strasmore Research
Market Recap

Market Recap: June 2026

Matt ConnorBy Matt Connor Updated 2026-07-26

June 2026 was a month of divergence: the large-cap indexes slipped while small caps rose, and beneath the index level, more stocks fell than rose. SPY finished at $746.32, -1.2% on the month, while IWM, the small-cap index, gained 4.2%. The month's other standout measured fact: one memory-chip maker put $995.7 billion through the tape in 21 sessions, more than SPY itself. Every number here is a stored query result; expand any panel for the exact SQL.

The month on the board

QueryJune 2026: open to close, range, and regular-hours turnover for the four index ETFs
tickermonth_openmonth_closemonth_return_pctmonth_highmonth_lowrth_dollar_bn
DIA509.85522.282.4526.57500.154.1
IWM288.37300.424.2301.5277.62162.4
QQQ737.04735.76-0.2748.65686.37672.8
SPY755.36746.32-1.2760.4716.58771.5
The exact SQL behind every number
SELECT ticker,
    round(argMinIf(toFloat64(open), window_start, (toHour(window_start) * 60 + toMinute(window_start)) BETWEEN 810 AND 1199), 2) AS month_open,
    round(argMaxIf(toFloat64(close), window_start, (toHour(window_start) * 60 + toMinute(window_start)) BETWEEN 810 AND 1199), 2) AS month_close,
    round((argMaxIf(toFloat64(close), window_start, (toHour(window_start) * 60 + toMinute(window_start)) BETWEEN 810 AND 1199) / argMinIf(toFloat64(open), window_start, (toHour(window_start) * 60 + toMinute(window_start)) BETWEEN 810 AND 1199) - 1) * 100, 1) AS month_return_pct,
    round(maxIf(toFloat64(high), (toHour(window_start) * 60 + toMinute(window_start)) BETWEEN 810 AND 1199), 2) AS month_high,
    round(minIf(toFloat64(low), (toHour(window_start) * 60 + toMinute(window_start)) BETWEEN 810 AND 1199), 2) AS month_low,
    round(sumIf(toFloat64(close) * toFloat64(volume), (toHour(window_start) * 60 + toMinute(window_start)) BETWEEN 810 AND 1199) / 1e9, 1) AS rth_dollar_bn
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker IN ('SPY', 'QQQ', 'DIA', 'IWM')
  AND window_start >= toDateTime('2026-06-01 00:00:00') AND window_start < toDateTime('2026-07-01 00:00:00')
GROUP BY ticker
ORDER BY ticker
Run this yourself

The split reads directly off the table: DIA 2.4% and IWM 4.2% rose while SPY fell -1.2% and QQQ finished -0.2%, the mega-cap growth index flat-to-down in the same month small caps gained four percent.

June against the five months before it

Was June's dip unusual? The trailing panel recomputes the same monthly return for every month of the half in one query, the June rows are produced identically to January's, live at generation time.

QueryMonthly returns, January through June 2026, recomputed live (SPY and QQQ)
period_starttickermonth_return_pctmonth_close
2026-01-01QQQ0.2621.43
2026-01-01SPY0.7690.57
2026-02-01QQQ-2604.93
2026-02-01SPY-0.8683.35
2026-03-01QQQ-3.9577.14
2026-03-01SPY-4.4650.24
2026-04-01QQQ14.8667.6
2026-04-01SPY9.9718.43
2026-05-01QQQ10.3738.25
2026-05-01SPY4.9756.4
2026-06-01QQQ-0.2735.76
2026-06-01SPY-1.2746.32
The exact SQL behind every number
SELECT toString(toStartOfMonth(toDate(toTimeZone(window_start, 'America/New_York')))) AS period_start, ticker,
    round((argMaxIf(toFloat64(close), window_start, (toHour(window_start) * 60 + toMinute(window_start)) BETWEEN 810 AND 1199) / argMinIf(toFloat64(open), window_start, (toHour(window_start) * 60 + toMinute(window_start)) BETWEEN 810 AND 1199) - 1) * 100, 1) AS month_return_pct,
    round(argMaxIf(toFloat64(close), window_start, (toHour(window_start) * 60 + toMinute(window_start)) BETWEEN 810 AND 1199), 2) AS month_close
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker IN ('SPY', 'QQQ')
  AND window_start >= toDateTime('2026-01-01 00:00:00') AND window_start < toDateTime('2026-07-01 00:00:00')
GROUP BY period_start, ticker
ORDER BY period_start, ticker
Run this yourself

By that yardstick June was ordinary: SPY's -1.2% sits well inside the half's range, March fell -4.4% and April rose 9.9%. The month's character was rotation, not direction.

June against every June on the tape

QueryEvery June on the tape: SPY, recomputed identically by year (session counts shown)
23 rows (showing 20)
ysessionsjune_return_pct
2004211.8
200522-0.4
200622-0.3
200721-2.5
200821-8.6
200922-1.8
201022-4.8
201122-1.9
2012215.3
201320-2.2
2014211.4
201522-2.9
2016220.2
201722-0.1
201821-0.4
2019206.5
2020221.6
2021221.3
202221-9.1
2023216
The exact SQL behind every number
SELECT toYear(toTimeZone(window_start, 'America/New_York')) AS y,
       uniqExact(toDate(toTimeZone(window_start, 'America/New_York'))) AS sessions,
       round((argMax(toFloat64(close), window_start) / argMin(toFloat64(open), window_start) - 1) * 100, 1) AS june_return_pct
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker IN ('SPY')
  AND window_start >= toDateTime('2003-01-01 00:00:00')
  AND window_start < toDateTime('2026-07-01 00:00:00')
  AND toMonth(toTimeZone(window_start, 'America/New_York')) = 6
  AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
GROUP BY y
HAVING sessions >= 17
ORDER BY y ASC
Run this yourself
QueryThe rank receipt: this June against every prior one (rank 1 = best; self-excluded)
june_2026_pctrank_bestjunes_comparedfirst_yearsessions_2026
-1.21523200421
The exact SQL behind every number
SELECT round(anyIf(ret, y = 2026), 1) AS june_2026_pct,
       arrayCount(x -> x > anyIf(ret, y = 2026), groupArrayIf(ret, y != 2026)) + 1 AS rank_best,
       count() AS junes_compared,
       min(y) AS first_year,
       anyIf(sessions, y = 2026) AS sessions_2026
FROM (
    SELECT toYear(toTimeZone(window_start, 'America/New_York')) AS y,
           uniqExact(toDate(toTimeZone(window_start, 'America/New_York'))) AS sessions,
           (argMax(toFloat64(close), window_start) / argMin(toFloat64(open), window_start) - 1) * 100 AS ret
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker IN ('SPY')
      AND window_start >= toDateTime('2003-01-01 00:00:00')
      AND window_start < toDateTime('2026-07-01 00:00:00')
      AND toMonth(toTimeZone(window_start, 'America/New_York')) = 6
      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
    GROUP BY y
    HAVING sessions >= 17
)
Run this yourself

SPY's -1.2% ranks 15 of 23 Junes since 2004 (rank 1 = best), mid-pack, slightly below the middle. An ordinary June, and ordinary is a valid finding: nothing about the month's index-level move stands out against two decades of Junes. What stood out was the rotation underneath it, the breadth panel below. Basis: regular-hours open-to-close within each calendar June, identical arithmetic every year, upper bound pinned at this month's end so the comparison set cannot silently grow.

Session by session

QuerySPY, all 21 June sessions: close and close-over-close change
21 rows (showing 20)
et_datespy_closechange_pct
2026-06-01758.430.3
2026-06-02759.450.1
2026-06-03754.19-0.7
2026-06-04757.060.4
2026-06-05737.42-2.6
2026-06-08739.310.3
2026-06-09737.05-0.3
2026-06-10725.42-1.6
2026-06-11737.641.7
2026-06-12741.630.5
2026-06-15754.661.8
2026-06-16750.39-0.6
2026-06-17741.02-1.2
2026-06-18746.560.7
2026-06-22744.37-0.3
2026-06-23733.67-1.4
2026-06-24733.12-0.1
2026-06-25733.260
2026-06-26729.09-0.6
2026-06-29740.881.6
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
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-05-29 00:00:00') AND window_start < toDateTime('2026-07-01 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-01 00:00:00') AND window_start < toDateTime('2026-07-01 00:00:00')
GROUP BY et_date
ORDER BY et_date
Run this yourself

The month's sharpest single-session fall came early, -2.6% on 2026-06-05, and the chart shows the shape of the month: a drift lower through mid-June, a floor near $716.58, and a partial recovery over the final sessions into the quarter turn. For the last week of that recovery in session-by-session detail, see the week recap.

Breadth: more fell than rose

QueryAdvancers vs decliners for June (close vs the last May close), liquidity filter disclosed
advancersdeclinersunchangeddropped_by_liquidity_filter
42404608482896
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
FROM (
    SELECT ticker,
        argMaxIf(toFloat64(close), window_start, toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2026-06-01') 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-05-29') 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-01')) < 5e6 AS dropped
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE window_start >= toDateTime('2026-05-29 00:00:00') AND window_start < toDateTime('2026-07-01 00:00:00')
    GROUP BY ticker
    HAVING countIf(toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-05-29') 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-01') AND (toHour(window_start) * 60 + toMinute(window_start)) BETWEEN 810 AND 1199) > 0
)
Run this yourself

4608 tickers fell on the month against 4240 that rose, negative breadth in a month where two of the four index ETFs gained. Index weighting and equal-count breadth answer different questions; June is a month where they disagreed. The liquidity filter set aside 2896 tickers under $5M of June volume, counted here rather than hidden. The day grain agrees with the ticker grain: 12 of the month's 21 sessions closed SPY lower against 9 higher.

QuerySPY sessions up vs down across the month, one cheap receipt
up_sessionsdown_sessionsflat_sessionssessions_total
912021
The exact SQL behind every number
SELECT countIf(day_ret > 0) AS up_sessions,
       countIf(day_ret < 0) AS down_sessions,
       countIf(day_ret = 0) AS flat_sessions,
       count() AS sessions_total
FROM (
    SELECT toDate(toTimeZone(window_start, 'America/New_York')) AS d,
           round((argMax(toFloat64(close), window_start) / argMin(toFloat64(open), window_start) - 1) * 100, 2) AS day_ret
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker = 'SPY'
      AND window_start >= toDateTime('2026-06-01 00:00:00')
      AND window_start < toDateTime('2026-07-01 00:00:00')
      AND (toHour(window_start) * 60 + toMinute(window_start)) BETWEEN 810 AND 1199
    GROUP BY d
)
Run this yourself

The tape's leaders

QueryJune regular-hours dollar volume, whole tape (one reused-symbol listing excluded pending entity verification)
tickerdollar_bnpct_of_leader
MU995.7100
SPY771.577.5
QQQ672.867.6
NVDA52352.5
SNDK381.638.3
TSLA342.734.4
MRVL302.730.4
INTC272.427.4
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-01 00:00:00') AND window_start < toDateTime('2026-07-01 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
Run this yourself

MU led the entire month at $995.7 billion, SPY, in second, did 77.5% of that. NVDA put $523 billion through in fourth; its month has its own tick-by-tick deep-dive. A semiconductor theme owns the list, four of the eight names. Basis: June 1–30 regular hours; one reused-symbol June listing is excluded pending entity verification, and its first month has its own post.

The options tape: June against May

QueryJune: whole-tape options contract volume and same-day-expiry share (one scan)
contracts_mmzero_dte_pctsessions
1477.934.321
The exact SQL behind every number
SELECT round(sum(toFloat64(volume)) / 1e6, 1) AS contracts_mm,
       round(100.0 * sumIf(toFloat64(volume), toDateOrNull(concat('20', substring(ticker, length(ticker) - 14, 6))) = toDate(toTimeZone(window_start, 'America/New_York'))) / sum(toFloat64(volume)), 1) AS zero_dte_pct,
       uniqExact(toDate(toTimeZone(window_start, 'America/New_York'))) AS sessions
FROM global_markets.options_minute_aggs
WHERE window_start >= toDateTime('2026-06-01 00:00:00')
  AND window_start < toDateTime('2026-07-01 00:00:00')
Run this yourself
QueryMay, recomputed identically for contrast (one scan)
contracts_mmzero_dte_pctsessions
1394.630.320
The exact SQL behind every number
SELECT round(sum(toFloat64(volume)) / 1e6, 1) AS contracts_mm,
       round(100.0 * sumIf(toFloat64(volume), toDateOrNull(concat('20', substring(ticker, length(ticker) - 14, 6))) = toDate(toTimeZone(window_start, 'America/New_York'))) / sum(toFloat64(volume)), 1) AS zero_dte_pct,
       uniqExact(toDate(toTimeZone(window_start, 'America/New_York'))) AS sessions
FROM global_markets.options_minute_aggs
WHERE window_start >= toDateTime('2026-05-01 00:00:00')
  AND window_start < toDateTime('2026-06-01 00:00:00')
Run this yourself

June's whole-tape options volume was 1477.9 million contracts with 34.3% trading on same-day expiries, against May's 1394.6 million and 30.3%. More volume AND a bigger same-day share: June's 0DTE share is the highest month of the half, and the January-onward evolution is in the H1 recap. A third of June's options volume expired the day it traded.

QuerySPY median quoted spread: the June sample session against May's (second Wednesdays)
sessionmed_spread_bpsquote_updates_minvalid_dropped
2026-05-130.272.64661
2026-06-100.4097.4812382
The exact SQL behind every number
SELECT toDate(toTimeZone(sip_timestamp, 'America/New_York')) AS session,
       round(quantileDeterministicIf(0.5)((toFloat64(ask_price) - toFloat64(bid_price)) / ((toFloat64(ask_price) + toFloat64(bid_price)) / 2) * 10000, toUInt64(toUnixTimestamp64Micro(sip_timestamp)), bid_price > 0 AND ask_price >= bid_price), 3) AS med_spread_bps,
       round(count() / 1e6, 2) AS quote_updates_m,
       countIf(NOT (bid_price > 0 AND ask_price > 0 AND ask_price >= bid_price)) AS invalid_dropped
FROM global_markets.cache_stocks_quotes
WHERE ticker = 'SPY'
  AND ((sip_timestamp >= toDateTime64('2026-05-13 13:30:00', 9) AND sip_timestamp < toDateTime64('2026-05-13 20:00:00', 9))
    OR (sip_timestamp >= toDateTime64('2026-06-10 13:30:00', 9) AND sip_timestamp < toDateTime64('2026-06-10 20:00:00', 9)))
GROUP BY session
ORDER BY session ASC
Run this yourself

The cost to trade the market's biggest ticket moved the same direction: SPY's median quoted spread on the June sample session (0.409 bps, June 10) is wider than May's (0.27 bps, May 13). Basis, disclosed: single labeled sample sessions, the second Wednesday of each month, not month-wide medians; invalid quotes are counted in the panel.

Rates: the curve barely moved

QueryTreasury yields through June: 10-year, 2-year, and the 2s10s spread
21 rows (showing 20)
dy10y2spread_2s10s_bp
2026-06-014.474.0542
2026-06-024.464.0541
2026-06-034.494.0841
2026-06-044.474.0542
2026-06-054.554.1738
2026-06-084.564.1541
2026-06-094.534.1340
2026-06-104.554.1342
2026-06-114.454.0540
2026-06-124.484.0939
2026-06-154.474.0740
2026-06-164.434.0538
2026-06-174.494.229
2026-06-184.464.1927
2026-06-224.514.2427
2026-06-234.54.1634
2026-06-244.414.1130
2026-06-254.44.0931
2026-06-264.384.0731
2026-06-294.384.128
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-01') AND date <= toDate('2026-06-30')
  AND isNotNull(yield_10_year) AND isNotNull(yield_2_year)
ORDER BY date
Run this yourself

The 10-year ended June at 4.44% with the 2s10s spread at 30 basis points, a quiet month for rates against a noisier half (the H1 recap carries the six-month curve story).

The calendar: heavy quarter-end traffic, one missing filing day

QueryJune's corporate calendar: dividends, splits, listings, filings (June 30 index gap disclosed)
ex_div_eventssplitsiposjune_filingsfilings_jun30news_articles
66511643568388315707
The exact SQL behind every number
SELECT
    (SELECT count() FROM global_markets.stocks_dividends WHERE ex_dividend_date >= toDate('2026-06-01') AND ex_dividend_date <= toDate('2026-06-30')) AS ex_div_events,
    (SELECT count() FROM global_markets.stocks_splits WHERE execution_date >= toDate('2026-06-01') AND execution_date <= toDate('2026-06-30')) AS splits,
    (SELECT count() FROM global_markets.stocks_ipos WHERE listing_date >= toDate('2026-06-01') AND listing_date <= toDate('2026-06-30')) AS ipos,
    (SELECT uniqExact(accession_number) FROM global_markets.stocks_sec_edgar_index WHERE filing_date >= toDate('2026-06-01') AND filing_date <= toDate('2026-06-30')) AS june_filings,
    (SELECT uniqExact(accession_number) FROM global_markets.stocks_sec_edgar_index WHERE filing_date = toDate('2026-06-30')) AS filings_jun30,
    (SELECT count() FROM global_markets.stocks_news WHERE published_utc >= toDateTime('2026-06-01 00:00:00') AND published_utc < toDateTime('2026-07-01 00:00:00')) AS news_articles
Run this yourself

June carried 6651 ex-dividend events, 164 splits, and 35 new listings, among them the month's headline debut, covered receipt-by-receipt in the SpaceX first-month deep-dive. One disclosure on the filing count: the SEC index shows 68388 June filings, but June 30 itself carries only 31, the index for the month's last day is materially incomplete (neighboring days carry thousands), so the June total is understated until the feed backfills, part of a 2026 month-end pattern with its own diagnostic note.

The dividend wave

QueryEx-dividend events by day through June
22 rows (showing 20)
dex_div_events
2026-06-01807
2026-06-02116
2026-06-03131
2026-06-04190
2026-06-05200
2026-06-08108
2026-06-09135
2026-06-10129
2026-06-11164
2026-06-12211
2026-06-15773
2026-06-16153
2026-06-17118
2026-06-18439
2026-06-1912
2026-06-22407
2026-06-23355
2026-06-24286
2026-06-25505
2026-06-26259
The exact SQL behind every number
SELECT toString(ex_dividend_date) AS d, count() AS ex_div_events
FROM global_markets.stocks_dividends
WHERE ex_dividend_date >= toDate('2026-06-01') AND ex_dividend_date <= toDate('2026-06-30')
GROUP BY d
ORDER BY d
Run this yourself

Dividend traffic clusters at the turn of the month: June opened with 807 ex-dividend events in a single day, and the chart carries the month's full rhythm, the daily churn in the low hundreds punctuated by month-boundary spikes. What an ex-dividend date actually does to a price is covered in the ex-dividend explainer.

The shorts: both June prints on file

QueryShort-interest settlements on file: May 29, June 15, and June 30
settlementtickers
2026-05-2921987
2026-06-1522178
2026-06-3022207
The exact SQL behind every number
SELECT toString(settlement_date) AS settlement, count() AS tickers
FROM global_markets.stocks_short_interest
WHERE settlement_date >= toDate('2026-05-20')
  AND settlement_date <= toDate('2026-06-30')
GROUP BY settlement
ORDER BY settlement
Run this yourself

Short interest settles twice a month and publishes on a lag. Both June prints are now on file: the 2026-06-30 settlement carries 22207 tickers, after 22178 on the June 15 file and 21987 on May 29. Daily short volume is a different dataset; its June caveat is the truncated June 29 file, carried in the week recap and the June 29 deep-dive.

The session receipt

Query21 sessions, verified from the tape (Juneteenth closure receipt included)
june_sessionsjuneteenth_spy_barsregular_bars_june
2108190
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-01 00:00:00') AND window_start < toDateTime('2026-07-01 00:00:00')) AS june_sessions,
    (SELECT count() FROM global_markets.delayed_stocks_minute_aggs
     WHERE ticker = 'SPY' AND window_start >= toDateTime('2026-06-19 00:00:00') AND window_start < toDateTime('2026-06-20 00:00:00')) AS juneteenth_spy_bars,
    (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-01 00:00:00') AND window_start < toDateTime('2026-07-01 00:00:00')) AS regular_bars_june
Run this yourself

June ran 21 sessions, Juneteenth (June 19) shows 0 SPY bars, and the 8190 regular bars work out to exactly 21 full 390-minute sessions.

Data notes

Full data notes
  • Short interest runs through the June 30 settlement, both of the month's prints are on file and carried inline above.
  • The June 30 filing-index day is materially incomplete (a handful of administrative filings against thousands on neighboring days); June filing totals are understated until the feed backfills. Every 2026 month-end whose last calendar day was a weekday shows the same signature, the diagnostic note carries the receipts; the Q2 recap carries the quarter-scale caveat.
  • The June 29 FINRA short-volume file is truncated market-wide, receipts in the week recap and the June 29 deep-dive.
  • 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 verification receipts.
  • Breadth compares each ticker's last June close against its last May close; tickers without both closes are excluded by construction and the liquidity filter's exclusions are counted in the panel.

Methodology

  • The period is June 1–30, 2026, 21 sessions, verified from observed bars (the receipt panel above). Monthly returns are first regular-hours open to last regular-hours close within the period.
  • Timestamps are stored UTC and filtered with raw UTC bounds; June 2026 is entirely EDT, so regular hours are 13:30–20:00 UTC. Dollar volume is minute close times minute volume over regular hours.
  • Trailing-month comparisons are recomputed live in the same query as June's own row, never read from a stored value. The every-June history block does the same across the full minute tape (Eastern-wall-clock regular hours, the DST-safe multi-year convention, with its upper bound pinned at this month's end and a minimum-session guard shown per year); the depth verification is in the H1 recap's methodology.
  • Generation runs through the gated read-only path; the public page never queries live. Warehouse state as of July 5, 2026.

This is the first edition of the standing monthly recap, July's edition will link back here. For the month's final week in session-by-session detail, see the week-of-June-29 recap; for the quarter this month closed, see the Q2 recap.