ccxt 4.5.61 → 4.5.62

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.
@@ -50,6 +50,7 @@ class htx extends htx$1["default"] {
50
50
  'linear': {
51
51
  'public': 'wss://api.hbdm.vn/linear-swap-ws',
52
52
  'private': 'wss://api.hbdm.vn/linear-swap-notification',
53
+ 'privateV5': 'wss://api.hbdm.vn/ws/v5/notification',
53
54
  },
54
55
  'inverse': {
55
56
  'public': 'wss://api.hbdm.vn/ws',
@@ -64,6 +65,7 @@ class htx extends htx$1["default"] {
64
65
  'linear': {
65
66
  'public': 'wss://api.hbdm.vn/linear-swap-ws',
66
67
  'private': 'wss://api.hbdm.vn/linear-swap-notification',
68
+ 'privateV5': 'wss://api.hbdm.vn/ws/v5/notification',
67
69
  },
68
70
  },
69
71
  },
@@ -78,6 +80,7 @@ class htx extends htx$1["default"] {
78
80
  'linear': {
79
81
  'public': 'wss://api.hbdm.vn/linear-swap-ws',
80
82
  'private': 'wss://api.hbdm.vn/linear-swap-notification',
83
+ 'privateV5': 'wss://api.hbdm.vn/ws/v5/notification',
81
84
  },
82
85
  'inverse': {
83
86
  'public': 'wss://api.hbdm.vn/ws',
@@ -88,6 +91,7 @@ class htx extends htx$1["default"] {
88
91
  'linear': {
89
92
  'public': 'wss://api.hbdm.vn/linear-swap-ws',
90
93
  'private': 'wss://api.hbdm.vn/linear-swap-notification',
94
+ 'privateV5': 'wss://api.hbdm.vn/ws/v5/notification',
91
95
  },
92
96
  'inverse': {
93
97
  'public': 'wss://api.hbdm.vn/swap-ws',
@@ -781,6 +785,7 @@ class htx extends htx$1["default"] {
781
785
  * @name htx#watchMyTrades
782
786
  * @description watches information on multiple trades made by the user
783
787
  * @see https://www.htx.com/en-us/opend/newApiPages/?id=7ec53dd5-7773-11ed-9966-0242ac110003
788
+ * @see https://www.htx.com/en-us/opend/newApiPages/?id=8cb89359-77b5-11ed-9966-195a35275ff
784
789
  * @param {string} symbol unified market symbol of the market trades were made in
785
790
  * @param {int} [since] the earliest time in ms to fetch trades for
786
791
  * @param {int} [limit] the maximum number of trade structures to retrieve
@@ -811,6 +816,10 @@ class htx extends htx$1["default"] {
811
816
  subType = this.safeString(params, 'subType', subType);
812
817
  params = this.omit(params, ['type', 'subType']);
813
818
  }
819
+ const linear = (subType === 'linear');
820
+ const swap = (type === 'swap');
821
+ const future = (type === 'future');
822
+ const isV5Linear = (linear && (swap || future));
814
823
  if (type === 'spot') {
815
824
  let mode = undefined;
816
825
  if (mode === undefined) {
@@ -821,6 +830,12 @@ class htx extends htx$1["default"] {
821
830
  messageHash = 'trade.clearing' + '#' + marketId + '#' + mode;
822
831
  channel = messageHash;
823
832
  }
833
+ else if (isV5Linear) {
834
+ const channelAndMessageHashAndParams = this.getV5LinearChannelAndMessageHash('trade', market, params);
835
+ channel = this.safeString(channelAndMessageHashAndParams, 0);
836
+ messageHash = this.safeString(channelAndMessageHashAndParams, 1);
837
+ params = this.safeValue(channelAndMessageHashAndParams, 2, {});
838
+ }
824
839
  else {
825
840
  const channelAndMessageHash = this.getOrderChannelAndMessageHash(type, subType, market, params);
826
841
  channel = this.safeString(channelAndMessageHash, 0);
@@ -829,7 +844,10 @@ class htx extends htx$1["default"] {
829
844
  // like symbol/margin/subtype/type variations
830
845
  messageHash = orderMessageHash + ':' + 'trade';
831
846
  }
832
- trades = await this.subscribePrivate(channel, messageHash, type, subType, params);
847
+ const subscriptionParams = {
848
+ 'isV5': isV5Linear,
849
+ };
850
+ trades = await this.subscribePrivate(channel, messageHash, type, subType, params, subscriptionParams);
833
851
  if (this.newUpdates) {
834
852
  limit = trades.getLimit(symbol, limit);
835
853
  }
@@ -880,11 +898,22 @@ class htx extends htx$1["default"] {
880
898
  }
881
899
  return [channel, messageHash];
882
900
  }
901
+ getV5LinearChannelAndMessageHash(topic, market = undefined, params = {}) {
902
+ const contractCode = (market !== undefined) ? market['id'] : this.safeString(params, 'contract_code', '*');
903
+ const channel = topic;
904
+ const messageHash = (contractCode === '*') ? topic : (topic + '.' + contractCode.toLowerCase());
905
+ params = this.omit(params, 'contract_code');
906
+ const requestParams = this.extend({
907
+ 'contract_code': contractCode,
908
+ }, params);
909
+ return [channel, messageHash, requestParams];
910
+ }
883
911
  /**
884
912
  * @method
885
913
  * @name htx#watchOrders
886
914
  * @description watches information on multiple orders made by the user
887
915
  * @see https://www.htx.com/en-us/opend/newApiPages/?id=7ec53c8f-7773-11ed-9966-0242ac110003
916
+ * @see https://www.htx.com/en-us/opend/newApiPages/?id=8cb89359-77b5-11ed-9966-195a208afe7
888
917
  * @param {string} symbol unified market symbol of the market orders were made in
889
918
  * @param {int} [since] the earliest time in ms to fetch orders for
890
919
  * @param {int} [limit] the maximum number of order structures to retrieve
@@ -911,18 +940,31 @@ class htx extends htx$1["default"] {
911
940
  subType = this.safeString(params, 'subType', subType);
912
941
  params = this.omit(params, ['type', 'subType']);
913
942
  }
943
+ const linear = (subType === 'linear');
944
+ const swap = (type === 'swap');
945
+ const future = (type === 'future');
946
+ const isV5Linear = (linear && (swap || future));
914
947
  let messageHash = undefined;
915
948
  let channel = undefined;
916
949
  if (type === 'spot') {
917
950
  messageHash = 'orders' + '#' + suffix;
918
951
  channel = messageHash;
919
952
  }
953
+ else if (isV5Linear) {
954
+ const channelAndMessageHashAndParams = this.getV5LinearChannelAndMessageHash('orders', market, params);
955
+ channel = this.safeString(channelAndMessageHashAndParams, 0);
956
+ messageHash = this.safeString(channelAndMessageHashAndParams, 1);
957
+ params = this.safeValue(channelAndMessageHashAndParams, 2, {});
958
+ }
920
959
  else {
921
960
  const channelAndMessageHash = this.getOrderChannelAndMessageHash(type, subType, market, params);
922
961
  channel = this.safeString(channelAndMessageHash, 0);
923
962
  messageHash = this.safeString(channelAndMessageHash, 1);
924
963
  }
925
- const orders = await this.subscribePrivate(channel, messageHash, type, subType, params);
964
+ const subscriptionParams = {
965
+ 'isV5': isV5Linear,
966
+ };
967
+ const orders = await this.subscribePrivate(channel, messageHash, type, subType, params, subscriptionParams);
926
968
  if (this.newUpdates) {
927
969
  limit = orders.getLimit(symbol, limit);
928
970
  }
@@ -1049,11 +1091,60 @@ class htx extends htx$1["default"] {
1049
1091
  // }
1050
1092
  //
1051
1093
  //
1094
+ // linear v5 watchOrders
1095
+ //
1096
+ // {
1097
+ // "op": "notify",
1098
+ // "topic": "orders",
1099
+ // "contract_code": "BTC-USDT",
1100
+ // "ts": 1782367563267,
1101
+ // "uid": "359305390",
1102
+ // "data": {
1103
+ // "side": "buy",
1104
+ // "type": "limit",
1105
+ // "price": "40000",
1106
+ // "volume": "1",
1107
+ // "state": "new",
1108
+ // "profit": "0",
1109
+ // "contract_code": "BTC-USDT",
1110
+ // "position_side": "both",
1111
+ // "price_match": null,
1112
+ // "order_id": "1519705236917489664",
1113
+ // "client_order_id": "1519705236917489664",
1114
+ // "margin_mode": "cross",
1115
+ // "lever_rate": 10,
1116
+ // "order_source": "api",
1117
+ // "reduce_only": false,
1118
+ // "time_in_force": "gtc",
1119
+ // "trade_avg_price": "0",
1120
+ // "trade_volume": "0",
1121
+ // "trade_turnover": "0",
1122
+ // "fee_currency": null,
1123
+ // "fee": "0",
1124
+ // "tp_trigger_price": "",
1125
+ // "tp_order_price": "",
1126
+ // "tp_type": "",
1127
+ // "tp_trigger_price_type": "",
1128
+ // "sl_trigger_price": "",
1129
+ // "sl_order_price": "",
1130
+ // "sl_type": "",
1131
+ // "sl_trigger_price_type": "",
1132
+ // "contract_type": "swap",
1133
+ // "cancel_reason": "",
1134
+ // "created_time": "1782367563239",
1135
+ // "updated_time": "1782367563239",
1136
+ // "self_match_prevent": "cancel_taker",
1137
+ // "amend_origin_volume": "",
1138
+ // "amend_source": "",
1139
+ // "amend_result": ""
1140
+ // }
1141
+ // }
1142
+ //
1052
1143
  const messageHash = this.safeString2(message, 'ch', 'topic');
1053
1144
  const data = this.safeValue(message, 'data');
1054
1145
  let marketId = this.safeString(message, 'contract_code');
1055
1146
  if (marketId === undefined) {
1056
- marketId = this.safeString(data, 'symbol');
1147
+ marketId = this.safeString2(data, 'contract_code', 'symbol');
1057
1148
  }
1058
1149
  const market = this.safeMarket(marketId);
1059
1150
  let parsedOrder = undefined;
@@ -1117,6 +1208,10 @@ class htx extends htx$1["default"] {
1117
1208
  const cachedOrders = this.orders;
1118
1209
  cachedOrders.append(parsedOrder);
1119
1210
  client.resolve(this.orders, messageHash);
1211
+ if ((messageHash === 'orders') && (marketId !== undefined)) {
1212
+ const specificMessageHash = messageHash + '.' + marketId.toLowerCase();
1213
+ client.resolve(this.orders, specificMessageHash);
1214
+ }
1120
1215
  // when we make a global subscription (for contracts only) our message hash can't have a symbol/currency attached
1121
1216
  // so we're removing it here
1122
1217
  let genericMessageHash = messageHash.replace('.' + market['lowercaseId'], '');
@@ -1238,22 +1333,64 @@ class htx extends htx$1["default"] {
1238
1333
  // "real_profit": 0
1239
1334
  // }
1240
1335
  //
1241
- const lastTradeTimestamp = this.safeInteger2(order, 'lastActTime', 'ts');
1242
- const created = this.safeInteger(order, 'orderCreateTime');
1336
+ // linear v5 watchOrders
1337
+ //
1338
+ // {
1339
+ // "side": "buy",
1340
+ // "type": "limit",
1341
+ // "price": "40000",
1342
+ // "volume": "1",
1343
+ // "state": "new",
1344
+ // "profit": "0",
1345
+ // "contract_code": "BTC-USDT",
1346
+ // "position_side": "both",
1347
+ // "price_match": null,
1348
+ // "order_id": "1519705236917489664",
1349
+ // "client_order_id": "1519705236917489664",
1350
+ // "margin_mode": "cross",
1351
+ // "lever_rate": 10,
1352
+ // "order_source": "api",
1353
+ // "reduce_only": false,
1354
+ // "time_in_force": "gtc",
1355
+ // "trade_avg_price": "0",
1356
+ // "trade_volume": "0",
1357
+ // "trade_turnover": "0",
1358
+ // "fee_currency": null,
1359
+ // "fee": "0",
1360
+ // "tp_trigger_price": "",
1361
+ // "tp_order_price": "",
1362
+ // "tp_type": "",
1363
+ // "tp_trigger_price_type": "",
1364
+ // "sl_trigger_price": "",
1365
+ // "sl_order_price": "",
1366
+ // "sl_type": "",
1367
+ // "sl_trigger_price_type": "",
1368
+ // "contract_type": "swap",
1369
+ // "cancel_reason": "",
1370
+ // "created_time": "1782367563239",
1371
+ // "updated_time": "1782367563239",
1372
+ // "self_match_prevent": "cancel_taker",
1373
+ // "amend_origin_volume": "",
1374
+ // "amend_source": "",
1375
+ // "amend_result": ""
1376
+ // }
1377
+ //
1378
+ const lastTradeTimestamp = this.safeIntegerN(order, ['lastActTime', 'updated_time', 'ts']);
1379
+ const created = this.safeInteger2(order, 'orderCreateTime', 'created_time');
1243
1380
  const marketId = this.safeString2(order, 'contract_code', 'symbol');
1244
1381
  market = this.safeMarket(marketId, market);
1245
1382
  const symbol = this.safeSymbol(marketId, market);
1246
1383
  const amount = this.safeString2(order, 'orderSize', 'volume');
1247
- const status = this.parseOrderStatus(this.safeString2(order, 'orderStatus', 'status'));
1384
+ const status = this.parseOrderStatus(this.safeStringN(order, ['orderStatus', 'state', 'status']));
1248
1385
  const id = this.safeString2(order, 'orderId', 'order_id');
1249
1386
  const clientOrderId = this.safeString2(order, 'clientOrderId', 'client_order_id');
1250
1387
  const price = this.safeString2(order, 'orderPrice', 'price');
1251
- const filled = this.safeString(order, 'execAmt');
1388
+ const filled = this.safeString2(order, 'execAmt', 'trade_volume');
1252
1389
  const typeSide = this.safeString(order, 'type');
1253
1390
  const feeCost = this.safeString(order, 'fee');
1254
1391
  let fee = undefined;
1255
1392
  if (feeCost !== undefined) {
1256
- const feeCurrencyId = this.safeString(order, 'fee_asset');
1393
+ const feeCurrencyId = this.safeString2(order, 'fee_asset', 'fee_currency');
1257
1394
  fee = {
1258
1395
  'cost': feeCost,
1259
1396
  'currency': this.safeCurrencyCode(feeCurrencyId),
@@ -1262,18 +1399,24 @@ class htx extends htx$1["default"] {
1262
1399
  const avgPrice = this.safeString(order, 'trade_avg_price');
1263
1400
  const rawTrades = this.safeValue(order, 'trade');
1264
1401
  let typeSideParts = [];
1402
+ let type = undefined;
1265
1403
  if (typeSide !== undefined) {
1266
- typeSideParts = typeSide.split('-');
1404
+ if (typeSide.indexOf('-') >= 0) {
1405
+ typeSideParts = typeSide.split('-');
1406
+ type = this.safeStringLower(typeSideParts, 1);
1407
+ }
1408
+ else {
1409
+ type = typeSide;
1410
+ }
1267
1411
  }
1268
- let type = this.safeStringLower(typeSideParts, 1);
1269
1412
  if (type === undefined) {
1270
1413
  type = this.safeString(order, 'order_price_type');
1271
1414
  }
1272
1415
  let side = this.safeStringLower(typeSideParts, 0);
1273
1416
  if (side === undefined) {
1274
- side = this.safeString(order, 'direction');
1417
+ side = this.safeString2(order, 'direction', 'side');
1275
1418
  }
1276
- const cost = this.safeString(order, 'orderValue');
1419
+ const cost = this.safeString2(order, 'orderValue', 'trade_turnover');
1277
1420
  return this.safeOrder({
1278
1421
  'info': order,
1279
1422
  'id': id,
@@ -1284,7 +1427,7 @@ class htx extends htx$1["default"] {
1284
1427
  'status': status,
1285
1428
  'symbol': symbol,
1286
1429
  'type': type,
1287
- 'timeInForce': undefined,
1430
+ 'timeInForce': this.safeStringUpper(order, 'time_in_force'),
1288
1431
  'postOnly': undefined,
1289
1432
  'side': side,
1290
1433
  'price': price,
@@ -1295,6 +1438,11 @@ class htx extends htx$1["default"] {
1295
1438
  'fee': fee,
1296
1439
  'average': avgPrice,
1297
1440
  'trades': rawTrades,
1441
+ 'reduceOnly': this.safeBool(order, 'reduce_only'),
1442
+ 'stopPrice': undefined,
1443
+ 'triggerPrice': undefined,
1444
+ 'takeProfitPrice': this.safeString2(order, 'tp_trigger_price', 'tp_order_price'),
1445
+ 'stopLossPrice': this.safeString2(order, 'sl_trigger_price', 'sl_order_price'),
1298
1446
  }, market);
1299
1447
  }
1300
1448
  parseOrderTrade(trade, market = undefined) {
@@ -1356,11 +1504,10 @@ class htx extends htx$1["default"] {
1356
1504
  /**
1357
1505
  * @method
1358
1506
  * @name htx#watchPositions
1359
- * @see https://www.huobi.com/en-in/opend/newApiPages/?id=8cb7de1c-77b5-11ed-9966-0242ac110003
1360
- * @see https://www.huobi.com/en-in/opend/newApiPages/?id=8cb7df0f-77b5-11ed-9966-0242ac110003
1507
+ * @description watch all open positions. Note: huobi has one channel for each marginMode and type
1361
1508
  * @see https://www.huobi.com/en-in/opend/newApiPages/?id=28c34a7d-77ae-11ed-9966-0242ac110003
1362
1509
  * @see https://www.huobi.com/en-in/opend/newApiPages/?id=5d5156b5-77b6-11ed-9966-0242ac110003
1363
- * @description watch all open positions. Note: huobi has one channel for each marginMode and type
1510
+ * @see https://www.htx.com/en-us/opend/newApiPages/?id=8cb89359-77b5-11ed-9966-195a35d6034
1364
1511
  * @param {string[]} [symbols] list of unified market symbols
1365
1512
  * @param {int} [since] timestamp in ms of the earliest position to fetch
1366
1513
  * @param {int} [limit] the maximum number of positions to fetch
@@ -1391,11 +1538,26 @@ class htx extends htx$1["default"] {
1391
1538
  symbols = this.marketSymbols(symbols);
1392
1539
  let marginMode = undefined;
1393
1540
  [marginMode, params] = this.handleMarginModeAndParams('watchPositions', params, 'cross');
1541
+ const linear = (subType === 'linear');
1542
+ const swap = (type === 'swap');
1543
+ const future = (type === 'future');
1544
+ const isV5Linear = (linear && (swap || future));
1394
1545
  const isLinear = (subType === 'linear');
1395
- const url = this.getUrlByMarketType(type, isLinear, true);
1546
+ const url = this.getUrlByMarketType(type, isLinear, true, false, isV5Linear);
1396
1547
  messageHash = marginMode + ':positions' + messageHash;
1397
- const channel = (marginMode === 'cross') ? 'positions_cross.*' : 'positions.*';
1398
- const newPositions = await this.subscribePrivate(channel, messageHash, type, subType, params);
1548
+ let channel = (marginMode === 'cross') ? 'positions_cross.*' : 'positions.*';
1549
+ if (isV5Linear) {
1550
+ const isOneMarket = (!this.isEmpty(symbols) && (symbols.length === 1));
1551
+ const v5Market = isOneMarket ? market : undefined;
1552
+ const channelAndMessageHashAndParams = this.getV5LinearChannelAndMessageHash('positions', v5Market, params);
1553
+ channel = this.safeString(channelAndMessageHashAndParams, 0);
1554
+ params = this.safeValue(channelAndMessageHashAndParams, 2, {});
1555
+ }
1556
+ const subscriptionParams = {
1557
+ 'isV5': isV5Linear,
1558
+ 'margin': marginMode,
1559
+ };
1560
+ const newPositions = await this.subscribePrivate(channel, messageHash, type, subType, params, subscriptionParams);
1399
1561
  if (this.newUpdates) {
1400
1562
  return newPositions;
1401
1563
  }
@@ -1437,9 +1599,53 @@ class htx extends htx$1["default"] {
1437
1599
  // ]
1438
1600
  // }
1439
1601
  //
1602
+ // watchPositions linear v5
1603
+ //
1604
+ // {
1605
+ // "op": "notify",
1606
+ // "topic": "positions",
1607
+ // "contract_code": "BTC-USDT",
1608
+ // "ts": 1782460576073,
1609
+ // "uid": "359305390",
1610
+ // "event": "snapshot",
1611
+ // "data": [
1612
+ // {
1613
+ // "contract_code": "BTC-USDT",
1614
+ // "symbol": "BTC",
1615
+ // "position_mode": "single_side",
1616
+ // "position_side": "both",
1617
+ // "direction": "buy",
1618
+ // "margin_mode": "cross",
1619
+ // "open_avg_price": "60547.9",
1620
+ // "volume": "1",
1621
+ // "available": "1",
1622
+ // "fee": "0.03632874",
1623
+ // "break_even_price": "60620.55748",
1624
+ // "total_trade_fee": "0.03632874",
1625
+ // "lever_rate": 10,
1626
+ // "adl_risk_percent": 4,
1627
+ // "liquidation_price": "-102094.847680676304309652",
1628
+ // "initial_margin": "6.05807",
1629
+ // "maintenance_margin": "0.20597438",
1630
+ // "profit_unreal": "0.0328",
1631
+ // "profit": "0",
1632
+ // "profit_rate": "0.0054",
1633
+ // "margin_rate": "0.0012",
1634
+ // "state": "normal",
1635
+ // "funding_fee": "0",
1636
+ // "mark_price": "60580.7",
1637
+ // "last_price": "60591.4",
1638
+ // "contract_type": "swap",
1639
+ // "version": 7,
1640
+ // "created_time": "1782460515119",
1641
+ // "updated_time": "1782460515119"
1642
+ // }
1643
+ // ]
1644
+ // }
1645
+ //
1440
1646
  const url = client.url;
1441
1647
  const topic = this.safeString(message, 'topic', '');
1442
- const marginMode = (topic === 'positions_cross') ? 'cross' : 'isolated';
1648
+ const defaultMarginMode = (topic === 'positions_cross') ? 'cross' : 'isolated';
1443
1649
  if (this.positions === undefined) {
1444
1650
  this.positions = {};
1445
1651
  }
@@ -1447,43 +1653,62 @@ class htx extends htx$1["default"] {
1447
1653
  if (clientPositions === undefined) {
1448
1654
  this.positions[url] = {};
1449
1655
  }
1450
- const clientMarginModePositions = this.safeValue(clientPositions, marginMode);
1451
- if (clientMarginModePositions === undefined) {
1452
- this.positions[url][marginMode] = new Cache.ArrayCacheBySymbolBySide();
1453
- }
1454
- const cache = this.positions[url][marginMode];
1455
1656
  const rawPositions = this.safeValue(message, 'data', []);
1456
- const newPositions = [];
1657
+ if (this.isEmpty(rawPositions)) {
1658
+ const prefixes = ['cross:positions', 'isolated:positions'];
1659
+ for (let i = 0; i < prefixes.length; i++) {
1660
+ const messageHashes = this.findMessageHashes(client, prefixes[i]);
1661
+ for (let j = 0; j < messageHashes.length; j++) {
1662
+ client.resolve([], messageHashes[j]);
1663
+ }
1664
+ }
1665
+ return;
1666
+ }
1667
+ const positionsByMarginMode = {};
1457
1668
  const timestamp = this.safeInteger(message, 'ts');
1458
1669
  for (let i = 0; i < rawPositions.length; i++) {
1459
1670
  const rawPosition = rawPositions[i];
1460
1671
  const position = this.parsePosition(rawPosition);
1461
1672
  position['timestamp'] = timestamp;
1462
1673
  position['datetime'] = this.iso8601(timestamp);
1463
- newPositions.push(position);
1674
+ let marginMode = this.safeStringLower(position, 'marginMode', defaultMarginMode);
1675
+ if ((marginMode !== 'cross') && (marginMode !== 'isolated')) {
1676
+ marginMode = defaultMarginMode;
1677
+ }
1678
+ let cache = this.safeValue(this.positions[url], marginMode);
1679
+ if (cache === undefined) {
1680
+ cache = new Cache.ArrayCacheBySymbolBySide();
1681
+ this.positions[url][marginMode] = cache;
1682
+ }
1683
+ positionsByMarginMode[marginMode] = this.safeValue(positionsByMarginMode, marginMode, []);
1684
+ positionsByMarginMode[marginMode].push(position);
1464
1685
  cache.append(position);
1465
1686
  }
1466
- const messageHashes = this.findMessageHashes(client, marginMode + ':positions::');
1467
- for (let i = 0; i < messageHashes.length; i++) {
1468
- const messageHash = messageHashes[i];
1469
- const parts = messageHash.split('::');
1470
- const symbolsString = parts[1];
1471
- const symbols = symbolsString.split(',');
1472
- const positions = this.filterByArray(newPositions, 'symbol', symbols, false);
1473
- if (!this.isEmpty(positions)) {
1474
- client.resolve(positions, messageHash);
1687
+ const marginModes = Object.keys(positionsByMarginMode);
1688
+ for (let i = 0; i < marginModes.length; i++) {
1689
+ const marginMode = marginModes[i];
1690
+ const marginModePositions = this.safeValue(positionsByMarginMode, marginMode, []);
1691
+ const messageHashes = this.findMessageHashes(client, marginMode + ':positions::');
1692
+ for (let j = 0; j < messageHashes.length; j++) {
1693
+ const messageHash = messageHashes[j];
1694
+ const parts = messageHash.split('::');
1695
+ const symbolsString = parts[1];
1696
+ const symbols = symbolsString.split(',');
1697
+ const positions = this.filterByArray(marginModePositions, 'symbol', symbols, false);
1698
+ if (!this.isEmpty(positions)) {
1699
+ client.resolve(positions, messageHash);
1700
+ }
1475
1701
  }
1702
+ client.resolve(marginModePositions, marginMode + ':positions');
1476
1703
  }
1477
- client.resolve(newPositions, marginMode + ':positions');
1478
1704
  }
1479
1705
  /**
1480
1706
  * @method
1481
1707
  * @name htx#watchBalance
1482
1708
  * @description watch balance and get the amount of funds available for trading or funds locked in orders
1483
1709
  * @see https://www.htx.com/en-us/opend/newApiPages/?id=7ec52e28-7773-11ed-9966-0242ac110003
1484
- * @see https://www.htx.com/en-us/opend/newApiPages/?id=10000084-77b7-11ed-9966-0242ac110003
1485
- * @see https://www.htx.com/en-us/opend/newApiPages/?id=8cb7dcca-77b5-11ed-9966-0242ac110003
1486
1710
  * @see https://www.htx.com/en-us/opend/newApiPages/?id=28c34995-77ae-11ed-9966-0242ac110003
1711
+ * @see https://www.htx.com/en-us/opend/newApiPages/?id=8cb89359-77b5-11ed-9966-195a6c94551
1487
1712
  * @param {object} [params] extra parameters specific to the exchange API endpoint
1488
1713
  * @returns {object} a [balance structure]{@link https://docs.ccxt.com/?id=balance-structure}
1489
1714
  */
@@ -1498,12 +1723,22 @@ class htx extends htx$1["default"] {
1498
1723
  let messageHash = undefined;
1499
1724
  let channel = undefined;
1500
1725
  let marginMode = undefined;
1726
+ const linear = (subType === 'linear');
1727
+ const swap = (type === 'swap');
1728
+ const future = (type === 'future');
1729
+ const isV5Linear = (linear && (swap || future));
1501
1730
  if (type === 'spot') {
1502
1731
  let mode = this.safeString2(this.options, 'watchBalance', 'mode', '2');
1503
1732
  mode = this.safeString(params, 'mode', mode);
1504
1733
  messageHash = 'accounts.update' + '#' + mode;
1505
1734
  channel = messageHash;
1506
1735
  }
1736
+ else if (isV5Linear) {
1737
+ marginMode = this.safeString(params, 'margin', 'cross');
1738
+ params = this.omit(params, ['currency', 'symbol', 'margin']);
1739
+ channel = 'account';
1740
+ messageHash = 'account';
1741
+ }
1507
1742
  else {
1508
1743
  const symbol = this.safeString(params, 'symbol');
1509
1744
  const currency = this.safeString(params, 'currency');
@@ -1575,6 +1810,7 @@ class htx extends htx$1["default"] {
1575
1810
  'type': type,
1576
1811
  'subType': subType,
1577
1812
  'margin': marginMode,
1813
+ 'isV5': isV5Linear,
1578
1814
  };
1579
1815
  // we are differentiating the channel from the messageHash for global subscriptions (*)
1580
1816
  // because huobi returns a different topic than the topic sent. Example: we send
@@ -1627,47 +1863,47 @@ class htx extends htx$1["default"] {
1627
1863
  // "uid":"123456789"
1628
1864
  // }
1629
1865
  //
1630
- // usdt / linear future, swap
1866
+ // watchBalance linear v5
1631
1867
  //
1632
1868
  // {
1633
- // "op":"notify",
1634
- // "topic":"accounts.btc-usdt", // or "accounts" for global subscriptions
1635
- // "ts":1603711370689,
1636
- // "event":"order.open",
1637
- // "data":[
1638
- // {
1639
- // "margin_mode":"cross",
1640
- // "margin_account":"USDT",
1641
- // "margin_asset":"USDT",
1642
- // "margin_balance":30.959342395,
1643
- // "margin_static":30.959342395,
1644
- // "margin_position":0,
1645
- // "margin_frozen":10,
1646
- // "profit_real":0,
1647
- // "profit_unreal":0,
1648
- // "withdraw_available":20.959342395,
1649
- // "risk_rate":153.796711975,
1650
- // "position_mode":"dual_side",
1651
- // "contract_detail":[
1652
- // {
1653
- // "symbol":"LTC",
1654
- // "contract_code":"LTC-USDT",
1655
- // "margin_position":0,
1656
- // "margin_frozen":0,
1657
- // "margin_available":20.959342395,
1658
- // "profit_unreal":0,
1659
- // "liquidation_price":null,
1660
- // "lever_rate":1,
1661
- // "adjust_factor":0.01,
1662
- // "contract_type":"swap",
1663
- // "pair":"LTC-USDT",
1664
- // "business_type":"swap",
1665
- // "trade_partition":"USDT"
1666
- // },
1667
- // ],
1668
- // "futures_contract_detail":[],
1669
- // }
1670
- // ]
1869
+ // "op": "notify",
1870
+ // "topic": "account",
1871
+ // "contract_code": "",
1872
+ // "ts": 1782459963509,
1873
+ // "uid": "359305390",
1874
+ // "event": "snapshot",
1875
+ // "data": {
1876
+ // "equity": "0",
1877
+ // "state": "normal",
1878
+ // "details": [
1879
+ // {
1880
+ // "currency": "USDT",
1881
+ // "equity": "162.331953938562004875",
1882
+ // "available": "162.331953938562004875",
1883
+ // "profit_unreal": "0",
1884
+ // "initial_margin": "0",
1885
+ // "maintenance_margin": "0",
1886
+ // "maintenance_margin_rate": "0",
1887
+ // "initial_margin_rate": "0",
1888
+ // "voucher": "0",
1889
+ // "voucher_value": "0",
1890
+ // "created_time": "1770293270932",
1891
+ // "updated_time": "1780329743956",
1892
+ // "isolated_equity": "0",
1893
+ // "isolated_profit_unreal": "0",
1894
+ // "withdraw_available": "162.331953938562004875"
1895
+ // }
1896
+ // ],
1897
+ // "initial_margin": "0",
1898
+ // "maintenance_margin": "0",
1899
+ // "maintenance_margin_rate": "0",
1900
+ // "profit_unreal": "0",
1901
+ // "available_margin": "0",
1902
+ // "created_time": "1770293268881",
1903
+ // "updated_time": "1780329743956",
1904
+ // "version": 5659,
1905
+ // "voucher_value": "0"
1906
+ // }
1671
1907
  // }
1672
1908
  //
1673
1909
  // inverse future
@@ -1716,12 +1952,32 @@ class htx extends htx$1["default"] {
1716
1952
  }
1717
1953
  else {
1718
1954
  // contract balance
1955
+ const topic = this.safeString(message, 'topic');
1956
+ if (topic === 'account') {
1957
+ const accountData = this.safeDict(message, 'data', {});
1958
+ const details = this.safeList(accountData, 'details', []);
1959
+ const detailsLength = details.length;
1960
+ for (let i = 0; i < detailsLength; i++) {
1961
+ const detail = details[i];
1962
+ const currencyId = this.safeString(detail, 'currency');
1963
+ const code = this.safeCurrencyCode(currencyId);
1964
+ if (code === undefined) {
1965
+ continue;
1966
+ }
1967
+ const account = this.account();
1968
+ account['free'] = this.safeString(detail, 'withdraw_available');
1969
+ account['total'] = this.safeString(detail, 'equity');
1970
+ this.balance[code] = account;
1971
+ }
1972
+ this.balance = this.safeBalance(this.balance);
1973
+ client.resolve(this.balance, 'account');
1974
+ return;
1975
+ }
1719
1976
  const dataLength = data.length;
1720
1977
  if (dataLength === 0) {
1721
1978
  return;
1722
1979
  }
1723
1980
  const first = this.safeValue(data, 0, {});
1724
- const topic = this.safeString(message, 'topic');
1725
1981
  const splitTopic = topic.split('.');
1726
1982
  let messageHash = this.safeString(splitTopic, 0);
1727
1983
  let subscription = this.safeValue2(client.subscriptions, messageHash, messageHash + '.*');
@@ -1999,6 +2255,9 @@ class htx extends htx$1["default"] {
1999
2255
  if (topic.indexOf('orders') >= 0) {
2000
2256
  this.handleOrder(client, message);
2001
2257
  }
2258
+ if (topic.indexOf('trade') >= 0) {
2259
+ this.handleMyTrade(client, message);
2260
+ }
2002
2261
  if (topic.indexOf('account') >= 0) {
2003
2262
  this.handleBalance(client, message);
2004
2263
  }
@@ -2289,20 +2548,65 @@ class htx extends htx$1["default"] {
2289
2548
  // ],
2290
2549
  // }
2291
2550
  //
2551
+ // linear v5 watchMyTrades
2552
+ //
2553
+ // {
2554
+ // "op": "notify",
2555
+ // "topic": "trade",
2556
+ // "contract_code": "BTC-USDT",
2557
+ // "ts": 1782367694387,
2558
+ // "uid": "359305390",
2559
+ // "data": [
2560
+ // {
2561
+ // "direction": "buy",
2562
+ // "id": "100121555172810-1519705786942156810-1",
2563
+ // "contract_code": "BTC-USDT",
2564
+ // "contract_type": "swap",
2565
+ // "order_id": "1519705786942156810",
2566
+ // "trade_id": "155233460",
2567
+ // "position_side": "both",
2568
+ // "trade_volume": "1",
2569
+ // "trade_price": "61629",
2570
+ // "trade_turnover": "61.629",
2571
+ // "role": "taker",
2572
+ // "client_order_id": "1519705786942156810",
2573
+ // "created_time": "1782367694375",
2574
+ // "updated_time": "1782367694385"
2575
+ // }
2576
+ // ]
2577
+ // }
2578
+ //
2292
2579
  if (this.myTrades === undefined) {
2293
2580
  const limit = this.safeInteger(this.options, 'tradesLimit', 1000);
2294
2581
  this.myTrades = new Cache.ArrayCacheBySymbolById(limit);
2295
2582
  }
2296
2583
  const cachedTrades = this.myTrades;
2297
- const messageHash = this.safeString(message, 'ch');
2584
+ const messageHash = this.safeString2(message, 'ch', 'topic');
2298
2585
  if (messageHash !== undefined) {
2299
2586
  const data = this.safeValue(message, 'data');
2300
2587
  if (data !== undefined) {
2301
- const parsed = this.parseWsTrade(data);
2302
- const symbol = this.safeString(parsed, 'symbol');
2303
- if (symbol !== undefined) {
2304
- cachedTrades.append(parsed);
2305
- client.resolve(this.myTrades, messageHash);
2588
+ const contractCode = this.safeString(message, 'contract_code');
2589
+ const market = (contractCode !== undefined) ? this.safeMarket(contractCode) : undefined;
2590
+ if (Array.isArray(data)) {
2591
+ for (let i = 0; i < data.length; i++) {
2592
+ const parsed = this.parseWsTrade(data[i], market);
2593
+ const symbol = this.safeString(parsed, 'symbol');
2594
+ if (symbol !== undefined) {
2595
+ cachedTrades.append(parsed);
2596
+ }
2597
+ }
2598
+ }
2599
+ else {
2600
+ const parsed = this.parseWsTrade(data, market);
2601
+ const symbol = this.safeString(parsed, 'symbol');
2602
+ if (symbol !== undefined) {
2603
+ cachedTrades.append(parsed);
2604
+ }
2605
+ }
2606
+ client.resolve(this.myTrades, messageHash);
2607
+ if ((messageHash === 'trade') && (contractCode !== undefined)) {
2608
+ const specificMessageHash = messageHash + '.' + contractCode.toLowerCase();
2609
+ client.resolve(this.myTrades, specificMessageHash);
2306
2610
  }
2307
2611
  }
2308
2612
  else {
@@ -2360,31 +2664,54 @@ class htx extends htx$1["default"] {
2360
2664
  // "feeDeductType":""
2361
2665
  // }
2362
2666
  //
2363
- const symbol = this.safeSymbol(this.safeString(trade, 'symbol'));
2364
- const side = this.safeString2(trade, 'side', 'orderSide');
2365
- const tradeId = this.safeString(trade, 'tradeId');
2366
- const price = this.safeString(trade, 'tradePrice');
2367
- const amount = this.safeString(trade, 'tradeVolume');
2368
- const order = this.safeString(trade, 'orderId');
2369
- const timestamp = this.safeInteger(trade, 'tradeTime');
2370
- market = this.market(symbol);
2371
- const orderType = this.safeString(trade, 'orderType');
2667
+ // linear v5 watchMyTrades
2668
+ //
2669
+ // {
2670
+ // "direction": "buy",
2671
+ // "id": "100121555172810-1519705786942156810-1",
2672
+ // "contract_code": "BTC-USDT",
2673
+ // "contract_type": "swap",
2674
+ // "order_id": "1519705786942156810",
2675
+ // "trade_id": "155233460",
2676
+ // "position_side": "both",
2677
+ // "trade_volume": "1",
2678
+ // "trade_price": "61629",
2679
+ // "trade_turnover": "61.629",
2680
+ // "role": "taker",
2681
+ // "client_order_id": "1519705786942156810",
2682
+ // "created_time": "1782367694375",
2683
+ // "updated_time": "1782367694385"
2684
+ // }
2685
+ //
2686
+ const marketId = this.safeString2(trade, 'symbol', 'contract_code');
2687
+ market = this.safeMarket(marketId, market);
2688
+ const symbol = this.safeString(market, 'symbol');
2689
+ const side = this.safeStringN(trade, ['side', 'orderSide', 'direction']);
2690
+ const tradeId = this.safeStringN(trade, ['tradeId', 'trade_id', 'id']);
2691
+ const price = this.safeString2(trade, 'tradePrice', 'trade_price');
2692
+ const amount = this.safeString2(trade, 'tradeVolume', 'trade_volume');
2693
+ const order = this.safeString2(trade, 'orderId', 'order_id');
2694
+ const timestamp = this.safeIntegerN(trade, ['tradeTime', 'updated_time', 'created_time']);
2695
+ const orderType = this.safeString2(trade, 'orderType', 'type');
2372
2696
  const aggressor = this.safeValue(trade, 'aggressor');
2373
2697
  let takerOrMaker = undefined;
2374
2698
  if (aggressor !== undefined) {
2375
2699
  takerOrMaker = aggressor ? 'taker' : 'maker';
2376
2700
  }
2701
+ else {
2702
+ takerOrMaker = this.safeStringLower(trade, 'role');
2703
+ }
2377
2704
  let type = undefined;
2378
2705
  let orderTypeParts = [];
2379
2706
  if (orderType !== undefined) {
2380
2707
  orderTypeParts = orderType.split('-');
2381
- type = this.safeString(orderTypeParts, 1);
2708
+ type = this.safeString(orderTypeParts, 1, orderType);
2382
2709
  }
2383
2710
  let fee = undefined;
2384
- const feeCurrency = this.safeCurrencyCode(this.safeString(trade, 'feeCurrency'));
2711
+ const feeCurrency = this.safeCurrencyCode(this.safeStringN(trade, ['feeCurrency', 'fee_currency', 'fee_asset']));
2385
2712
  if (feeCurrency !== undefined) {
2386
2713
  fee = {
2387
- 'cost': this.safeString(trade, 'transactFee'),
2714
+ 'cost': this.safeStringN(trade, ['transactFee', 'fee', 'trade_fee']),
2388
2715
  'currency': feeCurrency,
2389
2716
  };
2390
2717
  }
@@ -2404,7 +2731,7 @@ class htx extends htx$1["default"] {
2404
2731
  'fee': fee,
2405
2732
  }, market);
2406
2733
  }
2407
- getUrlByMarketType(type, isLinear = true, isPrivate = false, isFeed = false) {
2734
+ getUrlByMarketType(type, isLinear = true, isPrivate = false, isFeed = false, isV5 = false) {
2408
2735
  const api = this.safeString(this.options, 'api', 'api');
2409
2736
  const hostname = { 'hostname': this.hostname };
2410
2737
  let hostnameURL = undefined;
@@ -2426,7 +2753,17 @@ class htx extends htx$1["default"] {
2426
2753
  else {
2427
2754
  const baseUrl = this.urls['api']['ws'][api][type];
2428
2755
  const subTypeUrl = isLinear ? baseUrl['linear'] : baseUrl['inverse'];
2429
- url = isPrivate ? subTypeUrl['private'] : subTypeUrl['public'];
2756
+ if (isPrivate) {
2757
+ if (isV5 && isLinear) {
2758
+ url = this.safeString(subTypeUrl, 'privateV5', subTypeUrl['private']);
2759
+ }
2760
+ else {
2761
+ url = subTypeUrl['private'];
2762
+ }
2763
+ }
2764
+ else {
2765
+ url = subTypeUrl['public'];
2766
+ }
2430
2767
  }
2431
2768
  return url;
2432
2769
  }
@@ -2494,7 +2831,8 @@ class htx extends htx$1["default"] {
2494
2831
  };
2495
2832
  }
2496
2833
  const isLinear = subtype === 'linear';
2497
- const url = this.getUrlByMarketType(type, isLinear, true);
2834
+ const isV5 = this.safeBool(subscriptionParams, 'isV5', false);
2835
+ const url = this.getUrlByMarketType(type, isLinear, true, false, isV5);
2498
2836
  const hostname = (type === 'spot') ? this.urls['hostnames']['spot'] : this.urls['hostnames']['contract'];
2499
2837
  const authParams = {
2500
2838
  'type': type,