Skip to content

Commit

Permalink
chore: #142 - Add calendars (Argentina, Botswana, Brazil, Chile, China)
Browse files Browse the repository at this point in the history
  • Loading branch information
avhz committed Nov 12, 2023
1 parent 48d8925 commit 0350a53
Show file tree
Hide file tree
Showing 42 changed files with 476 additions and 57 deletions.
52 changes: 37 additions & 15 deletions src/time/calendar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
// - LICENSE-MIT.md
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

use time::OffsetDateTime;
use time::{Month, OffsetDateTime, Weekday};

/// Calendar trait.
/// The calendars follow generic settlement rules, not the exchange holiday rules.
Expand All @@ -17,22 +17,44 @@ pub trait Calendar {

/// Check if the date is a business day.
fn is_business_day(&self, date: OffsetDateTime) -> bool;

/// Unpacks an OffsetDateTime into a tuple in the following form:
///
/// ```ignore
/// (
/// weekday,
/// day_of_month,
/// month,
/// year,
/// day_of_year
/// )
/// ```
///
fn unpack_date(&self, date: OffsetDateTime) -> (Weekday, u8, Month, i32, u16) {
let w = date.weekday(); // Weekday (Monday-Sunday)
let d = date.day(); // Day of month (1-31)
let m = date.month(); // Month (January-December)
let y = date.year(); // Year
let dd = date.ordinal(); // Day of year (1-365)

(w, d, m, y, dd)
}

/// Returns the Easter Monday for the given year.
fn easter_monday(year: usize, is_orthodox: bool) -> u16 {
let index = if is_orthodox { 1 } else { 0 };

super::EASTER_MONDAYS[index][year - 1901]
}

/// Checks if date is a weekend.
fn is_weekend(date: OffsetDateTime) -> bool {
let w = date.weekday();

w == time::Weekday::Saturday || w == time::Weekday::Sunday
}
}

/// Holiday type.
/// This simply returns the name of the holiday.
pub struct Holiday(pub &'static str);

/// Returns the Easter Monday for the given year.
pub fn easter_monday(year: usize, is_orthodox: bool) -> u16 {
let index = if is_orthodox { 1 } else { 0 };

super::EASTER_MONDAYS[index][year - 1901]
}

/// Checks if date is a weekend.
pub fn is_weekend(date: OffsetDateTime) -> bool {
let w = date.weekday();

w == time::Weekday::Saturday || w == time::Weekday::Sunday
}
57 changes: 57 additions & 0 deletions src/time/calendars/argentina.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// RustQuant: A Rust library for quantitative finance tools.
// Copyright (C) 2023 https://github.com/avhz
// Dual licensed under Apache 2.0 and MIT.
// See:
// - LICENSE-APACHE.md
// - LICENSE-MIT.md
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

use crate::time::Calendar;
use time::{Month, OffsetDateTime, Weekday};

/// Argentina calendar.
pub struct Argentina;

impl Calendar for Argentina {
fn name(&self) -> &'static str {
"Argentina"
}

fn is_business_day(&self, date: OffsetDateTime) -> bool {
let (w, d, m, y, dd) = self.unpack_date(date);
let em = Self::easter_monday(y as usize, false);

if Self::is_weekend(date)
// New Year's Day
|| (d == 1 && m == Month::January)
// Holy Thursday
|| (dd == em-4)
// Good Friday
|| (dd == em-3)
// Labour Day
|| (d == 1 && m == Month::May)
// May Revolution
|| (d == 25 && m == Month::May)
// Death of General Manuel Belgrano
|| (d >= 15 && d <= 21 && w == Weekday::Monday && m == Month::June)
// Independence Day
|| (d == 9 && m == Month::July)
// Death of General José de San Martín
|| (d >= 15 && d <= 21 && w ==Weekday::Monday && m == Month::August)
// Columbus Day
|| ((d == 10 || d == 11 || d == 12 || d == 15 || d == 16)
&& w == Weekday::Monday && m == Month::October)
// Immaculate Conception
|| (d == 8 && m == Month::December)
// Christmas Eve
|| (d == 24 && m == Month::December)
// New Year's Eve
|| ((d == 31 || (d == 30 && w == Weekday::Friday)) && m == Month::December)
{
return false;
}

return true;
}
}
13 changes: 4 additions & 9 deletions src/time/calendars/australia.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
// - LICENSE-MIT.md
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

use crate::time::{is_weekend, Calendar};
use crate::time::Calendar;
use time::{Month, OffsetDateTime, Weekday};

