Does a volatility state rescue short 0DTE premium?
Three structures, three cost conventions, nine entry times — 1,073 SPX sessions
Read this before the result
This study tests plain short 0DTE premium — a straddle, a 0.50% strangle and a 0.50%/25-point iron condor — conditioned on a trailing realised-volatility state. It does not implement the paper's own selector, which is a delta-hedged ATM call written only when its price violates the upper second-order stochastic-dominance bound and bought when it violates the lower one. That selector is not rebuilt here. What is replicated is the shape of the Table 7 claim: that a trailing-RV state sorts the after-cost profitability of 0DTE selling into a large gap. A reader who wants a verdict on their exact strategy will not find it below. A reader who wants to know whether the state variable does the sorting will.
The claim, stated as a number. Almeida, Freire & Hizmeri (2025) report an after-cost per-trade Sharpe of 0.404-0.470 in a Low realised-volatility state against 0.004-0.082 in a High one — a Low-minus-High difference of +0.322 to +0.466 per trade. They also state that 0DTE profitability dissipates after 2026-05-11.
The question matters because a realised-volatility state is the most natural thing anyone conditions 0DTE selling on: it is free, it needs no chain, and every retail framing of "sell premium when it is quiet" is a version of it. This site has already published a null for the other obvious state variable — the dealer-gamma book — in Does the dealer-gamma state rescue short 0DTE premium?. This post is the same question with a different state variable on a sample thirty times larger, and the two agree.
Everything fixed before the first run
A rejection is only worth reading if the specification was written down before the numbers were. It was:
| choice | fixed as |
|---|---|
| state variable | rv5 — the annualised standard deviation of one-minute log returns, pooled over the five completed sessions before the trade date, x sqrt(252 x 390) x 100. It uses nothing from the trade session, so it is known before the open. |
| split | expanding-window median with a 60-session burn-in: session t is HighRV iff its rv5 exceeds the median rv5 of all strictly earlier sessions. No lookahead anywhere. The full-sample median split is reported beside it and labelled. Terciles were never run. |
| entry | 10:00 ET primary; the paper's whole 10:00-14:00 half-hour grid reported in full, plus an equal-weight portfolio of the nine. No entry time is chosen on results. |
| structures | straddle at the listed strike nearest spot; strangle_050 (shorts at the first strikes 0.50% out); condor_050_25 (that strangle plus 25-point wings) — definitions carried over unchanged from the earlier gamma study so the two are comparable. |
| exit | held to the cash settlement, no stops, no management. One lot, $100 multiplier. |
| inference | one row per session, so t and SE are day-clustered by construction. Lo (2002) SE on the per-trade Sharpe; 10,000-resample bootstrap for every interval. |
Nothing was tuned. The entry grid below is printed in full rather than at its best cell, and the split rule was not revisited after the first run.
The two states are real, and they are not the calendar
The paper's own weak point, noted when the claim was logged, is that its Table 7 spans 2012-2025, so "Low RV" is partly a proxy for "before 2022". That confound has to be checked here first, because if the split were a calendar the rest of the post would be meaningless.
It is not. The two states differ sharply in the thing they are supposed to measure, and they occur in every year of the sample:
| state | n | mean rv5 | min | max | mean realised |move| to the close |
|---|---|---|---|---|---|
| LowRV | 701 | 8.83% | 4.33% | 17.24% | 22.8 pt |
| HighRV | 307 | 15.44% | 9.89% | 62.35% | 36.4 pt |
The state predicts the move it is meant to predict — 22.8 points against 36.4 — which is the minimum a state variable has to do to be worth conditioning on. And the HighRV share by year runs 42.2% (2022), 8.1% (2023), 20.9% (2024), 46.7% (2025), 47.4% (2026), so the split is not quietly separating one year from another. Of 1,068 sessions with rv5 defined, the 60-session burn-in drops 60 and leaves 1,008 classified: 701 LowRV and 307 HighRV. The expanding median runs below the full-sample median (9.83%) for much of the early sample, which is why the primary, no-lookahead split is unbalanced while the full-sample split is 534/534 by construction.
The headline: nine cells, all the wrong sign
Low minus High, per-trade Sharpe, at the primary 10:00 entry. Three structures by three cost conventions. The paper's number is +0.322 to +0.466.
| structure / cost | n Low | SR/trade Low | n High | SR/trade High | Low - High | 95% bootstrap CI | boot p |
|---|---|---|---|---|---|---|---|
| straddle / mid | 701 | +0.0247 | 307 | +0.0555 | -0.0308 | [-0.1981, +0.1041] | 0.644 |
| straddle / honest | 701 | +0.0019 | 307 | +0.0336 | -0.0317 | [-0.1904, +0.0999] | 0.629 |
| straddle / honest + close-out | 701 | -0.0199 | 307 | +0.0108 | -0.0306 | [-0.1793, +0.0946] | 0.620 |
| strangle_050 / mid | 701 | +0.0213 | 307 | +0.0654 | -0.0441 | [-0.2342, +0.0947] | 0.527 |
| strangle_050 / honest | 701 | +0.0071 | 307 | +0.0509 | -0.0438 | [-0.2282, +0.0932] | 0.516 |
| strangle_050 / honest + close-out | 701 | +0.0084 | 307 | +0.0365 | -0.0281 | [-0.2023, +0.1041] | 0.661 |
| condor_050_25 / mid | 700 | +0.0018 | 304 | +0.0530 | -0.0512 | [-0.1845, +0.0833] | 0.463 |
| condor_050_25 / honest | 700 | -0.0393 | 304 | -0.0201 | -0.0192 | [-0.1541, +0.1135] | 0.792 |
| condor_050_25 / honest + close-out | 700 | -0.0656 | 304 | -0.0528 | -0.0127 | [-0.1452, +0.1192] | 0.878 |
Every one of the nine differences is negative — between -0.013 and -0.051 per trade — which is the opposite sign to the claim. None is individually distinguishable from zero; the smallest bootstrap p is 0.463. And the upper end of every confidence interval, the most favourable reading the data allows, tops out at +0.1192. The claimed +0.322 sits outside all nine intervals, and outside the largest of them by a factor of more than two and a half.
The secondary, full-sample-median split, which uses full-sample information for the class label only, moves nothing: seven of its nine differences are negative, the two positive ones are +0.0127 and +0.0220 on the condor, and its widest interval reaches +0.1384. No version of the split gets near the claimed gap.
Why this is a rejection and not a shrug
A negative result usually means one of two things, and readers are rarely told which. Either the effect is absent, or the sample was too small to see it — and most published nulls are quietly the second. The distinction has a number attached to it: the minimum detectable effect, the smallest difference the sample could have found at a given power. If the claimed effect is larger than the MDE, a null is evidence against the claim. If it is smaller, the null is evidence about nothing.
Here the MDE at 80% power is 0.192 per trade (0.1918 to 0.1926 across the nine cells; the 95%-significance threshold is 0.134). The claimed effect is 0.322 to 0.466 — between 1.7 and 2.4 times larger than what this sample would have resolved eight times in ten. Every one of the nine cells clears the bar.
| structure | cost | SE(diff) per trade | MDE at 95% significance | MDE at 80% power | observed diff | could it see +0.322? |
|---|---|---|---|---|---|---|
| straddle | honest | 0.0685 | 0.1342 | 0.1918 | -0.0317 | yes |
| strangle_050 | honest | 0.0685 | 0.1342 | 0.1919 | -0.0438 | yes |
| condor_050_25 | honest | 0.0687 | 0.1347 | 0.1925 | -0.0192 | yes |
So the honest statement is not "we could not find it". It is: an effect of the claimed size would have shown up here, in this trade, on this state variable, and it did not.
Costs, so the result cannot be waved away
Three cost conventions are carried through everything above. Mid enters at the midpoint of every leg and settles at intrinsic, no fees — the benchmark, never the conclusion. Honest crosses the full quoted spread on entry: sell the bid on every short leg, pay the ask on every long leg, $1.25 per contract per leg, then settle at intrinsic. Honest + close-out does that and then, instead of settling, buys the position back at the 15:55 snapshot crossing the full spread again, with fees on both sides. That last one is deliberately punitive; nobody has to pay a full exit spread on a cash-settled expiry.
What the market charges to put these on, at 10:00 across all 1,073 sessions:
| structure | n | mid credit | honest credit | spread cost | spread % of mid | mean payout |
|---|---|---|---|---|---|---|
| straddle | 1073 | $2,861 | $2,809 | $52 | 1.8% | $2,777 |
| strangle_050 | 1073 | $992 | $967 | $25 | 2.5% | $914 |
| condor_050_25 | 1067 | $576 | $537 | $40 | 6.9% | $559 |
The unconditional level, which is the thing the state is supposed to improve on, is zero within noise. Over 1,073 sessions the short straddle at honest fills returns +$30 per session, 95% bootstrap CI [-$129, +$184], per-trade Sharpe +0.0110 with a bootstrap interval of [-0.0451, +0.0760], t = +0.36, 58% winners. The strangle is +$51 and the condor at honest fills is -$27, neither signed.
One cell is significantly negative unconditionally: the condor closed out at the full spread, -$64 per session, 95% CI [-$117, -$14], per-trade Sharpe -0.0719 with an interval of [-0.1293, -0.0131], t = -2.35. Read that for what it is. The condor pays 6.9% of its mid credit in spread to enter and pays it again to exit; -$64 against a $576 mid credit is the round-trip spread arriving on schedule. It is a transaction-cost result, not a state result, and the same structure at honest entry fills is -$27 and unsigned.
The entry grid, printed whole
The paper confines entries to 10:00-14:00 because relative spreads are at their daily minimum there. All nine of those half-hour entries were declared in advance and are shown below, honest fills, expanding-median state, Low minus High per trade with its 95% interval. The PORTFOLIO row is an equal-weight average of the nine, one number per session.
| entry | straddle | strangle_050 | condor_050_25 |
|---|---|---|---|
| 10:00 | -0.0317 [-0.1868, +0.1002] | -0.0438 [-0.2293, +0.0911] | -0.0192 [-0.1560, +0.1158] |
| 10:30 | +0.0399 [-0.1088, +0.1694] | +0.0537 [-0.1169, +0.1874] | +0.0412 [-0.0950, +0.1764] |
| 11:00 | +0.0387 [-0.1203, +0.1626] | +0.0552 [-0.1115, +0.1882] | +0.0359 [-0.0971, +0.1689] |
| 11:30 | +0.0143 [-0.1558, +0.1419] | +0.0520 [-0.1391, +0.1938] | -0.0052 [-0.1447, +0.1330] |
| 12:00 | -0.0047 [-0.1971, +0.1238] | +0.0179 [-0.2037, +0.1560] | -0.0291 [-0.1721, +0.1096] |
| 12:30 | -0.0313 [-0.2152, +0.0969] | -0.0300 [-0.2660, +0.0983] | -0.0679 [-0.2122, +0.0660] |
| 13:00 | -0.0738 [-0.2882, +0.0648] | -0.0653 [-0.3253, +0.0758] | -0.1178 [-0.2657, +0.0216] |
| 13:30 | -0.1875 [-0.3396, -0.0423] | -0.2343 [-0.4089, -0.0719] | -0.1656 [-0.3180, -0.0255] |
| 14:00 | -0.1467 [-0.2978, -0.0030] | -0.1943 [-0.3597, -0.0361] | -0.1499 [-0.3051, -0.0093] |
| PORTFOLIO | -0.0284 [-0.2180, +0.1082] | -0.0167 [-0.2340, +0.1240] | -0.0501 [-0.1911, +0.0836] |
Of the 30 cells, 6 have an interval excluding zero and all 6 are negative. They are the 13:30 and 14:00 entries on all three structures. Nowhere in the grid is there a cell significantly in the direction the paper reports; the only significant cells are significantly the opposite. The late entries are where the HighRV state is better, not worse — SR/trade +0.2231 against +0.0356 on the straddle at 13:30 — which is what a shrinking time-to-expiry does to a short gamma position on a day that is already moving, and it is the reverse of the sorting the claim requires. The 10:30 and 11:00 entries lean the paper's way and neither is close to significant.
The 2026 regime cut, answered honestly
The paper says the edge dissipates after 2026-05-11. Our data cannot resolve that cut, and the arithmetic says so before the numbers do. The post-cut leg is 68 sessions (2026-05-11 to 2026-08-17). The standard error on a per-trade Sharpe there is 0.121. Resolving a +0.322 Low-minus-High gap at 80% power with that split's proportions needs about 321 sessions — roughly fifteen months of trading that have not happened yet.
The inconvenient part, stated rather than skipped: the post-cut tail is the one place in this whole study where the sign matches the paper. On the straddle, LowRV n=42 at +0.1596 against HighRV n=26 at -0.1697, a difference of +0.3293 — almost exactly the claimed magnitude — with an interval of [-0.1532, +0.9251] and a bootstrap p of 0.180. The condor is +0.3891, p = 0.113; the strangle is +0.1497, p = 0.523.
Three underpowered cells pointing the right way is not evidence. An interval running from -0.15 to +0.93 is consistent with the claim, with zero, and with the opposite of the claim; that is what 68 sessions buys. The pre-cut leg of 1,005 sessions, which is powered, gives -0.0489 on the straddle. The right conclusion is that the post-2026-05-11 window is unresolved and will stay that way until roughly late 2027, not that it supports either side.
The other cut in the paper, 2022-05-11, is untestable here for the opposite reason: the archive begins 2022-04-14, so the "before" leg is 14 sessions. It is reported with its n and nothing is read into it.
Against our own 34-session gamma null
The earlier gamma-state study ran on 34 sessions from 2026-05-01 to 2026-07-31 and reported the unconditional honest-fill short straddle at -$237 (10:00), -$371 (11:30) and -$244 (13:00) per session, with a bootstrap interval on the pooled figure of [-$991, +$342] — a sample that could not sign its own level. That window now holds 63 archived sessions after a backfill. Re-run on this build:
| entry | n | mean $ / session | t | win% | SR/trade | the 34-session figure |
|---|---|---|---|---|---|---|
| 10:00 | 63 | +$92 | +0.33 | 57% | +0.0413 | -$237 |
| 11:30 | 63 | -$225 | -0.84 | 57% | -0.1065 | -$371 |
| 13:00 | 63 | -$143 | -0.61 | 59% | -0.0799 | -$244 |
There is no contradiction. Two of the three entry times agree in sign, and all three sit far inside that study's own bootstrap interval of [-$991, +$342]. Running it the other way, the older study's -$284 point estimate sits inside this build's interval for the same window and entry ([-$460, +$628] at 10:00). Two samples that both fail to sign a number are not in conflict; they are both saying the level is unsettled. (The 34-session document settles on the official SPX close and this build settles on the parity spot at 15:59, so small dollar differences are the settlement definition rather than a disagreement.)
The two studies land on the same conclusion from opposite directions. Conditioning short 0DTE premium on a state variable — the dealer-gamma book there, trailing realised volatility here — does not produce the edge. The gamma study could not settle the absolute level and settled the comparison; this one settles the comparison far more tightly and still cannot settle the level.
What it does not say
It does not test the paper's strategy. Almeida, Freire & Hizmeri's Table 7 conditions a stochastic-dominance-violation selector — a delta-hedged ATM call, written above the upper bound, bought below the lower one, cash otherwise. That selector is not rebuilt here. The falsifiable content taken from the paper is the shape of the claim: that a trailing realised-volatility state sorts after-cost 0DTE selling into a large gap. It does not, on this trade, on this sample. It may still do so on theirs.
Settlement on 580 of the 1,073 sessions is a reconstruction, not the official print. The parity spot at 15:59 was scored against 493 official SPX closes (2024-08-05 to 2026-07-31): mean error -0.042 pt, mean absolute error 0.586 pt, sd 1.041, max 14.85. That is unbiased noise worth about $59 on a straddle against a P&L standard deviation of order $2,000, not a directional error. The sensitivity re-run on exactly those 493 sessions using the official close moves the per-trade Sharpe by 0.0027, 0.0027 and 0.0023 on the three structures — third decimal place.
Fills are one lot at the touch with no depth model; the earlier study found a median of 11 contracts on the ATM bid, and size beyond roughly ten lots would have to work the spread. Entries are the nine declared half-hours, held to expiry, unmanaged — no stops, no delta hedging, no ratios, no longer expiries. SPX only. Short 0DTE premium is an unbounded-loss position and every number above is one lot with no management. Nothing on this page is investment advice; see the Terms.
And the usual limit on any null: this rules out a large state-sorting effect on this trade over this window. It does not rule out a small one, and it does not rule out one that lives in a regime the archive does not contain — which is exactly the honest reading of the post-2026-05-11 section above.
Reproduce it
This study splits cleanly into a free half and a licensed half, and it is worth saying which is which.
The state and the price half need no licensed data. Every finished session's
JSON at https://gex.live/snapshots/YYYY-MM-DD.json carries spot, the
per-minute reconstructed index, 390 values aligned with minutes (index 30 is 10:00 ET,
index 389 is 15:59). That series alone rebuilds rv5 and the expanding-median state
split exactly as they are defined above — the state variable is a function of the index and nothing
else. The dates are the ones listed at /sessions.
# FREE -- rebuild rv5 and the expanding-median state split from spot alone import json, math, statistics, urllib.request DAYS = [...] # session dates in order, from /sessions rets = {} for d in DAYS: s = json.load(urllib.request.urlopen( "https://gex.live/snapshots/" + d + ".json"))["spot"] if len(s) != 390: # half sessions are dropped, as in the study continue rets[d] = [math.log(s[i + 1] / s[i]) for i in range(389)] days = sorted(rets) rv5 = {} for i in range(5, len(days)): # 5 COMPLETED sessions before the trade date pool = [r for j in range(i - 5, i) for r in rets[days[j]]] rv5[days[i]] = statistics.stdev(pool) * math.sqrt(252 * 390) * 100 # expanding-window median, 60-session burn-in, no lookahead anywhere ks, state = sorted(rv5), {} for i, d in enumerate(ks): if i < 60: continue # burn-in: unclassified, as in the study med = statistics.median(rv5[k] for k in ks[:i]) # strictly earlier only state[d] = "HighRV" if rv5[d] > med else "LowRV" # expect ~701 LowRV / ~307 HighRV over 2022-04-14 .. 2026-08-17, mean rv5 # 8.83% vs 15.44%, and a mean realised |move| of 22.8 pt vs 36.4 pt.
The P&L half does not. The same JSON carries straddle, the ATM
straddle mid in index points on the same per-minute clock, which reproduces the mid credit
of the straddle leg — on 2026-08-11 at index 30 that is 20.5 points against a spot of 7762.92 — and
with spot at index 389 it gives the mid-fill straddle P&L directly. It does not give
the bid and the ask, so it cannot produce the honest or close-out fills, and it carries no other
strikes, so it cannot produce the strangle or the condor at all. Redoing those needs a subscription
that carries the SPXW chain: ThetaData, the SPXW trade and NBBO
tick feed, from which the 5-minute full-chain quote snapshots and every honest fill above are built.
Reproducible does not mean free.
# FREE -- the mid-fill straddle leg, from the session JSON alone d = json.load(urllib.request.urlopen("https://gex.live/snapshots/2026-08-11.json")) i = 30 # index 30 = 10:00 ET (minutes start 09:30) credit = d["straddle"][i] * 100 # ATM straddle mid, $ per lot k = round(d["spot"][i] / 5) * 5 settle = d["spot"][-1] # 15:59 parity spot, the study's settlement payout = (max(settle - k, 0) + max(k - settle, 0)) * 100 pnl_mid = credit - payout # LICENSED -- everything else. ThetaData (https://thetadata.net): # SPXW option trade + NBBO tick feed -> 5-minute full-chain quote snapshots # entry sell the bid on every short leg, pay the ask on every long leg, at the # snapshot stamped exactly on the half hour, 10:00-14:00 ET # fees $1.25 per contract per leg entered (both sides for the close-out tier) # exit cash settlement at intrinsic; the close-out tier instead buys back at # the 15:55 snapshot crossing the full spread again # unit r = pnl / (spot_at_entry * 100), so a 4,400 index and a 7,800 index are # comparable; SR/trade = mean(r)/sd(r), annualised = x sqrt(252)
The per-session index series every number above is built on, minute by minute, is in the session archive, and the companion null on the other obvious state variable is here.
Part of gex.live research. Measured on the free session archive; every session is free to replay.