use super::Cashflow;
use time::Date;
#[derive(Debug, Clone, Default, PartialEq, PartialOrd)]
pub struct Leg {
cashflows: Vec<Cashflow>,
}
impl Leg {
pub fn new(cashflows: Vec<Cashflow>) -> Self {
Self { cashflows }
}
pub fn size(&self) -> usize {
self.cashflows.len()
}
pub fn npv(&self, discount_rate: f64) -> f64 {
self.cashflows.iter().map(|cf| cf.npv(discount_rate)).sum()
}
pub fn add_cashflow(&mut self, cashflow: Cashflow) {
self.cashflows.push(cashflow);
}
pub fn cashflows(&self) -> &[Cashflow] {
&self.cashflows
}
pub fn start_date(&self) -> Option<Date> {
self.cashflows.iter().map(Cashflow::date).min()
}
pub fn end_date(&self) -> Option<Date> {
self.cashflows.iter().map(Cashflow::date).max()
}
pub fn is_active(&self, current_date: Date) -> bool {
match (self.start_date(), self.end_date()) {
(Some(start), Some(end)) => current_date >= start && current_date <= end,
_ => false,
}
}
}
#[cfg(test)]
mod tests_legs {
use super::*;
use time::Duration;
use RustQuant_utils::{assert_approx_equal, RUSTQUANT_EPSILON as EPS};
fn today() -> Date {
time::OffsetDateTime::now_utc().date()
}
fn generate_simple_leg(now: Date) -> Leg {
let cashflows = vec![
Cashflow::new(100.0, now),
Cashflow::new(200.0, now + Duration::days(30)),
Cashflow::new(300.0, now + Duration::days(60)),
];
Leg::new(cashflows)
}
#[test]
fn test_size() {
let now = today();
let leg = generate_simple_leg(now);
assert_eq!(leg.size(), 3);
}
#[test]
fn test_npv() {
let now = today();
let leg = generate_simple_leg(now);
let df = 0.9;
assert_approx_equal!(leg.npv(df), 540.0, EPS);
}
#[test]
fn test_add_cashflow() {
let now = today();
let mut leg = generate_simple_leg(now);
let new_cashflow = Cashflow::new(400.0, now + Duration::days(90));
leg.add_cashflow(new_cashflow.clone());
assert_eq!(leg.size(), 4);
assert_approx_equal!(
leg.cashflows().last().unwrap().amount(),
new_cashflow.amount(),
EPS
);
}
#[test]
fn test_start_end_date() {
let now = today();
let leg = generate_simple_leg(now);
let start = leg.start_date().unwrap();
let end = leg.end_date().unwrap();
assert_eq!(start, now);
assert_eq!(end, now + Duration::days(60));
}
#[test]
fn test_is_active() {
let now = today();
let leg = generate_simple_leg(now);
assert!(leg.is_active(now));
assert!(leg.is_active(now + Duration::days(30)));
assert!(leg.is_active(now + Duration::days(60)));
assert!(!leg.is_active(now - Duration::days(1)));
assert!(!leg.is_active(now + Duration::days(61)));
}
}