binance 2.15.14 → 3.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/README.md +5 -2
  2. package/lib/coinm-client.js.map +1 -1
  3. package/lib/index.d.ts +10 -4
  4. package/lib/index.js +10 -4
  5. package/lib/index.js.map +1 -1
  6. package/lib/main-client.d.ts +13 -2
  7. package/lib/main-client.js +15 -1
  8. package/lib/main-client.js.map +1 -1
  9. package/lib/portfolio-client.js.map +1 -1
  10. package/lib/types/futures.d.ts +1 -0
  11. package/lib/types/futures.js +3 -3
  12. package/lib/types/futures.js.map +1 -1
  13. package/lib/types/spot.js +4 -4
  14. package/lib/types/spot.js.map +1 -1
  15. package/lib/types/websockets/ws-api-requests.d.ts +7 -0
  16. package/lib/types/websockets/ws-api-requests.js +3 -0
  17. package/lib/types/websockets/ws-api-requests.js.map +1 -0
  18. package/lib/types/websockets/ws-api-responses.d.ts +8 -0
  19. package/lib/types/websockets/ws-api-responses.js +3 -0
  20. package/lib/types/websockets/ws-api-responses.js.map +1 -0
  21. package/lib/types/websockets/ws-api.d.ts +201 -0
  22. package/lib/types/websockets/ws-api.js +29 -0
  23. package/lib/types/websockets/ws-api.js.map +1 -0
  24. package/lib/types/{websockets.d.ts → websockets/ws-events-formatted.d.ts} +4 -410
  25. package/lib/types/websockets/ws-events-formatted.js +3 -0
  26. package/lib/types/websockets/ws-events-formatted.js.map +1 -0
  27. package/lib/types/websockets/ws-events-raw.d.ts +401 -0
  28. package/lib/types/{websockets.js → websockets/ws-events-raw.js} +1 -1
  29. package/lib/types/websockets/ws-events-raw.js.map +1 -0
  30. package/lib/types/websockets/ws-general.d.ts +98 -0
  31. package/lib/types/websockets/ws-general.js +11 -0
  32. package/lib/types/websockets/ws-general.js.map +1 -0
  33. package/lib/usdm-client.js.map +1 -1
  34. package/lib/util/BaseRestClient.d.ts +1 -1
  35. package/lib/util/BaseRestClient.js +1 -1
  36. package/lib/util/BaseRestClient.js.map +1 -1
  37. package/lib/util/BaseWSClient.d.ts +225 -0
  38. package/lib/util/BaseWSClient.js +729 -0
  39. package/lib/util/BaseWSClient.js.map +1 -0
  40. package/lib/util/beautifier-maps.d.ts +151 -0
  41. package/lib/util/beautifier-maps.js +198 -37
  42. package/lib/util/beautifier-maps.js.map +1 -1
  43. package/lib/util/beautifier.d.ts +7 -3
  44. package/lib/util/beautifier.js +40 -11
  45. package/lib/util/beautifier.js.map +1 -1
  46. package/lib/util/browser-support.d.ts +2 -1
  47. package/lib/util/browser-support.js +46 -28
  48. package/lib/util/browser-support.js.map +1 -1
  49. package/lib/util/logger.d.ts +8 -0
  50. package/lib/util/logger.js +17 -0
  51. package/lib/util/logger.js.map +1 -0
  52. package/lib/util/node-support.d.ts +2 -1
  53. package/lib/util/node-support.js +35 -15
  54. package/lib/util/node-support.js.map +1 -1
  55. package/lib/util/requestUtils.d.ts +19 -19
  56. package/lib/util/requestUtils.js +119 -38
  57. package/lib/util/requestUtils.js.map +1 -1
  58. package/lib/util/typeGuards.d.ts +9 -1
  59. package/lib/util/typeGuards.js +59 -34
  60. package/lib/util/typeGuards.js.map +1 -1
  61. package/lib/util/usdm/exchangeInfo.js +2 -3
  62. package/lib/util/usdm/exchangeInfo.js.map +1 -1
  63. package/lib/util/webCryptoAPI.d.ts +14 -0
  64. package/lib/util/webCryptoAPI.js +120 -0
  65. package/lib/util/webCryptoAPI.js.map +1 -0
  66. package/lib/util/websockets/WsStore.d.ts +74 -0
  67. package/lib/util/websockets/WsStore.js +279 -0
  68. package/lib/util/websockets/WsStore.js.map +1 -0
  69. package/lib/util/websockets/WsStore.types.d.ts +53 -0
  70. package/lib/util/websockets/WsStore.types.js +14 -0
  71. package/lib/util/websockets/WsStore.types.js.map +1 -0
  72. package/lib/util/websockets/listen-key-state-cache.d.ts +21 -0
  73. package/lib/util/websockets/listen-key-state-cache.js +80 -0
  74. package/lib/util/websockets/listen-key-state-cache.js.map +1 -0
  75. package/lib/util/websockets/rest-client-cache.d.ts +13 -0
  76. package/lib/util/websockets/rest-client-cache.js +56 -0
  77. package/lib/util/websockets/rest-client-cache.js.map +1 -0
  78. package/lib/util/websockets/user-data-stream-manager.d.ts +54 -0
  79. package/lib/util/websockets/user-data-stream-manager.js +256 -0
  80. package/lib/util/websockets/user-data-stream-manager.js.map +1 -0
  81. package/lib/util/websockets/websocket-util.d.ts +124 -0
  82. package/lib/util/websockets/websocket-util.js +481 -0
  83. package/lib/util/websockets/websocket-util.js.map +1 -0
  84. package/lib/websocket-client-legacy.d.ts +288 -0
  85. package/lib/websocket-client-legacy.js +1113 -0
  86. package/lib/websocket-client-legacy.js.map +1 -0
  87. package/lib/websocket-client.d.ts +228 -168
  88. package/lib/websocket-client.js +927 -834
  89. package/lib/websocket-client.js.map +1 -1
  90. package/package.json +5 -5
  91. package/lib/logger.d.ts +0 -9
  92. package/lib/logger.js +0 -23
  93. package/lib/logger.js.map +0 -1
  94. package/lib/types/websockets.js.map +0 -1
  95. package/lib/util/WsStore.d.ts +0 -57
  96. package/lib/util/WsStore.js +0 -101
  97. package/lib/util/WsStore.js.map +0 -1
  98. package/lib/util/ws-utils.d.ts +0 -7
  99. package/lib/util/ws-utils.js +0 -16
  100. package/lib/util/ws-utils.js.map +0 -1
