WarpCore / Documentation
Liquidity Simulator
SOURCE · docs/specs/liquidity-sim-v1.md · synced from the engineering repo
Liquidity Simulator — Specification v1
Status: Draft for bank treasury review
Tool: warpcore-liqsim
Document version: 2026-04-11
Reference implementation: crates/warpcore-liqsim
1. Abstract
Banks running cross-border settlement today must pre-fund correspondent nostro accounts in every currency in which they transact. BIS surveys put the global total locked in nostro at roughly $27 trillion. This capital earns little or no return. It exists because under T+2 (and even T+0 batch) settlement, there is a window during which a bank’s outflows have committed but its offsetting inflows have not. The bank must therefore hold enough balance to absorb the deepest trough of its intraday net outflow, plus a safety buffer against volatility.
Under WarpCore’s atomic settlement model, every payment is settled in a single Kaspa transaction. Either both legs of a cross-border transfer commit or neither does. The intraday trough collapses because the bank only needs funds to cover the single instantaneous transaction in flight, not the cumulative net of all transactions through a settlement window.
This specification defines a simulator that replays any historical payment flow under both models and reports the difference — the capital unlock a bank would realise by migrating to WarpCore.
2. Non-goals
- The simulator does not value the unlocked capital. A CFO applies their own cost-of-capital to the result (typically 5–8% for a large bank).
- It does not model intraday credit lines, overdraft facilities, or central bank standing facilities, which some banks use to smooth troughs. The number produced is therefore a strict upper bound on the nostro requirement, not a point estimate of actual held balance.
- It does not simulate operational risk, counterparty risk, or FX risk.
3. Why this is honest
- Uses
rust_decimaleverywhere. No floats, no rounding drift. - Rejects inputs that would allow cherry-picking: empty flow sets, null currencies, negative amounts, self-transfers, unsorted timestamps.
- Reports per-participant, per-currency numbers. A skeptic can replay the simulator line-by-line.
- The safety buffer is an explicit parameter rather than a hidden constant. Banks that use 1.50 instead of 1.25 can re-run with their own number.
- Every test vector ships with the source.
4. Input format
A CSV file with five columns and a header row. Comment lines start with
#. Blank lines are skipped.
timestamp,debtor_bic,creditor_bic,currency,amount
2024-01-01T09:00:00Z,DEUTDEFF,CHASUS33,EUR,5000000
2024-01-01T09:00:01Z,CHASUS33,DBSSSGSG,USD,2500000
| Column | Format |
|---|---|
timestamp | RFC3339 UTC; must be monotonically non-decreasing |
debtor_bic | ISO 9362 BIC of the sender |
creditor_bic | ISO 9362 BIC of the receiver (must differ from debtor) |
currency | ISO 4217 alpha-3 code |
amount | Positive decimal; no floats, no currency symbol |
5. Model definitions
5.1 Traditional correspondent banking
For each (BIC, currency) pair, walk the flows in timestamp order and maintain a running cumulative net:
net_{t+1} = net_t - outflow_{t+1} + inflow_{t+1}
The peak outflow is max_{t} |min(net_t, 0)| — the absolute value of
the deepest negative trough. The traditional pre-funding requirement
for that (BIC, currency) is:
traditional = peak_outflow * safety_buffer
Default safety_buffer is 1.25. This matches published BIS guidance on
intraday liquidity buffers.
5.2 WarpCore atomic PvP
Every flow commits atomically. The participant does not need pre-committed liquidity beyond the instantaneous transaction value, which is captured elsewhere (on-demand funding at execution time). For the purpose of this simulator, the atomic pre-funding requirement is zero:
atomic = 0
More sophisticated simulations can substitute a non-zero value representing, e.g., a 15-second pre-commitment window for the largest expected transaction. That refinement lives in v2.
5.3 Capital unlock
unlock = traditional - atomic
6. Output format
6.1 Per-participant requirement
{
"bic": "DBSSSGSG",
"currency": "AED",
"peak_outflow": "18000000",
"traditional_prefunding": "22500000",
"atomic_prefunding": "0",
"unlock": "22500000",
"unlock_report_currency": "6126750.00"
}
6.2 Simulation report
{
"flow_count": 10,
"participant_count": 4,
"currency_count": 3,
"window_start": "2024-01-01T09:00:00Z",
"window_end": "2024-01-01T09:00:09Z",
"safety_buffer": "1.25",
"requirements": [ ... ],
"totals_by_currency": {
"AED": { "traditional_prefunding": "26875000", "atomic_prefunding": "0", "unlock": "26875000" },
"EUR": { "traditional_prefunding": "18437500", "atomic_prefunding": "0", "unlock": "18437500" },
"USD": { "traditional_prefunding": "15625000", "atomic_prefunding": "0", "unlock": "15625000" }
},
"grand_total_report_currency": "42855562.50",
"report_currency": "USD"
}
7. FX conversion
If --report-currency and at least one --rate are supplied, the
simulator converts per-currency unlocks to a single report currency for a
grand total. The conversion is:
unlock_report = unlock_native * rate_native_to_report
FX rates are expressed as decimals (AED=0.2723 means 1 AED = 0.2723
USD when the report currency is USD). The report currency itself has an
implicit 1.00 rate.
8. CLI
warpcore-liqsim <flows.csv> [OPTIONS]
OPTIONS:
--format <human|json> Output format (default: human)
--safety-buffer <DECIMAL> Traditional model buffer (default 1.25)
--report-currency <CCY> Report totals in this currency (requires --rate)
--rate <CCY=DECIMAL> FX rate. Can be given multiple times.
-h, --help Show help.
EXIT CODES:
0 simulation succeeded
1 simulation failed
2 invocation error
9. Worked example
Given the 10-flow CSV in tests/ (4 banks, 3 currencies), run:
warpcore-liqsim flows.csv \
--report-currency USD \
--rate EUR=1.08 \
--rate AED=0.2723 \
--rate USD=1.0
Output (abridged):
Per-currency totals:
CCY TRADITIONAL ATOMIC UNLOCK
AED 26,875,000.00 0 26,875,000.00
EUR 18,437,500.00 0 18,437,500.00
USD 15,625,000.00 0 15,625,000.00
Grand total unlock (USD): 42,855,562.50
Top 5 participant unlocks:
1. DBSSSGSG AED peak=18,000,000 unlock=22,500,000
2. DEUTDEFF EUR peak= 8,750,000 unlock=10,937,500
3. CHASUS33 USD peak= 6,500,000 unlock= 8,125,000
4. DEUTDEFF USD peak= 6,000,000 unlock= 7,500,000
5. NRKEAEAD EUR peak= 6,000,000 unlock= 7,500,000
Translation for a CFO: on a 10-transaction window, four banks would collectively release ~$43M of pre-funded nostro capital. Scale this up to a realistic day (hundreds of thousands of transactions) and the number is in the hundreds of millions.
10. Test vectors
| Test | Asserts |
|---|---|
single_flow_peak_equals_amount | A lone outflow → peak == amount |
netting_reduces_peak_below_gross | Inflows mid-window reduce peak |
later_deeper_trough_wins | Max is of the running net, not per-transaction |
currencies_are_isolated | USD inflows do not offset AED outflows |
safety_buffer_scales_traditional_only | Buffer affects traditional, not atomic |
fx_conversion_produces_report_currency_totals | Cross-currency grand total |
empty_flows_rejected | Empty input errors |
unsorted_flows_rejected | Non-monotonic timestamps error |
negative_amount_rejected | Negative amounts error |
self_transfer_rejected | A→A transfers error |
running_twice_gives_identical_output | Determinism |
csv_parse_roundtrip | CSV parser roundtrips |
csv_with_comments_and_blank_lines | Tolerates comments |
realistic_fx_desk_scenario | 20 flows × 4 banks × 3 ccys |
Count: 14 tests, all passing.
11. Methodology caveats
- The safety buffer is policy, not math. 1.25 is a reasonable default but each bank should use their own number — often derived from their Liquidity Coverage Ratio (LCR) calculation under Basel III.
- Window length matters. Running this over a single hour produces smaller peaks than running it over a full day. For board-level numbers, use at least 30 trading days of flows.
- Cross-currency netting is not modelled. Some banks use FX swaps to reduce cross-currency nostro requirements. The simulator models each currency in isolation, which is the conservative (upper-bound) assumption for capital unlock.
- Central bank standing facilities are not modelled. In the euro area, a bank can borrow from the ECB overnight at the marginal lending rate to cover unexpected outflows. Banks that actively use this facility have a smaller “hard” pre-funding requirement than the simulator reports. For those banks, treat the output as “upper bound on avoidable pre-funding cost”.
12. Reference implementation
- Crate:
warpcore-liqsim - Key files:
src/lib.rs—Simulator,Flow,SimParams,SimReport,FxRates, CSV loadersrc/bin.rs—warpcore-liqsimCLI with human + JSON outputtests/simulation.rs— 14 passing tests
- Build:
cargo build -p warpcore-liqsim --release - Test:
cargo test -p warpcore-liqsim
13. Roadmap
| Version | Planned |
|---|---|
| v1 (this doc) | Per-(BIC, currency) peaks, safety buffer, FX conversion |
| v1.1 | Intraday credit facility modelling, overdraft ceilings |
| v1.2 | Cross-currency FX swap modelling |
| v2 | Monte Carlo volatility simulation over historical variance |
| v3 | Plug-in cost-of-capital model; direct output in ”$ per year saved” |
14. Target audiences
- Bank CFOs and treasurers — top-of-funnel business case
- Bank sponsor search — quantified pitch for correspondent bank partners
- Regulatory sandboxes — demonstrating that WarpCore reduces systemic capital requirements
- Academic peer review — the methodology is transparent enough to submit to the Journal of Financial Market Infrastructures
Authors: WarpCore engineering