[R-SIG-Finance] Help replicating a paper

Ilya Kipnis ilya.kipnis at gmail.com
Sat Dec 20 03:15:11 CET 2014


I'm not sure this question gets posed often on this list, but here goes...

I'm trying to emulate the results from SSRN id2450017 aka Momentum,
Markowitz, and Smart Beta by W.J. Keller. My issue is that for a weighting
algorithm, it often only invests in one assets, no assets, or very few
assets. Furthermore, while using the 9 sector spiders, my results are
pretty terrible, and certainly nothing worthy of publishing in a paper, so
I know there's a mistake on my end somewhere, but I'm not quite sure where.

Here's the link to the paper:

http://papers.ssrn.com/sol3/papers.cfm?abstract_id=2450017

And my implementation of the MAA algorithm (special case at the moment,
will generalize later). I'm hoping my comments will help make the code
easier to follow.

*****************************

require(quantmod)
require(PerformanceAnalytics)

#Use 9 sector spiders
suffix <- c("Y", "P", "E", "F", "V", "I", "B", "K", "U")
symbols <- paste0("XL", suffix)
getSymbols(symbols, from="1990-01-01")
prices <- list()
for(i in 1:length(symbols)) {
  prices[[i]] <- Ad(get(symbols[i]))
}
prices <- do.call(cbind, prices)

rets <- Return.calculate(prices)
rets <- rets[-1,] #remove first row of NAs

weights <- list()
dates <- list()
monthlyEndpoints <- endpoints(rets, on = "months")
for(i in 2:(length(monthlyEndpoints) - 4)) {

  #subset returns and define mktRets as average daily returns
  returnsData <- rets[monthlyEndpoints[i]:monthlyEndpoints[i+4],]
  mktRets <- xts(rowMeans(returnsData), order.by=index(returnsData))

  #raw computed values -- annualized variance, cumulative returns
  #correlation to average daily returns, and annualized variance of
  #average daily returns
  variances <- as.numeric(StdDev.annualized(returnsData)^2)
  cumRets <- as.numeric(Return.cumulative(returnsData))
  cors <- as.numeric(cor(returnsData, mktRets))
  mktVar <- as.numeric(StdDev.annualized(mktRets) ^ 2)

  #shrink asset variances to the average by 50%
  #shrink cumulative returns to the average by 50%
  #shrink correlations to the average by 50%
  #shrink market variance to zero by 50%
  #see Keller (June 2014 Table 1 pg. 8)
  shrunkVariances <- (variances + mean(variances)) / 2
  shrunkCumRets <- (cumRets + mean(cumRets)) / 2
  shrunkCors <- (cors+mean(cors))/2
  shrunkMktVar <- mktVar/2

  #values computed from shrunken values
  #define beta as shrunken variances * shrunken correlations / shrunken
market variance
  #define idiosyncratic variances as shrunken variances minus shrunken
market variance * beta
  #see Keller (June 2014 under A.4)
  betas <- shrunkVariances*shrunkCors/shrunkMktVar
  idiosyncraticVars <- shrunkVariances-shrunkMktVar*betas

  #compute asset treynors
  assetTreynors <- shrunkCumRets/betas

  #set initial threshold to zero
  longs <- assetTreynors > 0

  converge <- FALSE
  while(!converge) {
    old <- longs

    #implement equation A.6 from Keller (June 2014)
    newTreynorThreshNum <- shrunkMktVar * sum(old * shrunkCumRets * betas /
idiosyncraticVars)
    newTreynorThreshDenom <- 1 + shrunkMktVar * sum(old * betas * betas /
idiosyncraticVars)
    newTreynorThresh <- newTreynorThreshNum/newTreynorThreshDenom

    #assets remaining are those that have a higher initial treynor ratio
    longs <- assetTreynors > newTreynorThresh

    #if our assets are the same as they were before the computation of the
new threshold, end
    if(sum(old - longs)==0) {
      converge <- TRUE
    }
  }

  #after convergence, allocate weights according to w_i~(1-t/t_i)*r_i/s_i
and normalize
  longs <-
longs*(1-newTreynorThresh/assetTreynors)*shrunkCumRets/idiosyncraticVars
  longs <- longs/sum(longs)

  weights[[i]] <-longs
  dates[[i]] <- as.character(index(returnsData)[nrow(returnsData)])
}

#set up weights xts
weights <- do.call(rbind, weights)
dates <- do.call(rbind, dates)
weights <- xts(weights, order.by=as.Date(dates))
weights[is.na(weights)] <- 0
colnames(weights) <- colnames(rets)

#for dates without investment, invest in risk-free
rets$riskFree <- 0
weights$riskFree <- rep(1, nrow(weights))-rowSums(weights)

#results are pretty terrible
test <- Return.rebalancing(R = rets, weights = weights)
charts.PerformanceSummary(test)
plot(log(cumprod(1+test)))
Return.annualized(test) #less than 5%
maxDrawdown(test) #greater than 30%

#Often invests in one security
head(round(weights, 3), 20)

*******************************
Thank you for any help ahead of time.

-Ilya

	[[alternative HTML version deleted]]



More information about the R-SIG-Finance mailing list