Event-Driven and Arbitrage Strategies › Fixed Income Arbitrage

Michael BrenndoerferJanuary 6, 20269 min read
Quantitative FinanceAll chapters
Part 11 of 15Quantitative Trading Strategies
Chapter 64Event-Driven and Arbitrage Strategies › Fixed Income Arbitrage
Reading section 6 of 11 · Fixed Income Arbitrage · 9 minSection 6 of 11 · 9 min

Book contents

Section 6 of 11 · 9 min read

Event-Driven and Arbitrage Strategies › Fixed Income ArbitrageOne section · Sections in this chapter6 / 11 ↓
Reading tools Definitions & inline links

Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.

Article links

Make inline references clickable

Fixed income arbitrage strategies exploit relative mispricings in bond markets. Unlike merger arbitrage, where discrete events drive convergence to known values on known dates, fixed income arbitrage typically involves continuous price relationships that should hold absent market friction. These strategies identify securities that are fundamentally similar but temporarily trade at different prices due to liquidity differences, supply and demand imbalances, or structural market features.

On-the-Run vs. Off-the-Run Treasury Spreads

The most recently issued Treasury security of a given maturity (the on-the-run bond) trades at a premium to older issues (the off-the-run bonds) due to its superior liquidity. This premium creates an arbitrage opportunity: buy the cheaper off-the-run bond and sell the expensive on-the-run bond.

On-the-Run Premium

The on-the-run premium reflects the convenience yield of holding the most liquid security. Dealers prefer on-the-run bonds for hedging and repo financing, driving their prices above fair value relative to off-the-run alternatives.

The trade profits as the premium decays when a new issue is auctioned, causing the current on-the-run to become off-the-run. At that moment, the formerly on-the-run bond loses its liquidity premium, and its price should converge toward the off-the-run bonds with similar characteristics.

In[24]:
Code
def analyze_onoff_spread(
    on_run_yield,
    off_run_yield,
    duration,
    expected_days_to_new_issue,
    financing_cost_diff=0.001,
):
    """
    Analyze on-the-run / off-the-run Treasury spread trade.

    Parameters:
    - on_run_yield: Yield of on-the-run bond
    - off_run_yield: Yield of off-the-run bond
    - duration: Modified duration of the bonds
    - expected_days_to_new_issue: Days until new auction
    - financing_cost_diff: Additional repo cost for off-the-run
    """
    # Spread in basis points
    spread_bp = (off_run_yield - on_run_yield) * 10000

    # Expected convergence: off-the-run yield stays, on-the-run rises to match
    # P&L = Duration * yield change * notional
    expected_yield_convergence = spread_bp / 2  # assume partial convergence

    # Price impact of yield change (per $100 face)
    price_impact_per_100 = duration * (expected_yield_convergence / 10000) * 100

    # Annualized return
    holding_period = expected_days_to_new_issue / 365
    gross_return = price_impact_per_100 / 100  # as percentage

    # Net of financing differential
    financing_cost = financing_cost_diff * holding_period
    net_return = gross_return - financing_cost
    annualized_return = net_return / holding_period

    return {
        "spread_bp": spread_bp,
        "expected_convergence_bp": expected_yield_convergence,
        "gross_return": gross_return,
        "financing_cost": financing_cost,
        "net_return": net_return,
        "annualized_return": annualized_return,
    }


# Example: 10-year Treasury spread trade
onoff_analysis = analyze_onoff_spread(
    on_run_yield=0.0420,  # 4.20%
    off_run_yield=0.0428,  # 4.28%
    duration=8.5,
    expected_days_to_new_issue=25,
)
Out[25]:
Console
On-the-Run / Off-the-Run Spread Analysis
==================================================
Initial Spread: 8.0 bp
Expected Convergence: 4.0 bp
Gross Return: 0.340%
Financing Cost: 0.007%
Net Return: 0.333%
Annualized Return: 4.86%

