AuthorJing Wu2018-06-07

Introduction

The change of volatility can have a significant impact on the performance of options trading. In addition to the Vega we explained in Greeks letter chapter, this part of the volatility tutorial will discuss the concept of volatility, specifically, we discuss realized and implied volatility, their meanings, measurements, uses, and limitations.

Historical Volatility

1. Definition

It is a measurement of how much the price of the asset has changed on average during the certain period of time. In common, volatility is said to be the standard deviation of the return of assets price.

2. Calculation

Next we discuss how to estimate the historical volatility of the option empirically.

Where (n+1) is the number of observations, is the stock price at end of it time interval

The standard deviation of the is given by

where is the mean of

If we assume there are n trading days per year. Then the estimate of historical volatility per annum is

import pandas as pd
from numpy import sqrt,mean,log,diff
from datetime import datetime 
qb = QuantBook()

# use the daily data of Google(NASDAQ: GOOG) from 01/2016 to 08/2016
goog = qb.AddEquity("GOOG").Symbol
close = qb.History(goog, datetime(2016,1,1), datetime(2016,9,1), Resolution.Daily).close
r = diff(log(close))
r_mean = mean(r)
diff_square = [(r[i]-r_mean)**2 for i in range(0,len(r))]
sigma = sqrt(sum(diff_square)*(1.0/(len(r)-1)))
vol = sigma*sqrt(252)

An asset has a historical volatility based on its past performance as described above, investors can gain insight on the fluctuations of the underlying price during the past period of time. But it does not tell us anything about the volatility in the market now and in the future. So here we introduce the implied volatility.

Implied Volatility

In contrast to historical volatility, the implied volatility looks ahead. It is often interpreted as the market’s expectation for the future volatility of a stock and is implied by the price of the stock’s options. Here implied volatility means it is not observable in the market but can be derived from the price of an option.

1. Definition

We use volatility as an input parameter in option pricing model. If we take a look at the BSM pricing, the theoretical price or the fair value of an option is P, where P is a function of historical volatility σ, stock  price S, strike price K, risk-free rate r and the time to expiration T. That is . But the market price of options is not always the same with the theoretical price. Now in contrast, if we are given the market’s prices of calls and puts written on some asset and also the value of S, K, r, T. For each asset we can solve a new volatility that corresponds to the price of each option – the implied volatility. Then the implied volatility is .

2. Calculation

Here we use the bisection method to solve the BSM pricing equation and find the root which is the implied volatility. We use Yahoo Finance Python API to get the real time option data.

import scipy.stats as stats

def bsm_price(option_type, sigma, s, k, r, T, q):
    # calculate the bsm price of European call and put options
    d1 = (np.log(s / k) + (r - q + sigma ** 2 * 0.5) * T) / (sigma * np.sqrt(T))
    d2 = d1 - sigma * np.sqrt(T)
    if option_type == 'c':
        return np.exp(-r*T) * (s * np.exp((r - q)*T) * stats.norm.cdf(d1) - k *  stats.norm.cdf(d2))
    if option_type == 'p':
        return np.exp(-r*T) * (k * stats.norm.cdf(-d2) - s * np.exp((r - q)*T) *  stats.norm.cdf(-d1))
    raise Exception(f'No such option type: {option_type}')

def implied_vol(option_type, option_price, s, k, r, T, q):
    # apply bisection method to get the implied volatility by solving the BSM function
    precision = 0.00001
    upper_vol = 500.0
    max_vol = 500.0
    min_vol = 0.0001
    lower_vol = 0.0001
    iteration = 50

    while iteration > 0:
        iteration -= 1
        mid_vol = (upper_vol + lower_vol)/2
        price = bsm_price(option_type, mid_vol, s, k, r, T, q)

        if option_type == 'c':
            lower_price = bsm_price(option_type, lower_vol, s, k, r, T, q)
            if (lower_price - option_price) * (price - option_price) > 0:
                lower_vol = mid_vol
            else:
                upper_vol = mid_vol
            if mid_vol > max_vol - 5 :
                return 0.000001

        if option_type == 'p':
            upper_price = bsm_price(option_type, upper_vol, s, k, r, T, q)
            if (upper_price - option_price) * (price - option_price) > 0:
                upper_vol = mid_vol
            else:
                lower_vol = mid_vol

        if abs(price - option_price) < precision:
            break

    return mid_vol

implied_vol('c', 0.3, 3, 3, 0.032, 30.0/365, 0.01)

