External ratios trends
Contents
External ratios trends#
This category group contains recent and medium-term trends of external balance ratios, conventionally relative to concurrent GDP, based on concurrent vintages. These trends often signal shifts in competitiveness, external adjustment and changing risk premia in local markets.
Short-term trade and current account balance trends#
Ticker: MTBGDPRATIO_SA_3MMA_D1M1ML3 / _6MMA_D1M1ML6
Label: Merchandise trade balance ratio change (sa): 3M/3M / 6M/6M
Definition: Merchandise trade (goods exports minus imports) divided by nominal gross domestic product, adjusted for seasonal effects, working days and holidays: 3-month moving average over previous 3 months / 6-month moving average over previous 6 months
Notes:
Imports and exports are separately adjusted for seasonal effects on a per release date vintage, using US Census X-13 algorithm. See Appendix 1 for further details.
Seasonal adjustment factors are sequentially re-estimated as new data are being released. Every re-estimation gives rise to a new data vintage which is the basis of concurrent trend estimation.
For most countries, calendar adjustment is applied, accounting for working day effects or moving holidays (e.g. Chinese New Year, Easter, Diwali, Ramadan).
The national sources series’ have been complemented with additional vintages provided by OECD’s ‘Revision Analysis’ dataset. See also Appendix 2 for details on OECD data integration.
Ticker: MTBGDPRATIO_NSA_12MMA_D1M1ML3
Label: Change in 12-month merchandise trade balance ratio over last three reported months.
Definition: External merchandise trade balance as % of nominal GDP, 1-year moving average, change over last three reported months.
Notes:
The merchandise trade balance only reports external trade in goods.
The ratio uses customs trade data for countries that publish them timely and in good-quality.
A positive trend means that, all other things equal, net demand for domestic currency from trade in merchandise has been increasing.
Ticker: CABGDPRATIO_SA_3MMA_D1M1ML3 / _1QMA_D1Q1QL1 / _6MMA_D1M1ML6 / _2QMA_D1Q1QL2
Label: Current account balance as % of GDP, seasonally adjusted: 3mma vs. previous 3mma / 1-quarter vs previous quarter / 6mma vs. previous 6mma / 2 qma vs previous 2 qma.
Definition: Current account balance as % of GDP, seasonally adjusted: 3month moving average vs. previous 3month moving averages / 1-quarter vs previous quarter / 6month moving average vs. previous 6month moving averages / 2 quarter moving average vs previous 2 quarter moving average.
Notes:
Current account balances are published by the national sources as:
Quarterly, non-seasonally adjusted: AUD, CAD, CHF, CLP, CNY, COP, INR, IDR, MYR, MXN, NOK, NZD, PEN, RUB, SEK, SGD, TWD, ZAR.
Monthly, seasonally adjusted: BRL, CZK, EUR, JPY, KRW, PLN, PHP, THB, TRY.
Quarterly, seasonally adjusted: HUF, ILS, and USD.
Monthly, seasonally adjusted: GBP.
When JPMaQS does seasonal adjustment for vintages, it adjusts the current account balance ratio to GDP by applying the additive method of the US Census X-13 seasonal adjustment algorithm for each release date, at their native observation frequency (monthly or quarterly). See Appendix 1 for further details.
For AUD, CAD, CHF, CLP, COP, INR, IDR, MXN, NOK, NZD, RUB, SEK, and ZAR the national sources series’ have been complemented with additional vintages provided by OECD’s ‘Revision Analysis’ dataset. See also Appendix 2 for details on OECD data integration.
Longer-term trade and current account balance trends#
Ticker: MTBGDPRATIO_SA_3MMAv24MMA / _3MMAv60MMA / _3MMAv120MMA
Label: Merchandise trade balance (sa) as % of GDP: latest 3 months versus 2-year average / latest 3 months versus 5-year average / latest 3 months versus 10-year average
Definition: Merchandise trade balance, seasonally adjusted as % of nominal GDP: latest 3 months versus 2-year average / latest 3 months versus 5-year average / latest 3 months versus 10-year average
Notes:
Imports and exports are separately adjusted for seasonal effects.
Seasonal adjustment factors are sequentially re-estimated as new data are being released. Every re-estimation gives rise to a new data vintage, which is the basis of concurrent trend estimation.
For most countries, calendar adjustment is applied and accounts for working day effects or of moving holidays (Chinese New Year, Easter, Diwali, Ramadan).
The national sources series have been complemented with additional vintages provided by the OECD’s ‘Revision Analysis’ dataset. See also Appendix 2 for details on OECD data integration.
Ticker: MTB_SA_3MMAvEWMAZ
Label: Merchandise trade balance, rolling z-score of 3mma based on exponential moving average.
Definition: Merchandise trade balance, 3-month moving average, z-score of difference to exponential average with 3-year half-life.
Notes:
The z-scores are calculated using exponential moving averages as follows: first, let \(x_{t}\) denote the 3-month moving average of the seasonally adjusted external balance. Then calculate \(z_{t} = (x_{t}-\mu_{t})/\sigma_{t}\), where \(\mu_{t} = \sum_{i=0}^{t} w_{i}x_{i}\) and \(\sigma^{2}_{t} = \sum_{i=0}^{t} w_{i}(x_{i}-\mu_{t})^{2}\). For both, the \(\{w_{i}\}_{i=0}^{t}\) are the exponential weights for a half-life of 3 years.
See notes for the previous indicator for details on the seasonal adjustment of the trade balance.
Ticker: CABGDPRATIO_SA_3MMAv60MMA / _1QMAv20QMA
Label: Current account balance as % of GDP, seasonally adjusted: 3mma vs. previous 5yma / vs. previous 5yma.
Definition: Current account balance as % of GDP, seasonally adjusted: 3-month moving average vs. previous 5-year moving average / vs. previous 5-year moving average
Notes:
Current account balances are published by the national sources as:
Quarterly, non-seasonally adjusted: AUD, CAD, CHF, CLP, CNY, COP, INR, IDR, MYR, MXN, NOK, NZD, PEN, RUB, SEK, SGD, TWD, ZAR.
Monthly, seasonally adjusted: BRL, CZK, EUR, JPY, KRW, PLN, PHP, THB, TRY.
Quarterly, seasonally adjusted: HUF, ILS, and USD.
Monthly, seasonally adjusted: GBP.
When JPMaQS does seasonal adjustment for vintages, it adjusts the current account balance ratio to GDP by applying the additive method of the US Census X-13 seasonal adjustment algorithm for each release date, at their native observation frequency (monthly or quarterly). See Appendix 1 for further details.
For AUD, CAD, CHF, CLP, COP, INR, IDR, MXN, NOK, NZD, RUB, SEK, and ZAR the national sources series have been complemented with additional vintages provided by OECD’s ‘Revision Analysis’ dataset. See also Appendix 2 for details on OECD data integration.
Medium-term basic external balance trend#
Ticker: BXBGDPRATIO_NSA_12MMA_D1M1ML3
Label: Change in basic external balance ratio over last three reported months.
Definition: Basic external balance as % of nominal GDP, 1-year moving average, change over last three reported months.
Notes:
The basic external balance is the sum of external current account balance and net FDI inflows trend.
This indicator is one of the broadest representations of stable external flows.
A positive trend means that net demand for domestic currency from the underlying economic transactions has been increasing.
Longer-term basic external balances trends#
Ticker: BXBGDPRATIO_NSA_12MMAv60MMA / _12MMAv120MMA
Label: Basic external balance as % of GDP: 1-year average versus 5-year average / 1-year average versus 10-year average
Definition: Basic external balance, as % of GDP: 1-year average versus 5-year average / 1-year average versus 10-year average
Notes:
The basic external balance is the sum of external current account balance and net FDI inflows trend.
This indicator is one of the broadest representations of stable external flows.
A positive trend means that net demand for domestic currency from the underlying economic transactions has been increasing.
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 indicatoreop_lag
referring to days elapsed since the end of the observation periodmop_lag
referring to the number of days elapsed since the mean observation periodgrade
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",
"IDR",
"INR",
"KRW",
"MYR",
"PHP",
"SGD",
"THB",
"TWD",
] # EM Asia countries (without "HKD")
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 = [
# Merchandise Trade Balances:
"MTBGDPRATIO_NSA_12MMA_D1M1ML3",
"MTBGDPRATIO_SA_3MMA_D1M1ML3",
"MTBGDPRATIO_SA_6MMA_D1M1ML6",
"MTBGDPRATIO_SA_3MMAv24MMA",
"MTBGDPRATIO_SA_3MMAv60MMA",
"MTBGDPRATIO_SA_3MMAv120MMA",
"MTB_SA_3MMAvEWMAZ",
# Basical External balances:
"BXBGDPRATIO_NSA_12MMA_D1M1ML3",
"BXBGDPRATIO_NSA_12MMAv60MMA",
"BXBGDPRATIO_NSA_12MMAv120MMA",
# Seasonally adjusted current account balances:
"CABGDPRATIO_SA_3MMA_D1M1ML3",
"CABGDPRATIO_SA_6MMA_D1M1ML6",
"CABGDPRATIO_SA_3MMAv60MMA",
"CABGDPRATIO_SA_1QMA_D1Q1QL1",
"CABGDPRATIO_SA_2QMA_D1Q1QL2",
"CABGDPRATIO_SA_1QMAv20QMA",
]
# economic context
econ = [
"INTRGDP_NSA_P1M1ML12_3MMA",
"BXBGDPRATIO_NSA_12MMA",
"EXMOPENNESS_NSA_1YMA",
]
# market links
mark = [
"FXXR_NSA",
"DU05YXR_VT10",
"DU05YCRY_VT10",
"FXTARGETED_NSA",
"FXUNTRADABLE_NSA",
]
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
oauth_id = os.getenv("DQ_CLIENT_ID") # Replace with own client ID
oauth_secret = os.getenv("DQ_CLIENT_SECRET") # Replace with own secret
# Download from DataQuery
with JPMaQSDownload(client_id=oauth_id, client_secret=oauth_secret) as downloader:
dfd = downloader.download(
tickers=tickers,
start_date=start_date,
metrics=["value", "eop_lag", "mop_lag", "grading"],
suppress_warning=True,
show_progress=True,
report_time_taken=True,
)
Maximum number of tickers is 888
Downloading data from JPMaQS.
Timestamp UTC: 2023-07-14 09:05:52
Connection successful!
Number of expressions requested: 3552
Requesting data: 100%|███████████████████████████████████████████████████████████████| 178/178 [00:55<00:00, 3.20it/s]
Downloading data: 100%|██████████████████████████████████████████████████████████████| 178/178 [02:28<00:00, 1.20it/s]
Time taken to download data: 205.02 seconds.
Time taken to convert to dataframe: 51.67 seconds.
Availability#
cids_exp = sorted(list(set(cids) - set(cids_dmec))) # cids expected in category panels
msm.missing_in_df(dfd, xcats=main, cids=cids_exp)
Missing xcats across df: set()
Missing cids for BXBGDPRATIO_NSA_12MMA_D1M1ML3: set()
Missing cids for BXBGDPRATIO_NSA_12MMAv120MMA: set()
Missing cids for BXBGDPRATIO_NSA_12MMAv60MMA: set()
Missing cids for CABGDPRATIO_SA_1QMA_D1Q1QL1: {'EUR', 'PLN', 'CZK', 'KRW', 'THB', 'GBP', 'RON', 'TRY', 'PHP', 'BRL', 'JPY'}
Missing cids for CABGDPRATIO_SA_1QMAv20QMA: {'EUR', 'PLN', 'CZK', 'KRW', 'THB', 'GBP', 'RON', 'TRY', 'PHP', 'BRL', 'JPY'}
Missing cids for CABGDPRATIO_SA_2QMA_D1Q1QL2: {'EUR', 'PLN', 'CZK', 'KRW', 'THB', 'GBP', 'RON', 'TRY', 'PHP', 'BRL', 'JPY'}
Missing cids for CABGDPRATIO_SA_3MMA_D1M1ML3: {'TWD', 'SEK', 'RON', 'PEN', 'RUB', 'CHF', 'MXN', 'AUD', 'CAD', 'NOK', 'SGD', 'HUF', 'ILS', 'CNY', 'COP', 'CLP', 'INR', 'USD', 'MYR', 'NZD', 'ZAR', 'IDR'}
Missing cids for CABGDPRATIO_SA_3MMAv60MMA: {'TWD', 'SEK', 'RON', 'PEN', 'RUB', 'CHF', 'MXN', 'AUD', 'CAD', 'NOK', 'SGD', 'HUF', 'ILS', 'CNY', 'COP', 'CLP', 'INR', 'USD', 'MYR', 'NZD', 'ZAR', 'IDR'}
Missing cids for CABGDPRATIO_SA_6MMA_D1M1ML6: {'TWD', 'SEK', 'RON', 'PEN', 'RUB', 'CHF', 'MXN', 'AUD', 'CAD', 'NOK', 'SGD', 'HUF', 'ILS', 'CNY', 'COP', 'CLP', 'INR', 'USD', 'MYR', 'NZD', 'ZAR', 'IDR'}
Missing cids for MTBGDPRATIO_NSA_12MMA_D1M1ML3: set()
Missing cids for MTBGDPRATIO_SA_3MMA_D1M1ML3: set()
Missing cids for MTBGDPRATIO_SA_3MMAv120MMA: set()
Missing cids for MTBGDPRATIO_SA_3MMAv24MMA: set()
Missing cids for MTBGDPRATIO_SA_3MMAv60MMA: set()
Missing cids for MTBGDPRATIO_SA_6MMA_D1M1ML6: set()
Missing cids for MTB_SA_3MMAvEWMAZ: set()
Real-time quantamental indicators of external ratios are available back to the 1990s for most developed countries and the early 2000s for some emerging economies.
For the explanation of currency symbols, which are related to currency areas or countries for which categories are available, please view Appendix 3.
xcatx = main
cidx = cids_exp
dfx = msm.reduce_df(dfd, xcats=xcatx, cids=cidx)
dfs = msm.check_startyears(dfx)
msm.visual_paneldates(dfs, size=(18, 8))
print("Last updated:", date.today())

