Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,10 @@
* [Trapped Rainwater](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/trapped_rainwater.rs)
* [Word Break](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/word_break.rs)
* Financial
* [Compound Interest](https://github.com/TheAlgorithms/Rust/blob/master/src/financial/compound_interest.rs)
* [Equated Monthly Installments](https://github.com/TheAlgorithms/Rust/blob/master/src/financial/equated_monthly_installments.rs)
* [Exponential Moving Average](https://github.com/TheAlgorithms/Rust/blob/master/src/financial/exponential_moving_average.rs)
* [Finance Ratios](https://github.com/TheAlgorithms/Rust/blob/master/src/financial/finance_ratios.rs)
* [Interest](https://github.com/TheAlgorithms/Rust/blob/master/src/financial/interest.rs)
* [Net Present Value](https://github.com/TheAlgorithms/Rust/blob/master/src/financial/npv.rs)
* [NPV Sensitivity](https://github.com/TheAlgorithms/Rust/blob/master/src/financial/npv_sensitivity.rs)
* [Payback Period](https://github.com/TheAlgorithms/Rust/blob/master/src/financial/payback.rs)
Expand Down
23 changes: 0 additions & 23 deletions src/financial/compound_interest.rs

This file was deleted.

211 changes: 211 additions & 0 deletions src/financial/interest.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
//! Calculates simple, compound, and APR interest on a principal amount.
//!
//! Formulas:
//! Simple Interest: I = p * r * t
//! Compound Interest: I = p * ((1 + r)^n - 1)
//! APR Interest: Compound interest with r = annual_rate / 365
//! and n = years * 365
//! where:
//! - `p` is the principal
//! - `r` is the interest rate per period
//! - `t` is the number of periods (days)
//! - `n` is the total number of compounding periods
//!
//! Reference: https://www.investopedia.com/terms/i/interest.asp

/// Calculates simple interest earned over a number of days.
///
/// # Arguments
/// * `principal` - The initial amount of money (must be > 0)
/// * `daily_interest_rate` - The daily interest rate as a decimal (must be >= 0)
/// * `days_between_payments` - The number of days between payments (must be > 0)
///
/// # Errors
/// Returns an `Err(&'static str)` if any argument is out of range.
pub fn simple_interest(
principal: f64,
daily_interest_rate: f64,
days_between_payments: f64,
) -> Result<f64, &'static str> {
if principal <= 0.0 {
return Err("principal must be > 0");
}
if daily_interest_rate < 0.0 {
return Err("daily_interest_rate must be >= 0");
}
if days_between_payments <= 0.0 {
return Err("days_between_payments must be > 0");
}
Ok(principal * daily_interest_rate * days_between_payments)
}

/// Calculates compound interest earned over a number of compounding periods.
///
/// # Arguments
/// * `principal` - The initial amount of money (must be > 0)
/// * `nominal_annual_interest_rate` - The rate per compounding period as a decimal (must be >= 0)
/// * `number_of_compounding_periods` - The total number of compounding periods (must be > 0)
///
/// # Errors
/// Returns an `Err(&'static str)` if any argument is out of range.
pub fn compound_interest(
principal: f64,
nominal_annual_interest_rate: f64,
number_of_compounding_periods: f64,
) -> Result<f64, &'static str> {
if principal <= 0.0 {
return Err("principal must be > 0");
}
if nominal_annual_interest_rate < 0.0 {
return Err("nominal_annual_interest_rate must be >= 0");
}
if number_of_compounding_periods <= 0.0 {
return Err("number_of_compounding_periods must be > 0");
}
Ok(
principal
* ((1.0 + nominal_annual_interest_rate).powf(number_of_compounding_periods) - 1.0),
)
}

/// Calculates interest using the Annual Percentage Rate (APR), compounded daily.
///
/// Converts the APR to a daily rate and compounds over the equivalent number
/// of days, giving a more accurate real-world figure than simple annual compounding.
///
/// # Arguments
/// * `principal` - The initial amount of money (must be > 0)
/// * `nominal_annual_percentage_rate` - The APR as a decimal (must be >= 0)
/// * `number_of_years` - The loan or investment duration in years (must be > 0)
///
/// # Errors
/// Returns an `Err(&'static str)` if any argument is out of range.
pub fn apr_interest(
principal: f64,
nominal_annual_percentage_rate: f64,
number_of_years: f64,
) -> Result<f64, &'static str> {
if principal <= 0.0 {
return Err("principal must be > 0");
}
if nominal_annual_percentage_rate < 0.0 {
return Err("nominal_annual_percentage_rate must be >= 0");
}
if number_of_years <= 0.0 {
return Err("number_of_years must be > 0");
}
// Divide annual rate by 365 to obtain the daily rate
// Multiply years by 365 to obtain the total number of daily compounding periods
compound_interest(
principal,
nominal_annual_percentage_rate / 365.0,
number_of_years * 365.0,
)
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_simple_interest() {
const EPSILON: f64 = 1e-9;

// Standard cases
assert!((simple_interest(18000.0, 0.06, 3.0).unwrap() - 3240.0).abs() < EPSILON);
assert!((simple_interest(0.5, 0.06, 3.0).unwrap() - 0.09).abs() < EPSILON);
assert!((simple_interest(18000.0, 0.01, 10.0).unwrap() - 1800.0).abs() < EPSILON);
assert!((simple_interest(5500.0, 0.01, 100.0).unwrap() - 5500.0).abs() < EPSILON);

// Zero interest rate yields zero interest
assert!((simple_interest(18000.0, 0.0, 3.0).unwrap() - 0.0).abs() < EPSILON);

// Error cases
assert_eq!(
simple_interest(-10000.0, 0.06, 3.0),
Err("principal must be > 0")
);
assert_eq!(
simple_interest(0.0, 0.06, 3.0),
Err("principal must be > 0")
);
assert_eq!(
simple_interest(10000.0, -0.06, 3.0),
Err("daily_interest_rate must be >= 0")
);
assert_eq!(
simple_interest(5500.0, 0.01, -5.0),
Err("days_between_payments must be > 0")
);
assert_eq!(
simple_interest(5500.0, 0.01, 0.0),
Err("days_between_payments must be > 0")
);
}

#[test]
fn test_compound_interest() {
const EPSILON: f64 = 1e-9;

// Standard cases
assert!(
(compound_interest(10000.0, 0.05, 3.0).unwrap() - 1_576.250_000_000_001_4).abs()
< EPSILON
);
assert!(
(compound_interest(10000.0, 0.05, 1.0).unwrap() - 500.000_000_000_000_45).abs()
< EPSILON
);
assert!(
(compound_interest(0.5, 0.05, 3.0).unwrap() - 0.078_812_500_000_000_06).abs() < EPSILON
);

// Zero interest rate yields zero interest
assert!((compound_interest(10000.0, 0.0, 5.0).unwrap() - 0.0).abs() < EPSILON);

// Error cases
assert_eq!(
compound_interest(-5500.0, 0.01, 5.0),
Err("principal must be > 0")
);
assert_eq!(
compound_interest(10000.0, -3.5, 3.0),
Err("nominal_annual_interest_rate must be >= 0")
);
assert_eq!(
compound_interest(10000.0, 0.06, -4.0),
Err("number_of_compounding_periods must be > 0")
);
}

#[test]
fn test_apr_interest() {
const EPSILON: f64 = 1e-9;

// Standard cases
assert!(
(apr_interest(10000.0, 0.05, 3.0).unwrap() - 1_618.223_072_263_547).abs() < EPSILON
);
assert!(
(apr_interest(10000.0, 0.05, 1.0).unwrap() - 512.674_964_674_473_2).abs() < EPSILON
);
assert!((apr_interest(0.5, 0.05, 3.0).unwrap() - 0.080_911_153_613_177_36).abs() < EPSILON);

// Zero interest rate yields zero interest
assert!((apr_interest(10000.0, 0.0, 5.0).unwrap() - 0.0).abs() < EPSILON);

// Error cases
assert_eq!(
apr_interest(-5500.0, 0.01, 5.0),
Err("principal must be > 0")
);
assert_eq!(
apr_interest(10000.0, -3.5, 3.0),
Err("nominal_annual_percentage_rate must be >= 0")
);
assert_eq!(
apr_interest(10000.0, 0.06, -4.0),
Err("number_of_years must be > 0")
);
}
}
4 changes: 2 additions & 2 deletions src/financial/mod.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
mod compound_interest;
mod equated_monthly_installments;
mod exponential_moving_average;
mod finance_ratios;
mod interest;
mod npv;
mod npv_sensitivity;
mod payback;
mod present_value;
mod treynor_ratio;

pub use self::compound_interest::compound_interest;
pub use self::equated_monthly_installments::equated_monthly_installments;
pub use self::exponential_moving_average::exponential_moving_average;
pub use self::finance_ratios::{
debt_to_equity, earnings_per_sale, gross_profit_margin, return_on_investment,
};
pub use self::interest::{apr_interest, compound_interest, simple_interest};
pub use self::npv::npv;
pub use self::npv_sensitivity::npv_sensitivity;
pub use self::payback::payback;
Expand Down