DEV Community

tomasz dobrowolski
tomasz dobrowolski

Posted on • Originally published at flashalpha.com

Build a VRP Dashboard with Python: Volatility Risk Premium Monitor in One API Call

If you've ever tried to compute the volatility risk premium yourself, you know the pain: source options chains, compute ATM IV, calculate realized vol across multiple windows, fit a term structure, decompose put vs call wing premiums, condition on dealer positioning, score strategy suitability. That's 3-6 months of engineering before you display a single number on a dashboard.

One API call replaces all of it.


What the Endpoint Returns

GET /v1/vrp/{symbol} returns the full VRP picture in a single JSON response:

Section What It Tells You
VRP core ATM IV, realized vol (5d/10d/20d/30d), VRP spread per window, z-score, percentile
Directional Put-wing vs call-wing IV, downside VRP vs upside VRP
Term structure VRP by DTE bucket (7d, 14d, 30d) — which expiration is richest
GEX-conditioned Gamma regime, harvest score (0-1), interpretation
Strategy scores Short straddle, strangle, iron condor, calendar, jade lizard scored 0-100
Regime Positive/negative gamma, VRP regime, net GEX, gamma flip
Macro VIX, VIX 3M, term slope, 10Y yield, HY spread, fed funds
Risk Dealer flow risk score, warnings array, net harvest score

No manual IV computation. No realized vol calculation. No regime detection logic. Pre-computed and updated throughout the trading day.


Quick Start

pip install flashalpha
Enter fullscreen mode Exit fullscreen mode
from flashalpha import FlashAlpha

fa = FlashAlpha("YOUR_KEY")
vrp = fa.vrp("SPY")

print(f"ATM IV:        {vrp['vrp']['atm_iv']:.1f}%")
print(f"20d RV:        {vrp['vrp']['rv_20d']:.1f}%")
print(f"VRP spread:    {vrp['vrp']['vrp_20d']:.1f}%")
print(f"Z-score:       {vrp['vrp']['z_score']:.2f}")
print(f"Percentile:    {vrp['vrp']['percentile']:.0f}th")
print(f"Harvest score: {vrp['net_harvest_score']:.2f}")
print(f"Best strategy: {max(vrp['strategy_scores'], key=lambda k: vrp['strategy_scores'][k] or 0)}")
Enter fullscreen mode Exit fullscreen mode

Or with curl:

curl -H "X-Api-Key: YOUR_KEY" \
  "https://lab.flashalpha.com/v1/vrp/SPY"
Enter fullscreen mode Exit fullscreen mode

Free tier: 10 requests/day, no credit card. Get your key.


Building the Dashboard Piece by Piece

1. The VRP Gauge: Rich or Thin?

The z-score tells you how many standard deviations the current VRP is from its 252-day mean. The percentile gives the same information on a 0-100 scale.

vrp_data = vrp['vrp']

if vrp_data['z_score'] > 1.5:
    signal = "RICH"       # Top ~7% of historical observations
    color = "#16a34a"
elif vrp_data['z_score'] > 0.5:
    signal = "MODERATE"
    color = "#ca8a04"
elif vrp_data['z_score'] > -0.5:
    signal = "FAIR"
    color = "#6b7280"
else:
    signal = "THIN"       # Premium compressed, don't sell
    color = "#dc2626"

print(f"VRP Signal: {signal}")
print(f"Z-score: {vrp_data['z_score']:.2f} ({vrp_data['percentile']}th percentile)")
Enter fullscreen mode Exit fullscreen mode

2. Multi-Window Realized Vol

The response includes realized vol across four windows and the VRP spread for each. If VRP is rich at 5d but thin at 30d, the premium is driven by recent calm, not structural richness.

for window in ['5d', '10d', '20d', '30d']:
    iv = vrp_data['atm_iv']
    rv = vrp_data[f'rv_{window}']
    spread = vrp_data[f'vrp_{window}']
    print(f"  {window}: IV {iv:.1f}% - RV {rv:.1f}% = VRP {spread:+.1f}%")
