← Mortgage Offer Analyzer

The math behind the Mortgage Offer Analyzer

A mathematical walkthrough of the tool.

Notation used throughout:

Symbol Meaning
PP principal — the amount actually borrowed
rr quoted annual nominal rate (as a decimal)
mm compounding periods per year (12 = monthly)
i=r/mi = r/m periodic rate
TT term in years
n=mTn = mT total number of payments
MM the fixed periodic payment (principal + interest)
BkB_k balance still owed after payment kk
FF upfront fees, paid at closing
VV home value at purchase
hh holding period in years; k=mhk = mh periods

Part 1 — The loan, and where the payment number comes from

The rule of the loan

Each period two things happen, in this order: the outstanding balance grows by the interest factor, then you hand over a fixed payment MM.

Bt=Bt−1(1+i)−M,B0=PB_t = B_{t-1}(1+i) - M, \qquad B_0 = P

That is the entire financial content of a mortgage. Everything else on the site is a consequence of this one line.

Solving the recurrence

It is a linear first-order recurrence, so unroll it:

Bk=P(1+i)k−M∑j=0k−1(1+i)j=P(1+i)k−M (1+i)k−1iB_k = P(1+i)^k - M\sum_{j=0}^{k-1}(1+i)^j = P(1+i)^k - M\,\frac{(1+i)^k - 1}{i}

The sum is geometric. Two competing terms: your debt compounding upward, and your accumulated payments compounding upward, racing each other.

Fixing the payment MM

A mortgage is defined by the condition that the balance reaches zero exactly at period nn — that is what “30-year fixed” means. Set Bn=0B_n = 0 and solve:

  M=P i(1+i)n(1+i)n−1  \boxed{\;M = P\,\frac{i(1+i)^n}{(1+i)^n - 1}\;}

Two limits worth checking yourself:

Total interest

Total handed over across the full term is MnMn. Of that, PP is money you actually borrowed. The rest is interest:

total interest=Mn−P\text{total interest} = Mn - P

Note this is not P r TP\,r\,T. That would be interest on the full principal for the whole term, but the balance is falling the entire time. The true amount is i∑k=0n−1Bki\sum_{k=0}^{n-1} B_k — a sum under the decaying balance curve — and the two expressions agree.

Effective annual rate

The quoted rate rr is a nominal convention: “6% compounded monthly” means 0.5% applied twelve times, which is not 6% of growth. Actual growth over one year:

EAR=(1+i)m−1\text{EAR} = (1+i)^m - 1

For r=6%r = 6\%: 1.00512−1=6.168%1.005^{12} - 1 = 6.168\%. This is the site’s apples-to-apples number when two offers compound differently.

One numerical note

Everything above needs (1+i)k−1(1+i)^k - 1. Computing that as pow(1+i,k) - 1 catastrophically cancels for small ii: you subtract 1 from a number barely above 1 and lose every significant digit. The site computes it as exp⁡ ⁣(klog⁡(1+i))−1\exp\!\big(k\log(1+i)\big) - 1 using expm1/log1p, which are built to hold precision near zero.


Part 2 — The balance at an arbitrary time, and the shape of that curve

A cleaner closed form

Part 1 gave Bk=P(1+i)k−M (1+i)k−1iB_k = P(1+i)^k - M\,\frac{(1+i)^k - 1}{i}. Regroup the terms that carry (1+i)k(1+i)^k:

  Bk=(P−Mi)(1+i)k+Mi  \boxed{\;B_k = \left(P - \frac{M}{i}\right)(1+i)^k + \frac{M}{i}\;}

This is the form worth remembering. It says the balance is an exponential displaced by a constant, and the constant M/iM/i is the whole story.

What M/iM/i is. It is the principal of a loan whose interest exactly equals the payment — the perpetuity from Part 1, the balance that would sit still forever. Call it the equilibrium B∗=M/iB^\ast = M/i. Then

Bk=B∗−(B∗−P)(1+i)kB_k = B^\ast - (B^\ast - P)(1+i)^k