The critical challenge for on/off-the-run trading is that the spreads are typically small (in this analysis, 8 basis points), requiring significant leverage to generate economically meaningful returns. Leverage in turn amplifies the portfolio's sensitivity to financing cost changes and creates rollover risk when market stress coincides with need to refinance positions. During crises when on/off-the-run premiums typically widen, financing becomes costly or unavailable, forcing liquidation at the worst possible time.

Yield Curve Trades

Yield curve arbitrage involves taking positions along different maturities based on views about curve shape changes. These trades are not pure arbitrage but rather relative value bets on how the term structure will evolve. The positions are constructed to be neutral to certain types of yield curve movements while expressing a view on others.

The main yield curve trades are:

  • Steepener: Profits when the yield curve becomes steeper (long end rates rise relative to short end). Implemented by being short long-dated bonds and long short-dated bonds.
  • Flattener: The opposite, profiting when the curve flattens.
  • Butterfly: A three-legged relative-value trade on curve curvature. The belly can be long or short depending on the signal; in the price-rich-belly example below, the trade shorts the 5-year and buys the 2-year and 10-year wings.
In[26]:
Code
def construct_butterfly_trade(
    two_yr_yield,
    five_yr_yield,
    ten_yr_yield,
    two_yr_dur,
    five_yr_dur,
    ten_yr_dur,
):
    """
    Construct a duration-neutral butterfly trade.

    Short the rich belly (5Y), long the wings (2Y and 10Y).
    Weights chosen to be duration-neutral.
    """
    # We want: w_2 * dur_2 + w_5 * dur_5 + w_10 * dur_10 = 0
    # With w_5 = -1 (normalize), each long wing offsets half the 5Y DV01

    w_5 = -1.0
    w_2 = 0.5 * five_yr_dur / two_yr_dur
    w_10 = 0.5 * five_yr_dur / ten_yr_dur

    # Current yield pickup/giveup
    yield_carry = w_2 * two_yr_yield + w_5 * five_yr_yield + w_10 * ten_yr_yield

    # Measure of current curvature (2s5s10s butterfly spread)
    butterfly_spread = (2 * five_yr_yield - two_yr_yield - ten_yr_yield) * 10000

    # Net duration
    net_duration = w_2 * two_yr_dur + w_5 * five_yr_dur + w_10 * ten_yr_dur

    return {
        "weights": {"2Y": w_2, "5Y": w_5, "10Y": w_10},
        "net_duration": net_duration,
        "butterfly_spread_bp": butterfly_spread,
        "yield_carry": yield_carry,
    }


# Example yield curve
butterfly = construct_butterfly_trade(
    two_yr_yield=0.045,  # 4.50%
    five_yr_yield=0.043,  # 4.30%
    ten_yr_yield=0.042,  # 4.20%
    two_yr_dur=1.9,
    five_yr_dur=4.5,
    ten_yr_dur=8.2,
)
Out[27]:
Console
Butterfly Trade Construction
=============================================

Position Weights (per $1 short notional in 5Y):
  2-Year: 1.184
  5-Year: -1.000
  10-Year: 0.274

Net Duration: 0.0000 (should be ~0)
Butterfly Spread: -10.0 bp
Net Yield Carry: 2.1814%
Out[28]:
Visualization
Line chart of Treasury yields at 2-year (4.50%), 5-year (4.30%), and 10-year (4.20%) maturities. A dashed interpolation from 2 to 10 years lies 8.8 basis points above the actual 5-year yield, marking a price-rich belly.
The 5-year Treasury yield of 4.30% sits 8.8 basis points below the 4.3875% straight-line interpolation between the 2-year and 10-year yields. Because bond prices move inversely to yields, the lower-yielding belly is price-rich relative to the wings; a mean-reversion butterfly shorts the 5-year and buys duration-matched wings.

