Source code for actuneo.finance.interest

"""
Interest Theory

Provides functions for interest calculations, present value, future value,
and various interest rate conversions.
"""

import numpy as np
from typing import Union, Optional


[docs] class InterestTheory: """ A class for performing various interest rate calculations and time value of money computations. """
[docs] def __init__(self, interest_rate: float = 0.05, compounding_frequency: int = 1): """ Initialize InterestTheory with interest rate and compounding frequency. Args: interest_rate: Annual interest rate (decimal) compounding_frequency: Compounding frequency per year (1=annual, 2=semi-annual, etc.) """ self.i = interest_rate self.m = compounding_frequency self.i_m = interest_rate / compounding_frequency # Periodic interest rate self.v = 1 / (1 + interest_rate) # Annual discount factor
[docs] def future_value(self, present_value: float, periods: Union[int, float], interest_rate: Optional[float] = None) -> float: """ Calculate future value of a present amount. Args: present_value: Present value amount periods: Number of periods interest_rate: Override default interest rate Returns: Future value """ i = interest_rate if interest_rate is not None else self.i return present_value * (1 + i) ** periods
[docs] def present_value(self, future_value: float, periods: Union[int, float], interest_rate: Optional[float] = None) -> float: """ Calculate present value of a future amount. Args: future_value: Future value amount periods: Number of periods interest_rate: Override default interest rate Returns: Present value """ i = interest_rate if interest_rate is not None else self.i return future_value * (1 + i) ** (-periods)
[docs] def annuity_present_value(self, payment: float, periods: int, interest_rate: Optional[float] = None, immediate: bool = True) -> float: """ Calculate present value of an annuity. Args: payment: Periodic payment amount periods: Number of periods interest_rate: Override default interest rate immediate: True for immediate annuity, False for annuity-due Returns: Present value of annuity """ i = interest_rate if interest_rate is not None else self.i if i == 0: return payment * periods if immediate: # Immediate annuity: payments at end of each period return payment * ((1 - (1 + i) ** (-periods)) / i) else: # Annuity-due: payments at beginning of each period return payment * ((1 - (1 + i) ** (-periods)) / i) * (1 + i)
[docs] def annuity_future_value(self, payment: float, periods: int, interest_rate: Optional[float] = None, immediate: bool = True) -> float: """ Calculate future value of an annuity. Args: payment: Periodic payment amount periods: Number of periods interest_rate: Override default interest rate immediate: True for immediate annuity, False for annuity-due Returns: Future value of annuity """ i = interest_rate if interest_rate is not None else self.i if i == 0: return payment * periods if immediate: # Immediate annuity return payment * (((1 + i) ** periods - 1) / i) else: # Annuity-due return payment * (((1 + i) ** periods - 1) / i) * (1 + i)
[docs] def loan_payment(self, principal: float, periods: int, interest_rate: Optional[float] = None) -> float: """ Calculate periodic loan payment amount. Args: principal: Loan principal amount periods: Number of periods interest_rate: Override default interest rate Returns: Periodic payment amount """ i = interest_rate if interest_rate is not None else self.i if i == 0: return principal / periods return principal * (i * (1 + i) ** periods) / ((1 + i) ** periods - 1)
[docs] def loan_balance(self, principal: float, periods: int, payments_made: int, interest_rate: Optional[float] = None) -> float: """ Calculate remaining loan balance after certain number of payments. Args: principal: Original loan principal periods: Total number of periods payments_made: Number of payments already made interest_rate: Override default interest rate Returns: Remaining balance """ i = interest_rate if interest_rate is not None else self.i payment = self.loan_payment(principal, periods, i) # Future value of remaining payments remaining_periods = periods - payments_made remaining_annuity_pv = self.annuity_present_value(payment, remaining_periods, i, immediate=True) return remaining_annuity_pv
[docs] def effective_annual_rate(self, nominal_rate: float, compounding_freq: int) -> float: """ Convert nominal rate to effective annual rate. Args: nominal_rate: Nominal annual interest rate compounding_freq: Compounding frequency per year Returns: Effective annual rate """ return (1 + nominal_rate / compounding_freq) ** compounding_freq - 1
[docs] def nominal_rate(self, effective_rate: float, compounding_freq: int) -> float: """ Convert effective annual rate to nominal rate. Args: effective_rate: Effective annual interest rate compounding_freq: Compounding frequency per year Returns: Nominal annual rate """ return compounding_freq * ((1 + effective_rate) ** (1 / compounding_freq) - 1)
[docs] def real_interest_rate(self, nominal_rate: float, inflation_rate: float) -> float: """ Calculate real interest rate using Fisher equation. Args: nominal_rate: Nominal interest rate inflation_rate: Inflation rate Returns: Real interest rate """ return (1 + nominal_rate) / (1 + inflation_rate) - 1
[docs] def inflation_adjusted_value(self, nominal_value: float, inflation_rate: float, periods: Union[int, float]) -> float: """ Calculate inflation-adjusted (real) value. Args: nominal_value: Nominal value inflation_rate: Annual inflation rate periods: Number of periods Returns: Real value adjusted for inflation """ return nominal_value / (1 + inflation_rate) ** periods