FA-58571 / Loan amortization schedules / Open access
Balloon maturity payment: amortization basis · case 01
Balloon loans are quoted fully amortizing payments over the short term.
ROOT CAUSE
The payment is leveled over the balloon term instead of the amortization period.
VERIFIED REPAIR
Level the payment over the amortization period.
Unsuccessful approach: Switching to the term when it exceeds amortization stretches loans that should finish early.
Case contract
x = {'principal', 'rate_bp', 'amort_months', 'term_months'}. The level payment amortizes over amort_months (exact annuity, half-up). Payments run to the earlier of term and amortization end; the last one pays balance + interest (the balloon). Monthly interest is round_half_up(balance*bp/120000). Return {'payment', 'final_payment', 'balloon_excess': final - payment, 'total_interest'}.
Why this case matters
Amortization engines drive borrower statements, payoff quotes and investor remittances; a misplaced rounding step, boundary or ordering rule compounds across hundreds of periods.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(x):
def rnd(n, d):
q, r = divmod(n, d)
return q + (1 if 2 * r >= d else 0)
def level(P, n):
if n <= 0 or P <= 0:
return 0
if bp == 0:
exact = Fraction(P, n)
else:
r = Fraction(bp, 120000)
exact = P * r / (1 - (1 + r) ** -n)
return math.floor(exact + Fraction(1, 2))
bp = x['rate_bp']
P = x['principal']
pay = level(P, x['term_months'])
last_k = min(x['term_months'], x['amort_months'])
bal = P
total = 0
final = 0
for k in range(1, last_k + 1):
interest = rnd(bal * bp, 120000)
total += interest
if k == last_k:
final = bal + interest
else:
bal -= pay - interest
return {'payment': pay, 'final_payment': final, 'balloon_excess': final - pay, 'total_interest': total}
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: amortization basis', {'principal': 100000, 'rate_bp': 0, 'amort_months': 5, 'term_months': 3}, {'payment': 20000, 'final_payment': 60000, 'balloon_excess': 40000, 'total_interest': 0}], ['regression: amortization basis, partial-repair probe', {'principal': 55555, 'rate_bp': 600, 'amort_months': 12, 'term_months': 84}, {'payment': 4781, 'final_payment': 4786, 'balloon_excess': 5, 'total_interest': 1822}], ['control 1', {'principal': 100000, 'rate_bp': 725, 'amort_months': 5, 'term_months': 5}, {'payment': 20364, 'final_payment': 20364, 'balloon_excess': 0, 'total_interest': 1820}], ['control 2', {'principal': 55555, 'rate_bp': 600, 'amort_months': 5, 'term_months': 5}, {'payment': 11278, 'final_payment': 11280, 'balloon_excess': 2, 'total_interest': 837}], ['control 3', {'principal': 1000000, 'rate_bp': 0, 'amort_months': 60, 'term_months': 60}, {'payment': 16667, 'final_payment': 16647, 'balloon_excess': -20, 'total_interest': 0}], ['control 4', {'principal': 100000, 'rate_bp': 725, 'amort_months': 60, 'term_months': 60}, {'payment': 1992, 'final_payment': 1987, 'balloon_excess': -5, 'total_interest': 19515}], ['control 5', {'principal': 1000000, 'rate_bp': 0, 'amort_months': 12, 'term_months': 12}, {'payment': 83333, 'final_payment': 83337, 'balloon_excess': 4, 'total_interest': 0}]], [['regression: amortization basis', {'principal': 100000, 'rate_bp': 0, 'amort_months': 12, 'term_months': 5}, {'payment': 8333, 'final_payment': 66668, 'balloon_excess': 58335, 'total_interest': 0}], ['regression: amortization basis, partial-repair probe', {'principal': 55555, 'rate_bp': 600, 'amort_months': 12, 'term_months': 60}, {'payment': 4781, 'final_payment': 4786, 'balloon_excess': 5, 'total_interest': 1822}], ['control 1', {'principal': 100000, 'rate_bp': 600, 'amort_months': 5, 'term_months': 5}, {'payment': 20301, 'final_payment': 20300, 'balloon_excess': -1, 'total_interest': 1504}], ['control 2', {'principal': 100000, 'rate_bp': 1200, 'amort_months': 60, 'term_months': 60}, {'payment': 2224, 'final_payment': 2262, 'balloon_excess': 38, 'total_interest': 33478}], ['control 3', {'principal': 100000, 'rate_bp': 0, 'amort_months': 60, 'term_months': 60}, {'payment': 1667, 'final_payment': 1647, 'balloon_excess': -20, 'total_interest': 0}], ['control 4', {'principal': 1000000, 'rate_bp': 600, 'amort_months': 60, 'term_months': 60}, {'payment': 19333, 'final_payment': 19321, 'balloon_excess': -12, 'total_interest': 159968}], ['control 5', {'principal': 55555, 'rate_bp': 725, 'amort_months': 5, 'term_months': 5}, {'payment': 11313, 'final_payment': 11314, 'balloon_excess': 1, 'total_interest': 1011}]], [['regression: amortization basis', {'principal': 100000, 'rate_bp': 0, 'amort_months': 5, 'term_months': 3}, {'payment': 20000, 'final_payment': 60000, 'balloon_excess': 40000, 'total_interest': 0}], ['regression: amortization basis, partial-repair probe', {'principal': 1000000, 'rate_bp': 1200, 'amort_months': 5, 'term_months': 60}, {'payment': 206040, 'final_payment': 206040, 'balloon_excess': 0, 'total_interest': 30200}], ['control 1', {'principal': 100000, 'rate_bp': 1200, 'amort_months': 12, 'term_months': 12}, {'payment': 8885, 'final_payment': 8884, 'balloon_excess': -1, 'total_interest': 6619}], ['control 2', {'principal': 100000, 'rate_bp': 0, 'amort_months': 12, 'term_months': 12}, {'payment': 8333, 'final_payment': 8337, 'balloon_excess': 4, 'total_interest': 0}], ['control 3', {'principal': 1000000, 'rate_bp': 1200, 'amort_months': 5, 'term_months': 5}, {'payment': 206040, 'final_payment': 206040, 'balloon_excess': 0, 'total_interest': 30200}], ['control 4', {'principal': 55555, 'rate_bp': 725, 'amort_months': 12, 'term_months': 12}, {'payment': 4813, 'final_payment': 4819, 'balloon_excess': 6, 'total_interest': 2207}], ['control 5', {'principal': 55555, 'rate_bp': 0, 'amort_months': 5, 'term_months': 5}, {'payment': 11111, 'final_payment': 11111, 'balloon_excess': 0, 'total_interest': 0}]], [['regression: amortization basis', {'principal': 1000000, 'rate_bp': 725, 'amort_months': 360, 'term_months': 12}, {'payment': 6822, 'final_payment': 997140, 'balloon_excess': 990318, 'total_interest': 72182}], ['regression: amortization basis, partial-repair probe', {'principal': 55555, 'rate_bp': 0, 'amort_months': 5, 'term_months': 84}, {'payment': 11111, 'final_payment': 11111, 'balloon_excess': 0, 'total_interest': 0}], ['control 1', {'principal': 1000000, 'rate_bp': 600, 'amort_months': 5, 'term_months': 5}, {'payment': 203010, 'final_payment': 203010, 'balloon_excess': 0, 'total_interest': 15050}], ['control 2', {'principal': 1000000, 'rate_bp': 1200, 'amort_months': 5, 'term_months': 5}, {'payment': 206040, 'final_payment': 206040, 'balloon_excess': 0, 'total_interest': 30200}], ['control 3', {'principal': 100000, 'rate_bp': 0, 'amort_months': 60, 'term_months': 60}, {'payment': 1667, 'final_payment': 1647, 'balloon_excess': -20, 'total_interest': 0}], ['control 4', {'principal': 1000000, 'rate_bp': 0, 'amort_months': 12, 'term_months': 12}, {'payment': 83333, 'final_payment': 83337, 'balloon_excess': 4, 'total_interest': 0}], ['control 5', {'principal': 1000000, 'rate_bp': 1200, 'amort_months': 60, 'term_months': 60}, {'payment': 22244, 'final_payment': 22281, 'balloon_excess': 37, 'total_interest': 334677}]], [['regression: amortization basis', {'principal': 3000000, 'rate_bp': 1200, 'amort_months': 60, 'term_months': 12}, {'payment': 66733, 'final_payment': 2600867, 'balloon_excess': 2534134, 'total_interest': 334930}], ['regression: amortization basis, partial-repair probe', {'principal': 55555, 'rate_bp': 1200, 'amort_months': 12, 'term_months': 60}, {'payment': 4936, 'final_payment': 4938, 'balloon_excess': 2, 'total_interest': 3679}], ['control 1', {'principal': 100000, 'rate_bp': 600, 'amort_months': 60, 'term_months': 60}, {'payment': 1933, 'final_payment': 1952, 'balloon_excess': 19, 'total_interest': 15999}], ['control 2', {'principal': 55555, 'rate_bp': 0, 'amort_months': 60, 'term_months': 60}, {'payment': 926, 'final_payment': 921, 'balloon_excess': -5, 'total_interest': 0}], ['control 3', {'principal': 100000, 'rate_bp': 1200, 'amort_months': 12, 'term_months': 12}, {'payment': 8885, 'final_payment': 8884, 'balloon_excess': -1, 'total_interest': 6619}], ['control 4', {'principal': 3000000, 'rate_bp': 725, 'amort_months': 5, 'term_months': 5}, {'payment': 610919, 'final_payment': 610918, 'balloon_excess': -1, 'total_interest': 54594}], ['control 5', {'principal': 1000000, 'rate_bp': 725, 'amort_months': 5, 'term_months': 5}, {'payment': 203640, 'final_payment': 203639, 'balloon_excess': -1, 'total_interest': 18199}]]]
for label, args, expected in fixtures[N-1]:
try:
actual = solve(args)
except Exception as exc:
actual = 'raised ' + type(exc).__name__
check(label, actual, expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: amortization basis | {'balloon_excess': 1, 'final_payment': 33334, 'payment': 33333, 'total_interest': 0} | {'balloon_excess': 40000, 'final_payment': 60000, 'payment': 20000, 'total_interest': 0} | Failed |
| regression: amortization basis, partial-repair probe | {'balloon_excess': 48965, 'final_payment': 49777, 'payment': 812, 'total_interest': 3154} | {'balloon_excess': 5, 'final_payment': 4786, 'payment': 4781, 'total_interest': 1822} | Failed |
| control 1 | {'balloon_excess': 0, 'final_payment': 20364, 'payment': 20364, 'total_interest': 1820} | {'balloon_excess': 0, 'final_payment': 20364, 'payment': 20364, 'total_interest': 1820} | Passed |
| control 2 | {'balloon_excess': 2, 'final_payment': 11280, 'payment': 11278, 'total_interest': 837} | {'balloon_excess': 2, 'final_payment': 11280, 'payment': 11278, 'total_interest': 837} | Passed |
| control 3 | {'balloon_excess': -20, 'final_payment': 16647, 'payment': 16667, 'total_interest': 0} | {'balloon_excess': -20, 'final_payment': 16647, 'payment': 16667, 'total_interest': 0} | Passed |
| control 4 | {'balloon_excess': -5, 'final_payment': 1987, 'payment': 1992, 'total_interest': 19515} | {'balloon_excess': -5, 'final_payment': 1987, 'payment': 1992, 'total_interest': 19515} | Passed |
| control 5 | {'balloon_excess': 4, 'final_payment': 83337, 'payment': 83333, 'total_interest': 0} | {'balloon_excess': 4, 'final_payment': 83337, 'payment': 83333, 'total_interest': 0} | Passed |
SHA-256 / e6d08e8a9d1b31bdf766848e5b8bdf7786f2706bebf7c069b0cd91f8c1578b33
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(x):
def rnd(n, d):
q, r = divmod(n, d)
return q + (1 if 2 * r >= d else 0)
def level(P, n):
if n <= 0 or P <= 0:
return 0
if bp == 0:
exact = Fraction(P, n)
else:
r = Fraction(bp, 120000)
exact = P * r / (1 - (1 + r) ** -n)
return math.floor(exact + Fraction(1, 2))
bp = x['rate_bp']
P = x['principal']
pay = level(P, x['amort_months']) if x['amort_months'] > x['term_months'] else level(P, x['term_months'])
last_k = min(x['term_months'], x['amort_months'])
bal = P
total = 0
final = 0
for k in range(1, last_k + 1):
interest = rnd(bal * bp, 120000)
total += interest
if k == last_k:
final = bal + interest
else:
bal -= pay - interest
return {'payment': pay, 'final_payment': final, 'balloon_excess': final - pay, 'total_interest': total}
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: amortization basis', {'principal': 100000, 'rate_bp': 0, 'amort_months': 5, 'term_months': 3}, {'payment': 20000, 'final_payment': 60000, 'balloon_excess': 40000, 'total_interest': 0}], ['regression: amortization basis, partial-repair probe', {'principal': 55555, 'rate_bp': 600, 'amort_months': 12, 'term_months': 84}, {'payment': 4781, 'final_payment': 4786, 'balloon_excess': 5, 'total_interest': 1822}], ['control 1', {'principal': 100000, 'rate_bp': 725, 'amort_months': 5, 'term_months': 5}, {'payment': 20364, 'final_payment': 20364, 'balloon_excess': 0, 'total_interest': 1820}], ['control 2', {'principal': 55555, 'rate_bp': 600, 'amort_months': 5, 'term_months': 5}, {'payment': 11278, 'final_payment': 11280, 'balloon_excess': 2, 'total_interest': 837}], ['control 3', {'principal': 1000000, 'rate_bp': 0, 'amort_months': 60, 'term_months': 60}, {'payment': 16667, 'final_payment': 16647, 'balloon_excess': -20, 'total_interest': 0}], ['control 4', {'principal': 100000, 'rate_bp': 725, 'amort_months': 60, 'term_months': 60}, {'payment': 1992, 'final_payment': 1987, 'balloon_excess': -5, 'total_interest': 19515}], ['control 5', {'principal': 1000000, 'rate_bp': 0, 'amort_months': 12, 'term_months': 12}, {'payment': 83333, 'final_payment': 83337, 'balloon_excess': 4, 'total_interest': 0}]], [['regression: amortization basis', {'principal': 100000, 'rate_bp': 0, 'amort_months': 12, 'term_months': 5}, {'payment': 8333, 'final_payment': 66668, 'balloon_excess': 58335, 'total_interest': 0}], ['regression: amortization basis, partial-repair probe', {'principal': 55555, 'rate_bp': 600, 'amort_months': 12, 'term_months': 60}, {'payment': 4781, 'final_payment': 4786, 'balloon_excess': 5, 'total_interest': 1822}], ['control 1', {'principal': 100000, 'rate_bp': 600, 'amort_months': 5, 'term_months': 5}, {'payment': 20301, 'final_payment': 20300, 'balloon_excess': -1, 'total_interest': 1504}], ['control 2', {'principal': 100000, 'rate_bp': 1200, 'amort_months': 60, 'term_months': 60}, {'payment': 2224, 'final_payment': 2262, 'balloon_excess': 38, 'total_interest': 33478}], ['control 3', {'principal': 100000, 'rate_bp': 0, 'amort_months': 60, 'term_months': 60}, {'payment': 1667, 'final_payment': 1647, 'balloon_excess': -20, 'total_interest': 0}], ['control 4', {'principal': 1000000, 'rate_bp': 600, 'amort_months': 60, 'term_months': 60}, {'payment': 19333, 'final_payment': 19321, 'balloon_excess': -12, 'total_interest': 159968}], ['control 5', {'principal': 55555, 'rate_bp': 725, 'amort_months': 5, 'term_months': 5}, {'payment': 11313, 'final_payment': 11314, 'balloon_excess': 1, 'total_interest': 1011}]], [['regression: amortization basis', {'principal': 100000, 'rate_bp': 0, 'amort_months': 5, 'term_months': 3}, {'payment': 20000, 'final_payment': 60000, 'balloon_excess': 40000, 'total_interest': 0}], ['regression: amortization basis, partial-repair probe', {'principal': 1000000, 'rate_bp': 1200, 'amort_months': 5, 'term_months': 60}, {'payment': 206040, 'final_payment': 206040, 'balloon_excess': 0, 'total_interest': 30200}], ['control 1', {'principal': 100000, 'rate_bp': 1200, 'amort_months': 12, 'term_months': 12}, {'payment': 8885, 'final_payment': 8884, 'balloon_excess': -1, 'total_interest': 6619}], ['control 2', {'principal': 100000, 'rate_bp': 0, 'amort_months': 12, 'term_months': 12}, {'payment': 8333, 'final_payment': 8337, 'balloon_excess': 4, 'total_interest': 0}], ['control 3', {'principal': 1000000, 'rate_bp': 1200, 'amort_months': 5, 'term_months': 5}, {'payment': 206040, 'final_payment': 206040, 'balloon_excess': 0, 'total_interest': 30200}], ['control 4', {'principal': 55555, 'rate_bp': 725, 'amort_months': 12, 'term_months': 12}, {'payment': 4813, 'final_payment': 4819, 'balloon_excess': 6, 'total_interest': 2207}], ['control 5', {'principal': 55555, 'rate_bp': 0, 'amort_months': 5, 'term_months': 5}, {'payment': 11111, 'final_payment': 11111, 'balloon_excess': 0, 'total_interest': 0}]], [['regression: amortization basis', {'principal': 1000000, 'rate_bp': 725, 'amort_months': 360, 'term_months': 12}, {'payment': 6822, 'final_payment': 997140, 'balloon_excess': 990318, 'total_interest': 72182}], ['regression: amortization basis, partial-repair probe', {'principal': 55555, 'rate_bp': 0, 'amort_months': 5, 'term_months': 84}, {'payment': 11111, 'final_payment': 11111, 'balloon_excess': 0, 'total_interest': 0}], ['control 1', {'principal': 1000000, 'rate_bp': 600, 'amort_months': 5, 'term_months': 5}, {'payment': 203010, 'final_payment': 203010, 'balloon_excess': 0, 'total_interest': 15050}], ['control 2', {'principal': 1000000, 'rate_bp': 1200, 'amort_months': 5, 'term_months': 5}, {'payment': 206040, 'final_payment': 206040, 'balloon_excess': 0, 'total_interest': 30200}], ['control 3', {'principal': 100000, 'rate_bp': 0, 'amort_months': 60, 'term_months': 60}, {'payment': 1667, 'final_payment': 1647, 'balloon_excess': -20, 'total_interest': 0}], ['control 4', {'principal': 1000000, 'rate_bp': 0, 'amort_months': 12, 'term_months': 12}, {'payment': 83333, 'final_payment': 83337, 'balloon_excess': 4, 'total_interest': 0}], ['control 5', {'principal': 1000000, 'rate_bp': 1200, 'amort_months': 60, 'term_months': 60}, {'payment': 22244, 'final_payment': 22281, 'balloon_excess': 37, 'total_interest': 334677}]], [['regression: amortization basis', {'principal': 3000000, 'rate_bp': 1200, 'amort_months': 60, 'term_months': 12}, {'payment': 66733, 'final_payment': 2600867, 'balloon_excess': 2534134, 'total_interest': 334930}], ['regression: amortization basis, partial-repair probe', {'principal': 55555, 'rate_bp': 1200, 'amort_months': 12, 'term_months': 60}, {'payment': 4936, 'final_payment': 4938, 'balloon_excess': 2, 'total_interest': 3679}], ['control 1', {'principal': 100000, 'rate_bp': 600, 'amort_months': 60, 'term_months': 60}, {'payment': 1933, 'final_payment': 1952, 'balloon_excess': 19, 'total_interest': 15999}], ['control 2', {'principal': 55555, 'rate_bp': 0, 'amort_months': 60, 'term_months': 60}, {'payment': 926, 'final_payment': 921, 'balloon_excess': -5, 'total_interest': 0}], ['control 3', {'principal': 100000, 'rate_bp': 1200, 'amort_months': 12, 'term_months': 12}, {'payment': 8885, 'final_payment': 8884, 'balloon_excess': -1, 'total_interest': 6619}], ['control 4', {'principal': 3000000, 'rate_bp': 725, 'amort_months': 5, 'term_months': 5}, {'payment': 610919, 'final_payment': 610918, 'balloon_excess': -1, 'total_interest': 54594}], ['control 5', {'principal': 1000000, 'rate_bp': 725, 'amort_months': 5, 'term_months': 5}, {'payment': 203640, 'final_payment': 203639, 'balloon_excess': -1, 'total_interest': 18199}]]]
for label, args, expected in fixtures[N-1]:
try:
actual = solve(args)
except Exception as exc:
actual = 'raised ' + type(exc).__name__
check(label, actual, expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: amortization basis | {'balloon_excess': 40000, 'final_payment': 60000, 'payment': 20000, 'total_interest': 0} | {'balloon_excess': 40000, 'final_payment': 60000, 'payment': 20000, 'total_interest': 0} | Passed |
| regression: amortization basis, partial-repair probe | {'balloon_excess': 48965, 'final_payment': 49777, 'payment': 812, 'total_interest': 3154} | {'balloon_excess': 5, 'final_payment': 4786, 'payment': 4781, 'total_interest': 1822} | Failed |
| control 1 | {'balloon_excess': 0, 'final_payment': 20364, 'payment': 20364, 'total_interest': 1820} | {'balloon_excess': 0, 'final_payment': 20364, 'payment': 20364, 'total_interest': 1820} | Passed |
| control 2 | {'balloon_excess': 2, 'final_payment': 11280, 'payment': 11278, 'total_interest': 837} | {'balloon_excess': 2, 'final_payment': 11280, 'payment': 11278, 'total_interest': 837} | Passed |
| control 3 | {'balloon_excess': -20, 'final_payment': 16647, 'payment': 16667, 'total_interest': 0} | {'balloon_excess': -20, 'final_payment': 16647, 'payment': 16667, 'total_interest': 0} | Passed |
| control 4 | {'balloon_excess': -5, 'final_payment': 1987, 'payment': 1992, 'total_interest': 19515} | {'balloon_excess': -5, 'final_payment': 1987, 'payment': 1992, 'total_interest': 19515} | Passed |
| control 5 | {'balloon_excess': 4, 'final_payment': 83337, 'payment': 83333, 'total_interest': 0} | {'balloon_excess': 4, 'final_payment': 83337, 'payment': 83333, 'total_interest': 0} | Passed |
SHA-256 / 32870e5ec7b8fd746480813dce3c4aa47b16f499ac4d5dcf7cc6305cb769a68e
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(x):
def rnd(n, d):
q, r = divmod(n, d)
return q + (1 if 2 * r >= d else 0)
def level(P, n):
if n <= 0 or P <= 0:
return 0
if bp == 0:
exact = Fraction(P, n)
else:
r = Fraction(bp, 120000)
exact = P * r / (1 - (1 + r) ** -n)
return math.floor(exact + Fraction(1, 2))
bp = x['rate_bp']
P = x['principal']
pay = level(P, x['amort_months'])
last_k = min(x['term_months'], x['amort_months'])
bal = P
total = 0
final = 0
for k in range(1, last_k + 1):
interest = rnd(bal * bp, 120000)
total += interest
if k == last_k:
final = bal + interest
else:
bal -= pay - interest
return {'payment': pay, 'final_payment': final, 'balloon_excess': final - pay, 'total_interest': total}
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: amortization basis', {'principal': 100000, 'rate_bp': 0, 'amort_months': 5, 'term_months': 3}, {'payment': 20000, 'final_payment': 60000, 'balloon_excess': 40000, 'total_interest': 0}], ['regression: amortization basis, partial-repair probe', {'principal': 55555, 'rate_bp': 600, 'amort_months': 12, 'term_months': 84}, {'payment': 4781, 'final_payment': 4786, 'balloon_excess': 5, 'total_interest': 1822}], ['control 1', {'principal': 100000, 'rate_bp': 725, 'amort_months': 5, 'term_months': 5}, {'payment': 20364, 'final_payment': 20364, 'balloon_excess': 0, 'total_interest': 1820}], ['control 2', {'principal': 55555, 'rate_bp': 600, 'amort_months': 5, 'term_months': 5}, {'payment': 11278, 'final_payment': 11280, 'balloon_excess': 2, 'total_interest': 837}], ['control 3', {'principal': 1000000, 'rate_bp': 0, 'amort_months': 60, 'term_months': 60}, {'payment': 16667, 'final_payment': 16647, 'balloon_excess': -20, 'total_interest': 0}], ['control 4', {'principal': 100000, 'rate_bp': 725, 'amort_months': 60, 'term_months': 60}, {'payment': 1992, 'final_payment': 1987, 'balloon_excess': -5, 'total_interest': 19515}], ['control 5', {'principal': 1000000, 'rate_bp': 0, 'amort_months': 12, 'term_months': 12}, {'payment': 83333, 'final_payment': 83337, 'balloon_excess': 4, 'total_interest': 0}]], [['regression: amortization basis', {'principal': 100000, 'rate_bp': 0, 'amort_months': 12, 'term_months': 5}, {'payment': 8333, 'final_payment': 66668, 'balloon_excess': 58335, 'total_interest': 0}], ['regression: amortization basis, partial-repair probe', {'principal': 55555, 'rate_bp': 600, 'amort_months': 12, 'term_months': 60}, {'payment': 4781, 'final_payment': 4786, 'balloon_excess': 5, 'total_interest': 1822}], ['control 1', {'principal': 100000, 'rate_bp': 600, 'amort_months': 5, 'term_months': 5}, {'payment': 20301, 'final_payment': 20300, 'balloon_excess': -1, 'total_interest': 1504}], ['control 2', {'principal': 100000, 'rate_bp': 1200, 'amort_months': 60, 'term_months': 60}, {'payment': 2224, 'final_payment': 2262, 'balloon_excess': 38, 'total_interest': 33478}], ['control 3', {'principal': 100000, 'rate_bp': 0, 'amort_months': 60, 'term_months': 60}, {'payment': 1667, 'final_payment': 1647, 'balloon_excess': -20, 'total_interest': 0}], ['control 4', {'principal': 1000000, 'rate_bp': 600, 'amort_months': 60, 'term_months': 60}, {'payment': 19333, 'final_payment': 19321, 'balloon_excess': -12, 'total_interest': 159968}], ['control 5', {'principal': 55555, 'rate_bp': 725, 'amort_months': 5, 'term_months': 5}, {'payment': 11313, 'final_payment': 11314, 'balloon_excess': 1, 'total_interest': 1011}]], [['regression: amortization basis', {'principal': 100000, 'rate_bp': 0, 'amort_months': 5, 'term_months': 3}, {'payment': 20000, 'final_payment': 60000, 'balloon_excess': 40000, 'total_interest': 0}], ['regression: amortization basis, partial-repair probe', {'principal': 1000000, 'rate_bp': 1200, 'amort_months': 5, 'term_months': 60}, {'payment': 206040, 'final_payment': 206040, 'balloon_excess': 0, 'total_interest': 30200}], ['control 1', {'principal': 100000, 'rate_bp': 1200, 'amort_months': 12, 'term_months': 12}, {'payment': 8885, 'final_payment': 8884, 'balloon_excess': -1, 'total_interest': 6619}], ['control 2', {'principal': 100000, 'rate_bp': 0, 'amort_months': 12, 'term_months': 12}, {'payment': 8333, 'final_payment': 8337, 'balloon_excess': 4, 'total_interest': 0}], ['control 3', {'principal': 1000000, 'rate_bp': 1200, 'amort_months': 5, 'term_months': 5}, {'payment': 206040, 'final_payment': 206040, 'balloon_excess': 0, 'total_interest': 30200}], ['control 4', {'principal': 55555, 'rate_bp': 725, 'amort_months': 12, 'term_months': 12}, {'payment': 4813, 'final_payment': 4819, 'balloon_excess': 6, 'total_interest': 2207}], ['control 5', {'principal': 55555, 'rate_bp': 0, 'amort_months': 5, 'term_months': 5}, {'payment': 11111, 'final_payment': 11111, 'balloon_excess': 0, 'total_interest': 0}]], [['regression: amortization basis', {'principal': 1000000, 'rate_bp': 725, 'amort_months': 360, 'term_months': 12}, {'payment': 6822, 'final_payment': 997140, 'balloon_excess': 990318, 'total_interest': 72182}], ['regression: amortization basis, partial-repair probe', {'principal': 55555, 'rate_bp': 0, 'amort_months': 5, 'term_months': 84}, {'payment': 11111, 'final_payment': 11111, 'balloon_excess': 0, 'total_interest': 0}], ['control 1', {'principal': 1000000, 'rate_bp': 600, 'amort_months': 5, 'term_months': 5}, {'payment': 203010, 'final_payment': 203010, 'balloon_excess': 0, 'total_interest': 15050}], ['control 2', {'principal': 1000000, 'rate_bp': 1200, 'amort_months': 5, 'term_months': 5}, {'payment': 206040, 'final_payment': 206040, 'balloon_excess': 0, 'total_interest': 30200}], ['control 3', {'principal': 100000, 'rate_bp': 0, 'amort_months': 60, 'term_months': 60}, {'payment': 1667, 'final_payment': 1647, 'balloon_excess': -20, 'total_interest': 0}], ['control 4', {'principal': 1000000, 'rate_bp': 0, 'amort_months': 12, 'term_months': 12}, {'payment': 83333, 'final_payment': 83337, 'balloon_excess': 4, 'total_interest': 0}], ['control 5', {'principal': 1000000, 'rate_bp': 1200, 'amort_months': 60, 'term_months': 60}, {'payment': 22244, 'final_payment': 22281, 'balloon_excess': 37, 'total_interest': 334677}]], [['regression: amortization basis', {'principal': 3000000, 'rate_bp': 1200, 'amort_months': 60, 'term_months': 12}, {'payment': 66733, 'final_payment': 2600867, 'balloon_excess': 2534134, 'total_interest': 334930}], ['regression: amortization basis, partial-repair probe', {'principal': 55555, 'rate_bp': 1200, 'amort_months': 12, 'term_months': 60}, {'payment': 4936, 'final_payment': 4938, 'balloon_excess': 2, 'total_interest': 3679}], ['control 1', {'principal': 100000, 'rate_bp': 600, 'amort_months': 60, 'term_months': 60}, {'payment': 1933, 'final_payment': 1952, 'balloon_excess': 19, 'total_interest': 15999}], ['control 2', {'principal': 55555, 'rate_bp': 0, 'amort_months': 60, 'term_months': 60}, {'payment': 926, 'final_payment': 921, 'balloon_excess': -5, 'total_interest': 0}], ['control 3', {'principal': 100000, 'rate_bp': 1200, 'amort_months': 12, 'term_months': 12}, {'payment': 8885, 'final_payment': 8884, 'balloon_excess': -1, 'total_interest': 6619}], ['control 4', {'principal': 3000000, 'rate_bp': 725, 'amort_months': 5, 'term_months': 5}, {'payment': 610919, 'final_payment': 610918, 'balloon_excess': -1, 'total_interest': 54594}], ['control 5', {'principal': 1000000, 'rate_bp': 725, 'amort_months': 5, 'term_months': 5}, {'payment': 203640, 'final_payment': 203639, 'balloon_excess': -1, 'total_interest': 18199}]]]
for label, args, expected in fixtures[N-1]:
try:
actual = solve(args)
except Exception as exc:
actual = 'raised ' + type(exc).__name__
check(label, actual, expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: amortization basis | {'balloon_excess': 40000, 'final_payment': 60000, 'payment': 20000, 'total_interest': 0} | {'balloon_excess': 40000, 'final_payment': 60000, 'payment': 20000, 'total_interest': 0} | Passed |
| regression: amortization basis, partial-repair probe | {'balloon_excess': 5, 'final_payment': 4786, 'payment': 4781, 'total_interest': 1822} | {'balloon_excess': 5, 'final_payment': 4786, 'payment': 4781, 'total_interest': 1822} | Passed |
| control 1 | {'balloon_excess': 0, 'final_payment': 20364, 'payment': 20364, 'total_interest': 1820} | {'balloon_excess': 0, 'final_payment': 20364, 'payment': 20364, 'total_interest': 1820} | Passed |
| control 2 | {'balloon_excess': 2, 'final_payment': 11280, 'payment': 11278, 'total_interest': 837} | {'balloon_excess': 2, 'final_payment': 11280, 'payment': 11278, 'total_interest': 837} | Passed |
| control 3 | {'balloon_excess': -20, 'final_payment': 16647, 'payment': 16667, 'total_interest': 0} | {'balloon_excess': -20, 'final_payment': 16647, 'payment': 16667, 'total_interest': 0} | Passed |
| control 4 | {'balloon_excess': -5, 'final_payment': 1987, 'payment': 1992, 'total_interest': 19515} | {'balloon_excess': -5, 'final_payment': 1987, 'payment': 1992, 'total_interest': 19515} | Passed |
| control 5 | {'balloon_excess': 4, 'final_payment': 83337, 'payment': 83333, 'total_interest': 0} | {'balloon_excess': 4, 'final_payment': 83337, 'payment': 83333, 'total_interest': 0} | Passed |
SHA-256 / cbdc8777f22c81f8551bac24df97cd04a0193f08e2eb812b4396147fbc699d9e
Verification & scope
A deterministic bounded teaching model with stipulated toy lending rules stated in the contract; money is integer cents and rates are basis points; it makes no claim of conformance to any regulation, servicing standard or product. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:46:28.050058+00:00.
Case digest / cafef744e488695c39d5e024d42f47a808c2bba7179af4a515538a0f41bcb9a7