Last updated: 2023-07-14
plot = msm.check_availability(
dfd, xcats=xcatx, cids=cidx, start_size=(18, 8), start_years=False, start=start_date
)

Vintage grades are mixed, with developed markets on average recording better grades than EM countries. The higher grades of seasonally-adjusted series reflect the sequential seasonal adjustment that allows deriving a good proxy of real-time data even from non-original vintages.
plot = msp.heatmap_grades(
dfd,
xcats=xcatx,
cids=cidx,
start=start_date,
size=(18, 8),
title=f"Average vintage grades, from {start_date} onwards",
)

mtbs = [
"MTBGDPRATIO_NSA_12MMA_D1M1ML3",
"MTBGDPRATIO_SA_3MMA_D1M1ML3",
"MTB_SA_3MMAvEWMAZ",
]
msp.view_ranges(
dfd,
xcats=mtbs,
cids=cids_exp,
val="eop_lag",
title="End of observation period lags (ranges of time elapsed since end of observation period in days)",
start="2000-01-01",
kind="box",
size=(16, 4),
)
msp.view_ranges(
dfd,
xcats=mtbs,
cids=cids_exp,
val="mop_lag",
title="Median of observation period lags (ranges of time elapsed since middle of observation period in days)",
start="2000-01-01",
kind="box",
size=(16, 4),
)


