use crate::currency::Currency;
use crate::errors::QlResult;
use crate::handle::Handle;
use crate::indexes::ibor::libor::Libor;
use crate::indexes::iborindex::IborIndex;
use crate::settings::Settings;
use crate::shared::Shared;
use crate::termstructures::yieldtermstructure::YieldTermStructure;
use crate::time::calendars::japan::Japan;
use crate::time::date::Date;
use crate::time::daycounters::actual360::Actual360;
use crate::time::period::Period;
pub struct JpyLibor;
impl JpyLibor {
#[allow(clippy::new_ret_no_self)]
pub fn new(
tenor: Period,
forwarding: Handle<dyn YieldTermStructure>,
settings: Shared<Settings<Date>>,
) -> QlResult<IborIndex> {
Libor::new(
"JPYLibor".into(),
tenor,
2,
Currency::jpy(),
Japan::new(),
Actual360::new(),
forwarding,
settings,
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::indexes::index::Index;
use crate::indexes::interestrateindex::InterestRateIndex;
use crate::instruments::MakeVanillaSwap;
use crate::math::interpolations::loglinear::LogLinear;
use crate::quotes::{Quote, SimpleQuote};
use crate::shared::shared;
use crate::termstructures::bootstraphelper::RateHelper;
use crate::termstructures::bootstraptraits::Discount;
use crate::termstructures::yields::{PiecewiseYieldCurve, SwapRateHelper};
use crate::time::businessdayconvention::BusinessDayConvention;
use crate::time::calendars::unitedkingdom::{Market as UkMarket, UnitedKingdom};
use crate::time::date::Month;
use crate::time::daycounters::thirty360::{Convention, Thirty360};
use crate::time::frequency::Frequency;
use crate::time::timeunit::TimeUnit;
use crate::types::Rate;
fn jpy_libor_6m(settings: Shared<Settings<Date>>) -> IborIndex {
JpyLibor::new(Period::new(6, TimeUnit::Months), Handle::empty(), settings)
.expect("a 6M JPYLibor tenor is valid")
}
#[test]
fn jpy_libor_carries_the_ice_configuration() {
let index = jpy_libor_6m(shared(Settings::<Date>::new()));
assert_eq!(index.name(), "JPYLibor6M Actual/360");
assert_eq!(index.fixing_days(), 2);
assert_eq!(index.currency(), &Currency::jpy());
assert_eq!(index.day_counter().name(), "Actual/360");
assert_eq!(index.fixing_calendar().name(), "London stock exchange");
assert_eq!(
index.maturity_calendar().name(),
"JoinHolidays(London stock exchange, Japan)"
);
assert_eq!(
index.business_day_convention(),
BusinessDayConvention::ModifiedFollowing
);
assert!(index.end_of_month());
}
#[test]
fn maturity_date_advances_on_the_joint_calendar() {
let index = jpy_libor_6m(shared(Settings::<Date>::new()));
let v = Date::new(23, Month::May, 2022);
let maturity = index.maturity_date(v).unwrap();
let uk = UnitedKingdom::new(UkMarket::Exchange);
let joint = index.maturity_calendar();
let expected = joint.advance_by_period(
v,
index.tenor(),
index.business_day_convention(),
index.end_of_month(),
);
let uk_only = uk.advance_by_period(
v,
index.tenor(),
index.business_day_convention(),
index.end_of_month(),
);
assert_eq!(maturity, expected);
assert_eq!(maturity, Date::new(24, Month::November, 2022));
assert_eq!(uk_only, Date::new(23, Month::November, 2022));
assert_ne!(maturity, uk_only);
let clone = index.clone_with(Handle::empty());
assert_eq!(clone.maturity_date(v).unwrap(), maturity);
}
const SWAP_DATA: [(i32, Rate); 15] = [
(1, 4.54),
(2, 4.63),
(3, 4.75),
(4, 4.86),
(5, 4.99),
(6, 5.11),
(7, 5.23),
(8, 5.33),
(9, 5.41),
(10, 5.47),
(12, 5.60),
(15, 5.75),
(20, 5.89),
(25, 5.95),
(30, 5.96),
];
#[test]
fn jpy_libor_swap_curve_reprices_its_input_swaps() {
let settings = shared(Settings::<Date>::new());
let today = Date::new(4, Month::October, 2007);
settings.set_evaluation_date(today);
let calendar = Japan::new();
let settlement = calendar.advance(
today,
2,
TimeUnit::Days,
BusinessDayConvention::Following,
false,
);
let index = jpy_libor_6m(settings.clone());
let mut instruments: Vec<Shared<dyn RateHelper>> = Vec::new();
for (n, rate) in SWAP_DATA {
let quote = Handle::new(shared(SimpleQuote::new(rate / 100.0)) as Shared<dyn Quote>);
instruments.push(SwapRateHelper::new(
quote,
Period::new(n, TimeUnit::Years),
calendar.clone(),
Frequency::Annual,
BusinessDayConvention::Unadjusted,
Thirty360::with_convention(Convention::BondBasis),
&index,
) as Shared<dyn RateHelper>);
}
let curve = PiecewiseYieldCurve::<Discount, LogLinear>::new(
settlement,
instruments,
Actual360::new(),
LogLinear,
)
.unwrap();
curve
.nodes()
.expect("the bootstrapped curve exposes its nodes");
let handle: Handle<dyn YieldTermStructure> =
Handle::new(curve as Shared<dyn YieldTermStructure>);
let jpylibor6m = shared(
JpyLibor::new(
Period::new(6, TimeUnit::Months),
handle.clone(),
settings.clone(),
)
.expect("a 6M JPYLibor tenor is valid"),
);
for (n, rate) in SWAP_DATA {
let mut swap = MakeVanillaSwap::new(
Period::new(n, TimeUnit::Years),
Shared::clone(&jpylibor6m),
Some(0.0),
Period::new(0, TimeUnit::Days),
settings.clone(),
)
.with_effective_date(settlement)
.with_discounting_term_structure(handle.clone())
.with_fixed_leg_day_count(Thirty360::with_convention(Convention::BondBasis))
.with_fixed_leg_tenor(Period::try_from(Frequency::Annual).unwrap())
.with_fixed_leg_convention(BusinessDayConvention::Unadjusted)
.with_fixed_leg_termination_date_convention(BusinessDayConvention::Unadjusted)
.with_fixed_leg_calendar(calendar.clone())
.with_floating_leg_calendar(calendar.clone())
.build()
.unwrap();
let estimated = swap.fixed_vs_floating_mut().fair_rate().unwrap();
let expected = rate / 100.0;
assert!(
(estimated - expected).abs() <= 1.0e-9,
"{n} year(s) swap: estimated {estimated} vs expected {expected}"
);
}
}
}