Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions crates/RustQuant_math/src/distributions/gamma.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,17 @@ pub struct Gamma {
impl Gamma {
/// New instance of a Gamma distribution.
///
/// # Examples
/// ```
/// # use RustQuant::utils::assert_approx_equal;
/// # use RustQuant::math::distributions::*;
///
/// let gamma = Gamma::new(2.0, 3.0);
///
/// assert_approx_equal!(gamma.mean(), 2.0 / 3.0, 1e-12);
/// assert_approx_equal!(gamma.variance(), 2.0 / 9.0, 1e-12);
/// ```
///
/// # Panics
///
/// Panics if alpha and beta are not positive.
Expand All @@ -52,6 +63,19 @@ impl Gamma {
}

impl Distribution for Gamma {
/// Characteristic function of the Gamma distribution.
///
/// # Examples
/// ```
/// # use RustQuant::utils::assert_approx_equal;
/// # use RustQuant::math::distributions::*;
///
/// let gamma = Gamma::new(1.0, 1.0);
/// let cf = gamma.cf(1.0);
///
/// assert_approx_equal!(cf.re, 0.5, 1e-10);
/// assert_approx_equal!(cf.im, 0.5, 1e-10);
/// ```
fn cf(&self, t: f64) -> Complex<f64> {
let i: Complex<f64> = Complex::i();
let alpha = self.alpha;
Expand All @@ -60,6 +84,17 @@ impl Distribution for Gamma {
(1.0 - i * t / beta).powf(-alpha)
}

/// Probability density function of the Gamma distribution.
///
/// # Examples
/// ```
/// # use RustQuant::utils::assert_approx_equal;
/// # use RustQuant::math::distributions::*;
///
/// // Gamma(1,1) is equivalent to Exp(1).
/// let gamma = Gamma::new(1.0, 1.0);
/// assert_approx_equal!(gamma.pdf(1.0), 0.367_879_441_171_442_5, 1e-12);
/// ```
fn pdf(&self, x: f64) -> f64 {
assert!(x > 0.0);

Expand All @@ -73,6 +108,16 @@ impl Distribution for Gamma {
self.pdf(x)
}

/// Cumulative distribution function of the Gamma distribution.
///
/// # Examples
/// ```
/// # use RustQuant::utils::assert_approx_equal;
/// # use RustQuant::math::distributions::*;
///
/// let gamma = Gamma::new(1.0, 1.0);
/// assert_approx_equal!(gamma.cdf(1.0), 0.632_120_558_828_558_1, 1e-12);
/// ```
fn cdf(&self, x: f64) -> f64 {
assert!(x > 0.0);

Expand All @@ -86,6 +131,16 @@ impl Distribution for Gamma {
unimplemented!()
}

/// Mean of the Gamma distribution.
///
/// # Examples
/// ```
/// # use RustQuant::utils::assert_approx_equal;
/// # use RustQuant::math::distributions::*;
///
/// let gamma = Gamma::new(2.0, 4.0);
/// assert_approx_equal!(gamma.mean(), 0.5, 1e-12);
/// ```
fn mean(&self) -> f64 {
self.alpha / self.beta
}
Expand All @@ -102,6 +157,16 @@ impl Distribution for Gamma {
}
}

/// Variance of the Gamma distribution.
///
/// # Examples
/// ```
/// # use RustQuant::utils::assert_approx_equal;
/// # use RustQuant::math::distributions::*;
///
/// let gamma = Gamma::new(2.0, 4.0);
/// assert_approx_equal!(gamma.variance(), 0.125, 1e-12);
/// ```
fn variance(&self) -> f64 {
self.alpha / self.beta.powi(2)
}
Expand Down
3 changes: 3 additions & 0 deletions crates/RustQuant_time/src/calendar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ pub enum Market {
Israel,
/// Italy national calendar.
Italy,
/// Japan national calendar.
Japan,
/// Mexico national calendar.
Mexico,
/// Netherlands national calendar.
Expand Down Expand Up @@ -428,6 +430,7 @@ impl Calendar {
Market::Indonesia => is_holiday_impl_indonesia(date),
Market::Israel => is_holiday_impl_israel(date),
Market::Italy => is_holiday_impl_italy(date),
Market::Japan => is_holiday_impl_japan(date),
Market::Mexico => is_holiday_impl_mexico(date),
Market::Netherlands => is_holiday_impl_netherlands(date),
Market::NewZealand => is_holiday_impl_new_zealand(date),
Expand Down
107 changes: 107 additions & 0 deletions crates/RustQuant_time/src/countries/japan.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
use crate::utilities::unpack_date;
use time::{Date, Month, Weekday};

pub(crate) fn is_holiday_impl_japan(date: Date) -> bool {
let (y, m, d, wd, _, _) = unpack_date(date, false);

// Fixed-date holidays (modern scope)
let fixed =
(m == Month::January && d == 1) // New Year's Day
|| (m == Month::February && d == 11) // National Foundation Day
|| (m == Month::February && d == 23 && y >= 2020) // Emperor's Birthday (Reiwa)
|| (m == Month::April && d == 29) // Showa Day
|| (m == Month::May && d == 3) // Constitution Memorial Day
|| (m == Month::May && d == 4) // Greenery Day
|| (m == Month::May && d == 5) // Children's Day
|| (m == Month::August && d == 11 && y >= 2016) // Mountain Day
|| (m == Month::November && d == 3) // Culture Day
|| (m == Month::November && d == 23); // Labor Thanksgiving Day

// Happy Monday system holidays
let happy_monday =
// Coming of Age Day: 2nd Monday of January
(m == Month::January && wd == Weekday::Monday && d >= 8 && d <= 14)
// Marine Day: 3rd Monday of July
|| (m == Month::July && wd == Weekday::Monday && d >= 15 && d <= 21)
// Respect for the Aged Day: 3rd Monday of September
|| (m == Month::September && wd == Weekday::Monday && d >= 15 && d <= 21)
// Sports Day: 2nd Monday of October
|| (m == Month::October && wd == Weekday::Monday && d >= 8 && d <= 14);

// Equinox holidays (standard approximation)
let vernal =
m == Month::March && d as i32 == vernal_equinox_day(y as i32);
let autumnal =
m == Month::September && d as i32 == autumnal_equinox_day(y as i32);

// Substitute holidays: if holiday falls on Sunday, following Monday is holiday.
let substitute_monday = wd == Weekday::Monday && is_recognized_holiday(date.previous_day().unwrap());

fixed || happy_monday || vernal || autumnal || substitute_monday
}

fn is_recognized_holiday(date: Date) -> bool {
let (y, m, d, wd, _, _) = unpack_date(date, false);

let fixed =
(m == Month::January && d == 1)
|| (m == Month::February && d == 11)
|| (m == Month::February && d == 23 && y >= 2020)
|| (m == Month::April && d == 29)
|| (m == Month::May && d == 3)
|| (m == Month::May && d == 4)
|| (m == Month::May && d == 5)
|| (m == Month::August && d == 11 && y >= 2016)
|| (m == Month::November && d == 3)
|| (m == Month::November && d == 23);

let happy_monday =
(m == Month::January && wd == Weekday::Monday && d >= 8 && d <= 14)
|| (m == Month::July && wd == Weekday::Monday && d >= 15 && d <= 21)
|| (m == Month::September && wd == Weekday::Monday && d >= 15 && d <= 21)
|| (m == Month::October && wd == Weekday::Monday && d >= 8 && d <= 14);

let vernal = m == Month::March && d as i32 == vernal_equinox_day(y as i32);
let autumnal = m == Month::September && d as i32 == autumnal_equinox_day(y as i32);

fixed || happy_monday || vernal || autumnal
}

fn vernal_equinox_day(year: i32) -> i32 {
(20.8431 + 0.242194 * (year - 1980) as f64 - ((year - 1980) / 4) as f64).floor() as i32
}

fn autumnal_equinox_day(year: i32) -> i32 {
(23.2488 + 0.242194 * (year - 1980) as f64 - ((year - 1980) / 4) as f64).floor() as i32
}

#[cfg(test)]
mod tests {
use crate::{Calendar, Market};
use time::macros::date;

const CALENDAR: Calendar = Calendar::new(Market::Japan);

#[test]
fn test_japan_holidays_2025() {
assert!(CALENDAR.is_holiday(date!(2025 - 01 - 01))); // New Year
assert!(CALENDAR.is_holiday(date!(2025 - 01 - 13))); // Coming of Age Day
assert!(CALENDAR.is_holiday(date!(2025 - 02 - 11))); // National Foundation Day
assert!(CALENDAR.is_holiday(date!(2025 - 03 - 20))); // Vernal Equinox
assert!(CALENDAR.is_holiday(date!(2025 - 04 - 29))); // Showa Day
assert!(CALENDAR.is_holiday(date!(2025 - 05 - 05))); // Children's Day
assert!(CALENDAR.is_holiday(date!(2025 - 07 - 21))); // Marine Day
assert!(CALENDAR.is_holiday(date!(2025 - 09 - 15))); // Respect for the Aged Day
assert!(CALENDAR.is_holiday(date!(2025 - 09 - 23))); // Autumnal Equinox
assert!(CALENDAR.is_holiday(date!(2025 - 10 - 13))); // Sports Day
assert!(CALENDAR.is_holiday(date!(2025 - 11 - 03))); // Culture Day
assert!(CALENDAR.is_holiday(date!(2025 - 11 - 24))); // Labor Thanksgiving substitute
}

#[test]
fn test_regular_business_days_2025() {
assert!(!CALENDAR.is_holiday(date!(2025 - 01 - 02)));
assert!(!CALENDAR.is_holiday(date!(2025 - 06 - 10)));
assert!(!CALENDAR.is_holiday(date!(2025 - 12 - 01)));
}
}
4 changes: 4 additions & 0 deletions crates/RustQuant_time/src/countries/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ pub(crate) use israel::*;
pub mod italy;
pub(crate) use italy::*;

/// Japan holidays and calendars.
pub mod japan;
pub(crate) use japan::*;

/// Mexico holidays and calendars
pub mod mexico;
pub(crate) use mexico::*;
Expand Down