Construction of SOFR Index from SOFR Rates

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.


Introduction


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.
Additional Information about Reference Rates Administered by the New York Fed (https://www.newyorkfed.org/markets/reference-rates/additional-information-about-reference-rates#sofr_ai_calculation_methodology)
SOFR Averages and Index Data (https://www.newyorkfed.org/markets/reference-rates/sofr-averages-and-index#historical-search)

Daily SOFR rates can be downloaded at the following page.
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 Average between 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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#=========================================================================#
# Financial Econometrics & Derivatives, ML/DL using R, Python, Tensorflow  
# by Sang-Heon Lee 
#-------------------------------------------------------------------------#
# Calculation of SOFR Index from Daily SOFR Rates
#=========================================================================#
 
# construction of data frame
df <- 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 format
df$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-decimal
df$Calc_Index <- round(df$Calc_Index,8)
 
# print df
print(df)
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.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
> print(df)
     Pub_Date SOFR_Index SOFR_Rate Calc_Index
1  2018-04-02   1.000000      1.80   1.000000
2  2018-04-03   1.000050      1.83   1.000050
3  2018-04-04   1.000101      1.74   1.000101
4  2018-04-05   1.000149      1.75   1.000149
5  2018-04-06   1.000198      1.75   1.000198
6  2018-04-09   1.000344      1.75   1.000344
7  2018-04-10   1.000392      1.75   1.000392
8  2018-04-11   1.000441      1.76   1.000441
9  2018-04-12   1.000490      1.73   1.000490
10 2018-04-13   1.000538      1.72   1.000538
11 2018-04-16   1.000681      1.77   1.000681
12 2018-04-17   1.000731      1.76   1.000731
13 2018-04-18   1.000779      1.75   1.000779
14 2018-04-19   1.000828      1.73   1.000828
15 2018-04-20   1.000876      1.72   1.000876
16 2018-04-23   1.001020      1.70   1.001020
> 
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\)


2 comments:

  1. pls make a workout how to valuate a OIS swap based on float rate of sofr o/n

    ReplyDelete
    Replies
    1. These days I am working on other issue not derivatives. After my works are completed, I will deal with that issue.

      Delete

Tentative Topics (Keeping Track to Avoid Forgetting)

Segmented Nelson-Siegel model
Shifting Endpoints Nelson-Siegel model
Nadaraya-Watson estimator
Locally weighted scatterplot smoothing (LOWESS)
Time-Varying Parameter Vector Autoregressions (TVP-VAR)
Time-varying or Dynamic Copula
Bayesian VAR
Adrian-Crump-Moench (ACM) term premium model
GARCH-EVT-Copula approach