// A is the value type we get for each candle asclass CandleBasedIndicator extends MetaWithChangeListeners { replace Candle with TradingCandle. // Number of candles we need to look back to settable int length = 20; event candleAdded(Candle candle); event candleRemoved(Candle candle); // get current indicator value abstract A value aka get(); simplyCached SimpleCircularBuffer candles() { ret new SimpleCircularBuffer(candlesNeeded()); } int candlesNeeded() { ret length; } Candle lastCandle() { ret candles().last(); } selfType feed(TradingCandleMaker candleMaker) { if (candleMaker != null) // TODO: cut list down first for (candle : candleMaker.candles()) add(candle); this; } selfType add aka feed(Iterable candles) { fOr (candle : candles) add(candle); this; } // Can add candles twice, will automatically ignore the second time selfType add aka feed(TradingCandle candle) { if (candle.ongoing()) this; if (lastCandle() != null && lessOrEq(candle.startTime, lastCandle().startTime)) this; while (candles().size() > max(length-1, 0)) candleRemoved(candles().remove()); candles().add(candle); candleAdded(candle); this; } void reset { resetFields(this, "candles_cache"); } selfType emptyClone() { var clone = shallowNonTransientClone(this); clone.reset(); ret clone; } }