← Back
Home › Systems › Finance and trading

aml-monitoring

Watches a bank's customer activity for patterns that suggest money laundering and turns them into cases investigators can close, escalate or report.

bank AML

Open the aml-monitoring pageaml-monitoring Partitioned commit logdistributed: A topic split into replicated append-only partitions: key hash keeps per-key order, offsets give position, and a high watermark limits readers to fully replicated records.›Windowed stream aggregationdistributed: Group events by event-time window per key, keep one partial aggregate per slice, and emit a window when the watermark passes its end.›BFS and DFSgraphs: The two basic graph traversals; BFS gives hop distances and levels, DFS gives discovery/finish order for cycles, components and topological order.›Graph community detectiongraphs: Louvain/label propagation community detection for rings, fraud clusters and campaignsUnion-findgraphs: Disjoint-set forest with union by size and path compression; merges sets and answers same-set queries in effectively constant time.›Evaluation metricsmachine-learning: Precision, recall, ROC AUC, average precision, log loss, NDCG and pass@k computed correctly, plus paired confidence intervals for comparing models.›Gradient-boosted decision trees(GBDT)machine-learning: Add small regression trees one at a time, each fitted to the loss gradient; histogram splits over 256 bins make it the strongest tabular default.›Isolation forestmachine-learning: Score each record by how few random axis-aligned splits it takes to isolate it in a small forest of random trees; anomalies are few and different, so they isolate fast.›Aho-Corasick multi-patternmatchingsearch-sort: Find all occurrences of many fixed patterns in one pass over the text, in time independent of the pattern count, using a trie with failure links.›Fuzzy string matchingsearch-sort: Edit distance, Jaro-Winkler and trigram indexes for approximate name and address matchingTamper-evident audit logsecurity: An append-only log whose Merkle root, signed per batch, lets anyone verify an entry is present and history was never rewritten, with log-size proofs.›Change data capturestorage: Decode the database's own commit log into row-change events in commit order, with a resume position, so any consumer can mirror the data exactly.›Entity resolutionstorage: Decide which records describe the same real-world thing: block to cut the pair space, score pairs with a probabilistic model, cluster the matches transitively.›Rete rules enginesystems: Match many rules against changing facts by caching partial matches in a shared network, so each change only does the work it actually affects.›Sliding-window counterssystems: Count events in the last W seconds with a ring of sub-bucket counters; the two-bucket weighted estimate gives rate limits in 16 bytes per key.›Fast hash functionsdata-structures: Non-cryptographic hashes built from 64×64→128-bit multiplies and xor-shifts; pick by key size, trust level and whether the hash is persisted.›Primary-backup replicationdistributed: One node orders all writes and ships them to standbys; the acknowledgement rule (none, one, all) sets what a crash loses, and an external arbiter decides who takes over.›Zero-copy I/Onetworking: Move bytes between files, sockets and NICs by passing page references instead of copying through user space: sendfile, splice, MSG_ZEROCOPY, kTLS.›Watermarksdistributed: Event-time progress markers that bound lateness and trigger window resultsHash aggregationstorage: GROUP BY via hash tables with partitioning and spill to diskLSM-treestorage: Buffer writes in memory, flush them as immutable sorted files and merge in the background; turns random writes into sequential I/O.›Hierarchical timing wheelsystems: Timers bucketed by expiry into wheels of slots, coarser wheels for far deadlines: O(1) insert and cancel for millions of timeouts.›Sparse matrices (CSR)numerics: Compressed sparse row stores only nonzeros as row pointers, column indices and values; SpMV streams them once, so it is memory-bandwidth bound.›PRNGs (PCG, xoshiro)numerics: Small, fast, statistically strong non-cryptographic generators: PCG32 and xoshiro256**, with seeding, parallel streams and unbiased ranges.›Segmented logdistributed: Split an append-only log into files named by their first offset, write only the newest, and find any record by binary search plus a sparse index.›Bootstrap confidence intervalsstatistics: Resampling-based confidence intervals for arbitrary statisticsConsumer offsetsdistributed: Each consumer group stores one integer per partition, the next position to read, so a restarted consumer resumes where it left off with at-least-once delivery.›+ 15 moreLeasesdistributed: A lock that expires on its own: bounded-time ownership that survives crashes and partitions, given bounded clock drift and fencing tokens on writes.›Health checkingsystems: Decide per backend whether to route to it using probes and real-traffic errors, with consecutive-count hysteresis, capped ejection and a panic fallback.›d-ary heapdata-structures: Array heap with d children per node (d = 4 typical): half the binary heap's depth, siblings in one cache line, faster priority queues.›Slab allocatorsystems: Pre-carve pages into equal-size slots per size class with an in-place free list: O(1) alloc and free, no external fragmentation, cache-warm reuse.›Fencing tokensdistributed: A number that grows with every lock grant and is checked by the protected resource, so a paused ex-holder's late write is rejected.›+ 12 more+ 6 more+ 1 more

