ccxt 4.3.69 → 4.3.70

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 (45) hide show
  1. package/README.md +4 -4
  2. package/dist/ccxt.browser.min.js +5 -5
  3. package/dist/cjs/ccxt.js +3 -1
  4. package/dist/cjs/src/ascendex.js +1 -1
  5. package/dist/cjs/src/base/Exchange.js +6 -0
  6. package/dist/cjs/src/binance.js +1 -1
  7. package/dist/cjs/src/blofin.js +63 -6
  8. package/dist/cjs/src/bybit.js +1 -1
  9. package/dist/cjs/src/coinbaseinternational.js +168 -2
  10. package/dist/cjs/src/cryptocom.js +9 -1
  11. package/dist/cjs/src/hitbtc.js +1 -1
  12. package/dist/cjs/src/poloniex.js +1 -0
  13. package/dist/cjs/src/pro/blofin.js +665 -0
  14. package/dist/cjs/src/pro/coinbaseinternational.js +154 -9
  15. package/dist/cjs/src/pro/cryptocom.js +3 -1
  16. package/dist/cjs/src/pro/hitbtc.js +26 -8
  17. package/dist/cjs/src/pro/okx.js +7 -0
  18. package/dist/cjs/src/pro/poloniex.js +37 -12
  19. package/dist/cjs/src/pro/woo.js +5 -4
  20. package/js/ccxt.d.ts +4 -1
  21. package/js/ccxt.js +3 -1
  22. package/js/src/abstract/coinbaseinternational.d.ts +1 -1
  23. package/js/src/ascendex.js +1 -1
  24. package/js/src/base/Exchange.d.ts +1 -0
  25. package/js/src/base/Exchange.js +6 -0
  26. package/js/src/binance.js +1 -1
  27. package/js/src/blofin.d.ts +1 -1
  28. package/js/src/blofin.js +63 -6
  29. package/js/src/bybit.js +1 -1
  30. package/js/src/coinbaseinternational.d.ts +6 -1
  31. package/js/src/coinbaseinternational.js +168 -2
  32. package/js/src/cryptocom.js +10 -2
  33. package/js/src/hitbtc.js +1 -1
  34. package/js/src/poloniex.js +1 -0
  35. package/js/src/pro/blofin.d.ts +39 -0
  36. package/js/src/pro/blofin.js +668 -0
  37. package/js/src/pro/coinbaseinternational.d.ts +5 -1
  38. package/js/src/pro/coinbaseinternational.js +155 -10
  39. package/js/src/pro/cryptocom.js +4 -2
  40. package/js/src/pro/hitbtc.d.ts +1 -1
  41. package/js/src/pro/hitbtc.js +26 -8
  42. package/js/src/pro/okx.js +7 -0
  43. package/js/src/pro/poloniex.js +37 -12
  44. package/js/src/pro/woo.js +5 -4
  45. package/package.json +1 -1
