Skip to content

Commit

Permalink
RS: add Schedule skeleton (#687)
Browse files Browse the repository at this point in the history
Co-authored-by: JHM Darbyshire (win11) <[email protected]>
  • Loading branch information
attack68 and attack68 authored Feb 20, 2025
1 parent 4cb060b commit 11cd85d
Show file tree
Hide file tree
Showing 7 changed files with 645 additions and 2 deletions.
73 changes: 72 additions & 1 deletion rust/calendars/dateroll.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::cmp::{Ordering, PartialEq};

/// A roll day.
#[pyclass(module = "rateslib.rs")]
#[derive(Copy, Clone)]
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum RollDay {
/// Inherit the day of the input date as the roll.
Unspecified {},
Expand All @@ -22,6 +22,23 @@ pub enum RollDay {
IMM {},
}

// impl RollDay {
// /// Validate whether a given date is a possible variant of the RollDay.
// ///
// /// Returns an error string if invalid or None if it is valid.
// pub(crate) fn validate_date(&self, date: &NaiveDateTime) -> Option<String> {
// match self {
// RollDay::Unspecified{} => None, // any date satisfies unspecified RollDay
// RollDay::Int{day: 31} | RollDay::Eom{} => {
//
// }
// RollDay::IMM {} => if is_imm(date) { None } else {Some("`date` does not align with given `roll`.".to_string())},
// RollDay::Int {day: value} => if date.day() == *value {None} else {Some("`date` does not align with given `roll`.".to_string())}
// RollDay::SoM {} => if date.day() == 1 {None} else {Some("`date` does not align with given `roll`.".to_string())}
// }
// }
// }

/// A rule to adjust a non-business day to a business day.
#[pyclass(module = "rateslib.rs", eq, eq_int)]
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
Expand Down Expand Up @@ -364,6 +381,34 @@ pub fn get_imm(year: i32, month: u32) -> NaiveDateTime {
}
}

/// Test whether a given date is an IMM (third Wednesday).
pub fn is_imm(date: &NaiveDateTime) -> bool {
let imm = get_imm(date.year(), date.month());
*date == imm
}

/// Return an end of month date for given month and year.
pub fn get_eom(year: i32, month: u32) -> NaiveDateTime {
let mut day = 31;
let mut date = NaiveDate::from_ymd_opt(year, month, day);
while date == None {
day = day - 1;
date = NaiveDate::from_ymd_opt(year, month, day);
}
date.unwrap().and_hms_opt(0, 0, 0).unwrap()
}

/// Test whether a given date is EoM.
pub fn is_eom(date: &NaiveDateTime) -> bool {
let eom = get_eom(date.year(), date.month());
*date == eom
}

/// Return if a given year is a leap year.
pub fn is_leap_year(year: i32) -> bool {
NaiveDate::from_ymd_opt(year, 2, 29).is_some()
}

fn roll_with_settlement(
date: &NaiveDateTime,
cal: &dyn DateRoll,
Expand Down Expand Up @@ -765,4 +810,30 @@ mod tests {
);
}
}

#[test]
fn test_is_imm() {
assert_eq!(true, is_imm(&ndt(2025, 3, 19)));
assert_eq!(false, is_imm(&ndt(2025, 3, 18)));
}

#[test]
fn test_get_eom() {
assert_eq!(ndt(2022, 2, 28), get_eom(2022, 2));
assert_eq!(ndt(2024, 2, 29), get_eom(2024, 2));
assert_eq!(ndt(2022, 4, 30), get_eom(2022, 4));
assert_eq!(ndt(2022, 3, 31), get_eom(2022, 3));
}

#[test]
fn test_is_eom() {
assert_eq!(true, is_eom(&ndt(2025, 3, 31)));
assert_eq!(false, is_eom(&ndt(2025, 3, 30)));
}

#[test]
fn test_is_leap() {
assert_eq!(true, is_leap_year(2024));
assert_eq!(false, is_leap_year(2022));
}
}
4 changes: 3 additions & 1 deletion rust/calendars/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,9 @@ pub mod named;
pub use crate::calendars::named::get_calendar_by_name;

mod dateroll;
pub use crate::calendars::dateroll::{get_imm, get_roll, DateRoll, Modifier, RollDay};
pub use crate::calendars::dateroll::{
get_eom, get_imm, get_roll, is_eom, is_imm, is_leap_year, DateRoll, Modifier, RollDay,
};

mod dcfs;
pub use crate::calendars::dcfs::Convention;
Expand Down
2 changes: 2 additions & 0 deletions rust/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ pub mod fx;
use fx::rates::ccy::Ccy;
use fx::rates::{FXRate, FXRates};

pub mod scheduling;

#[pymodule]
fn rs(m: &Bound<'_, PyModule>) -> PyResult<()> {
// JSON
Expand Down
Loading

0 comments on commit 11cd85d

Please sign in to comment.