Labor market dynamics#

This category group contains real-time measures of changes in employment and unemployment. Whilst labor market dynamics are often seen as lagging indicators of business cycles, they are also indicative of the impact of the economic situation on private households, popular sentiment and labor cost.

Employment growth#

Ticker: EMPL_NSA_P1M1ML12 / _P1M1ML12_3MMA / _P1Q1QL4

Label: Employment growth: %oya / %oya, 3mma / %oya, quarterly

Definition: Main measure of employment growth: % over a year ago / % over a year ago, 3-month moving average / % over a year ago, quarterly

Notes:

  • Employment data is taken only from official statistics, not job agencies or surveys.

  • Most countries release monthly-frequency data with fairly short publication lags. However, the following currency areas only release quarterly data: Switzerland (CHF), Czech Republic (CZK), the Eurozone (EUR), Mexico (MXN), Norway (NOK), New Zealand (NZD), the Phillipines (PHP), Singapore (SGD), Thailand (THB) and South Africa (ZAR). There are no employment growth rates on a monthly frequency for these countries.

  • The UK (GBP) and Peru (PEN) only release employment in 3-month moving averages. However, these are updated at a monthly frequency.

  • Employment growth has not been calculated for countries and periods with semi-annual and annual data, most prominently China and Indonesia.

Unemployment growth#

Ticker: UNEMPLRATE_NSA_3MMA_D1M1ML12 / _D1Q1QL4

Label: Unemployment rate change: oya, 3mma / oya, quarterly

Definition: Change in unemployment rate: over a year ago, 3-month moving average / over a year ago, quarterly

Notes:

  • Unemployment data is taken only from official statistics, not job agencies or surveys.

  • Most countries release monthly-frequency data with fairly short publication lags. However, the following currency areas only release quarterly data: the Phillipines (PHP), Singapore (SGD) and South Africa (ZAR). There are no unemployment growth rates on a monthly frequency for these countries.

  • Brazil (BRL) and Peru (PEN) only release unemployment in 3-month moving averages. However, these are updated at a monthly frequency.

  • Unemployment growth has not been calculated for countries that only provide semi-annual and annual data, most prominently China and Indonesia.

Ticker: UNEMPLRATE_SA_D3M3ML3 / _D6M6ML6 / _D1Q1QL1 / _D2Q2QL2

Label: Unemployment growth, seasonally adjusted: difference 3 months over 3 months ago / difference 6 months over 6 months ago / difference 1 quarter over 1 quarter ago / difference 2 quarters over 2 quarters ago

Definition: Change in seasonally adjusted unemployment rate: 3 months over previous 3 months / 6 months over previous 6 months / quarter over previous quarter / 2 quarters over previous two quarters

Notes:

  • Unemployment data is taken only from official statistics, not job agencies or surveys.

  • Most countries release monthly-frequency data with fairly short publication lags. However, the following currency areas only release quarterly data: the Phillipines (PHP), Thailand (THB) and South Africa (ZAR). There are no unemployment growth rates on a monthly frequency for these countries.

  • Brazil (BRL), the UK (GBP) and Peru (PEN) only release seasonally adjusted unemployment in 3-month moving averages. However, these are updated at a monthly frequency.

  • Unemployment growth has not been calculated for countries and periods with semi-annual and annual data, most prominently China and Indonesia.

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 = [
    "EMPL_NSA_P1M1ML12",
    "EMPL_NSA_P1M1ML12_3MMA",
    "EMPL_NSA_P1Q1QL4",
    "UNEMPLRATE_NSA_3MMA_D1M1ML12",
    "UNEMPLRATE_NSA_D1Q1QL4",
    "UNEMPLRATE_SA_D3M3ML3",
    "UNEMPLRATE_SA_D6M6ML6",
    "UNEMPLRATE_SA_D1Q1QL1",
    "UNEMPLRATE_SA_D2Q2QL2",
]
econ = ["INTRGDP_NSA_P1M1ML12_3MMA", "IMPORTS_SA_P1M1ML12_3MMA"]  # economic context
mark = [
    "FXXR_NSA",
    "EQXR_NSA",
    "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 = "1990-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,
    )
    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-07-14 13:14:42
Connection successful!
Number of expressions requested: 2584
Download time from DQ: 0:02:52.526203

Availability#