cabs = [
"CABGDPRATIO_SA_3MMA_D1M1ML3", "CABGDPRATIO_SA_6MMA_D1M1ML6",
"CABGDPRATIO_SA_3MMAv60MMA",
"CABGDPRATIO_SA_1QMA_D1Q1QL1", "CABGDPRATIO_SA_2QMA_D1Q1QL2",
"CABGDPRATIO_SA_1QMAv20QMA",
]
msp.view_ranges(
dfd,
xcats=cabs,
cids=cids_exp,
val="eop_lag",
title="End of observation period lags (ranges of time elapsed since end of observation period in days)",
start="2000-01-01",
kind="box",
size=(16, 4),
)
msp.view_ranges(
dfd,
xcats=cabs,
cids=cids_exp,
val="mop_lag",
title="Median of observation period lags (ranges of time elapsed since middle of observation period in days)",
start="2000-01-01",
kind="box",
size=(16, 4),
)


xbbs = [
"BXBGDPRATIO_NSA_12MMA_D1M1ML3",
"BXBGDPRATIO_NSA_12MMAv60MMA",
"BXBGDPRATIO_NSA_12MMAv120MMA",
]
msp.view_ranges(
dfd,
xcats=xbbs,
cids=cids_exp,
val="eop_lag",
title="End of observation period lags (ranges of time elapsed since end of observation period in days)",
start="2000-01-01",
kind="box",
size=(16, 4),
)
msp.view_ranges(
dfd,
xcats=xbbs,
cids=cids_exp,
val="mop_lag",
title="Median of observation period lags (ranges of time elapsed since middle of observation period in days)",
start="2000-01-01",
kind="box",
size=(16, 4),
)


