open to work
← All articles

Netting is not saving: what an open simulator shows about instant payment liquidity

November 05, 2026

In Pix, Brazil's instant payment system, every payment settles on its own, immediately. For that, the paying bank must hold a balance at the central bank before each transfer, at any hour of the day or night. In older systems, banks accumulated payments and offset inflows against outflows, the so-called netting, and needed far less idle money.

A study by the Central Bank of Brazil itself (Working Paper 619) showed that, with Pix, banks lost this ability to delay and net, and started holding more liquid assets. Looking at the numbers for any given day, almost everything seems nettable: what a bank pays is close to what it receives. That suggests a tempting idea: hold payments for a few seconds, find the ones that form a cycle (A pays B, B pays C, C pays A) and settle the cycle at once. The customer barely notices and the bank saves liquidity.

I wanted to measure how much of that idea turns into real money. So I built LoopSettle, an open simulator of liquidity in instant payments. This article shows what it found, including the part that knocked down my own hypothesis.

What I built

LoopSettle generates a synthetic day of payments between 200 institutions (large and mid-sized banks, fintechs and payment institutions) calibrated with the open Pix data published by the Central Bank of Brazil: volume, average ticket, concentration by participant and the profile of each hour of the day. It then settles the same flow under several system designs and compares how much money each one requires:

  • real-time gross settlement, like Pix;
  • deferred netting, from every 10 seconds to once a day;
  • atomic micro-cycles: in each short window the engine looks for cycles in the queue and settles each cycle entirely or not at all, with a waiting limit per payment.

The core is written in Rust and runs as a stream (a million payments use a few megabytes of memory). It also compiles to WebAssembly, so there is an interface that runs entirely in the browser. Everything was designed to fit on a laptop with a 4th-generation i3 processor and 4 GB of RAM.

The LoopSettle interface in the browser: the network of participants with the circular component highlighted in green, the micro-cycle controls and the liquidity and delay panels.

I use only public and synthetic data. LoopSettle is an independent project, not affiliated with or endorsed by the Central Bank of Brazil; Pix is a trademark of the BCB.

The result: netting is not saving

With the calibrated scenario, 10 seeds and from 1 to 30 million payments a day:

  • by the "potential" measure, which compares a day's inflows and outflows without looking at the order in time, 85% of the volume looks nettable;
  • micro-cycles of up to 10 seconds settle up to 63% of the value inside cycles, but save about 0.01% of the liquidity (0.0116%, with a 95% confidence interval from 0.008% to 0.015%);
  • netting once a day saves more, but that gain shrinks as volume grows. With 30 million payments a day and the structure between banks fitted to real data from WP 619, it is between 0.2% and 2.8%, depending on how different the banks' daily timing is.

In other words, nettable volume and saved liquidity are different things, and the gap between them is huge.

Why the numbers do not add up: the ceiling

The liquidity a bank needs is set by the worst moment of its day: the point where it has paid out much more than it has received. A mechanism that holds each payment for at most d seconds can only touch the payments that arrived in the d seconds before that peak. Everything earlier has already settled and is not coming back.

That gives a ceiling that holds for any algorithm, however clever: each bank's saving is at most the value it paid in the d seconds before its worst moment. In the calibrated scenario, with 10 seconds, this ceiling is below 1% of the liquidity in every configuration tested, and what micro-cycles actually achieve is a small and stable fraction of it, 3% to 5%. The ceiling answers the most common objection ("maybe a better algorithm would save more"): it would not, because the limit does not depend on the algorithm.

Where the idea works

The same ceiling explains why reordering payments pays off in large-value systems, where there are few payments and they are huge. The closest work to LoopSettle, by McMahon and co-authors with the Bank of Canada, used real data from Canada's large-value system: reordering batches of 70 payments, with about 90 seconds of waiting, saved on average 240 million Canadian dollars a day against arrival order. By my own arithmetic, that is of the order of 1% to 2% of the liquidity banks allocate. In LoopSettle, a stylised large-value scenario has a 10-second ceiling of 13.7%, against 0.76% in retail. The difference lies in the shape of the flow, not in the algorithm.

The constraint that matters is the weekend

The account banks use for Pix can only be funded while the reserves system is open, on business days from 6:30 to 18:30. So banks must set money aside for the nights and for the 59.5 hours of the weekend. In the simulator, that buffer is about 2.7 times the peak of a business day. Even there, netting everything at the end of the weekend would save only 0.4% to 1.0%. The need comes from balances that pile up in one direction, not from money going around in loops.

What the study does not say

  • The data are synthetic, calibrated to public aggregates. Who pays whom, in detail, is not public and remains the model's main assumption.
  • The scale is reduced: each simulated payment stands for about 239 real ones. The fractions are the result; the absolute values do not apply to the real Pix.
  • The remaining question (0.2% or 2.8%) can only be settled with per-participant intraday data, which only the central bank has.

What I learned as an engineer

I work in software quality, and the project was built the way I would like a financial system to be tested:

  • Invariants first. Eight rules that must never break, such as "total money never changes", "no balance goes negative" and "a cycle settles entirely or not at all", are checked by property-based tests with thousands of random cases on every run.
  • Full reproducibility. Every number comes from a command with a fixed seed and is reproduced byte for byte; the code version is recorded next to the result.
  • Kill criteria written in advance. Before running the robustness tests, I wrote down what would knock the result down. One of them knocked down my favourite headline: the first version said the potential was 630 times the realizable saving, but that potential shrinks with volume. The headline went, and the result that stayed is stronger because it survived.

Try it on your own data

Anyone with payment files (a bank, a fintech, a central bank) can run LoopSettle without the data leaving the machine: the file is streamed and the output contains aggregates only. The command loopsettle bound payments.csv computes the liquidity needed and the saving ceiling for each waiting time.

The code is open source (Apache-2.0), with documentation in English and Portuguese, an installation guide from scratch and every result. The next step is the scientific paper. Criticism, and data to test the ceiling, are very welcome.

Let's talk