app.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. const ai = new Vue({
  2. el: "#ai",
  3. data: {
  4. error: "",
  5. forecast: [],
  6. max: undefined,
  7. min: undefined,
  8. net: new brain.recurrent.LSTMTimeStep(),
  9. trained: false,
  10. training: false
  11. },
  12. methods: {
  13. predict: (values) => {
  14. final = [];
  15. for (value of ai.net.forecast(values, 30)) {
  16. final.push(value * (ai.max - ai.min) + ai.min);
  17. };
  18. ai.forecast = final;
  19. },
  20. train: (data) => {
  21. values = data.map((d) => { return d.value });
  22. trainData = [];
  23. ai.max = Math.max(...values);
  24. ai.min = Math.min(...values);
  25. for (value of values) {
  26. trainData.push( (value - ai.min) / (ai.max - ai.min) );
  27. }
  28. if (!ai.training && !ai.trained) {
  29. ai.error = "";
  30. ai.training = true;
  31. setTimeout(
  32. () => {
  33. ai.net.train([ trainData ], { iterations: 5 });
  34. ai.training = false;
  35. ai.trained = true;
  36. document.getElementById("network").innerHTML = brain.utilities.toSVG(
  37. ai.net, { height: 500 }
  38. );
  39. ai.predict(values);
  40. }, 250
  41. );
  42. } else {
  43. ai.predict(values);
  44. }
  45. }
  46. }
  47. });
  48. const graph = new Vue({
  49. el: "#graph",
  50. data: {
  51. candles: [],
  52. latest: undefined
  53. },
  54. methods: {
  55. addCandle: (candle) => {
  56. graph.candles.push({
  57. date: candle[0],
  58. value: candle[2]
  59. });
  60. graph.latest = new Date(candle[0]);
  61. }
  62. },
  63. watch: {
  64. candles: (values) => {
  65. sorted = values.slice();
  66. sorted.sort((a, b) => {
  67. if (a.date < b.date) return -1;
  68. if (a.date > b.date) return 1;
  69. return 0;
  70. });
  71. dates = sorted.map((d) => d.date);
  72. for (let i=0; i<30; i++) {
  73. last = dates[dates.length - 1];
  74. dates.push(last + 60000);
  75. }
  76. ai.train(sorted);
  77. d3.select("#data").select("g").remove();
  78. element = document.querySelector("#chart-space");
  79. margin = {
  80. top: 10,
  81. right: 30,
  82. bottom: 100,
  83. left: 60
  84. }
  85. height = element.clientHeight - margin.top - margin.bottom;
  86. width = element.clientWidth - margin.left - margin.right;
  87. svg = d3.select("#data")
  88. .append("g")
  89. .attr("transform", `translate(${margin.left}, ${margin.top})`);
  90. x = d3.scaleTime()
  91. .domain(d3.extent(dates, (d) => { return d }))
  92. .range([ 0, width ]);
  93. svg.append("g")
  94. .attr("transform", `translate(0, ${height})`)
  95. .call(d3.axisBottom(x));
  96. y = d3.scaleLinear()
  97. .domain([0, d3.max(sorted, (d) => { return +d.value })])
  98. .range([ height, 0 ]);
  99. svg.append("g")
  100. .call(d3.axisLeft(y));
  101. regression = ss.linearRegression(sorted.map((d) => { return [+d.date, d.value] }));
  102. line = ss.linearRegressionLine(regression);
  103. trendline = x.domain().map((x) => {
  104. return {
  105. date: x,
  106. value: line(+x)
  107. }
  108. });
  109. svg.append("path")
  110. .datum(trendline)
  111. .attr("fill", "none")
  112. .attr("stroke", "red")
  113. .attr("stroke-width", 1)
  114. .attr("d", d3.line().x((d) => {
  115. return x(d.date);
  116. }).y((d) => {
  117. return y(d.value);
  118. }));
  119. svg.append("path")
  120. .datum(sorted)
  121. .attr("fill", "none")
  122. .attr("stroke", "black")
  123. .attr("stroke-width", 2)
  124. .attr("d", d3.line().x((d) => {
  125. return x(d.date);
  126. }).y((d) => {
  127. return y(d.value);
  128. }));
  129. if (ai.forecast.length > 0) {
  130. forecast = [];
  131. position = 30;
  132. for (value of ai.forecast) {
  133. forecast.push({
  134. date: dates[dates.length - position],
  135. value: value
  136. });
  137. position--;
  138. }
  139. svg.append("path")
  140. .datum(forecast)
  141. .attr("fill", "none")
  142. .attr("stroke", "green")
  143. .attr("stroke-width", 1.5)
  144. .attr("d", d3.line().x((d) => {
  145. return x(d.date);
  146. }).y((d) => {
  147. return y(d.value);
  148. }));
  149. }
  150. }
  151. }
  152. });
  153. const websocket = new Vue({
  154. el: "#websocket",
  155. created: () => {
  156. this.socket = new WebSocket("wss://api-pub.bitfinex.com/ws/2")
  157. this.socket.onopen = () => {
  158. websocket.connected = true
  159. };
  160. this.socket.onclose = () => {
  161. websocket.info = "";
  162. websocket.connected = false;
  163. };
  164. this.socket.onmessage = (message) => {
  165. json = JSON.parse(message.data);
  166. if (json.event == "info") websocket.info = `Server ${json.serverId} v${json.version}`;
  167. if (Array.isArray(json) && json[1] !== "hb") websocket.processValues(json);
  168. };
  169. },
  170. data: {
  171. connected: false,
  172. info: "",
  173. socket: undefined
  174. },
  175. methods: {
  176. processCandle: (candle) => {
  177. graph.addCandle(candle);
  178. },
  179. processValues: (json) => {
  180. if (Array.isArray(json[1][0])) {
  181. json[1].forEach((data) => websocket.processCandle(data));
  182. } else {
  183. websocket.processCandle(json[1]);
  184. }
  185. }
  186. },
  187. watch: {
  188. connected: (connected) => {
  189. if (connected === true) this.socket.send('{"event":"subscribe","channel":"candles","key":"trade:1m:tBTCUSD"}');
  190. }
  191. }
  192. });