Cent-accurate mortgage amortization for Python, validated against 45 published regulatory, textbook, and lender examples
Project description
mortgagemath
Cent-accurate mortgage amortization for Python. Validated against CFPB regulatory disclosures, Fannie Mae GSE servicing guides, the Reg Z Sample H-14 ARM, the FHLBB Federal Home Loan Bank Review of March 1935, and 30+ open-licensed and modern copyrighted textbook worked examples — every committed fixture cell reproduces its source value to the cent. Decimal end-to-end; no runtime dependencies beyond the Python standard library.
Why mortgagemath?
Most mortgage libraries get the closed-form payment right but
diverge from real lender statements by 1–4 cents per row. The
discrepancy compounds over the schedule, breaks reconciliation
against an actual bank statement, and makes audit work painful.
mortgagemath is built around the rounding and accounting
conventions actual lenders use:
- Decimal arithmetic end-to-end (no float drift)
- Configurable rounding for the periodic payment and per-row
interest (
ROUND_UP,ROUND_DOWN,ROUND_HALF_UP,ROUND_HALF_EVEN) - Two balance-tracking modes —
ROUND_EACH(US lender statements) andCARRY_PRECISION(Excel / graduate CRE textbooks) - Both day-count conventions — 30/360 (residential) and Actual/360 (commercial)
- Non-monthly compounding and cadence — Canadian Interest
Act §6 (
j_2), effective-annual, weekly through annual - Adjustable-rate mortgages with rate schedules, optional payment caps, and capitalized negative amortization
- Flat per-period fees via
fee_per_period, validated against French assurance emprunteur payment columns - Convenience constructors for common US fixed-rate, Canadian
j_2, Actual/360 commercial, and fixed-payment loans - Exact zero ending balance — the final row trues up so the schedule lands at $0.00
- 45 validated fixtures (including 15+ full-schedule cell-for-cell
matches) auto-discovered by
pytest
Installation
pip install mortgagemath
Requires Python 3.11+. Zero runtime dependencies.
To verify a fresh install reproduces the same reference values the test suite validates:
python -m mortgagemath
This recomputes a CFPB sample Closing Disclosure, the Goldstein §10.3 Example 1 carry-precision schedule, and the Fannie Mae §1103 Tier 2 SARM monthly payment plus balloon-at-term. Exits 0 if every value matches the published source exactly.
Quick example
The CFPB's Closing Disclosure Sample H-25(B) — $162,000 at 3.875% for 30 years — from the command line:
mortgagemath schedule --principal 162000 --rate 3.875 --term-months 360 \
--payment-rounding ROUND_HALF_UP --interest-rounding ROUND_HALF_UP \
--format csv
The same loan from Python:
from mortgagemath import (
PaymentRounding, us_30_year_fixed,
periodic_payment, amortization_schedule,
)
loan = us_30_year_fixed(
"162000.00",
"3.875",
payment_rounding=PaymentRounding.ROUND_HALF_UP,
)
print(periodic_payment(loan)) # Decimal("761.78")
sched = amortization_schedule(loan)
print(sched[1].interest) # Decimal("523.13")
print(sched[1].principal) # Decimal("238.65")
print(sched[-1].balance) # Decimal("0.00") exact closure
The lower-level LoanParams dataclass remains available when you
need every knob explicitly. For the common cases, constructors such
as us_30_year_fixed, us_15_year_fixed, canada_fixed_j2,
us_actual_360_commercial, and fixed_payment_mortgage return
ordinary validated LoanParams objects.
For Canadian j_2 mortgages, US ARMs (with rate caps and payment caps), commercial Actual/360 with balloon, and the FHLBB 1935 given-payment convention, see the Worked examples vignette.
Pandas and Matplotlib Integration
Because mortgagemath returns pure Python dataclasses mapping directly to numerical values, it integrates seamlessly into the data science ecosystem.
You can easily convert an amortization schedule into a pandas.DataFrame to do vectorized analysis, date math, or plot the results with matplotlib.
import pandas as pd
import matplotlib.pyplot as plt
from mortgagemath import us_30_year_fixed, amortization_schedule
# 1. Create a schedule
loan = us_30_year_fixed("300000", "6.5")
schedule = amortization_schedule(loan)
# 2. Convert to DataFrame
df = pd.DataFrame(schedule)
# 3. Quick plot of Principal vs Interest over time
df_plot = df[df["number"] > 0]
fig, ax = plt.subplots(figsize=(8, 5))
ax.stackplot(
df_plot["number"],
df_plot["principal"].astype(float),
df_plot["interest"].astype(float),
labels=['Principal', 'Interest']
)
ax.set_title("Amortization Schedule")
ax.legend(loc='upper right')
plt.show()
See the Pandas and Data Visualization vignette for more examples, including date offsets and scenario analysis.
What's validated
45 fully published amortization tables from government regulatory documents, GSE servicing guides, and academic textbooks, exercised by a test suite of more than 300 tests that runs on every push and every release. Every committed fixture cell reproduces its source value to the cent; on the small number of historical sources that themselves contain an internal arithmetic typo (e.g., two rows of the Geltner CRE example), the divergent rows are documented rather than forced into the corpus. The sources span:
- Regulatory disclosures — CFPB Sample H-25(B); 12 CFR Part 1026 Appendix H Sample H-14 (the 1982–1996 1/1 ARM with periodic and lifetime caps, traced through the actual CMT history)
- GSE servicing guides — Fannie Mae Multifamily §1103 Tier 2 SARM ($25 M / 5.5% / 10-year term on 30-year amortization, Actual/360)
- Federal authorities — FHLBB Federal Home Loan Bank Review, March 1935 — Direct-Reduction Plan A
- Textbooks — OpenStax Contemporary Mathematics, Geltner et al. CRE Analysis, Skinner Mathematical Theory of Investment (1913), Arcones SOA Exam FM Manual, Goldstein Finite Mathematics, eCampus Ontario Mathematics of Finance, Olivier Business Math, Las Positas Math for Liberal Arts, Mississippi State Extension Service
- Synthetic boundary cases for half-cent rounding modes
See the Validation
vignette for the full
45-fixture × 8-parameter matrix and bibliography, generated
directly from the fixture [source] blocks.
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 | How to use Pandas DataFrames and plot schedules with Matplotlib |
| 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.0.tar.gz.
File metadata
- Download URL: mortgagemath-0.7.0.tar.gz
- Upload date:
- Size: 1.4 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 |
a09cc32625d959ba28e1b944a07bb40c94a111c874e1e896bd6d076f8cea8614
|
|
| MD5 |
fbcb2acaae3f458a0f3d8f74187be088
|
|
| BLAKE2b-256 |
6ce50fd4096e6cc79d5cb23d3e5dda8fae1de750e5de65fc93f1add255d4f350
|
File details
Details for the file mortgagemath-0.7.0-py3-none-any.whl.
File metadata
- Download URL: mortgagemath-0.7.0-py3-none-any.whl
- Upload date:
- Size: 29.3 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 |
97a1ac86d7ee293f123e3cc2744707bba6fde24df372efc956673ffe60680489
|
|
| MD5 |
ed2b9d0ea0f95d8c32b6dfae231bbe23
|
|
| BLAKE2b-256 |
bc111eec7fabd6977bd89aa46d65da18e7d0333dbaf1bb853517e8bccb4fe0db
|