Skip to content

Yield Curve and Term Structure of Interest Rates

We focus on risk-free government bonds for now as their yields serve as the backbone of pricing other bonds.

Background

As discussed earlier, a bond represents a series of cashflows in the future and its price equals the sum of present values of all cashflows, each of which may be discounted at different rates.

Whatever these different discount rates are, we can always find a single discount rate (named yield to maturity or YTM) which, when applied to all future payments, gives the market price at the time. Therefore, the yield to maturity is just the same thing as the bond's market price.

But how should a bond's price (hence yield to maturity) be determined?

Of course, price is shaped by transactions in the market. Yet how exactly do market participants value a bond so that they agree on the transaction price? Alternatively, if a new bond is to be issued, how should it be priced?

If we look at the bond pricing formula,

\[ P_{t} = \underbrace{\sum_{\tau=1}^{n} \frac{C}{(1+r_\tau)^{\tau}} + \frac{F}{(1+r_n)^n}}_{\text{each discounted at corresponding spot rate}} = \underbrace{\sum_{\tau=1}^{n} \frac{C}{(1+{y})^{\tau}} + \frac{F}{(1+{y})^n}}_{\text{all discounted at YTM}} \]

clearly the ultimate factor at play is the series of discount rates \(\{r_\tau\}\), which are the spot rates as at time \(t\). On the other hand, each spot rate is applied to discount a single cashflow promised in the future, which resembles the payment of a zero-coupon bond, therefore, the spot rate is also sometimes called the "zero rate".

Example

A two-year 5% coupon $100 bond with annual payments can be viewed as a portfolio of two separate assets:

  • Asset 1 pays a single cashflow of $5 in one year, nothing else in any other time.
  • Asset 2 pays a single cashflow of $105 in two years, nothing else in any other time.

To value Asset 1, we need the one-year spot rate. To value Asset 2, we need the two-year spot rate.

So, to value the bond, we need both the one-year spot rate and the two-year spot rate.

Ideally, we would like to have a table of spot rates that tells us all the spot rates for all maturities, e.g., the 6-month rate, the 1-year rate, 1.5-year rate, 2-year rate, 2.5-year rate, 3-year rate, ..., etc. This table mapping the term to maturity to the corresponding spot rate describes the term structure of interest rates, whose graphical representations are yield curves.

{ "$schema": "https://vega.github.io/schema/vega/v5.json", "description": "A graph of spot rate and maturity, made by Mingze Gao", "width": 700, "height": 300, "title": { "text": "An Illustrative Yield Curve", "fontSize": 18, "anchor": "middle" }, "data": [ { "name": "table", "transform": [ { "type": "sequence", "as": "year", "start": 0.5, "step": 0.5, "stop": 20.5 }, { "type": "formula", "as": "r", "expr": "pow(1.1, datum.year)-1" } ] } ], "scales": [ { "name": "x", "type": "linear", "domain": { "data": "table", "field": "year", "sort": true }, "range": "width" }, { "name": "y", "type": "linear", "domain": { "data": "table", "field": "r" }, "range": "height" } ], "axes": [ { "orient": "bottom", "scale": "x", "title": "Maturity (years)" }, { "orient": "left", "scale": "y", "title": "Spot Rate (%)" } ], "marks": [ { "type": "symbol", "from": { "data": "table" }, "encode": { "update": { "fill": { "value": "steelblue" }, "x": { "scale": "x", "field": "year" }, "y": { "scale": "y", "field": "r" }, "tooltip": { "signal": "{ 'Spot rate for year':format(datum.year, '1,.1f') }" } } } } ] }

Moreover, we want this curve to be continuous without gaps, so that at any point of time we would know the spot rate to use for a cashflow, say 3.14 years in the future.

However, do we have all spot rates readily available? Unfortunately, no. At least not directly.

What do we have in reality?