/// Australiann settlement calendar.
Expand All @@ -20,15 +20,10 @@ impl Calendar for Australia {
}

fn is_business_day(&self, date: OffsetDateTime) -> bool {
let w = date.weekday();
let d = date.day();
let m = date.month();
let y = date.year();
let dd = date.ordinal(); // Day of the year
let (w, d, m, y, dd) = self.unpack_date(date);
let em = Self::easter_monday(y as usize, false);

let em = crate::time::easter_monday(y as usize, false);

if is_weekend(date)
if Self::is_weekend(date)
// New Year's Day (possibly moved to Monday)
|| ((d == 1 || ((d == 2 || d == 3) && w == Weekday::Monday)) && m == Month::January)
// Australia Day, January 26th (possibly moved to Monday)
Expand Down
12 changes: 4 additions & 8 deletions src/time/calendars/austria.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
// - LICENSE-MIT.md
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

use crate::time::{is_weekend, Calendar};
use crate::time::Calendar;
use time::{Month, OffsetDateTime};

/// Austrian settlement calendar.
Expand All @@ -19,14 +19,10 @@ impl Calendar for Austria {
}

fn is_business_day(&self, date: OffsetDateTime) -> bool {
let d = date.day();
let m = date.month();
let y = date.year();
let dd = date.ordinal(); // Day of the year
let (w, d, m, y, dd) = self.unpack_date(date);
let em = Self::easter_monday(y as usize, false);

let em = crate::time::easter_monday(y as usize, false);

if is_weekend(date)
if Self::is_weekend(date)
// New Year's Day
|| (d == 1 && m == Month::January)
// Epiphany
Expand Down
60 changes: 60 additions & 0 deletions src/time/calendars/botswana.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// RustQuant: A Rust library for quantitative finance tools.
// Copyright (C) 2023 https://github.com/avhz
// Dual licensed under Apache 2.0 and MIT.
// See:
// - LICENSE-APACHE.md
// - LICENSE-MIT.md
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

use crate::time::Calendar;
use time::{Month, OffsetDateTime, Weekday};

/// Botswana calendar.
pub struct Botswana;

impl Calendar for Botswana {
fn name(&self) -> &'static str {
"Botswana"
}

fn is_business_day(&self, date: OffsetDateTime) -> bool {
let (w, d, m, y, dd) = self.unpack_date(date);
let em = Self::easter_monday(y as usize, false);

if Self::is_weekend(date)
// New Year's Day (possibly moved to Monday or Tuesday)
|| ((d == 1 || (d == 2 && w == Weekday::Monday) || (d == 3 && w == Weekday::Tuesday))
&& m == Month::January)
// Good Friday
|| (dd == em - 3)
// Easter Monday
|| (dd == em)
// Labour Day, May 1st (possibly moved to Monday)
|| ((d == 1 || (d == 2 && w == Weekday::Monday))
&& m == Month::May)
// Ascension
|| (dd == em + 38)
// Sir Seretse Khama Day, July 1st (possibly moved to Monday)
|| ((d == 1 || (d == 2 && w == Weekday::Monday))
&& m == Month::July)
// Presidents' Day (third Monday of July)
|| ((d >= 15 && d <= 21) && w == Weekday::Monday && m == Month::July)
// Independence Day, September 30th (possibly moved to Monday)
|| ((d == 30 && m == Month::September) ||
(d == 1 && w == Weekday::Monday && m == Month::October))
// Botswana Day, October 1st (possibly moved to Monday or Tuesday)
|| ((d == 1 || (d == 2 && w == Weekday::Monday) || (d == 3 && w == Weekday::Tuesday))
&& m == Month::October)
// Christmas
|| (d == 25 && m == Month::December)
// Boxing Day (possibly moved to Monday)
|| ((d == 26 || (d == 27 && w == Weekday::Monday))
&& m == Month::December)
{
return false;
}

return true;
}
}
54 changes: 54 additions & 0 deletions src/time/calendars/brazil.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// RustQuant: A Rust library for quantitative finance tools.
// Copyright (C) 2023 https://github.com/avhz
// Dual licensed under Apache 2.0 and MIT.
// See:
// - LICENSE-APACHE.md
// - LICENSE-MIT.md
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

use crate::time::Calendar;
use time::{Month, OffsetDateTime, Weekday};

/// Brazil calendar.
pub struct Brazil;

impl Calendar for Brazil {
fn name(&self) -> &'static str {
"Brazil"
}

fn is_business_day(&self, date: OffsetDateTime) -> bool {
let (w, d, m, y, dd) = self.unpack_date(date);
let em = Self::easter_monday(y as usize, false);

if Self::is_weekend(date)
// New Year's Day
|| (d == 1 && m == Month::January)
// Tiradentes Day
|| (d == 21 && m == Month::April)
// Labor Day
|| (d == 1 && m == Month::May)
// Independence Day
|| (d == 7 && m == Month::September)
// Nossa Sra. Aparecida Day
|| (d == 12 && m == Month::October)
// All Souls Day
|| (d == 2 && m ==Month:: November)
// Republic Day
|| (d == 15 && m == Month::November)
// Christmas
|| (d == 25 && m == Month::December)
// Passion of Christ
|| (dd == em-3)
// Carnival
|| (dd == em-49 || dd == em-48)
// Corpus Christi
|| (dd == em+59)
{
return false;
}

return true;
}
}
13 changes: 4 additions & 9 deletions src/time/calendars/canada.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
// - LICENSE-MIT.md
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

