Skip to main content

mortgagekit

Python 3.9+ Package: mortgagekit Amazon

Mortgage Markets in the Age of AI

A high-performance, pure-Python quantitative library for mortgage mathematics, prepayment modeling, cash-flow projections, and risk analytics. It serves as the companion computational engine for the book:

Mortgage Markets in the Age of AI
From Cash Flows, Stochastic Processes to AI Modeling: From Household Financial Decisions to Secondary Market MBS Pricing
Authors: Chiu Yan & Xuan Xin
Get the Book: Available on Amazon
Repository: https://github.com/henrywoo/mortgage-markets-in-the-age-of-ai


Key Features

  • Zero Mandatory Dependencies: Core pricing, amortization, and risk engines are built entirely on Python's standard library — installs anywhere with zero compile or dependency friction.
  • Loan-Level Amortization: Exact payment solvers, remaining balance formulas, and multi-period principal/interest amortization schedules.
  • Prepayment Mechanics: Bidirectional $CPR \leftrightarrow SMM$ conversions, survival probability analytics, and dynamic speed models (flat CPR, PSA ramps, and rate-sensitive refinancing S-curves).
  • Yield Curves & Discounting: Continuously compounded zero curves, knot bumping, par curve bootstrapping, present value, DV01, and CV01 analytics.
  • Institutional Valuation & Risk: Cash-flow projection with servicing strips, effective duration, effective convexity, effective DV01, key rate durations, static (Z-)spread, and cash-flow yield.
  • One-Call Analysis: Unified analyze(...) pipeline that delivers a complete institutional risk and valuation report in Python or JSON.
  • Built-in CLI: Full command-line interface for terminal-based pool pricing, scenario analysis, and cash-flow scheduling.
  • Fully Typed: Shipped with complete PEP 484 type annotations for IDE autocompletion and static analysis.

Installation

Install mortgagekit via pip:

pip install mortgagekit

Optional Extras

For research workflows, visualization, and advanced machine learning modeling, optional dependency bundles are available:

pip install mortgagekit[figures]   # matplotlib, numpy, mathicon (figure generation)
pip install mortgagekit[ml]        # torch, scikit-learn, scipy, xgboost (AI/ML models)

Quick Start

import mortgagekit as mk

# 1. Calculate monthly payment on a $400,000, 30-year mortgage at 6.5%
payment = mk.mortgage_payment(balance=400_000, annual_rate=0.065, months=360)
print(f"Monthly Payment: ${payment:.2f}")
# Monthly Payment: $2528.27

# 2. Convert a 12% annualized CPR into monthly SMM
smm = mk.cpr_to_smm(0.12)
print(f"Monthly SMM: {smm * 100:.4f}%")
# Monthly SMM: 1.0596%

# 3. Analyze a mortgage pool against a zero curve in one call
pool = mk.MortgagePool(balance=1_000_000, wac=0.065, wam=360, net_coupon=0.060)
curve = mk.Curve.from_zeros([(0.25, 0.043), (2, 0.041), (5, 0.042), (10, 0.045), (30, 0.047)])

report = mk.analyze(pool, curve, mk.PSA(200), spread=0.0075)
print(report.summary())

Core Capabilities & Mathematical Reference

1. Amortization and Loan Schedules

Contains analytical functions to compute fixed monthly payments, remaining loan balances, and multi-period amortization schedules.

Mathematical Formulation

For a self-amortizing fixed-rate mortgage with initial balance $B$, monthly interest rate $r_m = \text{annual_rate} / 12$, and total payment periods $N = \text{months}$, the fixed monthly payment $P$ is:

$$P = B \times \frac{r_m}{1 - (1 + r_m)^{-N}}$$

The remaining principal balance $B_k$ after the $k$-th payment period is given recursively by:

$$B_k = B_{k-1} \times (1 + r_m) - P$$

Python Example

from mortgagekit import mortgage_balance, mortgage_payment, mortgage_schedule

