use crate::currency::Currency;
use crate::errors::QlResult;
use crate::handle::Handle;
use crate::indexes::iborindex::IborIndex;
use crate::indexes::interestrateindex::InterestRateIndex;
use crate::settings::Settings;
use crate::shared::Shared;
use crate::termstructures::yieldtermstructure::YieldTermStructure;
use crate::time::businessdayconvention::BusinessDayConvention;
use crate::time::calendar::Calendar;
use crate::time::calendars::jointcalendar::{JointCalendar, JointCalendarRule};
use crate::time::calendars::unitedkingdom::{Market as UkMarket, UnitedKingdom};
use crate::time::date::Date;
use crate::time::daycounter::DayCounter;
use crate::time::period::Period;
use crate::time::timeunit::TimeUnit;
use crate::types::Natural;
use crate::{fail, require};
pub struct Libor;
impl Libor {
#[allow(clippy::new_ret_no_self)]
#[allow(clippy::too_many_arguments)]
pub fn new(
family_name: String,
tenor: Period,
settlement_days: Natural,
currency: Currency,
financial_center_calendar: Calendar,
day_counter: DayCounter,
forwarding: Handle<dyn YieldTermStructure>,
settings: Shared<Settings<Date>>,
) -> QlResult<IborIndex> {
let uk_exchange = UnitedKingdom::new(UkMarket::Exchange);
let mut index = IborIndex::new(
family_name,
tenor,
settlement_days,
currency.clone(),
uk_exchange.clone(),
libor_convention(tenor)?,
libor_eom(tenor)?,
day_counter,
forwarding,
settings,
);
require!(
index.tenor().units() != TimeUnit::Days,
"for daily tenors ({}) dedicated DailyTenor constructor must be used",
index.tenor()
);
require!(
currency != Currency::eur(),
"for EUR Libor dedicated EurLibor constructor must be used"
);
index.set_value_calendar(uk_exchange.clone());
index.set_maturity_calendar(JointCalendar::of_two(
uk_exchange,
financial_center_calendar,
JointCalendarRule::JoinHolidays,
));
Ok(index)
}
}
pub(crate) fn libor_convention(tenor: Period) -> QlResult<BusinessDayConvention> {
match tenor.units() {
TimeUnit::Days | TimeUnit::Weeks => Ok(BusinessDayConvention::Following),
TimeUnit::Months | TimeUnit::Years => Ok(BusinessDayConvention::ModifiedFollowing),
_ => fail!("invalid time units"),
}
}
pub(crate) fn libor_eom(tenor: Period) -> QlResult<bool> {
match tenor.units() {
TimeUnit::Days | TimeUnit::Weeks => Ok(false),
TimeUnit::Months | TimeUnit::Years => Ok(true),
_ => fail!("invalid time units"),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::shared::shared;
use crate::time::calendars::unitedstates::{Market as UsMarket, UnitedStates};
use crate::time::daycounters::actual360::Actual360;
fn libor(tenor: Period, currency: Currency) -> QlResult<IborIndex> {
Libor::new(
"TestLibor".into(),
tenor,
2,
currency,
UnitedStates::new(UsMarket::LiborImpact),
Actual360::new(),
Handle::empty(),
shared(Settings::<Date>::new()),
)
}
#[test]
fn daily_tenor_is_rejected() {
let err = libor(Period::new(3, TimeUnit::Days), Currency::usd())
.err()
.expect("daily tenors must be rejected");
assert!(err.to_string().contains("dedicated DailyTenor constructor"));
}
#[test]
fn eur_currency_is_rejected() {
let err = libor(Period::new(3, TimeUnit::Months), Currency::eur())
.err()
.expect("EUR must be rejected");
assert!(err.to_string().contains("dedicated EurLibor constructor"));
}
#[test]
fn convention_and_eom_derive_from_the_tenor_unit() {
let weekly = libor(Period::new(1, TimeUnit::Weeks), Currency::usd()).unwrap();
assert_eq!(
weekly.business_day_convention(),
BusinessDayConvention::Following
);
assert!(!weekly.end_of_month());
let monthly = libor(Period::new(3, TimeUnit::Months), Currency::usd()).unwrap();
assert_eq!(
monthly.business_day_convention(),
BusinessDayConvention::ModifiedFollowing
);
assert!(monthly.end_of_month());
}
#[test]
fn calendars_are_uk_exchange_and_the_joint_calendar() {
use crate::indexes::index::Index;
let index = libor(Period::new(3, TimeUnit::Months), Currency::usd()).unwrap();
assert_eq!(index.fixing_calendar().name(), "London stock exchange");
assert_eq!(index.value_calendar().name(), "London stock exchange");
assert_eq!(
index.maturity_calendar().name(),
"JoinHolidays(London stock exchange, US with Libor impact)"
);
}
}