The volatility risk premium is the systematic spread between implied volatility and subsequent realized volatility. In plain terms: options are consistently priced as if the underlying will move more than it actually does. The difference between what the market expects (IV) and what actually happens (RV) is the VRP.
The math is straightforward:
VRP = IV(ATM) − RV(window)
If SPY's ATM implied volatility is 20% and the 20-day realized volatility is 16%, the VRP is 4 percentage points. That 4% is what you're being paid — annualized — to sell options. It's the compensation you receive for bearing the risk that realized vol could spike above implied vol.
Why Does This Premium Exist?
The VRP isn't a market inefficiency — it's a structural feature. It exists because of a persistent supply-demand imbalance in the options market:
Institutional hedgers systematically buy protection. Portfolio managers, pension funds, and risk-constrained institutions are required to hedge. They buy puts and pay for collars regardless of whether vol is cheap or expensive. This demand is price-insensitive — they're buying insurance, not speculating on vol.
Insurance buyers overpay by design. Just as homeowners pay more for fire insurance than the actuarial cost of fires, options buyers pay more than the expected payout. The premium compensates sellers for bearing tail risk — the chance that realized vol could be 2x or 3x implied vol on any given day.
The market prices in worst-case scenarios that usually don't materialize. IV reflects the distribution of possible outcomes, weighted by fear. Realized vol reflects what actually happened. The gap between fear and reality is, on average, positive.
Leverage constraints create asymmetric demand. Institutions that want downside protection must buy it in the options market — they can't simply hold more cash or reduce leverage fast enough. This structural demand floor keeps IV elevated relative to RV.
This Is Not a Free Lunch
The VRP is often described as "picking up pennies in front of a steamroller," and there's truth to that characterization. The premium exists precisely because selling volatility has fat-tail risk. You collect small, consistent premiums on most days, then occasionally face outsized losses when realized vol explodes past implied vol.
Historical data tells the story:
- VRP is positive roughly 70–75% of trading days across the S&P 500 over the last 30 years.
- The average VRP is 2–4 percentage points (IV minus subsequent RV), depending on the window.
- The worst drawdowns come in clusters. VRP doesn't go negative gradually — it inverts violently during market stress (2008, 2018 Volmageddon, 2020 COVID, 2022 rate shock). These are the events that destroy undisciplined sellers.
- Long-term, systematic VRP harvesting has a positive Sharpe ratio — typically 0.3 to 0.6 depending on implementation — but with meaningful max drawdowns.
The edge is real. The question is whether you're measuring it before you trade, or just assuming it's there.
Why Most Retail Traders Already Trade VRP (Badly)
If you sell covered calls, you're harvesting VRP. If you sell iron condors, you're harvesting VRP. If you write credit spreads, cash-secured puts, or strangles, you're harvesting VRP. Nearly every premium-selling strategy is, at its core, a bet that IV will overstate realized vol.
The problem isn't the strategy — it's the execution. Most retail premium sellers make three critical errors:
Error 1: Selling Premium Without Checking if VRP Is Positive
If you sell a 30-DTE iron condor on SPY every month regardless of conditions, you're assuming VRP is always positive. It isn't. When IV is 18% and 20-day realized vol is 22%, you're selling insurance below cost. You're paying the market for the privilege of taking their risk.
This happens more often than you'd think — especially after sustained trending moves where realized vol spikes but IV hasn't caught up, or during extended low-vol regimes where IV compresses to levels that barely exceed RV.
Error 2: Using Symmetric Strategies When VRP Is Directional
The "total" VRP is an average across puts and calls. But the risk premium on the put side (crash protection) is structurally different from the call side. Selling an iron condor assumes the VRP is equal on both sides. It rarely is. If put VRP is 5% and call VRP is 1%, your iron condor is mostly a put trade with a call leg that's adding risk for minimal premium.
Error 3: Ignoring the Regime
VRP can be 4% — solidly positive — while dealers are in negative gamma and positioned to amplify any downturn. The premium looks attractive, but the tail risk is elevated because dealer hedging will worsen any sell-off. Measuring VRP without accounting for dealer positioning is like checking the weather forecast but not the flood warnings.
The fix for all three errors is the same: measure before you trade.
Before & After — The Same Trade, Different Outcomes
To make this concrete, here's the same trade decision made two ways.
Without VRP Data (How Most Retail Traders Do It)
It's March 15, 2026. SPY is at $572. ATM IV for the 30-DTE cycle is 19.8%. A 10-wide iron condor ($560/$570 puts, $580/$590 calls) collects $2.85 in premium. The trader thinks: "IV is close to 20%, premiums look fat, theta is on my side. I'll sell the condor."
What the trader doesn't know:
- 20-day realized vol is 23.1% — IV is BELOW realized vol. VRP is −3.3%. The trader is selling insurance below cost.
- VRP z-score is −1.4 — deeply negative. This is the 8th percentile of the last year. Premium hasn't been this cheap in months.
- Dealers are in negative gamma. The gamma flip is at $578, and SPY is below it. Any sell-off will be amplified by dealer hedging flows.
- Put-side VRP is −4.8%. The put spread (which carries most of the condor's risk) has the worst risk/reward of any component.
Over the next week, SPY drops to $561. The iron condor loses $6.80 against $2.85 collected — a 2.4:1 loss ratio. Dealers amplified the move exactly as their positioning predicted. The VRP was negative the entire time — there was never an edge.
With VRP Data (Systematic Approach)
Same day, same setup. The trader pulls the VRP endpoint before placing the trade:
resp = requests.get(
"https://lab.flashalpha.com/v1/vrp/SPY",
headers={"X-Api-Key": "YOUR_API_KEY"}
)
data = resp.json()
print(f"VRP 20d: {data['vrp_spreads']['vrp_20d']:+.2f}%") # -3.30%
print(f"Z-score: {data['z_score']:+.2f}") # -1.42
print(f"Regime: {data['gex_regime']['label']}") # negative_gamma
print(f"Put VRP: {data['directional_vrp']['put_vrp_20d']:+.2f}%") # -4.80%
The data is unambiguous: VRP is deeply negative, z-score is in the 8th percentile, regime is negative gamma, put VRP is worse than total VRP. Every signal says do not sell premium.
The trader passes. No trade. Keeps capital for a better day. A week later, after the sell-off exhausts itself, VRP flips to +4.2% with a z-score of +1.8 in positive gamma. Now the trader sells — with the data confirming the edge exists.
Same trader, same market, same strategy — different outcome because of one API call.
Measuring VRP — The Five Windows
VRP isn't a single number. Because realized vol depends on the lookback window, you get a different VRP reading for each time horizon. The FlashAlpha VRP endpoint returns five simultaneous windows, each telling you something different about the current vol environment.
| Window | Calculation | Character | Best For |
|---|---|---|---|
| 5-day VRP | ATM IV − RV5 | Noisy, fast-reacting, captures intraday regime shifts | Same-week premium selling, 0DTE context |
| 10-day VRP | ATM IV − RV10 | Short-term signal with moderate smoothing | Weekly expiration strategies, 5–10 DTE trades |
| 20-day VRP | ATM IV − RV20 | The "standard" window, most commonly referenced | Monthly premium selling, iron condors, strangles |
| 30-day VRP | ATM IV − RV30 | Smoothed, less reactive to recent outliers | 30–45 DTE strategies, structural positioning |
| 60-day VRP | ATM IV − RV60 | Structural VRP, captures the macro vol regime | Longer-dated trades, calendar spreads, portfolio-level decisions |
Here's how to pull all five VRP spreads for any symbol:
import requests
resp = requests.get(
"https://lab.flashalpha.com/v1/vrp/SPY",
headers={"X-Api-Key": "YOUR_API_KEY"}
)
data = resp.json()
vrp = data["vrp_spreads"]
print("=== SPY Volatility Risk Premium ===")
print(f"ATM IV: {data['atm_iv']:.1f}%")
print(f"5-day VRP: {vrp['vrp_5d']:+.2f}% (RV5 = {vrp['rv_5d']:.1f}%)")
print(f"10-day VRP: {vrp['vrp_10d']:+.2f}% (RV10 = {vrp['rv_10d']:.1f}%)")
print(f"20-day VRP: {vrp['vrp_20d']:+.2f}% (RV20 = {vrp['rv_20d']:.1f}%)")
print(f"30-day VRP: {vrp['vrp_30d']:+.2f}% (RV30 = {vrp['rv_30d']:.1f}%)")
print(f"60-day VRP: {vrp['vrp_60d']:+.2f}% (RV60 = {vrp['rv_60d']:.1f}%)")
Reading the VRP Spread
| VRP Level (20d) | Interpretation | Action |
|---|---|---|
| > 6% | Extremely elevated — IV is pricing in far more vol than realized | Aggressive premium selling. Wider strikes for larger credit. |
| 3% to 6% | Healthy — solid compensation for selling vol | Standard premium selling. Full-size positions. |
| 1% to 3% | Thin — you're being paid, but not much | Reduce size. Favor defined-risk structures. Be selective. |
| 0% to 1% | Marginal — barely any edge | Minimal exposure or sit out. |
| < 0% | Negative — IV understates realized vol | Do not sell premium. Consider buying vol. |
The key insight is that not every positive VRP reading is equally tradeable. A VRP of 1.5% is technically positive, but after commissions, slippage, and the risk of a tail event, the expected value is marginal. Save your capital for the 3%+ readings where the edge is clear.
Z-Score and Percentile — Is VRP Rich or Cheap Right Now?
Raw VRP tells you the spread between IV and RV. But "4% VRP" means something very different in a low-vol regime (where 4% is unusually wide) versus a high-vol regime (where 4% might be compressed). You need context.
That's what the z-score and percentile provide, calculated over a rolling 252-day (one year) lookback:
z = (VRP_today − μ_VRP,252d) / σ_VRP,252d
The z-score tells you how many standard deviations today's VRP is from its trailing mean. The percentile tells you where today's VRP falls in the distribution of the last 252 readings.
import requests
resp = requests.get(
"https://lab.flashalpha.com/v1/vrp/SPY",
headers={"X-Api-Key": "YOUR_API_KEY"}
)
data = resp.json()
z = data["z_score"]
pct = data["percentile"]
vrp_20d = data["vrp_spreads"]["vrp_20d"]
print(f"VRP (20d): {vrp_20d:+.2f}%")
print(f"Z-score: {z:+.2f}")
print(f"Percentile: {pct:.0f}th")
# Decision framework
if z > 1.5:
print("SIGNAL: VRP is significantly elevated vs history.")
print("ACTION: Aggressive premium selling. Full size. Wider strikes.")
elif z > 0.5:
print("SIGNAL: VRP is above average. Healthy edge.")
print("ACTION: Standard premium selling. Normal position sizing.")
elif z > -0.5:
print("SIGNAL: VRP is near average. Marginal edge.")
print("ACTION: Reduce size. Tighter strikes. Be selective.")
elif z > -1.0:
print("SIGNAL: VRP is compressed. Thin or no edge.")
print("ACTION: Minimal exposure. Consider sitting out.")
else:
print("SIGNAL: VRP is deeply compressed or negative. No edge.")
print("ACTION: Do not sell premium. Consider buying vol.")
Z-Score Decision Matrix
| Z-Score Range | Percentile (approx) | Interpretation | Position Sizing |
|---|---|---|---|
| > 2.0 | 97th+ | Extreme VRP expansion — rare, usually post-event | Maximum size. This is the fat pitch. |
| 1.5 to 2.0 | 90th–97th | Significantly rich — aggressive selling window | 75–100% of max size |
| 0.5 to 1.5 | 70th–90th | Above-average VRP — solid conditions | 50–75% of max size |
| −0.5 to 0.5 | 30th–70th | Average VRP — marginal edge, be selective | 25–50% of max size |
| −1.0 to −0.5 | 15th–30th | Compressed VRP — thin or no edge | 0–25% of max size |
| < −1.0 | Below 15th | Negative or deeply compressed — selling vol is −EV | Zero. Do not sell premium. |
The z-score is the single most important filter for VRP trading. A positive VRP with a z-score of −0.8 is telling you that while IV still exceeds RV, the spread is unusually narrow compared to recent history — the risk/reward has deteriorated. Wait for better conditions.
Directional VRP — The Put Side vs Call Side
Total VRP is an average. It masks the real structure of the premium, which is almost always asymmetric. The VRP endpoint decomposes VRP into put-side and call-side components, revealing where the actual edge sits.
Why Directional VRP Matters
Put options carry a structural premium over calls because institutional hedgers are persistent net buyers of downside protection. This creates a skew premium — the put side of the volatility surface is consistently richer than the call side. When you decompose VRP directionally, you typically see:
- Put VRP is larger than call VRP in most environments. The crash protection premium inflates put IV above what downside realized vol justifies. This is the norm — roughly 80% of the time.
- Call VRP is smaller but more consistent. Because call-side hedging demand is lower, call IV is closer to "fair value." The premium is thinner but more stable.
- The spread between put VRP and call VRP tells you where the edge is. If put VRP is 5.2% and call VRP is 1.1%, the edge is overwhelmingly on the put side.
import requests
resp = requests.get(
"https://lab.flashalpha.com/v1/vrp/SPY",
headers={"X-Api-Key": "YOUR_API_KEY"}
)
data = resp.json()
directional = data["directional_vrp"]
put_vrp = directional["put_vrp_20d"]
call_vrp = directional["call_vrp_20d"]
skew_spread = directional["skew_adjusted_spread"]
print(f"Put-side VRP (20d): {put_vrp:+.2f}%")
print(f"Call-side VRP (20d): {call_vrp:+.2f}%")
print(f"Skew spread: {skew_spread:+.2f}%")
# Directional decision
if put_vrp > call_vrp * 2:
print("\nEDGE: Heavily skewed to puts.")
print("STRATEGY: Sell put credit spreads or naked puts.")
print("AVOID: Iron condors (call side adds risk for minimal premium).")
elif call_vrp > put_vrp * 1.5:
print("\nEDGE: Unusual call-side premium. Check for earnings/events.")
print("STRATEGY: Sell call credit spreads or covered calls.")
elif abs(put_vrp - call_vrp) < 1.0:
print("\nEDGE: Roughly symmetric. Iron condors are appropriate.")
print("STRATEGY: Sell strangles or iron condors.")
else:
print(f"\nEDGE: Moderately skewed to {'puts' if put_vrp > call_vrp else 'calls'}.")
print("STRATEGY: Skew your position — larger size on the richer side.")
When Call VRP Exceeds Put VRP
This is rare in broad market indices but does happen in specific situations:
- Meme stock euphoria. When a stock experiences extreme upside momentum, call buyers overpay for upside exposure.
- Pre-earnings on high-momentum names. Heavy call buying can inflate call IV relative to realized upside vol.
- Short squeeze setups. When short interest is high and calls are being used to force a squeeze, call-side IV can become detached from fundamental vol.
Why Iron Condors Are Often Suboptimal
Iron condors assume symmetric VRP — equal edge on both sides. In reality, if put VRP is 4.8% and call VRP is 0.9%, your iron condor is essentially a put credit spread that happens to also have a call spread attached. The call spread adds margin requirement and tail risk but contributes almost no premium.
The directional VRP data lets you make a better decision: sell puts when put VRP is rich, sell calls when call VRP is rich, and only use iron condors when the skew spread is narrow.
GEX-Conditioned Regime — When VRP Is Safe vs Dangerous
VRP tells you the size of the premium. It doesn't tell you about the risk environment. A 4% VRP in a positive gamma regime (dealers dampening moves) is a fundamentally different trade than a 4% VRP in a negative gamma regime (dealers amplifying moves).
VRP harvesting works best when dealer positioning suppresses the very tail events that make selling vol dangerous.
How GEX Regime Affects VRP Trades
| Regime | VRP Level | Assessment | Action |
|---|---|---|---|
| Positive gamma | High (z > 1.0) | Best case — fat premium AND dealer dampening | Full size. Aggressive premium selling. |
| Positive gamma | Low (z < 0) | Safe but thin — dealers are dampening but premium is marginal | Reduce size. |
| Negative gamma | High (z > 1.0) | Tempting but dangerous — premium is there but tail risk is elevated | Reduce size 50%. Defined risk only. |
| Negative gamma | Low (z < 0) | Worst case — no premium AND amplified risk | Sit out entirely. |
import requests
resp = requests.get(
"https://lab.flashalpha.com/v1/vrp/SPY",
headers={"X-Api-Key": "YOUR_API_KEY"}
)
data = resp.json()
vrp_20d = data["vrp_spreads"]["vrp_20d"]
z = data["z_score"]
regime = data["gex_regime"]
label = regime["label"]
gamma_flip = regime["gamma_flip"]
net_gex = regime["net_gex"]
vanna = regime["vanna_interpretation"]
print(f"VRP (20d): {vrp_20d:+.2f}% | Z-score: {z:+.2f}")
print(f"Regime: {label} | Gamma flip: ${gamma_flip}")
print(f"Net GEX: {net_gex:,.0f} | Vanna: {vanna}")
# GEX-conditioned decision matrix
if label == "positive_gamma" and z > 1.0:
print("\n*** GREEN LIGHT — Full size premium selling ***")
print("Dealers are dampening moves AND VRP is elevated.")
elif label == "positive_gamma" and z > 0:
print("\n*** YELLOW — Standard premium selling ***")
print("Regime is supportive but VRP is only moderately elevated.")
elif label == "negative_gamma" and z > 1.0:
print("\n*** CAUTION — Reduce size, widen strikes ***")
print("VRP is attractive but dealers will amplify any adverse move.")
elif label == "negative_gamma":
print("\n*** RED — Do not sell premium ***")
print("No edge AND elevated tail risk. Sit this one out.")
The Gamma Flip and VRP Timing
The gamma_flip level tells you the price at which the regime changes from positive to negative gamma (or vice versa). This is critical for VRP trade management:
- If price is well above the gamma flip (positive gamma territory), your short vol position has the structural wind at its back.
- If price is near the gamma flip, the regime could flip at any time. Size down and set tighter stops.
- If price crosses below the gamma flip into negative gamma, consider closing short vol positions regardless of P&L.
Vanna and Charm Effects on VRP
In a positive gamma + positive vanna environment, any IV compression (which is what you're betting on when you sell premium) also creates supportive dealer buying flows. Your trade has a built-in tailwind.
In a negative gamma + negative vanna environment, any IV expansion creates additional selling pressure from dealer hedging. Your short vol position faces compounding headwinds.
Strategy Suitability Scores
Knowing that VRP is positive and the regime is supportive answers whether to sell premium. But which structure? A short straddle and an iron condor are both short vol, but they behave very differently in different environments.
The VRP endpoint returns strategy suitability scores (0–100) for six common structures, computed from the current VRP level, directional skew, GEX regime, realized vol, and term structure.
import requests
resp = requests.get(
"https://lab.flashalpha.com/v1/vrp/SPY",
headers={"X-Api-Key": "YOUR_API_KEY"}
)
data = resp.json()
scores = data["strategy_scores"]
print("=== Strategy Suitability Scores (0-100) ===")
for strategy, score in sorted(scores.items(), key=lambda x: -x[1]):
bar = "#" * (score // 5)
print(f"{strategy:20s} {score:3d} {bar}")
What Each Score Means
| Strategy | Best When | Score Drivers |
|---|---|---|
| Short straddle | VRP is high, regime is positive gamma, RV is low | Requires wide VRP AND low realized vol. High scores (80+) are the fat pitch. |
| Short strangle | VRP is moderate-to-high, positive gamma | Similar to straddle but more forgiving. Scores above 60 are good. |
| Iron condor | VRP is moderate, you need defined risk, skew is symmetric | Favored when put/call VRP are similar. |
| Put credit spread | Put VRP >> call VRP, put skew is steep | Scores highest when directional VRP is heavily skewed to puts. |
| Calendar spread | VRP term structure is steep (front-month richest) | Highest when front-month IV is significantly elevated relative to back-month. |
| Jade lizard | Call VRP is thin, put VRP is fat | Short put + short call spread — eliminates upside risk. |
The strategy scores save you from the most common structural mistake: using the same strategy every time. An iron condor trader who checks the scores might discover that today, a put credit spread scores 82 while the iron condor scores 41 — because put VRP is rich but call VRP is nonexistent.
Dealer Flow Risk — The Hidden Risk in VRP Trades
Most VRP traders think about risk in terms of realized volatility: "If RV spikes above IV, I lose." That's true but incomplete. The more immediate risk is mechanical dealer flow — the forced buying and selling that dealers do to maintain delta neutrality.
How Dealer Positioning Creates Non-Linear Risk
When dealers are net short gamma, every point of market movement forces dealers to hedge in the same direction as the move. If SPY drops $2 and dealers are short gamma, they must sell shares to maintain their hedge. That selling pushes SPY lower, triggering more hedging, which pushes it lower still. This is the gamma feedback loop that turns orderly sell-offs into flash crashes.
For a short vol position, this feedback loop is devastating. A 1% decline in positive gamma might stay at 1%. The same decline in negative gamma can cascade to 2–3% as dealer hedging amplifies the move.
import requests
resp = requests.get(
"https://lab.flashalpha.com/v1/vrp/SPY",
headers={"X-Api-Key": "YOUR_API_KEY"}
)
data = resp.json()
risk = data["dealer_risk"]
print("=== Dealer Flow Risk Assessment ===")
print(f"Gamma regime: {risk['gamma_regime']}")
print(f"Vanna direction: {risk['vanna_direction']}")
print(f"Hedging flow: {risk['hedging_flow_magnitude']}")
print(f"Risk level: {risk['risk_level']}")
if risk["risk_level"] == "elevated":
print("\nWARNING: Dealer positioning creates amplified tail risk.")
print("Reduce size by 50% and use defined-risk structures only.")
elif risk["risk_level"] == "high":
print("\nDANGER: Dealer positioning is actively hostile to short vol.")
print("Do not sell premium. Consider hedging existing positions.")
else:
print("\nDealer risk is contained. Proceed with normal sizing.")
Integrating Dealer Risk Into Position Sizing
The simplest approach is a multiplier system:
- Dealer risk "low": 100% of intended size. Dealers are dampening moves.
- Dealer risk "moderate": 75% of intended size. Some hedging pressure exists.
- Dealer risk "elevated": 50% of intended size. Material amplification risk.
- Dealer risk "high": 0% of intended size. Do not enter new short vol trades.
This is the edge that separates systematic VRP traders from retail premium sellers. Both might see a 4% VRP and think "sell." The systematic trader also checks dealer risk and discovers that negative gamma + vanna-driven selling pressure means the tail distribution is 2x wider than normal. Same premium, radically different risk.
Building a Systematic VRP Strategy
Here's a complete daily VRP workflow that integrates everything — VRP spreads, z-score, directional analysis, GEX regime, strategy selection, and position sizing.
import requests
from datetime import datetime
API_KEY = "YOUR_API_KEY"
BASE = "https://lab.flashalpha.com"
def get_vrp(symbol):
"""Pull VRP data for a symbol."""
resp = requests.get(
f"{BASE}/v1/vrp/{symbol}",
headers={"X-Api-Key": API_KEY}
)
return resp.json()
def assess_trade(symbol):
"""Full VRP trade assessment."""
data = get_vrp(symbol)
vrp = data["vrp_spreads"]
z = data["z_score"]
pct = data["percentile"]
directional = data["directional_vrp"]
regime = data["gex_regime"]
scores = data["strategy_scores"]
risk = data["dealer_risk"]
print(f"\n{'='*50}")
print(f" VRP Assessment: {symbol}")
print(f" {datetime.now().strftime('%Y-%m-%d %H:%M')}")
print(f"{'='*50}")
# Step 1: Is VRP positive and significant?
print(f"\n1. VRP SPREAD")
print(f" 20-day VRP: {vrp['vrp_20d']:+.2f}%")
print(f" Z-score: {z:+.2f} ({pct:.0f}th percentile)")
if z < -0.5:
print(" VERDICT: VRP is too thin. No trade today.")
return None
# Step 2: What's the regime?
print(f"\n2. GEX REGIME")
print(f" Regime: {regime['label']}")
print(f" Gamma flip: ${regime['gamma_flip']}")
if regime["label"] == "negative_gamma" and z < 0.5:
print(" VERDICT: Negative gamma + thin VRP. No trade.")
return None
# Step 3: Dealer risk check
print(f"\n3. DEALER RISK")
print(f" Risk level: {risk['risk_level']}")
size_multiplier = {
"low": 1.0, "moderate": 0.75,
"elevated": 0.5, "high": 0.0
}.get(risk["risk_level"], 0.5)
if size_multiplier == 0:
print(" VERDICT: Dealer risk too high. No trade.")
return None
# Step 4: Directional analysis
print(f"\n4. DIRECTIONAL VRP")
put_vrp = directional["put_vrp_20d"]
call_vrp = directional["call_vrp_20d"]
print(f" Put VRP: {put_vrp:+.2f}%")
print(f" Call VRP: {call_vrp:+.2f}%")
# Step 5: Strategy selection
print(f"\n5. STRATEGY SCORES")
for strat, score in sorted(scores.items(), key=lambda x: -x[1])[:3]:
print(f" {strat:20s} {score:3d}/100")
best = max(scores, key=scores.get)
best_score = scores[best]
# Step 6: Position sizing
base_size = 1.0
z_adj = min(z / 2.0, 1.0)
final_size = base_size * z_adj * size_multiplier
print(f"\n6. POSITION SIZING")
print(f" Base: {base_size:.0%}")
print(f" Z-adj: {z_adj:.0%}")
print(f" Risk adj: {size_multiplier:.0%}")
print(f" Final size: {final_size:.0%}")
print(f"\n{'='*50}")
print(f" RECOMMENDATION: {best} at {final_size:.0%} size")
print(f" Score: {best_score}/100 | VRP: {vrp['vrp_20d']:+.2f}%")
print(f"{'='*50}")
return {
"symbol": symbol,
"strategy": best,
"score": best_score,
"size": final_size,
"vrp": vrp["vrp_20d"],
"z_score": z,
"regime": regime["label"]
}
# Run assessment for a watchlist
watchlist = ["SPY", "QQQ", "IWM", "AAPL", "TSLA"]
results = []
for sym in watchlist:
try:
result = assess_trade(sym)
if result:
results.append(result)
except Exception as e:
print(f"Error processing {sym}: {e}")
# Summary
if results:
print(f"\n\n{'='*50}")
print(" TODAY'S TRADEABLE VRP OPPORTUNITIES")
print(f"{'='*50}")
for r in sorted(results, key=lambda x: -x["score"]):
print(f" {r['symbol']:5s} | {r['strategy']:20s} | "
f"Score: {r['score']:3d} | Size: {r['size']:.0%} | "
f"VRP: {r['vrp']:+.2f}% | {r['regime']}")
else:
print("\nNo tradeable VRP opportunities today. Sit tight.")
Using Historical VRP for Backtesting
import requests
import pandas as pd
resp = requests.get(
"https://lab.flashalpha.com/v1/vrp/SPY/history",
headers={"X-Api-Key": "YOUR_API_KEY"},
params={"start": "2024-01-01", "end": "2025-12-31"}
)
history = resp.json()
df = pd.DataFrame(history["data"])
df["date"] = pd.to_datetime(df["date"])
df = df.set_index("date")
# Basic stats
print(f"Days with positive VRP: {(df['vrp_20d'] > 0).mean():.1%}")
print(f"Mean VRP: {df['vrp_20d'].mean():.2f}%")
print(f"Median VRP: {df['vrp_20d'].median():.2f}%")
# Win rate by z-score bucket
for low, high, label in [
(1.5, 999, "z > 1.5"),
(0.5, 1.5, "0.5 < z < 1.5"),
(-0.5, 0.5, "-0.5 < z < 0.5"),
(-999, -0.5, "z < -0.5")
]:
mask = (df["z_score"] >= low) & (df["z_score"] < high)
subset = df[mask]
if len(subset) > 0:
win_rate = (subset["vrp_20d"] > 0).mean()
avg_vrp = subset["vrp_20d"].mean()
print(f" {label:15s}: {len(subset):4d} days, "
f"win rate {win_rate:.1%}, avg VRP {avg_vrp:+.2f}%")
Risk Management Rules
No VRP strategy works without discipline on the risk side:
- Max portfolio allocation to short vol: Never allocate more than 15–20% of portfolio notional across all names. Concentration kills.
- Per-position max loss: For undefined risk (straddles, strangles), set a hard stop at 2x the credit received. For defined risk, the max loss is already defined by the wings.
- Regime-based exit: If the GEX regime flips from positive to negative gamma while you're in a trade, close 50% immediately regardless of P&L.
- VRP inversion exit: If the VRP z-score drops below −1.0, close all short vol positions.
- Calendar-based rules: Reduce size by 50% going into FOMC, CPI, and NFP. Eliminate short vol entirely on the day of the event unless VRP z-score is above 2.0.
- Correlation awareness: If you're short vol on SPY, QQQ, and IWM simultaneously, you have one trade, not three. Size as if it's a single position.
Common Mistakes in VRP Trading
Mistake 1: Selling Premium When VRP Is Negative
VRP is negative roughly 25–30% of trading days, and those days often cluster during the exact periods when selling premium feels most tempting. The fix: check VRP before every trade. If vrp_20d is negative or the z-score is below −0.5, don't sell.
Mistake 2: Ignoring Dealer Positioning
A 4% VRP in positive gamma has a fundamentally different risk profile from a 4% VRP in negative gamma. Always check gex_regime and dealer_risk. In negative gamma, cut size by at least 50%.
Mistake 3: Not Adjusting for Term Structure
If front-month VRP is 2% but 60-day VRP is 5%, selling weekly options is suboptimal. Compare VRP across all five windows and sell the richest part of the curve.
Mistake 4: Over-Sizing Because VRP "Seems Safe"
VRP is persistent, but it has fat tails. Size based on worst-case loss, not average return. Cap maximum position size at a level where a 3-sigma adverse move doesn't threaten the portfolio.
Mistake 5: Using Symmetric Strategies When VRP Is Directional
If put VRP is 5% and call VRP is 0.5%, an iron condor is 90% a put trade. Check directional_vrp — if the skew spread is wider than 2%, tilt your structure toward the richer side.
Get the Data
The VRP endpoint returns everything covered in this guide in a single API call:
- VRP spreads across 5 windows (5d, 10d, 20d, 30d, 60d)
- Z-score and percentile vs trailing 252-day history
- Directional VRP — put-side and call-side decomposition
- VRP term structure
- GEX-conditioned regime — gamma label, flip level, net GEX, vanna
- Strategy suitability scores for six common structures
- Dealer flow risk — gamma regime, vanna direction, hedging flow magnitude
- Macro context — VIX level, VIX term structure, event calendar proximity
- Daily history via
/v1/vrp/{symbol}/historyfor backtesting
👉 API Docs | Playground | Pricing
Related Reading
- VRP Z-Score: How to Time Premium Selling — backtested win rates by z-score bucket
- Directional VRP: Why Iron Condors Are Usually Wrong — put vs call VRP decomposition
- GEX-Conditioned VRP: Dealer Positioning Changes Everything — the 4-cell regime matrix
- VRP Strategy Scoring: Pick the Right Structure — what drives each score
- Realized vs Implied Volatility — foundational concepts behind VRP
- 0DTE Trading Strategies — intraday premium selling with dealer positioning
Top comments (0)