USD Index Signal

A reproducible specification using FRED DTWEXBGS (Broad U.S. Dollar Index) to derive a macro‑directional risk signal, applicable across asset classes. This specification is USD‑centric and the labels reflect U.S. dollar direction.

Abstract

This document formalises a rules‑based signal from the Broad U.S. Dollar Index (DTWEXBGS). The method computes a 12‑month moving average and a 6‑month percentage change on a monthly series, then classifies regimes as Bullish, Neutral, or Bearish. Version 1.1 adds tiered logic and a hysteresis band around the moving average to reduce whipsaws, and recommends publishing continuous diagnostics alongside the discrete label. The labels refer strictly to U.S. dollar direction and are not asset‑specific.

1. Data

DTWEXBGS is a nominal trade‑weighted U.S. dollar index versus a broad basket of trading‑partner currencies. Daily observations are retrieved from FRED, with associated metadata (series ID and title) retained where available. Values are coerced to numeric (non‑numeric entries become missing) and standardised to a monthly calendar by resampling to MS (month‑start) and taking the last available observation in each month as an end‑of‑month proxy. This monthly normalisation ensures consistent alignment for medium‑term indicators and regime classification.

2. Alignment & Transformations

  1. Monthly alignment: resample to MS (month‑start) and take the last value in each month.
  2. Indicators: compute a 12‑month moving average, a 6‑month percentage change, and a continuous distance-to-MA:
    USD_{12M\,MA,t} = MA_{12}(USD_t)
    USD_{6M\,chg,t} = 100 \times \frac{USD_t - USD_{t-6}}{USD_{t-6}}
    USD_{dist,t} = 100 \times \left(\frac{USD_t}{USD_{12M\,MA,t}} - 1\right)

3. Signal Classification (Tiered + Hysteresis)

Interpretation is USD‑centric: a weakening U.S. dollar reflects potential easing in financial conditions, while a strengthening U.S. dollar indicates tightening. Prior versions used a dual trigger (6‑month change thresholds or position versus the 12‑month MA), which can flip the discrete label on trivial moves around the moving average. Version 1.1 uses tiered logic (require momentum and level to agree) and a hysteresis band around the MA to reduce whipsaws.

Parameters

  • Momentum threshold: T = 5% on USD_{6M\,chg}.
  • Hysteresis band: h around the MA (e.g., h = 0.5%), defining: Upper_t = USD_{12M\,MA,t} \times (1+h) and Lower_t = USD_{12M\,MA,t} \times (1-h).

Entry conditions (tiered)

  • Bullish (USD strengthening): USD_{6M\,chg,t} > +T and USD_t > Upper_t.
  • Bearish (USD weakening): USD_{6M\,chg,t} < -T and USD_t < Lower_t.
  • Neutral: otherwise or when inputs are missing.

Stateful hysteresis (persistence)

  • If previously Bullish, retain Bullish unless USD_t crosses below Lower_t (or momentum turns sufficiently negative), preventing rapid flips near the MA.
  • If previously Bearish, retain Bearish unless USD_t crosses above Upper_t (or momentum turns sufficiently positive).

Publishing recommendation: alongside the discrete label, publish USD_{dist} (distance to MA) and USD_{6M\,chg} (momentum) to preserve continuous information for downstream aggregation and diagnostics.

4. Implementation Notes (Python)

def fetch_fred_series_df(series_id):
    try:
        s = fred.get_series(series_id)
        if s is None or s.empty:
            return pd.DataFrame(columns=['Series_ID','title','Date','Value'])
        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 as e:
        print(f"✗ Error fetching {series_id}: {e}")
        return pd.DataFrame(columns=['Series_ID','title','Date','Value'])

# Fetch
usd_df = fetch_fred_series_df('DTWEXBGS')

# Monthly indicators
monthly = usd_df.set_index('Date')['Value'].resample('MS').last().to_frame()
monthly.columns = ['Value_Monthly']
monthly['USD_12Month_MA']    = monthly['Value_Monthly'].rolling(12).mean()
monthly['USD_6Month_Change'] = monthly['Value_Monthly'].pct_change(6) * 100
monthly['USD_Dist_to_MA']    = (monthly['Value_Monthly'] / monthly['USD_12Month_MA'] - 1.0) * 100

# Hysteresis bands (defaults; tune as needed)
T = 5.0          # momentum threshold (%)
h = 0.005        # 0.5% band around MA

monthly['MA_Upper'] = monthly['USD_12Month_MA'] * (1 + h)
monthly['MA_Lower'] = monthly['USD_12Month_MA'] * (1 - h)

