ccxt 4.3.85 → 4.3.86

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 (44) hide show
  1. package/README.md +7 -5
  2. package/dist/ccxt.browser.min.js +15 -15
  3. package/dist/cjs/ccxt.js +6 -1
  4. package/dist/cjs/src/abstract/hashkey.js +9 -0
  5. package/dist/cjs/src/base/Exchange.js +1 -1
  6. package/dist/cjs/src/binance.js +4 -2
  7. package/dist/cjs/src/bitfinex.js +2 -2
  8. package/dist/cjs/src/hashkey.js +4328 -0
  9. package/dist/cjs/src/hyperliquid.js +85 -65
  10. package/dist/cjs/src/indodax.js +37 -9
  11. package/dist/cjs/src/krakenfutures.js +12 -10
  12. package/dist/cjs/src/pro/ascendex.js +45 -5
  13. package/dist/cjs/src/pro/bingx.js +13 -12
  14. package/dist/cjs/src/pro/hashkey.js +839 -0
  15. package/dist/cjs/src/pro/hyperliquid.js +123 -0
  16. package/dist/cjs/src/pro/mexc.js +13 -7
  17. package/dist/cjs/src/pro/woo.js +1 -0
  18. package/dist/cjs/src/pro/woofipro.js +1 -0
  19. package/dist/cjs/src/pro/xt.js +1 -0
  20. package/js/ccxt.d.ts +8 -2
  21. package/js/ccxt.js +6 -2
  22. package/js/src/abstract/hashkey.d.ts +70 -0
  23. package/js/src/abstract/hashkey.js +11 -0
  24. package/js/src/base/Exchange.js +1 -1
  25. package/js/src/binance.js +4 -2
  26. package/js/src/bitfinex.js +2 -2
  27. package/js/src/hashkey.d.ts +178 -0
  28. package/js/src/hashkey.js +4329 -0
  29. package/js/src/hyperliquid.d.ts +3 -0
  30. package/js/src/hyperliquid.js +85 -65
  31. package/js/src/indodax.js +37 -9
  32. package/js/src/krakenfutures.js +12 -10
  33. package/js/src/pro/ascendex.d.ts +2 -0
  34. package/js/src/pro/ascendex.js +45 -5
  35. package/js/src/pro/bingx.js +13 -12
  36. package/js/src/pro/hashkey.d.ts +34 -0
  37. package/js/src/pro/hashkey.js +840 -0
  38. package/js/src/pro/hyperliquid.d.ts +7 -1
  39. package/js/src/pro/hyperliquid.js +123 -0
  40. package/js/src/pro/mexc.js +13 -7
  41. package/js/src/pro/woo.js +1 -0
  42. package/js/src/pro/woofipro.js +1 -0
  43. package/js/src/pro/xt.js +1 -0
  44. package/package.json +1 -1
