Cent-accurate mortgage amortization for Python — validated against 46 published sources across 6 countries. Zero dependencies.
Project description
mortgagemath
Cent-accurate mortgage amortization for Python. Every payment,
interest charge, and balance is computed with Decimal arithmetic
and validated against 46 published worked examples from government
regulators, bank servicing guides, and academic textbooks across
six countries. Zero runtime dependencies.
Why mortgagemath?
Most mortgage libraries get the monthly payment right but diverge
from real lender statements by 1–4 cents per row. That drift
compounds over the schedule, breaks reconciliation against actual
bank statements, and makes audit work painful. mortgagemath is
built around the rounding and accounting conventions that actual
lenders use:
- Decimal arithmetic end-to-end (no float drift)
- Configurable rounding —
ROUND_UP,ROUND_DOWN,ROUND_HALF_UP,ROUND_HALF_EVEN - Two balance-tracking modes —
ROUND_EACH(US lender statements) andCARRY_PRECISION(Excel / CRE textbooks) - Both day-count conventions — 30/360 and Actual/360
- International compounding — monthly (US), semi-annual (Canadian Interest Act j₂), and effective-annual
- Flexible payment frequency — monthly, bi-weekly, weekly, quarterly, semi-monthly, or annual
- Adjustable-rate mortgages with rate schedules, payment caps, and negative amortization
- Interest-only periods with automatic recast
- Flat per-period fees for French assurance emprunteur and similar insurance loadings
- Currency unit precision —
Decimal("0.01")for cents orDecimal("1")for yen/won - Exact zero ending balance — the final row trues up so the schedule lands at exactly zero
- 46 validated fixtures from the US, Canada, France, Japan, Italy, and South Korea
Installation
pip install mortgagemath
Requires Python 3.11+. Zero runtime dependencies.
python -m mortgagemath # self-check against reference values
Quick example
A standard 30-year fixed-rate mortgage from the command line:
mortgagemath summary --principal 300000 --rate 6.5 --term-months 360
Or generate the full payment-by-payment schedule:
mortgagemath schedule --principal 300000 --rate 6.5 --term-months 360
The same loan from Python:
from mortgagemath import us_30_year_fixed, periodic_payment, amortization_schedule
loan = us_30_year_fixed("300000", "6.5")
print(periodic_payment(loan)) # Decimal("1896.21")
sched = amortization_schedule(loan)
print(sched[1].interest) # Decimal("1625.00") — payment #1
print(sched[1].principal) # Decimal("271.21")
print(sched[-1].balance) # Decimal("0.00") — exact zero
print(sched[-1].total_interest) # Decimal("382628.90") — lifetime interest
The schedule is a plain list — sched[0] is the initial balance
(no payment), sched[1] through sched[360] are the monthly
payments.
For a quick summary without iterating the schedule:
from mortgagemath import us_30_year_fixed, loan_summary
s = loan_summary(us_30_year_fixed("300000", "6.5"))
print(s.periodic_payment) # Decimal("1896.21")
print(s.total_interest) # Decimal("382628.90")
print(s.total_paid) # Decimal("682628.90")
print(s.num_payments) # 360
Convenience constructors like us_30_year_fixed, us_15_year_fixed,
canada_fixed_j2, canada_accelerated_biweekly, and
us_actual_360_commercial handle common cases. The full
LoanParams dataclass is available when you need every parameter
explicitly:
from decimal import Decimal
from mortgagemath import LoanParams, PaymentRounding, amortization_schedule
loan = LoanParams(
principal=Decimal("162000"),
annual_rate=Decimal("3.875"),
term_months=360,
payment_rounding=PaymentRounding.ROUND_HALF_UP,
interest_rounding=PaymentRounding.ROUND_HALF_UP,
)
sched = amortization_schedule(loan)
print(sched[1].payment) # Decimal("761.78")
For Canadian j₂ mortgages, ARMs with rate and payment caps, commercial Actual/360 with balloon, interest-only periods, fee-loaded French schedules, and Japanese yen-precision loans, see the Worked examples vignette.
Pandas and Matplotlib integration
mortgagemath returns pure Python dataclasses, so converting to a
pandas.DataFrame for analysis or plotting is one line:
import pandas as pd
from mortgagemath import us_30_year_fixed, amortization_schedule
loan = us_30_year_fixed("300000", "6.5")
df = pd.DataFrame(amortization_schedule(loan))
See the Pandas and Data Visualization vignette for plotting examples and scenario analysis.
What's validated
46 published amortization tables exercised by a test suite of more than 400 tests that runs on every push and release. Every committed fixture cell reproduces its source value to the cent (or yen). The sources span six countries and a wide range of loan structures:
- US regulatory — CFPB sample disclosures, Reg Z Appendix H ARM with 15 years of historical rate adjustments, FHLBB 1935 direct-reduction plan
- US commercial — Fannie Mae Multifamily Actual/360 with balloon, Geltner CRE carry-precision
- US textbooks — OpenStax, Goldstein, Skinner (1913), Arcones SOA Exam FM, Las Positas, Mississippi State Extension
- Canada — Olivier and eCampus Ontario semi-annual j₂ mortgages (monthly and quarterly), RBC accelerated bi-weekly
- France — MoneyVox tableau d'amortissement with assurance emprunteur fee loading
- Japan — JHF Flat 35 and LoanKeisan full 360-row schedule with yen-precision truncation rounding
- Italy — Solution Bank and BCC Brescia regulatory transparency documents
- South Korea — Tistory worked mortgage with won-precision
- Reference works — TI BA II Plus official guidebook, Wikipedia mortgage calculator
- Synthetic — half-cent rounding boundaries, zero-interest edge case
See the Validation vignette for the full 46-fixture parameter matrix and bibliography.
Documentation
| Resource | Audience |
|---|---|
| Read the Docs | Installation, quickstart, full API reference, changelog |
| At a glance | 60-second orientation |
| Validation | Audit / risk review — full fixture matrix + bibliography |
| Worked examples | Picking the right configuration by country and loan type |
| History | Academic context — institutional and mathematical history of the level-payment mortgage |
| Pandas & Viz | DataFrames, plotting, and scenario analysis |
| HTML site | Vignettes with navigation and search |
Reporting a discrepancy
Found a published example or a real lender statement that
mortgagemath doesn't reproduce to the cent? Two paths:
- Reporters / users — open an issue using the Mortgage
example doesn't match
template. Paste the published values; no
pytestneeded. - Contributors — add a paired TOML + CSV fixture under
tests/schedules/. Seetests/schedules/README.mdfor the schema and contribution workflow.
For unrelated bugs or feature requests, use the Bug or feature request template.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mortgagemath-0.7.1.tar.gz.
File metadata
- Download URL: mortgagemath-0.7.1.tar.gz
- Upload date:
- Size: 4.0 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.10 {"installer":{"name":"uv","version":"0.11.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e5c242a124730c6b506779bccdd32e171c2053adc431d572a382cb8a263b46e3
|
|
| MD5 |
12b24355b583220827c7c157c2944f80
|
|
| BLAKE2b-256 |
ef91ffa69cb94c15a1fe47b6bbcde6321ef419f7914ac375874bd036b90ca1a3
|
File details
Details for the file mortgagemath-0.7.1-py3-none-any.whl.
File metadata
- Download URL: mortgagemath-0.7.1-py3-none-any.whl
- Upload date:
- Size: 31.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.10 {"installer":{"name":"uv","version":"0.11.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4ebb520ae826168ab03e11eb934d57df0c3fa007f0d13ca207757dc82afe9c04
|
|
| MD5 |
12e7fa68618144a1fc713fb6a20ccd85
|
|
| BLAKE2b-256 |
c231009a2393188c41f3455e8e91d53d6323d4996081ef119e0901e7b74578c3
|