Skip to content

rohankrgupta/Variance-Risk-Premium

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Variance Risk Premium — Phase 1

A Python research pipeline that documents the variance risk premium (VRP) on SPX / SPY using publicly available VIX and price data (2010-2024), constructs a systematic short-vol signal with a regime-based tail-risk filter, and honestly measures both its predictive content and the gap between signal quality and tradable P&L.

Thesis. Implied vol (VIX) systematically exceeds subsequently-realized 21-day vol — the empirical variance risk premium. The signal has predictive content for forward RV, but a naïve raw-VIX P&L proxy is dominated by single-day gap losses, which is itself the economically interesting finding: short-vol is insurance, and the premium is compensation for the tail.

This is Phase 1 of a three-phase build; Phase 2 upgrades to real SPX option data and delta-hedged P&L decomposition. See § Roadmap.


Results at a glance

Empirical VRP facts (2010-2024, 3,480 valid days after warm-up):

Fact Value
Mean forward VRP (IV − fwd 21d RV) +3.57 vol pts
Fraction of days with forward VRP > 0 83.5%
Mean contemporaneous VRP (IV − trailing RV) +3.57 vol pts

Consistent with Bollerslev, Tauchen & Zhou (2009): the premium is real, positive, and economically sized.

Backtest headline metrics (signal-quality proxy — see caveats):

Metric Value
Total return -616.37%
Annualized return -41.18%
Annualized vol 89.85%
Sharpe ratio -0.46
Max drawdown -717.01%
Hit rate (in-market) 52.09%
Number of trades 48
Avg holding (days) 25.9
% time in market 32.98%
Info coefficient 0.068

Key reads:

  1. Hit rate 52% — the classic short-vol profile: roughly half the in-market days make money. The arithmetic mean is dominated by a handful of gap days.
  2. IC = +0.068 — small but positive; VRP_z has real predictive content for forward vol direction (consistent with published VRP literature).
  3. Negative headline Sharpe — comes entirely from the raw-VIX P&L proxy over-weighting single-day spikes (Feb 5 2018: spot VIX +115.6% in one day, so a -1 short-vol unit takes a -115.6% daily "return"). A tradable instrument (VXX, VIX futures) moves much less per day and collects roll carry; see § Limitations.

Plots

All five plots are reproducible by running notebooks/analysis.ipynb.

1. Implied vs Realized Volatility

The IV line sits above RV most of the time. The gap is the premium.

IV vs RV

2. VRP with ±1σ bands and regime-filter kill zones

VRP with 252-day rolling mean ± 1σ. Red-shaded strips are days where VIX ≥ 30 forced the position flat.

VRP with regimes

3. Equity curve

Arithmetic cumulative P&L of the short-vol signal proxy. Named drops annotated.

Equity curve

4. Drawdown

Distance below running-max equity. Visualizes the gap-risk story.

Drawdown

5. Predictive content — VRP_z vs forward RV change

Hexbin of VRP_z vs change in RV over the next 21 days, with an OLS best-fit line. Negative slope ⇒ high VRP_z is followed by falling RV — the economic content of the premium.

Predictive scatter


Methodology

Data

  • SPY (split/dividend-adjusted close) and ^VIX from Yahoo Finance via yfinance, cached to data/raw.parquet.
  • Period: 2010-01-01 to 2024-12-31 (covers 2018 Volmageddon + 2020 COVID).
  • Daily frequency, business-day aligned, inner-joined on dates where both series print.

Signals (src/vrp/signals.py)

  • log_ret = ln(SPY_t / SPY_{t-1})
  • RV_t = std(log_ret_{t-20:t}) · √252 (21-day trailing, annualized)
  • IV_t = VIX_t / 100 (vol-point to decimal)
  • VRP_t = IV_t − RV_t (contemporaneous, real-time)
  • VRP_z_t = (VRP_t − roll_mean_{252}) / roll_std_{252}
  • fwd_RV_t = std(log_ret_{t+1:t+21}) · √252 (forward, ex-post eval only)
  • fwd_VRP_t = IV_t − fwd_RV_t (the academic VRP)

Signal rule (src/vrp/backtest.py)

Stateful rule, decision at close of day t, held from open of day t+1:

if  VIX_t ≥ 30                   →  position = 0   (regime kill switch)
elif VRP_z_t >  +1.0              →  position = -1  (short vol)
elif VRP_z_t <  -0.5              →  position =  0  (flat)
else                              →  carry previous position

Single 1-day execution lag is applied as a .shift(1) on the position series before multiplying by returns — the only place look-ahead could sneak in, tightly guarded and commented in code.

P&L proxy

daily_pnl_t = position_eff_t · vix_ret_t

where position_eff_t = position_{t-1} (lag) and vix_ret_t = VIX.pct_change(). A short (-1) position LOSES when VIX rises. This is the signs-correct form; the spec text uses position · (-vix_ret) which double-negates with the -1 encoding — we flag this directly in the code comments.

