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}