DEV Community

GEX.live
GEX.live

Posted on Originally published at gex.live

We replicated a published 0DTE options paper to four decimals. One unit-scale bug reverses its headline result — confirmed by the author

This is the short version of the full write-up, which carries every table: Replicating Vilkov (2026) on 0DTE trading rules.

Grigory Vilkov's "0DTE Trading Rules: Tail Risk, Implementation, and Tactical Timing" (SSRN 4641356) is a rare thing in the trading literature: the author shipped the code and the data panels behind his tables, MIT-licensed. So instead of comparing numbers to a memory, we could re-implement his construction independently and feed his own panel through it.

Step 1: the replication is exact. All seven option structures, both cost tiers, reproduce his published P&L table to four decimal places. No ambiguity about what the code does.

Step 2: the reproduction exposed a units bug. The panel stores the P&L column in percent of spot (reth_und = (payoff - mid) * 100) but the bid-ask spread column as a fraction of spot. The cost code subtracts one from the other directly:

pnl_ba = reth_und - half_spread_cost   # percent minus fraction: cost charged at 1/100
pnl_ba_fee05 = pnl_ba - 0.005          # the flat 0.5bp fee term was correctly scaled
Enter fullscreen mode Exit fullscreen mode

The half-spread was charged at one hundredth of its true size — about 0.022bp instead of 2.2bp. Rescale it and change nothing else: every unconditional structure in the paper goes negative. The best one (put ratio spread) has a mean gross edge of +0.0251% of spot per day, and the spread you cross to get it costs 0.0343%.

Step 3: the ML layer makes it worse, not better. The paper's conditional layer trains a classifier whose target is y = 1[PNL_net > 0] — the cost sits inside the training label, not just in the evaluation. Re-pricing the author's shipped predictions at the corrected cost takes the headline conditional Sharpe from +1.55 to +0.33. Rebuilding the labels with the corrected cost and retraining takes it to −0.70. Two-thirds of the published result was cost arithmetic; most of the rest was a model that had learned from mislabelled days.

Step 4: we emailed the author before publishing. His reply, within two days, quoted with permission:

Thank you for this. You have read the units correctly, the finding stands, and it is more consequential than the charitable version you offered me. Please publish it.

He reran the corrected code on the private, longer sample behind the paper — the bug is there too — and pushed the fix upstream (commit 85a447c) with a KNOWN-ISSUES.md and a unit-scale assert module, so this class of error now fails loudly. The paper's headline conditional result reverses sign: put ratio spread +0.93 published → −0.75 corrected, and no strategy or basket survives net of costs. His framing of what survives is worth quoting too: the gross directional signal is real; what died is the claim that crossing the quoted spread leaves any of it for the trader.

Takeaways for anyone who ships research code:

  1. Mixed scales in one dataframe are a landmine. reth_und in percent next to bas in fractions, in the same table, with no suffix on either name.
  2. Assert units at boundaries. The fix that made this permanent is not the * 100 — it's the guard that checks a cost series is on a plausible scale before it touches P&L.
  3. A metric bug can contaminate your labels. If your training target is derived from the quantity your bug distorts, retraining after the fix is part of the correction. Re-evaluating alone understates the damage.
  4. Ship your data. This error was findable, and fixable, only because the author published everything. That is the norm this literature needs — his response shows how it should be done.

The full post — with the corrected tables, the delta decomposition showing the result is not hidden index beta, the author's complete response, and a 46-session out-of-sample run on our own SPXW tick tape — is at gex.live/research/vilkov-0dte-rules-replication.

Top comments (0)