STRASMORE/EXPLORE 2,882 QUERIES

kurtosis_by_year

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-10-01, from real-returns-vs-random-walks.

as of ranking 21×4read in context →
kurtosis_by_year — 21 rows by 4 columns, computed from US exchange, SIP and OPRA data.
yearexcess_kurtosismoves_over_2pctfull_window_excess_kurtosis
20061.08214.95
20071.691414.95
20086.47014.95
20092.025014.95
201022214.95
20112.543314.95
20120.79714.95
20131.35414.95
20141.33414.95
20152.041114.95
20162.211014.95
20172.55014.95
20183.171914.95
20193.06714.95
20207.064214.95
20210.59814.95
20220.324614.95
2023-0.19214.95
20241.7714.95
202523.141414.95
20260.91414.95
Rows × columns
21 × 4
Computed
Completeness
No missing values
Source
US exchange, SIP and OPRA market data
Licence
Strasmore terms · free, no signup
Formats
JSON · CSV · the SQL below

What each column holds

Column definitions for kurtosis_by_year, derived from the stored result.
ColumnTypeRangeNotes
year number 2,006 to 2,026
excess_kurtosis number -0.19 to 23.14
moves_over_2pct number 0 to 70
full_window_excess_kurtosis number every row is 14.95

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 daily AS (SELECT date, argMax(toFloat64(close), _ingest_time) AS px FROM global_markets.stocks_daily_aggs WHERE ticker = 'SPY' AND date >= '2006-01-01' AND date <= '2026-09-30' GROUP BY date),
rets AS (SELECT date, px / prev_px - 1 AS ret FROM (SELECT date, px, lagInFrame(px) OVER (ORDER BY date ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS prev_px FROM daily) WHERE prev_px > 0),
full_window AS (SELECT avg(ret) AS mu, stddevPop(ret) AS sd FROM rets),
full_excess AS (SELECT round(avg(pow((r.ret - f.mu) / f.sd, 4)) - 3, 2) AS ek FROM rets AS r CROSS JOIN full_window AS f),
yearly AS (SELECT toYear(r.date) AS yr, r.ret AS ret, e.ek AS ek FROM rets AS r CROSS JOIN full_excess AS e),
year_stats AS (SELECT yr, avg(ret) AS mu, stddevPop(ret) AS sd FROM yearly GROUP BY yr)
SELECT
    y.yr                                             AS year,
    round(avg(pow((y.ret - s.mu) / s.sd, 4)) - 3, 2) AS excess_kurtosis,
    countIf(abs(y.ret) > 0.02)                       AS moves_over_2pct,
    round(any(y.ek), 2)                              AS full_window_excess_kurtosis
FROM yearly AS y
INNER JOIN year_stats AS s ON y.yr = s.yr
GROUP BY y.yr
ORDER BY y.yr
⌘/Ctrl + Enter

Work with this data in your AI assistant

Opens ready to query, with this page's data. Free, no account.

More from this analysisreal-returns-vs-random-walks
autocorrelation ranking 10×3 → sigma_bands ranking 6×4 → simulated_drawdowns ranking 5×4 → run_lengths table 7×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 → See all 2,882 queries →