Federal Reserve Liquidity Composite
Abstract
This document formalises a Fed liquidity composite built from four inputs: money supply (M2SL), Federal Reserve total assets (WALCL), reserve balances (WRESBAL), and overnight reverse repo usage (RRPONTSYD). Each series is aligned to month‑end, transformed into year‑over‑year and 3‑month percentage changes, and classified via symmetric thresholds. A z‑score blend across inputs yields a continuous composite that is discretised into Expansion, Neutral, or Contraction regimes. ON RRP is treated as a tightening gauge (falls are easing).
1. Data Sources
The composite blends monetary quantity (M2SL), Federal Reserve balance‑sheet scale (WALCL), reserve balances (WRESBAL), and a money‑market drain channel via the Overnight Reverse Repo facility (RRPONTSYD). Series are sourced via FRED and ultimately reported by the Federal Reserve System / Federal Reserve Bank of New York.
1.1 M2 Money Stock (M2SL)
- What it is: A broad money aggregate capturing currency, demand deposits, savings deposits, small time deposits, and retail money market funds.
- Frequency: Monthly (as published; used directly).
- How it maps to liquidity: Proxy for money/credit availability to the private sector; persistent acceleration is typically associated with easier monetary conditions, though the relationship can vary structurally.
- Key caveat: Simple‑sum money aggregates embed a “moneyness” assumption across components; Divisia aggregation is an academically motivated alternative for robustness checks.
1.2 Fed Total Assets (WALCL)
- What it is: Total consolidated assets on the Fed’s weekly balance sheet (H.4.1), a direct proxy for QE/QT scale.
- Frequency: Weekly (Wednesday level; aligned to month‑end via the last observation in the month).
- How it maps to liquidity: Balance‑sheet expansion tends to ease system‑wide funding conditions via reserves creation and portfolio‑balance effects; contraction is the opposite.
- Revision/staleness: Weekly balance‑sheet series can be revised; publication can be delayed, causing temporary staleness in the latest prints.
1.3 Reserve Balances (WRESBAL)
- What it is: Reserve balances held by depository institutions at the Federal Reserve (H.4.1 table), reflecting the immediate stock of settlement balances.
- Frequency: Weekly (week average; aligned to month‑end).
- How it maps to liquidity: Higher reserves generally indicate more abundant system liquidity, but transmission is not well‑summarised by a simple “money multiplier” mechanism.
- Key caveat: Reserves are partly policy‑determined and can move for operational reasons (TGA swings, repo facilities); interpretation should be cross‑checked with other channels in the composite.
1.4 ON RRP Usage (RRPONTSYD)
- What it is: Aggregated daily Overnight Reverse Repo (ON RRP) take‑up against Treasury collateral (reported by the New York Fed).
- Frequency: Daily (aligned to month‑end).
- How it maps to liquidity: ON RRP usage represents an overnight absorption of cash from eligible counterparties; declines are interpreted as easing. This model therefore inverts RRP growth rates when forming signals and the composite.
- Key caveat: Facility usage is sensitive to administered rates and money‑market plumbing; interpret directionally and in conjunction with reserves and balance‑sheet changes.
All inputs are trimmed to a rolling window of the most recent N years (N = 3 by default) using the latest available date per series to focus on the current regime and reduce sensitivity to distant structural episodes.
2. Alignment & Transformations
- Month‑end alignment: resample each series to month‑end and select the last observation for that month.
- Rate of change: compute percentage changes for YoY and 3‑month windows:
X_{YoY,t} = 100 × (X_t − X_{t-12}) / X_{t-12}X_{3M,t} = 100 × (X_t − X_{t-3}) / X_{t-3}
- Sign convention for ON RRP: interpret declines as easing; when forming signals and the composite, use −RRP_{YoY} and −RRP_{3M}.
3. Signal Classification
Each input is mapped to categorical Bullish/Neutral/Bearish using symmetric thresholds on level (YoY) and impulse (3M):
- M2: high_yoy=6.0, low_yoy=2.0, accel_3m=1.0, decel_3m=−1.0
- Fed Assets (WALCL): high_yoy=5.0, low_yoy=0.0, accel_3m=0.8, decel_3m=−0.5
- Reserves (WRESBAL): high_yoy=8.0, low_yoy=1.0, accel_3m=1.5, decel_3m=−0.8
- ON RRP (inverted): high_yoy=−5.0, low_yoy=0.0, accel_3m=−1.0, decel_3m=0.5
Rule: if YoY > high_yoy or 3M > accel_3m ⇒ Bullish; if YoY < low_yoy or 3M < decel_3m ⇒ Bearish; else Neutral. Missing inputs default to Neutral.
4. Composite Construction
- Standardise: form z‑scores of YoY and 3M for each input (population standard deviation):
z(s) = (s − μ(s)) / σ(s)
- Per‑input score: sum level and impulse components (e.g., z(M2_{YoY}) + z(M2_{3M})).
- Liquidity Composite: arithmetic mean of the four per‑input scores:
LC_t = mean( z(M2_{YoY})+z(M2_{3M}), z(FED_{YoY})+z(FED_{3M}), z(RES_{YoY})+z(RES_{3M}), z(−RRP_{YoY})+z(−RRP_{3M}) )
- Regime mapping: discretise using fixed cut‑offs:
LC ≤ −0.75 ⇒ Contraction; |LC| < 0.75 ⇒ Neutral; LC ≥ 0.75 ⇒ Expansion
5. Model Interpretation (How to Read the Composite)
5.1 What the composite is (and is not)
- A standardized blend: LC is the mean of per‑input z‑score sums (level + impulse). It is therefore a relative indicator of liquidity conditions within the chosen rolling window.
- Not an equilibrium model: It does not estimate causal effects, nor does it decompose transmission (portfolio‑balance, bank lending, risk‑taking). Use it as a conditioning variable or regime tag, not a structural forecast on its own.
5.2 Reading the sign and magnitude
- Sign: Positive LC indicates easing (higher M2/WALCL/WRESBAL growth and/or falling RRP usage). Negative indicates tightening.
- Magnitude: Because components are standardized, |LC| mainly reflects the breadth and coherence of moves across channels rather than raw basis points or dollars.
- Regime bands: The Expansion/Neutral/Contraction mapping is a coarse discretisation; treat it as a stability tag, not a hard boundary.
5.3 Decomposing “what moved” (diagnostics)
For interpretation, report both the aggregate LC and the per‑input contributions:
5.4 Why PCA / dynamic weighting is a sensible extension
Equal weights are transparent but impose a fixed information structure. In macroeconometrics, principal‑components and dynamic factor methods are standard ways to summarise co‑movement across multiple series and can improve robustness when relationships evolve. A practical extension is to estimate weights (or a single factor) from the standardized panel and use the first component as LC (or as a cross‑check).
5.5 Practical interpretation rules
- Check data freshness first: if balance‑sheet series are stale (release delays), down‑weight changes in the latest month or flag the observation as preliminary.
- Prefer breadth over one‑off swings: regime calls are more reliable when at least 2–3 channels agree (e.g., WALCL + reserves + RRP).
- Use as a conditioner: combine with price‑based confirmation (real yields, credit spreads, USD) before making directional calls.
6. Implementation Notes (Python)
# Window: last N years
years_back = 3
def _keep_last_n_years(df, years):
if df is None or df.empty: return df
df = df.copy(); df['Date'] = pd.to_datetime(df['Date'], errors='coerce')
max_date = df['Date'].max()
if pd.isna(max_date): return df
cutoff = max_date - pd.DateOffset(years=years)
return df.loc[df['Date'] >= cutoff].reset_index(drop=True)
# Align to month-end and compute changes
def align_to_month_end(df, date_col='Date', value_cols=['Value']):
dfa = df.copy(); dfa[date_col] = pd.to_datetime(dfa[date_col])
dfa = dfa.set_index(date_col).sort_index()
dfa = dfa[value_cols].resample('M').last().reset_index().rename(columns={'index':'Date'})
return dfa
import numpy as np
def pct_change(s, periods):
out = s.pct_change(periods=periods) * 100
return out.replace([np.inf, -np.inf], np.nan)
def add_change_columns(df, value_col='Value', prefix='X'):
df = df.copy()
df[f'{prefix}_YoY'] = pct_change(df[value_col], 12)
df[f'{prefix}_3M'] = pct_change(df[value_col], 3)
return df
def classify_signal(yoy, m3, high_yoy, low_yoy, accel_3m, decel_3m):
if pd.isna(yoy) or pd.isna(m3): return 'Neutral'
if (yoy > high_yoy) or (m3 > accel_3m): return 'Bullish'
if (yoy < low_yoy) or (m3 < decel_3m): return 'Bearish'
return 'Neutral'
def zscore(s):
return (s - s.mean()) / s.std(ddof=0)
# After fetching: m2sl_df, fed_assets_df (WALCL), reserves_df (WRESBAL), on_rrp_df (RRPONTSYD)
m2_aligned = align_to_month_end(m2sl_df, 'Date', ['Value'])
fed_assets_al = align_to_month_end(fed_assets_df, 'Date', ['Value'])
reserves_al = align_to_month_end(reserves_df, 'Date', ['Value'])
on_rrp_al = align_to_month_end(on_rrp_df, 'Date', ['Value'])
m2_aligned = add_change_columns(m2_aligned, 'Value', 'M2')
fed_assets_al = add_change_columns(fed_assets_al, 'Value', 'FED')
reserves_al = add_change_columns(reserves_al, 'Value', 'RES')
on_rrp_al = add_change_columns(on_rrp_al, 'Value', 'RRP')
# Merge panel (last years_back years)
base = m2_aligned[['Date','Value','M2_YoY','M2_3M']].rename(columns={'Value':'M2_Value'})
base = base.merge(fed_assets_al.rename(columns={'Value':'FED_Value'}), on='Date', how='outer')
base = base.merge(reserves_al.rename(columns={'Value':'RES_Value'}), on='Date', how='outer')
base = base.merge(on_rrp_al.rename(columns={'Value':'RRP_Value'}), on='Date', how='outer')
base = base.sort_values('Date').reset_index(drop=True)
cutoff = pd.to_datetime(base['Date'].max()) - pd.DateOffset(years=years_back)
base = base.loc[base['Date'] >= cutoff].reset_index(drop=True)
# Classify each input (note inverted RRP)
M2_THR = dict(high_yoy=6.0, low_yoy=2.0, accel_3m=1.0, decel_3m=-1.0)
FED_THR = dict(high_yoy=5.0, low_yoy=0.0, accel_3m=0.8, decel_3m=-0.5)
RES_THR = dict(high_yoy=8.0, low_yoy=1.0, accel_3m=1.5, decel_3m=-0.8)
RRP_THR = dict(high_yoy=-5.0, low_yoy=0.0, accel_3m=-1.0, decel_3m=0.5)
base['M2_Signal'] = base.apply(lambda r: classify_signal(r['M2_YoY'], r['M2_3M'], **M2_THR), axis=1)
base['FED_Signal']= base.apply(lambda r: classify_signal(r['FED_YoY'], r['FED_3M'], **FED_THR), axis=1)
base['RES_Signal']= base.apply(lambda r: classify_signal(r['RES_YoY'], r['RES_3M'], **RES_THR), axis=1)
base['RRP_Signal']= base.apply(lambda r: classify_signal(-r['RRP_YoY'] if pd.notna(r['RRP_YoY']) else np.nan,
-r['RRP_3M'] if pd.notna(r['RRP_3M']) else np.nan,
**RRP_THR), axis=1)
# Composite (z-score blend) and regime
comp_cols = {
'M2': zscore(base['M2_YoY']) + zscore(base['M2_3M']),
'FED': zscore(base['FED_YoY']) + zscore(base['FED_3M']),
'RES': zscore(base['RES_YoY']) + zscore(base['RES_3M']),
'RRP': zscore(-base['RRP_YoY']) + zscore(-base['RRP_3M']),
}
base['Liquidity_Composite'] = pd.DataFrame(comp_cols).mean(axis=1)
base['Liquidity_Regime'] = pd.cut(base['Liquidity_Composite'],
bins=[-np.inf,-0.75,0.75,np.inf],
labels=['Contraction','Neutral','Expansion'])
7. Assumptions & Limitations
- Weekly/daily series reduced to month‑end can suppress intra‑month swings; use weekly views for trading signals if needed.
- YoY changes are revision‑sensitive around structural breaks (e.g., QE/QT episodes); thresholds may need recalibration.
- Equal weighting assumes similar information value across inputs; alternative weights can be back‑tested.
- Balance‑sheet data are weekly with occasional revisions. Series can become stale when release timing is disrupted (e.g., delayed H.4.1 publication).
- Weights and scaling of subcomponents are fixed by design (equal‑weighted z‑score blend). Dynamic weighting or PCA/factor‑based aggregation can improve robustness when relationships shift.
8. Reproducibility
- Persist source IDs (M2SL, WALCL, WRESBAL, RRPONTSYD), download timestamps, and code commit hash.
- Record years_back, month‑end alignment policy, and threshold dictionaries.
- Store the panel (
base) and outputs (Liquidity_Composite,Liquidity_Regime) with dates for audit.
9. Applications
Suitable for macro dashboards, conditioning variables in cross‑asset models, and overlay rules for risk‑on/off tilts. Pairs naturally with price‑based signals (e.g., real‑yield or CPI–PPI divergence) for triangulation.
10. References (Selected)
- Stock, J.H. & Watson, M.W. (2002). Forecasting Using Principal Components From a Large Number of Predictors. Journal of the American Statistical Association.
- Stock, J.H. & Watson, M.W. (Handbook chapter). Dynamic Factor Models, Factor-Augmented Vector Autoregressions, and Structural Vector Autoregressions in Macroeconomics.
- Forni, M., Hallin, M., Lippi, M., & Reichlin, L. (2000/2004). Generalized Dynamic Factor Models (identification and consistency).
- Barnett, W.A. (1984). The New Divisia Monetary Aggregates. Journal of Political Economy; and related “Barnett critique” literature on simple‑sum money.
- Carpenter, S. & Demiralp, S. (2010/2012). Money, Reserves, and the Transmission of Monetary Policy (evidence on reserves and the money multiplier mechanism).
- Federal Reserve Board. H.4.1 Factors Affecting Reserve Balances (release documentation and tables).
- Federal Reserve Bank of New York. ON RRP Operations / FAQs (facility mechanics).