UNPKG

6.45 kBMarkdownView Raw
1## D3
2
3### Streaming Financial Chart
4
5Meanwhile, a cool graphic where the *stream* is an infinitely generated series of random numbers...
6
7Adapted from [Streaming Financial Chart](https://bl.ocks.org/ColinEberhardt/ab7805a9a7af9717e86adc1656fa98d9).
8
9
10```d3/playable
11smartdown.importCssCode(
12`
13.bollinger .area {
14 fill: #9cf;
15 fill-opacity: 0.5;
16}
17
18.bollinger .line {
19 stroke: #06c;
20}
21.chart {
22 height: 200px !important;
23}
24
25`);
26
27// window.d3 = window.d3v4;
28var target = this.div;
29target.classList.add('chart');
30
31// create some test data
32const stream = fc.randomFinancial().stream();
33const data = stream.take(110);
34
35function renderChart() {
36 if (target.style.display === 'none' && window.intervalId) {
37 window.clearInterval(window.intervalId);
38 return;
39 }
40
41 // add a new datapoint and remove an old one
42 data.push(stream.next());
43 data.shift();
44
45 const container = d3.select(target);
46
47 // Create and apply the bollinger algorithm
48 const bollingerAlgorithm = fc.indicatorBollingerBands()
49 .value(function(d) {
50 return d.close;
51 });
52 const bollingerData = bollingerAlgorithm(data);
53 const mergedData = data.map(function(d, i) {
54 return Object.assign({}, d, {
55 bollinger: bollingerData[i]
56 });
57 });
58
59 // Offset the range to include the full bar for the latest value
60 const DAY_MS = 1000 * 60 * 60 * 24;
61 const xTicks = 10;// $('#streaming-chart').width() >= 700 ? 10 : 5;
62 const xExtent = fc.extentDate()
63 .accessors([function(d) {
64 return d.date;
65 }])
66 .padUnit('domain')
67 .pad([DAY_MS * -bollingerAlgorithm.period()(mergedData), DAY_MS]);
68
69 // ensure y extent includes the bollinger bands
70 const yExtent = fc.extentLinear()
71 .accessors([
72 function(d) {
73 return Math.max(d.bollinger.upper, d.high);
74 },
75 function(d) {
76 return Math.min(d.bollinger.lower, d.low);
77 }
78 ]);
79
80 // create a chart
81 const chart = fc.chartSvgCartesian(
82 d3.scaleTime(),
83 d3.scaleLinear()
84 )
85 .xDomain(xExtent(mergedData))
86 .xTicks(xTicks)
87 .yDomain(yExtent(mergedData))
88 .chartLabel('Streaming Candlestick');
89
90 // Create the gridlines and series
91 const gridlines = fc.annotationSvgGridline().xTicks(xTicks);
92 const candlestick = fc.seriesSvgCandlestick();
93
94 const bollingerBands = function() {
95 const area = fc.seriesSvgArea()
96 .mainValue(function(d) {
97 return d.bollinger.upper;
98 })
99 .baseValue(function(d) {
100 return d.bollinger.lower;
101 });
102
103 const upperLine = fc.seriesSvgLine()
104 .mainValue(function(d) {
105 return d.bollinger.upper;
106 });
107
108 const averageLine = fc.seriesSvgLine()
109 .mainValue(function(d) {
110 return d.bollinger.average;
111 });
112
113 const lowerLine = fc.seriesSvgLine()
114 .mainValue(function(d) {
115 return d.bollinger.lower;
116 });
117
118 const crossValue = function(d) {
119 return d.date;
120 };
121 area.crossValue(crossValue);
122 upperLine.crossValue(crossValue);
123 averageLine.crossValue(crossValue);
124 lowerLine.crossValue(crossValue);
125
126 const bollingerMulti = fc.seriesSvgMulti()
127 .series([area, upperLine, lowerLine, averageLine])
128 .decorate(function(g, datum, index) {
129 g.enter()
130 .attr('class', function(_, i) {
131 return 'multi bollinger ' + ['area', 'upper', 'lower', 'average'][i];
132 });
133 });
134
135 return bollingerMulti;
136 };
137
138 // add them to the chart via a multi-series
139 const multi = fc.seriesSvgMulti()
140 .series([gridlines, bollingerBands(), candlestick]);
141
142 chart.plotArea(multi);
143
144 container
145 .style('margin-left', '20px')
146 .datum(mergedData)
147 .call(chart);
148}
149
150// re-render the chart every 200ms
151renderChart();
152
153if (window.intervalId) {
154 window.clearInterval(window.intervalId);
155}
156
157window.intervalId = setInterval(renderChart, 200);
158
159```
160
161
162### Smelly London Line Graph
163
164One of the visualizations available via Smelly London is a [Line Chart](https://github.com/Smelly-London/Smelly-London/tree/master/charts). I made some slight changes so that it can be rendered in Smartdown:
165
166
167
168```d3/playable
169var renderDiv = this.div;
170renderDiv.classList.add('foo');
171
172smartdown.importCssCode(
173`
174body .foo svg {
175 font: 10px sans-serif;
176 background: lightyellow;
177}
178
179.foo .axis path,
180.foo .axis line {
181 fill: none;
182 stroke: #000;
183 shape-rendering: crispEdges;
184}
185
186.foo .x.axis path {
187 display: none;
188}
189
190.foo .line {
191 fill: none;
192 stroke: #2091c1;
193 stroke-width: 1.5px;
194}
195`);
196
197
198var margin = {top: 20, right: 20, bottom: 30, left: 50},
199 width = 960 - margin.left - margin.right,
200 height = 500 - margin.top - margin.bottom;
201
202// var formatDate = d3.time.format("%d-%b-%y");
203var formatDateCarto = d3.timeParse("%Y/%m/%d");
204
205
206var x = d3.scaleTime()
207 .range([0, width]);
208
209var y = d3.scaleLinear()
210 .range([height, 0]);
211
212var xAxis = d3.axisBottom()
213 .scale(x);
214
215var yAxis = d3.axisLeft()
216 .scale(y);
217
218var line = d3.line()
219 .x(function(d) { return x(d.date); })
220 .y(function(d) { return y(d.number_of_smells); });
221
222var svg = d3.select(renderDiv).append("svg")
223 .attr("width", width + margin.left + margin.right)
224 .attr("height", height + margin.top + margin.bottom)
225 .append("g")
226 .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
227
228// d3.tsv("data/all_records.tsv", type, function(error, data) {
229// d3.tsv("data/output/Barnet.tsv", type, function(error, data) {
230// d3.tsv("data/output/Barnet.tsv", type, function(error, data) {
231// d3.tsv("data/output/Croydon.tsv", type, function(error, data) {
232d3.tsv("https://rawcdn.githack.com/Smelly-London/Smelly-London/master/charts/data/output/Tower Hamlets.tsv", type, function(error, data) {
233
234 if (error) throw error;
235
236 x.domain(d3.extent(data, function(d) { return d.date; }));
237 y.domain(d3.extent(data, function(d) { return d.number_of_smells; }));
238
239 svg.append("g")
240 .attr("class", "x axis")
241 .attr("transform", "translate(0," + height + ")")
242 .call(xAxis);
243
244 svg.append("g")
245 .attr("class", "y axis")
246 .call(yAxis)
247 .append("text")
248 .attr("transform", "rotate(-90)")
249 .attr("y", 6)
250 .attr("dy", ".71em")
251 .style("text-anchor", "end")
252 .text("Number of smells");
253
254 svg.append("path")
255 .datum(data)
256 .attr("class", "line")
257 .attr("d", line);
258});
259
260function type(d) {
261 d.date = formatDateCarto(d.date);
262 d.number_of_smells = +d.number_of_smells;
263 return d;
264}
265
266```
267
268---
269
270[Back to Home](:@Home)