Every liquid forward split, 2016-2025: median 3-month return after the split vs the S&P 500, by period
Answered against 22 years of US equities and 12 years of US options data and published with the query that produced it. This result is stored as of 2026-07-15, from Does a Stock Go Up After a Split?.
| period | splits | median_runup_before_pct | median_after_3mo_pct | median_spy_3mo_pct | median_gap_vs_spy_pct | pct_beat_spy |
|---|---|---|---|---|---|---|
| 2016-2019 | 139 | 10.6 | 1.8 | 3.8 | -2.1 | 40 |
| 2020-2023 | 177 | 17.1 | 1 | 4.9 | -2.8 | 37 |
| 2024-2025 | 98 | 10.8 | 0 | 3.3 | -1.9 | 41 |
- Rows × columns
- 3 × 7
- Computed
- Completeness
- No missing values
- Source
- US exchange, SIP and OPRA market data
- Licence
- Strasmore terms · free, no signup
What each column holds
| Column | Type | Range | Notes |
|---|---|---|---|
period |
text | 3 distinct values (2016-2019, 2020-2023, 2024-2025) | |
splits |
number | 98 to 177 | |
median_runup_before_pct |
number | 10.6 to 17.1 | percent |
median_after_3mo_pct |
number | 0 to 1.8 | percent |
median_spy_3mo_pct |
number | 3.3 to 4.9 | percent |
median_gap_vs_spy_pct |
number | -2.8 to -1.9 | percent |
pct_beat_spy |
number | 37 to 41 | percent |
Computed from Strasmore's warehouse of US exchange, SIP and OPRA market data. Equity prices are delayed; options greeks and implied volatility are end-of-day. This result is stored, not recomputed on load — it is exactly the numbers that were returned on , and the query below is what returned them.
Run it yourself
This is the exact query behind the result above. Change a ticker, a date or a column and run it against the warehouse — no account, no key. The no-signup tier is smaller than the one this page was computed on; a query that reaches past it comes back saying which plan runs it.
WITH
cohort AS (
SELECT ticker, min(execution_date) AS ex
FROM global_markets.stocks_splits
WHERE adjustment_type IN ('forward_split', 'stock_dividend') AND split_to >= 2 * split_from
AND execution_date >= '2016-01-01' AND execution_date <= '2025-12-31' AND ticker != 'SPCX'
GROUP BY ticker, execution_date
),
bars AS (
SELECT ticker, toDate(toTimeZone(window_start, 'America/New_York')) AS d,
toFloat64(argMax(close, window_start)) AS px
FROM global_markets.delayed_stocks_minute_aggs
WHERE (ticker IN (SELECT ticker FROM cohort) OR ticker = 'SPY')
AND window_start >= '2015-06-01 00:00:00' AND window_start < '2026-02-01 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 ticker, d
),
series AS (
SELECT ticker,
arrayMap(x -> x.1, arraySort(x -> x.1, groupArray((d, px)))) AS days,
arrayMap(x -> x.2, arraySort(x -> x.1, groupArray((d, px)))) AS prices
FROM bars GROUP BY ticker
),
spy AS (SELECT days AS sd, prices AS sp FROM series WHERE ticker = 'SPY'),
fwd AS (
SELECT ex,
(prices[i0 - 1] / prices[i0 - 1 - 126] - 1) * 100 AS run_up,
(prices[i0 + 63] / prices[i0] - 1) * 100 AS ret,
(sp[j0 + 63] / sp[j0] - 1) * 100 AS spy_ret
FROM (
SELECT c.ex AS ex, indexOf(s.days, c.ex) AS i0, indexOf(spy.sd, c.ex) AS j0,
s.prices AS prices, spy.sp AS sp
FROM cohort c INNER JOIN series s ON s.ticker = c.ticker CROSS JOIN spy
)
WHERE i0 > 130 AND length(prices) >= i0 + 63 AND prices[i0] > 0 AND prices[i0 - 1] >= 30
)
SELECT multiIf(toYear(ex) <= 2019, '2016-2019', toYear(ex) <= 2023, '2020-2023', '2024-2025') AS period,
count() AS splits,
round(median(run_up), 1) AS median_runup_before_pct,
round(median(ret), 1) AS median_after_3mo_pct,
round(median(spy_ret), 1) AS median_spy_3mo_pct,
round(median(ret - spy_ret), 1) AS median_gap_vs_spy_pct,
round(100.0 * countIf(ret > spy_ret) / count(), 0) AS pct_beat_spy
FROM fwd GROUP BY period ORDER BY period
Work with this data in your AI assistant
Opens ready to query, with this page's data. Free, no account.