"""Annuity calculations."""
from ..core.validators import validate_positive
[docs]
def ordinary_annuity_pv(
payment: float,
rate: float,
periods: int,
) -> float:
"""
Calculate present value of ordinary annuity.
Parameters
----------
payment : float
Periodic payment amount.
rate : float
Interest rate per period (as decimal).
periods : int
Number of periods.
Returns
-------
float
Present value of ordinary annuity.
Examples
--------
>>> ordinary_annuity_pv(1000, 0.05, 10)
7721.73
"""
validate_positive(payment, "payment")
validate_positive(rate, "rate")
validate_positive(periods, "periods")
return payment * (1 - (1 + rate) ** -periods) / rate
[docs]
def ordinary_annuity_fv(
payment: float,
rate: float,
periods: int,
) -> float:
"""
Calculate future value of ordinary annuity.
Parameters
----------
payment : float
Periodic payment amount.
rate : float
Interest rate per period (as decimal).
periods : int
Number of periods.
Returns
-------
float
Future value of ordinary annuity.
Examples
--------
>>> ordinary_annuity_fv(1000, 0.05, 10)
12577.89
"""
validate_positive(payment, "payment")
validate_positive(rate, "rate")
validate_positive(periods, "periods")
return payment * (((1 + rate) ** periods - 1) / rate)
[docs]
def annuity_due_pv(
payment: float,
rate: float,
periods: int,
) -> float:
"""
Calculate present value of annuity due.
Parameters
----------
payment : float
Periodic payment amount.
rate : float
Interest rate per period (as decimal).
periods : int
Number of periods.
Returns
-------
float
Present value of annuity due.
Examples
--------
>>> annuity_due_pv(1000, 0.05, 10)
8107.82
"""
return ordinary_annuity_pv(payment, rate, periods) * (1 + rate)
[docs]
def annuity_due_fv(
payment: float,
rate: float,
periods: int,
) -> float:
"""
Calculate future value of annuity due.
Parameters
----------
payment : float
Periodic payment amount.
rate : float
Interest rate per period (as decimal).
periods : int
Number of periods.
Returns
-------
float
Future value of annuity due.
Examples
--------
>>> annuity_due_fv(1000, 0.05, 10)
13206.79
"""
return ordinary_annuity_fv(payment, rate, periods) * (1 + rate)
[docs]
def annuity_pv(
payment: float,
rate: float,
periods: int,
due: bool = False,
) -> float:
"""
Calculate present value of annuity (ordinary or due).
Parameters
----------
payment : float
Periodic payment amount.
rate : float
Interest rate per period (as decimal).
periods : int
Number of periods.
due : bool, default False
If True, calculate annuity due. If False, ordinary annuity.
Returns
-------
float
Present value of annuity.
"""
if due:
return annuity_due_pv(payment, rate, periods)
return ordinary_annuity_pv(payment, rate, periods)
[docs]
def annuity_fv(
payment: float,
rate: float,
periods: int,
due: bool = False,
) -> float:
"""
Calculate future value of annuity (ordinary or due).
Parameters
----------
payment : float
Periodic payment amount.
rate : float
Interest rate per period (as decimal).
periods : int
Number of periods.
due : bool, default False
If True, calculate annuity due. If False, ordinary annuity.
Returns
-------
float
Future value of annuity.
"""
if due:
return annuity_due_fv(payment, rate, periods)
return ordinary_annuity_fv(payment, rate, periods)
[docs]
def perpetuity_value(
payment: float,
rate: float,
) -> float:
"""
Calculate present value of a perpetuity.
A perpetuity is a stream of equal payments that continues forever.
Parameters
----------
payment : float
Periodic payment amount.
rate : float
Interest rate per period (as decimal).
Returns
-------
float
Present value of perpetuity.
Raises
------
ValidationError
If any parameter is invalid.
Examples
--------
>>> perpetuity_value(50000, 0.04)
1250000.0
Notes
-----
The formula for a perpetuity is simply: PV = Payment / Rate
"""
validate_positive(payment, "payment")
validate_positive(rate, "rate")
return payment / rate
[docs]
def growing_annuity_pv(
payment: float,
rate: float,
growth_rate: float,
periods: int,
) -> float:
"""
Calculate present value of a growing annuity.
A growing annuity has payments that grow at a constant rate each period.
Parameters
----------
payment : float
Initial payment amount.
rate : float
Interest rate per period (as decimal).
growth_rate : float
Growth rate of payments per period (as decimal).
periods : int
Number of periods.
Returns
-------
float
Present value of growing annuity.
Raises
------
ValidationError
If any parameter is invalid.
Examples
--------
>>> growing_annuity_pv(2, 0.09, 0.05, 20)
24.36
Notes
-----
If rate equals growth_rate, the formula becomes: PV = PMT * n / (1 + r)
Otherwise: PV = PMT * [(1 - ((1 + g) / (1 + r))^n) / (r - g)]
"""
validate_positive(payment, "payment")
validate_positive(rate, "rate")
validate_positive(periods, "periods")
# Special case when rate equals growth rate
if abs(rate - growth_rate) < 1e-10:
return payment * periods / (1 + rate)
# Standard growing annuity formula
return (
payment
* (1 - ((1 + growth_rate) / (1 + rate)) ** periods)
/ (rate - growth_rate)
)