Original paper
Abstract
We conduct a pseudo real-time analysis of the existence and severity of speculative bubbles in eleven US sectors over the period 1973-2015. Based on the real-time bubble signals, a trading strategy is constructed which switches funds between the market index and those industry sectors that exhibit bubble dynamics. Our strategy generates highest after-transaction-cost return and Sharpe ratio, and first-order stochastically dominates three other investments (including two alternative active strategies as well as the buy-and-hold investment in the market index). Subsample analysis and specification checks confirm the robustness of the reported findings.
Keywords:Â Speculative bubbles, price-earnings ratio, explosive dynamics, real-time trading, stochastic dominance
Trading rules
- 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.
Python code
Backtrader
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[0]
t_stat = model.tvalues[0]
p_value = model.pvalues[0]
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_etfs' variable to work, it should be defined beforehand with the names of the ETFs or industry sectors
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.