9.0 KiB
This notebook calibrates the SABR (Stochastic Alpha, Beta, Rho) model to market data, specifically for the SPY options expiring in January 2026. It fetches option chains, extracts call and put options, and calculates the mid-prices. The SABR model is used to fit implied volatilities and generate a volatility smile. Finally, the code uses the Black model to calculate theoretical option prices and compares them to market prices. This is useful in practice for financial modeling and option pricing.
import pandas as pd import numpy as np import matplotlib.pyplot as plt from openbb_terminal.sdk import openbb from pysabr import Hagan2002LognormalSABR from pysabr import hagan_2002_lognormal_sabr as sabr from pysabr.black import lognormal_call
Set plot style and parameters for visualizations
plt.style.use("default") plt.rcParams["figure.figsize"] = [5.5, 4.0] plt.rcParams["figure.dpi"] = 140 plt.rcParams["lines.linewidth"] = 0.75 plt.rcParams["font.size"] = 8
Define the symbol and expiration date for the SPY options
symbol = "SPY" expiration = "2026-01-16"
Fetch the option chains for the given symbol from Yahoo Finance
spy = openbb.stocks.options.chains(symbol, source="YahooFinance")
Extract call options and calculate their mid-prices
calls = spy[spy.optionType == "call"] jan_2026_c = calls[calls.expiration == expiration].set_index("strike") jan_2026_c["mid"] = (jan_2026_c.ask + jan_2026_c.ask) / 2
Extract put options and calculate their mid-prices
puts = spy[spy.optionType == "put"] jan_2026_p = puts[puts.expiration == expiration].set_index("strike") jan_2026_p["mid"] = (jan_2026_p.ask + jan_2026_p.ask) / 2
Extract strikes and implied volatilities for the call options
strikes = jan_2026_c.index vols = jan_2026_c.impliedVolatility * 100
Find the forward price using put-call parity and calculate the time to expiration
f = ( (jan_2026_c.mid - jan_2026_p.mid) .dropna() .abs() .sort_values() .index[0] )
Calculate the time fraction until expiration in years
t = (pd.Timestamp(expiration) - pd.Timestamp.now()).days / 365
Set the beta parameter for the SABR model
beta = 0.5
Initialize the SABR model with the forward price, time to expiration, and beta
sabr_lognormal = Hagan2002LognormalSABR( f=f, t=t, beta=beta )
Fit the SABR model to the market implied volatilities
alpha, rho, volvol = sabr_lognormal.fit(strikes, vols) print(alpha, rho, volvol)
Calculate calibrated volatilities using the SABR model for each strike price
calibrated_vols = [ sabr.lognormal_vol(strike, f, t, alpha, beta, rho, volvol) * 100 for strike in strikes ]
Plot the volatility smile generated by the SABR model and compare it to market implied volatilities
plt.plot( strikes, calibrated_vols )
plt.xlabel("Strike") plt.ylabel("Volatility") plt.title("Volatility Smile") plt.plot(strikes, vols) plt.show()
Calculate theoretical option prices using the Black model for each strike price and calibrated volatility
black_values = [] for strike, calibrated_vol in zip(strikes.tolist(), calibrated_vols): black_value = lognormal_call( strike, f, t, calibrated_vol / 100, 0.05, cp="call" ) black_values.append(black_value)
Create a DataFrame to compare Black model values with market mid-prices and plot the differences
option_values = pd.DataFrame( { "black": black_values, "market": jan_2026_c.mid }, index=strikes )
(option_values.black - option_values.market).plot.bar()
PyQuant News is where finance practitioners level up with Python for quant finance, algorithmic trading, and market data analysis. Looking to get started? Check out the fastest growing, top-selling course to get started with Python for quant finance. For educational purposes. Not investment advise. Use at your own risk.
