Source code for loan_calculator.loan
from datetime import timedelta
from loan_calculator.schedule import SCHEDULE_TYPE_CLASS_MAP
from loan_calculator.schedule.base import AmortizationScheduleType
from loan_calculator.interest_rate import (
convert_to_daily_interest_rate, InterestRateType, YearSizeType)
[docs]class Loan(object):
"""Loan.
Attributes
----------
principal : float, required
The loan's principal.
annual_interest_rate : float, required
The loan's annual interest rate.
start_date : date, required
The loan's reference date. This date is usually the one when the
borrower signed the loan's contract.
return_dates : list, required
List of date objects with the expected return dates. These dates
are usually contractually agreed.
year_size : int, optional
The reference year size for converting from annual to daily
interest rates. (default 365)
grace_period : int, optional
The number of days for which the principal is not affected by the
capitalization process. (default 0)
amortization_schedule_type : str, optional
A discriminator string indicating the amortization schedule to be
adopted. The available schedules are progressive_price_schedule,
regressive_price_schedule, constant_amortization_schedule.
(default AmortizationScheduleType.progressive_price_schedule.value).
"""
def __init__(
self,
principal,
annual_interest_rate,
start_date,
return_dates,
year_size=YearSizeType.commercial,
grace_period=0,
amortization_schedule_type=(
AmortizationScheduleType.progressive_price_schedule.value
)
):
"""Initialize loan."""
self.principal = principal
self.annual_interest_rate = annual_interest_rate
self.daily_interest_rate = convert_to_daily_interest_rate(
annual_interest_rate,
InterestRateType.annual,
year_size,
)
self.start_date = start_date
self.capitalization_start_date = start_date + timedelta(grace_period)
self.return_dates = return_dates
self.year_size = year_size
self.grace_period = grace_period
self.amortization_schedule_type = (
AmortizationScheduleType(amortization_schedule_type)
)
self.amortization_schedule_cls = SCHEDULE_TYPE_CLASS_MAP[
self.amortization_schedule_type
]
if any(
self.capitalization_start_date >= r_date
for r_date in self.return_dates
):
raise ValueError('Grace period can not exceed loan start.')
self.amortization_schedule = self.amortization_schedule_cls(
principal,
self.daily_interest_rate,
[
(r_date - self.capitalization_start_date).days
for r_date in return_dates
]
)
@property
def amortization_function(self):
def f_(principal, daily_interest_rate, return_days):
return self.amortization_schedule_cls(
principal, daily_interest_rate, return_days
).amortizations
return f_
@property
def return_days(self):
return self.amortization_schedule.return_days # pragma: no cover
@property
def balance(self):
return self.amortization_schedule.balance # pragma: no cover
@property
def due_payments(self):
return self.amortization_schedule.due_payments # pragma: no cover
@property
def interest_payments(self):
return self.amortization_schedule.interest_payments # pragma: no cover
@property
def amortizations(self):
return self.amortization_schedule.amortizations # pragma: no cover
@property
def total_amortization(self):
return (
self.amortization_schedule.total_amortization # pragma: no cover
)
@property
def total_interest(self):
return self.amortization_schedule.total_interest # pragma: no cover
@property
def total_paid(self):
return self.amortization_schedule.total_paid # pragma: no cover