A small, dependency-free D library for fixed-rate loan amortization math — the numbers behind every mortgage, auto loan, and personal loan.
It turns a loan amount, rate, and term into the figures a borrower actually wants: the level monthly payment, the full period-by-period interest/principal schedule, the lifetime interest, the balance still owed part-way through, and how much a little extra each month saves.
The formula and worked examples mirror the model used at assetloancalculator.com.
A fully-amortizing, fixed-rate loan has a single constant payment:
payment = P·r·(1+r)^n / ((1+r)^n − 1)
where P is the principal, r is the monthly rate, and n is the number of
payments. Early on almost all of that payment is interest; late in the term
almost all of it is principal. This library computes the payment, walks the
schedule that splits each payment, and answers the two questions that actually
move the total interest: what do I still owe after N payments? and what does
paying extra each month do to the payoff?
Add it to your dub.json:
"dependencies": {
"loanamort": "~>0.1.0"
}import loanamort;
import std.stdio;
void main()
{
// $300k mortgage, 6.5% APR, 30-year term.
writeln("payment: ", monthlyPayment(300_000, 6.5, 360)); // ~1896.20 / mo
writeln("interest: ", totalInterest(300_000, 6.5, 360)); // ~382,633 lifetime
// Balance still owed after 5 years (60 payments):
writeln("owed @ 60: ", remainingBalance(300_000, 6.5, 360, 60));
// What $200/mo extra does to the payoff:
auto fast = payoffWithExtra(300_000, 6.5, 360, 200.0);
writeln("paid off in ", fast.months, " months, ",
"interest ", fast.totalInterest);
// Full schedule (interest/principal split per period):
auto sched = amortizationSchedule(300_000, 6.5, 360);
writeln("month 1: interest ", sched[0].interest,
" principal ", sched[0].principal,
" balance ", sched[0].remainingBalance);
}| Symbol | Description |
|---|---|
monthlyPayment(principal, annualRatePct, termMonths) |
Level payment on a fully-amortizing fixed-rate loan (zero-rate = straight-line). |
amortizationSchedule(principal, annualRatePct, termMonths) |
Array of AmortRow — the interest/principal split and running balance per payment; closes exactly on zero. |
totalInterest(principal, annualRatePct, termMonths) |
Lifetime interest paid over the full term. |
remainingBalance(principal, annualRatePct, termMonths, paymentsMade) |
Closed-form principal still owed after paymentsMade payments, clamped to [0, principal]. |
payoffWithExtra(principal, annualRatePct, termMonths, extraMonthly) |
Months and interest under an accelerated schedule that pays a fixed extra amount each month. |
| Symbol | Description |
|---|---|
AmortRow |
{ period, payment, interest, principal, remainingBalance } for one scheduled payment. |
PayoffResult |
{ months, totalInterest, totalPaid } for an accelerated payoff. |
The unit tests cross-check against a standard amortization table:
| Loan | Rate | Term | Monthly | Lifetime interest |
|---|---|---|---|---|
| $30,000 | 6.0% | 60 mo | $579.98 | $4,799.04 |
| $15,000 | 9.0% | 36 mo | $477.00 | $2,171.86 |
| $300,000 | 6.5% | 360 mo | $1,896.20 | $382,633.47 |
| $400,000 | 7.0% | 360 mo | $2,661.21 | $558,035.59 |
| $250,000 | 5.5% | 180 mo | $2,042.71 | $117,687.55 |
| $45,000 | 5.0% | 72 mo | $724.72 | $7,179.98 |
Figures are illustrative, not a quote or offer of credit.
dub build
dub testRequires a D compiler (DMD or LDC) and DUB. Every public
function is @safe pure and the package has no dependencies.
MIT — see LICENSE.