Overview
I started this in November 2025 as a script that ranked option contracts for a single ticker. It is now 185k lines of Python across 34 subpackages: live and archived chains from several providers, Black-Scholes pricing and Greeks, an SVI volatility smile, a 27-factor scorer whose weights shift with the VIX regime, portfolio and earnings gates, a paper-trading ledger, and four front ends (CLI, Streamlit dashboard, FastAPI service, Discord/Telegram bots) on one shared core.
The part I’m proudest of isn’t any of that. It’s that the system is built to tell me when it’s wrong. Every number a decision depends on is labelled as either verifiable (prices, IV, Greeks), calibrated (checked out of sample), or experimental, and a model that fails its own reliability test is refused at both the write side and the read side, so it can never reach a screen.
Architecture
The layers are separated by how much you should trust what they produce. The left half is descriptive: market prices, IV rank, spreads, open interest and Greeks computed from the chain. Those are facts, subject only to data staleness. The right half is predictive, and nothing predictive reaches a screen without passing through the guard.
Hard problems
Not trusting the data provider
Yahoo’s impliedVolatility field is wrong often enough on illiquid strikes to poison everything downstream: Greeks, probability of profit, expected value. Rather than trust it, the screener re-solves IV from each contract’s own mid price by inverting Black-Scholes with Brent’s method, and adopts the solved value wherever the two disagree by more than 15%. The contract is flagged, never dropped, and the card shows the correction (IV corrected (yahoo 72% → solved 38%)).
lo, hi = 0.005, 5.0
def _f(sigma: float) -> float:
return float(bs_price(option_type, S, K, T, r, sigma, q)) - mp
try:
f_lo = _f(lo)
f_hi = _f(hi)
if not (math.isfinite(f_lo) and math.isfinite(f_hi)):
return None
if f_lo == 0.0:
return lo
if f_hi == 0.0:
return hi
# No sign change -> price is outside the achievable range (below
# intrinsic or above the model max): no implied vol exists.
if f_lo * f_hi > 0:
return None
return float(brentq(_f, lo, hi, maxiter=100, xtol=1e-10))The bracket and the sign check matter more than the root-finder: a price below intrinsic, or above the model’s maximum, has no implied volatility, and returning None there is the correct answer rather than a failure.
Every quote also carries its source, timestamp, age and a fresh / delayed / stale / unknown label, so a stale quote is flagged in the output instead of being silently served.
A model that has to earn its place on the screen
The original “probability of profit” on the board turned out to identify the structure rather than the contract: measured across 909 closed trades it barely varied inside a strategy, so it could not order anything. I replaced it with a small logistic model of “does this position close green under the exit rules actually in force”, trained on realised outcomes and evaluated walk-forward in strict time order.
The rule for shipping it is blunt: bucket the out-of-sample predictions, regress realised win rate on predicted probability, and ship only if the slope’s 95% confidence interval lies entirely above zero.
def _slope_clears_zero(x: np.ndarray, y: np.ndarray,
w: np.ndarray) -> Tuple[bool, str]:
"""Weighted least squares; ships only if the slope's 95% CI is above 0."""
sw = w.sum()
xb = float((w * x).sum() / sw)
yb = float((w * y).sum() / sw)
sxx = float((w * (x - xb) ** 2).sum())
if sxx <= 0:
return (False, "slope undefined: every qualifying bucket carries the "
"same prediction")
slope = float((w * (x - xb) * (y - yb)).sum() / sxx)
intercept = yb - slope * xb
dof = len(x) - 2
if dof <= 0:
return (False, f"slope {slope:.3f} but only {len(x)} buckets — too "
f"few to put an interval on it")
resid = y - (intercept + slope * x)
ss_res = float((w * resid ** 2).sum())
se = math.sqrt((ss_res / dof) / sxx) if ss_res > 0 else 0.0
t = 1.96 if dof >= 30 else _t95(dof)
lo, hi = slope - t * se, slope + t * se
if lo > 0:
return (True, f"slope {slope:.3f}, 95% CI [{lo:.3f}, {hi:.3f}] — "
f"clears zero on {len(x)} buckets, n={int(sw)}")
return (False, f"slope {slope:.3f}, 95% CI [{lo:.3f}, {hi:.3f}] — "And the guard holds on the read side too. A refused artifact loads as None, and there is deliberately no fallback to the old number, because a silent substitution is exactly how an unvalidated score once ended up ranking the boards without anyone noticing.
def load_model(path: str = DEFAULT_MODEL_PATH) -> Optional[Model]:
"""The fitted model, or None if it is absent or was refused.
None for an unshipped artifact is deliberate. The guard has to hold at the
READ side as well as the write side, or a model that failed its own
reliability check still reaches a board. There is no fallback to
`pop_score`: a silent substitution is exactly how `quality_score` ended up
ranking this system's boards unnoticed.
"""
try:
with open(path) as fh:
payload = json.load(fh)
except Exception:
return None
if not payload.get("shipped"):
return NoneKeeping myself honest about the ranking
The tempting mistake in a project like this is to test a ranking rule on trades that the same rule selected. To avoid it, the paper book enters trades at random among contracts that survive the gates, so the pick at the top of the board and the trade the book takes are deliberately different things. Whether expected value actually orders the board is a pre-registered test with a fixed sample size and a single look, frozen until it reads out on 2026-11-19.
Results
These are the project’s own verdicts from its evidence snapshot (2026-08-24), measured on paper trades:
| Question | Verdict |
|---|---|
| Does the book as a whole have a measured edge? | No. Profit factor 1.044, 95% CI [0.87, 1.24] contains 1. |
| Which structure makes money? | Bull put spreads, and only those: +16.8% mean return on capital at risk, CI [+6.9%, +27.0%]. |
| Is the calibrated probability of closing green trustworthy? | Yes. Reliability slope 0.572, CI [0.329, 0.815] over 612 held-out trades. |
| Does anything predict expected profit per contract? | No. Both candidate models were refused by the guard and are not displayed. |
| Does expected value order the board? | Unknown until 2026-11-19, by design. |
The bull put result didn’t survive a harder question. A structure that sells puts earns money in a rising market just by being long it, so in October I pre-registered a test of whether the bull put book beats simply holding its own delta, and replayed the live rule on fourteen years of real SPY option quotes (optionsDX, 2010–2023, 927 trades). It earned 2.10% of capital at risk per trade less than its own delta, CI [−2.72%, −1.59%], entirely below zero: costly beta, not an edge. The paper book points the same way (−4.02% per trade against its delta, CI [−9.61%, +1.64%]). The project’s written decision rule now vetoes that rule for real money, and the screener stopped paper-logging bull puts the same day.
Separately, and outside the tool, I’ve traded its signals with my own money: +$9.2k realized from discretionary trades I picked from its ranked output. The tool does not trade on its own, and I don’t present that figure as evidence the model has an edge. The table above is that evidence, and it says the edge is narrow.
What I’d do next
- Read out the pre-registration on 2026-11-19 and act on the result, including if it says EV doesn’t order the board.
- Fix the within-strategy inversions. Inside bear calls and short puts the calibrated ordering still points the wrong way on about 50 trades a side. That’s reported today, not gated on, because choosing what to display from the same data that measured it would fit the display to noise.
- Replace model-priced backtest premiums with the real bid/ask history the daily chain archive has been compounding since June.
- Find a rule that beats its own delta, or stop looking. The decision rule’s only path to real money is a new rule, pre-registered, that clears its delta on the fourteen-year index replay and then on its own forward paper record, followed by a pilot capped at $1,500 of risk. The replay already says where to start: at mid-price fills the premium turns positive (+0.31%), so the cost of crossing the spread twice is the deficit to fix first.