Jaime López

Data Science Systems Developer

Rolling indicators for streaming data

Jan. 21, 2026

Motivation

Financial applications depend on indicators to make decisions. Usually, these indicators are computed from time series data. The most common approach is to compute the indicator from scratch every time a new data point is added. This approach seems simple and easy to implement, but it is not efficient.

In the context of this article, a rolling indicator for streaming data is a metric that keeps memory of previous computations to make it easy to refresh the measurement when a new value arrives. To introduce the concept, the computation of moving averages is used as an example, estimating the number of required operations and comparing that metric for both approaches: computing from scratch and using a rolling indicator.

Description

The moving average is a widely used indicator in finance for smoothing data and to identify trends. It is computed as the average of the last $n$ values in a time series.

$$\text{MA}(t, n) = \frac{1}{n} \sum_{i=t - n + 1}^{t} x_i$$

When a new value arrives, we can compute the moving average using the same formula, simply by shifting the window forward by one position.

$$\text{MA}(t + 1, n) = \frac{1}{n} \sum_{i=t - n + 2}^{t+1} x_i$$

An alternative is to remove the first value from the window, add the new value to the running sum, and divide by $n$, as shown below.

Given:

$$S_t = \sum_{i = t - n + 1}^{t} x_i$$

$$x_{\text{prev}} = x_{t - n + 1}$$

Then:

$$\text{MA}(t + 1, n) = \frac{1}{n} [S_t - x_{\text{prev}} + (x_{t + 1})]$$

Performance

Let's evaluate the required number of operations:

  • Method 1: Each step requires $n - 1$ additions and one division. For $t$ steps, the total number of operations is $tn$.
  • Method 2: The first step requires $n$ operations. Each subsequent step requires only $3$ operations: one to subtract the oldest value, one to add the new value, and one for the division by $n$. The total is $n + 3(t - 1) = n + 3t - 3$.

If the moving average with $n = 10$ and $t = 252$ (business days in one year), the performance of the second method is approximately $3x$. In another example, if $n = 90$ and $t = 1260$ (business days for 5 years), the performance of the second method is approximately $29x$.

Implementation

Below is a class definition for the moving average indicator. It is initialized with the number of periods. It has two methods: update and get. The first one is used to include a new value and returns the updated moving average. The second one is used to consult the moving average value at any moment.

class MA {
public:
    MA(size_t periods);
    double update(double value);
    double get() { return m_len < m_periods 
        ? NAN : m_accum / m_periods; }
private:
    double m_accum = 0.0;
    size_t m_periods;
    size_t m_len = 0;
    size_t m_pos = 0;  // current position in circular buffer
    std::vector<double> m_prevs; // n-periods array
};

The implementation for update is shown below. During the first few updates, it increments m_len until it reaches m_periods. At that point, the moving average becomes valid. In each update, value is added to m_accum and stored in the circular buffer m_prevs. The oldest value is subtracted from m_accum when it falls out of the window.

double MA::update(double value) {
    if (m_len < m_periods) {
        ++m_len;
    } else {
        m_accum -= m_prevs[m_pos];
    }
    m_prevs[m_pos] = value;
    m_accum += value;
    m_pos = (m_pos + 1) % m_periods;
    return get();
}

An implementation of these concepts is available in ta-zig, a Zig library that provides technical analysis indicators designed for streaming data. Another example is also available in the pybottrader library, which provides an Indicator class implemented in C++ with a Python interface.

Both libraries implement the rolling indicator pattern using circular buffers, keeping only a window of necessary values in memory, and include common indicators such as moving averages, MACD, RSI, Bollinger Bands, and others.

Discussion

The rolling indicator approach is convenient when processing streaming data at high frequency, such as tick data or real-time feeds, where recomputing from scratch for each new data point would be expensive. It is advantageous in scenarios with long time series where the number of updates significantly exceeds the window size, in memory-constrained environments where storing the full history is impractical, and in latency-sensitive applications like algorithmic trading or risk management.

However, the approach can be less convenient in batch processing scenarios where indicators are computed only periodically, such as hourly or daily snapshots, because the overhead of maintaining state may not justify the marginal savings. It can also be inconvenient when debugging and auditing require verifying results through independent recomputation. In high-level interpreted languages, the implementation overhead may dominate any computational savings, and when the warm-up period is significant relative to the total number of updates, the benefits of incremental computation are reduced.

Español