We Built a Fake Bank

10 min read Oisin Maher
#Frontier #Culper #Synthetic Data #AI Agents #AML #KYC #Evaluation #Training #Financial Crime #Risk #Architecture #Sandbox
Engraving of a classical bank building in banknote blue and cream, with halftone clouds and skyscrapers behind it

Note to any FINMA readers: Don't worry, we are not taking any deposits!

When we started building Culper, our autonomous risk platform, we ran into a chicken-and-egg problem: we wanted agents that hunt for threats in behavioural data and work a case the way an analyst does (triage the alert, investigate the customer, draft the narrative, propose the disposition), but we couldn't validate the concept without full access to a financial institution.

A typical pilot includes a few hundred accounts and several months of transactions. Names are redacted. Case histories, KYC evidence, and analyst notes are usually missing. That is enough to test an integration or build a cheap LLM wrapper, but nowhere near enough to train or evaluate an agent investigating a real alert.

Public datasets didn't solve it either. Most contain transactions and a fraud label. We needed the customer, their accounts, KYC documents, alerts, case notes, and the final decision, all linked and consistent over time. And a Swiss-flavoured dataset was pretty much impossible to find. Thankfully, I've had the privilege (I didn't always call it that) of seeing roughly three-quarters of Swiss banks from the inside, in a previous life as an auditor, lawyer, and later independent consultant, so I had a good sense of what we needed.

So we built a fake bank. We call it Frontier.

Frontier produces two kinds of data. There is the data a bank creates about itself: parties, accounts, transactions, KYC evidence, alerts, cases, analyst notes. And there is what hits the bank from outside: phishing waves, credential-stuffing runs, intrusions, the events that leave their mark in an institution's security telemetry. A synthetic bank needs both, because a real institution's risk picture is built from both.

The pilot-access problem

Financial crime is a data-privileged domain. Real customer information carries banking-secrecy protection under Article 47 of the Swiss Banking Act (sometimes weakened through general terms and conditions; check your contracts), tax-secrecy protection under separate cantonal rules, and personal-data protection under the revised Swiss Federal Act on Data Protection that came into force in September 2023. On top of that, the internal case files that an autonomous risk system would need to reason over (screening hits, KYC evidence, transaction-monitoring alerts, filed suspicious-activity reports (SARs), supervisor decisions) are treated by every institution we have worked with as second-line-of-defence output that leaves the walls of the bank only under an extraordinary chain of approvals. The same wall exists for any team working on sensitive enterprise or personal data; banking secrecy just makes the Swiss version explicit.

The practical result is that even a warm pilot conversation lands in the same place. The bank grants a slice: a small population, a short window, a subset of fields, no case linkage, all personally identifiable information either hashed or replaced with placeholders. The slice is legitimate and useful for wiring the integration, and it is genuinely how we begin every pilot. But the fields most needed for agent work (the ones behind the customer 360, the outreach log, the suspicious-activity narrative, the analyst's reasoning trace on why an alert was dismissed) are the same fields the bank is least willing to hand over, usually because they sit under the tightest access control inside the institution itself.

Pilot data access
Account list & balances (sample)
granted
Six-month transaction slice
granted
Party names
redacted
Full customer 360 (occupation, source of wealth)
not granted
Beneficial-ownership graph
not granted
KYC evidence documents
not granted
Screening & monitoring alert history
not granted
Case files with analyst notes
not granted
Filed suspicious-activity reports
not granted
Outreach log & advisor notes
not granted
Supervisor sign-offs & dispositions
not granted
granted redacted not granted
Two granted, one redacted, eight off the table. The missing eight are what an agent reasons over.
Real-bank pilots grant enough to prove the integration works. What an agent needs to reason about a case is far larger, and most of it is off the table by default.

The gap is structural, and a stronger contract does not close it. If a Swiss bank agrees to hand you the full analyst-note history on every suspicious-activity case for a random customer sample, it has just made a disclosure that, under a strict reading of Article 47, may itself be reportable. No compliance chief signs that.

Why public datasets stop short

We looked at public datasets next. There are a handful of usable public datasets in the space: the PaySim mobile-money fraud dataset (a synthetic simulator from Edgar Lopez-Rojas's mid-2010s PhD at Blekinge in Sweden) and its retail sibling BankSim (2014, calibrated on aggregated data from a Spanish bank), the IBM AML dataset released in 2023 (also fully synthetic, with a specific typology emphasis), and a scattering of Kaggle credit-card fraud files that are basically PCA-projected transaction features with no semantic surface at all. There are also open FinCEN advisories, annual reports from MROS, the Swiss financial-intelligence unit, and case narratives from the FATF typologies reports. Each one is useful for something. None of them, alone or stitched together, is enough.

The gaps we run into repeatedly:

  • No customer 360. A public transaction file is a table of transfers. Real casework requires knowing what the customer looks like: their occupation, their onboarding-declared source of funds, their KYC risk classification, their beneficial-ownership structure, their previous alert history, and the outreach log with their relationship manager. None of that ships with the public data.
  • No case-and-narrative pairs. The moment an autonomous system has to draft an analyst note or a suspicious-activity narrative, it needs training examples of what a good note looks like on this kind of case at this kind of institution. Public datasets label transactions as fraud or not fraud, without the reasoned analyst note that a human wrote when they decided the same thing.
  • No adversarial breadth. A dataset built around one typology (mobile-money fraud, or trade-based laundering, or elder-abuse patterns) covers that typology and leaves everything else uncovered. Real risk queues are heterogeneous: mule networks, structuring, sanctions evasion, deepfake CFO fraud, invoice manipulation, shadow-fleet payments, romance-scam mule accounts, all in the same week.
  • No temporal coherence. Real accounts have a life. Transactions cluster around salary dates, mortgage payments, tuition cycles, tax-return refunds. A public transaction file is usually cut at a fixed window with no seasonal signal, no relationship-lifecycle signal, no policy-change signal. Models trained on that data learn a shape that does not exist in production.
  • No linkage to the outcome that matters. The signal that determines whether an agent is doing well is the analyst's downstream decision (file the SAR, dismiss, escalate, request outreach), and public datasets almost never encode the disposition.

Public data gets a model past a first-week demo but not past a risk team's first serious evaluation session.

What a synthetic bank actually needs

To be useful, a synthetic bank has to reproduce how a real institution hangs together: the whole graph, transactions included.

Frontier, layer by layer
Population layer
Natural persons (age, occupation, PEP, adverse media)·Legal persons (incorporation, BO graph, activity)
Relationship layer
Relationships (private banking, brokerage, corporate mandate)·Accounts (currency, residency, opening date, life history)·Transactions (MT-type, purpose code, seasonal signal)
Risk-signal layer
KYC evidence (passports, PoA, source-of-funds narratives)·Screening hits (sanctions, PEP, adverse media)·Monitoring alerts (structuring, geography, velocity)
Case layer
Cases (triage state, disposition, prior history)·Analyst notes (reasoning trace, grounded in structured facts)
Reporting layer
Filed SARs (MROS predicate code, narrative, disposition)·Escalations (second-line owner, follow-up)
Every entity in a lower layer references entities above it. Cases carry analyst notes; notes are grounded in structured facts.
Frontier models the whole graph: parties, relationships, accounts, transactions, KYC evidence, screening hits, monitoring alerts, cases, filings, and the analyst notes that link them.

Frontier contains:

  • Parties. Natural and legal persons, with attributes that vary along the axes a KYC officer cares about: nationality, residency, PEP (politically exposed person) status, adverse-media flag, occupation, declared source of wealth. Each attribute has a plausible marginal distribution and, more importantly, plausible joint distributions. A 22-year-old Swiss student declaring CHF 240,000 in salary is a data-quality alert; the generator has to produce that when we want it, and never by accident.
  • Relationships and accounts. A single party often holds multiple relationships (private banking, joint brokerage, a corporate mandate for a company they beneficially own). Each relationship holds one or more accounts. The account has a currency, a residency, an opening date, and a life history.
  • Transactions. Credits and debits with counterparties, correspondent-bank routing, SWIFT MT message types, purpose codes, and the normal seasonal and lifecycle patterns you would see in a real book (payroll clusters, mortgage runs, quarterly VAT payments, holiday-season card activity).
  • KYC evidence. Documents linked to parties, with a plausibly noisy ingestion history: passports that expired and were re-uploaded, proof-of-address letters that were rejected once and re-supplied, source-of-funds attestations with narrative fields.
  • Screening and monitoring output. Name-screening hits against sanctions and PEP lists, transaction-monitoring alerts against the standard scenarios (structuring, rapid movement, high-risk geography, unusual counterparty velocity), and a realistic false-positive rate that varies by scenario.
  • Security telemetry. Authentication events, session histories, device and network fingerprints, and the security-alert stream the bank's own defences produce. It is generated for the same parties and accounts as everything else, so a compromised login connects to the account it endangered and to the case it later produced.
  • Cases and analyst notes. Every alert either goes into a case or does not. Every case resolves. Every resolution carries an analyst note explaining the reasoning. The note is the training signal that a public dataset never gives you.
  • Filed reports. A subset of cases becomes a SAR or a matter escalated to the second line. The synthetic bank models the narrative on the report, the classification against MROS predicate-offence codes, and the follow-up disposition.

Perfect realism on any of these is out of reach, and the limits section below is honest about that. But an agent reasoning about a case with no visible party, no KYC evidence, and no past-alert history is running a monologue over a stub.

The generator: entities, relationships, transactions

Frontier is built by a generator that produces the graph in layers, each seeded from the layer above.

At the top sits a population model. We start from Swiss cantonal demographic distributions (age, occupation, residency status, nationality mix) and layer on the client-mix distributions we have observed in the two pilots that have run long enough to matter. The population is polymorphic by design. The generator can be conditioned to produce a Zürich private-banking population, a Ticino corporate-cross-border population, or a Geneva family-office population, and the resulting distributions differ in exactly the ways an experienced KYC officer would predict.

Banks are not monoliths, and the risk profile is set by the institution type before a single customer walks in. Frontier's reference configuration emulates a G-SIB-scale universal bank, the widest case: retail and private-banking books, corporate and correspondent flows, trade finance, and a markets desk, each with its own typology exposure. From there the generator narrows. A private bank concentrates wealth, PEP exposure, and source-of-wealth questions, and sees few mule networks. A neobank inverts that profile: thin KYC files, high-velocity retail onboarding, and a queue dominated by mules, scams, and account takeovers. A cantonal retail bank sits somewhere else again, heavy on structuring and elder-abuse patterns. Same schema, different distributions, and an agent evaluated on one profile has learned little about the others.

Party generation draws from the population model. For each party the generator draws an attribute vector, then attaches a relationship set. The rare-event distributions (PEP status, adverse-media hits, high-risk-nationality flags) are drawn from calibrated base rates rather than uniformly, because a bank where 3% of parties are PEPs looks nothing like a bank where 0.3% are, and the alert queues diverge in ways that matter for training.

Account and transaction generation runs off a small library of behavioural archetypes. A retired Swiss couple with a private-banking mandate has one archetype (a portfolio account with intermittent fixed-income coupons, a spending account with predictable retail-and-utility patterns, occasional lump-sum transfers around known Swiss tax dates). A 28-year-old Zürich freelance consultant has another (a business account with monthly invoices from a handful of counterparties, a personal account fed by owner draws, with a standing order into the joint mortgage account). A Geneva-based single-family-office holding structure has another still (a small number of very large intra-group transfers, dividend distributions, and quarterly rebalancing). Archetypes are assigned from party attributes, and the transaction stream is generated within the archetype with the appropriate seasonal, weekly, and lifecycle signal.

Behavioural archetypes
JFMAMJJASOND
Retired couple
coupons, retail, tax dates
Freelance consultant
invoices in, mortgage out
Family office
rare large transfers, quarterly rebalancing
Each mark is a generated transaction; size follows amount.
Three archetypes from the library: the same twelve months produce three different transaction rhythms.

KYC evidence, screening output, and monitoring alerts are generated as a downstream function of the party and transaction graph. Screening hits are placed against parties whose attributes make them realistic matches (or realistic false positives) against reference snapshots of the sanctions and PEP lists. Monitoring alerts are generated by running the transaction stream through a battery of monitoring rules mirroring what a real Swiss institution would run. The rules produce alerts on the transactions that trigger them, at a false-positive rate we control per rule.

The generator borrows three invariants from RelGraphFM, our structured-data foundation-model track: sealed ground truth kept apart from the tables a model may ingest, every table materialised as of a query date so no future fact leaks into a training example, and worlds that replay exactly from their seeds. Point-in-time correctness decides whether a sandbox evaluation means anything, and it is the cheapest property to get wrong silently.

Cases, analyst notes, and filed reports are the most expensive layer to generate well, because they carry narrative. Generation is two-stage: a structured decision (dismiss, escalate, file) is drawn from a distribution conditioned on the case's structural features (party risk, transaction pattern, alert type, prior-alert history), and the narrative is generated by a strong LLM conditioned on that structured decision and on a library of style templates that mimic real analyst notes in tone and length. Every narrative is grounded in the structured facts of the case, so a note should not reference a party or transaction that does not exist in the sandbox.

Two caveats belong here. The style templates are written from accumulated memory of what such notes look like, not from any client file; that constraint is the whole point of the exercise. And because the narrative-drafting agent is later fine-tuned on notes an LLM generated, it risks learning Frontier's house style rather than any real institution's. That gap is one of the specific things the shadow-mode comparison in the limits section exists to measure.

Adversarial scenarios: writing the attacks in

The most valuable property of a synthetic bank, which no real-bank pilot can match, is that you can write the attacks into the ground truth on purpose. If you want to know whether an agent catches structuring, you inject structuring; if you want to know whether it catches a specific mule pattern, you inject that pattern; if you want to know whether it catches a novel typology the industry has not yet named, you inject that too.

The current library of adversarial scenarios, drawn from the MROS annual reports, the FATF typology documents, and the cybersecurity threat record of the past three years, breaks down into roughly the categories below.

Adversarial scenario library injected patternknown disposition
  • Structuring
    Split cash deposits under CHF 15,000
    Escalate; file if pattern repeats across 30 days
  • Mule networks
    Rapid pass-through with matched credit/debit
    Freeze account; refer to financial-crime desk
  • Sanctions evasion
    Front-company routing via low-transparency jurisdiction
    Block, screen counterparty, request source-of-funds
  • Deepfake CFO
    Urgent wire from an out-of-band voice instruction
    Verify against genuine CFO channel; block, file
  • Invoice manipulation
    Inflated invoice from newly formed vendor
    Hold, request supporting documentation
  • Shadow-fleet payments
    Insurance-adjacent payments to opaque flag-of-convenience owner
    Escalate; sanctions screen expanded set
  • Romance-scam mule
    Newly onboarded elderly client receives and forwards remittances
    Contact via outreach log; safeguarding referral
  • Trade-based ML
    Over-invoiced goods against commodity benchmarks
    Hold, cross-check against trade-price series
  • PEP onboarding gap
    PEP relative onboarded without an enhanced-due-diligence (EDD) flag
    Re-run EDD; supervisor sign-off before continue
  • Account takeover
    New device, odd-hour login, payment out minutes later
    Kill session, hold payment, verify via registered channel
  • Synthetic identity (in progress)
    Assembled identity, documents wrong only as a set
    Re-verify identity; review accounts sharing attributes
Scenarios compose. A shadow-fleet payment can arrive at an account with prior mule history; the case ground truth reflects the network view.
The current scenario library. Each entry produces cases where the ground-truth outcome (the correct disposition) is known at injection time, so agent behaviour can be scored against it.

Each scenario is a generator plug-in that produces the complete case, not just the transaction. A structuring case injects the split deposits, the account behaviour that surrounds them, the KYC risk profile of the party doing the splitting, and the analyst-note ground truth that says "this is structuring, escalate." A deepfake-CFO case injects the payment instruction, a convincing counterparty, the outreach-log entry that shows the manager tried to verify, and the ground truth that says "verified against genuine CFO channel, no match, block and file."

The harder cases need more than transactions. An account takeover lives in login events, device fingerprints, and payment instructions, so Frontier models those tables too, and an injected takeover carries its evidence across all of them: a new device, a login at an odd hour, a payment instruction minutes later, a beneficiary the account has never paid. Culper's detectors already consume those tables and flag the takeover from exactly that combination. Synthetic identity fraud goes a layer deeper, into the KYC records themselves: a party assembled from real and fabricated attributes whose documents pass onboarding individually and only look wrong as a set. That scenario family is the next one we are writing into the generator.

The same machinery lets us attack the bank itself. We run simulated cyber attacks against Frontier and write their traces into the security telemetry. A credential-stuffing run shows up as a burst of failed logins across hundreds of accounts from a small set of recycled infrastructure. A phishing wave shows up as a cluster of customers who click the same lure and then authenticate from unfamiliar networks. And when a compromise leads to fraud, the payment instruction the attacker files once inside lands in the transaction tables next to everything else. Because the attack is injected, the ground truth is complete: we know which sessions belong to the attacker, which accounts were compromised, and which alerts a perfect defence would have raised.

The attacks that hurt most cross the fraud-cyber boundary. The account takeover begins as a phishing email. The mule network activates hours after an intrusion. The finance team's mailbox shows a reconnaissance login two days before the deepfake CFO call. An agent that reads transactions and security telemetry from the same graph can connect those traces at alert time, the way the fraud team and the security team connect them in the post-mortem.

The scenarios also compose. A shadow-fleet payment can arrive at an account that already has a mule history, and the case ground truth reflects the compounding. A structuring pattern can be split across two related parties who are jointly beneficially owned by a third, and the ground truth reflects the network view. Overlapping scenarios are harder, and more useful: an agent that scores well on isolated scenarios often does poorly when they overlap.

For each scenario we hold out a private test partition. The training partition is available to any model we train on the sandbox. The held-out partition never touches training, and it is what the evaluation harness scores against, so we can tell the difference between a model that has learned the pattern and a model that has memorised the training cases.

What we use Frontier for

We use Frontier for three things: development, training, and evaluation of Culper.

Three loops, one bank
Development
Product & platform engineers
readsFull graph, arbitrary queries, realistic load
writes backNew UI, new endpoints, new agent tools
Training
Model team
readsCase-and-note pairs with per-scenario labels
writes backFine-tuned narrative, triage, outreach agents
Evaluation
Eval harness
readsHeld-out private partition, unambiguous ground truth
writes backPer-population scorecards, regression report
Frontier
The synthetic bank: populations, cases, ground truth, held-out partition
Evaluation feeds development, training data comes from the same generator, dev changes are re-scored against the same partition.
Developers, model trainers, and the evaluation harness all run against the same bank; evaluation scores against a private held-out partition.

Development. Product and platform engineers building Culper (the case queue, the outreach flow, the suspicious-activity narrative editor) need to see the system behave under realistic load. The sandbox gives them a bank they can run local queries against with the shape of a production institution and none of the access-control friction. A new "party network" widget is much easier to design against a graph that actually has interesting networks in it than against ten fake customers with no counterparties.

Training. Some of the sub-agents in the Culper stack are fine-tuned. The narrative-drafting agent, the case-triage classifier, and the outreach-suggestion agent all benefit from supervised fine-tuning on case-and-analyst-note pairs that a public dataset cannot supply. The sandbox produces those pairs in quantity, with per-scenario labels that let us hold out entire typologies for out-of-distribution evaluation.

Evaluation. The evaluation harness picks a private partition of sandbox cases, presents each case to the agent under evaluation (via the same tools and context an agent would have in production), and scores the agent's decisions against the ground-truth disposition. Because the sandbox controls the ground truth, the scoring is unambiguous. Because the sandbox controls the population, we can evaluate against a Swiss retail bank, a private-banking population, and a corporate-cross-border population separately and compare the same agent's behaviour across them.

What synthetic data cannot do (yet)

Frontier still has clear limits, though each is a statement about today. Academic work on synthetic data keeps moving the line: pretraining purely on synthetic tables already transfers to real prediction tasks, and the newest relational corpora mix synthetic and real worlds. Some of the limits below will soften.

Where the synthetic bank ends
Production realityWhat the sandbox covers1234
1
Real customer behaviour
Real weird is wider than synthetic weird, and production can still surprise an agent that scores well in the sandbox.
2
Emerging typologies
The scenario library covers what the industry has documented. A pattern without a name reaches the sandbox second.
3
Calibration drift
Population distributions are a quarterly snapshot of a book that keeps moving.
4
Second-order judgement
Tacit institutional knowledge informs real dispositions, and only some of it can be conditioned into the generator.
Frontier covers what the industry has documented and what the generator can imagine. Production reality is wider on every axis, and the four limits below live in the gap.

Ground truth on real customer behaviour. No matter how carefully we calibrate the generator, real customers do things the generator did not anticipate. The distribution of "real weird" is wider than the distribution of "synthetic weird" that any model can produce. An agent that scores well on the sandbox can still be surprised on production data. The response we settled on is to pair the sandbox score with a shadow-mode deployment on real data (agent runs alongside the human analyst, decisions are logged, decisions are compared) as soon as a pilot bank is willing to enable it.

Emerging typologies. The scenario library covers what the industry has already documented. It cannot cover typologies that have yet to be named. When a new fraud pattern shows up in production, the sandbox learns about it second. A sandbox score is a floor an agent must clear, and no forecast of what production will throw at it.

Calibration drift. The population distributions we use are as of a given calendar quarter. Real institutions drift: their client mix changes, regulatory guidance changes, monitoring rules change. The sandbox needs recalibration on a schedule, and even then it is a snapshot rather than a living book. We recalibrate quarterly, folding in each MROS annual report and new guidance from FINMA, the Swiss regulator, as they land.

Second-order judgement. A skilled analyst's decision on a case is informed by a lot of tacit institutional knowledge that never lands in the sandbox: which product manager tends to over-onboard, which cantonal prosecutor is currently investigating what, which correspondent bank is on internal watch. Some of that knowledge can be simulated by conditioning the generator, and some of it genuinely cannot.

What we are working on

Adversarial evaluation. As autonomous risk systems get better, the question that matters shifts from "can the agent classify the easy cases" to "can the agent hold up when the case is deliberately shaped to fool it." No bank signs off on red-teaming a production agent against its live queue, so the red-team loop runs on the synthetic bank. We are expanding the adversarial part of the scenario library and building a harness where a red-teamer sketches a novel typology and the generator produces a family of variations on it.

Training data for structured-data foundation models. Tabular foundation models are pretrained on synthetic data as a matter of course: TabPFN v2, published in Nature in 2025, was pretrained on roughly 130 million synthetic datasets; Google's TabFM, released in June 2026, was pretrained entirely on synthetic tables generated from structural causal models; and KumoRFM-2 pretrains on relational databases with a corpus that mixes synthetic and real data while preserving temporal consistency. RelGraphFM, our structured-data foundation-model track, follows the same recipe for risk prediction, with Frontier as one of its worlds.

We think autonomous risk systems will keep improving. LLMs are getting better at investigation, structured-data foundation models are getting better at prediction, and the training stack around both is becoming more autonomous itself. Together they will change how institutions handle risk. We don't have all the answers yet, and this post is part of working them out in the open.

If you are working on autonomous risk systems or synthetic data generation, we would love to hear from you. Write to oisin@bollwerk.ai.