Mean-Reversion Risk Tuning

This is just a test for fun. What if we buy everything that goes down to a certain point, say -%1 or -%5 at the end of the day everyday? There are probably a bunch of programs out there can do this but I prefer something home-made.

To get a list of all publicly traded stocks on NASDAQ, I went to EOData and grabbed a txt file for all NASDAQ tickers. But the trouble was even though the calculation I wanted to perform was simple, the volume was too large for my computer to handle. So I had to push everything to github, start an Amazon EC2 instance, configure it, pull from my own repository, run my script, then push everything back. The process itself is not complicated, just a lot of annoying details.

Anyhow, before everybody gets bored with my geeky life story, here are the results. The numbers in the legend represent mean-reversion signals. For instance, -0.12 means “what if we buy everything that goes down more than 12%?

It seems as we moveing the signal further away from 0, the strategy becomes more aggressive and performance improves gradually, except for -0.19 and -0.20, which might be two outliers or trend changers.

Source code in R.

## functions
EODMR <- function(returnsData, shockRates = seq(-0.01, -0.2, by = -0.01)) {
  output <- mrTest(returnsData, shockRate = 0)
  output <- calcRet(output)
  outputRet <- output
  annRet <- prod(1+output)^(250/length(output))-1
  annSd <- sd(output)*sqrt(250)
  Sharpe <- annRet/annSd
  output <- data.frame(shock0 = rbind(annRet, annSd, Sharpe))
  for (i in seq(along = shockRates)) {
	testData <- mrTest(returnsData, shockRate = shockRates[i])
	finalRet <- calcRet(testData)
        outputRet <- merge(outputRet, finalRet)
        names(outputRet)[i + 1] <- shockRates[i]
        annRet <- prod(1+finalRet)^(250/length(finalRet))-1
        annSd <- sd(finalRet)*sqrt(250)
        Sharpe <- annRet/annSd
        finalRet <- data.frame(rbind(annRet, annSd, Sharpe))
        names(finalRet) <- shockRates[i]
	output <- cbind(output, finalRet)
	print(paste(i, "out of ", length(shockRates), " finished"))
  }
  return(list(testStat = output, testReturns = outputRet))
}

# download and format returns data
getReturns <- function(tickers, start = Sys.Date() - 2520,
                       minVol = 100000) {  
  returnsData <- get.hist.quote("^GSPC", start = start,
                           quote = "AdjClose")
  returnsData <- merge(ROC(returnsData), Next(ROC(returnsData)))
  names(returnsData) <- paste("SPX", c("Ret0", "Ret1"), sep = ".")
  
  for (i in seq(along = tickers)) {
    nextTicker <- NULL
    nextTicker <- try(get.hist.quote(tickers[i], start = start,
                                     quote = c("AdjClose", "Volume")))
    
    if (!is.character(nextTicker[1])) {

      meanVol <- as.numeric(mean(nextTicker))
      if (meanVol >= minVol) {
        nextTicker <- nextTicker$Adj
      } else next

      nextTicker <- nextTicker[!duplicated(index(nextTicker))]
      nextTicker <- merge(ROC(nextTicker), Next(ROC(nextTicker)))
      names(nextTicker) <- paste(tickers[i], c("Ret0", "Ret1"), sep = ".")
      returnsData <- merge(retournsData, nextTicker)
    } else next
  print(paste(i, "out of", length(tickers), sep = " "))
  }
  return(returnsData)
}

# perform MR test
mrTest <- function(returnsData, shockRate = -0.05) {
  testRets <- returnsData[, 1:2]
  thisName <- gsub(".Ret0", "", names(testRets)[1])
  testRets <- ifelse(testRets[, 1] <= shockRate, testRets[, 2], 0)
  dim(testRets) <- c(length(testRets), 1)
  names(testRets) <- thisName
  
  secNum <- ncol(returnsData) / 2
  for (i in 2:secNum) {
    thisSec <- returnsData[, (i * 2 - 1):(i * 2)]
    thisName <- gsub(".Ret0", "", names(thisSec)[1])
    thisSec <- ifelse(thisSec[, 1] <= shockRate, thisSec[, 2], 0)
    dim(thisSec) <- c(length(thisSec), 1)
    names(thisSec) <- thisName
    testRets <- merge(testRets, thisSec)
    testRets[is.na(testRets)] <- 0
  }
  return(testRets)
}

# calculate strategy results
calcRet <- function(testRets) {
  timeLength <- nrow(testRets)
  finalRet <- rep(0, timeLength)
  for (i in 1:timeLength) {
    tmp <- as.numeric(testRets[i, ])
    tmp <- tmp[tmp != 0]
    finalRet[i] <- mean(tmp)
  }
  finalRet[is.na(finalRet)] <- 0
  finalRet <- zoo(finalRet, order.by = index(testRets))
  return(finalRet)
}

#### run code
# load packages
library(tseries)
library(quantmod)
library(PerformanceAnalytics)

# perform test
tickersNasdaq <- as.vector(read.table("~/R/EODMR_development/NASDAQ.txt",
                                      sep = "\t")[-1, 1])
testNasdaq <- EODMR(returnsNasdaq)

# visualize results
charts.PerformanceSummary(testNasdaq$testReturns, colorset = rainbow(21), ylog = 1)
chart.RiskReturnScatter(testNasdaq$testReturns, colorset = rainbow(21), symbolset = rep(3, 21))
About these ads

3 responses to “Mean-Reversion Risk Tuning

  1. Thierry 10/12/2012 at 2:33 am

    It works “well” (I mean performance is stratospheric) just because you are using the list of Nasdaq tickers as of today, without taking into account the tickers that disappeared in the meanwhile. These tickers are very important indeed as they include more than a few tickers that went down -20% at some time… and kept on going down rather than recovering back.

    • alphaism 10/12/2012 at 8:43 am

      You are right Thierry. I didn’t do anything with survivalship bias simply because I didn’t have the data. I wouldn’t worry too much about it though because even among delisted stocks, I have rarely seen anything goes straight down for days until it finally got removed. If that’s the case, momentum strategies would dominate the world.

  2. aimdal 10/14/2012 at 5:17 am

    Why not add a general trend filter just to see what happens? Really good test…

    http://backtestingvix.wordpress.com/

    http://nightlypatterns.wordpress.com/

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 131 other followers

%d bloggers like this: