ccxt 4.1.45 → 4.1.46

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 (54) hide show
  1. package/README.md +3 -3
  2. package/dist/ccxt.browser.js +1591 -188
  3. package/dist/ccxt.browser.min.js +2 -2
  4. package/dist/cjs/ccxt.js +1 -1
  5. package/dist/cjs/src/base/Exchange.js +11 -3
  6. package/dist/cjs/src/base/ws/Cache.js +50 -0
  7. package/dist/cjs/src/bitvavo.js +6 -5
  8. package/dist/cjs/src/bybit.js +84 -132
  9. package/dist/cjs/src/cryptocom.js +1 -1
  10. package/dist/cjs/src/gate.js +3 -1
  11. package/dist/cjs/src/huobi.js +1 -2
  12. package/dist/cjs/src/okx.js +19 -2
  13. package/dist/cjs/src/pro/binance.js +203 -1
  14. package/dist/cjs/src/pro/bitget.js +181 -0
  15. package/dist/cjs/src/pro/bybit.js +154 -10
  16. package/dist/cjs/src/pro/cryptocom.js +131 -1
  17. package/dist/cjs/src/pro/gate.js +161 -0
  18. package/dist/cjs/src/pro/huobi.js +128 -4
  19. package/dist/cjs/src/pro/krakenfutures.js +129 -0
  20. package/dist/cjs/src/pro/kucoinfutures.js +182 -0
  21. package/dist/cjs/src/pro/okx.js +121 -0
  22. package/js/ccxt.d.ts +1 -1
  23. package/js/ccxt.js +1 -1
  24. package/js/src/base/Exchange.d.ts +4 -1
  25. package/js/src/base/Exchange.js +11 -3
  26. package/js/src/base/ws/Cache.d.ts +5 -1
  27. package/js/src/base/ws/Cache.js +50 -1
  28. package/js/src/bitvavo.js +6 -5
  29. package/js/src/bybit.d.ts +0 -2
  30. package/js/src/bybit.js +84 -132
  31. package/js/src/cryptocom.js +1 -1
  32. package/js/src/gate.js +3 -1
  33. package/js/src/huobi.js +1 -2
  34. package/js/src/okx.js +19 -2
  35. package/js/src/pro/binance.d.ts +6 -0
  36. package/js/src/pro/binance.js +204 -2
  37. package/js/src/pro/bitget.d.ts +3 -0
  38. package/js/src/pro/bitget.js +182 -1
  39. package/js/src/pro/bybit.d.ts +5 -1
  40. package/js/src/pro/bybit.js +156 -12
  41. package/js/src/pro/cryptocom.d.ts +4 -0
  42. package/js/src/pro/cryptocom.js +132 -2
  43. package/js/src/pro/gate.d.ts +5 -0
  44. package/js/src/pro/gate.js +162 -1
  45. package/js/src/pro/huobi.d.ts +2 -0
  46. package/js/src/pro/huobi.js +129 -5
  47. package/js/src/pro/krakenfutures.d.ts +3 -0
  48. package/js/src/pro/krakenfutures.js +129 -0
  49. package/js/src/pro/kucoinfutures.d.ts +5 -0
  50. package/js/src/pro/kucoinfutures.js +182 -0
  51. package/js/src/pro/okx.d.ts +2 -0
  52. package/js/src/pro/okx.js +123 -2
  53. package/package.json +1 -1
  54. package/skip-tests.json +3 -1
@@ -22,8 +22,8 @@ class bybit extends bybit$1 {
22
22
  'watchTicker': true,
23
23
  'watchTickers': true,
24
24
  'watchTrades': true,
25
+ 'watchPositions': true,
25
26
  'watchTradesForSymbols': true,
26
- 'watchPosition': undefined,
27
27
  },