# Renaming extended categories for ease of subsequent analysis
rename_dict = {
"CABGDPRATIO_SA_1QMA_D1Q1QL1": "CABGDPRATIO_SA_3MMA_D1M1ML3",
"CABGDPRATIO_SA_2QMA_D1Q1QL2": "CABGDPRATIO_SA_6MMA_D1M1ML6",
"CABGDPRATIO_SA_1QMAv20QMA": "CABGDPRATIO_SA_3MMAv60MMA",
}
dfd["xcat"] = dfd["xcat"].map(lambda x: rename_dict.get(x, x))
History#
Medium-term trade balance trends#
The variability of short-term trends have been very different across countries. Small open economies and countries with large commodity exports have tended to experience larger variations in the trade balance-to-GDP ratios. Conversely, the U.S. and the euro area have recorded the smallest standard deviations.
xcatx = ["MTBGDPRATIO_NSA_12MMA_D1M1ML3"]
cidx = cids_exp
msp.view_ranges(
dfd,
xcats=xcatx,
cids=cidx,
sort_cids_by="std",
start=start_date,
kind="box",
size=(16, 8),
title="Boxplots of medium-term trade balance trends since 1995",
xcat_labels=["Change in 1-year moving average over preceding 3 months"],
)

xcatx = ["MTBGDPRATIO_NSA_12MMA_D1M1ML3", "EXMOPENNESS_NSA_1YMA"]
cidx = cids_exp
cr = msp.CategoryRelations(
dfd,
xcats=xcatx,
cids=cidx,
freq="M",
lag=0,
xcat_aggs=["std", "mean"],
start=start_date,
years=5,
)
cr.reg_scatter(
title="Long-term economic openness and standard deviations of medium-term trade balances differences, 1-year moving average, since 1995",
labels=True,
xlab="Standard deviations of change in reported trade balance, % of GDP, 1-year moving average",
# separator=2008,
ylab="Economic openness, % of GDP, 1-year moving average",
reg_robust=True,
)

