Timing S&P500 Using Full vs. Partial Employment
Log in to collectAcademic paper
Strategy in a nutshell
The strategy trades SPY and IEF ETFs based on employment data. Compare the annual growth rates of full-time and part-time employment. If full-time employment growth exceeds part-time, invest in SPY (equities); otherwise, invest in IEF (bonds). The portfolio is rebalanced monthly, though the rebalancing frequency and data choice (monthly or weekly) can be adjusted based on the investor's preference. This approach uses labor market trends to allocate between equities and bonds, aiming to capture economic momentum and adjust exposure based on workforce composition changes.
Economic rationale
This strategy forecasts recessionary activity rather than recessions themselves, focusing on identifying reliable patterns using diverse employment data. Analyzing large datasets, normalized to the same scale and treated as separate events, produces significant, usable results, unlike relying on single indicators like the unemployment rate. Macroeconomic data, unlike market-derived indicators, cannot be traded or arbitraged away, offering stable insights. Equity prices, by contrast, are more volatile and subject to non-economic influences. Simpler models, such as differences between two variables, often outperform complex, multi-variable conditions. Since 1956, every recession named by the National Bureau of Economic Research has been identified using this approach.
Backtest performance
Full Python code
import pandas as pdclass Full_Partial_Employment(QCAlgorithm): def Initialize(self): self.SetStartDate(2005, 1, 1) self.SetEndDate(2019, 7, 1) self.SetCash(100000) self.employment_data = pd.read_csv('https://docs.google.com/spreadsheets/d/1P0E8_ZUAm1NhqaMB1PK1Bc5uB2wL3f6W/export?format=csv', dtype={'date':str}, index_col='date') # header=None) symbols = ['SPY', 'IEF'] for symbol in symbols: self.AddEquity(symbol, Resolution.Daily) self.Schedule.On(self.DateRules.MonthStart(symbols[0]), self.TimeRules.AfterMarketOpen(symbols[0]), self.Rebalance) def Rebalance(self): date = str(self.Time.month) + '/' + str(self.Time.year) current_row_index = self.employment_data.index.get_loc(date) #one month lag due to employment data reporting this_month = self.employment_data.iloc[current_row_index-1] last_month = self.employment_data.iloc[current_row_index-2] full_time_diff = this_month['full_time'] - last_month['full_time'] part_time_diff = this_month['part_time'] - last_month['part_time'] if full_time_diff > part_time_diff: if not self.Portfolio['SPY'].Invested: self.Liquidate('IEF') self.SetHoldings('SPY', 1) else: if not self.Portfolio['IEF'].Invested: self.Liquidate('SPY') self.SetHoldings('IEF', 1)