### Question:
CachedIndicator extends AbstractIndicator<T> { @Override public T getValue(int index) { TimeSeries series = getTimeSeries(); if (series == null) { return calculate(index); } final int removedTicksCount = series.getRemovedTicksCount(); final int maximumResultCount = series.getMaximumTickCount(); T result; if (index < removedTicksCount) { log.trace("{}: result from tick {} already removed from cache, use {}-th instead", getClass().getSimpleName(), index, removedTicksCount); increaseLengthTo(removedTicksCount, maximumResultCount); highestResultIndex = removedTicksCount; result = results.get(0); if (result == null) { result = calculate(0); results.set(0, result); } } else { increaseLengthTo(index, maximumResultCount); if (index > highestResultIndex) { highestResultIndex = index; result = calculate(index); results.set(results.size()-1, result); } else { int resultInnerIndex = results.size() - 1 - (highestResultIndex - index); result = results.get(resultInnerIndex); if (result == null) { result = calculate(index); } results.set(resultInnerIndex, result); } } return result; } CachedIndicator(TimeSeries series);  CachedIndicator(Indicator indicator); @Override T getValue(int index);  }### Answer:
@Test public void ifCacheWorks() { SMAIndicator sma = new SMAIndicator(new ClosePriceIndicator(series), 3); Decimal firstTime = sma.getValue(4); Decimal secondTime = sma.getValue(4); assertEquals(firstTime, secondTime); }
@Test public void getValueWithNullTimeSeries() { ConstantIndicator<Decimal> constant = new ConstantIndicator<Decimal>(Decimal.TEN); assertEquals(Decimal.TEN, constant.getValue(0)); assertEquals(Decimal.TEN, constant.getValue(100)); assertNull(constant.getTimeSeries()); SMAIndicator sma = new SMAIndicator(constant, 10); assertEquals(Decimal.TEN, sma.getValue(0)); assertEquals(Decimal.TEN, sma.getValue(100)); assertNull(sma.getTimeSeries()); }
@Test public void getValueWithCacheLengthIncrease() { double[] data = new double[200]; Arrays.fill(data, 10); SMAIndicator sma = new SMAIndicator(new ClosePriceIndicator(new MockTimeSeries(data)), 100); assertDecimalEquals(sma.getValue(105), 10); }
@Test public void getValueWithOldResultsRemoval() { double[] data = new double[20]; Arrays.fill(data, 1); TimeSeries timeSeries = new MockTimeSeries(data); SMAIndicator sma = new SMAIndicator(new ClosePriceIndicator(timeSeries), 10); assertDecimalEquals(sma.getValue(5), 1); assertDecimalEquals(sma.getValue(10), 1); timeSeries.setMaximumTickCount(12); assertDecimalEquals(sma.getValue(19), 1); }
@Test public void getValueOnResultsCalculatedFromRemovedTicksShouldReturnFirstRemainingResult() { TimeSeries timeSeries = new MockTimeSeries(1, 1, 1, 1, 1); timeSeries.setMaximumTickCount(3); assertEquals(2, timeSeries.getRemovedTicksCount()); SMAIndicator sma = new SMAIndicator(new ClosePriceIndicator(timeSeries), 2); for (int i = 0; i < 5; i++) { assertDecimalEquals(sma.getValue(i), 1); } }
@Test public void recursiveCachedIndicatorOnMovingTimeSeriesShouldNotCauseStackOverflow() { series = new MockTimeSeries(); series.setMaximumTickCount(5); assertEquals(5, series.getTickCount()); ZLEMAIndicator zlema = new ZLEMAIndicator(new ClosePriceIndicator(series), 1); try { assertDecimalEquals(zlema.getValue(8), "9"); } catch (Throwable t) { fail(t.getMessage()); } }