# Compute monthly payment on a $400,000, 30-year (360-month) loan at 6.5% interest
payment = mortgage_payment(balance=400_000, annual_rate=0.065, months=360)
print(f"Monthly Payment: ${payment:.2f}")
# Output: Monthly Payment: $2528.27

# Check remaining scheduled balance after 5 years (60 months)
balance_after_5y = mortgage_balance(balance=400_000, annual_rate=0.065, months=360, month=60)
print(f"Balance after 5 Years: ${balance_after_5y:.2f}")

# Generate full multi-period amortization schedule
schedule = mortgage_schedule(balance=400_000, annual_rate=0.065, months=360)
m1 = schedule[0]
print(f"Month {m1.month}: Payment=${m1.payment:.2f}, Interest=${m1.interest:.2f}, Principal=${m1.principal:.2f}, Balance=${m1.balance:.2f}")

2. Prepayment Conventions and Speed Models

Maps annualized prepayment rates into month-over-month conditional rates and models behavioral prepayment speeds.

Mathematical Formulation

The relationship between the annualized Conditional Prepayment Rate ($CPR$) and monthly Single Monthly Mortality ($SMM$) is:

$$SMM = 1 - (1 - CPR)^{\frac{1}{12}}$$

$$CPR = 1 - (1 - SMM)^{12}$$

Under a constant prepayment speed of $CPR$, the survival probability $S(t)$ representing the probability that a loan balance has not prepaid after $t$ months is:

$$S(t) = (1 - SMM)^t$$

Prepayment Models

mortgagekit provides three prepayment speed specifications sharing the uniform smm(age, rate_shift) interface:

  • FlatCPR(cpr): Constant prepayment speed across all periods.
  • PSA(speed): Standard PSA ramp (100 PSA = 0.2% CPR increase per month up to 6.0% CPR at month 30, flat thereafter).
  • RefiSCurve(wac, mortgage_rate): S-curve model that accelerates prepayment when market rates fall below the loan's coupon and burns out as rates rise.

Python Example

from mortgagekit import FlatCPR, PSA, RefiSCurve, cpr_to_smm, smm_to_cpr, survival_probability

# Convert a 12% CPR to monthly SMM
smm_12 = cpr_to_smm(0.12)
print(f"12% CPR corresponds to {smm_12 * 100:.4f}% SMM")
# Output: 12% CPR corresponds to 1.0596% SMM

# Convert a monthly SMM of 1.5% back to annualized CPR
cpr_reconstructed = smm_to_cpr(0.015)
print(f"1.5% SMM corresponds to {cpr_reconstructed * 100:.4f}% CPR")

# Evaluate survival probability after 36 months at 8% CPR
survival_pct = survival_probability(cpr=0.08, months=36)
print(f"Active balance ratio after 3 years: {survival_pct * 100:.2f}%")

# Instantiate prepayment speed models
flat_model = FlatCPR(0.06)                               # 6% constant CPR
psa_model = PSA(200)                                     # 200% PSA benchmark
refi_model = RefiSCurve(wac=0.065, mortgage_rate=0.065)  # Rate-responsive S-curve

3. Yields, Rates, and Risk Sensitivities

Evaluates continuous-compounding discount factors, bond prices, DV01 (dollar value of 1 basis point), and CV01 (convexity value of 1 basis point).

Mathematical Formulation

Under continuous compounding with rate $r$, the discount factor $d(t)$ at maturity time $t$ is:

$$d(t) = e^{-r t}$$

For a set of discrete future cash flows ${ (t_i, C_i) }_{i=1}^n$, the bond price $B(y)$ under flat yield $y$ is:

$$B(y) = \sum_{i=1}^n C_i e^{-y t_i}$$

The first-order price sensitivity DV01 represents the dollar price increase from a one-basis-point ($10^{-4}$) drop in yields:

