Jegadeesh & Titman Strategy Implementation

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
2 messages Options
Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Jegadeesh & Titman Strategy Implementation

ROUX, Nicolas
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

        [[alternative HTML version deleted]]

_______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-sig-finance
-- Subscriber-posting only. If you want to post, subscribe first.
-- Also note that this is not the r-help list where general R questions should go.
Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Jegadeesh & Titman Strategy Implementation

Enrico Schumann-2
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

_______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-sig-finance
-- Subscriber-posting only. If you want to post, subscribe first.
-- Also note that this is not the r-help list where general R questions should go.
Loading...