Medium-term trade balance trends have often come in cycles of 1-2 years. Large fluctuations in trade balance ratios have occurred in the wake of economic or financial turmoil.
xcatx = ["MTBGDPRATIO_NSA_12MMA_D1M1ML3"]
cidx = cids_exp
msp.view_timelines(
dfd,
xcats=xcatx,
cids=cidx,
start=start_date,
title="Merchandise trade balance ratios, % of GDP, change in 1-year moving averages over preceding 3 months",
title_adj=1.02,
title_xadj=0.51,
title_fontsize=27,
legend_fontsize=17,
ncol=4,
same_y=False,
size=(16, 8),
aspect=1.7,
all_xticks=True,
)

Correlations have been very diverse. Trade balance changes have been a differentiating macro factor across currency areas, with correlations depending on export-import structure and financial system features.
msp.correl_matrix(
dfd,
xcats="MTBGDPRATIO_NSA_12MMA_D1M1ML3",
cids=cidx,
size=(20, 14),
start=start_date,
title="Cross-sectional correlation of medium-term trade balance trends since 1995",
)

Short-term trade balance trends#
The 3-month changes in trade-balances with shorter lookback still display pronounced cycles but a lot more short-term volatility.
As in the case of medium-term trends, smaller countries tend to post larger short-term trade balance changes relative to GDP, since the effect of actual changes in international trade and data volatility have a greater scale relative to the economy.
xcatx = ["MTBGDPRATIO_SA_3MMA_D1M1ML3", "MTBGDPRATIO_SA_6MMA_D1M1ML6"]
cidx = cids_exp
msp.view_ranges(
dfd,
xcats=xcatx,
cids=cidx,
sort_cids_by="std",
start=start_date,
title="Ranges of short-term trade balance trends since 1995",
xcat_labels=[
"Change in 3-month moving averages (sa) over preceding 3 months ",
"Change in 6-month moving averages (sa) over preceding 6 months",
],
kind="box",
size=(16, 8),
)

xcatx = ["MTBGDPRATIO_SA_3MMA_D1M1ML3", "MTBGDPRATIO_SA_6MMA_D1M1ML6"]
cidx = cids_exp
msp.view_timelines(
dfd,
xcats=xcatx,
cids=cidx,
start=start_date,
title="Short-term merchandise trade balance ratio changes, seasonally adjusted, % of GDP",
title_adj=1.02,
title_xadj=0.4,
title_fontsize=27,
legend_fontsize=17,
label_adj=0.075,
xcat_labels=[
"3mma (sa) vs preceding 3 months ",
"6mma (sa) vs preceding 6 months",
],
ncol=4,
same_y=False,
size=(16, 8),
aspect=1.7,
all_xticks=True,
)

Short-term current account balance trends#
While trade balance dynamics and current account balance dynamics are clearly correlated, they can have very different patterns at times. Differences can also be large for countries with a sizeable share of non-goods external trade, such as Switzerland. Current account series have often shorter history than trade balance dynamics.
xcatx = ["MTBGDPRATIO_SA_6MMA_D1M1ML6", "CABGDPRATIO_SA_6MMA_D1M1ML6"]
cidx = cids_exp
msp.view_timelines(
dfd,
xcats=xcatx,
cids=cids_exp,
start=start_date,
title="Seasonally adjusted external trade and current account balance changes, % of GDP, 6m/6m, sa",
title_adj=1.02,
title_xadj=0.435,
title_fontsize=27,
legend_fontsize=17,
xcat_labels=[
"Merchandise trade balance",
"Current account balance",
],
label_adj=0.075,
ncol=4,
same_y=False,
size=(12, 7),
aspect=1.7,
all_xticks=True,
)

Longer-term trade and current account trends#
The long horizon of these trends means that there are not many cycles in the dataset since the year 2000. For most currency areas, 1-4 cycles have taken place since the beginning of the century. This means that the effect of these trends are typically best analyzed in panel form, combining the experience of many countries.
xcatx = [
"MTBGDPRATIO_SA_3MMAv24MMA",
"MTBGDPRATIO_SA_3MMAv60MMA",
"MTBGDPRATIO_SA_3MMAv120MMA",
]
msp.view_timelines(
dfd,
xcats=xcatx,
cids=cidx,
start=start_date,
title="Longer term merchandise trade balance ratios, seasonally adjusted, % of GDP",
title_adj=1.02,
title_xadj=0.43,
title_fontsize=27,
legend_fontsize=17,
xcat_labels=[
"Latest 3mma vs past 2yma",
"Latest 3mma vs past 5yma",
"Latest 3mma vs past 10yma",
],
label_adj=0.075,
ncol=4,
same_y=False,
size=(12, 7),
aspect=1.7,
all_xticks=True,
)

