04-tooling / xmr-charts

readme

Mon Apr 20 2026 20:00:00 GMT-0400 (Eastern Daylight Time) ·tool ·status: v2
xmrspcwheelerchartingpython

XmR Chart Module — Wheeler/Chambers + xmrit.com style

Reusable Python module for producing XmR (X and Moving Range) charts with Wheeler-grade rule detection and locked-limit segmentation.

Recommended workflow: see mrr-bridge-and-annotation-layer for the full discipline — chart input metrics not output metrics, and capture an analyst annotation against every signal so the chart accumulates into a learning system rather than a museum exhibit. The chart math below is half the practice; the annotation layer is the other half.

Files

What it does

Sigma estimate

Uses Wheeler’s successive-differences method:

σ = mean(mR) / 1.128       # NOT global stdev — this is the load-bearing distinction
UNPL = mean(X) + 2.66 × mean(mR)
LNPL = mean(X) - 2.66 × mean(mR)   (clamped to 0 for $ data)
URL  = 3.268 × mean(mR)

For median MR (robust to baseline outliers):

σ = median(mR) / 0.9539
X-factor = 3.14, mR-factor = 3.865

Usage

Standard (additive) XmR

For series whose noise is roughly additive — dollars, counts, durations, anything where a $200 swing means the same thing whether you’re at $1k or $20k.

from datetime import datetime
from xmr import auto_segment, detect_all_signals, plot_xmr

X = [16000, 16000, 16000, ...]   # observations
dates = [datetime(2024, 1, 31), datetime(2024, 3, 1), ...]

# Auto-segmentation (will split on Rule 2 trigger)
segments = auto_segment(X, baseline_n=12)

# Or with explicit known process changes
segments = auto_segment(X, baseline_n=12, explicit_breaks=[19])

signals = detect_all_signals(X, segments)

plot_xmr(
    X, dates, segments, signals,
    title="My metric — XmR Chart",
    subtitle="Locked limits + segmentation",
    footer="Interpretation note here",
    output_path="/tmp/my-chart.png",
    y_label="Metric",
    money=True,
)

Log-transform XmR (added 2026-04-22)

For multiplicative or heteroskedastic series — revenue, MRR, headcount, traffic, anything where the natural step is a percentage rather than an absolute amount and variance grows with level. Standard XmR misapplied to such a series produces inflated limits and misses regime changes.

from xmr import log_xmr, plot_xmr_log

# Log-transform pipeline: log_X is for plotting context; segments + signals
# are computed in log space (CL/UNPL/LNPL are log values)
log_X, segments, signals = log_xmr(X, baseline_n=12, explicit_breaks=None)

plot_xmr_log(
    X, log_X, dates, segments, signals,
    title="MRR — XmR (log-transform)",
    subtitle="Multiplicative bands · ratio-based limits",
    footer="exp(CL) = geometric mean. Step bounds = upper/lower per-step ratio.",
    output_path="/tmp/my-mrr-chart.png",
    y_label="MRR ($, log scale)",
)

What changes vs the additive path:

When NOT to use log_xmr:

Design notes

Why explicit breaks beat auto-detection in many cases: If the baseline contains an outlier, the locked CL is biased and Rule 2 will fire spuriously on the “normal” range that follows. The xmrit.com guidance explicitly recommends user-driven baseline selection for known process-change events. Auto-detection is best when you genuinely don’t know if/when a shift occurred.

What we deliberately did NOT add (per Wheeler’s USPC 3rd ed Ch. 5.2):

Annotation policy: Rule 1 always annotated (severe, point-specific). Rule 2 and Rule 3 annotated only on the first point of each consecutive run, since they fire on every subsequent point in the run by construction.

Dependencies

Install:

~/.claude/scripts/finance-venv/bin/pip install matplotlib

Sources