We derive and calculate an ex-ante expectation for generalized trend-following rules, both on a single market as well as for a portfolio of trend strategies. Furthermore, the efficiency of trend-following rules applied to synthetic market data with varying degrees of drift and autocorrelation is discussed. Finding indicate that there is a positive relationship between drift, autocorrelation and the theoretically extractable Sharpe ratio for a trend following strategy. Drift is more important, since it is theoretically unbounded, but strong auto-correlation can create positive returns in the absence of long term drift.
The expected Sharpe ratio of a trend strategy is proportional to the absolute drift and auto-correlation of a market above a threshold. The expected return of a portfolio of trend strategies is also sensitive to the average correlation between the individual trading systems. Anyone engaging in trend following strategies, should expect to generate positive returns if the drift is strong enough or if there is enough autocorrelation, but should seldom expect to beat a market.
Keywords: Trend Following, Momentum, CTA, Managed Futures
- Target assets: Approximately 90 futures/ETFs spanning 5 major asset categories (developed equities, emerging equities, bonds, commodities, REITs).
- Apply a monthly trend analysis using a 10-month indicator on the asset types:
- Declining trend: Set aside 20% to a secure asset (U.S. Treasury Bills) by default.
- Ascending trend: Rank sub-components within asset class using 12-month return data normalized by prior 12-month volatility.
- Allocate to the top-performing 50% of the sub-categories (select the upper half of strong performers).
- Equal weighting: Among the main asset categories (each receiving 20%) and within the subcategories.
- Adjust the portfolio on a monthly basis.
import backtrader as bt import numpy as np class MomentumFilter(bt.Indicator): lines = ('momentum',) params = (('period', 10),) def __init__(self): self.addminperiod(self.params.period) def next(self): returns = (self.data.close / self.data.close[-self.params.period]) - 1 self.lines.momentum = returns class Strategy(bt.Strategy): params = ( ('momentum_period', 10), ('ranking_period', 12), ('risk_off_asset', 'US_T_Bills'), ) def __init__(self): self.investment_universe = self.datas self.inds = dict() for d in self.investment_universe: self.inds[d] = dict() self.inds[d]['momentum'] = MomentumFilter(d.close, period=self.params.momentum_period) def next(self): if self._counter % self.params.ranking_period == 0: risk_off = self.params.risk_off_asset rank_list =  for d in self.investment_universe: rank_list.append((d, self.inds[d]['momentum'])) rank_list.sort(key=lambda x: x, reverse=True) num_winners = len(rank_list) // 2 for i, (data, _) in enumerate(rank_list): if i < num_winners: target_weight = 0.2 / num_winners elif data._name == risk_off: target_weight = 0.2 else: target_weight = 0 self.order_target_percent(data, target_weight) self._counter += 1 # Your Backtrader Cerebro setup, adding data, strategy and running the backtest.