Financial Stress Index (STLFSI4)
A reproducible, weekly risk-conditions signal derived from FRED: STLFSI4 (St. Louis Fed Financial Stress Index, 4th rev.). v1.1 adds an optional pillar view (volatility / credit / liquidity) plus a confidence flag when component series are stale. 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 Sources (Composite + Pillars)
1.1 Primary composite (baseline)
- STLFSI4 — St. Louis Fed Financial Stress Index (rev. 4), weekly composite published by the Federal Reserve Bank of St. Louis via FRED.
We operate at a weekly Friday cadence ("W-FRI") and forward-fill within week to ensure continuity.
STLFSI4 itself is a composite built from multiple market indicators; we treat it as the authoritative headline stress level.
# 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())
1.2 Optional “pillar” components (for interpretability)
To make drivers explicit, we optionally source representative sub-series and map them into three pillars. These components are typically near real-time, but can suffer outages, late updates, or discontinuations (especially interbank-spread series).
| Pillar | Series (FRED) | What it captures | Notes |
|---|---|---|---|
| Volatility | VIXCLS |
Equity implied volatility (“risk aversion / uncertainty” proxy). | Daily close; resampled to W-FRI. |
| Credit | BAMLH0A0HYM2 (alt: BAMLC0A0CM) |
Corporate credit risk premium (high yield OAS; investment grade OAS as fallback). | Daily close; resampled to W-FRI. |
| Liquidity | TEDRATE |
Interbank/treasury funding stress proxy (TED spread). | Marked “DISCONTINUED” on FRED; when stale, pillar confidence is reduced and/or a replacement spread should be substituted. |
# Optional pillars (fredapi) -> resample weekly (W-FRI)
vix = fred.get_series("VIXCLS") # volatility
hy_oas = fred.get_series("BAMLH0A0HYM2") # credit
ted = fred.get_series("TEDRATE") # liquidity (may be discontinued)
df_pillars = pd.DataFrame({"VIXCLS": vix, "HY_OAS": hy_oas, "TEDRATE": ted})
df_pillars = df_pillars.resample("W-FRI").last()
Design intent: the headline classifier remains STLFSI4; pillars are used to explain why stress is changing, and to generate a staleness-aware confidence flag.
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)
2b) Pillar Contributions & Confidence (v1.1, optional)
When pillar series are enabled, we compute a robust z-score for each pillar and aggregate them into an explainable “pillar stress” score. Pillars are oriented so that higher values imply more stress.
- Volatility pillar: higher
VIXCLS→ higher stress. - Credit pillar: wider OAS (
BAMLH0A0HYM2) → higher stress. - Liquidity pillar: wider TED (
TEDRATE) → higher stress (but series may be discontinued).
We publish both the pillar z-scores and a simple contribution view. A default equal-weight aggregation is used unless explicitly
overridden (e.g., w_vol=0.33, w_credit=0.33, w_liq=0.34).
# Robust z per pillar (same robust_z as above)
df_pillars["VIX_z"] = robust_z(df_pillars["VIXCLS"])
df_pillars["Credit_z"] = robust_z(df_pillars["HY_OAS"])
df_pillars["Liq_z"] = robust_z(df_pillars["TEDRATE"])
weights = {"VIX_z": 1/3, "Credit_z": 1/3, "Liq_z": 1/3}
df_pillars["PillarStress_z"] = sum(df_pillars[k] * w for k, w in weights.items())
# Contributions (simple share of weighted z)
for k, w in weights.items():
df_pillars[f"contrib_{k}"] = (df_pillars[k] * w) / df_pillars["PillarStress_z"].replace(0, np.nan)
Staleness-aware confidence flag
Because component feeds can stall (outages, vendor maintenance, holidays, discontinued series), we attach a confidence flag based on how recently each pillar updated relative to the weekly anchor date.
def staleness_days(series):
return (series.index.max() - series.dropna().index.max()).days if series.dropna().size else 10_000
# Example rule-of-thumb thresholds (tune to your pipeline):
# 0–7d: OK, 8–21d: stale, >21d: critical (or discontinued)
stale = {
"vol": staleness_days(df_pillars["VIXCLS"]) > 7,
"credit": staleness_days(df_pillars["HY_OAS"]) > 7,
"liq": staleness_days(df_pillars["TEDRATE"]) > 21
}
df_pillars["FSI_Confidence"] = (
"HIGH" if not any(stale.values())
else "MED" if sum(stale.values()) == 1
else "LOW"
)
If pillars are disabled, confidence defaults to HIGH so long as STLFSI4 updated on schedule.
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",
# Optional interpretability columns
"VIXCLS","HY_OAS","TEDRATE","VIX_z","Credit_z","Liq_z","PillarStress_z","FSI_Confidence"]
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 (and the selected pillar series, if enabled).
- Staleness handling (v1.1): if any component series is stale/discontinued, we publish
FSI_Confidenceand avoid over-weighting missing pillars in narrative summaries.
6) Interpretation & Use (Model Interpretation)
6.1 What the headline level means
STLFSI4 is standardized around a long-run average. In practice, positive values indicate above-average financial stress/tightness, while negative values indicate below-average stress/looser conditions. Treat the level as a “headline” barometer of broad market stress rather than a single-market signal. (St. Louis Fed STLFSI4 documentation; see References.)
6.2 How to interpret the robust z-score
The robust z-score is a within-history extremeness measure. Use it to distinguish: (i) modest tightness/looseness (near 0) vs (ii) statistically unusual stress/ease (large magnitude). Because the z-score uses rolling median/MAD, it is less sensitive to crisis outliers than mean/std.
- z > +0.5: above-normal stress relative to the recent multi-year distribution (watch for “risk-off” behaviour).
- z < −0.5: below-normal stress / easy conditions (often compatible with “risk-on” positioning).
- Near thresholds: expect whipsaw risk; prefer sustained confirmation (e.g., 2–4 weekly closes) if you use this for regime switching.
6.3 Pillars: interpreting drivers, not just the label (v1.1)
When enabled, pillar contributions provide a decomposition into volatility (risk aversion), credit (risk premium / intermediation capacity), and liquidity (funding stress). This aligns with the academic treatment of financial stress as a multi-dimensional phenomenon captured by co-movement across spreads, volatility, and liquidity measures.
- Volatility-led stress: spikes in implied volatility can be sharp and mean-reverting; use for tactical risk management (VIX literature).
- Credit-led stress: widening spreads often carry higher macro predictive content, especially via “excess bond premium” dynamics.
- Liquidity-led stress: widening funding spreads can signal market-functioning risk; treat as high-severity when confirmed.
6.4 Confidence flag: how to use it
FSI_Confidence is a data-quality indicator, not a market call. If confidence is LOW, interpret pillar narratives cautiously and default to the headline STLFSI4 regime until components resume updating. This addresses real-time outage risk in VIX/credit-spread/funding-spread feeds.
6.5 Practical usage
- Strategic: use sustained “Bearish” readings as a headwind to cyclical risk exposure, not as a timing tool in isolation.
- Tactical: use rapid increases (level + z) and volatility-led surges for short-horizon risk controls.
- Macro integration: interpret stress jointly with growth/inflation conditions; the literature links severe stress episodes to downturn risk, but not every stress episode produces a recession.
References (selected)
- St. Louis Fed / FRED: STLFSI4 series notes and description (method and component categories).
- St. Louis Fed FRED Blog (2022): “The St. Louis Fed’s Financial Stress Index, version 4”.
- Hakkio, C. S., & Keeton, W. R. (2009). “Financial stress: what is it, how can it be measured, and why does it matter?” Federal Reserve Bank of Kansas City, Economic Review.
- Cardarelli, R., Elekdag, S., & Lall, S. (2009/2010). “Financial Stress, Downturns, and Recoveries / Financial Stress and Economic Contractions.” IMF Working Paper / related journal version.
- Whaley, R. E. (2000). “The Investor Fear Gauge.” (VIX interpretation).
- Gilchrist, S., & Zakrajšek, E. (2012). “Credit Spreads and Business Cycle Fluctuations.” American Economic Review.