sclass BollingerBands extends CandleBasedIndicator { replace Candle with TradingCandle. // length parameter is in base class // The magic average + standard deviation generator new Welford welford; // Probably wrong word. Defines how many times the standard deviation // we use as the tunnel size (well, twice that, actually) settable double deviation = 2; // result settable DoubleRange range; // Bands collected as tickers gettable TickerSequence upperBand = new TickerSequence("Upper Bollinger Band"); gettable TickerSequence lowerBand = new TickerSequence("Lower Bollinger Band"); gettable AverageAndStandardDeviation as; { onCandleRemoved(candle -> welford.remove(candle.endPrice())); onCandleAdded(candle -> { welford.add(candle.endPrice()); if (l(candles()) >= length) { var bCandles = candles().asList(); //var prices = mapToDoubleArray(bCandles, ->.endPrice()); //as = averageAndStandardDeviation(prices); as = welford!; range(bollingerRange(as, deviation)); upperBand?.addIfPriceChanged(range.end(), candle.endTime().toLong()); lowerBand?.addIfPriceChanged(range.start(), candle.endTime().toLong()); change(); } }); } L bandsAsTickers(L candles) { feed(candles); ret ll(upperBand, lowerBand); } DoubleRange value() { ret range(); } void reset :: after { resetFields(this, "welford range upperBand lowerBand as"); } }