github.com/diadata-org/diadata@v1.4.593/internal/pkg/rateDerivatives/rfr.go (about) 1 package ratederivatives 2 3 import ( 4 "errors" 5 "math" 6 "time" 7 8 "github.com/diadata-org/diadata/pkg/utils" 9 log "github.com/sirupsen/logrus" 10 ) 11 12 // RateFactor returns the integer a rate is multiplied by in the computation 13 // of (compounded) RFRs. 14 func RateFactor(date time.Time, holidays []time.Time) (int, error) { 15 rate := 1 16 date = date.AddDate(0, 0, 1) 17 for !utils.CheckWeekDay(date) || utils.ContainsDay(holidays, date) { 18 rate++ 19 date = date.AddDate(0, 0, 1) 20 } 21 return rate, nil 22 } 23 24 // CompoundedRate returns the compounded index for the rate values given by the slice @rate. 25 // @rates is a slice with daily rates for all business days in the respective period. 26 // @dateInit, @dateFinal determine the period of the loan. 27 // @holidays is a slice of strings where each entry corresponds to a special holiday (i.e. not a 28 // saturday or sunday) in the respective period. 29 // @daysPerYear determines the total number of days per business year. 30 // @rounding is a float of type 1e-n which rounds the result to n digits. If @rounding == 0 no rounding 31 func CompoundedRate(rates []float64, dateInit, dateFinal time.Time, holidays []time.Time, daysPerYear int, rounding int) (float64, error) { 32 33 // Check feasibility and consistency of input data 34 if !utils.CheckWeekDay(dateFinal) || utils.ContainsDay(holidays, dateFinal) { 35 // log.Info("No rate information for holidays or weekends") 36 return float64(0), errors.New("no rate information for holidays or weekends") 37 } 38 if utils.AfterDay(dateInit, dateFinal) { 39 log.Info("The final date cannot be before the initial date.") 40 return float64(0), errors.New("the final date cannot be before the initial date") 41 } 42 if daysPerYear == 0 { 43 log.Info("Days per year must be a positive integer.") 44 return float64(0), errors.New("days per year must be a positive integer") 45 } 46 NumBusinessDays, _ := utils.CountDays(dateInit, dateFinal, true) 47 NumBusinessDays -= len(holidays) 48 if NumBusinessDays == 0 { 49 log.Info("No business days in period of interest.") 50 return float64(0), errors.New("no business days in period of interest") 51 } 52 53 if !utils.CheckWeekDay(dateInit) || utils.ContainsDay(holidays, dateInit) { 54 // When first day is holiday or weekend, there has to be an additional rate for the 55 // previous working day which does not fall into the loan period. 56 if len(rates) != NumBusinessDays+1 { 57 log.Error("The given number of rate values and business days is not consistent.") 58 return float64(0), errors.New("date error") 59 } 60 } else { 61 if len(rates) != NumBusinessDays { 62 log.Error("The given number of rate values and business days is not consistent.") 63 return float64(0), errors.New("date error") 64 } 65 } 66 67 // Iterate through business days to compute the compounded rate 68 prod := float64(1) 69 for i := 0; i < len(rates); i++ { 70 n, _ := RateFactor(dateInit, holidays) 71 factor := 1 + (rates[i]/100)*float64(n)/float64(daysPerYear) 72 prod *= factor 73 dateInit = dateInit.AddDate(0, 0, n) 74 } 75 76 // In case of the SOFR Index, results are rounded to eight decimals 77 if rounding != 0 { 78 result := math.Round(prod*math.Pow(10, float64(rounding))) / math.Pow(10, float64(rounding)) 79 return result, nil 80 } 81 return prod, nil 82 } 83 84 // CompoundedRateSimple returns the compounded index for the rate values given by the slice @rate. 85 // @rates is a slice with daily rates for all calendar days in the respective period. 86 // @dateInit, @dateFinal determine the period of the loan. 87 // @daysPerYear determines the total number of days per business year. 88 // @rounding is a float of type 1e-n which rounds the result to n digits. If @rounding == 0 no rounding 89 func CompoundedRateSimple(rates []float64, dateInit, dateFinal time.Time, daysPerYear int, rounding int) (float64, error) { 90 91 // Check feasibility and consistency of input data 92 if utils.AfterDay(dateInit, dateFinal) { 93 log.Info("The final date cannot be before the initial date.") 94 return float64(0), errors.New("the final date cannot be before the initial date") 95 } 96 if daysPerYear == 0 { 97 log.Info("Days per year must be a positive integer.") 98 return float64(0), errors.New("days per year must be a positive integer") 99 } 100 101 // Iterate through business days to compute the compounded rate 102 prod := float64(1) 103 for i := 0; i < len(rates); i++ { 104 factor := 1 + rates[i]/100/float64(daysPerYear) 105 prod *= factor 106 } 107 108 // In case of the SOFR Index, results are rounded to eight decimals 109 if rounding != 0 { 110 result := math.Round(prod*math.Pow(10, float64(rounding))) / math.Pow(10, float64(rounding)) 111 return result, nil 112 } 113 return prod, nil 114 }