# An interactive Stock Comparison Chart with D3

##### 7 min read

·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 - try zooming and panning the chart!

## 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.

The 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.

This turns out to be another win for the component pattern - components can internally implement an optimised zoom method, so the component user gets fast zooming without any extra effort.

The chart below uses geometric zooming for the series lines, and should perform better for larger datasets.

For full code of the components, see GitHub.