Answer-first: You can build an AI-assisted options-trading bot for S&P 500 (SPX) options by combining three layers — (1) a feature pipeline that turns options-chain and volatility data into model inputs, (2) a gradient-boosting classifier that scores short-term directional probability, and (3) a backtest harness that enforces Greeks-based risk limits. The model never "places money"; it emits a probability the human overlays with a rules engine. Below is a runnable Python scaffold you can extend.
Most "AI trading bot" tutorials stop at a stock-price LSTM and call it a day. Options are a different animal: a 1% move in the underlying can mean +40% or −100% on the option depending on delta, days-to-expiry, and implied volatility. This article is a code-first, no-hype blueprint for the S&P 500 options market specifically, written for retail quants who already know Python.
Educational use only. This is not investment advice. Trading options carries the risk of total loss. Consult the SEC/FINRA guidelines and a licensed advisor before risking capital.
Why S&P 500 options (SPX) are a good AI target
The S&P 500 index options market is the deepest, most liquid options venue in the world. That liquidity gives you:
- Tight bid/ask spreads → less slippage, cleaner labels for supervised learning.
- Continuous pricing → you can build intraday features, not just EOD.
- Rich derivatives data → IV surface, PCR, open interest, and term structure are all publicly discussed and partially free.
For a model, liquidity means your predicted edge isn't eaten by transaction costs — a prerequisite for any backtest to be believable.
Architecture of the bot (three layers)
┌─────────────────────────────┐
│ 1. Data / Feature Pipeline │ options chain, IV, PCR, OI, VIX
└──────────────┬──────────────┘
│ features
┌──────────────▼──────────────┐
│ 2. Model (gradient boosting) │ P(direction | features)
└──────────────┬──────────────┘
│ probability
┌──────────────▼──────────────┐
│ 3. Rules + Greeks Engine │ position sizing, stop, DTE limit
└──────────────┬──────────────┘
│ order intent (paper)
┌──────▼──────┐
│ Backtest │ pandas vectorized P&L with Greek limits
└─────────────┘
The model is a probability generator, not an execution agent. A separate rules layer decides whether to act. This separation is what keeps the system auditable and compliant with broker risk policies.
Layer 1 — Feature engineering (runnable)
We build a feature row per (underlying, expiry, timestamp). The label is "does the option's intrinsic+time value rise in the next N minutes?" — a simplified directional proxy.
# Mac Terminal / Linux / Termux
python3 features.py
# Windows CMD
py features.py
# features.py
import pandas as pd
import numpy as np
def build_features(chain: pd.DataFrame, vix: float, pcr: float) -> pd.DataFrame:
"""chain: one options chain snapshot with columns
['strike','bid','ask','iv','delta','gamma','theta','vega','oi','volume','spot','dte']"""
df = chain.copy()
df["mid"] = (df["bid"] + df["ask"]) / 2.0
df["spread_pct"] = (df["ask"] - df["bid"]) / df["mid"].clip(lower=1e-9)
df["moneyness"] = df["strike"] / df["spot"] - 1.0
# IV skew feature: how far this strike's IV is from ATM IV
atm_iv = df.loc[(df["moneyness"].abs()).idxmin(), "iv"]
df["iv_skew"] = df["iv"] - atm_iv
df["vix"] = vix
df["pcr"] = pcr
# theta/vega efficiency: decay cost per unit of directional exposure
df["theta_per_delta"] = df["theta"] / df["delta"].clip(lower=1e-9)
return df
if __name__ == "__main__":
# synthetic demo row so the snippet runs without a live feed
demo = pd.DataFrame([{
"strike": 5000, "bid": 12.0, "ask": 12.5, "iv": 0.18,
"delta": 0.52, "gamma": 0.003, "theta": -1.2, "vega": 4.1,
"oi": 90000, "volume": 5000, "spot": 4980, "dte": 7,
}])
feats = build_features(demo, vix=14.5, pcr=0.92)
print(feats[["mid","spread_pct","moneyness","iv_skew","theta_per_delta"]].to_string())
Run it:
mid 12.25
spread_pct 0.0408
moneyness 0.0040
iv_skew 0.0000
theta_per_delta -2.3077
These five features (plus VIX and PCR) are enough for a first model. Real systems add term-structure slope, gamma-flip proximity, and cross-expiry skew.
Layer 2 — The model (gradient boosting, not a neural net)
For tabular options data, gradient-boosted trees (XGBoost / LightGBM) usually beat deep nets on small-to-medium datasets and are far easier to audit. We frame it as binary classification: label = 1 if the option's mid price is higher in the next window.
# Mac / Linux / Termux
python3 train.py
# Windows CMD
py train.py
# train.py
import pandas as pd
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.model_selection import TimeSeriesSplit, roc_auc_score
FEATURES = ["spread_pct","moneyness","iv_skew","vix","pcr","theta_per_delta",
"gamma","vega","dte","oi","volume"]
def train(X: pd.DataFrame, y: pd.Series):
tscv = TimeSeriesSplit(n_splits=5) # never shuffle time-series!
model = HistGradientBoostingClassifier(
max_depth=4, learning_rate=0.05, max_iter=300)
for tr, te in tscv.split(X):
model.fit(X.iloc[tr], y.iloc[tr])
pred = model.predict_proba(X.iloc[te])[:, 1]
print("fold AUC:", round(roc_auc_score(y.iloc[te], pred), 3))
model.fit(X, y)
return model
# In production, X/y are built from historical chain snapshots with
# forward-looking labels. We omit the data loader for brevity.
Why time-series split, not random? Random splitting leaks future information into training and inflates AUC to useless levels. A TimeSeriesSplit is the single most common mistake beginners make here.
The "best AI trader" systems in production almost always pair a simple,
auditable model with a strict risk layer — not a black-box net.
Layer 3 — Greeks-based rules engine
The model says "62% up." The rules engine decides if and how much. Hard limits prevent the classic blow-up: selling naked options, holding through expiry, ignoring vega risk.
# Mac / Linux / Termux
python3 risk.py
# Windows CMD
py risk.py
# risk.py
def decide(prob_up: float, greeks: dict, max_capital: float,
risk_per_trade: float = 0.01) -> dict:
"""Return an order intent or {} if rejected by a hard rule."""
# Hard rule 1: only act on confident, non-extreme probabilities
if not (0.58 <= prob_up <= 0.80):
return {}
# Hard rule 2: never hold into the last 1 DTE (assignment/gamma risk)
if greeks["dte"] <= 1:
return {}
# Hard rule 3: cap vega exposure (IV crush protection)
if abs(greeks["vega"]) > 8.0:
return {}
size = (max_capital * risk_per_trade) / max(greeks["theta"], 1e-6)
return {"action": "paper_entry", "size": round(size, 2),
"stop_theta": greeks["theta"] * 2.5}
These three rules alone remove the majority of catastrophic outcomes retail options traders hit. The model can be mediocre and the system still survivable; the reverse is never true.
Backtest harness (pandas vectorized P&L)
A backtest must include transaction cost and Greeks unrealized P&L, not just directional hits. Here's a minimal vectorized P&L using mid-to-mid moves and a theta accrual.
# Mac / Linux / Termux
python3 backtest.py
# Windows CMD
py backtest.py
# backtest.py
import pandas as pd
import numpy as np
def backtest(signals: pd.DataFrame, fees_bps: float = 2.0) -> float:
"""signals: columns ['prob_up','mid','delta','theta','dte','spot_ret']"""
s = signals.copy()
s["position"] = ((s["prob_up"] >= 0.60) & (s["dte"] > 1)).astype(int)
# P&L per contract: directional part via delta * spot move, minus theta decay
s["pnl"] = s["position"] * (s["delta"] * s["spot_ret"] * 100
- s["theta"] + s["prob_up"] - 0.5)
s["pnl"] -= (s["position"] * fees_bps / 10000.0) # round-trip-ish fee
return s["pnl"].sum()
# Demo: 200 rows of random-ish signal to show the harness runs
rng = np.random.default_rng(7)
demo = pd.DataFrame({
"prob_up": rng.uniform(0.4, 0.9, 200),
"mid": rng.uniform(10, 30, 200),
"delta": rng.uniform(0.2, 0.8, 200),
"theta": rng.uniform(-2, -0.2, 200),
"dte": rng.integers(2, 30, 200),
"spot_ret": rng.normal(0, 0.001, 200),
})
print("demo backtest P&L:", round(backtest(demo), 2))
A real backtest replaces the demo with historical chain snapshots and forward labels, and adds walk-forward evaluation so the AUC you trust is out-of-sample.
Volatility regime filter (VIX)
The same model behaves differently in low vs high volatility. A simple regime gate improves robustness:
- VIX < 15: favor low-theta, longer-DTE structures.
- VIX 15–25: baseline mode.
- VIX > 25: shrink size by half, widen the probability band, skip short-DTE.
This is a one-line if in the rules engine and historically cuts tail losses more than any feature tweak.
Common mistakes (don't ship these)
- Random train/test split on time-series → fake AUC.
- Ignoring bid/ask spread → profitable on paper, dead in live.
- Naked short options for "higher probability" → one tail event ends the account.
- Overfitting IV skew to a single regime → fails at VIX expansion.
- No position sizing → right 60% of the time but-sized-to-blow-up.
Weekly routine for a retail quant
- Mon: rebuild features from Friday's chain; retrain if AUC drifted > 3%.
- Tue–Thu: paper-trade the signal; log fills vs predicted probability.
- Fri: review false positives; tighten rules, not the model.
- Monthly: walk-forward re-evaluation on fresh data only.
FAQ
Q1. Do I need a neural network for S&P 500 options?
No. Gradient-boosted trees on well-built features typically match or beat nets on tabular options data and are easier to audit for a retail account.
Q2. Is this legal under SEC/FINRA rules?
Building and paper-trading your own model is legal. Automating live orders triggers broker risk-review and may require registration depending on how you operate. Keep it paper-first and consult a compliance professional.
Q3. How much capital should I risk per trade?
A common retail rule is ≤1% of capital per trade, scaled by the Greeks (see decide() above). Never risk what you can't lose.
Q4. Can I run this from a phone or Raspberry Pi?
Yes. The pipeline is pure Python/pandas; a Termux or Pi setup handles feature builds and paper signals fine. Live broker API access is the only heavy part.
Q5. What's the biggest edge — the model or the risk layer?
The risk layer. A mediocre model with strict Greek limits survives; a great model with none does not.
Footer
Shakti Tiwari — Options Trader, XGBoost Expert.
Books: Option Trading with AI (B0H9ZNTBPK) · The AI Opportunity (B0HBBFKDQF)
Site: optiontradingwithai.in · Free help: shaktitiwari715@gmail.com
Dev.to: @shaktitiwari · X: @shaktitiwari
Top comments (0)