bank-fraud / fraud-example.md
root
init
942b115
|
Raw
History Blame Contribute Delete
3.92 kB
# Triggering a fraud alert (demo/test guide)
The model learned one dominant signature from PaySim: **a `TRANSFER` or `CASH_OUT` that drains an account to exactly zero** β€” `amount == old balance` and `new balance == 0`. That pattern alone (regardless of the actual amount, RWF 181 or RWF 275,000) is what pushes the fraud probability toward 1.0 and the risk tier to **high**. `PAYMENT`, `CASH_IN`, and `DEBIT` transactions, and any partial (non-draining) transfer, score **low** almost every time β€” those are the two fraud-prone types identified in `reports/EDA_REPORT.md`.
## 1. Trigger it through the client UI (easiest, no raw PaySim fields needed)
The client-facing form (`/client/transactions/new`) only asks for type, amount, destination account, and date β€” balances are derived server-side from the account's real running balance. To reproduce the fraud pattern:
1. Log in as a client (or have an officer create one via `/officer/clients` first).
2. Note the account's **current balance** shown on `/client` (e.g. RWF 5,000).
3. Go to `/client/transactions/new`.
4. Type: **Transfer** (or **Cash out**)
5. Amount: **the exact current balance** (e.g. `5000.00`) β€” this drains the account to zero, the same signature real PaySim fraud rows have.
6. Destination: anything, e.g. `C_MULE_1`
7. Submit.
Expected result: `risk_tier: "high"`, and the UI shows *"This transaction is being reviewed for your security."* An alert appears immediately on `/officer/alerts` and on the client's own `/client/alerts`.
A **partial** transfer (e.g. RWF 2,000 out of a RWF 5,000 balance) stays low-risk β€” it's the *full drain to zero*, not the size of the transaction, that the model keys on.
## 2. Trigger it directly against `/api/predict` (officer-only, raw PaySim fields)
Useful for testing the model in isolation without going through the account-balance flow. Requires an officer session.
```bash
curl -X POST http://127.0.0.1:8811/api/predict \
-H "Content-Type: application/json" \
-b "session_token=<officer session cookie>" \
-d '{
"step": 5,
"type": "TRANSFER",
"amount": 181.0,
"nameOrig": "CFXFRAUD000A",
"oldbalanceOrg": 181.0,
"newbalanceOrig": 0.0,
"nameDest": "CFXFRAUD000B",
"oldbalanceDest": 0.0,
"newbalanceDest": 0.0
}'
```
Expect `"risk_tier": "high"` with `probability` close to 1.0, and the top SHAP features dominated by the balance-drain signal.
### More fraud-triggering examples (any amount works, as long as it fully drains)
| type | amount | oldbalanceOrg | newbalanceOrig | oldbalanceDest | newbalanceDest |
|---|---|---|---|---|---|
| TRANSFER | 181.00 | 181.00 | 0.00 | 0.00 | 0.00 |
| TRANSFER | 2,500.00 | 2,500.00 | 0.00 | 0.00 | 0.00 |
| TRANSFER | 50,000.00 | 50,000.00 | 0.00 | 0.00 | 0.00 |
| TRANSFER | 275,000.50 | 275,000.50 | 0.00 | 0.00 | 0.00 |
| CASH_OUT | 300.00 | 300.00 | 0.00 | 0.00 | 0.00 |
| CASH_OUT | 15,000.00 | 15,000.00 | 0.00 | 0.00 | 0.00 |
These are drawn straight from `tests/fixtures/labeled_transactions.json` (28 hand-labeled fraud fixtures in total β€” search for `"expected_label": "fraud"`).
## 3. What does *not* trigger fraud (for contrast)
| type | amount | oldbalanceOrg | newbalanceOrig | Expected |
|---|---|---|---|---|
| PAYMENT | 45.00 | 5,000.00 | 4,955.00 | low |
| TRANSFER | 2,000.00 | 5,000.00 | 3,000.00 | low (partial, not a drain) |
| CASH_IN | 1,000.00 | 3,000.00 | 4,000.00 | low |
| DEBIT | 60.00 | 1,200.00 | 1,140.00 | low |
## Reference
- Alert threshold: `FRAUD_ALERT_THRESHOLD=0.5` (probability above this becomes an alert)
- Display risk tiers (independent of the alert threshold): **low** < 0.30, **medium** 0.30–0.70, **high** β‰₯ 0.70
- Full model methodology: `reports/MODEL_COMPARISON_conservative.md`, `reports/EDA_REPORT.md`
- Regenerate/inspect the full fixture set: `python -m scripts.generate_fixtures`, then `tests/fixtures/labeled_transactions.json`