Volatility Surface
Calibrate an arbitrage-checked implied volatility surface across multiple expiries, by fitting a raw SVI (Stochastic Volatility Inspired, Gatheral 2004) curve to the market-implied smile (see get_implied_volatility) at each expiry, and checking the fitted surface for calendar-spread arbitrage.
A single per-expiry smile only tells you the shape of the market’s volatility skew at that one maturity. Stitching several calibrated SVI slices together instead gives a full surface, which is what is needed to price/interpolate options at maturities or strikes that don’t trade directly, and to check for term-structure inconsistencies (see Notes).
Before fitting, strikes whose market-implied volatility is a statistical outlier relative to its neighbors (a common symptom of a stale or wide-bid/ask illiquid quote) are dropped via a median-absolute-deviation filter, since a single bad quote can otherwise dominate the least-squares SVI fit for that whole expiry.
See: Gatheral, J. (2004), “A parsimonious arbitrage-free implied volatility parameterization with application to the valuation of volatility derivatives”, and Gatheral, J., & Jacquier, A. (2014), “Arbitrage-free SVI volatility surfaces”, Quantitative Finance, 14(1), 59-71.
Also known as: SVI surface, implied volatility surface.
Notes: A warning is logged (not raised) if the fitted surface has any calendar-spread arbitrage violations, i.e. total implied variance decreasing with time to expiration at some log-moneyness – this reflects genuine inconsistency in the underlying market quotes across expiries, not a fitting error, and is only checked, not corrected.
Volatility Surface in Python
Install the Finance Toolkit with:
pip install financetoolkit -U
Then call get_volatility_surface as shown below.
from financetoolkit import Toolkit
toolkit = Toolkit(["AAPL"], api_key="FINANCIAL_MODELING_PREP_KEY")
volatility_surface = toolkit.options.get_volatility_surface(number_of_expirations=3)
volatility_surface.loc["AAPL"]
Which returns:
| Strike Price | 2026-08-05 | 2026-08-07 |
|---|---|---|
| 277.5 | 0.5647 | nan |
| 285 | 0.5235 | nan |
| 287.5 | 0.5136 | nan |
| 290 | 0.5057 | 0.4245 |
| 292.5 | 0.5001 | 0.4166 |
| 295 | 0.4968 | 0.4098 |
| 297.5 | 0.4958 | 0.4043 |
| 300 | 0.4971 | 0.4001 |
Parameters
The function accepts the following parameters:
- expiration_dates (list[str] | None, optional): The expiration dates to
fit the surface over. Defaults to None, meaning the first
number_of_expirationsavailable expiration dates. - put_option (bool, optional): Whether to use put options instead of call options. Defaults to False.
- risk_free_rate (float, optional): The risk free rate to use for the calculation. Defaults to None which means it will use the current risk free rate.
- dividend_yield (float, optional): The dividend yield to use for the calculation. Defaults to None which means it will use the dividend yield as obtained through annual historical data.
- number_of_expirations (int, optional): The number of near-term
expiration dates to fit when
expiration_datesis not given. Defaults to 6. - outlier_threshold (float, optional): The number of median absolute deviations from the median implied volatility beyond which a quote is treated as an outlier and dropped before fitting. Defaults to 5.0.
- rounding (int | None, optional): The number of decimals to round the results to. Defaults to None.