Volatility Stat-Arb Shenanigans
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
This post deals with an impossible-to-implement statistical arbitrage strategy using VXX and XIV. The strategy is simple: if the average daily return of VXX and XIV was positive, short both of them at the close. This strategy makes two assumptions of varying dubiousness: that one can “observe the close and act on the close”, and that one can short VXX and XIV.
So, recently, I decided to play around with everyone’s two favorite instruments on this blog–VXX and XIV, with the idea that “hey, these two instruments are diametrically opposed, so shouldn’t there be a stat-arb trade here?”
So, in order to do a lick-finger-in-the-air visualization, I implemented Mike Harris’s momersion indicator.
momersion <- function(R, n, returnLag = 1) { momentum <- sign(R * lag(R, returnLag)) momentum[momentum < 0] <- 0 momersion <- runSum(momentum, n = n)/n * 100 colnames(momersion) <- "momersion" return(momersion) }
And then I ran the spread through it.
xiv <- xts(read.zoo("longXIV.txt", format="%Y-%m-%d", sep=",", header=TRUE)) vxx <- xts(read.zoo("longVXX.txt", format="%Y-%m-%d", sep=",", header=TRUE)) xivRets <- Return.calculate(Cl(xiv)) vxxRets <- Return.calculate(Cl(vxx)) volSpread <- xivRets + vxxRets volSpreadMomersion <- momersion(volSpread, n = 252) plot(volSpreadMomersion)
In other words, this spread is certainly mean-reverting at just about all times.
And here is the code for the results from 2011 onward, from when the XIV and VXX actually started trading.
#both sides sig <- -lag(sign(volSpread)) longShort <- sig * volSpread charts.PerformanceSummary(longShort['2011::'], main = 'long and short spread') #long spread only sig <- -lag(sign(volSpread)) sig[sig < 0] <- 0 longOnly <- sig * volSpread charts.PerformanceSummary(longOnly['2011::'], main = 'long spread only') #short spread only sig <- -lag(sign(volSpread)) sig[sig > 0] <- 0 shortOnly <- sig * volSpread charts.PerformanceSummary(shortOnly['2011::'], main = 'short spread only') threeStrats <- na.omit(cbind(longShort, longOnly, shortOnly))["2011::"] colnames(threeStrats) <- c("LongShort", "Long", "Short") rbind(table.AnnualizedReturns(threeStrats), CalmarRatio(threeStrats))
Here are the equity curves:
Long-short:
Long-only:
Short-only:
With the following statistics:
LongShort Long Short Annualized Return 0.115400 0.0015000 0.113600 Annualized Std Dev 0.049800 0.0412000 0.027900 Annualized Sharpe (Rf=0%) 2.317400 0.0374000 4.072100 Calmar Ratio 1.700522 0.0166862 7.430481
In other words, the short side is absolutely amazing as a trade–except for the one small fact of having it be impossible to actually execute, or at least as far as I’m aware. Anyhow, this was simply a for-fun post, but hopefully it served some purpose.
Thanks for reading.
NOTE: I am currently contracting and am looking to network in the Chicago area. You can find my LinkedIn here.
R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.