and B∗B^\ast is a repelling fixed point of the recurrence B↦B(1+i)−MB \mapsto B(1+i)-M. Start below it and you are pushed away downward, faster and faster. Start above it and the balance runs away upward — that is a loan that never amortizes. A real mortgage always has P<B∗P < B^\ast, and the gap B∗−PB^\ast - P is the seed of the exponential.

(Worked case used throughout: P=$400,000P = \$400{,}000, r=6.5%r=6.5\%, 30 years. Then M=$2,528.27M = \$2{,}528.27 and B∗=$466,758B^\ast = \$466{,}758. The loan starts only 17% below its own runaway point — which is why the early years feel like nothing is happening.)

Treating kk as continuous

Write δ=ln⁡(1+i)\delta = \ln(1+i), the continuously-compounded equivalent of ii (in finance, the force of interest). Then (1+i)k=eδk(1+i)^k = e^{\delta k} and

B(k)=B∗−(B∗−P)eδkB(k) = B^\ast - (B^\ast - P)e^{\delta k}

B′(k)=− δ (B∗−P) eδk,B′′(k)=− δ2(B∗−P) eδkB'(k) = -\,\delta\,(B^\ast - P)\,e^{\delta k}, \qquad B''(k) = -\,\delta^2 (B^\ast - P)\,e^{\delta k}

Both derivatives are negative for the whole term. So the balance curve is decreasing and concave: it falls slowly at first and the fall accelerates, lying above the straight line from (0,P)(0,P) to (n,0)(n,0) the entire way. Nothing about a mortgage is linear, and this is where the intuition breaks for most people.

Concavity has a blunt consequence. Setting B(k)=P/2B(k) = P/2:

k1/2=1δ ln⁡ ⁣B∗−P/2B∗−Pk_{1/2} = \frac{1}{\delta}\,\ln\!\frac{B^\ast - P/2}{B^\ast - P}

For the worked case: k1/2=256k_{1/2} = 256 payments =21.4= 21.4 years. You are 21 years into a 30-year loan before you have repaid half the principal.

How each payment splits

Payment tt is a fixed MM, but it is doing two jobs. Interest owed that period is iBt−1i B_{t-1}; whatever is left knocks down principal:

iBt−1⏟interest+(M−iBt−1)⏟principal=M\underbrace{i B_{t-1}}_{\text{interest}} + \underbrace{\big(M - i B_{t-1}\big)}_{\text{principal}} = M

The principal portion has an exact structure. Let pt=M−iBt−1p_t = M - iB_{t-1}. Then

pt+1=M−iBt=M−i(Bt−1(1+i)−M)=(1+i)(M−iBt−1)=(1+i) ptp_{t+1} = M - iB_t = M - i\big(B_{t-1}(1+i) - M\big) = (1+i)\big(M - iB_{t-1}\big) = (1+i)\,p_t

The principal portion grows by exactly the factor (1+i)(1+i) every single period — a clean geometric sequence, pt=p1(1+i)t−1p_t = p_1 (1+i)^{t-1}, with p1=M−iPp_1 = M - iP. The interest portion is its mirror image, M−ptM - p_t, decaying toward zero.

In the worked case, payment 1 is $2,166.67 interest and $361.61 principal — 86% of your money evaporating. Payment 360 is $13.62 interest and $2,514.65 principal. Same $2,528.27 either way.

What the site computes at a holding horizon

If you sell or refinance after hh years, that is k=mhk = mh periods. The site needs three numbers:

Bk=(P−Mi)(1+i)k+MiB_k = \left(P - \frac{M}{i}\right)(1+i)^k + \frac{M}{i}

paid=Mk\text{paid} = Mk

interest paid=Mk−(P−Bk)⏟principal retired\text{interest paid} = Mk - \underbrace{(P - B_k)}_{\text{principal retired}}

The last one is just conservation: of the MkMk dollars handed over, exactly P−BkP - B_k went to principal, so the remainder was interest.

horizon() evaluates these. Two details: it clamps k≤nk \le n, since holding past the term is just holding to the term, and it carries the same i=0i = 0 branch as calc(), where the balance degenerates to max⁡(P−Mk,  0)\max(P - Mk,\;0).