@@ -0,0 +1,668 @@
1
+ // ----------------------------------------------------------------------------
2
+
3
+ // PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
4
+ // https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
5
+ // EDIT THE CORRESPONDENT .ts FILE INSTEAD
6
+
7
+ // ---------------------------------------------------------------------------
8
+ import blofinRest from '../blofin.js';
9
+ import { NotSupported, ArgumentsRequired, ExchangeError } from '../base/errors.js';
10
+ import { ArrayCache, ArrayCacheBySymbolById, ArrayCacheByTimestamp, ArrayCacheBySymbolBySide } from '../base/ws/Cache.js';
11
+ import { sha256 } from '../static_dependencies/noble-hashes/sha256.js';
12
+ // ---------------------------------------------------------------------------
13
+ export default class blofin extends blofinRest {
14
+ describe() {
15
+ return this.deepExtend(super.describe(), {
16
+ 'has': {
17
+ 'ws': true,
18
+ 'watchTrades': true,
19
+ 'watchTradesForSymbols': true,
20
+ 'watchOrderBook': true,
21
+ 'watchOrderBookForSymbols': true,
22
+ 'watchTicker': true,
23
+ 'watchTickers': true,
24
+ 'watchOHLCV': true,
25
+ 'watchOHLCVForSymbols': true,
26
+ 'watchOrders': true,
27
+ 'watchOrdersForSymbols': true,
28
+ 'watchPositions': true,
29
+ },
30
+ 'urls': {
31
+ 'api': {
32
+ 'ws': {
33
+ 'swap': {
34
+ 'public': 'wss://openapi.blofin.com/ws/public',
35
+ 'private': 'wss://openapi.blofin.com/ws/private',
36
+ },
37
+ },
38
+ },
39
+ },
40
+ 'options': {
41
+ 'defaultType': 'swap',
42
+ 'tradesLimit': 1000,
43
+ // orderbook channel can be one from:
44
+ // - "books": 200 depth levels will be pushed in the initial full snapshot. Incremental data will be pushed every 100 ms for the changes in the order book during that period of time.
45
+ // - "books5": 5 depth levels snapshot will be pushed every time. Snapshot data will be pushed every 100 ms when there are changes in the 5 depth levels snapshot.
46
+ 'watchOrderBook': {
47
+ 'channel': 'books',
48
+ },
49
+ 'watchOrderBookForSymbols': {
50
+ 'channel': 'books',
51
+ },
52
+ },
53
+ 'streaming': {
54
+ 'ping': this.ping,
55
+ 'keepAlive': 25000, // 30 seconds max
56
+ },
57
+ });
58
+ }
59
+ ping(client) {
60
+ return 'ping';
61
+ }
62
+ handlePong(client, message) {
63
+ //
64
+ // 'pong'
65
+ //
66
+ client.lastPong = this.milliseconds();
67
+ }
68
+ async watchTrades(symbol, since = undefined, limit = undefined, params = {}) {
69
+ /**
70
+ * @method
71
+ * @name blofin#watchTrades
72
+ * @description get the list of most recent trades for a particular symbol
73
+ * @see https://docs.blofin.com/index.html#ws-trades-channel
74
+ * @param {string} symbol unified symbol of the market to fetch trades for
75
+ * @param {int} [since] timestamp in ms of the earliest trade to fetch
76
+ * @param {int} [limit] the maximum amount of trades to fetch
77
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
78
+ * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades}
79
+ */
80
+ params['callerMethodName'] = 'watchTrades';
81
+ return await this.watchTradesForSymbols([symbol], since, limit, params);
82
+ }
83
+ async watchTradesForSymbols(symbols, since = undefined, limit = undefined, params = {}) {
84
+ /**
85
+ * @method
86
+ * @name blofin#watchTradesForSymbols
87
+ * @description get the list of most recent trades for a list of symbols
88
+ * @see https://docs.blofin.com/index.html#ws-trades-channel
89
+ * @param {string[]} symbols unified symbol of the market to fetch trades for
90
+ * @param {int} [since] timestamp in ms of the earliest trade to fetch
91
+ * @param {int} [limit] the maximum amount of trades to fetch
92
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
93
+ * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades}
94
+ */
95
+ await this.loadMarkets();
96
+ const trades = await this.watchMultipleWrapper(true, 'trades', 'watchTradesForSymbols', symbols, params);
97
+ if (this.newUpdates) {
98
+ const firstMarket = this.safeDict(trades, 0);
99
+ const firstSymbol = this.safeString(firstMarket, 'symbol');
100
+ limit = trades.getLimit(firstSymbol, limit);
101
+ }
102
+ return this.filterBySinceLimit(trades, since, limit, 'timestamp', true);
103
+ }
104
+ handleTrades(client, message) {
105
+ //
106
+ // {
107
+ // arg: {
108
+ // channel: "trades",
109
+ // instId: "DOGE-USDT",
110
+ // },
111
+ // data : [
112
+ // <same object as shown in REST example>,
113
+ // ...
114
+ // ]
115
+ // }
116
+ //
117
+ const arg = this.safeDict(message, 'arg');
118
+ const channelName = this.safeString(arg, 'channel');
119
+ const data = this.safeList(message, 'data');
120
+ if (data === undefined) {
121
+ return;
122
+ }
123
+ for (let i = 0; i < data.length; i++) {
124
+ const rawTrade = data[i];
125
+ const trade = this.parseWsTrade(rawTrade);
126
+ const symbol = trade['symbol'];
127
+ let stored = this.safeValue(this.trades, symbol);
128
+ if (stored === undefined) {
129
+ const limit = this.safeInteger(this.options, 'tradesLimit', 1000);
130
+ stored = new ArrayCache(limit);
131
+ this.trades[symbol] = stored;
132
+ }
133
+ stored.append(trade);
134
+ const messageHash = channelName + ':' + symbol;
135
+ client.resolve(stored, messageHash);
136
+ }
137
+ }
138
+ parseWsTrade(trade, market = undefined) {
139
+ return this.parseTrade(trade, market);
140
+ }
141
+ async watchOrderBook(symbol, limit = undefined, params = {}) {
142
+ /**
143
+ * @method
144
+ * @name blofin#watchOrderBook
145
+ * @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
146
+ * @see https://docs.blofin.com/index.html#ws-order-book-channel
147
+ * @param {string} symbol unified symbol of the market to fetch the order book for
148
+ * @param {int} [limit] the maximum amount of order book entries to return
149
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
150
+ * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
151
+ */
152
+ params['callerMethodName'] = 'watchOrderBook';
153
+ return await this.watchOrderBookForSymbols([symbol], limit, params);
154
+ }
155
+ async watchOrderBookForSymbols(symbols, limit = undefined, params = {}) {
156
+ /**
157
+ * @method
158
+ * @name blofin#watchOrderBookForSymbols
159
+ * @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
160
+ * @see https://docs.blofin.com/index.html#ws-order-book-channel
161
+ * @param {string[]} symbols unified array of symbols
162
+ * @param {int} [limit] the maximum amount of order book entries to return
163
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
164
+ * @param {string} [params.depth] the type of order book to subscribe to, default is 'depth/increase100', also accepts 'depth5' or 'depth20' or depth50
165
+ * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
166
+ */
167
+ await this.loadMarkets();
168
+ let callerMethodName = undefined;
169
+ [callerMethodName, params] = this.handleParamString(params, 'callerMethodName', 'watchOrderBookForSymbols');
170
+ let channelName = undefined;
171
+ [channelName, params] = this.handleOptionAndParams(params, callerMethodName, 'channel', 'books');
172
+ // due to some problem, temporarily disable other channels
173
+ if (channelName !== 'books') {
174
+ throw new NotSupported(this.id + ' ' + callerMethodName + '() at this moment ' + channelName + ' is not supported, coming soon');
175
+ }
176
+ const orderbook = await this.watchMultipleWrapper(true, channelName, callerMethodName, symbols, params);
177
+ return orderbook.limit();
178
+ }
179
+ handleOrderBook(client, message) {
180
+ //
181
+ // {
182
+ // arg: {
183
+ // channel: "books",
184
+ // instId: "DOGE-USDT",
185
+ // },
186
+ // action: "snapshot", // can be 'snapshot' or 'update'
187
+ // data: {
188
+ // asks: [ [ 0.08096, 1 ], [ 0.08097, 123 ], ... ],
189
+ // bids: [ [ 0.08095, 4 ], [ 0.08094, 237 ], ... ],
190
+ // ts: "1707491587909",
191
+ // prevSeqId: "0", // in case of 'update' there will be some value, less then seqId
192
+ // seqId: "3374250786",
193
+ // },
194
+ // }
195
+ //
196
+ const arg = this.safeDict(message, 'arg');
197
+ const channelName = this.safeString(arg, 'channel');
198
+ const data = this.safeDict(message, 'data');
199
+ const marketId = this.safeString(arg, 'instId');
200
+ const market = this.safeMarket(marketId);
201
+ const symbol = market['symbol'];
202
+ const messageHash = channelName + ':' + symbol;
203
+ if (!(symbol in this.orderbooks)) {
204
+ this.orderbooks[symbol] = this.orderBook();
205
+ }
206
+ const orderbook = this.orderbooks[symbol];
207
+ const timestamp = this.safeInteger(data, 'ts');
208
+ const action = this.safeString(message, 'action');
209
+ if (action === 'snapshot') {
210
+ const orderBookSnapshot = this.parseOrderBook(data, symbol, timestamp);
211
+ orderBookSnapshot['nonce'] = this.safeInteger(data, 'seqId');
212
+ orderbook.reset(orderBookSnapshot);
213
+ }
214
+ else {
215
+ const asks = this.safeList(data, 'asks', []);
216
+ const bids = this.safeList(data, 'bids', []);
217
+ this.handleDeltasWithKeys(orderbook['asks'], asks);
218
+ this.handleDeltasWithKeys(orderbook['bids'], bids);
219
+ orderbook['timestamp'] = timestamp;
220
+ orderbook['datetime'] = this.iso8601(timestamp);
221
+ }
222
+ this.orderbooks[symbol] = orderbook;
223
+ client.resolve(orderbook, messageHash);
224
+ }
225
+ async watchTicker(symbol, params = {}) {
226
+ /**
227
+ * @method
228
+ * @name blofin#watchTicker
229
+ * @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
230
+ * @see https://docs.blofin.com/index.html#ws-tickers-channel
231
+ * @param {string} symbol unified symbol of the market to fetch the ticker for
232
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
233
+ * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
234
+ */
235
+ params['callerMethodName'] = 'watchTicker';
236
+ const market = this.market(symbol);
237
+ symbol = market['symbol'];
238
+ const result = await this.watchTickers([symbol], params);
239
+ return result[symbol];
240
+ }
241
+ async watchTickers(symbols = undefined, params = {}) {
242
+ /**
243
+ * @method
244
+ * @name blofin#watchTickers
245
+ * @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
246
+ * @see https://docs.blofin.com/index.html#ws-tickers-channel
247
+ * @param {string[]} symbols unified symbol of the market to fetch the ticker for
248
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
249
+ * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
250
+ */
251
+ if (symbols === undefined) {
252
+ throw new NotSupported(this.id + ' watchTickers() requires a list of symbols');
253
+ }
254
+ const ticker = await this.watchMultipleWrapper(true, 'tickers', 'watchTickers', symbols, params);
255
+ if (this.newUpdates) {
256
+ const tickers = {};
257
+ tickers[ticker['symbol']] = ticker;
258
+ return tickers;
259
+ }
260
+ return this.filterByArray(this.tickers, 'symbol', symbols);
261
+ }
262
+ handleTicker(client, message) {
263
+ //
264
+ // message
265
+ //
266
+ // {
267
+ // arg: {
268
+ // channel: "tickers",
269
+ // instId: "DOGE-USDT",
270
+ // },
271
+ // data: [
272
+ // <same object as shown in REST example>
273
+ // ],
274
+ // }
275
+ //
276
+ const arg = this.safeDict(message, 'arg');
277
+ const channelName = this.safeString(arg, 'channel');
278
+ const data = this.safeList(message, 'data');
279
+ for (let i = 0; i < data.length; i++) {
280
+ const ticker = this.parseWsTicker(data[i]);
281
+ const symbol = ticker['symbol'];
282
+ const messageHash = channelName + ':' + symbol;
283
+ this.tickers[symbol] = ticker;
284
+ client.resolve(this.tickers[symbol], messageHash);
285
+ }
286
+ }
287
+ parseWsTicker(ticker, market = undefined) {
288
+ return this.parseTicker(ticker, market);
289
+ }
290
+ async watchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
291
+ /**
292
+ * @method
293
+ * @name blofin#watchOHLCV
294
+ * @description watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
295
+ * @param {string} symbol unified symbol of the market to fetch OHLCV data for
296
+ * @param {string} timeframe the length of time each candle represents
297
+ * @param {int} [since] timestamp in ms of the earliest candle to fetch
298
+ * @param {int} [limit] the maximum amount of candles to fetch
299
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
300
+ * @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
301
+ */
302
+ params['callerMethodName'] = 'watchOHLCV';
303
+ const result = await this.watchOHLCVForSymbols([[symbol, timeframe]], since, limit, params);
304
+ return result[symbol][timeframe];
305
+ }
306
+ async watchOHLCVForSymbols(symbolsAndTimeframes, since = undefined, limit = undefined, params = {}) {
307
+ /**
308
+ * @method
309
+ * @name blofin#watchOHLCVForSymbols
310
+ * @description watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
311
+ * @see https://docs.blofin.com/index.html#ws-candlesticks-channel
312
+ * @param {string[][]} symbolsAndTimeframes array of arrays containing unified symbols and timeframes to fetch OHLCV data for, example [['BTC/USDT', '1m'], ['LTC/USDT', '5m']]
313
+ * @param {int} [since] timestamp in ms of the earliest candle to fetch
314
+ * @param {int} [limit] the maximum amount of candles to fetch
315
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
316
+ * @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
317
+ */
318
+ const symbolsLength = symbolsAndTimeframes.length;
319
+ if (symbolsLength === 0 || !Array.isArray(symbolsAndTimeframes[0])) {
320
+ throw new ArgumentsRequired(this.id + " watchOHLCVForSymbols() requires a an array of symbols and timeframes, like [['BTC/USDT', '1m'], ['LTC/USDT', '5m']]");
321
+ }
322
+ await this.loadMarkets();
323
+ const [symbol, timeframe, candles] = await this.watchMultipleWrapper(true, 'candle', 'watchOHLCVForSymbols', symbolsAndTimeframes, params);
324
+ if (this.newUpdates) {
325
+ limit = candles.getLimit(symbol, limit);
326
+ }
327
+ const filtered = this.filterBySinceLimit(candles, since, limit, 0, true);
328
+ return this.createOHLCVObject(symbol, timeframe, filtered);
329
+ }
330
+ handleOHLCV(client, message) {
331
+ //
332
+ // message
333
+ //
334
+ // {
335
+ // arg: {
336
+ // channel: "candle1m",
337
+ // instId: "DOGE-USDT",
338
+ // },
339
+ // data: [
340
+ // [ same object as shown in REST example ]
341
+ // ],
342
+ // }
343
+ //
344
+ const arg = this.safeDict(message, 'arg');
345
+ const channelName = this.safeString(arg, 'channel');
346
+ const data = this.safeList(message, 'data');
347
+ const marketId = this.safeString(arg, 'instId');
348
+ const market = this.safeMarket(marketId);
349
+ const symbol = market['symbol'];
350
+ const interval = channelName.replace('candle', '');
351
+ const unifiedTimeframe = this.findTimeframe(interval);
352
+ this.ohlcvs[symbol] = this.safeDict(this.ohlcvs, symbol, {});
353
+ let stored = this.safeValue(this.ohlcvs[symbol], unifiedTimeframe);
354
+ if (stored === undefined) {
355
+ const limit = this.safeInteger(this.options, 'OHLCVLimit', 1000);
356
+ stored = new ArrayCacheByTimestamp(limit);
357
+ this.ohlcvs[symbol][unifiedTimeframe] = stored;
358
+ }
359
+ for (let i = 0; i < data.length; i++) {
360
+ const candle = data[i];
361
+ const parsed = this.parseOHLCV(candle, market);
362
+ stored.append(parsed);
363
+ }
364
+ const resolveData = [symbol, unifiedTimeframe, stored];
365
+ const messageHash = 'candle' + interval + ':' + symbol;
366
+ client.resolve(resolveData, messageHash);
367
+ }
368
+ async watchBalance(params = {}) {
369
+ /**
370
+ * @method
371
+ * @name blofin#watchBalance
372
+ * @description query for balance and get the amount of funds available for trading or funds locked in orders
373
+ * @see https://docs.blofin.com/index.html#ws-account-channel
374
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
375
+ * @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure}
376
+ */
377
+ await this.loadMarkets();
378
+ await this.authenticate();
379
+ let marketType = undefined;
380
+ [marketType, params] = this.handleMarketTypeAndParams('watchBalance', undefined, params);
381
+ if (marketType === 'spot') {
382
+ throw new NotSupported(this.id + ' watchBalance() is not supported for spot markets yet');
383
+ }
384
+ const messageHash = marketType + ':balance';
385
+ const sub = {
386
+ 'channel': 'account',
387
+ };
388
+ const request = this.getSubscriptionRequest([sub]);
389
+ const url = this.implodeHostname(this.urls['api']['ws'][marketType]['private']);
390
+ return await this.watch(url, messageHash, this.deepExtend(request, params), messageHash);
391
+ }
392
+ handleBalance(client, message) {
393
+ //
394
+ // {
395
+ // arg: {
396
+ // channel: "account",
397
+ // },
398
+ // data: <same object as shown in REST example>,
399
+ // }
400
+ //
401
+ const marketType = 'swap'; // for now
402
+ if (!(marketType in this.balance)) {
403
+ this.balance[marketType] = {};
404
+ }
405
+ this.balance[marketType] = this.parseWsBalance(message);
406
+ const messageHash = marketType + ':balance';
407
+ client.resolve(this.balance[marketType], messageHash);
408
+ }
409
+ parseWsBalance(message) {
410
+ return this.parseBalance(message);
411
+ }
412
+ async watchOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
413
+ /**
414
+ * @method
415
+ * @name alpaca#watchOrders
416
+ * @description watches information on multiple orders made by the user
417
+ * @param {string} symbol unified market symbol of the market orders were made in
418
+ * @param {int} [since] the earliest time in ms to fetch orders for
419
+ * @param {int} [limit] the maximum number of order structures to retrieve
420
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
421
+ * @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure
422
+ */
423
+ params['callerMethodName'] = 'watchOrders';
424
+ const symbolsArray = (symbol !== undefined) ? [symbol] : [];
425
+ return await this.watchOrdersForSymbols(symbolsArray, since, limit, params);
426
+ }
427
+ async watchOrdersForSymbols(symbols, since = undefined, limit = undefined, params = {}) {
428
+ /**
429
+ * @method
430
+ * @name blofin#watchOrdersForSymbols
431
+ * @description watches information on multiple orders made by the user across multiple symbols
432
+ * @see https://docs.blofin.com/index.html#ws-order-channel
433
+ * @param {string} symbol unified market symbol of the market orders were made in
434
+ * @param {int} [since] the earliest time in ms to fetch orders for
435
+ * @param {int} [limit] the maximum number of order structures to retrieve
436
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
437
+ * @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure
438
+ */
439
+ await this.authenticate();
440
+ await this.loadMarkets();
441
+ const orders = await this.watchMultipleWrapper(false, 'orders', 'watchOrdersForSymbols', symbols, params);
442
+ if (this.newUpdates) {
443
+ const first = this.safeValue(orders, 0);
444
+ const tradeSymbol = this.safeString(first, 'symbol');
445
+ limit = orders.getLimit(tradeSymbol, limit);
446
+ }
447
+ return this.filterBySinceLimit(orders, since, limit, 'timestamp', true);
448
+ }
449
+ handleOrders(client, message) {
450
+ //
451
+ // {
452
+ // action: 'update',
453
+ // arg: { channel: 'orders' },
454
+ // data: [
455
+ // <same object as shown in REST example>
456
+ // ]
457
+ // }
458
+ //
459
+ if (this.orders === undefined) {
460
+ const limit = this.safeInteger(this.options, 'ordersLimit', 1000);
461
+ this.orders = new ArrayCacheBySymbolById(limit);
462
+ }
463
+ const orders = this.orders;
464
+ const arg = this.safeDict(message, 'arg');
465
+ const channelName = this.safeString(arg, 'channel');
466
+ const data = this.safeList(message, 'data');
467
+ for (let i = 0; i < data.length; i++) {
468
+ const order = this.parseWsOrder(data[i]);
469
+ const symbol = order['symbol'];
470
+ const messageHash = channelName + ':' + symbol;
471
+ orders.append(order);
472
+ client.resolve(orders, messageHash);
473
+ client.resolve(orders, channelName);
474
+ }
475
+ }
476
+ parseWsOrder(order, market = undefined) {
477
+ return this.parseOrder(order, market);
478
+ }
479
+ async watchPositions(symbols = undefined, since = undefined, limit = undefined, params = {}) {
480
+ /**
481
+ * @method
482
+ * @name blofin#watchPositions
483
+ * @see https://docs.blofin.com/index.html#ws-positions-channel
484
+ * @description watch all open positions
485
+ * @param {string[]|undefined} symbols list of unified market symbols
486
+ * @param {object} params extra parameters specific to the exchange API endpoint
487
+ * @returns {object[]} a list of [position structure]{@link https://docs.ccxt.com/en/latest/manual.html#position-structure}
488
+ */
489
+ await this.authenticate();
490
+ await this.loadMarkets();
491
+ const newPositions = await this.watchMultipleWrapper(false, 'positions', 'watchPositions', symbols, params);
492
+ if (this.newUpdates) {
493
+ return newPositions;
494
+ }
495
+ return this.filterBySymbolsSinceLimit(this.positions, symbols, since, limit);
496
+ }
497
+ handlePositions(client, message) {
498
+ //
499
+ // {
500
+ // arg: { channel: 'positions' },
501
+ // data: [
502
+ // <same object as shown in REST example>
503
+ // ]
504
+ // }
505
+ //
506
+ if (this.positions === undefined) {
507
+ this.positions = new ArrayCacheBySymbolBySide();
508
+ }
509
+ const cache = this.positions;
510
+ const arg = this.safeDict(message, 'arg');
511
+ const channelName = this.safeString(arg, 'channel');
512
+ const data = this.safeList(message, 'data');
513
+ const newPositions = [];
514
+ for (let i = 0; i < data.length; i++) {
515
+ const position = this.parseWsPosition(data[i]);
516
+ newPositions.push(position);
517
+ cache.append(position);
518
+ const messageHash = channelName + ':' + position['symbol'];
519
+ client.resolve(position, messageHash);
520
+ }
521
+ }
522
+ parseWsPosition(position, market = undefined) {
523
+ return this.parsePosition(position, market);
524
+ }
525
+ async watchMultipleWrapper(isPublic, channelName, callerMethodName, symbolsArray = undefined, params = {}) {
526
+ // underlier method for all watch-multiple symbols
527
+ await this.loadMarkets();
528
+ [callerMethodName, params] = this.handleParamString(params, 'callerMethodName', callerMethodName);
529
+ // if OHLCV method are being called, then symbols would be symbolsAndTimeframes (multi-dimensional) array
530
+ const isOHLCV = (channelName === 'candle');
531
+ let symbols = isOHLCV ? this.getListFromObjectValues(symbolsArray, 0) : symbolsArray;
532
+ symbols = this.marketSymbols(symbols, undefined, true, true);
533
+ let firstMarket = undefined;
534
+ const firstSymbol = this.safeString(symbols, 0);
535
+ if (firstSymbol !== undefined) {
536
+ firstMarket = this.market(firstSymbol);
537
+ }
538
+ let marketType = undefined;
539
+ [marketType, params] = this.handleMarketTypeAndParams(callerMethodName, firstMarket, params);
540
+ if (marketType !== 'swap') {
541
+ throw new NotSupported(this.id + ' ' + callerMethodName + '() does not support ' + marketType + ' markets yet');
542
+ }
543
+ let rawSubscriptions = [];
544
+ const messageHashes = [];
545
+ if (symbols === undefined) {
546
+ symbols = [];
547
+ }
548
+ const symbolsLength = symbols.length;
549
+ if (symbolsLength > 0) {
550
+ for (let i = 0; i < symbols.length; i++) {
551
+ const current = symbols[i];
552
+ let market = undefined;
553
+ let channel = channelName;
554
+ if (isOHLCV) {
555
+ market = this.market(current);
556
+ const tfArray = symbolsArray[i];
557
+ const tf = tfArray[1];
558
+ const interval = this.safeString(this.timeframes, tf, tf);
559
+ channel += interval;
560
+ }
561
+ else {
562
+ market = this.market(current);
563
+ }
564
+ const topic = {
565
+ 'channel': channel,
566
+ 'instId': market['id'],
567
+ };
568
+ rawSubscriptions.push(topic);
569
+ messageHashes.push(channel + ':' + market['symbol']);
570
+ }
571
+ }
572
+ else {
573
+ rawSubscriptions.push({ 'channel': channelName });
574
+ messageHashes.push(channelName);
575
+ }
576
+ // private channel are difference, they only need plural channel name for multiple symbols
577
+ if (this.inArray(channelName, ['orders', 'positions'])) {
578
+ rawSubscriptions = [{ 'channel': channelName }];
579
+ }
580
+ const request = this.getSubscriptionRequest(rawSubscriptions);
581
+ const privateOrPublic = isPublic ? 'public' : 'private';
582
+ const url = this.implodeHostname(this.urls['api']['ws'][marketType][privateOrPublic]);
583
+ return await this.watchMultiple(url, messageHashes, this.deepExtend(request, params), messageHashes);
584
+ }
585
+ getSubscriptionRequest(args) {
586
+ return {
587
+ 'op': 'subscribe',
588
+ 'args': args,
589
+ };
590
+ }
591
+ handleMessage(client, message) {
592
+ //
593
+ // message examples
594
+ //
595
+ // {
596
+ // arg: {
597
+ // channel: "trades",
598
+ // instId: "DOGE-USDT",
599
+ // },
600
+ // event: "subscribe"
601
+ // }
602
+ //
603
+ // incoming data updates' examples can be seen under each handler method
604
+ //
605
+ const methods = {
606
+ // public
607
+ 'pong': this.handlePong,
608
+ 'trades': this.handleTrades,
609
+ 'books': this.handleOrderBook,
610
+ 'tickers': this.handleTicker,
611
+ 'candle': this.handleOHLCV,
612
+ // private
613
+ 'account': this.handleBalance,
614
+ 'orders': this.handleOrders,
615
+ 'positions': this.handlePositions,
616
+ };
617
+ let method = undefined;
618
+ if (message === 'pong') {
619
+ method = this.safeValue(methods, 'pong');
620
+ }
621
+ else {
622
+ const event = this.safeString(message, 'event');
623
+ if (event === 'subscribe') {
624
+ return;
625
+ }
626
+ else if (event === 'login') {
627
+ client.resolve(message, 'authenticate_hash');
628
+ return;
629
+ }
630
+ else if (event === 'error') {
631
+ throw new ExchangeError(this.id + ' error: ' + this.json(message));
632
+ }
633
+ const arg = this.safeDict(message, 'arg');
634
+ const channelName = this.safeString(arg, 'channel');
635
+ method = this.safeValue(methods, channelName);
636
+ if (!method && channelName.indexOf('candle') >= 0) {
637
+ method = methods['candle'];
638
+ }
639
+ }
640
+ if (method) {
641
+ method.call(this, client, message);
642
+ }
643
+ }
644
+ async authenticate(params = {}) {
645
+ this.checkRequiredCredentials();
646
+ const milliseconds = this.milliseconds();
647
+ const messageHash = 'authenticate_hash';
648
+ const timestamp = milliseconds.toString();
649
+ const nonce = 'n_' + timestamp;
650
+ const auth = '/users/self/verify' + 'GET' + timestamp + '' + nonce;
651
+ const signature = this.stringToBase64(this.hmac(this.encode(auth), this.encode(this.secret), sha256));
652
+ const request = {
653
+ 'op': 'login',
654
+ 'args': [
655
+ {
656
+ 'apiKey': this.apiKey,
657
+ 'passphrase': this.password,
658
+ 'timestamp': timestamp,
659
+ 'nonce': nonce,
660
+ 'sign': signature,
661
+ },
662
+ ],
663
+ };
664
+ const marketType = 'swap'; // for now
665
+ const url = this.implodeHostname(this.urls['api']['ws'][marketType]['private']);
666
+ await this.watch(url, messageHash, this.deepExtend(request, params), messageHash);
667
+ }
668
+ }