Financial Stress Index (STLFSI4)

A reproducible, weekly risk-conditions signal derived from FRED: STLFSI4 (St. Louis Fed Financial Stress Index, 4th rev.). We align to Friday frequency, apply a robust rolling z-score to the level, and map combined level/z information into regimes. No plots.

FRED STLFSI4 Weekly (W‑FRI) Robust Z Regime Classification

Abstract

We convert the St. Louis Fed’s composite financial stress index into a weekly risk-on / risk-off classifier. The series is harmonised to a Friday weekly cadence and forward-filled to remove minor publication gaps. We compute a robust z-score on the level to quantify deviations from its long-run distribution, then classify regimes using transparent rules that combine absolute level with relative z-score.

1) Data (FRED Identifier)

We operate at a weekly Friday frequency ("W-FRI") and forward-fill within week to ensure continuity.

# Fetch & align (fredapi)
stlfsi = fred.get_series("STLFSI4")
df_stress = pd.DataFrame(stlfsi, columns=["STLFSI4"])
df_stress = df_stress.resample("W-FRI").last().ffill()
print("✅ STLFSI4 data retrieved."); print(df_stress.tail())

2) Robust Z-Score on Level

To measure relative tightness/looseness versus history, we compute a robust z-score using median and MAD over a rolling window. Given weekly data, we use a 3‑year target window (156 weeks) with a minimum of 52 weeks; if history is shorter, we adapt to 80% of available observations but never below 52.

\( z_t = \dfrac{X_t - \text{median}_W(X)}{1.4826\,\text{MAD}_W(X)} \)

Window choice: \(W = \max\{52, \min(156, \lfloor 0.8\,N \rfloor)\}\), where \(N\) is non-missing sample size.

def robust_z(series, window=156, min_window=52):
    s = series.astype(float)
    w = max(min_window, window if s.notna().sum() >= window else int(max(min_window, s.notna().sum()*0.8)))
    med = s.rolling(w, min_periods=min_window).median()
    mad = (s - med).abs().rolling(w, min_periods=min_window).median()
    return (s - med) / (1.4826 * mad.replace(0, np.nan))

df_stress["Stress_z"] = robust_z(df_stress["STLFSI4"], window=156, min_window=52)

3) Regime Classification

We combine absolute level and relative z-score to form weekly regimes:

  • Bearish (risk‑off): STLFSI4 > 0 or z > +0.5 → tightening / elevated stress
  • Bullish (risk‑on): STLFSI4 < 0 and z < −0.5 → loose conditions
  • Neutral: otherwise (including any NaNs)
def classify_stress(row, level_thresh=0.0, z_lo=-0.5, z_hi=0.5):
    lvl, z = row["STLFSI4"], row["Stress_z"]
    if np.isnan(lvl) or np.isnan(z):
        return "Neutral"
    if (lvl > level_thresh) or (z > z_hi):
        return "Bearish"
    if (lvl < level_thresh) and (z < z_lo):
        return "Bullish"
    return "Neutral"

df_stress["FinancialStress_Signal"] = df_stress.apply(classify_stress, axis=1)

Thresholds can be tuned based on backtests or risk appetite.

4) Outputs & Columns

# Core columns
["STLFSI4","Stress_z","FinancialStress_Signal"]

Console prints echo the tail of the aligned series and the final signal view. No plots are generated.

5) Data Handling & Validation

6) Interpretation & Use

Negative values of STLFSI4 generally indicate looser-than-average conditions; positive values indicate tighter-than-average. Bearish regimes suggest caution in risk assets; Bullish regimes align with easier conditions. Use alongside growth, labor, and inflation signals for a holistic macro regime map.