Original paper
Abstract
We find that the acceleration and deceleration patterns of historical prices are predictive of future expected returns in momentum investing in the U.S. equity market from 1962 to 2014. Winners with accelerated historical price increases deliver higher future expected returns and losers with accelerated historical price decreases perform more poorly in the future. Hence, the profit from buying past accelerated winners and shorting past accelerated losers is significantly higher than the momentum profit by 51.47%. Such profit cannot be subsumed by certain characteristics that have been considered to explain momentum. Possible explanations for our results include extrapolative bias and overreaction.
Keywords:Â Momentum, Historical price evolution, Extrapolation; Overreaction
Trading rules
- Focus on: Stocks listed on NYSE, AMEX, and NASDAQ
- Compute acceleration metric: Momentum over 6 months minus momentum from the previous 6 months (from 6 months ago)
- Categorize stocks into ten groups with equal distribution in each group
- Adopt a long position in stocks with high acceleration, and a short position in those with low acceleration
- Acceleration effect materializes after 6 months, strongest returns at 12 months.
- Ideal approach: Initiate stock purchases 6 months post-portfolio creation and maintain for an additional 6 months
- To integrate the acceleration and momentum effects:
- Determine acceleration by assessing: (Momentum from month T-7 to T-12) subtracted from (momentum from month T-13 to T-18)
- Organize stocks into ten groups based on their acceleration rate
- At month's end (T), adopt a long position on the highest momentum stocks (from month T-1 to T-6) of the top acceleration group
- Similarly, adopt a short position on the lowest momentum stocks (from month T-1 to T-6) of the lowest acceleration group
- Hold long/short portfolio for six months.
Python code
Backtrader
import backtrader as bt
import pandas as pd
import numpy as np
class AccelerationMomentum(bt.Strategy):
def __init__(self):
self.stock_data = dict()
def prenext(self):
self.next()
def next(self):
# Every 6 months, reevaluate the portfolio
if len(self) % 126 == 0:
self.rebalance_portfolio()
def rebalance_portfolio(self):
stocks_accel = []
for d in self.datas:
# Calculate 6-month momentum
momentum_6m = d.close[0] / d.close[-126]
# Calculate 6-month momentum six months prior
momentum_6m_prior = d.close[-126] / d.close[-252]
# Calculate acceleration ratio
acceleration_ratio = momentum_6m - momentum_6m_prior
stocks_accel.append((d, acceleration_ratio))
# Sort stocks by acceleration ratio and divide into deciles
sorted_stocks = sorted(stocks_accel, key=lambda x: x[1], reverse=True)
deciles = np.array_split(sorted_stocks, 10)
# Get top and bottom deciles
top_decile = deciles[0]
bottom_decile = deciles[-1]
# Sort top decile by momentum (T-1 to T-6)
top_decile_momentum = sorted(top_decile, key=lambda x: x[0].close[0] / x[0].close[-6], reverse=True)
# Sort bottom decile by momentum (T-1 to T-6)
bottom_decile_momentum = sorted(bottom_decile, key=lambda x: x[0].close[0] / x[0].close[-6])
# Go long on top decile stocks
for stock in top_decile_momentum:
self.order_target_percent(stock[0], target=1.0 / len(top_decile_momentum))
# Go short on bottom decile stocks
for stock in bottom_decile_momentum:
self.order_target_percent(stock[0], target=-1.0 / len(bottom_decile_momentum))
if __name__ == '__main__':
cerebro = bt.Cerebro()
# Add all stocks in the investment universe
# (Replace 'stock_data' with actual stock data)
for stock in stock_data:
data = bt.feeds.PandasData(dataname=stock_data[stock])
cerebro.adddata(data)
cerebro.addstrategy(AccelerationMomentum)
cerebro.broker.setcash(100000.0)
cerebro.run()
Note: This code is a starting point and should be adapted according to your specific data source and requirements. The ‘stock_data’ variable should be replaced with actual stock data from NYSE, AMEX, and NASDAQ. Additionally, ensure that your stock data has the required history for the calculations.