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

library("quantmod") library("PerformanceAnalytics") #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" #plot(cumsum(strategyReturns)) #SECTION 5 #### 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 dev.new() 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") print(table.CalendarReturns(zooTradeVec)) #Calculate the sharpe ratio cat("Sharpe Ratio") print(SharpeRatio.annualized(zooTradeVec)) #Calculate other statistics cat("Other Statistics") print(table.CAPM(zooTradeVec[,"Vol Carry Trade"],zooTradeVec[,"S&P500"])) dev.new() #Lets make a boxplot of the returns chart.Boxplot(zooTradeVec) dev.new() #Set the plotting area to a 2 by 2 grid layout(rbind(c(1,2),c(3,4))) #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")) |

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?

Thanks!

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