Momentum in R: Part 4 with Quantstrat

The past few posts on momentum with R focused on a relatively simple way to backtest momentum strategies. In part 4, I use the quantstrat framework to backtest a momentum strategy. Using quantstrat opens the door to several features and options as well as an order book to check the trades at the completion of the backtest.

I introduce a few new functions that are used to prep the data and compute the ranks. I won’t go through them in detail, these functions are available in my github repo in the rank-functions folder.

This first chunk of code just loads the necessary libraries, data, and applies the ave3ROC function to rank the assets based on averaging the 2, 4, and 6 month returns. Note that you will need to load the functions in Rank.R and monthly-fun.R.


symbols <- c("XLY", "XLP", "XLE", "AGG", "IVV")
stock(symbols, currency="USD")

# get data for the symbols
getSymbols(symbols, from="2005-01-01", to="2012-12-31")

# create an xts object of monthly adjusted close prices
symbols.close <- monthlyPrices(symbols)

# create an xts object of the symbol ranks
sym.rank <- applyRank(x=symbols.close, rankFun=ave3ROC, n=c(2, 4, 6))

Created by Pretty R at

The next chunk of code is a critical step in preparing the data to be used in quantstrat. With the ranks computed, the next step is to bind the ranks to the actual market data to be used with quantstrat. It is also important to change the column names to e.g. XLY.Rank because that will be used as the trade signal column when quantstrat is used.

# this is an important step in naming the columns, e.g. XLY.Rank
# the "Rank" column is used as the trade signal (similar to an indicator)
# in the qstratRank function
colnames(sym.rank) <- gsub(".Adjusted", ".Rank", colnames(sym.rank))

# ensure the order of order symbols is equal to the order of columns 
# in symbols.close
stopifnot(all.equal(gsub(".Adjusted", "", colnames(symbols.close)), symbols))

# bind the rank column to the appropriate symbol market data
# loop through symbols, convert the data to monthly and cbind the data
# to the rank
for(i in 1:length(symbols)) {
  x <- get(symbols[i])
  x <- to.monthly(x,indexAt='lastof',drop.time=TRUE)
  indexFormat(x) <- '%Y-%m-%d'
  colnames(x) <- gsub("x",symbols[i],colnames(x))
  x <- cbind(x, sym.rank[,i])

Created by Pretty R at

Now the backtest can be run. The function qstratRank is just a convenience function that hides the quantstrat implementation for my Rank strategy.

For this first backtest, I am trading the top 2 assets with a position size of 1000 units.

# run the backtest
bt <- qstratRank(symbols=symbols, init.equity=100000, top.N=2,
                  max.size=1000, max.levels=1)

# chart of returns
charts.PerformanceSummary(bt$returns[,"total"], geometric=FALSE, 
                          wealth.index=TRUE, main="Total Performance")

Created by Pretty R at


Changing the argument to max.levels=2 gives the flexibility of “scaling” in a trade. In this example, say asset ABC is ranked 1 in the first month — I buy 500 units. In month 2, asset ABC is still ranked 1 — I buy another 500 units.

# run the backtest
bt <- qstratRank(symbols=symbols, init.equity=100000, top.N=2,
                  max.size=1000, max.levels=2)

# chart of returns
charts.PerformanceSummary(bt$returns[,"total"], geometric=FALSE, 
                          wealth.index=TRUE, main="Total Performance")

Created by Pretty R at


Full code available here: quantstrat-rank-backtest.R