← Back to projects

Real-Time Fraud Detection System

Python XGBoost FastAPI Redis Plotly Dash

Live Dashboard

The Problem

Payment fraud costs businesses billions annually. The challenge isn't just building a model that detects fraud. It's building a system that does it in under 50 milliseconds, maintains per-card behavioral history in real-time, explains its decisions, and handles 27:1 class imbalance without drowning in false alarms.

What I Built

A streaming fraud detection system that scores transactions in real-time. A FastAPI inference service loads an XGBoost model, pulls per-card velocity features from Redis, returns a fraud probability with explanation, and updates the card history. A monitoring dashboard shows the system running live with a demo button visitors can click.

Components

The Dataset

IEEE-CIS Fraud Detection dataset from Vesta Corporation (a real payment processor). 590,540 transactions over 182 days. 3.5% fraud rate (27:1 class imbalance). 434 raw features including anonymous pre-engineered signals from Vesta's internal systems.

Data Pipeline

  1. Cleaning: Dropped 214 columns with over 50% missing values. Filled remaining nulls with medians. Label-encoded 9 categorical columns. Result: 220 clean features.
  2. Feature Engineering: Added 13 behavioral features (time-of-day, transaction velocity per card, amount deviation from card average, rapid transaction detection). Removed 51 highly correlated V-columns. Final: 179 features.
  3. Chronological Split: First 80% by time for training, last 20% for testing. No random shuffle. This simulates real deployment where you train on past data and predict future transactions.

Feature Engineering

FeatureWhat It CapturesSignal Strength
amount_deviationHow far this transaction is from the card's typical spendFraud: 0.154 vs legit: -0.006
is_rapid_txnLess than 60 seconds since the card's last transactionFraud: 5.9% vs legit: 3.2%
is_nightTransaction between midnight and 6amFraud: 26.4% vs legit: 24.1%
txn_count_cardTotal transactions on this card in the datasetLow-count cards are riskier
time_since_last_txnSeconds since this card's previous transactionVery short gaps are suspicious

Model Training

Trained XGBoost and LightGBM with class imbalance handling (scale_pos_weight=27.5). Iterated through 4 rounds of hyperparameter tuning:

IterationChangeAUC-PRNotes
1Default params, 300 trees0.499Barely above random
21000 trees, lr=0.02, stronger regularization0.519Hit max iterations
32000 trees, early stopping0.530Still wants more room
43000 trees, lr=0.01, removed correlated features0.531Final. Leaner model.

Results

MetricValue
AUC-PR0.531
AUC-ROC0.917
Recall64.1%
Precision35.5%
Inference latency (p50)2.8ms
Throughput160+ transactions/sec

Redis Feature Store

The key insight: a single transaction in isolation tells you almost nothing. But "this is the 4th transaction in 10 minutes from a card that normally spends $50, and this one is $500" is very suspicious. Redis stores per-card history with sub-millisecond read/write. Features computed at prediction time:

Data expires after 48 hours (TTL). Memory capped at 256MB with LRU eviction.

Explainability

Every prediction comes with the top 5 features driving the decision. The API response includes reasons like "Transaction amount: $5000.00" and "Amount vs card average: 4.2 std devs from normal." This is critical for human reviewers who need to decide whether to block or allow a flagged transaction.

Architecture

Transaction → FastAPI (/predict)
                ↓
         Redis feature store (read card history)
                ↓
         XGBoost inference (2.8ms)
                ↓
         Response: { fraud: true, probability: 0.82, reasons: [...] }
                ↓
         Redis feature store (update card history)

What I'd Improve