Hello all, Is there a package/function capable of implementing a momentum strategy described in Jegadeesh & Titman (1993) and backtesting it? General steps of the strategy are: 1- taking monthly stock prices/returns, 2- ranking monthly/period returns, 3- create equally weighted portfolio of top and bottom stock returns, 4- hold for "n" months (quarter, semester, year) with no updating in between, 5- rebalance portfolio after holding period, 6- return results. I have created a roundabout way using return.portfolio from performanceanalytics but would like to use a package which allows the possibility to progressively more complex strategies. I cannot find a way to implement the holding period in the quantstrat package or the ranking conditions and holding period in portfolioanalytics package (uses only a complex ranking method). Cheers, Nicolas Roux
Jegadeesh & Titman Strategy Implementation
2 messages · ROUX, Nicolas, Enrico Schumann
3 days later
On Thu, 16 Feb 2017, ROUX, Nicolas writes:
Hello all, Is there a package/function capable of implementing a momentum strategy described in Jegadeesh & Titman (1993) and backtesting it? General steps of the strategy are: 1- taking monthly stock prices/returns, 2- ranking monthly/period returns, 3- create equally weighted portfolio of top and bottom stock returns, 4- hold for "n" months (quarter, semester, year) with no updating in between, 5- rebalance portfolio after holding period, 6- return results. I have created a roundabout way using return.portfolio from performanceanalytics but would like to use a package which allows the possibility to progressively more complex strategies. I cannot find a way to implement the holding period in the quantstrat package or the ranking conditions and holding period in portfolioanalytics package (uses only a complex ranking method). Cheers, Nicolas Roux
Hi Nicolas, I have written some code, a package actually, that might be used for such computations. It is available from http://enricoschumann.net/R/packages/PMwR/index.htm or from https://github.com/enricoschumann/PMwR . It is far from complete, but perhaps it is useful. Below is a brief example, for which I create random monthly prices of 100 assets. These I store in a matrix P (see below for the code). Dates are stored in a vector 'timestamp'. Just plug in your own data instead, but with these random data data, you could directly run the example below. The key ingredient for simulating a strategy is a function that is called at any instant of time at which trading may take place, and which returns the desired position (or, alternatively, the desired weights). For a simple momentum strategy, it may look like this: mom_weights <- function() { k <- 10 ## Number of stocks in the portfolio. M <- Close(lag = 1)/Close(lag = 13) ## Compute 1-year return and r <- order(M) ## rank assets. Close(lag = ...) returns a ## single-row matrix of close prices. w <- numeric(length(M)) ## Set equal-weight portfolios. w[head(r, k)] <- -1/k w[tail(r, k)] <- 1/k w } You can then call the function 'btest' ('backtest') and either take the raw equity curve (as a zoo object, say) or have some stats computed. require("PMwR") result <- btest(prices = list(P), signal = mom_weights, b = 13, ## the burn-in convert.weights = TRUE, ## since mom_weights returns weights, ## convert them to positions initial.cash = 100, timestamp = timestamp, include.data = TRUE) summary(as.NAVseries(result)) ## --------------------------------------------------------- ## 31 Dec 1997 ==> 31 Dec 2016 (229 data points, 0 NAs) ## 100 81.2107 ## --------------------------------------------------------- ## High 105.04 (30 Nov 1999) ## Low 74.05 (31 Jan 2015) ## --------------------------------------------------------- ## Return (%) -1.1 (annualised) ## --------------------------------------------------------- ## Max. drawdown (%) 29.5 ## _ peak 105.04 (30 Nov 1999) ## _ trough 74.05 (31 Jan 2015) ## _ underwater now (%) 22.7 ## --------------------------------------------------------- ## Volatility (%) 6.3 (annualised) ## _ upside 4.2 ## _ downside 4.7 ## --------------------------------------------------------- ## ## Monthly returns ??????? ## ## Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec YTD ## 1998 0.0 -1.9 -1.0 2.2 1.7 0.1 -1.3 -0.3 -1.4 1.8 1.1 -2.1 -1.2 ## 1999 -2.3 1.1 -0.6 0.6 1.0 1.5 -0.2 0.2 3.1 1.7 0.1 -1.5 4.8 ## 2000 0.7 -3.0 -0.3 -1.3 -2.3 -1.0 0.7 0.4 -1.1 -1.8 2.0 -1.7 -8.6 ## [ ... ] (Since I used random data, these numbers signify nothing.) Now, the previous call of btest computed the portfolio every month: unique(journal(result)$timestamp) ## [1] "1998-01-31" "1998-02-28" "1998-03-31" "1998-04-30" "1998-05-31" ## [6] "1998-06-30" "1998-07-31" "1998-08-31" "1998-09-30" "1998-10-31" There are several possiblities for how to trade less often, but a simple one here would be to precompute the dates at which trading should take place, and pass these points in time as parameter 'do.signal'. For example, to trade only every second timestamp: result <- btest(list(P), signal = mom_weights, do.signal = seq(1, length(timestamp), by = 2), b = 13, convert.weights = TRUE, initial.cash = 100, timestamp = timestamp, include.data = TRUE) unique(journal(result)$timestamp) ## [1] "1998-02-28" "1998-04-30" "1998-06-30" "1998-08-31" "1998-10-31" ## [6] "1998-12-31" "1999-02-28" "1999-04-30" "1999-06-30" "1999-08-31" Kind regards Enrico ## Appendix: Random data na <- 100 ## number of assets timestamp <- seq(as.Date("1996-12-01"), as.Date("2016-12-31"), by = "1 day") timestamp <- aggregate(timestamp, by = list(format(timestamp, "%Y-%m")), FUN = tail, 1)[[2]] np <- length(timestamp) P <- array(rnorm(np*na, mean = 0.0025, sd = 0.04), dim = c(np, na)) P[1, ] <- 0 P <- apply(P, 2, function(x) cumprod(1 + x)) plot(P[ ,1])
Enrico Schumann Lucerne, Switzerland http://enricoschumann.net