finance_solution/cashflow/
future_value_annuity.rs

1#![allow(unused_imports)]
2
3//! **Future value _annuity_ calculations**. Given a series of cashflows, a number of periods such as years, and a fixed
4//! interest rate, what is the value of the series of cashflows (annuity) at the final payment?
5//! 
6//! For most common usages, we recommend the [`future_value_annuity_solution`](./fn.future_value_annuity_solution.html) function, which provides a better debugging experience and additional features.
7//! 
8//! ## Example
9//! ```
10//! let (rate, periods, annuity, due) = (0.034, 10, 500, false);
11//! let fv_ann = finance_solution::future_value_annuity_solution(rate, periods, annuity, due);
12//! dbg!(fv_ann);
13//! ```
14//! Outputs to terminal:
15//! ```text
16//! {
17//! calculated_field: FutureValueAnnuity,
18//! rate: 0.034,
19//! periods: 10,
20//! present_value: -4179.341028819186,
21//! future_value: -5838.660162934523,
22//! due_at_beginning: false,
23//! payment: 500.0,
24//! sum_of_payments: 5000.0,
25//! sum_of_interest: -5018.0011917537095,
26//! formula: "-500 * ((1. - (1. / (1. + 0.034)).powf(10)) / 0.034);",
27//! symbolic_formula: "-annuity * ((1. - (1. / (1. + rate)).powf(periods)) / rate);",
28//! }
29//! ```
30//! 
31
32// to-do: add "use log::warn;" and helper logs
33
34// Needed for the Rustdoc comments and module.
35use crate::future_value::future_value;
36use crate::present_value::present_value;
37use crate::cashflow::*;
38use crate::assert_approx_equal;
39
40fn check_future_value_annuity_parameters(rate:f64, periods:u32, cashflow:f64) {
41    assert!(rate > -1.0);
42    assert!(rate.is_finite());
43    assert!(cashflow.is_finite());
44    assert!(periods > 0);
45}
46
47/// Returns the future value of annuity (a series of constant cashflows) at a constant rate. Returns f64.
48/// 
49/// The future value annuity formula is:
50///
51/// future value ann = sum( cashflow * (1 + rate)<sup>period</sup> )
52/// 
53/// or
54/// 
55/// future value ann = Constant_Cashflow * ((1+periodic_rate)^n -1) / periodic_rate 
56///
57/// # Arguments
58/// * `rate` - The rate at which the investment grows or shrinks per period,
59/// expressed as a floating point number. For instance 0.05 would mean 5%. Often appears as
60/// `r` or `i` in formulas.
61/// * `periods` - The number of periods such as quarters or years. Often appears as `n` or `t`.
62/// * `cashflow` - The value of the constant cashflow (aka payment).
63/// * `due_at_beginning` - True if the payment is due at the beginning of the period. Typically the
64/// payment will be due at the end of the period so this will be false.
65///
66/// # Panics
67/// The call will fail if `rate` is less than -1.0 as this would mean the investment is
68/// losing more than its full value every period.
69///
70/// # Examples
71/// Quick Glance, how to use:
72/// ```
73/// use finance_solution::*;
74/// let (rate, periods, payment, due_at_beginning) = (0.034, 5, 500, false);
75/// let my_annuity = future_value_annuity(rate, periods, payment, due_at_beginning);
76/// assert_approx_equal!(my_annuity, -2_675.8789282); 
77/// ```
78/// 
79/// Or use the solution struct (recommended, more helpful to debugging and for student-learning)
80/// ```
81/// use finance_solution::*;
82/// let (rate, periods, pmt, due_at_beginning) = (0.034, 5, 500, false);
83/// let my_annuity = future_value_annuity_solution(rate, periods, pmt, due_at_beginning);
84/// dbg!(&my_annuity);
85/// ```
86/// Outputs to terminal: 
87/// ```text
88/// {
89///  calculated_field: FutureValueAnnuity,
90///  rate: 0.034,
91///  periods: 5,
92///  present_value: 2263.9340209510633,
93///  future_value: 2675.8789281680038,
94///  due_at_beginning: false,
95///  payment: 500.0,
96///  sum_of_payments: 2500.0,
97///  sum_of_interest: 7439.812949119067,
98///  formula: "500 * ((1. - (1. / (1. + 0.034)).powf(5)) / 0.034);",
99///  formula_symbolic: "annuity * ((1. - (1. / (1. + rate)).powf(periods)) / rate);",
100/// }
101/// ```
102/// ```
103/// # use finance_solution::*;
104/// # let (rate, periods, pmt, due_at_beginning) = (0.034, 5, 500, false);
105/// # let my_annuity = future_value_annuity_solution(rate, periods, pmt, due_at_beginning);
106/// // call the value as a method to get the solution value
107/// let final_answer = my_annuity.future_value();
108/// ```
109/// 
110/// Another example: Future value of a series of $2000 cashflows.
111/// ```
112/// # use finance_solution::*;
113/// // The rate is 2.1% per month.
114/// let rate = 0.021;
115///
116/// // The investment will grow for 12 months.
117/// let periods = 12;
118///
119/// // The cashflow will be $2,000.
120/// let cashflow = 2_000;
121///
122/// let due_at_beginning = false;
123///
124/// // Find the future value.
125/// let future_value_ann = future_value_annuity(rate, periods, cashflow, due_at_beginning);
126/// dbg!(&future_value_ann);
127///
128/// // Confirm that the future value is correct to four decimal places (one hundredth of a cent).
129/// // assert_approx_equal!( , future_value_ann);
130/// ```
131pub fn future_value_annuity<T>(rate: f64, periods: u32, annuity: T, due_at_beginning: bool) -> f64
132    where T: Into<f64> + Copy
133{
134    let pmt = annuity.into();
135    check_future_value_annuity_parameters(rate, periods, pmt);
136    // let mut fv_accumulator = 0_f64;
137    // for i in 0..periods { 
138    //     let future_value = future_value(rate, i as u32, pmt);
139    //     fv_accumulator = fv_accumulator + future_value;
140    // }
141    // fv_accumulator
142
143    // FV_ann = Constant_Cashflow * [ ( (1+periodic_rate)^n -1 )/ periodic_rate ]
144    
145    // let fv_ann= if due_at_beginning {
146    //     let mut fv_accumulator = (1. + rate) * pmt;
147    //     for i in 0..periods {
148    //         let future_value = future_value(rate, i as u32, pmt);
149    //         fv_accumulator = fv_accumulator + future_value;
150    //     }
151    //     fv_accumulator
152    // } else {
153    //     pmt * ((1. + rate).powf(periods as f64) - 1.) / rate
154    // };
155    // fv_ann
156
157    let fv_ann = -(1. + (rate * due_at_beginning as u32 as f64)) * pmt * ((1. + rate).powf(periods as f64) - 1.) / rate;
158    fv_ann
159}
160
161/// Returns the future value of annuity (a series of constant cashflows) at a constant rate. Returns custom solution struct with additional information and functionality.
162///
163/// Related functions:
164/// * To calculate a future value returning an f64, use [`present_value_annuity`].
165/// * To calculate a future value with a varying rate or varying cashflow or both, use [`present_value_annuity_schedule`].
166///
167/// The future value annuity formula is:
168///
169/// future value ann = sum( cashflow * (1 + rate)<sup>period</sup> )
170/// or
171/// future value ann = Constant_Cashflow * ((1+periodic_rate)^n -1) / periodic_rate 
172/// 
173/// # Arguments
174/// * `rate` - The rate at which the investment grows or shrinks per period,
175/// expressed as a floating point number. For instance 0.05 would mean 5%. Often appears as
176/// `r` or `i` in formulas.
177/// * `periods` - The number of periods such as quarters or years. Often appears as `n` or `t`.
178/// * `cashflow` - The value of the constant cashflow (aka payment).
179/// * `due_at_beginning` - True if the payment is due at the beginning of the period. Typically the
180/// payment will be due at the end of the period so this will be false.
181///
182// / # Panics
183// / The call will fail if `rate` is less than -1.0 as this would mean the investment is
184// / losing more than its full value every period.
185// /
186/// # Examples
187/// Future value of a $500 annuity (a series of $500 cashflows) at 3.4% for 10 years.
188/// ```
189/// use finance_solution::*;
190/// let (rate, periods, cashflow, due_at_beginning) = (0.034, 10, 500, false);
191/// let my_annuity = future_value_annuity_solution(rate, periods, cashflow, due_at_beginning);
192/// dbg!(&my_annuity);
193/// ```
194pub fn future_value_annuity_solution<T>(rate: f64, periods: u32, cashflow: T, due_at_beginning: bool) -> CashflowSolution
195    where T: Into<f64> + Copy
196{
197    let annuity = cashflow.into();
198    let fv = future_value_annuity(rate, periods, annuity, due_at_beginning);
199    let fvann_type= if due_at_beginning {
200        CashflowVariable::FutureValueAnnuityDue
201    } else {
202        CashflowVariable::FutureValueAnnuity
203    };
204    // check_future_value__annuity_varying_parameters(rate, periods, cashflow);
205
206    // add due to formulas
207    let formula = format!("-{} * ((1. - (1. / (1. + {})).powf({})) / {});", annuity, rate, periods, rate);
208    let formula_symbolic = format!("-annuity * ((1. - (1. / (1. + rate)).powf(periods)) / rate);");
209    // let fv = future_value_annuity(rate, periods, cashflow);
210    let pv = present_value(rate, periods, fv, false);
211    CashflowSolution::new(fvann_type, rate, periods, pv, fv, due_at_beginning, annuity, &formula, &formula_symbolic)
212}
213
214#[cfg(test)]
215mod tests {
216    use super::*;
217    use crate::*;
218
219    #[test]
220    fn test_future_value_annuity() {
221        let rate = 0.034;
222        let periods = 10;
223        let annuity = 500;
224        let fv = future_value_annuity(rate, periods, annuity, false);
225        // assert_approx_equal!(5838.66016, fv);
226        assert_eq!(-5838.66016, (fv * 100000.).round() / 100000.);
227    }
228
229    #[test]
230    fn test_future_value_annuity_1() {
231        let rate = 0.034;
232        let periods = 1;
233        let annuity = 500;
234        let fv = future_value_annuity(rate, periods, annuity, false);
235        // assert_approx_equal!(5838.66016, fv);
236        assert_eq!(-500.0000, (fv * 100000.).round() / 100000.);
237    }
238    #[test]
239    fn test_future_value_annuity_2() {
240        let rate = 0.034;
241        let periods = 400;
242        let annuity = 500;
243        let fv = future_value_annuity(rate, periods, annuity, false);
244        // assert_approx_equal!(9455966284.4844600, fv);
245        assert_eq!(-9455966284.4844600, (fv * 100000.).round() / 100000.);
246    }
247
248    #[test]
249    fn test_future_value_annuity_3() {
250        // big rate
251        let rate = 0.989;
252        let periods = 8;
253        let annuity = 120_000;
254        let fv = future_value_annuity(rate, periods, annuity, false);
255        assert_eq!(-29_599_651.75013, (fv * 100000.).round() / 100000.);
256    }
257
258    #[test]
259    fn test_future_value_annuity_4() {
260        let rate = 0.00009;
261        let periods = 780;
262        let annuity = 120_000;
263        let fv = future_value_annuity(rate, periods, annuity, false);
264        assert_eq!(-96_959_087.75951, (fv * 100000.).round() / 100000.);
265    }
266
267    #[test]
268    fn test_future_value_annuity_5() {
269        // negative rate
270        let rate = -0.0314;
271        let periods = 10;
272        let annuity = 13_000;
273        let fv = future_value_annuity(rate, periods, annuity, false);
274        assert_eq!(-113_087.68194, (fv * 100000.).round() / 100000.);
275    }
276
277    #[test]
278    fn test_future_value_annuity_6() {
279        // big negative rate
280        let rate = -0.999;
281        let periods = 10;
282        let annuity = 13_000;
283        let fv = future_value_annuity(rate, periods, annuity, false);
284        assert_eq!(-13_013.01301, (fv * 100000.).round() / 100000.);
285    }
286
287    #[test]
288    fn test_future_value_annuity_7() {
289        // big negative rate, big periods
290        // note: the convergence with the previous test
291        let rate = -0.999;
292        let periods = 780;
293        let annuity = 13_000;
294        let fv = future_value_annuity(rate, periods, annuity, false);
295        assert_eq!(-13_013.01301, (fv * 100000.).round() / 100000.);
296    }
297
298}