Creating Constant 30 Day Maturity From VIX1 | VIX2 Futures Contracts

In previous post Creating synthetic VXX and XIV data and plotting the VIX term structure using R we created the VIX term structure from CBOE data.

In this post we will create a 30 day constant maturity contract for the VIX1 | VIX2 futures contracts. This code is an extension and total rework of Ilya Kipnis term structure code.

The methodology for this procedure is to weight VIX1 and VIX2 based on the number of days remaining to expiration. The math for this taken from this prospectus, PS-32.

eg

where:
dt = The total number of business days in the current Roll Period beginning with, and including, the starting CBOE VIX Futures Settlement Date and ending with, but excluding, the following CBOE VIX Futures Settlement Date. The number of business days will not change for purposes of this calculation in cases of a new holiday introduced intra-month or an unscheduled market closure.
dr = The total number of business days within a roll period beginning with, and including, the following business day and ending with, but excluding, the following CBOE VIX Futures Settlement Date. The number of business days includes a new holiday introduced intra-month up to the business day preceding such a holiday.

At the close on the Tuesday corresponding to the start of the Roll Period, all of the weight is allocated to the first month contract. On each subsequent business day a fraction of the first month VIX futures contracts are sold and an equal notional amount of the second month VIX futures contracts is bought. The fraction, or quantity, is proportional to the number of first month VIX futures contracts as of the previous index roll day, and inversely proportional to the length of the current Roll Period. In this way the initial position in the first month contract is progressively moved to the second month over the course of the month, until the next following Roll Period starts when the old second month VIX futures contract becomes the new first month VIX futures contract. In addition to the transactions described above, the weight of each index component is also adjusted every day to ensure that the change in total dollar exposure for the index is only due to the price change of each contract and not due to using a different weight for a contract trading at a higher price

In short, on Tuesday before VIX Wednesday expiration 100% of the weight will be in the VIX1 contract, the former VIX2 contract. As a short example. Lets say there are 25 days in the roll period (dt). On day 10 (dr) the weight in the front month (CRW1,t) would be 100*dr/dt = 40%. On the Monday before Wednesday VIX expiration, dt = 25, dr = 1, CRW1,t = 100*dr/dt = 4%. As the prospectus describes, “At the close on the Tuesday corresponding to the start of the Roll Period, all of the weight is allocated to the first month contract.” Thus on Tuesday before Wednesday expiration, if (dt) = 20, (dr) also =20 and the weight at the start of a new roll period would be CRW,1,t = 100*dr/dt = 100% as per the prospectus. Remember on Tuesday, VIX2 becomes VIX1 because this is when the start of the roll period begins for the next expiration date.

One note here, in this example we provide no additional dollar weight adjustments.

With that out of the way. The R code to perform the above procedure.

# VIX1|VIX2 30 Day Constant Maturity
# Andrew Bannerman 12.10.2017 

require(xts)
require(data.table)
require(ggplot2)
require(lubridate)
require(magrittr)
require(scales)
require(reshape2)
require(dplyr)
require(PerformanceAnalytics)
require(quantmod)

# 06 through 18
years <- c(paste0("0", c(6:9)), as.character(c(10:18)))

# futures months
futMonths <- c("F", "G", "H", "J", "K", "M",
               "N", "Q", "U", "V", "X", "Z")

# expiries come from http://www.macroption.com/vix-expiration-calendar/
expiries <- read.table("D:/R Projects/Final Scripts/VIX_term_structure/expiries.txt", header=FALSE)

# convert expiries into dates in R
dateString <- paste(expiries$V3, expiries$V2, expiries$V1, sep = "-")
dates <- as.Date(dateString, format = "%Y-%B-%d")

# map futures months to numbers for dates
monthMaps <- cbind(futMonths, c("01", "02", "03", "04", "05", "06",
                                "07", "08", "09", "10", "11", "12"))
monthMaps <- data.frame(monthMaps)
colnames(monthMaps) <- c("futureStem", "monthNum")

settlement.dates <- data.frame(Date=dates, Expiration.Date=2)
dates <- data.frame(dates)
dates$dateMon <- substr(dates$dates, 1, 7) # Extract year month only

