I'm solving the following problem as a part of Interest Rate Models class on Coursera
I'm having a hard time using nonlinear root solver to invert the Black formula for Cap price in order to obtain a Black implied volatility that will be used further for calibration purposes. My Python code is attached below.
import numpy as np
import math
import scipy
from scipy import optimize
from scipy import stats
Time = [0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4]
ForwardRates = [0.06, 0.08, 0.09, 0.10, 0.10, 0.10, 0.09, 0.09]
CapPrices = [0.20, 0.80, 1.20, 1.60]
SimpleRates = []
DiscountFactors = []
SimpleRates.append(ForwardRates[0])
DiscountFactors.append(1/(1+Time[0]*SimpleRates[0]))
for i in range(1, len(Time)):
SimpleRates.append(((1+0.5*ForwardRates[i])*(1+Time[i-1]*SimpleRates[i-1])-1)/Time[i])
DiscountFactors.append(1/(1+Time[i]*SimpleRates[i]))
SwapRates = []
for k in range(1, len(Time)):
s = sum(DiscountFactors[i] for i in range(1, k + 1))
SwapRates.append((DiscountFactors[0] - DiscountFactors[k])/(0.5*s))
def d1(i, sigma):
return (np.log(ForwardRates[i]/SwapRates[i])+0.5*math.pow(sigma, 2)*Time[i-1])/(sigma*math.sqrt(Time[i-1]))
def d2(i, sigma):
return (np.log(ForwardRates[i]/SwapRates[i])+math.pow(sigma, 2)*Time[i-1])/(sigma*math.sqrt(Time[i-1])) - sigma*math.sqrt(Time[i-1])
def caplet(begin, end, sigma):
return 0.5*DiscountFactors[end]*(ForwardRates[end]*scipy.stats.norm.cdf(d1(end, sigma))-SwapRates[end]*scipy.stats.norm.cdf(d2(end, sigma)))
def cap(end, sigma):
price = 0
for j in range(1, end + 1):
price += caplet(j - 1, j, sigma)
return price
s = scipy.optimize.bisect(lambda sigma: cap(1, sigma) - CapPrices[0], 0.0005, 0.5)
I'm getting an error that f(a) and f(b) must have different signs regardless of my interval choice. What particular root solving method should I use in Python to get around that? Is there anything else that I'm doing wrong?