ccxt 4.5.22 → 4.5.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +6 -5
  2. package/dist/ccxt.browser.min.js +2 -2
  3. package/dist/cjs/ccxt.js +6 -1
  4. package/dist/cjs/src/abstract/bullish.js +11 -0
  5. package/dist/cjs/src/base/Exchange.js +3 -2
  6. package/dist/cjs/src/base/ws/WsClient.js +15 -0
  7. package/dist/cjs/src/binance.js +159 -36
  8. package/dist/cjs/src/bingx.js +2 -1
  9. package/dist/cjs/src/bitmart.js +1 -0
  10. package/dist/cjs/src/bullish.js +2919 -0
  11. package/dist/cjs/src/bybit.js +34 -37
  12. package/dist/cjs/src/gate.js +2 -2
  13. package/dist/cjs/src/htx.js +4 -1
  14. package/dist/cjs/src/hyperliquid.js +115 -12
  15. package/dist/cjs/src/kucoin.js +22 -3
  16. package/dist/cjs/src/mexc.js +7 -0
  17. package/dist/cjs/src/okx.js +117 -63
  18. package/dist/cjs/src/paradex.js +78 -3
  19. package/dist/cjs/src/pro/binance.js +131 -29
  20. package/dist/cjs/src/pro/bullish.js +781 -0
  21. package/dist/cjs/src/pro/coinbase.js +2 -2
  22. package/dist/cjs/src/pro/hyperliquid.js +75 -15
  23. package/dist/cjs/src/pro/upbit.js +28 -82
  24. package/js/ccxt.d.ts +8 -2
  25. package/js/ccxt.js +6 -2
  26. package/js/src/abstract/binance.d.ts +1 -0
  27. package/js/src/abstract/binancecoinm.d.ts +1 -0
  28. package/js/src/abstract/binanceus.d.ts +1 -0
  29. package/js/src/abstract/binanceusdm.d.ts +1 -0
  30. package/js/src/abstract/bingx.d.ts +1 -0
  31. package/js/src/abstract/bullish.d.ts +65 -0
  32. package/js/src/abstract/bullish.js +5 -0
  33. package/js/src/abstract/kucoin.d.ts +15 -0
  34. package/js/src/abstract/kucoinfutures.d.ts +15 -0
  35. package/js/src/abstract/mexc.d.ts +7 -0
  36. package/js/src/abstract/myokx.d.ts +90 -39
  37. package/js/src/abstract/okx.d.ts +90 -39
  38. package/js/src/abstract/okxus.d.ts +90 -39
  39. package/js/src/base/Exchange.d.ts +1 -1
  40. package/js/src/base/Exchange.js +3 -2
  41. package/js/src/base/ws/Client.d.ts +1 -0
  42. package/js/src/base/ws/WsClient.js +15 -0
  43. package/js/src/binance.d.ts +14 -5
  44. package/js/src/binance.js +159 -36
  45. package/js/src/bingx.js +2 -1
  46. package/js/src/bitmart.js +1 -0
  47. package/js/src/bullish.d.ts +446 -0
  48. package/js/src/bullish.js +2912 -0
  49. package/js/src/bybit.js +34 -37
  50. package/js/src/gate.js +2 -2
  51. package/js/src/htx.js +4 -1
  52. package/js/src/hyperliquid.d.ts +24 -0
  53. package/js/src/hyperliquid.js +115 -12
  54. package/js/src/kucoin.js +22 -3
  55. package/js/src/mexc.js +7 -0
  56. package/js/src/okx.js +117 -63
  57. package/js/src/paradex.d.ts +15 -1
  58. package/js/src/paradex.js +78 -3
  59. package/js/src/pro/binance.d.ts +7 -0
  60. package/js/src/pro/binance.js +131 -29
  61. package/js/src/pro/bullish.d.ts +108 -0
  62. package/js/src/pro/bullish.js +774 -0
  63. package/js/src/pro/coinbase.js +2 -2
  64. package/js/src/pro/hyperliquid.d.ts +13 -1
  65. package/js/src/pro/hyperliquid.js +75 -15
  66. package/js/src/pro/upbit.d.ts +0 -1
  67. package/js/src/pro/upbit.js +28 -82
  68. package/package.json +2 -2
