Structural “Arbitrage”: a Working Long-History Backtest
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
For this post, I would like to give my sincere thanks to Mr. Helmuth Vollmeier, for providing the long history daily data of XIV. It is tremendously helpful. Also, I put arbitrage in quotations now, for reasons we’ll see in this post.
To begin, here’s a script I wrote to create this backtest.
require(downloader) download("https://dl.dropboxusercontent.com/s/jk6der1s5lxtcfy/XIVlong.TXT", destfile="longXIV.txt") XIV <- read.csv("longXIV.txt", header=TRUE, stringsAsFactors=FALSE) head(XIV) XIV <- xts(XIV[,2:5], order.by=as.Date(XIV$Date)) XIVrets <- Return.calculate(Cl(XIV)) getSymbols("TLT", from="1990-01-01") TLTrets <- Return.calculate(Cl(TLT)) adTltRets <- Return.calculate(Ad(TLT)) both <- merge(XIVrets, TLTrets, join='inner') bothAd <- merge(XIVrets, adTltRets, join='inner') stratTest <- Return.rebalancing(both, weights=c(.4, 1.8), rebalance_on="weeks", geometric=FALSE) adStratTest <- Return.rebalancing(bothAd, weights=c(.4, 1.8), rebalance_on="weeks", geometric=FALSE) bothStrats <- merge(stratTest, adStratTest) colnames(bothStrats) <- c("Close TLT", "Adjusted TLT") getSymbols("SPY", from="1990-01-01") ClSPY <- Return.calculate(Cl(SPY)) AdSPY <- Return.calculate(Ad(SPY)) SPYs <- cbind(ClSPY, AdSPY) stratsAndBMs <- merge(bothStrats, SPYs, join='inner') charts.PerformanceSummary(stratsAndBMs)
First of all, in order to download files that start off with the https stem, users will need to install the “downloader” package from CRAN. So a simple
install.packages("downloader")
will work just fine, and a thank you to Winston Chang for this package.
Beyond this, the way to turn a data frame to an xts (xts objects are the foundation of almost all serious financial analysis in R) is to pass in a data frame object along with a recognized format for a date. The default date format in R is “yyyy-mm-dd”, while something like 02/20/2014 would be “%mm/%dd/%yyyy”.
After this point, the syntax is the standard fare for computing returns, joining return series, and creating a summary chart. I used both the close and the adjusted price of 3x leveraged TLT (not an absolute replication of TMF, but conceptually very similar), in order to satisfy both the close-price and adjusted-price camps when dealing with return data. I myself prefer close prices, rather than making assumptions about dividend reinvestment, though sometimes splits force the issue.
Here’s a quick glance at the performance comparisons.
While the equity curves for the strategies look good (adjusted in red, close in black), what concerns me more is the drawdown plot. As can be seen, this strategy would have resulted in a protracted and severe drawdown from 2007 through 2010, hitting around 50% total drawdown, which should be far beyond the risk tolerance of…just about anyone. In short, this is far from any arbitrage. From a hypothesis standpoint, if someone were indeed to say “short volatility”, one would expect to see drawdowns in some form in the financial crisis. However, the drawdowns for this strategy are on par with that of the S&P 500 itself, which is to say, pretty bad.
Here’s a closer look at 2007-2010 for the strategies and the corresponding S&P 500 (close returns in green, adjusted in blue):
Basically, the performance is barely distinguishable form the S&P 500 at its worst, which makes this far from an effective strategy at its worst.
Here are the risk/return metrics for the strategies with the benchmarks for comparison:
> Return.annualized(stratsAndBMs) Close.TLT Adjusted.TLT SPY.Close SPY.Adjusted Annualized Return 0.2328424 0.3239631 0.05336649 0.0748746 > SharpeRatio.annualized(stratsAndBMs) Close.TLT Adjusted.TLT SPY.Close SPY.Adjusted Annualized Sharpe Ratio (Rf=0%) 0.8780168 1.226562 0.2672673 0.3752571 > maxDrawdown(stratsAndBMs) Close.TLT Adjusted.TLT SPY.Close SPY.Adjusted Worst Drawdown 0.5040189 0.4256037 0.5647367 0.5518672
Here are the return, drawdown, and Sharpe ratio statistics by year:
> apply.yearly(stratsAndBMs, Return.cumulative) Close.TLT Adjusted.TLT SPY.Close SPY.Adjusted 2004-12-31 0.43091127 0.53673252 0.073541167 0.09027015 2005-12-30 0.38908539 0.50726218 0.030115000 0.04834811 2006-12-29 0.20547671 0.30869571 0.137418681 0.15843582 2007-12-31 -0.12139177 -0.04277199 0.032410676 0.05142241 2008-12-31 0.02308329 0.10593220 -0.382805554 -0.36791039 2009-12-31 -0.15364860 -0.09427527 0.234929078 0.26344690 2010-12-31 0.64545635 0.76914182 0.128409907 0.15053339 2011-12-30 0.37738081 0.47880348 -0.001988072 0.01897321 2012-12-31 0.55343030 0.62319271 0.134741036 0.15991238 2013-12-31 0.01191596 0.06800805 0.296889263 0.32309145 2014-10-01 0.38674137 0.44448550 0.052303861 0.06697777 > apply.yearly(stratsAndBMs, maxDrawdown) Close.TLT Adjusted.TLT SPY.Close SPY.Adjusted 2004-12-31 0.1626657 0.1508961 0.07304589 0.06961279 2005-12-30 0.1578365 0.1528919 0.07321443 0.06960143 2006-12-29 0.2504912 0.2297468 0.07593123 0.07592093 2007-12-31 0.3289898 0.3006318 0.09924591 0.09921458 2008-12-31 0.4309851 0.4236635 0.48396143 0.47582236 2009-12-31 0.3833131 0.3668421 0.27131700 0.27123750 2010-12-31 0.1270308 0.1219816 0.16098842 0.15703744 2011-12-30 0.1628584 0.1627968 0.19423880 0.18608682 2012-12-31 0.1123245 0.1054862 0.09686971 0.09686039 2013-12-31 0.2840916 0.2782082 0.06047736 0.05550422 2014-10-01 0.1065488 0.1023469 0.05696031 0.05698600 > apply.yearly(stratsAndBMs, SharpeRatio.annualized) Close.TLT Adjusted.TLT SPY.Close SPY.Adjusted 2004-12-31 2.90726854 3.7072225 0.893324847 1.10342092 2005-12-30 1.96324189 2.5862100 0.291120531 0.46836705 2006-12-29 0.95902528 1.4533427 1.369071018 1.58940411 2007-12-31 -0.47792925 -0.1693763 0.205060111 0.32457756 2008-12-31 0.07389051 0.3388196 -0.925155310 -0.88807262 2009-12-31 -0.45741108 -0.2815325 0.879806802 0.98927701 2010-12-31 2.31270808 2.7875988 0.714706742 0.83968381 2011-12-30 1.29489799 1.6371371 -0.008639479 0.08243543 2012-12-31 2.12645653 2.3967509 1.061570060 1.26650058 2013-12-31 0.04205873 0.2408626 2.667267167 2.91640716 2014-10-01 2.54201473 2.9678436 0.683911514 0.88274606
In short, when the strategy is good, it’s terrific. But when it’s bad, it’s terrible. Furthermore, even in good years, the drawdowns are definitely eye-popping, on the order of 10-15% when things are going smoothly, and anywhere between 25%-43% drawdown when they don’t, and those may not paint the whole story, either, as those are single-year max drawdowns, when one drawdown could have spanned years (which it did, in the financial crisis), getting worse than 50%. Indeed, far from an arbitrage, this strategy seems to be a bet on substantial returns usually, with painful drawdowns when incorrect.
For the record, here is the correlation table between the strategy and the benchmark:
> cor(stratsAndBMs) Close.TLT Adjusted.TLT SPY.Close SPY.Adjusted Close.TLT 1.0000000 0.9970102 0.2366392 0.2378570 Adjusted.TLT 0.9970102 1.0000000 0.2384042 0.2395180 SPY.Close 0.2366392 0.2384042 1.0000000 0.9987201 SPY.Adjusted 0.2378570 0.2395180 0.9987201 1.0000000
Of course, this does not mean that the strategy is pure alpha due to the low correlation with the S&P 500, just that the S&P may not be the greatest benchmark to measure it against–after all, this strategy carries a massive amount of risk in its raw form as posted by Harry Long on Seeking Alpha.
Thanks for reading.
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.