An open-high-low-close (OHLC) chart is a type of financial chart used to show price movements and help identify trends in a financial instrument over time. For each unit of time, a vertical line is plotted showing the highest and lowest prices reached in that time. Horizontal tick marks are plotted on each side of the line - the opening price for that time period on the left, and the closing price on the right. Usually an OHLC line will be coloured green if on that day the closing price exceeded the opening price (an ‘up day’), and coloured red if not (a ‘down day’).
While D3 does have a component to draw a line series on a chart (see here), it does not have an inbuilt component we can use to render an OHLC series. In this post, we’ll make one.
Reusable Chart Components
We’ll use D3 creator Mike Bostock’s convention for creating reusable components in D3. Essentially this means our component will be a closure with getter-setter methods. This follows the same pattern used by other D3 components and plugins, so will allow us to treat our OHLC component just like any other D3 component.
We’ll assume our data is an array of objects that look like this:
Here’s what the OHLC component will look like internally:
Here, we are attaching our component to the
sl.series namespace object. This gives us a nice way to organise the components we write. For example, if we were to implement an axis component, it could go in
Internally, we have 2 scales,
yScale. We’ll need these to map the dates and prices of our input to pixel positions on our chart. The scales are initialised to be default D3 scales. This allows us to use the component without attaching scales, although typically we’ll set them to the scales used by our axes.
These scales are exposed to users of the component using getter/setter functions. For example, calling the
xScale function with no arguments returns the internal
xScale, and calling it with one or more arguments sets the internal
xScale to the first argument. When called with arguments, these functions return the OHLC function. This allows setter calls to be chained together.
We’ll draw the OHLC bars in the
ohlc function returned by the component. We’ll use D3’s General Update Pattern. This is an important D3 concept. In simple terms, we
select page elements that may or may not exist, and bind data to these elements. Page elements are then created, updated or removed as necessary to reflect the data. Creation happens in the
enter() selection, updating in the update selection, and removal in the
exit() selection. This is nice because we can use the same function to both create and update our component to reflect changes in the bound data or in configuration.
To use an instance of the component, we’ll set an xScale and a yScale, bind data to a selection, then call the component on the selection. This will draw the series on the selection. This is how we’ll use our component when we come to drawing the chart.
Let’s go ahead and implement the
ohlc create/update function.
First we need an SVG group element to contain our series on the selection. We’ll use the general update pattern. We want just one element to be created, so we’ll bind the whole array of data to the ‘ohlc-series’ element. Now we can create this element in the
Next we need a group for each OHLC bar of our series. We will select all the elements with class ‘bar’ and bind a price object to each one. This time, we’ll also include a key function which returns the price object’s date. While not really necessary for this example, if we wanted to use D3’s transitions to animate updates to the data, this would ensure that D3 can match up existing bars with their new data, making for smooth animation. This idea is called Object Constancy.
With the series data bound, we can create the bar groups in the
enter() selection. In the update selection, we will give them a the css class ‘up-day’ or ‘down-day’ depending on the difference in opening and closing price. This means that we will be able to give colours to the up-day and down-day bars with CSS.
All that’s left to do is to draw the lines inside each bar. We need to draw 3 lines for each bar - one extending from the low price to the high price, and 2 horizontal ticks for the open and closing prices. First, in the body of
sl.series.ohlc, we will set up a
d3.svg.line with appropriate x and y accessors. This means that we will be able to generate svg lines by supplying
line with an array of points.
For each line, we’ll select a path element by its class and then append a path element in the
enter() selection. The
enter() selection is returned by binding data using
selection.data, but what data should we bind?
In this case we want to bind the price object that’s already bound to the the parent bar group element. It turns out that for this type of multiple selection we need to give a function to
selection.data which returns an array containing the elements to bind (see here). In the update selection, we will draw the line, scaling all x and y coordinates with the xScale and yScale respectively.
We’ll put the high low line create/update code in a function of its own which we will call from the OHLC create/update function. It looks like this:
It’s a similar situation for drawing the the open/close ticks:
We’ve built the component, so now let’s use it in a chart! First we’ll set up margins, scales, axes and our series.
Next, we will create an svg element. We’ll assume that our html page has a
div with id ‘chart’ to draw this inside. Following the usual D3 margin convention, we’ll draw our chart in a group element that translates the origin to the top left corner of the chart area. Our series will be drawn in the ‘plotArea’ - a group element which references a clipPath. The clipPath contains a
rect with dimensions equal to the inner dimensions of our chart. This will ensure that OHLC bars for dates that lie outside domain of the axes are not shown.
Next, we set the domains and ranges of the scales. For this example, we’ll have the x domain extend to a day after the most recent data point from about 1 month before the most recent data point. The y domain will extend from the lowest ‘low’ to the highest ‘high’ in our data.
Finally, we can draw the axes and the series.
We’ll colour the up-day and down-day bars with css by styling the
stroke property of the paths:
Here’s the end result:
Candlestick charts are very similar to OHLC charts, so with a small modification we can turn our OHLC component into a candlestick component. All that’s needed is to draw a rectangle between the open and close price instead of the open close ticks. The rectangle create/update function looks like this:
We’ll need to style the
fill property of the rectangles to get the right colours.
sl.series.candlestick component is identical to the
sl.series.ohlc component, but its create/update function calls
rectangle instead of
openCloseTicks. Since the components share a lot of code, we could have a function which contains the common code, takes
openCloseTicks as input, and produces the required component (we’ll leave out the details for now).
We can use the same code we used to create the OHLC chart. We just have to replace
sl.series.candlestick when creating the series. Here’s what our candlestick chart looks like:
We have made 2 reusable components for financial charts with D3. This is really just the beginning of what we would need for a fully featured financial chart. There are many components we could make using this pattern, including technical studies, comparison series and chart navigators. However, with these simple examples, we can already see the power of breaking chart features into reusable components. It would also be important to see how well these charts perform for large data sets. Ideally, we should be able to smoothly pan and zoom an OHLC chart which shows multiple years of prices. We’ll look at that in another post, where we’ll improve our OHLC component so that it is optimised for panning and zooming.