gap_frequency
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 stocks-with-the-biggest-iv-rv-gap.
| month | names_measured | positive_gap_share_pct | median_gap_pct |
|---|---|---|---|
| 2025-10 | 432 | 69.9 | 5.2 |
| 2025-11 | 320 | 87.8 | 9.3 |
| 2025-12 | 369 | 33.9 | -4 |
| 2026-01 | 395 | 34.4 | -5.1 |
| 2026-02 | 319 | 59.9 | 2.6 |
| 2026-03 | 338 | 72.5 | 6.7 |
| 2026-04 | 358 | 63.7 | 3.2 |
| 2026-05 | 396 | 38.6 | -1.9 |
| 2026-06 | 357 | 58.8 | 2.4 |
- Rows × columns
- 9 × 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 |
|---|---|---|---|
month |
text | 9 distinct values (2025-10, 2025-11, 2025-12…) | |
names_measured |
number | 319 to 432 | |
positive_gap_share_pct |
number | 33.9 to 87.8 | percent |
median_gap_pct |
number | -5.1 to 9.3 | 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
split_tickers AS
(
SELECT DISTINCT ticker
FROM global_markets.stocks_splits
WHERE execution_date >= '2025-10-01'
AND execution_date < '2026-08-01'
),
px AS
(
SELECT
ticker AS symbol,
date AS session,
max(toFloat64(close)) AS px
FROM global_markets.stocks_daily_aggs
WHERE date >= '2025-10-01'
AND date < '2026-08-01'
AND ticker NOT IN (SELECT ticker FROM split_tickers)
AND ticker NOT IN ('SPCX')
GROUP BY symbol, session
),
iv_rows AS
(
SELECT
g.symbol AS symbol,
g.session AS session,
g.iv AS iv
FROM
(
SELECT
underlying_symbol AS symbol,
toDate(date) AS session,
toFloat64(strike_price) AS strike,
implied_volatility AS iv
FROM global_markets.options_greeks
WHERE date >= '2025-10-01'
AND date < '2026-07-01'
AND lower(toString(option_type)) IN ('c', 'call')
AND days_to_expiry BETWEEN 20 AND 45
AND implied_volatility > 0
) AS g
INNER JOIN px AS p ON p.symbol = g.symbol AND p.session = g.session
WHERE p.px > 10
AND abs(g.strike / p.px - 1) < 0.05
),
implied AS
(
SELECT
symbol,
iv_month,
addMonths(iv_month, 1) AS forward_month,
implied_vol_pct
FROM
(
SELECT
symbol,
toStartOfMonth(session) AS iv_month,
round(100 * avg(iv), 1) AS implied_vol_pct
FROM iv_rows
GROUP BY symbol, iv_month
HAVING countDistinct(session) >= 15
AND count() >= 150
)
),
daily_returns AS
(
SELECT
symbol,
arrayJoin(arrayMap((a, b) -> (tupleElement(a, 1), log(tupleElement(a, 2) / tupleElement(b, 2))),
arraySlice(series, 2),
arraySlice(series, 1, length(series) - 1))) AS ret
FROM
(
SELECT
symbol,
arraySort(groupArray((session, px))) AS series
FROM px
WHERE symbol IN (SELECT symbol FROM implied)
GROUP BY symbol
)
),
realized AS
(
SELECT
symbol,
toStartOfMonth(tupleElement(ret, 1)) AS rv_month,
round(100 * sqrt(252) * stddevSamp(tupleElement(ret, 2)), 1) AS realized_vol_pct
FROM daily_returns
GROUP BY symbol, rv_month
HAVING count() >= 15
)
SELECT
formatDateTime(implied.iv_month, '%Y-%m') AS month,
count() AS names_measured,
round(100 * countIf(implied.implied_vol_pct > realized.realized_vol_pct) / count(), 1) AS positive_gap_share_pct,
round(quantileDeterministic(0.5)(implied.implied_vol_pct - realized.realized_vol_pct,
cityHash64(implied.symbol)), 1) AS median_gap_pct
FROM implied
INNER JOIN realized ON realized.symbol = implied.symbol
AND realized.rv_month = implied.forward_month
GROUP BY month
ORDER BY month
Work with this data in your AI assistant
Opens ready to query, with this page's data. Free, no account.