I. Synopsis

In finance, there exists a financial derivative product called Options. Options are often used as a hedging tool to cover from unwanted market movement of its underlying asset, and can be bought either through an exchange or through a bank market maker. The option market convention is to quote the Option price either by its Dollar Price or its “Implied Volatilty” (more information on Options can be found on this Wiki page).

A well-known formula called the Black-Scholes formula tries to mathematically express the Option Price, and states that the Price (or the Implied Volatility) is a function of (mainly) the 2 following variables: the time to Maturity and the Moneyness of the Option (both on a specific underlying asset).

For Coursera Developing Data Products Assignment 2, we will try to reproduce a 3D Implied Volatility Surface as used by traders using the Plotly package. The original data can be downloaded here at the Chicago Board Options Exchange a.k.a CBOE website, which gently provides us a sample of Facebook Stock Option End-of-Day Implied Volatility marks (June 1st 2017).

II. Plotly 3D graph with smoothing

We have a nice volatility skew drawn thanks to plotly. The IV has a nice smirk against Moneyess, and volatility decreases as Time to Maturity decreases. This matches the usual Stock Option Implied Volatility Surface tendency.

III. Appendix - R code

# 0. Load libraries
library(plotly)
library(reshape)


# I. Read file (data from CBOE website)
file = "FBOptionSkew.csv"
path = "C:/Users/tngch/Documents/R/Learning/9. Developing Data Product/"
df = read.csv(paste0(path, file))


# II. Data Processing
# Rewriting Moneyness
df$moneyness = df$moneyness * ifelse(df$option_type == "p", -1, 1)

# Implied volatility expressed in Percentage
df$iv = round(df$iv * 100, 1)

# Replacing 0 values by maximum IV
# Note: in finance, we would not approximate 0 implied volatility by the maximum IV of 
# an option of the right type/strike/maturity. Rather, we would interpolate it using a spline 
# interpolation. We kept it simple here, as the goal is not to produce a commercial IV surface.
temp = as.data.frame(tapply(df$iv, list(df$period, df$option_type), max))
temp$period = rownames(temp)
temp = melt(temp)

for (i in 1:nrow(df)) {
    if (df[i, "iv"] == 0) {
        n1 = df[i, "option_type"]
        n2 = df[i, "period"]
        df[i, "iv"] = temp[ (temp$variable == n1) & (temp$period == n2), ]$value
    }
}

# Dropping the unnecessary variables
df = df[, c("period", "moneyness", "iv")]

# Assuming Put-Call parity, replacing 0 value of Moneyness by the average of put/call IV
temp = df[df$moneyness == 0, ]
temp = as.data.frame(tapply(temp$iv, temp$period, mean))
colnames(temp) = "iv"
temp$period = rownames(temp)
temp$moneyness = 0

df = rbind(df[df$moneyness != 0,], temp)
df$period = as.numeric(df$period)
df = df[order(df$period, df$moneyness),]


# III. Reshaping the dataframe for Plotly processing (into a matrix form)
IV = reshape(df, direction = "wide", timevar = "moneyness", idvar = "period")
IV = IV[, -1]
Moneyness = unique(df$moneyness)
Maturity = unique(df$period)
IV = as.matrix(IV)


# IV. Plotting the IV surface with Plotly
axx = list(title = "x: Moneyness (in %, Neg. values are OTM call)")
axy = list(title = "y: Time to Maturity (in days)")
axz = list(title = "z: Implied Volatility")

plot_ly(x=~Moneyness, y=~Maturity, z=~IV) %>% 
    add_surface() %>%
    layout(title = "June 1st, 2017 Facebook Option Vol Surface",
           scene = list(xaxis=axx,yaxis=axy,zaxis=axz))