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.
No programming experience? With the Finance Toolkit MCP server, AI assistants such as Claude and ChatGPT can calculate the Volatility Surface for you. Just ask in plain English.
Calculate the Volatility Surface in Python
The Volatility Surface is available in the Options module of the open-source Finance Toolkit. Install it 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
get_volatility_surface 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.
Related Option Pricing
The Options module page introduces the module, and the sidebar lists all of its functions.