@@ -0,0 +1,781 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var bullish$1 = require('../bullish.js');
6
+ var Cache = require('../base/ws/Cache.js');
7
+ var errors = require('../base/errors.js');
8
+
9
+ // ---------------------------------------------------------------------------
10
+ // ---------------------------------------------------------------------------
11
+ class bullish extends bullish$1["default"] {
12
+ describe() {
13
+ return this.deepExtend(super.describe(), {
14
+ 'has': {
15
+ 'ws': true,
16
+ 'watchTicker': true,
17
+ 'watchTickers': false,
18
+ 'watchOrderBook': true,
19
+ 'watchOrders': true,
20
+ 'watchTrades': true,
21
+ 'watchPositions': true,
22
+ 'watchMyTrades': true,
23
+ 'watchBalance': true,
24
+ 'watchOHLCV': false,
25
+ },
26
+ 'urls': {
27
+ 'api': {
28
+ 'ws': {
29
+ 'public': 'wss://api.exchange.bullish.com',
30
+ 'private': 'wss://api.exchange.bullish.com/trading-api/v1/private-data',
31
+ },
32
+ },
33
+ 'test': {
34
+ 'ws': {
35
+ 'public': 'wss://api.simnext.bullish-test.com',
36
+ 'private': 'wss://api.simnext.bullish-test.com/trading-api/v1/private-data',
37
+ },
38
+ },
39
+ },
40
+ 'options': {
41
+ 'ws': {
42
+ 'cookies': {},
43
+ },
44
+ },
45
+ 'streaming': {
46
+ 'ping': this.ping,
47
+ 'keepAlive': 99000, // disconnect after 100 seconds of inactivity
48
+ },
49
+ });
50
+ }
51
+ requestId() {
52
+ const requestId = this.sum(this.safeInteger(this.options, 'requestId', 0), 1);
53
+ this.options['requestId'] = requestId;
54
+ return requestId;
55
+ }
56
+ ping(client) {
57
+ // bullish does not support built-in ws protocol-level ping-pong
58
+ // https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#overview--keep-websocket-open
59
+ const id = this.requestId().toString();
60
+ return {
61
+ 'jsonrpc': '2.0',
62
+ 'type': 'command',
63
+ 'method': 'keepalivePing',
64
+ 'params': {},
65
+ 'id': id,
66
+ };
67
+ }
68
+ handlePong(client, message) {
69
+ //
70
+ // {
71
+ // "id": "7",
72
+ // "jsonrpc": "2.0",
73
+ // "result": {
74
+ // "responseCodeName": "OK",
75
+ // "responseCode": "200",
76
+ // "message": "Keep alive pong"
77
+ // }
78
+ // }
79
+ //
80
+ client.lastPong = this.milliseconds();
81
+ return message; // current line is for transpilation compatibility
82
+ }
83
+ async watchPublic(url, messageHash, request = {}, params = {}) {
84
+ const id = this.requestId().toString();
85
+ const message = {
86
+ 'jsonrpc': '2.0',
87
+ 'type': 'command',
88
+ 'method': 'subscribe',
89
+ 'params': request,
90
+ 'id': id,
91
+ };
92
+ const fullUrl = this.urls['api']['ws']['public'] + url;
93
+ return await this.watch(fullUrl, messageHash, this.deepExtend(message, params), messageHash);
94
+ }
95
+ async watchPrivate(messageHash, subscribeHash, request = {}, params = {}) {
96
+ const url = this.urls['api']['ws']['private'];
97
+ const token = await this.handleToken();
98
+ const cookies = {
99
+ 'JWT_COOKIE': token,
100
+ };
101
+ this.options['ws']['cookies'] = cookies;
102
+ const id = this.requestId().toString();
103
+ const message = {
104
+ 'jsonrpc': '2.0',
105
+ 'type': 'command',
106
+ 'method': 'subscribe',
107
+ 'params': request,
108
+ 'id': id,
109
+ };
110
+ const result = await this.watch(url, messageHash, this.deepExtend(message, params), subscribeHash);
111
+ return result;
112
+ }
113
+ /**
114
+ * @method
115
+ * @name bullish#watchTrades
116
+ * @description get the list of most recent trades for a particular symbol
117
+ * @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#overview--unified-anonymous-trades-websocket-unauthenticated
118
+ * @param {string} symbol unified symbol of the market to fetch trades for
119
+ * @param {int} [since] timestamp in ms of the earliest trade to fetch
120
+ * @param {int} [limit] the maximum amount of trades to fetch
121
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
122
+ * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades}
123
+ */
124
+ async watchTrades(symbol, since = undefined, limit = undefined, params = {}) {
125
+ await this.loadMarkets();
126
+ const market = this.market(symbol);
127
+ const messageHash = 'trades::' + market['symbol'];
128
+ const url = '/trading-api/v1/market-data/trades';
129
+ const request = {
130
+ 'topic': 'anonymousTrades',
131
+ 'symbol': market['id'],
132
+ };
133
+ const trades = await this.watchPublic(url, messageHash, request, params);
134
+ if (this.newUpdates) {
135
+ limit = trades.getLimit(symbol, limit);
136
+ }
137
+ return this.filterBySinceLimit(trades, since, limit, 'timestamp', true);
138
+ }
139
+ handleTrades(client, message) {
140
+ //
141
+ // {
142
+ // "type": "snapshot",
143
+ // "dataType": "V1TAAnonymousTradeUpdate",
144
+ // "data": {
145
+ // "trades": [
146
+ // {
147
+ // "tradeId": "100086000000609304",
148
+ // "isTaker": true,
149
+ // "price": "104889.2063",
150
+ // "createdAtTimestamp": "1749124509118",
151
+ // "quantity": "0.01000000",
152
+ // "publishedAtTimestamp": "1749124531466",
153
+ // "side": "BUY",
154
+ // "createdAtDatetime": "2025-06-05T11:55:09.118Z",
155
+ // "symbol": "BTCUSDC"
156
+ // }
157
+ // ],
158
+ // "createdAtTimestamp": "1749124509118",
159
+ // "publishedAtTimestamp": "1749124531466",
160
+ // "symbol": "BTCUSDC"
161
+ // }
162
+ // }
163
+ //
164
+ const data = this.safeDict(message, 'data', {});
165
+ const marketId = this.safeString(data, 'symbol');
166
+ const symbol = this.safeSymbol(marketId);
167
+ const market = this.market(symbol);
168
+ const rawTrades = this.safeList(data, 'trades', []);
169
+ const trades = this.parseTrades(rawTrades, market);
170
+ if (!(symbol in this.trades)) {
171
+ const limit = this.safeInteger(this.options, 'tradesLimit', 1000);
172
+ const tradesArrayCache = new Cache.ArrayCache(limit);
173
+ this.trades[symbol] = tradesArrayCache;
174
+ }
175
+ const tradesArray = this.trades[symbol];
176
+ for (let i = 0; i < trades.length; i++) {
177
+ tradesArray.append(trades[i]);
178
+ }
179
+ this.trades[symbol] = tradesArray;
180
+ const messageHash = 'trades::' + market['symbol'];
181
+ client.resolve(tradesArray, messageHash);
182
+ }
183
+ /**
184
+ * @method
185
+ * @name bullish#watchTicker
186
+ * @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
187
+ * @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#overview--anonymous-market-data-price-tick-unauthenticated
188
+ * @param {string} symbol unified symbol of the market to fetch the ticker for
189
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
190
+ * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
191
+ */
192
+ async watchTicker(symbol, params = {}) {
193
+ await this.loadMarkets();
194
+ const market = this.market(symbol);
195
+ symbol = market['symbol'];
196
+ const url = this.urls['api']['ws']['public'] + '/trading-api/v1/market-data/tick/' + market['id'];
197
+ const messageHash = 'ticker::' + symbol;
198
+ return await this.watch(url, messageHash, params, messageHash); // no need to send a subscribe message, the server sends a ticker update on connect
199
+ }
200
+ handleTicker(client, message) {
201
+ //
202
+ // {
203
+ // "type": "update",
204
+ // "dataType": "V1TATickerResponse",
205
+ // "data": {
206
+ // "askVolume": "0.00100822",
207
+ // "average": "104423.1806",
208
+ // "baseVolume": "472.83799258",
209
+ // "bestAsk": "104324.6000",
210
+ // "bestBid": "104324.5000",
211
+ // "bidVolume": "0.00020146",
212
+ // "change": "-198.4864",
213
+ // "close": "104323.9374",
214
+ // "createdAtTimestamp": "1749132838951",
215
+ // "publishedAtTimestamp": "1749132838955",
216
+ // "high": "105966.6577",
217
+ // "last": "104323.9374",
218
+ // "lastTradeDatetime": "2025-06-05T14:13:56.111Z",
219
+ // "lastTradeSize": "0.02396100",
220
+ // "low": "104246.6662",
221
+ // "open": "104522.4238",
222
+ // "percentage": "-0.19",
223
+ // "quoteVolume": "49662592.6712",
224
+ // "symbol": "BTC-USDC-PERP",
225
+ // "type": "ticker",
226
+ // "vwap": "105030.6996",
227
+ // "currentPrice": "104324.7747",
228
+ // "ammData": [
229
+ // {
230
+ // "feeTierId": "1",
231
+ // "currentPrice": "104324.7747",
232
+ // "baseReservesQuantity": "8.27911366",
233
+ // "quoteReservesQuantity": "1067283.0234",
234
+ // "bidSpreadFee": "0.00000000",
235
+ // "askSpreadFee": "0.00000000"
236
+ // }
237
+ // ],
238
+ // "createdAtDatetime": "2025-06-05T14:13:58.951Z",
239
+ // "markPrice": "104289.6884",
240
+ // "fundingRate": "-0.000192",
241
+ // "openInterest": "92.24146651"
242
+ // }
243
+ // }
244
+ //
245
+ const updateType = this.safeString(message, 'type', '');
246
+ const data = this.safeDict(message, 'data', {});
247
+ const marketId = this.safeString(data, 'symbol');
248
+ const market = this.safeMarket(marketId);
249
+ const symbol = market['symbol'];
250
+ let parsed = undefined;
251
+ if ((updateType === 'snapshot')) {
252
+ parsed = this.parseTicker(data, market);
253
+ }
254
+ else if (updateType === 'update') {
255
+ const ticker = this.safeDict(this.tickers, symbol, {});
256
+ const rawTicker = this.safeDict(ticker, 'info', {});
257
+ const merged = this.extend(rawTicker, data);
258
+ parsed = this.parseTicker(merged, market);
259
+ }
260
+ this.tickers[symbol] = parsed;
261
+ const messageHash = 'ticker::' + symbol;
262
+ client.resolve(this.tickers[symbol], messageHash);
263
+ }
264
+ /**
265
+ * @method
266
+ * @name bullish#watchOrderBook
267
+ * @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
268
+ * @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#overview--multi-orderbook-websocket-unauthenticated
269
+ * @param {string} symbol unified symbol of the market to fetch the order book for
270
+ * @param {int} [limit] the maximum amount of order book entries to return
271
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
272
+ * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
273
+ */
274
+ async watchOrderBook(symbol, limit = undefined, params = {}) {
275
+ await this.loadMarkets();
276
+ const market = this.market(symbol);
277
+ const url = '/trading-api/v1/market-data/orderbook';
278
+ const messageHash = 'orderbook::' + market['symbol'];
279
+ const request = {
280
+ 'topic': 'l2Orderbook',
281
+ 'symbol': market['id'],
282
+ };
283
+ const orderbook = await this.watchPublic(url, messageHash, request, params);
284
+ return orderbook.limit();
285
+ }
286
+ handleOrderBook(client, message) {
287
+ //
288
+ // {
289
+ // "type": "snapshot",
290
+ // "dataType": "V1TALevel2",
291
+ // "data": {
292
+ // "timestamp": "1749372632028",
293
+ // "bids": [
294
+ // "105523.3000",
295
+ // "0.00046045",
296
+ // ],
297
+ // "asks": [
298
+ // "105523.4000",
299
+ // "0.00117112",
300
+ // ],
301
+ // "publishedAtTimestamp": "1749372632073",
302
+ // "datetime": "2025-06-08T08:50:32.028Z",
303
+ // "sequenceNumberRange": [ 1967862061, 1967862062 ],
304
+ // "symbol": "BTCUSDC"
305
+ // }
306
+ // }
307
+ //
308
+ // current channel is 'l2Orderbook' which returns only snapshots
309
+ const data = this.safeDict(message, 'data', {});
310
+ const marketId = this.safeString(data, 'symbol');
311
+ const symbol = this.safeSymbol(marketId);
312
+ const messageHash = 'orderbook::' + symbol;
313
+ const timestamp = this.safeInteger(data, 'timestamp');
314
+ if (!(symbol in this.orderbooks)) {
315
+ this.orderbooks[symbol] = this.orderBook();
316
+ }
317
+ const orderbook = this.orderbooks[symbol];
318
+ const bids = this.separateBidsOrAsks(this.safeList(data, 'bids', []));
319
+ const asks = this.separateBidsOrAsks(this.safeList(data, 'asks', []));
320
+ const snapshot = {
321
+ 'bids': bids,
322
+ 'asks': asks,
323
+ };
324
+ const parsed = this.parseOrderBook(snapshot, symbol, timestamp);
325
+ const sequenceNumberRange = this.safeList(data, 'sequenceNumberRange', []);
326
+ if (sequenceNumberRange.length > 0) {
327
+ const lastIndex = sequenceNumberRange.length - 1;
328
+ parsed['nonce'] = this.safeInteger(sequenceNumberRange, lastIndex);
329
+ }
330
+ orderbook.reset(parsed);
331
+ this.orderbooks[symbol] = orderbook;
332
+ client.resolve(orderbook, messageHash);
333
+ }
334
+ separateBidsOrAsks(entry) {
335
+ const result = [];
336
+ // 300 = '54885.0000000'
337
+ // 301 = '0.06141566'
338
+ // 302 ='53714.0000000'
339
+ for (let i = 0; i < entry.length; i++) {
340
+ if (i % 2 !== 0) {
341
+ continue;
342
+ }
343
+ const price = this.safeString(entry, i);
344
+ const amount = this.safeString(entry, i + 1);
345
+ result.push([price, amount]);
346
+ }
347
+ return result;
348
+ }
349
+ /**
350
+ * @method
351
+ * @name bullish#watchOrders
352
+ * @description watches information on multiple orders made by the user
353
+ * @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#overview--private-data-websocket-authenticated
354
+ * @param {string} symbol unified market symbol of the market orders were made in
355
+ * @param {int} [since] the earliest time in ms to fetch orders for
356
+ * @param {int} [limit] the maximum number of order structures to retrieve
357
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
358
+ * @param {string} [params.tradingAccountId] the trading account id to fetch entries for
359
+ * @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
360
+ */
361
+ async watchOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
362
+ await this.loadMarkets();
363
+ const subscribeHash = 'orders';
364
+ let messageHash = subscribeHash;
365
+ if (symbol !== undefined) {
366
+ symbol = this.symbol(symbol);
367
+ messageHash = messageHash + '::' + symbol;
368
+ }
369
+ const request = {
370
+ 'topic': 'orders',
371
+ };
372
+ const tradingAccountId = this.safeString(params, 'tradingAccountId');
373
+ if (tradingAccountId !== undefined) {
374
+ request['tradingAccountId'] = tradingAccountId;
375
+ params = this.omit(params, 'tradingAccountId');
376
+ }
377
+ const orders = await this.watchPrivate(messageHash, subscribeHash, request, params);
378
+ if (this.newUpdates) {
379
+ limit = orders.getLimit(symbol, limit);
380
+ }
381
+ return this.filterBySymbolSinceLimit(orders, symbol, since, limit, true);
382
+ }
383
+ handleOrders(client, message) {
384
+ // snapshot
385
+ // {
386
+ // "type": "snapshot",
387
+ // "tradingAccountId": "111309424211255",
388
+ // "dataType": "V1TAOrder",
389
+ // "data": [ ... ] // could be an empty list or a list of orders
390
+ // }
391
+ //
392
+ // update
393
+ // {
394
+ // "type": "update",
395
+ // "tradingAccountId": "111309424211255",
396
+ // "dataType": "V1TAOrder",
397
+ // "data": {
398
+ // "status": "OPEN",
399
+ // "createdAtTimestamp": "1751893427971",
400
+ // "quoteFee": "0.000000",
401
+ // "stopPrice": null,
402
+ // "quantityFilled": "0.00000000",
403
+ // "handle": null,
404
+ // "clientOrderId": null,
405
+ // "quantity": "0.10000000",
406
+ // "margin": false,
407
+ // "side": "BUY",
408
+ // "createdAtDatetime": "2025-07-07T13:03:47.971Z",
409
+ // "isLiquidation": false,
410
+ // "borrowedQuoteQuantity": null,
411
+ // "borrowedBaseQuantity": null,
412
+ // "timeInForce": "GTC",
413
+ // "borrowedQuantity": null,
414
+ // "baseFee": "0.000000",
415
+ // "quoteAmount": "0.0000000",
416
+ // "price": "0.0000000",
417
+ // "statusReason": "Order accepted",
418
+ // "type": "MKT",
419
+ // "statusReasonCode": 6014,
420
+ // "allowBorrow": false,
421
+ // "orderId": "862317981870850049",
422
+ // "publishedAtTimestamp": "1751893427975",
423
+ // "symbol": "ETHUSDT",
424
+ // "averageFillPrice": null
425
+ // }
426
+ // }
427
+ //
428
+ const type = this.safeString(message, 'type');
429
+ let rawOrders = [];
430
+ if (type === 'update') {
431
+ const data = this.safeDict(message, 'data', {});
432
+ rawOrders.push(data); // update is a single order
433
+ }
434
+ else {
435
+ rawOrders = this.safeList(message, 'data', []); // snapshot is a list of orders
436
+ }
437
+ if (rawOrders.length > 0) {
438
+ if (this.orders === undefined) {
439
+ const limit = this.safeInteger(this.options, 'ordersLimit', 1000);
440
+ this.orders = new Cache.ArrayCacheBySymbolById(limit);
441
+ }
442
+ const orders = this.orders;
443
+ const symbols = {};
444
+ for (let i = 0; i < rawOrders.length; i++) {
445
+ const rawOrder = rawOrders[i];
446
+ const parsedOrder = this.parseOrder(rawOrder);
447
+ orders.append(parsedOrder);
448
+ const symbol = this.safeString(parsedOrder, 'symbol');
449
+ symbols[symbol] = true;
450
+ }
451
+ const messageHash = 'orders';
452
+ client.resolve(orders, messageHash);
453
+ const keys = Object.keys(symbols);
454
+ for (let i = 0; i < keys.length; i++) {
455
+ const hashSymbol = keys[i];
456
+ const symbolMessageHash = messageHash + '::' + hashSymbol;
457
+ client.resolve(this.orders, symbolMessageHash);
458
+ }
459
+ }
460
+ }
461
+ /**
462
+ * @method
463
+ * @name bullish#watchMyTrades
464
+ * @description watches information on multiple trades made by the user
465
+ * @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#overview--private-data-websocket-authenticated
466
+ * @param {string} symbol unified market symbol of the market trades were made in
467
+ * @param {int} [since] the earliest time in ms to fetch trades for
468
+ * @param {int} [limit] the maximum number of trade structures to retrieve
469
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
470
+ * @param {string} [params.tradingAccountId] the trading account id to fetch entries for
471
+ * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure}
472
+ */
473
+ async watchMyTrades(symbol = undefined, since = undefined, limit = undefined, params = {}) {
474
+ await this.loadMarkets();
475
+ const subscribeHash = 'myTrades';
476
+ let messageHash = subscribeHash;
477
+ if (symbol !== undefined) {
478
+ symbol = this.symbol(symbol);
479
+ messageHash += '::' + symbol;
480
+ }
481
+ const request = {
482
+ 'topic': 'trades',
483
+ };
484
+ const tradingAccountId = this.safeString(params, 'tradingAccountId');
485
+ if (tradingAccountId !== undefined) {
486
+ request['tradingAccountId'] = tradingAccountId;
487
+ params = this.omit(params, 'tradingAccountId');
488
+ }
489
+ const trades = await this.watchPrivate(messageHash, subscribeHash, request, params);
490
+ if (this.newUpdates) {
491
+ limit = trades.getLimit(symbol, limit);
492
+ }
493
+ return this.filterBySinceLimit(trades, since, limit, 'timestamp', true);
494
+ }
495
+ handleMyTrades(client, message) {
496
+ //
497
+ // snapshot
498
+ // {
499
+ // "type": "snapshot",
500
+ // "tradingAccountId": "111309424211255",
501
+ // "dataType": "V1TATrade",
502
+ // "data": [ ... ] // could be an empty list or a list of trades
503
+ // }
504
+ //
505
+ // update
506
+ // {
507
+ // "type": "update",
508
+ // "tradingAccountId": "111309424211255",
509
+ // "dataType": "V1TATrade",
510
+ // "data": {
511
+ // "clientOtcTradeId": null,
512
+ // "tradeId": "100203000003940164",
513
+ // "baseFee": "0.00000000",
514
+ // "isTaker": true,
515
+ // "quoteAmount": "253.6012195",
516
+ // "price": "2536.0121950",
517
+ // "createdAtTimestamp": "1751914859840",
518
+ // "quoteFee": "0.0000000",
519
+ // "tradeRebateAmount": null,
520
+ // "tradeRebateAssetSymbol": null,
521
+ // "handle": null,
522
+ // "otcTradeId": null,
523
+ // "otcMatchId": null,
524
+ // "orderId": "862407873644725249",
525
+ // "quantity": "0.10000000",
526
+ // "publishedAtTimestamp": "1751914859843",
527
+ // "side": "SELL",
528
+ // "createdAtDatetime": "2025-07-07T19:00:59.840Z",
529
+ // "symbol": "ETHUSDT"
530
+ // }
531
+ // }
532
+ //
533
+ const type = this.safeString(message, 'type');
534
+ let rawTrades = [];
535
+ if (type === 'update') {
536
+ const data = this.safeDict(message, 'data', {});
537
+ rawTrades.push(data); // update is a single trade
538
+ }
539
+ else {
540
+ rawTrades = this.safeList(message, 'data', []); // snapshot is a list of trades
541
+ }
542
+ if (rawTrades.length > 0) {
543
+ if (this.myTrades === undefined) {
544
+ const limit = this.safeInteger(this.options, 'tradesLimit', 1000);
545
+ this.myTrades = new Cache.ArrayCacheBySymbolById(limit);
546
+ }
547
+ const trades = this.myTrades;
548
+ const symbols = {};
549
+ for (let i = 0; i < rawTrades.length; i++) {
550
+ const rawTrade = rawTrades[i];
551
+ const parsedTrade = this.parseTrade(rawTrade);
552
+ trades.append(parsedTrade);
553
+ const symbol = this.safeString(parsedTrade, 'symbol');
554
+ symbols[symbol] = true;
555
+ }
556
+ const messageHash = 'myTrades';
557
+ client.resolve(trades, messageHash);
558
+ const keys = Object.keys(symbols);
559
+ for (let i = 0; i < keys.length; i++) {
560
+ const hashSymbol = keys[i];
561
+ const symbolMessageHash = messageHash + '::' + hashSymbol;
562
+ client.resolve(this.myTrades, symbolMessageHash);
563
+ }
564
+ }
565
+ }
566
+ /**
567
+ * @method
568
+ * @name bullish#watchBalance
569
+ * @description watch balance and get the amount of funds available for trading or funds locked in orders
570
+ * @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#overview--private-data-websocket-authenticated
571
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
572
+ * @param {string} [params.tradingAccountId] the trading account id to fetch entries for
573
+ * @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure}
574
+ */
575
+ async watchBalance(params = {}) {
576
+ await this.loadMarkets();
577
+ const request = {
578
+ 'topic': 'assetAccounts',
579
+ };
580
+ let messageHash = 'balance';
581
+ const tradingAccountId = this.safeString(params, 'tradingAccountId');
582
+ if (tradingAccountId !== undefined) {
583
+ params = this.omit(params, 'tradingAccountId');
584
+ request['tradingAccountId'] = tradingAccountId;
585
+ messageHash += '::' + tradingAccountId;
586
+ }
587
+ return await this.watchPrivate(messageHash, messageHash, request, params);
588
+ }
589
+ handleBalance(client, message) {
590
+ //
591
+ // snapshot
592
+ // {
593
+ // "type": "snapshot",
594
+ // "tradingAccountId": "111309424211255",
595
+ // "dataType": "V1TAAssetAccount",
596
+ // "data": [
597
+ // {
598
+ // "updatedAtTimestamp": "1751989627509",
599
+ // "borrowedQuantity": "0.0000",
600
+ // "tradingAccountId": "111309424211255",
601
+ // "loanedQuantity": "0.0000",
602
+ // "lockedQuantity": "0.0000",
603
+ // "assetId": "5",
604
+ // "assetSymbol": "USDC",
605
+ // "publishedAtTimestamp": "1751989627512",
606
+ // "availableQuantity": "999672939.8767",
607
+ // "updatedAtDatetime": "2025-07-08T15:47:07.509Z"
608
+ // }
609
+ // ]
610
+ // }
611
+ //
612
+ // update
613
+ // {
614
+ // "type": "update",
615
+ // "tradingAccountId": "111309424211255",
616
+ // "dataType": "V1TAAssetAccount",
617
+ // "data": {
618
+ // "updatedAtTimestamp": "1751989627509",
619
+ // "borrowedQuantity": "0.0000",
620
+ // "tradingAccountId": "111309424211255",
621
+ // "loanedQuantity": "0.0000",
622
+ // "lockedQuantity": "0.0000",
623
+ // "assetId": "5",
624
+ // "assetSymbol": "USDC",
625
+ // "publishedAtTimestamp": "1751989627512",
626
+ // "availableQuantity": "999672939.8767",
627
+ // "updatedAtDatetime": "2025-07-08T15:47:07.509Z"
628
+ // }
629
+ // }
630
+ //
631
+ const tradingAccountId = this.safeString(message, 'tradingAccountId');
632
+ if (!(tradingAccountId in this.balance)) {
633
+ this.balance[tradingAccountId] = {};
634
+ }
635
+ const messageType = this.safeString(message, 'type');
636
+ if (messageType === 'snapshot') {
637
+ const data = this.safeList(message, 'data', []);
638
+ this.balance[tradingAccountId] = this.parseBalance(data);
639
+ }
640
+ else {
641
+ const data = this.safeDict(message, 'data', {});
642
+ const assetId = this.safeString(data, 'assetSymbol');
643
+ const account = this.account();
644
+ account['total'] = this.safeString(data, 'availableQuantity');
645
+ account['used'] = this.safeString(data, 'lockedQuantity');
646
+ const code = this.safeCurrencyCode(assetId);
647
+ this.balance[tradingAccountId][code] = account;
648
+ this.balance[tradingAccountId]['info'] = message;
649
+ this.balance[tradingAccountId] = this.safeBalance(this.balance[tradingAccountId]);
650
+ }
651
+ const messageHash = 'balance';
652
+ const tradingAccountIdHash = '::' + tradingAccountId;
653
+ client.resolve(this.balance[tradingAccountId], messageHash);
654
+ client.resolve(this.balance[tradingAccountId], messageHash + tradingAccountIdHash);
655
+ }
656
+ /**
657
+ * @method
658
+ * @name bullish#watchPositions
659
+ * @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#overview--private-data-websocket-authenticated
660
+ * @description watch all open positions
661
+ * @param {string[]} [symbols] list of unified market symbols
662
+ * @param {int} [since] the earliest time in ms to fetch positions for
663
+ * @param {int} [limit] the maximum number of positions to retrieve
664
+ * @param {object} params extra parameters specific to the exchange API endpoint
665
+ * @returns {object[]} a list of [position structure]{@link https://docs.ccxt.com/en/latest/manual.html#position-structure}
666
+ */
667
+ async watchPositions(symbols = undefined, since = undefined, limit = undefined, params = {}) {
668
+ await this.loadMarkets();
669
+ const subscribeHash = 'positions';
670
+ let messageHash = subscribeHash;
671
+ if (!this.isEmpty(symbols)) {
672
+ symbols = this.marketSymbols(symbols);
673
+ messageHash += '::' + symbols.join(',');
674
+ }
675
+ const request = {
676
+ 'topic': 'derivativesPositionsV2',
677
+ };
678
+ const positions = await this.watchPrivate(messageHash, subscribeHash, request, params);
679
+ if (this.newUpdates) {
680
+ return positions;
681
+ }
682
+ return this.filterBySymbolsSinceLimit(positions, symbols, since, limit, true);
683
+ }
684
+ handlePositions(client, message) {
685
+ // exchange does not return messages for sandbox mode
686
+ // current method is implemented blindly
687
+ // todo: check if this works with not-sandbox mode
688
+ const messageType = this.safeString(message, 'type');
689
+ let rawPositions = [];
690
+ if (messageType === 'update') {
691
+ const data = this.safeDict(message, 'data', {});
692
+ rawPositions.push(data);
693
+ }
694
+ else {
695
+ rawPositions = this.safeList(message, 'data', []);
696
+ }
697
+ if (this.positions === undefined) {
698
+ this.positions = new Cache.ArrayCacheBySymbolBySide();
699
+ }
700
+ const positions = this.positions;
701
+ const newPositions = [];
702
+ for (let i = 0; i < rawPositions.length; i++) {
703
+ const rawPosition = rawPositions[i];
704
+ const position = this.parsePosition(rawPosition);
705
+ positions.append(position);
706
+ newPositions.push(position);
707
+ }
708
+ const messageHashes = this.findMessageHashes(client, 'positions::');
709
+ for (let i = 0; i < messageHashes.length; i++) {
710
+ const messageHash = messageHashes[i];
711
+ const parts = messageHash.split('::');
712
+ const symbolsString = parts[1];
713
+ const symbols = symbolsString.split(',');
714
+ const symbolPositions = this.filterByArray(newPositions, 'symbol', symbols, false);
715
+ if (!this.isEmpty(symbolPositions)) {
716
+ client.resolve(symbolPositions, messageHash);
717
+ }
718
+ }
719
+ client.resolve(positions, 'positions');
720
+ }
721
+ handleErrorMessage(client, message) {
722
+ //
723
+ // {
724
+ // "data": {
725
+ // "errorCode": 401,
726
+ // "errorCodeName": "UNAUTHORIZED",
727
+ // "message": "Unable to authenticate; JWT is missing/invalid or unauthorised to access account"
728
+ // },
729
+ // "dataType": "V1TAErrorResponse",
730
+ // "type": "error"
731
+ // }
732
+ //
733
+ const data = this.safeDict(message, 'data', {});
734
+ const feedback = this.id + ' ' + this.json(data);
735
+ try {
736
+ const errorCode = this.safeString(data, 'errorCode');
737
+ const errorCodeName = this.safeString(data, 'errorCodeName');
738
+ this.throwExactlyMatchedException(this.exceptions['exact'], errorCode, feedback);
739
+ this.throwBroadlyMatchedException(this.exceptions['broad'], errorCodeName, feedback);
740
+ throw new errors.ExchangeError(feedback); // unknown message
741
+ }
742
+ catch (e) {
743
+ client.reject(e);
744
+ }
745
+ }
746
+ handleMessage(client, message) {
747
+ const dataType = this.safeString(message, 'dataType');
748
+ const result = this.safeDict(message, 'result');
749
+ if (result !== undefined) {
750
+ const response = this.safeString(result, 'message');
751
+ if (response === 'Keep alive pong') {
752
+ this.handlePong(client, message);
753
+ }
754
+ }
755
+ else if (dataType !== undefined) {
756
+ if (dataType === 'V1TAAnonymousTradeUpdate') {
757
+ this.handleTrades(client, message);
758
+ }
759
+ if (dataType === 'V1TATickerResponse') {
760
+ this.handleTicker(client, message);
761
+ }
762
+ if (dataType === 'V1TALevel2') {
763
+ this.handleOrderBook(client, message);
764
+ }
765
+ if (dataType === 'V1TAOrder') {
766
+ this.handleOrders(client, message);
767
+ }
768
+ if (dataType === 'V1TATrade') {
769
+ this.handleMyTrades(client, message);
770
+ }
771
+ if (dataType === 'V1TAAssetAccount') {
772
+ this.handleBalance(client, message);
773
+ }
774
+ if (dataType === 'V1TAErrorResponse') {
775
+ this.handleErrorMessage(client, message);
776
+ }
777
+ }
778
+ }
779
+ }
780
+
781
+ exports["default"] = bullish;