ccxt 4.4.21 → 4.4.23

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 (86) hide show
  1. package/README.md +112 -111
  2. package/dist/ccxt.browser.min.js +2 -2
  3. package/dist/cjs/ccxt.js +6 -1
  4. package/dist/cjs/src/abstract/coincatch.js +9 -0
  5. package/dist/cjs/src/alpaca.js +1 -0
  6. package/dist/cjs/src/base/Exchange.js +21 -0
  7. package/dist/cjs/src/bigone.js +3 -0
  8. package/dist/cjs/src/binance.js +172 -44
  9. package/dist/cjs/src/bitfinex.js +4 -0
  10. package/dist/cjs/src/bitflyer.js +58 -0
  11. package/dist/cjs/src/bitget.js +77 -0
  12. package/dist/cjs/src/bitrue.js +3 -0
  13. package/dist/cjs/src/bybit.js +80 -2
  14. package/dist/cjs/src/cex.js +1307 -1381
  15. package/dist/cjs/src/coinbase.js +1 -1
  16. package/dist/cjs/src/coinbaseexchange.js +3 -0
  17. package/dist/cjs/src/coincatch.js +5370 -0
  18. package/dist/cjs/src/coinex.js +63 -1
  19. package/dist/cjs/src/cryptocom.js +1 -1
  20. package/dist/cjs/src/gate.js +103 -3
  21. package/dist/cjs/src/htx.js +1 -7
  22. package/dist/cjs/src/hyperliquid.js +10 -8
  23. package/dist/cjs/src/kucoin.js +27 -59
  24. package/dist/cjs/src/latoken.js +6 -0
  25. package/dist/cjs/src/mexc.js +1 -1
  26. package/dist/cjs/src/oceanex.js +2 -0
  27. package/dist/cjs/src/okcoin.js +1 -0
  28. package/dist/cjs/src/okx.js +74 -0
  29. package/dist/cjs/src/poloniex.js +5 -0
  30. package/dist/cjs/src/pro/coincatch.js +1554 -0
  31. package/js/ccxt.d.ts +9 -3
  32. package/js/ccxt.js +6 -2
  33. package/js/src/abstract/binance.d.ts +21 -0
  34. package/js/src/abstract/binancecoinm.d.ts +21 -0
  35. package/js/src/abstract/binanceus.d.ts +21 -0
  36. package/js/src/abstract/binanceusdm.d.ts +21 -0
  37. package/js/src/abstract/bitflyer.d.ts +1 -0
  38. package/js/src/abstract/bitget.d.ts +3 -0
  39. package/js/src/abstract/cex.d.ts +28 -29
  40. package/js/src/abstract/coincatch.d.ts +97 -0
  41. package/js/src/abstract/coincatch.js +11 -0
  42. package/js/src/abstract/gate.d.ts +5 -0
  43. package/js/src/abstract/gateio.d.ts +5 -0
  44. package/js/src/abstract/kucoin.d.ts +1 -0
  45. package/js/src/abstract/kucoinfutures.d.ts +1 -0
  46. package/js/src/abstract/okx.d.ts +1 -0
  47. package/js/src/alpaca.js +1 -0
  48. package/js/src/base/Exchange.d.ts +8 -2
  49. package/js/src/base/Exchange.js +21 -0
  50. package/js/src/base/types.d.ts +8 -0
  51. package/js/src/bigone.js +3 -0
  52. package/js/src/binance.d.ts +3 -1
  53. package/js/src/binance.js +172 -44
  54. package/js/src/bitfinex.js +4 -0
  55. package/js/src/bitflyer.d.ts +3 -1
  56. package/js/src/bitflyer.js +58 -0
  57. package/js/src/bitget.d.ts +3 -1
  58. package/js/src/bitget.js +77 -0
  59. package/js/src/bitrue.js +3 -0
  60. package/js/src/bybit.d.ts +3 -1
  61. package/js/src/bybit.js +80 -2
  62. package/js/src/cex.d.ts +34 -20
  63. package/js/src/cex.js +1308 -1382
  64. package/js/src/coinbase.js +1 -1
  65. package/js/src/coinbaseexchange.js +3 -0
  66. package/js/src/coincatch.d.ts +130 -0
  67. package/js/src/coincatch.js +5371 -0
  68. package/js/src/coinex.d.ts +1 -0
  69. package/js/src/coinex.js +63 -1
  70. package/js/src/cryptocom.js +1 -1
  71. package/js/src/gate.d.ts +2 -0
  72. package/js/src/gate.js +103 -3
  73. package/js/src/htx.js +1 -7
  74. package/js/src/hyperliquid.js +10 -8
  75. package/js/src/kucoin.d.ts +0 -1
  76. package/js/src/kucoin.js +27 -59
  77. package/js/src/latoken.js +6 -0
  78. package/js/src/mexc.js +1 -1
  79. package/js/src/oceanex.js +2 -0
  80. package/js/src/okcoin.js +1 -0
  81. package/js/src/okx.d.ts +3 -1
  82. package/js/src/okx.js +74 -0
  83. package/js/src/poloniex.js +5 -0
  84. package/js/src/pro/coincatch.d.ts +57 -0
  85. package/js/src/pro/coincatch.js +1555 -0
  86. package/package.json +1 -1
