Skip to main content

madwatch

CI PyPI Python License: MIT

Robust anomaly detection that doesn't panic at paydays.

madwatch scores time-series values with the Modified Z-Score over MAD (Median Absolute Deviation) instead of mean/standard deviation. Medians don't care about your whale transaction: one huge outlier can't inflate the baseline and mask the next real anomaly.

demo

Install

pip install madwatch        # core, numpy only
pip install 'madwatch[cli]' # + CLI, pandas, matplotlib

Quickstart

from madwatch import RollingDetector

det = RollingDetector(window=40, threshold=3.5, min_samples=10)
for value in stream:
    score = det.update(value)
    if score.is_anomaly:
        alert(value, score.z)

Why MAD?

Standard deviation has a design flaw for anomaly detection: the anomaly you are trying to catch is inside the calculation. One spike inflates sigma, the threshold stretches, and the next three real anomalies walk through undetected. MAD is median-based, so a single outlier in the window barely moves the baseline. The 0.6745 constant makes the score comparable to a classic z-score on normal data, so the usual "flag at 3.5" rule still reads naturally.

Longer version: Why MAD instead of standard deviation?

Seasonal baselines

Mondays don't look like Saturdays and 9 AM doesn't look like midnight. Comparing a value against a global baseline produces false alarms at every weekly rhythm. SeasonalBaseline buckets history by day-of-week and/or hour and scores each point against its own bucket:

from madwatch import SeasonalBaseline

sb = SeasonalBaseline(granularity="dow_hour").fit(timestamps, values)
z = sb.score(new_timestamps, new_values)

In production use on financial streams, this combination cut false positives by roughly 60% compared to a naive z-score.

CLI

madwatch data.csv --column amount --window 40 --threshold 3.5 --plot out.png
madwatch data.csv --column amount --timestamp ts --seasonal dow_hour
where                        value         z
2026-02-07T12:00:00         512.00      9.41
1 anomalies in 312 points

API

Name What it does
mad(x) Median Absolute Deviation of an array
modified_zscore(x, scale=0.6745) Per-element robust z-score
RollingDetector(window, threshold, min_samples) Streaming detection over a trailing window
SeasonalBaseline(granularity) Per-bucket (dow/hour) baselines with global fallback

Behavior notes: constant windows (MAD = 0) score z = 0; the detector stays silent until min_samples values have arrived; NaN input raises ValueError (the CLI skips NaN rows with a warning).

Development

uv venv && uv pip install -e '.[dev]'
pytest
ruff check src tests

License

MIT

Metadata

Release files for madwatch 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for madwatch 0.1.0
File Size Uploaded
madwatch-0.1.0.tar.gz 64.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for madwatch 0.1.0
File Interpreter ABI Platform
madwatch-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 72.7 kB

Release files / madwatch-0.1.0.tar.gz

Download URL madwatch-0.1.0.tar.gz
Size 64.1 kB
Tags Source
SHA-256 checksum
How to use checksums
bfecc93a4641c9c62ad53d10c9aa3be993a41890435802420f53a34d9db654da
BLAKE2b-256 checksum
How to use checksums
1bac7998719fc6624b0d0204939dd19adf88023f7297611567beff1a05d28ec7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 12, 2026.

Transparency log

Release files / madwatch-0.1.0-py3-none-any.whl

Download URL madwatch-0.1.0-py3-none-any.whl
Size 8.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
67213c02ecd4cc3bef9c35d996c74122d527c5f02b63d066650e71365a63ade1
BLAKE2b-256 checksum
How to use checksums
e551773b2b43ebaa68fb7e7bcf86371da27f8a83c75bf6c606bb6444cfd06a61
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 12, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page