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}