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
@@ -67,14 +67,14 @@ class huobi extends huobi$1 {
67
67
  },
68
68
  },
69
69
  'swap': {
70
- 'inverse': {
71
- 'public': 'wss://api.hbdm.vn/swap-ws',
72
- 'private': 'wss://api.hbdm.vn/swap-notification',
73
- },
74
70
  'linear': {
75
71
  'public': 'wss://api.hbdm.vn/linear-swap-ws',
76
72
  'private': 'wss://api.hbdm.vn/linear-swap-notification',
77
73
  },
74
+ 'inverse': {
75
+ 'public': 'wss://api.hbdm.vn/swap-ws',
76
+ 'private': 'wss://api.hbdm.vn/swap-notification',
77
+ },
78
78
  },
79
79
  },
80
80
  },
@@ -1187,6 +1187,127 @@ class huobi extends huobi$1 {
1187
1187
  'fee': undefined,
1188
1188
  }, market);
1189
1189
  }
1190
+ async watchPositions(symbols = undefined, since = undefined, limit = undefined, params = {}) {
1191
+ /**
1192
+ * @method
1193
+ * @name huobi#watchPositions
1194
+ * @see https://www.huobi.com/en-in/opend/newApiPages/?id=8cb7de1c-77b5-11ed-9966-0242ac110003
1195
+ * @see https://www.huobi.com/en-in/opend/newApiPages/?id=8cb7df0f-77b5-11ed-9966-0242ac110003
1196
+ * @see https://www.huobi.com/en-in/opend/newApiPages/?id=28c34a7d-77ae-11ed-9966-0242ac110003
1197
+ * @see https://www.huobi.com/en-in/opend/newApiPages/?id=5d5156b5-77b6-11ed-9966-0242ac110003
1198
+ * @description watch all open positions. Note: huobi has one channel for each marginMode and type
1199
+ * @param {[string]|undefined} symbols list of unified market symbols
1200
+ * @param {object} params extra parameters specific to the huobi api endpoint
1201
+ * @returns {[object]} a list of [position structure]{@link https://docs.ccxt.com/en/latest/manual.html#position-structure}
1202
+ */
1203
+ await this.loadMarkets();
1204
+ let market = undefined;
1205
+ let messageHash = '';
1206
+ if (!this.isEmpty(symbols)) {
1207
+ market = this.getMarketFromSymbols(symbols);
1208
+ messageHash = '::' + symbols.join(',');
1209
+ }
1210
+ let type = undefined;
1211
+ let subType = undefined;
1212
+ if (market !== undefined) {
1213
+ type = market['type'];
1214
+ subType = market['linear'] ? 'linear' : 'inverse';
1215
+ }
1216
+ else {
1217
+ [type, params] = this.handleMarketTypeAndParams('watchPositions', market, params);
1218
+ if (type === 'spot') {
1219
+ type = 'future';
1220
+ }
1221
+ [subType, params] = this.handleOptionAndParams(params, 'watchPositions', 'subType', subType);
1222
+ }
1223
+ symbols = this.marketSymbols(symbols);
1224
+ let marginMode = undefined;
1225
+ [marginMode, params] = this.handleMarginModeAndParams('watchPositions', params, 'cross');
1226
+ const isLinear = (subType === 'linear');
1227
+ const url = this.getUrlByMarketType(type, isLinear, true);
1228
+ messageHash = marginMode + ':positions' + messageHash;
1229
+ const channel = (marginMode === 'cross') ? 'positions_cross.*' : 'positions.*';
1230
+ const newPositions = await this.subscribePrivate(channel, messageHash, type, subType, params);
1231
+ if (this.newUpdates) {
1232
+ return newPositions;
1233
+ }
1234
+ return this.filterBySymbolsSinceLimit(this.positions[url][marginMode], symbols, since, limit, false);
1235
+ }
1236
+ handlePositions(client, message) {
1237
+ //
1238
+ // {
1239
+ // op: 'notify',
1240
+ // topic: 'positions_cross',
1241
+ // ts: 1696767149650,
1242
+ // event: 'snapshot',
1243
+ // data: [
1244
+ // {
1245
+ // contract_type: 'swap',
1246
+ // pair: 'BTC-USDT',
1247
+ // business_type: 'swap',
1248
+ // liquidation_price: null,
1249
+ // symbol: 'BTC',
1250
+ // contract_code: 'BTC-USDT',
1251
+ // volume: 1,
1252
+ // available: 1,
1253
+ // frozen: 0,
1254
+ // cost_open: 27802.2,
1255
+ // cost_hold: 27802.2,
1256
+ // profit_unreal: 0.0175,
1257
+ // profit_rate: 0.000629446590557581,
1258
+ // profit: 0.0175,
1259
+ // margin_asset: 'USDT',
1260
+ // position_margin: 27.8197,
1261
+ // lever_rate: 1,
1262
+ // direction: 'buy',
1263
+ // last_price: 27819.7,
1264
+ // margin_mode: 'cross',
1265
+ // margin_account: 'USDT',
1266
+ // trade_partition: 'USDT',
1267
+ // position_mode: 'dual_side'
1268
+ // },
1269
+ // ]
1270
+ // }
1271
+ //
1272
+ const url = client.url;
1273
+ const topic = this.safeString(message, 'topic', '');
1274
+ const marginMode = (topic === 'positions_cross') ? 'cross' : 'isolated';
1275
+ if (this.positions === undefined) {
1276
+ this.positions = {};
1277
+ }
1278
+ const clientPositions = this.safeValue(this.positions, url);
1279
+ if (clientPositions === undefined) {
1280
+ this.positions[url] = {};
1281
+ }
1282
+ const clientMarginModePositions = this.safeValue(clientPositions, marginMode);
1283
+ if (clientMarginModePositions === undefined) {
1284
+ this.positions[url][marginMode] = new Cache.ArrayCacheBySymbolBySide();
1285
+ }
1286
+ const cache = this.positions[url][marginMode];
1287
+ const rawPositions = this.safeValue(message, 'data', []);
1288
+ const newPositions = [];
1289
+ const timestamp = this.safeInteger(message, 'ts');
1290
+ for (let i = 0; i < rawPositions.length; i++) {
1291
+ const rawPosition = rawPositions[i];
1292
+ const position = this.parsePosition(rawPosition);
1293
+ position['timestamp'] = timestamp;
1294
+ position['datetime'] = this.iso8601(timestamp);
1295
+ newPositions.push(position);
1296
+ cache.append(position);
1297
+ }
1298
+ const messageHashes = this.findMessageHashes(client, marginMode + ':positions::');
1299
+ for (let i = 0; i < messageHashes.length; i++) {
1300
+ const messageHash = messageHashes[i];
1301
+ const parts = messageHash.split('::');
1302
+ const symbolsString = parts[1];
1303
+ const symbols = symbolsString.split(',');
1304
+ const positions = this.filterByArray(newPositions, 'symbol', symbols, false);
1305
+ if (!this.isEmpty(positions)) {
1306
+ client.resolve(positions, messageHash);
1307
+ }
1308
+ }
1309
+ client.resolve(newPositions, marginMode + ':positions');
1310
+ }
1190
1311
  async watchBalance(params = {}) {
1191
1312
  /**
1192
1313
  * @method
@@ -1690,6 +1811,9 @@ class huobi extends huobi$1 {
1690
1811
  if (topic.indexOf('account') >= 0) {
1691
1812
  this.handleBalance(client, message);
1692
1813
  }
1814
+ if (topic.indexOf('positions') >= 0) {
1815
+ this.handlePositions(client, message);
1816
+ }
1693
1817
  }
1694
1818
  }
1695
1819
  async pong(client, message) {
@@ -23,6 +23,7 @@ class krakenfutures extends krakenfutures$1 {
23
23
  // 'watchStatus': true, // https://docs.futures.kraken.com/#websocket-api-public-feeds-heartbeat
24
24
  'watchOrders': true,
25
25
  'watchMyTrades': true,
26
+ 'watchPositions': true,
26
27
  },
27
28
  'urls': {
28
29
  'api': {
@@ -204,6 +205,133 @@ class krakenfutures extends krakenfutures$1 {
204
205
  const orderbook = await this.subscribePublic('book', [symbol], params);
205
206
  return orderbook.limit();
206
207
  }
208
+ async watchPositions(symbols = undefined, since = undefined, limit = undefined, params = {}) {
209
+ /**
210
+ * @method
211
+ * @name krakenfutures#watchPositions
212
+ * @see https://docs.futures.kraken.com/#websocket-api-private-feeds-open-positions
213
+ * @description watch all open positions
214
+ * @param {[string]|undefined} symbols list of unified market symbols
215
+ * @param {object} params extra parameters specific to the krakenfutures api endpoint
216
+ * @returns {[object]} a list of [position structure]{@link https://docs.ccxt.com/en/latest/manual.html#position-structure}
217
+ */
218
+ await this.loadMarkets();
219
+ let messageHash = '';
220
+ symbols = this.marketSymbols(symbols);
221
+ if (!this.isEmpty(symbols)) {
222
+ messageHash = '::' + symbols.join(',');
223
+ }
224
+ messageHash = 'positions' + messageHash;
225
+ const newPositions = await this.subscribePrivate('open_positions', messageHash, params);
226
+ if (this.newUpdates) {
227
+ return newPositions;
228
+ }
229
+ return this.filterBySymbolsSinceLimit(this.positions, symbols, since, limit, true);
230
+ }
231
+ handlePositions(client, message) {
232
+ //
233
+ // {
234
+ // feed: 'open_positions',
235
+ // account: '3b111acc-4fcc-45be-a622-57e611fe9f7f',
236
+ // positions: [
237
+ // {
238
+ // instrument: 'PF_LTCUSD',
239
+ // balance: 0.5,
240
+ // pnl: -0.8628305877699987,
241
+ // entry_price: 70.53,
242
+ // mark_price: 68.80433882446,
243
+ // index_price: 68.8091,
244
+ // liquidation_threshold: 0,
245
+ // effective_leverage: 0.007028866753648637,
246
+ // return_on_equity: -1.2233525985679834,
247
+ // unrealized_funding: 0.0000690610530935388,
248
+ // initial_margin: 0.7053,
249
+ // initial_margin_with_orders: 0.7053,
250
+ // maintenance_margin: 0.35265,
251
+ // pnl_currency: 'USD'
252
+ // }
253
+ // ],
254
+ // seq: 0,
255
+ // timestamp: 1698608414910
256
+ // }
257
+ //
258
+ if (this.positions === undefined) {
259
+ this.positions = new Cache.ArrayCacheBySymbolById();
260
+ }
261
+ const cache = this.positions;
262
+ const rawPositions = this.safeValue(message, 'positions', []);
263
+ const newPositions = [];
264
+ for (let i = 0; i < rawPositions.length; i++) {
265
+ const rawPosition = rawPositions[i];
266
+ const position = this.parseWsPosition(rawPosition);
267
+ const timestamp = this.safeInteger(message, 'timestamp');
268
+ position['timestamp'] = timestamp;
269
+ position['datetime'] = this.iso8601(timestamp);
270
+ newPositions.push(position);
271
+ cache.append(position);
272
+ }
273
+ const messageHashes = this.findMessageHashes(client, 'positions::');
274
+ for (let i = 0; i < messageHashes.length; i++) {
275
+ const messageHash = messageHashes[i];
276
+ const parts = messageHash.split('::');
277
+ const symbolsString = parts[1];
278
+ const symbols = symbolsString.split(',');
279
+ const positions = this.filterByArray(newPositions, 'symbol', symbols, false);
280
+ if (!this.isEmpty(positions)) {
281
+ client.resolve(positions, messageHash);
282
+ }
283
+ }
284
+ client.resolve(newPositions, 'positions');
285
+ }
286
+ parseWsPosition(position, market = undefined) {
287
+ //
288
+ // {
289
+ // instrument: 'PF_LTCUSD',
290
+ // balance: 0.5,
291
+ // pnl: -0.8628305877699987,
292
+ // entry_price: 70.53,
293
+ // mark_price: 68.80433882446,
294
+ // index_price: 68.8091,
295
+ // liquidation_threshold: 0,
296
+ // effective_leverage: 0.007028866753648637,
297
+ // return_on_equity: -1.2233525985679834,
298
+ // unrealized_funding: 0.0000690610530935388,
299
+ // initial_margin: 0.7053,
300
+ // initial_margin_with_orders: 0.7053,
301
+ // maintenance_margin: 0.35265,
302
+ // pnl_currency: 'USD'
303
+ // }
304
+ //
305
+ const marketId = this.safeString(position, 'instrument');
306
+ const hedged = 'both';
307
+ const balance = this.safeNumber(position, 'balance');
308
+ const side = (balance > 0) ? 'long' : 'short';
309
+ return this.safePosition({
310
+ 'info': position,
311
+ 'id': undefined,
312
+ 'symbol': this.safeSymbol(marketId),
313
+ 'notional': undefined,
314
+ 'marginMode': undefined,
315
+ 'liquidationPrice': this.safeNumber(position, 'liquidation_threshold'),
316
+ 'entryPrice': this.safeNumber(position, 'entry_price'),
317
+ 'unrealizedPnl': this.safeNumber(position, 'pnl'),
318
+ 'percentage': this.safeNumber(position, 'return_on_equity'),
319
+ 'contracts': this.parseNumber(Precise["default"].stringAbs(this.numberToString(balance))),
320
+ 'contractSize': undefined,
321
+ 'markPrice': this.safeNumber(position, 'mark_price'),
322
+ 'side': side,
323
+ 'hedged': hedged,
324
+ 'timestamp': undefined,
325
+ 'datetime': undefined,
326
+ 'maintenanceMargin': this.safeNumber(position, 'maintenance_margin'),
327
+ 'maintenanceMarginPercentage': undefined,
328
+ 'collateral': undefined,
329
+ 'initialMargin': this.safeNumber(position, 'initial_margin'),
330
+ 'initialMarginPercentage': undefined,
331
+ 'leverage': undefined,
332
+ 'marginRatio': undefined,
333
+ });
334
+ }
207
335
  async watchOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
