Hi thereRunbook · SPX Strategy
SPX Strategy Operations
How the SPX credit-spread system works today: the strategy stack, the server-side premium stop safety net, the v3 observe-mode workflow, and the roadmap. ← Back to Runbook home
What is running, and in what mode▸
- v2.2 Directional (spx_0dte_1min rail) — LIVE paper. 0DTE credit spreads with OTM cushion strikes (VIX-scaled), daily-SMA20 trend gate, VIX 16–18 chop skip, close-confirmed stop.
- Dip Harvester v1.1 (spx_0dte_pt50 rail) — LIVE paper. 1-minute mean reversion in the direction of the daily trend: extreme washout (RSI5 < 10 + Bollinger break) → sell a put spread below the washout low. Mirrored for pops in a downtrend.
- v3 Premium Harvester — OBSERVE MODE (webhooks off). The lead candidate: one script that picks the side from the daily trend, sells flushes (VWAP z-score), adds a quiet-day carry at 11:00a CT, sizes strikes at 1.1× the remaining-day expected move, and picks width from VIX. Run it on the 5-minute SP:SPX chart — that timeframe won Victor's robustness test (~6 months of history, all three engines positive).
- Retired: the 1DTE end-of-day strategy (still negative even with-trend) and the old 10-ITM entry logic (the single biggest loss driver in the 176-trade study).
The breakeven math that drives every decision▸
Credit-spread selling wins small and loses big. Our measured averages: wins of roughly $150–$190, losses of roughly $750–$1,600. At those numbers the system needs an 84–87% win rate just to break even.
That leads to the two operating rules:
- Tail size is THE lever. Shrinking the worst loss from $1,600 to $800 lowers the required win rate more than any entry tweak raises the actual one. This is why the server premium stop (below) exists.
- Only trade with the daily trend. The 176-trade study split +$5.7k (with-trend) vs −$14.6k (counter-trend). Every current script enforces this with a daily SMA20 gate.
What it is and how it works▸
A smoke alarm for every open SPX spread, running on the server (not on the chart):
- Every 10 minutes during market hours the server asks the broker what it would really cost to buy back each open spread.
- It compares that cost to the credit collected at entry.
- If the buyback cost reaches 2.5× the entry credit, the server closes the trade immediately at market — it cancels the resting profit-target order first, then buys the spread back, logs the loss, and journals the exit as SERVER_STOP_LOSS.
Worked example: collect $5.00 at entry → the stop fires if buyback reaches $12.50 → loss ≈ (12.50 − 5.00) × 100 = −$750, instead of drifting toward a −$1,000–$1,600 max-width loss.
Because it watches real quotes, the protection is identical no matter what chart timeframe the Pine script runs on. Chart stops only confirm on bar close — slow on a 5-minute chart; the server doesn't wait.
It protects all five SPX rails: v2.2, Dip Harvester, both 15-min variants, and the (retired) 1DTE — anything with an open position.
Tune, loosen, or disable the premium stop▸
The config lives in /home/victor/spx-server/allocations.json, block spread_premium_stop:
- stop_multiple — currently 2.5. Raise to 3.0 for fewer (but larger) stop-outs; the agreed sane range is 2.5–3.0.
- stop_multiple_overrides — per-strategy exceptions, e.g. {"spx_0dte_pt50": 3.0}.
- enabled — set false to turn the whole thing off (not recommended).
After editing, restart the SPX brain (after hours). SSH in first — prompt must say victor@spx-server:
kill -9 $(pgrep -f "spx-server/server.py" | head -1)
Wait ~15 seconds, then confirm it's healthy:
curl -s http://127.0.0.1:5005/health | head -c 200; echo; grep "Spread premium stop" /var/log/spx-server.log | tail -1
The log line shows the settings the server actually loaded — check the multiple matches what you set.
Verify a premium-stop fire (do this after the first one)▸
When the stop fires you'll want proof everything downstream recorded it correctly:
- Server log:
grep "PREMIUM_STOP_FIRED" /var/log/spx-server.log | tail -5 - Journal: the trade's exit row should read SERVER_STOP_LOSS with reason server_premium_stop:
grep "server_premium_stop" /home/victor/spx-server/journal/trades_journal_2026.csv | tail -3 - Panel: after 5:30p CT (the daily ledger rebuild), the trade should appear on the SPX pages as a closed loss of roughly (multiple − 1) × credit × 100. If it still shows open the next day, something is wrong — investigate.
Chart setup and what the table tells you▸
- Chart: SP:SPX, 5-minute. Indicator: "SPX Premium Harvester — v3 (observe mode)".
- The status table sits top-right. Top rows = live context (side, z-score, VIX → width, expected move, entries). Bottom rows = the instant rough backtest: Est P&L, W/L, average win/loss, worst loss, and per-engine attribution (Flush / Fade / Carry).
- Treat Est P&L as optimistic. It books profit-targets at 50% of credit instantly; real server fills lag. A table showing 96–98% wins realistically means ~88–92% live — still above breakeven, but don't celebrate the table number.
JSON label verification (before arming webhooks)▸
v3 has a debug switch: indicator Settings → Inputs → Webhook group → "Show JSON labels on chart".
When ON, every signal bar prints the exact JSON payload the webhook would send — the same string, byte for byte. Use it to eyeball, before going live:
- Option symbols — legs read like SPXW 260730P00007350: right root, today's date, P for put spreads / C for call spreads, strike padded to 8 digits.
- Strikes make sense — short strike beyond the expected move, long strike exactly one width farther out.
- Signal types — entries say ENTRY_BULL/ENTRY_BEAR; profit-take exits say EXIT_PT; stop and end-of-day exits say EXIT_EOD (the stop variant carries "reason":"stop_loss_proxy").
- position_id is consistent between a trade's entry and its exits.
The labels work while webhooks are OFF — that's the point. Verify first, arm later. Turn the switch off afterward to keep the chart clean.
Phases: where we are▸
- Phase 1 — DONE (2026-07-30): server premium stop live on all SPX rails. Makes the worst-case loss timeframe-independent.
- Phase 2 — IN PROGRESS: v3 observing on the 5-minute chart. Goal: 2–3 clean days where the signals it prints match what we'd want a server to do.
- Phase 3 — NEXT: assign v3 a server rail (its own strategy id in server.py or a retired slot), point a TradingView alert with webhooks ON at it, paper trade it, and compare against v2.2 + Dip Harvester before any capital decisions.