Labour Market Momentum
Composite signal tracking labour market strength via jobless claims and payroll momentum.
Abstract
The Labour Market Momentum signal measures shifts in employment strength by combining jobless claims trends with nonfarm payroll growth. Weekly claims are inverted (higher claims = weaker momentum), while payroll growth is directly positive. The composite reveals cycles of Tightening, Neutral, and Softening labour dynamics.
1. Data
- ICSA — Initial Claims, Seasonally Adjusted (FRED:
ICSA), weekly. - CCSA — Continuing Claims, Seasonally Adjusted (FRED:
CCSA), weekly. - PAYEMS — Total Nonfarm Payrolls, Seasonally Adjusted (FRED:
PAYEMS), monthly.
ICSA and CCSA are resampled to monthly frequency to align with PAYEMS.
2. Transformations
- Compute year‑on‑year (YoY) percent change for all three series.
- For PAYEMS, also compute the 3‑month difference to capture short‑term hiring momentum:
PAYEMS\_{3mDiff,t} = PAYEMS_t − PAYEMS_{t−3}
This highlights inflection points in employment growth.
3. Normalisation
z\_{rob}(X)_t = \dfrac{X_t − \mathrm{median}(X)_{t,w}}{1.4826·\mathrm{MAD}(X)_{t,w}}
Rolling window w = 36 months, minimum 18. Median‑based z‑scaling mitigates noise from data revisions.
4. Composite Construction
Jobless claims are inverted (rising claims = negative signal) before combining with payroll momentum:
Labor\_{Momentum,t} = \tfrac{1}{3}\big(z(-ICSA\_{YoY,t}) + z(-CCSA\_{YoY,t}) + z(PAYEMS\_{3mDiff,t})\big)
5. Regime Mapping
If\ C_t > 0.75 → \textit{Tightening};\quad |C_t| ≤ 0.75 → \textit{Neutral};\quad C_t < −0.75 → \textit{Softening}
6. Implementation (Python)
import pandas as pd
import numpy as np
def robust_z(s, win=36, min_win=18):
x = pd.to_numeric(s, errors="coerce").astype(float)
w = max(min_win, min(win, x.dropna().size))
med = x.rolling(w, min_periods=min_win).median()
mad = (x - med).abs().rolling(w, min_periods=min_win).median()
return (x - med) / (1.4826 * mad.replace(0, np.nan))
d = df_labor.copy()
for col in ["ICSA","CCSA","PAYEMS"]:
d[f"{col}_YoY"] = d[col].pct_change(12)
d["PAYEMS_3mDiff"] = d["PAYEMS"].diff(3)
d["Z_ICSA"] = robust_z(-d["ICSA_YoY"])
d["Z_CCSA"] = robust_z(-d["CCSA_YoY"])
d["Z_PAYEMS"] = robust_z(d["PAYEMS_3mDiff"])
d["Labor_Momentum"] = d[["Z_ICSA","Z_CCSA","Z_PAYEMS"]].mean(axis=1)
hi, lo = 0.75, -0.75
def _regime(v):
if pd.isna(v): return np.nan
return "Tightening" if v > hi else ("Softening" if v < lo else "Neutral")
d["Labor_Regime"] = d["Labor_Momentum"].apply(_regime)
df_sig_labor = d
display(df_sig_labor.tail())
7. Interpretation
- Tightening: declining claims and accelerating payrolls — late‑cycle strength.
- Neutral: steady labour market consistent with trend growth.
- Softening: rising claims and flattening payroll growth — potential early sign of slowdown.
8. Limitations
- Weekly claims data are volatile; month‑end averaging helps but may still produce noise.
- PAYEMS subject to periodic revisions; re‑benchmarking can alter short‑term momentum.
- Fixed thresholds (±0.75) may require calibration for different employment cycles.