Basile Simon (blog) Data, hackery, stories

Surfacing important news for a company with Yahoo Finance API and d3.js (part 2)

After a good start last week, we now know how to get a company share value's historical data with Yahoo Finance API. That's as simple as speaking YQL.

Today, we're going to have a look at how to graphically represent this data, with our good friend d3.js.
For those who don't know d3.js, it is a very popular Javascript library developed by New York Times' Mike Bostock. It is widely used for data-visualisaton, largely because of its impressive capacities when it comes to transitions and its wide range of possible visualisations.

Here's a little souvenir from the last article, in which we formulated the query to Yahoo and prepared its use with jQuery:

var myQuery = " * from where symbol in ("AAPL") and startDate = "2013-01-01" and endDate = "2014-01-01"&";
var data = $.getJSON(myQuery, foo);
function foo(data) {

The data variable contains our JSON blob, and the $.getJSON() bit calls a function named foo() - we will change this name soon.
Let's dive right into it by setting up the viewport, i.e. the box in which we will place the chart:

var margin = 50,
    width = 700,
    height = 600;

Now, our data is still resting quietly in the data variable, and we are going to use it right away.
Shall we? Throw a function in your code that takes our data variable as a parameter. How? Like this:

function draw(data) {
    //nasty d3 stuff happening here

Then, let's declare our extents (from where to where our array goes), so we can declare our scales, which we need for our axis.

// Extents
var x_extent = d3.extent( { return; }));
var y_extent = d3.extent( { return +d.Close; }));
// Scales
var y_scale = d3.scale.linear()
var x_scale = d3.time.scale()
                .range([margin, width-margin]);
// Axis
var x_axis = d3.svg.axis()
var y_axis = d3.svg.axis()

I know, that is very painful to go through all this, I do agree. What we just did was basically mapping our arrays (so the program is able to iterate through them), and then using d3's capabilities to create a y-axis that is linear (we want to populate it with US dollars values), and an x-axis that follows time.
We are almost ready. Let's make sure that our visualisation goes somewhere, and let's draw the axis. To do so, append a SVG element to your HTML document, and append to this SVG element your two axis:

var vis ="body")
              .attr("width", width)
              .attr("height", height);"svg")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + (height-margin) + ")")
    .attr("class", "y axis")
    .attr("transform", "translate(" + margin + ", 0 )")

Now. We want to draw a line chart of the price, right? Have a look at this clear example from the master himself.
We are going to call d3.svg.line, but with a subtlety:

var line = d3.svg.line()
             .x(function(d){ return x_scale(; })
             .y(function(d){ return y_scale(d.Close); })

See what I did there?
On the y-axis, we will have the daily value, by accessing through d.Close; and on the x-axis, the date. But wait, the name should be d.Date, right?
Right. I forgot to mention. There is some preparation to do here.

Outside of draw(data), declare a date format so d3 knows we're doing business with dates - and how it has to deal with them. Like that:

var format = d3.time.format("%Y-%m-%d");

Then, inside draw(data), parse the date accordingly (we are making sure here that d3 understands what to put on the axis).

data.query.results.quote.forEach(function(d) { = format.parse(d.Date);
  d.Close = +d.Close;
data.query.results.quote.sort(function(a,b) {
    return d3.ascending(,;

Append the SVG path to the big SVG element...

var path = vis.append("path")
              .style("fill", "none")
              .attr("class", "line")
              .attr("d", line);

We also need labels under our axis. Luckily, that's easy.".y.axis")
  .text("lol much text")
  .attr("transform", "rotate (-90, -43, 0) translate(-280)");".x.axis")
  .text("such axis")
  .attr("x", function(){return (width / 2) - margin;})
  .attr("y", margin/1.5);

And voilà! You're good to go!
If you want some sort of transition while the chart draws, here's an esoteric solution:

var totalLength = path.node().getTotalLength();
  .attr("stroke-dasharray", totalLength + " " + totalLength)
  .attr("stroke-dashoffset", totalLength)
    .attr("stroke-dashoffset", 0);

You may now take a breath.

Next week, we will look at how to use HTML inputs to make something more dynamic than what we have at the moment.