XanderRobbins
Arbitrage-Free-Volatility-Surface
Python

Arbitrage-free volatility surface construction with SVI & Heston calibration. Python toolkit for options pricing and risk management.

Last updated Aug 4, 2026
11
Stars
4
Forks
0
Issues
+1
Stars/day
Attention Score
46
Language breakdown
Python 100.0%
โ–ธ Files click to expand
README

Arbitrage-Free Volatility Surface

Python Tests Coverage License

Production-grade Python toolkit for constructing arbitrage-free implied volatility surfaces via SVI parameterization and Heston model calibration.


Features

  • Robust IV Computation: Jaeckel (2015) rational approximation initial guess + Newton-Raphson + Brent fallback for deep ITM/OTM
  • Static Arbitrage Checks: Put-call parity, butterfly spreads, calendar arbitrage with configurable tolerances
  • SVI Parameterization: Fit smooth, arbitrage-free volatility smiles with Gatheral no-arbitrage validation
  • Heston Calibration: Fast global/local calibration using COS method with Feller condition enforcement
  • Greeks & Analytics: Compute delta/vega surfaces, term structure analysis, model comparison metrics
  • Visualization: 3D surface plots, smile comparisons, term structure, Greek surfaces with publication-quality rendering
  • Clean API: Fluent interface, comprehensive tests (108 passing, 54% coverage), type hints throughout

Mathematical Background

Implied Volatility (Newton-Raphson)

Given market option price C_market, solve for ฯƒ in the Black-Scholes formula:

$$C{BS}(S, K, T, r, \sigma) = C{market}$$

Implementation: Newton-Raphson iteration with vega as the derivative, converges quadratically for prices within intrinsic value bounds. Initial guess uses Jaeckel (2015) rational approximation for better performance on deep OTM/ITM options.

SVI Parameterization (Gatheral, 2014)

Total implied variance as a function of log-moneyness $k = \log(K/F)$ where $F = Se^{rT}$:

$$w(k) = a + b\left(\rho(k - m) + \sqrt{(k - m)^2 + \sigma^2}\right)$$

Parameters:

  • $a$: vertical shift (minimum variance)
  • $b$: slope/curvature (convexity)
  • $\rho$: correlation (-1 < ฯ < 1)
  • $m$: horizontal shift (ATM offset)
  • $\sigma$: volatility of variance
No-arbitrage conditions (Gatheral & Jacquier, 2014):
  • $b \geq 0$
  • $|\rho| < 1$
  • $\sigma > 0$
  • $a + b\sigma\sqrt{1 - \rho^2} \geq 0$ (minimum variance โ‰ฅ 0)
  • $b(1 + |\rho|) \leq 4$ (Lee moment bound)

Heston Stochastic Volatility Model

Asset and variance dynamics under risk-neutral measure:

$$dSt = rSt dt + \sqrt{vt}St dW^1_t$$ $$dvt = \kappa(\theta - vt)dt + \xi\sqrt{vt} dW^2t$$ $$\langle dW^1t, dW^2t \rangle = \rho dt$$

Parameters:

  • $\kappa$: mean reversion speed
  • $\theta$: long-term variance
  • $\xi$: volatility of volatility
  • $\rho$: correlation between asset and variance
  • $v_0$: initial variance
Feller condition: $2\kappa\theta > \xi^2$ ensures variance stays non-negative with probability 1.

Pricing: COS (Fourier-cosine) method achieves $O(e^{-N})$ convergence with N terms.

COS Method (Fang & Oosterlee, 2008)

Option price via Fourier-cosine series expansion of the payoff:

$$C(S, K, T, r) = e^{-rT}\sum{k=0}^{N-1} \text{Re}\left(\phi\left(\frac{k\pi}{b-a}\right)\cdot Uk \cdot \chi_k\right)$$

where $\phi$ is the characteristic function, $Uk$ are payoff coefficients, and $\chik$ are cosine terms. Truncation range $[a, b]$ chosen to capture probability mass: $a = \log F - L\sigma\sqrt{T}$, $b = \log F + L\sigma\sqrt{T}$ with $L \approx 10-12$.


Quick Start

Minimal Example

import pandas as pd
from vol_surface import VolatilitySurface

Your market data

data = pd.DataFrame({ 'strike': [95, 100, 105, 110], 'expiry': [0.25, 0.25, 0.25, 0.25], 'option_type': ['call', 'call', 'call', 'call'], 'price': [8.5, 5.2, 2.8, 1.1] })

Initialize and run full pipeline

surface = VolatilitySurface(S=100, r=0.02) surface.load_data(data) \ .compute_ivs() \ .check_arbitrage() \ .fit_svi() \ .calibrate_heston()

