The brief
A pension fund holds a bond ladder: $2 million of face value in a bond maturing in each of the next five years. As each bond repays, the fund reinvests the cash at the long end.
The quarterly report needs each bond's yield to maturity and one yield for the whole ladder. The trustees ask for the second, and the plain average of the five yields is not it.
Your task
- Write
bond_price(coupon_rate, years, y): the price per $100 of face value of a bond paying100 * coupon_rateonce a year foryearsyears, and $100 at the end, at the yearly yieldy. It must work aty = 0. - Write
ytm(price, coupon_rate, years): the yield at whichbond_priceequalsprice. Yields can be negative. RaiseValueErrorfor a price of zero or below: no yield can produce it. - Write
ladder_yield(ladder): the one yearly yield that discounts all the ladder's cash flows, in dollars, to its market value. RaiseValueErrorfor an empty ladder, or for a bond with a price or face value of zero or below.
The data
LADDER in the code lists the five bonds. Each has a maturity in years, a coupon_rate as a decimal and a price per $100 of face value. Its face is the dollar amount the fund holds.
Every bond pays its coupon once a year, and today is a coupon date.
Hint: Price, then yield
Case study 1.5.5 prices a bond as an annuity of coupons plus the discounted face value. It needs a separate branch for a zero yield. Case study 1.3.5 turns a price into a yield with brentq, from a bracket that starts at .
Hint: One yield for the ladder
Add up the ladder's cash flows year by year, in dollars. Its yield is the internal rate of return of paying the market value today and receiving those flows (case study 1.3.5).
Hint: Why not the average?
The plain average gives each bond the same say. But a change in yield moves a long bond's price further than a short one's (case study 1.5.5). So the long bonds, which yield more here, count for more in the ladder's yield.