208
336
  /**
209
337
  * @method
@@ -1279,6 +1407,7 @@ class krakenfutures extends krakenfutures$1 {
1279
1407
  'open_orders_snapshot': this.handleOrderSnapshot,
1280
1408
  'balances': this.handleBalance,
1281
1409
  'balances_snapshot': this.handleBalance,
1410
+ 'open_positions': this.handlePositions,
1282
1411
  };
1283
1412
  const method = this.safeValue(methods, feed);
1284
1413
  if (method !== undefined) {
@@ -16,6 +16,9 @@ class kucoinfutures extends kucoinfutures$1 {
16
16
  'watchOrderBook': true,
17
17
  'watchOrders': true,
18
18
  'watchBalance': true,
19
+ 'watchPosition': true,
20
+ 'watchPositions': false,
21
+ 'watchPositionForSymbols': false,
19
22
  'watchTradesForSymbols': true,
20
23
  'watchOrderBookForSymbols': true,
21
24
  },
@@ -41,6 +44,10 @@ class kucoinfutures extends kucoinfutures$1 {
41
44
  'watchTicker': {
42
45
  'name': 'contractMarket/tickerV2', // market/ticker
43
46
  },
47
+ 'watchPosition': {
48
+ 'fetchPositionSnapshot': true,
49
+ 'awaitPositionSnapshot': true, // whether to wait for the position snapshot before providing updates
50
+ },
44
51
  },
45
52
  'streaming': {
46
53
  // kucoin does not support built-in ws protocol-level ping-pong
@@ -177,6 +184,178 @@ class kucoinfutures extends kucoinfutures$1 {
177
184
  client.resolve(ticker, messageHash);
178
185
  return message;
179
186
  }
187
+ async watchPosition(symbol = undefined, params = {}) {
188
+ /**
189
+ * @method
190
+ * @name kucoinfutures#watchPosition
191
+ * @description watch open positions for a specific symbol
192
+ * @see https://docs.kucoin.com/futures/#position-change-events
193
+ * @param {string|undefined} symbol unified market symbol
194
+ * @param {object} params extra parameters specific to the kucoinfutures api endpoint
195
+ * @returns {object} a [position structure]{@link https://docs.ccxt.com/en/latest/manual.html#position-structure}
196
+ */
197
+ this.checkRequiredSymbol('watchPosition', symbol);
198
+ await this.loadMarkets();
199
+ const url = await this.negotiate(true);
200
+ const market = this.market(symbol);
201
+ const topic = '/contract/position:' + market['id'];
202
+ const request = {
203
+ 'privateChannel': true,
204
+ };
205
+ const messageHash = 'position:' + market['symbol'];
206
+ const client = this.client(url);
207
+ this.setPositionCache(client, symbol);
208
+ const fetchPositionSnapshot = this.handleOption('watchPosition', 'fetchPositionSnapshot', true);
209
+ const awaitPositionSnapshot = this.safeValue('watchPosition', 'awaitPositionSnapshot', true);
210
+ const currentPosition = this.getCurrentPosition(symbol);
211
+ if (fetchPositionSnapshot && awaitPositionSnapshot && currentPosition === undefined) {
212
+ const snapshot = await client.future('fetchPositionSnapshot:' + symbol);
213
+ return snapshot;
214
+ }
215
+ return await this.subscribe(url, messageHash, topic, undefined, this.extend(request, params));
216
+ }
217
+ getCurrentPosition(symbol) {
218
+ if (this.positions === undefined) {
219
+ return undefined;
220
+ }
221
+ const cache = this.positions.hashmap;
222
+ const symbolCache = this.safeValue(cache, symbol, {});
223
+ const values = Object.values(symbolCache);
224
+ return this.safeValue(values, 0);
225
+ }
226
+ setPositionCache(client, symbol) {
227
+ const fetchPositionSnapshot = this.handleOption('watchPosition', 'fetchPositionSnapshot', false);
228
+ if (fetchPositionSnapshot) {
229
+ const messageHash = 'fetchPositionSnapshot:' + symbol;
230
+ if (!(messageHash in client.futures)) {
231
+ client.future(messageHash);
232
+ this.spawn(this.loadPositionSnapshot, client, messageHash, symbol);
233
+ }
234
+ }
235
+ }
236
+ async loadPositionSnapshot(client, messageHash, symbol) {
237
+ const position = await this.fetchPosition(symbol);
238
+ this.positions = new Cache.ArrayCacheBySymbolById();
239
+ const cache = this.positions;
240
+ cache.append(position);
241
+ // don't remove the future from the .futures cache
242
+ const future = client.futures[messageHash];
243
+ future.resolve(cache);
244
+ client.resolve(position, 'position:' + symbol);
245
+ }
246
+ handlePosition(client, message) {
247
+ //
248
+ // Position Changes Caused Operations
249
+ // {
250
+ // "type": "message",
251
+ // "userId": "5c32d69203aa676ce4b543c7", // Deprecated, will detele later
252
+ // "channelType": "private",
253
+ // "topic": "/contract/position:XBTUSDM",
254
+ // "subject": "position.change",
255
+ // "data": {
256
+ // "realisedGrossPnl": 0E-8, //Accumulated realised profit and loss
257
+ // "symbol": "XBTUSDM", //Symbol
258
+ // "crossMode": false, //Cross mode or not
259
+ // "liquidationPrice": 1000000.0, //Liquidation price
260
+ // "posLoss": 0E-8, //Manually added margin amount
261
+ // "avgEntryPrice": 7508.22, //Average entry price
262
+ // "unrealisedPnl": -0.00014735, //Unrealised profit and loss
263
+ // "markPrice": 7947.83, //Mark price
264
+ // "posMargin": 0.00266779, //Position margin
265
+ // "autoDeposit": false, //Auto deposit margin or not
266
+ // "riskLimit": 100000, //Risk limit
267
+ // "unrealisedCost": 0.00266375, //Unrealised value
268
+ // "posComm": 0.00000392, //Bankruptcy cost
269
+ // "posMaint": 0.00001724, //Maintenance margin
270
+ // "posCost": 0.00266375, //Position value
271
+ // "maintMarginReq": 0.005, //Maintenance margin rate
272
+ // "bankruptPrice": 1000000.0, //Bankruptcy price
273
+ // "realisedCost": 0.00000271, //Currently accumulated realised position value
274
+ // "markValue": 0.00251640, //Mark value
275
+ // "posInit": 0.00266375, //Position margin
276
+ // "realisedPnl": -0.00000253, //Realised profit and losts
277
+ // "maintMargin": 0.00252044, //Position margin
278
+ // "realLeverage": 1.06, //Leverage of the order
279
+ // "changeReason": "positionChange", //changeReason:marginChange、positionChange、liquidation、autoAppendMarginStatusChange、adl
280
+ // "currentCost": 0.00266375, //Current position value
281
+ // "openingTimestamp": 1558433191000, //Open time
282
+ // "currentQty": -20, //Current position
283
+ // "delevPercentage": 0.52, //ADL ranking percentile
284
+ // "currentComm": 0.00000271, //Current commission
285
+ // "realisedGrossCost": 0E-8, //Accumulated reliased gross profit value
286
+ // "isOpen": true, //Opened position or not
287
+ // "posCross": 1.2E-7, //Manually added margin
288
+ // "currentTimestamp": 1558506060394, //Current timestamp
289
+ // "unrealisedRoePcnt": -0.0553, //Rate of return on investment
290
+ // "unrealisedPnlPcnt": -0.0553, //Position profit and loss ratio
291
+ // "settleCurrency": "XBT" //Currency used to clear and settle the trades
292
+ // }
293
+ // }
294
+ // Position Changes Caused by Mark Price
295
+ // {
296
+ // "userId": "5cd3f1a7b7ebc19ae9558591", // Deprecated, will detele later
297
+ // "topic": "/contract/position:XBTUSDM",
298
+ // "subject": "position.change",
299
+ // "data": {
300
+ // "markPrice": 7947.83, //Mark price
301
+ // "markValue": 0.00251640, //Mark value
302
+ // "maintMargin": 0.00252044, //Position margin
303
+ // "realLeverage": 10.06, //Leverage of the order
304
+ // "unrealisedPnl": -0.00014735, //Unrealised profit and lost
305
+ // "unrealisedRoePcnt": -0.0553, //Rate of return on investment
306
+ // "unrealisedPnlPcnt": -0.0553, //Position profit and loss ratio
307
+ // "delevPercentage": 0.52, //ADL ranking percentile
308
+ // "currentTimestamp": 1558087175068, //Current timestamp
309
+ // "settleCurrency": "XBT" //Currency used to clear and settle the trades
310
+ // }
311
+ // }
312
+ // Funding Settlement
313
+ // {
314
+ // "userId": "xbc453tg732eba53a88ggyt8c", // Deprecated, will detele later
315
+ // "topic": "/contract/position:XBTUSDM",
316
+ // "subject": "position.settlement",
317
+ // "data": {
318
+ // "fundingTime": 1551770400000, //Funding time
319
+ // "qty": 100, //Position siz
320
+ // "markPrice": 3610.85, //Settlement price
321
+ // "fundingRate": -0.002966, //Funding rate
322
+ // "fundingFee": -296, //Funding fees
323
+ // "ts": 1547697294838004923, //Current time (nanosecond)
324
+ // "settleCurrency": "XBT" //Currency used to clear and settle the trades
325
+ // }
326
+ // }
327
+ // Adjustmet result of risk limit level
328
+ // {
329
+ // "userId": "xbc453tg732eba53a88ggyt8c",
330
+ // "topic": "/contract/position:ADAUSDTM",
331
+ // "subject": "position.adjustRiskLimit",
332
+ // "data": {
333
+ // "success": true, // Successful or not
334
+ // "riskLimitLevel": 1, // Current risk limit level
335
+ // "msg": "" // Failure reason
336
+ // }
337
+ // }
338
+ //
339
+ const topic = this.safeString(message, 'topic', '');
340
+ const parts = topic.split(':');
341
+ const marketId = this.safeString(parts, 1);
342
+ const symbol = this.safeSymbol(marketId, undefined, '');
343
+ const cache = this.positions;
344
+ const currentPosition = this.getCurrentPosition(symbol);
345
+ const messageHash = 'position:' + symbol;
346
+ const data = this.safeValue(message, 'data', {});
347
+ const newPosition = this.parsePosition(data);
348
+ const keys = Object.keys(newPosition);
349
+ for (let i = 0; i < keys.length; i++) {
350
+ const key = keys[i];
351
+ if (newPosition[key] === undefined) {
352
+ delete newPosition[key];
353
+ }
354
+ }
355
+ const position = this.extend(currentPosition, newPosition);
356
+ cache.append(position);
357
+ client.resolve(position, messageHash);
358
+ }
180
359
  async watchTrades(symbol, since = undefined, limit = undefined, params = {}) {
181
360
  /**
182
361
  * @method
@@ -729,6 +908,9 @@ class kucoinfutures extends kucoinfutures$1 {
729
908
  'match': this.handleTrade,
730
909
  'orderChange': this.handleOrder,
731
910
  'orderUpdated': this.handleOrder,
911
+ 'position.change': this.handlePosition,
912
+ 'position.settlement': this.handlePosition,
913
+ 'position.adjustRiskLimit': this.handlePosition,
732
914
  };
733
915
  const method = this.safeValue(methods, subject);
734
916
  if (method === undefined) {
@@ -22,6 +22,7 @@ class okx extends okx$1 {
22
22
  'watchOHLCV': true,
23
23
  'watchOrders': true,
24
24
  'watchMyTrades': true,
25
+ 'watchPositions': true,
25
26
  'createOrderWs': true,
26
27
  'editOrderWs': true,
27
28
  'cancelOrderWs': true,
@@ -850,6 +851,125 @@ class okx extends okx$1 {
850
851
  }
851
852
  return this.filterBySymbolSinceLimit(orders, symbol, since, limit, true);
852
853
  }
854
+ async watchPositions(symbols = undefined, since = undefined, limit = undefined, params = {}) {
855
+ /**
856
+ * @method
857
+ * @name okx#watchPositions
858
+ * @see https://www.okx.com/docs-v5/en/#trading-account-websocket-positions-channel
859
+ * @description watch all open positions
860
+ * @param {[string]|undefined} symbols list of unified market symbols
861
+ * @param {object} params extra parameters specific to the okx api endpoint
862
+ * @returns {[object]} a list of [position structure]{@link https://docs.ccxt.com/en/latest/manual.html#position-structure}
863
+ */
864
+ if (this.isEmpty(symbols)) {
865
+ throw new errors.ArgumentsRequired(this.id + ' watchPositions requires a list of symbols');
866
+ }
867
+ await this.loadMarkets();
868
+ await this.authenticate(params);
869
+ symbols = this.marketSymbols(symbols);
870
+ const request = {
871
+ 'instType': 'ANY',
872
+ };
873
+ const channel = 'positions';
874
+ const newPositions = await this.subscribeMultiple('private', channel, symbols, this.extend(request, params));
875
+ if (this.newUpdates) {
876
+ return newPositions;
877
+ }
878
+ return this.filterBySymbolsSinceLimit(this.positions, symbols, since, limit, true);
879
+ }
880
+ handlePositions(client, message) {
881
+ //
882
+ // {
883
+ // arg: {
884
+ // channel: 'positions',
885
+ // instType: 'ANY',
886
+ // instId: 'XRP-USDT-SWAP',
887
+ // uid: '464737184507959869'
888
+ // },
889
+ // data: [{
890
+ // adl: '1',
891
+ // availPos: '',
892
+ // avgPx: '0.52668',
893
+ // baseBal: '',
894
+ // baseBorrowed: '',
895
+ // baseInterest: '',
896
+ // bizRefId: '',
897
+ // bizRefType: '',
898
+ // cTime: '1693151444408',
899
+ // ccy: 'USDT',
900
+ // closeOrderAlgo: [],
901
+ // deltaBS: '',
902
+ // deltaPA: '',
903
+ // gammaBS: '',
904
+ // gammaPA: '',
905
+ // idxPx: '0.52683',
906
+ // imr: '17.564000000000004',
907
+ // instId: 'XRP-USDT-SWAP',
908
+ // instType: 'SWAP',
909
+ // interest: '',
910
+ // last: '0.52691',
911
+ // lever: '3',
912
+ // liab: '',
913
+ // liabCcy: '',
914
+ // liqPx: '0.3287514731020614',
915
+ // margin: '',
916
+ // markPx: '0.52692',
917
+ // mgnMode: 'cross',
918
+ // mgnRatio: '69.00363001456147',
919
+ // mmr: '0.26346',
920
+ // notionalUsd: '52.68620388000001',
921
+ // optVal: '',
922
+ // pTime: '1693151906023',
923
+ // pendingCloseOrdLiabVal: '',
924
+ // pos: '1',
925
+ // posCcy: '',
926
+ // posId: '616057041198907393',
927
+ // posSide: 'net',
928
+ // quoteBal: '',
929
+ // quoteBorrowed: '',
930
+ // quoteInterest: '',
931
+ // spotInUseAmt: '',
932
+ // spotInUseCcy: '',
933
+ // thetaBS: '',
934
+ // thetaPA: '',
935
+ // tradeId: '138745402',
936
+ // uTime: '1693151444408',
937
+ // upl: '0.0240000000000018',
938
+ // uplLastPx: '0.0229999999999952',
939
+ // uplRatio: '0.0013670539986328',
940
+ // uplRatioLastPx: '0.001310093415356',
941
+ // usdPx: '',
942
+ // vegaBS: '',
943
+ // vegaPA: ''
944
+ // }]
945
+ // }
946
+ //
947
+ const arg = this.safeValue(message, 'arg', {});
948
+ const channel = this.safeString(arg, 'channel', '');
949
+ const data = this.safeValue(message, 'data', []);
950
+ if (this.positions === undefined) {
951
+ this.positions = new Cache.ArrayCacheBySymbolBySide();
952
+ }
953
+ const cache = this.positions;
954
+ const newPositions = [];
955
+ for (let i = 0; i < data.length; i++) {
956
+ const rawPosition = data[i];
957
+ const position = this.parsePosition(rawPosition);
958
+ newPositions.push(position);
959
+ cache.append(position);
960
+ }
961
+ const messageHashes = this.findMessageHashes(client, channel + '::');
962
+ for (let i = 0; i < messageHashes.length; i++) {
963
+ const messageHash = messageHashes[i];
964
+ const parts = messageHash.split('::');
965
+ const symbolsString = parts[1];
966
+ const symbols = symbolsString.split(',');
967
+ const positions = this.filterByArray(newPositions, 'symbol', symbols, false);
968
+ if (!this.isEmpty(positions)) {
969
+ client.resolve(positions, messageHash);
970
+ }
971
+ }
972
+ }
853
973
  async watchOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
