Skip to main content

rustyqlib/rates/
utils.rs

1use chrono::{Local, NaiveDate, Weekday};
2use chrono::Datelike;
3use crate::rates::deposits::Deposit;
4use crate::core::traits::Rates;
5use serde::{Serialize,Deserialize};
6fn is_weekend(date: NaiveDate) -> bool {
7    // Check if the day of the week is Saturday (6) or Sunday (7)
8    let day_of_week = date.weekday();
9    day_of_week == Weekday::Sat || day_of_week == Weekday::Sun
10}
11
12fn is_holiday(date: NaiveDate) -> bool {
13    // Check if the date is Christmas
14    if date.month() == 12 && date.day() == 25{
15        return true;
16    }
17    else if date.month() == 1 && date.day() == 1{ // Check if the date is New Year's Day
18        return true;
19    }
20    else if date.month() == 1 && date.weekday() == Weekday::Mon
21        && date.day() > 14 && date.day() <= 21 {
22        // Check if the date is Martin Luther King Jr. Day
23        return true;
24    }
25    else if date.month() == 11 &&
26        date.weekday() == Weekday::Thu && date.day() > 21 && date.day() <= 28{
27        // Check if the date is in November and falls on the fourth Thursday Thanksgiving Day
28        return true;
29    }
30    else if date.month() == 9 && date.weekday() == Weekday::Mon && date.day() <= 7{
31        // Check if the date is in September and falls on the first Monday Labor Day
32        return true;
33    }
34    return false;
35}
36
37fn adjust_for_weekend(mut date: NaiveDate) -> NaiveDate {
38    // Increment the date until it's not a weekend
39    while is_holiday(date) || is_holiday(date) {
40        date = date.succ();
41
42    }
43    date
44}
45#[derive(Clone,Debug,Serialize,Deserialize)]
46pub enum DayCountConvention{
47    Act365,
48    Act360,
49    Thirty360,
50}
51impl DayCountConvention{
52    pub fn num_of_days(&self) -> usize
53    {
54        match self {
55            DayCountConvention::Act365 => 365,
56            DayCountConvention::Act360 => 360,
57            DayCountConvention::Thirty360 => 360,
58        }
59    }
60    pub fn get_year_fraction(&self,start_date:NaiveDate,maturity_date:NaiveDate) -> f64 {
61        let duration = maturity_date.signed_duration_since(start_date);
62        let year_fraction = duration.num_days() as f64 / self.num_of_days() as f64;
63        year_fraction
64    }
65}
66
67#[derive(Clone,Debug)]
68pub struct TermStructure {
69    pub date: Vec<NaiveDate>,
70    pub discount_factor: Vec<f64>,
71    pub rate: Vec<f64>,
72    pub day_count: DayCountConvention,
73}
74
75impl TermStructure {
76    pub fn new(date: Vec<NaiveDate>, discount_factor: Vec<f64>,rate:Vec<f64>,day_count:DayCountConvention) -> TermStructure {
77        TermStructure {
78            date,
79            discount_factor,
80            rate,
81            day_count
82        }
83    }
84
85    pub fn interpolate_log_linear(&self,val_date:NaiveDate,maturity_date:NaiveDate)-> f64{
86        let year_fraction = self.get_year_fraction(val_date);
87        let target_yf = maturity_date.signed_duration_since(val_date).num_days() as f64
88            / self.day_count.num_of_days() as f64;
89        let mut df1 = 1.0;
90        let mut df2 = 1.0;
91        let mut t1 = 0.0;
92        let mut t2 = 0.0;
93        for (i, time) in year_fraction.iter().enumerate() {
94            if time==&target_yf{
95                 return self.discount_factor[i];
96            }
97            else if time< &target_yf {
98                t1 = *time;
99                df1 = self.discount_factor[i];
100            }
101            else if time> &target_yf {
102                t2 = *time;
103                df2 = self.discount_factor[i];
104                break;
105            }
106
107        }
108        let log_df1 = f64::ln(df1);
109        let log_df2 = f64::ln(df2);
110        let w = (target_yf - t1) / (t2 - t1);
111        let log_df = log_df1 + w * (log_df2 - log_df1);
112        let df = f64::exp(log_df);
113        return df;
114        //let dfs  = self.discount_factor;
115
116    }
117    pub fn get_year_fraction(&self,val_date:NaiveDate) -> Vec<f64> {
118        let mut year_fraction_vec:Vec<f64> = Vec::new();
119        for time in self.date.iter() {
120            let duration = time.signed_duration_since(val_date);
121            let year_fraction = duration.num_days() as f64 / self.day_count.num_of_days() as f64;
122            year_fraction_vec.push(year_fraction);
123        }
124        year_fraction_vec
125    }
126    pub fn rates(&self,val_date:NaiveDate) -> Vec<f64> {
127        let mut rates:Vec<f64> = Vec::new();
128        for i in 0..self.discount_factor.len() {
129            let rate = (1.0 / self.discount_factor[i] - 1.0) / self.day_count.get_year_fraction(val_date,self.date[i]);
130            rates.push(rate);
131        }
132        return rates;
133    }
134    pub fn build_term_structure(&self,valuation_date:NaiveDate,deposits:Vec<Deposit>) -> TermStructure {
135        let mut discount_factor:Vec<f64> = Vec::new();
136        let mut rate:Vec<f64> = Vec::new();
137        let mut dates:Vec<NaiveDate> = Vec::new();
138        for deposit in deposits.iter() {
139            discount_factor.push(deposit.get_discount_factor());
140            dates.push(deposit.get_maturity_date());
141            rate.push(deposit.get_rate());
142        }
143        let day_count = deposits[0].day_count.clone();
144        let mut term_structure = TermStructure::new(dates,discount_factor,rate,day_count);
145        return term_structure;
146    }
147}
148
149pub fn convert_mm_to_date(mut date: String) -> NaiveDate {
150    let current_date = Local::now().date_naive();
151    date.pop();
152    let month = date.parse::<u32>().unwrap();
153
154    let (new_year, new_month) = if current_date.month() + month > 12 {
155        let year = ((current_date.month() + month) / 12) as i32;
156        let m:u32 = (year * 12) as u32;
157        let new_month = current_date.month() + month;
158        (current_date.year() + year, new_month-m)
159    } else {
160        (current_date.year(), current_date.month() + month)
161    };
162    let date_in_months = current_date.with_year(new_year).unwrap_or(current_date)
163        .with_month(new_month).unwrap_or(current_date);
164    let mut maturity_date = date_in_months;
165    maturity_date = adjust_for_weekend(maturity_date);
166    return maturity_date;
167}
168