I recently read Stocks on the Move: Beating the Market with Hedge Fund Momentum Strategies. Within this book Clenow uncovers a realistic approach to professional portfolio management. Through theoretical basics, logical reasoning behind the applied approach, simulation of the model and finally a year by year walk-through of the actual results Clenow leaves no stone unturned.
In the first chapter he sets out evidence as to why you should probably steer clear of mutual funds. Over a three year period 77.53% of US mutual funds failed to beat their benchmarks and for the privilege you would have to pay management fees on top! The remainder of the book sets out principles as to how to go about developing your own momentum based stock system, along with the critical thinking at each step of the development process. Although Clenow uses the S&P 500 index as a benchmark, there is no reason as to why the system cannot be applied to other indexes around the world. Momentum has been well documented and has been found to occur in many things not just stock prices.
I put together some R code which pulls the data in, filters stocks that are trading above their 100 day moving average without gaps > 15% then computes a momentum score as per the book.
When we have decided which index we will benchmark against we have to get a list of constituents.In my own case I wanted to use the FTSE100. Within R I built a function to scrape a list of constituents from here and clean up the stock code to be used with Yahoo finance.
Ftse100 <- function(){
require(rvest)
require(data.table)
urls <- paste0("https://www.londonstockexchange.com/exchange/prices-and-markets/stocks/indices/summary/summary-indices-constituents.html?index=UKX&page=", 1:6)
## Grab tickers
Ftse100 <- sapply(urls, function(x) read_html(x) %>% html_table(1))
Ftse100 <- rbindlist(Ftse100)
Ftse100$Code <- paste0(gsub("[.]", "-", sub("[.]$", "", Ftse100$Code)), ".L")
Ftse100 <- unlist(Ftse100$Code)
return(Ftse100)
}
To run the function we use tickers <- Ftse100() and this will save the list of tickers to our object tickers. Using the BatchGetSymbols library of tools we can feed in our object tickers and download historical data for our stocks.
library(BatchGetSymbols)
library(data.table)
# set dates
first.date <- Sys.Date() - 360
last.date <- Sys.Date()
freq.data <- 'daily'
# set tickers
dfl <- BatchGetSymbols(tickers = tickers,
first.date = first.date,
last.date = last.date,
freq.data = freq.data,
cache.folder = file.path(tempdir(),
'BGS_Cache') )
### Unlist stock data to single datatable
dt <- rbindlist(dfl[2])
#### Clean data and change NA's to zero
dt <- dt[, lapply(.SD, function(x) na.locf(x, na.rm=FALSE, fromLast = TRUE)), by = ticker]
This function runs Clenows analysis;
SOTM <- function(x){
require(TTR)
atr14 <- tail(ATR(dt[ticker == x, c(3,4,7)], 14)[,2], n=1)
atr14 <- as.numeric(format(round(atr14, 3), nsmall = 3))
sma100 <- tail(SMA(dt[ticker == x, 7], n = 100),n = 1)
close <- as.numeric(tail(dt[ticker == x, 7, ], n = 1))
maxgap <- max(dt[ticker == x, (price.adjusted - shift(price.open, n=1, type = "lag")) / shift(price.open, n=1, type = "lag")], na.rm = T)
y = 1:90
### Code checked as per clenows website
## http://www.followingthetrend.com/stocks-on-the-move-figures-and- charts/#prettyPhoto/15/
slope <- coef(lm(tail(log(dt[ticker == x, price.adjusted, ]), n = 90) ~ y))[2]
AnnualisedSlope <- (exp(slope)^250)-1
Rsq <- summary(lm(tail(log(dt[ticker == x, price.adjusted, ]), n = 90) ~ y))$r.squared
trendScore <- AnnualisedSlope * Rsq
trendScore <- as.numeric(format(round(trendScore, 5), nsmall = 5))
if(close > sma100 & maxgap < 0.15){
return(data.table(x, close, slope, AnnualisedSlope, Rsq, trendScore, atr14))
}
}## end of function
Once the function is saved we run it by using the following code;
results <- do.call(rbind, lapply(unique(dt$ticker), SOTM))
Then we can sort our list by how well each stock scored;
results <- results[order(-trendScore),]
The current results for stocks matching Clenows criteria are below as of 18th November 2018 however, as the actual FTSE100 index is well below its 100 day moving average and so no new positions would be added. Current open positions would be held provided they remained on the list.
results
x close slope AnnualisedSlope Rsq trendScore atr14
1: BT-A.L 255.00 1.835016e-03 0.582101405 7.543629e-01 0.43912 8.459
2: WTB.L 4533.00 2.182868e-03 0.725845577 5.758008e-01 0.41794 101.137
3: EVR.L 551.20 1.569816e-03 0.480609652 4.342090e-01 0.20869 25.777
4: RRS.L 6408.00 1.778062e-03 0.559734155 2.479656e-01 0.13879 223.604
5: AZN.L 6204.00 6.675006e-04 0.181606743 2.459882e-01 0.04467 153.265
6: SHP.L 4563.00 4.823677e-04 0.128164439 3.221321e-01 0.04129 91.579
7: ADM.L 1980.50 5.710003e-04 0.153441484 2.578602e-01 0.03957 43.081
8: AAL.L 1702.80 6.518307e-04 0.176986861 1.418505e-01 0.02511 55.496
9: CNA.L 145.25 5.459467e-04 0.146239591 1.633968e-01 0.02390 4.549
10: UU.L 738.20 -4.239573e-06 -0.001059332 1.545609e-05 0.00000 19.252
11: SN.L 1399.00 -4.628239e-05 -0.011503917 1.616789e-03 -0.00002 25.050
12: NG.L 823.00 -5.437290e-05 -0.013501255 2.669664e-03 -0.00004 17.571
13: CCL.L 4520.00 -1.176632e-04 -0.028987372 4.445775e-03 -0.00013 89.435
14: ABF.L 2448.00 -1.475662e-04 -0.036219352 9.370458e-03 -0.00034 64.234
15: BLT.L 1606.80 -2.752219e-04 -0.066491745 4.771988e-02 -0.00317 48.223
16: RIO.L 3910.00 -4.309863e-04 -0.102144888 7.943885e-02 -0.00811 112.370
17: GSK.L 1562.00 -3.435307e-04 -0.082298110 1.208291e-01 -0.00994 32.018
18: PSON.L 930.40 -5.698747e-04 -0.132785276 1.195457e-01 -0.01587 21.386
19: ULVR.L 4273.00 -6.426394e-04 -0.148418305 2.943647e-01 -0.04369 76.853
20: DGE.L 2802.50 -5.701194e-04 -0.132838325 3.613716e-01 -0.04800 48.255
21: EXPN.L 1879.00 -7.628626e-04 -0.173632469 3.162446e-01 -0.05491 46.431
22: REL.L 1620.00 -1.524051e-03 -0.316830811 6.479692e-01 -0.20530 30.350
Now that we have our stock list and data we can easily plot one of the stocks using
plot(dt[ticker == "<TICKER CODE>", price.adjusted], type= "l")

I hope the code works alright however if you have any problems feel free to drop me a message or leave a comment.
Hi jswinb007,
What a fantastic coding try on momentum strategy from the book! In recent, I finished the book. The author tried to apply momentum strategy on S&P 500 stocks and done several back-testing (more than 10 years) for reference. It is a good try to apply the same rules on Ftse 100.
Could you please provide the back-testing results and R codes on Ftse 100 stocks applying the momentum strategy? Many thanks and appreciate.
LikeLike
hi
LikeLike