From the result above, the implied volatility of European call option (with premium c=0.3, S=3, K=3, r=0.032, T =30 days, d=0.01) is 0.87.

3. Factors Affecting Implied Volatility

According to the time value description in the first tutorial, in general, the more time to expiration, the greater the time value of the option. Investors are willing to pay extra money for zero intrinsic value options which have more time to expiration because more time increases the likelihood of price movement and fluctuations, it is the options will become profitable. Implied volatility tends to be an increasing function of maturity. A short-dated option often has a low implied volatility, whereas a long-dated option tends to have a high implied volatility.

Volatility Skew

For European options of the same maturity and the same underlying assets, the implied volatilities vary with the strikes. For a series of put options or call options, if we plot these implied volatilities for a series of options which have the same expiration date and the same underlying with the x-axis being the different strikes, normally, we would get a convex curve. The shape of this curve is like people's smiling, it is being called the volatility smile. The shape of volatility smile depends on the assets and the market conditions.

Here we give an example how to plot the volatility smile by using the real time options data of SPDR S&P 500 ETF(NYSEARCA: SPY).

# Get option contracts
start_date = datetime(2017, 8, 15)
spy = qb.AddEquity('SPY', resolution=Resolution.Daily)
spy.VolatilityModel = StandardDeviationOfReturnsVolatilityModel(2)
contract_symbols = qb.OptionChainProvider.GetOptionContractList(spy.Symbol, start_date)
contract_symbols = [s for s in contract_symbols 
    if s.ID.Date == datetime(2018, 12, 21)]

# Subscribe to option contracts' data
symbols = []
for symbol in contract_symbols:
    contract = qb.AddOptionContract(symbol, resolution=Resolution.Daily, fillDataForward = False)
    contract.PriceModel = OptionPriceModels.BjerksundStensland()
    symbols.append(contract.Symbol)

# history request for IV
requests = []
for security in sorted(qb.Securities.Values, key=lambda x: x.Type):
    for subscription in security.Subscriptions:
        requests.append(HistoryRequest(subscription, security.Exchange.Hours, qb.StartDate-timedelta(hours=12), qb.StartDate))
history = qb.History(requests)

df = pd.DataFrame()
done_symbol = []
for slice in history:
    underlying_price = None

    # Update the security with QuoteBars information
    for bar in slice.QuoteBars.Values:
        symbol = bar.Symbol
        if symbol in done_symbol:
            continue
        done_symbol.append(symbol)
        security = qb.Securities[symbol]
        security.SetMarketPrice(bar)

        if security.Type == SecurityType.Equity:
            underlying_price = security.Price
            continue

        # Create the Option contract
        contract = OptionContract.Create(symbol, symbol.Underlying, bar.EndTime, security, underlying_price)
        # Evaluate the price model to get the IV
        result = security.PriceModel.Evaluate(security, None, contract)
        # Append the data to the DataFrame
        data = {
            "IV" : result.ImpliedVolatility,
            "Strike" : contract.Strike,
            "Right" : "Put" if symbol.ID.OptionRight == 1 else "Call"
        }
        index = [symbol.Value]
        df = pd.concat([df, pd.DataFrame(data, index=index)])

plt.figure(figsize=(16, 7))
df_call = df[df.Right == "Call"]
df_put = df[df.Right == "Put"]
e = plt.scatter(df_call.Strike, df_call.IV, c ='red', label="IV(call options)")
f = plt.scatter(df_put.Strike, df_put.IV, c = 'black', label="IV(put options)")
plt.xlabel('strike')
plt.ylabel('Implied Volatility')
plt.legend((e,f), ("IV (call options)", "IV (put options)"))

The current date is 08/14/2017. We plot the implied volatilities for SPY options which expire on 12/21/2018.

implied volatility of call and put options

Plotting these implied volatilities across strikes gives us the implied volatility skew. For the shape of volatility smile, it should be a symmetry convex curve. But from the above chart, the implied volatility curve slopes downward to the right. This is referred to the skew, which means that options with low strikes have higher implied volatilities than those with higher strikes. The smile is not symmetry. The skew of a distribution is a measure of its asymmetry. Although the volatility skew is dynamic, in equity markets it is almost always a decreasing function of the strike. Other asset classes such as FX and commodities have differently shaped skews.

From the above chart, we can see the implied volatility for put options is higher than call options. Usually, put options trade for a higher price than call options, because traders place more risk in the short put positions, which raises the amount of reward they require to sell the position. Higher option prices signify an increase in risk and are represented by higher implied volatility levels derived from the option pricing model. Then we scattered all the implied volatilities of contracts across all the strikes.

