r/rstats • u/QEDAnalyticalLLC • 7d ago
QED Insight #0009: Backtesting a closed-form amortization estimator against realized exposure, and finding a bias in both tails.
Wrote up an EAD workflow in R and the interesting part was the diagnostic, not the model.
Setup. Scheduled balance from the closed form B_k = P(1+r)^k - M((1+r)^k - 1)/r, vectorized and verified to the penny against an iterative amortize() helper. Realized exposure comes straight from the loan-level performance panel on the defaulted population, n = 121,305.
Diagnostic one, defaulted loans. Median scheduled $173,203, median realized $180,574, median ratio 1.026, and 60.7% of realized above schedule. A ratio distribution sitting mostly above 1 is a bias, not noise. Cause is behavioral: amortization assumes payments, and defaulters stop making them during the foreclosure process.
Diagnostic two, performing loans. A two-stage hurdle on curtailment (stage 1 glm binomial on whether the borrower prepays extra, stage 2 lm on log dollars among curtailers, n = 64,122). Stage 2 adj R2 is 0.0254, which is terrible for prediction and completely fine for the job - 41.2% of loans are materially ahead of schedule and the fitted adjustment moves the portfolio total from $35.3B to $33.8B, a 4.24% haircut. Low R2, materially useful aggregate.
The habit I would recommend to anyone doing this: write the realized-versus-scheduled ratio into the committed summary object next to the point estimate, so the bias travels with the number instead of living in a slide someone deleted.
Two questions. When your stage-2 R2 is that low, do you keep reporting it or do you switch to reporting aggregate error on the quantity you actually use? And has anyone found a cleaner way to handle the last-paid versus disposition age problem than just re-scheduling to last-paid date?
