forward_returns
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-09-12, from what-happens-after-a-reverse-stock-split.
| horizon | names_count | median_return_pct | share_below_split_close_pct |
|---|---|---|---|
| 20 sessions after | 586 | -12.7 | 69.5 |
| 60 sessions after | 557 | -26 | 73.1 |
| 120 sessions after | 513 | -38.6 | 75.6 |
- Rows × columns
- 3 × 4
- 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 |
|---|---|---|---|
horizon |
text | 3 distinct values | |
names_count |
number | 513 to 586 | count |
median_return_pct |
number | -38.6 to -12.7 | percent |
share_below_split_close_pct |
number | 69.5 to 75.6 | 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.
the exact SQL behind every number
WITH
reverse_splits AS
(
SELECT ticker, toDate(execution_date) AS execution_date
FROM global_markets.stocks_splits
WHERE split_to < split_from
AND toDate(execution_date) >= '2025-03-01'
AND toDate(execution_date) < '2026-03-01'
AND ticker NOT IN ('SPCX')
GROUP BY ticker, execution_date
),
post_split_closes AS
(
SELECT
d.ticker AS ticker,
r.execution_date AS execution_date,
arrayMap(x -> tupleElement(x, 2), arraySort(groupArray((d.date, toFloat64(d.close))))) AS closes
FROM global_markets.stocks_daily_aggs AS d
INNER JOIN reverse_splits AS r ON r.ticker = d.ticker
WHERE d.ticker IN (SELECT ticker FROM reverse_splits)
AND d.date >= '2025-03-01'
AND d.date < '2026-10-01'
AND d.date >= r.execution_date
AND d.date < r.execution_date + 200
AND d.close > 0
GROUP BY d.ticker, r.execution_date
)
SELECT
horizon,
count() AS names_count,
round(quantileDeterministic(0.5)(return_pct, cityHash64(ticker)), 1) AS median_return_pct,
round(100 * countIf(return_pct < 0) / count(), 1) AS share_below_split_close_pct
FROM
(
SELECT
ticker,
sessions_after,
concat(toString(sessions_after), ' sessions after') AS horizon,
100 * (closes[sessions_after + 1] / closes[1] - 1) AS return_pct
FROM post_split_closes
ARRAY JOIN [20, 60, 120] AS sessions_after
WHERE length(closes) > sessions_after
)
GROUP BY horizon
ORDER BY min(sessions_after)
Run your own version of this
The same 22 years of US equities and 12 years of options data are queryable in SQL or plain English. A free account runs 100 queries a day and takes no card.
More from this analysiswhat-happens-after-a-reverse-stock-split
ratio_buckets
ranking 5×3
→
monthly_counts
series 24×5
→
weekly_leaders
ranking 25×4
→
Annualized volatility vs total return, 25 large caps, calmest to wildest (~2 years)
ranking 25×3
→
SPY options median spread by expiration date, near-the-money strikes only
ranking 25×4
→
When market headlines publish, by New York clock hour
ranking 24×2
→
See all 2,214 queries →