#!/usr/bin/env python3 """ Reproduce every figure published on amortance.com from the published inputs. python3 verify.py # whichever specification is beside it python3 verify.py dental.json # or the files you name, wherever they are python3 verify.py --all # print every comparison, not only the tally MIT license, like the engine it checks. Copyright (c) 2026 Amortance. Full text: https://amortance.com/LICENSE This is a second implementation. It shares no code with the engine that produced the pages: it is written from the rules the specification states — the accrual, the allocation, how an installment is priced, what the last one absorbs, how a payoff date is searched for — and it re-derives every figure the specification publishes. Nothing in the files is taken on trust, including the posting log. The dates come from the contract's due dates, the amounts from the pricing rules, and the reconstructed log is then compared with the published one entry by entry: a payment on a date the contract does not name, or for an amount the rules do not produce, is a failure. A figure the specification publishes and this script does not recompute is a failure too, so "every figure" cannot quietly stop being true. What it is not: servicing software. It reproduces two published examples. """ import json import pathlib import sys from datetime import date, timedelta from decimal import Decimal, ROUND_HALF_UP CENT = Decimal("0.01") ZERO = Decimal("0") HERE = pathlib.Path(__file__).parent # ------------------------------------------------------------------ arithmetic def dec(x) -> Decimal: return Decimal(str(x)) def r2(x: Decimal) -> Decimal: """Half up to the cent, which is what the specification says and what a contract does. Banker's rounding would move several of the figures below by a cent.""" return x.quantize(CENT, rounding=ROUND_HALF_UP) def day(s: str) -> date: y, m, d = (int(p) for p in s.split("-")) return date(y, m, d) def days360(a: date, b: date) -> int: """US 30/360.""" d1, d2 = a.day, b.day if d1 == 31: d1 = 30 if d2 == 31 and d1 == 30: d2 = 30 return (b.year - a.year) * 360 + (b.month - a.month) * 30 + (d2 - d1) def basis(conv: str) -> Decimal: if conv in ("30/360", "actual/360"): return dec(360) if conv == "actual/365": return dec(365) raise SystemExit("unknown day count: " + conv) def count(a: date, b: date, conv: str) -> int: return days360(a, b) if conv == "30/360" else (b - a).days def add_months(dt: date, n: int) -> date: m = dt.month - 1 + n y, m = dt.year + m // 12, m % 12 + 1 leap = y % 4 == 0 and (y % 100 or y % 400 == 0) last = [31, 29 if leap else 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][m - 1] return date(y, m, min(dt.day, last)) def started_months(a: date, b: date) -> int: """Whole months elapsed plus a started one — how a service contract counts them.""" whole = (b.year - a.year) * 12 + (b.month - a.month) - (1 if b.day < a.day else 0) return whole + (0 if add_months(a, whole) == b else 1) def nominal(apr: Decimal, period_days: int, conv: str) -> Decimal: """The rate a contract quotes its installment at: a nominal period over the basis in force. Monthly on 30/360 this is APR/12; biweekly on actual/365, APR x 14/365.""" return apr * dec(period_days) / basis(conv) def annuity(principal: Decimal, rate: Decimal, n: int) -> Decimal: """=PMT. Level payment at a periodic rate, rounded half up to the cent.""" if rate == 0: return r2(principal / n) return r2(principal * rate / (1 - (1 + rate) ** -n)) # ---------------------------------------------------------------- the ledger ORDER = {"principal": 0, "fee": 1, "payment": 2} def post(on: date, kind: str, amount: Decimal) -> dict: return {"on": on, "kind": kind, "amount": amount} class Missing(Exception): """A row the specification implies but the replay never produced.""" class Book: """Simple interest on the outstanding principal, rounded at each posting. A payment settles accrued interest first, then principal; whatever a payment or a reduction cannot place is a credit due back.""" def __init__(self): self.rows: list[dict] = [] self.balance = self.accrued = self.charged = self.credit = ZERO def closed(self, default: date) -> date: """The date the balance first reached zero.""" for r in self.rows: if r["balance"] == 0 and r["kind"] in ("payment", "principal"): return r["on"] return default def at(self, on: date, kind: str) -> dict: for r in self.rows: if r["on"] == on and r["kind"] == kind: return r raise Missing(f"no {kind} posted on {on} — the log and the contract disagree " f"about what happened that day") def replay(start: date, apr: Decimal, conv: str, postings: list, through=None) -> Book: b = Book() cursor = start for p in sorted(postings, key=lambda p: (p["on"], ORDER[p["kind"]])): if through is not None and p["on"] > through: break n = count(cursor, p["on"], conv) interest = r2(b.balance * apr * dec(n) / basis(conv)) if n else ZERO b.accrued += interest b.charged += interest cursor = p["on"] to_i = to_p = ZERO if p["kind"] in ("principal", "fee"): b.balance += p["amount"] if b.balance < 0: # the order shrank below what is owed excess, b.balance = -b.balance, ZERO settled = min(b.accrued, excess) # the credit settles interest first b.accrued -= settled b.credit += excess - settled else: to_i = min(b.accrued, p["amount"]) b.accrued -= to_i rest = p["amount"] - to_i to_p = min(b.balance, rest) b.balance -= to_p b.credit += rest - to_p b.rows.append({"on": p["on"], "days": n, "interest": interest, "kind": p["kind"], "amount": p["amount"], "to_interest": to_i, "to_principal": to_p, "balance": b.balance, "accrued": b.accrued}) if through is not None and cursor < through: n = count(cursor, through, conv) interest = r2(b.balance * apr * dec(n) / basis(conv)) if n else ZERO b.accrued += interest b.charged += interest b.rows.append({"on": through, "days": n, "interest": interest, "kind": "accrual", "amount": ZERO, "to_interest": ZERO, "to_principal": ZERO, "balance": b.balance, "accrued": b.accrued}) return b def project(balance: Decimal, accrued: Decimal, rate: Decimal, dates: list, apr=None, conv=None, start=None) -> tuple: """Re-amortize: the dates hold and the installment moves. The installment is priced off the nominal periodic rate, the way a contract quotes it, while the rows accrue on the day count actually in force — the two coincide only when the periods are uniform. The last payment absorbs the residual.""" pay = annuity(balance + accrued, rate, len(dates)) bal, acc, rows = balance, accrued, [] cursor = start for i, dt in enumerate(dates): if apr is not None and cursor is not None: interest = r2(bal * apr * dec(count(cursor, dt, conv)) / basis(conv)) cursor = dt else: interest = r2(bal * rate) acc += interest amount = pay if i < len(dates) - 1 else r2(bal + acc) to_i = min(acc, amount) acc -= to_i to_p = amount - to_i bal -= to_p rows.append({"on": dt, "amount": amount, "interest": interest, "to_interest": to_i, "to_principal": to_p, "balance": bal}) return pay, rows def level_payment(ceiling: Decimal, trial_run) -> Decimal: """The smallest whole cent that, paid on every remaining date, retires the balance without the final payment exceeding it. The predicate is monotone in the payment, so bisect; `ceiling` has to be high enough that it is never the answer.""" lo, hi = 1, int(ceiling * 100) while lo < hi: mid = (lo + hi) // 2 lo, hi = (lo, mid) if trial_run(dec(mid) / 100) else (mid + 1, hi) if lo >= int(ceiling * 100): raise Missing(f"no level payment below {ceiling} retires the balance; " f"the search bound is too low to trust the answer") return dec(lo) / 100 # --------------------------------------------------------------- presentation def M(x: Decimal) -> str: s = f"{abs(x):,.2f}" return ("−" + s) if x < 0 else s def N(x) -> str: return str(int(x)) def entry(p: dict) -> str: return f"{p['on']} {p['kind']} {M(p['amount'])}" class Report: def __init__(self, show_all: bool): self.show_all = show_all self.checked = self.failed = self.figures = 0 self.problems: list[str] = [] def state(self, title: str) -> None: print(f"\n{title}") def _fail(self, problem: str, line: str) -> None: self.failed += 1 self.problems.append(problem) print(" FAIL " + line) def compare(self, got: dict, published: dict) -> None: """Every figure the specification publishes, against the one derived here.""" wrong = 0 for key in sorted(published): self.checked += 1 self.figures += 1 want = published[key] if key not in got: wrong += 1 self._fail(f"{key}: published as {want}, not reproduced at all", f"{key:<24} not reproduced published {want}") elif got[key] != want: wrong += 1 self._fail(f"{key}: got {got[key]}, published {want}", f"{key:<24}{got[key]:>18} published {want}") elif self.show_all: print(f" ok {key:<24}{got[key]:>18}") spare = sorted(set(got) - set(published)) if spare and self.show_all: print(f" .. also derived, not published: {', '.join(spare)}") print(f" {len(published) - wrong} of {len(published)} figures reproduced") def figure(self, label: str, got: str, want: str) -> None: """One published figure that lives outside the expected block.""" self.checked += 1 self.figures += 1 if got != want: self._fail(f"{label}: got {got}, published {want}", f"{label:<24}{got:>18} published {want}") elif self.show_all: print(f" ok {label:<24}{got:>18}") def log(self, derived: list, stated: list) -> None: """The published log against the one the contract and the rules imply. The dates belong to the contract and the amounts to the pricing rules, so neither is read out of the log being checked — only how many installments were collected.""" a = sorted(derived, key=lambda p: (p["on"], ORDER[p["kind"]])) b = sorted(stated, key=lambda p: (p["on"], ORDER[p["kind"]])) self.checked += 1 if len(a) != len(b): self._fail(f"log: {len(b)} postings published, {len(a)} implied by the rules", f"{'log: posting count':<24}{len(a):>18} published {len(b)}") return for i, (x, y) in enumerate(zip(a, b), 1): if (x["on"], x["kind"], x["amount"]) != (y["on"], y["kind"], y["amount"]): self._fail(f"log entry {i}: derived {entry(x)}, published {entry(y)}", f"{'log entry ' + str(i):<24}{entry(x):>26} published {entry(y)}") return if self.show_all: print(f" ok {'log: ' + str(len(a)) + ' postings':<24}" f"{'every date and amount':>26}") def must(self, label: str, ok: bool, detail: str) -> None: self.checked += 1 if not ok: self._fail(f"{label}: {detail}", f"{label:<24}{detail}") elif self.show_all: print(f" ok {label:<24}{detail}") # -------------------------------------------------------------------- dental def check_dental(spec: dict, rep: Report) -> None: apr = dec(spec["terms"]["apr"]) start = day(spec["order"]["accepted"]) down = dec(spec["terms"]["down_payment"]["amount"]) dues = [day(x) for x in spec["terms"]["due_dates"]] patient = dec(spec["order"]["totals"]["patient_share"]) financed = patient - down for st in spec["states"]: conv = st["policy"]["day_count"] rate = nominal(apr, 30, conv) rep.state(f"dental — {conv}, adjudication effective " f"{st['policy']['adjudication_effective']}") # The log identifies its own entries: the adjudication is the posting that names # the day it was priced on, the origination is the other principal posting at the # start, the addition is what is left. No note is read for meaning. stated = st["log"] adj = next(p for p in stated if "posted" in p) adj_on, adj_priced, adj_amt = day(adj["on"]), day(adj["posted"]), dec(adj["amount"]) principals = [p for p in stated if p["kind"] == "principal" and p is not adj] opening = next(p for p in principals if day(p["on"]) == start) added = next(p for p in principals if p is not opening) add_on, add_amt = day(added["on"]), dec(added["amount"]) # How many installments were collected before the plan was unwound is the one # thing only the log knows. Which dates they fall on is the contract's business. collected = sum(1 for p in stated if p["kind"] == "payment") rep.must("log: installments", collected <= len(dues), f"{collected} collected, of {len(dues)} due dates the contract names") paid_on = dues[:collected] before = [d for d in paid_on if d <= adj_priced] between = [d for d in paid_on if adj_priced < d <= add_on] after = [d for d in paid_on if d > add_on] # as signed: twelve level installments, nothing yet known to be wrong pay0 = annuity(financed, rate, len(dues)) _, as_rows = project(financed, ZERO, rate, dues, apr, conv, start) # the adjudication posts and the plan is repriced over the due dates that remain to_eob = ([post(start, "principal", financed), post(adj_on, "principal", adj_amt)] + [post(d, "payment", pay0) for d in before]) at_eob = replay(start, apr, conv, to_eob, through=adj_priced) rest = dues[len(before):] pay1 = annuity(at_eob.balance + at_eob.accrued, rate, len(rest)) # what the last of those installments would come to, and the level alternative tail = replay(start, apr, conv, to_eob + [post(d, "payment", pay1) for d in rest], through=rest[-1]) pay1_final = pay1 - tail.credit def retires_the_balance(trial: Decimal) -> bool: """Paid on every remaining date but the last, does `trial` leave the last one no larger than itself?""" lg = replay(start, apr, conv, to_eob + [post(d, "payment", trial) for d in rest[:-1]], through=rest[-1]) return lg.balance + lg.accrued <= trial pay1_level = level_payment(pay0 * 2, retires_the_balance) # production added, effective the day it was performed to_add = (to_eob + [post(d, "payment", pay1) for d in between] + [post(add_on, "principal", add_amt)]) at_add = replay(start, apr, conv, to_add, through=add_on) pay2 = annuity(at_add.balance + at_add.accrued, rate, len(dues) - len(before) - len(between)) derived = to_add + [post(d, "payment", pay2) for d in after] rep.log(derived, [post(day(p["on"]), p["kind"], dec(p["amount"])) for p in stated]) # the fork: the implant was financed and never placed. How much comes off is a # property of the event; the fork is about the date it takes effect, so the two # branches have to name the same amount. fork_key = next(k for k in st if k.startswith("fork_on_")) fork_day = day(fork_key.split("fork_on_", 1)[1]) branches = st[fork_key] amounts = {dec(br["amount"]) for br in branches.values()} cancel_amt = min(amounts) rep.must("fork: one amount", len(amounts) == 1, f"both branches remove {M(cancel_amt)}" if len(amounts) == 1 else f"the branches remove {len(amounts)} different amounts") book, published = {}, {} for br in branches.values(): effective = day(br["effective"]) key = "replayed" if effective == start else "forward" book[key] = replay(start, apr, conv, derived + [post(effective, "principal", dec(br["amount"]))], through=fork_day) published[key] = br rep.must("fork: both branches", set(book) == {"replayed", "forward"}, f"branches found: {', '.join(sorted(book))}") fwd, ret = book["forward"], book["replayed"] # the fork block publishes its own outcome as well as the expected block for key, lg in (("forward", fwd), ("replayed", ret)): br = published[key] rep.figure(f"fork {key}: interest", M(lg.charged), br["interest_charged"]) rep.figure(f"fork {key}: credit", M(lg.credit), br["credit_due_patient"]) rep.figure(f"fork {key}: closes", str(lg.closed(fork_day)), br["plan_closes"]) charged1 = as_rows[0] replayed1 = fwd.at(dues[0], "payment") in_force = financed + (adj_amt if adj_on == start else ZERO) days1 = count(start, dues[0], conv) exact = in_force * apr * dec(days1) / basis(conv) paid_total = down + pay0 + pay1 + pay2 * len(after) corrected = patient + adj_amt + add_amt + cancel_amt got = { "financed": M(financed), "pay0": M(pay0), "asSignedInterest": M(sum(r["interest"] for r in as_rows)), "eobBalance": M(at_eob.balance), "accruedToEob": M(at_eob.accrued), "instAfterEob": M(pay1), "instAfterEobFinal": M(pay1_final), "instAfterEobLevel": M(pay1_level), "accruedAtAdd": M(at_add.accrued), "instAfterAdd": M(pay2), "daysToAdd": N(count(dues[1], add_on, conv)), "paidToDate": M(paid_total), "correctedShare": M(corrected), "overCollected": M(paid_total - corrected), "forkAccrued": M(fwd.rows[-1]["interest"]), "aInterest": M(fwd.charged), "aCredit": M(fwd.credit), "aClose": str(fwd.closed(fork_day)), "aCloseMonth": fwd.closed(fork_day).strftime("%B"), "bInterest": M(ret.charged), "bCredit": M(ret.credit), "bClose": str(ret.closed(fork_day)), "bCloseMonth": ret.closed(fork_day).strftime("%B"), "gap": M(ret.credit - fwd.credit), "gapInterest": M(fwd.charged - ret.charged), "charged1Interest": M(charged1["interest"]), "charged1Principal": M(charged1["to_principal"]), "charged1Balance": M(charged1["balance"]), "replayed1Interest": M(replayed1["to_interest"]), "replayed1Principal": M(replayed1["to_principal"]), "replayed1Balance": M(replayed1["balance"]), "diff1Interest": M(replayed1["to_interest"] - charged1["interest"]), "diff1Principal": M(replayed1["to_principal"] - charged1["to_principal"]), "diff1Balance": M(replayed1["balance"] - charged1["balance"]), "ruleBalance": M(in_force), "ruleDays": N(days1), "ruleBasis": N(basis(conv)), "ruleExact": f"{exact:.4f}", "ruleResult": M(replayed1["to_interest"]), } for i in (0, 1, 2): r = as_rows[i] got[f"as{i+1}Payment"], got[f"as{i+1}Interest"] = M(r["amount"]), M(r["interest"]) got[f"as{i+1}Principal"] = M(r["to_principal"]) got[f"as{i+1}Balance"] = M(r["balance"]) last = as_rows[-1] got["asLastPayment"], got["asLastInterest"] = M(last["amount"]), M(last["interest"]) got["asLastPrincipal"], got["asLastBalance"] = M(last["to_principal"]), M(last["balance"]) rep.compare(got, st["expected"]) # ---------------------------------------------------------------------- bhph KNOWN_EVENTS = {"fee", "service_contract_canceled", "total_loss"} def check_bhph(spec: dict, rep: Report) -> None: apr = dec(spec["terms"]["apr"]) sale = day(spec["deal"]["sold"]) financed = (sum(dec(line["amount"]) for line in spec["deal"]["lines"]) - dec(spec["deal"]["cash_down"])) for st in spec["states"]: conv = st["policy"]["day_count"] sched = st["payments"] every, n = sched["every_days"], sched["count"] first = day(sched["first"]) dues = [first + timedelta(days=every * i) for i in range(n)] def due(k: int) -> date: """Payment k falls on the k-th due date. The figures are named for the payment (p13), the list is indexed from zero, and that is the only place those two meet.""" return dues[k - 1] rate = nominal(apr, every, conv) rep.state(f"bhph — {conv}, refund pro rated on {st['policy']['refund_pro_rated_on']}") events = {e["kind"]: e for e in st["events"]} rep.must("events: all understood", set(events) <= KNOWN_EVENTS, f"unhandled: {', '.join(sorted(set(events) - KNOWN_EVENTS)) or 'none'}") rep.must("events: none repeated", len(events) == len(st["events"]), f"{len(st['events'])} events, {len(events)} distinct kinds") pay0 = annuity(financed, rate, n) _, opening = project(financed, ZERO, rate, dues, apr, conv, sale) fee_ev = events["fee"] fee_on, fee_amt = day(fee_ev["on"]), dec(fee_ev["amount"]) moved = [e for e in sched["exceptions"] if "on" in e] stops = [e for e in sched["exceptions"] if "after" in e] rep.must("payments: exceptions", len(moved) + len(stops) == len(sched["exceptions"]), f"{len(sched['exceptions'])} stated: {len(moved)} moved, " f"{len(stops)} stop collection") skipped = {day(e["on"]) for e in moved} base = [post(sale, "principal", financed)] + [post(dt, "payment", pay0) for dt in dues] log = [p for p in base if not (p["kind"] == "payment" and p["on"] in skipped)] log += [post(fee_on, "fee", fee_amt)] log += [post(day(e["moved_to"]), "payment", pay0) for e in moved] # the service contract comes back pro rata, less the cancellation fee c = events["service_contract_canceled"] cancel_on, refund_on = day(c["on"]), day(c["received"]) premium, charge = dec(c["premium"]), dec(c["cancellation_fee"]) if st["policy"]["refund_pro_rated_on"] == "elapsed days": used, of = (cancel_on - sale).days, c["term"]["days"] else: used, of = started_months(sale, cancel_on), c["term"]["months"] gross = r2(premium * (1 - dec(used) / dec(of))) refund = gross - charge def payoff(effective): """The payment holds and the term moves: the contract ends at the first scheduled date where the balance reaches zero, and the schedule extends by one more period if a residual survives the last of them.""" postings = list(log) + ([post(effective, "principal", -refund)] if effective else []) dates = sorted({p["on"] for p in postings if p["kind"] == "payment"}) while True: for i, dt in enumerate(dates): b = replay(sale, apr, conv, postings, through=dt) if b.balance <= 0: return dt, r2(pay0 - b.credit), i + 1, b.charged dates.append(dates[-1] + timedelta(days=every)) postings.append(post(dates[-1], "payment", pay0)) none_, cancel_, receipt_ = payoff(None), payoff(cancel_on), payoff(refund_on) loss = events["total_loss"] loss_on, settle_on, acv = day(loss["on"]), day(loss["insurer_paid"]), dec(loss["acv"]) rep.must("payments: stop at the loss", all(day(e["after"]) == loss_on for e in stops), f"collection stops {', '.join(e['after'] for e in stops)}, loss {loss_on}") live = [p for p in log + [post(cancel_on, "principal", -refund)] if not (p["kind"] == "payment" and p["on"] > loss_on)] at_loss = replay(sale, apr, conv, live, through=loss_on) at_settle = replay(sale, apr, conv, live, through=settle_on) payoff_loss = r2(at_loss.balance + at_loss.accrued) payoff_settle = r2(at_settle.balance + at_settle.accrued) deficiency = r2(payoff_loss - acv) # The waiver names two exclusions: what was added to the balance after origination, # and an installment that fell due and was never collected before the loss. The # second one is the deferred payment, whose new date lands past the loss. excluded_skipped = sum((pay0 for ex in moved if day(ex["on"]) <= loss_on < day(ex["moved_to"])), ZERO) excludes = fee_amt + excluded_skipped waives = deficiency - excludes p11, p13, p14 = (at_settle.at(due(11), "payment"), at_settle.at(due(13), "payment"), at_settle.at(due(14), "payment")) fee_row = at_settle.at(fee_on, "fee") plain = replay(sale, apr, conv, base, through=due(13)).at(due(13), "payment") float_days = count(cancel_on, refund_on, conv) # Payment 15 was collected between the cancellation and the check. Split it as it # was split that day, with the refund not yet credited, and again with the refund # credited as of the cancellation date: same payment, same terms, same day. without = [p for p in live if not (p["kind"] == "principal" and p["amount"] == -refund)] charged15 = replay(sale, apr, conv, without, through=due(15)).at(due(15), "payment") replayed15 = at_settle.at(due(15), "payment") got = { "financed": M(financed), "pay0": M(pay0), "firstDay": first.strftime("%A"), "refundGross": M(gross), "refund": M(refund), "refundUsed": N(used), "refundOf": N(of), "refundCalc": (f"${M(premium)} × (1 − {used} ÷ {of}) = " f"${M(gross)}; ${M(gross)} − ${M(charge)} = ${M(refund)}"), "feeBalance": M(fee_row["balance"]), "feeInterest": M(fee_row["interest"]), "p11Balance": M(p11["balance"]), "p11Interest": M(p11["to_interest"]), "p11Principal": M(p11["to_principal"]), "p13Interest": M(p13["to_interest"]), "p13Principal": M(p13["to_principal"]), "p13Balance": M(p13["balance"]), "p13Accrued1": M(fee_row["interest"]), "p13Accrued2": M(p13["interest"]), "p13Plain": M(plain["to_principal"]), "charged15Interest": M(charged15["to_interest"]), "charged15Principal": M(charged15["to_principal"]), "charged15Balance": M(charged15["balance"]), "replayed15Interest": M(replayed15["to_interest"]), "replayed15Principal": M(replayed15["to_principal"]), "replayed15Balance": M(replayed15["balance"]), "diff15Interest": M(replayed15["to_interest"] - charged15["to_interest"]), "diff15Principal": M(replayed15["to_principal"] - charged15["to_principal"]), "diff15Balance": M(replayed15["balance"] - charged15["balance"]), "p14Interest": M(p14["to_interest"]), "p14Principal": M(p14["to_principal"]), "p14Balance": M(p14["balance"]), "endNoneDate": str(none_[0]), "endNoneFinal": M(none_[1]), "endNoneNo": N(none_[2]), "endNoneInterest": M(none_[3]), "endCancelDate": str(cancel_[0]), "endCancelFinal": M(cancel_[1]), "endCancelNo": N(cancel_[2]), "endCancelInterest": M(cancel_[3]), "endReceiptDate": str(receipt_[0]), "endReceiptFinal": M(receipt_[1]), "endReceiptNo": N(receipt_[2]), "endReceiptInterest": M(receipt_[3]), "float21": M(r2(refund * apr * dec(float_days) / basis(conv))), "floatLife": M(receipt_[1] - cancel_[1]), "earlyBy": N(none_[2] - cancel_[2]), "principalLoss": M(at_loss.balance), "accruedLoss": M(at_loss.accrued), "payoffLoss": M(payoff_loss), "deficiencyLoss": M(deficiency), "principalSettle": M(at_settle.balance), "accruedSettle": M(at_settle.accrued), "payoffSettle": M(payoff_settle), "deficiencySettle": M(r2(payoff_settle - acv)), "lossGap": M(r2(payoff_settle - payoff_loss)), "waiverExcludedFee": M(fee_amt), "waiverExcludedSkipped": M(excluded_skipped), "waiverExcludes": M(excludes), "waiverWaives": M(waives), "remainder": M(payoff_settle - acv - waives), "acv": M(acv), "daily": M(r2(at_loss.balance * apr / basis(conv))), } for i in (0, 1, 2): o = opening[i] got[f"op{i+1}Interest"], got[f"op{i+1}Principal"] = M(o["interest"]), M(o["to_principal"]) got[f"op{i+1}Balance"] = M(o["balance"]) got[f"op{i+1}Days"] = N(count(sale if i == 0 else dues[i - 1], dues[i], conv)) got["op11Interest"], got["op11Principal"] = M(p11["interest"]), M(p11["to_principal"]) got["op11Balance"], got["op11Days"] = M(p11["balance"]), N(p11["days"]) rep.compare(got, st["expected"]) rep.figure("payments: the amount", M(pay0), M(dec(sched["amount"]))) rep.figure("deal: amount financed", M(financed), M(dec(spec["deal"]["amount_financed"]))) # -------------------------------------------------------------------- driver # ----------------------------------------------------------------- self-check def check_selfcheck(spec: dict, rep: Report) -> None: """One contract, one credit, two candidate effective dates. Nothing is read out of the expected block: the due dates come from the contract, the installment from the pricing rule, and the payoff from replaying what the two imply.""" c, ev = spec["contract"], spec["event"] apr, conv = dec(c["apr"]), c["day_count"] signed, financed = day(c["signed"]), dec(c["amount_financed"]) n, refund = int(c["payments"]["count"]), dec(ev["refund"]) dues = [add_months(signed, i) for i in range(1, n + 1)] checked = [day(x) for x in spec["checked_payments"]] pay = annuity(financed, apr / dec(12), n) rep.must("due dates", dues[0] == day(c["payments"]["first"]), f"{n} installments, monthly from {dues[0]} to {dues[-1]}") rep.must("installment", M(pay) == M(dec(c["payments"]["amount"])), f"{M(pay)} priced at APR/12 over {n} dates") rep.must("payments checked", all(dt in dues and dt > day(ev["effective"]) for dt in checked), f"{checked[0]} and {checked[1]} are scheduled and fall after {ev['effective']}") for state in spec["states"]: credited = day(state["policy"]["refund_credited_on"]) if credited not in (day(ev["effective"]), day(ev["money_received"])): raise Missing(f"the refund is credited {credited}, which is neither the date the " f"contract names nor the day the money arrived") rep.state(f"credited {credited} — {state['policy']['reading']}") log = ([post(signed, "principal", financed)] + [post(dt, "payment", pay) for dt in dues] + [post(credited, "principal", -refund)]) dates = list(dues) while True: # payment holds, term moves done = None for i, dt in enumerate(dates): b = replay(signed, apr, conv, log, through=dt) if b.balance <= 0: done = (dt, r2(pay - b.credit), i + 1, b.charged) break if done: break nxt = add_months(dates[-1], 1) log.append(post(nxt, "payment", pay)) dates.append(nxt) last, final, made, life = done book = replay(signed, apr, conv, log, through=last) one, two = book.at(checked[0], "payment"), book.at(checked[1], "payment") rep.compare({ "financed": M(financed), "pay0": M(pay), "refund": M(refund), "firstInterest": M(one["to_interest"]), "firstPrincipal": M(one["to_principal"]), "firstBalance": M(one["balance"]), "secondInterest": M(two["to_interest"]), "secondBalance": M(two["balance"]), "finalAmount": M(final), "lastDate": str(last), "payments": N(made), "interestLife": M(r2(life)), }, state["expected"]) CHECKS = {"dental": check_dental, "bhph": check_bhph, "self-check": check_selfcheck} def main(argv: list) -> int: show_all = "--all" in argv paths = [pathlib.Path(a) for a in argv if not a.startswith("--")] if not paths: paths = [p for p in (HERE / f"{name}.json" for name in CHECKS) if p.exists()] if not paths: raise SystemExit( "Nothing to check. Either put dental.json or bhph.json beside this script, " "or pass one: python3 verify.py path/to/dental.json") rep = Report(show_all) for path in paths: if not path.exists(): raise SystemExit(f"{path} does not exist.") spec = json.loads(path.read_text(encoding="utf-8")) if "states" not in spec or "example" not in spec: raise SystemExit( f"{path} is not one of the published specifications: it has no 'example' and " "'states'. This script reproduces the examples on amortance.com; it is not a " "calculator for an arbitrary book.") # which one it is comes from the file, not from what it happens to be called name = str(spec["example"]) kind = ("dental" if name.startswith("dental") else "self-check" if name.startswith("self-check") else "bhph") print(f"\n{path.name} — {spec['example']}") try: CHECKS[kind](spec, rep) except Missing as e: rep.failed += 1 rep.problems.append(f"{path.name}: {e}") print(f" FAIL {e}") print() if rep.failed: print(f"{rep.failed} of {rep.checked} did not reproduce:") for problem in rep.problems: print(" " + problem) return 1 if not show_all: print("(python3 verify.py --all prints every figure, next to what was published)") print(f"all {rep.figures} published figures reproduced, and the {rep.checked - rep.figures} " f"checks on the logs and the policies behind them pass") return 0 if __name__ == "__main__": sys.exit(main(sys.argv[1:]))