"""Basic Time Value of Money calculations."""
import math
from ..core.validators import validate_positive
[docs]
def future_value(
present_value: float,
rate: float,
periods: int,
compounding_frequency: int = 1,
) -> float:
"""
Calculate future value of a present amount.
Parameters
----------
present_value : float
Present value amount.
rate : float
Annual interest rate (as decimal, e.g., 0.05 for 5%).
periods : int
Number of periods.
compounding_frequency : int, default 1
Number of times interest is compounded per period.
Returns
-------
float
Future value.
Raises
------
ValidationError
If any parameter is invalid.
Examples
--------
>>> future_value(1000, 0.05, 10)
1628.89
"""
validate_positive(present_value, "present_value")
validate_positive(rate, "rate")
validate_positive(periods, "periods")
validate_positive(compounding_frequency, "compounding_frequency")
return present_value * (1 + rate / compounding_frequency) ** (
compounding_frequency * periods
)
[docs]
def present_value(
future_value: float,
rate: float,
periods: int,
compounding_frequency: int = 1,
) -> float:
"""
Calculate present value of a future amount.
Parameters
----------
future_value : float
Future value amount.
rate : float
Annual interest rate (as decimal).
periods : int
Number of periods.
compounding_frequency : int, default 1
Number of times interest is compounded per period.
Returns
-------
float
Present value.
Examples
--------
>>> present_value(1628.89, 0.05, 10)
1000.0
"""
validate_positive(future_value, "future_value")
validate_positive(rate, "rate")
validate_positive(periods, "periods")
validate_positive(compounding_frequency, "compounding_frequency")
return future_value / (1 + rate / compounding_frequency) ** (
compounding_frequency * periods
)
[docs]
def interest_rate(
present_value: float,
future_value: float,
periods: int,
compounding_frequency: int = 1,
) -> float:
"""
Calculate interest rate given present value, future value, and periods.
Parameters
----------
present_value : float
Present value amount.
future_value : float
Future value amount.
periods : int
Number of periods.
compounding_frequency : int, default 1
Number of times interest is compounded per period.
Returns
-------
float
Annual interest rate (as decimal).
Raises
------
ValidationError
If any parameter is invalid.
Examples
--------
>>> interest_rate(1000, 1628.89, 10)
0.05
"""
validate_positive(present_value, "present_value")
validate_positive(future_value, "future_value")
validate_positive(periods, "periods")
validate_positive(compounding_frequency, "compounding_frequency")
# Solve for rate: FV = PV * (1 + r/n)^(n*t)
# (FV/PV)^(1/(n*t)) = 1 + r/n
# r = n * ((FV/PV)^(1/(n*t)) - 1)
rate = float(
compounding_frequency
* (
(future_value / present_value) ** (1 / (compounding_frequency * periods))
- 1
)
)
return rate
[docs]
def number_of_periods(
present_value: float,
future_value: float,
rate: float,
compounding_frequency: int = 1,
) -> float:
"""
Calculate number of periods required for present value to grow to future value.
Parameters
----------
present_value : float
Present value amount.
future_value : float
Future value amount.
rate : float
Annual interest rate (as decimal).
compounding_frequency : int, default 1
Number of times interest is compounded per period.
Returns
-------
float
Number of periods required.
Raises
------
ValidationError
If any parameter is invalid.
Examples
--------
>>> number_of_periods(1000, 3000, 0.12)
9.69
"""
validate_positive(present_value, "present_value")
validate_positive(future_value, "future_value")
validate_positive(rate, "rate")
validate_positive(compounding_frequency, "compounding_frequency")
# Solve for periods: FV = PV * (1 + r/n)^(n*t)
# log(FV/PV) = n*t * log(1 + r/n)
# t = log(FV/PV) / (n * log(1 + r/n))
periods = math.log(future_value / present_value) / (
compounding_frequency * math.log(1 + rate / compounding_frequency)
)
return periods
[docs]
def compound_interest(
principal: float,
rate: float,
periods: int,
compounding_frequency: int = 1,
) -> float:
"""
Calculate compound interest earned.
Parameters
----------
principal : float
Initial principal amount.
rate : float
Annual interest rate (as decimal).
periods : int
Number of periods.
compounding_frequency : int, default 1
Number of times interest is compounded per period.
Returns
-------
float
Compound interest earned.
Examples
--------
>>> compound_interest(1000, 0.05, 10)
628.89
"""
fv = future_value(principal, rate, periods, compounding_frequency)
return fv - principal
[docs]
def effective_rate(nominal_rate: float, compounding_frequency: int) -> float:
"""
Calculate effective annual rate from nominal rate.
Parameters
----------
nominal_rate : float
Nominal annual interest rate (as decimal).
compounding_frequency : int
Number of compounding periods per year.
Returns
-------
float
Effective annual rate.
Examples
--------
>>> effective_rate(0.12, 12) # 12% compounded monthly
0.1268
"""
validate_positive(nominal_rate, "nominal_rate")
validate_positive(compounding_frequency, "compounding_frequency")
return (1 + nominal_rate / compounding_frequency) ** compounding_frequency - 1
[docs]
def nominal_rate(effective_rate: float, compounding_frequency: int) -> float:
"""
Calculate nominal rate from effective annual rate.
Parameters
----------
effective_rate : float
Effective annual interest rate (as decimal).
compounding_frequency : int
Number of compounding periods per year.
Returns
-------
float
Nominal annual rate.
Examples
--------
>>> nominal_rate(0.1268, 12) # Effective 12.68% compounded monthly
0.12
"""
validate_positive(effective_rate, "effective_rate")
validate_positive(compounding_frequency, "compounding_frequency")
return float(
compounding_frequency
* ((1 + effective_rate) ** (1 / compounding_frequency) - 1)
)
[docs]
def continuous_compounding_fv(
present_value: float,
rate: float,
time: float,
) -> float:
"""
Calculate future value with continuous compounding.
Parameters
----------
present_value : float
Present value amount.
rate : float
Annual interest rate (as decimal).
time : float
Time period in years.
Returns
-------
float
Future value with continuous compounding.
Examples
--------
>>> continuous_compounding_fv(1000, 0.05, 10)
1648.72
"""
validate_positive(present_value, "present_value")
validate_positive(rate, "rate")
validate_positive(time, "time")
return present_value * math.exp(rate * time)
[docs]
def continuous_compounding_pv(
future_value: float,
rate: float,
time: float,
) -> float:
"""
Calculate present value with continuous compounding.
Parameters
----------
future_value : float
Future value amount.
rate : float
Annual interest rate (as decimal).
time : float
Time period in years.
Returns
-------
float
Present value with continuous compounding.
Examples
--------
>>> continuous_compounding_pv(1648.72, 0.05, 10)
1000.0
"""
validate_positive(future_value, "future_value")
validate_positive(rate, "rate")
validate_positive(time, "time")
return future_value * math.exp(-rate * time)