I want to make sure that I can properly use SABR model on 1-period interest rate options, i.e. caplets, therefore I attempted to get lognormal volatilities for 4%, 6%, ATM, 8%, 10% strikes for 3Mx6M caps on RUB MOSKP3 index with IRS ICVS179 RUB as both discounting and projecting curve and fit SABR model to corresponding volatility smile. I use pysabr library for SABR model implementation in python.
The Bloomberg data is as follows:
capStrikes = [0.04, 0.06, 0.0745136, 0.08, 0.10] capVols = [23.52/100.00, 16.24/100.00, 18.35/100.00, 20.17/100.00, 26.19/100.00] IRS curve = [1WK 0.0713, 1MO 0.0722, 2MO 0.0732, 3MO 0.0747, 6MO 0.0753, 1YR 0.0789]
I think this is all that I need to more or less fit the model to Bloomberg quotes. This is an example of the interface that I'm trying to use:
from pysabr import Hagan2002LognormalSABR # Forward = 2.5%, Shift = 3%, ATM Normal Vol = 40bps # Beta = 0.5, Rho = -20%, Volvol = 0.30 sabr = Hagan2002LognormalSABR(f=0.025, shift=0.03, t=1., v_atm_n=0.0040, beta=0.5, rho=-0.2, volvol=0.30) k = 0.025 sabr.lognormal_vol(k) * 100 # returns 7.27 sabr.normal_vol(k) *1e4 # returns 40
However I can't properly fit the smile. Please see my code below:
import pysabr from pysabr import Hagan2002LognormalSABR as LNsabr import numpy as np testStrikes = np.array([0.04, 0.06, 0.0745136, 0.08, 0.10]) testVols = np.array([23.52/100.00, 16.24/100.00, 18.35/100.00, 20.17/100.00, 26.19/100.00]) forward_3m_6m = (1/0.25) * (-1 + (1+0.0753*0.5) / (1+0.0747*0.25)) calibration = LNsabr(f = forward_3m_6m, shift = 0, t = 0.5, beta = 0.5).fit(testStrikes, testVols) smile =  for strike in testStrikes: smile.append(LNsabr(f = forward_3m_6m, shift = 0, t = 0.5, v_atm_n = 136.75/10000.00, beta = 0.5, rho = calibration, volvol = calibration).lognormal_vol(strike) * 100.00) print(smile)
The SABR implied lognormal results are pretty far away from the initial volatilities.
[21.331968636265458, 19.365310362267866, 18.36618998284387, 18.04637126862935, 17.06699480954601]
What am I doing wrong? Why my results are so far from the quotes? I will be very grateful if someone would show me a proper way to fit the Bloomberg smile using pysabr.
EDIT: I'm looking for either a fix to my code which will do a better SABR fit or a qualitative explanation why the obtained fit is that bad. Alternatively one can show me an example of fitting a pysabr to any other Bloomberg SWPM cap/floor smile given 4-5 strikes and their volatilities.