Data science / Case study
Credit card fraud detection
Finding fraud while keeping manual reviews manageable.
Python · DuckDB · XGBoost · FastAPI
Final test · 42,724 transactions · 52 fraud cases
What the model found
- Fraud detected
- 41 / 52
- Legitimate transactions flagged
- 175
- Fraud missed
- 11
216 transactions sent for review (0.51%). 78.85% of fraud detected.
18.98% of alerts were fraud.
How it works
Prepare the data
DuckDB validates the ULB dataset and prepares 30 transaction features. The outcome label is excluded from model inputs.
Compare models in time order
A dummy baseline, logistic regression and XGBoost are compared using three forward-validation folds. Separate windows handle training, calibration, threshold selection and final testing (60 / 15 / 10 / 15%).
Set a review budget
Unweighted XGBoost was selected by validation average precision. Its review threshold was chosen within a 1% validation review budget, then frozen before testing. FastAPI serves the saved model locally.
Model comparison
| Model | Validation AP | Test AP |
|---|---|---|
| Dummy baseline | 0.0019 | 0.0012 |
| Logistic regression | 0.6480 | 0.6018 |
| XGBoostSelected | 0.8136 | 0.7411 |
| Logistic regression · weighted | 0.7607 | 0.6879 |
| XGBoost · weighted | 0.7983 | 0.7540 |
Limitations
- Most alerts were legitimate transactions. The review budget applies to validation; future workload can change.
- Sigmoid calibration worsened test Brier score and log loss. Its fitting window had only 38 fraud cases.
- The dataset covers two days in 2013, with anonymised features and only 52 test frauds. These results do not establish performance on future banking transactions.
Explore the work
Python, SQL, tests and reproduction instructions. Dataset and trained model are not included.