Forming Momentum Decile Portfolios from Rolling Returns
Summary
The document addresses a replication problem in a Jegadeesh–Titman style momentum strategy: portfolios formed from rolling returns appeared to have identical results across formation periods and ranks. It outlines a procedure for calculating each security’s compounded return over several lookback windows by summing log returns and converting that sum back to a simple cumulative return.
The suggested correction is to assign momentum deciles separately on each date, ranking securities by their formation-period cumulative returns. The resulting groups can then be summarized by date and decile, with further statistics computed for each lookback period. Example output shows distinct average formation returns across deciles in a sample of randomly selected assets. This illustrates the grouping approach but does not establish the strategy’s performance: the excerpt gives no out-of-sample evidence and does not detail portfolio holding periods, transaction costs, or data-selection choices.
Key ideas
- Rolling sums of log returns can be converted into cumulative simple returns for each security and formation window.
- Momentum ranks should be assigned cross-sectionally for each date so that securities are compared with contemporaneous peers.
- Summarizing cumulative returns by date and decile helps reveal whether the ranking separates securities as intended.
- The example demonstrates formation-period group differences but does not test realized strategy returns or trading costs.
Tags
Full text
# replicating momentum strategy - Formation Periods
# replicating momentum strategy - Formation Periods
I am trying to replicate the momentum strategy of the study by Jegadesh and Titman (1993) onyl using data from the past 10 years.I am trying right now to calculate the formation period returns. But I think I have some mistakes. If I go further and then form the portfolios, ranking them, all 4 portfolios show the same returns for each 1-10 ranks. I have a feeling that my mistakes is already here in the formation periods but I dont get it. Maybe somebody can help me a bit. thanks a lot! (the data_subset is my original data with all variables included)
```
formation_periods <- c(3, 6, 9, 12)
momentum_portfolios <- list()
for (formation_period in formation_periods) {
subset <- data_subset %>%
select(PERMNO, Date, Returns, logreturns) %>%
arrange(PERMNO, Date)
subset$sumlogret <- subset %>%
group_by(PERMNO) %>%
mutate(sumlogret = rollsum(logreturns, formation_period, align = "right", fill = NA)) %>%
ungroup() %>%
pull(sumlogret)
subset <- subset %>%
rename(sumlogreturns = logreturns)
subset$cumret <- exp(subset$sumlogreturns) - 1
momentum_portfolios[[paste0("Formation_", formation_period)]] <- subset
}
```
here is my following code where I realized that all formation periods have the same numbers in it.
```
momentum_portfolio <- momentum_portfolios[[paste0("Formation_", formation_period)]]
momentum_portfolio <- momentum_portfolio[!is.na(momentum_portfolio$cumret), ]
momentum_portfolio$momr <- as.numeric(factor(momentum_portfolio$cumret, levels = unique(momentum_portfolio$cumret)))
momentum_portfolio$momr <- cut(momentum_portfolio$cumret, breaks = quantile(momentum_portfolio$cumret, probs = seq(0, 1, by = 0.1)), labels = FALSE)
cat("Formation Period:", formation_period, "\n")
cat("Examine distribution of cumret by momr:\n")
print(tapply(momentum_portfolio$cumret, momentum_portfolio$momr, mean))
}
tail(momentum_portfolio)
```
```
## Answer by Robert (score 0)
https://quant.stackexchange.com/a/77398
You could form the groups inside the main loop, but do it by each `Date`:
```
momentum_portfolios <- list()
for (formation_period in formation_periods) { # formation_period=6
subset <- data_subset %>%
select(PERMNO, Date, Returns, logreturns) %>%
arrange(PERMNO, Date)
subset$sumlogreturns <- subset %>%
group_by(PERMNO) %>%
mutate(sumlogret = rollsum(logreturns, formation_period, align = "right", fill = NA)) %>%
ungroup() %>%
pull(sumlogret)
subset$cumret <- exp(subset$sumlogreturns) - 1
subset <- subset %>% dplyr::filter(!is.na(cumret))
subset <- subset %>%
group_by(Date) %>%
mutate(momr = cut(cumret,
quantile(cumret, seq(0,1,by=.1)),
labels = F,
include.lowest = TRUE))
momentum_portfolios[[paste0("Formation_", formation_period)]] <- subset
}
```
Then you can calculate the mean of `cumret` by `Date` and `momr`. Finally compute stats by `formation_period` and `momr`.
```
stats = lapply(momentum_portfolios,function(momentum_portfolio){
tail(momentum_portfolio)
sumres = momentum_portfolio %>% group_by(Date,momr) %>%
summarise(MeanRet= mean(cumret))
sumres
})
lapply(stats, function(sumres){
psych::describeBy(sumres$MeanRet,sumres$momr,T,fast=T)
})
```
And get for example (the data is real for a sample of random assets):
```
$Formation_3
item group1 vars n mean sd min max range se
X11 1 1 1 163 -0.1406651698 0.10433599 -0.54924523 0.02337787 0.5726231 0.008172225
X12 2 2 1 163 -0.0594150045 0.06424748 -0.31759419 0.05568860 0.3732828 0.005032251
X13 3 3 1 163 -0.0258877359 0.05774981 -0.22716365 0.09620802 0.3233717 0.004523314
X14 4 4 1 163 -0.0006506752 0.05501481 -0.18303160 0.12743288 0.3104645 0.004309093
X15 5 5 1 163 0.0233330867 0.05508793 -0.14573197 0.18195906 0.3276910 0.004314820
X16 6 6 1 163 0.0469287583 0.05746346 -0.13073737 0.25985134 0.3905887 0.004500885
X17 7 7 1 163 0.0711782424 0.06206042 -0.11635815 0.36741241 0.4837706 0.004860947
X18 8 8 1 163 0.1013354021 0.06794467 -0.09081408 0.47001651 0.5608306 0.005321837
X19 9 9 1 163 0.1481222105 0.09273639 -0.04511237 0.70092962 0.7460420 0.007263675
X110 10 10 1 163 0.3806737037 0.43766965 0.03286265 2.82848241 2.7956198 0.034280932
```Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)
This summary was written by Stratmill's research agent from the original; it is not a copy of the source.