expiry_outcomes
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 are-0dte-options-high-risk.
| outcome | expiries |
|---|---|
| 1. Finished at or near zero (5% of the premium or less) | 10 |
| 2. Lost more than half | 1 |
| 3. Lost up to half | 5 |
| 4. Gained, less than doubled | 1 |
| 5. Doubled or better | 4 |
- Rows × columns
- 5 × 2
- 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 |
|---|---|---|---|
outcome |
text | 5 distinct values | |
expiries |
number | 1 to 10 |
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 spy_by_day AS
(
SELECT
toDate(date) AS d,
medianExact(toFloat64(underlying_close)) AS spot
FROM global_markets.options_greeks
WHERE underlying_symbol = 'SPY'
AND date >= toDate('2026-06-01')
AND date < toDate('2026-07-01')
AND underlying_close > 0
GROUP BY d
),
last_sessions AS
(
SELECT
toDate(expiration_date) AS exp_date,
max(toDate(date)) AS prior_session
FROM global_markets.options_greeks
WHERE underlying_symbol = 'SPY'
AND expiration_date >= toDate('2026-06-01')
AND expiration_date < toDate('2026-07-01')
AND date >= toDate('2026-05-22')
AND date < expiration_date
AND volume > 0
GROUP BY exp_date
),
atm AS
(
SELECT
toDate(g.expiration_date) AS exp_date,
argMin(toFloat64(g.strike_price), abs(toFloat64(g.strike_price) - toFloat64(g.underlying_close))) AS strike,
argMin(toFloat64(g.option_close), abs(toFloat64(g.strike_price) - toFloat64(g.underlying_close))) AS premium_before
FROM global_markets.options_greeks AS g
INNER JOIN last_sessions AS ls
ON ls.exp_date = toDate(g.expiration_date) AND ls.prior_session = toDate(g.date)
WHERE g.underlying_symbol = 'SPY'
AND lower(toString(g.option_type)) IN ('call', 'c')
AND g.date >= toDate('2026-05-22')
AND g.date < toDate('2026-07-01')
AND g.volume > 0
AND g.option_close > 0
GROUP BY exp_date
),
outcomes AS
(
SELECT
a.exp_date AS exp_date,
greatest(s.spot - a.strike, 0.0) / a.premium_before AS premium_ratio
FROM atm AS a
INNER JOIN spy_by_day AS s
ON s.d = a.exp_date
)
SELECT
tupleElement(b, 1) AS outcome,
countIf(o.premium_ratio >= tupleElement(b, 2) AND o.premium_ratio < tupleElement(b, 3)) AS expiries
FROM
(
SELECT arrayJoin([
('1. Finished at or near zero (5% of the premium or less)', -1.0, 0.05),
('2. Lost more than half', 0.05, 0.5),
('3. Lost up to half', 0.5, 1.0),
('4. Gained, less than doubled', 1.0, 2.0),
('5. Doubled or better', 2.0, 1000000.0)
]) AS b
) AS buckets
CROSS JOIN outcomes AS o
GROUP BY outcome
ORDER BY outcome
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 analysisare-0dte-options-high-risk
strike_gamma
ranking 9×3
→
expiry_day
series 21×6
→
premium_ladder
table 5×5
→
greeks_ladder
series 5×5
→
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,358 queries →