Where you are. Module 2 ended with the rails simulator: three engines, one Monday, one shared central bank underneath making every disagreement safe. This module took that central bank away and spent fifteen lessons rebuilding the crossing without it, four answers to one border. The relay: message split from money (lesson 2), correspondent hops with a screening gate (lesson 3), nostro and vostro mirroring one account (lesson 4), the pre-funded float (lesson 5), all wired into the chain simulator (lesson 6). The toll: the spread (lesson 7), the basis point that measures it (lesson 8), the stack that falls hardest on small payments (lesson 9). The atomic repair: Herstatt’s cost of paying first (lesson 10), the versus family’s both-or-neither (lesson 11), CLS running it at industrial scale (lesson 12). And the compression: bilateral netting (lesson 13), the multilateral hub (lesson 14), the honest weighing of cash moved against risk concentrated (lesson 15). Every mechanism is runnable. What you have never done is run them together. This project does exactly that: one script, four acts, and every claim the module made restated as an assertion.
One afternoon, both sides of the Pacific
It is mid-morning in New York. At Hudson, Alice’s payment to Kenji in Tokyo joins thirty-nine others heading down the same corridor, most clean, a few about to trip a sanctions screen. Downtown at Liberty, the FX desk has agreed to sell a million dollars to Sakura against yen: two legs of one trade, in two currencies that share no rail. And among the dealers - Alder, Birch, Cedar and Damson, four of module 2’s six banks, now trading currencies with each other - the day has piled up twenty dollar obligations that somebody must settle tonight. Three storeys of one afternoon: remittances on top, an interbank trade in the middle, the dealer day underneath. Everything this module built exists to carry exactly these three loads. The project runs all three, on real ledgers, measured and asserted, in well under a minute.
The idea in one paragraph
A module that answered the border four ways owes you the assembled crossing, and the milestone collects the debt in four acts. Act one drives forty seeded payment intents down the canonical corridor - Hudson to Liberty to Sakura to Fuji, New York to Tokyo - with every toll and delay the module named: fixed fee, lifting fees, the customer’s half of the spread, settlement windows in two time zones, a compliance hold. Act two breaks an FX settlement the Herstatt way, paying the dollar leg before the counterparty fails, then repairs the identical trade with pvp_settle(): loss equal to principal, then loss equal to zero. Act three feeds one seeded day of dealer obligations through the netting engine and prints the three-mode report: gross, bilateral, multilateral. Act four prices the same corridor for a 200 send and a 10,000 send and asserts what lesson 9 promised: the toll falls hardest on the smallest payment. Five headline assertions, all green, in under a minute - and the last thing the project teaches is that green assertions and a good experience are not the same thing.
The brief
Open code/project_border.py. The architecture is the module’s first fact made code: two Worlds, one per currency zone, and no account bridges them, ever. Sakura’s dollars are a deposit at Liberty - its nostro - so when value “crosses” the border, nothing crosses: Fedwire moves dollars into that nostro, a message crosses, and Sakura pays Kenji out of a yen float it parked in Tokyo in advance. The fee schedule and the clocks are constants at the top of the file: an origin fee of 15, a lifting fee of 10 at each correspondent, a 50-basis-point full spread of which the customer’s conversion pays half, Fedwire keeping New York hours, BOJ-NET keeping Tokyo’s, and a screening hold of two days. The numbers are stylised - round enough to read in a diff - but every one of them is a toll or a delay some lesson of this module priced.
Wider than the screen; scroll it sideways.
The figure is the whole build on one canvas. Across the top, the chain: only the dashed message crosses the border, while dollars end their journey in the nostro and yen begin theirs in the float. Bridging the two zones, in purple, the one atomic act in the system: pvp_settle(), both legs or neither. Below, the hub and its four dealers, the compression engine. Each block carries its lesson number, because the project adds no new machinery - only composition.
The corridor, measured
Act one sends the forty intents. Per intent: Hudson takes its fixed fee and Fedwire settles the remainder into Sakura’s nostro inside its window; Liberty lifts its fee off the vostro it hosts, screens the payment - roughly one in ten trips the gate by construction, six of forty on this seed - and messages Sakura; Sakura converts what remains at the bid, lifts its own fee in yen, and pays Kenji from the float inside BOJ-NET’s window. Every hop is a balanced posting, and both Worlds re-assert their invariants when the day is done.
| measure | value |
|---|---|
| intents crossed | 40 |
| screened en route | 6 |
| fastest crossing | 2.0 h |
| median crossing | 16.6 h |
| slowest crossing | 67.8 h |
n = 40 trials · one seeded day down the New York to Tokyo corridor
Read the three elapsed times as three different costs. The fastest, 2.0 hours, is pure mechanism - two half-hour settlement hops plus an hour of correspondent book transfer, zero waiting - and no payment can beat it: the script asserts even the luckiest intent pays every hop’s clock. The median, 16.6 hours, is the windows: Fedwire keeps New York’s day and BOJ-NET keeps Tokyo’s, the overlap is thin, and a payment that settles in New York by afternoon finds Tokyo’s rail hours from opening. The slowest, 67.8 hours, is lesson 3’s warning measured: the two-day compliance hold, worn on top of every window the payment then misses.
Then the money is audited. Alice’s account falls by exactly the face value she sent: every fee downstream comes out of what travels, never out of her twice. Hudson’s equity holds one origin fee per intent, Liberty’s one lifting fee per intent, and everything else that left New York is still in New York - piled up in Sakura’s nostro at Liberty, the accumulation lesson 5 drew. Kenji holds exactly the converted, fee-lightened yen, every unit paid out of the pre-funded float. The dollars never crossed. That audit runs as a block of assertions in check_corridor, and it is the corridor’s ledger truth.
The break and the repair
Act two shrinks to one trade: Liberty sells 1,000,000 dollars to Sakura for 150,000,000 yen, struck at mid. Without protection, the legs settle in their own zones on their own clocks - and between them, the regulator closes Sakura. The dollar leg has already settled with finality, module 2’s meaning of the word: irrevocable. The yen leg now cannot happen; the miniledger itself refuses it, because a bank whose desk was written down in wind-up cannot fund a posting. Liberty’s loss is 1,000,000 - the principal, not a margin - exactly the shape of loss lesson 10 named.
Then the identical trade, the identical failure, through pvp_settle(). The primitive checks that both payers can fund before either leg posts; Sakura is already gone, so it refuses the whole trade and nothing moves in either World. Liberty keeps its principal. The loss is 0.
The figure is the module’s sharpest before-and-after: the same trade, the same default, principal on one side and zero on the other. Nothing was survived, insured or recovered. The dangerous window between the legs was removed, which is lesson 11’s whole design and the reason lesson 12’s CLS exists at scale.
The difference, settled
Act three replays lessons 13 to 15 on one tape: twenty seeded dollar obligations among the four dealers, one day of FX dollar legs.
| mode | payments | cash moved |
|---|---|---|
| gross | 20 | 5,370 |
| bilateral net | 6 | 2,790 |
| multilateral net | 4 | 2,240 |
n = 20 trials · one seeded day of FX dollar legs, four dealer banks
Gross moves the tape’s face value in twenty payments. Bilateral netting pairs off each two-bank relationship into one difference: six payments, 2,790 moved. Multilateral netting hands the whole day to the hub and each dealer settles one signed net: four payments, 2,240 moved, and the nets sum to zero because one bank’s receipt is another’s payment. The script asserts both columns fall strictly, row over row. The compression is real, and lesson 15’s caution rides with it: the cheapest row is also the one where every obligation runs through a single point.
The toll, priced
Act four calls cost_stack() twice. The 200 send pays the fixed fee of 15, two lifting fees totalling 20 and a spread of 0.44 - a total of 35.44, which is 17.72% of the payment. The 10,000 send pays the same 15, the same 20, and a spread of 24.94: 59.94 in all, 0.60%. The fixed parts did not move; only the spread scaled with the notional, exactly as lesson 8 said a proportional toll must. This corridor charges the small sender nearly thirty times the rate it charges the large one, and the script asserts at least ten, so the claim survives any reasonable re-tuning of the stylised fees.
Check yourself
1. Every ledger posting in the corridor is instantaneous, yet the fastest crossing took 2.0 hours and the median 16.6. Where do the hours live?
In the clock, not the ledger. Every intent pays the mechanism’s floor - two half-hour settlement hops plus an hour of book transfer, the 2.0 - and then waits for whichever window is shut: Fedwire keeps New York’s day, BOJ-NET keeps Tokyo’s, and the overlap is thin, so most payments spend most of their crossing parked between rails. The ledgers record value; the clock records when it becomes real for Kenji. Lesson 6 built exactly that separation, and the median is what it outputs.
2. Alice’s account falls by exactly the face amount, yet Kenji receives less than face converted at mid. Account for the difference, leg by leg.
Hudson keeps the fixed origin fee of 15 in its equity. Liberty lifts 10 off the vostro it hosts. Sakura converts at the bid rather than mid - the customer’s 25-basis-point half of the spread - and lifts its own fee in yen. Kenji receives what remains, and check_corridor asserts every one of those numbers to the unit: each toll lessons 7 to 9 described is a posting you can point at. A toll you can locate on a ledger is a toll you can audit; the spread is the one collected silently inside the rate, which is why lesson 7 had to name it.
3. Without PvP the loss is the full principal - not the spread, not a margin. Why total?
Because paying first is an unsecured loan of the whole amount for the gap between the legs. Liberty’s dollar leg settled with finality - irrevocable, module 2’s definition - so when Sakura failed there was nothing to reverse, and nothing left to hold but an unsecured claim in the wind-up. Settlement risk is principal risk: the loss scales with the trade, not with any price move, which is why lesson 10 treats it as different in kind from a bad trade.
4. pvp_settle() checks both payers’ balances before either leg posts. What does the assertion settled is False demonstrate that a successful settlement could not?
The refusal path, which is the safety property itself. A successful run shows only the happy path, and the naked version manages that too whenever nobody fails. The refused trade shows the primitive doing its work: the counterparty is already gone, the check fails, and nothing moves in either World, so Liberty’s loss reads zero. Both-or-neither is only visible when the answer is neither. That is lesson 11’s promise, and the reason lesson 12’s CLS is judged by the failures it refuses, not the days it settles.
5. The netting report’s cash moved falls 5,370 to 2,790 to 2,240, yet lesson 15 refused to call the last row better. What does the multilateral row cost that the gross row does not?
Concentration, and waiting. Gross settles each obligation fully funded, hub-free, final on the spot; nothing depends on anything else. The multilateral row routes every obligation through one hub, and until the nets land, each dealer’s day is entangled with everyone else’s ability to pay - module 2’s deferred-net warning, now standing between currencies. The table prices cash in motion; it does not price the hub’s failure. The two columns you can print trade against a third you cannot: where the risk sits.
Do this
Thirty-five minutes, from module-03-across-borders. Open code/project_border.py: the corridor, the Herstatt pair, the netting report and the cost stack are all written; run_project is yours. Work the TODO(you) in order: build the corridor and send every intent from make_intents() through send_intent(), run check_corridor(), and print the crossing counts and the fastest, slowest and median hours; run both Herstatt functions and assert principal then zero, printing both losses on one line; print netting_report(obligation_tape()) as a three-row table and assert both columns fall strictly; print cost_stack(200) and cost_stack(10_000) as two rows and assert the small percentage is more than ten times the large. Then assert the wall clock stays under sixty seconds and close with the module’s line.
python3 code/project_border.py
As shipped, the starter stops at NotImplementedError before printing anything. Done right, it prints the corridor summary, the one-line Herstatt verdict, the two tables from this lesson and the wall clock, and ends with the line, exactly
the border crossed, the loss removed, the difference settled
Then take the docstring’s invitation: if your own wrappers from lessons 6, 11 and 13 to 15 are importable on sys.path, swap them in for the compact versions here. The measurements must not change - which turns the swap into a differential test of your module against the module’s. The completed version is solutions/project_border.py; compare when you are green.
What you can now do. You can cross the border end to end and prove every clause of the crossing: a corridor whose fees, holds and windows land where the ledger says they must; a Herstatt break whose loss is the principal; a PvP repair whose loss is zero; a netted day that moves less cash in fewer payments the more of it you net; and a toll that you can decompose to the unit for any size of send. You leave module 3 with the correspondent-chain simulator and the netting engine as code, pvp_settle() as a primitive whose loss column reads zero, and both truths of the crossing held honestly: the machinery works, and the experience is still slow and hardest on the smallest sender - the bench modules 6 and 7 will be judged on. First, though, the course changes direction. Module 4 stops moving money and turns to the markets bolted on top of it: what all this plumbing exists to settle.