@@ -0,0 +1,1555 @@
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 coincatchRest from '../coincatch.js';
9
+ import { ArgumentsRequired, AuthenticationError, BadRequest, ChecksumError, ExchangeError, NotSupported, RateLimitExceeded, UnsubscribeError } from '../base/errors.js';
10
+ import { Precise } from '../base/Precise.js';
11
+ import { sha256 } from '../static_dependencies/noble-hashes/sha256.js';
12
+ import { ArrayCache, ArrayCacheBySymbolById, ArrayCacheBySymbolBySide, ArrayCacheByTimestamp } from '../base/ws/Cache.js';
13
+ // ---------------------------------------------------------------------------
14
+ export default class coincatch extends coincatchRest {
15
+ describe() {
16
+ return this.deepExtend(super.describe(), {
17
+ 'has': {
18
+ 'ws': true,
19
+ 'watchTrades': true,
20
+ 'watchTradesForSymbols': true,
21
+ 'watchOrderBook': true,
22
+ 'watchOrderBookForSymbols': true,
23
+ 'watchOHLCV': true,
24
+ 'watchOHLCVForSymbols': false,
25
+ 'watchOrders': true,
26
+ 'watchMyTrades': false,
27
+ 'watchTicker': true,
28
+ 'watchTickers': true,
29
+ 'watchBalance': true,
30
+ 'watchPositions': true,
31
+ },
32
+ 'urls': {
33
+ 'api': {
34
+ 'ws': {
35
+ 'public': 'wss://ws.coincatch.com/public/v1/stream',
36
+ 'private': 'wss://ws.coincatch.com/private/v1/stream',
37
+ },
38
+ },
39
+ },
40
+ 'options': {
41
+ 'tradesLimit': 1000,
42
+ 'OHLCVLimit': 200,
43
+ 'timeframesForWs': {
44
+ '1m': '1m',
45
+ '5m': '5m',
46
+ '15m': '15m',
47
+ '30m': '30m',
48
+ '1h': '1H',
49
+ '4h': '4H',
50
+ '12h': '12H',
51
+ '1d': '1D',
52
+ '1w': '1W',
53
+ },
54
+ 'watchOrderBook': {
55
+ 'checksum': true,
56
+ },
57
+ },
58
+ 'streaming': {
59
+ 'ping': this.ping,
60
+ },
61
+ 'exceptions': {
62
+ 'ws': {
63
+ 'exact': {
64
+ '30001': BadRequest,
65
+ '30002': AuthenticationError,
66
+ '30003': BadRequest,
67
+ '30004': AuthenticationError,
68
+ '30005': AuthenticationError,
69
+ '30006': RateLimitExceeded,
70
+ '30007': RateLimitExceeded,
71
+ '30011': AuthenticationError,
72
+ '30012': AuthenticationError,
73
+ '30013': AuthenticationError,
74
+ '30014': BadRequest,
75
+ '30015': AuthenticationError, // { event: 'error', code: 30015, msg: 'Invalid sign' }
76
+ },
77
+ 'broad': {},
78
+ },
79
+ },
80
+ });
81
+ }
82
+ getMarketFromArg(entry) {
83
+ const instId = this.safeString(entry, 'instId');
84
+ const instType = this.safeString(entry, 'instType');
85
+ const baseAndQuote = this.parseSpotMarketId(instId);
86
+ const baseId = baseAndQuote['baseId'];
87
+ const quoteId = baseAndQuote['quoteId'];
88
+ let suffix = '_SPBL'; // spot suffix
89
+ if (instType === 'mc') {
90
+ if (quoteId === 'USD') {
91
+ suffix = '_DMCBL';
92
+ }
93
+ else {
94
+ suffix = '_UMCBL';
95
+ }
96
+ }
97
+ const marketId = this.safeCurrencyCode(baseId) + this.safeCurrencyCode(quoteId) + suffix;
98
+ return this.safeMarketCustom(marketId);
99
+ }
100
+ async authenticate(params = {}) {
101
+ this.checkRequiredCredentials();
102
+ const url = this.urls['api']['ws']['private'];
103
+ const client = this.client(url);
104
+ const messageHash = 'authenticated';
105
+ const future = client.future(messageHash);
106
+ const authenticated = this.safeValue(client.subscriptions, messageHash);
107
+ if (authenticated === undefined) {
108
+ const timestamp = this.seconds().toString();
109
+ const auth = timestamp + 'GET' + '/user/verify';
110
+ const signature = this.hmac(this.encode(auth), this.encode(this.secret), sha256, 'base64');
111
+ const operation = 'login';
112
+ const request = {
113
+ 'op': operation,
114
+ 'args': [
115
+ {
116
+ 'apiKey': this.apiKey,
117
+ 'passphrase': this.password,
118
+ 'timestamp': timestamp,
119
+ 'sign': signature,
120
+ },
121
+ ],
122
+ };
123
+ const message = this.extend(request, params);
124
+ this.watch(url, messageHash, message, messageHash);
125
+ }
126
+ return await future;
127
+ }
128
+ async watchPublic(messageHash, subscribeHash, args, params = {}) {
129
+ const url = this.urls['api']['ws']['public'];
130
+ const request = {
131
+ 'op': 'subscribe',
132
+ 'args': [args],
133
+ };
134
+ const message = this.extend(request, params);
135
+ return await this.watch(url, messageHash, message, subscribeHash);
136
+ }
137
+ async unWatchPublic(messageHash, args, params = {}) {
138
+ const url = this.urls['api']['ws']['public'];
139
+ const request = {
140
+ 'op': 'unsubscribe',
141
+ 'args': [args],
142
+ };
143
+ const message = this.extend(request, params);
144
+ return await this.watch(url, messageHash, message, messageHash);
145
+ }
146
+ async watchPrivate(messageHash, subscribeHash, args, params = {}) {
147
+ await this.authenticate();
148
+ const url = this.urls['api']['ws']['private'];
149
+ const request = {
150
+ 'op': 'subscribe',
151
+ 'args': [args],
152
+ };
153
+ const message = this.extend(request, params);
154
+ return await this.watch(url, messageHash, message, subscribeHash);
155
+ }
156
+ async watchPrivateMultiple(messageHashes, subscribeHashes, args, params = {}) {
157
+ await this.authenticate();
158
+ const url = this.urls['api']['ws']['private'];
159
+ const request = {
160
+ 'op': 'subscribe',
161
+ 'args': args,
162
+ };
163
+ const message = this.extend(request, params);
164
+ return await this.watchMultiple(url, messageHashes, message, subscribeHashes);
165
+ }
166
+ handleAuthenticate(client, message) {
167
+ //
168
+ // { event: "login", code: 0 }
169
+ //
170
+ const messageHash = 'authenticated';
171
+ const future = this.safeValue(client.futures, messageHash);
172
+ future.resolve(true);
173
+ }
174
+ async watchPublicMultiple(messageHashes, subscribeHashes, argsArray, params = {}) {
175
+ const url = this.urls['api']['ws']['public'];
176
+ const request = {
177
+ 'op': 'subscribe',
178
+ 'args': argsArray,
179
+ };
180
+ const message = this.extend(request, params);
181
+ return await this.watchMultiple(url, messageHashes, message, subscribeHashes);
182
+ }
183
+ async unWatchChannel(symbol, channel, messageHashTopic, params = {}) {
184
+ await this.loadMarkets();
185
+ const market = this.market(symbol);
186
+ const [instType, instId] = this.getPublicInstTypeAndId(market);
187
+ const messageHash = 'unsubscribe:' + messageHashTopic + ':' + symbol;
188
+ const args = {
189
+ 'instType': instType,
190
+ 'channel': channel,
191
+ 'instId': instId,
192
+ };
193
+ return await this.unWatchPublic(messageHash, args, params);
194
+ }
195
+ getPublicInstTypeAndId(market) {
196
+ const instId = market['baseId'] + market['quoteId'];
197
+ let instType = undefined;
198
+ if (market['spot']) {
199
+ instType = 'SP';
200
+ }
201
+ else if (market['swap']) {
202
+ instType = 'MC';
203
+ }
204
+ else {
205
+ throw new NotSupported(this.id + ' supports only spot and swap markets');
206
+ }
207
+ return [instType, instId];
208
+ }
209
+ handleDMCBLMarketByMessageHashes(market, hash, client, timeframe = undefined) {
210
+ const marketId = market['id'];
211
+ const messageHashes = this.findMessageHashes(client, hash);
212
+ // the exchange counts DMCBL markets as the same market with different quote currencies
213
+ // for example symbols ETHUSD:ETH and ETH/USD:BTC both have the same marketId ETHUSD_DMCBL
214
+ // we need to check all markets with the same marketId to find the correct market that is in messageHashes
215
+ const marketsWithCurrentId = this.safeList(this.markets_by_id, marketId, []);
216
+ let suffix = '';
217
+ if (timeframe !== undefined) {
218
+ suffix = ':' + timeframe;
219
+ }
220
+ for (let i = 0; i < marketsWithCurrentId.length; i++) {
221
+ market = marketsWithCurrentId[i];
222
+ const symbol = market['symbol'];
223
+ const messageHash = hash + symbol + suffix;
224
+ if (this.inArray(messageHash, messageHashes)) {
225
+ return market;
226
+ }
227
+ }
228
+ return market;
229
+ }
230
+ async watchTicker(symbol, params = {}) {
231
+ /**
232
+ * @method
233
+ * @name coincatch#watchTicker
234
+ * @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
235
+ * @see https://coincatch.github.io/github.io/en/spot/#tickers-channel
236
+ * @param {string} symbol unified symbol of the market to fetch the ticker for
237
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
238
+ * @param {string} [params.instType] the type of the instrument to fetch the ticker for, 'SP' for spot markets, 'MC' for futures markets (default is 'SP')
239
+ * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
240
+ */
241
+ await this.loadMarkets();
242
+ const market = this.market(symbol);
243
+ const [instType, instId] = this.getPublicInstTypeAndId(market);
244
+ const channel = 'ticker';
245
+ const messageHash = channel + ':' + symbol;
246
+ const args = {
247
+ 'instType': instType,
248
+ 'channel': channel,
249
+ 'instId': instId,
250
+ };
251
+ return await this.watchPublic(messageHash, messageHash, args, params);
252
+ }
253
+ async unWatchTicker(symbol, params = {}) {
254
+ /**
255
+ * @method
256
+ * @name coinctach#unWatchTicker
257
+ * @description unsubscribe from the ticker channel
258
+ * @see https://coincatch.github.io/github.io/en/mix/#tickers-channel
259
+ * @param {string} symbol unified symbol of the market to unwatch the ticker for
260
+ * @returns {any} status of the unwatch request
261
+ */
262
+ await this.loadMarkets();
263
+ return await this.unWatchChannel(symbol, 'ticker', 'ticker', params);
264
+ }
265
+ async watchTickers(symbols = undefined, params = {}) {
266
+ /**
267
+ * @method
268
+ * @name coincatch#watchTickers
269
+ * @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
270
+ * @see https://coincatch.github.io/github.io/en/mix/#tickers-channel
271
+ * @param {string[]} symbols unified symbol of the market to watch the tickers for
272
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
273
+ * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
274
+ */
275
+ await this.loadMarkets();
276
+ if (symbols === undefined) {
277
+ symbols = this.symbols;
278
+ }
279
+ const topics = [];
280
+ const messageHashes = [];
281
+ for (let i = 0; i < symbols.length; i++) {
282
+ const symbol = symbols[i];
283
+ const market = this.market(symbol);
284
+ const [instType, instId] = this.getPublicInstTypeAndId(market);
285
+ const args = {
286
+ 'instType': instType,
287
+ 'channel': 'ticker',
288
+ 'instId': instId,
289
+ };
290
+ topics.push(args);
291
+ messageHashes.push('ticker:' + symbol);
292
+ }
293
+ const tickers = await this.watchPublicMultiple(messageHashes, messageHashes, topics, params);
294
+ if (this.newUpdates) {
295
+ const result = {};
296
+ result[tickers['symbol']] = tickers;
297
+ return result;
298
+ }
299
+ return this.filterByArray(this.tickers, 'symbol', symbols);
300
+ }
301
+ handleTicker(client, message) {
302
+ //
303
+ // action: 'snapshot',
304
+ // arg: { instType: 'sp', channel: 'ticker', instId: 'ETHUSDT' },
305
+ // data: [
306
+ // {
307
+ // instId: 'ETHUSDT',
308
+ // last: '2421.06',
309
+ // open24h: '2416.93',
310
+ // high24h: '2441.47',
311
+ // low24h: '2352.99',
312
+ // bestBid: '2421.03',
313
+ // bestAsk: '2421.06',
314
+ // baseVolume: '9445.2043',
315
+ // quoteVolume: '22807159.1148',
316
+ // ts: 1728131730687,
317
+ // labeId: 0,
318
+ // openUtc: '2414.50',
319
+ // chgUTC: '0.00272',
320
+ // bidSz: '3.866',
321
+ // askSz: '0.124'
322
+ // }
323
+ // ],
324
+ // ts: 1728131730688
325
+ //
326
+ const arg = this.safeDict(message, 'arg', {});
327
+ let market = this.getMarketFromArg(arg);
328
+ const marketId = market['id'];
329
+ const hash = 'ticker:';
330
+ if (marketId.indexOf('_DMCBL') >= 0) {
331
+ market = this.handleDMCBLMarketByMessageHashes(market, hash, client);
332
+ }
333
+ const data = this.safeList(message, 'data', []);
334
+ const ticker = this.parseWsTicker(this.safeDict(data, 0, {}), market);
335
+ const symbol = market['symbol'];
336
+ this.tickers[symbol] = ticker;
337
+ const messageHash = hash + symbol;
338
+ client.resolve(this.tickers[symbol], messageHash);
339
+ }
340
+ parseWsTicker(ticker, market = undefined) {
341
+ //
342
+ // spot
343
+ // {
344
+ // instId: 'ETHUSDT',
345
+ // last: '2421.06',
346
+ // open24h: '2416.93',
347
+ // high24h: '2441.47',
348
+ // low24h: '2352.99',
349
+ // bestBid: '2421.03',
350
+ // bestAsk: '2421.06',
351
+ // baseVolume: '9445.2043',
352
+ // quoteVolume: '22807159.1148',
353
+ // ts: 1728131730687,
354
+ // labeId: 0,
355
+ // openUtc: '2414.50',
356
+ // chgUTC: '0.00272',
357
+ // bidSz: '3.866',
358
+ // askSz: '0.124'
359
+ // }
360
+ //
361
+ // swap
362
+ // {
363
+ // instId: 'ETHUSDT',
364
+ // last: '2434.47',
365
+ // bestAsk: '2434.48',
366
+ // bestBid: '2434.47',
367
+ // high24h: '2471.68',
368
+ // low24h: '2400.01',
369
+ // priceChangePercent: '0.00674',
370
+ // capitalRate: '0.000082',
371
+ // nextSettleTime: 1728489600000,
372
+ // systemTime: 1728471993602,
373
+ // markPrice: '2434.46',
374
+ // indexPrice: '2435.44',
375
+ // holding: '171450.25',
376
+ // baseVolume: '1699298.91',
377
+ // quoteVolume: '4144522832.32',
378
+ // openUtc: '2439.67',
379
+ // chgUTC: '-0.00213',
380
+ // symbolType: 1,
381
+ // symbolId: 'ETHUSDT_UMCBL',
382
+ // deliveryPrice: '0',
383
+ // bidSz: '26.12',
384
+ // askSz: '49.6'
385
+ // }
386
+ //
387
+ const last = this.safeString(ticker, 'last');
388
+ const timestamp = this.safeInteger2(ticker, 'ts', 'systemTime');
389
+ return this.safeTicker({
390
+ 'symbol': market['symbol'],
391
+ 'timestamp': timestamp,
392
+ 'datetime': this.iso8601(timestamp),
393
+ 'high': this.safeString(ticker, 'high24h'),
394
+ 'low': this.safeString(ticker, 'low24h'),
395
+ 'bid': this.safeString(ticker, 'bestBid'),
396
+ 'bidVolume': this.safeString(ticker, 'bidSz'),
397
+ 'ask': this.safeString(ticker, 'bestAsk'),
398
+ 'askVolume': this.safeString(ticker, 'askSz'),
399
+ 'vwap': undefined,
400
+ 'open': this.safeString2(ticker, 'open24h', 'openUtc'),
401
+ 'close': last,
402
+ 'last': last,
403
+ 'previousClose': undefined,
404
+ 'change': undefined,
405
+ 'percentage': Precise.stringMul(this.safeString(ticker, 'chgUTC'), '100'),
406
+ 'average': undefined,
407
+ 'baseVolume': this.safeNumber(ticker, 'baseVolume'),
408
+ 'quoteVolume': this.safeNumber(ticker, 'quoteVolume'),
409
+ 'indexPrice': this.safeString(ticker, 'indexPrice'),
410
+ 'markPrice': this.safeString(ticker, 'markPrice'),
411
+ 'info': ticker,
412
+ }, market);
413
+ }
414
+ async watchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
415
+ /**
416
+ * @method
417
+ * @name coincatch#watchOHLCV
418
+ * @description watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
419
+ * @see https://coincatch.github.io/github.io/en/spot/#candlesticks-channel
420
+ * @param {string} symbol unified symbol of the market to fetch OHLCV data for
421
+ * @param {string} timeframe the length of time each candle represents
422
+ * @param {int} [since] timestamp in ms of the earliest candle to fetch (not including)
423
+ * @param {int} [limit] the maximum amount of candles to fetch (not including)
424
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
425
+ * @param {bool} [params.instType] the type of the instrument to fetch the OHLCV data for, 'SP' for spot markets, 'MC' for futures markets (default is 'SP')
426
+ * @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
427
+ */
428
+ await this.loadMarkets();
429
+ const market = this.market(symbol);
430
+ const timeframes = this.options['timeframesForWs'];
431
+ const channel = 'candle' + this.safeString(timeframes, timeframe);
432
+ const [instType, instId] = this.getPublicInstTypeAndId(market);
433
+ const args = {
434
+ 'instType': instType,
435
+ 'channel': channel,
436
+ 'instId': instId,
437
+ };
438
+ const messageHash = 'ohlcv:' + symbol + ':' + timeframe;
439
+ const ohlcv = await this.watchPublic(messageHash, messageHash, args, params);
440
+ if (this.newUpdates) {
441
+ limit = ohlcv.getLimit(symbol, limit);
442
+ }
443
+ return this.filterBySinceLimit(ohlcv, since, limit, 0, true);
444
+ }
445
+ async unWatchOHLCV(symbol, timeframe = '1m', params = {}) {
446
+ /**
447
+ * @method
448
+ * @name coincatch#unWatchOHLCV
449
+ * @description unsubscribe from the ohlcv channel
450
+ * @see https://www.bitget.com/api-doc/spot/websocket/public/Candlesticks-Channel
451
+ * @param {string} symbol unified symbol of the market to unwatch the ohlcv for
452
+ * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
453
+ */
454
+ await this.loadMarkets();
455
+ const timeframes = this.options['timeframesForWs'];
456
+ const interval = this.safeString(timeframes, timeframe);
457
+ const channel = 'candle' + interval;
458
+ return await this.unWatchChannel(symbol, channel, 'ohlcv:' + interval, params);
459
+ }
460
+ handleOHLCV(client, message) {
461
+ //
462
+ // {
463
+ // action: 'update',
464
+ // arg: { instType: 'sp', channel: 'candle1D', instId: 'ETHUSDT' },
465
+ // data: [
466
+ // [
467
+ // '1728316800000',
468
+ // '2474.5',
469
+ // '2478.21',
470
+ // '2459.8',
471
+ // '2463.51',
472
+ // '86.0551'
473
+ // ]
474
+ // ],
475
+ // ts: 1728317607657
476
+ // }
477
+ //
478
+ const arg = this.safeDict(message, 'arg', {});
479
+ let market = this.getMarketFromArg(arg);
480
+ const marketId = market['id'];
481
+ const hash = 'ohlcv:';
482
+ const data = this.safeList(message, 'data', []);
483
+ const channel = this.safeString(arg, 'channel');
484
+ const klineType = channel.slice(6);
485
+ const timeframe = this.findTimeframe(klineType);
486
+ if (marketId.indexOf('_DMCBL') >= 0) {
487
+ market = this.handleDMCBLMarketByMessageHashes(market, hash, client, timeframe);
488
+ }
489
+ const symbol = market['symbol'];
490
+ if (!(symbol in this.ohlcvs)) {
491
+ this.ohlcvs[symbol] = {};
492
+ }
493
+ if (!(timeframe in this.ohlcvs[symbol])) {
494
+ const limit = this.safeInteger(this.options, 'OHLCVLimit', 1000);
495
+ this.ohlcvs[symbol][timeframe] = new ArrayCacheByTimestamp(limit);
496
+ }
497
+ const stored = this.ohlcvs[symbol][timeframe];
498
+ for (let i = 0; i < data.length; i++) {
499
+ const candle = this.safeList(data, i, []);
500
+ const parsed = this.parseWsOHLCV(candle, market);
501
+ stored.append(parsed);
502
+ }
503
+ const messageHash = hash + symbol + ':' + timeframe;
504
+ client.resolve(stored, messageHash);
505
+ }
506
+ parseWsOHLCV(ohlcv, market = undefined) {
507
+ //
508
+ // [
509
+ // '1728316800000',
510
+ // '2474.5',
511
+ // '2478.21',
512
+ // '2459.8',
513
+ // '2463.51',
514
+ // '86.0551'
515
+ // ]
516
+ //
517
+ return [
518
+ this.safeInteger(ohlcv, 0),
519
+ this.safeNumber(ohlcv, 1),
520
+ this.safeNumber(ohlcv, 2),
521
+ this.safeNumber(ohlcv, 3),
522
+ this.safeNumber(ohlcv, 4),
523
+ this.safeNumber(ohlcv, 5),
524
+ ];
525
+ }
526
+ async watchOrderBook(symbol, limit = undefined, params = {}) {
527
+ /**
528
+ * @method
529
+ * @name coincatch#watchOrderBook
530
+ * @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
531
+ * @see https://coincatch.github.io/github.io/en/spot/#depth-channel
532
+ * @param {string} symbol unified symbol of the market to fetch the order book for
533
+ * @param {int} [limit] the maximum amount of order book entries to return
534
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
535
+ * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
536
+ */
537
+ return await this.watchOrderBookForSymbols([symbol], limit, params);
538
+ }
539
+ async unWatchOrderBook(symbol, params = {}) {
540
+ /**
541
+ * @method
542
+ * @name coincatch#unWatchOrderBook
543
+ * @description unsubscribe from the orderbook channel
544
+ * @see https://coincatch.github.io/github.io/en/spot/#depth-channel
545
+ * @param {string} symbol unified symbol of the market to fetch the order book for
546
+ * @param {int} [params.limit] orderbook limit, default is undefined
547
+ * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
548
+ */
549
+ await this.loadMarkets();
550
+ let channel = 'books';
551
+ const limit = this.safeInteger(params, 'limit');
552
+ if ((limit === 5) || (limit === 15)) {
553
+ params = this.omit(params, 'limit');
554
+ channel += limit.toString();
555
+ }
556
+ return await this.unWatchChannel(symbol, channel, channel, params);
557
+ }
558
+ async watchOrderBookForSymbols(symbols, limit = undefined, params = {}) {
559
+ /**
560
+ * @method
561
+ * @name coincatch#watchOrderBook
562
+ * @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
563
+ * @see https://coincatch.github.io/github.io/en/spot/#depth-channel
564
+ * @param {string} symbol unified symbol of the market to fetch the order book for
565
+ * @param {int} [limit] the maximum amount of order book entries to return
566
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
567
+ * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
568
+ */
569
+ await this.loadMarkets();
570
+ symbols = this.marketSymbols(symbols);
571
+ const channel = 'books';
572
+ const topics = [];
573
+ const messageHashes = [];
574
+ for (let i = 0; i < symbols.length; i++) {
575
+ const symbol = symbols[i];
576
+ const market = this.market(symbol);
577
+ const [instType, instId] = this.getPublicInstTypeAndId(market);
578
+ const args = {
579
+ 'instType': instType,
580
+ 'channel': channel,
581
+ 'instId': instId,
582
+ };
583
+ topics.push(args);
584
+ messageHashes.push(channel + ':' + symbol);
585
+ }
586
+ const orderbook = await this.watchPublicMultiple(messageHashes, messageHashes, topics, params);
587
+ return orderbook.limit();
588
+ }
589
+ handleOrderBook(client, message) {
590
+ //
591
+ // {
592
+ // action: 'update',
593
+ // arg: { instType: 'sp', channel: 'books', instId: 'ETHUSDT' },
594
+ // data: [
595
+ // {
596
+ // asks: [ [ 2507.07, 0.4248 ] ],
597
+ // bids: [ [ 2507.05, 0.1198 ] ],
598
+ // checksum: -1400923312,
599
+ // ts: '1728339446908'
600
+ // }
601
+ // ],
602
+ // ts: 1728339446908
603
+ // }
604
+ //
605
+ const arg = this.safeDict(message, 'arg', {});
606
+ let market = this.getMarketFromArg(arg);
607
+ const marketId = market['id'];
608
+ const hash = 'books:';
609
+ if (marketId.indexOf('_DMCBL') >= 0) {
610
+ market = this.handleDMCBLMarketByMessageHashes(market, hash, client);
611
+ }
612
+ const symbol = market['symbol'];
613
+ const channel = this.safeString(arg, 'channel');
614
+ const messageHash = hash + symbol;
615
+ const data = this.safeList(message, 'data', []);
616
+ const rawOrderBook = this.safeDict(data, 0);
617
+ const timestamp = this.safeInteger(rawOrderBook, 'ts');
618
+ const incrementalBook = channel;
619
+ if (incrementalBook) {
620
+ if (!(symbol in this.orderbooks)) {
621
+ const ob = this.countedOrderBook({});
622
+ ob['symbol'] = symbol;
623
+ this.orderbooks[symbol] = ob;
624
+ }
625
+ const storedOrderBook = this.orderbooks[symbol];
626
+ const asks = this.safeList(rawOrderBook, 'asks', []);
627
+ const bids = this.safeList(rawOrderBook, 'bids', []);
628
+ this.handleDeltas(storedOrderBook['asks'], asks);
629
+ this.handleDeltas(storedOrderBook['bids'], bids);
630
+ storedOrderBook['timestamp'] = timestamp;
631
+ storedOrderBook['datetime'] = this.iso8601(timestamp);
632
+ const checksum = this.safeBool(this.options, 'checksum', true);
633
+ const isSnapshot = this.safeString(message, 'action') === 'snapshot';
634
+ if (!isSnapshot && checksum) {
635
+ const storedAsks = storedOrderBook['asks'];
636
+ const storedBids = storedOrderBook['bids'];
637
+ const asksLength = storedAsks.length;
638
+ const bidsLength = storedBids.length;
639
+ const payloadArray = [];
640
+ for (let i = 0; i < 25; i++) {
641
+ if (i < bidsLength) {
642
+ payloadArray.push(storedBids[i][2][0]);
643
+ payloadArray.push(storedBids[i][2][1]);
644
+ }
645
+ if (i < asksLength) {
646
+ payloadArray.push(storedAsks[i][2][0]);
647
+ payloadArray.push(storedAsks[i][2][1]);
648
+ }
649
+ }
650
+ const payload = payloadArray.join(':');
651
+ const calculatedChecksum = this.crc32(payload, true);
652
+ const responseChecksum = this.safeInteger(rawOrderBook, 'checksum');
653
+ if (calculatedChecksum !== responseChecksum) {
654
+ this.spawn(this.handleCheckSumError, client, symbol, messageHash);
655
+ return;
656
+ }
657
+ }
658
+ }
659
+ else {
660
+ const orderbook = this.orderBook({});
661
+ const parsedOrderbook = this.parseOrderBook(rawOrderBook, symbol, timestamp);
662
+ orderbook.reset(parsedOrderbook);
663
+ this.orderbooks[symbol] = orderbook;
664
+ }
665
+ client.resolve(this.orderbooks[symbol], messageHash);
666
+ }
667
+ async handleCheckSumError(client, symbol, messageHash) {
668
+ await this.unWatchOrderBook(symbol);
669
+ const error = new ChecksumError(this.id + ' ' + this.orderbookChecksumMessage(symbol));
670
+ client.reject(error, messageHash);
671
+ }
672
+ handleDelta(bookside, delta) {
673
+ const bidAsk = this.parseBidAsk(delta, 0, 1);
674
+ bidAsk.push(delta);
675
+ bookside.storeArray(bidAsk);
676
+ }
677
+ handleDeltas(bookside, deltas) {
678
+ for (let i = 0; i < deltas.length; i++) {
679
+ this.handleDelta(bookside, deltas[i]);
680
+ }
681
+ }
682
+ async watchTrades(symbol, since = undefined, limit = undefined, params = {}) {
683
+ /**
684
+ * @method
685
+ * @name coincatch#watchTrades
686
+ * @description get the list of most recent trades for a particular symbol
687
+ * @see https://coincatch.github.io/github.io/en/spot/#trades-channel
688
+ * @param {string} symbol unified symbol of the market to fetch trades for
689
+ * @param {int} [since] timestamp in ms of the earliest trade to fetch
690
+ * @param {int} [limit] the maximum amount of trades to fetch
691
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
692
+ * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades}
693
+ */
694
+ return await this.watchTradesForSymbols([symbol], since, limit, params);
695
+ }
696
+ async watchTradesForSymbols(symbols, since = undefined, limit = undefined, params = {}) {
697
+ /**
698
+ * @method
699
+ * @name coincatch#watchTrades
700
+ * @description watches information on multiple trades made in a market
701
+ * @see https://coincatch.github.io/github.io/en/spot/#trades-channel
702
+ * @param {string} symbol unified market symbol of the market trades were made in
703
+ * @param {int} [since] the earliest time in ms to fetch orders for
704
+ * @param {int} [limit] the maximum number of trade structures to retrieve
705
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
706
+ * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure}
707
+ */
708
+ const symbolsLength = symbols.length;
709
+ if (symbolsLength === 0) {
710
+ throw new ArgumentsRequired(this.id + ' watchTradesForSymbols() requires a non-empty array of symbols');
711
+ }
712
+ await this.loadMarkets();
713
+ symbols = this.marketSymbols(symbols);
714
+ const topics = [];
715
+ const messageHashes = [];
716
+ for (let i = 0; i < symbols.length; i++) {
717
+ const symbol = symbols[i];
718
+ const market = this.market(symbol);
719
+ const [instType, instId] = this.getPublicInstTypeAndId(market);
720
+ const args = {
721
+ 'instType': instType,
722
+ 'channel': 'trade',
723
+ 'instId': instId,
724
+ };
725
+ topics.push(args);
726
+ messageHashes.push('trade:' + symbol);
727
+ }
728
+ const trades = await this.watchPublicMultiple(messageHashes, messageHashes, topics, params);
729
+ if (this.newUpdates) {
730
+ const first = this.safeDict(trades, 0);
731
+ const tradeSymbol = this.safeString(first, 'symbol');
732
+ limit = trades.getLimit(tradeSymbol, limit);
733
+ }
734
+ return this.filterBySinceLimit(trades, since, limit, 'timestamp', true);
735
+ }
736
+ async unWatchTrades(symbol, params = {}) {
737
+ /**
738
+ * @method
739
+ * @name coincatch#unWatchTrades
740
+ * @description unsubscribe from the trades channel
741
+ * @see https://coincatch.github.io/github.io/en/spot/#trades-channel
742
+ * @param {string} symbol unified symbol of the market to unwatch the trades for
743
+ * @returns {any} status of the unwatch request
744
+ */
745
+ await this.loadMarkets();
746
+ return await this.unWatchChannel(symbol, 'trade', 'trade', params);
747
+ }
748
+ handleTrades(client, message) {
749
+ //
750
+ // {
751
+ // action: 'update',
752
+ // arg: { instType: 'sp', channel: 'trade', instId: 'ETHUSDT' },
753
+ // data: [ [ '1728341807469', '2421.41', '0.478', 'sell' ] ],
754
+ // ts: 1728341807482
755
+ // }
756
+ //
757
+ const arg = this.safeDict(message, 'arg', {});
758
+ let market = this.getMarketFromArg(arg);
759
+ const marketId = market['id'];
760
+ const hash = 'trade:';
761
+ if (marketId.indexOf('_DMCBL') >= 0) {
762
+ market = this.handleDMCBLMarketByMessageHashes(market, hash, client);
763
+ }
764
+ const symbol = market['symbol'];
765
+ if (!(symbol in this.trades)) {
766
+ const limit = this.safeInteger(this.options, 'tradesLimit', 1000);
767
+ this.trades[symbol] = new ArrayCache(limit);
768
+ }
769
+ const stored = this.trades[symbol];
770
+ let data = this.safeList(message, 'data', []);
771
+ if (data !== undefined) {
772
+ data = this.sortBy(data, 0);
773
+ for (let i = 0; i < data.length; i++) {
774
+ const trade = this.safeList(data, i);
775
+ const parsed = this.parseWsTrade(trade, market);
776
+ stored.append(parsed);
777
+ }
778
+ }
779
+ const messageHash = hash + symbol;
780
+ client.resolve(stored, messageHash);
781
+ }
782
+ parseWsTrade(trade, market = undefined) {
783
+ //
784
+ // [
785
+ // '1728341807469',
786
+ // '2421.41',
787
+ // '0.478',
788
+ // 'sell'
789
+ // ]
790
+ //
791
+ const timestamp = this.safeInteger(trade, 0);
792
+ return this.safeTrade({
793
+ 'id': undefined,
794
+ 'timestamp': timestamp,
795
+ 'datetime': this.iso8601(timestamp),
796
+ 'symbol': market['symbol'],
797
+ 'side': this.safeStringLower(trade, 3),
798
+ 'price': this.safeString(trade, 1),
799
+ 'amount': this.safeString(trade, 2),
800
+ 'cost': undefined,
801
+ 'takerOrMaker': undefined,
802
+ 'type': undefined,
803
+ 'order': undefined,
804
+ 'fee': undefined,
805
+ 'info': trade,
806
+ }, market);
807
+ }
808
+ async watchBalance(params = {}) {
809
+ /**
810
+ * @method
811
+ * @name coincatch#watchBalance
812
+ * @description watch balance and get the amount of funds available for trading or funds locked in orders
813
+ * @see https://coincatch.github.io/github.io/en/spot/#account-channel
814
+ * @see https://coincatch.github.io/github.io/en/mix/#account-channel
815
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
816
+ * @param {str} [params.type] 'spot' or 'swap' (default is 'spot')
817
+ * @param {string} [params.instType] *swap only* 'umcbl' or 'dmcbl' (default is 'umcbl')
818
+ * @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure}
819
+ */
820
+ let type = undefined;
821
+ [type, params] = this.handleMarketTypeAndParams('watchBalance', undefined, params);
822
+ let instType = 'spbl'; // must be lower case for spot
823
+ if (type === 'swap') {
824
+ instType = 'umcbl';
825
+ }
826
+ const channel = 'account';
827
+ [instType, params] = this.handleOptionAndParams(params, 'watchBalance', 'instType', instType);
828
+ const args = {
829
+ 'instType': instType,
830
+ 'channel': channel,
831
+ 'instId': 'default',
832
+ };
833
+ const messageHash = 'balance:' + instType.toLowerCase();
834
+ return await this.watchPrivate(messageHash, messageHash, args, params);
835
+ }
836
+ handleBalance(client, message) {
837
+ //
838
+ // spot
839
+ // {
840
+ // action: 'snapshot',
841
+ // arg: { instType: 'spbl', channel: 'account', instId: 'default' },
842
+ // data: [
843
+ // {
844
+ // coinId: '3',
845
+ // coinName: 'ETH',
846
+ // available: '0.0000832',
847
+ // frozen: '0',
848
+ // lock: '0'
849
+ // }
850
+ // ],
851
+ // ts: 1728464548725
852
+ // }
853
+ //
854
+ // // swap
855
+ // {
856
+ // action: 'snapshot',
857
+ // arg: { instType: 'dmcbl', channel: 'account', instId: 'default' },
858
+ // data: [
859
+ // {
860
+ // marginCoin: 'ETH',
861
+ // locked: '0.00000000',
862
+ // available: '0.00001203',
863
+ // maxOpenPosAvailable: '0.00001203',
864
+ // maxTransferOut: '0.00001203',
865
+ // equity: '0.00001203',
866
+ // usdtEquity: '0.029092328738',
867
+ // coinDisplayName: 'ETH'
868
+ // }
869
+ // ],
870
+ // ts: 1728650777643
871
+ // }
872
+ //
873
+ const data = this.safeList(message, 'data', []);
874
+ for (let i = 0; i < data.length; i++) {
875
+ const rawBalance = data[i];
876
+ const currencyId = this.safeString2(rawBalance, 'coinName', 'marginCoin');
877
+ const code = this.safeCurrencyCode(currencyId);
878
+ const account = (code in this.balance) ? this.balance[code] : this.account();
879
+ const freeQuery = ('maxTransferOut' in rawBalance) ? 'maxTransferOut' : 'available';
880
+ account['free'] = this.safeString(rawBalance, freeQuery);
881
+ account['total'] = this.safeString(rawBalance, 'equity');
882
+ account['used'] = this.safeString(rawBalance, 'frozen');
883
+ this.balance[code] = account;
884
+ }
885
+ this.balance = this.safeBalance(this.balance);
886
+ const arg = this.safeDict(message, 'arg');
887
+ const instType = this.safeStringLower(arg, 'instType');
888
+ const messageHash = 'balance:' + instType;
889
+ client.resolve(this.balance, messageHash);
890
+ }
891
+ async watchOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
892
+ /**
893
+ * @method
894
+ * @name coincatch#watchOrders
895
+ * @description watches information on multiple orders made by the user
896
+ * @see https://coincatch.github.io/github.io/en/spot/#order-channel
897
+ * @see https://coincatch.github.io/github.io/en/mix/#order-channel
898
+ * @see https://coincatch.github.io/github.io/en/mix/#plan-order-channel
899
+ * @param {string} symbol unified market symbol of the market orders were made in
900
+ * @param {int} [since] the earliest time in ms to fetch orders for
901
+ * @param {int} [limit] the maximum number of order structures to retrieve
902
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
903
+ * @param {string} [params.type] 'spot' or 'swap'
904
+ * @param {string} [params.instType] *swap only* 'umcbl' or 'dmcbl' (default is 'umcbl')
905
+ * @param {bool} [params.trigger] *swap only* whether to watch trigger orders (default is false)
906
+ * @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
907
+ */
908
+ const methodName = 'watchOrders';
909
+ await this.loadMarkets();
910
+ let market = undefined;
911
+ let marketId = undefined;
912
+ if (symbol !== undefined) {
913
+ market = this.market(symbol);
914
+ symbol = market['symbol'];
915
+ marketId = market['id'];
916
+ }
917
+ let marketType = undefined;
918
+ [marketType, params] = this.handleMarketTypeAndParams(methodName, market, params);
919
+ let instType = 'spbl';
920
+ let instId = marketId;
921
+ if (marketType === 'spot') {
922
+ if (symbol === undefined) {
923
+ throw new ArgumentsRequired(this.id + ' ' + methodName + '() requires a symbol argument for ' + marketType + ' markets.');
924
+ }
925
+ }
926
+ else {
927
+ instId = 'default';
928
+ instType = 'umcbl';
929
+ if (symbol === undefined) {
930
+ [instType, params] = this.handleOptionAndParams(params, methodName, 'instType', instType);
931
+ }
932
+ else {
933
+ if (marketId.indexOf('_DMCBL') >= 0) {
934
+ instType = 'dmcbl';
935
+ }
936
+ }
937
+ }
938
+ let channel = 'orders';
939
+ const isTrigger = this.safeBool(params, 'trigger');
940
+ if (isTrigger) {
941
+ channel = 'ordersAlgo'; // channel does not return any data
942
+ params = this.omit(params, 'trigger');
943
+ }
944
+ const args = {
945
+ 'instType': instType,
946
+ 'channel': channel,
947
+ 'instId': instId,
948
+ };
949
+ let messageHash = 'orders';
950
+ if (symbol !== undefined) {
951
+ messageHash += ':' + symbol;
952
+ }
953
+ const orders = await this.watchPrivate(messageHash, messageHash, args, params);
954
+ if (this.newUpdates) {
955
+ limit = orders.getLimit(symbol, limit);
956
+ }
957
+ return this.filterBySymbolSinceLimit(orders, symbol, since, limit, true);
958
+ }
959
+ handleOrder(client, message) {
960
+ //
961
+ // spot
962
+ //
963
+ // {
964
+ // action: 'snapshot',
965
+ // arg: { instType: 'spbl', channel: 'orders', instId: 'ETHUSDT_SPBL' },
966
+ // data: [
967
+ // {
968
+ // instId: 'ETHUSDT_SPBL',
969
+ // ordId: '1228627925964996608',
970
+ // clOrdId: 'f0cccf74-c535-4523-a53d-dbe3b9958559',
971
+ // px: '2000',
972
+ // sz: '0.001',
973
+ // notional: '2',
974
+ // ordType: 'limit',
975
+ // force: 'normal',
976
+ // side: 'buy',
977
+ // accFillSz: '0',
978
+ // avgPx: '0',
979
+ // status: 'new',
980
+ // cTime: 1728653645030,
981
+ // uTime: 1728653645030,
982
+ // orderFee: [],
983
+ // eps: 'API'
984
+ // }
985
+ // ],
986
+ // ts: 1728653645046
987
+ // }
988
+ //
989
+ // swap
990
+ //
991
+ // {
992
+ // action: 'snapshot',
993
+ // arg: { instType: 'umcbl', channel: 'orders', instId: 'default' },
994
+ // data: [
995
+ // {
996
+ // accFillSz: '0',
997
+ // cTime: 1728653796976,
998
+ // clOrdId: '1228628563272753152',
999
+ // eps: 'API',
1000
+ // force: 'normal',
1001
+ // hM: 'single_hold',
1002
+ // instId: 'ETHUSDT_UMCBL',
1003
+ // lever: '5',
1004
+ // low: false,
1005
+ // notionalUsd: '20',
1006
+ // ordId: '1228628563188867072',
1007
+ // ordType: 'limit',
1008
+ // orderFee: [],
1009
+ // posSide: 'net',
1010
+ // px: '2000',
1011
+ // side: 'buy',
1012
+ // status: 'new',
1013
+ // sz: '0.01',
1014
+ // tS: 'buy_single',
1015
+ // tdMode: 'cross',
1016
+ // tgtCcy: 'USDT',
1017
+ // uTime: 1728653796976
1018
+ // }
1019
+ // ],
1020
+ // ts: 1728653797002
1021
+ // }
1022
+ //
1023
+ //
1024
+ const arg = this.safeDict(message, 'arg', {});
1025
+ const instType = this.safeString(arg, 'instType');
1026
+ const argInstId = this.safeString(arg, 'instId');
1027
+ let marketType = undefined;
1028
+ if (instType === 'spbl') {
1029
+ marketType = 'spot';
1030
+ }
1031
+ else {
1032
+ marketType = 'swap';
1033
+ }
1034
+ const data = this.safeList(message, 'data', []);
1035
+ if (this.orders === undefined) {
1036
+ const limit = this.safeInteger(this.options, 'ordersLimit', 1000);
1037
+ this.orders = new ArrayCacheBySymbolById(limit);
1038
+ }
1039
+ const hash = 'orders';
1040
+ const stored = this.orders;
1041
+ let symbol = undefined;
1042
+ for (let i = 0; i < data.length; i++) {
1043
+ const order = data[i];
1044
+ const marketId = this.safeString(order, 'instId', argInstId);
1045
+ const market = this.safeMarket(marketId, undefined, undefined, marketType);
1046
+ const parsed = this.parseWsOrder(order, market);
1047
+ stored.append(parsed);
1048
+ symbol = parsed['symbol'];
1049
+ const messageHash = 'orders:' + symbol;
1050
+ client.resolve(stored, messageHash);
1051
+ }
1052
+ client.resolve(stored, hash);
1053
+ }
1054
+ parseWsOrder(order, market = undefined) {
1055
+ //
1056
+ // spot
1057
+ // {
1058
+ // instId: 'ETHUSDT_SPBL',
1059
+ // ordId: '1228627925964996608',
1060
+ // clOrdId: 'f0cccf74-c535-4523-a53d-dbe3b9958559',
1061
+ // px: '2000',
1062
+ // sz: '0.001',
1063
+ // notional: '2',
1064
+ // ordType: 'limit',
1065
+ // force: 'normal',
1066
+ // side: 'buy',
1067
+ // accFillSz: '0',
1068
+ // avgPx: '0',
1069
+ // status: 'new',
1070
+ // cTime: 1728653645030,
1071
+ // uTime: 1728653645030,
1072
+ // orderFee: orderFee: [ { fee: '0', feeCcy: 'USDT' } ],
1073
+ // eps: 'API'
1074
+ // }
1075
+ //
1076
+ // swap
1077
+ // {
1078
+ // accFillSz: '0',
1079
+ // cTime: 1728653796976,
1080
+ // clOrdId: '1228628563272753152',
1081
+ // eps: 'API',
1082
+ // force: 'normal',
1083
+ // hM: 'single_hold',
1084
+ // instId: 'ETHUSDT_UMCBL',
1085
+ // lever: '5',
1086
+ // low: false,
1087
+ // notionalUsd: '20',
1088
+ // ordId: '1228628563188867072',
1089
+ // ordType: 'limit',
1090
+ // orderFee: [ { fee: '0', feeCcy: 'USDT' } ],
1091
+ // posSide: 'net',
1092
+ // px: '2000',
1093
+ // side: 'buy',
1094
+ // status: 'new',
1095
+ // sz: '0.01',
1096
+ // tS: 'buy_single',
1097
+ // tdMode: 'cross',
1098
+ // tgtCcy: 'USDT',
1099
+ // uTime: 1728653796976
1100
+ // }
1101
+ //
1102
+ const marketId = this.safeString(order, 'instId');
1103
+ const settleId = this.safeString(order, 'tgtCcy');
1104
+ market = this.safeMarketCustom(marketId, market, settleId);
1105
+ const timestamp = this.safeInteger(order, 'cTime');
1106
+ const symbol = market['symbol'];
1107
+ const rawStatus = this.safeString(order, 'status');
1108
+ const orderFee = this.safeList(order, 'orderFee', []);
1109
+ const fee = this.safeDict(orderFee, 0);
1110
+ const feeCost = Precise.stringMul(this.safeString(fee, 'fee'), '-1');
1111
+ const feeCurrency = this.safeString(fee, 'feeCcy');
1112
+ let price = this.omitZero(this.safeString(order, 'px'));
1113
+ const priceAvg = this.omitZero(this.safeString(order, 'avgPx'));
1114
+ if (price === undefined) {
1115
+ price = priceAvg;
1116
+ }
1117
+ const type = this.safeStringLower(order, 'ordType');
1118
+ return this.safeOrder({
1119
+ 'id': this.safeString(order, 'ordId'),
1120
+ 'clientOrderId': this.safeString(order, 'clOrdId'),
1121
+ 'timestamp': timestamp,
1122
+ 'datetime': this.iso8601(timestamp),
1123
+ 'lastTradeTimestamp': undefined,
1124
+ 'lastUpdateTimestamp': this.safeInteger(order, 'uTime'),
1125
+ 'status': this.parseOrderStatus(rawStatus),
1126
+ 'symbol': symbol,
1127
+ 'type': type,
1128
+ 'timeInForce': this.parseOrderTimeInForce(this.safeStringLower(order, 'force')),
1129
+ 'side': this.safeStringLower(order, 'side'),
1130
+ 'price': price,
1131
+ 'average': this.safeString(order, 'avgPx'),
1132
+ 'amount': this.safeString(order, 'sz'),
1133
+ 'filled': this.safeString(order, 'accFillSz'),
1134
+ 'remaining': undefined,
1135
+ 'triggerPrice': undefined,
1136
+ 'takeProfitPrice': undefined,
1137
+ 'stopLossPrice': undefined,
1138
+ 'cost': this.safeString(order, 'notional'),
1139
+ 'trades': undefined,
1140
+ 'fee': {
1141
+ 'currency': feeCurrency,
1142
+ 'cost': feeCost,
1143
+ },
1144
+ 'reduceOnly': this.safeBool(order, 'low'),
1145
+ 'postOnly': undefined,
1146
+ 'info': order,
1147
+ }, market);
1148
+ }
1149
+ async watchPositions(symbols = undefined, since = undefined, limit = undefined, params = {}) {
1150
+ /**
1151
+ * @method
1152
+ * @name coincatch#watchPositions
1153
+ * @description watch all open positions
1154
+ * @see https://coincatch.github.io/github.io/en/mix/#positions-channel
1155
+ * @param {string[]|undefined} symbols list of unified market symbols
1156
+ * @param {object} params extra parameters specific to the exchange API endpoint
1157
+ * @returns {object[]} a list of [position structure]{@link https://docs.ccxt.com/en/latest/manual.html#position-structure}
1158
+ */
1159
+ await this.loadMarkets();
1160
+ symbols = this.marketSymbols(symbols, 'swap');
1161
+ const messageHashes = [];
1162
+ const hash = 'positions';
1163
+ let instTypes = [];
1164
+ if (symbols !== undefined) {
1165
+ for (let i = 0; i < symbols.length; i++) {
1166
+ const symbol = symbols[i];
1167
+ const market = this.market(symbol);
1168
+ const instType = this.getPrivateInstType(market);
1169
+ if (!this.inArray(instType, instTypes)) {
1170
+ instTypes.push(instType);
1171
+ }
1172
+ messageHashes.push(hash + '::' + symbol);
1173
+ }
1174
+ }
1175
+ else {
1176
+ instTypes = ['umcbl', 'dmcbl'];
1177
+ messageHashes.push(hash);
1178
+ }
1179
+ const args = [];
1180
+ const subscribeHashes = [];
1181
+ for (let i = 0; i < instTypes.length; i++) {
1182
+ const instType = instTypes[i];
1183
+ const arg = {
1184
+ 'instType': instType,
1185
+ 'channel': hash,
1186
+ 'instId': 'default',
1187
+ };
1188
+ subscribeHashes.push(hash + '::' + instType);
1189
+ args.push(arg);
1190
+ }
1191
+ const newPositions = await this.watchPrivateMultiple(messageHashes, subscribeHashes, args, params);
1192
+ if (this.newUpdates) {
1193
+ return newPositions;
1194
+ }
1195
+ return this.filterBySymbolsSinceLimit(newPositions, symbols, since, limit, true);
1196
+ }
1197
+ getPrivateInstType(market) {
1198
+ const marketId = market['id'];
1199
+ if (marketId.indexOf('_DMCBL') >= 0) {
1200
+ return 'dmcbl';
1201
+ }
1202
+ return 'umcbl';
1203
+ }
1204
+ handlePositions(client, message) {
1205
+ //
1206
+ // {
1207
+ // action: 'snapshot',
1208
+ // arg: { instType: 'umcbl', channel: 'positions', instId: 'default' },
1209
+ // data: [
1210
+ // {
1211
+ // posId: '1221355728745619456',
1212
+ // instId: 'ETHUSDT_UMCBL',
1213
+ // instName: 'ETHUSDT',
1214
+ // marginCoin: 'USDT',
1215
+ // margin: '5.27182',
1216
+ // marginMode: 'crossed',
1217
+ // holdSide: 'long',
1218
+ // holdMode: 'single_hold',
1219
+ // total: '0.01',
1220
+ // available: '0.01',
1221
+ // locked: '0',
1222
+ // averageOpenPrice: '2635.91',
1223
+ // leverage: 5,
1224
+ // achievedProfits: '0',
1225
+ // upl: '-0.0267',
1226
+ // uplRate: '-0.005064664576',
1227
+ // liqPx: '-3110.66866033',
1228
+ // keepMarginRate: '0.0033',
1229
+ // marginRate: '0.002460827254',
1230
+ // cTime: '1726919818102',
1231
+ // uTime: '1728919604312',
1232
+ // markPrice: '2633.24',
1233
+ // autoMargin: 'off'
1234
+ // }
1235
+ // ],
1236
+ // ts: 1728919604329
1237
+ // }
1238
+ //
1239
+ if (this.positions === undefined) {
1240
+ this.positions = new ArrayCacheBySymbolBySide();
1241
+ }
1242
+ const cache = this.positions;
1243
+ const rawPositions = this.safeValue(message, 'data', []);
1244
+ const dataLength = rawPositions.length;
1245
+ if (dataLength === 0) {
1246
+ return;
1247
+ }
1248
+ const newPositions = [];
1249
+ const symbols = [];
1250
+ for (let i = 0; i < rawPositions.length; i++) {
1251
+ const rawPosition = rawPositions[i];
1252
+ const position = this.parseWsPosition(rawPosition);
1253
+ symbols.push(position['symbol']);
1254
+ newPositions.push(position);
1255
+ cache.append(position);
1256
+ }
1257
+ const hash = 'positions';
1258
+ const messageHashes = this.findMessageHashes(client, hash);
1259
+ for (let i = 0; i < messageHashes.length; i++) {
1260
+ const messageHash = messageHashes[i];
1261
+ const parts = messageHash.split('::');
1262
+ const symbol = parts[1];
1263
+ if (this.inArray(symbol, symbols)) {
1264
+ const positionsForSymbol = [];
1265
+ for (let j = 0; j < newPositions.length; j++) {
1266
+ const position = newPositions[j];
1267
+ if (position['symbol'] === symbol) {
1268
+ positionsForSymbol.push(position);
1269
+ }
1270
+ }
1271
+ client.resolve(positionsForSymbol, messageHash);
1272
+ }
1273
+ }
1274
+ client.resolve(newPositions, hash);
1275
+ }
1276
+ parseWsPosition(position, market = undefined) {
1277
+ //
1278
+ // {
1279
+ // posId: '1221355728745619456',
1280
+ // instId: 'ETHUSDT_UMCBL',
1281
+ // instName: 'ETHUSDT',
1282
+ // marginCoin: 'USDT',
1283
+ // margin: '5.27182',
1284
+ // marginMode: 'crossed',
1285
+ // holdSide: 'long',
1286
+ // holdMode: 'single_hold',
1287
+ // total: '0.01',
1288
+ // available: '0.01',
1289
+ // locked: '0',
1290
+ // averageOpenPrice: '2635.91',
1291
+ // leverage: 5,
1292
+ // achievedProfits: '0',
1293
+ // upl: '-0.0267',
1294
+ // uplRate: '-0.005064664576',
1295
+ // liqPx: '-3110.66866033',
1296
+ // keepMarginRate: '0.0033',
1297
+ // marginRate: '0.002460827254',
1298
+ // cTime: '1726919818102',
1299
+ // uTime: '1728919604312',
1300
+ // markPrice: '2633.24',
1301
+ // autoMargin: 'off'
1302
+ // }
1303
+ //
1304
+ const marketId = this.safeString(position, 'symbol');
1305
+ const settleId = this.safeString(position, 'marginCoin');
1306
+ market = this.safeMarketCustom(marketId, market, settleId);
1307
+ const timestamp = this.safeInteger(position, 'cTime');
1308
+ const marginModeId = this.safeString(position, 'marginMode');
1309
+ const marginMode = this.getSupportedMapping(marginModeId, {
1310
+ 'crossed': 'cross',
1311
+ 'isolated': 'isolated',
1312
+ });
1313
+ let isHedged = undefined;
1314
+ const holdMode = this.safeString(position, 'holdMode');
1315
+ if (holdMode === 'double_hold') {
1316
+ isHedged = true;
1317
+ }
1318
+ else if (holdMode === 'single_hold') {
1319
+ isHedged = false;
1320
+ }
1321
+ const percentageDecimal = this.safeString(position, 'uplRate');
1322
+ const percentage = Precise.stringMul(percentageDecimal, '100');
1323
+ const margin = this.safeNumber(position, 'margin');
1324
+ return this.safePosition({
1325
+ 'symbol': market['symbol'],
1326
+ 'id': undefined,
1327
+ 'timestamp': timestamp,
1328
+ 'datetime': this.iso8601(timestamp),
1329
+ 'contracts': this.safeNumber(position, 'total'),
1330
+ 'contractSize': undefined,
1331
+ 'side': this.safeStringLower(position, 'holdSide'),
1332
+ 'notional': margin,
1333
+ 'leverage': this.safeInteger(position, 'leverage'),
1334
+ 'unrealizedPnl': this.safeNumber(position, 'upl'),
1335
+ 'realizedPnl': this.safeNumber(position, 'achievedProfits'),
1336
+ 'collateral': undefined,
1337
+ 'entryPrice': this.safeNumber(position, 'averageOpenPrice'),
1338
+ 'markPrice': this.safeNumber(position, 'markPrice'),
1339
+ 'liquidationPrice': this.safeNumber(position, 'liqPx'),
1340
+ 'marginMode': marginMode,
1341
+ 'hedged': isHedged,
1342
+ 'maintenanceMargin': undefined,
1343
+ 'maintenanceMarginPercentage': this.safeNumber(position, 'keepMarginRate'),
1344
+ 'initialMargin': margin,
1345
+ 'initialMarginPercentage': undefined,
1346
+ 'marginRatio': this.safeNumber(position, 'marginRate'),
1347
+ 'lastUpdateTimestamp': this.safeInteger(position, 'uTime'),
1348
+ 'lastPrice': undefined,
1349
+ 'stopLossPrice': undefined,
1350
+ 'takeProfitPrice': undefined,
1351
+ 'percentage': percentage,
1352
+ 'info': position,
1353
+ });
1354
+ }
1355
+ handleErrorMessage(client, message) {
1356
+ //
1357
+ // { event: "error", code: 30001, msg: "Channel does not exist" }
1358
+ //
1359
+ const event = this.safeString(message, 'event');
1360
+ try {
1361
+ if (event === 'error') {
1362
+ const code = this.safeString(message, 'code');
1363
+ const feedback = this.id + ' ' + this.json(message);
1364
+ this.throwExactlyMatchedException(this.exceptions['ws']['exact'], code, feedback);
1365
+ const msg = this.safeString(message, 'msg', '');
1366
+ this.throwBroadlyMatchedException(this.exceptions['ws']['broad'], msg, feedback);
1367
+ throw new ExchangeError(feedback);
1368
+ }
1369
+ return false;
1370
+ }
1371
+ catch (e) {
1372
+ if (e instanceof AuthenticationError) {
1373
+ const messageHash = 'authenticated';
1374
+ client.reject(e, messageHash);
1375
+ if (messageHash in client.subscriptions) {
1376
+ delete client.subscriptions[messageHash];
1377
+ }
1378
+ }
1379
+ else {
1380
+ client.reject(e);
1381
+ }
1382
+ return true;
1383
+ }
1384
+ }
1385
+ handleMessage(client, message) {
1386
+ // todo handle with subscribe and unsubscribe
1387
+ if (this.handleErrorMessage(client, message)) {
1388
+ return;
1389
+ }
1390
+ const content = this.safeString(message, 'message');
1391
+ if (content === 'pong') {
1392
+ this.handlePong(client, message);
1393
+ return;
1394
+ }
1395
+ if (message === 'pong') {
1396
+ this.handlePong(client, message);
1397
+ return;
1398
+ }
1399
+ const event = this.safeString(message, 'event');
1400
+ if (event === 'login') {
1401
+ this.handleAuthenticate(client, message);
1402
+ return;
1403
+ }
1404
+ if (event === 'subscribe') {
1405
+ this.handleSubscriptionStatus(client, message);
1406
+ return;
1407
+ }
1408
+ if (event === 'unsubscribe') {
1409
+ this.handleUnSubscriptionStatus(client, message);
1410
+ return;
1411
+ }
1412
+ const data = this.safeDict(message, 'arg', {});
1413
+ const channel = this.safeString(data, 'channel');
1414
+ if (channel === 'ticker') {
1415
+ this.handleTicker(client, message);
1416
+ }
1417
+ if (channel.indexOf('candle') >= 0) {
1418
+ this.handleOHLCV(client, message);
1419
+ }
1420
+ if (channel.indexOf('books') >= 0) {
1421
+ this.handleOrderBook(client, message);
1422
+ }
1423
+ if (channel === 'trade') {
1424
+ this.handleTrades(client, message);
1425
+ }
1426
+ if (channel === 'account') {
1427
+ this.handleBalance(client, message);
1428
+ }
1429
+ if ((channel === 'orders') || (channel === 'ordersAlgo')) {
1430
+ this.handleOrder(client, message);
1431
+ }
1432
+ if (channel === 'positions') {
1433
+ this.handlePositions(client, message);
1434
+ }
1435
+ }
1436
+ ping(client) {
1437
+ return 'ping';
1438
+ }
1439
+ handlePong(client, message) {
1440
+ client.lastPong = this.milliseconds();
1441
+ return message;
1442
+ }
1443
+ handleSubscriptionStatus(client, message) {
1444
+ return message;
1445
+ }
1446
+ handleUnSubscriptionStatus(client, message) {
1447
+ let argsList = this.safeList(message, 'args');
1448
+ if (argsList === undefined) {
1449
+ argsList = [this.safeDict(message, 'arg', {})];
1450
+ }
1451
+ for (let i = 0; i < argsList.length; i++) {
1452
+ const arg = argsList[i];
1453
+ const channel = this.safeString(arg, 'channel');
1454
+ if (channel === 'books') {
1455
+ this.handleOrderBookUnSubscription(client, message);
1456
+ }
1457
+ else if (channel === 'trade') {
1458
+ this.handleTradesUnSubscription(client, message);
1459
+ }
1460
+ else if (channel === 'ticker') {
1461
+ this.handleTickerUnSubscription(client, message);
1462
+ }
1463
+ else if (channel.startsWith('candle')) {
1464
+ this.handleOHLCVUnSubscription(client, message);
1465
+ }
1466
+ }
1467
+ return message;
1468
+ }
1469
+ handleOrderBookUnSubscription(client, message) {
1470
+ const arg = this.safeDict(message, 'arg', {});
1471
+ const instType = this.safeStringLower(arg, 'instType');
1472
+ const type = (instType === 'sp') ? 'spot' : 'swap';
1473
+ const instId = this.safeString(arg, 'instId');
1474
+ const market = this.safeMarket(instId, undefined, undefined, type);
1475
+ const symbol = market['symbol'];
1476
+ const messageHash = 'unsubscribe:orderbook:' + market['symbol'];
1477
+ const subMessageHash = 'orderbook:' + symbol;
1478
+ if (symbol in this.orderbooks) {
1479
+ delete this.orderbooks[symbol];
1480
+ }
1481
+ if (subMessageHash in client.subscriptions) {
1482
+ delete client.subscriptions[subMessageHash];
1483
+ }
1484
+ if (messageHash in client.subscriptions) {
1485
+ delete client.subscriptions[messageHash];
1486
+ }
1487
+ const error = new UnsubscribeError(this.id + 'orderbook ' + symbol);
1488
+ client.reject(error, subMessageHash);
1489
+ client.resolve(true, messageHash);
1490
+ }
1491
+ handleTradesUnSubscription(client, message) {
1492
+ const arg = this.safeDict(message, 'arg', {});
1493
+ const instType = this.safeStringLower(arg, 'instType');
1494
+ const type = (instType === 'sp') ? 'spot' : 'swap';
1495
+ const instId = this.safeString(arg, 'instId');
1496
+ const market = this.safeMarket(instId, undefined, undefined, type);
1497
+ const symbol = market['symbol'];
1498
+ const messageHash = 'unsubscribe:trade:' + market['symbol'];
1499
+ const subMessageHash = 'trade:' + symbol;
1500
+ if (symbol in this.trades) {
1501
+ delete this.trades[symbol];
1502
+ }
1503
+ if (subMessageHash in client.subscriptions) {
1504
+ delete client.subscriptions[subMessageHash];
1505
+ }
1506
+ if (messageHash in client.subscriptions) {
1507
+ delete client.subscriptions[messageHash];
1508
+ }
1509
+ const error = new UnsubscribeError(this.id + 'trades ' + symbol);
1510
+ client.reject(error, subMessageHash);
1511
+ client.resolve(true, messageHash);
1512
+ }
1513
+ handleTickerUnSubscription(client, message) {
1514
+ const arg = this.safeDict(message, 'arg', {});
1515
+ const instType = this.safeStringLower(arg, 'instType');
1516
+ const type = (instType === 'sp') ? 'spot' : 'swap';
1517
+ const instId = this.safeString(arg, 'instId');
1518
+ const market = this.safeMarket(instId, undefined, undefined, type);
1519
+ const symbol = market['symbol'];
1520
+ const messageHash = 'unsubscribe:ticker:' + market['symbol'];
1521
+ const subMessageHash = 'ticker:' + symbol;
1522
+ if (symbol in this.tickers) {
1523
+ delete this.tickers[symbol];
1524
+ }
1525
+ if (subMessageHash in client.subscriptions) {
1526
+ delete client.subscriptions[subMessageHash];
1527
+ }
1528
+ if (messageHash in client.subscriptions) {
1529
+ delete client.subscriptions[messageHash];
1530
+ }
1531
+ const error = new UnsubscribeError(this.id + 'ticker ' + symbol);
1532
+ client.reject(error, subMessageHash);
1533
+ client.resolve(true, messageHash);
1534
+ }
1535
+ handleOHLCVUnSubscription(client, message) {
1536
+ const arg = this.safeDict(message, 'arg', {});
1537
+ const instType = this.safeStringLower(arg, 'instType');
1538
+ const type = (instType === 'sp') ? 'spot' : 'swap';
1539
+ const instId = this.safeString(arg, 'instId');
1540
+ const channel = this.safeString(arg, 'channel');
1541
+ const interval = channel.replace('candle', '');
1542
+ const timeframes = this.safeDict(this.options, 'timeframesForWs');
1543
+ const timeframe = this.findTimeframe(interval, timeframes);
1544
+ const market = this.safeMarket(instId, undefined, undefined, type);
1545
+ const symbol = market['symbol'];
1546
+ const messageHash = 'unsubscribe:ohlcv:' + timeframe + ':' + market['symbol'];
1547
+ const subMessageHash = 'ohlcv:' + symbol + ':' + timeframe;
1548
+ if (symbol in this.ohlcvs) {
1549
+ if (timeframe in this.ohlcvs[symbol]) {
1550
+ delete this.ohlcvs[symbol][timeframe];
1551
+ }
1552
+ }
1553
+ this.cleanUnsubscription(client, subMessageHash, messageHash);
1554
+ }
1555
+ }