FAILURE MAP
← Case archive

FA-58596 / Loan amortization schedules / Open access

Negative amortization cap recast: recast principal · case 01

The recast payment leaves capitalized interest unamortized.

Verified by executionVariant 1 · 7 checks per implementationDownload source bundle ↓JSON ↗

ROOT CAUSE

The recast levels the original principal instead of the grown balance.

VERIFIED REPAIR

Recast on the current balance including capitalized interest.

Unsuccessful approach: Excluding deferred interest still leaves part of the balance unamortized.

Case contract

x = {'principal', 'rate_bp', 'min_payment', 'cap_pct_bp' (e.g. 11000 = 110% of principal), 'months'}. Each month interest = round_half_up(balance*bp/120000); the shortfall interest - payment (if positive) is deferred; balance += interest - payment. The first time balance*10000 exceeds principal*cap_pct_bp the loan recasts once: payment = level(balance, months - k) (exact annuity, half-up). Return {'balance', 'recast_month' or None, 'deferred', 'payment'}.

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']
    n = x['months']
    bal = P
    pay = x['min_payment']
    deferred = 0
    recast = None
    limit = P * x['cap_pct_bp']
    for k in range(1, n + 1):
        interest = rnd(bal * bp, 120000)
        if pay < interest:
            deferred += interest - pay
        bal += interest - pay
        if recast is None and bal * 10000 > limit:
            recast = k
            pay = level(P, n - k)
    return {'balance': bal, 'recast_month': recast, 'deferred': deferred, 'payment': pay}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: recast principal', {'principal': 1000000, 'rate_bp': 1200, 'min_payment': 5000, 'cap_pct_bp': 10500, 'months': 12}, {'balance': 1, 'recast_month': 10, 'deferred': 52312, 'payment': 534061}], ['control 1', {'principal': 1000000, 'rate_bp': 2400, 'min_payment': 10000, 'cap_pct_bp': 11000, 'months': 6}, {'balance': 1063081, 'recast_month': None, 'deferred': 63081, 'payment': 10000}], ['control 2', {'principal': 1000000, 'rate_bp': 1200, 'min_payment': 10000, 'cap_pct_bp': 10200, 'months': 12}, {'balance': 1000000, 'recast_month': None, 'deferred': 0, 'payment': 10000}], ['control 3', {'principal': 200000, 'rate_bp': 2400, 'min_payment': 4000, 'cap_pct_bp': 10100, 'months': 36}, {'balance': 200000, 'recast_month': None, 'deferred': 0, 'payment': 4000}], ['control 4', {'principal': 1000000, 'rate_bp': 600, 'min_payment': 5000, 'cap_pct_bp': 10500, 'months': 12}, {'balance': 1000000, 'recast_month': None, 'deferred': 0, 'payment': 5000}], ['control 5', {'principal': 200000, 'rate_bp': 1200, 'min_payment': 4000, 'cap_pct_bp': 11000, 'months': 36}, {'balance': 113849, 'recast_month': None, 'deferred': 0, 'payment': 4000}], ['control 6', {'principal': 1000000, 'rate_bp': 2400, 'min_payment': 20000, 'cap_pct_bp': 10100, 'months': 36}, {'balance': 1000000, 'recast_month': None, 'deferred': 0, 'payment': 20000}]], [['regression: recast principal', {'principal': 200000, 'rate_bp': 2400, 'min_payment': 2000, 'cap_pct_bp': 10100, 'months': 12}, {'balance': 0, 'recast_month': 2, 'deferred': 4040, 'payment': 22715}], ['control 1', {'principal': 100000, 'rate_bp': 600, 'min_payment': 500, 'cap_pct_bp': 10100, 'months': 36}, {'balance': 100000, 'recast_month': None, 'deferred': 0, 'payment': 500}], ['control 2', {'principal': 200000, 'rate_bp': 600, 'min_payment': 1000, 'cap_pct_bp': 11000, 'months': 12}, {'balance': 200000, 'recast_month': None, 'deferred': 0, 'payment': 1000}], ['control 3', {'principal': 100000, 'rate_bp': 2400, 'min_payment': 2000, 'cap_pct_bp': 11000, 'months': 36}, {'balance': 100000, 'recast_month': None, 'deferred': 0, 'payment': 2000}], ['control 4', {'principal': 200000, 'rate_bp': 600, 'min_payment': 1000, 'cap_pct_bp': 10200, 'months': 36}, {'balance': 200000, 'recast_month': None, 'deferred': 0, 'payment': 1000}], ['control 5', {'principal': 100000, 'rate_bp': 2400, 'min_payment': 2000, 'cap_pct_bp': 10100, 'months': 6}, {'balance': 100000, 'recast_month': None, 'deferred': 0, 'payment': 2000}], ['control 6', {'principal': 100000, 'rate_bp': 600, 'min_payment': 1000, 'cap_pct_bp': 11000, 'months': 6}, {'balance': 96962, 'recast_month': None, 'deferred': 0, 'payment': 1000}]], [['regression: recast principal', {'principal': 1000000, 'rate_bp': 2400, 'min_payment': 10000, 'cap_pct_bp': 10100, 'months': 12}, {'balance': 4, 'recast_month': 2, 'deferred': 20200, 'payment': 113575}], ['control 1', {'principal': 1000000, 'rate_bp': 2400, 'min_payment': 20000, 'cap_pct_bp': 11000, 'months': 36}, {'balance': 1000000, 'recast_month': None, 'deferred': 0, 'payment': 20000}], ['control 2', {'principal': 200000, 'rate_bp': 600, 'min_payment': 500, 'cap_pct_bp': 10200, 'months': 6}, {'balance': 203039, 'recast_month': None, 'deferred': 3039, 'payment': 500}], ['control 3', {'principal': 1000000, 'rate_bp': 600, 'min_payment': 2500, 'cap_pct_bp': 11000, 'months': 6}, {'balance': 1015189, 'recast_month': None, 'deferred': 15189, 'payment': 2500}], ['control 4', {'principal': 1000000, 'rate_bp': 600, 'min_payment': 20000, 'cap_pct_bp': 10200, 'months': 24}, {'balance': 618521, 'recast_month': None, 'deferred': 0, 'payment': 20000}], ['control 5', {'principal': 100000, 'rate_bp': 600, 'min_payment': 2000, 'cap_pct_bp': 11000, 'months': 6}, {'balance': 90887, 'recast_month': None, 'deferred': 0, 'payment': 2000}], ['control 6', {'principal': 200000, 'rate_bp': 1200, 'min_payment': 4000, 'cap_pct_bp': 11000, 'months': 12}, {'balance': 174636, 'recast_month': None, 'deferred': 0, 'payment': 4000}]], [['regression: recast principal', {'principal': 100000, 'rate_bp': 2400, 'min_payment': 250, 'cap_pct_bp': 10100, 'months': 12}, {'balance': -3, 'recast_month': 1, 'deferred': 1750, 'payment': 10397}], ['control 1', {'principal': 200000, 'rate_bp': 1200, 'min_payment': 4000, 'cap_pct_bp': 10100, 'months': 6}, {'balance': 187696, 'recast_month': None, 'deferred': 0, 'payment': 4000}], ['control 2', {'principal': 200000, 'rate_bp': 2400, 'min_payment': 500, 'cap_pct_bp': 11000, 'months': 6}, {'balance': 222078, 'recast_month': 6, 'deferred': 22078, 'payment': 0}], ['control 3', {'principal': 200000, 'rate_bp': 2400, 'min_payment': 4000, 'cap_pct_bp': 10200, 'months': 12}, {'balance': 200000, 'recast_month': None, 'deferred': 0, 'payment': 4000}], ['control 4', {'principal': 100000, 'rate_bp': 2400, 'min_payment': 2000, 'cap_pct_bp': 10100, 'months': 12}, {'balance': 100000, 'recast_month': None, 'deferred': 0, 'payment': 2000}], ['control 5', {'principal': 1000000, 'rate_bp': 1200, 'min_payment': 10000, 'cap_pct_bp': 10200, 'months': 6}, {'balance': 1000000, 'recast_month': None, 'deferred': 0, 'payment': 10000}], ['control 6', {'principal': 200000, 'rate_bp': 1200, 'min_payment': 4000, 'cap_pct_bp': 10100, 'months': 36}, {'balance': 113849, 'recast_month': None, 'deferred': 0, 'payment': 4000}]], [['regression: recast principal', {'principal': 1000000, 'rate_bp': 2400, 'min_payment': 10000, 'cap_pct_bp': 11000, 'months': 12}, {'balance': 1, 'recast_month': 10, 'deferred': 109498, 'payment': 571446}], ['control 1', {'principal': 1000000, 'rate_bp': 2400, 'min_payment': 20000, 'cap_pct_bp': 11000, 'months': 36}, {'balance': 1000000, 'recast_month': None, 'deferred': 0, 'payment': 20000}], ['control 2', {'principal': 100000, 'rate_bp': 2400, 'min_payment': 2000, 'cap_pct_bp': 10100, 'months': 24}, {'balance': 100000, 'recast_month': None, 'deferred': 0, 'payment': 2000}], ['control 3', {'principal': 200000, 'rate_bp': 600, 'min_payment': 500, 'cap_pct_bp': 10200, 'months': 6}, {'balance': 203039, 'recast_month': None, 'deferred': 3039, 'payment': 500}], ['control 4', {'principal': 200000, 'rate_bp': 2400, 'min_payment': 4000, 'cap_pct_bp': 10200, 'months': 24}, {'balance': 200000, 'recast_month': None, 'deferred': 0, 'payment': 4000}], ['control 5', {'principal': 100000, 'rate_bp': 1200, 'min_payment': 1000, 'cap_pct_bp': 10500, 'months': 6}, {'balance': 100000, 'recast_month': None, 'deferred': 0, 'payment': 1000}], ['control 6', {'principal': 200000, 'rate_bp': 600, 'min_payment': 1000, 'cap_pct_bp': 11000, 'months': 24}, {'balance': 200000, 'recast_month': None, 'deferred': 0, 'payment': 1000}]]]
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 fixtureActualExpectedOutcome
regression: recast principal{'balance': 53364, 'deferred': 52312, 'payment': 507512, 'recast_month': 10}{'balance': 1, 'deferred': 52312, 'payment': 534061, 'recast_month': 10}Failed
control 1{'balance': 1063081, 'deferred': 63081, 'payment': 10000, 'recast_month': None}{'balance': 1063081, 'deferred': 63081, 'payment': 10000, 'recast_month': None}Passed
control 2{'balance': 1000000, 'deferred': 0, 'payment': 10000, 'recast_month': None}{'balance': 1000000, 'deferred': 0, 'payment': 10000, 'recast_month': None}Passed
control 3{'balance': 200000, 'deferred': 0, 'payment': 4000, 'recast_month': None}{'balance': 200000, 'deferred': 0, 'payment': 4000, 'recast_month': None}Passed
control 4{'balance': 1000000, 'deferred': 0, 'payment': 5000, 'recast_month': None}{'balance': 1000000, 'deferred': 0, 'payment': 5000, 'recast_month': None}Passed
control 5{'balance': 113849, 'deferred': 0, 'payment': 4000, 'recast_month': None}{'balance': 113849, 'deferred': 0, 'payment': 4000, 'recast_month': None}Passed
control 6{'balance': 1000000, 'deferred': 0, 'payment': 20000, 'recast_month': None}{'balance': 1000000, 'deferred': 0, 'payment': 20000, 'recast_month': None}Passed