Why do we not have a table of all spot rates? This is firstly because there are no government bonds for some maturities. For example, the U.S. government issues only Treasury securities of a selected range of maturities:1

  • Treasury Bills (no coupon): 4, 8, 13, 17, 26, and 52 weeks.
  • Treasury Notes (semi-annual coupon): 2, 3, 5, 7, or 10 years.
  • Treasury Bonds (semi-annual coupon): 20 or 30 years.

Further, the YTM of a bond is a complicated weighted average of spot rates \(\{r_\tau\}\), conditional on other bond-specific characteristics. For example, the YTM \(y_n\) of a \(n\)-year coupon bond with annual payments \(C\) and face value \(F\) is function \(g\) of \(\{r_{\tau}\}, C, F, n\):

\[ y_{n} = g(\{r_\tau\}| C, F, n) \]

We can observe from the market the YTMs \(\{y_n\}\) of Treasury securities of all maturities. If we plot them, we may end up with a "yield (to maturity) curve" like below:

{ "$schema": "https://vega.github.io/schema/vega/v5.json", "description": "An Illustrative Graph of Treasury Securities' YTMs, made by Mingze Gao", "width": 700, "height": 300, "title": { "text": "An Illustrative Graph of Treasury Securities' YTMs", "fontSize": 18, "anchor": "middle" }, "data": [ { "name": "table", "values": [ { "year": 0.0769, "text": "" }, { "year": 0.1538, "text": "" }, { "year": 0.25, "text": "y₃ₘ" }, { "year": 0.5, "text": "y₆ₘ" }, { "year": 1, "text": "y₁" }, { "year": 2, "text": "y₂" }, { "year": 3, "text": "y₃" }, { "year": 5, "text": "y₅" }, { "year": 7, "text": "y₇" }, { "year": 10, "text": "y₁₀=g({rₜ|t<=10})" }, { "year": 20, "text": "y₂₀=g({rₜ|t<=20})" }, { "year": 30, "text": "y₃₀=g({rₜ|t<=30})" } ], "transform": [ { "type": "formula", "as": "r", "expr": "pow(1.07, datum.year)-1" } ] } ], "scales": [ { "name": "x", "type": "linear", "domain": { "data": "table", "field": "year", "sort": true }, "range": "width" }, { "name": "y", "type": "linear", "domain": { "data": "table", "field": "r" }, "range": "height" } ], "axes": [ { "orient": "bottom", "scale": "x", "title": "Maturity (years)" }, { "orient": "left", "scale": "y", "title": "YTM (%)" } ], "marks": [ { "type": "symbol", "from": { "data": "table" }, "encode": { "update": { "fill": { "value": "steelblue" }, "x": { "scale": "x", "field": "year" }, "y": { "scale": "y", "field": "r" } } } }, { "type": "text", "from": { "data": "table" }, "encode": { "enter": { "x": { "scale": "x", "field": "year", "offset": 5 }, "y": { "scale": "y", "field": "r", "offset": 5 }, "text": { "field": "text" }, "fontSize": { "value": 22 }, "align": { "value": "left" } } } } ] }

Notice that here we plot the YTMs \(\{y_n\}\), not the spot rates \(\{r_\tau\}\).

Immediately, we can feel the need for some further work:

  • First, we need some reasonable interpolation methods to fill the gaps and connect the dots.
  • Second, we need a method to extract spot rate \(r\) from yield \(y\).

For the first point, unfortunately, finding such a reasonable interpolation method is not easy and requires advanced finance and mathematical knowledge. It is beyond the scope of discussion here.

Further, for those coupon-paying Treasury securities (TNotes and TBonds), their observed market yields are subject to the "coupon effect", i.e., the bond yield is affected by coupon rate holding everything else constant.

But luckily, once we have removed the "coupon effect" from the yields, the second point is relatively straightforward as we will see soon.

The "coupon effect" and "par yield"

Recall that a bond's YTM is a function of spot rates conditional on its coupon rate and other characteristics. This means that, other things equal, coupon rates affect YTM. This effect is unwanted - we are only interested in the relationship between yields and maturities without the distortion in yield comparisons caused by differences in coupon rates among bonds of different maturities.

