A robot that sells defined-risk option spreads when volatility looks overpriced
Helm runs a small automated options seller alongside its stock trader. Twice a trading day it screens a fixed watchlist, and when it finds an option spread whose premium looks genuinely rich, it sells it — collecting the credit and betting the underlying stays inside a band. Every position is a defined-risk vertical credit spread: max loss is capped and known before entry.
It is paper only right now. The account holds about $980 of simulated cash and has never opened a position — for reasons that are the whole point of section 04. The goal I set for it: be hard to lose with, not maximize turnover.
Selling insurance that's priced richer than the risk
The entire thesis is the variance risk premium: option prices imply a certain amount of future movement (implied volatility), but underlyings tend to actually move less than that (realized volatility), because buyers pay up for protection. Persistently, implied runs above realized. Selling that gap is the edge.
This matters because it's the one thing the strategy's earlier versions got fundamentally wrong (section 04). A premium seller with no view on implied-vs-realized is just collecting pennies in front of the market's own fair pricing.
It sells put spreads on names in a strong uptrend (above the 200-day average, top of a momentum rank) and call spreads on names in a downtrend (below the 200-day). Middling names, it sits out. So it leans premium-selling with the trend. Is that the right lean, or should credit spreads be direction-agnostic / mean-reverting? Genuinely unsure.
From watchlist to a specific two-leg spread
Everything downstream is deterministic — given the same market data, the same spread comes out. No LLM picks the trade; a language model only fetches and formats the option chain, and a coded screen does the rest.
| Step | Rule |
|---|---|
| universe | ~29 curated names — SPDR sector ETFs plus liquid mid-caps chosen for fine strike spacing (so a small-width spread fits a ~$300 collateral cap) |
| direction | put-credit on uptrend names, call-credit on downtrend names, else skip |
| expiry | 30–45 days out, targeting 37 DTE |
| short leg | the out-of-the-money strike with |delta| ≈ 0.20–0.30 (targets 0.25 — roughly a 1-in-4 chance of finishing in the money) |
| long wing | scans every further-OTM strike and keeps the one with the best worst-case credit-to-width, subject to collateral ≤ $300 |
| fills | priced at worst-case (sell the short at the bid, buy the wing at the ask) — the ledger never assumes a friendly fill |
The old code grabbed the nearest wing ("minimum width"). But at worst-case fills the bid/ask cost is roughly fixed per leg while the credit scales with width — so the narrowest wing systematically minimized the credit-to-width the gate then judged. It now scans all wings for the best ratio. On modelled chains the nearest wing scored ~3× worse than the optimum.
Why it has never traded, and the fix I just shipped
This is the part I most want you to break. The gate decides whether a constructed spread is worth selling. It has been wrong twice, in the same way, and I only proved it mathematically last week.
credit / width ≥ |delta| + 0.05
The idea was "never sell a spread priced below its own probability of loss, plus a margin." But credit/width ≈ N(−d2) and |delta| ≈ N(−d1) are two estimates of the same quantity (the risk-neutral chance of finishing in the money), pulled from the same quotes. By no-arbitrage they're nearly equal — the gap between them is only ~0.027 even at high IV, so a 0.05 buffer can never be cleared. Nothing passed at any volatility, even at zero bid/ask. 14 straight empty screens; zero trades ever. The gate was comparing the market to itself.
The new gate splits into three independent layers:
Sanity — priced at least at fair value
midCredit / width ≥ |delta|At the mid (not worst-case), the spread must be priced at least at its risk-neutral loss probability. This is satisfiable (the worst-case version wasn't) and mainly rejects malformed or too-wide structures.
The actual decider — is volatility rich?
IV / realizedVol ≥ 1.20Sell only when the short leg's implied vol runs ≥20% above the underlying's realized vol (21-day, annualized). This is the variance-risk-premium signal from section 02 — the one comparison the old gate never made. Fails closed if either input is missing.
Honest accounting (unchanged)
worst-case credit → collateral, P&LWhatever passes is still booked at worst-case fills, so the simulated results never flatter the strategy with mid-price optimism.
I couldn't calibrate 1.20 from data, because the old gate never let a single observation through, so there's no history of what IV/realized looks like on these names. It's seeded from published equity variance-premium studies (typically 1.1–1.3). The screener now records the IV/realized reading for every name it looks at — pass or fail — so within a few weeks I'll have a real distribution to replace the guess with. Is 1.20 sane as a starting point? Too loose, too tight?
Caps, exits, and the safety machinery
| Control | Setting |
|---|---|
| max loss | capped per spread — it's a defined-risk vertical, collateral ≤ $300 |
| take profit | buy back at 50% of the credit captured |
| stop loss | exit if the spread doubles against you (2× credit) |
| time exit | close at 21 DTE regardless, to dodge gamma risk into expiry |
| position caps | ≤ 2 opens per run, ≤ 3/week, ≤ 5 concurrent |
| approval guard | a server-side check recomputes every proposed trade before it books; the trading process itself can't place anything the guard didn't independently approve |
| kill-switch | a reconciler compares the ledger to reality each run; any drift > $0.01 trips a hard stop |
| dead-man's switch | a separate clock alerts if the screener silently stops running |
The management rules (50% / 2× / 21 DTE) are the textbook defaults for short premium. I have not stress-tested them against this specific universe — another place your instinct is welcome.
The honest list — including whether this should exist at all
I'd rather you attack these than nod along. In rough priority:
- Does the edge survive friction? These are fine-strike ETFs, and every spread pays the bid/ask twice (open and close). It's entirely possible the variance premium on this universe gets eaten by that friction, in which case the right answer is "don't run credit spreads here" — not a lower threshold to force trades. I've built it to be able to tell me that. What would you look at to decide?
- Is 1.20 the right harvest threshold, and should it be an absolute IV/realized ratio or an IV-rank percentile once I have a year of history?
- The direction lean (puts on strong, calls on weak) — right, or should premium selling ignore momentum?
- Universe — 29 names skewed to sector ETFs. Too narrow? Wrong names? Should single stocks with fatter premiums be in scope despite the assignment/earnings risk?
- Management params — are 50% / 2× / 21-DTE defensible here, or lazy defaults?
- Realized-vol window — I use 21 trading days to compare against ~37-DTE options. Mismatched horizon?
Bottom line — would you run it (on paper)?
Anything that didn't fit a section above — a gut call on the whole approach, something I'm missing entirely, or a "this is fine, ship it and watch the data." Then copy your feedback back to me.