During a previous post, S&P500 Seasonal Study + Other Commodities: https://flare9xblog.wordpress.com/2017/10/12/seasonal-study/

It was determined that the best months to be long the S&P500 was from months October through to the end of May.

As a refresher:

We back test this theme to see how it performs over monthly bars from 1960 to present day (10.12.2017):

What we see is that we are invested 67% of the time being long from the Open bar of October and selling at the Close of May. We do beat the S&P500 and I believe this is partly due to drawing down less post 1999 bear market. Our maximum draw down is slightly less than than buy and hold at 43%.

What stands out is the average gain during this October to May holding period. We see that the average trade is 14.3%.

To see some relative benchmark, lets see what it looks like only buying May open and selling at the Close of September:

> Return.cumulative(xts1, geometric = TRUE) [,1] Cumulative Return -0.1973985

We see cumulative returns for Buying May open and selling Close of September are -0.1973985. That is the growth of $1. Essentially May to September is negative to flat. The cumulative returns do not even warrant shorting May to September.

In closing we see that the majority of the S&P500 gains seem to be attributed to seasonal trends specifically buying in October and Selling in May.

Full back test R Code for the above

# S&P500 Seasonal Back Test # Month Bars # Buy Open of October, Sell End of April (Based on seasonal study) # Andrew Bannerman 10.12.2017 require(lubridate) require(dplyr) require(magrittr) require(TTR) require(zoo) require(data.table) require(xts) require(PerformanceAnalytics) # Data path data.dir <- "C:/R Projects/Final Scripts/Seasonal Strategies/Data" data.read.spx <- paste(data.dir,"$SPX.Monthly.csv",sep="/") # Read data read.spx <- read.csv(data.read.spx,header=TRUE, sep=",",skip=0,stringsAsFactors=FALSE) # Make dataframe new.df <- data.frame(read.spx) # Convert Values To Numeric cols <-c(3:8) new.df[,cols] %<>% lapply(function(x) as.numeric(as.character(x))) # Convert Date Column [1] new.df$Date <- ymd(new.df$Date) # Add day of month column # This is a helper column for creating buy/sell rules group <- new.df %>% dplyr::mutate(month = lubridate::month(Date)) %>% group_by(month) new.df <- data.frame(new.df,group$month) # Calculate Returns from open to close new.df$ocret <- apply(new.df[,c('Open', 'Close')], 1, function(x) { (x[2]-x[1])/x[1]} ) # Calculate Close-to-Close returns new.df$clret <- ROC(new.df$Close, type = c("discrete")) new.df$clret[1] <- 0 # Subset by Date new.df <- subset(new.df, Date >= as.POSIXct("1960-01-01") ) # Enter Long Rules # Buy October To End of April x = new.df$group.month # October to May Hold Period Rules #new.df$enter <- ifelse(x == 1,1,0 | ifelse(x == 2,1,0 | ifelse(x == 3,1,0 | ifelse(x == 4,1,0 | ifelse(x==5,1,0 | ifelse(x == 10,1,0 | ifelse(x == 11,1,0 | ifelse(x == 12,1,0)))))))) #May to September Hold Period Rules new.df$enter <- ifelse(x == 5,1,0 | ifelse(x == 6,1,0 | ifelse(x == 7,1,0 | ifelse(x == 8,1,0 | ifelse(x==9,1,0))))) # Calculate equity curves # Long new.df <- new.df %>% dplyr::mutate(RunID = rleid(enter)) %>% group_by(RunID) %>% dplyr::mutate(seasonal.equity = ifelse(enter == 0, 0, ifelse(row_number() == 1, ocret, clret))) %>% ungroup() %>% select(-RunID) # Pull select columns from data frame to make XTS whilst retaining formats xts1 = xts(new.df$seasonal.equity, order.by=as.POSIXct(new.df$Date, format="%Y-%m-%d")) xts2 = xts(new.df$clret, order.by=as.POSIXct(new.df$Date, format="%Y-%m-%d")) str(new.df) # Join XTS together compare <- cbind(xts1,xts2) # Use the PerformanceAnalytics package for trade statistics colnames(compare) <- c("Seasonal","Buy And Hold") charts.PerformanceSummary(compare,main="Long May Open To Close of September", wealth.index=TRUE, colorset=rainbow12equal) performance.table <- rbind(table.AnnualizedReturns(compare),maxDrawdown(compare), CalmarRatio(compare),table.DownsideRisk(compare)) drawdown.table <- rbind(table.Drawdowns(compare)) #dev.off() #logRets <- log(cumprod(1+compare)) #chart.TimeSeries(logRets, legend.loc='topleft', colorset=rainbow12equal) print(performance.table) print(drawdown.table) # Find net trade result of multiple 'n' day trades # Find start day of trade, find end day, perform (last(Close) - first(Open))/first(Open) % calculation new.df <- new.df %>% dplyr::mutate(RunID = data.table::rleid(enter)) %>% group_by(RunID) %>% dplyr::mutate(perc.output = ifelse(enter == 0, 0, ifelse(row_number() == n(), (last(Close) - first(Open))/first(Open), 0))) %>% ungroup() %>% select(-RunID) # Win / Loss % # All Holding Months winning.trades <- sum(new.df$seasonal.equity > '0', na.rm=TRUE) losing.trades <- sum(new.df$seasonal.equity < '0', na.rm=TRUE) even.trades <- sum(new.df$seasonal.equity == '0', na.rm=TRUE) total.days <- NROW(new.df$seasonal.equity) # Multi Month Trades multi.winning.trades <- sum(new.df$perc.output > '0', na.rm=TRUE) multi.losing.trades <- sum(new.df$perc.output < '0', na.rm=TRUE) multi.total.days <- multi.winning.trades+multi.losing.trades # % Time Invested time.invested <- (winning.trades + losing.trades) / total.days winning.trades + losing.trades # Calcualte win loss % # All months total <- winning.trades + losing.trades win.percent <- winning.trades / total loss.percent <- losing.trades / total # Multi Month Trades multi.total <- multi.winning.trades + multi.losing.trades multi.win.percent <- multi.winning.trades / multi.total multi.loss.percent <- multi.losing.trades / multi.total # Calculate Consecutive Wins Loss # All Months remove.zero <- new.df[-which(new.df$seasonal.equity == 0 ), ] # removing rows 0 values consec.wins <- max(rle(sign(remove.zero$seasonal.equity))[[1]][rle(sign(remove.zero$seasonal.equity))[[2]] == 1]) consec.loss <- max(rle(sign(remove.zero$seasonal.equity))[[1]][rle(sign(remove.zero$seasonal.equity))[[2]] == -1]) consec.wins # Multi Month Trades multi.remove.zero <- new.df[-which(new.df$perc.output == 0 ), ] # removing rows 0 values multi.consec.wins <- max(rle(sign(multi.remove.zero$perc.output))[[1]][rle(sign(multi.remove.zero$perc.output))[[2]] == 1]) multi.consec.loss <-max(rle(sign(multi.remove.zero$perc.output))[[1]][rle(sign(multi.remove.zero$perc.output))[[2]] == -1]) # Calculate Summary Statistics # All Months average.trade <- mean(new.df$seasonal.equity) average.win <- mean(new.df$seasonal.equity[new.df$seasonal.equity >0]) average.loss <- mean(new.df$seasonal.equity[new.df$seasonal.equity <0]) median.win <- median(new.df$seasonal.equity[new.df$seasonal.equity >0]) median.loss <- median(new.df$seasonal.equity[new.df$seasonal.equity <0]) max.gain <- max(new.df$seasonal.equity) max.loss <- min(new.df$seasonal.equity) win.loss.ratio <- winning.trades / abs(losing.trades) summary <- cbind(winning.trades,losing.trades,even.trades,total.days,win.percent,loss.percent,win.loss.ratio,time.invested,average.trade,average.win,average.loss,median.win,median.loss,consec.wins,consec.loss,max.gain,max.loss) summary <- as.data.frame(summary) colnames(summary) <- c("Winning Trades","Losing Trades","Even Trades","Total Trades","Win %","Loss %","Win Loss Ratio","Time Invested","Average Trade","Average Win","Average Loss","Median Gain","Median Loss","Consec Wins","Consec Loss","Maximum Win","Maximum Loss") print(summary) # Multi Month Trades multi.average.trade <- mean(new.df$perc.output) multi.average.win <- mean(new.df$perc.output[new.df$perc.output >0]) multi.average.loss <- mean(new.df$perc.output[new.df$perc.output <0]) multi.median.win <- median(new.df$perc.output[new.df$perc.output >0]) multi.median.loss <- median(new.df$perc.output[new.df$perc.output <0]) multi.win.loss.ratio <- multi.average.win / abs(multi.average.loss) multi.max.gain <- max(new.df$perc.output) multi.max.loss <- min(new.df$perc.output) multi.summary <- cbind(multi.winning.trades,multi.losing.trades,multi.total.days,multi.win.percent,multi.loss.percent,multi.win.loss.ratio,time.invested,multi.average.trade,multi.average.win,multi.average.loss,multi.median.win,multi.median.loss,multi.consec.wins,multi.consec.loss,multi.max.gain,multi.max.loss) multi.summary <- as.data.frame(multi.summary) colnames(multi.summary) <- c("Winning Trades","Losing Trades","Total Trades","Win %","Loss %","Win Loss Ratio","Time Invested","Average Trade","Average Win","Average Loss","Median Gain","Median Loss","Consec Wins","Consec Loss","Maximum Win","Maximum Loss") print(multi.summary) print(performance.table) print(drawdown.table) table.Drawdowns(xts1, top=10) Return.cumulative(xts1, geometric = TRUE) # Write output to file write.csv(new.df,file="C:/R Projects/SP500.Seasonal.csv")

Could you make the raw data ($SPX.Monthly.csv) available? Where may I get it?

LikeLike

You can try monthly data from yahoo https://finance.yahoo.com/quote/%5EGSPC/history/

LikeLike