| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206 |
- const ai = new Vue({
- el: "#ai",
- data: {
- error: "",
- forecast: [],
- max: undefined,
- min: undefined,
- net: new brain.recurrent.LSTMTimeStep(),
- trained: false,
- training: false
- },
- methods: {
- predict: (values) => {
- final = [];
- for (value of ai.net.forecast(values, 30)) {
- final.push(value * (ai.max - ai.min) + ai.min);
- };
- ai.forecast = final;
- },
- train: (data) => {
- values = data.map((d) => { return d.value });
- trainData = [];
- ai.max = Math.max(...values);
- ai.min = Math.min(...values);
- for (value of values) {
- trainData.push( (value - ai.min) / (ai.max - ai.min) );
- }
- if (!ai.training && !ai.trained) {
- ai.error = "";
- ai.training = true;
- setTimeout(
- () => {
- ai.net.train([ trainData ], { iterations: 5 });
- ai.training = false;
- ai.trained = true;
- document.getElementById("network").innerHTML = brain.utilities.toSVG(
- ai.net, { height: 500 }
- );
- ai.predict(values);
- }, 250
- );
- } else {
- ai.predict(values);
- }
- }
- }
- });
- const graph = new Vue({
- el: "#graph",
- data: {
- candles: [],
- latest: undefined
- },
- methods: {
- addCandle: (candle) => {
- graph.candles.push({
- date: candle[0],
- value: candle[2]
- });
- graph.latest = new Date(candle[0]);
- }
- },
- watch: {
- candles: (values) => {
- sorted = values.slice();
- sorted.sort((a, b) => {
- if (a.date < b.date) return -1;
- if (a.date > b.date) return 1;
- return 0;
- });
- dates = sorted.map((d) => d.date);
- for (let i=0; i<30; i++) {
- last = dates[dates.length - 1];
- dates.push(last + 60000);
- }
- ai.train(sorted);
- d3.select("#data").select("g").remove();
- element = document.querySelector("#chart-space");
- margin = {
- top: 10,
- right: 30,
- bottom: 100,
- left: 60
- }
- height = element.clientHeight - margin.top - margin.bottom;
- width = element.clientWidth - margin.left - margin.right;
- svg = d3.select("#data")
- .append("g")
- .attr("transform", `translate(${margin.left}, ${margin.top})`);
- x = d3.scaleTime()
- .domain(d3.extent(dates, (d) => { return d }))
- .range([ 0, width ]);
- svg.append("g")
- .attr("transform", `translate(0, ${height})`)
- .call(d3.axisBottom(x));
- y = d3.scaleLinear()
- .domain([0, d3.max(sorted, (d) => { return +d.value })])
- .range([ height, 0 ]);
- svg.append("g")
- .call(d3.axisLeft(y));
- regression = ss.linearRegression(sorted.map((d) => { return [+d.date, d.value] }));
- line = ss.linearRegressionLine(regression);
- trendline = x.domain().map((x) => {
- return {
- date: x,
- value: line(+x)
- }
- });
- svg.append("path")
- .datum(trendline)
- .attr("fill", "none")
- .attr("stroke", "red")
- .attr("stroke-width", 1)
- .attr("d", d3.line().x((d) => {
- return x(d.date);
- }).y((d) => {
- return y(d.value);
- }));
- svg.append("path")
- .datum(sorted)
- .attr("fill", "none")
- .attr("stroke", "black")
- .attr("stroke-width", 2)
- .attr("d", d3.line().x((d) => {
- return x(d.date);
- }).y((d) => {
- return y(d.value);
- }));
- if (ai.forecast.length > 0) {
- forecast = [];
- position = 30;
- for (value of ai.forecast) {
- forecast.push({
- date: dates[dates.length - position],
- value: value
- });
- position--;
- }
- svg.append("path")
- .datum(forecast)
- .attr("fill", "none")
- .attr("stroke", "green")
- .attr("stroke-width", 1.5)
- .attr("d", d3.line().x((d) => {
- return x(d.date);
- }).y((d) => {
- return y(d.value);
- }));
- }
- }
- }
- });
- const websocket = new Vue({
- el: "#websocket",
- created: () => {
- this.socket = new WebSocket("wss://api-pub.bitfinex.com/ws/2")
- this.socket.onopen = () => {
- websocket.connected = true
- };
- this.socket.onclose = () => {
- websocket.info = "";
- websocket.connected = false;
- };
- this.socket.onmessage = (message) => {
- json = JSON.parse(message.data);
- if (json.event == "info") websocket.info = `Server ${json.serverId} v${json.version}`;
- if (Array.isArray(json) && json[1] !== "hb") websocket.processValues(json);
- };
- },
- data: {
- connected: false,
- info: "",
- socket: undefined
- },
- methods: {
- processCandle: (candle) => {
- graph.addCandle(candle);
- },
- processValues: (json) => {
- if (Array.isArray(json[1][0])) {
- json[1].forEach((data) => websocket.processCandle(data));
- } else {
- websocket.processCandle(json[1]);
- }
- }
- },
- watch: {
- connected: (connected) => {
- if (connected === true) this.socket.send('{"event":"subscribe","channel":"candles","key":"trade:1m:tBTCUSD"}');
- }
- }
- });
|