Backtesting a Trading Strategy
[This article was first published on Adventures in Statistical Computing, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
I’ve ordered Time Series Analysis and Its Applications: With R Examples (Springer Texts in Statistics) to help me up the time series in R learning curve. So far what I have seen it looks good. The author has a good page with the issues in R and time series. The book should arrive by the end of the week.Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
In the meantime, I came across a trading strategy while reading an article provide on John Mauldin’s “Over My Shoulder” service (which I highly recommend). The crux of it was that in the bear market that started with the tech bubble crash, a strategy of betting on mean reversion of the S&P500 generated significant returns. Naturally I wanted to test.
Please note, I am not recommending anything that follows. Do your homework and speak with an investment professional if you have questions.
The strategy is to go long the S&P500 when the market closes at a maximum over the previous 3 days. Reverse the trade and go long when the market closes at the minimum over the previous 3 days. ETFs make this strategy relatively easy to trade. SPY will be our vehicle for being long the S&P500 and SH will be our vehicle for going short.
The SH began trading on 06/21/2006. We focus our backtesting from that point until now.
Using the importSeries() function we previously created, get all the values for SPY and SH.
to = “2012-01-14”
from = “2006-06-21”
spy = importSeries(“spy”,to=to,from=from)
sh = importSeries(“sh”,to=to, from=from)
series = merge(spy,sh)[,c(“spy.Open”,“spy.Close”,“spy.Return”,
“sh.Open”,“sh.Close”,“sh.Return”)]
We need to create some additional timeSeries to hold
- Long/Short Flag — lets us know the current status of our holdings.
- Trade Flag — signals that we instituted a trade on this date.
- Strat.Returns — nominal return for the day with the strategy.
- Dollar Amount — a gross dollar value of the portfolio assuming a $10,000 dollar value on 06/21/2006, and a $2 transaction fee when we trade.
After we calculate the strategy we will also create a gross return series from the Dollar Amount series.
f = function(x) 0*x
ls = fapply(series[,1],FUN=f)
colnames(ls) = “long_short”
rets = fapply(series[,1],FUN=f)
colnames(rets) = “Strat.Return”
trades = rets;
colnames(trades) = “trade”
amt = rets
colnames(amt) = “DollarAmount”
amt[seq(1,3)] = 10000
We will loop from the 3rd day of the series until the end and calculate the values.
n = nrow(series)
for (i in seq(3,n)){
maxSpy = max(series[seq(i,i–2),“spy.Close”])
minSpy = min(series[seq(i,i–2),“spy.Close”])
#get the appropriate return
if (ls[i–1] == 1){
rets[i] = series[i,“spy.Return”]
} else if (ls[i–1] == –1){
rets[i] = series[i,“sh.Return”]
}
#change long/short as appropriate
if (maxSpy == series[i,“spy.Close”]){
ls[i] = –1
} else if (minSpy == series[i,“spy.Close”]){
ls[i] = 1
} else {
ls[i] = ls[i–1]
}
#mark a trade if we did one
if (ls[i] != ls[i–1]) trades[i] = 1
#Calculate the dollar amount
amt[i] = amt[i–1]*exp(rets[i])
if (trades[i]) amt[i] = amt[i] – 2
}
#Calculate gross returns
amt2 = returns(amt)
colnames(amt2) = “Strat.DollarReturns”
Next let’s output the annualized returns and CAPM statistics. We will do this for the entire period as well as for each year.
#Merge all the series
series=merge(series,ls)
series = merge(series,rets)
series = merge(series,trades)
series = merge(series,amt)
series = merge(series,amt2)
vars = c(“spy.Return”,“sh.Return”,“Strat.DollarReturns”)
series[1,vars] = 0
#Calculate the Annualized Statistics and the CAPM statistics
print(“Total”)
table.AnnualizedReturns(series[,vars])
table.CAPM(as.xts(series[,“Strat.Return”,drop=FALSE]),as.xts(series[,“spy.Return”,drop=FALSE]))
#Overall cumulative returns
png(“c:\\temp\\overall.png”)
chart.CumReturns(series[,vars],main=“Total Return”,legend.loc=“topleft”)
dev.off()
#Create the outputs for each year.
for (year in seq(2006,2011)){
start = paste(year,“-01-01”,sep=“”)
end = paste(year,“-12-31”,sep=“”)
title = paste(“Total Return “,year,sep=“”)
file = paste(“c:\\temp\\”,year,“.png”,sep=“”)
s = window(series,start=start, end=end)
png(file)
chart.CumReturns(s[,vars],main=title,legend.loc=“topleft”)
dev.off()
print(paste(year,“Returns”,sep=” “))
print(table.AnnualizedReturns(s[,vars]))
print(table.CAPM(as.xts(s[,“Strat.Return”,drop=FALSE]),as.xts(s[,“spy.Return”,drop=FALSE])))
}
spy.Return | sh.Return | Strat.DollarReturns | |
Annualized Return | -0.0067 | -0.0903 | 0.3535 |
Annualized Std Dev | 0.2529 | 0.2512 | 0.2508 |
Annualized Sharpe (Rf=0%) | -0.0263 | -0.3593 | 1.4092 |
Strat.Return to spy.Return | |
Alpha | 0.0013 |
Beta | 0.1921 |
Beta+ | 0.6830 |
Beta- | -0.0803 |
R-squared | 0.0374 |
Annualized Alpha | 0.3990 |
Correlation | 0.1934 |
Correlation p-value | 0.0000 |
Tracking Error | 0.7447 |
Active Premium | 0.3694 |
Information Ratio | 0.4960 |
Treynor Ratio | 1.8885 |
So there seems to be something to this strategy. The yearly return and CAPM tables are close to the total. Some years are better than others. I will leave it to you to create and study them (mostly to save space on here).
There are things to think of:
- It should be noted that this strategy is NOT tax efficient — any gains will be taxed at the short term capital gains rate.
- There were 411 trades. A trade involves buying and selling, so 822 times would you be charged a brokerage fee. I assumed 1 dollar per buy/sell — what is charged by Interactive Brokers. Using someone like TD Ameritrade would cost FAR more.
- This also assumes that you can buy and sell at the market closing price. Something that is possible, but slippage will occur.
To leave a comment for the author, please follow the link and comment on their blog: Adventures in Statistical Computing.
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.