0
$\begingroup$

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?

$\endgroup$
2
  • 1
    $\begingroup$ the problem you have, is that if the initially vol is too small, then the vega is tiny - i.e. nearly zero, which you then divide by. The best way of doing it i'm aware of is peter jaeckel's, you can see it [here](www.jaeckel.org), in a paper called "Let's Be Rational". I advise you read all of his papers, they're all interesting. $\endgroup$
    – will
    Jan 25 '20 at 19:02
  • $\begingroup$ @will This fixes the problem in the example that I originally posted. Hovewer, it does not work with a new example. $\endgroup$
    – Appliqué
    Jan 25 '20 at 19:14
2
$\begingroup$

you can use this algoithm to calibrate volatility (the use of vega deos not work for far OTM and ITM options):

        define vol1=0.0;
        define vol2=1.99;
        define z;
        define z2;
        for (i=0;i<max_iterations;i++) {
            z=0.5*(vol1+vol2);
            z2=z/(1.0-z);
            if (price>bs_price(z2)) {
                vol1=z;
                vol2=vol2;
            } else {
                vol1=vol1;
                vol2=z;
            }
        }
        z=0.5*(vol1+vol2);
        return z/(1.0-z);
$\endgroup$
2
  • $\begingroup$ I see. One just uses the bisection method. $\endgroup$
    – Appliqué
    Jan 25 '20 at 21:39
  • 1
    $\begingroup$ if you're going to do a bisection method, then if i recall, it's actually more efficient to use the golden ratio to get your next guess, rather than half way. $\endgroup$
    – will
    Jan 26 '20 at 0:13

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.