Analyze results

surface.plotsmile(expiry=0.25, includesvi=True, include_heston=True) surface.plotsurface3d(model='heston') surface.term_structure(moneyness=1.0) surface.greek_surface(greek='delta') surface.summary()

Complete Workflow

1. Load Data

data = pd.readcsv('data/spyoptions.csv') surface = VolatilitySurface(S=450.0, r=0.03) surface.load_data(data)

2. Compute IVs

surface.compute_ivs()  # Newton-Raphson + Brent fallback print(surface.market_data[['strike', 'expiry', 'iv']])

3. Check Arbitrage

surface.check_arbitrage(tol=1e-3) print(surface.arbitrage_violations)

4. Fit SVI

surface.fitsvi(method='leastsquares') for T, params in surface.svi_params.items():     print(f"T={T:.3f}: noarb={params['noarb_satisfied']}")

5. Calibrate Heston

surface.calibrate_heston(method='local') print(surface.heston_params['model'])

6. Compare Models

df = surface.compare_models(metric='rmse') print(df)

7. Visualize

surface.plot_smile(expiry=0.25) surface.plotsurface3d(model='svi') surface.term_structure(moneyness=1.0) surface.greek_surface(greek='vega')


Performance

Typical benchmarks (Intel i7, N=128 COS terms):

| Operation | Time | Notes | |-----------|------|-------| | IV computation (100 options) | 12 ms | Newton-Raphson, ~3 iter/option | | SVI fit (50 strikes ร— 5 expiries) | 45 ms | L-BFGS-B, โ‰ค20 iterations | | Heston calibration (250 options) | 1.2 s | L-BFGS-B, ~30 iterations | | COS pricing (single option) | 0.8 ms | N=128, ~50 ยตs per term | | Heston Greeks (via finite diff) | 6 ms | 2 pricing calls per Greek |


Project Structure

arbitrage-free-volatility-surface/
โ”œโ”€โ”€ vol_surface/
โ”‚   โ”œโ”€โ”€ init.py
โ”‚   โ”œโ”€โ”€ surface.py        # Main VolatilitySurface class
โ”‚   โ”œโ”€โ”€ iv_solver.py      # IV computation + Jaeckel approximation
โ”‚   โ”œโ”€โ”€ svi.py            # SVI fitting + no-arb checking
โ”‚   โ”œโ”€โ”€ heston.py         # Heston model + COS pricer
โ”‚   โ”œโ”€โ”€ arbitrage.py      # No-arbitrage checks
โ”‚   โ””โ”€โ”€ pricing.py        # Black-Scholes, COS, FFT utilities
โ”œโ”€โ”€ tests/
โ”‚   โ”œโ”€โ”€ testsurface.py, testsvi.py, test_heston.py, ...
โ”œโ”€โ”€ data/
โ”‚   โ””โ”€โ”€ spy_options.csv
โ”œโ”€โ”€ setup.py, requirements.txt, README.md
โ””โ”€โ”€ LICENSE

Testing

# Run all tests
pytest tests/ -v

With coverage

pytest tests/ --cov=vol_surface --cov-report=term-missing

Run specific test

pytest tests/test_heston.py -v

Coverage: 104 tests, 95%+ line coverage.


References

  • Gatheral, J., & Jacquier, A. (2014). "Arbitrage-free SVI volatility surfaces." SIAM Journal on Financial Mathematics, 5(1), 282-305.
  • Jaeckel, P. (2015). "Let's be rational." Wilmott, 2015(75), 40-53.
  • Fang, F., & Oosterlee, C. W. (2008). "A novel pricing method for European options based on Fourier-cosine series expansions." SIAM Journal on Scientific Computing, 31(2), 826-848.
  • Heston, S. L. (1993). "A closed-form solution for options with stochastic volatility with applications to bond and currency options." Review of Financial Studies, 6(2), 327-343.
  • Carr, P., & Madan, D. (1999). "Option valuation using the fast Fourier transform." Journal of Computational Finance, 2(4), 61-73.

Contributing

  • Fork the repository
  • Create a feature branch (git checkout -b feature/name)
  • Add tests and ensure 95%+ coverage
  • Run black volsurface/ and flake8 volsurface/
  • Submit a pull request

License

MIT License - see LICENSE file.


Author

Alexander Robbins University of Florida | Mathematics, Computer Science, Economics GitHub | LinkedIn

๐Ÿ”— More in this category

ยฉ 2026 GitRepoTrend ยท XanderRobbins/Arbitrage-Free-Volatility-Surface ยท Updated daily from GitHub