The z-score trends reveal strong long-term directional biases across countries. As trading signals they would give rise to seriously long-term positions. This reflects the use of long 3-years half-lives that are applied to the mean calculation.
xcatx = ["MTB_SA_3MMAvEWMAZ"]
cidx = cids_exp
msp.view_ranges(
dfd,
xcats=xcatx,
cids=cidx,
sort_cids_by="mean",
start=start_date,
title="Means and standard deviations of z-scores of longer-term merchandise trade balance trends since 1995",
xcat_labels=["Sequential z-scores based on 3-year half-life exponential weights"],
kind="bar",
size=(16, 8),
)

xcatx = ["MTB_SA_3MMAvEWMAZ"]
cidx = cids_exp
msp.view_timelines(
dfd,
xcats=xcatx,
cids=cidx,
start=start_date,
title="Sequential z-scores based on 3-year half-life exponential weights",
title_adj=1.02,
title_fontsize=27,
legend_fontsize=17,
title_xadj=0.5,
ncol=4,
same_y=False,
size=(12, 7),
aspect=1.7,
all_xticks=True,
)

Longer-term current account dynamics often show pronounced multi-year trends with short-term disruptions and sometimes sudden shifts.
xcatx = ["CABGDPRATIO_SA_3MMAv60MMA"]
cidx = cids_exp
msp.view_timelines(
dfd,
xcats=xcatx,
cids=cidx,
start=start_date,
title="Seasonally adjusted current account balance changes, % of GDP, 3-months (sa) versus previous 5 years",
title_adj=1.02,
title_xadj=0.5,
title_fontsize=27,
legend_fontsize=17,
ncol=4,
same_y=False,
size=(12, 7),
aspect=1.7,
all_xticks=True,
)

Medium-term basic external balance trends#
Basic external balances have posted much larger swings than trade balances, due to the variability of net FDI flows. The volatility of net FDI flows has been only partly tamed by use of trimmed (moving) means, which discard lowest and highest observations.
xcatx = ["BXBGDPRATIO_NSA_12MMA_D1M1ML3"]
cidx = cids_exp
msp.view_ranges(
dfd,
xcats=xcatx,
cids=cidx,
sort_cids_by="std",
start=start_date,
title="Means and standard deviations of medium-term basic external balance trends since 1995",
xcat_labels=["Change in 1-year moving average over preceding 3 months"],
kind="box",
size=(16, 8),
)

xcatx = ["BXBGDPRATIO_NSA_12MMA_D1M1ML3", "MTBGDPRATIO_NSA_12MMA_D1M1ML3"]
cidx = cids_exp
msp.view_timelines(
dfd,
xcats=xcatx,
cids=cids_exp,
start=start_date,
title="Medium term external ratios, % of GDP, change in 1-year moving averages over preceding 3 months",
title_adj=1.02,
title_xadj=0.43,
title_fontsize=27,
legend_fontsize=17,
label_adj=0.075,
xcat_labels=[
"Basic external balance",
"Merchandise trade balance",
],
ncol=4,
same_y=False,
size=(12, 7),
aspect=1.7,
all_xticks=True,
)

In some developed countries, such as Sweden, Norway, Switzerland and the UK, the basic external balance changes have been dominated by services, factor income and net FDI flows. Thus, the basic external balance trends bear little resemblance to the merchandise trade balance trends.
xcatx = ["BXBGDPRATIO_NSA_12MMA_D1M1ML3", "MTBGDPRATIO_NSA_12MMA_D1M1ML3"]
cidx = "CHF"
msp.view_timelines(
dfd,
xcats=xcatx,
cids=cidx,
start=start_date,
title="Swiss medium term external ratios, % of GDP, change in 1-year moving averages over preceding 3 months",
title_adj=1.05,
title_xadj=0.5,
title_fontsize=27,
legend_fontsize=17,
ncol=3,
same_y=False,
size=(14, 7),
)

Longer-term basic external balance trends#
The 12-month ratios versus 5-10 year averages naturally only capture very long cycles. These may be particularly useful indicators of the underlying structural changes in the competitiveness of countries.
xcatx = [
"BXBGDPRATIO_NSA_12MMAv60MMA",
"BXBGDPRATIO_NSA_12MMAv120MMA",
]
cidx = cids_exp
msp.view_timelines(
dfd,
xcats=xcatx,
cids=cidx,
start=start_date,
title="Longer-term basic external balance ratios, % of GDP, 1-year moving averages vs 5y/10y moving averages",
title_adj=1.02,
title_xadj=0.45,
title_fontsize=27,
legend_fontsize=17,
xcat_labels=[
"1yma vs 5yma",
"1yma vs 10yma",
],
ncol=4,
same_y=False,
label_adj=0.075,
size=(12, 7),
aspect=1.7,
all_xticks=True,
)

