A professional-grade options pricer and risk analytics engine built for quantitative finance roles. Implements three independent pricing methods, SVI volatility surface fitting, Gamma Exposure analysis, and portfolio stress testing — all accessible through an interactive Streamlit dashboard.
08_VolEngine_X/
├── engine/ Mathematical core (3 independent pricers)
│ ├── pricer_bs.py Black-Scholes + Newton-Raphson IV solver
│ ├── pricer_fdm.py Crank-Nicolson Finite Difference Method
│ ├── pricer_mc.py Vectorized Monte Carlo (Euler / Milstein)
│ └── greeks.py Analytical + numerical Greeks (bumping)
├── models/
│ └── smile_fit.py SVI volatility surface + arbitrage detection
├── risk/
│ ├── gex_analyzer.py Gamma Exposure profile + Gamma Flip
│ └── stress_test.py Portfolio P&L stress scenarios
├── data_feed/
│ ├── polygon_client.py Polygon.io REST client (with caching)
│ └── yfinance_fallback.py yfinance fallback (no key required)
├── tests/ Pytest test suite (25+ assertions)
├── app.py Streamlit dashboard (4 tabs)
└── main.py CLI entry point
Closed-form European option pricing:
C = S·N(d₁) − K·e^{−rT}·N(d₂)
d₁ = [ln(S/K) + (r + σ²/2)·T] / (σ√T)
d₂ = d₁ − σ√T
IV Solver — Newton-Raphson with bisection fallback:
σ_{n+1} = σ_n + (V_market − V_BS(σ_n)) / Vega(σ_n)
Initial guess: Brenner-Subrahmanyam approximation σ₀ ≈ √(2π/T) · V/S.
Solves the Black-Scholes PDE on a discrete (S, t) grid.
The Crank-Nicolson scheme averages the explicit (forward) and implicit (backward) operators:
M_imp · V^{j+1} = M_exp · V^j + boundary_rhs
M_imp = I − 0.5·dt·L
M_exp = I + 0.5·dt·L
where L is the tridiagonal Black-Scholes spatial operator with coefficients:
αᵢ = ¼·dt·(σ²i² − r·i)
βᵢ = −½·dt·(σ²i² + r)
γᵢ = ¼·dt·(σ²i² + r·i)
Uses scipy.sparse tridiagonal matrices — the standard bank implementation (O(N) solve per time step vs O(N³) for dense matrices).
Key properties:
- Unconditionally stable (no CFL condition)
- Second-order accurate in both space and time:
O(dS² + dt²) - Supports American early-exercise constraint
Verified accuracy vs. BS analytical:
| Case | BS Price | FDM Price | Rel. Error |
|---|---|---|---|
| ATM Call S=100 K=100 T=1Y σ=20% | 10.4506 | 10.4534 | 0.027% |
| OTM Call S=100 K=110 T=1Y σ=20% | 5.8594 | 5.8621 | 0.046% |
| ATM Put S=100 K=100 T=1Y σ=20% | 5.5735 | 5.5761 | 0.047% |
| High Vol S=100 K=100 T=3M σ=30% | 8.9543 | 8.9575 | 0.036% |
Milstein scheme (second-order vs. first-order Euler):
S_{T} = S · exp[(r − σ²/2)·T + σ·dW + ½·σ²·(dW² − T)]
Antithetic variates: pairs each path Z with −Z, halving variance without extra computation:
Z_av = np.concatenate([Z, -Z]) # zero extra RNG callsFully vectorized — no Python loops. All n_paths paths computed simultaneously as NumPy array operations.
Analytical BS Greeks:
| Greek | Formula |
|---|---|
| Δ Delta | N(d₁) for calls, N(d₁)−1 for puts |
| Γ Gamma | N'(d₁) / (S·σ·√T) |
| ν Vega | S·N'(d₁)·√T (per 1 vol point) |
| Θ Theta | −(S·N'(d₁)·σ)/(2√T) − r·K·e^{−rT}·N(d₂) (per day) |
| ρ Rho | K·T·e^{−rT}·N(d₂) (per 1%) |
Numerical bumping for FDM/MC pricers:
Δ = [V(S+ε) − V(S−ε)] / (2ε) central difference
Γ = [V(S+ε) − 2V(S) + V(S−ε)] / ε² second derivative
ν = [V(σ+ε) − V(σ−ε)] / (2ε·100) per vol point
Gatheral's Raw SVI parametrization:
w(k) = a + b · [ρ·(k−m) + √((k−m)² + σ²)]
where k = log(K/F) is log-moneyness and w = σ²_IV · T is total variance.
One SVI slice is fitted per expiry via weighted least-squares (ATM-weighted). The surface interpolates linearly in total-variance space between expiries.
Arbitrage detection:
- Butterfly:
w''(k) ≥ 0everywhere (convexity check) - Calendar spread:
w(k, T₂) ≥ w(k, T₁)forT₂ > T₁ - Mispricing scanner: flags options where
|IV_market − IV_surface| > threshold
GEX = Σ [ |Γ| × OI × 100 × S² ]
Dealer sign convention:
- Short calls → negative GEX (dealers are short gamma, amplify moves)
- Long puts → positive GEX (dealers are long gamma, dampen moves)
Gamma Flip: the spot level where cumulative GEX crosses zero.
- Above flip: positive GEX → mean-reversion regime
- Below flip: negative GEX → trend / reflexive regime
streamlit run app.py| Tab | Content |
|---|---|
| 🌐 Volatility Surface | Interactive 3D SVI surface + mispricing scanner |
| ⚡ Option Pricer | BS / FDM / MC comparison + Greeks cards + PCP check |
| 📐 GEX Analysis | GEX bar chart, Gamma Flip, regime banner, key levels |
| 🔥 Stress Test | Portfolio builder, P&L heatmap, max-loss scenario |
pytest tests/ -v| Test | Assertion |
|---|---|
| FDM vs BS | Relative error < 0.5% for 7 reference cases |
| MC vs BS | Result within 3σ confidence interval |
| IV round-trip | iv(bs_price(σ)) = σ to 1e-5 precision |
| Put-call parity | Verified for BS, FDM, and MC |
| Greek accuracy | Numerical bumping < 1% error vs. analytical |
pip install -r requirements.txt
# Dashboard
streamlit run app.py
# CLI — SPY analysis (yfinance, no key needed)
python main.py SPY
# Run tests
pytest tests/ -v| Library | Version | Purpose |
|---|---|---|
| numpy | ≥1.24 | Vectorized numerics |
| scipy | ≥1.10 | Sparse linear algebra, optimization |
| pandas | ≥2.0 | Data manipulation |
| streamlit | ≥1.32 | Interactive dashboard |
| plotly | ≥5.18 | 3D surface, heatmaps |
| yfinance | ≥0.2.36 | Free options chain fallback |
| requests | ≥2.31 | Polygon.io REST client |
| pytest | ≥7.4 | Test suite |
Tom Rolland — Quantitative Developer
sevenson506@gmail.com
Built as part of a quantitative finance portfolio project.