class Comms { constructor() { // Callback collections that will receive various updates. this.candleUpdateCallbacks = []; this.candleCloseCallbacks = []; this.indicatorUpdateCallbacks = []; // Flags this.connectionOpen = false; } /** * Register a callback function to receive updates for a specific target. * @param {string} target - The target for the callback ('candle_update', 'candle_close', or 'indicator_update'). * @param {function} callbackFunc - The callback function to register. */ registerCallback(target, callbackFunc) { const callbackMap = { 'candle_update': this.candleUpdateCallbacks, 'candle_close': this.candleCloseCallbacks, 'indicator_update': this.indicatorUpdateCallbacks }; const callbackList = callbackMap[target]; if (callbackList) { callbackList.push(callbackFunc); } else { console.log('Comms: Invalid target for callback'); } } /* Callback declarations */ candleUpdate(newCandle) { for (const callback of this.candleUpdateCallbacks) { callback(newCandle); } } candleClose(newCandle) { this.sendToApp('candle_data', newCandle); for (const callback of this.candleCloseCallbacks) { callback(newCandle); } } indicatorUpdate(data) { for (const callback of this.indicatorUpdateCallbacks) { callback(data); } } /** * Fetches the price history data for a user. * @param {string} userName - The name of the user. * @returns {Promise} - A promise that resolves * to the fetched price history data, or null if an error occurs. */ async getPriceHistory(userName) { try { const response = await fetch('http://localhost:5000/api/history', { credentials: 'include', mode: 'cors', method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ "user_name": userName }) }); const data = await response.json(); console.log('Communication[56]: Received candles from the server:'); console.log(data); return data; } catch (error) { console.error('Error fetching price history:', error); return null; } } /** * Fetches the Indicator data for a specific user. * @param {string} userName - The name of the user. * @returns {Promise} - A promise that resolves * to the fetched Indicator data, or null if an error occurs. */ async getIndicatorData(userName) { try { const response = await fetch('http://localhost:5000/api/indicator_init', { credentials: 'same-origin', mode: 'cors', method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ "user_name": userName }) }); const data = await response.json(); return data; } catch (error) { console.error('Error fetching indicator data:', error); return null; } } /** * Sends a message to the application server. * @param {string} messageType - The type of the message. * @param {Object} data - The data to be sent with the message. */ sendToApp(messageType, data) { console.log('window.UI.data.comms.sendToApp(): Sending->'); console.log(JSON.stringify({ message_type: messageType, data: data })); if (this.connectionOpen) { this.appCon.send(JSON.stringify({ message_type: messageType, data: data })); } else { setTimeout(() => { window.UI.data.comms.appCon.send(JSON.stringify({ message_type: messageType, data: data })); }, 1000); } } /** * Sets up the WebSocket connection to the application server. */ setAppCon() { this.appCon = new WebSocket('ws://localhost:5000/ws'); this.appCon.onopen = () => { this.appCon.send("Connection OK"); this.connectionOpen = true; }; this.appCon.addEventListener('message', (event) => { if (event.data) { const message = JSON.parse(event.data); if (message && message.request !== undefined) { console.log('Received a request from the server'); console.log(message.request); } if (message && message.reply !== undefined) { if (message.reply === 'updates') { const { i_updates, s_updates, stg_updts, trade_updts } = message.data; if (i_updates) { this.indicatorUpdate(i_updates); window.UI.signals.i_update(i_updates); } if (s_updates) { const updates = s_updates; window.UI.signals.update_signal_states(updates); window.UI.alerts.publish_alerts('signal_changes', updates); } if (stg_updts) { const stg_updts = stg_updts; window.UI.strats.update_received(stg_updts); } if (trade_updts) { const trade_updts = trade_updts; window.UI.trade.update_received(trade_updts); } } else if (message.reply === 'signals') { window.UI.signals.set_data(message.data); } else if (message.reply === 'strategies') { window.UI.strats.set_data(message.data); } else if (message.reply === 'trades') { window.UI.trade.set_data(message.data); } else if (message.reply === 'signal_created') { const list_of_one = [message.data]; window.UI.signals.set_data(list_of_one); } else if (message.reply === 'trade_created') { const list_of_one = [message.data]; window.UI.trade.set_data(list_of_one); } else { console.log(message.reply); console.log(message.data); } } } }); } /** * Sets up a WebSocket connection to the exchange for receiving candlestick data. * @param {string} interval - The interval of the candlestick data. * @param {string} tradingPair - The trading pair to subscribe to. */ setExchangeCon(interval, tradingPair) { tradingPair = tradingPair.toLowerCase(); const ws = `wss://stream.binance.com:9443/ws/${tradingPair}@kline_${interval}`; this.exchangeCon = new WebSocket(ws); this.exchangeCon.onmessage = (event) => { let message = JSON.parse(event.data); let candlestick = message.k; let newCandle = { time: candlestick.t / 1000, open: candlestick.o, high: candlestick.h, low: candlestick.l, close: candlestick.c, vol: candlestick.v }; this.candleUpdate(newCandle); if (candlestick.x === true) { this.candleClose(newCandle); } }; } }