library("quantmod")
library("PerformanceAnalytics")
library("zoo")
#Script parameters
symbolLst <- c("ADN.L","ADM.L","AGK.L","AMEC.L","AAL.L","ANTO.L","ARM.L","ASHM.L","ABF.L","AZN.L","AV.L","BA.L","BARC.L","BG.L","BLT.L","BP.L","BATS.L","BLND.L","BSY.L","BNZL.L","BRBY.L","CSCG.L","CPI.L","CCL.L","CNA.L","CPG.L","CRH.L","CRDA.L","DGE.L","ENRC.L","EXPN.L","FRES.L","GFS.L","GKN.L","GSK.L","HMSO.L","HL.L","HSBA.L","IAP.L","IMI.L","IMT.L","IHG.L","IAG.L","IPR.L","ITRK.L","ITV.L","JMAT.L","KAZ.L","KGF.L","LAND.L","LGEN.L","LLOY.L","EMG.L","MKS.L","MGGT.L","MRW.L","NG.L","NXT.L","OML.L","PSON.L","PFC.L","PRU.L","RRS.L","RB.L","REL.L","RSL.L","REX.L","RIO.L","RR.L","RBS.L","RDSA.L","RSA.L","SAB.L","SGE.L","SBRY.L","SDR.L","SRP.L","SVT.L","SHP.L","SN.L","SMIN.L","SSE.L","STAN.L","SL.L","TATE.L","TSCO.L","TLW.L","ULVR.L","UU.L","VED.L","VOD.L","WEIR.L","WTB.L","WOS.L","WPP.L","XTA.L")
#Specify dates for downloading data
startDate = as.Date("2000-01-01") #Specify what date to get the prices from
symbolData <- new.env() #Make a new environment for quantmod to store data in
clClRet <- new.env()
downloadedSymbols <- list()
for(i in 1:length(symbolLst)){
#Download one stock at a time
print(paste(i,"/",length(symbolLst),"Downloading",symbolLst[i]))
tryCatch({
getSymbols(symbolLst[i], env = symbolData, src = "yahoo", from = startDate)
cleanName <- sub("^","",symbolLst[i],fixed=TRUE)
mktData <- get(cleanName,symbolData)
print(paste("-Calculating close close returns for:",cleanName))
ret <-(Cl(mktData)/Lag(Cl(mktData)))-1
if(max(abs(ret),na.rm=TRUE)>0.5){
print("-There is a abs(return) > 50% the data is odd lets not use this stock")
next;
}
downloadedSymbols <- c(downloadedSymbols,symbolLst[i])
assign(cleanName,ret,envir = clClRet)
}, error = function(e) {
print(paste("Couldn't download: ", symbolLst[i]))
})
}
#Combine all the returns into a zoo object (joins the returns by date)
#Not a big fan of this loop, think it's suboptimal
zooClClRet <- zoo()
for(i in 1:length(downloadedSymbols)){
cleanName <- sub("^","",downloadedSymbols[i],fixed=TRUE)
print(paste("Combining the close close returns to the zoo:",cleanName))
if(length(zooClClRet)==0){
zooClClRet <- as.zoo(get(cleanName,clClRet))
} else {
zooClClRet <- merge(zooClClRet,as.zoo(get(cleanName,clClRet)))
}
}
print(head(zooClClRet))
#This will take inzoo or data frame
#And convert each row into quantiles
#Quantile 1 = 0-0.25
#Quantile 2 = 0.25-0.5 etc...
quasiQuantileFunction <- function(dataIn){
quantileFun <- function(rowIn){
quant <- quantile(rowIn,na.rm=TRUE)
#print(quant)
a <- (rowIn<=quant[5])
b <- (rowIn<=quant[4])
c <- (rowIn<=quant[3])
d <- (rowIn<=quant[2])
rowIn[a] <- 4
rowIn[b] <- 3
rowIn[c] <- 2
rowIn[d] <- 1
return(rowIn)
}
return (apply(dataIn,2,quantileFun))
}
avgReturnPerQuantile <- function(returnsData,quantileData){
q1index <- (clClQuantiles==1)
q2index <- (clClQuantiles==2)
q3index <- (clClQuantiles==3)
q4index <- (clClQuantiles==4)
q1dat <- returnsData
q1dat[!q1index] <- NaN
q2dat <- returnsData
q2dat[!q2index] <- NaN
q3dat <- returnsData
q3dat[!q3index] <- NaN
q4dat <- returnsData
q4dat[!q4index] <- NaN
avgFunc <- function(x) {
#apply(x,1,median,na.rm=TRUE) #median is more resistant to outliers
apply(x,1,mean,na.rm=TRUE)
}
res <- returnsData[,1:4] #just to maintain the time series (there must be a better way)
res[,1] <- avgFunc(q1dat)
res[,2] <- avgFunc(q2dat)
res[,3] <- avgFunc(q3dat)
res[,4] <- avgFunc(q4dat)
colnames(res) <- c("Q1","Q2","Q3","Q4")
return(res)
}
nLookback <- 250 #~1year trading calendar
clClVol <- rollapply(zooClClRet,nLookback,sd,na.rm=TRUE)
clClQuantiles <- quasiQuantileFunction(clClVol)
returnPerVolQuantile <- avgReturnPerQuantile(zooClClRet,clClQuantiles)
colnames(returnPerVolQuantile) <- c("Q1 min vol","Q2","Q3","Q4 max vol")
returnPerVolQuantile[is.nan(returnPerVolQuantile)]<-0 #Assume if there is no return data that it's return is 0
#returnPerVolQuantile[returnPerVolQuantile>0.2] <- 0 #I was having data issues leading to days with 150% returns! This filters them out
cumulativeReturnsByQuantile <- apply(returnPerVolQuantile,2,cumsum)
dev.new()
charts.PerformanceSummary(returnPerVolQuantile,main=paste("Arithmetic Cumulative Returns per Vol Quantile - Lookback=",nLookback),geometric=FALSE)
print(table.Stats(returnPerVolQuantile))
cat("Sharpe Ratio")
print(SharpeRatio.annualized(returnPerVolQuantile))
dev.new()
par(oma=c(0,0,2,0))
par(mfrow=c(3,3))
for(i in seq(2012,2004,-1)){
print(as.Date(paste(i,"-01-01",sep="")))
print(as.Date(paste(i+1,"-01-01",sep="")))
windowedData <- window(as.zoo(returnPerVolQuantile),start=as.Date(paste(i,"-01-01",sep="")),end=as.Date(paste(i+1,"-01-01",sep="")))
chart.CumReturns(windowedData,main=paste("Year",i,"to",i+1),geometric=FALSE)
}
title(main=paste("Arithmetic Cumulative Returns per Vol Quantile - Lookback=",nLookback),outer=T)
dev.new()
charts.PerformanceSummary(returnPerVolQuantile,main=paste("Geometric Cumulative Returns per Vol Quantile - Lookback=",nLookback),geometric=TRUE)
print(table.Stats(returnPerVolQuantile))
cat("Sharpe Ratio")
print(SharpeRatio.annualized(returnPerVolQuantile))
dev.new()
par(oma=c(0,0,2,0))
par(mfrow=c(3,3))
for(i in seq(2012,2004,-1)){
print(as.Date(paste(i,"-01-01",sep="")))
print(as.Date(paste(i+1,"-01-01",sep="")))
windowedData <- window(as.zoo(returnPerVolQuantile),start=as.Date(paste(i,"-01-01",sep="")),end=as.Date(paste(i+1,"-01-01",sep="")))
chart.CumReturns(windowedData,main=paste("Year",i,"to",i+1),geometric=TRUE)
}
title(main=paste("Geometric Cumulative Returns per Vol Quantile - Lookback=",nLookback),outer=T) |