Quick notes on the coupon effect
  • If the yield curve (of spot rates) is not flat, then bonds with the same maturity but different coupons will have different yields.
    • If the yield curve is upward-sloping, then for any given maturity, higher coupon bonds will have lower yields.
    • If the yield curve is downward-sloping, then for any given maturity, higher coupon bonds will have higher yields.

See for example this NYU Stern course note on the coupon effect.

There is, fortunately, a situation where the "coupon effect" is not a concern, that is when the bonds are trading at par. When bonds are trading at par, their coupon rates and yields are aligned, and this "coupon effect" is mitigated.

Therefore, when we initially select the Treasury securities in our analysis, we would like to choose those that are trading at par. We'd also like to choose those on-the-run(1) Treasury securities because their yields are the most recent reflection of market expectation, and they are more likely to be trading at par or close to par.

  1. "on-the-run" refers to the most recently issued and actively traded securities in a particular category, usually referring to government bonds. These are the latest bonds or securities issued by a government or other issuing entity, and they are typically the ones that are most actively traded in the market.

    In the context of government bonds, such as U.S. Treasury bonds, the term "on-the-run" is used to describe the most recently auctioned bonds of a particular maturity. These bonds are considered the benchmark or reference securities for that maturity because they represent the most current market conditions.

The YTMs of these Treasury securities trading at par are called the "par yields". Notice that while they're named "yield", but they are also the coupon rate of bonds such that these bonds are traded at par.

In other words, par yield is like a market consensus of what the coupon rate (hence yield) of a newly issued Treasury bond should be, so that it is fairly issued at par.

Of course, not all on-the-run Treasury securities will necessarily be trading at par. Some adjustments may be applied by the Treasury. Though the Treasury does not disclose the exact method, but they do report the daily par yields for all maturities.

The yield curve

Now that par yields remove the coupon effect, we may proceed to computing spot rates (zero rates), which will then be used in bond pricing.

Note

We didn't talk about how to "fill the gaps" between consecutive par yields because the methods used by central banks are pretty complicated. Let's assume that we are provided with such par yield curve without gaps to facilitate our discussions.

It should be noted, though, given a series of par yields, there should be a corresponding series zero rates. Hence, a yield curve can either be represented in terms of par yields or zero rates - they carry the same information.

Par yields to zero rates

Suppose at the end of the day, we observe the following par yields:

Maturity Type Par Yield Zero Rate
6 months TBill (no coupon) \(y_{0.5yr}=1.05\%\) 1.05%
1 year TBill (no coupon) \(y_{1yr}=1.2\%\) 1.2%
1.5 years Hypothetical TNote \(y_{1.5yr}=1.5\%\)
2 year TNote \(y_{2yr}=2.2\%\)
2.5 year Hypothetical TNote \(y_{2.5yr}=2.4\%\)
3 year TNote \(y_{3yr}=2.8\%\)
3.5 year Hypothetical TNote \(y_{3.5yr}=3\%\)
4 year Hypothetical TNote \(y_{4yr}=3.4\%\)
...

We can compute the zero rates from the given par yields using a method called bootstrapping.

Bootstrapping

Bootstrapping is an iterative process. In each iteration, we compute a single output based known information plus all the previously computed outputs, so that the "information set" grows gradually as we move forward.

Iteration 1

Let's consider the pricing of the (hypothetical) 1.5-year TNote. Its par yield and hence coupon rate is \(y_{1.5}=1.5\%\). Notice that the only unknown variable is the 1.5-year spot rate (zero rate) \({\color{red}r_{1.5}}\):

\[ P(\text{1.5yr } y_{1.5}\% \text{ TNote}) = \frac{0.5\times y_{1.5}}{1+\frac{r_{0.5}}{2}} + \frac{0.5\times y_{1.5}}{(1+\frac{r_{1}}{2})^2} + \frac{0.5\times y_{1.5}+100}{(1+\frac{\color{red}r_{1.5}}{2})^3} = 100 \]