The equal-wing butterfly spread, 2y5−y2−y102y_{5} - y_{2} - y_{10}, is -10 basis points. The straight-line interpolation at five years is 4.3875%, placing the actual 4.30% yield 8.75 basis points lower. Since lower yields imply higher bond prices, the belly is rich relative to the wings. A mean-reversion position therefore shorts the 5-year and buys the 2-year and 10-year in duration-neutral amounts; it profits if the 5-year yield rises relative to the wings while remaining insulated from parallel curve shifts.

Basis Trading: Cash vs. Futures

Another classic fixed income arbitrage involves trading the basis between Treasury bonds and Treasury futures. The basis is the difference between the cash bond price and the futures-implied price. This relationship should be governed by the cost of carry, which reflects the financing cost of holding the cash bond offset by the coupon income received:

Basis=Pcash−(Pfutures×CF)\text{Basis} = P_{\text{cash}} - (P_{\text{futures}} \times \text{CF})

where:

  • Basis\text{Basis}: basis between cash and futures prices
  • PcashP_{\text{cash}}: price of the cash bond
  • PfuturesP_{\text{futures}}: price of the Treasury futures contract
  • CF\text{CF}: conversion factor for the specific bond being delivered
  • Pfutures×CFP_{\text{futures}} \times \text{CF}: the adjusted futures price, making the contract comparable to the specific cash bond

The conversion factor adjustment is necessary because Treasury futures allow delivery of multiple eligible bonds, and the conversion factor attempts to equate bonds with different coupons and maturities to a standard benchmark. When the basis is abnormally wide or narrow relative to fair value, arbitrageurs buy the cheap instrument and sell the expensive one.

The basis trade involves understanding the cheapest-to-deliver (CTD) option embedded in Treasury futures, as discussed in our derivatives pricing chapters. The futures seller has the option to deliver any eligible bond, and this option affects the fair value relationship between cash and futures. The CTD option has value because the seller can choose which bond to deliver, selecting the one that minimizes their cost.

In[29]:
Code
def calculate_basis(
    cash_price,
    futures_price,
    conversion_factor,
    accrued_interest,
    repo_rate,
    days_to_delivery,
):
    """
    Calculate the basis between a Treasury bond and futures.

    Basis = Cash Price - (Futures Price * Conversion Factor)

    The basis captures financing costs, accrued interest, and delivery option value.
    """
    # Gross basis
    gross_basis = cash_price - (futures_price * conversion_factor)

    # Net basis adjusts for carry
    T = days_to_delivery / 360  # money market convention
    carry = (cash_price + accrued_interest) * repo_rate * T
    net_basis = gross_basis - carry

    # Implied repo rate (what financing cost would make basis = 0)
    implied_repo = (gross_basis / (cash_price + accrued_interest)) / T

    return {
        "gross_basis": gross_basis,
        "net_basis": net_basis,
        "carry": carry,
        "implied_repo": implied_repo,
        "basis_in_32nds": gross_basis * 32,  # market convention
        "cash_price": cash_price,
        "futures_price": futures_price,
        "conversion_factor": conversion_factor,
    }


# Example basis calculation
basis_result = calculate_basis(
    cash_price=98.50,
    futures_price=116.25,
    conversion_factor=0.8432,
    accrued_interest=1.25,
    repo_rate=0.05,
    days_to_delivery=45,
)
Out[30]:
Console
Treasury Basis Analysis
=============================================
Cash Price: $98.50
Futures Price: $116.25
Conversion Factor: 0.8432

Gross Basis: $0.4780
Gross Basis (32nds): 15.30/32
Carry Cost: $0.6234
Net Basis: $-0.1454
Implied Repo Rate: 3.83%

The positive net basis of approximately 0.5 cents suggests the cash bond is slightly expensive relative to the adjusted futures price. However, the magnitude is economically small (166per166 per 100,000 contract), indicating efficient market pricing of the cash-futures relationship after accounting for carry costs. At typical trading costs and bid-offer spreads in Treasury futures, executing this as an arbitrage would yield losses rather than profits, illustrating why basis trades are typically executed only by dealers with institutional access and low transaction costs.