Implementation Scope

This is a narrow rates-duration proxy that borrows the carry intuition. It should not be read as a general carry implementation across asset classes.

The Intuition

Carry is one of the most durable risk premia in finance. In bonds, carry is the yield earned from holding a bond minus financing costs. When interest rates fall (bond prices rise), long bond positions earn both carry and capital appreciation. The Carry Trade strategy here operationalises a simple version: use the momentum of the 10-year Treasury yield as a signal for duration positioning in TLT (the 20+ year Treasury ETF).

The economic channel: when yields are falling (rate momentum negative), bond prices are rising — the primary driver is typically either economic slowdown (risk-off), falling inflation expectations, or central bank easing. All three scenarios are bullish for TLT. When yields are rising, the reverse holds. The strategy bets that rate trends, once established, persist for long enough to be tradeable.

Koijen, Moskowitz, Pedersen, and Vrugt (2018) documented the carry premium across 8 asset classes: bonds, equities, currencies, commodities, credit, options, equity factors, and global fixed income. The carry premium is positive on average in every class, though the risk-adjusted excess is most reliable in currencies and government bonds. The common thread: carry strategies are implicitly short volatility and tail risk — they earn steady returns in calm environments but suffer in sudden regime changes.

Key assumptions: (1) The 10-year yield (^TNX) is a clean signal for rate trends — in practice it can be noisy, and practitioners use yields across the term structure to construct a cleaner trend signal. (2) Rate momentum persists long enough to be captured by the lookback window. (3) TLT provides clean exposure to rate duration — it does, being a 20+ year Treasury ETF, but it also embeds credit risk and liquidity factors. (4) The momentum window (20 days default) is appropriate — some practitioners use longer windows (3–12 months) for rate signals.

The critical risk in carry strategies is the "carry crash" — sudden reversal in the carry direction, often coinciding with global risk-off episodes. In bond markets, carry crashes happen when markets suddenly price in rate hikes (as in 1994's "bond massacre" or 2022's aggressive Fed tightening). Position sizing and stop-losses are essential companions to any carry strategy.

The Math

Read this as a compact model summary: what the signal sees, what it ignores, and where fragility can creep in.

mom_rates(t) = (^TNX(t) - ^TNX(t-n)) / ^TNX(t-n)

Signal(t) = +1  if mom_rates(t) < 0   [rates falling → bond rally]
          = -1  if mom_rates(t) > 0   [rates rising  → bond decline]

Parameters

ParameterTypeDefaultDescription
window int 20 Momentum window for 10Y yield direction

Source Code

def run(ticker: str, start: str, end: str, **params) -> dict:
    window = int(params.get("window", 20))
    df = fetch_ohlcv(ticker, start, end)

    # Use 10Y Treasury yield as rates signal
    try:
        df_tnx = fetch_ohlcv("^TNX", start, end)
        common = df.index.intersection(df_tnx.index)
        df_aligned = df.loc[common]
        tnx_aligned = df_tnx.loc[common, "Close"]
        tnx_mom = tnx_aligned.pct_change(window)
        pos = pd.Series(0.0, index=df_aligned.index)
        pos[tnx_mom < 0] = 1.0   # rates falling → long bonds
        pos[tnx_mom > 0] = -1.0  # rates rising → short bonds
        return run_backtest(df_aligned, pos, ticker=ticker, start=start, end=end,
                            strategy=METADATA["slug"], params={"window": window})
    except Exception:
        # Fallback: just use price momentum of the ticker
        mom = df["Close"].pct_change(window)
        pos = pd.Series(0.0, index=df.index)
        pos[mom < 0] = 1.0
        pos[mom > 0] = -1.0
        return run_backtest(df, pos, ticker=ticker, start=start, end=end,
                            strategy=METADATA["slug"], params={"window": window})

Further Reading

  • Koijen, R., Moskowitz, T., Pedersen, L. & Vrugt, E. (2018). Carry. Journal of Financial Economics, 127(2), 197–225.
  • Ilmanen, A. (2011). Expected Returns. Wiley.
  • Asness, C., Moskowitz, T. & Pedersen, L. (2013). Value and Momentum Everywhere. Journal of Finance, 68(3), 929–985.

When It Works / When It Fails

Works
  • Rate-trending environments with sustained directional cycles
  • Macro regimes with persistent central bank rate moves
  • Portfolios wanting explicit duration or carry exposure
Fails
  • Range-bound interest rates with no directional trend
  • Rate spikes driven by non-macro idiosyncratic factors
  • When equity-rate correlation breaks down (e.g. stagflation)

Regime Fit

Bull / Calm or Normal vol
Best conditions at 1.0. Rate trend is aligned with risk-on; carry signal most reliable.
Bull / Stressed vol
Scaled to 0.75. Signal valid but vol-adjusted sizing applies.
Transition / any vol
0.0–0.35. No directional rate conviction; whipsaw rate is highest during transitions.
Bear side
Mirrors Bull profile on short scale. Falling yield momentum drives short exposure symmetrically.

Compared to Alternatives

vs TSMOM
Both are trend signals; Carry uses rate-change direction specifically as the signal series. TSMOM uses price return sign — Carry is an explicit factor bet on yield direction.
vs MA Crossover
MA Crossover applies SMA level comparison to the price series; Carry uses yield momentum as the signal. Different underlying series, same trend-following intuition.
vs Dual Momentum
Dual Momentum uses price return with a cross-asset relative filter; Carry uses yield-direction only. Different factor exposures with different macro regime dependencies.
Run This Strategy →