cids_exp = sorted(
    list(set(cids) - set(cids_dmec + ["CNY", "IDR", "INR"]))
)  # cids expected in category panels
msm.missing_in_df(dfd, xcats=main, cids=cids_exp)
Missing xcats across df:  set()
Missing cids for EMPL_NSA_P1M1ML12:  {'ZAR', 'THB', 'CHF', 'NOK', 'EUR', 'GBP', 'SGD', 'NZD', 'CZK', 'PEN', 'PHP', 'MXN'}
Missing cids for EMPL_NSA_P1M1ML12_3MMA:  {'ZAR', 'THB', 'CHF', 'NOK', 'EUR', 'SGD', 'NZD', 'CZK', 'PHP', 'MXN'}
Missing cids for EMPL_NSA_P1Q1QL4:  {'PLN', 'BRL', 'TWD', 'KRW', 'MYR', 'ILS', 'GBP', 'USD', 'JPY', 'RUB', 'COP', 'RON', 'PEN', 'TRY', 'CLP', 'HUF', 'AUD', 'HKD', 'SEK', 'CAD'}
Missing cids for UNEMPLRATE_NSA_3MMA_D1M1ML12:  {'SGD', 'PHP', 'ZAR', 'NZD'}
Missing cids for UNEMPLRATE_NSA_D1Q1QL4:  {'PLN', 'BRL', 'THB', 'RON', 'CHF', 'NOK', 'CZK', 'PEN', 'TRY', 'MXN', 'TWD', 'KRW', 'MYR', 'ILS', 'CLP', 'HUF', 'EUR', 'GBP', 'USD', 'AUD', 'JPY', 'HKD', 'SEK', 'CAD', 'RUB', 'COP'}
Missing cids for UNEMPLRATE_SA_D1Q1QL1:  {'PLN', 'BRL', 'RON', 'CHF', 'NOK', 'SGD', 'NZD', 'CZK', 'PEN', 'TRY', 'MXN', 'TWD', 'KRW', 'MYR', 'ILS', 'CLP', 'HUF', 'EUR', 'GBP', 'USD', 'AUD', 'JPY', 'HKD', 'SEK', 'CAD', 'RUB', 'COP'}
Missing cids for UNEMPLRATE_SA_D2Q2QL2:  {'PLN', 'BRL', 'RON', 'CHF', 'NOK', 'SGD', 'NZD', 'CZK', 'PEN', 'TRY', 'MXN', 'TWD', 'KRW', 'MYR', 'ILS', 'CLP', 'HUF', 'EUR', 'GBP', 'USD', 'AUD', 'JPY', 'HKD', 'SEK', 'CAD', 'RUB', 'COP'}
Missing cids for UNEMPLRATE_SA_D3M3ML3:  {'THB', 'PHP', 'ZAR'}
Missing cids for UNEMPLRATE_SA_D6M6ML6:  {'THB', 'PHP', 'ZAR'}

Availability of real-time quantamental indicators of labor market dynamics, either quarterly or monthly, is very diverse, with some series’ going back to the 1970s and others only starting in the 2010s.

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=(18, 6))

print("Last updated:", date.today())
../_images/Labor market dynamics_17_0.png
Last updated: 2023-07-14
plot = msm.check_availability(
    dfd, xcats=xcatx, cids=cidx, start_size=(18, 6), start_years=False, start=start_date
)
../_images/Labor market dynamics_18_0.png

Vintage grading is mixed, with Chile the only country with high grade vintages consistently available across indicators.

plot = msp.heatmap_grades(
    dfd,
    xcats=xcatx,
    cids=cidx,
    start=start_date,
    size=(16, 4),
    title=f"Average vintage grades, from {start_date} onwards",
)
../_images/Labor market dynamics_20_0.png
xcatx = ["EMPL_NSA_P1M1ML12", "EMPL_NSA_P1M1ML12_3MMA", "EMPL_NSA_P1Q1QL4"]

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="2000-01-01",
    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="2000-01-01",
    kind="box",
    size=(16, 4),
)
../_images/Labor market dynamics_21_0.png ../_images/Labor market dynamics_21_1.png
xcatx = [
    "UNEMPLRATE_SA_D3M3ML3",
    "UNEMPLRATE_SA_D6M6ML6",
    "UNEMPLRATE_SA_D1Q1QL1",
    "UNEMPLRATE_SA_D2Q2QL2",
]

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="2000-01-01",
    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="2000-01-01",
    kind="box",
    size=(16, 4),
)
../_images/Labor market dynamics_22_0.png ../_images/Labor market dynamics_22_1.png

History#

Employment growth#

For the purpose of the below presentation, we have renamed the quarterly-frequency annual growth rates to 3-month moving average annual growth rates in order to have a full panel of similar measures across most countries. The two series’ are not identical but are close substitutes.

