Labor Market Turnover Differential

A reproducible, monthly signal that gauges labor‑market tightness by comparing worker‑initiated quits to employer‑initiated layoffs & discharges. Built from FRED JOLTS series using robust z‑scores and clear regime rules.

FRED JOLTS Robust Z Regime Classification

Abstract

We compute the monthly Turnover Differential as the quits rate minus the layoffs & discharges rate. Intuition: a rising gap (quits ≫ layoffs) reflects worker confidence and tight conditions; a shrinking or negative gap signals loosening. We standardise the differential with a robust rolling z‑score and map it, alongside the level, into categorical regimes (Bullish, Neutral, Bearish) for downstream macro & markets use.

1. Data (FRED Identifiers)

Both are monthly. We coerce to a common monthly index at Month‑Start ("MS") and forward‑fill within month to handle publication alignment.

# Fetch & align (fredapi)
q = fred.get_series("JTSQUR").to_frame("QuitsRate")
l = fred.get_series("JTSLDR").to_frame("LayoffRate")
q.index = pd.to_datetime(q.index); l.index = pd.to_datetime(l.index)

turnover_raw_df = (
  pd.concat([q, l], axis=1).sort_index()
    .resample("MS").last().ffill().reset_index()
    .rename(columns={"index": "Date"})
)

2. Signal Construction

The level differential (percentage points) is:

\( \text{Turnover\_Diff}^{pp}_t = \text{QuitsRate}_t - \text{LayoffRate}_t \)

We then compute a robust z‑score using a rolling window with median and MAD, guarding against outliers and short histories.

\( z_t = \dfrac{x_t - \text{median}_{W}(x)}{1.4826\,\text{MAD}_{W}(x)} \)

Default window \(W=60\) months with a minimum of 24. If history is shorter, the window adapts: \(W = \max(24, \lfloor0.8\cdot N\rfloor)\).

def _robust_z(s, window=60, min_window=24):
    s = pd.to_numeric(s, errors="coerce").astype(float)
    n = s.notna().sum()
    w = max(min_window, window if n >= window else int(n * 0.8) if n else min_window)
    med = s.rolling(w, min_periods=min_window).median()
    mad = (s - med).abs().rolling(w, min_periods=min_window).median()
    return (s - med) / (mad.replace(0, np.nan) * 1.4826)

3. Regime Classification

We combine information from the level (pp) and the robust z‑score into three regimes. Neutral “bands” prevent over‑trading on noise:

  • Level neutral band (pp): [-0.20, 0.50]
  • Z neutral band: [-0.50, 0.50]
If level < −0.20 or z < −0.50 → Bearish
Else if level > 0.50 and z > 0.50 → Bullish
Else → Neutral
def _classify(row):
    lvl, z = row["Turnover_Diff_pp"], row["Turnover_Diff_z"]
    if pd.isna(lvl) or pd.isna(z): return "Neutral"
    if (lvl < -0.20) or (z < -0.50): return "Bearish"
    if (lvl >  0.50) and (z >  0.50): return "Bullish"
    return "Neutral"

Thresholds are configurable via level_neutral_band and z_neutral_band.

4. Outputs & Metadata

# Core columns
["Date","QuitsRate","LayoffRate",
 "Turnover_Diff_pp","Turnover_Diff_z","Turnover_Regime"]

# Attached metadata
attrs["series"] = {"quits": "JTSQUR", "layoffs": "JTSLDR"}
attrs["generated_at"] = datetime.utcnow().isoformat() + "Z"
attrs["rules"] = {
  "level_neutral_band_pp": (-0.20, 0.50),
  "z_neutral_band": (-0.50, 0.50),
  "definition": "Bullish if quits ≫ layoffs; Bearish if layoffs ≥ quits and/or negative momentum."
}

For reporting, the last five years can be rendered as an HTML table for dashboards or emails.

5. Data Handling & Validation

6. Interpretation & Use Cases

The Turnover Differential is a concise barometer of labor‑market tightness. Bullish (quits ≫ layoffs) historically co‑moves with strong wage pressure and resilient consumption; Bearish regimes often precede labor softening and increased recession risk. Use alongside other macro pillars (growth, inflation, financial conditions) to condition asset‑allocation tilts.

7. Implementation Notes (Python)

# ➊ Fetch & harmonise monthly series (fredapi)
# ➋ Compute level differential in percentage points
# ➌ Apply robust rolling z-score with adaptive window
# ➍ Classify regimes with level & z neutral bands
# ➎ Persist metadata & (optional) HTML table for last 5y

8. Governance & Change Control