Creating candlestick charts in Javascript

Introduction

In Keynesian economics, price is where supply meets demand. Inefficiencies, such as price differences between markets, means there is no singular price at any given time. Even within markets, price is defined somewhere between bid prices and asking prices. This is best shown with an order book (see below).

Here, the solid portion of the candle within the buy-sell spread defines the open & close price, and the candle “wick” defines the maximum and minimum price range.

For this reason, candlestick charts are often used in finance to illustrate ranges of prices. This post will detail how to create candlestick charts within Javascript.

Setup

First, create an HTML file as follows. I am using Techan.js and d3 to display the charts, and Bootstrap for my CSS style.

<!DOCTYPE html>
<html>
    <head>
        <link rel="stylesheet" href="./static/bootstrap.min.css">
        <link rel="stylesheet" href="./static/style.css">
        <title>Techan Example</title>
    </head>
    <body>
        <script src="./static/jquery.min.js"></script>
        <script src="./static/bootstrap.min.js"></script>
        <script src="./static/d3.v4.min.js"></script>
        <script src="./static/techan.min.js"></script>
        <script src="./static/custom.js"></script>
    </body>
</html>

Next, add a stage for the chart within the body.

<div id="ohlcv" class="d-flex justify-content-center">
</div>
<div class="d-flex justify-content-center">
    <div class="btn-group btn-group-toggle" data-toggle="buttons">
        <label class="btn btn-sm btn-secondary">
        <input type="radio" autocomplete="off" checked="" onchange="draw('1d')"> 1D
        </label>
        <label class="btn btn-sm btn-secondary">
        <input type="radio" autocomplete="off" checked="" onchange="draw('1w')"> 1W
        </label>
        <label class="btn btn-sm btn-secondary">
        <input type="radio" autocomplete="off" checked="" onchange="draw('1m')"> 1M
        </label>
    </div>
</div>

Next, add styles for our candles within the style.css file.

#ohlcv {
  font: 10px sans-serif;
}
path.candle {
  stroke: #000000;
}
path.candle.body {
  stroke-width: 0;
}
path.candle.up {
  fill: #00AA00;
  stroke: #00AA00;
}
path.candle.down {
  fill: #FF0000;
  stroke: #FF0000;
}

Now we will add our Javascript within the custom.js file. This is where the magic happens.

var margin = {top: 20, right: 20, bottom: 30, left: 60},
        width = 960 - margin.left - margin.right,
        height = 500 - margin.top - margin.bottom;

var parseDate = d3.timeParse("%Y-%m-%dT%H:%M:%S%Z");

var x = techan.scale.financetime()
        .range([0, width]);

var y = d3.scaleLinear()
        .range([height, 0]);

var candlestick = techan.plot.candlestick()
        .xScale(x)
        .yScale(y);

var xAxis = d3.axisBottom()
        .scale(x);

var yAxis = d3.axisLeft()
        .scale(y);

function draw(freq="1d") {
    d3.select("#ohlcv svg").remove();
    var svg = d3.select("#ohlcv").append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
        .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    d3.json("./static/data/" + freq + ".json", function(error, data) {
        var accessor = candlestick.accessor();
        data = data.map(function(d) {
          return {
              date:   parseDate(d.date),
              open:   +d.open,
              high:   +d.high,
              low:    +d.low,
              close:  +d.close,
              volume: +d.volume
          };
        });
        data = data.slice(data.length-1440, data.length);
        svg.append("g")
            .attr("class", "candlestick");

        svg.append("g")
            .attr("class", "x axis")
            .attr("transform", "translate(0," + height + ")");

        svg.append("g")
            .attr("class", "y axis")
            .append("text")
            .attr("transform", "rotate(-90)")
            .attr("y", 6)
            .attr("dy", ".71em")
            .style("text-anchor", "end");

        x.domain(data.map(candlestick.accessor().d));
        y.domain(techan.scale.plot.ohlc(data, candlestick.accessor()).domain());

        svg.selectAll("g.candlestick").datum(data).call(candlestick);
        svg.selectAll("g.x.axis").call(xAxis);
        svg.selectAll("g.y.axis").call(yAxis);

    });
}
draw();

Finally, we will need some financial data. In this example, I am using AAPL stock. This code is set up for loading a local JSON file within “./static/data/1d.json”, but an API can also be used. Here is the format:

[
  {
    "date": "2019-01-02T00:00:00Z",
    "open": 154.89,
    "high": 158.85,
    "low": 154.23,
    "close": 157.92,
    "volume": 37039700
  },
  ...
  {
    "date": "2019-12-27T00:00:00Z",
    "open": 291.12,
    "high": 293.97,
    "low": 288.12,
    "close": 289.8,
    "volume": 36566500
  },
  {
    "date": "2019-12-30T00:00:00Z",
    "open": 289.46,
    "high": 292.69,
    "low": 285.22,
    "close": 291.52,
    "volume": 36028600
  }
]

Result

Feb 2019 Mar 2019 Apr 2019 May 2019 Jun 2019 Jul 2019 Aug 2019 Sep 2019 Oct 2019 Nov 2019 Dec 2019 140 160 180 200 220 240 260 280




About the author



Hi, I'm Nathan. I'm an electrical engineer in the Los Angeles area. Keep an eye out for more content being posted soon.


Leave a Reply

Your email address will not be published. Required fields are marked *