Employment growth has been positive across all currency areas since 2000 or over the available sample periods. Fluctuations have been larger in American economies than elsewhere.

dfxx = dfd.copy()
dfxx.replace(
    to_replace="EMPL_NSA_P1Q1QL4", value="EMPL_NSA_P1M1ML12_3MMA", inplace=True
)
cidx = list(set(cids_exp) - set(["TWD", "CZK"]))
xcatx = ["EMPL_NSA_P1M1ML12_3MMA"]

msp.view_ranges(
    dfxx,
    xcats=xcatx,
    cids=cidx,
    sort_cids_by="mean",
    start=start_date,
    kind="bar",
    size=(16, 8),
    title="Means and standard deviations of employment growth since 1990",
    xcat_labels=["Employment growth, % over a year ago, 3-month moving average"],
)
../_images/Labor market dynamics_27_0.png
cidx = list(set(cids_exp) - set(["TWD", "CZK"]))
xcatx = ["EMPL_NSA_P1M1ML12", "EMPL_NSA_P1M1ML12_3MMA"]

msp.view_timelines(
    dfxx,
    xcats=xcatx,
    cids=cidx,
    start=start_date,
    title="Employment growth, % over a year ago",
    xcat_labels=["Monthly (if available)", "3-month moving average"],
    ncol=4,
    same_y=False,
    title_adj=1.03,
    title_xadj=0.43,
    label_adj=0.1,
    legend_fontsize=17,
    title_fontsize=27,
    size=(12, 7),
    aspect=1.7,
    all_xticks=True,
)
../_images/Labor market dynamics_28_0.png

Employment growth has been predominantly positively correlated across currency markets.

msp.correl_matrix(
    dfxx,
    xcats="EMPL_NSA_P1M1ML12_3MMA",
    cids=cidx,
    size=(20, 14),
    start=start_date,
    title="Cross-sectional correlation of employment growth, since 1990",
)
../_images/Labor market dynamics_30_0.png

Annual changes in unemployment rates#

For the purpose of the below presentation, we have renamed the quarterly-frequency annual growth rates to 3-month average annual growth rates. The two series’ are not the same but are close subsititutes.

dfxx = dfd.copy()
dfxx.replace(
    to_replace="UNEMPLRATE_NSA_D1Q1QL4",
    value="UNEMPLRATE_NSA_3MMA_D1M1ML12",
    inplace=True,
)

Volatility of unemployment rate changes have been very different across countries, reflecting differences in labor markets and social security incentives for registration, as well as the general registered level of joblessness.

xcatx = ["UNEMPLRATE_NSA_3MMA_D1M1ML12"]
cidx = list(set(cids_exp))

msp.view_ranges(
    dfxx,
    xcats=xcatx,
    cids=cidx,
    sort_cids_by="std",
    start=start_date,
    title="Means and standard deviations of unemployment rate changes since 1990",
    xcat_labels=["Annual change in unemployment rates, 3-month moving average"],
    kind="bar",
    size=(16, 8),
)
../_images/Labor market dynamics_35_0.png
xcatx = ["UNEMPLRATE_NSA_3MMA_D1M1ML12"]
cidx = list(set(cids_exp))

msp.view_timelines(
    dfxx,
    xcats=xcatx,
    cids=cidx,
    start=start_date,
    title="Unemployment rates, change over a year, 3-month moving average (or quarterly)",
    title_adj=1.02,
    title_xadj=0.5,
    legend_fontsize=17,
    title_fontsize=27,
    xcat_labels=["3-month moving average"],
    ncol=4,
    same_y=False,
    size=(12, 7),
    aspect=1.7,
    all_xticks=True,
)
../_images/Labor market dynamics_36_0.png

Changes in employment and unemployment are strongly correlated, but the correlation is not perfect across the panel, reflecting the different methodologies of the two metrics as well as the influence of labor participation rates. Using monthly averages, the magnitude of negative correlation has been roughly 0.66. Scatterplots reveal a number of outliers.

xcatx = ["EMPL_NSA_P1M1ML12_3MMA", "UNEMPLRATE_NSA_3MMA_D1M1ML12"]
cidx = list(
    set(cids_exp)
    - set(["CHF", "CZK", "EUR", "MXN", "NOK", "NZD", "PHP", "SGD", "THB", "ZAR"])
)  # remove missing cids

cr = msp.CategoryRelations(
    dfxx,
    xcats=xcatx,
    cids=cidx,
    freq="M",
    lag=0,
    xcat_aggs=["mean", "mean"],
    start=start_date,
)

