CusumDetector extends AbstractOutlierDetector { @Override public DetectorResult detect(MetricData metricData) { AssertUtil.notNull(metricData, "metricData can't be null"); val params = getParams(); val trusted = isTrusted(); val observed = metricData.getValue(); this.movingRange += Math.abs(this.prevValue - observed); val stdDev = avgMovingRange() / STD_DEV_DIVISOR; val slack = params.getSlackParam() * stdDev; val weakDelta = params.getWeakSigmas() * stdDev; val strongDelta = params.getStrongSigmas() * stdDev; this.sumHigh = Math.max(0, this.sumHigh + observed - (params.getTargetValue() + slack)); this.sumLow = Math.min(0, this.sumLow + observed - (params.getTargetValue() - slack)); this.prevValue = observed; this.totalDataPoints++; Double upperStrong; Double upperWeak; Double lowerStrong; Double lowerWeak; AnomalyLevel level; if (totalDataPoints <= params.getWarmUpPeriod()) { level = MODEL_WARMUP; } else { level = NORMAL; upperWeak = weakDelta; upperStrong = strongDelta; lowerWeak = -weakDelta; lowerStrong = -strongDelta; switch (params.getType()) { case LEFT_TAILED: lowerWeak = -weakDelta; lowerStrong = -strongDelta; if (this.sumLow < lowerStrong) { level = STRONG; resetSums(); } else if (this.sumLow < lowerWeak) { level = WEAK; } break; case RIGHT_TAILED: if (this.sumHigh > upperStrong) { level = STRONG; resetSums(); } else if (this.sumHigh > upperWeak) { level = WEAK; } break; case TWO_TAILED: if (this.sumHigh > upperStrong || this.sumLow < lowerStrong) { level = STRONG; resetSums(); } else if (this.sumHigh > upperWeak || this.sumLow < lowerWeak) { level = WEAK; } break; default: throw new IllegalStateException("Illegal type: " + params.getType()); } } return new OutlierDetectorResult(level, trusted); } CusumDetector(UUID uuid, CusumDetectorParams params, boolean trusted); @Override DetectorResult detect(MetricData metricData); @Override String getName();  }
@Test public void testClassify_rightTailed() { val testRows = data.listIterator(); val testRow0 = testRows.next(); val anomalyType = AnomalyType.RIGHT_TAILED; val trusted = true; val params = new CusumDetectorParams() .setType(anomalyType) .setTargetValue(0.16) .setWeakSigmas(WEAK_SIGMAS) .setStrongSigmas(STRONG_SIGMAS) .setSlackParam(0.5) .setInitMeanEstimate(testRow0.getObserved()) .setWarmUpPeriod(WARMUP_PERIOD); val detector = new CusumDetector(detectorUuid, params, trusted); int numDataPoints = 1; while (testRows.hasNext()) { val testRow = testRows.next(); val observed = testRow.getObserved(); val metricData = new MetricData(metricDefinition, observed, epochSecond); val result = (OutlierDetectorResult) detector.detect(metricData); val level = result.getAnomalyLevel(); assertEquals(AnomalyLevel.valueOf(testRow.getLevel()), level); if (numDataPoints >= WARMUP_PERIOD) { assertApproxEqual(testRow.getSh(), detector.getSumHigh()); assertApproxEqual(testRow.getSl(), detector.getSumLow()); } numDataPoints++; } }