ccxt 4.5.45 → 4.5.47

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