Importance#
Research links#
“For 181 countries…we find a significant and empirically robust relationship between exchange rate flexibility and the speed of external adjustment. Our results are supported by several ‘natural experiments’ of exogenous changes in bilateral exchange rate regimes.” Ghosh, Qureshi, and Tsangarides, 2014
“[For the calculation of] fundamentally enhanced carry…we calculated a score that reflects half external ratios and recent (1-year) changes in such balances…This simple enhancement exemplifies the beneficial effect of considering quantitative-fundamental information…Naïve PnL generation improves in terms of risk-adjusted return and robustness to global market fluctuation.” Macrosynergy
Empirical clues#
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"
) # exclude periods of FX trageting or illiquidity
There has been a clear negative correlation between increases in merchandise trade balance ratios (shifts towards surplus) and economic growth trends. High-growth countries more often display a shift towards a deficit. Low-growth countries more often display a shift towards a surplus. In conjunction, these two indicators can provide a rough characterization of the macro state of economies. High-growth and rising-deficit countries often experience overheating and subsequent policy tightening, whereas low-growth and rising-surplus countries often lack policy stimulus and subsequently enjoy accommodative monetary policy.
xcatx = ["MTBGDPRATIO_NSA_12MMA_D1M1ML3", "INTRGDP_NSA_P1M1ML12_3MMA"]
cids_fx = sorted(list(set(cids_exp) - set(["EUR", "JPY", "USD"])))
cr = msp.CategoryRelations(
dfd,
xcats=["MTBGDPRATIO_NSA_12MMA_D1M1ML3", "INTRGDP_NSA_P1M1ML12_3MMA"],
cids=cids_fx,
freq="M",
lag=0,
xcat_aggs=["mean", "mean"],
start=start_date,
years=5,
)
cr.reg_scatter(
title="Trade balance ratio trend and GDP growth trend across countries and half-decades",
labels=True,
coef_box="upper right",
ylab="GDP growth trend, % over a year ago (intuitive growth metric)",
xlab="Merchandise trade balance ratio as % of GDP, change over preceding 3 months in 12-month moving average",
)

The negative correlation between merchandise trade balance ratios and GDP growth trends can be exploited
cids_eur = ["CHF", "CZK", "HUF", "NOK", "PLN", "RON", "SEK"] # EUR benchmark cids
cids_eud = ["GBP", "TRY", "RUB"] # mean(EUR,USD) benchmark cids
cids_usd = list(set(cidx) - set(cids_eur) - set(cids_eud)) # USD benchmark cids
xcatx = [
"INTRGDP_NSA_P1M1ML12_3MMA",
]
for xc in xcatx:
calc_eur = [f"{xc}vBM = {xc} - iEUR_{xc}"]
calc_usd = [f"{xc}vBM = {xc} - iUSD_{xc}"]
calc_eud = [f"{xc}vBM = {xc} - 0.5 * ( iEUR_{xc} + iUSD_{xc} )"]
dfa_eur = msp.panel_calculator(dfd, calcs=calc_eur, cids=cids_eur)
dfa_usd = msp.panel_calculator(dfd, calcs=calc_usd, cids=cids_usd)
dfa_eud = msp.panel_calculator(dfd, calcs=calc_eud, cids=cids_eud)
dfa = pd.concat([dfa_eur, dfa_usd, dfa_eud])
dfd = msm.update_df(dfd, dfa)
# Compute Zn scores relative to zero for both gdp growth and trade balance changes
xcatx = [
# Merchandise trade balances
"MTBGDPRATIO_NSA_12MMA_D1M1ML3",
"MTBGDPRATIO_SA_6MMA_D1M1ML6",
"MTBGDPRATIO_SA_3MMAv24MMA",
"MTBGDPRATIO_SA_3MMAv60MMA",
# Growth
"INTRGDP_NSA_P1M1ML12_3MMAvBM",
]
for xcat in xcatx:
dfa = msp.make_zn_scores(
df=dfd,
cids=list(cidx),
xcat=xcat,
start=start_date,
est_freq="m",
neutral="zero",
thresh=4,
)
dfd = msm.update_df(dfd, dfa)
calcs = [
"GDP_MTDv24_ZSC = 1/2 * ( MTBGDPRATIO_SA_3MMAv24MMAZN + INTRGDP_NSA_P1M1ML12_3MMAvBM ) "
]
dfa = msp.panel_calculator(dfd, calcs=calcs, cids=cidx)
dfd = msm.update_df(dfd, dfa)
xcatx = ["GDP_MTDv24_ZSC", "FXXR_NSA"]
cidx = list(set(cids_exp).intersection(cids_dm) - set(["USD"]))
cr = msp.CategoryRelations(
dfd,
xcats=xcatx,
cids=cidx,
freq="Q",
lag=1,
xcat_aggs=["last", "sum"],
# xcat_trims=[20, 40],
blacklist=fxblack,
start=start_date,
years=None,
)
cr.reg_scatter(
title="Composite merchandise trade balance and GDP growth trends, and subsequent quarterly FX forward returns, since 2000",
labels=False,
coef_box="upper right",
xlab="Average of MTB growth and GDP growth vs benchmark zn-scores, last value of quarter",
ylab="Cumulative FX forward returns, next-quarter",
)

