decades
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-18, from the-september-effect.
| label | sep_avg_return_pct | other_months_avg_return_pct | sample_count |
|---|---|---|---|
| 2000s | -0.05 | 0.26 | 6 |
| 2010s | 0.33 | 1.01 | 10 |
| 2020s | -3.12 | 1.56 | 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 |
|---|---|---|---|
label |
text | 3 distinct values (2000s, 2010s, 2020s) | |
sep_avg_return_pct |
number | -3.12 to 0.33 | percent |
other_months_avg_return_pct |
number | 0.26 to 1.56 | percent |
sample_count |
number | 6 to 10 | count |
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 month_ends AS
(
SELECT
toStartOfMonth(date) AS month_start,
argMax(toFloat64(close), date) AS month_end_close
FROM global_markets.stocks_daily_aggs
WHERE ticker = 'SPY'
AND date < toStartOfYear(today())
GROUP BY month_start
),
monthly_returns AS
(
SELECT
month_start,
month_end_close,
lagInFrame(month_end_close, 1) OVER (ORDER BY month_start ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS prev_close
FROM month_ends
)
SELECT
concat(toString(intDiv(toYear(month_start), 10) * 10), 's') AS label,
round(avgIf(monthly_return, toMonth(month_start) = 9) * 100, 2) AS sep_avg_return_pct,
round(avgIf(monthly_return, toMonth(month_start) != 9) * 100, 2) AS other_months_avg_return_pct,
countIf(toMonth(month_start) = 9) AS sample_count
FROM
(
SELECT
month_start,
month_end_close / prev_close - 1 AS monthly_return
FROM monthly_returns
WHERE prev_close > 0
)
GROUP BY label
HAVING countIf(toMonth(month_start) = 9) > 0
ORDER BY label
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 analysisthe-september-effect
septembers_by_year
ranking 22×2
→
worst_septembers
ranking 3×2
→
monthly
table 12×6
→
Top 25 weekly-options underlyings by distinct contracts traded, with expiration weekdays
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
→
See all 2,358 queries →