SHA-256 / 88443bb4d1c1d79c0c390a5719157434a507beb4b9a29554da452373061736ab

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']
    n = x['months']
    bal = P
    pay = x['min_payment']
    deferred = 0
    recast = None
    limit = P * x['cap_pct_bp']
    for k in range(1, n + 1):
        interest = rnd(bal * bp, 120000)
        if pay < interest:
            deferred += interest - pay
        bal += interest - pay
        if recast is None and bal * 10000 > limit:
            recast = k
            pay = level(bal - deferred, n - k)
    return {'balance': bal, 'recast_month': recast, 'deferred': deferred, 'payment': pay}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: recast principal', {'principal': 1000000, 'rate_bp': 1200, 'min_payment': 5000, 'cap_pct_bp': 10500, 'months': 12}, {'balance': 1, 'recast_month': 10, 'deferred': 52312, 'payment': 534061}], ['control 1', {'principal': 1000000, 'rate_bp': 2400, 'min_payment': 10000, 'cap_pct_bp': 11000, 'months': 6}, {'balance': 1063081, 'recast_month': None, 'deferred': 63081, 'payment': 10000}], ['control 2', {'principal': 1000000, 'rate_bp': 1200, 'min_payment': 10000, 'cap_pct_bp': 10200, 'months': 12}, {'balance': 1000000, 'recast_month': None, 'deferred': 0, 'payment': 10000}], ['control 3', {'principal': 200000, 'rate_bp': 2400, 'min_payment': 4000, 'cap_pct_bp': 10100, 'months': 36}, {'balance': 200000, 'recast_month': None, 'deferred': 0, 'payment': 4000}], ['control 4', {'principal': 1000000, 'rate_bp': 600, 'min_payment': 5000, 'cap_pct_bp': 10500, 'months': 12}, {'balance': 1000000, 'recast_month': None, 'deferred': 0, 'payment': 5000}], ['control 5', {'principal': 200000, 'rate_bp': 1200, 'min_payment': 4000, 'cap_pct_bp': 11000, 'months': 36}, {'balance': 113849, 'recast_month': None, 'deferred': 0, 'payment': 4000}], ['control 6', {'principal': 1000000, 'rate_bp': 2400, 'min_payment': 20000, 'cap_pct_bp': 10100, 'months': 36}, {'balance': 1000000, 'recast_month': None, 'deferred': 0, 'payment': 20000}]], [['regression: recast principal', {'principal': 200000, 'rate_bp': 2400, 'min_payment': 2000, 'cap_pct_bp': 10100, 'months': 12}, {'balance': 0, 'recast_month': 2, 'deferred': 4040, 'payment': 22715}], ['control 1', {'principal': 100000, 'rate_bp': 600, 'min_payment': 500, 'cap_pct_bp': 10100, 'months': 36}, {'balance': 100000, 'recast_month': None, 'deferred': 0, 'payment': 500}], ['control 2', {'principal': 200000, 'rate_bp': 600, 'min_payment': 1000, 'cap_pct_bp': 11000, 'months': 12}, {'balance': 200000, 'recast_month': None, 'deferred': 0, 'payment': 1000}], ['control 3', {'principal': 100000, 'rate_bp': 2400, 'min_payment': 2000, 'cap_pct_bp': 11000, 'months': 36}, {'balance': 100000, 'recast_month': None, 'deferred': 0, 'payment': 2000}], ['control 4', {'principal': 200000, 'rate_bp': 600, 'min_payment': 1000, 'cap_pct_bp': 10200, 'months': 36}, {'balance': 200000, 'recast_month': None, 'deferred': 0, 'payment': 1000}], ['control 5', {'principal': 100000, 'rate_bp': 2400, 'min_payment': 2000, 'cap_pct_bp': 10100, 'months': 6}, {'balance': 100000, 'recast_month': None, 'deferred': 0, 'payment': 2000}], ['control 6', {'principal': 100000, 'rate_bp': 600, 'min_payment': 1000, 'cap_pct_bp': 11000, 'months': 6}, {'balance': 96962, 'recast_month': None, 'deferred': 0, 'payment': 1000}]], [['regression: recast principal', {'principal': 1000000, 'rate_bp': 2400, 'min_payment': 10000, 'cap_pct_bp': 10100, 'months': 12}, {'balance': 4, 'recast_month': 2, 'deferred': 20200, 'payment': 113575}], ['control 1', {'principal': 1000000, 'rate_bp': 2400, 'min_payment': 20000, 'cap_pct_bp': 11000, 'months': 36}, {'balance': 1000000, 'recast_month': None, 'deferred': 0, 'payment': 20000}], ['control 2', {'principal': 200000, 'rate_bp': 600, 'min_payment': 500, 'cap_pct_bp': 10200, 'months': 6}, {'balance': 203039, 'recast_month': None, 'deferred': 3039, 'payment': 500}], ['control 3', {'principal': 1000000, 'rate_bp': 600, 'min_payment': 2500, 'cap_pct_bp': 11000, 'months': 6}, {'balance': 1015189, 'recast_month': None, 'deferred': 15189, 'payment': 2500}], ['control 4', {'principal': 1000000, 'rate_bp': 600, 'min_payment': 20000, 'cap_pct_bp': 10200, 'months': 24}, {'balance': 618521, 'recast_month': None, 'deferred': 0, 'payment': 20000}], ['control 5', {'principal': 100000, 'rate_bp': 600, 'min_payment': 2000, 'cap_pct_bp': 11000, 'months': 6}, {'balance': 90887, 'recast_month': None, 'deferred': 0, 'payment': 2000}], ['control 6', {'principal': 200000, 'rate_bp': 1200, 'min_payment': 4000, 'cap_pct_bp': 11000, 'months': 12}, {'balance': 174636, 'recast_month': None, 'deferred': 0, 'payment': 4000}]], [['regression: recast principal', {'principal': 100000, 'rate_bp': 2400, 'min_payment': 250, 'cap_pct_bp': 10100, 'months': 12}, {'balance': -3, 'recast_month': 1, 'deferred': 1750, 'payment': 10397}], ['control 1', {'principal': 200000, 'rate_bp': 1200, 'min_payment': 4000, 'cap_pct_bp': 10100, 'months': 6}, {'balance': 187696, 'recast_month': None, 'deferred': 0, 'payment': 4000}], ['control 2', {'principal': 200000, 'rate_bp': 2400, 'min_payment': 500, 'cap_pct_bp': 11000, 'months': 6}, {'balance': 222078, 'recast_month': 6, 'deferred': 22078, 'payment': 0}], ['control 3', {'principal': 200000, 'rate_bp': 2400, 'min_payment': 4000, 'cap_pct_bp': 10200, 'months': 12}, {'balance': 200000, 'recast_month': None, 'deferred': 0, 'payment': 4000}], ['control 4', {'principal': 100000, 'rate_bp': 2400, 'min_payment': 2000, 'cap_pct_bp': 10100, 'months': 12}, {'balance': 100000, 'recast_month': None, 'deferred': 0, 'payment': 2000}], ['control 5', {'principal': 1000000, 'rate_bp': 1200, 'min_payment': 10000, 'cap_pct_bp': 10200, 'months': 6}, {'balance': 1000000, 'recast_month': None, 'deferred': 0, 'payment': 10000}], ['control 6', {'principal': 200000, 'rate_bp': 1200, 'min_payment': 4000, 'cap_pct_bp': 10100, 'months': 36}, {'balance': 113849, 'recast_month': None, 'deferred': 0, 'payment': 4000}]], [['regression: recast principal', {'principal': 1000000, 'rate_bp': 2400, 'min_payment': 10000, 'cap_pct_bp': 11000, 'months': 12}, {'balance': 1, 'recast_month': 10, 'deferred': 109498, 'payment': 571446}], ['control 1', {'principal': 1000000, 'rate_bp': 2400, 'min_payment': 20000, 'cap_pct_bp': 11000, 'months': 36}, {'balance': 1000000, 'recast_month': None, 'deferred': 0, 'payment': 20000}], ['control 2', {'principal': 100000, 'rate_bp': 2400, 'min_payment': 2000, 'cap_pct_bp': 10100, 'months': 24}, {'balance': 100000, 'recast_month': None, 'deferred': 0, 'payment': 2000}], ['control 3', {'principal': 200000, 'rate_bp': 600, 'min_payment': 500, 'cap_pct_bp': 10200, 'months': 6}, {'balance': 203039, 'recast_month': None, 'deferred': 3039, 'payment': 500}], ['control 4', {'principal': 200000, 'rate_bp': 2400, 'min_payment': 4000, 'cap_pct_bp': 10200, 'months': 24}, {'balance': 200000, 'recast_month': None, 'deferred': 0, 'payment': 4000}], ['control 5', {'principal': 100000, 'rate_bp': 1200, 'min_payment': 1000, 'cap_pct_bp': 10500, 'months': 6}, {'balance': 100000, 'recast_month': None, 'deferred': 0, 'payment': 1000}], ['control 6', {'principal': 200000, 'rate_bp': 600, 'min_payment': 1000, 'cap_pct_bp': 11000, 'months': 24}, {'balance': 200000, 'recast_month': None, 'deferred': 0, 'payment': 1000}]]]
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 fixtureActualExpectedOutcome
regression: recast principal{'balance': 53364, 'deferred': 52312, 'payment': 507512, 'recast_month': 10}{'balance': 1, 'deferred': 52312, 'payment': 534061, 'recast_month': 10}Failed
control 1{'balance': 1063081, 'deferred': 63081, 'payment': 10000, 'recast_month': None}{'balance': 1063081, 'deferred': 63081, 'payment': 10000, 'recast_month': None}Passed
control 2{'balance': 1000000, 'deferred': 0, 'payment': 10000, 'recast_month': None}{'balance': 1000000, 'deferred': 0, 'payment': 10000, 'recast_month': None}Passed
control 3{'balance': 200000, 'deferred': 0, 'payment': 4000, 'recast_month': None}{'balance': 200000, 'deferred': 0, 'payment': 4000, 'recast_month': None}Passed
control 4{'balance': 1000000, 'deferred': 0, 'payment': 5000, 'recast_month': None}{'balance': 1000000, 'deferred': 0, 'payment': 5000, 'recast_month': None}Passed
control 5{'balance': 113849, 'deferred': 0, 'payment': 4000, 'recast_month': None}{'balance': 113849, 'deferred': 0, 'payment': 4000, 'recast_month': None}Passed
control 6{'balance': 1000000, 'deferred': 0, 'payment': 20000, 'recast_month': None}{'balance': 1000000, 'deferred': 0, 'payment': 20000, 'recast_month': None}Passed