cr.reg_scatter(
    title="Employment growth versus unemployment rate changes, since 1990",
    labels=False,
    reg_robust=True,
    coef_box="upper right",
    ylab="Unemployment rate, difference over a year, quarterly or 3-month moving average",
    xlab="Employment growth, percentage over a year, quarterly or 3-month moving average",
)
../_images/Labor market dynamics_38_0.png

Shorter-term changes in unemployment rates#

Seasonally adjusted unemployment changes can be more timely in detecting labor market and broader business cycle trends.

The 6-month-over-6-month changes look like good trend indicators for most countries. The 3-month-over-3-month changes are subject to greater volatility, but often detect large shifts in labor market conditions earlier.

dfxxx = dfd.copy()
dfxxx.replace(
    to_replace="UNEMPLRATE_SA_D1Q1QL1", value="UNEMPLRATE_SA_D3M3ML3", inplace=True
)
dfxxx.replace(
    to_replace="UNEMPLRATE_SA_D2Q2QL2", value="UNEMPLRATE_SA_D6M6ML6", inplace=True
)
xcatx = ["UNEMPLRATE_SA_D3M3ML3", "UNEMPLRATE_SA_D6M6ML6"]
cidx = list(set(cids_exp))
msp.view_timelines(
    dfxxx,
    xcats=xcatx,
    cids=cidx,
    start=start_date,
    title="Changes in seasonally-adjusted unemployment rates since 1990",
    xcat_labels=["3 months over previous 3 months", "6 months over previous 6 months"],
    title_adj=1.02,
    title_xadj=0.425,
    legend_fontsize=17,
    title_fontsize=27,
    label_adj=0.075,
    ncol=4,
    same_y=False,
    size=(12, 7),
    aspect=1.7,
    all_xticks=True,
)
../_images/Labor market dynamics_42_0.png

Information states on unemployment trends have been dominantly positively correlated across currency markets.

msp.correl_matrix(dfxx, xcats="UNEMPLRATE_SA_D6M6ML6", cids=cidx, size=(20, 14))
../_images/Labor market dynamics_44_0.png

Importance#

Empirical clues#

Employment growth has been strongly negatively correlated with subsequent equity market performance in past decades. This plausibly reflects that strong labour market dynamics place upward pressure on real interest rates and wage growth. The relation has prevailed across decades.

xcatx = ["EMPL_NSA_P1M1ML12_3MMA", "EQXR_NSA"]
cidx = list(set(cids_dmca) - set(["CHF", "EUR", "NOK", "NZD"]))
cr = msp.CategoryRelations(
    dfxx,
    xcats=xcatx,
    cids=cidx,
    freq="Q",
    lag=1,
    xcat_aggs=["last", "sum"],
    start=start_date,
)

cr.reg_scatter(
    title="Employment growth trend and subsequent equity performance in major developed countries since 2000",
    labels=False,
    coef_box="lower left",
    ylab="1-month local equity index future return, following quarter",
    xlab="Employment growth, % oya, 3-month moving average, end of quarter",
)

cr.reg_scatter(
    title="Employment growth trend and subsequent equity performance in major developed countries since 2000",
    labels=False,
    coef_box="lower left",
    separator=2010,
    ylab="1-month local equity index future return, following quarter",
    xlab="Employment growth, % oya, 3-month moving average, end of quarter",
    prob_est="map",
)
../_images/Labor market dynamics_51_0.png ../_images/Labor market dynamics_51_1.png

The labor market - equity nexus can also be shown through using unemployment data. There is evidence of a significant positive correlation between unemployment rate changes and future equity returns, particularly in developed markets.

xcatx = ["UNEMPLRATE_SA_D6M6ML6", "EQXR_NSA"]
cidx = list(set(cids_dmca) - set(["NOK", "NZD"]))  # remove missing cids

cr = msp.CategoryRelations(
    dfxx,
    xcats=["UNEMPLRATE_SA_D6M6ML6", "EQXR_NSA"],
    cids=cidx,
    freq="M",
    lag=1,
    xcat_aggs=["last", "sum"],
    start=start_date,
    xcat_trims=[2, 30],
)

cr.reg_scatter(
    title="Changes in unemployment rates and subsequent equity performance in major developed countries since 2000",
    labels=False,
    coef_box="lower left",
    ylab="1-month local equity index future return, following month",
    xlab="Unemployment rate change, 6m/6m, sa, month end",
    separator=2010,
)
../_images/Labor market dynamics_53_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).