rateslib/scheduling/calendars/
adjuster.rs

1use crate::scheduling::DateRoll;
2use chrono::prelude::*;
3use chrono::Days;
4use serde::{Deserialize, Serialize};
5
6/// Specifier for date adjustment rules.
7#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
8pub enum Adjuster {
9    /// Actual date without adjustment.
10    Actual {},
11    /// Following adjustment rule.
12    Following {},
13    /// Modified following adjustment rule.
14    ModifiedFollowing {},
15    /// Previous adjustment rule.
16    Previous {},
17    /// Modified previous adjustment rule.
18    ModifiedPrevious {},
19    /// Following adjustment rule, enforcing settlement calendar.
20    FollowingSettle {},
21    /// Modified following adjustment rule, enforcing settlement calendar.
22    ModifiedFollowingSettle {},
23    /// Previous adjustment rule, enforcing settlement calendar.
24    PreviousSettle {},
25    /// Modified previous adjustment rule, enforcing settlement calendar.
26    ModifiedPreviousSettle {},
27    /// A set number of business days, defined by a given calendar,
28    /// using calendar lag rules and enforcing settlement calendars.
29    BusDaysLagSettle(i32),
30    /// A set number of calendar days enforcing settlement calendars, defined by a
31    /// given calendar.
32    CalDaysLagSettle(i32),
33    /// Following adjustment rule except uses actual date for the last date in a vector.
34    FollowingExLast {},
35    /// Following adjustment rule, enforcing settlement, except uses actual date for the last date in a vector.
36    FollowingExLastSettle {},
37    /// A set number of business days, enforcing settlement, except uses the period start date for a vector.
38    BusDaysLagSettleInAdvance(i32),
39}
40
41/// Perform date adjustment according to calendar definitions, i.e. a known [`DateRoll`].
42pub trait Adjustment {
43    /// Adjust a date under an adjustment rule.
44    fn adjust<T: DateRoll>(&self, udate: &NaiveDateTime, calendar: &T) -> NaiveDateTime;
45
46    /// Perform a reverse adjustment to derive potential unadjusted date candidates.
47    fn reverse<T: DateRoll>(&self, adate: &NaiveDateTime, calendar: &T) -> Vec<NaiveDateTime>;
48
49    /// Adjust a vector of dates under an adjustment rule;
50    fn adjusts<T: DateRoll>(&self, udates: &Vec<NaiveDateTime>, calendar: &T)
51        -> Vec<NaiveDateTime>;
52}
53
54/// Perform date adjustment according to adjustment rules, i.e. a given [`Adjuster`].
55pub trait CalendarAdjustment {
56    /// Adjust a date under an adjustment rule.
57    fn adjust(&self, udate: &NaiveDateTime, adjuster: &Adjuster) -> NaiveDateTime
58    where
59        Self: Sized + DateRoll,
60    {
61        adjuster.adjust(udate, self)
62    }
63
64    /// Adjust a vector of dates under an adjustment rule;
65    fn adjusts(&self, udates: &Vec<NaiveDateTime>, adjuster: &Adjuster) -> Vec<NaiveDateTime>
66    where
67        Self: Sized + DateRoll,
68    {
69        adjuster.adjusts(udates, self)
70    }
71}
72
73impl Adjustment for Adjuster {
74    fn adjust<T: DateRoll>(&self, udate: &NaiveDateTime, calendar: &T) -> NaiveDateTime {
75        match self {
76            Adjuster::Actual {} => *udate,
77            Adjuster::Following {} => calendar.roll_forward_bus_day(udate),
78            Adjuster::Previous {} => calendar.roll_backward_bus_day(udate),
79            Adjuster::ModifiedFollowing {} => calendar.roll_mod_forward_bus_day(udate),
80            Adjuster::ModifiedPrevious {} => calendar.roll_mod_backward_bus_day(udate),
81            Adjuster::FollowingSettle {} => calendar.roll_forward_settled_bus_day(udate),
82            Adjuster::PreviousSettle {} => calendar.roll_backward_settled_bus_day(udate),
83            Adjuster::ModifiedFollowingSettle {} => {
84                calendar.roll_forward_mod_settled_bus_day(udate)
85            }
86            Adjuster::ModifiedPreviousSettle {} => {
87                calendar.roll_backward_mod_settled_bus_day(udate)
88            }
89            Adjuster::BusDaysLagSettle(n) => calendar.lag_bus_days(udate, *n, true),
90            Adjuster::CalDaysLagSettle(n) => {
91                let adj = if *n < 0 {
92                    Adjuster::PreviousSettle {}
93                } else {
94                    Adjuster::FollowingSettle {}
95                };
96                calendar.add_cal_days(udate, *n, &adj)
97            }
98            Adjuster::FollowingExLast {} => calendar.roll_forward_bus_day(udate), // no vector
99            Adjuster::FollowingExLastSettle {} => calendar.roll_forward_settled_bus_day(udate), // no vector
100            Adjuster::BusDaysLagSettleInAdvance(n) => calendar.lag_bus_days(udate, *n, true), // no vector
101        }
102    }
103
104    fn reverse<T: DateRoll>(&self, adate: &NaiveDateTime, calendar: &T) -> Vec<NaiveDateTime> {
105        match self {
106            Adjuster::Actual {} => vec![*adate],
107            Adjuster::Following {} => reverse_forward_type(adate, self, calendar),
108            Adjuster::Previous {} => reverse_backward_type(adate, self, calendar),
109            Adjuster::ModifiedFollowing {} => reverse_modified_type(adate, self, calendar),
110            Adjuster::ModifiedPrevious {} => reverse_modified_type(adate, self, calendar),
111            Adjuster::FollowingSettle {} => reverse_forward_type(adate, self, calendar),
112            Adjuster::PreviousSettle {} => reverse_backward_type(adate, self, calendar),
113            Adjuster::ModifiedFollowingSettle {} => reverse_modified_type(adate, self, calendar),
114            Adjuster::ModifiedPreviousSettle {} => reverse_modified_type(adate, self, calendar),
115            Adjuster::BusDaysLagSettle(n) => reverse_lag_settle_type(adate, self, calendar, n),
116            Adjuster::CalDaysLagSettle(n) => reverse_lag_settle_type(adate, self, calendar, n),
117            Adjuster::FollowingExLast {} => reverse_forward_type(adate, self, calendar), // no vector
118            Adjuster::FollowingExLastSettle {} => reverse_forward_type(adate, self, calendar), // no vector
119            Adjuster::BusDaysLagSettleInAdvance(n) => {
120                reverse_lag_settle_type(adate, self, calendar, n)
121            } // no vector
122        }
123    }
124
125    fn adjusts<T: DateRoll>(
126        &self,
127        udates: &Vec<NaiveDateTime>,
128        calendar: &T,
129    ) -> Vec<NaiveDateTime> {
130        let mut non_vector_adates: Vec<NaiveDateTime> = udates
131            .iter()
132            .map(|udate| self.adjust(udate, calendar))
133            .collect();
134
135        // mutate for vector adjustment
136        match self {
137            Adjuster::FollowingExLast {} | Adjuster::FollowingExLastSettle {} => {
138                non_vector_adates[udates.len() - 1] = udates[udates.len() - 1];
139            }
140            Adjuster::BusDaysLagSettleInAdvance(_n) => {
141                for i in (1..udates.len()).rev() {
142                    non_vector_adates[i] = non_vector_adates[i - 1];
143                }
144            }
145            _ => {}
146        }
147        non_vector_adates
148    }
149}
150
151fn reverse_forward_type<T: DateRoll>(
152    adate: &NaiveDateTime,
153    adjuster: &Adjuster,
154    calendar: &T,
155) -> Vec<NaiveDateTime> {
156    let mut ret: Vec<NaiveDateTime>;
157    if (*adjuster).adjust(adate, calendar) == *adate {
158        // adate is valid reversal
159        ret = vec![*adate];
160    } else {
161        // adate is an unadjusted date and is not valid: it has no reversal.
162        return vec![];
163    }
164    let mut date = *adate - Days::new(1);
165    while (*adjuster).adjust(&date, calendar) == *adate {
166        ret.push(date);
167        date = date - Days::new(1);
168    }
169    ret
170}
171
172fn reverse_backward_type<T: DateRoll>(
173    adate: &NaiveDateTime,
174    adjuster: &Adjuster,
175    calendar: &T,
176) -> Vec<NaiveDateTime> {
177    let mut ret: Vec<NaiveDateTime>;
178    if (*adjuster).adjust(adate, calendar) == *adate {
179        // adate is valid reversal
180        ret = vec![*adate];
181    } else {
182        // adate is an unadjusted date and is not valid: it has no reversal.
183        return vec![];
184    }
185    let mut date = *adate + Days::new(1);
186    while (*adjuster).adjust(&date, calendar) == *adate {
187        ret.push(date);
188        date = date + Days::new(1);
189    }
190    ret
191}
192
193fn reverse_modified_type<T: DateRoll>(
194    adate: &NaiveDateTime,
195    adjuster: &Adjuster,
196    calendar: &T,
197) -> Vec<NaiveDateTime> {
198    let mut ret: Vec<NaiveDateTime>;
199    if (*adjuster).adjust(adate, calendar) == *adate {
200        // adate is valid reversal of itself
201        ret = vec![*adate];
202    } else {
203        // adate is an unadjusted date and is not valid: it has no reversal.
204        return vec![];
205    }
206    let mut date = *adate - Days::new(1);
207    let mut adj = (*adjuster).adjust(&date, calendar);
208    while adj == *adate && date.month() == adate.month() {
209        ret.push(date);
210        date = date - Days::new(1);
211        adj = (*adjuster).adjust(&date, calendar);
212    }
213    date = *adate + Days::new(1);
214    adj = (*adjuster).adjust(&date, calendar);
215    while adj == *adate && date.month() == adate.month() {
216        ret.push(date);
217        date = date + Days::new(1);
218        adj = (*adjuster).adjust(&date, calendar);
219    }
220    ret
221}
222
223fn reverse_lag_settle_type<T: DateRoll>(
224    adate: &NaiveDateTime,
225    adjuster: &Adjuster,
226    calendar: &T,
227    n: &i32,
228) -> Vec<NaiveDateTime> {
229    if (Adjuster::FollowingSettle {}).adjust(adate, calendar) != *adate {
230        // input adjusted date has no candidate reversals, return empty vec
231        vec![]
232    } else {
233        // will generally only be necessary when lagging by zero days
234        let mut ret: Vec<NaiveDateTime> = vec![];
235        if (*adjuster).adjust(adate, calendar) == *adate {
236            ret.push(*adate);
237        }
238
239        let mut date = *adate;
240        let mut adj_date: NaiveDateTime;
241        if *n < 0 {
242            loop {
243                date = date + Days::new(1);
244                adj_date = (*adjuster).adjust(&date, calendar);
245                if adj_date == *adate {
246                    ret.push(date);
247                } else if adj_date > *adate {
248                    break;
249                }
250            }
251        } else {
252            loop {
253                date = date - Days::new(1);
254                adj_date = (*adjuster).adjust(&date, calendar);
255                if adj_date == *adate {
256                    ret.push(date);
257                } else if adj_date < *adate {
258                    break;
259                }
260            }
261        }
262        ret
263    }
264}
265
266// UNIT TESTS
267#[cfg(test)]
268mod tests {
269    use super::*;
270    use crate::scheduling::{ndt, Cal, Calendar, UnionCal};
271
272    fn fixture_hol_cal() -> Cal {
273        let hols = vec![ndt(2015, 9, 5), ndt(2015, 9, 7)]; // Saturday and Monday
274        Cal::new(hols, vec![5, 6])
275    }
276
277    #[test]
278    fn test_equality() {
279        assert_eq!(Adjuster::Following {}, Adjuster::Following {});
280        assert_eq!(Adjuster::BusDaysLagSettle(3), Adjuster::BusDaysLagSettle(3));
281        assert_ne!(Adjuster::BusDaysLagSettle(3), Adjuster::BusDaysLagSettle(5));
282    }
283
284    #[test]
285    fn test_adjusts() {
286        let cal = fixture_hol_cal();
287        let udates = vec![
288            ndt(2015, 9, 4),
289            ndt(2015, 9, 5),
290            ndt(2015, 9, 6),
291            ndt(2015, 9, 7),
292        ];
293        let result = Adjuster::Following {}.adjusts(&udates, &cal);
294        assert_eq!(
295            result,
296            vec![
297                ndt(2015, 9, 4),
298                ndt(2015, 9, 8),
299                ndt(2015, 9, 8),
300                ndt(2015, 9, 8)
301            ]
302        );
303    }
304
305    #[test]
306    fn test_adjusts_ex_last() {
307        // the last date in the vector is unadjusted
308        let cal = fixture_hol_cal();
309        let udates = vec![
310            ndt(2015, 9, 4),
311            ndt(2015, 9, 5),
312            ndt(2015, 9, 6),
313            ndt(2015, 9, 7),
314        ];
315        let result = Adjuster::FollowingExLast {}.adjusts(&udates, &cal);
316        assert_eq!(
317            result,
318            vec![
319                ndt(2015, 9, 4),
320                ndt(2015, 9, 8),
321                ndt(2015, 9, 8),
322                ndt(2015, 9, 7)
323            ]
324        );
325    }
326
327    #[test]
328    fn test_adjusts_in_advance() {
329        // the vector is adjusted to in advance
330        let cal = fixture_hol_cal();
331        let udates = vec![
332            ndt(2015, 9, 4),
333            ndt(2015, 9, 5),
334            ndt(2015, 9, 6),
335            ndt(2015, 9, 7),
336        ];
337        let result = Adjuster::BusDaysLagSettleInAdvance(0).adjusts(&udates, &cal);
338        assert_eq!(
339            result,
340            vec![
341                ndt(2015, 9, 4),
342                ndt(2015, 9, 4),
343                ndt(2015, 9, 8),
344                ndt(2015, 9, 8)
345            ]
346        );
347    }
348
349    #[test]
350    fn test_reverse() {
351        let cal = Cal::new(
352            vec![
353                ndt(2000, 1, 31),
354                ndt(2000, 1, 29),
355                ndt(2000, 1, 10),
356                ndt(2000, 1, 11),
357                ndt(2000, 1, 16),
358                ndt(2000, 1, 1),
359                ndt(2000, 1, 3),
360            ],
361            vec![],
362        );
363
364        let options: Vec<(NaiveDateTime, Adjuster, Vec<NaiveDateTime>)> = vec![
365            // No reversals for holidays
366            (ndt(2000, 1, 1), Adjuster::Following {}, vec![]),
367            (ndt(2000, 1, 1), Adjuster::Previous {}, vec![]),
368            (ndt(2000, 1, 1), Adjuster::ModifiedPrevious {}, vec![]),
369            (ndt(2000, 1, 11), Adjuster::CalDaysLagSettle(3), vec![]),
370            (ndt(2000, 1, 11), Adjuster::BusDaysLagSettle(3), vec![]),
371            // Valid reversals for adjusted dates.
372            (
373                ndt(2000, 1, 2),
374                Adjuster::Following {},
375                vec![ndt(2000, 1, 2), ndt(2000, 1, 1)],
376            ),
377            (
378                ndt(2000, 1, 30),
379                Adjuster::Following {},
380                vec![ndt(2000, 1, 30), ndt(2000, 1, 29)],
381            ),
382            (
383                ndt(2000, 1, 2),
384                Adjuster::FollowingSettle {},
385                vec![ndt(2000, 1, 2), ndt(2000, 1, 1)],
386            ),
387            (
388                ndt(2000, 1, 30),
389                Adjuster::FollowingSettle {},
390                vec![ndt(2000, 1, 30), ndt(2000, 1, 29)],
391            ),
392            (
393                ndt(2000, 1, 2),
394                Adjuster::Previous {},
395                vec![ndt(2000, 1, 2), ndt(2000, 1, 3)],
396            ),
397            (
398                ndt(2000, 1, 30),
399                Adjuster::Previous {},
400                vec![ndt(2000, 1, 30), ndt(2000, 1, 31)],
401            ),
402            (
403                ndt(2000, 1, 2),
404                Adjuster::PreviousSettle {},
405                vec![ndt(2000, 1, 2), ndt(2000, 1, 3)],
406            ),
407            (
408                ndt(2000, 1, 30),
409                Adjuster::PreviousSettle {},
410                vec![ndt(2000, 1, 30), ndt(2000, 1, 31)],
411            ),
412            (
413                ndt(2000, 1, 2),
414                Adjuster::FollowingExLast {},
415                vec![ndt(2000, 1, 2), ndt(2000, 1, 1)],
416            ),
417            (
418                ndt(2000, 1, 30),
419                Adjuster::FollowingExLast {},
420                vec![ndt(2000, 1, 30), ndt(2000, 1, 29)],
421            ),
422            (
423                ndt(2000, 1, 2),
424                Adjuster::FollowingExLastSettle {},
425                vec![ndt(2000, 1, 2), ndt(2000, 1, 1)],
426            ),
427            (
428                ndt(2000, 1, 30),
429                Adjuster::FollowingExLastSettle {},
430                vec![ndt(2000, 1, 30), ndt(2000, 1, 29)],
431            ),
432            (
433                ndt(2000, 1, 2),
434                Adjuster::ModifiedFollowing {},
435                vec![ndt(2000, 1, 2), ndt(2000, 1, 1)],
436            ),
437            (
438                ndt(2000, 1, 30),
439                Adjuster::ModifiedFollowing {},
440                vec![ndt(2000, 1, 30), ndt(2000, 1, 29), ndt(2000, 1, 31)],
441            ),
442            (
443                ndt(2000, 1, 2),
444                Adjuster::ModifiedPrevious {},
445                vec![ndt(2000, 1, 2), ndt(2000, 1, 1), ndt(2000, 1, 3)],
446            ),
447            (
448                ndt(2000, 1, 30),
449                Adjuster::ModifiedPrevious {},
450                vec![ndt(2000, 1, 30), ndt(2000, 1, 31)],
451            ),
452            (
453                ndt(2000, 1, 2),
454                Adjuster::ModifiedFollowingSettle {},
455                vec![ndt(2000, 1, 2), ndt(2000, 1, 1)],
456            ),
457            (
458                ndt(2000, 1, 30),
459                Adjuster::ModifiedFollowingSettle {},
460                vec![ndt(2000, 1, 30), ndt(2000, 1, 29), ndt(2000, 1, 31)],
461            ),
462            (
463                ndt(2000, 1, 2),
464                Adjuster::ModifiedPreviousSettle {},
465                vec![ndt(2000, 1, 2), ndt(2000, 1, 1), ndt(2000, 1, 3)],
466            ),
467            (
468                ndt(2000, 1, 30),
469                Adjuster::ModifiedPreviousSettle {},
470                vec![ndt(2000, 1, 30), ndt(2000, 1, 31)],
471            ),
472            (ndt(2000, 1, 2), Adjuster::Actual {}, vec![ndt(2000, 1, 2)]),
473            (
474                ndt(2000, 1, 30),
475                Adjuster::Actual {},
476                vec![ndt(2000, 1, 30)],
477            ),
478            (
479                ndt(2000, 1, 30),
480                Adjuster::Actual {},
481                vec![ndt(2000, 1, 30)],
482            ),
483            (
484                ndt(2000, 1, 15),
485                Adjuster::CalDaysLagSettle(5),
486                vec![ndt(2000, 1, 10)],
487            ),
488            (
489                ndt(2000, 1, 12),
490                Adjuster::CalDaysLagSettle(5),
491                vec![ndt(2000, 1, 7), ndt(2000, 1, 6), ndt(2000, 1, 5)],
492            ),
493            (
494                ndt(2000, 1, 18),
495                Adjuster::BusDaysLagSettle(5),
496                vec![ndt(2000, 1, 12)],
497            ),
498            (
499                ndt(2000, 1, 17),
500                Adjuster::BusDaysLagSettle(5),
501                vec![ndt(2000, 1, 11), ndt(2000, 1, 10), ndt(2000, 1, 9)],
502            ),
503        ];
504        for option in options {
505            let result = option.1.reverse(&option.0, &Calendar::Cal(cal.clone()));
506            assert_eq!(result, option.2)
507        }
508    }
509
510    #[test]
511    fn test_forward_book_reverse() {
512        // Test Following and FollowingSettle from the book diagram
513        let cal = Cal::new(vec![ndt(2000, 6, 27), ndt(2000, 6, 30)], vec![]);
514        let settle = Cal::new(
515            vec![
516                ndt(2000, 6, 26),
517                ndt(2000, 6, 29),
518                ndt(2000, 6, 30),
519                ndt(2000, 7, 1),
520            ],
521            vec![],
522        );
523        let uni = UnionCal::new(vec![cal], Some(vec![settle]));
524
525        // adjustments for a Following Adjuster
526        let options: Vec<(NaiveDateTime, NaiveDateTime)> = vec![
527            (ndt(2000, 6, 26), ndt(2000, 6, 26)),
528            (ndt(2000, 6, 27), ndt(2000, 6, 28)),
529            (ndt(2000, 6, 28), ndt(2000, 6, 28)),
530            (ndt(2000, 6, 29), ndt(2000, 6, 29)),
531            (ndt(2000, 6, 30), ndt(2000, 7, 1)),
532            (ndt(2000, 7, 1), ndt(2000, 7, 1)),
533            (ndt(2000, 7, 2), ndt(2000, 7, 2)),
534        ];
535        for option in options {
536            let result = Adjuster::Following {}.adjust(&option.0, &Calendar::UnionCal(uni.clone()));
537            assert_eq!(result, option.1)
538        }
539
540        // reversals for a Following Adjuster
541        let options: Vec<(NaiveDateTime, Vec<NaiveDateTime>)> = vec![
542            (ndt(2000, 6, 26), vec![ndt(2000, 6, 26)]),
543            (ndt(2000, 6, 27), vec![]),
544            (ndt(2000, 6, 28), vec![ndt(2000, 6, 28), ndt(2000, 6, 27)]),
545            (ndt(2000, 6, 29), vec![ndt(2000, 6, 29)]),
546            (ndt(2000, 6, 30), vec![]),
547            (ndt(2000, 7, 1), vec![ndt(2000, 7, 1), ndt(2000, 6, 30)]),
548            (ndt(2000, 7, 2), vec![ndt(2000, 7, 2)]),
549        ];
550        for option in options {
551            let result =
552                Adjuster::Following {}.reverse(&option.0, &Calendar::UnionCal(uni.clone()));
553            assert_eq!(result, option.1)
554        }
555
556        // adjustments for a FollowingSettle Adjuster
557        let options: Vec<(NaiveDateTime, NaiveDateTime)> = vec![
558            (ndt(2000, 6, 26), ndt(2000, 6, 28)),
559            (ndt(2000, 6, 27), ndt(2000, 6, 28)),
560            (ndt(2000, 6, 28), ndt(2000, 6, 28)),
561            (ndt(2000, 6, 29), ndt(2000, 7, 2)),
562            (ndt(2000, 6, 30), ndt(2000, 7, 2)),
563            (ndt(2000, 7, 1), ndt(2000, 7, 2)),
564            (ndt(2000, 7, 2), ndt(2000, 7, 2)),
565        ];
566        for option in options {
567            let result =
568                Adjuster::FollowingSettle {}.adjust(&option.0, &Calendar::UnionCal(uni.clone()));
569            assert_eq!(result, option.1)
570        }
571
572        // reversals for a FollowingSettle Adjuster
573        let options: Vec<(NaiveDateTime, Vec<NaiveDateTime>)> = vec![
574            (ndt(2000, 6, 26), vec![]),
575            (ndt(2000, 6, 27), vec![]),
576            (
577                ndt(2000, 6, 28),
578                vec![ndt(2000, 6, 28), ndt(2000, 6, 27), ndt(2000, 6, 26)],
579            ),
580            (ndt(2000, 6, 29), vec![]),
581            (ndt(2000, 6, 30), vec![]),
582            (ndt(2000, 7, 1), vec![]),
583            (
584                ndt(2000, 7, 2),
585                vec![
586                    ndt(2000, 7, 2),
587                    ndt(2000, 7, 1),
588                    ndt(2000, 6, 30),
589                    ndt(2000, 6, 29),
590                ],
591            ),
592        ];
593        for option in options {
594            let result =
595                Adjuster::FollowingSettle {}.reverse(&option.0, &Calendar::UnionCal(uni.clone()));
596            assert_eq!(result, option.1)
597        }
598    }
599
600    #[test]
601    fn test_backward_book_reverse() {
602        // Test Previous and PreviousSettle from the book diagram
603        let cal = Cal::new(vec![ndt(2000, 6, 27), ndt(2000, 6, 30)], vec![]);
604        let settle = Cal::new(
605            vec![
606                ndt(2000, 6, 26),
607                ndt(2000, 6, 29),
608                ndt(2000, 6, 30),
609                ndt(2000, 7, 1),
610            ],
611            vec![],
612        );
613        let uni = UnionCal::new(vec![cal], Some(vec![settle]));
614
615        // adjustments for a Previous Adjuster
616        let options: Vec<(NaiveDateTime, NaiveDateTime)> = vec![
617            (ndt(2000, 6, 26), ndt(2000, 6, 26)),
618            (ndt(2000, 6, 27), ndt(2000, 6, 26)),
619            (ndt(2000, 6, 28), ndt(2000, 6, 28)),
620            (ndt(2000, 6, 29), ndt(2000, 6, 29)),
621            (ndt(2000, 6, 30), ndt(2000, 6, 29)),
622            (ndt(2000, 7, 1), ndt(2000, 7, 1)),
623            (ndt(2000, 7, 2), ndt(2000, 7, 2)),
624        ];
625        for option in options {
626            let result = Adjuster::Previous {}.adjust(&option.0, &Calendar::UnionCal(uni.clone()));
627            assert_eq!(result, option.1)
628        }
629
630        // reversals for a Previous Adjuster
631        let options: Vec<(NaiveDateTime, Vec<NaiveDateTime>)> = vec![
632            (ndt(2000, 6, 26), vec![ndt(2000, 6, 26), ndt(2000, 6, 27)]),
633            (ndt(2000, 6, 27), vec![]),
634            (ndt(2000, 6, 28), vec![ndt(2000, 6, 28)]),
635            (ndt(2000, 6, 29), vec![ndt(2000, 6, 29), ndt(2000, 6, 30)]),
636            (ndt(2000, 6, 30), vec![]),
637            (ndt(2000, 7, 1), vec![ndt(2000, 7, 1)]),
638            (ndt(2000, 7, 2), vec![ndt(2000, 7, 2)]),
639        ];
640        for option in options {
641            let result = Adjuster::Previous {}.reverse(&option.0, &Calendar::UnionCal(uni.clone()));
642            assert_eq!(result, option.1)
643        }
644
645        // adjustments for a PreviousSettle Adjuster
646        let options: Vec<(NaiveDateTime, NaiveDateTime)> = vec![
647            (ndt(2000, 6, 26), ndt(2000, 6, 25)),
648            (ndt(2000, 6, 27), ndt(2000, 6, 25)),
649            (ndt(2000, 6, 28), ndt(2000, 6, 28)),
650            (ndt(2000, 6, 29), ndt(2000, 6, 28)),
651            (ndt(2000, 6, 30), ndt(2000, 6, 28)),
652            (ndt(2000, 7, 1), ndt(2000, 6, 28)),
653            (ndt(2000, 7, 2), ndt(2000, 7, 2)),
654        ];
655        for option in options {
656            let result =
657                Adjuster::PreviousSettle {}.adjust(&option.0, &Calendar::UnionCal(uni.clone()));
658            assert_eq!(result, option.1)
659        }
660
661        // reversals for a PreviousSettle Adjuster
662        let options: Vec<(NaiveDateTime, Vec<NaiveDateTime>)> = vec![
663            (
664                ndt(2000, 6, 25),
665                vec![ndt(2000, 6, 25), ndt(2000, 6, 26), ndt(2000, 6, 27)],
666            ),
667            (ndt(2000, 6, 26), vec![]),
668            (ndt(2000, 6, 27), vec![]),
669            (
670                ndt(2000, 6, 28),
671                vec![
672                    ndt(2000, 6, 28),
673                    ndt(2000, 6, 29),
674                    ndt(2000, 6, 30),
675                    ndt(2000, 7, 1),
676                ],
677            ),
678            (ndt(2000, 6, 29), vec![]),
679            (ndt(2000, 6, 30), vec![]),
680            (ndt(2000, 7, 1), vec![]),
681            (ndt(2000, 7, 2), vec![ndt(2000, 7, 2)]),
682        ];
683        for option in options {
684            let result =
685                Adjuster::PreviousSettle {}.reverse(&option.0, &Calendar::UnionCal(uni.clone()));
686            assert_eq!(result, option.1)
687        }
688    }
689
690    #[test]
691    fn test_modified_forward_book_reverse() {
692        // Test ModifiedFollowing and ModifiedFollowingSettle from the book diagram
693        let cal = Cal::new(vec![ndt(2000, 6, 27), ndt(2000, 6, 30)], vec![]);
694        let settle = Cal::new(
695            vec![
696                ndt(2000, 6, 26),
697                ndt(2000, 6, 29),
698                ndt(2000, 6, 30),
699                ndt(2000, 7, 1),
700            ],
701            vec![],
702        );
703        let uni = UnionCal::new(vec![cal], Some(vec![settle]));
704
705        // adjustments for a ModifiedFollowing Adjuster
706        let options: Vec<(NaiveDateTime, NaiveDateTime)> = vec![
707            (ndt(2000, 6, 26), ndt(2000, 6, 26)),
708            (ndt(2000, 6, 27), ndt(2000, 6, 28)),
709            (ndt(2000, 6, 28), ndt(2000, 6, 28)),
710            (ndt(2000, 6, 29), ndt(2000, 6, 29)),
711            (ndt(2000, 6, 30), ndt(2000, 6, 29)),
712            (ndt(2000, 7, 1), ndt(2000, 7, 1)),
713            (ndt(2000, 7, 2), ndt(2000, 7, 2)),
714        ];
715        for option in options {
716            let result =
717                Adjuster::ModifiedFollowing {}.adjust(&option.0, &Calendar::UnionCal(uni.clone()));
718            assert_eq!(result, option.1)
719        }
720
721        // reversals for a ModifiedFollowing Adjuster
722        let options: Vec<(NaiveDateTime, Vec<NaiveDateTime>)> = vec![
723            (ndt(2000, 6, 26), vec![ndt(2000, 6, 26)]),
724            (ndt(2000, 6, 27), vec![]),
725            (ndt(2000, 6, 28), vec![ndt(2000, 6, 28), ndt(2000, 6, 27)]),
726            (ndt(2000, 6, 29), vec![ndt(2000, 6, 29), ndt(2000, 6, 30)]),
727            (ndt(2000, 6, 30), vec![]),
728            (ndt(2000, 7, 1), vec![ndt(2000, 7, 1)]),
729            (ndt(2000, 7, 2), vec![ndt(2000, 7, 2)]),
730        ];
731        for option in options {
732            let result =
733                Adjuster::ModifiedFollowing {}.reverse(&option.0, &Calendar::UnionCal(uni.clone()));
734            assert_eq!(result, option.1)
735        }
736
737        // adjustments for a ModifiedFollowingSettle Adjuster
738        let options: Vec<(NaiveDateTime, NaiveDateTime)> = vec![
739            (ndt(2000, 6, 26), ndt(2000, 6, 28)),
740            (ndt(2000, 6, 27), ndt(2000, 6, 28)),
741            (ndt(2000, 6, 28), ndt(2000, 6, 28)),
742            (ndt(2000, 6, 29), ndt(2000, 6, 28)),
743            (ndt(2000, 6, 30), ndt(2000, 6, 28)),
744            (ndt(2000, 7, 1), ndt(2000, 7, 2)),
745            (ndt(2000, 7, 2), ndt(2000, 7, 2)),
746        ];
747        for option in options {
748            let result = Adjuster::ModifiedFollowingSettle {}
749                .adjust(&option.0, &Calendar::UnionCal(uni.clone()));
750            assert_eq!(result, option.1)
751        }
752
753        // reversals for a ModifiedFollowingSettle Adjuster
754        let options: Vec<(NaiveDateTime, Vec<NaiveDateTime>)> = vec![
755            (ndt(2000, 6, 26), vec![]),
756            (ndt(2000, 6, 27), vec![]),
757            (
758                ndt(2000, 6, 28),
759                vec![
760                    ndt(2000, 6, 28),
761                    ndt(2000, 6, 27),
762                    ndt(2000, 6, 26),
763                    ndt(2000, 6, 29),
764                    ndt(2000, 6, 30),
765                ],
766            ),
767            (ndt(2000, 6, 29), vec![]),
768            (ndt(2000, 6, 30), vec![]),
769            (ndt(2000, 7, 1), vec![]),
770            (ndt(2000, 7, 2), vec![ndt(2000, 7, 2), ndt(2000, 7, 1)]),
771        ];
772        for option in options {
773            let result = Adjuster::ModifiedFollowingSettle {}
774                .reverse(&option.0, &Calendar::UnionCal(uni.clone()));
775            assert_eq!(result, option.1)
776        }
777    }
778
779    #[test]
780    fn test_modified_backward_book_reverse() {
781        // Test ModifiedPrevious and ModifiedPreviousSettle from the book diagram
782        let cal = Cal::new(vec![ndt(2000, 6, 27), ndt(2000, 6, 30)], vec![]);
783        let settle = Cal::new(
784            vec![
785                ndt(2000, 6, 26),
786                ndt(2000, 6, 29),
787                ndt(2000, 6, 30),
788                ndt(2000, 7, 1),
789            ],
790            vec![],
791        );
792        let uni = UnionCal::new(vec![cal], Some(vec![settle]));
793
794        // adjustments for a ModifiedPrevious Adjuster
795        let options: Vec<(NaiveDateTime, NaiveDateTime)> = vec![
796            (ndt(2000, 6, 26), ndt(2000, 6, 26)),
797            (ndt(2000, 6, 27), ndt(2000, 6, 26)),
798            (ndt(2000, 6, 28), ndt(2000, 6, 28)),
799            (ndt(2000, 6, 29), ndt(2000, 6, 29)),
800            (ndt(2000, 6, 30), ndt(2000, 6, 29)),
801            (ndt(2000, 7, 1), ndt(2000, 7, 1)),
802            (ndt(2000, 7, 2), ndt(2000, 7, 2)),
803        ];
804        for option in options {
805            let result =
806                Adjuster::ModifiedPrevious {}.adjust(&option.0, &Calendar::UnionCal(uni.clone()));
807            assert_eq!(result, option.1)
808        }
809
810        // reversals for a ModifiedPrevious Adjuster
811        let options: Vec<(NaiveDateTime, Vec<NaiveDateTime>)> = vec![
812            (ndt(2000, 6, 25), vec![ndt(2000, 6, 25)]),
813            (ndt(2000, 6, 26), vec![ndt(2000, 6, 26), ndt(2000, 6, 27)]),
814            (ndt(2000, 6, 27), vec![]),
815            (ndt(2000, 6, 28), vec![ndt(2000, 6, 28)]),
816            (ndt(2000, 6, 29), vec![ndt(2000, 6, 29), ndt(2000, 6, 30)]),
817            (ndt(2000, 6, 30), vec![]),
818            (ndt(2000, 7, 1), vec![ndt(2000, 7, 1)]),
819            (ndt(2000, 7, 2), vec![ndt(2000, 7, 2)]),
820        ];
821        for option in options {
822            let result =
823                Adjuster::ModifiedPrevious {}.reverse(&option.0, &Calendar::UnionCal(uni.clone()));
824            assert_eq!(result, option.1)
825        }
826
827        // adjustments for a ModifiedPreviousSettle Adjuster
828        let options: Vec<(NaiveDateTime, NaiveDateTime)> = vec![
829            (ndt(2000, 6, 26), ndt(2000, 6, 25)),
830            (ndt(2000, 6, 27), ndt(2000, 6, 25)),
831            (ndt(2000, 6, 28), ndt(2000, 6, 28)),
832            (ndt(2000, 6, 29), ndt(2000, 6, 28)),
833            (ndt(2000, 6, 30), ndt(2000, 6, 28)),
834            (ndt(2000, 7, 1), ndt(2000, 7, 2)),
835            (ndt(2000, 7, 2), ndt(2000, 7, 2)),
836        ];
837        for option in options {
838            let result = Adjuster::ModifiedPreviousSettle {}
839                .adjust(&option.0, &Calendar::UnionCal(uni.clone()));
840            assert_eq!(result, option.1)
841        }
842
843        // reversals for a ModifiedPreviousSettle Adjuster
844        let options: Vec<(NaiveDateTime, Vec<NaiveDateTime>)> = vec![
845            (
846                ndt(2000, 6, 25),
847                vec![ndt(2000, 6, 25), ndt(2000, 6, 26), ndt(2000, 6, 27)],
848            ),
849            (ndt(2000, 6, 26), vec![]),
850            (ndt(2000, 6, 27), vec![]),
851            (
852                ndt(2000, 6, 28),
853                vec![ndt(2000, 6, 28), ndt(2000, 6, 29), ndt(2000, 6, 30)],
854            ),
855            (ndt(2000, 6, 29), vec![]),
856            (ndt(2000, 6, 30), vec![]),
857            (ndt(2000, 7, 1), vec![]),
858            (ndt(2000, 7, 2), vec![ndt(2000, 7, 2), ndt(2000, 7, 1)]),
859        ];
860        for option in options {
861            let result = Adjuster::ModifiedPreviousSettle {}
862                .reverse(&option.0, &Calendar::UnionCal(uni.clone()));
863            assert_eq!(result, option.1)
864        }
865    }
866
867    #[test]
868    fn test_bus_days_lag_settle_reverse() {
869        // Test BusDaysLagSettle(2) from the book diagram
870        let cal = Cal::new(vec![ndt(2000, 6, 27), ndt(2000, 6, 30)], vec![]);
871        let settle = Cal::new(
872            vec![
873                ndt(2000, 6, 26),
874                ndt(2000, 6, 29),
875                ndt(2000, 6, 30),
876                ndt(2000, 7, 1),
877            ],
878            vec![],
879        );
880        let uni = UnionCal::new(vec![cal], Some(vec![settle]));
881
882        // adjustments for a BusDaysLagSettle(2) Adjuster
883        let options: Vec<(NaiveDateTime, NaiveDateTime)> = vec![
884            (ndt(2000, 6, 25), ndt(2000, 6, 28)),
885            (ndt(2000, 6, 26), ndt(2000, 7, 2)),
886            (ndt(2000, 6, 27), ndt(2000, 7, 2)),
887            (ndt(2000, 6, 28), ndt(2000, 7, 2)),
888            (ndt(2000, 6, 29), ndt(2000, 7, 2)),
889            (ndt(2000, 6, 30), ndt(2000, 7, 2)),
890        ];
891        for option in options {
892            let result =
893                Adjuster::BusDaysLagSettle(2).adjust(&option.0, &Calendar::UnionCal(uni.clone()));
894            assert_eq!(result, option.1)
895        }
896
897        // reversal for a BusDaysLagSettle(2) Adjuster
898        let options: Vec<(NaiveDateTime, Vec<NaiveDateTime>)> = vec![
899            (ndt(2000, 6, 28), vec![ndt(2000, 6, 25), ndt(2000, 6, 24)]),
900            (ndt(2000, 6, 29), vec![]),
901            (ndt(2000, 6, 30), vec![]),
902            (ndt(2000, 7, 1), vec![]),
903            (
904                ndt(2000, 7, 2),
905                vec![
906                    ndt(2000, 6, 30),
907                    ndt(2000, 6, 29),
908                    ndt(2000, 6, 28),
909                    ndt(2000, 6, 27),
910                    ndt(2000, 6, 26),
911                ],
912            ),
913        ];
914        for option in options {
915            let result =
916                Adjuster::BusDaysLagSettle(2).reverse(&option.0, &Calendar::UnionCal(uni.clone()));
917            assert_eq!(result, option.1)
918        }
919
920        // adjustments for a BusDaysLagSettle(1) Adjuster
921        let options: Vec<(NaiveDateTime, NaiveDateTime)> = vec![
922            (ndt(2000, 6, 25), ndt(2000, 6, 28)),
923            (ndt(2000, 6, 26), ndt(2000, 6, 28)),
924            (ndt(2000, 6, 27), ndt(2000, 6, 28)),
925            (ndt(2000, 6, 28), ndt(2000, 7, 2)),
926            (ndt(2000, 6, 29), ndt(2000, 7, 2)),
927            (ndt(2000, 6, 30), ndt(2000, 7, 2)),
928            (ndt(2000, 7, 1), ndt(2000, 7, 2)),
929        ];
930        for option in options {
931            let result =
932                Adjuster::BusDaysLagSettle(1).adjust(&option.0, &Calendar::UnionCal(uni.clone()));
933            assert_eq!(result, option.1)
934        }
935
936        // reversal for a BusDaysLagSettle(1) Adjuster
937        let options: Vec<(NaiveDateTime, Vec<NaiveDateTime>)> = vec![
938            (
939                ndt(2000, 6, 28),
940                vec![ndt(2000, 6, 27), ndt(2000, 6, 26), ndt(2000, 6, 25)],
941            ),
942            (ndt(2000, 6, 29), vec![]),
943            (ndt(2000, 6, 30), vec![]),
944            (ndt(2000, 7, 1), vec![]),
945            (
946                ndt(2000, 7, 2),
947                vec![
948                    ndt(2000, 7, 1),
949                    ndt(2000, 6, 30),
950                    ndt(2000, 6, 29),
951                    ndt(2000, 6, 28),
952                ],
953            ),
954        ];
955        for option in options {
956            let result =
957                Adjuster::BusDaysLagSettle(1).reverse(&option.0, &Calendar::UnionCal(uni.clone()));
958            assert_eq!(result, option.1)
959        }
960
961        // adjustments for a BusDaysLagSettle(0) Adjuster
962        let options: Vec<(NaiveDateTime, NaiveDateTime)> = vec![
963            (ndt(2000, 6, 25), ndt(2000, 6, 25)),
964            (ndt(2000, 6, 26), ndt(2000, 6, 28)),
965            (ndt(2000, 6, 27), ndt(2000, 6, 28)),
966            (ndt(2000, 6, 28), ndt(2000, 6, 28)),
967            (ndt(2000, 6, 29), ndt(2000, 7, 2)),
968            (ndt(2000, 6, 30), ndt(2000, 7, 2)),
969            (ndt(2000, 7, 1), ndt(2000, 7, 2)),
970            (ndt(2000, 7, 2), ndt(2000, 7, 2)),
971        ];
972        for option in options {
973            let result =
974                Adjuster::BusDaysLagSettle(0).adjust(&option.0, &Calendar::UnionCal(uni.clone()));
975            assert_eq!(result, option.1)
976        }
977
978        // reversal for a BusDaysLagSettle(0) Adjuster
979        let options: Vec<(NaiveDateTime, Vec<NaiveDateTime>)> = vec![
980            (ndt(2000, 6, 25), vec![ndt(2000, 6, 25)]),
981            (
982                ndt(2000, 6, 28),
983                vec![ndt(2000, 6, 28), ndt(2000, 6, 27), ndt(2000, 6, 26)],
984            ),
985            (ndt(2000, 6, 29), vec![]),
986            (ndt(2000, 6, 30), vec![]),
987            (ndt(2000, 7, 1), vec![]),
988            (
989                ndt(2000, 7, 2),
990                vec![
991                    ndt(2000, 7, 2),
992                    ndt(2000, 7, 1),
993                    ndt(2000, 6, 30),
994                    ndt(2000, 6, 29),
995                ],
996            ),
997        ];
998        for option in options {
999            let result =
1000                Adjuster::BusDaysLagSettle(0).reverse(&option.0, &Calendar::UnionCal(uni.clone()));
1001            assert_eq!(result, option.1)
1002        }
1003    }
1004}