Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

This post replicates the construction process of the SOFR index from daily SOFR rates. Since this index is published in a daily basis officially, there is no need to make it. But in the process of pricing SOFR swaps and floating rate notes, this sort of calculation is needed.

# SOFR Rates and Index

LIBOR will transition to an alternative reference rate. The SOFR(Secured Overnight Financing Rate) is the risk-free alternative reference rates for USD and is an in-arrears rate. Unlike LIBOR, SOFR is a secured overnight rate, not a forward looking term rate. SOFR Averages (30-, 90-, and 180-calendar days) are constructed by the geometric average of the daily rates. There are two method to make them. The first is to use the daily rate directly and the second is to use the SOFR index.

Before going into SOFR Averages, we will calculate SOFR Index at first. The following pages are the information regarding the SOFR.
SOFR Averages and Index Data (https://www.newyorkfed.org/markets/reference-rates/sofr-averages-and-index#historical-search)

Secured Overnight Financing Rate (SOFR) (https://fred.stlouisfed.org/series/SOFR)
We try to replicate the SOFR index from the following page.
Daily indicative SOFR averages and index (Apr. 2018 – Feb. 2020) (https://www.newyorkfed.org/medialibrary/media/markets/historical_indicative_sofr_avg_ind_data.xlsx)
The above excel file have the following form (to get all visible rows of averages, some selected rows are hidden)

Before going into the detail, It is important to distinguish between publication date and value date (date of rate). SOFR rate available at publication date is the rate which is determined at previous business date(value date). If there is no holiday and weekend in between, the publiation date is the time $$t$$ and the previous business date is the time $$t-1$$.

The SOFR Index measures the cumulative index of compounding the SOFR on a unit of investment over time, with the initial value set to 1.00000000 on April 2, 2018, the first value date of the SOFR. The Index is compounded by the value of each SOFR thereafter.

\begin{align} \text{SOFR Index at time t} = \prod_{i=April, 3, 2018}^{t} \left( 1 + \frac{SOFR_i \times n_i }{ 360}\right) \end{align} Here, $$i$$ denotes a sequential business day from April, 3, 2018 to $$t$$. $$SOFR_i$$ is the SOFR applicable on business day i and $$n_i$$ is the number of calendar days for which $$SOFR_i$$ applies.

From this SOFR Index, SOFR averages can be calculated using the following formula.

\begin{align} \text{SOFR Avg btw x and y} = \left(\frac{\text{SOFR Index_y}}{\text{SOFR Index_x}} -1 \right) \times \left(\frac{360}{dc} \right) \end{align}
Here, $$x$$ and $$y$$ are the start and end date of calculation period respectively. $$dc$$ is the number of calendar dates in the calculation period.

We can calculate the SOFR index from the daily SOFR rates using the following R code.

 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748 #=========================================================================## Financial Econometrics & Derivatives, ML/DL using R, Python, Tensorflow  # by Sang-Heon Lee #————————————————————————-## Calculation of SOFR Index from Daily SOFR Rates#=========================================================================# # construction of data framedf <– data.frame(rbind(    c(“2018-04-02”,    1.00000000,    1.80),    c(“2018-04-03”,    1.00005000,    1.83),    c(“2018-04-04”,    1.00010084,    1.74),    c(“2018-04-05”,    1.00014917,    1.75),    c(“2018-04-06”,    1.00019779,    1.75),    c(“2018-04-09”,    1.00034365,    1.75),    c(“2018-04-10”,    1.00039228,    1.75),    c(“2018-04-11”,    1.00044091,    1.76),    c(“2018-04-12”,    1.00048982,    1.73),    c(“2018-04-13”,    1.00053790,    1.72),    c(“2018-04-16”,    1.00068131,    1.77),    c(“2018-04-17”,    1.00073051,    1.76),    c(“2018-04-18”,    1.00077944,    1.75),    c(“2018-04-19”,    1.00082809,    1.73),    c(“2018-04-20”,    1.00087618,    1.72),    c(“2018-04-23”,    1.00101964,    1.70)))    colnames(df) <– c(“Pub_Date”,“SOFR_Index”, “SOFR_Rate”) # string to date formatdf$Pub_Date <– as.Date(df$Pub_Date)df$SOFR_Index <– as.numeric(df$SOFR_Index)df$SOFR_Rate <– as.numeric(df$SOFR_Rate) for(i in 1:nrow(df)) {    if (i==1) {        df$Calc_Index[i] = 1 } else { ni = as.numeric(df$Pub_Date[i] – df$Pub_Date[i–1]) df$Calc_Index[i] = df$Calc_Index[i–1]* (1+df$SOFR_Rate[i–1]/100*ni/360)    }} # rounding at 8-decimaldf$Calc_Index <– round(df$Calc_Index,8) # print dfprint(df)Colored by Color Scripter cs

In the above R code, data.frame (df) have all data as string formats because Pub_Date is of a string type. Therefore type recast is applied. The content of rest of code is the implementation of the SOFR Index by its formula. The following result shows that Calc_Index (the right most column of df) is the same as the SOFR_Index.

 12345678910111213141516171819 > print(df)     Pub_Date SOFR_Index SOFR_Rate Calc_Index1  2018–04–02   1.000000      1.80   1.0000002  2018–04–03   1.000050      1.83   1.0000503  2018–04–04   1.000101      1.74   1.0001014  2018–04–05   1.000149      1.75   1.0001495  2018–04–06   1.000198      1.75   1.0001986  2018–04–09   1.000344      1.75   1.0003447  2018–04–10   1.000392      1.75   1.0003928  2018–04–11   1.000441      1.76   1.0004419  2018–04–12   1.000490      1.73   1.00049010 2018–04–13   1.000538      1.72   1.00053811 2018–04–16   1.000681      1.77   1.00068112 2018–04–17   1.000731      1.76   1.00073113 2018–04–18   1.000779      1.75   1.00077914 2018–04–19   1.000828      1.73   1.00082815 2018–04–20   1.000876      1.72   1.00087616 2018–04–23   1.001020      1.70   1.001020> Colored by Color Scripter cs

Next time, we will make SOFR averages from the SOFR index. After that, we try to get the same results from the direct use of SOFR rates successively. $$\blacksquare$$