contracts <- expand.grid(futMonths, years)
contracts <- paste0(contracts[,1], contracts[,2])
#contracts <- c(contracts, "F18")
stem <- "https://cfe.cboe.com/Publish/ScheduledTask/MktData/datahouse/CFE_"
#contracts <- paste0(stem, contracts, "_VX.csv")

masterlist <- list()
timesToExpiry <- list()
i=1
for(i in 1:length(contracts)) {

  # obtain data
  contract <- contracts[i]
  dataFile <- paste0(stem, contract, "_VX.csv")
  expiryYear <- paste0("20",substr(contract, 2, 3)) # Paste element 2 and 3 from the contract xYY
  expiryMonth <- monthMaps$monthNum[monthMaps$futureStem == substr(contract,1,1)]
  expiryDate <- dates$dates[dates$dateMon == paste(expiryYear, expiryMonth, sep="-")]
  data <- suppressWarnings(fread(dataFile))
  # create dates
  dataDates <- as.Date(data$`Trade Date`, format = '%m/%d/%Y')

  # create time to expiration xts
  toExpiry <- xts(expiryDate - dataDates, order.by=dataDates)
  colnames(toExpiry) <- contract
  timesToExpiry[[i]] <- toExpiry

  # get settlements
  settlement <- xts(data$Settle, order.by=dataDates)
  colnames(settlement) <- contract
  masterlist[[i]] <- settlement
}
i
# cbind outputs
masterlist <- do.call(cbind, masterlist)
timesToExpiry <- do.call(cbind, timesToExpiry)

# NA out zeroes in settlements
masterlist[masterlist==0] <- NA

sumNonNA <- function(row) {
  return(sum(!is.na(row)))
}

simultaneousContracts <- xts(apply(masterlist, 1, sumNonNA), order.by=index(masterlist))
chart.TimeSeries(simultaneousContracts)

dim(masterlist)
i=1
termStructure <- list()
expiryStructure <- list()
masterDates <- unique(c(first(index(masterlist)), dates$dates[dates$dates %in% index(masterlist)], Sys.Date()-1)) # %in% operator matches dates, sys.date-1 to include final range date
for(i in 1:(length(masterDates)-1)) {
  subsetDates <- masterDates[c(i, i+1)]
  dateRange <- paste(subsetDates[1], subsetDates[2], sep="::")
  subset <- masterlist[dateRange,c(i:(i+7))]
  subset <- subset[-1,]
  expirySubset <- timesToExpiry[index(subset), c(i:(i+7))]
  colnames(subset) <- colnames(expirySubset) <- paste0("C", c(1:8))
  termStructure[[i]] <- subset
  expiryStructure[[i]] <- expirySubset
}

termStructure <- do.call(rbind, termStructure)
expiryStructure <- do.call(rbind, expiryStructure)

simultaneousContracts <- xts(apply(termStructure, 1, sumNonNA), order.by=index(termStructure))
chart.TimeSeries(simultaneousContracts)

plot(t(coredata(last(termStructure))), type = 'b')
# Plot specific date term structure
backwardation <- termStructure["2017-11-30"] # Subset specific date
back.df <- as.data.frame(backwardation)
back.df <- setDT(back.df, keep.rownames = TRUE)[] # Set row names
colnames(back.df)[1] <- "Date"
back.df$Date <- ymd(back.df$Date)
back.df <- melt(data = back.df,id.vars = 'Date')   # melt df for plotting with ggplot2
colnames(back.df)[2] <- "Contract"

# plot
ggplot(data=back.df,aes(x=Contract,y=value,group = 1))+
  geom_point()+  geom_line()+
  theme_classic()+
  ggtitle("VIX Term Structure for Date 2017-11-30",subtitle="Example of Contango")+
  labs(x="Contract",y="Settlement")+
  theme(plot.title = element_text(hjust=0.5),plot.subtitle =element_text(hjust=0.5))

# Save Term Structure to data frame
term.structure.df <- as.data.frame(termStructure)
term.structure.df <- setDT(term.structure.df, keep.rownames = TRUE)[] # Set row names
colnames(term.structure.df)[1] <- "Date"
term.structure.df$Date <- ymd(term.structure.df$Date)