854
974
  /**
855
975
  * @method
@@ -1422,6 +1542,7 @@ class okx extends okx$1 {
1422
1542
  'books50-l2-tbt': this.handleOrderBook,
1423
1543
  'books-l2-tbt': this.handleOrderBook,
1424
1544
  'tickers': this.handleTicker,
1545
+ 'positions': this.handlePositions,
1425
1546
  'index-tickers': this.handleTicker,
1426
1547
  'sprd-tickers': this.handleTicker,
1427
1548
  'block-tickers': this.handleTicker,
package/js/ccxt.d.ts CHANGED
@@ -4,7 +4,7 @@ import * as functions from './src/base/functions.js';
4
4
  import * as errors from './src/base/errors.js';
5
5
  import { Market, Trade, Fee, Ticker, OrderBook, Order, Transaction, Tickers, Currency, Balance, DepositAddress, WithdrawalResponse, DepositAddressResponse, OHLCV, Balances, PartialBalances, Dictionary, MinMax, Position, FundingRateHistory, Liquidation, FundingHistory } from './src/base/types.js';
6
6
  import { BaseError, ExchangeError, PermissionDenied, AccountNotEnabled, AccountSuspended, ArgumentsRequired, BadRequest, BadSymbol, MarginModeAlreadySet, BadResponse, NullResponse, InsufficientFunds, InvalidAddress, InvalidOrder, OrderNotFound, OrderNotCached, CancelPending, OrderImmediatelyFillable, OrderNotFillable, DuplicateOrderId, NotSupported, NetworkError, DDoSProtection, RateLimitExceeded, ExchangeNotAvailable, OnMaintenance, InvalidNonce, RequestTimeout, AuthenticationError, AddressPending, NoChange } from './src/base/errors.js';
7
- declare const version = "4.1.44";
7
+ declare const version = "4.1.45";
8
8
  import ace from './src/ace.js';
9
9
  import alpaca from './src/alpaca.js';
10
10
  import ascendex from './src/ascendex.js';