This post will deal with a quick, finger in the air way of seeing how well a strategy scales–namely, how sensitive it is to latency between signal and execution, using a simple volatility trading strategy as an example. The signal will be the VIX/VXV ratio trading VXX and XIV, an idea I got from Volatility Made Simple’s amazing blog, particularly this post. The three signals compared will be the “magical thinking” signal (observe the close, buy the close, named from the ruleOrderProc setting in quantstrat), buy on next-day open, and buy on next-day close.
Let’s get started.
require(downloader) require(PerformanceAnalytics) require(IKTrading) require(TTR) download("http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vxvdailyprices.csv", destfile="vxvData.csv") download("https://dl.dropboxusercontent.com/s/jk6der1s5lxtcfy/XIVlong.TXT", destfile="longXIV.txt") download("https://dl.dropboxusercontent.com/s/950x55x7jtm9x2q/VXXlong.TXT", destfile="longVXX.txt") #requires downloader package getSymbols('^VIX', from = '1990-01-01') 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)) vxv <- xts(read.zoo("vxvData.csv", header=TRUE, sep=",", format="%m/%d/%Y", skip=2)) vixVxv <- Cl(VIX)/Cl(vxv) 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)) vxxCloseRets <- Return.calculate(Cl(vxx)) vxxOpenRets <- Return.calculate(Op(vxx)) xivCloseRets <- Return.calculate(Cl(xiv)) xivOpenRets <- Return.calculate(Op(xiv)) vxxSig <- vixVxv > 1 xivSig <- 1-vxxSig magicThinking <- vxxCloseRets * lag(vxxSig) + xivCloseRets * lag(xivSig) nextOpen <- vxxOpenRets * lag(vxxSig, 2) + xivOpenRets * lag(xivSig, 2) nextClose <- vxxCloseRets * lag(vxxSig, 2) + xivCloseRets * lag(xivSig, 2) tradeWholeDay <- (nextOpen + nextClose)/2 compare <- na.omit(cbind(magicThinking, nextOpen, nextClose, tradeWholeDay)) colnames(compare) <- c("Magic Thinking", "Next Open", "Next Close", "Execute Through Next Day") charts.PerformanceSummary(compare) rbind(table.AnnualizedReturns(compare), maxDrawdown(compare), CalmarRatio(compare)) par(mfrow=c(1,1)) chart.TimeSeries(log(cumprod(1+compare), base = 10), legend.loc='topleft', ylab='log base 10 of additional equity', main = 'VIX vx. VXV different execution times')
So here’s the run-through. In addition to the magical thinking strategy (observe the close, buy that same close), I tested three other variants–a variant which transacts the next open, a variant which transacts the next close, and the average of those two. Effectively, I feel these three could give a sense of a strategy’s performance under more realistic conditions–that is, how well does the strategy perform if transacted throughout the day, assuming you’re managing a sum of money too large to just plow into the market in the closing minutes (and if you hope to get rich off of trading, you will have a larger sum of money than the amount you can apply magical thinking to). Ideally, I’d use VWAP pricing, but as that’s not available for free anywhere I know of, that means that readers can’t replicate it even if I had such data.
In any case, here are the results.
Log scale (for Mr. Tony Cooper and others):
Magic Thinking Next Open Next Close Execute Through Next Day Annualized Return 0.814100 0.8922000 0.5932000 0.821900 Annualized Std Dev 0.622800 0.6533000 0.6226000 0.558100 Annualized Sharpe (Rf=0%) 1.307100 1.3656000 0.9529000 1.472600 Worst Drawdown 0.566122 0.5635336 0.6442294 0.601014 Calmar Ratio 1.437989 1.5831686 0.9208586 1.367510
My reaction? The execute on next day’s close performance being vastly lower than the other configurations (and that deterioration occurring in the most recent years) essentially means that the fills will have to come pretty quickly at the beginning of the day. While the strategy seems somewhat scalable through the lens of this finger-in-the-air technique, in my opinion, if the first full day of possible execution after signal reception will tank a strategy from a 1.44 Calmar to a .92, that’s a massive drop-off, after holding everything else constant. In my opinion, I think this is quite a valid question to ask anyone who simply sells signals, as opposed to manages assets. Namely, how sensitive are the signals to execution on the next day? After all, unless those signals come at 3:55 PM, one is most likely going to be getting filled the next day.
Now, while this strategy is a bit of a tomato can in terms of how good volatility trading strategies can get (they can get a *lot* better in my opinion), I think it made for a simple little demonstration of this technique. Again, a huge thank you to Mr. Helmuth Vollmeier for so kindly keeping up his dropbox all this time for the volatility data!
Thanks for reading.
NOTE: I am currently contracting in a data science capacity in Chicago. You can email me at [email protected], or find me on my LinkedIn here. I’m always open to beers after work if you’re in the Chicago area.
NOTE 2: Today, on October 21, 2015, if you’re in Chicago, there’s a Chicago R Users Group conference at Jaks Tap at 6:00 PM. Free pizza, beer, networking, and R, hosted by Paul Teetor, who’s a finance guy. Hope to see you there.