Source code for loan_calculator.schedule.constant
from loan_calculator.schedule.base import (
BaseSchedule, AmortizationScheduleType
)
[docs]class ConstantAmortizationSchedule(BaseSchedule):
"""Implement constant amortization schedule.
The constant amortization schedule is defined as the amortization schedule
where all the amortizations have the same value, given as
:math:`s/k`, where :math:`s` is the principal and :math:`k`
is the number of due payments. Therefore, the due payments do not
have all the same value, as in the French system, but differ
according to how much interest was accumulated over time. If
:math:`d` is the daily interest rate, :math:`P_i` is the :math:`i`-th
due payment, :math:`A_i` and :math:`J_i` are the associated amortization
and interest, respectively, and :math:`b_i` is the balance after the
:math:`i`-th payment, then
- :math:`A_i = \\frac{s}{k}`.
- :math:`J_i = ((1+d)^{n_i} - (1+d)^{n_{i-1}})
(s - A\\displaystyle\\sum_{1\\leq j\\leq i-1}\\frac{1}{(1+d)^{n_j}})`.
- :math:`P_i = A + J_i`.
- :math:`b_i = s - iA`.
"""
schedule_type = AmortizationScheduleType.constant_amortization_schedule
[docs] def calculate_balance(self):
"""Calculate the balance after each payment.
The balance after each payment is given by
.. math::
b_i := s(1 - \\frac{i}{k}),
\\ \\mathrm{for\\ all}\\ i,0\\leq i\\leq k,
where :math:`s` is the principal and :math:`k` is the number of
instalments.
"""
k = len(self.return_days)
return [self.principal * (1 - float(i) / k) for i in range(k + 1)]
[docs] def calculate_amortizations(self):
"""Calculate the amortizations by payments.
Since this is a constant amortization schedule, all amortizations have
the same value, given by
.. math::
A_i := \\frac{s}{k},\\mathrm{for\\ all}\\ i,1\\leq i\\leq k,
where :math:`s` is the principal and :math:`k` is the number of
instalments.
"""
return [
self.principal / len(self.return_days)
for _ in self.return_days
]
[docs] def calculate_interest(self):
"""Calculate the interest in each payment.
The interest is calculated over the last balance and is given by
.. math::
J_i := b_{i-1}((1+d)^{n_i-n_{i-1}}-1)
\\ \\mathrm{for\\ all}\\ i,1\\leq i\\leq k,
where :math:`b_{i-1}` is the :math:`(i-1)`-th balance, :math:`d` is the
daily interest rate and :math:`n_1,\\ldots,n_k` are the return days.
"""
# rename variable to make the math more explicit
d = self.daily_interest_rate
return [
b * ((1 + d) ** (n - m) - 1)
for b, n, m in zip(self.balance[:-1],
self.return_days,
[0] + self.return_days[:-1])
]
[docs] def calculate_due_payments(self):
"""Calculate the due payments.
In the constant amortization schedule, the instalment value is not
fixed as in Price type schedules but depends on how much interest is
due for each period and the amortization, which is constant. The
payments are then given by
.. math::
P_i = b_{i-1}((1+d)^{n_i-n_{i-1}}-1) + \\frac{S}{k},
\\ \\mathrm{for\\ all}\\ i,1\\leq i\\leq k,
where :math:`b_{i-1}` is the :math:`(i-1)`-th balance, :math:`d` is the
daily interest rate, :math:`S` is the principal and
:math:`n_1,\\ldots,n_k` are the return days.
"""
# variables are renamed to make the math more explicit
p = self.principal
d = self.daily_interest_rate
k = len(self.return_days)
return [
b * ((1 + d) ** (n - m) - 1) + p / k
for b, n, m in zip(
self.balance[:-1],
self.return_days, [0] + self.return_days[:-1]
)
]