Enter fullscreen mode Exit fullscreen mode

3. Directional Decomposition

Most VRP analysis treats the premium as symmetric. It isn't. The directional object decomposes VRP into put-side and call-side using 25-delta wing IVs.

d = vrp['directional']

print(f"Put wing IV:  {d['put_wing_iv_25d']:.1f}%")
print(f"Call wing IV: {d['call_wing_iv_25d']:.1f}%")
print(f"Downside VRP: {d['downside_vrp']:+.1f}%")
print(f"Upside VRP:   {d['upside_vrp']:+.1f}%")

if d['downside_vrp'] > d['upside_vrp'] + 1.0:
    print("Put side richer - short put spreads or jade lizards")
elif d['upside_vrp'] > d['downside_vrp'] + 1.0:
    print("Call side richer - covered calls or call credit spreads")
else:
    print("Balanced - symmetric structures (straddles, iron condors)")
Enter fullscreen mode Exit fullscreen mode

When put-side VRP is 5% and call-side is 1%, selling an iron condor is wrong because the premium is concentrated on one side. This is what separates a useful VRP dashboard from a basic IV-vs-RV chart.

4. VRP Term Structure

Which expiration has the richest premium per unit of time:

print("VRP Term Structure:")
for bucket in vrp['term_vrp']:
    bar = "" * int(bucket['vrp'] * 3)
    print(f"  {bucket['dte']:3d} DTE: IV {bucket['iv']:.1f}% - RV {bucket['rv']:.1f}% = VRP {bucket['vrp']:+.1f}%  {bar}")

richest = max(vrp['term_vrp'], key=lambda b: b['vrp'])
print(f"\nRichest bucket: {richest['dte']} DTE ({richest['vrp']:+.1f}% VRP)")
Enter fullscreen mode Exit fullscreen mode

5. Strategy Scores

Five premium-selling structures scored 0-100 based on current VRP, gamma regime, directional skew, and macro context:

scores = vrp['strategy_scores']
ranked = sorted(scores.items(), key=lambda x: x[1] or 0, reverse=True)

