use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, PartialOrd, Default, Deserialize, Serialize)]
pub struct ProfitFactor {
pub value: Decimal,
}
impl ProfitFactor {
pub fn calculate(profits_gross_abs: Decimal, losses_gross_abs: Decimal) -> Option<Self> {
if profits_gross_abs.is_zero() && losses_gross_abs.is_zero() {
return None;
}
let value = if losses_gross_abs.is_zero() {
Decimal::MAX
} else if profits_gross_abs.is_zero() {
Decimal::MIN
} else {
profits_gross_abs
.abs()
.checked_div(losses_gross_abs.abs())?
};
Some(Self { value })
}
}
#[cfg(test)]
mod tests {
use super::*;
use rust_decimal_macros::dec;
use std::str::FromStr;
#[test]
fn test_profit_factor_calculate() {
assert_eq!(
ProfitFactor::calculate(
Decimal::from_scientific("1e-20").unwrap(),
Decimal::from_scientific("1e-20").unwrap()
)
.unwrap()
.value,
Decimal::ONE
);
assert_eq!(
ProfitFactor::calculate(Decimal::MAX / dec!(2), Decimal::MAX / dec!(2))
.unwrap()
.value,
Decimal::ONE
);
assert_eq!(ProfitFactor::calculate(dec!(0.0), dec!(0.0)), None);
assert_eq!(
ProfitFactor::calculate(dec!(0.0), dec!(1.0)).unwrap().value,
Decimal::MIN
);
assert_eq!(
ProfitFactor::calculate(dec!(1.0), dec!(0.0)).unwrap().value,
Decimal::MAX
);
assert_eq!(
ProfitFactor::calculate(dec!(10.0), dec!(5.0))
.unwrap()
.value,
dec!(2.0)
);
assert_eq!(
ProfitFactor::calculate(dec!(10.0), dec!(-5.0))
.unwrap()
.value,
dec!(2.0)
);
assert_eq!(
ProfitFactor::calculate(dec!(10.5555), dec!(5.2345))
.unwrap()
.value,
Decimal::from_str("2.016524978507975928933040405").unwrap()
);
}
}