These three are not yet the cost of holding for hh years. Two components have not appeared: mortgage insurance, derived in Part 3, and the upfront fees FF, which Part 4 folds in alongside the payoff of BkB_k.


Part 3 — Mortgage insurance, the one piecewise part of the model

Everything so far has been one smooth exponential. Mortgage insurance is where that stops.

What it is

If you borrow more than 80% of what the home is worth, the lender requires private mortgage insurance (PMI), a policy insuring the lender against your default. It protects the lender, not you, and you pay for it on top of MM, until the loan is small enough relative to the home that the lender stops demanding it.

Three new inputs: the home’s value at purchase VV, an annual premium rate qq, and a choice of removal rule θ\theta.

Loan-to-value

LTVk=BkV\mathrm{LTV}_k = \frac{B_k}{V}

VV here is the value at purchase, and it never moves. Not the current market value, not an appraisal you order later. That is not a modeling shortcut: the federal Homeowners Protection Act (HPA) defines termination against the original value — “the lesser of the sales price … or the appraised value at the time at which the subject residential mortgage transaction was consummated” (12 U.S.C. § 4901(12)) — so a rising market does not shorten this schedule.

The only moving part in the ratio is the numerator BkB_k, falling along the Part 2 curve. That fall is the whole mechanism by which PMI ends.

Is it owed at all?

Only if the loan starts above 80% LTV:

PMI required  ⟺  PV>0.80\text{PMI required} \iff \frac{P}{V} > 0.80

pmiOwed() tests this. It compares against 0.80+ε0.80 + \varepsilon with ε=10−12\varepsilon = 10^{-12}, so a loan sitting exactly at 80% — P=$360,000P = \$360{,}000 on a $450,000\$450{,}000 home — is not dragged over the line by binary rounding.

The premium

Charged per period at rate q/mq/m, against one of two bases the lender picks:

