Source code for tallywallet.common.test.test_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/>.

import datetime
import decimal
from decimal import Decimal
import unittest

from tallywallet.common.finance import Amortization
from tallywallet.common.finance import Note
from tallywallet.common.finance import discount_simple
from tallywallet.common.finance import schedule
from tallywallet.common.finance import value_series
from tallywallet.common.finance import value_simple

class AmortizationTests(unittest.TestCase):

    def test_loan_payment(self):
        """
        MoF 2Ed 7.1

        A debt of $6000 with interest at 16% compounded semiannually is
        to be amortized by equal semiannual payments of $R over the next 3
        years, the first payment due in 6 months.

        Find the payment rounded up to the nearest cent.

        """
        now = datetime.datetime.utcnow()
        loan = Note(
            now, 6000, "USD",
            datetime.timedelta(days=360*3),
            Decimal("0.16"), datetime.timedelta(days=180))

        R = discount_simple(loan)[2]
        self.assertEqual(
            Decimal("1297.90"),
            R.quantize(Decimal("0.01"), rounding=decimal.ROUND_UP)
        )

[docs] def test_amortization_schedule(self): """ MoF 2Ed 7.4 A debt of $6000 with interest at 16% compounded semiannually is to be amortized by equal semiannual payments of $R over the next 3 years, the first payment due in 6 months. Construct a complete amortization schedule for the debt. """ now = datetime.datetime.utcnow() loan = Note( now, 6000, "USD", datetime.timedelta(days=360*3), Decimal("0.16"), datetime.timedelta(days=180)) record = list(schedule(loan, places=0)) self.assertEqual(6, len(record)) self.assertEqual( Decimal("7787.21"), sum(i.payment for i in record).quantize(Decimal("0.01")) ) self.assertEqual( Decimal("1787.21"), sum(i.interest for i in record).quantize(Decimal("0.01")) ) self.assertEqual(6000, sum(i.repaid for i in record)) self.assertEqual(0, record[-1].balance)
class TestCompoundInterest(unittest.TestCase): def test_value_series(self): """Schaum's MoF 2Ed 4.1""" note = Note( date=datetime.date(2012, 7, 30), principal=1000, currency=None, term=datetime.timedelta(days=720), interest=Decimal("0.12"), period=datetime.timedelta(days=360) ) series = list(value_series(m=2, **vars(note))) self.assertEqual(4, len(series)) # NB: Pennies are off-by-one from reference -> ¬ ¬ self.assertEqual( [Decimal(i) for i in ("1060", "1123.6", "1191.01", "1262.47")], [i[1].quantize(Decimal("0.01"), rounding=decimal.ROUND_05UP) for i in series] ) class TestPromissoryNote(unittest.TestCase): def test_value_simple(self): """Schaum's MoF 2Ed 3.29""" note = Note( date=datetime.date(1995, 5, 11), principal=1500, currency=None, term=datetime.timedelta(days=90), interest=Decimal("0.08"), period=datetime.timedelta(days=360) ) self.assertEqual( Decimal("1530.00"), value_simple(note) ) def test_sale_at_discount(self): """Schaum's MoF 2Ed 3.30""" note = Note( date=datetime.date(1995, 5, 11), principal=1500, currency=None, term=datetime.timedelta(days=90), interest=Decimal("0.08"), period=datetime.timedelta(days=360) ) valueAtMaturity = value_simple(note) self.assertEqual(1530, valueAtMaturity) dateOfSale = datetime.date(1995, 7, 2) termRemaining = note.date + note.term - dateOfSale self.assertEqual(38, termRemaining.days) _, valueAtDiscount = next(value_series( date=note.date + note.term, principal=valueAtMaturity, term=(-termRemaining), period=note.period, interest=Decimal("0.09"))) self.assertEqual( Decimal("1515.46"), valueAtDiscount.quantize( Decimal("0.01"), rounding=decimal.ROUND_05UP) )