How it is built

Architecture overview

Anti-money-laundering (AML) transaction monitoring is the system through which a bank or payment company watches its own customers' activity for patterns that suggest laundering, and turns what it finds into cases that a human investigator can close, escalate or report. In the United States a bank that decides activity is suspicious files a Suspicious Activity Report with FinCEN (source: FinCEN, filing information); other jurisdictions have their own reporting offices, and industry bodies such as the Wolfsberg Group publish guidance on the risk-based approach. Laundering is rare but missing it is costly, so the system alerts on far more than is truly illicit, and each alert costs investigator time. Real labels are scarce and biased: only what was alerted gets examined, and the UN estimate of 2 to 5 percent of global GDP laundered each year (cited by Altman et al., 2023) coexists with the fact that many laundering transactions in real data are never detected. The system therefore combines explicit rules that a compliance officer can read and defend, statistics over time windows that capture velocity and change of behaviour, graph analysis that links accounts into networks, and machine learning that ranks alerts so investigators see the likeliest first. The one idea that makes it work: detection is a pipeline over an ordered, replayable event log, and every alert, rule version, model version and human decision is recorded as evidence, so that any decision can be explained and any rule change can be tested on history before it goes live.

Parts:

 core banking ──CDC──► commit log (partitioned by customer) ──────────────────────────────┐
 payments, cards                │                                                         │
 KYC changes                    ├─► enrich + entity resolution ─► customer profile store    │ replay
                                │                                     │                   │ for backtests
                                ├─► real-time path: rules (Rete) + window counters ──► alerts
                                ├─► screening: names, narratives vs lists ───────────► alerts
                                └─► batch/near-line: graph links, communities, ML ───► alerts
                                                                                          │
                                       alert store ◄───── dedupe, merge, score ◄──────────┘
                                           │
                                investigator queue ──► case ──► decision: close / escalate / report
                                           │                                   │
                                           └──────► audit log (signed) ◄───────┘
                                       outcomes ──► labels ──► model training and rule tuning

How the parts fit together

Ingestion

Monitoring must see every transaction, in order per customer, and must be able to see it again. Change data capture reads the core system's own commit log into row-change events with a resume position, so monitoring needs no hooks in the posting path and cannot miss a committed transaction. Events land in a Partitioned commit log keyed by customer (or by account owner), because every detection that follows is per-customer state, and a key hash keeps one customer's events ordered while consumers scale out by partition. The log is retained for months: it is what lets a new rule be run against last year's traffic and what lets a crashed consumer resume exactly where it stopped.

The alternative is a nightly batch over the data warehouse: simpler, but a day late and unable to hold an instant payment. Many programs run both: real time for velocity and screening, batch for expensive graph and model scenarios.

Enrichment and identity

An alert is only useful if it concerns the right entity. The same person appears as a customer in two systems, a counterparty typed three different ways, a beneficial owner behind a company. Entity resolution decides which records describe the same real-world party: block records to cut the pair space, score pairs with a probabilistic model, and cluster the matches transitively. The output is a stable party id that every later stage uses, so "this customer sent money to that one" and "these two accounts have the same owner" can be answered at all.

Name matching is approximate: transliteration, nicknames, word order, company suffixes. The block fuzzy-string-matching (no page yet) is what a screening and entity-resolution engine rests on. The enrichment stage also attaches the customer's risk rating, expected activity from onboarding (declared income, business type, countries), and peer group, because most scenarios compare actual behaviour with those expectations.

Real-time detection

Rules. A scenario such as "cash deposits just under a reporting threshold, repeated within a week" or "payment to a high-risk country from a new account" is a rule over facts. A bank has hundreds of them, owned by compliance, changed often and applied to millions of facts per hour. A Rete rules engine caches partial matches in a shared network so each new transaction only does the work it affects, instead of re-evaluating every rule against every fact. A regulator can read a rule, and compliance can change its threshold without a deploy. The open product Jube describes a rule builder that compliance staff can edit (source: jube-home/aml-fraud-transaction-monitoring README); Marble describes transaction monitoring tailored to the business with all data visible to the user (source: checkmarble/marble README).

Velocity. Many scenarios are counts or sums over a recent window: number of transactions to new payees in the last hour, total cash in the last seven days. Sliding-window counters keep a ring of sub-bucket counters per key, which bounds memory per customer and makes reads constant-time. They fit short windows on the hot path.

