For an ATM call the implied volatility can be computed by using the Newton-Raphson method:
import numpy as np
from scipy.stats import norm
def bs_d1(S,K,T,sigma,R):
return (np.log(S/K) + (r+sigma**2/2)*T)/(sigma*np.sqrt(T))
def bs_call(S,K,T,sigma,r):
d1 = bs_d1(S,K,T,sigma,r)
return S*norm.cdf(d1) - np.exp(-r*T)*K*norm.cdf(d1-sigma*np.sqrt(T))
def bs_vega(S,K,T,sigma,r):
d1 = bs_d1(S,K,T,sigma,r)
return S*np.sqrt(T)*norm.pdf(d1)
def sigma_i(c,S,K,T,r,eps=1e-3):
# first approximation
sigma = np.sqrt(2*np.pi/T)*c/S
while True:
diff = bs_call(S,K,T,sigma,r) - c
vega = bs_vega(S,K,T,sigma,r)
if np.abs(diff) < eps:
break
sigma = sigma - diff/vega
return sigma
However the method fails when I use OTM and ITM calls since Vega becomes very close to zero. One example:
# OTM
S = 100
K = 140
T = 0.25
r = 0.03
c = 0.0015581689540368482 # bs_call(S,K,T,0.2,r)
sigma = sigma_i(c,S,K,T,r)
Another example:
# ITM
S = 100
K = 80
T = 1.5
r = 0.03
c = 42.36652921873271 # bs_call(S,K,T,0.7,r)
sigma = sigma_i(c,S,K,T,r)
How to compute the implied volatility in these cases?