Industrial Production Signal
Abstract
This document formalises a two‑factor signal: (i) industrial demand via INDPRO year‑over‑year growth and (ii) a proxy for primary supply pressure via the mining value‑added share of GDP (RIWG21222S). Series are aligned to a monthly index and transformed to YoY rates. Regimes—Bullish, Neutral, and Bearish—are assigned with transparent thresholds oriented to silver/cyclical risk.
1. Data
- INDPRO — Industrial Production Index (monthly, FRED).
- RIWG21222S — Value Added by Industry: Mining as a Share of GDP (frequency varies on FRED; aligned to month‑start for panel merge).
Each series is fetched from FRED with title metadata. Values are coerced to numeric and reindexed to a common monthly calendar.
2. Alignment & Transformations
- Monthly alignment: set
Dateas index and reindex each series toMS(month‑start), forward‑filling within series. - Merge panel: concatenate aligned series and reset the index to a
Datecolumn. - Rates of change: compute 12‑month percentage changes for both inputs:
INDPRO_{YoY,t} = 100 \times \frac{INDPRO_t - INDPRO_{t-12}}{INDPRO_{t-12}}RIWG_{YoY,t} = 100 \times \frac{RIWG_t - RIWG_{t-12}}{RIWG_{t-12}}
3. Signal Classification
Define thresholds consistent with a demand‑versus‑supply framing for metals:
- Bullish: INDPRO_{YoY} > +3% and RIWG_{YoY} ≤ 0% (robust demand, no rising mining share).
- Bearish: INDPRO_{YoY} < 0% and RIWG_{YoY} > 0% (weak demand alongside rising mining share).
- Neutral: otherwise.
Missing inputs default to Neutral. Thresholds are illustrative and can be calibrated via backtests.
4. Implementation Notes (Python)
def fetch_fred_series_df(series_id):
try:
s = fred.get_series(series_id)
df = s.reset_index(); df.columns = ['Date','Value']
df['Series_ID'] = series_id
try:
meta = fred.get_series_info(series_id)
df['title'] = getattr(meta, 'title', None)
except Exception:
df['title'] = None
df['Date'] = pd.to_datetime(df['Date'])
df['Value'] = pd.to_numeric(df['Value'], errors='coerce')
return df[['Series_ID','title','Date','Value']]
except Exception:
return pd.DataFrame(columns=['Series_ID','title','Date','Value'])
# Fetch
indpro_df = fetch_fred_series_df('INDPRO')
riwg21222s_df = fetch_fred_series_df('RIWG21222S')
# Monthly alignment (MS) & forward-fill
for d in (indpro_df, riwg21222s_df):
d['Date'] = pd.to_datetime(d['Date'])
d.set_index('Date', inplace=True)
d['Value'] = pd.to_numeric(d['Value'], errors='coerce')
IND = indpro_df['Value'].reindex(pd.date_range(indpro_df.index.min(), indpro_df.index.max(), freq='MS')).ffill()
RIW = riwg21222s_df['Value'].reindex(pd.date_range(riwg21222s_df.index.min(), riwg21222s_df.index.max(), freq='MS')).ffill()
merged = pd.concat([IND, RIW], axis=1)
merged.columns = ['Value_INDPRO','Value_RIWG21222S']
merged = merged.reset_index().rename(columns={'index':'Date'})
# YoY transforms
merged['INDPRO_YoY'] = merged['Value_INDPRO'].pct_change(12) * 100
merged['RIWG21222S_YoY'] = merged['Value_RIWG21222S'].pct_change(12) * 100
# Signal rules
def generate_industrial_mining_signal(row):
indpro_yoy = row['INDPRO_YoY']
riwg_yoy = row['RIWG21222S_YoY']
if pd.isna(indpro_yoy) or pd.isna(riwg_yoy):
return 'Neutral'
if (indpro_yoy > 3) and (riwg_yoy <= 0):
return 'Bullish'
if (indpro_yoy < 0) and (riwg_yoy > 0):
return 'Bearish'
return 'Neutral'
merged['Industrial_Mining_Signal'] = merged.apply(generate_industrial_mining_signal, axis=1)
5. Assumptions & Limitations
- Forward‑fill of RIWG21222S assumes within‑period stability; consider quarterly alignment if higher fidelity is required.
- YoY rates can be distorted by base effects around recessions or commodity booms; review alternative windows when applicable.
- Thresholds (3%, 0%) are heuristic; tune by asset class and loss function.
6. Reproducibility
- Persist series IDs (INDPRO, RIWG21222S), retrieval timestamps, and library versions.
- Record resampling policy (
MS+ forward‑fill) and any threshold overrides. - Store the final panel with
INDPRO_YoY,RIWG21222S_YoY, andIndustrial_Mining_Signalfor audit.
7. Applications
Useful as a demand‑supply overlay for metals (e.g., silver) and cyclicals, and as an explanatory variable in macro factor models. Naturally complements liquidity and real‑yield signals for regime triangulation.