Aggregates over event time. Longer or cross-entity comparisons ("this customer's monthly outgoing total against their own previous twelve months") need event-time windows with watermarks so late events land in the right window. Windowed stream aggregation groups events per key and window, keeps one partial aggregate per slice and emits a window when the watermark passes its end. The result is written to the customer profile store as features, which both the rules and the models read.

The Rete network and counters keep state, so they sit behind the log: each partition's consumer owns its customers' state and checkpoints its log offset together with the state. Recovery means restoring the checkpoint and replaying from the offset.

Name and list screening

Separate from behaviour, every party and sometimes every payment narrative is checked against sanctions and watchlists. Two problems appear. The first is scanning free text, such as a payment reference or a trade-finance document, for any of tens of thousands of listed names, aliases, vessels or place names. Aho-Corasick multi-pattern matching finds all occurrences of many fixed patterns in one pass, with time independent of the number of patterns, which is the right tool for narrative scanning. The second is party-to-list matching, where names are not identical: this is approximate matching and scoring, with the cutoff a policy choice.

Moov's open Watchman is a worked example: it downloads OFAC and other lists, indexes them in memory, and scores each customer or counterparty from 0 to 1 with a field-level matcher; a matching passport, IMO number, SWIFT/BIC or crypto address scores 1.0 and the cutoff (minMatch) is configured by policy, with debug output for field-level scores (source: moov-io/watchman README). The README reports that on 472,477 labelled records Jaro-Winkler at a minMatch of 0.80 had precision of 0.99; treat such figures as the project's own benchmark on its own data, not a guarantee for yours. Lower cutoffs raise recall and alert volume together.

Network analysis

Individual transactions rarely prove laundering; the shape of a network does. Three structural questions, each a classic graph block.

  • Who is linked through shared attributes? Accounts sharing a phone number, address, device or beneficial owner form clusters. Union-find merges the sets as links arrive and answers same-set queries in effectively constant time, so a streaming job can maintain "linked group" ids cheaply.
  • Where did the money go? Layering moves funds through hops to hide the origin; fan-out then fan-in is a common shape, and money returning to its origin through intermediaries is a cycle. BFS and DFS give hop distances from a flagged account, reachable-set size within N hops, and cycle detection on the transaction graph.
  • Which groups transact mostly among themselves? Tight communities of accounts whose money rarely leaves the group are a typical review target. Block: graph-community-detection (no page yet).

Graph jobs run near-line, on a rolling window of the log, because they scan far more than one event. The output is features and alerts: "two hops from a previously reported account" is a model feature and, in some programs, an alert.

Model scoring

Two model types fit different data.

Unsupervised. Most customers behave like their peers, and a few do not. Isolation forest scores each record by how few random splits it takes to isolate it, so unusual feature vectors (a dormant account suddenly active, a retail account moving company volumes) get high scores without any labels. It finds the unexpected, not necessarily the illicit.

Supervised. Past investigator outcomes become labels (alert closed as false positive, case escalated, report filed). A GBDT model on the profile features ranks new alerts so that the likeliest are seen first, and can suppress the lowest-risk alerts under controlled review. Tabular boosted trees are the sensible default: in the original paper that released the Elliptic bitcoin dataset, a Random Forest outperformed the graph convolutional network that the authors had set out to test (Weber et al., 2019). Two cautions. Labels exist only for what was alerted, so the model inherits the rules' blind spots; keep a reviewed random sample of non-alerted activity to measure what is missing. And the model must be explainable to a regulator: monotonic constraints, feature attributions, reviewed versions.

Real data is not shareable, so Altman et al. (2023) release a synthetic generator and datasets with complete ground-truth labels for comparing models.

Evaluation and tuning

A rule or a model earns its place by what it adds. Evaluation metrics matter more than usual because positives are rare: accuracy is meaningless, and ROC AUC flatters a model on a tiny positive rate. What teams track is precision at the alert budget (of the N alerts investigators can handle per day, how many were worth escalating), recall on known positives, and average precision. Rule changes go through backtesting on the replayed log and a shadow period where the new rule alerts silently, with the results compared to the current rule on both volume and yield.

Case management and evidence

Alerts from all paths flow to a store, which merges duplicates (the same customer from five rules becomes one case with five reasons), applies the model score and assigns a queue. An investigator reviews the case with the customer profile and network view, and records a decision with reasons. Defensibility depends on records: rule and model version, what the investigator saw, the decision, who approved a report. A Tamper-evident audit log signs Merkle roots of those records in batches, so a reviewer or regulator can verify that an entry existed at the time and that history was not rewritten. Decisions feed back as labels and as tuning inputs.

Data and control flow

