Twelve million synthetic bank transfers and two real Bitcoin graphs, every number with its interval
Each transfer looked ordinary. Together they were a laundering ring.
Scoring each account by the accounts it trades with caught - of the
laundering in a 1% alert queue. Scoring it alone caught -.
Launderers move money as a pattern: fanning in from many accounts to one, scattering out
and gathering again, cycling back to where it started. One payment at a time, none of it
looks unusual. This project put three ways of scoring on the same accounts, in the same
time-ordered split, and measured what each one catches: the row alone, the row plus
features from each account's neighbours, and a graph neural network, with a foundation
model beside them that is never trained.
-of laundering transfers caught by the same 1% alert queue, with graph features against the row alone
-precision-recall AUC gained on real Bitcoin wallets, by adding the neighbourhood
-datasets where the graph neural network beat plain graph features
-to fetch a transfer's neighbourhood and score it live, at the median
-what the usual random split reports, against the honest time-ordered one
The real data is a published, anonymised Bitcoin transaction graph; the bank transfers come
from IBM's laundering generator, and only those synthetic accounts are ever drawn here. Every
interval is a 95% bootstrap over time steps or hours, never over single accounts, and no
model was chosen by its test score. See how it's built, in
three diagrams.
The idea, in one ring
One transfer from the ring below, first as the row model sees it, then with the accounts around it. With sound.
The basics, in plain words
Three ideas carry the rest of this page. If you build fraud models for a living, skip ahead to the ring.
A transaction graph
Every account is a point and every payment is an arrow between two points. An account's
neighbours are the accounts it paid or was paid by: one hop. Their neighbours are two hops.
A laundering ring is a small pattern of arrows that looks ordinary one arrow at a time.
Three ways of scoring, and a fourth
R gradient-boosted trees on the account's own row.
G the same trees, given the row plus counts and amounts
summed over its neighbours. N a graph neural network that
passes messages along the arrows. TabFM Google's
tabular foundation model, shown the training rows and asked to score, with no training.
An alert queue, and AUPRC
A bank can investigate only so many accounts, so the useful question is how much of the
fraud lands in the top 1% of scores: the alert queue. Precision-recall AUC (AUPRC) sums up
the whole ranking in one number, from 0 to 1; with fraud this rare, a random guess scores
about the share of fraud, a few percent.
The three datasets, and why each one is here
Elliptic real
203,769 Bitcoin transactions over 49 time steps, about 4,500 labelled illicit by
Elliptic. Its features already summarise each transaction's neighbours, which makes it a
hard test for anything that adds the graph again.
Elliptic++ real
The same network seen as 822,942 wallets and the payments between them, used with the
authors' written permission. Its published wallet features summarise each wallet's whole
life, so they were rebuilt step by step here, so that no wallet is scored with
knowledge of its future.
IBM AML synthetic
Twelve million bank transfers between 1.2 million accounts, from IBM's generator,
which plants eight named laundering shapes and records which transfers are laundering.
The only set where recall can be measured by shape, and the only one drawn here.
How every number was made honest
1Split by time, never at random. Train on the past, test on later accounts the model has never seen, the way a bank meets them.
2No feature reads a label. A registry refuses any neighbourhood feature that touches a fraud label, and a leakage test runs on every build.
3The same items for every arm. One evaluator reads every model's scores without knowing which model wrote them, and refuses to compare runs that scored different items.
4Choose on validation. The hop count and the network design shown for each arm are the ones with the best validation score, never the best test score.
5Intervals over groups. Resample time steps or hours, not accounts, because accounts in the same hour are not independent.
6Time it on a named machine. Latency comes from a rented server with its price and date, never from a laptop.
One laundering ring, as each model saw it
in this model's 1% alert queuescored, missednot scored: before the test days or in the generator's tail
Does the graph help? On every dataset, yes
Each dot is one model's precision-recall AUC on the test period, with its 95% interval. The
grey tick in each group is the row model, the line to beat. Switch to the paired view to see
each model's gain over the row model on exactly the same items, where an interval clear of
zero is a real difference.
IBM's rows leave out the generator's last few days, which are nearly all laundering. For graph features and the graph neural network, the hop count and design shown are the ones with the best validation score.
Which laundering shapes only the graph can see
IBM's generator records which shape each laundering transfer belongs to. For each shape, the
share of its transfers that one shared 1% alert queue reaches, from the row model's dot to the
graph features' dot. The longer the bar, the more the neighbourhood adds.
IBM HI-Small, all test days, 95% intervals in the hover. The shapes are the generator's own, so this measures what it plants, not what a bank sees; about a fifth of the attempts behind each name are too small to show their shape.
How far guilt travels
More hops means looking at neighbours of neighbours, and fetching more of the graph for every
score. AUPRC by hop count; hop 0 is the row alone. The shaded band is the 95% interval.
When the world changes overnight
At Elliptic's time step 43 a large dark market was shut down, and illicit Bitcoin traffic
changed shape. Every model was trained on steps 1 to 34 and then frozen. Each line runs from
its score before the shutdown to its score after it. The dashed lines are allowed to recover:
before each test step, the row model is retrained, or the foundation model's examples are
swapped, on the four latest labelled steps.
The number most papers report is not the one a bank would get
Most published results on these datasets split accounts at random, so the model trains on the
same days it is tested on. This project's split is by time. Each row runs from the honest
score to the random-split score of the same model.
time-ordered split (this project)random split (the usual one)features that could see later days
All test days, IBM included. The random split is run once per model, to publish this gap, and never carries a headline number.
Can an investigator read why it flagged?
A flag an investigator cannot explain is a flag they cannot act on. For every flagged transfer,
each neighbouring account is removed in turn and the transfer rescored through the exported
model; the neighbours whose removal drops the score most are the explanation. The test: how
often do those top neighbours include an account the generator actually used for laundering,
against the same number of neighbours picked at random?
neighbours the explanation picksthe same number picked at random
Would it pass a release gate?
Project 03's release gate decides whether a challenger is at least as good as the model it would
replace, within a stated margin, before anyone ships it. Here the question is the business
one: of the laundering in the test period, how much more does the 1% alert queue reach than
the row model's, allowing a 3-point margin?
Paired over the same illicit items; the gate's own interval and verdict, from its ledger in the repository.
What a live score costs
A score behind a payment decision has a few dozen milliseconds. One call here scores one
transfer from scratch: both accounts' neighbourhoods fetched from the store, the graph
features computed by the batch run's own code, and the score from the exported model. A
thousand calls on a rented server. The bar is the median; the whisker reaches the 99th
percentile.
Where the result is weakest
A result is only as useful as its stated limits. These are the ones that matter most.
Tried and rejected
Scoring an account by how many of its neighbours are already known to be illicit
What to take from it
Measured on four datasets with these models and versions. Not laws of nature, but the right
questions to ask before funding a graph.
Start with graph features, not a graph neural network
Neighbourhood counts and sums fed to the gradient-boosted trees a team already runs gave
the gain on every dataset. The graph neural network never beat them, cost GPU time to
train, and moved more from one random seed to the next.
One hop, from counters, is the live design
On the bank transfers the second hop added nothing measurable and cost several times as
much to fetch. One hop, with each account's own counts kept as running counters, scores in
milliseconds in every store tested.
Ask how the split was made
A random split hides the drift a bank lives with and can make a weak model look
excellent. Any graph fraud result should be tested on accounts from a later period than
the ones it learned from.
Plan for the shift, not just the model
When the market changed, every frozen model collapsed, graph or not. What recovered
was fresh labels: retraining on the latest weeks, or swapping the foundation model's
examples, which costs no training at all.
All results
The headline run of every model on every dataset, as the repository's README tables print them.