Metrics

See compute_metrics in src/vrp/backtest.py for exact definitions. Sharpe is annualized via √252. The IC is corr(VRP_z_t, −VIX.pct_change(21)_{t+21}) — the degree to which high VRP predicts subsequent VIX declines.


Stress-period behavior

The two canonical short-vol disaster windows:

Window Short days Flat days Max VIX Total P&L Worst day
2018 Volmageddon (Jan 15 – Mar 15) 7 35 37.3 -155.2% -115.6%
COVID crash (Feb 15 – Apr 30) 1 51 82.7 -42.1% -42.1%

Read: The regime filter worked partially. It held us flat for 83% and 98% of each window respectively, but could not prevent the single-day gap losses — by the time VIX closes above 30, damage is done intraday. This is the defining weakness of reactive regime filters and is the primary motivation for the Phase 2 upgrades.


Limitations

Listed explicitly, because this is where interview questions live.

  1. VIX as IV proxy. VIX is a model-free, variance-swap-style 30-day measure. True ATM SPX implied vol differs by the volatility smile and term structure — our VRP is therefore a variance-risk-swap-ish premium, not a pure Black-Scholes IV − RV gap. Material but not directional.
  2. Raw VIX return as P&L proxy. VIX is not tradable. A real short-vol position would use VIX futures (roll carry applies) or VXX (an inverse- VIX ETF with roll decay). Both have smaller daily moves than spot VIX, so the proxy over-weights tail days and the headline Sharpe underestimates a tradable version of the signal. This is the single biggest simplification — Phase 2 replaces it.
  3. No transaction costs, no slippage, no financing, no margin.
  4. No position sizing / vol targeting. Position is {0, −1}. A real strategy would scale by inverse-vol or target a fixed ex-ante vol.
  5. Single asset. SPX only. A real vol book diversifies across NDX, RUT, and single names.
  6. Regime filter is reactive. Threshold on close-of-day VIX; cannot anticipate an intraday spike. Phase 2 explores HMM / vol-of-vol predictive filters.
  7. Look-ahead hygiene. All decisions at time t use only information available at close of t; execution lag of 1 day applied before P&L. The fwd_VRP column is tagged and used only for ex-post evaluation, never for signal generation.

Reproducing

# 1. Clone and enter
git clone https://github.com/rohankrgupta/Variance-Risk-Premium.git
cd Variance-Risk-Premium

# 2. Env
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt

# 3. Run the notebook end-to-end (fetches + caches data on first run)
jupyter nbconvert --to notebook --execute notebooks/analysis.ipynb --output analysis.ipynb

All plots land in figures/; metrics land in figures/metrics.md.


Repo layout

src/vrp/
    data.py       # yfinance fetch + parquet cache + sanity checks
    signals.py    # RV, IV, VRP, z-score, forward RV, build_features
    backtest.py   # positions, P&L sim with lag, metrics dataclass
notebooks/
    analysis.ipynb   # thin reporting wrapper around the package
data/
    raw.parquet      # cached SPY+VIX (gitignored; regenerable)
figures/             # png outputs + metrics.md
requirements.txt
README.md

Roadmap

  • Phase 2 (20-30 hrs): Replace VIX with a 30-day constant-maturity ATM IV from the SPX option chain; add a delta-hedged short-straddle simulation with theta / gamma / vega P&L attribution.
  • Phase 3 (40+ hrs, optional, one of): dispersion trading (index vs single-name IV), SVI surface fitting + arb, or a C++ vol-engine re-implementation of the Phase 2 hot path.

References

  1. Bollerslev, T., Tauchen, G., & Zhou, H. (2009). Expected Stock Returns and Variance Risk Premia. Review of Financial Studies, 22(11), 4463–4492.
  2. CBOE. VIX White Paper: The CBOE Volatility Index (VIX)®. Chicago Board Options Exchange.
  3. Sinclair, E. (2013). Volatility Trading (2nd ed.), Chapter 1. Wiley.

Resume bullet (Phase 1 deliverable)

Variance Risk Premium Study — SPX (2010–2024) · Apr 2026

  • Built a Python research pipeline (yfinance, pandas, matplotlib) isolating the variance risk premium on SPX; documented a +3.6 vol-point mean with 83.5% positive-day frequency and information coefficient +0.07 against forward realized vol change, consistent with Bollerslev–Tauchen–Zhou.
  • Implemented a z-score signal with VIX-level regime kill switch; backtested across 2018 Volmageddon and 2020 COVID stress windows, diagnosing that reactive filters neutralize trend regimes but cannot prevent single-day gap losses — motivating the Phase 2 upgrade to real option-chain IV and delta-hedged P&L attribution.

About

Variance Risk Premium Analysis on SPX from 2010–2024

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors