Skip to content

Commit 5b64143

Browse files
authored
Merge branch 'master' into finstuff
2 parents 21541d8 + bd8418f commit 5b64143

File tree

5 files changed

+141
-0
lines changed

5 files changed

+141
-0
lines changed

DIRECTORY.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,11 @@
106106
* Financial
107107
* [Present Value](https://github.com/TheAlgorithms/Rust/blob/master/src/financial/present_value.rs)
108108
* [Net Present Value](https://github.com/TheAlgorithms/Rust/blob/master/src/financial/npv.rs)
109+
* [NPV Sensitivity](https://github.com/TheAlgorithms/Rust/blob/master/src/financial/npv_sensitivity.rs)
109110
* [Compound Interest](https://github.com/TheAlgorithms/Rust/blob/master/src/financial/compound_interest.rs)
110111
* [Payback Period](https://github.com/TheAlgorithms/Rust/blob/master/src/financial/payback.rs)
112+
* [Finance Ratios](https://github.com/TheAlgorithms/Rust/blob/master/src/financial/finance_ratios.rs)
113+
* [Treynor Ratio](https://github.com/TheAlgorithms/Rust/blob/master/src/financial/treynor_ratio.rs)
111114
* General
112115
* [Convex Hull](https://github.com/TheAlgorithms/Rust/blob/master/src/general/convex_hull.rs)
113116
* [Fisher Yates Shuffle](https://github.com/TheAlgorithms/Rust/blob/master/src/general/fisher_yates_shuffle.rs)

src/financial/finance_ratios.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Calculating simple ratios like Return on Investment (ROI), Debt to Equity, Gross Profit Margin
2+
// and Earnings per Sale (EPS)
3+
pub fn return_on_investment(gain: f64, cost: f64) -> f64 {
4+
(gain - cost) / cost
5+
}
6+
7+
pub fn debt_to_equity(debt: f64, equity: f64) -> f64 {
8+
debt / equity
9+
}
10+
11+
pub fn gross_profit_margin(revenue: f64, cost: f64) -> f64 {
12+
(revenue - cost) / revenue
13+
}
14+
15+
pub fn earnings_per_sale(net_income: f64, pref_dividend: f64, share_avg: f64) -> f64 {
16+
(net_income - pref_dividend) / share_avg
17+
}
18+
19+
#[cfg(test)]
20+
mod tests {
21+
use super::*;
22+
23+
#[test]
24+
fn test_return_on_investment() {
25+
// let gain = 1200, cost = 1000 thus, ROI = (1200 - 1000)/1000 = 0.2
26+
let result = return_on_investment(1200.0, 1000.0);
27+
assert!((result - 0.2).abs() < 0.001);
28+
}
29+
30+
#[test]
31+
fn test_debt_to_equity() {
32+
// let debt = 300, equity = 150 thus, debt to equity ratio = 300/150 = 2
33+
let result = debt_to_equity(300.0, 150.0);
34+
assert!((result - 2.0).abs() < 0.001);
35+
}
36+
37+
#[test]
38+
fn test_gross_profit_margin() {
39+
// let revenue = 1000, cost = 800 thus, gross profit margin = (1000-800)/1000 = 0.2
40+
let result = gross_profit_margin(1000.0, 800.0);
41+
assert!((result - 0.2).abs() < 0.01);
42+
}
43+
44+
#[test]
45+
fn test_earnings_per_sale() {
46+
// let net_income = 350, pref_dividend = 50, share_avg = 25 this EPS = (350-50)/25 = 12
47+
let result = earnings_per_sale(350.0, 50.0, 25.0);
48+
assert!((result - 12.0).abs() < 0.001);
49+
}
50+
}

src/financial/mod.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
11
mod compound_interest;
2+
mod finance_ratios;
23
mod npv;
4+
mod npv_sensitivity;
35
mod payback;
46
mod present_value;
7+
mod treynor_ratio;
58
pub use compound_interest::compound_interest;
69
pub use npv::npv;
10+
pub use npv_sensitivity::npv_sensitivity;
711
pub use payback::payback;
812
pub use present_value::present_value;
13+
pub use treynor_ratio::treynor_ratio;
14+
15+
pub use finance_ratios::debt_to_equity;
16+
pub use finance_ratios::earnings_per_sale;
17+
pub use finance_ratios::gross_profit_margin;
18+
pub use finance_ratios::return_on_investment;

src/financial/npv_sensitivity.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/// Computes the Net Present Value (NPV) of a cash flow series
2+
/// at multiple discount rates to show sensitivity.
3+
///
4+
/// # Inputs:
5+
/// - `cash_flows`: A slice of cash flows, where each entry is a period value
6+
/// e.g., year 0 is initial investment, year 1+ are returns or costs
7+
/// - `discount_rates`: A slice of discount rates, e.g. `[0.05, 0.10, 0.20]`,
8+
/// where each rate is evaluated independently.
9+
///
10+
/// # Output:
11+
/// - Returns a vector of NPV values, each corresponding to a rate in `discount_rates`.
12+
/// For example, output is `[npv_rate1, npv_rate2, ...]`.
13+
14+
pub fn npv_sensitivity(cash_flows: &[f64], discount_rates: &[f64]) -> Vec<f64> {
15+
discount_rates
16+
.iter()
17+
.cloned()
18+
.map(|rate| {
19+
cash_flows
20+
.iter()
21+
.enumerate()
22+
.map(|(t, &cf)| cf / (1.0 + rate).powi(t as i32))
23+
.sum()
24+
})
25+
.collect()
26+
}
27+
28+
#[cfg(test)]
29+
mod tests {
30+
use super::*;
31+
#[test]
32+
fn test_npv_sensitivity() {
33+
let cashflows = [-1000.00, 400.00, 400.00, 400.00];
34+
let rates = [0.05, 0.1, 0.2];
35+
let expected = [89.30, -5.26, -157.41];
36+
let out = npv_sensitivity(&cashflows, &rates);
37+
assert_eq!(out.len(), 3);
38+
// value check
39+
for (o, e) in out.iter().zip(expected.iter()) {
40+
assert!((o - e).abs() < 0.1);
41+
}
42+
}
43+
}

src/financial/treynor_ratio.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/// Calculates the Treynor Ratio for a portfolio.
2+
///
3+
/// # Inputs
4+
/// - `portfolio_return`: Portfolio return
5+
/// - `risk_free_rate`: Risk-free rate
6+
/// - `beta`: Portfolio beta
7+
/// where Beta is a financial metric that measures the systematic risk of a security or portfolio compared to the overall market.
8+
///
9+
/// # Output
10+
/// - Returns excess return per unit of market risk
11+
pub fn treynor_ratio(portfolio_return: f64, risk_free_rate: f64, beta: f64) -> f64 {
12+
if beta == 0.0 {
13+
f64::NAN
14+
} else {
15+
(portfolio_return - risk_free_rate) / beta
16+
}
17+
}
18+
19+
#[cfg(test)]
20+
mod tests {
21+
use super::*;
22+
23+
#[test]
24+
fn test_treynor_ratio() {
25+
// for portfolio_return = 0.10, risk_free_rate = 0.05, beta = 1.5
26+
// expected result: (0.10 - 0.05) / 1.5 = 0.033333...
27+
assert!((treynor_ratio(0.10, 0.05, 1.50) - 0.03333).abs() < 0.01);
28+
}
29+
30+
#[test]
31+
fn test_treynor_ratio_empty_beta() {
32+
// test for zero beta (undefined ratio)
33+
assert!(treynor_ratio(0.10, 0.05, 0.00).is_nan());
34+
}
35+
}

0 commit comments

Comments
 (0)