An Update on Flexible Asset Allocation

A few weeks back, after seeing my replication, one of the original authors of the Flexible Asset Allocation paper got in touch with me to tell me to make a slight adjustment to the code, in that rather than remove any negative-momentum securities before performing any ranking, to perform all ranking without taking absolute momentum into account, and only removing negative absolute momentum securities at the very end, after allocating weights.

Here’s the new code:

FAA <- function(prices, monthLookback = 4,
                weightMom = 1, weightVol = .5, weightCor = .5, 
                riskFreeName = NULL, bestN = 3,
                stepCorRank = FALSE, stepStartMethod = c("best", "default"),
                geometric = TRUE, ...) {
  stepStartMethod <- stepStartMethod[1]
  if(is.null(riskFreeName)) {
    prices$zeroes <- 0
    riskFreeName <- "zeroes"
    warning("No risk-free security specified. Recommended to use one of: quandClean('CHRIS/CME_US'), SHY, or VFISX. 
            Using vector of zeroes instead.")
  }
  returns <- Return.calculate(prices)
  monthlyEps <- endpoints(prices, on = "months")
  riskFreeCol <- grep(riskFreeName, colnames(prices))
  tmp <- list()
  dates <- list()
  
  for(i in 2:(length(monthlyEps) - monthLookback)) {
    #subset data
    priceData <- prices[monthlyEps[i]:monthlyEps[i+monthLookback],]
    returnsData <- returns[monthlyEps[i]:monthlyEps[i+monthLookback],]
    
    #perform computations
    momentum <- data.frame(t(t(priceData[nrow(priceData),])/t(priceData[1,]) - 1))
    momentum <- momentum[,!is.na(momentum)]
    #momentum[is.na(momentum)] <- -1 #set any NA momentum to negative 1 to keep R from crashing
    priceData <- priceData[,names(momentum)]
    returnsData <- returnsData[,names(momentum)]
    
    momRank <- rank(momentum)
    vols <- data.frame(StdDev(returnsData))
    volRank <- rank(-vols)
    cors <- cor(returnsData, use = "complete.obs")
    if (stepCorRank) {
      if(stepStartMethod=="best") {
        compositeMomVolRanks <- weightMom*momRank + weightVol*volRank
        maxRank <- compositeMomVolRanks[compositeMomVolRanks==max(compositeMomVolRanks)]
        corRank <- stepwiseCorRank(corMatrix=cors, startNames = names(maxRank), 
                                        bestHighestRank = TRUE, ...)
        
      } else {
        corRank <- stepwiseCorRank(corMatrix=cors, bestHighestRank=TRUE, ...)
      }
    } else {
      corRank <- rank(-rowSums(cors))
    }
    
    totalRank <- rank(weightMom*momRank + weightVol*volRank + weightCor*corRank)
    
    upper <- length(names(returnsData))
    lower <- max(upper-bestN+1, 1)
    topNvals <- sort(totalRank, partial=seq(from=upper, to=lower))[c(upper:lower)]
    
    #compute weights
    longs <- totalRank %in% topNvals #invest in ranks length - bestN or higher (in R, rank 1 is lowest)
    longs[momentum < 0] <- 0 #in previous algorithm, removed momentums < 0, this time, we zero them out at the end.
    longs <- longs/sum(longs) #equal weight all candidates
    longs[longs > 1/bestN] <- 1/bestN #in the event that we have fewer than top N invested into, lower weights to 1/top N
    names(longs) <- names(totalRank)
    
    
    #append removed names (those with momentum < 0)
    removedZeroes <- rep(0, ncol(returns)-length(longs))
    names(removedZeroes) <- names(returns)[!names(returns) %in% names(longs)]
    longs <- c(longs, removedZeroes)
    
    #reorder to be in the same column order as original returns/prices
    longs <- data.frame(t(longs))
    longs <- longs[, names(returns)]
    
    #append lists
    tmp[[i]] <- longs
    dates[[i]] <- index(returnsData)[nrow(returnsData)]
  }
  
  weights <- do.call(rbind, tmp)
  dates <- do.call(c, dates)
  weights <- xts(weights, order.by=as.Date(dates)) 
  weights[, riskFreeCol] <- weights[, riskFreeCol] + 1-rowSums(weights)
  strategyReturns <- Return.rebalancing(R = returns, weights = weights, geometric = geometric)
  colnames(strategyReturns) <- paste(monthLookback, weightMom, weightVol, weightCor, sep="_")
  return(strategyReturns)
}

And here are the new results, both with the original configuration, and using the stepwise correlation ranking algorithm introduced by David Varadi:

mutualFunds <- c("VTSMX", #Vanguard Total Stock Market Index
                 "FDIVX", #Fidelity Diversified International Fund
                 "VEIEX", #Vanguard Emerging Markets Stock Index Fund
                 "VFISX", #Vanguard Short-Term Treasury Fund
                 "VBMFX", #Vanguard Total Bond Market Index Fund
                 "QRAAX", #Oppenheimer Commodity Strategy Total Return 
                 "VGSIX" #Vanguard REIT Index Fund
)

#mid 1997 to end of 2012
getSymbols(mutualFunds, from="1997-06-30", to="2014-10-30")
tmp <- list()
for(fund in mutualFunds) {
  tmp[[fund]] <- Ad(get(fund))
}

#always use a list hwne intending to cbind/rbind large quantities of objects
adPrices <- do.call(cbind, args = tmp)
colnames(adPrices) <- gsub(".Adjusted", "", colnames(adPrices))

original <- FAA(adPrices, riskFreeName="VFISX")
swc <- FAA(adPrices, riskFreeName="VFISX", stepCorRank = TRUE)
originalOld <- FAAreturns(adPrices, riskFreeName="VFISX")
swcOld <- FAAreturns(adPrices, riskFreeName="VFISX", stepCorRank=TRUE)
all4 <- cbind(original, swc, originalOld, swcOld)
names(all4) <- c("original", "swc", "origOld", "swcOld")
charts.PerformanceSummary(all4)
> rbind(Return.annualized(all4)*100,
+       maxDrawdown(all4)*100,
+       SharpeRatio.annualized(all4))
                                 original       swc   origOld    swcOld
Annualized Return               12.795205 14.135997 13.221775 14.037137
Worst Drawdown                  11.361801 11.361801 13.082294 13.082294
Annualized Sharpe Ratio (Rf=0%)  1.455302  1.472924  1.377914  1.390025

And the resulting equity curve comparison

Overall, it seems filtering on absolute momentum after applying all weightings using only relative momentum to rank actually improves downside risk profiles ever so slightly compared to removing negative momentum securities ahead of time. In any case, FAAreturns will be the function that removes negative momentum securities ahead of time, and FAA will be the ones that removes them after all else is said and done.

I’ll return to the standard volatility trading agenda soon.

Thanks for reading.

Note: I am a freelance consultant in quantitative analysis on topics related to this blog. If you have contract or full time roles available for proprietary research that could benefit from my skills, please contact me through my LinkedIn here.

33 thoughts on “An Update on Flexible Asset Allocation

  1. Pingback: The Whole Street’s Daily Wrap for 11/25/2014 | The Whole Street

  2. Ilya, using your code and the default bestN=3, there are many months where you have 4 Positions.

    VTSMX FDIVX VEIEX VFISX VBMFX QRAAX VGSIX
    2014-05-30 0.0000000 0 0.3333333 0.0000000 0.0000000 0.3333333 0.3333333
    2014-06-30 0.0000000 0 0.3333333 0.0000000 0.3333333 0.0000000 0.3333333
    2014-07-31 0.0000000 0 0.3333333 0.0000000 0.3333333 0.0000000 0.3333333
    2014-08-29 0.2500000 0 0.2500000 0.2500000 0.2500000 0.0000000 0.0000000
    2014-09-30 0.3333333 0 0.0000000 0.3333333 0.3333333 0.0000000 0.0000000
    2014-10-30 0.2500000 0 0.0000000 0.2500000 0.2500000 0.0000000 0.2500000

    Months that have 4 positions always have a cash position. There might be a bug in your function.
    suggestion: instead of having the function return only strategyReturns return a list of strategyReturns AND weights like so ( last 2 lines)

    output <- list(strategyReturns, weights)
    return(output)

    this way you have the weights available after running the function and can easily check as well as plot the weightings over time.

    • I am very new to R. I was able to get the code in the original post to run and successfully replicate the results. How would the code below need to change to pass the “weights” value so that it can be viewed as part of the Data. I added the two lines above with the “output” formula in the FAA function but that didn’t work.

      mutualFunds <- c("VTSMX", #Vanguard Total Stock Market Index
      "FDIVX", #Fidelity Diversified International Fund
      "VEIEX", #Vanguard Emerging Markets Stock Index Fund
      "VFISX", #Vanguard Short-Term Treasury Fund
      "VBMFX", #Vanguard Total Bond Market Index Fund
      "QRAAX", #Oppenheimer Commodity Strategy Total Return
      "VGSIX" #Vanguard REIT Index Fund
      )

      #mid 1997 to end of 2012
      getSymbols(mutualFunds, from="1997-06-30", to="2014-10-30")
      tmp <- list()
      for(fund in mutualFunds) {
      tmp[[fund]] <- Ad(get(fund))
      }

      #always use a list hwne intending to cbind/rbind large quantities of objects
      adPrices <- do.call(cbind, args = tmp)
      colnames(adPrices) <- gsub(".Adjusted", "", colnames(adPrices))

      original <- FAA(adPrices, riskFreeName="VFISX")
      swc <- FAA(adPrices, riskFreeName="VFISX", stepCorRank = TRUE)
      originalOld <- FAAreturns(adPrices, riskFreeName="VFISX")
      swcOld <- FAAreturns(adPrices, riskFreeName="VFISX", stepCorRank=TRUE)
      all4 <- cbind(original, swc, originalOld, swcOld)
      names(all4) <- c("original", "swc", "origOld", "swcOld")
      charts.PerformanceSummary(all4)

  3. Helmuth, I am running this on a 12 asset portfolio and I noted this behaviour (see my comments an Ilya’s explanation here: https://quantstrattrader.wordpress.com/2014/10/31/combining-faa-and-stepwise-correlation/#comments). What I didn’t notice is if one of the assets was always cash. I will go back this weekend and check on that and then comment back here. Although I agree that cash can be an asset that ties in the top N values, I would be concerned if it was *always* there when ties took place. Thanks!

    • Well, intuitively speaking, the thing about something like VFISX (or other “risk-free” fund) is this: they usually have very low volatility, and are uncorrelated with a universe otherwise consisting of volatile equities. Furthermore, cash securities usually go up over any four month period (usually, not always). So, intuitively speaking, it wouldn’t surprise me that cash ranked highly among the FAA algorithms. Of course, it’d be possible to record the ranks and the weights as well (the xts of weights can be returned with a slight modification to the code, the ranks would be a bit more difficult), but if one would like to remove cash from the equation, I suppose one can run FAA on non-cash securities, return the weights, and look at the weighting of the zeroes (aka default risk-free security, consisting of simply a flat line returning zero, equivalent to stashing cash under your mattress), and then substitute those weights for a cash security after the fact.

      However, to my intuition, cash as an asset has a fantastic reward for the risk–just that the reward is small, while the risk is smaller still. Of course, the issue is that one can’t simply infinitely leverage to buy a cash security in the real world (though I suppose, the banks have tried). So I suppose it would make sense that cash would only not appear as a high-ranking security if its diversification benefits wouldn’t make up for lagging sufficiently in momentum.

      • Ilya, my comment was not about cash and its characteristics, I wanted to point out that in some months there are 3 assets (including cash) and in some months there are 4 assets. It’s about consistency.
        If 3 assets – without cash – satisfy the requirements why reduce the weightings to 25% from 33% and add 25% cash as a 4th asset ? In some month each of the 3 assets keep the 33% allocation as expected and no cash portion is added. ( compare end-of August /September in my print-out above)
        My comment about cash was to point to a pattern concerning a possible bug: each time when there are 4 assets one of them ist cash. There is no case where 4 non-cash assets are held. Cheers!

      • Helmuth, this was a nice observation – thanks!. It caused me to go back and look a little deeper. I posted my results below.

      • Ilya, just saw GeraldM’s comment and your explanation about how you treat ties. I see. But be aware that this affects is a large proportion ( 35 months out of 151 months where you have a cash position ). Alternatives are to ignore cash when there is a tie or keep cash out of the ranking procedure and add it only when you have less than 3 assets which satisfy your requirements. Have a nice Thanksgiving!

      • I like the idea of leaving cash out and only adding it in only if there are N-1 assets selected. Given the low volatility and correlation of cash, it may be adverse to the ranking algorithm anyway (my understanding is that it is not part of the ranking and is only added in when there are less than N assets with positive absolute momentum – yet the ties suggest it is being ranked). Helmuth, how did you produce that chart? I suspect it is only a line or two of code plotting the weighting matrix but if you don’t mid, could you share it? Thanks!

  4. I did a quick run on the 7Twelve ETF Portfolio from June 2009 to yesterday. There were many instances of ties and not all of those scenarios selected cash as an asset. The 7Twelve portfolio has a bunch of assets that can be highly correlated over the 4 months term (like BND, TIP, IGOV). So it is not surprising that often these assets will show up in a tie. The point is that cash isn’t always selected when ties occur so I am fairly confident the code is ranking properly.

  5. Using SHY as a cash proxy, here are two examples I got where there were several ties and cash was not selected (I don’t know how this will show up – formatting will be an issue – but cash is the last column).
    SPY VO VBR EFA VWO VNQ XLB GSG BND TIP IGOV SHY
    2009-10-30 0.0000000 0.0000000 0.1428571 0.1428571 0.1428571 0.1428571 0.0000000 0.0000000 0.1428571 0.1428571 0.1428571 0.00
    2011-03-31 0.1666667 0.1666667 0.1666667 0.0000000 0.0000000 0.0000000 0.1666667 0.1666667 0.0000000 0.1666667 0.0000000 0.00

    And here is a month where only 4 assets were picked and SHY is not one of them:
    SPY VO VBR EFA VWO VNQ XLB GSG BND TIP IGOV SHY
    2010-04-30 0.0000000 0.0000000 0.2500000 0.0000000 0.0000000 0.2500000 0.0000000 0.0000000 0.2500000 0.2500000 0.0000000 0.00

    There are also several occurrences where only 4 assets were picked and cash was one of them.

    I am not defending this portfolio for FAA BTW as it’s has many correlated assets. But it does show me how the ranking works in situations where ties are common.

  6. I just want to say your work is great, and the cohesiveness and level of detail you go into is something I’d aspire to (if I ever get to making a quantitative type blog).

    My comment is on the function code. In the line where you are equal weighting the assets that have ranks within the topN values, there is a possibility of the DIV/0 error. Your denominator is a sum of the elements of longs (a binary vector), so having a zero denominator can introduce some NaNs into the weights. I just inserted an IF to avoid dividing by zero, and things went well after that.

    Incidentally, it was by outputting the weights that I noticed this behaviour. I suppose the conditions for this occurring is if all the top ranked assets (I wasn’t using the 7 asset universe above) have negative momentum, which seems to have happened in 2008. At least, in my logic.

    Looking forward to more posts.

  7. There is a strange bug that if you leave out QRAAX, the historical data for all is cut off sometime in 2004 and the portfolio ends.

  8. The question of “When is cash king?” is always a good one. Clearly, the first answer is “Sometimes,” but the fun comes in being more specific. I have a fear that we’re tempted to attribute too much to small variations in performance that may be just good or bad luck. We typically consider the performance of monthly-trading algorithms like this for metric evaluation and trading at the end of the month (easily available data), but has anyone looked at the effect of choosing other trading times within the month? I’ve done Excel studies using weekly data; holding the analysis period constant (around 11 years for ETF data) and trading every 5 weeks, I’ve seen variations in CAGR of more than 2%/year for different choices of the trade week within the 5-week repeat period (first week, second week, etc.). Has anyone seen such behavior?

  9. Pingback: For A New Year, A New Asset Allocation System Just Published in SSRN | QuantStrat TradeR

  10. Pingback: Comparing Flexible and Elastic Asset Allocation | QuantStrat TradeR

      • Not the same for me.

        mutualFunds <- c("VTSMX", #Vanguard Total Stock Market Index
        "FDIVX", #Fidelity Diversified International Fund
        "VEIEX", #Vanguard Emerging Markets Stock Index Fund
        "VFISX", #Vanguard Short-Term Treasury Fund
        "VBMFX", #Vanguard Total Bond Market Index Fund
        "QRAAX", #Oppenheimer Commodity Strategy Total Return
        "VGSIX" #Vanguard REIT Index Fund
        )

        #mid 1997 to end of 2012
        getSymbols(mutualFunds, from="1997-06-30", to="2014-10-30")
        tmp <- list()
        for(fund in mutualFunds) {
        tmp[[fund]] <- Ad(get(fund))
        }

        #always use a list hwne intending to cbind/rbind large quantities of objects
        adPrices <- do.call(cbind, args = tmp)
        colnames(adPrices) <- gsub(".Adjusted", "", colnames(adPrices))
        faa1 <- FAA(prices = adPrices, monthLookback = 1, weightVol = 0, weightCor = 0,
        riskFreeName = "VFISX", stepCorRank = TRUE)

        faa6 <- FAA(prices = adPrices, monthLookback = 6, weightVol = 0, weightCor = 0,
        riskFreeName = "VFISX", stepCorRank = TRUE)

        faa12 <- FAA(prices = adPrices, monthLookback = 12, weightVol = 0, weightCor = 0,
        riskFreeName = "VFISX", stepCorRank = TRUE)
        compare <- na.omit(cbind(faa1, faa6, faa12))
        table.AnnualizedReturns(compare)

  11. Hi Ilya, I had a look at this code. Have you checked this for look ahead bias? When I debug it, it seems to use the returns in the month to calculate which securities it will hold, however it should look at the previous months returns. I may be wrong but can you point me to the part of the code that puts the lag in as I cant seem to find it? Thanks, great work on your blog by the way.

  12. Pingback: A Review of Modern Asset Allocation For Wealth Management, by David M. Berns, PhD | QuantStrat TradeR

Leave a comment