28
28
  'urls': {
29
29
  'api': {
@@ -67,6 +67,10 @@ class bybit extends bybit$1 {
67
67
  'watchTicker': {
68
68
  'name': 'tickers', // 'tickers' for 24hr statistical ticker or 'tickers_lt' for leverage token ticker
69
69
  },
70
+ 'watchPositions': {
71
+ 'fetchPositionsSnapshot': true,
72
+ 'awaitPositionsSnapshot': true, // whether to wait for the positions snapshot before providing updates
73
+ },
70
74
  'spot': {
71
75
  'timeframes': {
72
76
  '1m': '1m',
@@ -180,7 +184,7 @@ class bybit extends bybit$1 {
180
184
  }
181
185
  topic += '.' + market['id'];
182
186
  const topics = [topic];
183
- return await this.watchTopics(url, messageHash, topics, params);
187
+ return await this.watchTopics(url, messageHash, topics, messageHash, params);
184
188
  }
185
189
  async watchTickers(symbols = undefined, params = {}) {
186
190
  /**
@@ -377,7 +381,7 @@ class bybit extends bybit$1 {
377
381
  const timeframeId = this.safeString(this.timeframes, timeframe, timeframe);
378
382
  const topics = ['kline.' + timeframeId + '.' + market['id']];
379
383
  const messageHash = 'kline' + ':' + timeframeId + ':' + symbol;
380
- ohlcv = await this.watchTopics(url, messageHash, topics, params);
384
+ ohlcv = await this.watchTopics(url, messageHash, topics, messageHash, params);
381
385
  if (this.newUpdates) {
382
386
  limit = ohlcv.getLimit(symbol, limit);
383
387
  }
@@ -534,7 +538,7 @@ class bybit extends bybit$1 {
534
538
  }
535
539
  }
536
540
  const topics = ['orderbook.' + limit.toString() + '.' + market['id']];
537
- const orderbook = await this.watchTopics(url, messageHash, topics, params);
541
+ const orderbook = await this.watchTopics(url, messageHash, topics, messageHash, params);
538
542
  return orderbook.limit();
539
543
  }
540
544
  async watchOrderBookForSymbols(symbols, limit = undefined, params = {}) {
@@ -672,7 +676,7 @@ class bybit extends bybit$1 {
672
676
  params = this.cleanParams(params);
673
677
  const messageHash = 'trade:' + symbol;
674
678
  const topic = 'publicTrade.' + market['id'];
675
- const trades = await this.watchTopics(url, messageHash, [topic], params);
679
+ const trades = await this.watchTopics(url, messageHash, [topic], messageHash, params);
676
680
  if (this.newUpdates) {
677
681
  limit = trades.getLimit(symbol, limit);
678
682
  }
@@ -868,7 +872,7 @@ class bybit extends bybit$1 {
868
872
  'usdc': 'user.openapi.perp.trade',
869
873
  };
870
874
  const topic = this.safeValue(topicByMarket, this.getPrivateType(url));
871
- const trades = await this.watchTopics(url, messageHash, [topic], params);
875
+ const trades = await this.watchTopics(url, messageHash, [topic], messageHash, params);
872
876
  if (this.newUpdates) {
873
877
  limit = trades.getLimit(symbol, limit);
874
878
  }
@@ -966,6 +970,145 @@ class bybit extends bybit$1 {
966
970
  const messageHash = 'myTrades';
967
971
  client.resolve(trades, messageHash);
968
972
  }
973
+ async watchPositions(symbols = undefined, since = undefined, limit = undefined, params = {}) {
974
+ /**
975
+ * @method
976
+ * @name bybit#watchPositions
977
+ * @see https://bybit-exchange.github.io/docs/v5/websocket/private/position
978
+ * @description watch all open positions
979
+ * @param {[string]|undefined} symbols list of unified market symbols
980
+ * @param {object} params extra parameters specific to the bybit api endpoint
981
+ * @returns {[object]} a list of [position structure]{@link https://docs.ccxt.com/en/latest/manual.html#position-structure}
982
+ */
983
+ await this.loadMarkets();
984
+ const method = 'watchPositions';
985
+ let messageHash = '';
986
+ if (!this.isEmpty(symbols)) {
987
+ symbols = this.marketSymbols(symbols);
988
+ messageHash = '::' + symbols.join(',');
989
+ }
990
+ const firstSymbol = this.safeString(symbols, 0);
991
+ const url = this.getUrlByMarketType(firstSymbol, true, method, params);
992
+ messageHash = 'positions' + messageHash;
993
+ const client = this.client(url);
994
+ await this.authenticate(url);
995
+ this.setPositionsCache(client, symbols);
996
+ const cache = this.positions;
997
+ const fetchPositionsSnapshot = this.handleOption('watchPositions', 'fetchPositionsSnapshot', true);
998
+ const awaitPositionsSnapshot = this.safeValue('watchPositions', 'awaitPositionsSnapshot', true);
999
+ if (fetchPositionsSnapshot && awaitPositionsSnapshot && cache === undefined) {
1000
+ const snapshot = await client.future('fetchPositionsSnapshot');
1001
+ return this.filterBySymbolsSinceLimit(snapshot, symbols, since, limit, true);
1002
+ }
1003
+ const topics = ['position'];
1004
+ const newPositions = await this.watchTopics(url, messageHash, topics, 'position', params);
1005
+ if (this.newUpdates) {
1006
+ return newPositions;
1007
+ }
1008
+ return this.filterBySymbolsSinceLimit(cache, symbols, since, limit, true);
1009
+ }
1010
+ setPositionsCache(client, symbols = undefined) {
1011
+ if (this.positions !== undefined) {
1012
+ return this.positions;
1013
+ }
1014
+ const fetchPositionsSnapshot = this.handleOption('watchPositions', 'fetchPositionsSnapshot', true);
1015
+ if (fetchPositionsSnapshot) {
1016
+ const messageHash = 'fetchPositionsSnapshot';
1017
+ if (!(messageHash in client.futures)) {
1018
+ client.future(messageHash);
1019
+ this.spawn(this.loadPositionsSnapshot, client, messageHash);
1020
+ }
1021
+ }
1022
+ else {
1023
+ this.positions = new Cache.ArrayCacheBySymbolBySide();
1024
+ }
1025
+ }
1026
+ async loadPositionsSnapshot(client, messageHash) {
1027
+ // as only one ws channel gives positions for all types, for snapshot must load all positions
1028
+ const fetchFunctions = [
1029
+ this.fetchPositions(undefined, { 'type': 'swap', 'subType': 'linear' }),
1030
+ this.fetchPositions(undefined, { 'type': 'swap', 'subType': 'inverse' }),
1031
+ ];
1032
+ const promises = await Promise.all(fetchFunctions);
1033
+ this.positions = new Cache.ArrayCacheBySymbolBySide();
1034
+ const cache = this.positions;
1035
+ for (let i = 0; i < promises.length; i++) {
1036
+ const positions = promises[i];
1037
+ for (let ii = 0; ii < positions.length; ii++) {
1038
+ const position = positions[ii];
1039
+ cache.append(position);
1040
+ }
1041
+ }
1042
+ // don't remove the future from the .futures cache
1043
+ const future = client.futures[messageHash];
1044
+ future.resolve(cache);
1045
+ client.resolve(cache, 'position');
1046
+ }
1047
+ handlePositions(client, message) {
1048
+ //
1049
+ // {
1050
+ // topic: 'position',
1051
+ // id: '504b2671629b08e3c4f6960382a59363:3bc4028023786545:0:01',
1052
+ // creationTime: 1694566055295,
1053
+ // data: [{
1054
+ // bustPrice: '15.00',
1055
+ // category: 'inverse',
1056
+ // createdTime: '1670083436351',
1057
+ // cumRealisedPnl: '0.00011988',
1058
+ // entryPrice: '19358.58553268',
1059
+ // leverage: '10',
1060
+ // liqPrice: '15.00',
1061
+ // markPrice: '25924.00',
1062
+ // positionBalance: '0.0000156',
1063
+ // positionIdx: 0,
1064
+ // positionMM: '0.001',
1065
+ // positionIM: '0.0000015497',
1066
+ // positionStatus: 'Normal',
1067
+ // positionValue: '0.00015497',
1068
+ // riskId: 1,
1069
+ // riskLimitValue: '150',
1070
+ // side: 'Buy',
1071
+ // size: '3',
1072
+ // stopLoss: '0.00',
1073
+ // symbol: 'BTCUSD',
1074
+ // takeProfit: '0.00',
1075
+ // tpslMode: 'Full',
1076
+ // tradeMode: 0,
1077
+ // autoAddMargin: 1,
1078
+ // trailingStop: '0.00',
1079
+ // unrealisedPnl: '0.00003925',
1080
+ // updatedTime: '1694566055293',
1081
+ // adlRankIndicator: 3
1082
+ // }]
1083
+ // }
1084
+ //
1085
+ // each account is connected to a different endpoint
1086
+ // and has exactly one subscriptionhash which is the account type
1087
+ if (this.positions === undefined) {
1088
+ this.positions = new Cache.ArrayCacheBySymbolBySide();
1089
+ }
1090
+ const cache = this.positions;
1091
+ const newPositions = [];
1092
+ const rawPositions = this.safeValue(message, 'data', []);
1093
+ for (let i = 0; i < rawPositions.length; i++) {
1094
+ const rawPosition = rawPositions[i];
1095
+ const position = this.parsePosition(rawPosition);
1096
+ newPositions.push(position);
1097
+ cache.append(position);
1098
+ }
1099
+ const messageHashes = this.findMessageHashes(client, 'positions::');
1100
+ for (let i = 0; i < messageHashes.length; i++) {
1101
+ const messageHash = messageHashes[i];
1102
+ const parts = messageHash.split('::');
1103
+ const symbolsString = parts[1];
1104
+ const symbols = symbolsString.split(',');
1105
+ const positions = this.filterByArray(newPositions, 'symbol', symbols, false);
1106
+ if (!this.isEmpty(positions)) {
1107
+ client.resolve(positions, messageHash);
1108
+ }
1109
+ }
1110
+ client.resolve(newPositions, 'positions');
1111
+ }
969
1112
  async watchOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
970
1113
  /**
971
1114
  * @method
@@ -993,7 +1136,7 @@ class bybit extends bybit$1 {
993
1136
  'usdc': ['user.openapi.perp.order'],
994
1137
  };
995
1138
  const topics = this.safeValue(topicsByMarket, this.getPrivateType(url));
996
- const orders = await this.watchTopics(url, messageHash, topics, params);
1139
+ const orders = await this.watchTopics(url, messageHash, topics, messageHash, params);
997
1140
  if (this.newUpdates) {
998
1141
  limit = orders.getLimit(symbol, limit);
999
1142
  }
@@ -1310,7 +1453,7 @@ class bybit extends bybit$1 {
1310
1453
  }
1311
1454
  }
1312
1455
  const topics = [this.safeValue(topicByMarket, this.getPrivateType(url))];
1313
- return await this.watchTopics(url, messageHash, topics, params);
1456
+ return await this.watchTopics(url, messageHash, topics, messageHash, params);
1314
1457
  }
1315
1458
  handleBalance(client, message) {
1316
1459
  //
@@ -1548,14 +1691,14 @@ class bybit extends bybit$1 {
1548
1691
  this.balance[code] = account;
1549
1692
  }
1550
1693
  }
1551
- async watchTopics(url, messageHash, topics = [], params = {}) {
1694
+ async watchTopics(url, messageHash, topics, subscriptionHash, params = {}) {
1552
1695
  const request = {
1553
1696
  'op': 'subscribe',
1554
1697
  'req_id': this.requestId(),
1555
1698
  'args': topics,
1556
1699
  };
1557
1700
  const message = this.extend(request, params);
1558
- return await this.watch(url, messageHash, message, messageHash);
1701
+ return await this.watch(url, messageHash, message, subscriptionHash);
1559
1702
  }
1560
1703
  async authenticate(url, params = {}) {
1561
1704
  this.checkRequiredCredentials();
@@ -1682,6 +1825,7 @@ class bybit extends bybit$1 {
1682
1825
  'execution': this.handleMyTrades,
1683
1826
  'ticketInfo': this.handleMyTrades,
1684
1827
  'user.openapi.perp.trade': this.handleMyTrades,
1828
+ 'position': this.handlePositions,
1685
1829
  };
1686
1830
  const exacMethod = this.safeValue(methods, topic);
1687
1831
  if (exacMethod !== undefined) {
@@ -22,6 +22,7 @@ class cryptocom extends cryptocom$1 {
22
22
  'watchOrderBookForSymbols': true,
23
23
  'watchOrders': true,
24
24
  'watchOHLCV': true,
25
+ 'watchPositions': true,
25
26
  'createOrderWs': true,
26
27
  'cancelOrderWs': true,
27
28
  'cancelAllOrders': true,
@@ -38,7 +39,12 @@ class cryptocom extends cryptocom$1 {
38
39
  'private': 'wss://uat-stream.3ona.co/exchange/v1/user',
39
40
  },
40
41
  },
41
- 'options': {},
42
+ 'options': {
43
+ 'watchPositions': {
44
+ 'fetchPositionsSnapshot': true,
45
+ 'awaitPositionsSnapshot': true, // whether to wait for the positions snapshot before providing updates
46
+ },
47
+ },
42
48
  'streaming': {},
43
49
  });
44
50
  }
@@ -447,6 +453,129 @@ class cryptocom extends cryptocom$1 {
447
453
  client.resolve(stored, 'user.order');
448
454
  }
449
455
  }
456
+ async watchPositions(symbols = undefined, since = undefined, limit = undefined, params = {}) {
457
+ /**
458
+ * @method
459
+ * @name cryptocom#watchPositions
460
+ * @description watch all open positions
461
+ * @see https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#user-position_balance
462
+ * @param {[string]|undefined} symbols list of unified market symbols
463
+ * @param {object} params extra parameters specific to the cryptocom api endpoint
464
+ * @returns {[object]} a list of [position structure]{@link https://docs.ccxt.com/en/latest/manual.html#position-structure}
465
+ */
466
+ await this.loadMarkets();
467
+ await this.authenticate();
468
+ const url = this.urls['api']['ws']['private'];
469
+ const id = this.nonce();
470
+ const request = {
471
+ 'method': 'subscribe',
472
+ 'params': {
473
+ 'channels': ['user.position_balance'],
474
+ },
475
+ 'nonce': id,
476
+ };
477
+ let messageHash = 'positions';
478
+ symbols = this.marketSymbols(symbols);
479
+ if (!this.isEmpty(symbols)) {
480
+ messageHash = '::' + symbols.join(',');
481
+ }
482
+ const client = this.client(url);
483
+ this.setPositionsCache(client, symbols);
484
+ const fetchPositionsSnapshot = this.handleOption('watchPositions', 'fetchPositionsSnapshot', true);
485
+ const awaitPositionsSnapshot = this.safeValue('watchPositions', 'awaitPositionsSnapshot', true);
486
+ if (fetchPositionsSnapshot && awaitPositionsSnapshot && this.positions === undefined) {
487
+ const snapshot = await client.future('fetchPositionsSnapshot');
488
+ return this.filterBySymbolsSinceLimit(snapshot, symbols, since, limit, true);
489
+ }
490
+ const newPositions = await this.watch(url, messageHash, this.extend(request, params));
491
+ if (this.newUpdates) {
492
+ return newPositions;
493
+ }
494
+ return this.filterBySymbolsSinceLimit(this.positions, symbols, since, limit, true);
495
+ }
496
+ setPositionsCache(client, type, symbols = undefined) {
497
+ const fetchPositionsSnapshot = this.handleOption('watchPositions', 'fetchPositionsSnapshot', false);
498
+ if (fetchPositionsSnapshot) {
499
+ const messageHash = 'fetchPositionsSnapshot';
500
+ if (!(messageHash in client.futures)) {
501
+ client.future(messageHash);
502
+ this.spawn(this.loadPositionsSnapshot, client, messageHash);
503
+ }
504
+ }
505
+ else {
506
+ this.positions = new Cache.ArrayCacheBySymbolBySide();
507
+ }
508
+ }
509
+ async loadPositionsSnapshot(client, messageHash) {
510
+ const positions = await this.fetchPositions();
511
+ this.positions = new Cache.ArrayCacheBySymbolBySide();
512
+ const cache = this.positions;
513
+ for (let i = 0; i < positions.length; i++) {
514
+ const position = positions[i];
515
+ const contracts = this.safeNumber(position, 'contracts', 0);
516
+ if (contracts > 0) {
517
+ cache.append(position);
518
+ }
519
+ }
520
+ // don't remove the future from the .futures cache
521
+ const future = client.futures[messageHash];
522
+ future.resolve(cache);
523
+ client.resolve(cache, 'positions');
524
+ }
525
+ handlePositions(client, message) {
526
+ //
527
+ // {
528
+ // subscription: "user.position_balance",
529
+ // channel: "user.position_balance",
530
+ // data: [{
531
+ // balances: [{
532
+ // instrument_name: "USD",
533
+ // quantity: "8.9979961950886",
534
+ // update_timestamp_ms: 1695598760597,
535
+ // }],
536
+ // positions: [{
537
+ // account_id: "96a0edb1-afb5-4c7c-af89-5cb610319e2c",
538
+ // instrument_name: "LTCUSD-PERP",
539
+ // type: "PERPETUAL_SWAP",
540
+ // quantity: "1.8",
541
+ // cost: "114.766",
542
+ // open_position_pnl: "-0.0216206",
543
+ // session_pnl: "0.00962994",
544
+ // update_timestamp_ms: 1695598760597,
545
+ // open_pos_cost: "114.766",
546
+ // }],
547
+ // }],
548
+ // }
549
+ //
550
+ // each account is connected to a different endpoint
551
+ // and has exactly one subscriptionhash which is the account type
552
+ const data = this.safeValue(message, 'data', []);
553
+ const firstData = this.safeValue(data, 0, {});
554
+ const rawPositions = this.safeValue(firstData, 'positions', []);
555
+ if (this.positions === undefined) {
556
+ this.positions = new Cache.ArrayCacheBySymbolBySide();
557
+ }
558
+ const cache = this.positions;
559
+ const newPositions = [];
560
+ for (let i = 0; i < rawPositions.length; i++) {
561
+ const rawPosition = rawPositions[i];
562
+ const position = this.parsePosition(rawPosition);
563
+ newPositions.push(position);
564
+ cache.append(position);
565
+ }
566
+ const messageHashes = this.findMessageHashes(client, 'positions::');
567
+ for (let i = 0; i < messageHashes.length; i++) {
568
+ const messageHash = messageHashes[i];
569
+ const parts = messageHash.split('::');
570
+ const symbolsString = parts[1];
571
+ const symbols = symbolsString.split(',');
572
+ const positions = this.filterByArray(newPositions, 'symbol', symbols, false);
573
+ if (!this.isEmpty(positions)) {
574
+ client.resolve(positions, messageHash);
575
+ }
576
+ }
577
+ client.resolve(newPositions, 'positions');
578
+ }
450
579
  async watchBalance(params = {}) {
451
580
  /**
452
581
  * @method
@@ -713,6 +842,7 @@ class cryptocom extends cryptocom$1 {
713
842
  'user.order': this.handleOrders,
714
843
  'user.trade': this.handleTrades,
715
844
  'user.balance': this.handleBalance,
845
+ 'user.position_balance': this.handlePositions,
716
846
  };
717
847
  const result = this.safeValue2(message, 'result', 'info');
718
848
  const channel = this.safeString(result, 'channel');
@@ -21,6 +21,7 @@ class gate extends gate$1 {
21
21
  'watchOHLCV': true,
22
22
  'watchBalance': true,
23
23
  'watchOrders': true,
24
+ 'watchPositions': true,
24
25
  },
25
26
  'urls': {
26
27
  'api': {
@@ -72,6 +73,10 @@ class gate extends gate$1 {
72
73
  'settle': 'usdt',
73
74
  'spot': 'spot.balances', // spot.margin_balances, spot.funding_balances or spot.cross_balances
74
75
  },
76
+ 'watchPositions': {
77
+ 'fetchPositionsSnapshot': true,
78
+ 'awaitPositionsSnapshot': true, // whether to wait for the positions snapshot before providing updates
79
+ },
75
80
  },
76
81
  'exceptions': {
77
82
  'ws': {
@@ -750,6 +755,145 @@ class gate extends gate$1 {
750
755
  this.balance = this.safeBalance(this.balance);
751
756
  client.resolve(this.balance, messageHash);
752
757
  }
758
+ async watchPositions(symbols = undefined, since = undefined, limit = undefined, params = {}) {
759
+ /**
760
+ * @method
761
+ * @name gate#watchPositions
762
+ * @see https://www.gate.io/docs/developers/futures/ws/en/#positions-subscription
763
+ * @see https://www.gate.io/docs/developers/delivery/ws/en/#positions-subscription
764
+ * @see https://www.gate.io/docs/developers/options/ws/en/#positions-channel
765
+ * @description watch all open positions
766
+ * @param {[string]|undefined} symbols list of unified market symbols
767
+ * @param {object} params extra parameters specific to the gate api endpoint
768
+ * @returns {[object]} a list of [position structure]{@link https://docs.ccxt.com/en/latest/manual.html#position-structure}
769
+ */
770
+ await this.loadMarkets();
771
+ let market = undefined;
772
+ symbols = this.marketSymbols(symbols);
773
+ const payload = ['!' + 'all'];
774
+ if (!this.isEmpty(symbols)) {
775
+ market = this.getMarketFromSymbols(symbols);
776
+ }
777
+ let type = undefined;
778
+ let query = undefined;
779
+ [type, query] = this.handleMarketTypeAndParams('watchPositions', market, params);
780
+ if (type === 'spot') {
781
+ type = 'swap';
782
+ }
783
+ const typeId = this.getSupportedMapping(type, {
784
+ 'future': 'futures',
785
+ 'swap': 'futures',
786
+ 'option': 'options',
787
+ });
788
+ let messageHash = type + ':positions';
789
+ if (!this.isEmpty(symbols)) {
790
+ messageHash += '::' + symbols.join(',');
791
+ }
792
+ const channel = typeId + '.positions';
793
+ let subType = undefined;
794
+ [subType, query] = this.handleSubTypeAndParams('watchPositions', market, query);
795
+ const isInverse = (subType === 'inverse');
796
+ const url = this.getUrlByMarketType(type, isInverse);
797
+ const client = this.client(url);
798
+ this.setPositionsCache(client, type, symbols);
799
+ const fetchPositionsSnapshot = this.handleOption('watchPositions', 'fetchPositionsSnapshot', true);
800
+ const awaitPositionsSnapshot = this.safeValue('watchPositions', 'awaitPositionsSnapshot', true);
801
+ const cache = this.safeValue(this.positions, type);
802
+ if (fetchPositionsSnapshot && awaitPositionsSnapshot && cache === undefined) {
803
+ return await client.future(type + ':fetchPositionsSnapshot');
804
+ }
805
+ const positions = await this.subscribePrivate(url, messageHash, payload, channel, query, true);
806
+ if (this.newUpdates) {
807
+ return positions;
808
+ }
809
+ return this.filterBySymbolsSinceLimit(this.positions, symbols, since, limit, true);
810
+ }
811
+ setPositionsCache(client, type, symbols = undefined) {
812
+ if (this.positions === undefined) {
813
+ this.positions = {};
814
+ }
815
+ if (type in this.positions) {
816
+ return;
817
+ }
818
+ const fetchPositionsSnapshot = this.handleOption('watchPositions', 'fetchPositionsSnapshot', false);
819
+ if (fetchPositionsSnapshot) {
820
+ const messageHash = type + ':fetchPositionsSnapshot';
821
+ if (!(messageHash in client.futures)) {
822
+ client.future(messageHash);
823
+ this.spawn(this.loadPositionsSnapshot, client, messageHash, type);
824
+ }
825
+ }
826
+ else {
827
+ this.positions[type] = new Cache.ArrayCacheBySymbolBySide();
828
+ }
829
+ }
830
+ async loadPositionsSnapshot(client, messageHash, type) {
831
+ const positions = await this.fetchPositions(undefined, { 'type': type });
832
+ this.positions[type] = new Cache.ArrayCacheBySymbolBySide();
833
+ const cache = this.positions[type];
834
+ for (let i = 0; i < positions.length; i++) {
835
+ const position = positions[i];
836
+ cache.append(position);
837
+ }
838
+ // don't remove the future from the .futures cache
839
+ const future = client.futures[messageHash];
840
+ future.resolve(cache);
841
+ client.resolve(cache, type + ':position');
842
+ }
843
+ handlePositions(client, message) {
844
+ //
845
+ // {
846
+ // time: 1693158497,
847
+ // time_ms: 1693158497204,
848
+ // channel: 'futures.positions',
849
+ // event: 'update',
850
+ // result: [{
851
+ // contract: 'XRP_USDT',
852
+ // cross_leverage_limit: 0,
853
+ // entry_price: 0.5253,
854
+ // history_pnl: 0,
855
+ // history_point: 0,
856
+ // last_close_pnl: 0,
857
+ // leverage: 0,
858
+ // leverage_max: 50,
859
+ // liq_price: 0.0361,
860
+ // maintenance_rate: 0.01,
861
+ // margin: 4.89609962852,
862
+ // mode: 'single',
863
+ // realised_pnl: -0.0026265,
864
+ // realised_point: 0,
865
+ // risk_limit: 500000,
866
+ // size: 1,
867
+ // time: 1693158497,
868
+ // time_ms: 1693158497195,
869
+ // update_id: 1,
870
+ // user: '10444586'
871
+ // }]
872
+ // }
873
+ //
874
+ const type = this.getMarketTypeByUrl(client.url);
875
+ const data = this.safeValue(message, 'result', []);
876
+ const cache = this.positions[type];
877
+ const newPositions = [];
878
+ for (let i = 0; i < data.length; i++) {
879
+ const rawPosition = data[i];
880
+ const position = this.parsePosition(rawPosition);
881
+ newPositions.push(position);
882
+ cache.append(position);
883
+ }
884
+ const messageHashes = this.findMessageHashes(client, type + ':positions::');
885
+ for (let i = 0; i < messageHashes.length; i++) {
886
+ const messageHash = messageHashes[i];
887
+ const parts = messageHash.split('::');
888
+ const symbolsString = parts[1];
889
+ const symbols = symbolsString.split(',');
890
+ const positions = this.filterByArray(newPositions, 'symbol', symbols, false);
891
+ if (!this.isEmpty(positions)) {
892
+ client.resolve(positions, messageHash);
893
+ }
894
+ }
895
+ client.resolve(newPositions, type + ':positions');
896
+ }
753
897
  async watchOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
754
898
  /**
755
899
  * @method
@@ -1036,6 +1180,7 @@ class gate extends gate$1 {
1036
1180
  'usertrades': this.handleMyTrades,
1037
1181
  'candlesticks': this.handleOHLCV,
1038
1182
  'orders': this.handleOrder,
1183
+ 'positions': this.handlePositions,
1039
1184
  'tickers': this.handleTicker,
1040
1185
  'book_ticker': this.handleTicker,
1041
1186
  'trades': this.handleTrades,
@@ -1077,6 +1222,22 @@ class gate extends gate$1 {
1077
1222
  return url;
1078
1223
  }
1079
1224
  }
1225
+ getMarketTypeByUrl(url) {
1226
+ const findBy = {
1227
+ 'op-': 'option',
1228
+ 'delivery': 'future',
1229
+ 'fx': 'swap',
1230
+ };
1231
+ const keys = Object.keys(findBy);
1232
+ for (let i = 0; i < keys.length; i++) {
1233
+ const key = keys[i];
1234
+ const value = findBy[key];
1235
+ if (url.indexOf(key) >= 0) {
1236
+ return value;
1237
+ }
1238
+ }
1239
+ return 'spot';
1240
+ }
1080
1241
  requestId() {
1081
1242
  // their support said that reqid must be an int32, not documented
1082
1243
  const reqid = this.sum(this.safeInteger(this.options, 'reqid', 0), 1);