def classify_usd_signal_row(row, prev_signal='Neutral'):
    chg  = row['USD_6Month_Change']
    usd  = row['Value_Monthly']
    ma   = row['USD_12Month_MA']
    up   = row['MA_Upper']
    low  = row['MA_Lower']

    if pd.isna(chg) or pd.isna(usd) or pd.isna(ma) or pd.isna(up) or pd.isna(low):
        return 'Neutral'

    # Tiered entry logic (momentum AND level beyond the band)
    bullish_entry = (chg >  T) and (usd > up)
    bearish_entry = (chg < -T) and (usd < low)

    # Stateful hysteresis / persistence
    if prev_signal == 'Bullish':
        if bearish_entry:
            return 'Bearish'
        # retain Bullish unless a decisive break occurs
        if usd < low and chg < 0:
            return 'Neutral'
        return 'Bullish'

    if prev_signal == 'Bearish':
        if bullish_entry:
            return 'Bullish'
        if usd > up and chg > 0:
            return 'Neutral'
        return 'Bearish'

    # Neutral: only enter on tiered conditions
    if bullish_entry:
        return 'Bullish'
    if bearish_entry:
        return 'Bearish'
    return 'Neutral'

# Apply sequentially to preserve hysteresis behaviour
signals = []
prev = 'Neutral'
for _, r in monthly.iterrows():
    prev = classify_usd_signal_row(r, prev_signal=prev)
    signals.append(prev)

monthly['USD_Signal'] = signals

# Recommended outputs (publish alongside label)
# - USD_Dist_to_MA
# - USD_6Month_Change
# - MA_Upper / MA_Lower (for diagnostics)

5. Assumptions & Limitations

6. Reproducibility

7. Applications

The USD Index Signal serves as a broad macro indicator of currency strength, risk sentiment, and financial conditions. It can be applied to FX positioning, cross‑asset risk analysis, equity rotation strategies, and multi‑factor macro regime models alongside liquidity, rates, and inflation metrics.

8. Model Interpretation & Academic Context

This signal is designed to be interpreted as a state and momentum diagnostic for U.S. dollar conditions rather than a point‑forecasting model. Interpretation should emphasise direction, persistence, and regime alignment, not short‑term turning‑point precision.

8.1 Interpreting the Components

  • 6‑Month Percentage Change (Momentum): Captures medium‑term trend persistence. Academic literature shows that FX and macro series exhibit time‑series momentum at horizons of 3–12 months (e.g. Moskowitz, Ooi & Pedersen, 2012). Large positive (negative) values indicate reinforcing trend dynamics rather than isolated shocks.
  • Position Relative to the 12‑Month Moving Average (Level): Acts as a slow‑moving regime anchor. Moving‑average filters are widely used to distinguish expansionary versus contractionary states and to reduce noise in macro indicators (e.g. Fama & French, 1988; Brock, Lakonishok & LeBaron, 1992).
  • Distance to MA (Continuous): The magnitude of deviation from the regime anchor provides information on trend maturity and potential mean‑reversion risk. Larger distances often coincide with elevated positioning or late‑cycle dynamics rather than incremental trend initiation.

8.2 Interpreting the Discrete Signal

The discrete Bullish / Neutral / Bearish labels should be interpreted as probabilistic regime descriptors, not deterministic trade signals. Requiring agreement between momentum and level aligns with academic evidence that combined filters improve robustness relative to single‑rule systems.

  • Bullish (USD): Indicates a strengthening‑dollar regime with reinforcing momentum. Historically associated with tighter global financial conditions, headwinds for USD‑priced commodities, and pressure on non‑USD assets (Bruno & Shin, 2015).
  • Bearish (USD): Indicates a weakening‑dollar regime with reinforcing downside momentum. Often coincides with easing financial conditions, improved global risk appetite, and tailwinds for commodities and non‑USD assets.
  • Neutral: Reflects transition, conflict, or insufficient confirmation. Neutral states are informative and frequently precede regime shifts or volatility expansions.

8.3 Role of Hysteresis

The hysteresis band around the moving average introduces state persistence, a concept aligned with regime‑switching and filtering literature. By preventing rapid oscillation near thresholds, hysteresis reduces false regime changes and improves interpretability at the cost of modest signal delay. This trade‑off is well documented in macro filtering and trend‑following research.

8.4 How to Use This Signal in Practice

  • Use the discrete signal for regime classification and high‑level allocation context.
  • Use USD_{6M chg} and USD_{dist} for assessing conviction, maturity, and asymmetry.
  • Combine with complementary macro signals (rates, liquidity, credit) rather than in isolation.
  • Avoid interpreting single‑period changes as regime shifts; focus on persistence.

8.5 Selected Academic References

  • Moskowitz, T., Ooi, Y., & Pedersen, L. (2012). Time Series Momentum. Journal of Financial Economics.
  • Brock, W., Lakonishok, J., & LeBaron, B. (1992). Simple Technical Trading Rules and the Stochastic Properties of Stock Returns. Journal of Finance.
  • Fama, E., & French, K. (1988). Permanent and Temporary Components of Stock Prices. Journal of Political Economy.
  • Bruno, V., & Shin, H. S. (2015). Cross‑Border Banking and Global Liquidity. Review of Economic Studies.
  • Hamilton, J. (1989). A New Approach to the Economic Analysis of Nonstationary Time Series and the Business Cycle. Econometrica.