Seasonally-adjusted current account changes#
Current account shifts towards lower deficits or higher surpluses increase, all other things equal, prospects for capital inflows and currency appreciation. Both bode well for the idiosyncratic performance of local fixed income markets. The below analysis shows some evidence that quarterly changes in current account ratios, multiplied with openness (ratio of exports and imports to GDP) positively predict subsequent IRS fixed-receiver returns relative to the U.S. market.
cidx = list(set(cids) - set(["USD"]))
calcs = [
"CAB_IMPACT = EXMOPENNESS_NSA_1YMA * CABGDPRATIO_SA_3MMA_D1M1ML3",
"DU05YXR_VT10vUSD = DU05YXR_VT10 - iUSD_DU05YXR_VT10",
]
dfa = msp.panel_calculator(dfd, calcs=calcs, cids=cidx)
dfd = msm.update_df(dfd, dfa)
xcatx = ["CAB_IMPACT", "DU05YXR_VT10vUSD"]
cidx = list(
set(cids)
- set(["BRL", "DEM", "ESP", "FRF", "ITL", "NLG", "PEN", "PHP", "RON", "USD"])
) # missing cids from CAB_IMPACT or DU05YXR_VT10vUSD
cr = msp.CategoryRelations(
dfd,
xcats=xcatx,
cids=cidx,
freq="Q",
lag=1,
xcat_aggs=["last", "sum"],
xcat_trims=[25, 100], # exclude invalid outliers
blacklist=fxblack,
start=start_date,
years=None,
)
cr.reg_scatter(
title="Current account changes and subsequent duration returns (versus USD), EM and DM since 2000",
labels=False,
coef_box="lower right",
xlab="Change in current account ratio (sa) to GDP, 3mma over previous 3mma, adjusted for openness quarter end",
ylab="5-year IRS receiever returns (10%ar vol targeted) versus USD, next quarter",
prob_est="map",
)

Appendices#
Appendix 1: Seasonal adjustment procedure#
We deploy the state-of-the-art US Census X-13 algorithm to handle seasonality in economic series. There are two characteristics of the model worth noting in connection to the point-in-time estimation JPMaQS is delivering:
The underlying econometric model is a seasonal ARIMA model leveraging a two-sided filter. As both the earliest and latest observations are included in the historical seasonal component estimation, JPMaQS forces the model (and seasonal component) to being re-estimated for each release date’s time-series. This ensures that only information available at a given point-in-time is used, avoiding leakage of future information.
The algorithm has an embedded endogenous model specification using an Akaike Information Criteria, ensuring the parameters are dynamically chosen using only information available at each point-in-time.
For our seasonally adjustment of the Merchandise Trade Balances, we sequentially apply the multiplicative method of the U.S. Census X-13 algorithm to the levels of imports and exports respectively for each of their vintages. We then subtract the imports from the exports to get the seasonally adjusted merchandise trade balances, and take ratios to nominal GDP at the end. This contrast with the Current Account Balances, were we first take the ratio to GDP to deal deal with non-stationarity in the data, and then apply the additive X-13 method to these ratios for each release date. As current account balances can be both in surplus and deficit, we cannot use the multiplicative method to deal with the non-stationarity of the data.
Appendix 2: Notes on OECD data integration#
Some indicators in this notebook are constructed using vintages provided by the OECD’s Revision Analysis Dataset in addition to national sources series’. The integration of the OECD datasets follows the following rules:
The following priority order is applied for combining vintages. First, JPMaQS uses seasonally and calendar adjusted original vintages from national sources. Beyond that JPMaQS uses OECD vintages.
OECD vintages inform on the month of release but not the exact date. Actual release dates for these vintages are estimated based on release days of subsequent vintages.
Inconsistencies, data errors and missing values in the OECD vintages have been corrected for JPMaQS.
OECD data is seasonally adjusted and denominated in native currency. No such consistency is found in national sources. Often figures are stated in a foreign denomination (EUR or USD) and no seasonal adjustment has been applied. To integrate these two data sources we first currency convert national sources into their native currency and then apply seasonal adjustment.
Appendix 3: 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 (Philippine 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).