25 min

Batch rails: deferred net settlement

Batch rails collect payments all day, cancel offsetting flows, and settle only the differences over RTGS at fixed cutoffs; netting is what makes everyday payments cheap.

Where you are. Four lessons have priced immediacy. RTGS settles each payment gross and irrevocably the moment it arrives (lesson 3); each payment therefore needs its full value in reserves at its own moment (lesson 4); skimping on those reserves buys delay along a measurable frontier (lesson 5); and a queue that deadlocks can be freed by offsetting a cycle so it settles almost without liquidity (lesson 6). You also carry an older piece: module 0’s clearing house, where the walking clerks totalled a day’s cheques and paid only the differences. This lesson joins the two. The offsetting trick stops being a rescue and becomes a rail of its own - the batch rail that carries a country’s everyday payments.

The room, industrialised

The Five Bells is gone; the arithmetic outlived the building. What replaced the room is a machine. All day, files of payment instructions stream into a data centre - salaries, rents, supplier invoices, standing orders, none of them urgent, all of them numerous. The machine settles none of them. It reads each instruction, checks it, and appends it to a queue. At half past four the queue closes, and one scheduled job runs the old table’s arithmetic at industrial width: every instruction totalled against every other, one signed number per bank. Then a handful of transfers cross the central bank on the heavy rail from lesson 3, and a whole day of a country’s payments is done, for a small fraction of the cash it appeared to contain. No clerk walked anywhere. Nothing about the idea changed. The tavern room became a batch job, and this lesson has you write it.

The idea in one paragraph

Deferred net settlement - DNS from here on - is module 0’s clearing house made a schedule: hold every payment as a queued promise, let the day accumulate, and at a fixed cutoff total the queue against itself, one net number per bank; then settle only those nets, over the RTGS rail you already built, in central bank money. Customers are paid in full - every deposit leg posts gross - but the traffic between banks shrinks to the day’s differences, a small fraction of its face value. The price is the shape of the promise: everything waits for the cutoff, and until the nets land as reserves, every payment in the queue is a promise that can still fail. Least cash, most waiting, exposure until the batch lands: the exact opposite end of lesson 5’s frontier from RTGS’s no-delay corner.

Queue, cutoff, net, settle

Your ledger has known half of this rail since module 1: the two-phase API. submit validates and queues; settle moves money. RTGS was the discipline of calling settle after every submit. The batch rail is the discipline of not calling it. The day’s payments accumulate as queued promises, and the first assertion of module 1’s project - after submitting, nothing has moved - holds for hours at a stretch, on purpose.

At the cutoff the queue closes and the netting runs, and it is module 0’s arithmetic verbatim: for each bank, everything the day owes it minus everything it owes the day, one signed number, not pairwise but against the whole room. The nets must sum to zero, because every payment is one bank’s debit and another bank’s credit. That conservation law was a checksum in module 0’s exercise; today it becomes an assert in real code.

Then settlement, in one compressed act: each net debtor’s reserve line falls by its difference, each net creditor’s rises, in central bank money, over the rail from lesson 3. On lesson 2’s map this rail splits the day cleanly in two. Clearing - agreeing who owes what - runs from the first file of the morning to the cutoff; settlement is one commit at the end. RTGS fused the two into every single payment; the batch rail pulls them hours apart, by design.

day opens moments later the day's queue payments arrive all day; none settles yet netting offsetting flows cancel cutoff nets only central bank settled in reserves
A batch day: payments accumulate in the queue all morning, the gold cutoff closes it, the netting funnel narrows the whole day, and only a few green net differences settle at the central bank

Wider than the screen; scroll it sideways.

The rescue becomes the timetable

Lesson 6 nearly built this rail without meaning to. The offsetting pass that freed a gridlocked queue was netting run as an emergency: the queue jammed first, and the arithmetic came to the rescue. DNS schedules the jam. It queues on purpose, all day, precisely so the cutoff has the widest possible batch to cancel against - the accumulation window is not an accident to be cleared but the design to be protected. Module 0 made the same point about the clearing room’s fixed hour: flush after every operation and the coalescing never gets the chance to happen.

That is also where the money is. On lesson 5’s frontier the two rails sit at opposite ends: RTGS with ample reserves is the no-delay corner and pays for it in parked liquidity; the batch rail accepts a day’s worth of delay and pays almost nothing in cash, because each bank funds only its net debit. Lesson 4’s liquidity bill scales with the day’s imbalance instead of the day’s volume, and that collapse in parked reserves is what the summary line of this lesson means by cheap.

