Industry Alpha Bubble Strategy: Monthly Long Allocation to Statistically Significant Outperforming Sectors
Log in to collectStrategy in a nutshell
The investment universe consists of equity industry funds (or ETFs), which are proxy for equity industry indexes. An investor uses ten years of past data to calculate the industry’s alpha based on the CAPM model (from the regression model industry_return = alpha + beta*market return, it is possible to use alternative models like the Fama/French 3 factor model). A bubble in an industry is detected if the industry’s alpha is statistically significant (academic
Economic rationale
A system’s validity seems strong as research shows that the industry bubbles are a different phenomenon than industry momentum. Since bubbles end with large negative abnormal returns, they cannot be explained by an underreaction to the good news.
Industry bubbles do not result from a misspecification of the asset pricing models used in research study: the bubbles cannot be explained by an omitted risk factor, an omitted structural break, or by a combination of factors, therefore, the trading strategy could be used as an independent add-on to the portfolio of strategies with potential for diversification.
III. SOURCE PAPER
Riding Bubbles [Click to Open PDF]
Nadja Guenster, University of Münster - Finance Center Muenster; University of California, Berkeley
Erik Kole, Erasmus University Rotterdam - Erasmus School of Economics - Econometric Institute; Erasmus Research Institute of Management; Tinbergen Institute
Ben Jacobsen, Tilburg University - TIAS School for Business and Society; Massey University
We empirically analyze rational investors' optimal response to asset price bubbles. We define bubbles as a sudden acceleration of price growth beyond the growth in fundamental value given by an asset pricing model. Our new bubble detection method requires only a limited time-series of historical returns. We apply our method to US industries and find strong statistical and economic support for the riding bubbles hypothesis: when an investor detects a bubble, her optimal portfolio weight increases significantly. A dynamic riding bubble strategy that uses only real-time information earns abnormal annual returns of 3% to 8%.
Backtest performance
Full Python code
import numpy as np
from AlgorithmImports import *
import statsmodels.api as sm
class RidingIndustryBubbles(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2008, 1, 1)
self.SetCash(100000)
self.spy = 'SPY'
self.symbols = ['XLF', 'XLV', 'XLP', 'XLY', 'XLI', 'XLE', 'XLB', 'XLK', 'XLU']
self.period = 10 * 12 * 21
self.SetWarmUp(self.period)
# Daily price data.
self.data = {}
for symbol in self.symbols + [self.spy]:
data = self.AddEquity(symbol, Resolution.Daily)
self.data[symbol] = RollingWindow[float](self.period)
self.recent_month = -1
def OnData(self, data):
# Store daily price data.
for symbol in self.symbols + [self.spy]:
symbol_obj = self.Symbol(symbol)
if symbol_obj in data and data[symbol_obj]:
self.data[symbol].Add(data[symbol_obj].Value)
if self.recent_month == self.Time.month:
return
self.recent_month = self.Time.month
if not self.data[self.spy].IsReady and self.spy in data: return
market_closes = [x for x in self.data[self.spy]]
separete_months = [market_closes[x:x+21] for x in range(0, len(market_closes),21)]
market_monthly_returns = []
for month in separete_months:
month_of_prices = [x for x in month]
market_monthly_returns.append(month_of_prices[0] / month_of_prices[-1] - 1)
# Prepared for regression.
market_monthly_returns = np.array(market_monthly_returns).T
market_monthly_returns = sm.add_constant(market_monthly_returns)
t_stat = {}
for symbol in self.symbols:
if self.data[symbol].IsReady and symbol in data:
closes = [x for x in self.data[symbol]]
separete_months = [closes[x:x+21] for x in range(0, len(closes),21)]
etf_monthly_returns = []
for month in separete_months:
month_of_prices = [x for x in month]
etf_monthly_returns.append(month_of_prices[0] / month_of_prices[-1] - 1)
# alpha t-stat calc.
model = sm.OLS(etf_monthly_returns, market_monthly_returns)
results = model.fit()
alpha_tstat = results.tvalues[0]
alpha_pvalue = results.pvalues[0]
t_stat[symbol] = (alpha_tstat, alpha_pvalue)
long = []
if len(t_stat) != 0:
long = [x[0] for x in t_stat.items() if x[1][0] >= 2 and x[1][1] >= 0.025] # The result is statistically significant, by the standards of the study, when p ≤ α
# Trade execution.
invested = [x.Key.Value for x in self.Portfolio if x.Value.Invested]
for symbol in invested:
if symbol not in long:
self.Liquidate(symbol)
for symbol in long:
self.SetHoldings(symbol, 1 / len(long))