ccxt 4.3.85 → 4.3.87

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