Skip to content
AuthoredWritten by hand against the code; every example on it is executed by the docs checks. No human has reviewed this page yet.

util::fast_ewma

ewma turns an oldest-first numeric sequence into an equally long sequence of weighted means. Use it to smooth a feature or threshold when recent observations should count more than older ones. window is a span, not a rolling cutoff: with span 3, each older observation receives half the weight of the next one. No timestamps are accepted, so decay is per observation, even across time gaps.

The implementation in fast_ewma.rs normalizes by the weights available so far. For α=2/(w+1)\alpha=2/(w+1) and q=1−αq=1-\alpha,

mt=∑j=0tqt−jxj∑j=0tqt−j.m_t=\frac{\sum_{j=0}^{t}q^{t-j}x_j}{\sum_{j=0}^{t}q^{t-j}}.

Equivalently, update a numerator Nt=qNt−1+xtN_t=qN_{t-1}+x_t and denominator Dt=qDt−1+1D_t=qD_{t-1}+1, starting with N0=x0,D0=1N_0=x_0,D_0=1, and return Nt/DtN_t/D_t. This is adjusted EWMA; applying mt=αxt+(1−α)mt−1m_t=\alpha x_t+(1-\alpha)m_{t-1} directly would give different early values.

For [3, 6, 12] at span 3, the final numerator is 0.25×3+0.5×6+12=15.750.25\times3+0.5\times6+12=15.75 and the denominator is 1.751.75, giving 9. The intermediate value is (0.5×3+6)/1.5=5(0.5\times3+6)/1.5=5.

At span 3, the third adjusted mean uses weights 0.25, 0.5 and 1, normalized by their sum 1.75. At span 3, the third adjusted mean uses weights 0.25, 0.5 and 1, normalized by their sum 1.75.
Analytic weights for the three-value example; regenerate with python3 docs-site/scripts/figures/utility_data_figures.py.
import math
from openquant import fast_ewma
values = [3.0, 6.0, 12.0]
smoothed = fast_ewma.ewma(values, window=3)
assert smoothed == [3.0, 5.0, 9.0]
assert fast_ewma.ewma(values, window=1) == values
assert fast_ewma.ewma([], window=3) == []
missing = fast_ewma.ewma([3.0, float("nan"), 12.0], window=3)
print(smoothed)
print([math.isnan(x) for x in missing])
try:
fast_ewma.ewma(values, window=0)
except ValueError:
print("zero span rejected")
[3.0, 5.0, 9.0]
[False, True, True]
zero span rejected

The numerical assertions come from the weighted fractions above, not a fitted reference series. The missing-value example illustrates a deliberate limit: a NaN contaminates every later output; this is not pandas’ default skip-missing behavior. Clean or explicitly segment the input before smoothing.

use openquant::util::fast_ewma::ewma;
let smoothed = ewma(&[3.0, 6.0, 12.0], 3)?;
assert_eq!(smoothed, vec![3.0, 5.0, 9.0]);
assert!(ewma(&[], 3)?.is_empty());
assert!(ewma(&[3.0], 0).is_err());
Input or caseBehavior
arr in Python / arr_in in RustNumeric observations, oldest first
Positive windowSpan; larger spans give slower decay
Empty sequenceEmpty output, provided the span is positive
Span 1Returns finite inputs unchanged
Span 0Python ValueError; Rust InputError::OutOfRange
Nonfinite valuesNot validated; NaNs propagate

Smoothing does not turn a nonstationary feature into a stationary one, and a span is not a forecast horizon. Choose the observation frequency and span as part of the feature definition, then evaluate them in the downstream workflow.

Signatures, parameters, return values and errors for every function: openquant.fast_ewma.

  • fast_ewma.ewma — Bias-corrected exponentially weighted moving average with span `window`.

Rustdoc for this commit, with every public item of openquant::util::fast_ewma (after a crates.io release the same pages are on docs.rs):