← Exit study (recorded entries) · 6-month long-history test (Tier B) →

0DTE Bot — Full entry-simulation backtest

The ENTIRE strategy simulated from raw data — GEX score, permission gate, contract selection, entries, exits — nothing taken from the bot's ledger · worst-case fills headline · generated 2026-07-30
Verdict. Simulated end-to-end over the 10 faithful GEX days (Jul 16-29, 2026; 12 OPRA days minus 2 with no SPX/NDX GEX recordings), the full strategy under worst-case fills (buy at the real ask, sell at the real bid, $1.32 fees/round-trip) lost -10,075$ across 1114 simulated entries. The split is the whole story: SPX made +42,589$ (732 trades, +58$/trade) while NDX lost -52,664$ (382 trades, -138$/trade) — NDX median entry spread is $3.60 vs $0.10 on SPX; the strategy's edge does not survive the NDX spread. In the bot's own optimistic mid-fill accounting the same simulation shows +137,232$ — the gap to worst-case is pure spread cost. Peak simultaneous premium at risk: ~64,080$.
Validation A — is my score engine the bot's score engine?
At every recorded live decision timestamp I recomputed score and permission from point-in-time snapshots only, then compared with what the bot recorded (trade_permissions, 2988 rank-1 decisions). On the three days where the DB stores the bot's exact scan inputs AND the current rubric was live (Jul 27-29, gex-rubric-2026-w31-v1, n=449): score match 95.3%, permission match 98.4%, technicals-adjustment match 100.0% (gate: ≥90% — PASSED). Contract Pick #1 match at the same timestamps: 42% overall — SPX 65%, NDX 20%; mismatches are the same side and typically 1-2 strikes away (SPX median miss 5 pts, score delta ~0.5) — the cost of 1-minute NBBO vs the bot's 1-second stream.
Jul 16-23 recorded decisions used an OLDER rubric (legacy-unversioned) and their snapshots were later overwritten by GEXBot day-blob rebuilds (different sub-minute sampling), so recorded-score agreement there (12%) measures rubric drift + vendor resampling, not engine error; those days are still simulated — with today's rubric, as a backtest should.
Validation B — do my simulated entries look like the bot's real entries?
On Jul 27-29 the bot really traded: 245 live SPX/NDX entries. My simulation, run blind on the same days, produced 313 entries and matched 116 of them on exact (contract, 30-min block). It covered 86% of the bot's SPX entries but only 24% of NDX ones — same-side, near-strike differences driven by 1-minute quotes and (on Jul 27) a denser decision grid than the bot's actual ~2-minute live scan rate. The simulation trades MORE than the live bot (313 vs 245): it never sleeps, while the live tab scanned irregularly.
entries sim vs live

1 · Equity curve — the full simulated strategy

equity curve per-day P/L

2 · Exit policies on the same simulated entries (worst-case fills)

Exit policyNet P/L (ALL)SPXNDXWin rate Profit factorMax DD $Avg hold
time_10m+3,020+37,624-34,60446%1.0177,26810m
fixed_t30_s30-2,020-28,256+26,23646%1.00108,07722m
baseline_full-10,075+42,589-52,66451%0.9646,0744m
fixed_t30_s40-13,030-45,246+32,21650%0.98123,08728m
fixed_t20_s40-15,170-29,596+14,42658%0.97124,31622m
time_15m-18,065+41,704-59,76945%0.97106,99715m
pl_150_80_120-43,640-46,621+2,98167%0.91144,69341m
time_30m-98,495+2,349-100,84440%0.87188,29430m
hold_to_end-1,474,965-411,716-1,063,24924%0.271,523,030191m

baseline_full = the bot's actual rule set: $150/80%/$120 trailing profit lock on every minute mark + exit after 2 consecutive same-key adverse GEX/permission/zero-gamma/VWAP decision readings + labeled 15:55 ET force close (the live bot has NO forced close — audit finding). pl_150_80_120 = profit lock alone (no GEX exits): it holds losers to the close and does far worse. Exit reasons in baseline_full: gex_exit:permission 716, profit_lock 321, gex_exit:bias 38, gex_exit:zero-gamma 19, gex_exit:vwap 19, eod_force_1555 1.

policy comparison

3 · Day by day (baseline_full, worst-case)

DaySPX tradesSPX P/LNDX tradesNDX P/L TotalDecision data
2026-07-1644+4,1820+4,182blob rebuild (15 s grid)
2026-07-1792+7,00938-5,120+1,888blob rebuild (15 s grid)
2026-07-2096+2,98332-6,282-3,299blob rebuild (15 s grid)
2026-07-2185+3,80343-16,327-12,524blob rebuild (15 s grid)
2026-07-2278-15342-20,885-21,038blob rebuild (15 s grid)
2026-07-2384+4,33942+2,065+6,404blob rebuild (15 s grid)
2026-07-2480+9,50445-9,469+35blob rebuild (15 s grid)
2026-07-2789+8,82838-6,480+2,347live+blob (15 s grid)
2026-07-2839-50649+9,185+8,679live scan cadence (~1-2 min)
2026-07-2945+2,60153+650+3,251live scan cadence (~1-2 min)

4 · What was simulated, exactly

Caveats — read before believing.
Code: backtests/src/entry_sim.py · results: backtests/results/entrysim_*.csv|parquet · strategy audit: backtests/STRATEGY_AUDIT.md · exit study on recorded entries: index.html · 6-month long-history test: longhist.html