$$\text{DV01} = -\frac{dB}{dy} \times 10^{-4} = \left( \sum_{i=1}^n t_i C_i e^{-y t_i} \right) \times 10^{-4}$$

The second-order curvature sensitivity CV01 represents the convexity adjustment for a one-basis-point shock:

$$\text{CV01} = \frac{1}{2} \frac{d^2B}{dy^2} \times (10^{-4})^2 = \frac{1}{2} \left( \sum_{i=1}^n t_i^2 C_i e^{-y t_i} \right) \times 10^{-8}$$

Python Example

from mortgagekit import bond_price, cv01, discount_factor, dv01

# Continuous discount factor for 2.5 years at 5.0% rate
df = discount_factor(rate=0.05, t=2.5)
print(f"Discount Factor: {df:.6f}")

# Define discrete cash flows: list of (time_in_years, cashflow_amount)
cashflows = [
    (0.5, 12_000.0),
    (1.0, 12_000.0),
    (1.5, 12_000.0),
    (2.0, 112_000.0),
]

# Price cash flows under a flat 4.5% yield
price = bond_price(cashflows, y=0.045)
print(f"Bond Present Value: ${price:.2f}")

# Compute DV01 and CV01 risk metrics
dollar_value_01 = dv01(cashflows, y=0.045)
convexity_value_01 = cv01(cashflows, y=0.045)

print(f"DV01 Risk: ${dollar_value_01:.4f} per 1 bp decline")
print(f"CV01 Risk: ${convexity_value_01:.6f} curvature sensitivity")

The Valuation and Risk Engine

The valuation engine projects what a mortgage pool pays, discounts each cash-flow slice along a term-structure zero curve, and measures how pool value moves across yield curve shifts.

Projecting and Pricing a Mortgage Pool

from mortgagekit import Curve, MortgagePool, PSA, pool_cashflows, price, weighted_average_life

# Create a $1mm pool of 6.5% loans passing 6.0% through to investors after 50bp servicing
pool = MortgagePool(balance=1_000_000, wac=0.065, wam=360, net_coupon=0.060)

# Build a term structure from benchmark zero rate knots
curve = Curve.from_zeros([(0.25, 0.043), (2, 0.041), (5, 0.042), (10, 0.045), (30, 0.047)])

# Project monthly cash flows under 200 PSA
flows = pool_cashflows(pool, PSA(200))
print(f"Weighted Average Life (WAL): {weighted_average_life(flows):.2f} years")
print(f"Discounted Price: ${price(pool, curve, PSA(200)):,.0f}")

Each month, cash flows follow standard servicer accounting:

  1. Interest: Accrues on opening balance at the net coupon.
  2. Scheduled Principal: Recomputed from opening balance over remaining maturity.
  3. Prepayment: SMM applied to the balance remaining after scheduled principal.
  4. Total Principal: Scheduled principal + prepayment. Principal always returns in full — prepayment changes when money returns, never how much.

Full Institutional Risk Report in One Call

from mortgagekit import Curve, MortgagePool, RefiSCurve, analyze

pool = MortgagePool(balance=1_000_000, wac=0.065, wam=360, net_coupon=0.060)
curve = Curve.from_zeros([(0.25, 0.043), (2, 0.041), (5, 0.042), (10, 0.045), (30, 0.047)])

report = analyze(pool, curve, RefiSCurve(wac=0.065, mortgage_rate=0.065), spread=0.0075)
print(report.summary())

Output:

Price                   104.5184 per 100
WAL                         6.43 years
Cash-flow yield           5.1078 %  (monthly nominal)
Static spread               75.0 bp over the curve
Effective duration          3.83
Static duration             5.01
Duration given up           1.18  to the borrower's option
Effective convexity      -363.02
Effective DV01          400.7424 per 1bp fall in rates
Final cash flow              360 months out
Key rate durations    0.25y +0.09  2y +0.79  5y +1.70  10y +2.06  30y +0.37