print("Strategy Suitability:")
for name, score in ranked:
    if score is not None:
        bar = "" * (score // 5)
        print(f"  {name:20s} {score:3d}/100  {bar}")
Enter fullscreen mode Exit fullscreen mode

Iron condor scores high when VRP is elevated and balanced, gamma is positive, and VIX term structure is in contango. Jade lizard scores high when put-side VRP is disproportionately rich. Calendar spreads score high when the term VRP curve is steep. The API does the scoring logic. Your dashboard just renders it.

6. The Harvest Score: Should You Sell Premium?

This is the most important number. It combines VRP richness with the gamma regime into a single 0-1 composite.

gex = vrp['gex_conditioned']
regime = vrp['regime']

print(f"Gamma regime:   {regime['gamma']}")
print(f"VRP regime:     {regime['vrp_regime']}")
print(f"Harvest score:  {gex['harvest_score']:.2f}")
print(f"Interpretation: {gex['interpretation']}")
print(f"Net GEX:        ${regime['net_gex']:,.0f}")
print(f"Gamma flip:     {regime['gamma_flip']}")

if gex['harvest_score'] > 0.7:
    print("FAVORABLE for premium selling")
elif gex['harvest_score'] > 0.4:
    print("ACCEPTABLE with caution")
else:
    print("UNFAVORABLE - consider sitting out")
Enter fullscreen mode Exit fullscreen mode

Elevated VRP in a negative gamma environment is a trap. The premium is rich because the risk is real. The harvest score catches this. TSLA might show a z-score of 2.14 (93rd percentile) but a harvest score of only 0.52 because dealers are short gamma. SPY might show a lower z-score of 1.42 but a harvest score of 0.78 because dealers are long gamma and pinning price. Your dashboard should surface this distinction.

7. Risk Flags

print(f"Dealer flow risk: {vrp['dealer_flow_risk']}/100")

if vrp['warnings']:
    for w in vrp['warnings']:
        print(f"{w}")
else:
    print("No active warnings")
Enter fullscreen mode Exit fullscreen mode

Warnings include negative_gamma, FOMC, earnings, and low_liquidity. Display prominently. They override any positive VRP signal.


Multi-Symbol Scanner

Scan your universe and sort by harvest score:

from flashalpha import FlashAlpha

fa = FlashAlpha("YOUR_KEY")
universe = ["SPY", "QQQ", "IWM", "TSLA", "NVDA", "AAPL", "AMZN", "META", "GOOGL", "MSFT"]

results = []
for sym in universe:
    try:
        v = fa.vrp(sym)
        results.append({
            'symbol': sym,
            'z_score': v['vrp']['z_score'],
            'percentile': v['vrp']['percentile'],
            'harvest_score': v['net_harvest_score'],
            'best_strategy': max(v['strategy_scores'], key=lambda k: v['strategy_scores'][k] or 0),
            'warnings': v['warnings'],
            'dealer_risk': v['dealer_flow_risk']
        })
    except Exception as e:
        print(f"Skipping {sym}: {e}")

results.sort(key=lambda r: r['harvest_score'], reverse=True)

print(f"\n{'Symbol':>6}  {'Harvest':>8}  {'Z-Score':>8}  {'Pctl':>5}  {'Risk':>5}  {'Strategy':>20}")
print("-" * 65)
for r in results:
    warn = "" if r['warnings'] else ""
    print(f"{r['symbol']:>6}  {r['harvest_score']:>8.2f}  {r['z_score']:>+8.2f}  {r['percentile']:>4.0f}%  {r['dealer_risk']:>5}  {r['best_strategy']:>20}{warn}")
Enter fullscreen mode Exit fullscreen mode

Example output:

Symbol   Harvest   Z-Score   Pctl   Risk              Strategy
-----------------------------------------------------------------
   SPY      0.78     +1.42    84%     22          iron_condor
  AAPL      0.71     +0.34    58%     18          iron_condor
   QQQ      0.65     +0.88    71%     35       short_strangle
  NVDA      0.61     +1.67    88%     41         jade_lizard
  TSLA      0.52     +2.14    93%     58       short_strangle ⚠
Enter fullscreen mode Exit fullscreen mode

TSLA has the richest raw VRP but the lowest harvest score. The API already factored in the dangerous gamma regime. Your dashboard shows what matters: not just "is premium rich" but "is it safe to harvest."


Using with AI Agents (MCP)

FlashAlpha provides an MCP server. Connect it to Claude, Cursor, or any MCP-compatible agent:

{
  "mcpServers": {
    "flashalpha": {
      "url": "https://lab.flashalpha.com/mcp",
      "headers": {
        "X-Api-Key": "YOUR_KEY"
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

The agent can query VRP data, interpret z-scores and strategy scores, and generate trade recommendations grounded in live data. The AI becomes the dashboard.


Why Not Compute VRP Yourself?

You can. Here's what it takes:

  1. Options chain data source ($200-2,500/mo)
  2. ATM IV calculation with skew and forward price handling
  3. Realized vol across 4 windows with split/dividend adjustment
  4. Rolling z-score and percentile (252-day lookback)
  5. Directional decomposition (25-delta wing extraction)
  6. Term structure fitting across DTE buckets
  7. Gamma regime conditioning (net GEX, dealer positioning) — itself a major project
  8. Strategy scoring model (VRP + skew + gamma + macro + event risk)
  9. Infrastructure to run intraday for your universe

That's 3-6 months of engineering. The VRP endpoint exists so you don't have to.


API Reference

GET /v1/vrp/{symbol}

Headers:
  X-Api-Key: YOUR_KEY

Response: Full VRP analysis (see field table above)

Rate limits:
  Free:   10 req/day
  Basic:  250 req/day
  Growth: 2,500 req/day
  Alpha:  Unlimited
Enter fullscreen mode Exit fullscreen mode

The VRP endpoint is available on all plans including the free tier for basic VRP data. Full strategy scores, GEX conditioning, and macro context require Growth or Alpha.

Related guides:

Top comments (0)