Skip to content

Commit

Permalink
Web UI: Replace plotly.js with echarts (DeviaVir#1240)
Browse files Browse the repository at this point in the history
* Web UI: Replace plotly.js with echarts

This should drastically improve loading and response time of the dashboard with many datapoints.

* Web UI: More

* Limit initial zoom to 100 trades
* Move candle to the back
* Add plot for my_trades

*  Web UI: Move Volume behind candle

* Web UI: Chart

* increase dataZoom startValue to 500 trades
* reduce left/right margin
* hide Chinese tooltips

* Web UI: Fix candlestick data order

* Web UI: Fix profit not printed if 0
  • Loading branch information
defkev authored and DeviaVir committed Feb 19, 2018
1 parent 4581bad commit 05fdc75
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 109 deletions.
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"convnetjs": "0.3.0",
"counterup": "^1.0.2",
"css-loader": "^0.28.7",
"echarts": "^4.0.2",
"ejs": "^2.5.7",
"exports-loader": "^0.7.0",
"expose-loader": "^0.7.4",
Expand Down Expand Up @@ -78,7 +79,6 @@
"number-abbreviate": "^2.0.0",
"numbro": "highvelocityspace/numbro",
"path": "^0.12.7",
"plotly.js": "^1.32.0",
"poloniex.js": "0.0.8",
"popper.js": "^1.12.9",
"postcss-loader": "^2.0.9",
Expand All @@ -97,7 +97,6 @@
"superagent": "^3.8.2",
"talib": "^1.0.4",
"timebucket": "^0.4.0",
"transform-loader": "^0.2.4",
"trend": "0.3.0",
"url-loader": "^0.6.2",
"uuid": "^3.1.0",
Expand Down
248 changes: 156 additions & 92 deletions templates/dashboard.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -164,19 +164,14 @@
<div class="col-md-12 col-lg-12 col-sm-12 col-12">
<div class="white-box">
<h3 class="box-title"><%= exchange.name.toUpperCase() %> <%= asset.toUpperCase() %>/<%= currency.toUpperCase() %> Trade chart</h3>
<div id="trades_plot" style="height: 505px;">
<div id="trade_chart" style="height: 505px;">
</div>
<script src="assets-wp/plotly.bundle.js" charset="utf8"></script>
<script src="/assets-wp/echarts.bundle.js" charset="utf8"</script>
<script type="text/javascript">
function unpack(rows, key, offset) {
return rows.map(function(row) {
if (key == 'time') {
// Plotly’s date format is 'yyyy-mm-dd HH:MM:SS.ssssss'
return new Date(row.time - offset * 60000).toISOString().replace(/T/g, ' ').replace(/Z/g, '');
} else {
return row[key];
}
return { value: [ row.time - offset * 60000, row[key] ] };
});
}
Expand All @@ -189,102 +184,171 @@
var my_trades = tradeData.my_trades;
var offset = tradeData.tz_offset;
var price = {
type: "scatter",
name: 'Price',
mode: 'lines+markers',
x: unpack(trades, 'time', offset),
y: unpack(trades, 'price')
};
var volume = {
type: 'bar',
name: 'Volume',
yaxis: 'y2',
opacity: '0.25',
x: unpack(trades, 'time', offset),
y: unpack(trades, 'size'),
width: 3600*4
};
var ohlc = {
type: 'ohlc',
name: 'OHLC',
x: unpack(lookback, 'time', offset),
open: unpack(lookback, 'open'),
high: unpack(lookback, 'high'),
low: unpack(lookback, 'low'),
close: unpack(lookback, 'close')
};
var data = [ price, volume, ohlc ];
var trade_chart = echarts.init(document.getElementById('trade_chart'));
var lastTrade = new Date(price.x[price.x.length - 1]);
lastTrade.setMinutes(lastTrade.getMinutes() + 1);
var rangeEnd = new Date(lastTrade - ((new Date()).getTimezoneOffset() * 60000)).toISOString().replace(/T/g, ' ').replace(/Z/g, '');
lastTrade.setMinutes(lastTrade.getMinutes() - 31);
var rangeStart = new Date(lastTrade - ((new Date()).getTimezoneOffset() * 60000)).toISOString().replace(/T/g, ' ').replace(/Z/g, '');
var lastTrade = trades[trades.length < 500 ? 0 : trades.length - 500].time;
var rangeStart = lastTrade - offset * 60000;
var layout = {
showlegend: false,
xaxis: {
range: [ rangeStart, rangeEnd ],
rangeselector: {
buttons: [
{
count: 5,
label: '5m',
step: 'minute',
stepmode: 'todate'
},
{
count: 30,
label: '30m',
step: 'minute',
stepmode: 'todate'
},
{
count: 1,
label: '1h',
step: 'hour',
stepmode: 'backward'
},
{
step: 'all'
var options = {
useUTC: true,
grid: {
left: 60,
right: 60
},
xAxis: [
{
type: 'time',
axisLabel: {
formatter: function (value) {
return echarts.format.formatTime('yyyy-MM-dd hh:mm:ss', value, true);
}
]
}
}
],
yAxis: [
{
name: 'Price',
position: 'right',
scale: true
},
rangeslider: {},
type: 'date'
},
yaxis: {
title: 'Price',
overlaying: 'y2',
side: 'right'
{
name: 'Volume',
scale: true
}
],
toolbox: {
show: true,
showTitle: false,
feature: {
dataZoom: {},
restore: {},
saveAsImage: {}
}
},
yaxis2: {
title: 'Volume',
side: 'left'
tooltip : {
trigger: 'axis',
axisPointer: {
type: 'cross',
}
},
annotations: []
dataZoom: [
{
startValue: rangeStart
},
{
type: 'inside'
},
],
series: [
{
name: 'Price',
type: 'line',
data: unpack(trades, 'price', offset)
},
{
name: 'Volume',
type: 'bar',
yAxisIndex: 1,
z: 1,
itemStyle: {
opacity: 0.25
},
data: unpack(trades, 'size', offset)
},
{
name: 'High',
type: 'candlestick',
z: 1,
itemStyle: {
color: 'rgba(40,167,69,0.5)', // bullish
borderColor: '#28a745',
color0: 'rgba(220,53,69,0.5)', // bearish
borderColor0: '#dc3545'
},
data: []
},
{
type: 'line',
smooth: true,
lineStyle: {
type: 'dotted'
},
data: unpack(my_trades, 'price', offset),
tooltip: {
show: false
}
},
{
name: 'Buy',
type: 'scatter',
symbol: 'triangle',
data: [],
itemStyle: {
color: '#28a745'
},
markPoint: {
symbol: 'arrow',
itemStyle: {
color: '#28a745'
},
symbolOffset: [ 0, 10 ],
data: []
}
},
{
name: 'Sell',
type: 'scatter',
symbol: 'triangle',
symbolRotate: 180,
data: [],
itemStyle: {
color: '#dc3545'
},
markPoint: {
symbol: 'arrow',
itemStyle: {
color: '#dc3545'
},
symbolRotate: 180,
symbolOffset: [ 0, -10 ],
data: []
}
}
]
};
for (i = 0; i < my_trades.length; i++) {
layout.annotations.push({
x: new Date(my_trades[i].time - offset * 60000).toISOString().replace(/T/g, ' ').replace(/Z/g, ''),
y: my_trades[i].price,
xref: 'x',
yref: 'y',
text: my_trades[i].type,
ax: 0,
ay: -50
for (i = 0; i < lookback.length; i++) {
options.series[2].data.push({
value: [ lookback[i].close_time - offset * 60000, lookback[i].open, lookback[i].close, lookback[i].low, lookback[i].high ] // OCLH -> OHLC
});
}
Plotly.newPlot('trades_plot', data, layout);
for (i = 0; i < my_trades.length; i++) {
if (my_trades[i].type == 'buy') {
options.series[4].data.push({
value: [ my_trades[i].time - offset * 60000, my_trades[i].price ]
});
options.series[4].markPoint.data.push({
xAxis: my_trades[i].time - offset * 60000,
yAxis: my_trades[i].price,
value: 'Bought',
});
} else {
options.series[5].data.push({
value: [ my_trades[i].time - offset * 60000, my_trades[i].price ]
});
options.series[5].markPoint.data.push({
xAxis: my_trades[i].time - offset * 60000,
yAxis: my_trades[i].price,
value: 'Sold',
});
}
}
trade_chart.setOption(options);
window.onresize = function() {
Plotly.Plots.resize('trades_plot');
trade_chart.resize();
};
}
};
Expand Down Expand Up @@ -355,7 +419,7 @@
<td><span class="text-<% if (trade.slippage > 0) { %>danger<% } else { %>success<% } %>"><%= new Intl.NumberFormat("en-US", {style: "percent", useGrouping: false, minimumFractionDigits: 4, maximumFractionDigits: 4}).format(trade.slippage) %></span></td>
<td><%= moment(trade.time).format('YYYY-MM-DD HH:mm:ss') %></td>
<td><%= moment.duration(trade.execution_time).humanize() %></td>
<td><% if (trade.profit) { %><span class="text-<% if(trade.profit < 0) { %>danger<% } else { %>success<% } %>"><%= new Intl.NumberFormat("en-US", {style: "percent", useGrouping: false, minimumFractionDigits: 2, maximumFractionDigits: 2}).format(trade.profit) %></span><% } else { %>-<% } %></td>
<td><% if (trade.profit != null) { %><span class="text-<% if(trade.profit < 0) { %>danger<% } else { %>success<% } %>"><%= new Intl.NumberFormat("en-US", {style: "percent", useGrouping: false, minimumFractionDigits: 2, maximumFractionDigits: 2}).format(trade.profit) %></span><% } else { %>-<% } %></td>
</tr>
<% }); %>
<% } %>
Expand Down
12 changes: 12 additions & 0 deletions webpack-src/js/echarts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
var echarts = require('echarts/lib/echarts');

require('echarts/lib/chart/line');
require('echarts/lib/chart/bar');
require('echarts/lib/chart/candlestick');
require('echarts/lib/chart/scatter');
require('echarts/lib/component/tooltip');
require('echarts/lib/component/dataZoom');
require('echarts/lib/component/markPoint');
require('echarts/lib/component/toolbox');

module.exports = echarts;
8 changes: 0 additions & 8 deletions webpack-src/js/plotly.js

This file was deleted.

10 changes: 3 additions & 7 deletions webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const webpack = require('webpack')
module.exports = {
entry: {
app: './webpack-src/js/app.js',
plotly: './webpack-src/js/plotly.js'
echarts: './webpack-src/js/echarts.js'
},
plugins: [
new webpack.ProvidePlugin({
Expand Down Expand Up @@ -62,14 +62,10 @@ module.exports = {
}]
},
{
test: /\.js$/,
use: 'transform-loader?plotly.js/tasks/util/compress_attributes.js',
},
{
test: require.resolve('./webpack-src/js/plotly.js'),
test: require.resolve('./webpack-src/js/echarts.js'),
use: [{
loader: 'expose-loader',
options: 'Plotly'
options: 'echarts'
}]
}
],
Expand Down

0 comments on commit 05fdc75

Please sign in to comment.