* Used to subscribe to a combined futures websocket endpoint * @param {string} streams - streams to connect to * @param {function} callback - the function to call when information is received * @param {object} params - Optional reconnect {boolean} (whether to reconnect on disconnect),
(streams, callback: Callback, params: Dict = {})
| 1854 | * @return {WebSocket} - websocket reference |
| 1855 | */ |
| 1856 | futuresSubscribe(streams, callback: Callback, params: Dict = {}) { |
| 1857 | if (typeof streams === 'string') return this.futuresSubscribeSingle(streams, callback, params); |
| 1858 | if (typeof params === 'boolean') params = { reconnect: params }; |
| 1859 | if (!params.reconnect) params.reconnect = false; |
| 1860 | if (!params.openCallback) params.openCallback = false; |
| 1861 | if (!params.id) params.id = false; |
| 1862 | const httpsproxy = this.getHttpsProxy(); |
| 1863 | let socksproxy = this.getSocksProxy(); |
| 1864 | const queryParams = streams.join('/'); |
| 1865 | // Binance routes USDⓈ-M futures streams to separate endpoints by category |
| 1866 | // (/public, /market, /private) and will not push cross-category streams on a |
| 1867 | // single connection. Reject mixed-category combos so they fail loudly instead |
| 1868 | // of silently dropping data — subscribe to each category on its own connection. |
| 1869 | const category = this.classifyFuturesStream(streams[0]); |
| 1870 | const mismatch = streams.find(s => this.classifyFuturesStream(s) !== category); |
| 1871 | if (mismatch !== undefined) { |
| 1872 | const mismatchCategory = this.classifyFuturesStream(mismatch); |
| 1873 | throw new Error(`futuresSubscribe: cannot combine '${category}' stream "${streams[0]}" with '${mismatchCategory}' stream "${mismatch}" on one connection. Binance routes futures streams to separate /public, /market and /private endpoints; subscribe to each category separately.`); |
| 1874 | } |
| 1875 | const baseUrl = this.getFStreamUrl(category); |
| 1876 | let ws: any = undefined; |
| 1877 | if (socksproxy) { |
| 1878 | socksproxy = this.proxyReplacewithIp(socksproxy); |
| 1879 | if (this.Options.verbose) this.Options.log(`futuresSubscribe: using socks proxy server ${socksproxy}`); |
| 1880 | const agent = new SocksProxyAgent({ |
| 1881 | protocol: this.parseProxy(socksproxy)[0], |
| 1882 | host: this.parseProxy(socksproxy)[1], |
| 1883 | port: this.parseProxy(socksproxy)[2] |
| 1884 | }); |
| 1885 | ws = new WebSocket(baseUrl + queryParams, { agent }); |
| 1886 | } else if (httpsproxy) { |
| 1887 | if (this.Options.verbose) this.Options.log(`futuresSubscribe: using proxy server ${httpsproxy}`); |
| 1888 | const config = url.parse(httpsproxy); |
| 1889 | const agent = new HttpsProxyAgent(config); |
| 1890 | ws = new WebSocket(baseUrl + queryParams, { agent }); |
| 1891 | } else { |
| 1892 | ws = new WebSocket(baseUrl + queryParams); |
| 1893 | } |
| 1894 | |
| 1895 | ws.reconnect = this.Options.reconnect; |
| 1896 | ws.endpoint = stringHash(queryParams); |
| 1897 | ws.isAlive = false; |
| 1898 | if (this.Options.verbose) { |
| 1899 | this.Options.log(`futuresSubscribe: Subscribed to [${ws.endpoint}] ${queryParams}`); |
| 1900 | } |
| 1901 | ws.on('open', this.handleFuturesSocketOpen.bind(this, ws, params.openCallback)); |
| 1902 | ws.on('pong', this.handleFuturesSocketHeartbeat.bind(this, ws)); |
| 1903 | ws.on('error', this.handleFuturesSocketError.bind(this, ws)); |
| 1904 | ws.on('close', this.handleFuturesSocketClose.bind(this, ws, params.reconnect)); |
| 1905 | ws.on('message', data => { |
| 1906 | try { |
| 1907 | if (this.Options.verbose) this.Options.log('futuresSubscribe: Received data:', data); |
| 1908 | callback(JSONbig.parse(data).data); |
| 1909 | } catch (error) { |
| 1910 | this.Options.log(`futuresSubscribe: Parse error: ${error.message}`); |
| 1911 | } |
| 1912 | }); |
| 1913 | return ws; |
no test coverage detected