xxxxxxxxxx
<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='https://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>
Modified http://d3js.org/d3.v3.min.js to a secure url
https://d3js.org/d3.v3.min.js