finance/
lib.rs

1// finance - rust library (crate)
2// GNU licensed, license file can be found at the root of the repository
3// Copyright 2016 - Mohamed Hayibor
4
5extern crate round;
6use round::round;
7
8//  present_value implementation (PV)
9pub fn present_value(rate: f64, compounding_periods: f64, future_value: f64) -> f64 {
10    let discount_factor: f64 = 1. + rate;
11
12    future_value / (discount_factor.powf(compounding_periods))
13}
14
15#[test]
16fn test_present_value() {
17    let test_value = present_value(0.1, 1., 1000.);
18    assert_eq!( round(test_value, 2), 909.09);
19}
20
21//  future_value implementation (FV)
22pub fn future_value(rate: f64, compounding_periods: f64, present_value: f64) -> f64 {
23    let compound_factor: f64 = 1. + rate;
24
25    present_value * (compound_factor.powf(compounding_periods))
26}
27
28#[test]
29fn test_future_value() {
30    let test_value = future_value(0.1, 1., 1000.);
31    assert_eq!( round(test_value, 2), 1100.00);
32}
33
34//  net_present_value implementation
35//  Here cfs means cash_flows: it can be a slice or vector
36//  cfs[0] being the cash flow at time 0
37//  refer to, if you're not sure how this works: https://en.wikipedia.org/wiki/Net_present_value#Interpretation_as_integral_transform
38pub fn net_present_value(rate: f64, cfs: &[f64]) -> f64 {
39    let discount_factor = 1. + rate;
40    let mut npv: f64 = 0.;
41
42    for n in 0..cfs.len() {
43        npv += cfs[n] / discount_factor.powf(n as f64);
44    }
45
46    npv
47}
48
49#[test]
50fn test_net_present_value() {
51    let test_npv = net_present_value(0.1, &[-1000., 500., 500., 500.]);
52
53    assert_eq!(round(test_npv, 2), 243.43);
54}
55
56// payment implementation > PMT in formulas
57pub fn payment(present_value: f64, number_of_compounding: f64, rate: f64) -> f64 {
58    present_value / ( (1. - (1. / (1. + rate).powf(number_of_compounding)) ) / rate )
59}
60
61#[test]
62fn main () {
63    let test_value = payment(190000., 30.0, 0.08);
64    assert_eq!(round(test_value, 2), 16877.21);
65}
66
67// implementing periodic_interest_rate
68pub fn periodic_interest_rate(annual_percentage_rate: f64, number_of_compounding: f64) -> f64 {
69    annual_percentage_rate / number_of_compounding
70}
71
72#[test]
73fn test_periodic_interest_rate() {
74    let test_value = periodic_interest_rate(0.10, 4.);
75
76    assert_eq!(round(test_value, 3), 0.025);
77}
78
79// implementing HPR > holding period return
80pub fn holding_period_return(profit: f64, cost: f64) -> f64 {
81    profit / cost
82}
83
84#[test]
85fn test_hpr() {
86    let test_value = holding_period_return(5000., 4000.);
87    assert_eq!(test_value, 1.25);
88}
89
90// implementing number of compounding > has the notation of n in formulas
91pub fn number_of_compounding(future_value: f64, present_value: f64, rate: f64) -> f64 {
92    (future_value / present_value).ln() / (1. + rate).ln()
93}
94
95#[test]
96fn test_number_of_compounding() {
97    let test_value = number_of_compounding(5000., 4000., 0.02);
98
99    assert_eq!(round(test_value, 2), 11.27);
100}
101
102//  Return On Investment (ROI) implementation
103// no rounding as ROI is super sensitive
104pub fn return_on_investment(earnings: f64, cf0: f64) -> f64 {
105    (earnings - cf0.abs()) / cf0.abs()
106}
107
108#[test]
109fn test_roi() {
110    let test_value = round( return_on_investment(5000., 4000.), 2);
111
112    assert_eq!(test_value, 0.25);
113}
114
115// implementing interest_rate sometimes called growth rate or discount rate
116pub fn interest_rate(future_value: f64, present_value: f64, number_of_compounding: f64) -> f64 {
117
118    // recip > takes the inverse of a number
119    (future_value / present_value).powf( number_of_compounding.recip() ) - 1.
120}
121
122#[test]
123fn test_interest_rate() {
124    let test_value = interest_rate(5000., 4000., 4.);
125    assert_eq!(round(test_value, 4), 0.0574);
126}
127
128
129//  Rule of 72 (quick and dirty calculation to estimate when an investment will double: https://en.wikipedia.org/wiki/Rule_of_72
130//  Please note that for consistency sake rate is getting passed as a plain not float and not as a percentage (%)
131pub fn rule_of_72(rate: f64) -> f64 {
132    72. / (rate * 100.)
133}
134
135#[test]
136fn test_rule_of_72() {
137    assert_eq!( round( rule_of_72(0.035), 2) , 20.57);
138}
139
140// Rule of 70
141pub fn rule_of_70(rate: f64) -> f64 {
142   70. / (rate * 100.)
143}
144
145#[test]
146fn test_rule_of_70() {
147    assert_eq!( round(rule_of_70(0.035), 2) , 20.);
148}
149
150//  Leverage ratio (LR) 
151pub fn leverage_ratio(total_liabilities: f64, total_debts: f64, total_income: f64) -> f64 {
152    ((total_liabilities + total_debts) / total_income)
153}
154
155#[test]
156fn test_leverage_ratio() {
157    let test_ratio = leverage_ratio(1000., 2000., 4000.);
158    assert_eq!( round( test_ratio, 2) , 0.75);
159}
160
161//  Weighted Cost of capital (WACC)
162//  Be aware that decimal formats are expected to be passed and not percentages.
163pub fn weighted_cost_of_capital(market_value_of_equity: f64, market_value_of_debt: f64, cost_of_equity: f64, cost_of_debt: f64, tax_rate: f64) -> f64 {
164    // champion of verbosity, lets abrreviate
165    let e = market_value_of_equity;
166    let d = market_value_of_debt;
167    // v: market value of the entity, no worries it is not taking owership as primitives have the copy trait implemented
168    let v = e + d;
169    let re = cost_of_equity;
170    let rd = cost_of_debt;
171    let t = tax_rate;
172
173    ((e / v) * re ) + (((d / v) * rd ) * (1. - t))
174}
175
176#[test]
177fn test_wacc() {
178    // implement an example later
179    let test_value = weighted_cost_of_capital(2000000.00, 1000000.00, 0.07, 0.05, 0.4);
180    assert_eq!( round(test_value, 4), 0.0567);
181}
182
183// implementing effective_annual_rate > EAR
184pub fn effective_annual_rate(annual_rate: f64, number_of_compounding: f64) -> f64 {
185    (1. + (annual_rate / number_of_compounding)).powf(number_of_compounding) - 1. 
186}
187
188#[test]
189fn test_effective_annual_rate () {
190    let test = effective_annual_rate(0.05, 12.);
191    assert_eq!(round(test, 4), 0.0512);
192}
193