Why STL is different from DBSCAN

Voidly Atlas already runs a CenDTect-style DBSCAN anomaly detector at /v1/anomaly/dbscan/{cc}. DBSCAN is shape-anomaly detection: it takes a country-day's 12-feature vector and asks how far it sits from any DBSCAN core cluster in the prior 45-day window. That catches days where the signal-type mix or block-rate has an unusual shape.

STL catches a different kind of anomaly. Egypt may always have a high anomaly_rate on Fridays. DBSCAN sees that Friday spike as “normal” for Egypt because it's shape-typical. A non-Friday spike, however, breaks Egypt's own weekly rhythm — STL flags it; DBSCAN often won't.

The two detectors are orthogonal: they catch different failure modes from the same evidence. Running both gives us a wider catchment of weird country-days without trading off precision.

How the model works

For each country with at least 60 days of evidence in the trailing 90-day window:

First-run numbers

Endpoints

Honest caveats

Cron

Daily at 04:45 UTC, after the DBSCAN refresh and before the atlas-digest builder. scripts/build-stl-seasonal-anomaly.py rebuilds the parquet at /opt/voidly-ai/data/stl_seasonal_anomaly.parquet and the sidecar at /opt/voidly-ai/models/stl_seasonal_anomaly_v1.json. The Flask endpoints mtime-cache the parquet so they pick up the new fit on the next request without a service restart.