com_croftsoft_core/math/finance_lib/mod.rs
1// =============================================================================
2//! - Financial calculations
3//!
4//! # Usage
5//! - Since any time period can be used, "payment stream" equals "annuity"
6//!
7//! # Metadata
8//! - Copyright: © 1999 - 2022 [`CroftSoft Inc`]
9//! - Author: [`David Wallace Croft`]
10//! - Rust version: 2022-08-23
11//! - Rust since: 2022-07-30
12//! - Java version: 2001-10-10
13//! - Java since: 1999-08-15
14//!
15//! # History
16//! - Adapted from the Java class com.croftsoft.core.math.FinanceLib
17//! - In the Java-based [`CroftSoft Core Library`]
18//!
19//! [`CroftSoft Core Library`]: https://www.croftsoft.com/library/code/
20//! [`CroftSoft Inc`]: https://www.croftsoft.com/
21//! [`David Wallace Croft`]: https://www.croftsoft.com/people/david/
22// =============================================================================
23
24// -----------------------------------------------------------------------------
25/// Replaced by PeriodicSavingsNeeded
26// -----------------------------------------------------------------------------
27#[deprecated(since = "0.2.0", note = "Replaced by PeriodicSavingsNeeded")]
28pub fn annual_savings_needed(
29 f: f64,
30 r: f64,
31 t: f64,
32) -> f64 {
33 f * r / ((1.0 + r).powf(t) - 1.0)
34}
35
36// -----------------------------------------------------------------------------
37/// Calculates the future value of a cash flow received today
38///
39/// # Example
40/// ```
41/// use com_croftsoft_core::math::finance_lib::FutureValue;
42/// assert_eq!(
43/// FutureValue {
44/// cash_flow: 1.0, // Investing $1 today
45/// interest_rate: 0.12, // At 12% per year
46/// time_periods: 6.0, // For six years with interest compounded annually
47/// }.calculate(),
48/// 1.973_822_685_184_001); // Will double your money (see the "Rule of 72")
49/// ```
50// -----------------------------------------------------------------------------
51#[derive(Clone, Copy, Debug)]
52pub struct FutureValue {
53 /// Cash flow invested or received today
54 pub cash_flow: f64,
55 /// Interest, discount, or inflation rate (use 0.01 for 1%)
56 pub interest_rate: f64,
57 /// Number of periods from today when the value is evaluated
58 pub time_periods: f64,
59}
60
61impl FutureValue {
62 pub fn calculate(&self) -> f64 {
63 let c = self.cash_flow;
64 let r = self.interest_rate;
65 let t = self.time_periods;
66 c * (1.0 + r).powf(t)
67 }
68}
69
70// -----------------------------------------------------------------------------
71/// Calculates the future value of a payment stream such as an annuity
72///
73/// # Example
74/// ```
75/// use com_croftsoft_core::math::finance_lib::FutureValuePaymentStream;
76/// assert_eq!(
77/// FutureValuePaymentStream {
78/// cash_income: 10_000.0, // Future payments of $10k per year
79/// interest_rate: 0.10, // With 10% interest annually on the payments
80/// time_periods: 10.0, // Paying each year for ten years
81/// }.calculate(),
82/// 159_374.246_010_000_2); // Will be worth ~$159k in the future
83/// ```
84// -----------------------------------------------------------------------------
85#[derive(Clone, Copy, Debug)]
86pub struct FutureValuePaymentStream {
87 /// Periodic cash income payment starting one period from today
88 pub cash_income: f64,
89 /// Periodic interest earned on income (use 0.01 for 1%)
90 pub interest_rate: f64,
91 /// Number of periods of cash income
92 pub time_periods: f64,
93}
94
95impl FutureValuePaymentStream {
96 pub fn calculate(&self) -> f64 {
97 let c = self.cash_income;
98 let r = self.interest_rate;
99 let t = self.time_periods;
100 c * ((1.0 + r).powf(t) - 1.0) / r
101 }
102}
103
104// -----------------------------------------------------------------------------
105/// The calculated discount rate where the net present value is zero
106///
107/// # Examples
108/// ```
109/// use com_croftsoft_core::math::finance_lib::*;
110/// static POSITIVE_FUTURE_CASH_FLOWS: [f64; 3] = [
111/// -2.0, // $2 paid out today
112/// 1.10, // $1.10 received a year from today
113/// 1.21]; // and another $1.21 received two years from today
114/// static NEGATIVE_FUTURE_CASH_FLOWS: [f64; 3] = [
115/// 2.0, // $2 received today
116/// -1.10, // $1.10 paid out a year from today
117/// -1.21]; // and another $1.21 paid out two years from today
118/// static IRR_ESTIMATE: f64 = 0.01; // Initial IRR estimate is 1% per year
119/// assert_eq!(
120/// InternalRateOfReturn {
121/// cash_flows: &POSITIVE_FUTURE_CASH_FLOWS,
122/// irr_estimate: IRR_ESTIMATE,
123/// }.calculate().unwrap(),
124/// 0.09999999999999998); // Calculated IRR ~10%
125/// assert_eq!(
126/// InternalRateOfReturn {
127/// cash_flows: &POSITIVE_FUTURE_CASH_FLOWS,
128/// irr_estimate: 0.0, // Use zero when there is no initial estimate
129/// }.calculate().unwrap(),
130/// 0.09999999999999998); // Calculated IRR ~10%
131/// assert_eq!(
132/// InternalRateOfReturn {
133/// cash_flows: &NEGATIVE_FUTURE_CASH_FLOWS,
134/// irr_estimate: IRR_ESTIMATE,
135/// }.calculate().unwrap(),
136/// 0.09999999999999998); // Calculated IRR ~10%
137/// assert_eq!(
138/// InternalRateOfReturn {
139/// cash_flows: &[-1.0, 1.0], // A dollar paid today is returned in a year
140/// irr_estimate: IRR_ESTIMATE,
141/// }.calculate().unwrap(),
142/// -1.1053349683155043e-17); // Calculated IRR ~0%
143/// assert_eq!(
144/// InternalRateOfReturn {
145/// cash_flows: &[0.0, -1.0, 1.1], // The first cash flow is in the future
146/// irr_estimate: IRR_ESTIMATE,
147/// }.calculate().unwrap(),
148/// 0.10000000000000009); // Calculated IRR ~10%
149/// assert_eq!(
150/// InternalRateOfReturn {
151/// cash_flows: &[1.0, 0.0],
152/// irr_estimate: IRR_ESTIMATE,
153/// }.calculate().unwrap_err(),
154/// InternalRateOfReturnError::CashFlowsAllNonNegative);
155/// assert_eq!(
156/// InternalRateOfReturn {
157/// cash_flows: &[-1.0, 0.0],
158/// irr_estimate: IRR_ESTIMATE,
159/// }.calculate().unwrap_err(),
160/// InternalRateOfReturnError::CashFlowsAllNonPositive);
161/// assert_eq!(
162/// InternalRateOfReturn {
163/// cash_flows: &[0.0, 0.0], // All zero cash flows
164/// irr_estimate: IRR_ESTIMATE,
165/// }.calculate().unwrap_err(),
166/// InternalRateOfReturnError::CashFlowsAllZero);
167/// assert_eq!(
168/// InternalRateOfReturn {
169/// cash_flows: &[], // Zero length cash flows
170/// irr_estimate: IRR_ESTIMATE,
171/// }.calculate().unwrap_err(),
172/// InternalRateOfReturnError::CashFlowsLengthLessThanTwo);
173/// assert_eq!(
174/// InternalRateOfReturn {
175/// cash_flows: &[-1.0], // Single length cash flows
176/// irr_estimate: IRR_ESTIMATE,
177/// }.calculate().unwrap_err(),
178/// InternalRateOfReturnError::CashFlowsLengthLessThanTwo);
179/// ```
180// -----------------------------------------------------------------------------
181#[derive(Clone, Debug)]
182pub struct InternalRateOfReturn<'a> {
183 /// Array of cash flows received in the future, indexed from time zero
184 pub cash_flows: &'a [f64],
185 /// Initial estimate for the IRR (use 0.10 for 10%; use 0.0 for no estimate)
186 pub irr_estimate: f64,
187}
188
189#[derive(Debug, Eq, PartialEq)]
190pub enum InternalRateOfReturnError {
191 CashFlowsAllNonNegative,
192 CashFlowsAllNonPositive,
193 CashFlowsAllZero,
194 CashFlowsLengthLessThanTwo,
195}
196
197impl<'a> InternalRateOfReturn<'a> {
198 pub fn calculate(&self) -> Result<f64, InternalRateOfReturnError> {
199 if self.cash_flows.len() < 2 {
200 return Err(InternalRateOfReturnError::CashFlowsLengthLessThanTwo);
201 }
202 let mut has_a_negative_cash_flow = false;
203 let mut has_a_positive_cash_flow = false;
204 for cash_flow in self.cash_flows {
205 if *cash_flow < 0.0 {
206 has_a_negative_cash_flow = true;
207 if has_a_positive_cash_flow {
208 break;
209 }
210 } else if *cash_flow > 0.0 {
211 has_a_positive_cash_flow = true;
212 if has_a_negative_cash_flow {
213 break;
214 }
215 }
216 }
217 if has_a_negative_cash_flow {
218 if !has_a_positive_cash_flow {
219 return Err(InternalRateOfReturnError::CashFlowsAllNonPositive);
220 }
221 } else if has_a_positive_cash_flow {
222 return Err(InternalRateOfReturnError::CashFlowsAllNonNegative);
223 } else {
224 return Err(InternalRateOfReturnError::CashFlowsAllZero);
225 }
226 let mut irr: f64 = self.irr_estimate;
227 let mut delta_irr: f64 = -0.1 * irr;
228 if delta_irr == 0.0 {
229 delta_irr = 0.001;
230 }
231 let mut old_npv: f64 = core::f64::NAN;
232 Ok(loop {
233 let npv: f64 = NetPresentValue {
234 cash_flows: self.cash_flows,
235 discount_rate: irr,
236 }
237 .calculate();
238 if npv == 0.0 {
239 break irr;
240 }
241 if old_npv < 0.0 {
242 if npv > 0.0 {
243 delta_irr *= -0.9;
244 } else if npv > old_npv {
245 delta_irr *= 1.1;
246 } else if npv < old_npv {
247 delta_irr = -delta_irr;
248 } else {
249 // where npv == old_npv
250 break irr;
251 }
252 } else if old_npv > 0.0 {
253 if npv < 0.0 {
254 delta_irr *= -0.9;
255 } else if npv < old_npv {
256 delta_irr *= 1.1;
257 } else if npv > old_npv {
258 delta_irr = -delta_irr;
259 } else {
260 // where npv == old_npv
261 break irr;
262 }
263 }
264 if delta_irr == 0.0 {
265 break irr;
266 }
267 irr += delta_irr;
268 old_npv = npv;
269 })
270 }
271}
272
273// -----------------------------------------------------------------------------
274/// The discounted value of multiple cash flows received in the future
275///
276/// # Example
277/// ```
278/// use com_croftsoft_core::math::finance_lib::NetPresentValue;
279/// assert_eq!(
280/// NetPresentValue {
281/// cash_flows: &[1.0], // A dollar today
282/// discount_rate: 0.10, // At a discount rate of 10% per time period
283/// }.calculate(),
284/// 1.0); // Is worth a dollar today
285/// assert_eq!(
286/// NetPresentValue {
287/// cash_flows: &[0.0, 1.0], // A dollar next year
288/// discount_rate: 0.10, // At a discount rate of 10% per year
289/// }.calculate(),
290/// 0.9090909090909091); // Is worth ~$0.91 today
291/// assert_eq!(
292/// NetPresentValue {
293/// cash_flows: &[0.0, 0.0, 1.0], // A dollar received in two years
294/// discount_rate: 0.10, // Discounted at 10% per year
295/// }.calculate(),
296/// 0.8264462809917354); // Is worth ~$0.83 today
297/// assert_eq!(
298/// NetPresentValue {
299/// cash_flows: &[1.0; 11], // $1 today plus $1 per year for 10 years
300/// discount_rate: 0.10, // At a discount rate of 10% per year
301/// }.calculate(),
302/// 7.144567105704681); // Is worth ~$7.14 today
303/// ```
304// -----------------------------------------------------------------------------
305#[derive(Clone, Debug)]
306pub struct NetPresentValue<'a> {
307 // Cash flows received in the future indexed from time zero
308 pub cash_flows: &'a [f64],
309 /// The discount rate or cost of capital (use 0.01 for 1%)
310 pub discount_rate: f64,
311}
312
313impl<'a> NetPresentValue<'a> {
314 pub fn calculate(&self) -> f64 {
315 self.cash_flows.iter().enumerate().fold(0.0, |sum, (index, cash_flow)| {
316 sum + cash_flow / (1.0 + self.discount_rate).powf(index as f64)
317 })
318 }
319}
320
321// -----------------------------------------------------------------------------
322/// Calculates the periodic investments required to accumulate a future value
323///
324/// # Examples
325/// ```
326/// use com_croftsoft_core::math::finance_lib::PeriodicSavingsNeeded;
327/// assert_eq!(
328/// PeriodicSavingsNeeded {
329/// future_value: 1_000_000.0, // To have a million dollars in the future
330/// interest_rate: 0.12, // At 12% interest compounded annually
331/// time_periods: 10.0, // Investing each year for ten years
332/// }.calculate(),
333/// 56_984.164_159_844_026); // Invest ~$57k per year
334/// assert_eq!(
335/// PeriodicSavingsNeeded {
336/// future_value: 100_000_000.0, // To have a hundred million cents
337/// interest_rate: 0.01, // At 1% interest compounded monthly
338/// time_periods: 120.0, // Investing each month for 120 months
339/// }.calculate(),
340/// 434_709.484_025_873_1); // Invest ~435k cents per month (~$52k per year)
341/// let mut calculated_values = [0.0; 12];
342/// let mut periodic_savings_needed = PeriodicSavingsNeeded {
343/// future_value: 1_000_000.0,
344/// interest_rate: 0.00,
345/// time_periods: 10.0,
346/// };
347/// for index in 0..12 {
348/// periodic_savings_needed.interest_rate = (index + 1) as f64 / 100.0;
349/// calculated_values[index] = periodic_savings_needed.calculate();
350/// }
351/// assert_eq!(calculated_values[ 0], 95_582.076_551_171_35 ); // @ 1%
352/// assert_eq!(calculated_values[ 4], 79_504.574_965_456_62 ); // @ 5%
353/// assert_eq!(calculated_values[ 7], 69_029.488_697_075_34 ); // @ 8%
354/// assert_eq!(calculated_values[11], 56_984.164_159_844_026); // @ 12%
355/// ```
356// -----------------------------------------------------------------------------
357#[derive(Clone, Copy, Debug)]
358pub struct PeriodicSavingsNeeded {
359 /// Future value desired
360 pub future_value: f64,
361 /// Periodic interest rate (use 0.01 for 1%)
362 pub interest_rate: f64,
363 /// Number of time periods of investment
364 pub time_periods: f64,
365}
366
367impl PeriodicSavingsNeeded {
368 pub fn calculate(&self) -> f64 {
369 let f = self.future_value;
370 let r = self.interest_rate;
371 let t = self.time_periods;
372 f * r / ((1.0 + r).powf(t) - 1.0)
373 }
374}
375
376// -----------------------------------------------------------------------------
377/// The discounted value of a single cash flow received in the future
378///
379/// # Example
380/// ```
381/// use com_croftsoft_core::math::finance_lib::PresentValue;
382/// assert_eq!(
383/// PresentValue {
384/// cash_flow: 1.0, // A dollar in the future
385/// discount_rate: 0.10, // With inflation at 10% per year
386/// time_periods: 1.0, // Received one year from now
387/// }.calculate(),
388/// 0.9090909090909091); // Will have the spending power of ~$0.91 today
389/// assert_eq!(
390/// PresentValue {
391/// cash_flow: 1.0, // A dollar in the future
392/// discount_rate: 0.10, // If it could be invested today at 10% per year
393/// time_periods: 2.0, // Received two years from now
394/// }.calculate(),
395/// 0.8264462809917354); // Would be worth the same as ~$0.83 invested today
396/// ```
397// -----------------------------------------------------------------------------
398#[derive(Clone, Copy, Debug)]
399pub struct PresentValue {
400 // Cash flow received in the future
401 pub cash_flow: f64,
402 /// The discount rate or inflation rate per time period (use 0.01 for 1%)
403 pub discount_rate: f64,
404 /// Number of time periods from today when the cash flow is received
405 pub time_periods: f64,
406}
407
408impl PresentValue {
409 pub fn calculate(&self) -> f64 {
410 let c = self.cash_flow;
411 let r = self.discount_rate;
412 let t = self.time_periods;
413 c / (1.0 + r).powf(t)
414 }
415}
416
417// -----------------------------------------------------------------------------
418/// The discounted value of varying periodic cash flows
419///
420/// # Example
421/// ```
422/// use com_croftsoft_core::math::finance_lib::PresentValueCashFlows;
423/// assert_eq!(
424/// PresentValueCashFlows {
425/// cash_flows: &[1.0], // A dollar received one year in the future
426/// discount_rate: 0.10, // With inflation at 10% per year
427/// }.calculate(),
428/// 0.9090909090909091); // Will have the spending power of ~$0.91 today
429/// assert_eq!(
430/// PresentValueCashFlows {
431/// cash_flows: &[0.0, 1.0], // A dollar received in two years
432/// discount_rate: 0.10, // With interest at 10% per year
433/// }.calculate(),
434/// 0.8264462809917354); // Would be worth the same as ~$0.83 invested today
435/// assert_eq!(
436/// PresentValueCashFlows {
437/// cash_flows: &[1.0, 2.0, 3.0], // $1, $2, and $3 over 3 years
438/// discount_rate: 0.0, // With no inflation
439/// }.calculate(),
440/// 6.0); // Would be worth $6 today
441/// assert_eq!(
442/// PresentValueCashFlows {
443/// cash_flows: &[1.0, 2.0, 3.0], // $1, $2, and $3 over 3 years
444/// discount_rate: 0.10, // With inflation at 10% per year
445/// }.calculate(),
446/// 4.8159278737791125); // Would be worth ~$4.82 today
447/// ```
448// -----------------------------------------------------------------------------
449#[derive(Clone, Debug)]
450pub struct PresentValueCashFlows<'a> {
451 // Cash flows received in the future starting one time period from now
452 pub cash_flows: &'a [f64],
453 /// The discount rate or inflation rate per time period (use 0.01 for 1%)
454 pub discount_rate: f64,
455}
456
457impl<'a> PresentValueCashFlows<'a> {
458 pub fn calculate(&self) -> f64 {
459 self.cash_flows.iter().enumerate().fold(0.0, |sum, (index, cash_flow)| {
460 sum
461 + PresentValue {
462 cash_flow: *cash_flow,
463 discount_rate: self.discount_rate,
464 time_periods: (index + 1) as f64,
465 }
466 .calculate()
467 })
468 }
469}
470
471// -----------------------------------------------------------------------------
472/// Calculates the present value of a payment stream such as an annuity
473///
474/// # Example
475/// ```
476/// use com_croftsoft_core::math::finance_lib::PresentValuePaymentStream;
477/// assert_eq!(
478/// PresentValuePaymentStream {
479/// cash_flow : 1.0, // A dollar every year starting a year from today
480/// inflation_rate: 0.10, // With inflation at 10% per year
481/// time_periods: 10.0, // For ten years
482/// }.calculate(),
483/// 6.144567105704685); // Is the same as receiving ~$6.14 today
484/// ```
485// -----------------------------------------------------------------------------
486#[derive(Clone, Copy, Debug)]
487pub struct PresentValuePaymentStream {
488 // Periodic cash income staring one time period from today
489 pub cash_flow: f64,
490 /// The inflation rate or interest rate per time period (use 0.01 for 1%)
491 pub inflation_rate: f64,
492 /// Number of time periods of cash income
493 pub time_periods: f64,
494}
495
496impl PresentValuePaymentStream {
497 pub fn calculate(&self) -> f64 {
498 let c = self.cash_flow;
499 let r = self.inflation_rate;
500 let t = self.time_periods;
501 c * (1.0 - 1.0 / (1.0 + r).powf(t)) / r
502 }
503}