@Override
 protected void seriesAdded(Series<String, Number> series, int seriesIndex) {
   // handle any data already in series
   for (int j = 0; j < series.getData().size(); j++) {
     Data item = series.getData().get(j);
     Node candle = createCandle(seriesIndex, item, j);
     if (shouldAnimate()) {
       candle.setOpacity(0);
       getPlotChildren().add(candle);
       // fade in new candle
       FadeTransition ft = new FadeTransition(Duration.millis(500), candle);
       ft.setToValue(1);
       ft.play();
     } else {
       getPlotChildren().add(candle);
     }
   }
   // create series path
   Path seriesPath = new Path();
   seriesPath.getStyleClass().setAll("candlestick-average-line", "series" + seriesIndex);
   series.setNode(seriesPath);
   getPlotChildren().add(seriesPath);
 }
 @Override
 protected void seriesRemoved(Series<String, Number> series) {
   // remove all candle nodes
   for (XYChart.Data<String, Number> d : series.getData()) {
     final Node candle = d.getNode();
     if (shouldAnimate()) {
       // fade out old candle
       FadeTransition ft = new FadeTransition(Duration.millis(500), candle);
       ft.setToValue(0);
       ft.setOnFinished(
           (ActionEvent actionEvent) -> {
             getPlotChildren().remove(candle);
           });
       ft.play();
     } else {
       getPlotChildren().remove(candle);
     }
   }
 }
 /**
  * This is called when the range has been invalidated and we need to update it. If the axis are
  * auto ranging then we compile a list of all data that the given axis has to plot and call
  * invalidateRange() on the axis passing it that data.
  */
 @Override
 protected void updateAxisRange() {
   // For candle stick chart we need to override this method as we need to let the axis know that
   // they need to be able
   // to cover the whole area occupied by the high to low range not just its center data value
   final Axis<String> xa = getXAxis();
   final Axis<Number> ya = getYAxis();
   List<String> xData = null;
   List<Number> yData = null;
   if (xa.isAutoRanging()) {
     xData = new ArrayList<>();
   }
   if (ya.isAutoRanging()) {
     yData = new ArrayList<>();
   }
   if (xData != null || yData != null) {
     for (Series<String, Number> series : getData()) {
       for (Data<String, Number> data : series.getData()) {
         if (xData != null) {
           xData.add(data.getXValue());
         }
         if (yData != null) {
           BarData extras = (BarData) data.getExtraValue();
           if (extras != null) {
             yData.add(extras.getHigh());
             yData.add(extras.getLow());
           } else {
             yData.add(data.getYValue());
           }
         }
       }
     }
     if (xData != null) {
       xa.invalidateRange(xData);
     }
     if (yData != null) {
       ya.invalidateRange(yData);
     }
   }
 }