Effective vs. Static Duration & Negative Convexity

A static duration differentiates the price of fixed, unchanging cash flows. An effective duration reprices the pool while allowing prepayment cash flows to adjust dynamically to the rate shock:

$$D_{\text{eff}} = \frac{P_{-} - P_{+}}{2 P_0 \Delta y} \qquad C_{\text{eff}} = \frac{P_{+} + P_{-} - 2 P_0}{P_0 (\Delta y)^2}$$

where $P_{\pm}$ are repriced after reprojecting cash flows under the shocked yield curve. Holding cash flows static during rate shifts is the classic modeling mistake that hides mortgage negative convexity.

Priced at a 75 bp spread, compare a 6% pass-through with embedded prepayment options against the same pool with the option turned off:

Rate Shock CPR Price (With Option) Price (No Option) Option Drag
−300 bp 43.7% 111.329 126.114 −14.785
−100 bp 29.1% 107.061 111.322 −4.260
0 bp 15.6% 104.518 104.978 −0.460
+100 bp 8.6% 98.849 99.219 −0.370
+300 bp 6.1% 85.760 89.177 −3.417

The mortgage pool loses on both sides: it shortens into rallies (upside capped) and extends into selloffs (losses compounded). That asymmetry is what negative convexity ($-363$) means, and why duration-hedged mortgage desks still bleed without convexity protection.


Static Spread (Z-Spread) vs. OAS

  • static_spread: Solves for the single constant spread over the zero curve that reprices the pool along a single deterministic cash-flow projection. This is a Z-spread.
  • OAS (Option-Adjusted Spread): The spread that survives once cash flows are simulated across hundreds of stochastic interest rate paths. mortgagekit's engine is deliberately deterministic, exact, and fast — stochastic OAS simulation is covered in Chapter 6 of the book.

Key Rate Durations (KRD)

from mortgagekit import Curve, MortgagePool, PSA, key_rate_durations

pool = MortgagePool(balance=1_000_000, wac=0.065, wam=360, net_coupon=0.060)
curve = Curve.from_zeros([(0.25, 0.043), (2, 0.041), (5, 0.042), (10, 0.045), (30, 0.047)])

for tenor, krd in key_rate_durations(pool, curve, PSA(200)):
    print(f"{tenor:>5}y: {krd:+.3f}")

Bumping individual knots produces triangular basis shifts along the yield curve. With prepayment held constant, the key rate durations sum to the static duration, isolating curve steepening, flattening, and butterfly risks.


Command-Line Interface (CLI)

mortgagekit includes a terminal CLI executable via mortgagekit or python -m mortgagekit:

# Price a pool with a 6.5% WAC, 6.0% net coupon, 75 bp spread, and refi S-curve
mortgagekit --balance 1000000 --wac 6.5 --coupon 6.0 --refi 6.5 --spread 75

# Price a pool under 200 PSA and output the first 12 months schedule in JSON
mortgagekit --balance 1000000 --wac 6.5 --coupon 6.0 --psa 200 --schedule 12 --json

CLI Options

Argument Description Default
--balance Initial pool balance 1000000
--wac Weighted average coupon (in %, e.g., 6.5) 6.5
--wam Weighted average maturity in months 360
--coupon Pass-through net coupon (in %, e.g., 6.0) 6.0
--spread Spread over the zero curve in basis points 0.0
--psa Select PSA prepayment model with given speed —
--cpr Select constant CPR prepayment model (in %) —
--refi Select refinance S-curve model with market rate (in %) —
--schedule N Print the first $N$ months of projected cash flows —
--json Output the complete report and schedules as structured JSON —

Verification & Type Safety

Verify your installation:

python -c "import mortgagekit as mk; print(f'mortgagekit {mk.__version__} ready')"

mortgagekit includes comprehensive PEP 484 type annotations, providing immediate IDE autocomplete and static type validation for financial engineering pipelines.


