Private‑Credit Impulse

Composite signal tracking growth momentum in private credit creation using bank credit, business loans, and total consumer loans.

Abstract

The Private‑Credit Impulse signal quantifies acceleration or deceleration in aggregate private‑sector credit growth. It blends six‑month annualised growth rates of total bank credit, commercial & industrial loans, and consumer loans. Each growth series is robust‑standardised and averaged to create a momentum composite that identifies Accelerating, Stable, and Decelerating credit regimes.

1. Data

All inputs are monthly levels in USD billions; resample and forward‑fill missing observations if required.

2. Growth‑Rate Transformation

To capture short‑term momentum, compute six‑month annualised growth:

g_t = \left(\frac{X_t}{X_{t-6}}\right)^2 − 1

This approximates the rate that would prevail if six‑month growth continued for a full year, doubling the observed half‑year change.

3. Normalisation

z\_{rob}(X)_t = \dfrac{X_t − \mathrm{median}(X)_{t,w}}{1.4826·\mathrm{MAD}(X)_{t,w}}
with rolling window w = 48 months (minimum 18). Median absolute deviation (MAD) provides robust scaling resistant to extreme episodes.

4. Composite Construction

  1. Compute ann6 growth for each source.
  2. Apply robust_z to the growth series.
  3. Average the z‑scores across components:
Private\_{Credit\_Impulse,t} = \tfrac{1}{3}\big(Z\_{TOTBKCR,t} + Z\_{BUSLOANS,t} + Z\_{TOTALSL,t}\big)

5. Regime Mapping

If\ C_t > 0.75 → \textit{Accelerating};\quad |C_t| ≤ 0.75 → \textit{Stable};\quad C_t < −0.75 → \textit{Decelerating}

6. Implementation (Python)

import pandas as pd
import numpy as np

def robust_z(s, win=48, 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 ann6(series):
    return (series / series.shift(6))**2 - 1.0

d = df_priv_credit.copy()

for col in ["TOTBKCR","BUSLOANS","TOTALSL"]:
    d[f"{col}_ann6"] = ann6(d[col])

zcols = []
for col in ["TOTBKCR_ann6","BUSLOANS_ann6","TOTALSL_ann6"]:
    z = robust_z(d[col])
    d[f"Z_{col}"] = z
    zcols.append(f"Z_{col}")

d["Private_Credit_Impulse"] = d[zcols].mean(axis=1)

hi, lo = 0.75, -0.75

def _regime(v):
    if pd.isna(v): return np.nan
    return "Accelerating" if v > hi else ("Decelerating" if v < lo else "Stable")

d["Credit_Impulse_Regime"] = d["Private_Credit_Impulse"].apply(_regime)

df_sig_priv_credit = d
display(df_sig_priv_credit.tail())

7. Interpretation

8. Limitations