# Get option contracts
start_date = datetime(2017, 8, 15)
spy = qb.AddEquity('SPY', resolution=Resolution.Daily)
spy.VolatilityModel = StandardDeviationOfReturnsVolatilityModel(2)
contract_symbols = qb.OptionChainProvider.GetOptionContractList(spy.Symbol, start_date)

# Subscribe to option contracts' data
symbols = []
for symbol in contract_symbols:
    contract = qb.AddOptionContract(symbol, resolution=Resolution.Daily, fillDataForward = False)
    contract.PriceModel = OptionPriceModels.BjerksundStensland()
    symbols.append(contract.Symbol)

# history request for IV
requests = []
for security in sorted(qb.Securities.Values, key=lambda x: x.Type):
    for subscription in security.Subscriptions:
        requests.append(HistoryRequest(subscription, security.Exchange.Hours, qb.StartDate-timedelta(1), qb.StartDate))
history = qb.History(requests)

df = pd.DataFrame()
done_symbol = []
for slice in history:
    underlying_price = None

    # Update the security with QuoteBars information
    for bar in slice.QuoteBars.Values:
        symbol = bar.Symbol
        if symbol in done_symbol:
            continue
        done_symbol.append(symbol)
        security = qb.Securities[symbol]
        security.SetMarketPrice(bar)

        if security.Type == SecurityType.Equity:
            underlying_price = security.Price
            continue

        # Create the Option contract
        contract = OptionContract.Create(symbol, symbol.Underlying, bar.EndTime, security, underlying_price)
        # Evaluate the price model to get the IV
        result = security.PriceModel.Evaluate(security, None, contract)
        # Append the data to the DataFrame
        data = {
            "IV" : result.ImpliedVolatility,
            "Strike" : contract.Strike,
            "Time till expiry (days)" : (contract.Expiry - bar.EndTime).days
        }
        index = [symbol.Value]
        df = pd.concat([df, pd.DataFrame(data, index=index)])

x = df["Time till expiry (days)"]
y = df.Strike
z = df.IV
fig = plt.figure(figsize=(20,11))
ax = fig.add_subplot(111, projection='3d')
ax.view_init(20,10)
ax.scatter(x,y,z)

implied volatility surface

Volatility Surface

By fixing the maturity and looking at the implied volatilities of European options on the same underlying but different strikes, we obtain the implied volatility skew or smile. The volatility surface is the three-dimensional surface when we plots the market implied volatilities of European options with different strikes and different maturities.

Through the interpolation method, we can generate the implied volatility surface of SPY options for both put and call options as follows:

implied volatility surface for call options implied volatility surface for put options

The Reason for Volatility Skew

The volatility skew reveals that for put options, implied volatility is higher for deep OTM options and is decreasing as it moves toward ITM options. For call options, the implied volatility is higher for deep ITM options and is decreasing as it moves toward OTM options. From the demand and supply degree, the skew reflects that investors are more willing to buy deep OTM puts and ITM calls. Why there is volatility skew in the market?

First, the majority of the equity positions are long. Investors usually have two ways to hedge those long positions risks: Buying downside puts or selling upside calls. The increase in demand create increases in the price of downside puts and decreases in the price of upside calls. The volatility is a reflection of options price. Therefore the volatility of in-the-money put is higher and the volatility of in-the-money call is lower.

The second reason for volatility skew is that the market moves down faster than it moves up. The downside market move is riskier than the upside move. Thus the price of OTM puts is higher than OTM calls.

Summary

In this chapter, we discussed the historical volatility and the implied volatility. The historical volatility of an asset is the statistical measure we know as the standard deviation of the stock return series. The implied volatility of the same asset, on the other hand, is the volatility parameter that we can infer from the prices of traded options written on this asset. In contrast to historical volatility, which looks at fluctuations of asset prices in the past, implied volatility looks ahead. The two volatilities do not necessarily coincide, and although they may be close, they are typically not equal.

Now we know the constant volatility assumption in Black-Sholes-Merton model is not applicable in the real market because there is volatility skew for most options. In next chapter, we will introduce some volatility models to capture the volatility skew in options pricing.


Reference

  1. Options Pricing: Intrinsic Value And Time Value, Jean Folger, Online Copy

QuantConnect Logo

Try the world leading quantitative analysis platform today

Sign Up

Previous: The Greek Letters Next: Local Volatility and Stochastic Volatility