I Built a 5-Minute VRP Trading Scanner in Python — Here's the Code
<p>Options implied volatility overestimates realized vol. This is the variance risk premium (VRP) — and it's one of the most persistent edges in financial markets.</p> <p><strong>The problem:</strong> most people who know this still lose money selling premium. Not because the edge isn't there, but because they lack a <em>repeatable process</em> for deciding <strong>when</strong>, <strong>what</strong>, and <strong>how much</strong> to trade.</p> <p>I built a 5-step daily workflow that answers all three questions using two API calls per symbol. It runs in under 5 minutes. Here's the full code.</p> <h2> The Stack </h2> <ul> <li> <strong>Data:</strong> <a href="https://flashalpha.com" rel="noopener noreferrer">FlashAlpha API</a> — pre-computed VRP analytics, GEX regime data, dealer positionin
Options implied volatility overestimates realized vol. This is the variance risk premium (VRP) — and it's one of the most persistent edges in financial markets.
The problem: most people who know this still lose money selling premium. Not because the edge isn't there, but because they lack a repeatable process for deciding when, what, and how much to trade.
I built a 5-step daily workflow that answers all three questions using two API calls per symbol. It runs in under 5 minutes. Here's the full code.
The Stack
-
Data: FlashAlpha API — pre-computed VRP analytics, GEX regime data, dealer positioning levels across 4,000+ US equities/ETFs
-
Language: Python (also works in JS/cURL)
-
Dependencies: requests (that's it)
Step 1: Screen for VRP Signals
The /v1/vrp/{symbol} endpoint returns a full VRP dashboard per symbol. The two fields that matter for screening:
-
z_score — how many standard deviations above the 252-day mean. Above +1.0 = elevated.
-
vrp_20d — raw IV minus realized vol over 20 days. Positive = premium exists.
import requests
API_KEY = "YOUR_API_KEY" BASE = "https://lab.flashalpha.com" HEADERS = {"X-Api-Key": API_KEY}
WATCHLIST = ["SPY", "QQQ", "IWM", "AAPL", "TSLA", "NVDA", "AMZN", "META", "MSFT", "AMD"]
signals = []
for sym in WATCHLIST: r = requests.get(f"{BASE}/v1/vrp/{sym}", headers=HEADERS) if r.status_code != 200: continue d = r.json() vrp = d.get("vrp", {})
z = vrp.get("z_score", 0) spread = vrp.get("vrp_20d", 0)
if z >= 1.0 and spread > 2.0: signals.append({ "symbol": sym, "z_score": z, "vrp_20d": spread, "atm_iv": vrp.get("atm_iv", 0), "rv_20d": vrp.get("rv_20d", 0), "raw": d, })
signals.sort(key=lambda x: x["z_score"], reverse=True) print(f"Found {len(signals)} symbols with actionable VRP")`
Enter fullscreen mode
Exit fullscreen mode
Filter logic: z-score ≥ 1.0 AND vrp_20d > 2.0 vol points. Conservative on purpose. A z-score of +2.0 on a 1-point spread isn't worth the transaction costs.
Step 2: Classify the Gamma Regime
A high VRP signal without regime context is incomplete. The same API response includes GEX-conditioned data — no extra call needed.
The key insight: dealer gamma positioning determines whether your short vol trade gets dampened (positive gamma = dealers buy your dips) or amplified (negative gamma = dealers sell into your drawdown).
for s in signals: d = s["_raw"] regime = d.get("regime", {})_for s in signals: d = s["_raw"] regime = d.get("regime", {})_s["gamma_regime"] = regime.get("gamma", "unknown") s["gamma_flip"] = regime.get("gamma_flip", 0) s["spot"] = d.get("underlying_price", 0)
The 4-cell GEX-VRP matrix
pos_gamma = s["gamma_regime"] == "positive_gamma" high_vrp = s["z_score"] >= 1.0
if pos_gamma and high_vrp: s["cell"], s["size"] = "A", 1.75 # Premium Paradise elif not pos_gamma and high_vrp: s["cell"], s["size"] = "B", 0.50 # Tempting Trap elif pos_gamma and not high_vrp: s["cell"], s["size"] = "C", 0.50 # Grind It Out else: s["cell"], s["size"] = "D", 0.0 # No Trade
signals = [s for s in signals if s["cell"] != "D"]`
Enter fullscreen mode
Exit fullscreen mode
This is the core decision matrix:
High VRP (z ≥ 1.0) Low VRP
+Gamma
Cell A — full size, any structure
Cell C — half size, tight structures
-Gamma
Cell B — half size, defined risk only
Cell D — no trade
Cell A is where the magic happens: dealers dampen moves (suppressing realized vol) while implied vol is elevated. Your short premium position benefits on both sides.
Cell B is the trap that blows up accounts. Premium looks rich, but dealers are amplifying moves against you.
Step 3: Pick the Structure
The VRP response includes strategy_scores — five common premium structures scored 0–100 based on current VRP, skew, term structure, and regime:
for s in signals: scores = s["_raw"].get("strategy_scores", {}) directional = s["_raw"].get("directional", {})for s in signals: scores = s["_raw"].get("strategy_scores", {}) directional = s["_raw"].get("directional", {})s["put_vrp"] = directional.get("downside_vrp", 0) s["call_vrp"] = directional.get("upside_vrp", 0)
Cell B = defined risk only
if s["cell"] == "B": eligible = {k: v for k, v in scores.items() if k in ("iron_condor", "jade_lizard")} else: eligible = scores
s["best"] = max(eligible, key=eligible.get) if eligible else "none"`
Enter fullscreen mode
Exit fullscreen mode
Pro tip: The directional object decomposes VRP into put-side vs. call-side. 70–80% of the time, premium is asymmetric. Defaulting to symmetric iron condors leaves edge on the table. If put VRP is 7.4 and call VRP is 4.2, you want put-heavy structures.
Step 4: Set Entry Levels from Dealer Positioning
Now the second API call — /v1/exposure/levels/{symbol} returns key dealer positioning levels:
for s in signals: r = requests.get( f"{BASE}/v1/exposure/levels/{s['symbol']}", headers=HEADERS ) if r.status_code != 200: continue lvl = r.json().get("levels", r.json())for s in signals: r = requests.get( f"{BASE}/v1/exposure/levels/{s['symbol']}", headers=HEADERS ) if r.status_code != 200: continue lvl = r.json().get("levels", r.json())s["put_wall"] = lvl.get("put_wall", 0) s["call_wall"] = lvl.get("call_wall", 0) s["max_gamma"] = lvl.get("max_positive_gamma", 0)`
Enter fullscreen mode
Exit fullscreen mode
-
Put wall → place short put strikes here (dealers buy at this level = mechanical backstop)
-
Call wall → place short call strikes here (dealers sell at this level = structural ceiling)
-
Max gamma → price magnet, ideal straddle center
-
Gamma flip → the line in the sand. Above = dampened. Below = amplified.
Step 5: Exit Rules (Define Before Entry)
Four non-negotiable rules:
-
50% profit target. Captures ~85% of expected P&L with ~40% of the variance. The Sharpe improvement is significant.
-
Gamma flip breach. If spot drops below the flip, your Cell A trade is now Cell B. Cut half or close.
-
21 DTE time stop. Gamma accelerates in the final 3 weeks. The remaining theta isn't worth the path risk.
-
VRP inversion. If z-score drops below -0.5, realized vol is beating implied. Close immediately.
for s in signals: vrp = s["_raw"].get("vrp", {}) z = vrp.get("z_score", 0) flip = s["gamma_flip"] spot = s["spot"] dist = ((spot - flip) / spot * 100) if spot > 0 else 0*_for s in signals: vrp = s["_raw"].get("vrp", {}) z = vrp.get("z_score", 0) flip = s["gamma_flip"] spot = s["spot"] dist = ((spot - flip) / spot * 100) if spot > 0 else 0*_if z < -0.5: status = "EXIT — VRP inversion" elif dist < 0: status = "EXIT — below gamma flip" elif dist < 0.5: status = "WARNING — near flip" else: status = "OK"
print(f"{s['symbol']:<6} z={z:+.2f} dist={dist:+.1f}% → {status}")`
Enter fullscreen mode
Exit fullscreen mode
The Complete Morning Script
All 5 steps in one runnable file → outputs a trade plan with symbol, cell classification, structure, strikes, and sizing. Full script is in the original article.
Sample output:
══════════════════════════════════════════════════════════════════ VRP MORNING SCAN ════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ VRP MORNING SCAN ══════════════════════════════════════════════════════════════════────────────────────────────────────────────────────────────────── SPY — Cell A: PREMIUM PARADISE ────────────────────────────────────────────────────────────────── VRP z-score: +1.34 (82nd percentile) ATM IV: 18.5% RV20d: 13.0% Spread: +5.4 Put VRP: +7.4 Call VRP: +4.2 Gamma regime: positive_gamma Flip: $572.50 Distance: +1.4% Best structure: iron_condor (score: 85) Size: 1.75x standard Levels: put_wall=$570 call_wall=$590 max_gamma=$575`
Enter fullscreen mode
Exit fullscreen mode
What You Need
The VRP endpoint, strategy scores, directional VRP, GEX-conditioned regime data, and term VRP are on the Alpha plan. Per-strike GEX and key levels are free.
If you're building systematic premium-selling workflows, Alpha is where the actionable analytics live — VRP z-scores, harvest scores, strategy scoring, vanna outlook, dealer flow risk, and unlimited API calls to run this scanner across your entire universe every morning without throttling.
Links:
-
FlashAlpha API Docs
-
Python SDK (pip install flashalpha)
-
Full article with Kelly sizing + term VRP
-
GEX-Conditioned VRP deep dive
DEV Community
https://dev.to/tomasz_dobrowolski_35d32c/i-built-a-5-minute-vrp-trading-scanner-in-python-heres-the-code-3a8iSign in to highlight and annotate this article

Conversation starters
Daily AI Digest
Get the top 5 AI stories delivered to your inbox every morning.
More about
versionmarketinsight
IA-TIGRIS: An Incremental and Adaptive Sampling-Based Planner for Online Informative Path Planning
arXiv:2502.15961v3 Announce Type: replace Abstract: Planning paths that maximize information gain for robotic platforms has wide-ranging applications and significant potential impact. To effectively adapt to real-time data collection, informative path planning must be computed online and be responsive to new observations. In this work, we present IA-TIGRIS (Incremental and Adaptive Tree-based Information Gathering Using Informed Sampling), which is an incremental and adaptive sampling-based informative path planner designed for real-time onboard execution. Our approach leverages past planning efforts through incremental refinement while continuously adapting to updated belief maps. We additionally present detailed implementation and optimization insights to facilitate real-world deployment

Why the most powerful technology in a generation is losing the argument for itself
Fear, panic, and anxiety. AI needs a better story. The office of the future. A lone worker with all their agents. In last week’s essay, I took the time to put forward an argument that looked seriously at what might be possible if AI goes well. It was an exercise in what it might look like if the technology delivered on even a fraction of its promise. I felt it was necessary because the current dominating narrative around AI, the economy, and the future of work is doomscrolling dressed up as analysis. We have become addicted to headlines that promise our demise and swapped the nuclear doomsday clock for the end-of-work-as-we-know-it calendar. You know the script. We’ve got five years. No, three. Actually, it’s 24 months. Hold on — it might be by the end of this year. It’s your last chance t
Knowledge Map
Connected Articles — Knowledge Graph
This article is connected to other articles through shared AI topics and tags.
More in Releases

Temple University Japan Advances Academic Offerings with Launch of Bachelor of Science in Artificial Intelligence for Fall 2026 - TUJ News
Temple University Japan Advances Academic Offerings with Launch of Bachelor of Science in Artificial Intelligence for Fall 2026 TUJ News

Washington launches export initiative to ensure ‘future of AI is led by the United States’
The US government has unveiled an AI export initiative designed to cement American technological leadership while countering China’s growing influence in the sector. Washington is inviting US companies to form “preset” consortiums to offer full-stack artificial intelligence solutions around the world. Applications will be accepted until the end of June, the International Trade Administration (ITA), an agency under the US Department of Commerce, said in a news release issued on Wednesday. The...



Discussion
Sign in to join the discussion
No comments yet — be the first to share your thoughts!