“Investor utilizes Russell’s ETFs for six equity styles. Monthly, they calculate 12-month momentum, going long on winners and short on losers, with monthly rebalancing.”

I. STRATEGY IN A NUTSHELL

Russell’s ETFs for six equity styles are used (small-cap value, mid-cap value, large-cap value, small-cap growth, mid-cap growth, large-cap growth). Each month, the investor calculates 12-month momentum for each style and goes long on the winner and short on the loser. The portfolio is rebalanced each month.

II. ECONOMIC RATIONALE

The obvious observation is that styles perform differently over time (the same way different assets do). The popularity of style investing itself may influence the structure and dynamics of asset returns since prices deviate substantially from fundamental values as styles become popular or unpopular. This non-random behavior gives the foundation to the rise of exploitable momentum.

III. SOURCE PAPER

Using Style Index Momentum to Generate Alpha [Click to Open PDF]

Samuel L. Tibbs, American University of Sharjah – School of Business and Management ; Stanley G. Eakins, East Carolina University – College of Business ; William DeShurko, 401 Advisor, LLC

<Abstract>

Russell style indexes exhibit significant momentum, particularly after medium term out- and underperformance. The existence of this momentum produces a diversified, index-based low-cost means to exploit momentum by incorporating relative style index performance into tactical allocation strategies. Such style index momentum trading strategies have outperformed on both a raw and risk-adjusted return basis, with the long minus short portfolio generating an average 9.25% annual return over the 34-year period analyzed. Although the excess returns vary, they are robust through time and after controlling for potentially confounding effects. Additionally, the returns are not driven by any single style index and portfolio reconstruction is, on average, required every six months.

IV. BACKTEST PERFORMANCE

Annualised Return9.25%
Volatility16.01%
Beta-0.042
Sharpe Ratio-0.246
Sortino Ratio-0.278
Maximum Drawdown53.5%
Win Rate52%

V. FULL PYTHON CODE

from AlgoLib import *

class MomentumFactorAndStyleRotationEffect(XXX):

    def Initialize(self):
        self.SetStartDate(2000, 1, 1)
        self.SetCash(100000)
        
        self.tickers = [
            'IWS', # iShares Russell Midcap Value ETF
            'IWP', # iShares Russell Midcap Growth ETF
            'IWN', # iShares Russell 2000 Value ETF
            'IWO', # iShares Russell 2000 Growth ETF
            'IVE', # iShares S&P 500 Value ETF
            'IVW'  # iShares S&P 500 Growth ETF       
        ]
        
        self.mom = {}
        
        self.period = 12 * 21
        self.SetWarmUp(self.period)
        
        for ticker in self.tickers:
            security = self.AddEquity(ticker, Resolution.Daily)
            security.SetFeeModel(CustomFeeModel())
            security.SetLeverage(10)
            
            self.mom[security.Symbol] = self.MOM(security.Symbol, self.period)
        
        self.recent_month = -1
        
    def OnData(self, data):
        if self.recent_month == self.Time.month:
            return
        self.recent_month = self.Time.month
        
        mom_ready = [ s for s in self.mom if self.mom[s].IsReady and s in data]
        if mom_ready:
            sorted_mom = sorted(mom_ready, key = lambda x: self.mom[x].Current.Value, reverse=True)
            
            for symbol in sorted_mom[1:-1]:
                if self.Portfolio[symbol].Invested:
                    self.Liquidate(symbol)
            
            winner = sorted_mom[0]
            loser = sorted_mom[-1]
            
            if self.Securities[winner].Price != 0 and self.Securities[winner].IsTradable:
                if (self.Time.month == 10 and self.Time.year == 2020) and winner.Value == 'IVW':    # prevent data error
                    self.Liquidate(winner)
                else:
                    self.SetHoldings(winner, 1)
            
            if self.Securities[loser].Price != 0 and self.Securities[loser].IsTradable:
                if (self.Time.month == 10 and self.Time.year == 2020) and loser.Value == 'IVW':     # prevent data error
                    self.Liquidate(loser)
                else:
                    self.SetHoldings(loser, -1)
        
# Custom fee model.
class CustomFeeModel(FeeModel):
    def GetOrderFee(self, parameters):
        fee = parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0.00005
        return OrderFee(CashAmount(fee, "USD"))

Leave a Reply

Discover more from Quant Buffet

Subscribe now to keep reading and get access to the full archive.

Continue reading