use crate::currency::Currency;
use crate::indexes::inflationindex::ZeroInflationIndex;
use crate::indexes::region::Region;
use crate::settings::Settings;
use crate::shared::Shared;
use crate::time::date::Date;
use crate::time::frequency::Frequency;
use crate::time::period::Period;
use crate::time::timeunit::TimeUnit;
pub struct UkRpi;
impl UkRpi {
#[allow(clippy::new_ret_no_self)]
pub fn new(settings: Shared<Settings<Date>>) -> ZeroInflationIndex {
ZeroInflationIndex::new(
"RPI".into(),
Region::uk(),
false,
Frequency::Monthly,
Period::new(1, TimeUnit::Months),
Currency::gbp(),
settings,
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::errors::QlResult;
use crate::indexes::index::Index;
use crate::indexes::inflationindex::{
Cpi, CpiInterpolationType, InflationIndex, inflation_period,
};
use crate::shared::shared;
use crate::time::businessdayconvention::BusinessDayConvention;
use crate::time::calendars::unitedkingdom::{Market, UnitedKingdom};
use crate::time::date::Month::{
August, December, February, January, June, March, May, November, October,
};
use crate::time::schedule::MakeSchedule;
use crate::types::Rate;
const FIX_DATA: [Rate; 32] = [
189.9, 189.9, 189.6, 190.5, 191.6, 192.0, 192.2, 192.2, 192.6, 193.1, 193.3, 193.6, 194.1,
193.4, 194.2, 195.0, 196.5, 197.7, 198.5, 198.5, 199.2, 200.1, 200.4, 201.1, 202.7, 201.6,
203.1, 204.4, 205.4, 206.2, 207.3, 206.1,
];
#[test]
fn construction_matches_the_cpp_table() {
let index = UkRpi::new(shared(Settings::<Date>::new()));
assert_eq!(index.name(), "UK RPI");
assert_eq!(index.frequency(), Frequency::Monthly);
assert!(!index.revised());
assert_eq!(index.availability_lag(), Period::new(1, TimeUnit::Months));
assert_eq!(index.currency().code(), "GBP");
}
#[test]
fn a_loaded_history_reads_back_constant_within_each_period() {
let settings = shared(Settings::<Date>::new());
let evaluation_date = UnitedKingdom::new(Market::Settlement).adjust(
Date::new(13, August, 2007),
BusinessDayConvention::Following,
);
settings.set_evaluation_date(evaluation_date);
let index = UkRpi::new(settings);
let schedule = MakeSchedule::new()
.from(Date::new(1, January, 2005))
.to(Date::new(1, August, 2007))
.with_frequency(Frequency::Monthly)
.build();
let dates = schedule.dates();
assert_eq!(dates.len(), FIX_DATA.len());
assert_eq!(dates[0], Date::new(1, January, 2005));
for (date, value) in dates.iter().zip(FIX_DATA) {
index
.add_fixing(*date, value)
.expect("adding a published figure");
}
assert_eq!(
index.last_fixing_date().expect("the index has a history"),
Date::new(1, August, 2007)
);
let horizon = inflation_period(
evaluation_date - index.availability_lag(),
index.frequency(),
)
.expect("a monthly index")
.0;
for (date, value) in dates.iter().zip(FIX_DATA).take(dates.len() - 1) {
let (first, last) =
inflation_period(*date, index.frequency()).expect("a monthly index");
for day in (0..=(last - first)).map(|offset| first + offset) {
if day < horizon {
let fixing = index.fixing(day, false).expect("the period is published");
assert!((fixing - value).abs() < 1e-8, "{fixing} at {day}");
}
}
}
}
#[test]
fn a_quarterly_figure_is_attributed_to_the_start_of_its_quarter() {
let index = ZeroInflationIndex::new(
"CPI".into(),
Region::new("Custom", "XX"),
false,
Frequency::Quarterly,
Period::new(1, TimeUnit::Months),
Currency::gbp(),
shared(Settings::<Date>::new()),
);
index
.add_fixing(Date::new(15, December, 2007), 100.0)
.expect("adding a fixing on a quarterly index");
assert_eq!(
index.last_fixing_date().expect("the index has a history"),
Date::new(1, October, 2007)
);
}
fn a_ukrpi_with_2021_fixings() -> ZeroInflationIndex {
let settings = shared(Settings::<Date>::new());
settings.set_evaluation_date(Date::new(10, February, 2022));
let index = UkRpi::new(settings);
for (date, value) in [
(Date::new(1, November, 2020), 293.5),
(Date::new(1, December, 2020), 295.4),
(Date::new(1, January, 2021), 294.6),
(Date::new(1, February, 2021), 296.0),
(Date::new(1, March, 2021), 296.9),
] {
index
.add_fixing(date, value)
.expect("adding a published figure");
}
index
}
fn lagged_fixing(
index: &ZeroInflationIndex,
date: Date,
interpolation_type: CpiInterpolationType,
) -> QlResult<Rate> {
Cpi::lagged_fixing(
index,
date,
Period::new(3, TimeUnit::Months),
interpolation_type,
)
}
#[test]
fn a_flat_observation_reads_the_lagged_period() {
let index = a_ukrpi_with_2021_fixings();
for (date, expected) in [
(Date::new(10, February, 2021), 293.5),
(Date::new(12, May, 2021), 296.0),
(Date::new(25, June, 2021), 296.9),
] {
let calculated = lagged_fixing(&index, date, CpiInterpolationType::Flat)
.expect("the observed period is published");
assert!(
(calculated - expected).abs() < 1e-8,
"{calculated} at {date}"
);
}
}
#[test]
fn a_linear_observation_interpolates_the_bracketing_fixings() {
let index = a_ukrpi_with_2021_fixings();
for (date, expected) in [
(
Date::new(10, February, 2021),
293.5 * (19.0 / 28.0) + 295.4 * (9.0 / 28.0),
),
(
Date::new(12, May, 2021),
296.0 * (20.0 / 31.0) + 296.9 * (11.0 / 31.0),
),
] {
let calculated = lagged_fixing(&index, date, CpiInterpolationType::Linear)
.expect("both observed periods are published");
assert!(
(calculated - expected).abs() < 1e-8,
"{calculated} at {date}"
);
}
}
#[test]
fn a_linear_observation_propagates_a_missing_second_fixing() {
let index = a_ukrpi_with_2021_fixings();
let error = lagged_fixing(
&index,
Date::new(25, June, 2021),
CpiInterpolationType::Linear,
)
.expect_err("April 2021 was never published");
assert!(error.to_string().contains("Missing"), "err was: {error}");
}
#[test]
fn a_linear_observation_on_a_period_start_skips_the_second_fixing() {
let index = a_ukrpi_with_2021_fixings();
let calculated = lagged_fixing(
&index,
Date::new(1, June, 2021),
CpiInterpolationType::Linear,
)
.expect("the special case never reads April");
assert!((calculated - 296.9).abs() < 1e-8, "{calculated}");
}
}