Back to portfolio

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

  1. Prepare the data

    DuckDB validates the ULB dataset and prepares 30 transaction features. The outcome label is excluded from model inputs.

  2. 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%).

  3. 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
AP = average precision: a ranking measure, not accuracy. Validation is the mean across training folds; test uses calibrated scores. Model selection used validation only.
ModelValidation APTest AP
Dummy baseline0.00190.0012
Logistic regression0.64800.6018
XGBoostSelected0.81360.7411
Logistic regression · weighted0.76070.6879
XGBoost · weighted0.79830.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.