Jfree Candlestick Chart Example
Through this article, we are going to show you how to create a stock Candlestick
chart using JFreeChart
, JFreeChart
is a free open source Java library for generating charts, it includes a wide range of charts such as pie charts, bar charts, line charts, scatter plots, time series charts (including moving averages, high-low-open-close charts and candlestick plots), Gantt charts, meter charts (dial and thermometer), combination charts and more.
Also, this example will demonstrate the combined charts facility of JFreeChart
, it provides a flexible mechanism for combining multiple plots on a single chart. A combined XY plot is a plot that has two or more subplots sharing either the horizontal or the vertical axis.
To demonstrate, we will create a candlestick combined with volume chart. This is a common type of chart used in the finance industry. It is used to plot the (High
, Low
, Open
, Close
) prices of specific stock, along with the stock’s trading volume (the number of units traded) based on intraday financial market trades.
1. Project Environment
- JfreeChart 1.0.13
- Apache Maven 3.0.5
- JDK 1.8
- Eclipse 4.4 (Luna)
2. Project Structure
We create a simple Java Maven project with the following structure.
3. Dependencies
We have the following dependencies inside our below POM file.
pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.jcg.example</groupId> <artifactId>jfreecandlestickchart-example-code</artifactId> <version>1.0</version> <packaging>jar</packaging> <name>jfreecandlestickchart-example-code</name> <properties> <!-- Generic properties --> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>jfree</groupId> <artifactId>jfreechart</artifactId> <version>1.0.13</version> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.2</version> <configuration> <source>${java.version}</source> <target>${java.version}</target> </configuration> </plugin> <plugin> <artifactId>maven-assembly-plugin</artifactId> <version>2.5.3</version> <executions> <execution> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> <configuration> <archive> <manifest> <mainClass>com.fx.jfree.chart.demo.JfreeCandlestickChartDemo</mainClass> </manifest> </archive> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> <finalName>${project.name}</finalName> <appendAssemblyId>false</appendAssemblyId> </configuration> </plugin> </plugins> </build> </project>
4. Constructing the Jfree Candlestick Chart
To create a Jfree combined chart, you should create the below components:
- Candlestick subplot
- Volume subplot
- Chart main plot to combine
candlestick
andvolume
subplots - JFreeChart with previously created main plot
We have used the OHLCSeriesCollection
class to represent both the Candlestick dataset. However, we used the TimeSeriesCollection
class to represent the volume dataset. These datasets will be updated using the method addCandel(long time, double o, double h, double l, double c, long v)
of JfreeCandlestickChart.java
class.
Notice how each of the subplots has a null
domain axis, since they share the parent plot’s axis.
Also, the combined plot is created with a VERTICAL
orientation, which means that the sub-plots are stacked from top to bottom. You can control the amount of space allocated to each plot by specifying a weight
for each plot as you add them to the parent plot where each plot is allocated space based on its weight as a percentage of the total.
In our example, the first subplot is allocated 3/4
of the space, and the second subplot is allocated 1/4
of the space.
JfreeCandlestickChart.java:
package com.fx.jfree.chart.candlestick; import java.awt.BorderLayout; import java.awt.Color; import java.text.DateFormat; import java.text.DecimalFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import javax.swing.JPanel; import org.jfree.chart.ChartPanel; import org.jfree.chart.JFreeChart; import org.jfree.chart.axis.DateAxis; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.labels.StandardXYToolTipGenerator; import org.jfree.chart.plot.CombinedDomainXYPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.xy.CandlestickRenderer; import org.jfree.chart.renderer.xy.XYBarRenderer; import org.jfree.data.time.FixedMillisecond; import org.jfree.data.time.TimeSeries; import org.jfree.data.time.TimeSeriesCollection; import org.jfree.data.time.ohlc.OHLCSeries; import org.jfree.data.time.ohlc.OHLCSeriesCollection; import com.fx.jfree.chart.model.Trade; import com.fx.jfree.chart.utils.MathUtils; import com.fx.jfree.chart.utils.TimeUtils; /** * The Class JfreeCandlestickChart. * * @author ashraf */ @SuppressWarnings("serial") public class JfreeCandlestickChart extends JPanel { private static final DateFormat READABLE_TIME_FORMAT = new SimpleDateFormat("kk:mm:ss"); private OHLCSeries ohlcSeries; private TimeSeries volumeSeries; private static final int MIN = 60000; // Every minute private int timeInterval = 1; private Trade candelChartIntervalFirstPrint = null; private double open = 0.0; private double close = 0.0; private double low = 0.0; private double high = 0.0; private long volume = 0; public JfreeCandlestickChart(String title) { // Create new chart final JFreeChart candlestickChart = createChart(title); // Create new chart panel final ChartPanel chartPanel = new ChartPanel(candlestickChart); chartPanel.setPreferredSize(new java.awt.Dimension(1200, 500)); // Enable zooming chartPanel.setMouseZoomable(true); chartPanel.setMouseWheelEnabled(true); add(chartPanel, BorderLayout.CENTER); } private JFreeChart createChart(String chartTitle) { /** * Creating candlestick subplot */ // Create OHLCSeriesCollection as a price dataset for candlestick chart OHLCSeriesCollection candlestickDataset = new OHLCSeriesCollection(); ohlcSeries = new OHLCSeries("Price"); candlestickDataset.addSeries(ohlcSeries); // Create candlestick chart priceAxis NumberAxis priceAxis = new NumberAxis("Price"); priceAxis.setAutoRangeIncludesZero(false); // Create candlestick chart renderer CandlestickRenderer candlestickRenderer = new CandlestickRenderer(CandlestickRenderer.WIDTHMETHOD_AVERAGE, false, new CustomHighLowItemLabelGenerator(new SimpleDateFormat("kk:mm"), new DecimalFormat("0.000"))); // Create candlestickSubplot XYPlot candlestickSubplot = new XYPlot(candlestickDataset, null, priceAxis, candlestickRenderer); candlestickSubplot.setBackgroundPaint(Color.white); /** * Creating volume subplot */ // creates TimeSeriesCollection as a volume dataset for volume chart TimeSeriesCollection volumeDataset = new TimeSeriesCollection(); volumeSeries = new TimeSeries("Volume"); volumeDataset.addSeries(volumeSeries); // Create volume chart volumeAxis NumberAxis volumeAxis = new NumberAxis("Volume"); volumeAxis.setAutoRangeIncludesZero(false); // Set to no decimal volumeAxis.setNumberFormatOverride(new DecimalFormat("0")); // Create volume chart renderer XYBarRenderer timeRenderer = new XYBarRenderer(); timeRenderer.setShadowVisible(false); timeRenderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator("Volume--> Time={1} Size={2}", new SimpleDateFormat("kk:mm"), new DecimalFormat("0"))); // Create volumeSubplot XYPlot volumeSubplot = new XYPlot(volumeDataset, null, volumeAxis, timeRenderer); volumeSubplot.setBackgroundPaint(Color.white); /** * Create chart main plot with two subplots (candlestickSubplot, * volumeSubplot) and one common dateAxis */ // Creating charts common dateAxis DateAxis dateAxis = new DateAxis("Time"); dateAxis.setDateFormatOverride(new SimpleDateFormat("kk:mm")); // reduce the default left/right margin from 0.05 to 0.02 dateAxis.setLowerMargin(0.02); dateAxis.setUpperMargin(0.02); // Create mainPlot CombinedDomainXYPlot mainPlot = new CombinedDomainXYPlot(dateAxis); mainPlot.setGap(10.0); mainPlot.add(candlestickSubplot, 3); mainPlot.add(volumeSubplot, 1); mainPlot.setOrientation(PlotOrientation.VERTICAL); JFreeChart chart = new JFreeChart(chartTitle, JFreeChart.DEFAULT_TITLE_FONT, mainPlot, true); chart.removeLegend(); return chart; } /** * Fill series with data. * * @param t the t */ public void addCandel(long time, double o, double h, double l, double c, long v) { try { // Add bar to the data. Let's repeat the same bar FixedMillisecond t = new FixedMillisecond( READABLE_TIME_FORMAT.parse(TimeUtils.convertToReadableTime(time))); ohlcSeries.add(t, o, h, l, c); volumeSeries.add(t, v); } catch (ParseException e) { e.printStackTrace(); } } /** * Aggregate the (open, high, low, close, volume) based on the predefined time interval (1 minute) * * @param t the t */ public void onTrade(Trade t) { double price = t.getPrice(); if (candelChartIntervalFirstPrint != null) { long time = t.getTime(); if (timeInterval == (int) ((time / MIN) - (candelChartIntervalFirstPrint.getTime() / MIN))) { // Set the period close price close = MathUtils.roundDouble(price, MathUtils.TWO_DEC_DOUBLE_FORMAT); // Add new candle addCandel(time, open, high, low, close, volume); // Reset the intervalFirstPrint to null candelChartIntervalFirstPrint = null; } else { // Set the current low price if (MathUtils.roundDouble(price, MathUtils.TWO_DEC_DOUBLE_FORMAT) high) high = MathUtils.roundDouble(price, MathUtils.TWO_DEC_DOUBLE_FORMAT); volume += t.getSize(); } } else { // Set intervalFirstPrint candelChartIntervalFirstPrint = t; // the first trade price in the day (day open price) open = MathUtils.roundDouble(price, MathUtils.TWO_DEC_DOUBLE_FORMAT); // the interval low low = MathUtils.roundDouble(price, MathUtils.TWO_DEC_DOUBLE_FORMAT); // the interval high high = MathUtils.roundDouble(price, MathUtils.TWO_DEC_DOUBLE_FORMAT); // set the initial volume volume = t.getSize(); } } }
Also, we created FxMarketPxFeeder.java
class which will be played as the financial market data feeder, it reads our trades file twtr.csv
which contains the intraday trades for Twitter stock, it feeds the JfreeCandlestickChart.java
using the callback method onTrade(Trade t)
to Aggregate the (open
, high
, low
, close
, volume
) based on the predefined time interval (1 minute
). then, it updates the JfreeCandlestickChart
using the addCandel(long time, double o, double h, double l, double c, long v)
method.
FxMarketPxFeeder.java:
package com.fx.jfree.chart.common; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import com.fx.jfree.chart.candlestick.JfreeCandlestickChart; import com.fx.jfree.chart.model.Trade; import com.fx.jfree.chart.utils.TimeUtils; /** * The Class FxMarketPxFeeder. * * @author ashraf */ public class FxMarketPxFeeder { private JfreeCandlestickChart jfreeCandlestickChart; private String stockTradesFile; private int simulationTime; private ExecutorService executorService; public FxMarketPxFeeder(JfreeCandlestickChart jfreeCandlestickChart, String stockTradesFile, int simulationTime) { super(); this.executorService = Executors.newCachedThreadPool(); this.stockTradesFile = stockTradesFile; this.jfreeCandlestickChart = jfreeCandlestickChart; this.simulationTime = simulationTime; } public void run() { executorService.execute(() -> read()); } private void read() { try (BufferedReader br = new BufferedReader( new InputStreamReader(this.getClass().getResourceAsStream(stockTradesFile)))) { while (true) { Thread.sleep(simulationTime); String line = br.readLine(); if (line != null) { // Parse line and convert it to trade String[] tradeElements = line.split(Constants.DELIMITER); Trade t = new Trade(tradeElements[Constants.STOCK_IDX], TimeUtils.convertToMillisTime(tradeElements[Constants.TIME_IDX]), Double.parseDouble(tradeElements[Constants.PRICE_IDX]), Long.parseLong(tradeElements[Constants.SIZE_IDX])); // Add trade to the jfreeCandlestickChart jfreeCandlestickChart.onTrade(t); } else { executorService.shutdown(); break; } } } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } }
Notice that twtr.csv
file contains the intraday Twitter stock trades, each trade has stock
, time
, price
and shares
.
twtr.csv:
TWTR|09:30:00.000|64.890|100 TWTR|09:30:00.000|64.890|25 TWTR|09:30:00.000|64.890|245 TWTR|09:30:00.000|64.890|55 TWTR|09:30:00.425|64.890|500 TWTR|09:30:00.425|64.900|100 TWTR|09:30:00.700|64.960|200 TWTR|09:30:00.700|64.950|50 TWTR|09:30:00.700|64.950|50 TWTR|09:30:04.375|65.000|163399 TWTR|09:30:04.375|64.960|100 TWTR|09:30:04.375|64.960|100 TWTR|09:30:04.375|64.960|100 TWTR|09:30:04.375|64.970|100 TWTR|09:30:04.375|64.970|300 TWTR|09:30:04.375|64.970|190 TWTR|09:30:04.375|64.970|100 TWTR|09:30:04.425|64.960|400 TWTR|09:30:04.425|64.970|10 TWTR|09:30:04.425|64.970|90
5. Running the Jfree Candlestick Chart
We create JfreeCandlestickChartDemo.java
class which serve as main class to running our example.
JfreeCandlestickChartDemo.java:
package com.fx.jfree.chart.demo; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.SwingUtilities; import com.fx.jfree.chart.candlestick.JfreeCandlestickChart; import com.fx.jfree.chart.common.FxMarketPxFeeder; /** * The Class JfreeCandlestickChartDemo. * * @author ashraf */ @SuppressWarnings("serial") public class JfreeCandlestickChartDemo extends JPanel { /** * Create the GUI and show it. For thread safety, * this method should be invoked from the * event-dispatching thread. */ private static void createAndShowGUI() { //Make sure we have nice window decorations. JFrame.setDefaultLookAndFeelDecorated(true); //Create and set up the window. JFrame frame = new JFrame("JfreeCandlestickChartDemo"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //Create and set up the chart. JfreeCandlestickChart jfreeCandlestickChart = new JfreeCandlestickChart("TWTR"); new FxMarketPxFeeder(jfreeCandlestickChart, "/twtr.csv", 2).run(); frame.setContentPane(jfreeCandlestickChart); //Disable the resizing feature frame.setResizable(false); //Display the window. frame.pack(); frame.setVisible(true); } public static void main(String[] args) { //creating and showing this application's GUI. SwingUtilities.invokeLater(new Runnable() { public void run() { createAndShowGUI(); } }); } }
Output:
6. Download the Source Code
This was an example to show how to create a Jfree candlestick chart.
You can download the full source code of this example here: JfreeCandlestickChartExampleCode.zip
Hi, Thanks for the demo. I ran it ok in eclipse, but the chart is very slow to construct. I wanted to try running it from the CLI but I get errors about missing packages and symbols. I think I need to list the class paths in the command but can’t work it out. Any recommendations?
Thanks
Hi. Here is class Trade. Where is description of this class? I couldn’t find it ((