GDP growth #
This category group includes simple measures of short- and long-term real GDP growth based on national accounts alone. Unlike “intuitive” and “technical” GDP growth estimates they do not use higher-frequency activity indicators. Long-term growth rates are typically used as benchmarks for the performance of concurrent activity and real interest rates. In strategy development, long-term real GDP growth serves as a proxy for “potential growth”, the sustainable trend path of the economy. Traditional measures of potential GDP growth would require elaborate estimation and are subject to much methodological debate.
Simple GDP growth rates #
Ticker : RGDP_SA_P1Q1QL1AR / _P2Q2QL2AR / RGDP_SA_P1Q1QL4
Label : Real GDP: %q/q, saar / %2q/2q, saar / % oya
Definition : Real GDP based on quarterly national accounts: %quarter-on-quarter, seasonally adjusted annualized / %2quarters-over-2quarters, seasonally adjusted annualized / % over a year ago
Notes :
-
In general all these growth indicators rate used vintages of national accounts data only.
-
For some countries and older vintages where quarterly national countries were not available, annual data history has been used.
5-year real GDP growth #
Ticker : RGDP_SA_P1Q1QL4_20QMA / _P1Q1QL4_20QMM
Label : Long-term real GDP growth: 5-year moving average / 5-year moving median
Definition : Long-term real GDP growth, % over a year ago, based on a trailing lookback window: 5-year (20 quarters) moving average / 5-year (20 quarters) moving median
Notes :
-
This average uses only quarterly national accounts data of the concurrently available vintages. For some older history where quarterly reports were not available, annual data history has been used.
10-year real GDP growth #
Ticker : RGDP_SA_P1Q1QL4_40QMA / _P1Q1QL4_40QMM
Label : Long-term real GDP growth: 10-year moving average / 10-year moving median.
Definition : Long-term real GDP growth, % over a year ago, based on a trailing lookback window: 10-year (40 quarters) moving average / 10-year (40 quarters) moving median.
Notes :
-
This average uses only quarterly national accounts data of the concurrently available vintages. For some older history where quarterly reports were not available, annual data history has been used.
Imports #
Only the standard Python data science packages and the specialized
macrosynergy
package are needed.
import os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import math
import json
import yaml
import macrosynergy.management as msm
import macrosynergy.panel as msp
import macrosynergy.signal as mss
import macrosynergy.pnl as msn
from macrosynergy.download import JPMaQSDownload
from timeit import default_timer as timer
from datetime import timedelta, date, datetime
import warnings
warnings.simplefilter("ignore")
The
JPMaQS
indicators we consider are downloaded using the J.P. Morgan Dataquery API interface within the
macrosynergy
package. This is done by specifying
ticker strings
, formed by appending an indicator category code
<category>
to a currency area code
<cross_section>
. These constitute the main part of a full quantamental indicator ticker, taking the form
DB(JPMAQS,<cross_section>_<category>,<info>)
, where
<info>
denotes the time series of information for the given cross-section and category.
The following types of information are available:
-
value
giving the latest available values for the indicator -
eop_lag
referring to days elapsed since the end of the observation period -
mop_lag
referring to the number of days elapsed since the mean observation period -
grade
denoting a grade of the observation, giving a metric of real time information quality.
After instantiating the
JPMaQSDownload
class within the
macrosynergy.download
module, one can use the
download(tickers,
start_date,
metrics)
method to obtain the data. Here
tickers
is an array of ticker strings,
start_date
is the first release date to be considered and
metrics
denotes the types of information requested.
# Cross-sections of interest
cids_dmca = [
"AUD",
"CAD",
"CHF",
"EUR",
"GBP",
"JPY",
"NOK",
"NZD",
"SEK",
"USD",
] # DM currency areas
cids_dmec = ["DEM", "ESP", "FRF", "ITL", "NLG"] # DM euro area countries
cids_latm = ["BRL", "COP", "CLP", "MXN", "PEN"] # Latam countries
cids_emea = ["CZK", "HUF", "ILS", "PLN", "RON", "RUB", "TRY", "ZAR"] # EMEA countries
cids_emas = [
"CNY",
"HKD",
"IDR",
"INR",
"KRW",
"MYR",
"PHP",
"SGD",
"THB",
"TWD",
] # EM Asia countries
cids_dm = cids_dmca + cids_dmec
cids_em = cids_latm + cids_emea + cids_emas
cids = sorted(cids_dm + cids_em)
# Quantamental categories of interest
main = [
"RGDP_SA_P1Q1QL4",
"RGDP_SA_P2Q2QL2AR",
"RGDP_SA_P1Q1QL1AR",
"RGDP_SA_P1Q1QL4_20QMA",
"RGDP_SA_P1Q1QL4_20QMM",
"RGDP_SA_P1Q1QL4_40QMA",
"RGDP_SA_P1Q1QL4_40QMM",
]
econ = [
"INTRGDPv5Y_NSA_P1M1ML12_3MMA",
"INTRGDPv10Y_NSA_P1M1ML12_3MMA",
] # economic context
mark = [
"EQXR_NSA",
"EQXR_VT10",
"FXXR_NSA",
"FXXR_VT10",
"DU05YXR_NSA",
"DU05YXR_VT10",
"FXTARGETED_NSA",
"FXUNTRADABLE_NSA",
] # market links
xcats = main + econ + mark
# Download series from J.P. Morgan DataQuery by tickers
start_date = "1995-01-01"
tickers = [cid + "_" + xcat for cid in cids for xcat in xcats]
print(f"Maximum number of tickers is {len(tickers)}")
# Retrieve credentials
client_id: str = os.getenv("DQ_CLIENT_ID")
client_secret: str = os.getenv("DQ_CLIENT_SECRET")
# Download from DataQuery
with JPMaQSDownload(client_id=client_id, client_secret=client_secret) as downloader:
start = timer()
df = downloader.download(
tickers=tickers,
start_date=start_date,
metrics=["value", "eop_lag", "mop_lag", "grading"],
suppress_warning=True,
show_progress=True,
)
end = timer()
dfd = df
print("Download time from DQ: " + str(timedelta(seconds=end - start)))
Maximum number of tickers is 646
Downloading data from JPMaQS.
Timestamp UTC: 2024-07-02 13:33:22
Connection successful!
Requesting data: 100%|██████████| 130/130 [00:29<00:00, 4.40it/s]
Downloading data: 100%|██████████| 130/130 [00:38<00:00, 3.37it/s]
Some expressions are missing from the downloaded data. Check logger output for complete list.
348 out of 2584 expressions are missing. To download the catalogue of all available expressions and filter the unavailable expressions, set `get_catalogue=True` in the call to `JPMaQSDownload.download()`.
Some dates are missing from the downloaded data.
2 out of 7699 dates are missing.
Download time from DQ: 0:01:19.271962
Availability #
cids_exp = sorted(
list(set(cids) - set(cids_dmec + ["ARS", "HKD"]))
) # cids expected in category panels
msm.missing_in_df(dfd, xcats=main, cids=cids_exp)
No missing XCATs across DataFrame.
Missing cids for RGDP_SA_P1Q1QL1AR: []
Missing cids for RGDP_SA_P1Q1QL4: []
Missing cids for RGDP_SA_P1Q1QL4_20QMA: []
Missing cids for RGDP_SA_P1Q1QL4_20QMM: []
Missing cids for RGDP_SA_P1Q1QL4_40QMA: []
Missing cids for RGDP_SA_P1Q1QL4_40QMM: []
Missing cids for RGDP_SA_P2Q2QL2AR: []
At present, choice of lookback window is a trade-off between length of history and availability of data. Real-time quantamental indicators of long-term GDP growth are typically available from the late 90s for developed markets. For some EM countries, the series’ only begin in the mid-2000s for the 5-year lookback and for some even after 2010 for the 10-year lookback.
For the explanation of currency symbols, which are related to currency areas or countries for which categories are available, please view Appendix 1 .
xcatx = main
cidx = cids_exp
dfx = msm.reduce_df(dfd, xcats=xcatx, cids=cidx)
dfs = msm.check_startyears(dfx)
msm.visual_paneldates(dfs, size=(20, 3))
print("Last updated:", date.today())
Last updated: 2024-07-02
plot = msm.check_availability(
dfd, xcats=main, cids=cids_exp, start_size=(20, 2), start_years=False
)
Vintage grading is mixed in EM space, whilst most developed markets post grade 1 vintages consistently across indicator categories.
plot = msp.heatmap_grades(
dfd,
xcats=main,
cids=cids_exp,
size=(19, 3),
title=f"Average vintage grades from {start_date} onwards",
)
xcatx = main
cidx = cids_exp
msp.view_ranges(
dfd,
xcats=xcatx,
cids=cidx,
val="eop_lag",
title="End of observation period lags (ranges of time elapsed since end of observation period in days)",
start=start_date,
kind="box",
size=(16, 4),
legend_loc="best",
legend_bbox_to_anchor=(1, 1),
)
msp.view_ranges(
dfd,
xcats=xcatx,
cids=cidx,
val="mop_lag",
title="Median of observation period lags (ranges of time elapsed since middle of observation period in days)",
start=start_date,
kind="box",
size=(16, 4),
legend_loc="best",
legend_bbox_to_anchor=(1, 1),
)
History #
Simple GDP growth rates #
Quarterly GDP growth rates can be very volatile. The 2-quarter-over-2-quarter growth rate is more stable and posts only slightly larger variance than the standard over-a-year ago growth rate.
xcatx = [
"RGDP_SA_P1Q1QL4",
"RGDP_SA_P2Q2QL2AR",
"RGDP_SA_P1Q1QL1AR",
]
cidx = cids_exp
msp.view_ranges(
dfd,
xcats=xcatx,
cids=cidx,
sort_cids_by="mean",
start=start_date,
kind="bar",
size=(16, 8),
title="Means and standard deviations of real GDP growth, seasonally-adjusted",
xcat_labels=["% oya", "%2q/2q, saar", "%q/q, saar"],
)
xcatx = [
"RGDP_SA_P1Q1QL4",
"RGDP_SA_P2Q2QL2AR",
"RGDP_SA_P1Q1QL1AR",
]
cidx = cids_exp
msp.view_timelines(
dfd,
xcats=xcatx,
cids=cidx,
start=start_date,
title="Real GDP growth based on national accounts only",
title_fontsize=27,
legend_fontsize=17,
label_adj=0.075,
xcat_labels=["% oya", "%2q/2q, saar", "%q/q, saar"],
ncol=5,
same_y=False,
size=(12, 7),
aspect=1.7,
all_xticks=True,
)
5-year lookback #
Over the recorded sample periods, long-term growth across countries has ranged from below 1% (Japan) to near 9% (China). The moving medians have been substantially more stable and robust to business cycles.
xcatx = ["RGDP_SA_P1Q1QL4_20QMA", "RGDP_SA_P1Q1QL4_20QMM"]
cidx = cids_exp
msp.view_ranges(
dfd,
xcats=xcatx,
cids=cidx,
sort_cids_by="mean",
start=start_date,
kind="bar",
size=(16, 8),
title="Means and standard deviations of real GDP growth, 5-year lookback, seasonally-adjusted",
xcat_labels=[
"5-year moving average, % over a year ago",
"5-year moving median, % over a year ago",
],
)
xcatx = ["RGDP_SA_P1Q1QL4_20QMA", "RGDP_SA_P1Q1QL4_20QMM"]
cidx = cids_exp
msp.view_timelines(
dfd,
xcats=xcatx,
cids=cidx,
start=start_date,
title="Long-term GDP growth, % over a year ago, 5-year moving average/median",
title_fontsize=27,
legend_fontsize=17,
label_adj=0.075,
xcat_labels=["Moving average", "Moving median"],
ncol=4,
same_y=False,
size=(12, 7),
aspect=1.7,
all_xticks=True,
)
10 year lookback #
Over the sample period, the long-term growth trend has been downward for most countries.
xcatx = ["RGDP_SA_P1Q1QL4_40QMA", "RGDP_SA_P1Q1QL4_40QMM"]
cidx = cids_exp
msp.view_timelines(
dfd,
xcats=xcatx,
cids=cidx,
start=start_date,
title="Long-term GDP growth, % over a year ago, 10-year moving average/median",
title_fontsize=27,
legend_fontsize=17,
label_adj=0.075,
xcat_labels=["Moving average", "Moving median"],
ncol=4,
same_y=False,
size=(12, 7),
aspect=1.7,
all_xticks=True,
)
Importance #
Research links #
“The output gap [is] the production level relative to output at a sustainable operating rate. In the past, even a simple proxy of this gap seems to have provided an information advantage in FX markets. Empirical analysis suggests that [1] following the output gap in simple strategies would have turned a trading profit in the long-term, and [2] the return profile would have been quite different from classical FX trading factors.” Macrosynergy
Empirical clues #
Real-time long-term growth rates are mainly a benchmark or building block for the construction of “excess” growth rates or the current cyclical position of the economy. For example, growth trends versus long-term growth rates are an indication of rising operating rates in an economy and the need for monetary policy adjustment.
For example, there has been clear negative correlation between excess growth and subsequent local currency returns in global panels since 2000. The relation has prevailed both in the developed and emerging markets.
dfb = dfd[dfd["xcat"].isin(["FXTARGETED_NSA", "FXUNTRADABLE_NSA"])].loc[
:, ["cid", "xcat", "real_date", "value"]
]
dfba = (
dfb.groupby(["cid", "real_date"])
.aggregate(value=pd.NamedAgg(column="value", aggfunc="max"))
.reset_index()
)
dfba["xcat"] = "FXBLACK"
fxblack = msp.make_blacklist(dfba, "FXBLACK")
xcatx = ["INTRGDPv10Y_NSA_P1M1ML12_3MMA", "FXXR_NSA"]
cidx = list(
set(cids_dm) - set(["DEM", "ESP", "FRF", "ITL", "NLG", "USD"])
) # remove missing cids
cr = msp.CategoryRelations(
dfd,
xcats=xcatx,
cids=cidx,
freq="Q",
lag=1,
xcat_aggs=["last", "sum"],
start="2000-01-01",
blacklist=fxblack,
)
cr.reg_scatter(
title="Growth versus long-term trend and subsequent FX returns across non-U.S. developed markets since 2000",
labels=False,
coef_box="lower left",
ylab="1-month FX forward return, next quarter",
xlab="Growth trend above long-term trend (10-year lookback)",
reg_robust=True,
)
xcatx = ["INTRGDPv10Y_NSA_P1M1ML12_3MMA", "FXXR_NSA"]
cidx = list(set(cids_em) - set(["HKD"]))
cr = msp.CategoryRelations(
dfd,
xcats=xcatx,
cids=cidx,
freq="Q",
lag=1,
xcat_aggs=["last", "sum"],
start="2000-01-01",
blacklist=fxblack,
)
cr.reg_scatter(
title="Growth versus long-term trend and subsequent FX returns across emerging markets since 2000",
labels=False,
coef_box="lower left",
ylab="1-month FX forward return, next quarter",
xlab="Growth trend above long-term trend (10-year lookback)",
reg_robust=True,
)
xcatx = ["INTRGDPv10Y_NSA_P1M1ML12_3MMA", "FXXR_NSA"]
cidx = list(set(cids_exp) - set(["USD"]))
cr = msp.CategoryRelations(
dfd,
xcats=xcatx,
cids=cidx,
freq="Q",
lag=1,
xcat_aggs=["last", "sum"],
start="2000-01-01",
blacklist=fxblack,
)
cr.reg_scatter(
title="Growth versus long-term trend and subsequent FX returns, non-USD cross-sections, since 2000",
labels=False,
coef_box="lower left",
ylab="1-month FX forward return, next quarter",
xlab="Growth trend above long-term trend (10-year lookback)",
reg_robust=True,
)
Appendices #
Appendix 1: Currency symbols #
The word ‘cross-section’ refers to currencies, currency areas or economic areas. In alphabetical order, these are AUD (Australian dollar), BRL (Brazilian real), CAD (Canadian dollar), CHF (Swiss franc), CLP (Chilean peso), CNY (Chinese yuan renminbi), COP (Colombian peso), CZK (Czech Republic koruna), DEM (German mark), ESP (Spanish peseta), EUR (Euro), FRF (French franc), GBP (British pound), HKD (Hong Kong dollar), HUF (Hungarian forint), IDR (Indonesian rupiah), ITL (Italian lira), JPY (Japanese yen), KRW (Korean won), MXN (Mexican peso), MYR (Malaysian ringgit), NLG (Dutch guilder), NOK (Norwegian krone), NZD (New Zealand dollar), PEN (Peruvian sol), PHP (Phillipine peso), PLN (Polish zloty), RON (Romanian leu), RUB (Russian ruble), SEK (Swedish krona), SGD (Singaporean dollar), THB (Thai baht), TRY (Turkish lira), TWD (Taiwanese dollar), USD (U.S. dollar), ZAR (South African rand).