@@ -0,0 +1,840 @@
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 hashkeyRest from '../hashkey.js';
9
+ import { ArrayCache, ArrayCacheBySymbolById, ArrayCacheBySymbolBySide, ArrayCacheByTimestamp } from '../base/ws/Cache.js';
10
+ // ---------------------------------------------------------------------------
11
+ export default class hashkey extends hashkeyRest {
12
+ describe() {
13
+ return this.deepExtend(super.describe(), {
14
+ 'has': {
15
+ 'ws': true,
16
+ 'watchBalance': true,
17
+ 'watchMyTrades': true,
18
+ 'watchOHLCV': true,
19
+ 'watchOrderBook': true,
20
+ 'watchOrders': true,
21
+ 'watchTicker': true,
22
+ 'watchTrades': true,
23
+ 'watchPositions': false,
24
+ },
25
+ 'urls': {
26
+ 'api': {
27
+ 'ws': {
28
+ 'public': 'wss://stream-glb.hashkey.com/quote/ws/v1',
29
+ 'private': 'wss://stream-glb.hashkey.com/api/v1/ws',
30
+ },
31
+ 'test': {
32
+ 'ws': {
33
+ 'public': 'wss://stream-glb.sim.hashkeydev.com/quote/ws/v1',
34
+ 'private': 'wss://stream-glb.sim.hashkeydev.com/api/v1/ws',
35
+ },
36
+ },
37
+ },
38
+ },
39
+ 'options': {
40
+ 'listenKeyRefreshRate': 3600000,
41
+ 'listenKey': undefined,
42
+ 'watchBalance': {
43
+ 'fetchBalanceSnapshot': true,
44
+ 'awaitBalanceSnapshot': false, // whether to wait for the balance snapshot before providing updates
45
+ },
46
+ },
47
+ 'streaming': {
48
+ 'keepAlive': 10000,
49
+ },
50
+ });
51
+ }
52
+ async wathPublic(market, topic, messageHash, params = {}) {
53
+ const request = {
54
+ 'symbol': market['id'],
55
+ 'topic': topic,
56
+ 'event': 'sub',
57
+ };
58
+ const url = this.urls['api']['ws']['public'];
59
+ return await this.watch(url, messageHash, this.deepExtend(request, params), messageHash);
60
+ }
61
+ async watchPrivate(messageHash) {
62
+ const listenKey = await this.authenticate();
63
+ const url = this.getPrivateUrl(listenKey);
64
+ return await this.watch(url, messageHash, undefined, messageHash);
65
+ }
66
+ getPrivateUrl(listenKey) {
67
+ return this.urls['api']['ws']['private'] + '/' + listenKey;
68
+ }
69
+ async watchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
70
+ /**
71
+ * @method
72
+ * @name hashkey#watchOHLCV
73
+ * @description watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
74
+ * @see https://hashkeyglobal-apidoc.readme.io/reference/websocket-api#public-stream
75
+ * @param {string} symbol unified symbol of the market to fetch OHLCV data for
76
+ * @param {string} timeframe the length of time each candle represents
77
+ * @param {int} [since] timestamp in ms of the earliest candle to fetch
78
+ * @param {int} [limit] the maximum amount of candles to fetch
79
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
80
+ * @param {bool} [params.binary] true or false - default false
81
+ * @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
82
+ */
83
+ await this.loadMarkets();
84
+ const market = this.market(symbol);
85
+ symbol = market['symbol'];
86
+ const interval = this.safeString(this.timeframes, timeframe, timeframe);
87
+ const topic = 'kline_' + interval;
88
+ const messageHash = 'ohlcv:' + symbol + ':' + timeframe;
89
+ const ohlcv = await this.wathPublic(market, topic, messageHash, params);
90
+ if (this.newUpdates) {
91
+ limit = ohlcv.getLimit(symbol, limit);
92
+ }
93
+ return this.filterBySinceLimit(ohlcv, since, limit, 0, true);
94
+ }
95
+ handleOHLCV(client, message) {
96
+ //
97
+ // {
98
+ // "symbol": "DOGEUSDT",
99
+ // "symbolName": "DOGEUSDT",
100
+ // "topic": "kline",
101
+ // "params": {
102
+ // "realtimeInterval": "24h",
103
+ // "klineType": "1m"
104
+ // },
105
+ // "data": [
106
+ // {
107
+ // "t": 1722861660000,
108
+ // "s": "DOGEUSDT",
109
+ // "sn": "DOGEUSDT",
110
+ // "c": "0.08389",
111
+ // "h": "0.08389",
112
+ // "l": "0.08389",
113
+ // "o": "0.08389",
114
+ // "v": "0"
115
+ // }
116
+ // ],
117
+ // "f": true,
118
+ // "sendTime": 1722861664258,
119
+ // "shared": false
120
+ // }
121
+ //
122
+ const marketId = this.safeString(message, 'symbol');
123
+ const market = this.safeMarket(marketId);
124
+ const symbol = this.safeSymbol(marketId, market);
125
+ if (!(symbol in this.ohlcvs)) {
126
+ this.ohlcvs[symbol] = {};
127
+ }
128
+ const params = this.safeDict(message, 'params');
129
+ const klineType = this.safeString(params, 'klineType');
130
+ const timeframe = this.findTimeframe(klineType);
131
+ if (!(timeframe in this.ohlcvs[symbol])) {
132
+ const limit = this.safeInteger(this.options, 'OHLCVLimit', 1000);
133
+ this.ohlcvs[symbol][timeframe] = new ArrayCacheByTimestamp(limit);
134
+ }
135
+ const data = this.safeList(message, 'data', []);
136
+ const stored = this.ohlcvs[symbol][timeframe];
137
+ for (let i = 0; i < data.length; i++) {
138
+ const candle = this.safeDict(data, i, {});
139
+ const parsed = this.parseWsOHLCV(candle, market);
140
+ stored.append(parsed);
141
+ }
142
+ const messageHash = 'ohlcv:' + symbol + ':' + timeframe;
143
+ client.resolve(stored, messageHash);
144
+ }
145
+ parseWsOHLCV(ohlcv, market = undefined) {
146
+ //
147
+ // {
148
+ // "t": 1722861660000,
149
+ // "s": "DOGEUSDT",
150
+ // "sn": "DOGEUSDT",
151
+ // "c": "0.08389",
152
+ // "h": "0.08389",
153
+ // "l": "0.08389",
154
+ // "o": "0.08389",
155
+ // "v": "0"
156
+ // }
157
+ //
158
+ return [
159
+ this.safeInteger(ohlcv, 't'),
160
+ this.safeNumber(ohlcv, 'o'),
161
+ this.safeNumber(ohlcv, 'h'),
162
+ this.safeNumber(ohlcv, 'l'),
163
+ this.safeNumber(ohlcv, 'c'),
164
+ this.safeNumber(ohlcv, 'v'),
165
+ ];
166
+ }
167
+ async watchTicker(symbol, params = {}) {
168
+ /**
169
+ * @method
170
+ * @name hahskey#watchTicker
171
+ * @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
172
+ * @see https://hashkeyglobal-apidoc.readme.io/reference/websocket-api#public-stream
173
+ * @param {string} symbol unified symbol of the market to fetch the ticker for
174
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
175
+ * @param {bool} [params.binary] true or false - default false
176
+ * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
177
+ */
178
+ await this.loadMarkets();
179
+ const market = this.market(symbol);
180
+ symbol = market['symbol'];
181
+ const topic = 'realtimes';
182
+ const messageHash = 'ticker:' + symbol;
183
+ return await this.wathPublic(market, topic, messageHash, params);
184
+ }
185
+ handleTicker(client, message) {
186
+ //
187
+ // {
188
+ // "symbol": "ETHUSDT",
189
+ // "symbolName": "ETHUSDT",
190
+ // "topic": "realtimes",
191
+ // "params": {
192
+ // "realtimeInterval": "24h"
193
+ // },
194
+ // "data": [
195
+ // {
196
+ // "t": 1722864411064,
197
+ // "s": "ETHUSDT",
198
+ // "sn": "ETHUSDT",
199
+ // "c": "2195",
200
+ // "h": "2918.85",
201
+ // "l": "2135.5",
202
+ // "o": "2915.78",
203
+ // "v": "666.5019",
204
+ // "qv": "1586902.757079",
205
+ // "m": "-0.2472",
206
+ // "e": 301
207
+ // }
208
+ // ],
209
+ // "f": false,
210
+ // "sendTime": 1722864411086,
211
+ // "shared": false
212
+ // }
213
+ //
214
+ const data = this.safeList(message, 'data', []);
215
+ const ticker = this.parseTicker(this.safeDict(data, 0));
216
+ const symbol = ticker['symbol'];
217
+ const messageHash = 'ticker:' + symbol;
218
+ this.tickers[symbol] = ticker;
219
+ client.resolve(this.tickers[symbol], messageHash);
220
+ }
221
+ async watchTrades(symbol, since = undefined, limit = undefined, params = {}) {
222
+ /**
223
+ * @method
224
+ * @name hashkey#watchTrades
225
+ * @description watches information on multiple trades made in a market
226
+ * @see https://hashkeyglobal-apidoc.readme.io/reference/websocket-api#public-stream
227
+ * @param {string} symbol unified market symbol of the market trades were made in
228
+ * @param {int} [since] the earliest time in ms to fetch orders for
229
+ * @param {int} [limit] the maximum number of trade structures to retrieve
230
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
231
+ * @param {bool} [params.binary] true or false - default false
232
+ * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure}
233
+ */
234
+ await this.loadMarkets();
235
+ const market = this.market(symbol);
236
+ symbol = market['symbol'];
237
+ const topic = 'trade';
238
+ const messageHash = 'trades:' + symbol;
239
+ const trades = await this.wathPublic(market, topic, messageHash, params);
240
+ if (this.newUpdates) {
241
+ limit = trades.getLimit(symbol, limit);
242
+ }
243
+ return this.filterBySinceLimit(trades, since, limit, 'timestamp', true);
244
+ }
245
+ handleTrades(client, message) {
246
+ //
247
+ // {
248
+ // "symbol": "ETHUSDT",
249
+ // "symbolName": "ETHUSDT",
250
+ // "topic": "trade",
251
+ // "params": {
252
+ // "realtimeInterval": "24h"
253
+ // },
254
+ // "data": [
255
+ // {
256
+ // "v": "1745922896272048129",
257
+ // "t": 1722866228075,
258
+ // "p": "2340.41",
259
+ // "q": "0.0132",
260
+ // "m": true
261
+ // },
262
+ // ...
263
+ // ],
264
+ // "f": true,
265
+ // "sendTime": 1722869464248,
266
+ // "channelId": "668498fffeba4108-00000001-00113184-562e27d215e43f9c-c188b319",
267
+ // "shared": false
268
+ // }
269
+ //
270
+ const marketId = this.safeString(message, 'symbol');
271
+ const market = this.safeMarket(marketId);
272
+ const symbol = market['symbol'];
273
+ if (!(symbol in this.trades)) {
274
+ const limit = this.safeInteger(this.options, 'tradesLimit', 1000);
275
+ this.trades[symbol] = new ArrayCache(limit);
276
+ }
277
+ const stored = this.trades[symbol];
278
+ let data = this.safeList(message, 'data');
279
+ if (data !== undefined) {
280
+ data = this.sortBy(data, 't');
281
+ for (let i = 0; i < data.length; i++) {
282
+ const trade = this.safeDict(data, i);
283
+ const parsed = this.parseWsTrade(trade, market);
284
+ stored.append(parsed);
285
+ }
286
+ }
287
+ const messageHash = 'trades' + ':' + symbol;
288
+ client.resolve(stored, messageHash);
289
+ }
290
+ async watchOrderBook(symbol, limit = undefined, params = {}) {
291
+ /**
292
+ * @method
293
+ * @name alpaca#watchOrderBook
294
+ * @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
295
+ * @see https://hashkeyglobal-apidoc.readme.io/reference/websocket-api#public-stream
296
+ * @param {string} symbol unified symbol of the market to fetch the order book for
297
+ * @param {int} [limit] the maximum amount of order book entries to return.
298
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
299
+ * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
300
+ */
301
+ await this.loadMarkets();
302
+ const market = this.market(symbol);
303
+ symbol = market['symbol'];
304
+ const topic = 'depth';
305
+ const messageHash = 'orderbook:' + symbol;
306
+ const orderbook = await this.wathPublic(market, topic, messageHash, params);
307
+ return orderbook.limit();
308
+ }
309
+ handleOrderBook(client, message) {
310
+ //
311
+ // {
312
+ // "symbol": "ETHUSDT",
313
+ // "symbolName": "ETHUSDT",
314
+ // "topic": "depth",
315
+ // "params": { "realtimeInterval": "24h" },
316
+ // "data": [
317
+ // {
318
+ // "e": 301,
319
+ // "s": "ETHUSDT",
320
+ // "t": 1722873144371,
321
+ // "v": "84661262_18",
322
+ // "b": [
323
+ // [ "1650", "0.0864" ],
324
+ // ...
325
+ // ],
326
+ // "a": [
327
+ // ["4085", "0.0074" ],
328
+ // ...
329
+ // ],
330
+ // "o": 0
331
+ // }
332
+ // ],
333
+ // "f": false,
334
+ // "sendTime": 1722873144589,
335
+ // "channelId": "2265aafffe68b588-00000001-0011510c-9e9ca710b1500854-551830bd",
336
+ // "shared": false
337
+ // }
338
+ //
339
+ const marketId = this.safeString(message, 'symbol');
340
+ const symbol = this.safeSymbol(marketId);
341
+ const messageHash = 'orderbook:' + symbol;
342
+ if (!(symbol in this.orderbooks)) {
343
+ this.orderbooks[symbol] = this.orderBook({});
344
+ }
345
+ const orderbook = this.orderbooks[symbol];
346
+ const data = this.safeList(message, 'data', []);
347
+ const dataEntry = this.safeDict(data, 0);
348
+ const timestamp = this.safeInteger(dataEntry, 't');
349
+ const snapshot = this.parseOrderBook(dataEntry, symbol, timestamp, 'b', 'a');
350
+ orderbook.reset(snapshot);
351
+ orderbook['nonce'] = this.safeInteger(message, 'id');
352
+ this.orderbooks[symbol] = orderbook;
353
+ client.resolve(orderbook, messageHash);
354
+ }
355
+ async watchOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
356
+ /**
357
+ * @method
358
+ * @name hashkey#watchOrders
359
+ * @description watches information on multiple orders made by the user
360
+ * @see https://hashkeyglobal-apidoc.readme.io/reference/websocket-api#private-stream
361
+ * @param {string} symbol unified market symbol of the market orders were made in
362
+ * @param {int} [since] the earliest time in ms to fetch orders for
363
+ * @param {int} [limit] the maximum number of order structures to retrieve
364
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
365
+ * @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
366
+ */
367
+ await this.loadMarkets();
368
+ let messageHash = 'orders';
369
+ if (symbol !== undefined) {
370
+ symbol = this.symbol(symbol);
371
+ messageHash = messageHash + ':' + symbol;
372
+ }
373
+ const orders = await this.watchPrivate(messageHash);
374
+ if (this.newUpdates) {
375
+ limit = orders.getLimit(symbol, limit);
376
+ }
377
+ return this.filterBySymbolSinceLimit(orders, symbol, since, limit, true);
378
+ }
379
+ handleOrder(client, message) {
380
+ //
381
+ // swap
382
+ // {
383
+ // "e": "contractExecutionReport",
384
+ // "E": "1723037391181",
385
+ // "s": "ETHUSDT-PERPETUAL",
386
+ // "c": "1723037389677",
387
+ // "S": "BUY_OPEN",
388
+ // "o": "LIMIT",
389
+ // "f": "IOC",
390
+ // "q": "1",
391
+ // "p": "2561.75",
392
+ // "X": "FILLED",
393
+ // "i": "1747358716129257216",
394
+ // "l": "1",
395
+ // "z": "1",
396
+ // "L": "2463.36",
397
+ // "n": "0.001478016",
398
+ // "N": "USDT",
399
+ // "u": true,
400
+ // "w": true,
401
+ // "m": false,
402
+ // "O": "1723037391140",
403
+ // "Z": "2463.36",
404
+ // "C": false,
405
+ // "v": "5",
406
+ // "reqAmt": "0",
407
+ // "d": "1747358716255075840",
408
+ // "r": "0",
409
+ // "V": "2463.36",
410
+ // "P": "0",
411
+ // "lo": false,
412
+ // "lt": ""
413
+ // }
414
+ //
415
+ if (this.orders === undefined) {
416
+ const limit = this.safeInteger(this.options, 'ordersLimit', 1000);
417
+ this.orders = new ArrayCacheBySymbolById(limit);
418
+ }
419
+ const parsed = this.parseWsOrder(message);
420
+ const orders = this.orders;
421
+ orders.append(parsed);
422
+ const messageHash = 'orders';
423
+ client.resolve(orders, messageHash);
424
+ const symbol = parsed['symbol'];
425
+ const symbolSpecificMessageHash = messageHash + ':' + symbol;
426
+ client.resolve(orders, symbolSpecificMessageHash);
427
+ }
428
+ parseWsOrder(order, market = undefined) {
429
+ const marketId = this.safeString(order, 's');
430
+ market = this.safeMarket(marketId, market);
431
+ const timestamp = this.safeInteger(order, 'O');
432
+ let side = this.safeStringLower(order, 'S');
433
+ let reduceOnly = undefined;
434
+ [side, reduceOnly] = this.parseOrderSideAndReduceOnly(side);
435
+ let type = this.parseOrderType(this.safeString(order, 'o'));
436
+ let timeInForce = this.safeString(order, 'f');
437
+ let postOnly = undefined;
438
+ [type, timeInForce, postOnly] = this.parseOrderTypeTimeInForceAndPostOnly(type, timeInForce);
439
+ if (market['contract']) { // swap orders are always have type 'LIMIT', thus we can not define the correct type
440
+ type = undefined;
441
+ }
442
+ return this.safeOrder({
443
+ 'id': this.safeString(order, 'i'),
444
+ 'clientOrderId': this.safeString(order, 'c'),
445
+ 'datetime': this.iso8601(timestamp),
446
+ 'timestamp': timestamp,
447
+ 'lastTradeTimestamp': undefined,
448
+ 'lastUpdateTimestamp': undefined,
449
+ 'status': this.parseOrderStatus(this.safeString(order, 'X')),
450
+ 'symbol': market['symbol'],
451
+ 'type': type,
452
+ 'timeInForce': timeInForce,
453
+ 'side': side,
454
+ 'price': this.safeString(order, 'p'),
455
+ 'average': this.safeString(order, 'V'),
456
+ 'amount': this.omitZero(this.safeString(order, 'q')),
457
+ 'filled': this.safeString(order, 'z'),
458
+ 'remaining': this.safeString(order, 'r'),
459
+ 'stopPrice': undefined,
460
+ 'triggerPrice': undefined,
461
+ 'takeProfitPrice': undefined,
462
+ 'stopLossPrice': undefined,
463
+ 'cost': this.omitZero(this.safeString(order, 'Z')),
464
+ 'trades': undefined,
465
+ 'fee': {
466
+ 'currency': this.safeCurrencyCode(this.safeString(order, 'N')),
467
+ 'amount': this.omitZero(this.safeString(order, 'n')),
468
+ },
469
+ 'reduceOnly': reduceOnly,
470
+ 'postOnly': postOnly,
471
+ 'info': order,
472
+ }, market);
473
+ }
474
+ async watchMyTrades(symbol = undefined, since = undefined, limit = undefined, params = {}) {
475
+ /**
476
+ * @method
477
+ * @name hashkey#watchMyTrades
478
+ * @description watches information on multiple trades made by the user
479
+ * @see https://hashkeyglobal-apidoc.readme.io/reference/websocket-api#private-stream
480
+ * @param {string} symbol unified market symbol of the market trades were made in
481
+ * @param {int} [since] the earliest time in ms to fetch trades for
482
+ * @param {int} [limit] the maximum number of trade structures to retrieve
483
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
484
+ * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure}
485
+ */
486
+ await this.loadMarkets();
487
+ let messageHash = 'myTrades';
488
+ if (symbol !== undefined) {
489
+ symbol = this.symbol(symbol);
490
+ messageHash += ':' + symbol;
491
+ }
492
+ const trades = await this.watchPrivate(messageHash);
493
+ if (this.newUpdates) {
494
+ limit = trades.getLimit(symbol, limit);
495
+ }
496
+ return this.filterBySinceLimit(trades, since, limit, 'timestamp', true);
497
+ }
498
+ handleMyTrade(client, message, subscription = {}) {
499
+ //
500
+ // {
501
+ // "e": "ticketInfo",
502
+ // "E": "1723037391156",
503
+ // "s": "ETHUSDT-PERPETUAL",
504
+ // "q": "1.00",
505
+ // "t": "1723037391147",
506
+ // "p": "2463.36",
507
+ // "T": "1747358716187197441",
508
+ // "o": "1747358716129257216",
509
+ // "c": "1723037389677",
510
+ // "a": "1735619524953226496",
511
+ // "m": false,
512
+ // "S": "BUY"
513
+ // }
514
+ //
515
+ if (this.myTrades === undefined) {
516
+ const limit = this.safeInteger(this.options, 'tradesLimit', 1000);
517
+ this.myTrades = new ArrayCacheBySymbolById(limit);
518
+ }
519
+ const tradesArray = this.myTrades;
520
+ const parsed = this.parseWsTrade(message);
521
+ tradesArray.append(parsed);
522
+ this.myTrades = tradesArray;
523
+ const messageHash = 'myTrades';
524
+ client.resolve(tradesArray, messageHash);
525
+ const symbol = parsed['symbol'];
526
+ const symbolSpecificMessageHash = messageHash + ':' + symbol;
527
+ client.resolve(tradesArray, symbolSpecificMessageHash);
528
+ }
529
+ parseWsTrade(trade, market = undefined) {
530
+ //
531
+ // watchTrades
532
+ // {
533
+ // "v": "1745922896272048129",
534
+ // "t": 1722866228075,
535
+ // "p": "2340.41",
536
+ // "q": "0.0132",
537
+ // "m": true
538
+ // }
539
+ //
540
+ // watchMyTrades
541
+ // {
542
+ // "e": "ticketInfo",
543
+ // "E": "1723037391156",
544
+ // "s": "ETHUSDT-PERPETUAL",
545
+ // "q": "1.00",
546
+ // "t": "1723037391147",
547
+ // "p": "2463.36",
548
+ // "T": "1747358716187197441",
549
+ // "o": "1747358716129257216",
550
+ // "c": "1723037389677",
551
+ // "a": "1735619524953226496",
552
+ // "m": false,
553
+ // "S": "BUY"
554
+ // }
555
+ //
556
+ const marketId = this.safeString(trade, 's');
557
+ market = this.safeMarket(marketId, market);
558
+ const timestamp = this.safeInteger(trade, 't');
559
+ const isMaker = this.safeBool(trade, 'm');
560
+ let takerOrMaker = undefined;
561
+ if (isMaker !== undefined) {
562
+ if (isMaker) {
563
+ takerOrMaker = 'maker';
564
+ }
565
+ else {
566
+ takerOrMaker = 'taker';
567
+ }
568
+ }
569
+ return this.safeTrade({
570
+ 'id': this.safeString2(trade, 'v', 'T'),
571
+ 'timestamp': timestamp,
572
+ 'datetime': this.iso8601(timestamp),
573
+ 'symbol': market['symbol'],
574
+ 'side': this.safeStringLower(trade, 'S'),
575
+ 'price': this.safeString(trade, 'p'),
576
+ 'amount': this.safeString(trade, 'q'),
577
+ 'cost': undefined,
578
+ 'takerOrMaker': takerOrMaker,
579
+ 'type': undefined,
580
+ 'order': this.safeString(trade, 'o'),
581
+ 'fee': undefined,
582
+ 'info': trade,
583
+ }, market);
584
+ }
585
+ async watchPositions(symbols = undefined, since = undefined, limit = undefined, params = {}) {
586
+ /**
587
+ * @method
588
+ * @name hashkey#watchPositions
589
+ * @see https://hashkeyglobal-apidoc.readme.io/reference/websocket-api#private-stream
590
+ * @description watch all open positions
591
+ * @param {string[]|undefined} symbols list of unified market symbols
592
+ * @param {object} params extra parameters specific to the exchange API endpoint
593
+ * @returns {object[]} a list of [position structure]{@link https://docs.ccxt.com/en/latest/manual.html#position-structure}
594
+ */
595
+ await this.loadMarkets();
596
+ const listenKey = await this.authenticate();
597
+ symbols = this.marketSymbols(symbols);
598
+ const messageHash = 'positions';
599
+ const messageHashes = [];
600
+ if (symbols === undefined) {
601
+ messageHashes.push(messageHash);
602
+ }
603
+ else {
604
+ for (let i = 0; i < symbols.length; i++) {
605
+ const symbol = symbols[i];
606
+ messageHashes.push(messageHash + ':' + symbol);
607
+ }
608
+ }
609
+ const url = this.getPrivateUrl(listenKey);
610
+ const positions = await this.watchMultiple(url, messageHashes, undefined, messageHashes);
611
+ if (this.newUpdates) {
612
+ return positions;
613
+ }
614
+ return this.filterBySymbolsSinceLimit(this.positions, symbols, since, limit, true);
615
+ }
616
+ handlePosition(client, message) {
617
+ //
618
+ // {
619
+ // "e": "outboundContractPositionInfo",
620
+ // "E": "1723084699801",
621
+ // "A": "1735619524953226496",
622
+ // "s": "ETHUSDT-PERPETUAL",
623
+ // "S": "LONG",
624
+ // "p": "2429.6",
625
+ // "P": "2",
626
+ // "a": "2",
627
+ // "f": "10760.14",
628
+ // "m": "1.0085",
629
+ // "r": "-0.0029",
630
+ // "up": "0.0478",
631
+ // "pr": "0.0492",
632
+ // "pv": "4.8592",
633
+ // "v": "5.00",
634
+ // "mt": "CROSS",
635
+ // "mm": "0.0367"
636
+ // }
637
+ //
638
+ if (this.positions === undefined) {
639
+ this.positions = new ArrayCacheBySymbolBySide();
640
+ }
641
+ const positions = this.positions;
642
+ const parsed = this.parseWsPosition(message);
643
+ positions.append(parsed);
644
+ const messageHash = 'positions';
645
+ client.resolve(parsed, messageHash);
646
+ const symbol = parsed['symbol'];
647
+ client.resolve(parsed, messageHash + ':' + symbol);
648
+ }
649
+ parseWsPosition(position, market = undefined) {
650
+ const marketId = this.safeString(position, 's');
651
+ market = this.safeMarket(marketId);
652
+ const timestamp = this.safeInteger(position, 'E');
653
+ return this.safePosition({
654
+ 'symbol': market['symbol'],
655
+ 'id': undefined,
656
+ 'timestamp': timestamp,
657
+ 'datetime': this.iso8601(timestamp),
658
+ 'contracts': this.safeNumber(position, 'P'),
659
+ 'contractSize': undefined,
660
+ 'side': this.safeStringLower(position, 'S'),
661
+ 'notional': this.safeNumber(position, 'pv'),
662
+ 'leverage': this.safeInteger(position, 'v'),
663
+ 'unrealizedPnl': this.safeNumber(position, 'up'),
664
+ 'realizedPnl': this.safeNumber(position, 'r'),
665
+ 'collateral': undefined,
666
+ 'entryPrice': this.safeNumber(position, 'p'),
667
+ 'markPrice': undefined,
668
+ 'liquidationPrice': this.safeNumber(position, 'f'),
669
+ 'marginMode': this.safeStringLower(position, 'mt'),
670
+ 'hedged': true,
671
+ 'maintenanceMargin': this.safeNumber(position, 'mm'),
672
+ 'maintenanceMarginPercentage': undefined,
673
+ 'initialMargin': this.safeNumber(position, 'm'),
674
+ 'initialMarginPercentage': undefined,
675
+ 'marginRatio': undefined,
676
+ 'lastUpdateTimestamp': undefined,
677
+ 'lastPrice': undefined,
678
+ 'stopLossPrice': undefined,
679
+ 'takeProfitPrice': undefined,
680
+ 'percentage': undefined,
681
+ 'info': position,
682
+ });
683
+ }
684
+ async watchBalance(params = {}) {
685
+ /**
686
+ * @method
687
+ * @name bitmart#watchBalance
688
+ * @description watch balance and get the amount of funds available for trading or funds locked in orders
689
+ * @see https://hashkeyglobal-apidoc.readme.io/reference/websocket-api#private-stream
690
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
691
+ * @param {string} [params.type] 'spot' or 'swap' - the type of the market to watch balance for (default 'spot')
692
+ * @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure}
693
+ */
694
+ const listenKey = await this.authenticate();
695
+ await this.loadMarkets();
696
+ let type = 'spot';
697
+ [type, params] = this.handleMarketTypeAndParams('watchBalance', undefined, params, type);
698
+ const messageHash = 'balance:' + type;
699
+ const url = this.getPrivateUrl(listenKey);
700
+ const client = this.client(url);
701
+ this.setBalanceCache(client, type, messageHash);
702
+ let fetchBalanceSnapshot = undefined;
703
+ let awaitBalanceSnapshot = undefined;
704
+ [fetchBalanceSnapshot, params] = this.handleOptionAndParams(this.options, 'watchBalance', 'fetchBalanceSnapshot', true);
705
+ [awaitBalanceSnapshot, params] = this.handleOptionAndParams(this.options, 'watchBalance', 'awaitBalanceSnapshot', false);
706
+ if (fetchBalanceSnapshot && awaitBalanceSnapshot) {
707
+ await client.future(type + ':fetchBalanceSnapshot');
708
+ }
709
+ return await this.watch(url, messageHash, undefined, messageHash);
710
+ }
711
+ setBalanceCache(client, type, subscribeHash) {
712
+ if (subscribeHash in client.subscriptions) {
713
+ return;
714
+ }
715
+ const options = this.safeDict(this.options, 'watchBalance');
716
+ const snapshot = this.safeBool(options, 'fetchBalanceSnapshot', true);
717
+ if (snapshot) {
718
+ const messageHash = type + ':' + 'fetchBalanceSnapshot';
719
+ if (!(messageHash in client.futures)) {
720
+ client.future(messageHash);
721
+ this.spawn(this.loadBalanceSnapshot, client, messageHash, type);
722
+ }
723
+ }
724
+ this.balance[type] = {};
725
+ // without this comment, transpilation breaks for some reason...
726
+ }
727
+ async loadBalanceSnapshot(client, messageHash, type) {
728
+ const response = await this.fetchBalance({ 'type': type });
729
+ this.balance[type] = this.extend(response, this.safeValue(this.balance, type, {}));
730
+ // don't remove the future from the .futures cache
731
+ const future = client.futures[messageHash];
732
+ future.resolve();
733
+ client.resolve(this.balance[type], 'balance:' + type);
734
+ }
735
+ handleBalance(client, message) {
736
+ //
737
+ // {
738
+ // "e": "outboundContractAccountInfo", // event type
739
+ // // outboundContractAccountInfo
740
+ // "E": "1714717314118", // event time
741
+ // "T": true, // can trade
742
+ // "W": true, // can withdraw
743
+ // "D": true, // can deposit
744
+ // "B": [ // balances changed
745
+ // {
746
+ // "a": "USDT", // asset
747
+ // "f": "474960.65", // free amount
748
+ // "l": "24835.178056020383226869", // locked amount
749
+ // "r": "" // to be released
750
+ // }
751
+ // ]
752
+ // }
753
+ //
754
+ const event = this.safeString(message, 'e');
755
+ const data = this.safeList(message, 'B', []);
756
+ const balanceUpdate = this.safeDict(data, 0);
757
+ const isSpot = event === 'outboundAccountInfo';
758
+ const type = isSpot ? 'spot' : 'swap';
759
+ if (!(type in this.balance)) {
760
+ this.balance[type] = {};
761
+ }
762
+ this.balance[type]['info'] = message;
763
+ const currencyId = this.safeString(balanceUpdate, 'a');
764
+ const code = this.safeCurrencyCode(currencyId);
765
+ const account = this.account();
766
+ account['free'] = this.safeString(balanceUpdate, 'f');
767
+ account['used'] = this.safeString(balanceUpdate, 'l');
768
+ this.balance[type][code] = account;
769
+ this.balance[type] = this.safeBalance(this.balance[type]);
770
+ const messageHash = 'balance:' + type;
771
+ client.resolve(this.balance[type], messageHash);
772
+ }
773
+ async authenticate(params = {}) {
774
+ let listenKey = this.safeString(this.options, 'listenKey');
775
+ if (listenKey !== undefined) {
776
+ return listenKey;
777
+ }
778
+ const response = await this.privatePostApiV1UserDataStream(params);
779
+ //
780
+ // {
781
+ // "listenKey": "atbNEcWnBqnmgkfmYQeTuxKTpTStlZzgoPLJsZhzAOZTbAlxbHqGNWiYaUQzMtDz"
782
+ // }
783
+ //
784
+ listenKey = this.safeString(response, 'listenKey');
785
+ this.options['listenKey'] = listenKey;
786
+ const listenKeyRefreshRate = this.safeInteger(this.options, 'listenKeyRefreshRate', 3600000);
787
+ this.delay(listenKeyRefreshRate, this.keepAliveListenKey, listenKey, params);
788
+ return listenKey;
789
+ }
790
+ async keepAliveListenKey(listenKey, params = {}) {
791
+ if (listenKey === undefined) {
792
+ return;
793
+ }
794
+ const request = {
795
+ 'listenKey': listenKey,
796
+ };
797
+ try {
798
+ await this.privatePutApiV1UserDataStream(this.extend(request, params));
799
+ const listenKeyRefreshRate = this.safeInteger(this.options, 'listenKeyRefreshRate', 1200000);
800
+ this.delay(listenKeyRefreshRate, this.keepAliveListenKey, listenKey, params);
801
+ }
802
+ catch (error) {
803
+ const url = this.getPrivateUrl(listenKey);
804
+ const client = this.client(url);
805
+ this.options['listenKey'] = undefined;
806
+ client.reject(error);
807
+ delete this.clients[url];
808
+ }
809
+ }
810
+ handleMessage(client, message) {
811
+ if (Array.isArray(message)) {
812
+ message = this.safeDict(message, 0, {});
813
+ }
814
+ const topic = this.safeString2(message, 'topic', 'e');
815
+ if (topic === 'kline') {
816
+ this.handleOHLCV(client, message);
817
+ }
818
+ else if (topic === 'realtimes') {
819
+ this.handleTicker(client, message);
820
+ }
821
+ else if (topic === 'trade') {
822
+ this.handleTrades(client, message);
823
+ }
824
+ else if (topic === 'depth') {
825
+ this.handleOrderBook(client, message);
826
+ }
827
+ else if ((topic === 'contractExecutionReport') || (topic === 'executionReport')) {
828
+ this.handleOrder(client, message);
829
+ }
830
+ else if (topic === 'ticketInfo') {
831
+ this.handleMyTrade(client, message);
832
+ }
833
+ else if (topic === 'outboundContractPositionInfo') {
834
+ this.handlePosition(client, message);
835
+ }
836
+ else if ((topic === 'outboundAccountInfo') || (topic === 'outboundContractAccountInfo')) {
837
+ this.handleBalance(client, message);
838
+ }
839
+ }
840
+ }