use crate::FloatLike;
pub fn apr<T: FloatLike>(ear: T, npery: T) -> T {
let nth_root = (T::one() + ear).powf(T::one() / npery);
npery * (nth_root - T::one())
}
pub fn ear<T: FloatLike>(apr: T, npery: T) -> T {
let nth_root = T::one() + apr / npery;
nth_root.powf(npery) - T::one()
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(not(feature = "std"))]
extern crate std;
#[cfg(not(feature = "std"))]
use std::assert;
#[test]
fn test_apr() {
struct TestCase {
n: f64,
ear: f64,
expected: f64,
description: &'static str,
}
impl TestCase {
fn new(n: f64, ear: f64, expected: f64, description: &'static str) -> TestCase {
TestCase {
n,
ear,
expected,
description,
}
}
}
let test_cases = [
TestCase::new(
12.0,
0.05,
0.04889,
"Standard case with EAR of 0.05 and monthly compounding",
),
TestCase::new(12.0, 0.0, 0.0, "Zero EAR should result in zero APR"),
TestCase::new(12.0, 0.2, 0.18371, "High EAR of 0.2 with monthly compounding"),
];
for case in &test_cases {
let calculated_apr = apr(case.ear, case.n);
assert!(
(calculated_apr - case.expected).abs() < 1e-5,
"Failed on case: {}. Expected {}, got {}",
case.description,
case.expected,
calculated_apr
);
}
}
#[test]
fn test_ear() {
struct TestCase {
n: f64,
apr: f64,
expected: f64,
description: &'static str,
}
impl TestCase {
fn new(n: f64, apr: f64, expected: f64, description: &'static str) -> TestCase {
TestCase {
n,
apr,
expected,
description,
}
}
}
let test_cases = [
TestCase::new(
12.0,
0.05,
0.05116,
"Standard case with APR of 0.05 and monthly compounding",
),
TestCase::new(12.0, 0.0, 0.0, "Zero APR should result in zero EAR"),
TestCase::new(12.0, 0.2, 0.21939, "High APR of 0.2 with monthly compounding"),
];
for case in &test_cases {
let calculated_ear = ear(case.apr, case.n);
assert!(
(calculated_ear - case.expected).abs() < 1e-5,
"Failed on case: {}. Expected {}, got {}",
case.description,
case.expected,
calculated_ear
);
}
}
}