Implied Volatility with the Newton-Raphson Method

from py_vollib.black_scholes import black_scholes as bs
from py_vollib.black_scholes.greeks.analytical import vega
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np

from IPython.display import HTML, Image # For GIF
from matplotlib import rc
rc('animation', html='jshtml')

Implementing Newton-Raphson Method in Python

def implied_vol(S0, K, T, r, market_price, flag='c', tol=0.00001):
    """Compute the implied volatility of a European Option
        S0: initial stock price
        K:  strike price
        T:  maturity
        r:  risk-free rate
        market_price: market observed price
        tol: user choosen tolerance
    """
    max_iter = 200 #max number of iterations
    vol_old = 0.30 #initial guess

    for k in range(max_iter):
        bs_price = bs(flag, S0, K, T, r, vol_old)
        Cprime =  vega(flag, S0, K, T, r, vol_old)*100
        C = bs_price - market_price
        vol_new = vol_old - C/Cprime
        bs_new = bs(flag, S0, K, T, r, vol_new)

        if (abs(vol_old - vol_new) < tol or abs(bs_new - market_price) < tol):
            break
        vol_old = vol_new

    implied_vol = vol_old
    return implied_vol

Calculating an example option’s IV

S0, K, T, r = 30, 28, 0.2, 0.025
market_price = 3.97
implied_vol_est = implied_vol(S0, K, T, r, market_price, flag='c')
print("Implied Volatility is : ", round(implied_vol_est,2)*100, "%")

Plotting the calculation of IV as Newton-Raphson Method progresses

def implied_vol(S0, K, T, r, market_price, flag='c', tol=0.000001):
    """Compute the implied volatility of a European Option
        S0: initial stock price
        K:  strike price
        T:  maturity
        r:  risk-free rate
        market_price: market observed price
        tol: user choosen tolerance
    """
    max_iter = 200 #max number of iterations
    vol_old = 0.11 #initial guess

    x_vals = []
    y_vals = []

    for k in range(max_iter):
        bs_price = bs(flag, S0, K, T, r, vol_old)
        Cprime =  vega(flag, S0, K, T, r, vol_old)*100
        C = bs_price - market_price
        vol_new = vol_old - C/Cprime

        #append 1 - move up
        x_vals.append([vol_old*100,vol_old*100])
        y_vals.append([0,bs_price])

        bs_new = bs(flag, S0, K, T, r, vol_new)

        #append 2 - take step
        x_vals.append([vol_old*100,vol_new*100])
        y_vals.append([bs_price,0])

        if (abs(vol_old - vol_new) < tol):
            break

        vol_old = vol_new

        

    implied_vol = vol_old
    return implied_vol, x_vals, y_vals

S0, K, T, r, sigma = 30, 28, 0.2, 0.025, 0.3
prices, vols = [], []
for sigma in range(1,125):
    # print(bs('c', S0, K, T, r, sigma/100))
    # print(sigma)
    prices.append( bs('c', S0, K, T, r, sigma/100) )
    vols.append( sigma )

# print(prices)

market_price = 3.9790765403377035
implied_vol, x_vals, y_vals = implied_vol(S0, K, T, r, market_price, flag='c')

fig, ax = plt.subplots()
plt.title('Newton-Raphson Method for Option Implied Volatility')
plt.ylabel('Call Price ($)')
plt.xlabel('Implied Volatility (%)')
xdata, ydata = [], []
y1, = ax.plot(vols, prices, label = 'Black Scholes Price')
y3, = ax.plot([54], [market_price], 'bo', label = 'Market Price')
y2, = ax.plot([], [], 'r--')
y4, = ax.plot([], [], 'ro', label = 'Calc. Price')

y5, = ax.plot([], [], 'go', label = 'Calc. Implied Vol')
y6, = ax.plot([], [], 'g--')

# print(x_vals)
# print(y_vals)

def init():
    ax.set_xlim(0, 125)
    ax.set_ylim(0, max(prices))
    return y2, y4

def update(frame):
    xdata.append(x_vals[frame])
    ydata.append(y_vals[frame])
    if (frame % 2) == 0:
        y2.set_data(x_vals[frame], y_vals[frame])
        y4.set_data(x_vals[frame][1], y_vals[frame][1])
        return y2, y4
    else:
        y6.set_data(x_vals[frame], y_vals[frame])
        y5.set_data(x_vals[frame][1], y_vals[frame][1])
        return y2, y4

anim = animation.FuncAnimation(fig, update, frames=len(x_vals),
                    init_func=init, interval=750, repeat=True, save_count = 0, blit=False)
ax.legend(loc='upper left')
# plt.show()

HTML(anim.to_html5_video())