Since there is only one unknown, we can solve for the 1.5-year spot rate (zero rate) \({\color{red}r_{1.5}}\). In this example, the value of \(\color{green}r_{1.5}=1.5026\%\)

Maturity Type Par Yield Zero Rate
6 months TBill (no coupon) \(y_{0.5yr}=1.05\%\) 1.05%
1 year TBill (no coupon) \(y_{1yr}=1.2\%\) 1.2%
1.5 years Hypothetical TNote \(y_{1.5yr}=1.5\%\) 1.5026%
2 year TNote \(y_{2yr}=2.2\%\)
2.5 year Hypothetical TNote \(y_{2.5yr}=2.4\%\)
3 year TNote \(y_{3yr}=2.8\%\)
3.5 year Hypothetical TNote \(y_{3.5yr}=3\%\)
4 year Hypothetical TNote \(y_{4yr}=3.4\%\)
...

Iteration 2

Now consider the pricing of the 2-year TNote. Its par yield hence coupon rate is \(y_{2}=2.2\%\). Notice that we now have the 1.5-year spot rate \(\color{green}r_{1.5}\) available from last iteration, and the only unknown variable is now the 2-year spot rate (zero rate) \({\color{red}r_{2}}\):

\[ P(\text{2yr } y_2\% \text{ TNote}) = \frac{0.5\times y_2}{1+\frac{r_{0.5}}{2}} + \frac{0.5\times y_2}{(1+\frac{r_{1}}{2})^2} + \frac{0.5\times y_2}{(1+\frac{\color{green}r_{1.5}}{2})^3} +\frac{0.5\times y_2+100}{(1+\frac{\color{red}r_{2}}{2})^4}=100 \]

We can solve for the 2-year spot rate (zero rate) \({\color{red}r_{2}}\). It turns out that in this example, \({\color{green}r_{2}=2.2146\%}\).

Maturity Type Par Yield Zero Rate
6 months TBill (no coupon) \(y_{0.5yr}=1.05\%\) 1.05%
1 year TBill (no coupon) \(y_{1yr}=1.2\%\) 1.2%
1.5 years Hypothetical TNote \(y_{1.5yr}=1.5\%\) 1.5026%
2 year TNote \(y_{2yr}=2.2\%\) 2.2146%
2.5 year Hypothetical TNote \(y_{2.5yr}=2.4\%\)
3 year TNote \(y_{3yr}=2.8\%\)
3.5 year Hypothetical TNote \(y_{3.5yr}=3\%\)
4 year Hypothetical TNote \(y_{4yr}=3.4\%\)
...

Iteration 3

Now consider the pricing of the 2.5-year TNote. Its par yield hence coupon rate is \(y_{2.5}=2.4\%\). Notice that we now have \(\color{green}r_{1.5}\) and \(\color{green}r_{2}\) available from last iterations, and the only unknown variable is now the 2.5-year spot rate (zero rate) \({\color{red}r_{2.5}}\):

\[ P(\text{2.5yr } y_{2.5}\% \text{ TNote}) = \frac{0.5\times y_{2.5}}{1+\frac{r_{0.5}}{2}} + \frac{0.5\times y_{2.5}}{(1+\frac{r_{1}}{2})^2} + \frac{0.5\times y_{2.5}}{(1+\frac{\color{green}r_{1.5}}{2})^3} +\frac{0.5\times y_{2.5}}{(1+\frac{\color{green}r_{2}}{2})^4} + \frac{0.5\times y_{2.5}+100}{(1+\frac{\color{red}r_{2.5}}{2})^4}=100 \]

We can solve for the 2.5-year spot rate (zero rate) \({\color{red}r_{2.5}}\). It turns out that in this example, \({\color{green}r_{2.5}=2.4176\%}\).

