Code and Finance

Finding the implied volatility

Black-Scholes describes option prices as a function of the underlying price, strike, risk-free interest rate, time to expiry and volatility:

\begin{equation*} V = BS(S, K, r, T, \sigma) \end{equation*}

The volatility value used here is an estimxate of the future realised price volatility. (we calculated the historical price volatility a few articles ago.

Given that the stock price, the strike, risk-free interest rate, and time to expiry are all known and easily found, we can actually think of a price for an option in the market as a function of \(\sigma\) instead.

\begin{equation*} V = BS(\sigma) \end{equation*}

The price of an option is monotonically increasing in \(\sigma\) , which means that as volatility increases, so does the value of the option, as shown below.


At the money the effect is fairly linear, and even for strikes that are ITM or OTM there's a positive effect on the price for an increase in volatility.

Finding the market implied volatility

Below is an call option quote for GOOG, it has a strike of 585.00 and an expiry of 18th October 2014. It last traded for $17.50.


So far we know our option price, \(V\) , our strike, \(K\) , and time to expiry, \(T\) . We can read off from Yahoo! that GOOG is trading at $586.08, and now we have \(S\) . The only thing missing is the risk-free rate, \(r\) . We can approximtate the risk-free rate by finding the Treasury bill rate that matures near when our option expires. The 4 week rate is currently 0.02% , now we have \(r\) .

From the data we've gathered so far, we can see that:

\begin{equation*} V = BS(S, K, r, T, \sigma) \end{equation*}
\begin{equation*} $17.50 = BS(586.08, 585.00, 0.0002, 0.10958...., \sigma) \end{equation*}

Now we have an equation where we only have one unknown variable, volatility. Unfortunately, we can't rearrange the Black-Scholes equation to solve for volatility. However, we know the price is monotonically increasing with respect to volatility, so we could try guess what the volatility is and then find out what that would value the option at. If the value is too high, we lower our guess and vice versa, and repeat until we've found a value for \(\sigma\) that gives us an option price close enough to the market price.

While the method above would work, it's fairly imprecise. What we could do instead is use a root finding method like the bisection method or Newton's method.

Newton's method

Newton's method is a method for finding increasingly improved approximations to the roots of a function. With implied volatility we're trying to find what value of \(\sigma\) makes our option price as close to $8.50 as possible.

If we have a function \(f(x)\) and its derivative \(f'(x)\) we can start with an initial guess and successively improve it with an updated guess by doing:

\begin{equation*} x_{n+1} = x_n - \frac{f(x_n)}{f'(x_n)} \end{equation*}

To find the implied volatility, the root we want to find is where our Black-Scholes price matches the market price to some level of precision. We can express that like:

\begin{equation*} | V_{market} - V_{model} | >= \epsilon \end{equation*}

Where \(\epsilon\) is the level of precision and \(V_{market}\) is the market price for the option.

Here, our \(f(x)\) function is the Black-Scholes equation for pricing an option:

\begin{equation*} V_{model} = f(x) = BS(\sigma) \end{equation*}

As you can probably guess, the derivative of price with respect to volatility is vega, which will be our \(f'(x)\) function.

So let's implement Newton's method in python:

def find_vol(target_value, call_put, S, K, T, r):
    PRECISION = 1.0e-5

    sigma = 0.5
    for i in xrange(0, MAX_ITERATIONS):
        price = bs_price(call_put, S, K, T, r, sigma)
        vega = bs_vega(call_put, S, K, T, r, sigma)

        price = price
        diff = target_value - price  # our root

        print i, sigma, diff

        if (abs(diff) < PRECISION):
            return sigma
        sigma = sigma + diff/vega # f(x) / f'(x)

    # value wasn't found, return best guess so far
    return sigma

Here, we've set our precision to be 1e-5 (0.00001) and that we won't perform any more than 100 guesses.

We also need the two Black-Scholes functions, bs_price and bs_vega:

n = norm.pdf
N = norm.cdf

def bs_price(cp_flag,S,K,T,r,v,q=0.0):
    d1 = (log(S/K)+(r+v*v/2.)*T)/(v*sqrt(T))
    d2 = d1-v*sqrt(T)
    if cp_flag == 'c':
        price = S*exp(-q*T)*N(d1)-K*exp(-r*T)*N(d2)
        price = K*exp(-r*T)*N(-d2)-S*exp(-q*T)*N(-d1)
    return price

def bs_vega(cp_flag,S,K,T,r,v,q=0.0):
    d1 = (log(S/K)+(r+v*v/2.)*T)/(v*sqrt(T))
    return S * sqrt(T)*n(d1)

Let's try finding the implied vol for our quote above:

V_market = 17.5
K = 585
T = (,10,18) -,9,8)).days / 365.
S = 586.08
r = 0.0002
cp = 'c' # call option

implied_vol = find_vol(V_market, cp, S, K, T, r)

print 'Implied vol: %.2f%%' % (implied_vol * 100)

print 'Market price = %.2f' % V_market
print 'Model price = %.2f' % bs_price(cp, S, K, T, r, implied_vol)

Running it yields:

Implied vol: 21.92%
Market price = 17.50
Model price = 17.50

We've successfuly recovered the implied volatility for the option above. The $585 call expiring in 18th October 2014 had an implied vol of 21.92%.