use crate::{
instruments::Instrument,
time::{today, DayCountConvention},
};
use time::Date;
pub struct CoxIngersollRoss {
a: f64,
b: f64,
r: f64,
sigma: f64,
pub evaluation_date: Option<Date>,
pub expiration_date: Date,
}
impl Instrument for CoxIngersollRoss {
fn price(&self) -> f64 {
let a = self.a;
let b = self.b;
let sigma = self.sigma;
let r = self.r;
let tau = DayCountConvention::default().day_count_factor(
self.evaluation_date.unwrap_or(today()),
self.expiration_date,
);
let gamma = (a * a + 2.0 * sigma.powi(2)).sqrt();
let b_t = 2.0 * ((gamma * tau).exp() - 1.0)
/ ((gamma + a) * ((gamma * tau).exp() - 1.0) + 2.0 * gamma);
let a_t = (2.0 * gamma * ((a + gamma) * tau / 2.0).exp()
/ ((gamma + a) * ((gamma * tau).exp() - 1.0) + 2.0 * gamma))
.powf(2.0 * a * b / sigma.powi(2));
a_t * (-b_t * r).exp()
}
fn error(&self) -> Option<f64> {
None
}
fn valuation_date(&self) -> Date {
self.evaluation_date.unwrap_or(today())
}
fn instrument_type(&self) -> &'static str {
"Zero Coupon Bond"
}
}
#[cfg(test)]
mod tests {
use crate::assert_approx_equal;
use super::*;
#[test]
fn test_cir_zero_coupon_bond() {
let expiry = today() + time::Duration::days(365);
let cir = CoxIngersollRoss {
a: 0.3,
b: 0.1,
sigma: 0.03,
r: 0.03,
evaluation_date: None,
expiration_date: expiry,
};
let cir_price = cir.price();
assert_approx_equal!(cir_price, 0.9613, 1e-4);
}
}