[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)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”< o:p>
from = “2006-06-21”< o:p>
spy = importSeries(“spy”,to=to,from=from)< o:p>
sh = importSeries(“sh”,to=to, from=from)< o:p>
series = merge(spy,sh)[,c(“spy.Open”,“spy.Close”,“spy.Return”,< o:p>
“sh.Open”,“sh.Close”,“sh.Return”)]< o:p>
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< o:p>
ls = fapply(series[,1],FUN=f)< o:p>
colnames(ls) = “long_short”< o:p>
rets = fapply(series[,1],FUN=f)< o:p>
colnames(rets) = “Strat.Return”< o:p>
trades = rets;< o:p>
colnames(trades) = “trade”< o:p>
amt = rets< o:p>
colnames(amt) = “DollarAmount”< o:p>
amt[seq(1,3)] = 10000< o:p>
We will loop from the 3rd day of the series until the end and calculate the values.
n = nrow(series)< o:p>
for (i in seq(3,n)){< o:p>
maxSpy = max(series[seq(i,i–2),“spy.Close”])< o:p>
minSpy = min(series[seq(i,i–2),“spy.Close”])< o:p>
< o:p>
#get the appropriate return< o:p>
if (ls[i–1] == 1){< o:p>
rets[i] = series[i,“spy.Return”]< o:p>
} else if (ls[i–1] == –1){< o:p>
rets[i] = series[i,“sh.Return”]< o:p>
}< o:p>
< o:p>
< o:p>
#change long/short as appropriate< o:p>
if (maxSpy == series[i,“spy.Close”]){< o:p>
ls[i] = –1< o:p>
} else if (minSpy == series[i,“spy.Close”]){< o:p>
ls[i] = 1< o:p>
} else {< o:p>
ls[i] = ls[i–1]< o:p>
}< o:p>
< o:p>
#mark a trade if we did one< o:p>
if (ls[i] != ls[i–1]) trades[i] = 1< o:p>
< o:p>
#Calculate the dollar amount< o:p>
amt[i] = amt[i–1]*exp(rets[i])< o:p>
if (trades[i]) amt[i] = amt[i] – 2< o:p>
}< o:p>
#Calculate gross returns< o:p>
amt2 = returns(amt)< o:p>
colnames(amt2) = “Strat.DollarReturns”< o:p>
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< o:p>
series=merge(series,ls)< o:p>
series = merge(series,rets)< o:p>
series = merge(series,trades)< o:p>
series = merge(series,amt)< o:p>
series = merge(series,amt2)< o:p>
vars = c(“spy.Return”,“sh.Return”,“Strat.DollarReturns”)< o:p>
series[1,vars] = 0< o:p>
#Calculate the Annualized Statistics and the CAPM statistics< o:p>
print(“Total”)< o:p>
table.AnnualizedReturns(series[,vars])< o:p>
table.CAPM(as.xts(series[,“Strat.Return”,drop=FALSE]),as.xts(series[,“spy.Return”,drop=FALSE]))< o:p>
#Overall cumulative returns< o:p>
png(“c:\\temp\\overall.png”)< o:p>
chart.CumReturns(series[,vars],main=“Total Return”,legend.loc=“topleft”)< o:p>
dev.off()< o:p>
#Create the outputs for each year.< o:p>
for (year in seq(2006,2011)){< o:p>
start = paste(year,“-01-01”,sep=“”)< o:p>
end = paste(year,“-12-31”,sep=“”)< o:p>
< o:p>
title = paste(“Total Return “,year,sep=“”)< o:p>
file = paste(“c:\\temp\\”,year,“.png”,sep=“”)< o:p>
< o:p>
s = window(series,start=start, end=end)< o:p>
png(file)< o:p>
chart.CumReturns(s[,vars],main=title,legend.loc=“topleft”)< o:p>
dev.off()< o:p>
< o:p>
print(paste(year,“Returns”,sep=” “))< o:p>
print(table.AnnualizedReturns(s[,vars]))< o:p>
print(table.CAPM(as.xts(s[,“Strat.Return”,drop=FALSE]),as.xts(s[,“spy.Return”,drop=FALSE])))< o:p>
}< o:p>
< o:p> Total | spy.Return< o:p> | sh.Return< o:p> | Strat.DollarReturns< o:p> |
Annualized Return< o:p> | -0.0067< o:p> | -0.0903< o:p> | 0.3535< o:p> |
Annualized Std Dev< o:p> | 0.2529< o:p> | 0.2512< o:p> | 0.2508< o:p> |
Annualized Sharpe (Rf=0%)< o:p> | -0.0263< o:p> | -0.3593< o:p> | 1.4092< o:p> |
< o:p> Total | Strat.Return to spy.Return< o:p> |
Alpha< o:p> | 0.0013< o:p> |
Beta< o:p> | 0.1921< o:p> |
Beta+< o:p> | 0.6830< o:p> |
Beta-< o:p> | -0.0803< o:p> |
R-squared< o:p> | 0.0374< o:p> |
Annualized Alpha< o:p> | 0.3990< o:p> |
Correlation< o:p> | 0.1934< o:p> |
Correlation p-value< o:p> | 0.0000< o:p> |
Tracking Error< o:p> | 0.7447< o:p> |
Active Premium< o:p> | 0.3694< o:p> |
Information Ratio< o:p> | 0.4960< o:p> |
Treynor Ratio< o:p> | 1.8885< o:p> |
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.