Original paper
Abstract
Using a large sample of countries and 60 years of data, the authors found a strong and intuitive link between demographic transitions and both GDP growth and capital market returns. Unlike previous researchers, who used ad hoc and restrictive demographic variables, the authors imposed a smooth and parsimonious polynomial curve across all age groups. They also performed robustness checks and produced forecasts for the coming decade, with all the necessary caveats.
Keywords:Â demography, demographics
Trading rules
- Target investment: Focus on 15 developed countries (Australia, Austria, Belgium, Canada, Denmark, France, Germany, Italy, Japan, Netherlands, Spain, Sweden, Switzerland, UK, US).
- Evaluate the proportion of adults over 65 in every nation.
- Determine the mean and variability for this proportion.
- Adopt a long position in nations where the elderly (population >65) constitute a percentage that's at least 1 standard deviation under the mean.
- Adopt a short position in nations where the elderly represent a percentage that's at least 1 standard deviation over the mean.
- Adjust portfolio positions once every year.
Python code
Backtrader
import backtrader as bt
import pandas as pd
class DemographicStrategy(bt.Strategy):
def __init__(self):
self.country_data = pd.DataFrame({
'country': ['Australia', 'Austria', 'Belgium', 'Canada', 'Denmark', 'France', 'Germany', 'Italy', 'Japan', 'Netherlands', 'Spain', 'Sweden', 'Switzerland', 'UK', 'US'],
'percent_over_65': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] # Fill in actual data
})
def next(self):
if self.datetime.date().month != 12:
return
mean_over_65 = self.country_data['percent_over_65'].mean()
std_over_65 = self.country_data['percent_over_65'].std()
long_countries = self.country_data[self.country_data['percent_over_65'] < mean_over_65 - std_over_65]
short_countries = self.country_data[self.country_data['percent_over_65'] > mean_over_65 + std_over_65]
# Allocate equal weights to long and short positions long_weight = 1.0 / len(long_countries)
short_weight = -1.0 / len(short_countries)
# Adjust positions
for d in self.getdatanames():
country = d.split('.')[0]
if country in long_countries['country'].values:
self.order_target_percent(data=d, target=long_weight)
elif country in short_countries['country'].values:
self.order_target_percent(data=d, target=short_weight)
else:
self.order_target_percent(data=d, target=0.0)
cerebro = bt.Cerebro()
# Add data feeds for each country's ETF
# Data should be in a format that includes the country name in the dataname field
# Example: cerebro.adddata(bt.feeds.YahooFinanceData(dataname='Australia.ETF', ...))
# ...
cerebro.addstrategy(DemographicStrategy)
cerebro.run()
Please note that you will need to replace the ‘percent_over_65’ values in the ‘country_data’ DataFrame with the actual data. Also, make sure to add the data feeds for each country’s ETF to the ‘cerebro’ object before running the strategy.