Skip to content

Instantly share code, notes, and snippets.

@k-izzo
Created June 2, 2014 13:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save k-izzo/62571bf97696ac46fb7f to your computer and use it in GitHub Desktop.
Save k-izzo/62571bf97696ac46fb7f to your computer and use it in GitHub Desktop.
candlesticks
date open high low close vol
May 30, 2014 560.80 561.35 555.91 559.89 1,766,300
May 29, 2014 563.35 564 558.71 560.08 1,350,400
May 28, 2014 564.57 567.84 561 561.68 1,647,500
May 27, 2014 556 566 554.35 565.95 2,098,400
May 23, 2014 547.26 553.64 543.7 552.7 1,926,900
May 22, 2014 541.13 547.6 540.78 545.06 1,611,400
May 21, 2014 532.9 539.18 531.91 538.94 1,193,000
May 20, 2014 529.74 536.23 526.3 529.77 1,779,900
May 19, 2014 519.7 529.78 517.58 528.86 1,274,300
May 16, 2014 521.39 521.8 515.44 520.63 1,481,200
May 15, 2014 525.7 525.87 517.42 519.98 1,699,700
May 14, 2014 533 533 525.29 526.65 1,188,500
May 13, 2014 530.89 536.07 529.51 533.09 1,646,400
May 12, 2014 523.51 530.19 519.01 529.92 1,907,300
May 9, 2014 510.75 519.9 504.2 518.73 2,431,800
May 8, 2014 508.46 517.23 506.45 511 2,015,800
May 7, 2014 515.79 516.68 503.3 509.96 3,215,500
May 6, 2014 525.23 526.81 515.06 515.14 1,684,400
May 5, 2014 524.82 528.9 521.32 527.81 1,021,300
May 2, 2014 533.76 534 525.61 527.93 1,683,900
May 1, 2014 527.11 532.93 523.88 531.35 1,900,300
Apr 30, 2014 527.6 528 522.52 526.66 1,746,400
Apr 29, 2014 516.9 529.46 516.32 527.7 2,691,700
Apr 28, 2014 517.18 518.6 502.8 517.15 3,326,400
Apr 25, 2014 522.51 524.7 515.42 516.18 2,094,600
Apr 24, 2014 530.07 531.65 522.12 525.16 1,878,000
Apr 23, 2014 533.79 533.87 526.25 526.94 2,046,700
Apr 22, 2014 528.64 537.23 527.51 534.81 2,358,900
Apr 21, 2014 536.1 536.7 525.6 528.62 2,559,700
Apr 17, 2014 548.81 549.5 531.15 536.1 6,790,900
Apr 16, 2014 543 557 540 556.54 4,879,900
Apr 15, 2014 536.82 538.45 518.46 536.44 3,844,500
Apr 14, 2014 538.25 544.1 529.56 532.52 2,568,000
Apr 11, 2014 532.55 540 526.53 530.6 3,914,100
Apr 10, 2014 565 565 539.9 540.95 4,025,800
Apr 9, 2014 559.62 565.37 552.95 564.14 3,321,700
Apr 8, 2014 542.6 555 541.61 554.9 3,142,600
Apr 7, 2014 540.74 548.48 527.15 538.15 4,389,600
Apr 4, 2014 574.65 577.77 543 543.14 6,351,900
Apr 3, 2014 569.85 587.28 564.13 569.74 5,085,200
Apr 2, 2014 599.99 604.83 562.19 567 146,700
Apr 1, 2014 558.71 568.45 558.71 567.16 7,900
<!DOCTYPE html>
<meta charset="utf-8">
<head><link rel='stylesheet' type='text/css' href='style.css'></head>
<body style='background-color:lightgray'>
<div id='container'></div>
<script src='http://d3js.org/d3.v3.min.js'></script>
<script>
var format_date = function (raw) {
var formatted = raw;
if (raw.length !== 12) {
formatted = formatted.slice(0, 4) + '0' + formatted.slice(4);
}
return formatted;
};
var parse_date = d3.time.format("%b %d, %Y");
var scale = 0.85,
chart_w = 950 * scale,
chart_h = 600 * scale,
top_h = chart_h * 0.65,
margin = {left: 140 * scale, top: 140 * scale, right: 50 * scale, bottom: 130 * scale, mid: 80 * scale};
d3.select('#container')
.style({
'margin-left': -1 * (chart_w + margin.left + margin.right)/2 + 'px',
'margin-top': -1 * (chart_h + margin.top + margin.bottom)/2 + 'px',
width: (chart_w + margin.left + margin.right) + 'px',
height: (chart_h + margin.top + margin.bottom) + 'px'
});
var get_global_range = function (objects) {
var global_min = objects[0].open, global_max = objects[0].open,
local_min, local_max;
var get_local_range = function (object) {
var min, max, values = [];
Object.keys(object).forEach(function (prop) {
if (prop !== 'date' && prop !== 'vol') { values.push(object[prop]); }
});
values.sort(function (a, b) { return a - b; });
min = values[0];
max = values[values.length - 1];
return [min, max];
};
objects.slice(1).forEach(function (object) {
local_min = get_local_range(object)[0];
if (local_min < global_min) { global_min = local_min; }
local_max = get_local_range(object)[1];
if (local_max > global_max) { global_max = local_max; }
});
return [global_min, global_max];
};
d3.csv('data.csv', function (data) {
data.forEach(function (d) {
d.date = parse_date.parse(format_date(d.date));
d.open = +d.open;
d.close = +d.close;
d.low = +d.low;
d.high = +d.high;
d.vol = +(d.vol.replace(/,/g,'')) / 1000000;
});
data.reverse()
var xs = d3.scale.ordinal()
.domain(d3.range(data.length))
.rangePoints([0, chart_w], 0.05);
var price_range = get_global_range(data),
price_min = price_range[0],
price_max = price_range[1];
var ys = d3.scale.linear()
.domain([price_min, price_max])
.range([top_h, 0]);
var vol_max = d3.max(data, function (d) { return d.vol; });
var vol_ys = d3.scale.linear()
.domain([0, vol_max])
.range([chart_h, top_h + margin.mid]);
var area = d3.svg.area()
.x(function (d, i) { return xs(i); })
.y0(chart_h)
.y1(function (d) { return vol_ys(d.vol); })
.interpolate('cardinal')
.tension(0.85);
var line = d3.svg.line()
.x(function (d, i) { return xs(i); })
.y(function (d) { return vol_ys(d.vol); })
.interpolate('cardinal')
.tension(0.85);
var yaxis = d3.svg.axis()
.scale(ys)
.orient('left')
.ticks(5);
var vol_yaxis = d3.svg.axis()
.scale(vol_ys)
.orient('left')
.ticks(4);
var tvals = [];
data.forEach(function (d) {
var insert = parse_date(d.date).slice(0, 6);
if (insert[0] !== 'x') {
tvals.push(insert);
} else {
tvals.push(insert.slice(1));
}
});
var xaxis = d3.svg.axis()
.scale(xs)
.orient('bottom')
.tickFormat('');
var vis = d3.select('#container')
.append('svg')
.attr({
width: chart_w + margin.left + margin.right,
height: chart_h + margin.top + margin.bottom
})
.append('g')
.attr('transform', 'translate(' + [margin.left, margin.top] + ')');
vis.append('path')
.classed('area_fill', true)
.attr('d', area(data, function (d) { return d.vol; }));
vis.append('path')
.classed('area_line', true)
.attr('d', line(data, function (d) { return d.vol; }));
var sticks = vis.selectAll('.stick')
.data(data)
.enter()
.append('g')
.classed('stick', true)
.attr('transform', function (d, i) { return 'translate(' + [xs(i), 0] + ')'; });
var tops = sticks.append('line')
.classed('wick', true)
.attr({
x1: 0,
y1: function (d) { return ys(d.high) },
x2: 0,
y2: function (d) { return ys(Math.max(d.open, d.close)); }
});
var bottoms = sticks.append('line')
.classed('wick', true)
.attr({
x1: 0,
y1: function (d) { return ys(Math.min(d.open, d.close)); },
x2: 0,
y2: function (d) { return ys(d.low); }
});
var mids = sticks.append('rect')
.attr({
class: function (d) { return d.close > d.open ? 'pos' : 'neg'; },
x: -7,
y: function (d) { return ys(Math.max(d.open, d.close)); },
width: 14,
height: function (d) { return ys(Math.min(d.open, d.close)) - ys(Math.max(d.open, d.close)); }
});
var verts = sticks.append('rect')
.classed('vert', true)
.attr({
x: -7,
y: 0,
width: 14,
height: chart_h
})
.style('opacity', 0);
sticks.on('mouseenter', function () {
d3.select(this).select('rect').style('fill-opacity', 1);
var tip_group = d3.select(this)
.append('g')
.classed('tip_group', true)
.attr('transform', function (d) { return 'translate(' + [0, ys(d.high) - 71] + ')'; })
.style('opacity', 0);
tip_group.append('text')
.text(function (d) { return d.date.toString().slice(4,10); })
.attr({
x: 0,
y: -4
})
.style('text-anchor', 'middle')
.style('font-size', 14);
tip_group.append('rect')
.attr({
x: -30,
y: 0,
width: 60,
height: 59
})
tip_group.selectAll('.interior_lab')
.data(['open:', 'close:', 'high:', 'low:', 'vol:'])
.enter()
.append('text')
.classed('interior_lab', true)
.text(function (d) { return d; })
.attr({
x: 1,
y: function (d, i) { return 8 + i*11; }
})
.style({
'font-size': '12px',
'text-anchor': 'end',
'alignment-baseline': 'text-after-edge'
});
var pts = ['open', 'close', 'high', 'low', 'vol'];
pts.forEach(function (x, j) {
tip_group.append('text')
.text(function (d) { return x === 'vol' ? Math.round(d[x]*10)/10 : Math.round(d[x]); })
.attr({
x: 5,
y: 8 + j * 11
})
.style({
'font-size': 12,
'alignment-baseline': 'text-after-edge'
});
})
tip_group.append('circle')
.attr({
cx: function (d, i) { return xs(i); },
cy: function (d) { return 71 - ys(d.high) + vol_ys(d.vol); },
r: 5
})
tip_group.style('opacity', 1)
})
.on('mouseout', function () {
d3.select(this).select('rect').style('fill-opacity', 0.45);
d3.select('.tip_group').remove()
})
vis.append('g')
.classed('axis', true)
.attr('transform', 'translate(' + [-20, 0] + ')')
.call(yaxis);
vis.append('g')
.classed('axis', true)
.attr('transform', 'translate(' + [-20, 0] + ')')
.call(vol_yaxis);
var xaxis_group = vis.append('g')
.classed('axis', true)
.attr('transform', 'translate(' + [0, chart_h + 30] + ')')
.call(xaxis)
var x_lab_group = xaxis_group.append('g')
.classed('x_labs', true)
.attr('transform', 'translate(' + [0, 18] + ')')
var x_labs = x_lab_group.selectAll('.x_lab')
.data(tvals)
.enter()
.append('g')
.attr('transform', function (d, i) { return 'translate(' + [5 + xs(i), 0] + ')'; })
x_labs.append('text')
.text(function (d) { return d; })
.attr({
transform: 'rotate(-50)',
})
.style({
'text-anchor': 'end',
'font-size': '14px'
})
vis.append('text')
.text('GOOG Daily Performace')
.classed('large_label', true)
.attr({
x: chart_w / 2,
y: 0 - margin.top / 2 - 25,
'text-anchor': 'middle',
'alignment-baseline': 'baseline'
});
vis.append('text')
.text('Apr 1 - May 30, 2014')
.classed('small_label', true)
.attr({
x: chart_w / 2,
y: 0 - margin.top / 2,
'text-anchor': 'middle',
'alignment-baseline': 'baseline'
});
vis.append('text')
.text('Price')
.classed('large_label', true)
.attr({
x: -20,
y: -35,
'text-anchor': 'end'
});
vis.append('text')
.text('($/share)')
.classed('small_label', true)
.attr({
x: -20,
y: -15,
'text-anchor': 'end'
});
vis.append('text')
.text('Volume')
.classed('large_label', true)
.attr({
x: -20,
y: top_h + margin.mid - 35,
'text-anchor': 'end'
});
vis.append('text')
.text('(mill. shares)')
.classed('small_label', true)
.attr({
x: -20,
y: top_h + margin.mid - 15,
'text-anchor': 'end'
});
var key_group = vis.append('g')
.attr('transform', 'translate(' + [chart_w - 250, 0 - margin.top/2 - 25] + ')scale(1)')
key_group.append('rect')
.attr({
x: 0,
y: 0,
width: 250,
height: 110
})
.style('fill', 'rgb(231, 231, 231)')
.style('fill-opacity', 1)
var symbol_groups = key_group.selectAll('.symbol')
.data(d3.range(2))
.enter()
.append('g')
.attr({
id: function (d, i) { return 'sg' + i; },
'transform': function (d, i) { return 'translate(' + [250/2 - 60 + i * 120, 0] + ')'}
});
symbol_groups.append('line')
.classed('wick', true)
.attr({
x1: 0,
y1: 20,
x2: 0,
y2: 40
})
symbol_groups.append('rect')
.classed('pos', true)
.attr({
class: function (d, i) { return d === 0 ? 'pos' : 'neg'; },
x: -7,
y: 40,
width: 14,
height: 30
})
symbol_groups.append('line')
.classed('wick', true)
.attr({
x1: 0,
y1: 70,
x2: 0,
y2: 90
})
key_group.selectAll('.high_low')
.data(['daily high', 'daily low'])
.enter()
.append('text')
.classed('key_label', true)
.text(function (d) { return d; })
.attr({
x: 250 / 2,
y: function (d, i) { return 20 + i * 70; },
'text-anchor': 'middle'
});
d3.select('#sg0').selectAll('.close_open')
.data(['close', 'open'])
.enter()
.append('text')
.classed('key_label', true)
.text(function (d) { return d; })
.attr({
x: -45,
y: function (d, i) { return 40 + i * 30; },
'text-anchor': 'middle'
});
d3.select('#sg0').selectAll('.arrow_line')
.data(d3.range(2))
.enter()
.append('line')
.attr({
x1: -27,
y1: function (d, i) { return 40 + i * 30; },
x2: -12,
y2: function (d, i) { return 40 + i * 30; }
});
d3.select('#sg1').selectAll('.arrow_line')
.data(d3.range(2))
.enter()
.append('line')
.attr({
x1: 27,
y1: function (d, i) { return 40 + i * 30; },
x2: 12,
y2: function (d, i) { return 40 + i * 30; }
});
key_group.selectAll('.arrow_line')
.data(d3.range(2))
.enter()
.append('line')
.attr({
x1: 250 / 2 - 60 + 5,
y1: function (d, i) { return 20 + i * 70; },
x2: 250 / 2 - 60 + 25,
y2: function (d, i) { return 20 + i * 70; }
});
key_group.selectAll('.arrow_line')
.data(d3.range(2))
.enter()
.append('line')
.attr({
x1: 250 / 2 + 60 - 25,
y1: function (d, i) { return 20 + i * 70; },
x2: 250 / 2 + 60 - 5,
y2: function (d, i) { return 20 + i * 70; }
});
d3.select('#sg1').selectAll('.close_open')
.data(['open', 'close'])
.enter()
.append('text')
.classed('key_label', true)
.text(function (d) { return d; })
.attr({
x: 45,
y: function (d, i) { return 40 + i * 30; },
'text-anchor': 'middle'
});
var triangle = d3.svg.line()
.x(function (d) { return d.x; })
.y(function (d) { return d.y; })
.interpolate('cardinal-closed')
.tension(1);
var points_right = [{x: -5, y: -2}, {x: -5, y: 2}, {x: 0, y: 0}],
points_left = [{x: 5, y: -2}, {x: 5, y: 2}, {x: 0, y: 0}],
arrow_coords = [
{x: 250 / 2 - 60 + 5, y: 20, dir: 'l'},
{x: 250 / 2 - 60 + 5, y: 90, dir: 'l'},
{x: 250 / 2 + 60 - 5, y: 20, dir: 'r'},
{x: 250 / 2 + 60 - 5, y: 90, dir: 'r'},
{x: 250 / 2 - 60 - 12, y: 40, dir: 'r'},
{x: 250 / 2 - 60 - 12, y: 70, dir: 'r'},
{x: 250 / 2 + 60 + 12, y: 40, dir: 'l'},
{x: 250 / 2 + 60 + 12, y: 70, dir: 'l'}
];
var ah_groups = key_group.selectAll('.ah_group')
.data(arrow_coords)
.enter()
.append('g')
.attr('transform', function (d) { return 'translate(' + [d.x, d.y] + ')'; })
ah_groups.append('path')
.attr('d', function (d) { return d.dir === 'l' ? triangle(points_left) : triangle(points_right); });
});
</script>
#container {
position: absolute;
top: 50%;
left: 50%;
}
svg {
background-color: lightgray;
}
.wick, .area_line {
stroke: gray;
stroke-width: 2;
fill: none;
}
line {
stroke: gray;
stroke-width: 1;
fill: none;
}
path {
stroke: gray;
stroke-width: 0.8;
fill: gray;
fill-opacity: .8;
}
.area_fill {
stroke: none;
fill: darkgray;
}
rect {
stroke: gray;
stroke-width: 2;
fill: rgb(231, 231, 231);
}
circle {
stroke: none;
stroke-width: 2;
fill: #525252;
}
.pos {
fill: steelblue;
fill-opacity: 0.45;
}
.neg {
fill: red;
fill-opacity: 0.45;
}
text {
stroke: none;
fill: #525252;
}
.large_label {
font-size: 22px;
}
.small_label {
font-size: 16px;
}
.key_label {
font-size: 14px;
alignment-baseline: text-after-edge;
}
.axis {
font-size: 16px;
}
.axis path {
fill: none;
stroke: gray;
stroke-width: 2;
}
.axis .tick line {
stroke: gray;
stroke-width: 2;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment