Comparing Some Strategies from Easy Volatility Investing, and the Table.Drawdowns Command
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
This post will be about comparing strategies from the paper “Easy Volatility Investing”, along with a demonstration of R’s table.Drawdowns command.
First off, before going further, while I think the execution assumptions found in EVI don’t lend the strategies well to actual live trading (although their risk/reward tradeoffs also leave a lot of room for improvement), I think these strategies are great as benchmarks.
So, some time ago, I did an out-of-sample test for one of the strategies found in EVI, which can be found here.
Using the same source of data, I also obtained data for SPY (though, again, AlphaVantage can also provide this service for free for those that don’t use Quandl).
Here’s the new code.
require(downloader) require(quantmod) require(PerformanceAnalytics) require(TTR) require(Quandl) require(data.table) download("http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vix3mdailyprices.csv", destfile="vxvData.csv") VIX <- fread("http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vixcurrent.csv", skip = 1) VIXdates <- VIX$Date VIX$Date <- NULL; VIX <- xts(VIX, order.by=as.Date(VIXdates, format = '%m/%d/%Y')) vxv <- xts(read.zoo("vxvData.csv", header=TRUE, sep=",", format="%m/%d/%Y", skip=2)) ma_vRatio <- SMA(Cl(VIX)/Cl(vxv), 10) xivSigVratio <- ma_vRatio < 1 vxxSigVratio <- ma_vRatio > 1 # V-ratio (VXV/VXMT) vRatio <- lag(xivSigVratio) * xivRets + lag(vxxSigVratio) * vxxRets # vRatio <- lag(xivSigVratio, 2) * xivRets + lag(vxxSigVratio, 2) * vxxRets # Volatility Risk Premium Strategy spy <- Quandl("EOD/SPY", start_date='1990-01-01', type = 'xts') spyRets <- Return.calculate(spy$Adj_Close) histVol <- runSD(spyRets, n = 10, sample = FALSE) * sqrt(252) * 100 vixDiff <- Cl(VIX) - histVol maVixDiff <- SMA(vixDiff, 5) vrpXivSig <- maVixDiff > 0 vrpVxxSig <- maVixDiff < 0 vrpRets <- lag(vrpXivSig, 1) * xivRets + lag(vrpVxxSig, 1) * vxxRets obsCloseMomentum <- magicThinking # from previous post compare <- na.omit(cbind(xivRets, obsCloseMomentum, vRatio, vrpRets)) colnames(compare) <- c("BH_XIV", "DDN_Momentum", "DDN_VRatio", "DDN_VRP")
So, an explanation: there are four return streams here–buy and hold XIV, the DDN momentum from a previous post, and two other strategies.
The simpler one, called the VRatio is simply the ratio of the VIX over the VXV. Near the close, check this quantity. If this is less than one, buy XIV, otherwise, buy VXX.
The other one, called the Volatility Risk Premium strategy (or VRP for short), compares the 10 day historical volatility (that is, the annualized running ten day standard deviation) of the S&P 500, subtracts it from the VIX, and takes a 5 day moving average of that. Near the close, when that’s above zero (that is, VIX is higher than historical volatility), go long XIV, otherwise, go long VXX.
Again, all of these strategies are effectively “observe near/at the close, buy at the close”, so are useful for demonstration purposes, though not for implementation purposes on any large account without incurring market impact.
Here are the results, since 2011 (that is, around the time of XIV’s actual inception):
To note, both the momentum and the VRP strategy underperform buying and holding XIV since 2011. The VRatio strategy, on the other hand, does outperform.
Here’s a summary statistics function that compiles some top-level performance metrics.
stratStats <- function(rets) { stats <- rbind(table.AnnualizedReturns(rets), maxDrawdown(rets)) stats[5,] <- stats[1,]/stats[4,] stats[6,] <- stats[1,]/UlcerIndex(rets) rownames(stats)[4] <- "Worst Drawdown" rownames(stats)[5] <- "Calmar Ratio" rownames(stats)[6] <- "Ulcer Performance Index" return(stats) }
And the result:
> stratStats(compare['2011::']) BH_XIV DDN_Momentum DDN_VRatio DDN_VRP Annualized Return 0.3801000 0.2837000 0.4539000 0.2572000 Annualized Std Dev 0.6323000 0.5706000 0.6328000 0.6326000 Annualized Sharpe (Rf=0%) 0.6012000 0.4973000 0.7172000 0.4066000 Worst Drawdown 0.7438706 0.6927479 0.7665093 0.7174481 Calmar Ratio 0.5109759 0.4095285 0.5921650 0.3584929 Ulcer Performance Index 1.1352168 1.2076995 1.5291637 0.7555808
To note, all of the benchmark strategies suffered very large drawdowns since XIV’s inception, which we can examine using the table.Drawdowns command, as seen below:
> table.Drawdowns(compare[,1]['2011::'], top = 5) From Trough To Depth Length To Trough Recovery 1 2011-07-08 2011-11-25 2012-11-26 -0.7439 349 99 250 2 2015-06-24 2016-02-11 2016-12-21 -0.6783 379 161 218 3 2014-07-07 2015-01-30 2015-06-11 -0.4718 236 145 91 4 2011-02-15 2011-03-16 2011-04-20 -0.3013 46 21 25 5 2013-04-15 2013-06-24 2013-07-22 -0.2877 69 50 19 > table.Drawdowns(compare[,2]['2011::'], top = 5) From Trough To Depth Length To Trough Recovery 1 2014-07-07 2016-06-27 2017-03-13 -0.6927 677 499 178 2 2012-03-27 2012-06-13 2012-09-13 -0.4321 119 55 64 3 2011-10-04 2011-10-28 2012-03-21 -0.3621 117 19 98 4 2011-02-15 2011-03-16 2011-04-21 -0.3013 47 21 26 5 2011-06-01 2011-08-04 2011-08-18 -0.2723 56 46 10 > table.Drawdowns(compare[,3]['2011::'], top = 5) From Trough To Depth Length To Trough Recovery 1 2014-01-23 2016-02-11 2017-02-14 -0.7665 772 518 254 2 2011-09-13 2011-11-25 2012-03-21 -0.5566 132 53 79 3 2012-03-27 2012-06-01 2012-07-19 -0.3900 80 47 33 4 2011-02-15 2011-03-16 2011-04-20 -0.3013 46 21 25 5 2013-04-15 2013-06-24 2013-07-22 -0.2877 69 50 19 > table.Drawdowns(compare[,4]['2011::'], top = 5) From Trough To Depth Length To Trough Recovery 1 2015-06-24 2016-02-11 2017-10-11 -0.7174 581 161 420 2 2011-07-08 2011-10-03 2012-02-03 -0.6259 146 61 85 3 2014-07-07 2014-12-16 2015-05-21 -0.4818 222 115 107 4 2013-02-20 2013-07-08 2014-06-10 -0.4108 329 96 233 5 2012-03-27 2012-06-01 2012-07-17 -0.3900 78 47 31
Note that the table.Drawdowns command only examines one return stream at a time. Furthermore, the top argument specifies how many drawdowns to look at, sorted by greatest drawdown first.
One reason I think that these strategies seem to suffer the drawdowns they do is that they’re either all-in on one asset, or its exact opposite, with no room for error.
One last thing, for the curious, here is the comparison with my strategy since 2011 (essentially XIV inception) benchmarked against the strategies in EVI (which I have been trading with live capital since September, and have recently opened a subscription service for):
stratStats(compare['2011::']) QST_vol BH_XIV DDN_Momentum DDN_VRatio DDN_VRP Annualized Return 0.8133000 0.3801000 0.2837000 0.4539000 0.2572000 Annualized Std Dev 0.3530000 0.6323000 0.5706000 0.6328000 0.6326000 Annualized Sharpe (Rf=0%) 2.3040000 0.6012000 0.4973000 0.7172000 0.4066000 Worst Drawdown 0.2480087 0.7438706 0.6927479 0.7665093 0.7174481 Calmar Ratio 3.2793211 0.5109759 0.4095285 0.5921650 0.3584929 Ulcer Performance Index 10.4220721 1.1352168 1.2076995 1.5291637 0.7555808
Thanks for reading.
NOTE: I am currently looking for networking and full-time opportunities related to my skill set. My LinkedIn profile can be found 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.