#!/usr/bin/env -S uv run python """Reperform Linden Peak's receivable aging, note, and transfer schedules. Run directly with uv; this script uses only the Python standard library: uv run python linden-peak-aging-note-and-transfer.py uv run python linden-peak-aging-note-and-transfer.py --forecast-shock 0.02 """ from __future__ import annotations import argparse def money(amount: float) -> str: """Format dollars with commas and accounting-style negatives.""" rounded = round(amount) if rounded < 0: return f"(${abs(rounded):,.0f})" if rounded == 0: return "—" return f"${rounded:,.0f}" def percent(rate: float) -> str: """Format a decimal rate as a percentage.""" return f"{rate:.1%}" def build_close(forecast_shock: float) -> dict[str, object]: opening_gross = 300_000.0 credit_sales = 900_000.0 collections = 850_000.0 writeoffs = 20_000.0 recoveries = 1_000.0 opening_allowance = 9_000.0 ending_gross = opening_gross + credit_sales - collections - writeoffs pools = { "Current": (180_000.0, 0.005, 0.0, 0.0), "1–30 days": (80_000.0, 0.015, 0.0, 0.0), "31–60 days": (40_000.0, 0.04, 0.01, 0.01), "61+ days": (30_000.0, 0.12, 0.03, 0.05 + forecast_shock), } pool_results: dict[str, dict[str, float]] = {} for name, (exposure, historical, current, forecast) in pools.items(): adjusted_rate = historical + current + forecast if not 0 <= adjusted_rate <= 1: raise ValueError(f"{name} adjusted rate must stay between 0% and 100%") pool_results[name] = { "exposure": exposure, "historical": historical, "current": current, "forecast": forecast, "adjusted_rate": adjusted_rate, "expected_loss": exposure * adjusted_rate, } ending_allowance = sum(row["expected_loss"] for row in pool_results.values()) credit_loss_expense = ending_allowance - opening_allowance + writeoffs - recoveries ending_net = ending_gross - ending_allowance face = 121_000.0 market_rate = 0.10 periods = 2 carrying = face / (1 + market_rate) ** periods note_rows: list[dict[str, float]] = [] for period_number in range(1, periods + 1): opening = carrying interest = opening * market_rate carrying = opening + interest note_rows.append( { "period": float(period_number), "opening": opening, "interest": interest, "ending": carrying, } ) sale = { "gross": 100_000.0, "allowance": 5_000.0, "cash": 92_000.0, "holdback": 5_000.0, "recourse": 2_000.0, "costs": 1_000.0, } sale["net_carrying"] = sale["gross"] - sale["allowance"] sale["recognized_consideration"] = ( sale["cash"] + sale["holdback"] - sale["recourse"] - sale["costs"] ) sale["result"] = sale["recognized_consideration"] - sale["net_carrying"] borrowing = {"pledged_receivables": 150_000.0, "allowance": 7_500.0, "cash": 120_000.0} return { "opening_gross": opening_gross, "credit_sales": credit_sales, "collections": collections, "writeoffs": writeoffs, "recoveries": recoveries, "opening_allowance": opening_allowance, "ending_gross": ending_gross, "pools": pool_results, "ending_allowance": ending_allowance, "credit_loss_expense": credit_loss_expense, "ending_net": ending_net, "note_initial": face / (1 + market_rate) ** periods, "note_face": face, "note_rows": note_rows, "sale": sale, "borrowing": borrowing, } def print_close(close: dict[str, object]) -> None: pools = close["pools"] note_rows = close["note_rows"] sale = close["sale"] borrowing = close["borrowing"] assert isinstance(pools, dict) assert isinstance(note_rows, list) assert isinstance(sale, dict) assert isinstance(borrowing, dict) print("LINDEN PEAK — RECEIVABLES CLOSE TRACE") print("Scope: arithmetic under supplied pool, yield, and transfer judgments") print() print("GROSS RECEIVABLE ROLLFORWARD") print(f" Opening gross receivables {money(close['opening_gross']):>14}") print(f" Credit sales {money(close['credit_sales']):>14}") print(f" Cash collections {money(-float(close['collections'])):>14}") print(f" Writeoffs {money(-float(close['writeoffs'])):>14}") print(f" Ending gross receivables {money(close['ending_gross']):>14}") print() print("AGING AND EXPECTED LOSS") print(f" {'Pool':16} {'Exposure':>14} {'Adjusted':>10} {'Expected loss':>16}") for name, row in pools.items(): print( f" {name:16} {money(row['exposure']):>14} " f"{percent(row['adjusted_rate']):>10} {money(row['expected_loss']):>16}" ) print() print("ALLOWANCE ROLLFORWARD") print(f" Opening allowance {money(close['opening_allowance']):>14}") print(f" Credit-loss expense {money(close['credit_loss_expense']):>14}") print(f" Writeoffs {money(-float(close['writeoffs'])):>14}") print(f" Recoveries {money(close['recoveries']):>14}") print(f" Ending allowance {money(close['ending_allowance']):>14}") print(f" Ending net receivables {money(close['ending_net']):>14}") print() print("ZERO-COUPON NOTE") print(f" Initial carrying amount {money(close['note_initial']):>14}") for row in note_rows: period_number = int(row["period"]) print( f" Year {period_number} interest on {money(row['opening']):>10} " f"{money(row['interest']):>12} ending {money(row['ending']):>12}" ) print() print("STIPULATED TRANSFER PATHS") print(f" Sale net carrying amount {money(sale['net_carrying']):>14}") print(f" Sale recognized consideration {money(sale['recognized_consideration']):>14}") print(f" Sale result {money(sale['result']):>14}") print(f" Secured-borrowing cash {money(borrowing['cash']):>14}") print(f" Receivables derecognized {money(0):>14}") checks = { "Aging exposure to gross receivables": sum(row["exposure"] for row in pools.values()) - float(close["ending_gross"]), "Note maturity to face": note_rows[-1]["ending"] - float(close["note_face"]), "Sale result to stipulated loss": sale["result"] + 1_000.0, "Borrowing sale result": 0.0, } print() print("RECONCILIATION CHECKS") for label, difference in checks.items(): status = "PASS" if abs(difference) < 0.005 else "CHECK" print(f" {status:5} {label:38} {money(difference):>12}") print() print("A PASS verifies arithmetic only. It does not approve pool design, loss") print("evidence, market yield, legal isolation, transfer control, or disclosure.") def main() -> None: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument( "--forecast-shock", type=float, default=0.0, help="Additional decimal forecast rate for the 61+ pool; default: 0", ) args = parser.parse_args() try: close = build_close(args.forecast_shock) except ValueError as error: parser.error(str(error)) print_close(close) if __name__ == "__main__": main()