use crate::{rate::pct_change, ONE};
use rust_decimal::prelude::*;
pub fn twr(values: &[(Decimal, Decimal)], annualization_period: Option<Decimal>) -> Decimal {
let total_return = values
.windows(2)
.map(|window| {
let (start_value, _) = window[0];
let (end_value, end_cashflow) = window[1];
let adjusted_end = end_value - end_cashflow;
pct_change(start_value, adjusted_end)
.map(|pct| ONE + pct)
.expect("TWR: pct_change failed, (ending value - cash flow) should not be zero")
})
.product::<Decimal>();
annualization_period
.map(|period| (total_return).powd(ONE / period) - ONE)
.unwrap_or(total_return - ONE)
}
#[cfg(test)]
mod tests {
use super::*;
use rust_decimal_macros::*;
#[cfg(not(feature = "std"))]
extern crate std;
#[cfg(not(feature = "std"))]
use std::assert;
#[cfg(not(feature = "std"))]
use std::prelude::v1::*;
#[test]
fn test_twr() {
let values = vec![
(dec!(1000), dec!(0)),
(dec!(1600), dec!(400)),
(dec!(1450), dec!(-200)),
(dec!(1700), dec!(200)),
(dec!(2200), dec!(300)),
];
let twr_qtr = twr(&values, None);
let expected_qtr = dec!(0.43078093);
assert!((twr_qtr - expected_qtr).abs() < dec!(1e-5));
let twr_yr = twr(&values, Some(dec!(4)));
let expected_yr = dec!(0.093688);
assert!((twr_yr - expected_yr).abs() < dec!(1e-5));
let values_bankruptcy = vec![
(dec!(1000), dec!(0)),
(dec!(1600), dec!(400)),
(dec!(1450), dec!(-200)),
(dec!(1700), dec!(200)),
(dec!(2200), dec!(300)),
(dec!(2500), dec!(0)),
(dec!(3000), dec!(-300)),
(dec!(1700), dec!(-1500)),
(dec!(0), dec!(0)),
];
let twr_bankruptcy = twr(&values_bankruptcy, Some(dec!(2)));
println!("TWR with bankruptcy: {}", twr_bankruptcy);
let expected_bankruptcy = dec!(-1);
assert_eq!(twr_bankruptcy, expected_bankruptcy);
let values_6qtr = vec![
(dec!(1000), dec!(0)),
(dec!(1600), dec!(400)),
(dec!(1450), dec!(-200)),
(dec!(1700), dec!(200)),
(dec!(2200), dec!(300)),
(dec!(2500), dec!(0)),
(dec!(3000), dec!(-300)),
];
let twr_6qtr = twr(&values_6qtr, Some(dec!(1.5)));
let expected_6qtr = dec!(0.663832);
assert!((twr_6qtr - expected_6qtr).abs() < dec!(1e-5));
let values_2 = vec![(dec!(1000), dec!(0)), (dec!(1600), dec!(400))];
let twr_2 = twr(&values_2, None);
assert_eq!(twr_2, dec!(0.2));
}
}