Amortization in Python

Caution

There may be errors! Use at your own risk and be sure to examine the results to be sure they are correct. On July 16, 2020, I received an example that produced incorrect results. I believe I have now corrected that error. You might prefer to use the web app version at http://nepotism.net/amort.

Features

Installation

The README file has more installation information. Essentially you download and unzip amort.zip into an appropriate directory (such as amort), then you create a small custom file in the same directory for each amortization schedule you wish to produce.

Simple Example

That custom file is really a small Python program and it can look like this. For a loan to John Smith, you might name the file johnsmith.py.


from amort import Loan

loan1 = Loan (
        originDate="2015-07-10",
        loanAmount=2000.00,
        payment=100.00,
        interest=7,
        days="actual",
        basis="actual",
        dueDay=10,
        numberOfPayments="unknown")

loan1.amort()

The above specifies a loan in the amount of $2000.00 made on July 10, 2015 at 7% per annum, with a monthly payment of $100.00 due on the 10th of each month. Since the payment is specified, the number of payments is not specified (it is set to “unknown”), and the software will figure out how many payments are needed.

To actually create the amortization schedule, simply run that program. For example, in Linux, open a terminal, change to your amort directory, and execute it:

$ cd ~/amort
$ ./johnsmith.py

This displays the amortization schedule in the terminal. You can copy/paste it to a file and edit it further. Or, you can run it as follows to redirect the output to a file:

$ ./johnsmith.py > johnsmithloan.txt

Here is the output from the above:

 ----  ----------  ------------   ------------   -------------  ------------  ------------  ------------
    1  2015-08-10      2,000.00         100.00   31/365                11.89         88.11      1,911.89
    2  2015-09-10      1,911.89         100.00   31/365                11.37         88.63      1,823.26
    3  2015-10-10      1,823.26         100.00   30/365                10.49         89.51      1,733.75
    4  2015-11-10      1,733.75         100.00   31/365                10.31         89.69      1,644.06
    5  2015-12-10      1,644.06         100.00   30/365                 9.46         90.54      1,553.52
    6  2016-01-10      1,553.52         100.00   22/365,9/366           9.23         90.77      1,462.75
    7  2016-02-10      1,462.75         100.00   31/366                 8.67         91.33      1,371.42
    8  2016-03-10      1,371.42         100.00   29/366                 7.61         92.39      1,279.03
    9  2016-04-10      1,279.03         100.00   31/366                 7.58         92.42      1,186.61
   10  2016-05-10      1,186.61         100.00   30/366                 6.81         93.19      1,093.42
   11  2016-06-10      1,093.42         100.00   31/366                 6.48         93.52        999.90
   12  2016-07-10        999.90         100.00   30/366                 5.74         94.26        905.64
   13  2016-08-10        905.64         100.00   31/366                 5.37         94.63        811.01
   14  2016-09-10        811.01         100.00   31/366                 4.81         95.19        715.82
   15  2016-10-10        715.82         100.00   30/366                 4.11         95.89        619.93
   16  2016-11-10        619.93         100.00   31/366                 3.68         96.32        523.61
   17  2016-12-10        523.61         100.00   30/366                 3.00         97.00        426.61
   18  2017-01-10        426.61         100.00   22/366,9/365           2.53         97.47        329.14
   19  2017-02-10        329.14         100.00   31/365                 1.96         98.04        231.10
   20  2017-03-10        231.10         100.00   28/365                 1.24         98.76        132.34
   21  2017-04-10        132.34         100.00   31/365                 0.79         99.21         33.13
   22  2017-05-10         33.13          33.32   30/365                 0.19         33.13          0.00

Of course, under Linux, the johnsmith.py file must be marked as executable, e.g.,

$ chmod +x johnsmith.py

Or, you can start by copying the included sample.py file, e.g.,

$ cp sample.py johnsmith.py

and not need to mark it executable (since sample.py was already marked executable).

Or, you can run it this way without marking it as executable:

$ python3 johnsmith.py

TMI

The previous example may display more information than you are interested in. In particular, you may not need the “period” column which indicates the fraction of a year covered by each payment. This can be removed easily with an editor, such as Emacs, that allows deleting rectangles.

This column is present to help you verify the correctness of the amortization schedule.

(Don’t) Trust the Computer

Or, perhaps more accurately, don’t trust me! I believe the calculations are correct when using the actual days conventions (actual/actual, actual/365, actual/360) but I am not nearly as confident about the 30 days conventions (30/actual, 30/365, 30/360). Please be vigilant regardless, but be especially vigilant if you use one of the 30 days conventions.

If you find an error, please email me at with the details.

Further examples


from amort import Loan

loan1 = Loan (
        originDate="2014-03-10",
        loanAmount=2000.00,
        payment=100.00,
        interest=7,
        firstPayments=(("2014-04-15", 100.00),
                       ("2014-05-10", 100.00)),
        days="actual",
        basis="actual",
        dueDay=10,
        numberOfPayments="unknown")

loan1.amort()

Above shows two first payments, after which the specified payment kicks in. Note, the first of the two has an “irregular” date in that it is not on the 10th of the month. The second of the two does not actually need to be specified, as it is “regular” in both amount and payment date.

Below is a similar example, but with four first payments. The fourth is not actually irregular and could have been omitted. Note each of the first payments consists of a date and an amount, wrapped in parenthesis marks and the entire group of first payments is wrapped in parenthesis marks.


from amort import Loan

loan = Loan (
        originDate="2014-03-10",
        loanAmount=2000.00,
        payment=100.00,
        interest=7,
        firstPayments=(("2014-04-15",  50.00),
                       ("2014-05-12",  75.00),
                       ("2014-06-09",  45.00),
                       ("2014-07-10", 100.00)),
        days="actual",
        basis="actual",
        dueDay=10,
        numberOfPayments="unknown")

loan.amort()