Credit Conditions
Composite signal of credit stress using high‑yield and BBB option‑adjusted spreads plus equity‑volatility (VIX). Outputs a smoothed index and discrete regime labels: Tightening, Neutral, Easing.
Abstract
We proxy credit conditions by combining spread‑level stress from corporate bond markets with equity‑implied volatility. Each input is robust‑normalised over a rolling window, averaged, and then exponentially smoothed to reduce transient spikes. Thresholds of ±0.75 on the final index map to tightening/easing regimes relevant for risk appetite and funding conditions.
1. Data
- HY_OAS — ICE BofA US High Yield Option‑Adjusted Spread (FRED typical:
BAMLH0A0HYM2), daily/weekly. - BBB_OAS — ICE BofA US BBB Option‑Adjusted Spread (FRED typical:
BAMLC0A4CBBB), daily/weekly. - VIXCLS — CBOE Volatility Index (close), daily.
Resample inputs to a common frequency (month‑end recommended) before normalisation.
2. Normalisation
z\_{rob}(X)_t = \dfrac{X_t − \mathrm{median}(X)_{t,w}}{1.4826 · \mathrm{MAD}(X)_{t,w}}
with rolling window w = 36 months (minimum 18). Robust scaling reduces the impact of crisis outliers and level shifts.
3. Composite Construction
- Inputs: Z\_{HY}, Z\_{BBB}, Z\_{VIX}.
- Raw index: arithmetic mean of the three z‑scores.
- Smoothing: exponential moving average with span = 3 months.
Credit\_{Conditions,t} = \mathrm{EMA}_3\big(\tfrac{Z\_{HY,t} + Z\_{BBB,t} + Z\_{VIX,t}}{3}\big)
4. Regime Classification
C_t > 0.75 \Rightarrow \textit{Tightening};\quad |C_t| \le 0.75 \Rightarrow \textit{Neutral};\quad C_t < -0.75 \Rightarrow \textit{Easing}
5. 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))
def ema(s, span=3):
return s.ewm(span=span, min_periods=1, adjust=False).mean()
c = df_credit_cond.copy()
c["Z_HY"] = robust_z(c["HY_OAS"])
c["Z_BBB"] = robust_z(c["BBB_OAS"])
c["Z_VIX"] = robust_z(c["VIXCLS"])
c["Credit_Conditions_Raw"] = c[["Z_HY","Z_BBB","Z_VIX"]].mean(axis=1)
c["Credit_Conditions"] = ema(c["Credit_Conditions_Raw"], span=3)
hi, lo = 0.75, -0.75
def _regime(v):
if pd.isna(v): return np.nan
return "Tightening" if v > hi else ("Easing" if v < lo else "Neutral")
c["Credit_Regime"] = c["Credit_Conditions"].apply(_regime)
df_sig_credit = c
6. Interpretation
- Tightening: widening credit spreads and/or rising equity volatility — risk‑off conditions, higher financing costs.
- Neutral: benign funding backdrop; spreads and volatility near typical ranges.
- Easing: narrowing spreads and subdued volatility — supportive for risk assets.
7. Assumptions & Limitations
- OAS series may be weekly; ensure proper frequency handling and alignment with daily VIX.
- VIX adds an equity‑risk dimension; if a pure credit view is required, omit or down‑weight.
- Static thresholds (±0.75) are heuristic; consider tuning for specific trading objectives.