Black and Bachelier Caplet Pricing Use Different Volatility Inputs
Summary
The document compares an attempted Black 76 caplet valuation with a Bachelier, or normal-model, implementation for an interest rate caplet. The central modeling distinction in the replies is that Black volatility is quoted as a relative, lognormal change, while Bachelier volatility is an absolute change in the rate. Thus, using the same numerical volatility in both models does not generally represent the same market assumption.
A second reply identifies a parenthesization issue in the Black d-plus calculation. The supplied Bachelier code also appears to use a cumulative normal probability where the normal density is needed in the volatility term, so the volatility-units observation alone does not make that implementation correct. The discussion gives no validated normal-model price, volatility conversion, or practical SABR implementation. It is a limited coding discussion, and the example’s inputs should not be treated as a market calibration.
Key ideas
- Black 76 treats the forward rate as lognormal and uses relative volatility.
- Bachelier models absolute rate changes, so its volatility input has different units and scale.
- The Black implementation’s d-plus expression is flagged for a parenthesization correction.
- The displayed Bachelier formula should be checked carefully because its volatility term appears to require the normal density.
Tags
Full text
# Black and Normal Model for Caplet using Python
# Black and Normal Model for Caplet using Python
I am able to Price Caplet using Black 76 model in Python. However, I am unable to price the same with Normal Model. Can anyone suggest what is missing ?
I am valuing caplet that caps interest rate on 10000 loan at 8% p.a. quarterly compounding for three months starting in one year.
The zero curve is flat at 6.9394% p.a. one year volatility is 20% p.a.
Also, If you can suggest best place to see practical SABR model implementation using python that would be great.
Code for Black in Python:
```
from scipy.stats import norm
import math
def black(F_0,y,expiry,vol,rfr,expiry_1,isCall):
option_value = 0
if expiry * vol == 0.0:
if isCall:
option_value = max(F_0 - y, 0.0)
else:
option_value = max(y - F_0 , 0.0)
else:
d1 = dPlusBlack(F_0 = F_0 , y = y, expiry = expiry ,vol = vol)
d2 = dMinusBlack(F_0 = F_0 , y = y, expiry = expiry ,vol = vol)
if isCall:
option_value = (math.exp((-rfr)*expiry_1))*(F_0 * norm.cdf(d1) - y *norm.cdf(d2))
else:
option_value = (math.exp((-rfr)*expiry_1))*(y * norm.cdf(-d2) - F_0 *norm.cdf(-d1))
return option_value
def dPlusBlack(F_0 , y, expiry , vol):
d_plus = ((math.log(F_0 / y) + 0.5 * vol * vol * expiry)/ vol / math.sqrt(expiry))
return d_plus
def dMinusBlack(F_0 , y, expiry , vol):
d_minus = (dPlusBlack(F_0 = F_0 , y = y, expiry = expiry ,vol = vol ) - vol * math.sqrt(expiry))
return d_minus
a = black(0.07,0.08,1,0.20,0.069394,1.25,"isCall")
a = 0.0020646470930435683
```
Code for bachelier
```
from scipy.stats import norm
import math
def bachelier(F_0,y,expiry,vol,rfr,expiry_1,isCall):
option_value = 0
if expiry * vol == 0.0:
if isCall:
option_value = max(F_0 - y, 0.0)
else:
option_value = max(y - F_0 , 0.0)
else:
d1 = dPlusBachelier(F_0 = F_0 , y = y, expiry = expiry ,vol = vol)
if isCall:
option_value = (math.exp((-rfr)*expiry_1))*((F_0 - y)* norm.cdf(d1) + vol * math.sqrt(expiry) * norm.cdf(d1))
else:
option_value = (math.exp((-rfr)*expiry_1))*((y-F_0) * norm.cdf(-d1) + vol * math.sqrt(expiry) * norm.cdf(-d1))
return option_value
def dPlusBachelier(F_0 , y, expiry , vol):
d_plus = (F_0 - y)/ (vol * math.sqrt(expiry))
return d_plus
a = bachelier(0.07,0.08,1,0.20,0.069394,1.25,"isCall")
```
## Answer by Kiann (score 5)
https://quant.stackexchange.com/a/44558
are you using the same volatility 20% for both black76 and Bachelier?
The black76 is a lognormal model, where volatilities are quoted as relative price changes. The bachelier/normal model quotes volatilities as absolute changes.
That might be what you're missing?
Kind regards
## Answer by Varun (score 0)
https://quant.stackexchange.com/a/46603
Sorry I am bit late to the party. Just saw your post while trying to write my own black model. I am going to the mistake is a typo in dplus
```
d_plus = ((math.log(F_0 / y) + 0.5 * vol * vol * expiry)/ vol / math.sqrt(expiry))
```
Should be:
```
d_plus = ((math.log(F_0 / y) + 0.5 * vol * vol * expiry)/( vol * math.sqrt(expiry)))
```
Warm Regards, VarunShown 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.