Recently, Trading the odds posted one of many flavors of mean reverting strategies and I decided to get my hands dirty by writing R code and testing it.
You can find full description of the strategy by following latter link above. Long story short – if SPY shows lower open, high and close 3 days in a row, then buy on the close of third day and sell it 1 days later.
Let’s do simple test:
require('xts') require('quantmod') getSymbols('SPY',from='1995-01-01',index.class=c("POSIXt","POSIXct")) dividends=getDividends('SPY',from='1995-01-01',index.class=c("POSIXt","POSIXct")) temp=cbind(dividends,SPY) temp[,1][is.na(temp[,1])]=0 SPY=cbind(temp[,2],temp[,3],temp[,4],temp[,1]+temp[,5]) colnames(SPY)=c('Open','High','Low','Close') #one day before lag1=lag((SPY),1) #two days defore lag2=lag((SPY),2) signal=ifelse( (Cl(lag2)>Cl(lag1) & Cl(lag1)>Cl(SPY))& (Hi(lag2)>Hi(lag1) & Hi(lag1)>Hi(SPY)) & (Op(lag2)>Op(lag1) & Op(lag1)>Op(SPY)), 1,0 ) #one day later lag3=lag(Cl(SPY),-1) profit=(lag3/Cl(SPY)-1)*signal profit[is.na(profit)]=0 png(file='first.png',width=500) plot(cumprod(profit+1),main='Profit 1995-2010') dev.off()
The code above supposed to produce something similar:
Nice curve, isn’t it? But neither commissions nor slippage were taken into account. So, let’s run more complicated test. For that purpose I utilized blotter package. Here’s the code:
require('xts') require('quantmod') require('blotter') require('PerformanceAnalytics') require('FinancialInstrument') getSymbols('SPY',from='1995-01-01',index.class=c("POSIXt","POSIXct")) dividends=getDividends('SPY',from='1995-01-01',index.class=c("POSIXt","POSIXct")) temp=cbind(dividends,SPY) temp[,1][is.na(temp[,1])]=0 SPY=-cbind(temp[,2],temp[,3],temp[,4],temp[,1]+temp[,5]) colnames(SPY)=c('Open','High','Low','Close') #one day before lag1=lag((SPY),1) #two days defore lag2=lag((SPY),2) signal=ifelse( (Cl(lag2)>Cl(lag1) & Cl(lag1)>Cl(SPY))& (Hi(lag2)>Hi(lag1) & Hi(lag1)>Hi(SPY)) & (Op(lag2)>Op(lag1) & Op(lag1)>Op(SPY)), 1,0 ) #one day later lag3=lag(Cl(SPY),-1) symbols=c('SPY') initDate=index(get(symbols)[1]) initEq=10000 rm(list=ls(envir=.blotter),envir=.blotter) ltportfolio='3days' ltaccount='3days' initPortf(ltportfolio,symbols, initDate=initDate) initAcct(ltaccount,portfolios=c(ltportfolio), initDate=initDate,initEq=initEq) currency("USD") stock("SPY",currency="USD",multiplier=1) signal[is.na(signal)]=0 counter=0 for(i in 2:length(signal)) { currentDate= time(signal)[i] equity = 10000 #getEndEq(ltaccount, currentDate) #print(paste("equity ",equity)) position = getPosQty(ltportfolio, Symbol=symbols[1], Date=currentDate) print(currentDate) if(position==0) { #open a new position if signal is >0 if(signal[i]>0 &counter ==0) { print('open position') closePrice=as.double(Cl(SPY[currentDate])) unitSize = as.numeric(trunc((equity/closePrice))) commssions=-unitSize*closePrice*0.0003 addTxn(ltportfolio, Symbol=symbols[1], TxnDate=currentDate, TxnPrice=closePrice, TxnQty = unitSize , TxnFees=commssions, verbose=T) counter=1 } } else { #position is open. If signal is 0 - close it. if(position>0 &counter>=1) { print('close position>>>>') position = getPosQty(ltportfolio, Symbol=symbols[1], Date=currentDate) closePrice=as.double((Cl(SPY[currentDate])))#as.double(get(symbols[1])[i+100]) commssions=-position*closePrice*0.0003 addTxn(ltportfolio, Symbol=symbols[1], TxnDate=currentDate, TxnPrice=closePrice, TxnQty = -position , TxnFees=commssions, verbose=T) counter=0 } else counter=counter+1 } updatePortf(ltportfolio, Dates = currentDate) updateAcct(ltaccount, Dates = currentDate) updateEndEq(ltaccount, Dates = currentDate) } result=rez1$symbols$SPY$txn[,7] result=result[result!=0] png(file='second.png',width=500) #fix commission rate 2*3 plot(cumsum(result-6)) #next line will allow you to compare the performace with and without commissions #chart.CumReturns(cbind((result)/10000,(result-6)/10000)) dev.off()
Nice curve, but let’s look beyond that. First of all, here’s nice function in PerformanceAnalytics package, AnnulizedReturns:
table.AnnualizedReturns((result-6)/10000)
Gross.Txn.Realized.PL
Annualized Return 0.0265
Annualized Std Dev 0.0494
Annualized Sharpe (Rf=0%) 0.5366
Well, Sharpe ratio is not impressive. The profit percentage of this strategy is 57% and mean of profitable return is 111$ against 98$ loss. Profit factor is ~1.55.
I think, this strategy can be as one of the parameter or vote in another system, but alone it is weak.
R-bloggers.com offers daily e-mail updates about R news and tutorials on topics such as: visualization (ggplot2, Boxplots, maps, animation), programming (RStudio, Sweave, LaTeX, SQL, Eclipse, git, hadoop, Web Scraping) statistics (regression, PCA, time series,ecdf, trading) and more...



Zero Inflated Models and Generalized Linear Mixed Models with R.
Zuur, Saveliev, Ieno (2012).