Customers gross, reserves net

Watch the trick land on the ledger, because this is where DNS stops being arithmetic and becomes postings. At the cutoff, each bank makes one balanced posting: every customer leg of its day at face value - each payer down the full amount, each payee up the full amount - plus a single reserves leg carrying the net. Stylised: Birch’s customers were paid 300 in over the day and paid 240 out, so its posting carries all those deposit legs gross, plus ("reserves", +60). It balances the way lesson 8 of module 1 taught: deposits are IOUs, reserves the asset that pays for absorbing them, and both sides of Birch’s sheet grow by the same 60. The cancelling happens inside the posting, in that one reserves leg.

The central bank then posts once, swinging every bank’s line by its net; the zero-sum is exactly what makes that single posting balance. Notice who can see what. The customer tier ends the day indistinguishable from RTGS - full-value entries, every payee paid whole. The reserve tier barely saw the day at all. Netting is invisible from above and everything from below.

The price of the saving

The waiting is the visible cost, and it is universal: an instruction submitted at 09:01 and one submitted a minute before the cutoff settle at the same instant, so on this rail “paid today” means “in tonight’s batch”. The invisible cost is heavier.

The proof, in numbers

A claim this strong - same outcome, a fraction of the cash - should not rest on prose, and the exercise refuses to let it. code/dns_batch.py builds two identical worlds and seeds both with the same day: 300 payments among six banks. One world settles the day over RTGS, every payment gross, one by one. The other holds the whole day in the queue and settles it as a single netted batch at the cutoff. Then the script asserts, bank by bank, that every customer balance and every reserve balance in the DNS world equals its RTGS twin - reserves end where the nets say, whichever road they took - and prints how much central bank money each road moved. Same destination, different traffic: that is the entire case for batch rails, stated as two asserts and a division.

image/svg+xml Matplotlib v3.11.1, https://matplotlib.org/ RTGS: every payment moves cash DNS: only the nets move 0 10000 20000 30000 40000 50000 60000 70000 80000 central bank money moved shrinkage 16.8x on the same day
cash moved, RTGS versus the netted batch

The figure runs the same comparison over lesson 1’s fixture day, the seeded payment set your workbench built, and prints the ratio on the chart: shrinkage 16.8x, for the identical outcome. Module 0’s hand-worked day of sixteen obligations shrank 10.4x; your exercise’s seeded day lands at 10.6x. Three stylised days, one behaviour: most of any day’s payments are traffic flowing in both directions, so most of the movement cancels.

Review

Queue, cutoff, net, settle

Deferred net settlement is the clearing house made a schedule. Hold every payment as a queued promise, let the day accumulate, and at a fixed cutoff total the queue against itself, producing one net number per bank. Then settle only those nets, over the real- time gross settlement rail you already built, in central bank money. Your ledger has known half of this since the two-phase interface: submit validates and queues, settle moves money. Gross settlement was the discipline of calling settle after every submit. The batch rail is the discipline of not calling it. The netting is arithmetic against the whole room rather than pairwise, and the nets must sum to zero, because every payment is one bank’s debit and another bank’s credit.

Customers gross, reserves net

Customers are paid in full: every deposit leg posts gross, and nobody receives a fraction of what they were sent. What shrinks is the traffic between banks, which falls to the day’s differences, a small fraction of its face value. That is the whole economic point, and it is why everyday payments are cheap. On the map of the day this rail splits clearing from settlement cleanly in two: agreeing who owes what runs from the first file of the morning to the cutoff, and settlement is one compressed commit at the end. Gross settlement fused the two into every single payment; the batch rail pulls them hours apart, by design.

The price of the saving

The saving is paid for in the shape of the promise. Everything waits for the cutoff, and until the nets land as reserves, every payment in the queue is a promise that can still fail. So the first assertion you ever wrote, that after submitting nothing has moved, now holds for hours at a stretch on purpose. Least cash, most waiting, and exposure that lasts until the batch lands: that is the exact opposite corner of the liquidity-delay frontier from where gross settlement sits, which pays the most cash to have no delay and no exposure at all. Neither is correct. They are two ends of one trade-off, and a country usually runs both.

Check yourself

1. The DNS world’s final balances, every customer and every reserve line, are asserted equal to the RTGS world’s. What did netting actually change?

Only the traffic. Both worlds discharge all 300 obligations in full and end in the same state; what differs is how much central bank money moved to get there - 30,930 gross against 2,920 in nets. Netting compresses movement, never owing. Module 0 said that of a table of cheques; the exercise’s asserts now say it of real ledgers.