premt=qm×{Bt−1declining basisPfixed basis\text{prem}_t = \frac{q}{m} \times \begin{cases} B_{t-1} & \text{declining basis}\\[2pt] P & \text{fixed basis} \end{cases}

Note the balance used is Bt−1B_{t-1}, the balance before payment tt. Under the declining basis the premium shrinks along the amortization curve; under the fixed basis it is a constant dollar amount. Both conventions are in real use, which is why it is an input rather than an assumption.

When it stops

Two rules, both from 12 U.S.C. § 4902. PMI ends at whichever fires first.

1. The LTV threshold. Premiums stop once the balance reaches θV\theta V. The site offers the two HPA exit points: θ=0.80\theta = 0.80 (you may request cancellation in writing) and θ=0.78\theta = 0.78 (the servicer must terminate automatically).

Setting B(k)=θVB(k) = \theta V in the Part 2 closed form:

kθ=1δ ln⁡B∗−θVB∗−Pk_\theta = \frac{1}{\delta}\,\ln\frac{B^\ast - \theta V}{B^\ast - P}

2. The amortization midpoint. § 4902© forbids the requirement “beyond the first day of the month immediately following … the midpoint of the amortization period”, regardless of balance. This binds only when the loan amortizes too slowly to reach θV\theta V in half the term — a high initial LTV, a long term, or both.

tend=min⁡(⌈kθ⌉,  ⌊n/2⌋)t_{\text{end}} = \min\big(\lceil k_\theta \rceil,\; \lfloor n/2 \rfloor\big)

(Worked case: P=$400,000P=\$400{,}000 at 6.25%, 30 years, V=$450,000V=\$450{,}000, θ=0.78\theta=0.78. Then kθ=98.997k_\theta = 98.997, the midpoint is 180, so the threshold binds and tend=99t_{\text{end}} = 99 — PMI runs 8.25 years. The site reports period 99.)

Note: Prepayment effects are not modeled

The site has no extra-payment input: it models the scheduled balance of Part 2 and nothing else, so scheduled and actual coincide and kθk_\theta is the answer for either threshold.

If you do pay extra, the two thresholds diverge, and the statute is explicit about it. The 80% cancellation date is, at your option, the date the balance “based solely on actual payments, reaches 80 percent of the original value” (§ 4901(2)(A)(ii)) — prepayment pulls it in. The 78% termination date is fixed “based solely on the initial amortization schedule … and irrespective of the outstanding balance” (§ 4901(18)(A)) — prepayment does not move it, and neither does it move the midpoint.

So under prepayment the site’s PMI figure stays exact for θ=0.78\theta = 0.78 and becomes an upper bound for θ=0.80\theta = 0.80.

Accumulated PMI

The site carries the running total

cumPMIk=∑t=1kpremt\mathrm{cumPMI}_k = \sum_{t=1}^{k}\text{prem}_t

so it can be read off at any horizon. Under the fixed basis this is trivially linear, qPmmin⁡(k,tend)\frac{qP}{m}\min(k, t_{\text{end}}). Under the declining basis, sum the Part 2 closed form for k≤tendk \le t_{\text{end}}:

∑t=1kBt−1=∑j=0k−1[B∗−(B∗−P)(1+i)j]=kB∗−(B∗−P)(1+i)k−1i\sum_{t=1}^{k} B_{t-1} = \sum_{j=0}^{k-1}\Big[B^\ast - (B^\ast - P)(1+i)^j\Big] = kB^\ast - (B^\ast - P)\frac{(1+i)^k - 1}{i}

and recognize the second term: from Part 2, P−Bk=(B∗−P)[(1+i)k−1]P - B_k = (B^\ast - P)[(1+i)^k-1], so the sum is kB∗−(P−Bk)/ikB^\ast - (P - B_k)/i. Substituting B∗=M/iB^\ast = M/i and multiplying by q/m=qi/rq/m = qi/r, since i=r/mi=r/m:

  cumPMIk=qr[Mk−(P−Bk)]=qr×interest paid through k  \boxed{\;\mathrm{cumPMI}_k = \frac{q}{r}\Big[Mk - (P - B_k)\Big] = \frac{q}{r}\times\text{interest paid through }k\;}

Declining-basis PMI is a fixed fraction q/rq/r of the interest paid over the same window. Which makes sense once you see it: both are the same balance integral, one scaled by q/mq/m and the other by i=r/mi = r/m. In the worked case q/r=0.0055/0.0625=8.8%q/r = 0.0055/0.0625 = 8.8\%, interest through period 99 is $194,822\$194{,}822, and 8.8%8.8\% of that is $17,144\$17{,}144 — exactly the total PMI the site reports.

The site does not use this identity; calc() accumulates the sum in a loop and stores it. The stored array makes horizon() an O(1)O(1) lookup, and the chart calls it hundreds of times per offer to draw a line — cheaper than recomputing logarithms at every sample. And the loop is the specification: its per-period test is the termination rule transcribed, where the closed form is a derivation whose rounding has to be kept in agreement with it.

Why the cost curve has no jump at tendt_{\text{end}}

cumPMIk\mathrm{cumPMI}_k is a running total, so it is continuous everywhere. PMI ending removes a rate, not a level: the sequence premt\text{prem}_t drops to zero, which is a discontinuity in the increment. In the sum, only the slope inherits that discontinuity; the value stays continuous. Formally cumPMI\mathrm{cumPMI} is C0C^0 and not C1C^1 at tendt_{\text{end}}.

Part 5 quantifies how small that slope discontinuity is on screen.


Part 4 — Total cost, and the cost to walk away

Parts 1-3 produced pieces. This part adds upfront fees, assembles everything into the two totals that decide which offer is cheaper, and states what those totals leave out.

Fees

FF is the upfront cost of taking the loan: origination, points, and the rest of the closing charges. It is paid in cash at closing and not rolled into PP, so it never accrues interest and never amortizes. In the model it is a constant, added once.

It is also the only quantity so far that is large at k=0k=0 and never grows. That asymmetry is what makes the comparison interesting: a lower rate bought with points is a fixed cost now against a slowly accumulating saving later.

Cost to term

If you hold the loan to the end:

  Cterm=Mn⏟all payments+F+cumPMIn  \boxed{\;C_{\text{term}} = \underbrace{Mn}_{\text{all payments}} + F + \mathrm{cumPMI}_n\;}

Cost to walk away

If you sell or refinance at hh years, k=mhk = mh periods, you stop paying MM and must clear the remaining balance in one lump:

  Cclear(k)=Mk⏟payments made+cumPMIk+F+Bk  \boxed{\;C_{\text{clear}}(k) = \underbrace{Mk}_{\text{payments made}} + \mathrm{cumPMI}_k + F + B_k\;}

Note: PMI enters as cumPMIk\mathrm{cumPMI}_k, the premiums actually paid through kk, and nothing more. Paying off BkB_k ends the loan, and premiums that would have been charged between kk and tendt_{\text{end}} are never incurred.

The two agree where they should. At k=nk = n: Bn=0B_n = 0, cumPMIn\mathrm{cumPMI}_n is the full total, and Cclear(n)=CtermC_{\text{clear}}(n) = C_{\text{term}}.

The five-component decomposition

The site draws both totals as a stacked bar. The components are chosen to partition the total exactly — they sum to it, with no overlap and no remainder:

Component At horizon kk To term
Interest paid Mk−(P−Bk)Mk - (P - B_k) Mn−PMn - P
PMI cumPMIk\mathrm{cumPMI}_k cumPMIn\mathrm{cumPMI}_n
Upfront fees FF FF
Remaining balance BkB_k 00
Principal retired P−BkP - B_k PP

To see that they sum correctly, add interest paid and principal retired. The (P−Bk)(P - B_k) subtracted from the first is exactly the second, so it cancels:

[Mk−(P−Bk)]+(P−Bk)=Mk\big[Mk - (P - B_k)\big] + (P - B_k) = Mk

which is every payment you made. Adding the remaining balance, PMI, and fees gives Cclear(k)C_{\text{clear}}(k). The same cancellation at k=nk = n gives CtermC_{\text{term}}.

Inflation and equity are not modeled

Two modeling choices are worth stating plainly, because both affect how you should read the comparison.

No time value of money. Every term above is a nominal dollar, and dollars from different years are added as equals. There is no discounting, no assumed inflation, no return on the cash you did not spend on points. A dollar of fees at closing counts the same as a dollar of interest in year 28, even though the first is unambiguously more expensive.

Not your net financial position. CclearC_{\text{clear}} is the total cash the loan consumes. The house is not credited back — no sale price, no appreciation, no selling costs, no tax treatment of interest. Notice this makes the “principal retired” component misleading if read alone: that money was not lost, it became equity. It appears in the total because the total is cash out, not net worth.

These choices are made because the tool is designed to compare offers on the same house. Every omitted term — the house’s value, the appreciation, the sale costs — is identical across offers and cancels in the difference. What survives is exactly what the offers differ on: MM, FF, and the PMI schedule.

The consequence is that the differences between offers are trustworthy and the absolute totals are not a forecast of your finances.


Part 5 — The cost-over-time chart

The chart plots CclearC_{\text{clear}} from Part 4 as a function of how long you hold, one curve per offer, jj:

yj(h)=Cclear(j)(mh),h∈[0,T]y_j(h) = C^{(j)}_{\text{clear}}(mh), \qquad h \in [0, T]

Everything interesting about it follows from one derivative.

The slope

Take the difference of CclearC_{\text{clear}} over one period. The payment MM and the change in balance are both in there, and they nearly cancel:

ΔCk=M⏟payment+premk⏟PMI+(Bk−Bk−1)⏟balance change=M+premk+(iBk−1−M)\Delta C_k = \underbrace{M}_{\text{payment}} + \underbrace{\text{prem}_k}_{\text{PMI}} + \underbrace{(B_k - B_{k-1})}_{\text{balance change}} = M + \text{prem}_k + \big(iB_{k-1} - M\big)

  ΔCk=iBk−1+premk  \boxed{\;\Delta C_k = i B_{k-1} + \text{prem}_k\;}

Holding the loan one more period costs exactly the interest accrued plus the premium charged. The payment cancels completely. This is the Part 4 point in derivative form: principal is a transfer, not a cost, so the portion of MM that retires principal moves money from your pocket into your equity and nets to zero here. Only interest and insurance leave.

Three consequences, all visible in the chart:

The PMI slope discontinuity, quantified

Under the declining basis premk=qmBk−1\text{prem}_k = \frac{q}{m}B_{k-1}, so while PMI runs

ΔCk=Bk−1(i+qm)\Delta C_k = B_{k-1}\left(i + \frac{q}{m}\right)

and afterwards it is Bk−1 iB_{k-1}\,i. Comparing the two at the same balance, the slope falls by

q/m i+q/m =qr+q\frac{q/m}{\,i + q/m\,} = \frac{q}{r + q}

independent of principal, balance, term, and time. For a typical q=0.6%q = 0.6\% against r=6.5%r = 6.5\%, that is 0.006/0.071=8.5%0.006/0.071 = 8.5\%.

Under the fixed basis premk=qmP\text{prem}_k = \frac{q}{m}P, a constant, so the slope falls by that constant amount. The relative drop is not universal — it depends on how far the loan has amortized by tendt_{\text{end}}:

qP/m iBtend+qP/m =qλ rθ+qλ ,λ=PV\frac{q P/m}{\,i B_{t_{\text{end}}} + q P/m\,} = \frac{q\lambda}{\,r\theta + q\lambda\,}, \qquad \lambda = \frac{P}{V}

where the second form uses Btend≈θVB_{t_{\text{end}}} \approx \theta V, valid when the threshold rule binds rather than the midpoint. It is always the larger of the two, since the fixed premium never shrank while the balance did. With λ=0.91\lambda = 0.91 and θ=0.80\theta = 0.80 on the same rates, 9.5%9.5\% against 8.5%8.5\%.

That is the whole answer to why tendt_{\text{end}} is invisible on screen. The value is continuous (Part 3), so there is no step; the slope changes by under ten percent at a single point, on a curve that climbs tens of thousands of dollars across the plot. A few pixels of bend.

Break-evens

A break-even is not where two curves cross. With four offers, two also-ran curves can cross each other and nothing about the recommendation changes. What matters is where the winner changes, so the site tracks the pointwise argmin:

lead(k)=arg⁡min⁡j yj(k)\text{lead}(k) = \arg\min_j \, y_j(k)

and records the kk where lead\text{lead} changes value. Between the two samples that bracket a change, the gap d=yprev−ycurd = y_{\text{prev}} - y_{\text{cur}} is interpolated linearly to place the crossing:

h∗=hk−1+dk−1dk−1−dk (hk−hk−1)h^\ast = h_{k-1} + \frac{d_{k-1}}{d_{k-1} - d_k}\,(h_k - h_{k-1})

Two filters keep the result honest. A lead change is ignored unless the new leader is ahead by more than half a dollar, so floating-point noise is not reported as a crossing — the same tie window the verdict text uses. And a leader must hold the lead for at least 2% of the plotted range to be labeled: when two offers differ mainly in fees, the curves are nearly coincident at the start and the argmin can flicker between them many times before settling.

Economically the picture is simple. At h=0h = 0 the ranking is by fees alone. As hh grows the rate difference accumulates against that fixed head start, and the break-even is where it has been repaid.

Sampling

horizon() rounds its argument to a whole number of periods, k=round(mh)k = \mathrm{round}(mh). Sampling the curve on an evenly spaced pixel grid would therefore land many adjacent samples on the same kk and produce a visible staircase. The site instead places a vertex at every scheduled payment, h=t/mh = t/m, which is the natural grid for a function that is only defined there. For long terms it strides to keep the vertex count bounded.

The break-even search uses a separate uniform grid, since it is looking for sign changes in a difference rather than drawing a shape.

Generated from MATH.md in the project repository. Nothing on this page is legal or financial advice.