Documentation & Resources

Metadata

Release files for mortgagekit 0.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for mortgagekit 0.0.1
File
mortgagekit-0.0.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl CPython 3.14 CPython 3.14 Linux glibc 2.28+ x86-64, Linux glibc 2.17+ x86-64 Details
mortgagekit-0.0.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl CPython 3.13 CPython 3.13 Linux glibc 2.17+ x86-64, Linux glibc 2.28+ x86-64 Details
mortgagekit-0.0.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl CPython 3.12 CPython 3.12 Linux glibc 2.17+ x86-64, Linux glibc 2.28+ x86-64 Details
mortgagekit-0.0.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl CPython 3.11 CPython 3.11 Linux glibc 2.28+ x86-64, Linux glibc 2.17+ x86-64 Details
mortgagekit-0.0.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl CPython 3.10 CPython 3.10 Linux glibc 2.28+ x86-64, Linux glibc 2.17+ x86-64 Details
mortgagekit-0.0.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl CPython 3.9 CPython 3.9 Linux glibc 2.17+ x86-64, Linux glibc 2.28+ x86-64 Details

Total release size: 844.1 kB

Release files / mortgagekit-0.0.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl

Download URL mortgagekit-0.0.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Size 157.7 kB
Tags CPython 3.14 Linux glibc 2.17+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
0deae93e6fdf952ffe6be38c78832a6248c7a584a7ca4302aabd6fd671b39262
BLAKE2b-256 checksum
How to use checksums
cf50dee9ccd153382c739cb29d3a00b2ea47e5af8c51f260b3ec548bd18e01f3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.12

Release files / mortgagekit-0.0.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl

Download URL mortgagekit-0.0.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Size 150.1 kB
Tags CPython 3.13 Linux glibc 2.17+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
01e748f51e2acc5296025b33d6f43322dd39c793f26470144bfc7b6aeda608b9
BLAKE2b-256 checksum
How to use checksums
10981db0d8db6bcec5093de494ac4374ea5a0db4d89dd64f9fd3241f278162f0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.12

Release files / mortgagekit-0.0.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl

Download URL mortgagekit-0.0.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Size 150.2 kB
Tags CPython 3.12 Linux glibc 2.17+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
b1ce46c2943e26a7cd3a8894110498d3c89d7e482308ef1520373fda6965553c
BLAKE2b-256 checksum
How to use checksums
7d23f78a823797bcd68e9a63784d1eac047ad68e519df56f46d7d5b5c550ba63
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.12

Release files / mortgagekit-0.0.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl

Download URL mortgagekit-0.0.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Size 155.4 kB
Tags CPython 3.11 Linux glibc 2.17+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
8cc97a72e1a24fd475502f2112804cee2aed824d130e8302f94477406a516c50
BLAKE2b-256 checksum
How to use checksums
b084ecf29f6fd5cdb573173a0c7cc325317d7d506bcbd5070bd700f105d50d8b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.12

Release files / mortgagekit-0.0.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl

Download URL mortgagekit-0.0.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Size 116.1 kB
Tags CPython 3.10 Linux glibc 2.17+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
d6209a028e1ad2021705ea0dac756f5c8ff2fa01d55afbd5abf422a821f8fda3
BLAKE2b-256 checksum
How to use checksums
6326472298a80136afdcb42e98ab157a83778d62060a181199aa5a0ca2318d19
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.12

Release files / mortgagekit-0.0.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl

Download URL mortgagekit-0.0.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Size 114.6 kB
Tags CPython 3.9 Linux glibc 2.17+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
d9322431121b1e87d4619fc9c3b6f8d358ed23402239ae7b55e9e467ae65daca
BLAKE2b-256 checksum
How to use checksums
e00ff946dfd84f52411c4d67276a9c4c56f5f57402ecbd1c7ba2bb1e4415c9ee
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.12

Security for mortgagekit 0.0.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page