2
$\begingroup$

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")
$\endgroup$
  • $\begingroup$ Shouldn't the second part of your option_value in the bachelier code be norm.pdf(d1)? $\endgroup$ – David Duarte Jan 9 at 16:15
5
$\begingroup$

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

$\endgroup$
0
$\begingroup$

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, Varun

$\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.