/** * Construct a new CandleStickChart with the given axis. * * @param title The chart title * @param xAxis The x axis to use * @param yAxis The y axis to use * @param bars The bars to display on the chart * @param maxBarsToDisplay The maximum number of bars to display on the chart. */ public CandleStickChart( String title, CategoryAxis xAxis, NumberAxis yAxis, List<BarData> bars, int maxBarsToDisplay) { super(xAxis, yAxis); this.xAxis = xAxis; this.yAxis = yAxis; this.maxBarsToDisplay = maxBarsToDisplay; yAxis.autoRangingProperty().set(true); yAxis.forceZeroInRangeProperty().setValue(Boolean.FALSE); setTitle(title); setAnimated(true); getStylesheets() .add(getClass().getResource("/styles/CandleStickChartStyles.css").toExternalForm()); xAxis.setAnimated(true); yAxis.setAnimated(true); verticalGridLinesVisibleProperty().set(false); XYChart.Series<String, Number> series = new XYChart.Series<>(); List<BarData> sublist = getSubList(bars, maxBarsToDisplay); for (BarData bar : sublist) { String label = ""; label = sdf.format(bar.getDateTime().getTime()); series.getData().add(new XYChart.Data<>(label, bar.getOpen(), bar)); logger.log(Level.INFO, "Adding bar with date/time: {0}", bar.getDateTime().getTime()); logger.log(Level.INFO, "Adding bar with price: {0}", bar.getOpen()); } dataSeries = FXCollections.observableArrayList(series); setData(dataSeries); lastBar = sublist.get(sublist.size() - 1); }
/** * Update the "Last" price of the most recent bar * * @param price The Last price of the most recent bar. */ public void updateLast(double price) { if (lastBar != null) { lastBar.update(price); logger.log( Level.INFO, "Updating last bar with date/time: {0}", lastBar.getDateTime().getTime()); int datalength = dataSeries.get(0).getData().size(); dataSeries.get(0).getData().get(datalength - 1).setYValue(lastBar.getOpen()); dataSeries.get(0).getData().get(datalength - 1).setExtraValue(lastBar); logger.log( Level.INFO, "Updating last bar with formatteddate/time: {0}", dataSeries.get(0).getData().get(datalength - 1).getXValue()); } }
/** * 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); } } }
/** * Appends a new bar on to the end of the chart. * * @param bar The bar to append to the chart */ public void addBar(BarData bar) { if (dataSeries.get(0).getData().size() >= maxBarsToDisplay) { dataSeries.get(0).getData().remove(0); } int datalength = dataSeries.get(0).getData().size(); dataSeries.get(0).getData().get(datalength - 1).setYValue(bar.getOpen()); dataSeries.get(0).getData().get(datalength - 1).setExtraValue(bar); String label = sdf.format(bar.getDateTime().getTime()); logger.log(Level.INFO, "Adding bar with actual time: {0}", bar.getDateTime().getTime()); logger.log(Level.INFO, "Adding bar with formated time: {0}", label); lastBar = new BarData( bar.getDateTime(), bar.getClose(), bar.getClose(), bar.getClose(), bar.getClose(), 0); Data<String, Number> data = new XYChart.Data<>(label, lastBar.getOpen(), lastBar); dataSeries.get(0).getData().add(data); }
/** Called to update and layout the content for the plot */ @Override protected void layoutPlotChildren() { // we have nothing to layout if no data is present if (getData() == null) { return; } // update candle positions for (int seriesIndex = 0; seriesIndex < getData().size(); seriesIndex++) { Series<String, Number> series = getData().get(seriesIndex); Iterator<Data<String, Number>> iter = getDisplayedDataIterator(series); Path seriesPath = null; if (series.getNode() instanceof Path) { seriesPath = (Path) series.getNode(); seriesPath.getElements().clear(); } while (iter.hasNext()) { Data<String, Number> item = iter.next(); double x = getXAxis().getDisplayPosition(getCurrentDisplayedXValue(item)); double y = getYAxis().getDisplayPosition(getCurrentDisplayedYValue(item)); Node itemNode = item.getNode(); BarData bar = (BarData) item.getExtraValue(); if (itemNode instanceof Candle && item.getYValue() != null) { Candle candle = (Candle) itemNode; double close = getYAxis().getDisplayPosition(bar.getClose()); double high = getYAxis().getDisplayPosition(bar.getHigh()); double low = getYAxis().getDisplayPosition(bar.getLow()); double candleWidth = 10; // update candle candle.update(close - y, high - y, low - y, candleWidth); // update tooltip content candle.updateTooltip(bar.getOpen(), bar.getClose(), bar.getHigh(), bar.getLow()); // position the candle candle.setLayoutX(x); candle.setLayoutY(y); } } } }