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%.
Keywords: bubbles, limits to arbitrage, market efficiency, structural breaks
- Investment scope: Industry-specific equity funds (or ETFs) as proxies for equity industry indices.
- Use 10 years of historical data to derive industry alpha with the CAPM formula (though other methods like the Fama/French 3 factor approach are acceptable).
- Identify industry bubbles by looking for notably significant alphas (referenced academic paper suggests a 97.5% confidence level, but alternative levels can be applied).
- Long positions: Allocate funds to industries showing bubble tendencies using an equal distribution strategy across such industries.
- Don’t invest if no signs of bubbles are found.
- Monthly data examination, alpha calculation, and portfolio rebalancing.
import backtrader as bt import pandas as pd import numpy as np import statsmodels.api as sm from scipy import stats class IndustryBubbleStrategy(bt.Strategy): def __init__(self): self.industries = self.datas def next(self): long_positions =  significance_threshold = 0.975 for industry in self.industries: prices = industry.get(size=120) market_returns = prices / prices.shift(1) - 1 industry_returns = industry.get(size=120) / industry.get(size=120).shift(1) - 1 X = sm.add_constant(market_returns) model = sm.OLS(industry_returns, X).fit() alpha = model.params t_stat = model.tvalues p_value = model.pvalues if p_value < (1 - significance_threshold) and t_stat > 0: long_positions.append(industry) if long_positions: position_size = 1 / len(long_positions) for industry in long_positions: self.order_target_percent(industry, target=position_size) else: for industry in self.industries: self.order_target_percent(industry, target=0.0) if __name__ == "__main__": cerebro = bt.Cerebro() data = bt.feeds.PandasData(dataname=pd.DataFrame(), timeframe=bt.TimeFrame.Months) for industry in industry_etfs: cerebro.adddata(data) cerebro.addstrategy(IndustryBubbleStrategy) cerebro.run()
Please note that this code snippet assumes you have data for each industry ETF (or fund) in a variable called
industry_etfs. Replace it with the appropriate data source.