-
Notifications
You must be signed in to change notification settings - Fork 1
Tutorial
Some of you are familiar with my [url=http://www.bogleheads.org/forum/viewtopic.php?p=576831#576831]long quest[/url] to understand whether a bond fund is somehow riskier than an individual bond. In the process, I have read (and extensively edited) the [url=http://www.bogleheads.org/wiki/Individual_Bonds_vs_a_Bond_Fund]wiki[/url], read two books by Fabozzi on the mathematics of bonds, and written some software to help play around with them. At the last Philly Bogleheads meeting, we played around with the software, and people seemed to find it interesting. Consequently I am releasing it under an open-source license and will demo it here in the hopes that folks will find it helpful to understanding bond behavior.
A few notes first: -This software is highly incomplete. In particular, the ability to roll over bond portfolios into the future exists but you will have to write your own rollover function to do so. The next version will likely have bug fixes, and the version after that will likely incorporate mixed stock/bond accounts, if I ever get around to it. -This software has not been extensively tested. Therefore, don't use it for anything remotely resembling a market transaction! -For any sizeable simulation, it is likely to be slow. The reason is that I have avoided using shortcuts. If you ask the software for the duration of a portfolio of bonds, it will calculate the duration of every cashflow from every bond and thence the duration of the portfolio. If you ask it for the price change, it will calculate the exact price change, rather than using the shortcut based on the Taylor approximation (e.g. duration * interest rate change). If you ask it to give you the value of the account in the future, the account that it returns will contain all of the constituent bond objects, so that you can examine exactly what happened. All of this record keeping is not conducive to speed. Still, for most purposes on modern hardware it should be plenty fast.
With those caveats, let's get down to brass tacks.
First, you'll need a copy of R. R is an open-source statistical programming package that has become pretty standard in academia and industry. It's a little more programming-like than most statistical packages, in ways both good (it has real functions and real objects) and bad (it can be slightly intimidating to learn if you're not a programmer). For this use, you won't need to do much programming unless you really want to ask interesting questions :-).
The easiest way to get R working is to use [url=http://rstudio.org/]RStudio[/url], which wraps a copy of R inside a pretty candy shell: http://rstudio.org/download/ Go there, download, and install. It's free.
Alternatively you can just use R directly, although it's not as integrated an interface: http://cran.stat.ucla.edu/
Once you've loaded up R (henceforth I'll refer to it as R, whether or not you're running it through RStudio or not), you'll need to install the maRketSim package. So type: [code]install.packages("maRketSim")[/code]
You'll only have to do that once for a given R installation. Each time you load R, however, you'll need to load the maRketSim library into memory: [code]library(maRketSim)[/code]
First let's create some bond markets. Markets are fundamental to maRketSim concept. A market exists at a single time point (usually a year, although there's no reason you couldn't use minutes/seconds/etc. since all time calculations are relative), and contains sub-markets containing information on what the market state was at that period in time. In practice, that currently means it contains a yield curve for bonds (either a flat one, specified by "i=" or a complicated one, specified by "yield.curve="). Eventually, other sub-market types (such as for stocks, mutual funds, etc.) will exist.
[code]# All that is required to specify a bond market is an interest rate mkt1 <- market(market.bond(i=.05),t=0) mkt1C <- market(market.bond(i=.1),t=0) [/code]
Typing in an object name will print information about it. Since we have created two market objects, we will examine their contents:
[code]> mkt1 Market time is: 0 Yield curve is flat, with interest rate 0.05 Money market rate is: 0.05
mkt1C Market time is: 0 Yield curve is flat, with interest rate 0.1 Money market rate is: 0.1[/code]
Note that both markets exist in the exact same moment (time 0).
In keeping with what the R console displays, when a command is followed by output I will precede the command part with ">" .
We proceed by creating some bonds:
[code]bnd.A <- bond(mkt=mkt1,mat=5) # Bonds can be specified by maturity bnd.B <- bond(mkt=mkt1,dur=2.5) # or duration, in which case the maturity under prevailing interest rates is calculated bnd.C <- bond(mkt=mkt1C,mat=15)[/code]
Again, typing an object name displays basic information about it. [code]> bnd.A Bond issued at time 0 with interest of 5%, maturity of 5 years, price of $1000, coupon of $25 paid every 0.5 year.
summary(bnd.A,mkt1) 5-year bond with interest of 5% under a current market yield of 5%. Current maturity of 5 years. Par value is $1000, paying $25 every 0.5 year. Present value (price) of the bond under current market conditions is $1000 Macaulay duration is 4.49 years, modified duration is 4.38 years, with a convexity of 22.61.[/code]
summary() is a function we will use over and over again. It typically takes an object like a bond or portfolio and evaluates that object under given market conditions.
Now is a good time to mention that you can get help on any function using the ? operator. So [code]?bond[/code] gives you help on the bond() function. For generic functions like summary, put a period in between the function and the object you want it to work on. Thus [code]?summary.bond[/code] gets you help on the particular version of summary we just ran. To you programmers out there, this all works via a fairly weak form of object-oriented programming.
Let's make some more markets, with a yield curve and a different time period this time: [code]mkt2 <- market(market.bond(yield.curve=quote(mat/100+0.01),MMrate=.01),t=2) #yield curve must be in format specified here. t=2 implies this is a rate change in the future mkt1B <- market(market.bond(yield.curve=quote(mat/100+0.01),MMrate=.01),t=0) # we'll need this guy later to demonstrate automatic portfolio generation[/code]
Now we can evaluate the same bond two years later, with the intervening coupon payments disappearing into the ether (accounts will address that)
[code]sum.bnd.A <- summary(bnd.A,mkt2)[/code]
Note that nothing printed this time when we ran summary(). That's because we stored the results to another object called sum.bnd.A. We can force the results to print by typing in the name again: [code]> sum.bnd.A 5-year bond with interest of 5% under a current market yield of 6%. Current maturity of 3 years. Par value is $1000, paying $25 every 0.5 year. Present value (price) of the bond under current market conditions is $972.91 Macaulay duration is 2.82 years, modified duration is 2.74 years, with a convexity of 9.11.[/code]
But we can do other things with the summary object, like look at what's inside for later use (making graphs, etc.): [code]> str(sum.bnd.A) List of 11 $ mat : num 5 $ i : num 0.05 $ market.rate : num 0.06 $ effective.mat: num 3 $ par : num 1000 $ cpn : num 25 $ f : num 0.5 $ dur : num 2.74 $ dur.Macaulay : num 2.82 $ pv : num 973 $ cnvx : num 9.11
- attr(*, "class")= chr [1:2] "sum.bond" "bond"[/code]
In the last of this installment, we will use this extraction capability to answer an interesting question: What happens to the duration of a bond with a flat market rate of 10% interest as time passes and it approaches its maturity date?
One reasonable guess would be that duration and maturity decline in lockstep, that is to say linearly. But it turns out that [url=http://www.bogleheads.org/wiki/Duration]duration [/url]can be viewed as the (discounted) dollar-weighted average of the time to future cash flows. The par value is a lot greater than the coupon payments. When it is way off in the distance it counts for very little since it is very discounted, and therefore the duration changes relatively little (the bond looks more like a perpetual annuity). As the maturity date approaches, the par value looms large, and therefore the duration changes more linearly (it looks more like a zero-coupon bond).
How do we see this using maRketSim? Take a bond that we've already created and evaluate it under a bunch markets which differ only by their time. Then for each evaluation we extract the duration, and at the end we plot the durations.
[code]durs <- c() ts <- seq(0,15,.5) for(t in ts) { d <- summary(bnd.C,market(market.bond(i=.1),t=t))$dur durs <- c(durs,d) } plot(durs~ts,main="Duration vs. the passage of time",xlab="Time",ylab="Duration")[/code]
[img][/img]
[b]maRketSim walkthrough, part 2[/b]
So far we have learned about markets and bonds. But all of this machinery seems like a lot of work just to calculate some equations we could have read from a textbook and run through a pocket calculator. To see the real value of maRketSim we have to start aggregating objects together into [i]portfolios[/i].
A portfolio is just a collection of bonds that gets created under a given market (including that market's time):
[code]prt1 <- portfolio(name="bond1",bonds=list(bnd.A,bnd.B,bnd.C),mkt=mkt1) [/code]
As before, typing in the name displays basic information. In the case of a portfolio, it will show the bonds in the portfolio:
[code]. prt1
Portfolio 'bond1' created at time 0 containing 3 bonds.
i (%) Maturity Par Coupon t
5.00 5.0 yr $1000 $25 0.0
5.00 2.5 yr $1000 $25 0.0
10.00 15.0 yr $1000 $50 0.0
[/code]
To see the portfolio under the market it was created in, just run summary: [code]. summary(prt1) Portfolio 'bond1' of 3 bonds created at time 0. Current time is 0. PV of portfolio is $3523.257 Duration of portfolio is 5.861768 Coupon yield is 5.68% Coupon total is $200 per year. N.B. Portfolio summaries do not include reinvested coupons or maturing securities. For that, place the portfolio in an account.[/code]
To see it under a different market, specify the market: [code]. summary(prt1,mkt=mkt2) Portfolio 'bond1' of 3 bonds created at time 0. Current time is 2. PV of portfolio is $2655.986 Duration of portfolio is 2.690954 Coupon yield is 7.53% Coupon total is $200 per year. N.B. Portfolio summaries do not include reinvested coupons or maturing securities. For that, place the portfolio in an account.[/code]
If you want to view the portfolio contents as a data.frame (a standard R structure than can then be written to a CSV, etc.), coerce it: . as.data.frame(prt1) i mat par f cpn t.issue 2 0.05 5.0 1000 0.5 25 0 3 0.05 2.5 1000 0.5 25 0 4 0.10 15.0 1000 0.5 50 0
All well and good, but creating a list of bonds by hand rapidly becomes too much work. What if we want to generate a portfolio of bonds with certain characteristics. For instance, we could ask maRketSim to give us a portfolio of 10 bonds with a portfolio duration of 5 containing bonds whose durations' standard deviation was 1.5? Stay tuned.