# Adjust for 10:1 split pre 03-26-2007
# Split information here: http://cfe.cboe.com/publish/CFEinfocirc/CFEIC07-003%20.pdf
df.10.split <- subset(term.structure.df, Date < as.POSIXct("2007-03-26/") ) # subset data prior to split
library(magrittr)
df.10.split[,2:9] %<>% lapply(function(x) x / 10) # appply split to all columns excluding date 1
df.post.split <- subset(term.structure.df, Date >= as.POSIXct("2007-03-26/") )  # subset post split
term.structure.df <- rbind(df.10.split,df.post.split) # rbind pre and post split data frames
tail(term.structure.df,50)
length(term.structure.df)

# Find last dates
last <- tail(term.structure.df$Date,1)
last.settle <- tail(settlement.dates$Date,1)
date.fill <- as.data.frame(seq(as.Date(last), as.Date(last.settle), "days"))
colnames(date.fill)[1] = "Date"
date.excl.wknds <- date.fill[which(weekdays(as.Date(date.fill$Date, format = "%Y/%m/%d"))
                              %in% c('Monday','Tuesday', 'Wednesday', 'Thursday', 'Friday')), ]

# Dummy data in order to fill out to future expiration dates
dates.df <- data.frame(date.excl.wknds,NA,NA,NA,NA,NA,NA,NA,NA)
colnames(dates.df)[1] <- "Date"
colnames(dates.df)[2] <- "C1"
colnames(dates.df)[3] <- "C2"
colnames(dates.df)[4] <- "C3"
colnames(dates.df)[5] <- "C4"
colnames(dates.df)[6] <- "C5"
colnames(dates.df)[7] <- "C6"
colnames(dates.df)[8] <- "C7"
colnames(dates.df)[9] <- "C8"
head(dates.df)
term.structure.df <- rbind(term.structure.df,dates.df)

# Calculate constant 30 day VIX1|VIX2 maturity
# Check weights http://www.ipathetn.com/US/16/en/details.app?instrumentId=259118
# USe method: http://app.velocitysharesetns.com/files/prospectus/PRICING_SUPPLEMENT_No__VLS_ETN-1_A36_long_form_2.pdf
# Join settlement dates
term.structure.df <- full_join(term.structure.df, settlement.dates, by = c("Date" = "Date"))
term.structure.df$Expiration.Date[is.na(term.structure.df$Expiration.Date)] <- 1  # Turn all NA to 1
# Calculate days in roll period
#term.structure.df <- subset(term.structure.df, Date >= as.POSIXct("2014-09-16") & Date <= as.POSIXct("2014-11-21")) # Subset between matches
term.structure.df$Day.in.roll.period <- NULL
for(i in 1:nrow(term.structure.df)) {
  term.structure.df$Day.in.roll.period[i] <- ifelse(term.structure.df$Expiration.Date[i + 1]==2,1,term.structure.df$Day.in.roll.period[i - 1]+1)
}

# Calculate dt = Total days in roll period
term.structure.df$dt <- NULL  # Initialize
for(i in nrow(term.structure.df):1) {
  term.structure.df$dt[i] <- ifelse(term.structure.df$Expiration.Date[i+2]==2,term.structure.df$Day.in.roll.period[i],NA)
}
library(zoo)
dt.total.days.in.roll.period  <- na.locf(term.structure.df$dt, fromLast = TRUE)
length(dt.total.days.in.roll.period)
diff <- nrow(term.structure.df) - length(dt.total.days.in.roll.period)  # Find difference in lengths
dt.total.days.in.roll.period<- c(dt.total.days.in.roll.period ,rep(NA,diff)) # Pad out with NA to match
term.structure.df <- data.frame(term.structure.df,dt.total.days.in.roll.period)

# Calculate dr = Days remaining in roll period
term.structure.df$dr <- NULL  # Initialize
for(i in nrow(term.structure.df):1) {
  term.structure.df$dr[i] <- ifelse(term.structure.df$Expiration.Date[i+2]==2,1,term.structure.df$dr[i + 1]+1)
}

# Calculate CRW(1,t) Front month weight at close of day
term.structure.df$CRW <- (term.structure.df$dr / term.structure.df$dt.total.days.in.roll.period)*1

# Adjust front and 2nd month contacts at Tuesday prior to Expiration
# Shift VIX2 at Tuesday before wednesday expiration
# Pass 1
term.structure.df$C.1.roll.prices.pass.1 <- NULL  # Initialize
for(i in nrow(term.structure.df):1) {
  term.structure.df$C.1.roll.prices.pass.1[i] <- ifelse(term.structure.df$Expiration.Date[i+1]==2,term.structure.df$C2[i],term.structure.df$C1[i])
}
# Pass 2
term.structure.df$C.1.roll.prices.pass.2 <- NULL  # Initialize
for(i in nrow(term.structure.df):1) {
  term.structure.df$C.1.roll.prices.pass.2[i] <- ifelse(term.structure.df$Expiration.Date[i]==2,term.structure.df$C2[i],term.structure.df$C.1.roll.prices.pass.1[i])
}
colnames(term.structure.df)[17] = "C1.Rolled"

# Shift VIX3 at Tuesday before wednesday expiration
# Pass 1
term.structure.df$C.2.roll.prices.pass.1 <- NULL  # Initialize
for(i in nrow(term.structure.df):1) {
  term.structure.df$C.2.roll.prices.pass.1[i] <- ifelse(term.structure.df$Expiration.Date[i+1]==2,term.structure.df$C3[i],term.structure.df$C2[i])
}
# Pass 2
term.structure.df$C.2.roll.prices.pass.2 <- NULL  # Initialize
for(i in nrow(term.structure.df):1) {
  term.structure.df$C.2.roll.prices.pass.2[i] <- ifelse(term.structure.df$Expiration.Date[i]==2,term.structure.df$C3[i],term.structure.df$C.2.roll.prices.pass.1[i])
}
colnames(term.structure.df)[19] = "C2.Rolled"

# 30 Day Constant Maturity futures price VIX1 | VIX2
# CRW(1,t) * VIX1 + (1-CRW(1,t)) * VIX2
term.structure.df$vix1.2.contstant <- (term.structure.df$CRW * term.structure.df$C1.Rolled) +((1-term.structure.df$CRW)* term.structure.df$C2.Rolled)
# Download spot VIX from CBOE
VIX <- fread("http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vixcurrent.csv", skip = 1)
VIX <- as.data.frame(VIX)
VIX$Date <- mdy(VIX$Date)
colnames(VIX)[5] <- "Spot_VIX"
head(VIX)
#VIX <- xts(VIX$vix_close, order.by=as.Date(VIX$Date, format = '%Y/%m/%d'))
#VIX1VIX2 = xts(term.structure.df$vix1.2.contstant, order.by=as.Date(term.structure.df$Date, format="%Y-%m-%d"))
tail(term.structure.df$vix1.2.contstant)
# Merge VIX1|VIX2 by common date
vix12.df <- data.frame(Date=term.structure.df$Date,VIX1VIX2=term.structure.df$vix1.2.contstant)
plot.df <- full_join(vix12.df, VIX, by = c("Date" = "Date"))

# Plot Spot Vix vs VIX1|VIX2 30 day constant maturity
ggplot() +
  geom_line(data=plot.df ,aes(x=Date,y=VIX1VIX2), colour="red3") +
  geom_line(data=plot.df,aes(x=Date,y=Spot_VIX), colour="#0072B2")+
  theme_classic()+
  scale_x_date(breaks = date_breaks("years"), labels = date_format("%Y"))+
  ggtitle("VIX Spot Vs VIX1|VIX2 30 Day Constant Maturity", subtitle = "") +
  labs(x="Date",y="Settlement")+
  theme(plot.title = element_text(hjust=0.5),plot.subtitle =element_text(hjust=0.5))+
  annotate("text", label = "Spot VIX", x = as.Date("2005-04-26"), y = 30, color = "#0072B2")+
  annotate("text", label = "VIX1|VIX2", x = as.Date("2011-10-26"), y = 60, color = "red3")

And this is the net result. Plotting Spot VIX to the VIX1|VIX2 constant 30 maturity:

Rplot136_constant

All code can be found on my github.

Thanks for reading, any questions comments or concerns please let me know!

Author: Andrew Bannerman

Integrity Inspector. Quantitative Analysis is a favorite past time.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s