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-frueqncy 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 / _P2Q2QL1AR / 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:  2023-10-18 15:03:50
Connection successful!
Number of expressions requested: 2584
Requesting data: 100%|██████████| 130/130 [00:46<00:00,  2.81it/s]
Downloading data: 100%|██████████| 130/130 [00:19<00:00,  6.53it/s]
Download time from DQ: 0:01:51.769495

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)
Missing xcats across df:  []
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())
../_images/GDP growth_18_0.png
Last updated: 2023-10-18
plot = msm.check_availability(
    dfd, xcats=main, cids=cids_exp, start_size=(20, 2), start_years=False
)
../_images/GDP growth_19_0.png

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",
)
../_images/GDP growth_21_0.png
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),
)
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),
)
../_images/GDP growth_22_0.png ../_images/GDP growth_22_1.png

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"],
)
../_images/GDP growth_26_0.png
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_adj=1.02,
    title_fontsize=27,
    legend_fontsize=17,
    title_xadj=0.43,
    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,
)
../_images/GDP growth_27_0.png

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",
    ],
)
../_images/GDP growth_30_0.png
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_adj=1.02,
    title_fontsize=27,
    legend_fontsize=17,
    title_xadj=0.43,
    label_adj=0.075,
    xcat_labels=["Moving average", "Moving median"],
    ncol=4,
    same_y=False,
    size=(12, 7),
    aspect=1.7,
    all_xticks=True,
)
../_images/GDP growth_31_0.png

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_adj=1.02,
    title_fontsize=27,
    legend_fontsize=17,
    title_xadj=0.43,
    label_adj=0.075,
    xcat_labels=["Moving average", "Moving median"],
    ncol=4,
    same_y=False,
    size=(12, 7),
    aspect=1.7,
    all_xticks=True,
)
../_images/GDP growth_34_0.png

Importance#

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,
)
../_images/GDP growth_41_0.png
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,
)
../_images/GDP growth_42_0.png
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,
)
../_images/GDP growth_43_0.png

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).