Skip to content

Instantly share code, notes, and snippets.

@abeppu
Last active April 24, 2023 13:19
Show Gist options
  • Star 23 You must be signed in to star a gist
  • Fork 10 You must be signed in to fork a gist
  • Save abeppu/1074045 to your computer and use it in GitHub Desktop.
Save abeppu/1074045 to your computer and use it in GitHub Desktop.
candlestick charts using d3
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.js?1.25.0"></script>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.time.js?1.25.0"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
</head>
<body>
<div id="chart"></div>
<script type="text/javascript">
var width = 900;
var height = 500;
String.prototype.format = function() {
var formatted = this;
for (var i = 0; i < arguments.length; i++) {
var regexp = new RegExp('\\{'+i+'\\}', 'gi');
formatted = formatted.replace(regexp, arguments[i]);
}
return formatted;
};
var dateFormat = d3.time.format("%Y-%m-%d");
var end = new Date();
var start = new Date(end.getTime() - 1000 * 60 * 60 * 24 * 60);
var data = [];
function min(a, b){ return a < b ? a : b ; }
function max(a, b){ return a > b ? a : b; }
function buildChart(data){
var margin = 50;
var chart = d3.select("#chart")
.append("svg:svg")
.attr("class", "chart")
.attr("width", width)
.attr("height", height);
var y = d3.scale.linear()
.domain([d3.min(data.map(function(x) {return x["Low"];})), d3.max(data.map(function(x){return x["High"];}))])
.range([height-margin, margin]);
var x = d3.scale.linear()
.domain([d3.min(data.map(function(d){return dateFormat.parse(d.Date).getTime();})),
d3.max(data.map(function(d){return dateFormat.parse(d.Date).getTime();}))])
.range([margin,width-margin]);
chart.selectAll("line.x")
.data(x.ticks(10))
.enter().append("svg:line")
.attr("class", "x")
.attr("x1", x)
.attr("x2", x)
.attr("y1", margin)
.attr("y2", height - margin)
.attr("stroke", "#ccc");
chart.selectAll("line.y")
.data(y.ticks(10))
.enter().append("svg:line")
.attr("class", "y")
.attr("x1", margin)
.attr("x2", width - margin)
.attr("y1", y)
.attr("y2", y)
.attr("stroke", "#ccc");
chart.selectAll("text.xrule")
.data(x.ticks(10))
.enter().append("svg:text")
.attr("class", "xrule")
.attr("x", x)
.attr("y", height - margin)
.attr("dy", 20)
.attr("text-anchor", "middle")
.text(function(d){ var date = new Date(d * 1000); return (date.getMonth() + 1)+"/"+date.getDate(); });
chart.selectAll("text.yrule")
.data(y.ticks(10))
.enter().append("svg:text")
.attr("class", "yrule")
.attr("x", width - margin)
.attr("y", y)
.attr("dy", 0)
.attr("dx", 20)
.attr("text-anchor", "middle")
.text(String);
chart.selectAll("rect")
.data(data)
.enter().append("svg:rect")
.attr("x", function(d) { return x(dateFormat.parse(d.Date).getTime()); })
.attr("y", function(d) {return y(max(d.Open, d.Close));})
.attr("height", function(d) { return y(min(d.Open, d.Close))-y(max(d.Open, d.Close));})
.attr("width", function(d) { return 0.5 * (width - 2*margin)/data.length; })
.attr("fill",function(d) { return d.Open > d.Close ? "red" : "green" ;});
chart.selectAll("line.stem")
.data(data)
.enter().append("svg:line")
.attr("class", "stem")
.attr("x1", function(d) { return x(dateFormat.parse(d.Date).getTime()) + 0.25 * (width - 2 * margin)/ data.length;})
.attr("x2", function(d) { return x(dateFormat.parse(d.Date).getTime()) + 0.25 * (width - 2 * margin)/ data.length;})
.attr("y1", function(d) { return y(d.High);})
.attr("y2", function(d) { return y(d.Low); })
.attr("stroke", function(d){ return d.Open > d.Close ? "red" : "green"; })
}
function appendToData(x){
if(data.length > 0){
return;
}
data = x.query.results.quote;
for(var i=0;i<data.length;i++){
data[i].timestamp = (new Date(data[i].date).getTime() / 1000);
}
data = data.sort(function(x, y){ return dateFormat.parse(x.Date).getTime() - dateFormat.parse(y.Date).getTime(); });
buildChart(data);
}
function buildQuery(){
var symbol = window.location.hash;
if(symbol === ""){
symbol = "AMZN";
}
symbol = symbol.replace("#", "");
var base = "select * from yahoo.finance.historicaldata where symbol = \"{0}\" and startDate = \"{1}\" and endDate = \"{2}\"";
var getDateString = d3.time.format("%Y-%m-%d");
var query = base.format(symbol, getDateString(start), getDateString(end));
query = encodeURIComponent(query);
var url = "http://query.yahooapis.com/v1/public/yql?q={0}&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys&callback=appendToData".format(query);
return url;
}
function fetchData(){
url = buildQuery();
scriptElement = document.createElement("SCRIPT");
scriptElement.type = "text/javascript";
// i add to the url the call back function
scriptElement.src = url;
document.getElementsByTagName("HEAD")[0].appendChild(scriptElement);
}
$(document).ready(fetchData);
</script>
</body>
</html>
@mbostock
Copy link

Nice example! For getDateString, you can use d3.time.format. Something like:

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

You can also use the built-in Math.min and Math.max functions. And lastly, a handy trick for sorting numbers:

data.sort(function(x, y) { return x.timestamp - y.timestamp; });

This has the nice property that it returns 0 if the timestamps are equal.

@datakungfu
Copy link

Hi Mike and Aaron,
Is it possible to remove the weekends from the chart(i.e., continuous candles)?

Thanks,
Tim

@timelyportfolio
Copy link

really appreciate the example; hope to build on it soon. thanks.

@eyeti
Copy link

eyeti commented Sep 14, 2013

hi abeppu, could you provide the raw query that would work on http://developer.yahoo.com/yql/console/.
I need to see the format of the data Yahoo returns so I can format my data accordingly.

I have tried

http://developer.yahoo.com/yql/console/#h=select+*+from+yahoo.finance.historicaldata+where+symbol+%3D+%22AMZN%22+and+startDate+%3D+%222013-01-01%22+and+endDate+%3D+%222013-02-01%22

and it gives me a 'No definition found for Table yahoo.finance.historicaldata' error

@jyek
Copy link

jyek commented Nov 16, 2014

Nice example! For this to work as of today, I had to change this line:

data[i].timestamp = (new Date(data[i].date).getTime() / 1000);

to this

data[i].timestamp = (new Date(data[i]['Date']).getTime() / 1000);

@xmspix
Copy link

xmspix commented Sep 29, 2015

Thank you for the code, how can I make this chart interactive like in this example: http://bl.ocks.org/andredumas/edf630690c10b89be390

Can anyone help please ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment