Trading Strategy – Volatility Carry Trade

This strategy is going to look at a vega neutral volatility carry trading strategy. Two different futures contract will be traded, the VXX and VXZ. These contracts are rolling futures on the S&P 500 Vix index, the VXX is a short term future and the VXZ is a medium term future. Annualized Sharpe Ratio (Rf=0%) is 1.759449.

The strategy is very simple, the rules are:

  • If VXX / VXZ > 1 then in backwardation so do a reverse carry trade (buy VXX, sell VXZ)
  • If VXX / VXZ < 1 then do a carry trade (sell VXX, buy VXZ)

If the volatility spot price doesn’t change, then we’re extracting the cost of carry. Due to buying and selling (or vice versa) the short to mid term futures the vega exposure is hedged.

In the script the above two rules have been slightly changed, a slight offset is added/subtracted from the ratio. Essentially we want to be deep into contango zone or deep into backwardation zone before we trade, if we’re close to the flip point then don’t trade.

Section 1: Downloaded the data, and calculate the Open to Close return. This strategy will look for entry at the open and exit at the close.

Section 2: Regress the daily returns of VXX with VXZ to calculate the hedge ratio

Section 3: Generate the backwardation / contango signal

Section 4: Simulate the trading

Section 5: Analyse the performance

Onto the code:

?View Code RSPLUS
#Control variables for entering a trade
#Used to check for the level of contango / backwardation
#IF signal < signalLowLim then in contango and do a carry trade
#IF signal > signalUpperLim then in backwardation so do a reverse carry
#ELSE do nothing
signalLowLim <- 0.9
signalUpperLim <- 1.1
#Use volatility futures, shortdate vs medium dated
#VXX iPath S&P 500 VIX Short-Term Futures ETN (VXX)
#VXZ iPath S&P 500 VIX Mid-Term Futures ETN (VXZ)
symbolLst <- c("VXX","VXZ")
#Specify dates for downloading data, training models and running simulation
startDate = as.Date("2009-01-01") #Specify what date to get the prices from
hedgeTrainingStartDate = as.Date("2009-01-01") #Start date for training the hedge ratio
hedgeTrainingEndDate = as.Date("2009-05-01") #End date for training the hedge ratio
tradingStartDate = as.Date("2009-05-02") #Date to run the strategy from
### SECTION 1 - Download Data & Calculate Returns ###
#Download the data
symbolData <- new.env() #Make a new environment for quantmod to store data in
getSymbols(symbolLst, env = symbolData, src = "yahoo", from = startDate)
#Plan is to check for trade entry at the open, and exit the trade at the close
#So need to calculate the open to close return as they represens our profit or loss
#Calculate returns for VXX and VXZ
vxxRet <- (Cl(symbolData$VXX)/Op(symbolData$VXX))-1
colnames(vxxRet) <- "Ret"
symbolData$VXX <- cbind(symbolData$VXX,vxxRet)
vxzRet <- (Cl(symbolData$VXZ)/Op(symbolData$VXZ))-1
colnames(vxzRet) <- "Ret"
symbolData$VXZ <- cbind(symbolData$VXZ,vxzRet)
### SECTION 2 - Calculating the hedge ratio ###
#Want to work out a hedge ratio, so that we can remain Vega neutral (the futures contact are trading VEGA)
#Select a small amount of data for training the hedge model on
subVxx <- window(symbolData$VXX$Ret,start=hedgeTrainingStartDate ,end=hedgeTrainingEndDate)
subVxz <- window(symbolData$VXZ$Ret,start=hedgeTrainingStartDate ,end=hedgeTrainingEndDate)
datablock = na.omit(cbind(subVxx,subVxz))
colnames(datablock) <- c("VXX","VXZ")
#Simply linearly regress the returns of Vxx with Vxz
regression <- lm(datablock[,"VXZ"] ~ datablock[,"VXX"]) #Linear Regression
#Plot the regression
plot(x=as.vector(datablock[,"VXX"]), y=as.vector(datablock[,"VXZ"]), main=paste("Hedge Regression: XXZret =",regression$coefficient[2]," * RXXret + intercept"),
  	 xlab="Vxx Ret", ylab="Vxz ", pch=19)
abline(regression, col = 2 )
hedgeratio = regression$coefficient[2]
### SECTION 3 - Generate trading signals ###
#Generate Trading signal
#Check ratio to see if contango or backwarded volatility future
#If shortTermVega < midTermVega in contango so do carry trade
#If shortTermVega > midTermVega in backwardation so do reverse carry
#If VXX less than VXZ then want to short VXX and long VXZ
#Calculate the contango / backwardation signal
tSig <- Op(symbolData$VXX)/Op(symbolData$VXZ)
colnames(tSig) <- "Signal"
### SECTION 4 - Do the trading ###
#Generate the individual buy/sell signals for each of the futures contract
vxxSignal <- apply(tSig,1, function(x) {if(x<signalLowLim) { return (-1) } else { if(x>signalUpperLim) { return(1) } else { return (0) }}})
vxzSignal <- -1 * vxxSignal
#Strategy returns are simply the direction * the Open-to-Close return for the day
#Include the hedge ratio here so that we remain vega neutral
strategyReturns <- ((vxxSignal * symbolData$VXX$Ret) + hedgeratio * (vxzSignal * symbolData$VXZ$Ret) )
strategyReturns <- window(strategyReturns,start=tradingStartDate,end=Sys.Date(), extend = FALSE)
#Normalise the amount of money being invested on each trade so that we can compare to the S&P index later
strategyReturns <- strategyReturns * 1 / (1+abs(hedgeratio))
colnames(strategyReturns) <- "StrategyReturns"
#### Performance Analysis ###
#Get the S&P 500 index data
indexData <- new.env()
startDate = as.Date("2009-01-01") #Specify what date to get the prices from
getSymbols("^GSPC", env = indexData, src = "yahoo", from = startDate)
#Calculate returns for the index
indexRet <- (Cl(indexData$GSPC)-lag(Cl(indexData$GSPC),1))/lag(Cl(indexData$GSPC),1)
colnames(indexRet) <- "IndexRet"
zooTradeVec <- cbind(as.zoo(strategyReturns),as.zoo(indexRet)) #Convert to zoo object
colnames(zooTradeVec) <- c("Vol Carry Trade","S&P500")
zooTradeVec <- na.omit(zooTradeVec)
#Lets see how all the strategies faired against the index
charts.PerformanceSummary(zooTradeVec,main="Performance of Volatility Carry Trade",geometric=FALSE)
#Lets calculate a table of montly returns by year and strategy
cat("Calander Returns - Note 13.5 means a return of 13.5%\n")
#Calculate the sharpe ratio
cat("Sharpe Ratio")
#Calculate other statistics
cat("Other Statistics")
print(table.CAPM(zooTradeVec[,"Vol Carry Trade"],zooTradeVec[,"S&P500"]))
#Lets make a boxplot of the returns
#Set the plotting area to a 2 by 2 grid
#Plot various histograms with different overlays added
chart.Histogram(zooTradeVec, main = "Plain", methods = NULL)
chart.Histogram(zooTradeVec, main = "Density", breaks=40, methods = c("add.density", "add.normal"))
chart.Histogram(zooTradeVec, main = "Skew and Kurt", methods = c("add.centered", "add.rug"))
chart.Histogram(zooTradeVec, main = "Risk Measures", methods = c("add.risk"))

5 thoughts on “Trading Strategy – Volatility Carry Trade

  1. GREAT blog! This has to be one of the best I’ve run across! I would love to collaborate on any projects you have in R that you could use a hand with. Do you trade any of these strategies yourself?


  2. tricky part is balancing vol carry and tail risk. What would be interesting would be to model out the implied scenario probabilities of the option vol surface and see if there are any interesting pricing abnormalities. chances are its clean pricing bc there are so many HFs that look to arb the market and S&P options market is so deep and liquid. Still, there definitely has to be some inefficiencies in there somewhere. Its dangerous to play the vol carry game, but it is very profitable if you can balance the tail risk with the steep contango.

    • You could try being vega flat by hedging your short term future with a longer dated one.

      Some simple filters can elevate this strategy to Sharpe ratios in the region greater than 2.5

  3. Hello!

    strategyReturns <- ((vxxSignal * symbolData$VXX$Ret) + hedgeratio * (vxzSignal * symbolData$VXZ$Ret) )

    Why we multiply hedgeratio to VXZ? To my mind, we should multiply hedgeratio to VXX.

    Thank you!

Leave a Reply

Your email address will not be published. Required fields are marked *