Last time we saw how to calculate the current value of a bond based on the current interest rate. We’re ready to take our simulator for a spin.
Starting the simulation
Remember, here are the steps:
- Buy a 10-year Treasury bond for $100.
- One year later sell it.
- Buy a new 10-year Treasury bond.
Now we just need a source of interest rates. The St. Louis Fed (FRED) has tons of cool data available for free. One of their data series is the monthly 10-year Treasury Constant Maturity Rates (GS10), which has data from 1953 on.
Let’s start with January 1954, when the rates were 2.48%. So we buy one of those 2.48% bonds. We hold it for one year. We collect the interest. Then we sell it. How much can we sell it for? Well, now that it is January 1955, the interest rates are 2.61%. Using our Present Value calculation
=pv(2.61%, 9, $2.48, $100)
We can sell it for $98.97. Now we have $98.97 (the proceeds of the bond sale) and $2.48 (the interest for one year), for a total of $101.45. That’s a total annual return of 1.45%.
We can feed a series of interest rates into our simulation engine and it’ll spit out the annual return. How closely do this match the actual return of a real bond fund?
We can compare it to the Bloomberg Barclays US 10 Year Treasury Index from 2004–2013 to see:
It isn’t perfect but it is pretty close for a simulation.
Building a bond ladder
If you look at a real fund, like Vanguard’s Intermediate-Term Treasury Fund (VFIUX) we can see that they don’t track the Bloomberg Barclays US 10-Year Treasury Index, they track something else: the Bloomberg Barclays US 5–10 Year Treasury Index.
And we can see that our simple simulation doesn’t match that quite as well. (Though it still isn’t too bad!)
The reason for the difference is because this index actually has a variety of bonds. Some of them are 10 years old…but some a only 5 years old. Remember that our simple simulation only has a single bond that we replenish every year.
We can get a better fit by also holding multiple bonds. And this is where python comes in. Previously we could do the calculations on a piece of paper or with a simple spreadsheet. But as the number of bonds we track increases, we begin to strain even spreadsheets.
We want to build a bond ladder. We want a 10-year bond, a 9-year bond, an 8-year bond, a 7-year bond, a 6-year bond, and a 5-year bond. Every year we will sell the 5-year bond and buy a new 10-year bond, refilling the ladder.
But we only have 10-year interest rates? What interest rate do we use when doing our Present Value calculation for all of these other maturities?
Well, FRED has a lot of other data we can make use of.
- 1-year interest rates: https://fred.stlouisfed.org/series/GS1
- 2-year interest rates: https://fred.stlouisfed.org/series/GS2
- 3-year interest rates: https://fred.stlouisfed.org/series/GS3
- 5-year interest rates: https://fred.stlouisfed.org/series/GS5
- 7-year interest rates: https://fred.stlouisfed.org/series/GS5
Those fill in many of the gaps but we still have quite a few. There are no 9-year interest rates available, for instance. We can fill the gaps by just doing a linear interpolation.
If the 3-year rate is 4% and the 5-year rate is 5% then we will assume the 4-year rate is 4.5%. This isn’t perfect (google “yield curves”) but let’s stick with it for now. We’re not looking for multiple digit perfect precision, after all. Hopefully our linear interpolation is “good enough”.
That looks better. Not perfect. But better. The big gaps around 2011 and 2014 are gone.
If you had invested $10,000 in the Bloomberg Barclays index at the start of 2002, by the end of 2015 you would have $20,502.26.
If you had invested $10,000 in our “Simulated 10–5 Bond Fund” at the start of 2002, by the end of 2015 you would have $20,518.47. Or a difference of just $16.20 after 13 years.
Our first attempt at a simulation, using just a single bond, resulted in a difference of $2,240.09, so we can see that we made a significant improvement by adding the complexity of multiple bonds with varying maturities.
I think that’s close enough for our purposes :)
I’ve created a jupyter notebook that does all the heavy lifting.