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.
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)
- STLFSI4: St. Louis Fed Financial Stress Index (rev. 4)
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.
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 > 0orz > +0.5→ tightening / elevated stress - Bullish (risk‑on):
STLFSI4 < 0andz < −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
- Frequency alignment: resample to weekly Friday (
"W-FRI") with.last(), then.ffill(). - NaN policy: robust z uses median/MAD; where MAD = 0, z is NaN; classification treats any NaN as Neutral.
- Reproducibility: all transforms are deterministic given STLFSI4.
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.