use crate::time::{is_weekend, Calendar};
use crate::time::Calendar;
use time::{Month, OffsetDateTime, Weekday};

/// Canadian settlement calendar.
Expand All @@ -19,15 +19,10 @@ impl Calendar for Canada {
}

fn is_business_day(&self, date: OffsetDateTime) -> bool {
let w = date.weekday();
let d = date.day();
let m = date.month();
let y = date.year();
let dd = date.ordinal(); // Day of the year
let (w, d, m, y, dd) = self.unpack_date(date);
let em = Self::easter_monday(y as usize, false);

let em = crate::time::easter_monday(y as usize, false); // assuming you have a similar easter_monday function

if is_weekend(date)
if Self::is_weekend(date)
|| ((d == 1 || ((d == 2 || d == 3) && w == Weekday::Monday)) && m == Month::January)
|| ((15..=21).contains(&d) && w == Weekday::Monday && m == Month::February && y >= 2008)
|| (dd == em - 3)
Expand Down
71 changes: 71 additions & 0 deletions src/time/calendars/chile.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// RustQuant: A Rust library for quantitative finance tools.
// Copyright (C) 2023 https://github.com/avhz
// Dual licensed under Apache 2.0 and MIT.
// See:
// - LICENSE-APACHE.md
// - LICENSE-MIT.md
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

use crate::time::Calendar;
use time::{Month, OffsetDateTime, Weekday};

/// Chile calendar.
pub struct Chile;

impl Calendar for Chile {
fn name(&self) -> &'static str {
"Chile"
}

fn is_business_day(&self, date: OffsetDateTime) -> bool {
let (w, d, m, y, dd) = self.unpack_date(date);
let em = Self::easter_monday(y as usize, false);

if Self::is_weekend(date)
// New Year's Day
|| (d == 1 && m == Month::January)
|| (d == 2 && m == Month::January && w == Weekday::Monday && y > 2016)
// Good Friday
|| (dd == em-3)
// Easter Saturday
|| (dd == em-2)
// Labour Day
|| (d == 1 && m == Month::May)
// Navy Day
|| (d == 21 && m == Month::May)
// Day of Aboriginal People
|| (d == 21 && m == Month::June && y >= 2021)
// St. Peter and St. Paul
|| (d >= 26 && d <= 29 && m == Month::June && w == Weekday::Monday)
|| (d == 2 && m == Month::July && w == Weekday::Monday)
// Our Lady of Mount Carmel
|| (d == 16 && m == Month::July)
// Assumption Day
|| (d == 15 && m == Month::August)
// Independence Day
|| (d == 17 && m == Month::September && ((w == Weekday::Monday && y >= 2007) || (w == Weekday::Friday && y > 2016)))
|| (d == 18 && m == Month::September)
// Army Day
|| (d == 19 && m == Month::September)
|| (d == 20 && m == Month::September && w == Weekday::Friday && y >= 2007)
// Discovery of Two Worlds
|| (d >= 9 && d <= 12 && m == Month::October && w == Weekday::Monday)
|| (d == 15 && m == Month::October && w == Weekday::Monday)
// Reformation Day
|| (((d == 27 && m == Month::October && w == Weekday::Friday)
|| (d == 31 && m == Month::October && w != Weekday::Tuesday && w != Weekday::Wednesday)
|| (d == 2 && m == Month::November && w == Weekday::Friday)) && y >= 2008)
// All Saints' Day
|| (d == 1 && m == Month::November)
// Immaculate Conception
|| (d == 8 && m == Month::December)
// Christmas Day
|| (d == 25 && m == Month::December)
{
return false;
}

return true;
}
}
Loading

0 comments on commit 0350a53

Please sign in to comment.