rateslib/scheduling/calendars/
adjuster.rs

1use crate::scheduling::DateRoll;
2use chrono::prelude::*;
3use serde::{Deserialize, Serialize};
4
5/// A list of rules for performing date adjustment.
6#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
7pub enum Adjuster {
8    /// Actual date without adjustment.
9    Actual {},
10    /// Following adjustment rule.
11    Following {},
12    /// Modified following adjustment rule.
13    ModifiedFollowing {},
14    /// Previous adjustment rule.
15    Previous {},
16    /// Modified previous adjustment rule.
17    ModifiedPrevious {},
18    /// Following adjustment rule, enforcing settlement calendar.
19    FollowingSettle {},
20    /// Modified following adjustment rule, enforcing settlement calendar.
21    ModifiedFollowingSettle {},
22    /// Previous adjustment rule, enforcing settlement calendar.
23    PreviousSettle {},
24    /// Modified previous adjustment rule, enforcing settlement calendar.
25    ModifiedPreviousSettle {},
26    /// A set number of business days, defined by a given calendar,
27    /// using calendar lag rules and enforcing settlement calendars.
28    BusDaysLagSettle(i32),
29    /// A set number of calendar days enforcing settlement calendars, defined by a
30    /// given calendar.
31    CalDaysLagSettle(i32),
32    /// Following adjustment rule except uses actual date for the last date in a vector.
33    FollowingExLast {},
34    /// Following adjustment rule, enforcing settlement, except uses actual date for the last date in a vector.
35    FollowingExLastSettle {},
36}
37
38/// Perform date adjustment according to calendar definitions, i.e. a known [`DateRoll`].
39pub trait Adjustment {
40    /// Adjust a date under an adjustment rule.
41    fn adjust<T: DateRoll>(&self, udate: &NaiveDateTime, calendar: &T) -> NaiveDateTime;
42
43    /// Adjust a vector of dates under an adjustment rule;
44    fn adjusts<T: DateRoll>(&self, udates: &Vec<NaiveDateTime>, calendar: &T)
45        -> Vec<NaiveDateTime>;
46}
47
48/// Perform date adjustment according to adjustment rules, i.e. a given [`Adjuster`].
49pub trait CalendarAdjustment {
50    /// Adjust a date under an adjustment rule.
51    fn adjust(&self, udate: &NaiveDateTime, adjuster: &Adjuster) -> NaiveDateTime
52    where
53        Self: Sized + DateRoll,
54    {
55        adjuster.adjust(udate, self)
56    }
57
58    /// Adjust a vector of dates under an adjustment rule;
59    fn adjusts(&self, udates: &Vec<NaiveDateTime>, adjuster: &Adjuster) -> Vec<NaiveDateTime>
60    where
61        Self: Sized + DateRoll,
62    {
63        adjuster.adjusts(udates, self)
64    }
65}
66
67impl Adjustment for Adjuster {
68    fn adjust<T: DateRoll>(&self, udate: &NaiveDateTime, calendar: &T) -> NaiveDateTime {
69        match self {
70            Adjuster::Actual {} => *udate,
71            Adjuster::Following {} => calendar.roll_forward_bus_day(udate),
72            Adjuster::Previous {} => calendar.roll_backward_bus_day(udate),
73            Adjuster::ModifiedFollowing {} => calendar.roll_mod_forward_bus_day(udate),
74            Adjuster::ModifiedPrevious {} => calendar.roll_mod_backward_bus_day(udate),
75            Adjuster::FollowingSettle {} => calendar.roll_forward_settled_bus_day(udate),
76            Adjuster::PreviousSettle {} => calendar.roll_backward_settled_bus_day(udate),
77            Adjuster::ModifiedFollowingSettle {} => {
78                calendar.roll_forward_mod_settled_bus_day(udate)
79            }
80            Adjuster::ModifiedPreviousSettle {} => {
81                calendar.roll_backward_mod_settled_bus_day(udate)
82            }
83            Adjuster::BusDaysLagSettle(n) => calendar.lag_bus_days(udate, *n, true),
84            Adjuster::CalDaysLagSettle(n) => {
85                let adj = if *n < 0 {
86                    Adjuster::PreviousSettle {}
87                } else {
88                    Adjuster::FollowingSettle {}
89                };
90                calendar.add_cal_days(udate, *n, &adj)
91            }
92            Adjuster::FollowingExLast {} => calendar.roll_forward_bus_day(udate), // no vector
93            Adjuster::FollowingExLastSettle {} => calendar.roll_forward_settled_bus_day(udate), // no vector
94        }
95    }
96
97    fn adjusts<T: DateRoll>(
98        &self,
99        udates: &Vec<NaiveDateTime>,
100        calendar: &T,
101    ) -> Vec<NaiveDateTime> {
102        match self {
103            Adjuster::FollowingExLast {} | Adjuster::FollowingExLastSettle {} => {
104                let mut adates: Vec<NaiveDateTime> = udates
105                    .iter()
106                    .map(|udate| self.adjust(udate, calendar))
107                    .collect();
108                adates[udates.len() - 1] = udates[udates.len() - 1];
109                adates
110            }
111            _ => udates
112                .iter()
113                .map(|udate| self.adjust(udate, calendar))
114                .collect(),
115        }
116    }
117}
118
119// UNIT TESTS
120#[cfg(test)]
121mod tests {
122    use super::*;
123    use crate::scheduling::{ndt, Cal};
124
125    fn fixture_hol_cal() -> Cal {
126        let hols = vec![ndt(2015, 9, 5), ndt(2015, 9, 7)]; // Saturday and Monday
127        Cal::new(hols, vec![5, 6])
128    }
129
130    #[test]
131    fn test_adjusts() {
132        let cal = fixture_hol_cal();
133        let udates = vec![
134            ndt(2015, 9, 4),
135            ndt(2015, 9, 5),
136            ndt(2015, 9, 6),
137            ndt(2015, 9, 7),
138        ];
139        let result = Adjuster::Following {}.adjusts(&udates, &cal);
140        assert_eq!(
141            result,
142            vec![
143                ndt(2015, 9, 4),
144                ndt(2015, 9, 8),
145                ndt(2015, 9, 8),
146                ndt(2015, 9, 8)
147            ]
148        );
149    }
150
151    #[test]
152    fn test_adjusts_ex_last() {
153        // the last date in the vector is unadjusted
154        let cal = fixture_hol_cal();
155        let udates = vec![
156            ndt(2015, 9, 4),
157            ndt(2015, 9, 5),
158            ndt(2015, 9, 6),
159            ndt(2015, 9, 7),
160        ];
161        let result = Adjuster::FollowingExLast {}.adjusts(&udates, &cal);
162        assert_eq!(
163            result,
164            vec![
165                ndt(2015, 9, 4),
166                ndt(2015, 9, 8),
167                ndt(2015, 9, 8),
168                ndt(2015, 9, 7)
169            ]
170        );
171    }
172}