Maturity Type Par Yield Zero Rate
6 months TBill (no coupon) \(y_{0.5yr}=1.05\%\) 1.05%
1 year TBill (no coupon) \(y_{1yr}=1.2\%\) 1.2%
1.5 years Hypothetical TNote \(y_{1.5yr}=1.5\%\) 1.5026%
2 year TNote \(y_{2yr}=2.2\%\) 2.2146%
2.5 year Hypothetical TNote \(y_{2.5yr}=2.4\%\) 2.4176%
3 year TNote \(y_{3yr}=2.8\%\)
3.5 year Hypothetical TNote \(y_{3.5yr}=3\%\)
4 year Hypothetical TNote \(y_{4yr}=3.4\%\)
...

Iterations continue...

Eventually, we will be able to fill all the blanks in the table by working out the zero rates for all maturities.

Plotting yield curves

In terms of the example above, we can plot the par yields (blue) and bootstrapped zero rates (red) as below.

{ "$schema": "https://vega.github.io/schema/vega/v5.json", "description": "An Illustrative Graph of Par Yields and Zero Rates, made by Mingze Gao", "width": 700, "height": 300, "title": { "text": "An Illustrative Graph of Par Yields and Zero Rates", "fontSize": 18, "anchor": "middle" }, "data": [ { "name": "table", "values": [ { "year": 0.5, "par_yield": 1.05, "spot_rate": 1.05 }, { "year": 1, "par_yield": 1.2, "spot_rate": 1.2 }, { "year": 1.5, "par_yield": 1.5, "spot_rate": 1.5026393890380874 }, { "year": 2.0, "par_yield": 2.2, "spot_rate": 2.2146487236022976 }, { "year": 2.5, "par_yield": 2.4, "spot_rate": 2.417717552185062 }, { "year": 3.0, "par_yield": 2.8, "spot_rate": 2.8317731857299844 }, { "year": 3.5, "par_yield": 3, "spot_rate": 3.0386474132537877 }, { "year": 4.0, "par_yield": 3.4, "spot_rate": 3.4637652397155807 }, { "year": 4.5, "par_yield": 3.5, "spot_rate": 3.565935420989996 }, { "year": 5.0, "par_yield": 3.6, "spot_rate": 3.669886875152593 }, { "year": 5.5, "par_yield": 3.8, "spot_rate": 3.8880950450897283 }, { "year": 6.0, "par_yield": 4, "spot_rate": 4.1098044395446856 }, { "year": 6.5, "par_yield": 4.3, "spot_rate": 4.453684520721444 }, { "year": 7.0, "par_yield": 4.8, "spot_rate": 5.053358459472664 }, { "year": 7.5, "par_yield": 5, "spot_rate": 5.290201091766365 }, { "year": 8.0, "par_yield": 5.2, "spot_rate": 5.533113956451425 }, { "year": 8.5, "par_yield": 5.35, "spot_rate": 5.714883279800425 }, { "year": 9.0, "par_yield": 5.4, "spot_rate": 5.763193321228037 }, { "year": 9.5, "par_yield": 5.45, "spot_rate": 5.8138489723205655 }, { "year": 10.0, "par_yield": 5.5, "spot_rate": 5.866698265075694 } ] } ], "scales": [ { "name": "x", "type": "linear", "domain": { "data": "table", "field": "year", "sort": true }, "range": "width" }, { "name": "y", "type": "linear", "domain": { "data": "table", "field": "par_yield" }, "range": "height" } ], "axes": [ { "orient": "bottom", "scale": "x", "title": "Maturity (years)" }, { "orient": "left", "scale": "y", "title": "Yield (%)" } ], "marks": [ { "type": "symbol", "from": { "data": "table" }, "encode": { "update": { "fill": { "value": "steelblue" }, "x": { "scale": "x", "field": "year" }, "y": { "scale": "y", "field": "par_yield" }, "tooltip": { "value": "Par Yield" } } } }, { "type": "symbol", "from": { "data": "table" }, "encode": { "update": { "fill": { "value": "#d6001c" }, "x": { "scale": "x", "field": "year" }, "y": { "scale": "y", "field": "spot_rate" }, "tooltip": { "value": "Zero Rate" } } } } ] }

