The treasuryTR package offers the functionality to calculate the TR from constant-maturity bond yields.
While Treasury yields are easy to come by1, total return (TR) indices are not. The TR is what is earned by investors, and is therefore of of paramount importance e.g. when simulating a treasury-stock diversified portfolio. A supplier for proprietary TR Treasury index data is CRSP2. Their data can be purchased or accesses trough a handful of commercial research platforms.
The TR can be computed from publicly available (constant-maturity) yield-to-maturity using standard (fixed-income) textbook formulas. Swinkels 2019 compares the TR series with alternative series (CRSP, Bloomberg etc.) and find that the returns are very close and are therefore a high-quality alternative to commercially available data.
get_yields()
relied on quantmod::getSymbols()
and can be used for downloading constant-maturity US treasury returns.
Common maturities are: DGS1MO 1-Month, DGS3MO: 3-Month, DGS6MO: 6-Month, DGS1 1-Year, DGS2 2-Year, DGS3 3-Year, DGS5 5-Year, DGS7 7-Year, DGS10 10-Year, DGS20 20-Year, and DGS30 30-Year Treasury Constant Maturity Rate.
library(treasuryTR)
yield_1y <- get_yields("DGS1")
yield_10y <- get_yields("DGS10")
yield_20y <- get_yields("DGS20")
tr_1y <- total_return(yield_1y, maturity = 1, scale = 261)
tr_10y <- total_return(yield_10y, maturity = 10, scale = 261)
tr_20y <- total_return(yield_20y, maturity = 20, scale = 261)
head(cbind.xts(tr_1y, tr_10y, tr_20y))
#> DGS1 DGS10 DGS20
#> 1962-01-02 NA NA NA
#> 1962-01-03 -7.377978e-05 0.0026052037 0.0001528606
#> 1962-01-04 1.221768e-04 0.0034295689 0.0015146560
#> 1962-01-05 -7.300865e-05 -0.0022968905 -0.0012057347
#> 1962-01-08 -3.647573e-04 -0.0006649767 -0.0012041801
#> 1962-01-09 2.721722e-05 -0.0014782454 0.0001532288
library(PerformanceAnalytics)
table.AnnualizedReturns(cbind.xts(tr_1y, tr_10y, tr_20y), Rf = tr_1y, scale = 262)
#> DGS1 DGS10 DGS20
#> Annualized Return 0.0504 0.0671 0.0754
#> Annualized Std Dev 0.0121 0.0718 0.1026
#> Annualized Sharpe (Rf=NA%) 0.0000 0.2274 0.2370
In the example we above, we get yields for the 1-Year, the 10-Year, and the 20-Year treasuries. All of these yield series start in 1962. We calculate the TR using total_return()
. It is worth noting that we use scale 261, as this is the average number of days per year that the yield is reported on.
Swinkels (2019) compares the TR series he computes based on monthly 10-Year treasury yields to common treasury indices from CRSP, Global Financial Data, Ibbotson, and Bloomberg. He finds that
yield_10y_monthly <- yield_10y[endpoints(yield_10y, on = "months", k = 1)]
tr_10y_monthly <- total_return(yield_10y_monthly, 10, scale = 12)
performance_10y <- cumprod(1+tr_10y_monthly[-1])-1
plot(performance_10y)
library(dplyr)
yield_10y_df <- get_yields("DGS10", format_out = "tibble")
tr_10y_df <- yield_10y_df %>%
mutate(TR = total_return(DGS10, maturity = 10))
tr_10y_df %>%
filter(!is.na(TR)) %>%
summarise(mu = mean(TR)*262,
sigma = sd(TR)*sqrt(262))
#> # A tibble: 1 x 2
#> mu sigma
#> <dbl> <dbl>
#> 1 0.0676 0.0718
Step-by-step calculation.
tr_10y_df_stepbystep <- yield_10y_df %>%
mutate(mod_duration = mod_duration(DGS10, 10),
convexity = convexity(DGS10, 10),
TR = total_return(DGS10, maturity = 10,
mdur = mod_duration,
convex = convexity))
tail(tr_10y_df_stepbystep)
#> # A tibble: 6 x 5
#> date DGS10 mod_duration convexity TR
#> <date> <dbl> <dbl> <dbl> <dbl>
#> 1 2021-01-27 0.0104 9.47 97.3 0.000988
#> 2 2021-01-28 0.0107 9.46 97.1 -0.00279
#> 3 2021-01-29 0.0111 9.44 96.8 -0.00373
#> 4 2021-02-01 0.0109 9.45 97.0 0.00193
#> 5 2021-02-02 0.0112 9.44 96.8 -0.00278
#> 6 2021-02-03 0.0115 9.42 96.6 -0.00278
Let’s use Swiss yield data that we download using the dataseries
package.
library(dataseries)
library(tidyr)
library(ggplot2)
swiss_yields <- ds(c("ch_snb_rendoblim.1j",
"ch_snb_rendoblim.10j",
"ch_snb_rendoblim.20j"))
swiss_tr <- swiss_yields %>%
mutate(TR1 = total_return(ch_snb_rendoblim.1j/100, maturity = 1, scale = 12),
TR10 = total_return(ch_snb_rendoblim.10j/100, maturity = 10, scale = 12),
TR20 = total_return(ch_snb_rendoblim.20j/100, maturity = 20, scale = 12)) %>%
select(time, starts_with("TR")) %>%
pivot_longer(cols = -time) %>%
filter(!is.na(value)) %>%
arrange(name, time)
swiss_tr %>%
group_by(name) %>%
mutate(performance = cumprod(1+value)-1) %>%
ggplot(aes(x = time, y = performance*100, color = name)) +
geom_line() +
scale_y_continuous() +
scale_x_date(date_breaks = "1 year", date_labels = "%Y") +
labs(title = "Cumulative performance since 1962 of Swiss Confederation Bonds",
x = "", y = "%", color = "") +
theme_classic() +
theme(legend.position = "top",
plot.title = element_text(hjust = 0.5),
axis.text.x = element_text(angle = 90, vjust = 0.5))
swiss_tr %>%
group_by(name, year = format(time, "%Y")) %>%
summarise(TR = prod(1+value)-1) %>%
ggplot(aes(x = year, y = TR*100, fill = name)) +
geom_col(position = position_dodge2(), alpha = 0.9) +
scale_y_continuous() +
labs(title = "TR per calendar year of Swiss Confederation Bonds",
x = "", y = "%", fill = "") +
theme_classic() +
theme(legend.position = "top",
plot.title = element_text(hjust = 0.5),
axis.text.x = element_text(angle = 90, vjust = 0.5),
panel.grid.major.x = element_line())
#> `summarise()` has grouped output by 'name'. You can override using the `.groups` argument.
Swinkels, L. (2019) Treasury Bond Return Data Starting in 1962. Data 4(3), 91 https://doi.org/10.3390/data4030091
Swinkels, L. (2019) Data: International Government Bond Returns Since 1947. figshare. Dataset. https://doi.org/10.25397/eur.8152748
E.g. on the Federal Reserve Bank of St. Louis’s data portal “FRED”, see https://fred.stlouisfed.org/series/DGS5 (5 year), https://fred.stlouisfed.org/series/DGS10 (10 year), https://fred.stlouisfed.org/series/DGS20 (20 years), https://fred.stlouisfed.org/series/DGS30 (30 years)↩︎
Center for Research in Security Prices, LLC, see http://www.crsp.org/↩︎