31 — Carry, Rolldown, Financing, and TIPS Breakevens¶
Learning objectives¶
Compute carry and rolldown separately and combined; see how repo specialness can swing a position's carry by several percentage points; compute breakeven inflation and a TIPS's inflation-adjusted principal. This extends Day 6 (rates portfolio management) with the practitioner layer that sits underneath every curve trade: what do you earn just for holding the position, and how does financing actually work.
Free learning pack¶
reference/fixed_income/carry_and_rolldown.mdreference/fixed_income/repo_and_financing.mdreference/fixed_income/tips_and_breakevens.md- Carry Roll-Down Explained — Risk Hub https://riskhub.org/blogs/carry-roll-down-explained
- On the run (finance) — Wikipedia https://en.wikipedia.org/wiki/On_the_run_(finance)
Do not search for more material until these are insufficient.
PREDICT¶
A 5Y bond yields 4.00% and is financed at 3.00% repo. Is carry positive or negative? What if the repo rate rises to 5.00% instead — same question?
Formula (carry)¶
carry = (running_yield - financing_rate) * horizon_years,
running_yield = coupon_income / price
# MANUAL FIRST:
# 5Y bond yielding 4.00% (priced at par), financed at 3.00% repo, held 1 year.
coupon_income = 4.0
price = 100.0
financing_rate = 0.03
horizon_years = 1.0
carry = None
print(carry)
# CHECK (uncomment after your attempt):
# import numpy as np
# assert np.isclose(carry, 0.01)
PREDICT¶
This repo's own Treasury curve data is locally inverted between the 4Y and 5Y points (4Y yields more than 5Y). If a 5Y bond "rolls down" to become a 4Y bond after one year and the curve doesn't move, will rolldown be positive or negative here — the opposite of the textbook upward-sloping-curve case?
Formula (rolldown)¶
rolldown = -modified_duration * (rolled_yield - current_yield)
from pm.fixed_income.bond import bond_price
from pm.fixed_income.curve import interpolate_zero_rate
from pm.fixed_income.duration import modified_duration
tenors = [1, 2, 3, 4, 5, 10, 30]
yields = [0.0480, 0.0450, 0.0430, 0.0420, 0.0410, 0.0400, 0.0410]
# MANUAL FIRST:
y5 = None # interpolate_zero_rate(5, tenors, yields)
y4 = None # interpolate_zero_rate(4, tenors, yields)
print("5Y yield:", y5, " 4Y yield:", y4)
bond_5y_price = bond_price(y5, face=100, coupon_rate=0.04, years=5, frequency=2)
bond_5y_mod_dur = modified_duration(y5, face=100, coupon_rate=0.04, years=5, frequency=2)
rolldown = None
print("rolldown:", rolldown)
# CHECK (uncomment after your attempt):
# assert y4 > y5, "confirms the curve really is inverted here"
# assert rolldown < 0, "rolling to a HIGHER yield must be a price loss"
Combine carry and rolldown on the real curve¶
This bond is financed at 4.5% repo (above its ~4.0% running yield in this environment) - combine both legs and see the total.
from pm.fixed_income.carry import carry_and_rolldown
# MANUAL FIRST:
total_carry_and_roll = None
print(total_carry_and_roll)
# CHECK (uncomment after your attempt):
# assert total_carry_and_roll < 0, (
# "both legs are negative here: financing (4.5%) exceeds running yield, "
# "and the curve is locally inverted - a real lesson about when "
# "duration-extension carry trades don't work"
# )
PREDICT¶
A 10Y note is financed at the 5.00% general-collateral (GC) repo rate. The same note then goes "special" — demand to borrow it for short- covering pushes its repo rate down to 1.00%. Roughly how large a swing in carry does that represent — a few basis points, or something much larger?
# MANUAL FIRST:
# same 10Y note (coupon 4.00, price 100), first at GC repo (5.00%), then
# at the special rate (1.00%). Use carry_return for each.
gc_carry = None
special_carry = None
pickup = None
print("GC:", gc_carry, " special:", special_carry, " pickup:", pickup)
# CHECK (uncomment after your attempt):
# import numpy as np
# assert np.isclose(pickup, 0.04), "a 4-percentage-point swing from financing alone"
PREDICT (TIPS)¶
A 10Y nominal Treasury yields 4.5%. A 10Y TIPS (real yield) yields 2.0%. Is the market pricing in more or less than 2.5% average inflation over the next 10 years if the breakeven comes out above 2.5%?
Formula (TIPS)¶
breakeven_inflation = nominal_yield - real_yield
index_ratio = CPI_reference_now / CPI_reference_at_issuance
from pm.fixed_income.linkers import (
breakeven_inflation,
tips_index_ratio,
tips_inflation_adjusted_principal,
)
# MANUAL FIRST:
breakeven = None # nominal 4.5%, real 2.0%
print("breakeven:", breakeven)
index_ratio = None # CPI_now=310, CPI_at_issuance=300
adjusted_principal = None # apply to a $100 original principal
print("index ratio:", index_ratio, " adjusted principal:", adjusted_principal)
# CHECK (uncomment after your attempt):
# import numpy as np
# assert np.isclose(breakeven, 0.025)
# assert np.isclose(adjusted_principal, 100 * 310 / 300)
Reference¶
reference/fixed_income/carry_and_rolldown.md
reference/fixed_income/repo_and_financing.md
reference/fixed_income/tips_and_breakevens.md
Promote¶
Use src/pm/fixed_income/carry.py and src/pm/fixed_income/linkers.py
only after your own implementation.
Test¶
pytest tests/test_carry_and_linkers.py
ORAL CHECK¶
Explain to a PM why "carry and roll" isn't a fixed property of owning a bond — walk through how financing rate, curve shape, and repo specialness each independently flip the sign. Then explain what a rising breakeven rate is telling you about market inflation expectations, and why it's a market price, not a forecast.
Try /tutor carry and rolldown for an adaptive walkthrough.