> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.nvidia.com/sdgm/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.nvidia.com/sdgm/_mcp/server.

# Money Laundering Detection

## Solution Background and Business Value

Financial institutions and law enforcement agencies need to detect and prevent money laundering before illicit funds leave an account.
Undetected laundering exposes institutions to legal liability and enables continued criminal activity.
The challenge is compounded in cryptocurrency transactions, where users can create multiple accounts easily and single transactions may involve multiple parties.

Graph Neural Networks (GNNs) are effective for identifying suspicious patterns in transaction networks that traditional fraud detection methods miss.

This document outlines how to:

* **Structure your data** for money laundering detection.

* **Train a classifier using Kumo AI**.

* **Deploy the model** in real-world fraud detection systems.

## Data Requirements and Schema

**Core Tables**

1. **Accounts Table**

   * Stores account details.

   * Key attributes:

     * `account_id`: Unique identifier for each account.

     * **Optional:** Location, phone number, creation timestamp, account type, risk score.

2. **Transactions Table**

   * Records all transactions (deposits, withdrawals, transfers).

   * Key attributes:

     * `transaction_id`: Unique identifier.

     * `timestamp`: Transaction time.

     * `amount`: Transaction value.

3. **Inputs Table**

   * Tracks the **source accounts** for each transaction.

   * Key attributes:

     * `transaction_id`: Links to a transaction.

     * `account_id`: Links to the sender's account.

     * `timestamp`: Time of transaction.

4. **Outputs Table**

   * Tracks the **destination accounts** for each transaction.

   * Key attributes:

     * `transaction_id`: Links to a transaction.

     * `account_id`: Links to the receiver's account.

     * `timestamp`: Time of transaction.

5. **Reports Table**

   * Tracks accounts reported for money laundering.

   * Key attributes:

     * `account_id`: Links to an account.

     * `timestamp`: Time of report.

     * **Optional:** Reason, severity, reporting entity.

**Entity Relationship Diagram (ERD)**

```mermaid
erDiagram
    ACCOUNTS {
        INT account_id PK
        STRING location
        STRING account_type
        TIMESTAMP creation_timestamp
    }
    
    TRANSACTIONS {
        INT transaction_id PK
        TIMESTAMP timestamp
        FLOAT amount
    }
    
    INPUTS {
        INT input_id PK
        INT transaction_id FK
        INT account_id FK
        TIMESTAMP timestamp
    }
    
    OUTPUTS {
        INT output_id PK
        INT transaction_id FK
        INT account_id FK
        TIMESTAMP timestamp
    }
    
    REPORTS {
        INT report_id PK
        INT account_id FK
        TIMESTAMP timestamp
    }

    ACCOUNTS ||--o{ INPUTS : "funds sent"
    ACCOUNTS ||--o{ OUTPUTS : "funds received"
    TRANSACTIONS ||--o{ INPUTS : "has"
    TRANSACTIONS ||--o{ OUTPUTS : "has"
    ACCOUNTS ||--o{ REPORTS : "reported"
```

## Predictive Queries

Detecting money laundering requires predicting fraud risk as soon as funds enter an account.

**Money Laundering Prediction**

This model predicts the **probability that an account will be reported for money laundering in the next N days**:

```pql
PREDICT COUNT(reports.*, 0, N, days ) > 0
FOR EACH accounts.account_id
WHERE COUNT(inputs.*, -1, 0, days) > 0
```

**Different Time Horizons**

To detect different **fraud patterns**, train models for various time windows:

```pql
// Likely to be reported within 10 days
PREDICT COUNT(reports.*, 0, 10, days ) > 0
FOR EACH accounts.account_id
WHERE COUNT(inputs.*, -1, 0, days) > 0

// Likely to be reported in 10-30 days
PREDICT COUNT(reports.*, 10, 30, days ) > 0
FOR EACH accounts.account_id
WHERE COUNT(inputs.*, -1, 0, days) > 0

// Likely to be reported in 30-90 days
PREDICT COUNT(reports.*, 30, 90, days ) > 0
FOR EACH accounts.account_id
WHERE COUNT(inputs.*, -1, 0, days) > 0
```

## Building models in Kumo Fine-Tune SDK

**1. Initialize the Kumo Fine-Tune SDK**

```python
import kumoai as kumo

kumo.init(url="https://<customer_id>.kumoai.cloud/api", api_key=API_KEY)
```

**2. Connect data**

```python
connector = kumo.S3Connector("s3://your-dataset-location/")
```

**3. Select tables**

```python
accounts = kumo.Table.from_source_table(
    source_table=connector.table('accounts'),
    primary_key='account_id',
).infer_metadata()

transactions = kumo.Table.from_source_table(
    source_table=connector.table('transactions'),
    time_column='timestamp',
).infer_metadata()

inputs = kumo.Table.from_source_table(
    source_table=connector.table('inputs'),
    time_column='timestamp',
).infer_metadata()

outputs = kumo.Table.from_source_table(
    source_table=connector.table('outputs'),
    time_column='timestamp',
).infer_metadata()

reports = kumo.Table.from_source_table(
    source_table=connector.table('reports'),
    time_column='timestamp',
).infer_metadata()
```

**4. Define graph schema**

```python
graph = kumo.Graph(
    tables={
        'accounts': accounts,
        'transactions': transactions,
        'inputs': inputs,
        'outputs': outputs,
        'reports': reports,
    },
    edges=[
        dict(src_table='inputs', fkey='transaction_id', dst_table='transactions'),
        dict(src_table='inputs', fkey='account_id', dst_table='accounts'),
        dict(src_table='outputs', fkey='transaction_id', dst_table='transactions'),
        dict(src_table='outputs', fkey='account_id', dst_table='accounts'),
        dict(src_table='reports', fkey='account_id', dst_table='accounts'),
    ],
)

graph.validate(verbose=True)
```

**5. Train the model**

```python
pquery = kumo.PredictiveQuery(
    graph=graph,
    query="PREDICT COUNT(reports.*, 0, N, days ) > 0 FOR EACH accounts.account_id"
)
pquery.validate(verbose=True)

model_plan = pquery.suggest_model_plan()
trainer = kumo.Trainer(model_plan)
training_job = trainer.fit(
    graph=graph,
    train_table=pquery.generate_training_table(non_blocking=True),
    non_blocking=False,
)
print(f"Training metrics: {training_job.metrics()}")
```