2017 update
A lot has changed in the world of JavaScript and D3 since this post was written. Check out the d3fc project to see what some of the ideas in this post have evolved into!
Comparison charts, as their name suggests, are great for comparing the percentage price change of multiple stocks in time. In this post, we’ll make one using D3.
This post continues a series of posts on making financial charts using D3. We’ve seen how to use the pattern of reusable components to make simple OHLC and candlestick charts with annotations, technical studies and interactive navigators, as well as how to boost performance when panning and zooming.
Here’s what we’ll be making.
Comparison Series Component
First, a word about data. We’ll assume that our chart data is an array of objects, each with name
and data
properties. The data
property will be an array of price objects with date
, open
, high
, low
, and close
properties, sorted by date
. Our component will plot the percentage change of the close
prices for each named series.
We’ll take Mike Bostock’s Multi-Series Line Chart as a starting point. It plots a line for each data series, each with a colour given from a d3.scale.category10
ordinal scale that maps series names to colours. We’ll package the line drawing and colour mapping into a component using the usual D3 component convention. Our component will also need to calculate the percentage change of prices from an initial date (the leftmost date on the x axis), and update the y scale to reflect the minimum and maximum visible percentage changes.
Below is an outline of the comparison series component. We’ll create/update the lines and update the yScale domain in the comparison
function. The lines themselves will be drawn using the d3.svg.line
component.
Gridlines
The standard way to draw gridlines with D3 is by obtaining ticks from a scale using scale.ticks()
, and drawing lines using the tick values. Guess what? We can encapsulate this as a component!
Putting it Together
One of the biggest strengths of the component pattern is the ease in which we can add new components to an existing chart, or swap out components of an existing chart to make a new one. Suppose we had a chart set up with margins, scales, axes, a plot area, and a series (like the OHLC chart from this post). Then we can build a comparison chart with gridlines using our new components very easily.
First we make instances of our components.
Then we add them to the preexisting plot area:
We’ll get zooming and panning working by using a D3 zoom behaviour. We’ll implement semantic zooming, and we’ll use Andy Aiken’s trick of limiting the panning extent by compensating for any overshoot of the zoom behaviour’s x translation. Our zoomed
listener looks like this:
Finally, we need to format the y axis to show a percentage.
Geometric Zooming
In an earlier post, we discussed the differences between semantic zooming (redraw an element to reflect new scale domains) and geometric zooming (transform the element to where it should be given the new scale domains). We saw that geometric zooming generally performed better, with some caveats.
A disadvantage of geometric zooming was the relative complexity of the implementation compared to semantic zooming with features like an automatically updating y scale. This is true for our comparison series component as well. On zoom, we can’t just apply a single transformation to the comparison series element - the series lines need to be moved independently to their new positions.
A solution is to have the component itself implement geometric zooming of the series lines, by internally computing a transformation for each line. Each transformation is the composition of 2 transformations - one to move the line to reflect the new initial date on the x axis, and one to reflect the updated y scale domain.