Manufacturers’ New Durable Goods Orders (Ex‑Transportation)

A reproducible, monthly signal derived from FRED series ADXTNO that tracks momentum in core U.S. manufacturing orders by excluding transportation volatility. We use year‑over‑year and 3‑month changes, an optional robust z‑score, and simple regime rules.

FRED ADXTNO YoY & 3M Momentum Robust Z Regime Classification

Abstract

We construct a monthly Durable Goods ex‑Transportation signal from the FRED series ADXTNO. The series is aligned to month‑end, with within‑month forward‑fill to ensure a complete monthly time series. We compute year‑over‑year and 3‑month percentage changes, optionally standardise YoY with a robust rolling z‑score, and classify regimes (Bullish, Neutral, Bearish) using transparent thresholds.

1. Data (FRED Identifier)

We work at a month‑end frequency ("M") and forward‑fill within month to align release dates.

# Fetch & align (fredapi)
durables = fred.get_series("ADXTNO")
df_durables = pd.DataFrame(durables, columns=["DurableGoods_Ex_Trans"])
df_durables = df_durables.resample("M").last().ffill()

2. Signal Construction

We compute momentum using both long‑run (YoY) and near‑term (3M) percentage changes. With \(X_t\) as the level:

\( \text{YoY\_pct}_t = 100\times\left(\dfrac{X_t}{X_{t-12}}-1\right) \)
\( \text{3M\_pct}_t = 100\times\left(\dfrac{X_t}{X_{t-3}}-1\right) \)
df_durables["YoY_pct"] = df_durables["DurableGoods_Ex_Trans"].pct_change(12) * 100
df_durables["3M_pct"]  = df_durables["DurableGoods_Ex_Trans"].pct_change(3)  * 100

Optionally, we compute a robust z‑score on YoY to stabilise scaling against outliers:

\( z_t = \dfrac{y_t - \text{median}_{W}(y)}{1.4826\,\text{MAD}_{W}(y)} \)

Default rolling window \(W=60\) months; observations with \(\text{MAD}=0\) yield undefined z and are left as NaN.

def robust_z(series, window=60):
    med = series.rolling(window).median()
    mad = (series - med).abs().rolling(window).median()
    return (series - med) / (1.4826 * mad.replace(0, np.nan))

df_durables["YoY_z"] = robust_z(df_durables["YoY_pct"], window=60)

3. Regime Classification

We map momentum into categorical regimes using transparent rules. Neutral bands avoid whipsaw:

  • Bullish: YoY > +5% or 3M > +1%
  • Bearish: YoY < −3% or 3M < −1%
  • Neutral: otherwise
def classify_durables(row, yoy_hi=5, yoy_lo=-3, m3_hi=1, m3_lo=-1):
    yoy, m3 = row["YoY_pct"], row["3M_pct"]
    if np.isnan(yoy) or np.isnan(m3):
        return "Neutral"
    if (yoy > yoy_hi) or (m3 > m3_hi):
        return "Bullish"
    if (yoy < yoy_lo) or (m3 < m3_lo):
        return "Bearish"
    return "Neutral"

df_durables["DurableGoods_Signal"] = df_durables.apply(classify_durables, axis=1)

Thresholds can be tuned (e.g., based on backtests or sector sensitivity).

4. Outputs & Columns

# Core columns created
["DurableGoods_Ex_Trans","YoY_pct","3M_pct","YoY_z(optional)","DurableGoods_Signal"]

Console prints provide quick verification (tail of raw and final frames).

5. Data Handling & Validation

6. Interpretation & Use Cases

ADXTNO strips transportation to better capture core order momentum. Bullish regimes suggest firming manufacturing demand, supportive of cyclical risk appetite and upstream commodities; Bearish regimes indicate softening order books and potential growth downgrades. Combine with broader macro pillars (growth, inflation, financial conditions) for multi‑signal frameworks.

7. Governance & Change Control