Pricing Swaptions with Negative Rates Using Normal Volatility
Summary
The note addresses a QuantLib pricing error that occurs when a Black swaption engine is used with a negative forward rate. The error indicates that the Black model requires the forward rate, after any displacement, to be positive; with no displacement and a negative forward, that condition is not met.
The answer demonstrates using the Bachelier swaption engine with a normal volatility quote instead. This model supports negative rates and provides an alternative for valuing swaptions when the Black model’s positivity requirement is unsuitable. The example uses a vanilla EUR swaption and a flat yield curve, but the note does not compare the models, explain how to choose or calibrate normal volatility, or discuss other possible remedies such as a shifted Black model.
Key ideas
- The standard Black swaption model requires a positive forward rate after displacement.
- A negative forward can trigger a pricing error when Black is used without a suitable displacement.
- The Bachelier model supports negative rates and can be used through QuantLib’s Bachelier swaption engine.
- The example does not explain volatility calibration or compare alternative model choices.
Tags
Full text
# Evaluating swaptions with negative interest rates
# Evaluating swaptions with negative interest rates
Does anyone know if it is possible to evaluate swaptions with negative interest rates with Quantlib?
```
blackEngine = ql.BlackSwaptionEngine(discountingTermStructure, ql.QuoteHandle(ql.SimpleQuote(vol)))
swaption.setPricingEngine(blackEngine)
swaptionnpv = swaption.NPV()
```
My vol value is an interpolated value from a volatility matrix which is equal to 0.34.
```
RuntimeError: forward + displacement (-0.00128788 + 0) must be positive
```
Thanks in advance
## Answer by David Duarte (score 3)
https://quant.stackexchange.com/a/68750
Here is an example on pricing the the Bachelier model (Normal vols):
```
import QuantLib as ql
yts = ql.YieldTermStructureHandle(ql.FlatForward(2, ql.TARGET(), 0.05, ql.Actual360()))
calendar = ql.TARGET()
today = ql.Date().todaysDate()
exerciseDate = calendar.advance(today, ql.Period('5y'))
exercise = ql.EuropeanExercise(exerciseDate)
swap = ql.MakeVanillaSwap(ql.Period('5y'), ql.Euribor6M(yts), 0.05, ql.Period('5y'))
swaption = ql.Swaption(swap, exercise)
bachelierEngine = ql.BachelierSwaptionEngine(yts, ql.QuoteHandle(ql.SimpleQuote(0.0055)))
swaption.setPricingEngine(bachelierEngine)
swaption.NPV()
```Shown 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.