2. Place the batch rail on lesson 2’s map: when does clearing happen, when does settlement, and over what does settlement run?

Clearing runs all day: validating, queueing and finally totalling the instructions is exactly “agreeing who owes what”. Settlement is one commit at the cutoff, when the nets cross the central bank in reserves - over RTGS, because the nets still need a rail that is instant and irrevocable. DNS is a schedule on top of the heavy rail, not a new kind of money.

3. Each bank arrives at the cutoff needing to cover only its net debit. Restate that in lesson 4’s terms, and say why it makes the rail cheap.

Lesson 4 priced gross settlement: every payment fully funded at its own moment, so the liquidity bill scales with volume and timing. At a cutoff, the day’s inflows fund the day’s outflows, and a bank parks reserves only against its imbalance. Parked reserves are the running cost of a rail; a rail that needs a tenth of them can carry salaries and subscriptions without the liquidity bill swallowing the business.

4. A cutoff posting at one bank has dozens of customer legs and a single reserves leg. Why does it balance, and who can see the netting?

The reserves leg carries exactly the number the customer legs sum to: deposits, the bank’s IOUs, move by the net, and reserves, its assets, move by the same net, so the sheet moves together just as module 1 lesson 8’s payment legs did. The whole compression lives in that one leg. Customers see full-value entries indistinguishable from RTGS; only the reserve tier sees the day shrink.

5. It is 15:00 and a payment to bob sits in the queue. What stands between bob and his money, and how is that different on RTGS?

Everything still has to happen: the cutoff must arrive, every bank in the net must cover its position, and the batch must land. Bob holds a promise whose fate is entangled with banks he has never heard of, because his payment settles as one term inside a total. On RTGS the same payment would have been final seconds after submission. That gap between arrived and final is the next lesson; what happens when a bank inside the net fails is module 3’s.

Do this

Twenty minutes, from module-02-domestic-rails. Open code/dns_batch.py. The fixture seeds one day, 300 payments among six banks, deterministic, so your numbers must match to the digit. build_world gives each bank 3,000 of reserves by endowment and each customer 100,000 of deposits by lending - module 1 lesson 9 earning its keep: the deposits the day spends are loan-created, and the reserves that settle the day are a small fraction of them. Reserves are deliberately ample; scarce-reserve queueing was lessons 5 and 6’s story, and today measures cash moved, not delay.

settle_rtgs_all is written: the baseline pushes every payment through submit-and-settle at full value, the day gross. Your work is net_and_settle, five numbered steps in the TODO(you): one pass over the queue building each bank’s net and its gross customer legs; assert the nets sum to zero; one balanced posting per bank, all its customer legs plus ("reserves", net); one central bank posting swinging every reserve line by its net; then world.assert_world() and return the cash actually moved, the sum of the positive nets. Before your function even runs, the harness checks the held queue - not one balance has moved - and after it, the balance-for-balance comparison with the RTGS world.

python3 code/dns_batch.py

Green is exactly this scoreboard, ending with the final line verbatim:

payments settled            300
gross value settled         30930
cash moved over RTGS        30930
cash moved over DNS         2920
shrinkage                   10.6x
netting cancels offsetting flows and settles only the differences: that is what makes everyday payments cheap

If tier mismatch greets you instead, your banks moved reserves and the central bank never heard about it: the central bank posting is missing or has its signs flipped, the same two-tellings discipline module 1 lesson 8 taught. If the zero-sum assert fires, you added an amount to the payer instead of subtracting it. And if every assert passes but shrinkage prints 1.0x, your reserve legs moved once per payment instead of once per bank: you have rebuilt RTGS, faithfully, inside the function meant to replace it. The completed version is solutions/dns_batch.py; compare after you are green.

What you can now do. You can run a whole day as one netted batch on real ledgers and prove the strongest claim a rail design can make: the same 300 obligations discharged in full, the same final balance on every account in the system, for a tenth of the central bank money moved. You can place both rails on lesson 5’s frontier and say why each end exists - immediacy for the payments that cannot wait, netting for the everyday millions that can - and you can name the price the cheap end pays: a day of waiting, and a web of promises that holds only if every bank covers its net. The module project will give your batch engine two cutoffs and race all three rails on one payment set. Before that, lesson 8 climbs inside the batch day itself - files, returns and reversals, and the customer’s discovery that on a deferred rail, money that has arrived is not yet money that is final.

What you can now do

You can run the whole day as one netted batch and prove it lands the same balances as RTGS.