Moving avg aggregation

Moving avg aggregation #

A parent pipeline aggregation smoothing a metric with a moving average over the last N buckets — one averaged point per bucket, flattening noise so the trend underneath shows.

"smooth": {
  "moving_avg": { "buckets_path": "per_day>revenue", "window": 3 }
}

How it works #

In this engine moving_avg is evaluated in the sibling placement: declare it next to the bucket aggregation and point buckets_path at the series with the full parent>metric form. After the buckets are built, the engine slides a trailing window over their values and emits a standalone buckets array (keys 0..n−1) with one value per consumed bucket — the average of the window ending at that bucket.

  • The window is truncated at the start of the series: the first bucket averages just itself, the second at most two values.
  • window defaults to 5; 0 is treated as 1; a negative or fractional value is rejected when the request parses.
  • Gaps follow gap_policy: skip (default) leaves empty buckets out of the series before averaging; insert_zeros contributes 0.

Three models are implemented, chosen by model:

ModelBehavior
simple (default)Plain arithmetic mean of the window.
linearWeighted mean, weights rising 1..n — the newest value weighs the most.
ewmaExponentially weighted average s = α·x + (1−α)·s, seeded with the oldest window value; α comes from settings.alpha, default 0.3.

holt and holt_winters are accepted for compatibility but currently fall back to the simple mean — double/triple exponential smoothing is not implemented. Executed example over the series 60, 150, 150: simple window 3 gives 60, 105, 120; linear window 2 gives 60, 120; ewma window 2 gives 60, 87, 150, and ewma with alpha: 1.0 degenerates to the newest value.

The nested placement — moving_avg declared inside the histogram’s aggs with a bare buckets_path — parses but is not wired to the nested injection path yet: every bucket’s entry stays null. Use the sibling form above, or moving_fn, which works nested.

This engine still computes moving_avg.

Examples #

The teaching dataset sells exactly once per day, so each smoothed point is the mean of that day’s amount and the two before it — smooth.buckets[i].value tracks the trailing 3-day average while per_day keeps the raw daily figure.

Use cases #

  • Dashboard smoothing — raw daily revenue jitters; the trailing average is the line people actually read trend from.
  • Trend baseline for alerting — compare the current bucket against the moving average instead of yesterday’s outlier.
  • Recent-weighted health signals — ewma with a higher alpha reacts faster to shifts, catching regime changes a plain mean dilutes.
  • Pre-smoothing before differencing — feed the smoothed series into derivative / serial_diff to difference signal, not noise.

Parameters for moving_avg #

  • buckets_path
    (Required, string) Path to the consumed series: "<bucket_agg>><metric>" (sibling placement).
  • window
    (Optional, integer, default: 5) Number of buckets in the sliding window; clamped to a minimum of 1.
  • model
    (Optional, string, default: simple) simple, linear or ewma; holt/holt_winters fall back to simple.
  • settings
    (Optional, object) Model settings — alpha for ewma (default 0.3).
  • gap_policy
    (Optional, string) skip (default) or insert_zeros.
  • moving_fn — the general window statistic behind every model here (any script over values); also works nested
  • derivative and serial_diff — change instead of level
  • avg_bucket — one mean over all buckets, not a sliding one
Calendar October 6, 2026
Edit Edit this page