Calibrating a SABR Volatility Smile with QuantLib
Summary
The document presents a Python workflow for fitting a SABR volatility smile with QuantLib. The example supplies option strikes and implied volatilities, constructs a NoArbSabrInterpolatedSmileSection, sets initial values for alpha, beta, nu, and rho, and chooses which parameters to hold fixed. It also specifies a stopping criterion and a differential-evolution optimizer. The data are for options on a short-rate futures contract, and the code converts volatility percentages to decimals before calibration.
The reported failure appears when retrieving the fitted alpha: QuantLib raises an error that rho is outside its bounds, reporting a value of 0.99. The document does not provide a diagnosis or a corrected calibration, and its only response points readers to a separate example without reproducing it. As a result, it is useful for seeing the calibration interface and the kind of parameter-bound failure that can occur, but it does not establish which inputs, constraints, or data-quality checks would resolve this particular error.
Key ideas
- The example fits a SABR smile to market implied volatilities using QuantLib's interpolated smile section.
- The setup provides initial parameter guesses and allows beta to remain fixed while other parameters are fitted.
- The reported calibration raises an out-of-bounds error for rho when the fitted alpha is queried.
- The document does not explain the cause of the error or give a working correction.
Tags
Full text
# Using quantlib in python to optimise SABR parameters
# Using quantlib in python to optimise SABR parameters
I'm trying to use SABR to model volatility smile using QuantLib in python. Can someone provide an easy example of optimising SABR parameters using quantlib and returning a quantlib sabr smile?
I've added some further details and my attempted solution
Options data is as follows:
```
data = pd.DataFrame(
{
'date': dt.date(2024, 9, 10),
'contract_code': 'SFR',
'expiry_code': 'Z25',
'strike': [94.125, 94.25, 94.375, 94.5, 94.625, 94.75, 94.875, 95.0, 95.125, 95.25, 95.375, 95.5, 95.625, 95.75, 95.875, 96.0, 96.125,
96.25, 96.375, 96.5, 96.625, 96.75, 96.875, 97.0, 97.125, 97.25, 97.375, 97.5, 97.625, 97.75, 97.875, 98.0, 98.125, 98.25,
98.375, 98.5, 98.625, 98.75, 98.875, 99.0, 99.125, 99.25, 99.375, 99.5, 99.625, 99.75, 99.875, 100.0, 100.125],
'price': [0.01, 0.0125, 0.015, 0.0175, 0.02, 0.0225, 0.0275, 0.0325, 0.0375, 0.045, 0.0525, 0.0625, 0.0725, 0.085, 0.1025, 0.1225,
0.145, 0.1725, 0.205, 0.24, 0.28, 0.3225, 0.37, 0.425, 0.485, 0.5, 0.4475, 0.4, 0.355, 0.315, 0.2775, 0.245, 0.2175, 0.1925,
0.17, 0.15, 0.1325, 0.1175, 0.1025, 0.09, 0.0775, 0.0675, 0.0575, 0.05, 0.0425, 0.0375, 0.0325, 0.0275, 0.025],
'expiry_date': dt.date(2025, 12, 12),
'underlying': 97.2,
'discount': 0.954263,
'strike_rate': [0.05875, 0.0575, 0.05625, 0.055, 0.05375, 0.0525, 0.05125, 0.05, 0.04875, 0.0475, 0.04625, 0.045, 0.04375, 0.0425,
0.04125, 0.04, 0.03875, 0.0375, 0.03625, 0.035, 0.03375, 0.0325, 0.03125, 0.03, 0.02875, 0.0275, 0.02625, 0.025, 0.02375,
0.0225, 0.02125, 0.02, 0.01875, 0.0175, 0.01625, 0.015, 0.01375, 0.0125, 0.01125, 0.01, 0.00875, 0.0075, 0.00625, 0.005,
0.00375, 0.0025, 0.00125, 0.0, -0.00125],
'underlying_rate': 0.028,
'iv': [132.69826311096224, 132.6135998674864, 131.74391649530892, 130.2713933900565, 128.3101954353009, 125.93667550873326, 125.56317516483074,
124.40993955031576, 122.62840728809596, 121.96250429167836, 120.53097623805455, 119.77806107150022, 118.23800214789529, 117.10953251807356,
117.1802345710329, 117.15922895760573, 116.9829008238809, 117.36798298135997, 118.13524431159198, 118.49245653666745, 119.09935624508483,
119.271343264066, 119.62504006972723, 120.71994677387485, 121.93816832073068, 122.76110065088719, 123.53032918656201, 124.49442096239669,
125.08718288273084, 125.95250561358881, 126.50962001530628, 127.45924149981506, 128.91910012542854, 130.28270398837554, 131.6082404394893,
132.96538266595968, 134.43958655981677, 136.13652599810882, 137.12827877498975, 138.46836961840881, 139.08198492215723, 140.2211015214183,
140.6247832406773, 141.82579296748509, 142.33243969844253, 144.06330163737894, 145.25016082696825, 145.78344706531442, 148.339319620407],
'atm_vol': 122.76110065088719
}
)
def build_sabr_smile(data: pd.DataFrame):
date = pd.to_datetime(data['date']).dt.date[0]
ql_date = ql.Date.from_date(date)
ql.Settings.instance().evaluationDate = ql_date
ql_dcc = ql.Actual365Fixed()
expiry_time = ql.Date.from_date(pd.to_datetime(data['expiry_date']).dt.date[0])
forward_rate = data['underlying_rate'].values[0]
strike_prices = data['strike_rate'].values
atm_vol = data['atm_vol'].values[0]/100
market_vols = data['iv'].values/100
sabr_smile = ql.NoArbSabrInterpolatedSmileSection(
expiry_time,
forward_rate,
strike_prices,
True, # hasFloatingStrikes
atm_vol, # atmVolatility (using the at-the-money volatility)
market_vols,
0.4, # alpha (initial guess)
0.5, # beta (initial guess)
0.1, # nu (initial guess)
0.1, # rho (initial guess)
False, # isAlphaFixed
True, # isBetaFixed
False, # isNuFixed
False, # isRhoFixed
True, # vegaWeighted
ql.EndCriteria(1000, 100, 1e-6, 1e-6, 1e-6),
ql.DifferentialEvolution()
)
return sabr_smile
ql_sabr_smile = build_sabr_smile(clean_smile_data)
ql_sabr_smile.alpha()
```
i run into this error
```
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
~\AppData\Local\Temp\ipykernel_29140\4249249177.py in ?()
----> 1 ql_sabr_smile.alpha()
~\AppData\Local\miniconda3\envs\analytics\Lib\site-packages\QuantLib\QuantLib.py in ?(self)
13297 def alpha(self):
13298 r"""alpha(NoArbSabrInterpolatedSmileSection self) -> Real"""
> 13299 return _QuantLib.NoArbSabrInterpolatedSmileSection_alpha(self)
RuntimeError: rho (0.99) out of bounds
```
any help would be appreciated
## Answer by David Duarte (score 3)
https://quant.stackexchange.com/a/80910
Here is an example: SABR Model Pricing Engine in Python QuantLib
Just a friendly reminder: readers are advised to search before asking questionsShown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)
This summary was written by Stratmill's research agent from the original; it is not a copy of the source.