SHA-256 / 43e61d0dc1e10db797b73068d07625e50f96f31bc9490462f9c817e9afe2f0ed

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']
    n = x['months']
    bal = P
    pay = x['min_payment']
    deferred = 0
    recast = None
    limit = P * x['cap_pct_bp']
    for k in range(1, n + 1):
        interest = rnd(bal * bp, 120000)
        if pay < interest:
            deferred += interest - pay
        bal += interest - pay
        if recast is None and bal * 10000 > limit:
            recast = k
            pay = level(bal, n - k)
    return {'balance': bal, 'recast_month': recast, 'deferred': deferred, 'payment': pay}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: recast principal', {'principal': 1000000, 'rate_bp': 1200, 'min_payment': 5000, 'cap_pct_bp': 10500, 'months': 12}, {'balance': 1, 'recast_month': 10, 'deferred': 52312, 'payment': 534061}], ['control 1', {'principal': 1000000, 'rate_bp': 2400, 'min_payment': 10000, 'cap_pct_bp': 11000, 'months': 6}, {'balance': 1063081, 'recast_month': None, 'deferred': 63081, 'payment': 10000}], ['control 2', {'principal': 1000000, 'rate_bp': 1200, 'min_payment': 10000, 'cap_pct_bp': 10200, 'months': 12}, {'balance': 1000000, 'recast_month': None, 'deferred': 0, 'payment': 10000}], ['control 3', {'principal': 200000, 'rate_bp': 2400, 'min_payment': 4000, 'cap_pct_bp': 10100, 'months': 36}, {'balance': 200000, 'recast_month': None, 'deferred': 0, 'payment': 4000}], ['control 4', {'principal': 1000000, 'rate_bp': 600, 'min_payment': 5000, 'cap_pct_bp': 10500, 'months': 12}, {'balance': 1000000, 'recast_month': None, 'deferred': 0, 'payment': 5000}], ['control 5', {'principal': 200000, 'rate_bp': 1200, 'min_payment': 4000, 'cap_pct_bp': 11000, 'months': 36}, {'balance': 113849, 'recast_month': None, 'deferred': 0, 'payment': 4000}], ['control 6', {'principal': 1000000, 'rate_bp': 2400, 'min_payment': 20000, 'cap_pct_bp': 10100, 'months': 36}, {'balance': 1000000, 'recast_month': None, 'deferred': 0, 'payment': 20000}]], [['regression: recast principal', {'principal': 200000, 'rate_bp': 2400, 'min_payment': 2000, 'cap_pct_bp': 10100, 'months': 12}, {'balance': 0, 'recast_month': 2, 'deferred': 4040, 'payment': 22715}], ['control 1', {'principal': 100000, 'rate_bp': 600, 'min_payment': 500, 'cap_pct_bp': 10100, 'months': 36}, {'balance': 100000, 'recast_month': None, 'deferred': 0, 'payment': 500}], ['control 2', {'principal': 200000, 'rate_bp': 600, 'min_payment': 1000, 'cap_pct_bp': 11000, 'months': 12}, {'balance': 200000, 'recast_month': None, 'deferred': 0, 'payment': 1000}], ['control 3', {'principal': 100000, 'rate_bp': 2400, 'min_payment': 2000, 'cap_pct_bp': 11000, 'months': 36}, {'balance': 100000, 'recast_month': None, 'deferred': 0, 'payment': 2000}], ['control 4', {'principal': 200000, 'rate_bp': 600, 'min_payment': 1000, 'cap_pct_bp': 10200, 'months': 36}, {'balance': 200000, 'recast_month': None, 'deferred': 0, 'payment': 1000}], ['control 5', {'principal': 100000, 'rate_bp': 2400, 'min_payment': 2000, 'cap_pct_bp': 10100, 'months': 6}, {'balance': 100000, 'recast_month': None, 'deferred': 0, 'payment': 2000}], ['control 6', {'principal': 100000, 'rate_bp': 600, 'min_payment': 1000, 'cap_pct_bp': 11000, 'months': 6}, {'balance': 96962, 'recast_month': None, 'deferred': 0, 'payment': 1000}]], [['regression: recast principal', {'principal': 1000000, 'rate_bp': 2400, 'min_payment': 10000, 'cap_pct_bp': 10100, 'months': 12}, {'balance': 4, 'recast_month': 2, 'deferred': 20200, 'payment': 113575}], ['control 1', {'principal': 1000000, 'rate_bp': 2400, 'min_payment': 20000, 'cap_pct_bp': 11000, 'months': 36}, {'balance': 1000000, 'recast_month': None, 'deferred': 0, 'payment': 20000}], ['control 2', {'principal': 200000, 'rate_bp': 600, 'min_payment': 500, 'cap_pct_bp': 10200, 'months': 6}, {'balance': 203039, 'recast_month': None, 'deferred': 3039, 'payment': 500}], ['control 3', {'principal': 1000000, 'rate_bp': 600, 'min_payment': 2500, 'cap_pct_bp': 11000, 'months': 6}, {'balance': 1015189, 'recast_month': None, 'deferred': 15189, 'payment': 2500}], ['control 4', {'principal': 1000000, 'rate_bp': 600, 'min_payment': 20000, 'cap_pct_bp': 10200, 'months': 24}, {'balance': 618521, 'recast_month': None, 'deferred': 0, 'payment': 20000}], ['control 5', {'principal': 100000, 'rate_bp': 600, 'min_payment': 2000, 'cap_pct_bp': 11000, 'months': 6}, {'balance': 90887, 'recast_month': None, 'deferred': 0, 'payment': 2000}], ['control 6', {'principal': 200000, 'rate_bp': 1200, 'min_payment': 4000, 'cap_pct_bp': 11000, 'months': 12}, {'balance': 174636, 'recast_month': None, 'deferred': 0, 'payment': 4000}]], [['regression: recast principal', {'principal': 100000, 'rate_bp': 2400, 'min_payment': 250, 'cap_pct_bp': 10100, 'months': 12}, {'balance': -3, 'recast_month': 1, 'deferred': 1750, 'payment': 10397}], ['control 1', {'principal': 200000, 'rate_bp': 1200, 'min_payment': 4000, 'cap_pct_bp': 10100, 'months': 6}, {'balance': 187696, 'recast_month': None, 'deferred': 0, 'payment': 4000}], ['control 2', {'principal': 200000, 'rate_bp': 2400, 'min_payment': 500, 'cap_pct_bp': 11000, 'months': 6}, {'balance': 222078, 'recast_month': 6, 'deferred': 22078, 'payment': 0}], ['control 3', {'principal': 200000, 'rate_bp': 2400, 'min_payment': 4000, 'cap_pct_bp': 10200, 'months': 12}, {'balance': 200000, 'recast_month': None, 'deferred': 0, 'payment': 4000}], ['control 4', {'principal': 100000, 'rate_bp': 2400, 'min_payment': 2000, 'cap_pct_bp': 10100, 'months': 12}, {'balance': 100000, 'recast_month': None, 'deferred': 0, 'payment': 2000}], ['control 5', {'principal': 1000000, 'rate_bp': 1200, 'min_payment': 10000, 'cap_pct_bp': 10200, 'months': 6}, {'balance': 1000000, 'recast_month': None, 'deferred': 0, 'payment': 10000}], ['control 6', {'principal': 200000, 'rate_bp': 1200, 'min_payment': 4000, 'cap_pct_bp': 10100, 'months': 36}, {'balance': 113849, 'recast_month': None, 'deferred': 0, 'payment': 4000}]], [['regression: recast principal', {'principal': 1000000, 'rate_bp': 2400, 'min_payment': 10000, 'cap_pct_bp': 11000, 'months': 12}, {'balance': 1, 'recast_month': 10, 'deferred': 109498, 'payment': 571446}], ['control 1', {'principal': 1000000, 'rate_bp': 2400, 'min_payment': 20000, 'cap_pct_bp': 11000, 'months': 36}, {'balance': 1000000, 'recast_month': None, 'deferred': 0, 'payment': 20000}], ['control 2', {'principal': 100000, 'rate_bp': 2400, 'min_payment': 2000, 'cap_pct_bp': 10100, 'months': 24}, {'balance': 100000, 'recast_month': None, 'deferred': 0, 'payment': 2000}], ['control 3', {'principal': 200000, 'rate_bp': 600, 'min_payment': 500, 'cap_pct_bp': 10200, 'months': 6}, {'balance': 203039, 'recast_month': None, 'deferred': 3039, 'payment': 500}], ['control 4', {'principal': 200000, 'rate_bp': 2400, 'min_payment': 4000, 'cap_pct_bp': 10200, 'months': 24}, {'balance': 200000, 'recast_month': None, 'deferred': 0, 'payment': 4000}], ['control 5', {'principal': 100000, 'rate_bp': 1200, 'min_payment': 1000, 'cap_pct_bp': 10500, 'months': 6}, {'balance': 100000, 'recast_month': None, 'deferred': 0, 'payment': 1000}], ['control 6', {'principal': 200000, 'rate_bp': 600, 'min_payment': 1000, 'cap_pct_bp': 11000, 'months': 24}, {'balance': 200000, 'recast_month': None, 'deferred': 0, 'payment': 1000}]]]
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 fixtureActualExpectedOutcome
regression: recast principal{'balance': 1, 'deferred': 52312, 'payment': 534061, 'recast_month': 10}{'balance': 1, 'deferred': 52312, 'payment': 534061, 'recast_month': 10}Passed
control 1{'balance': 1063081, 'deferred': 63081, 'payment': 10000, 'recast_month': None}{'balance': 1063081, 'deferred': 63081, 'payment': 10000, 'recast_month': None}Passed
control 2{'balance': 1000000, 'deferred': 0, 'payment': 10000, 'recast_month': None}{'balance': 1000000, 'deferred': 0, 'payment': 10000, 'recast_month': None}Passed
control 3{'balance': 200000, 'deferred': 0, 'payment': 4000, 'recast_month': None}{'balance': 200000, 'deferred': 0, 'payment': 4000, 'recast_month': None}Passed
control 4{'balance': 1000000, 'deferred': 0, 'payment': 5000, 'recast_month': None}{'balance': 1000000, 'deferred': 0, 'payment': 5000, 'recast_month': None}Passed
control 5{'balance': 113849, 'deferred': 0, 'payment': 4000, 'recast_month': None}{'balance': 113849, 'deferred': 0, 'payment': 4000, 'recast_month': None}Passed
control 6{'balance': 1000000, 'deferred': 0, 'payment': 20000, 'recast_month': None}{'balance': 1000000, 'deferred': 0, 'payment': 20000, 'recast_month': None}Passed

SHA-256 / 74e6318f43cdde75b7daf1ec10f851a52d6952718f2746c9a17571e522d52da2

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.172151+00:00.

Case digest / 403d9c6bc5241da4d8e1fdb50d0019ba48123becdcc75af646e790a95f4207b7