[#45] Fix ACCT DIVERGENCE when acquirer inherits standing bond offers #50
No reviewers
Labels
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Winnie/bankwars!50
Loading…
Reference in a new issue
No description provided.
Delete branch "45-acct"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
"Fixes #45.\n\n## Root cause\n\n
report()computed the income-statement line asDepInterest = b.Deposits * b.DepRate\u2014 a fresh recomputation against the post-acquisition deposit balance. On the turn a bank is acquired:\n\n1.financePhasepays the failed bank's own deposit interest out of the failed bank's cash before the auction runs.\n2.bankrupt()transfers the failed bank's deposits to the acquirer.\n3.report()then charges the acquirer interest on the acquired deposits too.\n\nThe interest was therefore double-counted: once inside the bargain purchase gain (the failed bank's cash, which already had the interest deducted, feedsfailedNet), and once again as the acquirer'sDepInterest. The overcharge is exactlyfailed.Deposits \u00d7 acquirer.DepRate\u2014 the 531 in the smoke log (dEq -2042 netInc -2573 diff 531), and 250 in the unit test. The standing-bond-offer handover itself is accounting-neutral (committed cash \u2194BondComm, fills \u2194PendingBondIncome/BondInvat face, expiries \u2194BondsReturned\u2014 all verified); the bug fires on every acquisition where the failed bank has deposits and the acquirer posts a nonzero rate, which is why only the bond-heavy smoke (which drives banks to insolvency) exposed it.\n\n## Fix\n\nOne line infinance.go: the income statement books the interest actually paid this turn (b.CF.DepInterest, recorded byfinancePhasebefore any acquisition), matching the statement of cash flows and restoring the identityequity change == net incomeexactly.\n\n## Regression test\n\nTestAcquisitionAccounting(acquisition_accounting_test.go) constructs the issue scenario deterministically: bank A holds a standing bond offer (66,500 committed cash) plus 60,000 bond assets and 30,000 bond debt, goes insolvent (rate spike makes interest + op expense uncovered), and bank B acquires the position at the Vickrey second price (0). The test then runs the inherited books through their full lifecycle \u2014 offer aging and expiry (committed cash returns), bond asset maturity, bond debt repayment \u2014 assertingequity change == net incomeexactly for every alive bank on every turn, plus zeroDIVERGENCElog lines.\n\n- Before fix: FAILS \u2014 turn 2 diff 250 (exactlyfailed.Deposits \u00d7 acquirer.DepRate).\n- After fix: PASSES \u2014 diff 0.00 through turn 10.\n\n## Verification\n\n-go build -o bankwars .\u2014 clean\n-go vet ./...\u2014 clean\n-go test .\u2014 all pass\n- Spectate smoke, seed 33, 6 players, deposit-bond-spec \u00d72 + 4 mains (/tmp/cfg.json): 0 DIVERGENCE lines (ACCT and CF), 0 AI hook errors, 3 acquisitions exercised (Bank 1 acquires Bank 3 for 0, Bank 2 acquires Bank 1 for 0, Bank 2 acquires Bank 6 for 0), game completes with 3 survivors. The same smoke on the unfixed engine logs 3ACCT DIVERGENCElines, including the exact original signatureBank 1 turn 15 ... diff 531.\n"@Winnie Can you explain this in simple terms?
In plain terms:
Think of each bank's books as a checkbook that has to balance. Every turn, the game prints an income statement and then checks one rule: "the change in the bank's net worth must exactly equal the profit it reported." If those two numbers don't match, something got counted twice or not at all. This PR fixes a case where interest got counted twice.
The bug: when a bank fails and another bank buys its corpse at auction, the buyer takes over the dead bank's depositors, and from then on the buyer pays interest on those deposits. Fine. But on the day of the takeover, the accounting code looked at the buyer's balance sheet, saw the freshly acquired deposits, and charged the buyer interest on them for that day. The dead bank had already paid that day's interest itself before it failed. So the same interest was paid twice, and the books were off by exactly that amount.
The fix is one line: the income statement now reports the interest the bank actually paid that turn (which the game already records when the cash leaves), instead of recomputing it from the current deposit balance.
How I'm sure: I wrote a test that re-creates the exact scenario, takeover and all. Before the fix, the books were off by 250. After the fix, off by zero, every turn. And the original bug report's exact game now runs clean.
Why it mattered in practice: every time a takeover happened involving depositors, the buyer's reported profit was wrong. In games with lots of failures, those small errors could add up and make a bank look healthier or sicker than it really was.