Roughly half a million card transactions over one quarter, with a fraud team asking a simple question: is today normal? For nine days in June it was not. The monitoring view below found the attack, sized it, and showed exactly when the attackers clock in.
Switch to the attack window and watch the flagged count jump while legitimate volume barely moves. That gap is the whole detection story.
Daily transaction counts looked healthy for all 90 days. The fraud rate is what moved: from a baseline near 25 basis points to over 100 at the peak. Monitoring totals without monitoring rates is how a quarter like this gets signed off as normal.
Flagged transactions during the attack cluster between midnight and 6am, when review staffing is thinnest and cardholders are asleep. Small authorization amounts, high velocity, one merchant category. The heatmap makes the pattern legible in one glance.
Online Retail carried the large majority of attack-window flags while in-person categories held their baseline. That points at a specific gateway integration rather than a broad compromise, which changes who gets the incident ticket.
Alert on fraud rate per hour, not daily totals, with a threshold set from the trailing 30-day baseline. Add velocity rules for sub-dollar authorizations overnight, and route Online Retail gateway anomalies to a named owner. In this dataset those rules would have surfaced the attack on its first night instead of day three.
In Tableau this ships as one workbook: the daily extract on a live connection, LOD expressions for the baseline math, and dashboard actions so clicking the spike filters the flagged-transaction detail.
// trailing baseline, fixed across the view Baseline Fraud Rate := { FIXED : AVG([Fraud Flagged] / [Transactions]) } // how unusual is today? Rate vs Baseline := ([Fraud Flagged] / [Transactions]) / [Baseline Fraud Rate] // drives the alert color on the time series Alert Level := IF [Rate vs Baseline] >= 3 THEN 'Critical' ELSEIF [Rate vs Baseline] >= 1.8 THEN 'Watch' ELSE 'Normal' END
-- hour-of-week fraud density for the heatmap SELECT EXTRACT(dow FROM tx_time) AS weekday, EXTRACT(hour FROM tx_time) AS hour, COUNT(*) FILTER (WHERE is_flagged) AS flagged, COUNT(*) AS transactions FROM card_transactions WHERE tx_time >= now() - INTERVAL '90 days' GROUP BY 1, 2 ORDER BY 1, 2;