finance_solution/cashflow/
present_value_annuity.rs

1#![allow(unused_imports)]
2
3//! **Present value _annuity_ calculations**. Given a series of cashflows, a number of periods such as years, and fixed
4//! or varying interest rates, what is the current value of the series of cashflows (annuity) right now?
5//! 
6//! For most common usages, we recommend the [`present_value_annuity_solution`](./fn.present_value_annuity_solution.html) function, which provides a better debugging experience and additional features.
7//! 
8//! ## Example
9//! ```
10//! # use finance_solution::present_value_annuity_solution; // this function will be hidden
11//! let (rate, periods, annuity, due) = (0.034, 10, 500, false);
12//! let pv_ann = present_value_annuity_solution(rate, periods, annuity, due);
13//! dbg!(pv_ann);
14//! ```
15//! Outputs to terminal:
16//! ```text
17//! {
18//! calculated_field: PresentValueAnnuity,
19//! rate: 0.034,
20//! periods: 10,
21//! present_value: -4179.341028819192,
22//! future_value: -5838.660162934531,
23//! due_at_beginning: false,
24//! payment: 500.0,
25//! sum_of_payments: 5000.0,
26//! sum_of_interest: -5018.001191753723,
27//! formula: "-500 * ((1. - (1. / (1. + 0.034)).powf(10)) / 0.034) * (1 + (0.034 * 0));",
28//! symbolic_formula: "-annuity * ((1. - (1. / (1. + rate)).powf(periods)) / rate) * (1. + (rate * due));",
29//! }
30//! ```
31//!  
32
33// to do: add "use log::warn;" and helper logs
34
35// Needed for the Rustdoc comments.
36use crate::present_value::present_value;
37use crate::future_value::future_value;
38use crate::cashflow::*;
39
40/// Returns the **present value of an annuity** (series of constant cashflows) at a constant rate. Returns f64.
41///
42/// The present value annuity formula is (both yield the same result):
43///
44/// present_value = sum( cashflow / (1 + rate)<sup>period</sup> )
45/// 
46/// or
47/// 
48/// present value = annuity * ((1. - (1. / (1. + rate)).powf(periods)) / rate)
49///
50/// # Arguments
51/// * `rate` - The rate at which the investment grows or shrinks per period,
52/// expressed as a floating point number. For instance 0.05 would mean 5%. Often appears as
53/// `r` or `i` in formulas.
54/// * `periods` - The number of periods such as quarters or years. Often appears as `n` or `t`.
55/// * `cashflow` - The value of the constant cashflow (aka payment, or annuity).
56/// * `due_at_beginning` - True if the payment is due at the beginning of the period. Typically the
57/// payment will be due at the end of the period so this will be false.
58///
59/// # Panics
60/// The call will fail if `rate` is less than -1.0 as this would mean the investment is
61/// losing more than its full value every period.
62///
63/// # Examples
64/// 
65/// Quick glance, how to use:
66/// ```
67/// # use finance_solution::*;
68/// let (rate, periods, annuity, due_at_beginning)  = (0.034, 10, 21_000, false);
69/// let my_annuity = present_value_annuity_solution(rate, periods, annuity, due_at_beginning);
70/// dbg!(my_annuity);
71/// ```
72/// 
73/// Present value of a series of $2000 cashflows.
74/// ```
75/// # use finance_solution::*;
76/// // The rate is 2.1% per month.
77/// let rate = 0.021;
78///
79/// // The investment will grow for 12 months.
80/// let periods = 12;
81///
82/// // The cashflow will be $2,000.
83/// let cashflow = 2_000;
84///
85/// let due_at_beginning = false;
86///
87/// // Find the current value.
88/// let present_value_ann = present_value_annuity(rate, periods, cashflow, due_at_beginning);
89/// dbg!(&present_value_ann);
90///
91/// // Confirm that the present value is correct to four decimal places (one hundredth of a cent).
92/// assert_approx_equal!(-21021.368565, present_value_ann);
93/// ```
94pub fn present_value_annuity<T>(rate: f64, periods: u32, annuity: T, due_at_beginning: bool) -> f64
95    where T: Into<f64> + Copy
96{
97    let pmt = annuity.into();
98    // check_present_value__annuity_parameters(rate, periods, cashflow);
99    let pv_ann = -(1. + (rate * due_at_beginning as u32 as f64)) * pmt * ((1. - (1. / (1. + rate)).powf(periods as f64)) / rate);
100    pv_ann
101
102}
103
104pub fn present_value_annuity_accumulator<T>(rate: f64, periods: u32, annuity: T, due_at_beginning: bool) -> f64
105    where T: Into<f64> + Copy
106{
107    let pmt = annuity.into();
108    // check_present_value__annuity_parameters(rate, periods, cashflow);
109
110    let mut pv_accumulator = if due_at_beginning {
111        (1. + rate) * pmt
112    } else {
113        0.0
114    };
115    for i in 1..=periods {
116        let present_value = present_value(rate, i as u32, pmt, false);
117        pv_accumulator += present_value;
118    }
119    pv_accumulator
120}
121
122// / Returns the present value of a series of cashflows and rates, which can be varying. Receives vectors and returns f64.
123// /
124// / Related functions:
125// / * To calculate a present value with a constant cashflow and rate, use [`present_value_annuity`].
126// /
127// / The present value annuity formula is:
128// /
129// / present_value = sum( cashflow / (1 + rate)<sup>period</sup> )
130// /
131// / # Arguments
132// / * `rate` - The rate at which the investment grows or shrinks per period,
133// / expressed as a floating point number. For instance 0.05 would mean 5%. Often appears as
134// / `r` or `i` in formulas.
135// / * `periods` - The number of periods such as quarters or years. Often appears as `n` or `t`.
136// / * `cashflow` - The value of the cashflow at the time of that period (ie, future value).
137// /
138// / # Panics
139// / The call will fail if `rate` is less than -1.0 as this would mean the investment is
140// / losing more than its full value every period. 
141// /
142// / # Examples
143// / Present value of a series of $2000 cashflows.
144// / ```
145// / // The rate is varying each month. 
146// / let rates = vec![0.021, 0.028, 0.019];
147// / 
148// / // The cashflow will be $2,000.
149// / // The number of periods is inferred by the length of the vector.
150// / // The rep! macro is used to create a vector of repeating values.
151// / // let cashflows = finance_solution::repeat!(2_000, rate.len());
152// / let  cashflows = vec![2000,2000,2000];
153// / 
154// / // Find the current value.
155// / let present_value_ann = finance_solution::present_value_annuity_schedule(rates, cashflows);
156// / dbg!(&present_value_ann);
157// /
158// / // Confirm that the present value is correct to four decimal places (one hundredth of a cent).
159// / // finance_solution::assert_approx_equal!( , present_value_ann);
160// / ```
161// / 
162 
163// pub fn present_value_annuity_schedule<T>(rates: &[f64], cashflows: &[T]) -> f64
164//     where T: Into<f64> + Copy
165// {
166//     // check_present_value__annuity_varying_parameters(rate, periods, cashflow);
167
168//     // update 
169//     let periods = rates.len();
170
171//     let mut pv_accumulator = 0_f64;
172//     for i in 0..periods { 
173//         let pmt = cashflows[i].into();
174//         let rate = rates[i];
175//         let present_value = present_value(rate, i as u32, pmt);
176//         pv_accumulator = pv_accumulator + present_value;
177//     }
178//     pv_accumulator
179// }
180
181
182/// Returns the present value of a future series of constant cashflows and constant rate. Returns custom solution type with additional information and functionality.
183///
184/// Related functions:
185/// * To calculate a present value returning an f64, use [`present_value_annuity`].
186/// * To calculate a present value with a varying rate or varying cashflow or both, use [`present_value_annuity_schedule`].
187///
188/// The present value annuity formula is:
189///
190/// present_value = sum( cashflow / (1 + rate)<sup>period</sup> )
191/// or
192/// present value = annuity * ((1. - (1. / (1. + rate)).powf(periods)) / rate)
193/// 
194/// # Arguments
195/// * `rate` - The rate at which the investment grows or shrinks per period,
196/// expressed as a floating point number. For instance 0.05 would mean 5%. Often appears as
197/// `r` or `i` in formulas.
198/// * `periods` - The number of periods such as quarters or years. Often appears as `n` or `t`.
199/// * `cashflow` - The value of the constant cashflow (aka payment).
200/// * `due_at_beginning` - True if the payment is due at the beginning of the period. Typically the
201/// payment will be due at the end of the period so this will be false.
202///
203// / # Panics
204// / The call will fail if `rate` is less than -1.0 as this would mean the investment is
205// / losing more than its full value every period.
206// /
207/// # Examples
208/// Present value of a $500 annuity (a series of $500 cashflows).
209/// ```
210/// # use finance_solution::*;
211/// // The rate is 3.4% per month.
212/// let rate = 0.034;
213///
214/// // The annuity will provide yearly payments for 10 years.
215/// let periods = 10;
216///
217/// // The cashflow will be $500.
218/// let cashflow = 500;
219///
220/// let due_at_beginning = false;
221///
222/// // Find the current value.
223/// let present_value_ann = present_value_annuity_solution(rate, periods, cashflow, due_at_beginning);
224/// dbg!(&present_value_ann);
225/// ```
226/// Outputs to terminal:
227/// ```text
228///  {
229///      calculated_field: PresentValueAnnuity,
230///      rate: 0.034,
231///      periods: 10,
232///      present_value: -4179.341028819192,
233///      future_value: -5838.660162934531,
234///      due_at_beginning: false,
235///      payment: 500.0,
236///      sum_of_payments: 5000.0,
237///      sum_of_interest: -5018.001191753723,
238///      formula: "-500 * ((1. - (1. / (1. + 0.034)).powf(10)) / 0.034) * (1 + (0.034 * 0));",
239///      symbolic_formula: "-annuity * ((1. - (1. / (1. + rate)).powf(periods)) / rate) * (1. + (rate * due));",
240///  }
241/// ```
242pub fn present_value_annuity_solution<T>(rate: f64, periods: u32, cashflow: T, due_at_beginning: bool) -> CashflowSolution
243    where T: Into<f64> + Copy
244{
245    let annuity = cashflow.into();
246    let pv = present_value_annuity(rate, periods, annuity, due_at_beginning);
247    let pvann_type = if due_at_beginning {
248        CashflowVariable::PresentValueAnnuityDue
249    } else {
250        CashflowVariable::PresentValueAnnuity
251    };
252    // check_present_value__annuity_varying_parameters(rate, periods, cashflow);
253    let formula = format!("-{} * ((1. - (1. / (1. + {})).powf({})) / {}) * (1 + ({} * {}));", annuity, rate, periods, rate, rate, due_at_beginning as u32 as f64);
254    let formula_symbolic = format!("-annuity * ((1. - (1. / (1. + rate)).powf(periods)) / rate) * (1. + (rate * due));");
255    // let fv = future_value_annuity(rate, periods, cashflow);
256    let fv = future_value(rate, periods, pv, false);
257    CashflowSolution::new(pvann_type, rate, periods, pv, fv, due_at_beginning, annuity, &formula, &formula_symbolic)
258}
259
260#[cfg(test)]
261mod tests {
262    use super::*;
263    use crate::*;
264
265#[test]
266    fn test_present_value_annuity_1() {
267        // one period
268        let (rate, periods, annuity) = (0.034, 1, 500);
269        let pv = present_value_annuity(rate, periods, annuity, false);
270        assert_eq!(-483.55899, (pv * 100000.).round() / 100000.);
271    }
272    #[test]
273    fn test_present_value_annuity_2() {
274        // big periods
275        let (rate, periods, annuity) = (0.034, 400, 500);
276        let pv = present_value_annuity(rate, periods, annuity, false);
277        assert_eq!(-14705.85948, (pv * 100000.).round() / 100000.);
278    }
279    #[test]
280    fn test_present_value_annuity_due_2() {
281        // big periods, due
282        let (rate, periods, annuity) = (0.034, 400, 500);
283        let pv = present_value_annuity(rate, periods, annuity, true);
284        assert_eq!(-15205.8587, (pv * 100000.).round() / 100000.);
285    }
286
287    #[test]
288    fn test_present_value_annuity_3() {
289        // negative rate
290        let (rate, periods, annuity) = (-0.034, 52, 500);
291        let pv = present_value_annuity(rate, periods, annuity, false);
292        assert_eq!(-74_148.8399, (pv * 100000.).round() / 100000.);
293    }
294
295    #[test]
296    fn test_present_value_annuity_4() {
297        // big negative rate
298        let (rate, periods, annuity) = (-0.999, 3, 500);
299        let pv = present_value_annuity(rate, periods, annuity, false);
300        assert_eq!(-500_500_499_999.999, (pv * 1000.).round() / 1000.);
301    }
302
303    #[test]
304    fn test_present_value_annuity_due_4() {
305        // big negative rate, due
306        let (rate, periods, annuity) = (-0.999, 3, 500);
307        let pv = present_value_annuity(rate, periods, annuity, true);
308        assert_eq!(-500_500_499.999999, (pv * 1000000.).round() / 1000000.);
309    }
310
311    #[test]
312    fn test_present_value_annuity_5() {
313        // big precision
314        let (rate, periods, annuity) = (0.00034, 2_800, 5_000_000);
315        let pv = present_value_annuity(rate, periods, annuity, false);
316        assert_eq!(-9028959259.06, (pv * 100.).round() / 100.);
317    }
318
319}