0
$\begingroup$

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[1], volvol = calibration[2]).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.

$\endgroup$
1
+50
$\begingroup$

Bloomberg swpm doesn't fit caps vol. It's quite complicated what is done by BBG and they offer a lengthy white paper, on HELP VCUB - "Documents" called the "Bloomberg Volatility Cube". Cap stripping is explained from P.9 onwards. In general, the cap vol shown in swpm is NOT a SABR fitted vol. The caplets are from VCUB (and actually swaption vols). Here are some (very basic) ideas of how Bloomberg does it. The main tab of SWPM shows the cap vol itself, which is simply the flat vol which, when used as optlet vol in cap pricing, gives the deal the same premium. It is neither a market quote, nor directly SABR fitted.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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