Source code for tallywallet.common.finance

#!/usr/bin/env python3.4
#   encoding: UTF-8

# This file is part of tallywallet.
#
# Tallywallet is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Tallywallet is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with tallywallet.  If not, see <http://www.gnu.org/licenses/>.

from collections import namedtuple
import datetime
import decimal
from decimal import Decimal

Note = namedtuple(
    "Note",
    ["date", "principal", "currency", "term", "interest", "period"])
Note.__doc__ = """`{}`

A Note keeps all the details of a promise to pay a debt,
as follows:

    date
        The date on which the loan is agreed.
    principal
        The amount of the original loan.
    currency
        The currency in which the loan will be paid.
    term
        The duration of the loan.
    interest
        The rate of interest.
    period
        The period of time over which interest is calculated.
""".format(Note.__doc__)

Amortization = namedtuple(
    "Amortization",
    ["date", "payment", "interest", "repaid", "balance"])
Amortization.__doc__ = """`{}`

An Amortization object records a payment against an amortization schedule:

    date
        The date on which the payment is made.
    payment
        The total amount of the payment.
    interest
        The amount paid as interest.
    repaid
        The amount repaid from the principal.
    balance
        The remaining balance of the loan.
""".format(Amortization.__doc__)


[docs]def discount_simple(note:Note): """ Calculates the discounted value of an ordinary simple annuity. See `Schaum's Mathematics of Finance` Equation 5.2 . Returns a 3-tuple of (number of payments, annualised rate, annuity payment) """ n = int(note.term / note.period) i = note.interest * Decimal(note.period / datetime.timedelta(days=360)) R = note.principal * i / (1 - (1 + i) ** -n) return (n, i, R)
[docs]def schedule(note:Note, places=2, rounding=decimal.ROUND_UP): """ Calculate the amortization schedule for a :py:class:`Note <tallywallet.common.finance.Note>`. places An integer. Round the balance to this number of decimal places at each calculation interval. rounding Selects the rounding method. Must be one of the constants defined for this purpose in the `decimal` standard library module. This function is a generator. It produces a sequence of :py:class:`Amortization <tallywallet.common.finance.Amortization>` objects. """ quantum = Decimal(10) ** -places n, rate, annuity = discount_simple(note) payment = annuity.quantize(quantum, rounding=rounding) balance = note.principal end = note.date + note.term ts = note.date while ts < end: ts += note.period interest = rate * balance payment = min(balance + interest, payment) repaid = payment - interest balance -= repaid yield Amortization(ts, payment, interest, repaid, balance)
[docs]def value_simple(note:Note): """ Returns the simple value of a promissory note on maturity. """ return next(value_series(m=1, **vars(note)))[1]
[docs]def value_series(date, principal, term, period, interest, m=1, **kwargs): """ Calculate the value of a debt over time. Parameters are as for the :py:class:`Note <tallywallet.common.finance.Note>` type. Additionally: m The number of times per `period` that interest is compounded. This function is a generator. It produces 2-tuples of (date, value). """ interval = min(term, period / m) rate = interest * Decimal(interval / period) t = date for i in range(term // interval): t += interval principal = principal * (1 + rate) yield (t, principal)