A transaction through the real-time path

  1. The core commits a payment; change data capture writes an event to the commit log, partitioned by customer.
  2. The partition's consumer enriches it: party ids from entity resolution, customer risk rating, peer group, the counterparty's country.
  3. Counters are updated (payments in the last hour, new payees in the last day) and the transaction and its features are asserted as facts in the Rete network.
  4. Rules that match produce alert candidates with reasons; screening of the counterparty name and the payment narrative runs in parallel and may add candidates.
  5. The state and the log offset are checkpointed together; the candidates are written to the alert store. Time goes here: a few state reads and writes per event, normally well inside the payment's own latency budget, so a high-risk hit can hold a payment before release.

A weekly investigation cycle

  1. A near-line job rebuilds linked-group ids with union-find over the last weeks of shared-attribute links, and runs hop and cycle queries from flagged accounts.
  2. The model job joins profile features with graph features and scores every active customer with isolation forest and the supervised model.
  3. Alert merging combines the new candidates with the existing queue; scores order it.
  4. Investigators work the queue; closure reasons are recorded and signed into the audit log.
  5. Analytics compute precision at the daily alert budget per rule and per model version, and propose threshold changes, which are backtested on the log and shadowed before activation.

Scaling

  1. Alert volume, not compute, is the first limit: investigators are the scarce resource. Levers: better rule thresholds and segmentation by peer group, alert merging per customer, model ranking with a suppression policy under review, and closing the label loop.
  2. State size per partition (counters, windows, profiles) grows with customers. Levers: more partitions, compact counters, keeping long-window aggregates in the profile store instead of in memory.
  3. Rule count raises per-event cost. Levers: the Rete network's sharing of conditions, rule groups by customer segment, and removing rules whose yield is zero.
  4. Graph jobs are the heaviest. Levers: restrict to a rolling window, incremental union-find rather than full recompute, limit hop depth, and run on a separate cluster off the log.
  5. Screening volume rises with every payment and every list update. Levers: partitioned in-memory indexes by name token, a cheap prefilter before expensive scoring, and re-screening only when a list entry or a party changes.

Failure modes

FailureSymptomCauseFix
Alert floodinvestigators cannot keep up, backlog agesthresholds too low, no merging, rule overlapsegment thresholds, merge per customer, remove zero-yield rules, rank by model
Silent blind spotreal laundering found only by outside tiprules cover known typologies only, labels only from alertsreview a random non-alerted sample, add unsupervised scoring, new scenarios from cases
Missed eventa transaction never evaluatedconsumer lost its offset or skipped a bad recordCDC with resume position, replay from checkpoint, dead-letter for bad events
Wrong partyalert attached to a stranger, or two real parties mergedentity-resolution threshold too loosetighter match, human review of merges, keep merge history reversible
Screening false positivescommon names blockedcutoff too low, no secondary identifiersscore IDs and dates alongside names, tune cutoff by policy, whitelist with expiry
Screening false negativeslisted party not caughttransliteration, stale list, alias not indexednormalise and index aliases, refresh lists on publication, test with known variants
Model driftalert precision falls after a product launchbehaviour changed, features shiftedmonitor precision at budget per version, retrain, shadow new models
Graph blow-upgraph job never finishessupernodes (exchange or payroll accounts) link everythingcap degree, treat known hubs separately, bound hop depth
Unexplainable decisioncannot justify why a case was closedmissing rule or model version, no record of what was seenstore versions and inputs with every alert; signed audit log

Real implementations

namewhat it isrepolicencenotes
Marblereal-time decision engine for fraud and AML: transaction monitoring, screening, case investigationcheckmarble/marblesee repoopen source core, on-premise or SaaS (per README)
Jubeopen-source real-time AML and fraud transaction monitoringjube-home/aml-fraud-transaction-monitoringAGPL-3.0rules, velocity, sanctions, supervised and unsupervised ML, case management
Moov Watchmansanctions screening engine over OFAC and other listsmoov-io/watchmanApache-2.0field-level scoring, configurable cutoff, in-memory indexes
IBM synthetic AML datasetsgenerator and datasets for benchmarking AML modelsarXiv:2306.16424see papercomplete ground truth, calibrated to real transactions
Elliptic data setlabelled bitcoin transaction graph used to compare classifiersarXiv:1908.02591see paperthe paper compares LR, RF, MLP and GCN

The alternative: a rules-only system (a vendor scenario library with fixed thresholds) is easy to explain and audit and is where most programs start. It produces high alert volume and cannot find patterns nobody wrote down. Adding scoring and graph features does not replace the rules; the rules remain the part a regulator reads, and models decide what to look at first.

Sources

Similar systems

Share the most building blocks with this one.

In-house systems

Named systems of this kind at large companies, with their paper or write-up.