Live demo

15 series in. 4 alerts out.

A marketplace, 60 days of behavioural signals, 15 series across four acquisition channels. On day 45 a scripted ring started registering through one of them. No transaction is labelled, no rule matches, and the chargeback rate has not moved and will not for weeks. Find the attack.

Every number on this page is computed in your browser right now, by the same CUSUM and Bayesian changepoint code that runs in production. Nothing here is a screenshot.

The comparison that matters

Against the rule most teams actually ship

The standard configuration is a percentage or sigma threshold against a trailing mean, evaluated per metric. Run it over the exact same 15 series and count what lands in the channel.

Naive rule
41
alerts sent · alert if |Δ| > 2.5σ vs 7-day mean
Of those, false
21
fired before anything was wrong
This engine
4
alerts sent, all correct
Noise reduction
90%
fewer messages, same catch

The false-alarm count is not a judgement call. The incident was injected at a known index, so every naive alert before that index is wrong by construction.

What the engine found

Detection run

Detection run4 alerts15 series tested · 4 fired · 0 suppressed by BH at FDR=0.05
MetricTrendBaselineNowChangeConfidenceMethod
Device reuse rate
channel=affiliate-b
3.41%14.8%+334%99.9%CUSUM
Velocity score
channel=affiliate-b
1.1492.638+130%99.9%CUSUM
Time to first transaction
channel=affiliate-b
614.59s215.00s−65.0%99.9%CUSUM
Session duration
channel=affiliate-b
213.84s94.14s−56.0%99.9%CUSUM

Show your work

Device reuse rate · channel=affiliate-b

Baseline
3.41%
After changepoint
14.8%
Change
+334%
Confidence
99.9%
CUSUM (Page 1954)
The series60 daysdashed line = baseline mean · red = detected changepoint
1.61%10.0%18.5%changepointJun 29Aug 27
The CUSUM accumulatorsmall deviations stack slowly; a real shift stacks fast
h = 0.624 (decision boundary)3.460

Baseline mean μ = 0.0131 and σ = 0.1560, both computed from the first two thirds of the day-over-day percentage changes. The slack k = 0.0780 is half a sigma, and the decision boundary h = 0.6240 is four. The accumulator runs on percentage changes rather than raw levels so a healthy growing series cannot drift across the boundary on its own.

What the naive rule did on this same series

alert if |Δ| > 2.5σ vs 7-day mean fired 3 times across this one metric, including 2 before anything was actually wrong. The statistical pipeline sent one message, on the day the regime actually changed.

What lands in Slack

One message, with the work already done

F
Fraud DriftAPP#eng-alerts
🔴 Device reuse rate jumped +334%channel=affiliate-b
Baseline
3.41%
Now
14.8%
Confidence
99.9%
Started
Aug 13
Segment
channel=affiliate-b
Changepoint
Aug 13
Corroborating signals
4 of 4 behavioural
Chargeback rate
unchanged
Likely cause · Four independent behavioural signals inside channel=affiliate-b changed regime on the same day, and no other channel moved. Device reuse quadrupled while time to first transaction and session duration both collapsed, which is the signature of scripted registration rather than a traffic-mix change. Chargeback rate for the whole marketplace is flat, as expected: disputes on these transactions have not settled yet.
  1. 1.Step up verification on affiliate-b registrations from Aug 13 forward.
  2. 2.Pull the reused device fingerprints and check for a shared ASN or emulator signature.
  3. 3.Confirm with the affiliate team that no promotion or traffic-source change landed on Aug 13.
  4. 4.Label the flagged cohort now so the supervised model has training data before the chargebacks arrive.
AcknowledgeFalse positiveCUSUM · q < 0.0001 · BH-corrected

The written cause is generated only after the statistics confirm the change. The model never decides whether something is an anomaly — it explains one that has already been established. Getting that order backwards is how these products hallucinate.