Efficient calculation of bollinger bands

Aug 21, 2018 programming fintech

The Bollinger bands used in technical analysis is the +/- 2 * standard deviation of the closing price plotted on the chart. This prices are expected to fluctuate in this band 95% of the time. As with any sliding window calculation the easy way is to calculate everything each time for each window. Which usually means quadratic algorithm. A better way exists to calculate the values for standard deviations using some smart manipulations on the standard deviation formula. (to be more precise the variance formula which is the square of the standard deviation.)

To the variance formula is (k = )

$$ \sum_{i=1}^{n} (price_i - mean)^2 $$

expanding the quadratic term

$$ \sum_{i=1}^{n} (price_i^2 - 2 \times price_i \times mean + mean^2) $$

extracting the last constant term independent of i

$$ \sum_{i=1}^{n} (price_i^2 - 2 \times price_i \times mean) + n \times mean^2 $$

extract the term independent of i from the 2nd part

$$ \sum_{i=1}^{n} (price_i^2) + \sum_{i=1}^{n}(-2 \times price_i) \times mean + n \times mean^2 $$

first term and last term can be handled the way moving averages are handle. When the window slides right, add the new term remove the old term on the left. This is efficient.

$$ \sum_{i=1}^{n} price_i = total = mean \times n $$

so the middle term becomes

$$ -2 \times mean^2 \times n $$

now the second term can also be calculated using the efficient method.

Here is an implementation in code for bollinger bands

public SuperList<BollingerBand> calculate() {
    double totalAvg = 0d;
    double totalSq = 0d;
    int i = 0;
    SuperList<BollingerBand> result = new SuperList<>();
    for (Double value : values) {
        totalAvg += value;
        totalSq += value * value;
        if (i >= PERIOD - 1) {
            double avg = totalAvg / PERIOD;
            double stddev = Math.sqrt((totalSq - (totalAvg*totalAvg)/PERIOD)/ PERIOD);
            BollingerBand band = new BollingerBand(avg, avg + 2 * stddev, avg - 2 * stddev);
            result.add(band);
            totalAvg -= values.get(i - PERIOD + 1);
            totalSq -= values.get(i - PERIOD + 1) * values.get(i - PERIOD + 1);
        }
        i++;
    }
    return result;
}