Just noticed after upgrading to the most recent version of Quantlib Python that the class ql.SabrSwaptionVolCube is now available. This is a very useful class in that it behaves in very much the same way as the now deprecated ql.SwaptionVolCube1 class and takes the same inputs (swaption ATM vol matrix, strike spreads, vol spreads ...etc) along with $\alpha,\beta,\nu,\rho$ vectors to return a SABR vol cube.

Now, to calibrate the ATM vols $\sigma_{N,ATM}$, we can take method 2 of this approach: i.e. fix $\alpha$ and set $\beta=0$ (assuming a Bachelier distribution of the forwards) and recursively perturb the $\alpha$ parameter by find the (smallest positive) root of the cubic polynomial $$\frac{β(β−2)T}{ 24F^{(2−2β)}}α^3 +\frac{ρβνT}{4F^{(1−β)}}α^2+(1+\frac{2−3ρ^2}{24}ν^2T)α−σ_{N,ATM}F^{−β}=0$$ via calibrating the $\nu,\rho$. However, this obviously works for the option expiries and swap tenors specified in the skew matrix (what the class refers to as "sparse parameters"). It does not calibrate $\sigma_{N,ATM}$ for expires and tenors not given in the skew matrix (what the class refers to as "dense parameters"). So, for example, let's say I input skew data for the subset of expiries 1m,3m,1y,5y,10y,30y on the subset of swap tenors 2y,5y,10y,30y then the above approach will return the correctly calibrated $\sigma_{N,ATM}$ for, say, 3m10y but not 3m15y (even though the ATM swaption vol matrix being supplied has the 15y tail).

My question is how does one achieve the ATM calibration for those tenors not in the skew matrix? One approach is to include every tail and expiry in the skew matrix from the whole ATM vol matrix but this is impractical as skew data is sparsely available in the market (that's the whole point of using SABR!). Ideally, what is it to be achieved is for the skew to be derived from the sparse parameters while the ATM vol coming from the ATM vol matrix.

In any case, one approach that does work is to supply the full set of expiries and tenors and fill the strike spreads with the "appropriate values" from sparse data (i.e. if the strike ATM+1% entry for the 3m10y is say +0.15% normals from the market then the same value should be entered for 3m15Y). If any Quantlib experts out there have a more intelligent solution to the above, or if I'm missing something in my approach, please do respond.

  • 1
    $\begingroup$ UPDATE: So it turns out ql.SabrSwaptionVolCube has an attribute called VolCubeAtmCalibrated which returns a (what looks like linearly inter/extrapolated) dense set of ATM smile sections (implied from the sparse set of sections inputted) for the full set of expiries/swap tenors in the swaption vol surface. Plugging this full skew dataset into a new instance of SabrSwaptionVolCube and then performing the above calibration gives exactly what's needed: a SABR vol cube with correctly calibrated ATM vols derived from a sparse set of market data. $\endgroup$
    – user35980
    May 14 at 22:40


Your Answer

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

Browse other questions tagged or ask your own question.