Built with blockbuilder.org
xxxxxxxxxx
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
svg {
font: 10px sans-serif;
}
.brush-area {
fill: grey;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1px;
clip-path: url(#clip);
}
.focus_box .line {
stroke: #080808;
stroke-width: 2;
fill: none;
opacity: 0.4
}
.axis path,
.axis line {
fill: none;
stroke: none;
stroke-width: 1px;
shape-rendering: crispEdges;
}
.tick line {
stroke: lightgrey;
stroke-width: 1;
shape-rendering: crispEdges;
stroke-dasharray: 5,5;
}
.brush .extent {
stroke: #fff;
fill: red;
fill-opacity: .125;
stroke-width: 2.5px;
shape-rendering: crispEdges;
clip-path: url(#clip);
}
.scan .extent {
stroke: orange;
stroke-width: 3px;
fill: yellow;
fill-opacity: .125;
stroke-width: 1.5px;
shape-rendering: crispEdges;
clip-path: url(#clip);
}
.overlay {
fill: none;
pointer-events: all;
}
.focus_dropline {
opacity: 0.7;
}
.focus_dropline circle {
fill: none;
stroke: black;
}
.focus_dropline line {
fill: none;
stroke: black;
stroke-width: 1.5px;
stroke-dasharray: 3 3;
}
</style>
<body>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 10, right: 10, bottom: 100, left: 40},
margin2 = {top: 430, right: 10, bottom: 20, left: 40},
margin3 = {top: 0, right: 10, bottom: 0, left: 40},
margin4 = {top: 30, right: 10, bottom: 10, left: 10},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
height2 = 500 - margin2.top - margin2.bottom;
height3 = 35 - margin3.top - margin3.bottom;
height4 = 800 - margin4.top - margin4.bottom;
var parseDate = d3.time.format("%Y%m%d").parse,
bisectDate = d3.bisector(function(d) { return d.date; }).left;
var x = d3.time.scale().range([0, width]),
x2 = d3.time.scale().range([0, width]),
y = d3.scale.linear().range([height, 0]),
y2 = d3.scale.linear().range([height2, 0]);
var xAxis = d3.svg.axis().scale(x).orient("bottom"),
xAxis2 = d3.svg.axis().scale(x2).orient("bottom"),
yAxis = d3.svg.axis().scale(y).orient("left").innerTickSize(-width);
var maxDate = 0, minDate =0;
var line = d3.svg.line()
.interpolate("step-after")
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.price); })
.defined(function(d) { return d.price; }); // Hiding line value defaults of 0 for missing data
var area_brush = d3.svg.area()
.interpolate("monotone")
.x(function(d) { return x2(d.date); })
.y0(height2)
.y1(function(d) { return y2(d.price); })
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
focus.append('text')
.attr('x', 10)
.attr('y', 25)
.text("Nasdaq Composite Index")
.style("font-size", 14);
var context = svg.append("g")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
var lineFunction = d3.svg.line()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.interpolate("linear");
var data; // stock data global variable
var brush = d3.svg.brush()
.x(x2)
.extent([0.9, 1])
.on("brush", brushed);
var scan = d3.svg.brush()
.x(x)
.on("brush", scanned)
.on("brushstart", function () {d3.select("g.focus_box").remove();});
d3.csv("datapoints.csv", function(error, csv_data) {
data = csv_data;
data.forEach(function(d) {
d.date = parseDate(d.date);
d.price = +d.price;
});
x.domain(d3.extent(data.map(function(d) { return d.date; })));
y.domain([0, d3.max(data.map(function(d) { return d.price; }))]);
x2.domain(x.domain());
y2.domain(y.domain());
focus.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
focus.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
focus.append("g")
.attr("class", "y axis")
.call(yAxis);
focus.append("g")
.attr("class", "x scan")
.call(scan)
.selectAll("rect")
.attr("height", height )
.on("mouseover", function() { focus_dropline.style("display", null); })
.on("mouseout", function() { focus_dropline.style("display", "none"); })
.on("mousemove", mousemove);
// prettify: to block the stroke of the scanner
focus.append("line")
.style("stroke", "white")
.style("stroke-width", "2px")
.attr("x1",2)
.attr("x2",width)
// prettify: to block the stroke of the scanner at bottom
focus.append("line")
.style("stroke", "black")
.style("stroke-width", "2px")
.attr("x1",0)
.attr("x2",width)
.attr("y1",height)
.attr("y2",height);
// for the drop line
var focus_dropline = focus.append("g")
.attr("class", "focus_dropline")
.style("display", "none");
focus_dropline.append("circle")
.attr("r", 4.5);
focus_dropline.append("line")
.classed("x", true)
focus_dropline.append("line")
.classed("y", true)
focus_dropline.append("text")
.attr("x", 20)
.attr("y", 20)
.attr("dy", ".35em");
function mousemove() {
var x0 = x.invert(d3.mouse(this)[0]),
i = bisectDate(data, x0, 1),
d0 = data[i - 1],
d1 = data[i],
d = x0 - d0.date > d1.date - x0 ? d1 : d0;
focus_dropline.attr("transform", "translate(" + x(d.date) + "," + y(d.price) + ")");
focus_dropline.select("line.x")
.attr("x1", 0)
.attr("x2", -x(d.date))
.attr("y1", 0)
.attr("y2", 0)
focus_dropline.select("line.y")
.attr("x1", 0)
.attr("x2", 0)
.attr("y1", 0)
.attr("y2", height - y(d.price))
focus_dropline.select("text").text(d.price.toFixed(2));
}
context.append("path")
.datum(data)
.attr("class", "brush-area")
.attr("d", area_brush);
context.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2);
context.append("g")
.attr("class", "x brush")
.call(brush)
.selectAll("rect")
.attr("y", -6)
.attr("height", height2 + 7);
});
function brushed() {
x.domain(brush.empty() ? x2.domain() : brush.extent());
var dataFiltered = data.filter(function(d, i) {
if ( (d.date >= x.domain()[0]) && (d.date <= x.domain()[1]) ) {
return d.price;
}
});
// to re-scale y-axis
y.domain([0.9*d3.min(dataFiltered.map(function(d) { return d.price; })),
1.1*d3.max(dataFiltered.map(function(d) { return d.price; }))]);
focus.select(".line").attr("d", line);
focus.select(".x.axis").transition().call(xAxis);
focus.select(".y.axis").call(yAxis);
d3.selectAll('circle.dot')
.attr("cx",function(d) { return x( d.FileDate ); })
.attr("cy", function(d) {
var i_price = bisectDate(data, d.FileDate, 1),
d0_price = data[i_price - 1],
d1_price = data[i_price],
d_price = d.FileDate - d0_price.date > d1_price.date - d.FileDate ? d1_price.price : d0_price.price;
return y( d_price); });
};
// here's the function that is called upon brushing the main chart
function scanned() {
// get the brush that is being manipulated
var brush_event = d3.event.target;
// check if it is empty - if not, then do the analytics box
if(!brush_event.empty()) {
// get the extent of the brush as a two-element array [min, max]
var extent = brush_event.extent();
minDate = extent[0];
maxDate = extent[1];
console.log("minDate",minDate)
console.log("maxDate",maxDate)
// day difference
var timeDiff = Math.abs(maxDate-minDate);
var diffDays = Math.ceil(timeDiff / (1000 * 3600 * 24));
console.log("TimeDiff", Date(timeDiff))
var i_min = bisectDate(data, minDate, 1),
d0_min = data[i_min - 1],
d1_min = data[i_min],
d_min = minDate - d0_min.date > d1_min.date - minDate ? d1_min : d0_min;
var i_max = bisectDate(data, maxDate, 1),
d0_max = data[i_max - 1],
d1_max = data[i_max],
d_max = maxDate - d0_max.date > d1_max.date - maxDate ? d1_max : d0_max;
period_return = (d_max.price - d_min.price) / d_min.price
annualized_return = (d_max.price - d_min.price) / d_min.price / (diffDays/365)
// get line data dynamically
var lineData = [{"x": x(minDate), "y":y(d_min.price) }, {"x": x(maxDate), "y": y(d_max.price)}];
console.log(lineData);
console.log(lineFunction(lineData));
//focus_box
var focus_box = focus.selectAll("g.focus_box")
.data([maxDate]);
focus_box
.enter()
.append("g")
.attr("class", "focus_box")
focus_box.selectAll("*").remove();
focus_box
.append("g")
.attr("class", "indicator")
.append("path")
.attr("class", "line")
.attr("d", lineFunction(lineData))
focus_box
.append("circle")
.attr("cx", x(maxDate))
.attr("cy", y(d_max.price))
.attr("r", 8)
.attr("fill", "none")
.attr("stroke", "black")
.attr("stroke-width", 2);
focus_box
.append("circle")
.attr("cx", x(maxDate))
.attr("cy", y(d_max.price))
.attr("r", 2)
.attr("fill", "black");
focus_box
.append("circle")
.attr("cx", x(minDate))
.attr("cy", y(d_min.price))
.attr("r", 3)
.attr("fill", "black")
.attr("stroke", "black")
.attr("stroke-width", 1);
focus_box
.append("rect")
.attr("width", 120)
.attr("height", 65)
.attr('x', Math.min(width - 125, x(maxDate)+14))
.attr('y', height - 80)
.attr("fill", "black")
.attr("stroke", "steelblue")
.attr("stroke-width", "1px")
.attr("opacity", 0.2);
focus_box.append("text")
.attr('x', Math.min(width - 125, x(maxDate)+20))
.attr('y', height - 70)
.attr("dy", ".3em")
.text("Return (period)");
focus_box.append("text")
.attr('x', Math.min(width - 125, x(maxDate)+20))
.attr('y', height - 55)
.attr("dy", ".35em")
.text(((period_return*100).toFixed(1)).toString().concat("%"));
focus_box.append("text")
.attr('x', Math.min(width - 125, x(maxDate)+20))
.attr('y', height - 40)
.attr("dy", ".3em")
.text("Return (annualized)");
focus_box.append("text")
.attr('x', Math.min(width - 125, x(maxDate)+20))
.attr('y', height - 25)
.attr("dy", ".35em")
.text(((annualized_return*100).toFixed(1)).toString().concat("%"));
}
};
</script>
Modified http://d3js.org/d3.v3.min.js to a secure url
https://d3js.org/d3.v3.min.js