Momentum profits, resulting from buying winners and selling losers, are robust in the stock market worldwide. However, more than 40% of winners and losers immediately fall out of their respective groups in the month following formation, suggesting that intermediate-term momentum persistency is not universal among all stocks with extreme past performance. The return reversals that these nonpersistent winners and losers exhibit in the month following formation are strong, resulting in a monthly loss of more than 17% for a momentum strategy constructed on such stocks. By contrast, persistent winners and losers, defined as those staying in their groups for at least one more month, exhibit much stronger performance persistency. Further analysis indicates that the persistency is stronger for stocks with greater information asymmetry and more extensively heterogeneous investor beliefs, consistent with the underreaction hypothesis for price momentum.
Keywords: Delayed reaction hypothesis; Duration; Persistent losers; Persistent momentum strategy; Persistent winners; Heterogeneous beliefs; Information asymmetry
- Target stocks: Those listed on NYSE, AMEX, and NASDAQ having a minimum of 7 months of historical price records in the CRSP database.
- Construct a zero-investment portfolio at the end of the month (t) by:
- Going long on stocks ranked in the top 10% for returns during both t-7 to t-1 and t-6 to t intervals.
- Going short on stocks ranked in the bottom 10% for returns during both t-7 to t-1 and t-6 to t intervals.
- Equal weighting for stocks in the portfolio.
- Holding period: 6 months, no rebalancing.
- One-month skip between formation and holding periods.
import backtrader as bt import pandas as pd class ConsistentMomentumStrategy(bt.Strategy): params = ( ('lookback1', 7), ('lookback2', 6), ('holding_period', 6), ('skip_period', 1), ('decile', 0.1), ) def __init__(self): self.rank_data =  def next(self): if len(self.data) < self.params.lookback1 + self.params.holding_period: return if self.data.datetime.date(0).month == self.data.datetime.date(-1).month: return if self.data.datetime.date(0).month != self.data.datetime.date(-self.params.holding_period).month: return self.rank_data =  for data in self.datas: if len(data) >= self.params.lookback1 + self.params.holding_period: ret1 = (data.close - data.close[-self.params.lookback1]) / data.close[-self.params.lookback1] ret2 = (data.close - data.close[-self.params.lookback2]) / data.close[-self.params.lookback2] self.rank_data.append((data, ret1, ret2)) sorted_data = sorted(self.rank_data, key=lambda x: (x, x), reverse=True) top_decile = int(len(sorted_data) * self.params.decile) bottom_decile = len(sorted_data) - top_decile for i in range(top_decile): self.order_target_percent(sorted_data[i], target=1 / top_decile) for i in range(bottom_decile): self.order_target_percent(sorted_data[-(i + 1)], target=-1 / bottom_decile) self.rank_data =  if __name__ == '__main__': cerebro = bt.Cerebro() # Add your data feeds for NYSE, AMEX, and NASDAQ stocks # cerebro.adddata(feed) cerebro.addstrategy(ConsistentMomentumStrategy) cerebro.run()
Remember to replace the comment
# cerebro.adddata(feed) with your data feeds for NYSE, AMEX, and NASDAQ stocks with at least 7 months of price data on the CRSP database.