EMA_LFILTER

This function computes an exponential moving average (EMA) by applying a first-order recursive filter.

With smoothing factor \alpha, the recursion is:

\text{EMA}_t = \alpha x_t + (1-\alpha)\,\text{EMA}_{t-1}

Lower \alpha values produce stronger smoothing, while higher \alpha values react more quickly to new observations.

Excel Usage

=EMA_LFILTER(data, alpha, initial)
  • data (list[list], required): Time-series observations as a 2D range (data points).
  • alpha (float, optional, default: 0.2): Smoothing factor between 0 and 1 (unitless).
  • initial (float, optional, default: null): Optional initial EMA level used before processing the first observation (value).

Returns (list[list]): Column vector of exponentially smoothed values.

Example 1: EMA with default smoothing factor

Inputs:

data
10 12 11 13 15

Excel formula:

=EMA_LFILTER({10,12,11,13,15})

Expected output:

Result
2
4
5.4
6.92
8.536
Example 2: EMA with faster response smoothing

Inputs:

data alpha
5 8 7 10 0.6

Excel formula:

=EMA_LFILTER({5,8,7,10}, 0.6)

Expected output:

Result
3
6
6.6
8.64
Example 3: EMA with explicit initial level

Inputs:

data alpha initial
20 19 21 22 0.3 18

Excel formula:

=EMA_LFILTER({20,19,21,22}, 0.3, 18)

Expected output:

Result
18.6
18.72
19.404
20.1828
Example 4: Scalar input EMA

Inputs:

data alpha
9 0.5

Excel formula:

=EMA_LFILTER(9, 0.5)

Expected output:

4.5

Python Code

import numpy as np
from scipy.signal import lfilter as sp_lfilter

def ema_lfilter(data, alpha=0.2, initial=None):
    """
    Compute an exponential moving average using recursive linear filtering.

    See: https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.lfilter.html

    This example function is provided as-is without any representation of accuracy.

    Args:
        data (list[list]): Time-series observations as a 2D range (data points).
        alpha (float, optional): Smoothing factor between 0 and 1 (unitless). Default is 0.2.
        initial (float, optional): Optional initial EMA level used before processing the first observation (value). Default is None.

    Returns:
        list[list]: Column vector of exponentially smoothed values.
    """
    try:
        def to2d(x):
            return [[x]] if not isinstance(x, list) else x

        data = to2d(data)

        if not isinstance(data, list) or not all(isinstance(row, list) for row in data):
            return "Error: Invalid input - data must be a 2D list"

        if alpha <= 0 or alpha > 1:
            return "Error: alpha must be greater than 0 and less than or equal to 1"

        series = []
        for row in data:
            for item in row:
                try:
                    series.append(float(item))
                except (TypeError, ValueError):
                    continue

        if not series:
            return "Error: data must contain at least one numeric value"

        b = [float(alpha)]
        a = [1.0, -(1.0 - float(alpha))]
        arr = np.asarray(series, dtype=float)

        if initial is None:
            ema = sp_lfilter(b, a, arr)
        else:
            zi = [(1.0 - float(alpha)) * float(initial)]
            ema, _ = sp_lfilter(b, a, arr, zi=zi)

        return [[float(x)] for x in np.asarray(ema, dtype=float).tolist()]
    except Exception as e:
        return f"Error: {str(e)}"

Online Calculator

Time-series observations as a 2D range (data points).
Smoothing factor between 0 and 1 (unitless).
Optional initial EMA level used before processing the first observation (value).