0
$\begingroup$

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

$\endgroup$

1 Answer 1

3
$\begingroup$

Here is an example: SABR Model Pricing Engine in Python QuantLib

Just a friendly reminder: readers are advised to search before asking questions

$\endgroup$
1
  • $\begingroup$ Hi David, Thank you for response. yes I have come across your example, it was very informative! i've added further details to my question for more clarity on what i meant. i've tried looking but perhaps i may have overlooked $\endgroup$ Commented Oct 17 at 9:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.