@@ -0,0 +1,1113 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __rest = (this && this.__rest) || function (s, e) {
12
+ var t = {};
13
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
14
+ t[p] = s[p];
15
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
16
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
17
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
18
+ t[p[i]] = s[p[i]];
19
+ }
20
+ return t;
21
+ };
22
+ var __importDefault = (this && this.__importDefault) || function (mod) {
23
+ return (mod && mod.__esModule) ? mod : { "default": mod };
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.WebsocketClientV1 = void 0;
27
+ /* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */
28
+ const events_1 = require("events");
29
+ const isomorphic_ws_1 = __importDefault(require("isomorphic-ws"));
30
+ const beautifier_1 = __importDefault(require("./util/beautifier"));
31
+ const logger_1 = require("./util/logger");
32
+ const requestUtils_1 = require("./util/requestUtils");
33
+ const typeGuards_1 = require("./util/typeGuards");
34
+ const listen_key_state_cache_1 = require("./util/websockets/listen-key-state-cache");
35
+ const rest_client_cache_1 = require("./util/websockets/rest-client-cache");
36
+ const websocket_util_1 = require("./util/websockets/websocket-util");
37
+ const WsStore_1 = require("./util/websockets/WsStore");
38
+ const WsStore_types_1 = require("./util/websockets/WsStore.types");
39
+ const wsBaseEndpoints = {
40
+ spot: 'wss://stream.binance.com:9443',
41
+ crossMargin: 'wss://stream.binance.com:9443',
42
+ isolatedMargin: 'wss://stream.binance.com:9443',
43
+ usdm: 'wss://fstream.binance.com',
44
+ usdmTestnet: 'wss://stream.binancefuture.com',
45
+ coinm: 'wss://dstream.binance.com',
46
+ coinmTestnet: 'wss://dstream.binancefuture.com',
47
+ options: 'wss://vstream.binance.com',
48
+ optionsTestnet: 'wss://testnetws.binanceops.com',
49
+ riskDataMargin: '',
50
+ spotTestnet: '',
51
+ portfoliom: '',
52
+ };
53
+ /**
54
+ * This legacy websocket client creates one websocket connection per topic.
55
+ *
56
+ * If subscribing to a lot of topics, consider using the new multiplex `WebsocketClient`.
57
+ */
58
+ class WebsocketClientV1 extends events_1.EventEmitter {
59
+ constructor(options, logger) {
60
+ super();
61
+ this.beautifier = new beautifier_1.default({
62
+ warnKeyMissingInMap: false,
63
+ });
64
+ this.restClientCache = new rest_client_cache_1.RestClientCache();
65
+ this.logger = logger || logger_1.DefaultLogger;
66
+ this.wsStore = new WsStore_1.WsStore(this.logger);
67
+ this.listenKeyStateCache = new listen_key_state_cache_1.ListenKeyStateCache(this.logger);
68
+ this.options = Object.assign({
69
+ // Some defaults:
70
+ testnet: false, pongTimeout: 7500, pingInterval: 10000, reconnectTimeout: 500, recvWindow: 5000,
71
+ // Automatically send an authentication op/request after a connection opens, for private connections.
72
+ authPrivateConnectionsOnConnect: false,
73
+ // Individual requests require a signature
74
+ authPrivateRequests: true }, options);
75
+ this.wsUrlKeyMap = {};
76
+ // add default error handling so this doesn't crash node (if the user didn't set a handler)
77
+ this.on('error', () => { });
78
+ }
79
+ getRestClientOptions() {
80
+ return Object.assign(Object.assign(Object.assign({}, this.options), this.options.restOptions), { api_key: this.options.api_key, api_secret: this.options.api_secret });
81
+ }
82
+ connectToWsUrl(url, wsKey, forceNewConnection) {
83
+ const wsRefKey = wsKey || url;
84
+ const oldWs = this.wsStore.getWs(wsRefKey);
85
+ if (oldWs && this.wsStore.isWsOpen(wsRefKey) && !forceNewConnection) {
86
+ this.logger.trace('connectToWsUrl(): Returning existing open WS connection', Object.assign(Object.assign({}, websocket_util_1.WS_LOGGER_CATEGORY), { wsRefKey }));
87
+ return oldWs;
88
+ }
89
+ this.logger.trace(`connectToWsUrl(): Opening WS connection to URL: ${url}`, Object.assign(Object.assign({}, websocket_util_1.WS_LOGGER_CATEGORY), { wsRefKey }));
90
+ const _a = this.options.wsOptions || {}, { protocols = [] } = _a, wsOptions = __rest(_a, ["protocols"]);
91
+ const ws = new isomorphic_ws_1.default(url, protocols, wsOptions);
92
+ this.wsUrlKeyMap[url] = wsRefKey;
93
+ if (typeof ws.on === 'function') {
94
+ ws.on('ping', (event) => this.onWsPing(event, wsRefKey, ws, 'event'));
95
+ ws.on('pong', (event) => this.onWsPong(event, wsRefKey, 'event'));
96
+ }
97
+ ws.onopen = (event) => this.onWsOpen(event, wsRefKey, url);
98
+ ws.onerror = (event) => this.parseWsError('WS Error Event', event, wsRefKey, url);
99
+ ws.onclose = (event) => this.onWsClose(event, wsRefKey, ws, url);
100
+ ws.onmessage = (event) => this.onWsMessage(event, wsRefKey, 'function');
101
+ // Not sure these work in the browser, the traditional event listeners are required for ping/pong frames in node
102
+ ws.onping = (event) => this.onWsPing(event, wsRefKey, ws, 'function');
103
+ ws.onpong = (event) => this.onWsPong(event, wsRefKey, 'function');
104
+ // Add ws connection with key to store
105
+ this.wsStore.setWs(wsRefKey, ws);
106
+ ws.wsKey = wsRefKey;
107
+ return ws;
108
+ }
109
+ tryWsSend(wsKey, wsMessage) {
110
+ try {
111
+ this.logger.trace('Sending upstream ws message: ', Object.assign(Object.assign({}, websocket_util_1.WS_LOGGER_CATEGORY), { wsMessage,
112
+ wsKey }));
113
+ if (!wsKey) {
114
+ throw new Error('No wsKey provided');
115
+ }
116
+ const ws = this.getWs(wsKey);
117
+ if (!ws) {
118
+ throw new Error(`No active websocket connection exists for wsKey: ${wsKey}`);
119
+ }
120
+ ws.send(wsMessage);
121
+ }
122
+ catch (e) {
123
+ this.logger.error('Failed to send WS message', Object.assign(Object.assign({}, websocket_util_1.WS_LOGGER_CATEGORY), { wsMessage,
124
+ wsKey, exception: e }));
125
+ }
126
+ }
127
+ tryWsPing(wsKey) {
128
+ try {
129
+ // this.logger.trace(`Sending upstream ping: `, { ...loggerCategory, wsKey });
130
+ if (!wsKey) {
131
+ throw new Error('No wsKey provided');
132
+ }
133
+ const ws = this.getWs(wsKey);
134
+ if (!ws) {
135
+ throw new Error(`No active websocket connection exists for wsKey: ${wsKey}`);
136
+ }
137
+ // Binance allows unsolicited pongs, so we send both (though we expect a pong in response to our ping if the connection is still alive)
138
+ if (ws.readyState === 1) {
139
+ ws.ping();
140
+ ws.pong();
141
+ }
142
+ else {
143
+ this.logger.trace('WS ready state not open - refusing to send WS ping', Object.assign(Object.assign({}, websocket_util_1.WS_LOGGER_CATEGORY), { wsKey, readyState: ws === null || ws === void 0 ? void 0 : ws.readyState }));
144
+ }
145
+ }
146
+ catch (e) {
147
+ this.logger.error('Failed to send WS ping', Object.assign(Object.assign({}, websocket_util_1.WS_LOGGER_CATEGORY), { wsKey, exception: e }));
148
+ }
149
+ }
150
+ onWsOpen(ws, wsKey, wsUrl) {
151
+ this.logger.trace(`onWsOpen(): ${wsUrl} : ${wsKey}`);
152
+ if (this.wsStore.isConnectionState(wsKey, WsStore_types_1.WsConnectionStateEnum.RECONNECTING)) {
153
+ this.logger.info('Websocket reconnected', Object.assign(Object.assign({}, websocket_util_1.WS_LOGGER_CATEGORY), { wsKey }));
154
+ this.emit('reconnected', { wsKey, ws });
155
+ }
156
+ else {
157
+ this.logger.info('Websocket connected', Object.assign(Object.assign({}, websocket_util_1.WS_LOGGER_CATEGORY), { wsKey }));
158
+ this.emit('open', { wsKey, ws });
159
+ }
160
+ this.setWsState(wsKey, WsStore_types_1.WsConnectionStateEnum.CONNECTED);
161
+ const topics = [...this.wsStore.getTopics(wsKey)];
162
+ if (topics.length) {
163
+ this.requestSubscribeTopics(wsKey, topics);
164
+ }
165
+ if (!this.options.disableHeartbeat) {
166
+ const wsState = this.wsStore.get(wsKey, true);
167
+ if (wsState.activePingTimer) {
168
+ clearInterval(wsState.activePingTimer);
169
+ }
170
+ wsState.activePingTimer = setInterval(() => this.sendPing(wsKey, wsUrl), this.options.pingInterval);
171
+ }
172
+ }
173
+ onWsClose(event, wsKey, ws, wsUrl) {
174
+ var _a;
175
+ const wsConnectionState = this.wsStore.getConnectionState(wsKey);
176
+ const { market, listenKey, isUserData } = (0, websocket_util_1.getContextFromWsKey)(wsKey);
177
+ this.logger.info('Websocket connection closed', Object.assign(Object.assign({}, websocket_util_1.WS_LOGGER_CATEGORY), { wsKey, eventCloseCode: (_a = event === null || event === void 0 ? void 0 : event.target) === null || _a === void 0 ? void 0 : _a._closeCode, wsConnectionState,
178
+ isUserData,
179
+ listenKey,
180
+ market }));
181
+ // Clear any timers before we initiate revival
182
+ this.clearTimers(wsKey);
183
+ // User data sockets include the listen key. To prevent accummulation in memory we should clean up old disconnected states
184
+ if (isUserData) {
185
+ this.wsStore.delete(wsKey);
186
+ if (listenKey) {
187
+ this.listenKeyStateCache.clearAllListenKeyState(listenKey);
188
+ }
189
+ }
190
+ if (wsConnectionState !== WsStore_types_1.WsConnectionStateEnum.CLOSING) {
191
+ this.reconnectWithDelay(wsKey, this.options.reconnectTimeout, wsUrl);
192
+ this.emit('reconnecting', { wsKey, event, ws });
193
+ }
194
+ else {
195
+ this.setWsState(wsKey, WsStore_types_1.WsConnectionStateEnum.INITIAL);
196
+ this.emit('close', { wsKey, event, ws });
197
+ }
198
+ }
199
+ onWsMessage(event, wsKey, source) {
200
+ try {
201
+ this.clearPongTimer(wsKey);
202
+ const msg = (0, websocket_util_1.parseRawWsMessage)(event);
203
+ // Edge case where raw event does not include event type, detect using wsKey and mutate msg.e
204
+ (0, requestUtils_1.appendEventIfMissing)(msg, wsKey);
205
+ (0, websocket_util_1.appendEventMarket)(msg, wsKey);
206
+ const eventType = (0, websocket_util_1.parseEventTypeFromMessage)(wsKey, msg);
207
+ if (eventType) {
208
+ this.emit('message', msg);
209
+ if (eventType === 'listenKeyExpired') {
210
+ const { market } = (0, websocket_util_1.getContextFromWsKey)(wsKey);
211
+ this.logger.info(`${market} listenKey expired - attempting to respawn user data stream: ${wsKey}`);
212
+ // Just closing the connection (with the last parameter as true) will handle cleanup and respawn
213
+ const shouldTriggerReconnect = true;
214
+ this.close(wsKey, shouldTriggerReconnect);
215
+ }
216
+ if (this.options.beautify) {
217
+ const beautifiedMessage = this.beautifier.beautifyWsMessage(msg, eventType, false);
218
+ this.emit('formattedMessage', beautifiedMessage);
219
+ // emit a separate event for user data messages
220
+ if (!Array.isArray(beautifiedMessage)) {
221
+ if ([
222
+ 'balanceUpdate',
223
+ 'executionReport',
224
+ 'listStatus',
225
+ 'listenKeyExpired',
226
+ 'outboundAccountPosition',
227
+ 'ACCOUNT_CONFIG_UPDATE',
228
+ 'ACCOUNT_UPDATE',
229
+ 'MARGIN_CALL',
230
+ 'ORDER_TRADE_UPDATE',
231
+ 'TRADE_LITE',
232
+ 'CONDITIONAL_ORDER_TRIGGER_REJECT',
233
+ ].includes(eventType)) {
234
+ this.emit('formattedUserDataMessage', beautifiedMessage);
235
+ }
236
+ }
237
+ }
238
+ return;
239
+ }
240
+ if (msg.result !== undefined) {
241
+ this.emit('reply', {
242
+ type: event.type,
243
+ data: msg,
244
+ wsKey,
245
+ });
246
+ return;
247
+ }
248
+ this.logger.error('Bug? Unhandled ws message event type. Check if appendEventIfMissing needs to parse wsKey.', Object.assign(Object.assign({}, websocket_util_1.WS_LOGGER_CATEGORY), { parsedMessage: JSON.stringify(msg), rawEvent: event, wsKey,
249
+ source }));
250
+ }
251
+ catch (e) {
252
+ this.logger.error('Exception parsing ws message: ', Object.assign(Object.assign({}, websocket_util_1.WS_LOGGER_CATEGORY), { rawEvent: event, wsKey, error: e, source }));
253
+ this.emit('error', { wsKey, error: e, rawEvent: event, source });
254
+ }
255
+ }
256
+ sendPing(wsKey, wsUrl) {
257
+ this.clearPongTimer(wsKey);
258
+ this.logger.trace('Sending ping', Object.assign(Object.assign({}, websocket_util_1.WS_LOGGER_CATEGORY), { wsKey }));
259
+ this.tryWsPing(wsKey);
260
+ this.wsStore.get(wsKey, true).activePongTimer = setTimeout(() => this.executeReconnectableClose(wsKey, 'Pong timeout', wsUrl), this.options.pongTimeout);
261
+ }
262
+ onWsPing(event, wsKey, ws, source) {
263
+ this.logger.trace('Received ping, sending pong frame', Object.assign(Object.assign({}, websocket_util_1.WS_LOGGER_CATEGORY), { wsKey,
264
+ source }));
265
+ ws.pong();
266
+ }
267
+ onWsPong(event, wsKey, source) {
268
+ this.logger.trace('Received pong, clearing pong timer', Object.assign(Object.assign({}, websocket_util_1.WS_LOGGER_CATEGORY), { wsKey,
269
+ source }));
270
+ this.clearPongTimer(wsKey);
271
+ }
272
+ /**
273
+ * Closes a connection, if it's even open. If open, this will trigger a reconnect asynchronously.
274
+ * If closed, trigger a reconnect immediately
275
+ */
276
+ executeReconnectableClose(wsKey, reason, wsUrl) {
277
+ this.logger.info(new Date(), `${reason} - closing socket to reconnect`, Object.assign(Object.assign({}, websocket_util_1.WS_LOGGER_CATEGORY), { wsKey,
278
+ reason }));
279
+ const wasOpen = this.wsStore.isWsOpen(wsKey);
280
+ (0, websocket_util_1.safeTerminateWs)(this.getWs(wsKey), true);
281
+ this.clearPingTimer(wsKey);
282
+ this.clearPongTimer(wsKey);
283
+ if (!wasOpen) {
284
+ this.logger.info(`${reason} - socket already closed - trigger immediate reconnect`, Object.assign(Object.assign({}, websocket_util_1.WS_LOGGER_CATEGORY), { wsKey,
285
+ reason }));
286
+ this.reconnectWithDelay(wsKey, this.options.reconnectTimeout, wsUrl);
287
+ }
288
+ }
289
+ close(wsKey, shouldReconnectAfterClose) {
290
+ var _a;
291
+ this.logger.info('Closing connection', Object.assign(Object.assign({}, websocket_util_1.WS_LOGGER_CATEGORY), { wsKey, willReconnect: shouldReconnectAfterClose }));
292
+ this.setWsState(wsKey, shouldReconnectAfterClose
293
+ ? WsStore_types_1.WsConnectionStateEnum.RECONNECTING
294
+ : WsStore_types_1.WsConnectionStateEnum.CLOSING);
295
+ this.clearTimers(wsKey);
296
+ (_a = this.getWs(wsKey)) === null || _a === void 0 ? void 0 : _a.close();
297
+ const { listenKey } = (0, websocket_util_1.getContextFromWsKey)(wsKey);
298
+ if (listenKey) {
299
+ this.teardownUserDataListenKey(listenKey, this.getWs(wsKey));
300
+ }
301
+ else {
302
+ (0, websocket_util_1.safeTerminateWs)(this.getWs(wsKey), true);
303
+ }
304
+ }
305
+ closeAll(shouldReconnectAfterClose) {
306
+ const keys = this.wsStore.getKeys();
307
+ this.logger.info(`Closing all ws connections: ${keys}`);
308
+ keys.forEach((key) => {
309
+ this.close(key, shouldReconnectAfterClose);
310
+ });
311
+ }
312
+ closeWs(ws, shouldReconnectAfterClose) {
313
+ const wsKey = this.wsUrlKeyMap[ws.url] || (ws === null || ws === void 0 ? void 0 : ws.wsKey);
314
+ if (!wsKey) {
315
+ throw new Error('Cannot close websocket as it has no known wsKey attached.');
316
+ }
317
+ return this.close(wsKey, shouldReconnectAfterClose);
318
+ }
319
+ parseWsError(context, error, wsKey, wsUrl) {
320
+ this.logger.error(context, Object.assign(Object.assign({}, websocket_util_1.WS_LOGGER_CATEGORY), { wsKey, error }));
321
+ if (!error.message) {
322
+ this.logger.error(`${context} due to unexpected error: `, error);
323
+ this.emit('error', { error, wsKey, wsUrl });
324
+ return;
325
+ }
326
+ switch (error.message) {
327
+ case 'Unexpected server response: 401':
328
+ this.logger.error(`${context} due to 401 authorization failure.`, Object.assign(Object.assign({}, websocket_util_1.WS_LOGGER_CATEGORY), { wsKey }));
329
+ break;
330
+ default:
331
+ if (this.wsStore.getConnectionState(wsKey) !==
332
+ WsStore_types_1.WsConnectionStateEnum.CLOSING) {
333
+ this.logger.error(`${context} due to unexpected response error: "${(error === null || error === void 0 ? void 0 : error.msg) || (error === null || error === void 0 ? void 0 : error.message) || error}"`, Object.assign(Object.assign({}, websocket_util_1.WS_LOGGER_CATEGORY), { wsKey, error }));
334
+ this.executeReconnectableClose(wsKey, 'unhandled onWsError', wsUrl);
335
+ }
336
+ else {
337
+ this.logger.info(`${wsKey} socket forcefully closed. Will not reconnect.`);
338
+ }
339
+ break;
340
+ }
341
+ this.emit('error', { error, wsKey, wsUrl });
342
+ }
343
+ reconnectWithDelay(wsKey, connectionDelayMs, wsUrl) {
344
+ var _a;
345
+ this.clearTimers(wsKey);
346
+ if (this.wsStore.getConnectionState(wsKey) !==
347
+ WsStore_types_1.WsConnectionStateEnum.CONNECTING) {
348
+ this.setWsState(wsKey, WsStore_types_1.WsConnectionStateEnum.RECONNECTING);
349
+ }
350
+ this.logger.info('Reconnecting to websocket with delay...', Object.assign(Object.assign({}, websocket_util_1.WS_LOGGER_CATEGORY), { wsKey,
351
+ connectionDelayMs }));
352
+ if ((_a = this.wsStore.get(wsKey)) === null || _a === void 0 ? void 0 : _a.activeReconnectTimer) {
353
+ this.clearReconnectTimer(wsKey);
354
+ }
355
+ this.wsStore.get(wsKey, true).activeReconnectTimer = setTimeout(() => {
356
+ this.clearReconnectTimer(wsKey);
357
+ if (wsKey.includes('userData')) {
358
+ const { market, symbol, isTestnet } = (0, websocket_util_1.getContextFromWsKey)(wsKey);
359
+ this.logger.info('Reconnecting to user data stream', Object.assign(Object.assign({}, websocket_util_1.WS_LOGGER_CATEGORY), { wsKey,
360
+ market,
361
+ symbol }));
362
+ // We'll set a new one once the new stream respawns, with a diff listenKey in the key
363
+ this.wsStore.delete(wsKey);
364
+ this.respawnUserDataStream(market, symbol, isTestnet);
365
+ return;
366
+ }
367
+ this.logger.info('Reconnecting to public websocket', Object.assign(Object.assign({}, websocket_util_1.WS_LOGGER_CATEGORY), { wsKey,
368
+ wsUrl }));
369
+ this.connectToWsUrl(wsUrl, wsKey);
370
+ }, connectionDelayMs);
371
+ }
372
+ clearTimers(wsKey) {
373
+ this.clearPingTimer(wsKey);
374
+ this.clearPongTimer(wsKey);
375
+ this.clearReconnectTimer(wsKey);
376
+ }
377
+ // Send a ping at intervals
378
+ clearPingTimer(wsKey) {
379
+ const wsState = this.wsStore.get(wsKey);
380
+ if (wsState === null || wsState === void 0 ? void 0 : wsState.activePingTimer) {
381
+ clearInterval(wsState.activePingTimer);
382
+ wsState.activePingTimer = undefined;
383
+ }
384
+ }
385
+ // Expect a pong within a time limit
386
+ clearPongTimer(wsKey) {
387
+ const wsState = this.wsStore.get(wsKey);
388
+ if (wsState === null || wsState === void 0 ? void 0 : wsState.activePongTimer) {
389
+ clearTimeout(wsState.activePongTimer);
390
+ wsState.activePongTimer = undefined;
391
+ }
392
+ }
393
+ // Timer tracking that a reconnect is about to happen / in progress
394
+ clearReconnectTimer(wsKey) {
395
+ const wsState = this.wsStore.get(wsKey);
396
+ if (wsState === null || wsState === void 0 ? void 0 : wsState.activeReconnectTimer) {
397
+ clearTimeout(wsState.activeReconnectTimer);
398
+ wsState.activeReconnectTimer = undefined;
399
+ }
400
+ }
401
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
402
+ getWsBaseUrl(market, wsKey) {
403
+ if (this.options.wsUrl) {
404
+ return this.options.wsUrl;
405
+ }
406
+ return wsBaseEndpoints[market];
407
+ }
408
+ getWs(wsKey) {
409
+ return this.wsStore.getWs(wsKey);
410
+ }
411
+ setWsState(wsKey, state) {
412
+ this.wsStore.setConnectionState(wsKey, state);
413
+ }
414
+ /**
415
+ * Send WS message to subscribe to topics. Use subscribe() to call this.
416
+ */
417
+ requestSubscribeTopics(wsKey, topics) {
418
+ const wsMessage = JSON.stringify({
419
+ method: 'SUBSCRIBE',
420
+ params: topics,
421
+ id: new Date().getTime(),
422
+ });
423
+ this.tryWsSend(wsKey, wsMessage);
424
+ }
425
+ /**
426
+ * Send WS message to unsubscribe from topics. Use unsubscribe() to call this.
427
+ */
428
+ requestUnsubscribeTopics(wsKey, topics) {
429
+ const wsMessage = JSON.stringify({
430
+ op: 'UNSUBSCRIBE',
431
+ params: topics,
432
+ id: new Date().getTime(),
433
+ });
434
+ this.tryWsSend(wsKey, wsMessage);
435
+ }
436
+ /**
437
+ * Send WS message to unsubscribe from topics.
438
+ */
439
+ requestListSubscriptions(wsKey, requestId) {
440
+ const wsMessage = JSON.stringify({
441
+ method: 'LIST_SUBSCRIPTIONS',
442
+ id: requestId,
443
+ });
444
+ this.tryWsSend(wsKey, wsMessage);
445
+ }
446
+ /**
447
+ * Send WS message to set property state
448
+ */
449
+ requestSetProperty(wsKey, property, value, requestId) {
450
+ const wsMessage = JSON.stringify({
451
+ method: 'SET_PROPERTY',
452
+ params: [property, value],
453
+ id: requestId,
454
+ });
455
+ this.tryWsSend(wsKey, wsMessage);
456
+ }
457
+ /**
458
+ * Send WS message to get property state
459
+ */
460
+ requestGetProperty(wsKey, property, requestId) {
461
+ const wsMessage = JSON.stringify({
462
+ method: 'GET_PROPERTY',
463
+ params: [property],
464
+ id: requestId,
465
+ });
466
+ this.tryWsSend(wsKey, wsMessage);
467
+ }
468
+ /**
469
+ * --------------------------
470
+ * User data listen key tracking & persistence
471
+ * --------------------------
472
+ **/
473
+ setKeepAliveListenKeyTimer(listenKey, market, ws, wsKey, symbol, isTestnet) {
474
+ this.listenKeyStateCache.clearAllListenKeyState(listenKey);
475
+ const listenKeyState = this.listenKeyStateCache.getListenKeyState(listenKey, market);
476
+ this.logger.trace(`Created new listen key interval timer for ${listenKey}`);
477
+ // Set timer to keep WS alive every 50 minutes
478
+ const minutes50 = 1000 * 60 * 50;
479
+ listenKeyState.keepAliveTimer = setInterval(() => this.checkKeepAliveListenKey(listenKey, market, ws, wsKey, symbol, isTestnet), minutes50);
480
+ }
481
+ sendKeepAliveForMarket(listenKey, market, ws, wsKey, symbol, isTestnet) {
482
+ switch (market) {
483
+ case 'spot':
484
+ return this.restClientCache
485
+ .getSpotRestClient(this.getRestClientOptions(), this.options.requestOptions)
486
+ .keepAliveSpotUserDataListenKey(listenKey);
487
+ case 'spotTestnet':
488
+ return this.restClientCache
489
+ .getSpotRestClient(this.getRestClientOptions(), this.options.requestOptions, true)
490
+ .keepAliveSpotUserDataListenKey(listenKey);
491
+ case 'crossMargin':
492
+ return this.restClientCache
493
+ .getSpotRestClient(this.getRestClientOptions(), this.options.requestOptions)
494
+ .keepAliveMarginUserDataListenKey(listenKey);
495
+ case 'isolatedMargin':
496
+ return this.restClientCache
497
+ .getSpotRestClient(this.getRestClientOptions(), this.options.requestOptions)
498
+ .keepAliveIsolatedMarginUserDataListenKey({
499
+ listenKey,
500
+ symbol: symbol,
501
+ });
502
+ case 'coinm':
503
+ case 'options':
504
+ case 'optionsTestnet':
505
+ case 'usdm':
506
+ return this.restClientCache
507
+ .getUSDMRestClient(this.getRestClientOptions(), this.options.requestOptions)
508
+ .keepAliveFuturesUserDataListenKey();
509
+ case 'usdmTestnet':
510
+ return this.restClientCache
511
+ .getUSDMRestClient(this.getRestClientOptions(), this.options.requestOptions, isTestnet)
512
+ .keepAliveFuturesUserDataListenKey();
513
+ case 'coinmTestnet':
514
+ return this.restClientCache
515
+ .getCOINMRestClient(this.getRestClientOptions(), this.options.requestOptions, isTestnet)
516
+ .keepAliveFuturesUserDataListenKey();
517
+ case 'portfoliom':
518
+ return this.restClientCache
519
+ .getPortfolioClient(this.getRestClientOptions(), this.options.requestOptions)
520
+ .keepAlivePMUserDataListenKey();
521
+ case 'riskDataMargin': {
522
+ throw new Error('Unsupported user data stream. Use the new "WebsocketClient" to use this stream.');
523
+ }
524
+ default:
525
+ throw (0, typeGuards_1.neverGuard)(market, `Failed to send keep alive for user data stream in unhandled market ${market}`);
526
+ }
527
+ }
528
+ checkKeepAliveListenKey(listenKey, market, ws, wsKey, symbol, isTestnet) {
529
+ return __awaiter(this, void 0, void 0, function* () {
530
+ const listenKeyState = this.listenKeyStateCache.getListenKeyState(listenKey, market);
531
+ try {
532
+ if (listenKeyState.keepAliveRetryTimer) {
533
+ clearTimeout(listenKeyState.keepAliveRetryTimer);
534
+ listenKeyState.keepAliveRetryTimer = undefined;
535
+ }
536
+ // Simple way to test keep alive failure handling:
537
+ // throw new Error(`Fake keep alive failure`);
538
+ yield this.sendKeepAliveForMarket(listenKey, market, ws, wsKey, symbol, isTestnet);
539
+ listenKeyState.lastKeepAlive = Date.now();
540
+ listenKeyState.keepAliveFailures = 0;
541
+ this.logger.info(`Completed keep alive cycle for listenKey(${listenKey}) in market(${market})`, Object.assign(Object.assign({}, websocket_util_1.WS_LOGGER_CATEGORY), { listenKey }));
542
+ }
543
+ catch (e) {
544
+ listenKeyState.keepAliveFailures++;
545
+ // code: -1125,
546
+ // message: 'This listenKey does not exist.',
547
+ const errorCode = e === null || e === void 0 ? void 0 : e.code;
548
+ if (errorCode === -1125) {
549
+ this.logger.error('FATAL: Failed to keep WS alive for listen key - listen key expired/invalid. Respawning with fresh listen key...', Object.assign(Object.assign({}, websocket_util_1.WS_LOGGER_CATEGORY), { listenKey, error: e, errorCode, errorMsg: e === null || e === void 0 ? void 0 : e.message }));
550
+ const shouldReconnectAfterClose = false;
551
+ this.close(wsKey, shouldReconnectAfterClose);
552
+ this.respawnUserDataStream(market, symbol);
553
+ return;
554
+ }
555
+ // If max failurees reached, tear down and respawn if allowed
556
+ if (listenKeyState.keepAliveFailures >= 3) {
557
+ this.logger.error('FATAL: Failed to keep WS alive for listen key after 3 attempts', Object.assign(Object.assign({}, websocket_util_1.WS_LOGGER_CATEGORY), { listenKey, error: e }));
558
+ // reconnect follows a less automatic workflow since this is tied to a listen key (which may need a new one).
559
+ // Kill connection first, with instruction NOT to reconnect automatically
560
+ const shouldReconnectAfterClose = false;
561
+ this.close(wsKey, shouldReconnectAfterClose);
562
+ // Then respawn a connection with a potentially new listen key (since the old one may be invalid now)
563
+ this.respawnUserDataStream(market, symbol);
564
+ return;
565
+ }
566
+ const reconnectDelaySeconds = 1000 * 15;
567
+ this.logger.info(`Userdata keep alive request failed due to error, trying again with short delay (${reconnectDelaySeconds} seconds)`, Object.assign(Object.assign({}, websocket_util_1.WS_LOGGER_CATEGORY), { listenKey, error: e, keepAliveAttempts: listenKeyState.keepAliveFailures }));
568
+ listenKeyState.keepAliveRetryTimer = setTimeout(() => this.checkKeepAliveListenKey(listenKey, market, ws, wsKey, symbol), reconnectDelaySeconds);
569
+ }
570
+ });
571
+ }
572
+ // TODO: Used in the close() fn in the legacy client. Still needed?
573
+ teardownUserDataListenKey(listenKey, ws) {
574
+ if (listenKey) {
575
+ this.listenKeyStateCache.clearAllListenKeyState(listenKey);
576
+ (0, websocket_util_1.safeTerminateWs)(ws, true);
577
+ }
578
+ }
579
+ respawnUserDataStream(market, symbol, isTestnet, respawnAttempt) {
580
+ return __awaiter(this, void 0, void 0, function* () {
581
+ // If another connection attempt is in progress for this listen key, don't initiate a retry or the risk is multiple connections on the same listen key
582
+ const forceNewConnection = false;
583
+ const isReconnecting = true;
584
+ let ws;
585
+ try {
586
+ switch (market) {
587
+ case 'spot':
588
+ ws = yield this.subscribeSpotUserDataStream(forceNewConnection, isReconnecting);
589
+ break;
590
+ case 'crossMargin':
591
+ ws = yield this.subscribeMarginUserDataStream(forceNewConnection, isReconnecting);
592
+ break;
593
+ case 'isolatedMargin':
594
+ ws = yield this.subscribeIsolatedMarginUserDataStream(symbol, forceNewConnection, isReconnecting);
595
+ break;
596
+ case 'usdm':
597
+ ws = yield this.subscribeUsdFuturesUserDataStream(isTestnet, forceNewConnection, isReconnecting);
598
+ break;
599
+ case 'usdmTestnet':
600
+ ws = yield this.subscribeUsdFuturesUserDataStream(true, forceNewConnection, isReconnecting);
601
+ break;
602
+ case 'coinm':
603
+ ws = yield this.subscribeCoinFuturesUserDataStream(isTestnet, forceNewConnection, isReconnecting);
604
+ break;
605
+ case 'coinmTestnet':
606
+ ws = yield this.subscribeCoinFuturesUserDataStream(true, forceNewConnection, isReconnecting);
607
+ break;
608
+ case 'portfoliom':
609
+ case 'spotTestnet':
610
+ case 'options':
611
+ case 'optionsTestnet':
612
+ case 'riskDataMargin': {
613
+ throw new Error('Unsupported user data stream. Use the new "WebsocketClient" to use this stream.');
614
+ }
615
+ default:
616
+ throw (0, typeGuards_1.neverGuard)(market, `Failed to respawn user data stream - unhandled market: ${market}`);
617
+ }
618
+ }
619
+ catch (e) {
620
+ this.logger.error('Exception trying to spawn user data stream', Object.assign(Object.assign({}, websocket_util_1.WS_LOGGER_CATEGORY), { market,
621
+ symbol,
622
+ isTestnet, error: e }));
623
+ this.emit('error', { wsKey: market + '_' + 'userData', error: e });
624
+ }
625
+ if (!ws) {
626
+ const delayInSeconds = 2;
627
+ this.logger.error('User key respawn failed, trying again with short delay', Object.assign(Object.assign({}, websocket_util_1.WS_LOGGER_CATEGORY), { market,
628
+ symbol,
629
+ isTestnet,
630
+ respawnAttempt,
631
+ delayInSeconds }));
632
+ // TODO: this timer should probably be tracked/singleton
633
+ setTimeout(() => this.respawnUserDataStream(market, symbol, isTestnet, respawnAttempt ? respawnAttempt + 1 : 1), 1000 * delayInSeconds);
634
+ }
635
+ });
636
+ }
637
+ /**
638
+ * --------------------------
639
+ * Universal market websocket streams (may apply to one or more API markets)
640
+ * --------------------------
641
+ **/
642
+ /**
643
+ * Subscribe to a universal market websocket stream
644
+ */
645
+ subscribeEndpoint(endpoint, market, forceNewConnection) {
646
+ const wsKey = (0, websocket_util_1.getLegacyWsStoreKeyWithContext)(market, endpoint);
647
+ return this.connectToWsUrl(this.getWsBaseUrl(market, wsKey) + `/ws/${endpoint}`, wsKey, forceNewConnection);
648
+ }
649
+ /**
650
+ * Subscribe to aggregate trades for a symbol in a market category
651
+ */
652
+ subscribeAggregateTrades(symbol, market, forceNewConnection) {
653
+ const lowerCaseSymbol = symbol.toLowerCase();
654
+ const streamName = 'aggTrade';
655
+ const wsKey = (0, websocket_util_1.getLegacyWsStoreKeyWithContext)(market, streamName, lowerCaseSymbol);
656
+ return this.connectToWsUrl(this.getWsBaseUrl(market, wsKey) + `/ws/${lowerCaseSymbol}@${streamName}`, wsKey, forceNewConnection);
657
+ }
658
+ /**
659
+ * Subscribe to trades for a symbol in a market category
660
+ * IMPORTANT: This topic for usdm and coinm is not listed in the api docs and might stop working without warning
661
+ */
662
+ subscribeTrades(symbol, market, forceNewConnection) {
663
+ const lowerCaseSymbol = symbol.toLowerCase();
664
+ const streamName = 'trade';
665
+ const wsKey = (0, websocket_util_1.getLegacyWsStoreKeyWithContext)(market, streamName, lowerCaseSymbol);
666
+ return this.connectToWsUrl(this.getWsBaseUrl(market, wsKey) + `/ws/${lowerCaseSymbol}@${streamName}`, wsKey, forceNewConnection);
667
+ }
668
+ /**
669
+ * Subscribe to coin index for a symbol in COINM Futures markets
670
+ */
671
+ subscribeCoinIndexPrice(symbol, updateSpeedMs = 3000, forceNewConnection) {
672
+ const lowerCaseSymbol = symbol.toLowerCase();
673
+ const streamName = 'indexPrice';
674
+ const speedSuffix = updateSpeedMs === 1000 ? '@1s' : '';
675
+ const market = 'coinm';
676
+ const wsKey = (0, websocket_util_1.getLegacyWsStoreKeyWithContext)(market, streamName, lowerCaseSymbol);
677
+ return this.connectToWsUrl(this.getWsBaseUrl(market, wsKey) +
678
+ `/ws/${lowerCaseSymbol}@${streamName}${speedSuffix}`, wsKey, forceNewConnection);
679
+ }
680
+ /**
681
+ * Subscribe to mark price for a symbol in a market category
682
+ */
683
+ subscribeMarkPrice(symbol, market, updateSpeedMs = 3000, forceNewConnection) {
684
+ const lowerCaseSymbol = symbol.toLowerCase();
685
+ const streamName = 'markPrice';
686
+ const speedSuffix = updateSpeedMs === 1000 ? '@1s' : '';
687
+ const wsKey = (0, websocket_util_1.getLegacyWsStoreKeyWithContext)(market, streamName, lowerCaseSymbol);
688
+ return this.connectToWsUrl(this.getWsBaseUrl(market, wsKey) +
689
+ `/ws/${lowerCaseSymbol}@${streamName}${speedSuffix}`, wsKey, forceNewConnection);
690
+ }
691
+ /**
692
+ * Subscribe to mark price for all symbols in a market category
693
+ */
694
+ subscribeAllMarketMarkPrice(market, updateSpeedMs = 3000, forceNewConnection) {
695
+ const streamName = '!markPrice@arr';
696
+ const speedSuffix = updateSpeedMs === 1000 ? '@1s' : '';
697
+ const wsKey = (0, websocket_util_1.getLegacyWsStoreKeyWithContext)(market, streamName);
698
+ return this.connectToWsUrl(this.getWsBaseUrl(market, wsKey) + `/ws/${streamName}${speedSuffix}`, wsKey, forceNewConnection);
699
+ }
700
+ /**
701
+ * Subscribe to klines(candles) for a symbol in a market category
702
+ */
703
+ subscribeKlines(symbol, interval, market, forceNewConnection) {
704
+ const lowerCaseSymbol = symbol.toLowerCase();
705
+ const streamName = 'kline';
706
+ const wsKey = (0, websocket_util_1.getLegacyWsStoreKeyWithContext)(market, streamName, lowerCaseSymbol, interval);
707
+ return this.connectToWsUrl(this.getWsBaseUrl(market, wsKey) +
708
+ `/ws/${lowerCaseSymbol}@${streamName}_${interval}`, wsKey, forceNewConnection);
709
+ }
710
+ /**
711
+ * Subscribe to continuous contract klines(candles) for a symbol futures
712
+ */
713
+ subscribeContinuousContractKlines(symbol, contractType, interval, market, forceNewConnection) {
714
+ const lowerCaseSymbol = symbol.toLowerCase();
715
+ const streamName = 'continuousKline';
716
+ const wsKey = (0, websocket_util_1.getLegacyWsStoreKeyWithContext)(market, streamName, lowerCaseSymbol, interval);
717
+ return this.connectToWsUrl(this.getWsBaseUrl(market, wsKey) +
718
+ `/ws/${lowerCaseSymbol}_${contractType}@${streamName}_${interval}`, wsKey, forceNewConnection);
719
+ }
720
+ /**
721
+ * Subscribe to index klines(candles) for a symbol in a coinm futures
722
+ */
723
+ subscribeIndexKlines(symbol, interval, forceNewConnection) {
724
+ const lowerCaseSymbol = symbol.toLowerCase();
725
+ const streamName = 'indexPriceKline';
726
+ const market = 'coinm';
727
+ const wsKey = (0, websocket_util_1.getLegacyWsStoreKeyWithContext)(market, streamName, lowerCaseSymbol, interval);
728
+ return this.connectToWsUrl(this.getWsBaseUrl(market, wsKey) +
729
+ `/ws/${lowerCaseSymbol}@${streamName}_${interval}`, wsKey, forceNewConnection);
730
+ }
731
+ /**
732
+ * Subscribe to index klines(candles) for a symbol in a coinm futures
733
+ */
734
+ subscribeMarkPriceKlines(symbol, interval, forceNewConnection) {
735
+ const lowerCaseSymbol = symbol.toLowerCase();
736
+ const streamName = 'markPrice_kline';
737
+ const market = 'coinm';
738
+ const wsKey = (0, websocket_util_1.getLegacyWsStoreKeyWithContext)(market, streamName, lowerCaseSymbol, interval);
739
+ return this.connectToWsUrl(this.getWsBaseUrl(market, wsKey) +
740
+ `/ws/${lowerCaseSymbol}@${streamName}_${interval}`, wsKey, forceNewConnection);
741
+ }
742
+ /**
743
+ * Subscribe to mini 24hr ticker for a symbol in market category.
744
+ */
745
+ subscribeSymbolMini24hrTicker(symbol, market, forceNewConnection) {
746
+ const lowerCaseSymbol = symbol.toLowerCase();
747
+ const streamName = 'miniTicker';
748
+ const wsKey = (0, websocket_util_1.getLegacyWsStoreKeyWithContext)(market, streamName, lowerCaseSymbol);
749
+ return this.connectToWsUrl(this.getWsBaseUrl(market, wsKey) + `/ws/${lowerCaseSymbol}@${streamName}`, wsKey, forceNewConnection);
750
+ }
751
+ /**
752
+ * Subscribe to mini 24hr mini ticker in market category.
753
+ */
754
+ subscribeAllMini24hrTickers(market, forceNewConnection) {
755
+ const streamName = 'miniTicker';
756
+ const wsKey = (0, websocket_util_1.getLegacyWsStoreKeyWithContext)(market, streamName);
757
+ return this.connectToWsUrl(this.getWsBaseUrl(market, wsKey) + `/ws/!${streamName}@arr`, wsKey, forceNewConnection);
758
+ }
759
+ /**
760
+ * Subscribe to 24hr ticker for a symbol in any market.
761
+ */
762
+ subscribeSymbol24hrTicker(symbol, market, forceNewConnection) {
763
+ const lowerCaseSymbol = symbol.toLowerCase();
764
+ const streamName = 'ticker';
765
+ const wsKey = (0, websocket_util_1.getLegacyWsStoreKeyWithContext)(market, streamName, lowerCaseSymbol);
766
+ return this.connectToWsUrl(this.getWsBaseUrl(market, wsKey) + `/ws/${lowerCaseSymbol}@${streamName}`, wsKey, forceNewConnection);
767
+ }
768
+ /**
769
+ * Subscribe to 24hr ticker in any market.
770
+ */
771
+ subscribeAll24hrTickers(market, forceNewConnection) {
772
+ const streamName = 'ticker';
773
+ const wsKey = (0, websocket_util_1.getLegacyWsStoreKeyWithContext)(market, streamName);
774
+ return this.connectToWsUrl(this.getWsBaseUrl(market, wsKey) + `/ws/!${streamName}@arr`, wsKey, forceNewConnection);
775
+ }
776
+ /**
777
+ * Subscribe to rolling window ticker statistics for all market symbols,
778
+ * computed over multiple windows. Note that only tickers that have
779
+ * changed will be present in the array.
780
+ *
781
+ * Notes:
782
+ * - Supported window sizes: 1h,4h,1d.
783
+ * - Supported markets: spot
784
+ */
785
+ subscribeAllRollingWindowTickers(market, windowSize, forceNewConnection) {
786
+ const streamName = 'ticker';
787
+ const wsKey = (0, websocket_util_1.getLegacyWsStoreKeyWithContext)(market, streamName, windowSize);
788
+ const wsUrl = this.getWsBaseUrl(market, wsKey) + `/ws/!${streamName}_${windowSize}@arr`;
789
+ return this.connectToWsUrl(wsUrl, wsKey, forceNewConnection);
790
+ }
791
+ /**
792
+ * Subscribe to best bid/ask for symbol in spot markets.
793
+ */
794
+ subscribeSymbolBookTicker(symbol, market, forceNewConnection) {
795
+ const lowerCaseSymbol = symbol.toLowerCase();
796
+ const streamName = 'bookTicker';
797
+ const wsKey = (0, websocket_util_1.getLegacyWsStoreKeyWithContext)(market, streamName, lowerCaseSymbol);
798
+ return this.connectToWsUrl(this.getWsBaseUrl(market, wsKey) + `/ws/${lowerCaseSymbol}@${streamName}`, wsKey, forceNewConnection);
799
+ }
800
+ /**
801
+ * Subscribe to best bid/ask for all symbols in spot markets.
802
+ */
803
+ subscribeAllBookTickers(market, forceNewConnection) {
804
+ const streamName = 'bookTicker';
805
+ const wsKey = (0, websocket_util_1.getLegacyWsStoreKeyWithContext)(market, streamName);
806
+ return this.connectToWsUrl(this.getWsBaseUrl(market, wsKey) + `/ws/!${streamName}`, wsKey, forceNewConnection);
807
+ }
808
+ /**
809
+ * Subscribe to best bid/ask for symbol in spot markets.
810
+ */
811
+ subscribeSymbolLiquidationOrders(symbol, market, forceNewConnection) {
812
+ const lowerCaseSymbol = symbol.toLowerCase();
813
+ const streamName = 'forceOrder';
814
+ const wsKey = (0, websocket_util_1.getLegacyWsStoreKeyWithContext)(market, streamName, lowerCaseSymbol);
815
+ return this.connectToWsUrl(this.getWsBaseUrl(market, wsKey) + `/ws/${lowerCaseSymbol}@${streamName}`, wsKey, forceNewConnection);
816
+ }
817
+ /**
818
+ * Subscribe to best bid/ask for all symbols in spot markets.
819
+ */
820
+ subscribeAllLiquidationOrders(market, forceNewConnection) {
821
+ const streamName = 'forceOrder@arr';
822
+ const wsKey = (0, websocket_util_1.getLegacyWsStoreKeyWithContext)(market, streamName);
823
+ return this.connectToWsUrl(this.getWsBaseUrl(market, wsKey) + `/ws/!${streamName}`, wsKey, forceNewConnection);
824
+ }
825
+ /**
826
+ * Subscribe to partial book depths (snapshots).
827
+ *
828
+ * Note:
829
+ * - spot only supports 1000ms or 100ms for updateMs
830
+ * - futures only support 100, 250 or 500ms for updateMs
831
+ *
832
+ * Use getContextFromWsKey(data.wsKey) to extract symbol from events
833
+ */
834
+ subscribePartialBookDepths(symbol, levels, updateMs, market, forceNewConnection) {
835
+ const lowerCaseSymbol = symbol.toLowerCase();
836
+ const streamName = 'depth';
837
+ const wsKey = (0, websocket_util_1.getLegacyWsStoreKeyWithContext)(market, streamName, lowerCaseSymbol);
838
+ const updateMsSuffx = typeof updateMs === 'number' ? `@${updateMs}ms` : '';
839
+ return this.connectToWsUrl(this.getWsBaseUrl(market, wsKey) +
840
+ `/ws/${lowerCaseSymbol}@${streamName}${levels}${updateMsSuffx}`, wsKey, forceNewConnection);
841
+ }
842
+ /**
843
+ * Subscribe to orderbook depth updates to locally manage an order book.
844
+ *
845
+ * Note that the updatems parameter depends on which market you're trading
846
+ *
847
+ * - Spot: https://binance-docs.github.io/apidocs/spot/en/#diff-depth-stream
848
+ * - USDM Futures: https://binance-docs.github.io/apidocs/futures/en/#diff-book-depth-streams
849
+ *
850
+ * Use getContextFromWsKey(data.wsKey) to extract symbol from events
851
+ */
852
+ subscribeDiffBookDepth(symbol, updateMs = 100, market, forceNewConnection) {
853
+ const lowerCaseSymbol = symbol.toLowerCase();
854
+ const streamName = 'diffBookDepth';
855
+ const wsKey = (0, websocket_util_1.getLegacyWsStoreKeyWithContext)(market, 'diffBookDepth', lowerCaseSymbol, String(updateMs));
856
+ const updateMsSuffx = typeof updateMs === 'number' ? `@${updateMs}ms` : '';
857
+ return this.connectToWsUrl(this.getWsBaseUrl(market, wsKey) +
858
+ `/ws/${lowerCaseSymbol}@${streamName}${updateMsSuffx}`, wsKey, forceNewConnection);
859
+ }
860
+ /**
861
+ * Subscribe to best bid/ask for all symbols in spot markets.
862
+ */
863
+ subscribeContractInfoStream(market, forceNewConnection) {
864
+ const streamName = '!contractInfo';
865
+ const wsKey = (0, websocket_util_1.getLegacyWsStoreKeyWithContext)(market, streamName);
866
+ return this.connectToWsUrl(this.getWsBaseUrl(market, wsKey) + `/ws/${streamName}`, wsKey, forceNewConnection);
867
+ }
868
+ /**
869
+ * --------------------------
870
+ * SPOT market websocket streams
871
+ * --------------------------
872
+ **/
873
+ /**
874
+ * Subscribe to aggregate trades for a symbol in spot markets.
875
+ */
876
+ subscribeSpotAggregateTrades(symbol, forceNewConnection) {
877
+ return this.subscribeAggregateTrades(symbol, 'spot', forceNewConnection);
878
+ }
879
+ /**
880
+ * Subscribe to trades for a symbol in spot markets.
881
+ */
882
+ subscribeSpotTrades(symbol, forceNewConnection) {
883
+ return this.subscribeTrades(symbol, 'spot', forceNewConnection);
884
+ }
885
+ /**
886
+ * Subscribe to candles for a symbol in spot markets.
887
+ */
888
+ subscribeSpotKline(symbol, interval, forceNewConnection) {
889
+ return this.subscribeKlines(symbol, interval, 'spot', forceNewConnection);
890
+ }
891
+ /**
892
+ * Subscribe to mini 24hr ticker for a symbol in spot markets.
893
+ */
894
+ subscribeSpotSymbolMini24hrTicker(symbol, forceNewConnection) {
895
+ return this.subscribeSymbolMini24hrTicker(symbol, 'spot', forceNewConnection);
896
+ }
897
+ /**
898
+ * Subscribe to mini 24hr mini ticker in spot markets.
899
+ */
900
+ subscribeSpotAllMini24hrTickers(forceNewConnection) {
901
+ return this.subscribeAllMini24hrTickers('spot', forceNewConnection);
902
+ }
903
+ /**
904
+ * Subscribe to 24hr ticker for a symbol in spot markets.
905
+ */
906
+ subscribeSpotSymbol24hrTicker(symbol, forceNewConnection) {
907
+ return this.subscribeSymbol24hrTicker(symbol, 'spot', forceNewConnection);
908
+ }
909
+ /**
910
+ * Subscribe to 24hr ticker in spot markets.
911
+ */
912
+ subscribeSpotAll24hrTickers(forceNewConnection) {
913
+ return this.subscribeAll24hrTickers('spot', forceNewConnection);
914
+ }
915
+ /**
916
+ * Subscribe to best bid/ask for symbol in spot markets.
917
+ */
918
+ subscribeSpotSymbolBookTicker(symbol, forceNewConnection) {
919
+ return this.subscribeSymbolBookTicker(symbol, 'spot', forceNewConnection);
920
+ }
921
+ /**
922
+ * Subscribe to best bid/ask for all symbols in spot markets.
923
+ */
924
+ subscribeSpotAllBookTickers(forceNewConnection) {
925
+ return this.subscribeAllBookTickers('spot', forceNewConnection);
926
+ }
927
+ /**
928
+ * Subscribe to top bid/ask levels for symbol in spot markets.
929
+ */
930
+ subscribeSpotPartialBookDepth(symbol, levels, updateMs = 1000, forceNewConnection) {
931
+ return this.subscribePartialBookDepths(symbol, levels, updateMs, 'spot', forceNewConnection);
932
+ }
933
+ /**
934
+ * Subscribe to spot orderbook depth updates to locally manage an order book.
935
+ */
936
+ subscribeSpotDiffBookDepth(symbol, updateMs = 1000, forceNewConnection) {
937
+ return this.subscribeDiffBookDepth(symbol, updateMs, 'spot', forceNewConnection);
938
+ }
939
+ /**
940
+ * Subscribe to a spot user data stream. Use REST client to generate and persist listen key.
941
+ * Supports spot, margin & isolated margin listen keys.
942
+ */
943
+ subscribeSpotUserDataStreamWithListenKey(listenKey, forceNewConnection, isReconnecting) {
944
+ const market = 'spot';
945
+ const wsKey = (0, websocket_util_1.getLegacyWsStoreKeyWithContext)(market, 'userData', undefined, listenKey);
946
+ if (!forceNewConnection &&
947
+ this.wsStore.isConnectionAttemptInProgress(wsKey)) {
948
+ this.logger.trace('Existing spot user data connection in progress for listen key. Avoiding duplicate');
949
+ return this.getWs(wsKey);
950
+ }
951
+ this.setWsState(wsKey, isReconnecting
952
+ ? WsStore_types_1.WsConnectionStateEnum.RECONNECTING
953
+ : WsStore_types_1.WsConnectionStateEnum.CONNECTING);
954
+ const ws = this.connectToWsUrl(this.getWsBaseUrl(market, wsKey) + `/ws/${listenKey}`, wsKey, forceNewConnection);
955
+ // Start & store timer to keep alive listen key (and handle expiration)
956
+ this.setKeepAliveListenKeyTimer(listenKey, market, ws, wsKey);
957
+ return ws;
958
+ }
959
+ /**
960
+ * Subscribe to spot user data stream - listen key is automaticallyr generated. Calling multiple times only opens one connection.
961
+ */
962
+ subscribeSpotUserDataStream(forceNewConnection, isReconnecting) {
963
+ return __awaiter(this, void 0, void 0, function* () {
964
+ try {
965
+ const { listenKey } = yield this.restClientCache
966
+ .getSpotRestClient(this.getRestClientOptions(), this.options.requestOptions)
967
+ .getSpotUserDataListenKey();
968
+ return this.subscribeSpotUserDataStreamWithListenKey(listenKey, forceNewConnection, isReconnecting);
969
+ }
970
+ catch (e) {
971
+ this.logger.error('Failed to connect to spot user data', Object.assign(Object.assign({}, websocket_util_1.WS_LOGGER_CATEGORY), { error: e }));
972
+ this.emit('error', {
973
+ wsKey: 'spot' + '_' + 'userData',
974
+ error: (e === null || e === void 0 ? void 0 : e.stack) || e,
975
+ });
976
+ }
977
+ });
978
+ }
979
+ /**
980
+ * Subscribe to margin user data stream - listen key is automatically generated.
981
+ */
982
+ subscribeMarginUserDataStream(forceNewConnection, isReconnecting) {
983
+ return __awaiter(this, void 0, void 0, function* () {
984
+ try {
985
+ const { listenKey } = yield this.restClientCache
986
+ .getSpotRestClient(this.getRestClientOptions(), this.options.requestOptions)
987
+ .getMarginUserDataListenKey();
988
+ const market = 'crossMargin';
989
+ const wsKey = (0, websocket_util_1.getLegacyWsStoreKeyWithContext)(market, 'userData', undefined, listenKey);
990
+ if (!forceNewConnection &&
991
+ this.wsStore.isConnectionAttemptInProgress(wsKey)) {
992
+ this.logger.trace('Existing margin user data connection in progress for listen key. Avoiding duplicate');
993
+ return this.getWs(wsKey);
994
+ }
995
+ this.setWsState(wsKey, isReconnecting
996
+ ? WsStore_types_1.WsConnectionStateEnum.RECONNECTING
997
+ : WsStore_types_1.WsConnectionStateEnum.CONNECTING);
998
+ const ws = this.connectToWsUrl(this.getWsBaseUrl(market, wsKey) + `/ws/${listenKey}`, wsKey, forceNewConnection);
999
+ // Start & store timer to keep alive listen key (and handle expiration)
1000
+ this.setKeepAliveListenKeyTimer(listenKey, market, ws, wsKey);
1001
+ return ws;
1002
+ }
1003
+ catch (e) {
1004
+ this.logger.error('Failed to connect to margin user data', Object.assign(Object.assign({}, websocket_util_1.WS_LOGGER_CATEGORY), { error: e }));
1005
+ this.emit('error', { wsKey: 'margin' + '_' + 'userData', error: e });
1006
+ }
1007
+ });
1008
+ }
1009
+ /**
1010
+ * Subscribe to isolated margin user data stream - listen key is automatically generated.
1011
+ */
1012
+ subscribeIsolatedMarginUserDataStream(symbol, forceNewConnection, isReconnecting) {
1013
+ return __awaiter(this, void 0, void 0, function* () {
1014
+ try {
1015
+ const lowerCaseSymbol = symbol.toLowerCase();
1016
+ const { listenKey } = yield this.restClientCache
1017
+ .getSpotRestClient(this.getRestClientOptions(), this.options.requestOptions)
1018
+ .getIsolatedMarginUserDataListenKey({
1019
+ symbol: lowerCaseSymbol,
1020
+ });
1021
+ const market = 'isolatedMargin';
1022
+ const wsKey = (0, websocket_util_1.getLegacyWsStoreKeyWithContext)(market, 'userData', lowerCaseSymbol, listenKey);
1023
+ if (!forceNewConnection &&
1024
+ this.wsStore.isConnectionAttemptInProgress(wsKey)) {
1025
+ this.logger.trace('Existing isolated margin user data connection in progress for listen key. Avoiding duplicate');
1026
+ return this.getWs(wsKey);
1027
+ }
1028
+ this.setWsState(wsKey, isReconnecting
1029
+ ? WsStore_types_1.WsConnectionStateEnum.RECONNECTING
1030
+ : WsStore_types_1.WsConnectionStateEnum.CONNECTING);
1031
+ const ws = this.connectToWsUrl(this.getWsBaseUrl(market, wsKey) + `/ws/${listenKey}`, wsKey, forceNewConnection);
1032
+ // Start & store timer to keep alive listen key (and handle expiration)
1033
+ this.setKeepAliveListenKeyTimer(listenKey, market, ws, wsKey, symbol);
1034
+ return ws;
1035
+ }
1036
+ catch (e) {
1037
+ this.logger.error('Failed to connect to isolated margin user data', Object.assign(Object.assign({}, websocket_util_1.WS_LOGGER_CATEGORY), { error: e, symbol }));
1038
+ this.emit('error', {
1039
+ wsKey: 'isolatedMargin' + '_' + 'userData',
1040
+ error: e,
1041
+ });
1042
+ }
1043
+ });
1044
+ }
1045
+ /**
1046
+ * --------------------------
1047
+ * End of SPOT market websocket streams
1048
+ * --------------------------
1049
+ **/
1050
+ /**
1051
+ * Subscribe to USD-M Futures user data stream - listen key is automatically generated.
1052
+ */
1053
+ subscribeUsdFuturesUserDataStream(isTestnet, forceNewConnection, isReconnecting) {
1054
+ return __awaiter(this, void 0, void 0, function* () {
1055
+ try {
1056
+ const restClient = this.restClientCache.getUSDMRestClient(this.getRestClientOptions(), this.options.requestOptions, isTestnet);
1057
+ const { listenKey } = yield restClient.getFuturesUserDataListenKey();
1058
+ const market = isTestnet ? 'usdmTestnet' : 'usdm';
1059
+ const wsKey = (0, websocket_util_1.getLegacyWsStoreKeyWithContext)(market, 'userData', undefined, listenKey);
1060
+ if (!forceNewConnection &&
1061
+ this.wsStore.isConnectionAttemptInProgress(wsKey)) {
1062
+ this.logger.trace('Existing usd futures user data connection in progress for listen key. Avoiding duplicate');
1063
+ return this.getWs(wsKey);
1064
+ }
1065
+ // Necessary so client knows this is a reconnect
1066
+ this.setWsState(wsKey, isReconnecting
1067
+ ? WsStore_types_1.WsConnectionStateEnum.RECONNECTING
1068
+ : WsStore_types_1.WsConnectionStateEnum.CONNECTING);
1069
+ const ws = this.connectToWsUrl(this.getWsBaseUrl(market, wsKey) + `/ws/${listenKey}`, wsKey, forceNewConnection);
1070
+ // Start & store timer to keep alive listen key (and handle expiration)
1071
+ this.setKeepAliveListenKeyTimer(listenKey, market, ws, wsKey, undefined, isTestnet);
1072
+ return ws;
1073
+ }
1074
+ catch (e) {
1075
+ this.logger.error('Failed to connect to USD Futures user data', Object.assign(Object.assign({}, websocket_util_1.WS_LOGGER_CATEGORY), { error: e }));
1076
+ this.emit('error', { wsKey: 'usdm' + '_' + 'userData', error: e });
1077
+ }
1078
+ });
1079
+ }
1080
+ /**
1081
+ * Subscribe to COIN-M Futures user data stream - listen key is automatically generated.
1082
+ */
1083
+ subscribeCoinFuturesUserDataStream(isTestnet, forceNewConnection, isReconnecting) {
1084
+ return __awaiter(this, void 0, void 0, function* () {
1085
+ try {
1086
+ const { listenKey } = yield this.restClientCache
1087
+ .getCOINMRestClient(this.getRestClientOptions(), this.options.requestOptions, isTestnet)
1088
+ .getFuturesUserDataListenKey();
1089
+ const market = isTestnet ? 'coinmTestnet' : 'coinm';
1090
+ const wsKey = (0, websocket_util_1.getLegacyWsStoreKeyWithContext)(market, 'userData', undefined, listenKey);
1091
+ if (!forceNewConnection &&
1092
+ this.wsStore.isConnectionAttemptInProgress(wsKey)) {
1093
+ this.logger.trace('Existing usd futures user data connection in progress for listen key. Avoiding duplicate');
1094
+ return this.getWs(wsKey);
1095
+ }
1096
+ // Necessary so client knows this is a reconnect
1097
+ this.setWsState(wsKey, isReconnecting
1098
+ ? WsStore_types_1.WsConnectionStateEnum.RECONNECTING
1099
+ : WsStore_types_1.WsConnectionStateEnum.CONNECTING);
1100
+ const ws = this.connectToWsUrl(this.getWsBaseUrl(market, wsKey) + `/ws/${listenKey}`, wsKey, forceNewConnection);
1101
+ // Start & store timer to keep alive listen key (and handle expiration)
1102
+ this.setKeepAliveListenKeyTimer(listenKey, market, ws, wsKey, undefined, isTestnet);
1103
+ return ws;
1104
+ }
1105
+ catch (e) {
1106
+ this.logger.error('Failed to connect to COIN Futures user data', Object.assign(Object.assign({}, websocket_util_1.WS_LOGGER_CATEGORY), { error: e }));
1107
+ this.emit('error', { wsKey: 'coinm' + '_' + 'userData', error: e });
1108
+ }
1109
+ });
1110
+ }
1111
+ }
1112
+ exports.WebsocketClientV1 = WebsocketClientV1;
1113
+ //# sourceMappingURL=websocket-client-legacy.js.map