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.
1 · Equity curve — the full simulated strategy
2 · Exit policies on the same simulated entries (worst-case fills)
3 · Day by day (baseline_full, worst-case)
4 · What was simulated, exactly
- Decisions: 23,681 decision timestamps across 10 days x 2 tickers —
the recorded GEX snapshot cadence, downsampled to the 15 s Auto-Live minimum.
All 19 score components recomputed per timestamp (GEX majors/zero-gamma/max-change from
production snapshots; VWAP/SMA50/SMA200/Fib from the same Alpaca IEX SPY/QQQ bars the live
bot uses — including its daily-bar lookahead of the running session close, reproduced
deliberately and verified against recorded SMA values; vanna/charm flow from stored
profiles; VIX from stored VIX snapshots where present, else the recorded-score residual
inference described in the caveats).
- Permission gate: blockers exactly as coded, incl. "too close":
|spot − level| ≤ max($3.00, 0.05% of spot) against classic major +/− GEX (volume).
- Contract selection: same-day-expiry (0DTE ENFORCED — the live code
does not enforce it; labeled deviation), strike band [96%, 104%] of spot-to-target, sorted by
distance-to-GEX-target, first 20 priced from real OPRA cbbo-1m NBBO (staleness ≤120 s,
crossed/zero/sentinel quotes rejected), Black-Scholes greeks from the mid at r=4.5% exactly
like the bot's databento path, and the bot's 12-step score. OI is absent on the databento
path (bonus never applies) — live parity.
- Entries: permission = "possible trade after confirmation", Pick #1 only,
score ≥100, bullish→calls / bearish→puts, one entry per contract per 30-min block,
≤10 entries per block (shared across tickers; the cap never bound), entry at the real ask
(worst) / mid (optimistic).
- Exits: replayed on real per-minute NBBO; every worst-case sell uses the
actual bid of that minute, never a bar high.
Caveats — read before believing.
- 1-minute resolution: the bot lives on 1 s quotes; this simulation prices
decisions and exits on 1-minute NBBO. Sub-minute spikes are invisible: profit locks/stops
trigger on minute marks. Direction of bias is mixed — peaks are understated (fewer lock
arms) but so are intraminute stop-outs.
- 10 days, one regime: Jul 16-29, 2026 was a drifting-then-selling
market with VIX 15-21. Nothing here generalizes beyond that regime; a 60/20/20 walk-forward
is impossible on 10 days.
- Entries are simulated, not recorded: on Jul 16-24 the bot's own live
scans are unrecoverable (snapshots overwritten by vendor day-blobs; recorded decisions used
an older rubric), so the entry stream is my reconstruction of what THIS rubric would have
done on that data at 15 s cadence — 801 of the 1114
trades. The three live days validate the machinery (Validation A/B above).
- VIX component: no licensed VIX feed. Jul 14-16 have recorded GEXBot VIX
(15.7-16.8). Jul 27-29 use a per-timestamp adjustment inferred from the bot's own recorded
scores (disclosed, point-in-time). Jul 17-24 assume adjustment 0 (VIX 15-20) — if VIX
actually crossed 20 on those days, scores there are up to 0.5 too high, which can only
REDUCE the number of qualifying entries.
- Labeled deviations from live code: 0DTE-only universe enforced;
15:55 ET force close added; GEX-confirmation exits evaluated at decision cadence
(≥15 s) not the browser's irregular refresh; decision cadence on blob days (15 s) is
denser than the bot's actual erratic scan rate — more entries than the live bot would
have taken, each still individually rule-faithful.
- Sequencing: block caps/dedup applied per ticker sequentially (SPX
before NDX within a day); the 10-per-block cap never triggered, so ordering had no effect.
- Fees $0.66/contract/side; no assignment/exercise modeling (positions closed by 15:55 ET).
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