What does a yield curve look like in real world?

See an animation of the daily yield curves since January 2021 to August 2023(1). This is the par yield curve without interpolation. I simply connect consecutive par yields straightly.

  1. Yes, yield curve is not constant. As time changes, the par yields (hence spot rates) change!

Stata code for the animation
/* Yield Curve Animation Frames */

/* Import data from FRED */
import fred DGS1MO DGS3MO DGS6MO DGS1 DGS2 DGS3 DGS5 DGS7 DGS10 DGS20 DGS30, ///
  daterange(2021-01-01 2023-08-25) aggregate(daily,eop) clear
drop datestr 

/* Rename vars to reshape */
foreach yr in 1 2 3 5 7 10 20 30 {
  loc months = `yr'*12
  rename DGS`yr' DGS`months'
}
foreach m in 1 3 6 {
  rename DSG`m'MO DGS`m'
}
reshape long DGS, i(daten) j(maturityInMonths)
rename DGS yield

/* Vars */
gen maturity = maturityInMonths/12
label var maturity "Maturity (years)"
label var yield "Yield (%)"

/* Remove no-yield dates */
drop if yield == .

loc i = 0
forvalues year = 2021/2023 {
  forvalues month = 1/12 {
    forvalues day = 1/31 {
      /* Skip dates without data */
      loc date = mdy(`month', `day', `year')
      if `date' == . {
        continue
      }
      local formatted_date : display %tdY-m-d `date'
      di "`formatted_date'"
      su if daten== mdy(`month',`day',`year'), meanonly
      if r(N)==0 {
        continue
      }
      /* Plot and index each date as a frame */
      loc i = `i' + 1
      twoway (spike yield maturity, sort lcolor(%30) lpattern(dash)) ///
      (connected yield maturity, sort) if daten== mdy(`month',`day',`year'), ///
      ylabel(0(1)10) xtitle(Maturity) ///
      xlabel(0.5 "6M" 1 "1Y" 2 "2Y" 3 "3Y" 5 "5Y" 7 "7Y" ///
        10 "10Y" 20 "20Y" 30 "30Y", labsize(vsmall) ///
      angle(forty_five)) title(Yield Curve of U.S. Treasury Securities) ///
      subtitle("Date: `formatted_date'") ///
      caption(Made by Dr. Mingze Gao, size(vsmall) position(5)) ///
      note(Source: FRED) legend(off)
      graph export "yiled_curve_`i'.png", replace
    }
  }
}

Shapes of yield curve

We can see from the animation above that the yield curve has been overall shifted upwardly in the past few years. Moreover, the shape of the yield curve has changed.

  • In 2021, the yield curve's shape was normal, meaning that it's upward-sloping with short-term yields lower than long-term yields.
  • In 2022, the yield curve's shape became flat, meaning that the short-term yields are similar to those long-term yields.
  • Since late 2022 and in 2023, the yield curve's shape is inverted, meaning that it's downward-sloping the short-term yields higher than long-term yields.

These shapes of yield curves are informative.

  • An upward-sloping yield curve generally indicates that investors expect the economy to grow in the future, leading to higher inflation and interest rates. It reflects the market's anticipation of increasing borrowing costs and potentially rising demand for credit.
  • A flat yield curve could suggest uncertainty about the economic outlook. It might also indicate that the central bank is maintaining a neutral stance or that market participants believe that current interest rates are close to their peak.
  • An inverted yield curve is often seen as a signal of potential economic slowdown or recession. It suggests that investors expect future interest rates to decline due to weaker economic conditions. An inverted curve can indicate that market participants expect the central bank to cut interest rates to stimulate economic growth.

  1. See TreasuryDirect for a list of available Treasury securities. This is the official website to buy U.S. Treasury securities. 

Comments