ccxt 4.3.11__py2.py3-none-any.whl → 4.3.12__py2.py3-none-any.whl

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 (72) hide show
  1. ccxt/__init__.py +1 -1
  2. ccxt/async_support/__init__.py +1 -1
  3. ccxt/async_support/base/exchange.py +1 -1
  4. ccxt/async_support/bigone.py +22 -22
  5. ccxt/async_support/binance.py +7 -7
  6. ccxt/async_support/bingx.py +2 -2
  7. ccxt/async_support/bitget.py +10 -8
  8. ccxt/async_support/bitmart.py +7 -11
  9. ccxt/async_support/bitmex.py +2 -2
  10. ccxt/async_support/bybit.py +75 -65
  11. ccxt/async_support/coinbase.py +8 -8
  12. ccxt/async_support/coinbaseinternational.py +2 -2
  13. ccxt/async_support/coinex.py +501 -445
  14. ccxt/async_support/coinlist.py +12 -12
  15. ccxt/async_support/coinmetro.py +2 -2
  16. ccxt/async_support/cryptocom.py +16 -16
  17. ccxt/async_support/digifinex.py +3 -3
  18. ccxt/async_support/gate.py +2 -2
  19. ccxt/async_support/hitbtc.py +3 -3
  20. ccxt/async_support/htx.py +6 -9
  21. ccxt/async_support/indodax.py +2 -2
  22. ccxt/async_support/kraken.py +3 -1
  23. ccxt/async_support/kucoin.py +4 -4
  24. ccxt/async_support/kucoinfutures.py +6 -6
  25. ccxt/async_support/mexc.py +5 -5
  26. ccxt/async_support/okx.py +9 -9
  27. ccxt/async_support/poloniexfutures.py +4 -4
  28. ccxt/async_support/probit.py +2 -2
  29. ccxt/async_support/whitebit.py +72 -1
  30. ccxt/async_support/woo.py +2 -2
  31. ccxt/base/exchange.py +14 -2
  32. ccxt/base/types.py +25 -0
  33. ccxt/bigone.py +22 -22
  34. ccxt/binance.py +7 -7
  35. ccxt/bingx.py +2 -2
  36. ccxt/bitget.py +10 -8
  37. ccxt/bitmart.py +7 -11
  38. ccxt/bitmex.py +2 -2
  39. ccxt/bybit.py +75 -65
  40. ccxt/coinbase.py +8 -8
  41. ccxt/coinbaseinternational.py +2 -2
  42. ccxt/coinex.py +501 -445
  43. ccxt/coinlist.py +12 -12
  44. ccxt/coinmetro.py +2 -2
  45. ccxt/cryptocom.py +16 -16
  46. ccxt/digifinex.py +3 -3
  47. ccxt/gate.py +2 -2
  48. ccxt/hitbtc.py +3 -3
  49. ccxt/htx.py +6 -9
  50. ccxt/indodax.py +2 -2
  51. ccxt/kraken.py +3 -1
  52. ccxt/kucoin.py +4 -4
  53. ccxt/kucoinfutures.py +6 -6
  54. ccxt/mexc.py +5 -5
  55. ccxt/okx.py +9 -9
  56. ccxt/poloniexfutures.py +4 -4
  57. ccxt/pro/__init__.py +1 -1
  58. ccxt/pro/bitget.py +139 -87
  59. ccxt/pro/bybit.py +192 -12
  60. ccxt/pro/coinbase.py +90 -20
  61. ccxt/pro/mexc.py +21 -1
  62. ccxt/probit.py +2 -2
  63. ccxt/test/base/test_datetime.py +6 -0
  64. ccxt/test/base/test_ledger_entry.py +2 -2
  65. ccxt/whitebit.py +72 -1
  66. ccxt/woo.py +2 -2
  67. {ccxt-4.3.11.dist-info → ccxt-4.3.12.dist-info}/METADATA +4 -4
  68. {ccxt-4.3.11.dist-info → ccxt-4.3.12.dist-info}/RECORD +70 -72
  69. ccxt/async_support/flowbtc.py +0 -34
  70. ccxt/flowbtc.py +0 -34
  71. {ccxt-4.3.11.dist-info → ccxt-4.3.12.dist-info}/WHEEL +0 -0
  72. {ccxt-4.3.11.dist-info → ccxt-4.3.12.dist-info}/top_level.txt +0 -0
ccxt/pro/bitget.py CHANGED
@@ -658,54 +658,69 @@ class bitget(ccxt.async_support.bitget):
658
658
  # "side": "buy",
659
659
  # "tradeId": "1116461060594286593"
660
660
  # }
661
+ # swap private
661
662
  #
662
- # order with trade in it
663
- # {
664
- # accBaseVolume: '0.1',
665
- # baseVolume: '0.1',
666
- # cTime: '1709221342922',
667
- # clientOid: '1147122943507734528',
668
- # enterPointSource: 'API',
669
- # feeDetail: [Array],
670
- # fillFee: '-0.0049578',
671
- # fillFeeCoin: 'USDT',
672
- # fillNotionalUsd: '8.263',
673
- # fillPrice: '82.63',
674
- # fillTime: '1709221342986',
675
- # force: 'gtc',
676
- # instId: 'LTCUSDT',
677
- # leverage: '10',
678
- # marginCoin: 'USDT',
679
- # marginMode: 'crossed',
680
- # notionalUsd: '8.268',
681
- # orderId: '1147122943499345921',
682
- # orderType: 'market',
683
- # pnl: '0',
684
- # posMode: 'hedge_mode',
685
- # posSide: 'short',
686
- # price: '0',
687
- # priceAvg: '82.63',
688
- # reduceOnly: 'no',
689
- # side: 'sell',
690
- # size: '0.1',
691
- # status: 'filled',
692
- # tradeId: '1147122943772479563',
693
- # tradeScope: 'T',
694
- # tradeSide: 'open',
695
- # uTime: '1709221342986'
696
- # }
663
+ # {
664
+ # "orderId": "1169142761031114781",
665
+ # "tradeId": "1169142761312637004",
666
+ # "symbol": "LTCUSDT",
667
+ # "orderType": "market",
668
+ # "side": "buy",
669
+ # "price": "80.87",
670
+ # "baseVolume": "0.1",
671
+ # "quoteVolume": "8.087",
672
+ # "profit": "0",
673
+ # "tradeSide": "open",
674
+ # "posMode": "hedge_mode",
675
+ # "tradeScope": "taker",
676
+ # "feeDetail": [
677
+ # {
678
+ # "feeCoin": "USDT",
679
+ # "deduction": "no",
680
+ # "totalDeductionFee": "0",
681
+ # "totalFee": "-0.0048522"
682
+ # }
683
+ # ],
684
+ # "cTime": "1714471276596",
685
+ # "uTime": "1714471276596"
686
+ # }
687
+ # spot private
688
+ # {
689
+ # "orderId": "1169142457356959747",
690
+ # "tradeId": "1169142457636958209",
691
+ # "symbol": "LTCUSDT",
692
+ # "orderType": "market",
693
+ # "side": "buy",
694
+ # "priceAvg": "81.069",
695
+ # "size": "0.074",
696
+ # "amount": "5.999106",
697
+ # "tradeScope": "taker",
698
+ # "feeDetail": [
699
+ # {
700
+ # "feeCoin": "LTC",
701
+ # "deduction": "no",
702
+ # "totalDeductionFee": "0",
703
+ # "totalFee": "0.000074"
704
+ # }
705
+ # ],
706
+ # "cTime": "1714471204194",
707
+ # "uTime": "1714471204194"
708
+ # }
697
709
  #
698
- instId = self.safe_string(trade, 'instId')
710
+ instId = self.safe_string_2(trade, 'symbol', 'instId')
711
+ posMode = self.safe_string(trade, 'posMode')
712
+ defaultType = 'contract' if (posMode is not None) else 'spot'
699
713
  if market is None:
700
- market = self.safe_market(instId, None, None, 'contract')
714
+ market = self.safe_market(instId, None, None, defaultType)
701
715
  timestamp = self.safe_integer_n(trade, ['uTime', 'cTime', 'ts'])
702
- feeCost = self.safe_string(trade, 'fillFee')
716
+ feeDetail = self.safe_list(trade, 'feeDetail', [])
717
+ first = self.safe_dict(feeDetail, 0)
703
718
  fee = None
704
- if feeCost is not None:
705
- feeCurrencyId = self.safe_string(trade, 'fillFeeCoin')
719
+ if first is not None:
720
+ feeCurrencyId = self.safe_string(first, 'feeCoin')
706
721
  feeCurrencyCode = self.safe_currency_code(feeCurrencyId)
707
722
  fee = {
708
- 'cost': Precise.string_abs(feeCost),
723
+ 'cost': Precise.string_abs(self.safe_string(first, 'totalFee')),
709
724
  'currency': feeCurrencyCode,
710
725
  }
711
726
  return self.safe_trade({
@@ -715,12 +730,12 @@ class bitget(ccxt.async_support.bitget):
715
730
  'timestamp': timestamp,
716
731
  'datetime': self.iso8601(timestamp),
717
732
  'symbol': market['symbol'],
718
- 'type': None,
733
+ 'type': self.safe_string(trade, 'orderType'),
719
734
  'side': self.safe_string(trade, 'side'),
720
- 'takerOrMaker': None,
735
+ 'takerOrMaker': self.safe_string(trade, 'tradeScope'),
721
736
  'price': self.safe_string_2(trade, 'priceAvg', 'price'),
722
- 'amount': self.safe_string(trade, 'size'),
723
- 'cost': self.safe_string(trade, 'fillNotionalUsd'),
737
+ 'amount': self.safe_string_2(trade, 'size', 'baseVolume'),
738
+ 'cost': self.safe_string_2(trade, 'amount', 'quoteVolume'),
724
739
  'fee': fee,
725
740
  }, market)
726
741
 
@@ -1100,8 +1115,6 @@ class bitget(ccxt.async_support.bitget):
1100
1115
  marketSymbols = {}
1101
1116
  for i in range(0, len(data)):
1102
1117
  order = data[i]
1103
- if 'tradeId' in order:
1104
- self.handle_my_trades(client, order)
1105
1118
  marketId = self.safe_string(order, 'instId')
1106
1119
  market = self.safe_market(marketId, None, None, marketType)
1107
1120
  parsed = self.parse_ws_order(order, market)
@@ -1293,8 +1306,6 @@ class bitget(ccxt.async_support.bitget):
1293
1306
  :param dict [params]: extra parameters specific to the exchange API endpoint
1294
1307
  :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
1295
1308
  """
1296
- # only contracts stream provides the trade info consistently in between order updates
1297
- # the spot stream only provides on limit orders updates so we can't support it for spot
1298
1309
  await self.load_markets()
1299
1310
  market = None
1300
1311
  messageHash = 'myTrades'
@@ -1302,16 +1313,12 @@ class bitget(ccxt.async_support.bitget):
1302
1313
  market = self.market(symbol)
1303
1314
  symbol = market['symbol']
1304
1315
  messageHash = messageHash + ':' + symbol
1305
- type = None
1306
- type, params = self.handle_market_type_and_params('watchMyTrades', market, params)
1307
- if type == 'spot':
1308
- raise NotSupported(self.id + ' watchMyTrades is not supported for ' + type + ' markets.')
1309
1316
  instType = None
1310
1317
  instType, params = self.get_inst_type(market, params)
1311
- subscriptionHash = 'order:trades'
1318
+ subscriptionHash = 'fill:' + instType
1312
1319
  args = {
1313
1320
  'instType': instType,
1314
- 'channel': 'orders',
1321
+ 'channel': 'fill',
1315
1322
  'instId': 'default',
1316
1323
  }
1317
1324
  trades = await self.watch_private(messageHash, subscriptionHash, args, params)
@@ -1321,47 +1328,91 @@ class bitget(ccxt.async_support.bitget):
1321
1328
 
1322
1329
  def handle_my_trades(self, client: Client, message):
1323
1330
  #
1324
- # order and trade mixin(contract)
1325
- #
1331
+ # spot
1332
+ # {
1333
+ # "action": "snapshot",
1334
+ # "arg": {
1335
+ # "instType": "SPOT",
1336
+ # "channel": "fill",
1337
+ # "instId": "default"
1338
+ # },
1339
+ # "data": [
1340
+ # {
1341
+ # "orderId": "1169142457356959747",
1342
+ # "tradeId": "1169142457636958209",
1343
+ # "symbol": "LTCUSDT",
1344
+ # "orderType": "market",
1345
+ # "side": "buy",
1346
+ # "priceAvg": "81.069",
1347
+ # "size": "0.074",
1348
+ # "amount": "5.999106",
1349
+ # "tradeScope": "taker",
1350
+ # "feeDetail": [
1351
+ # {
1352
+ # "feeCoin": "LTC",
1353
+ # "deduction": "no",
1354
+ # "totalDeductionFee": "0",
1355
+ # "totalFee": "0.000074"
1356
+ # }
1357
+ # ],
1358
+ # "cTime": "1714471204194",
1359
+ # "uTime": "1714471204194"
1360
+ # }
1361
+ # ],
1362
+ # "ts": 1714471204270
1363
+ # }
1364
+ # swap
1326
1365
  # {
1327
- # "accBaseVolume": "0",
1328
- # "cTime": "1701920553759",
1329
- # "clientOid": "1116501214318198793",
1330
- # "enterPointSource": "WEB",
1331
- # "feeDetail": [{
1332
- # "feeCoin": "USDT",
1333
- # "fee": "-0.162003"
1334
- # }],
1335
- # "force": "gtc",
1336
- # "instId": "BTCUSDT",
1337
- # "leverage": "20",
1338
- # "marginCoin": "USDT",
1339
- # "marginMode": "isolated",
1340
- # "notionalUsd": "105",
1341
- # "orderId": "1116501214293032964",
1342
- # "orderType": "limit",
1343
- # "posMode": "hedge_mode",
1344
- # "posSide": "long",
1345
- # "price": "35000",
1346
- # "reduceOnly": "no",
1347
- # "side": "buy",
1348
- # "size": "0.003",
1349
- # "status": "canceled",
1350
- # "tradeSide": "open",
1351
- # "uTime": "1701920595866"
1366
+ # "action": "snapshot",
1367
+ # "arg": {
1368
+ # "instType": "USDT-FUTURES",
1369
+ # "channel": "fill",
1370
+ # "instId": "default"
1371
+ # },
1372
+ # "data": [
1373
+ # {
1374
+ # "orderId": "1169142761031114781",
1375
+ # "tradeId": "1169142761312637004",
1376
+ # "symbol": "LTCUSDT",
1377
+ # "orderType": "market",
1378
+ # "side": "buy",
1379
+ # "price": "80.87",
1380
+ # "baseVolume": "0.1",
1381
+ # "quoteVolume": "8.087",
1382
+ # "profit": "0",
1383
+ # "tradeSide": "open",
1384
+ # "posMode": "hedge_mode",
1385
+ # "tradeScope": "taker",
1386
+ # "feeDetail": [
1387
+ # {
1388
+ # "feeCoin": "USDT",
1389
+ # "deduction": "no",
1390
+ # "totalDeductionFee": "0",
1391
+ # "totalFee": "-0.0048522"
1392
+ # }
1393
+ # ],
1394
+ # "cTime": "1714471276596",
1395
+ # "uTime": "1714471276596"
1396
+ # }
1397
+ # ],
1398
+ # "ts": 1714471276629
1352
1399
  # }
1353
1400
  #
1354
1401
  if self.myTrades is None:
1355
1402
  limit = self.safe_integer(self.options, 'tradesLimit', 1000)
1356
1403
  self.myTrades = ArrayCache(limit)
1357
1404
  stored = self.myTrades
1358
- parsed = self.parse_ws_trade(message)
1359
- stored.append(parsed)
1360
- symbol = parsed['symbol']
1405
+ data = self.safe_list(message, 'data', [])
1406
+ length = len(data)
1361
1407
  messageHash = 'myTrades'
1408
+ for i in range(0, length):
1409
+ trade = data[i]
1410
+ parsed = self.parse_ws_trade(trade)
1411
+ stored.append(parsed)
1412
+ symbol = parsed['symbol']
1413
+ symbolSpecificMessageHash = 'myTrades:' + symbol
1414
+ client.resolve(stored, symbolSpecificMessageHash)
1362
1415
  client.resolve(stored, messageHash)
1363
- symbolSpecificMessageHash = 'myTrades:' + symbol
1364
- client.resolve(stored, symbolSpecificMessageHash)
1365
1416
 
1366
1417
  async def watch_balance(self, params={}) -> Balances:
1367
1418
  """
@@ -1623,6 +1674,7 @@ class bitget(ccxt.async_support.bitget):
1623
1674
  methods = {
1624
1675
  'ticker': self.handle_ticker,
1625
1676
  'trade': self.handle_trades,
1677
+ 'fill': self.handle_my_trades,
1626
1678
  'orders': self.handle_order,
1627
1679
  'ordersAlgo': self.handle_order,
1628
1680
  'account': self.handle_balance,
ccxt/pro/bybit.py CHANGED
@@ -7,7 +7,7 @@ import ccxt.async_support
7
7
  from ccxt.async_support.base.ws.cache import ArrayCache, ArrayCacheBySymbolById, ArrayCacheBySymbolBySide, ArrayCacheByTimestamp
8
8
  import asyncio
9
9
  import hashlib
10
- from ccxt.base.types import Balances, Int, Order, OrderBook, Position, Str, Strings, Ticker, Tickers, Trade
10
+ from ccxt.base.types import Balances, Int, Num, Order, OrderBook, OrderSide, OrderType, Position, Str, Strings, Ticker, Tickers, Trade
11
11
  from ccxt.async_support.base.ws.client import Client
12
12
  from typing import List
13
13
  from ccxt.base.errors import ExchangeError
@@ -22,7 +22,7 @@ class bybit(ccxt.async_support.bybit):
22
22
  return self.deep_extend(super(bybit, self).describe(), {
23
23
  'has': {
24
24
  'ws': True,
25
- 'createOrderWs': False,
25
+ 'createOrderWs': False, # available only in sandbox
26
26
  'editOrderWs': False,
27
27
  'fetchOpenOrdersWs': False,
28
28
  'fetchOrderWs': False,
@@ -60,6 +60,7 @@ class bybit(ccxt.async_support.bybit):
60
60
  },
61
61
  'contract': 'wss://stream.{hostname}/v5/private',
62
62
  'usdc': 'wss://stream.{hostname}/trade/option/usdc/private/v1',
63
+ 'trade': 'wss://stream-testnet.bybit.com/v5/trade',
63
64
  },
64
65
  },
65
66
  },
@@ -78,6 +79,7 @@ class bybit(ccxt.async_support.bybit):
78
79
  },
79
80
  'contract': 'wss://stream-testnet.{hostname}/v5/private',
80
81
  'usdc': 'wss://stream-testnet.{hostname}/trade/option/usdc/private/v1',
82
+ 'trade': 'wss://stream-testnet.bybit.com/v5/trade',
81
83
  },
82
84
  },
83
85
  },
@@ -172,6 +174,127 @@ class bybit(ccxt.async_support.bybit):
172
174
  params = self.omit(params, ['type', 'subType', 'settle', 'defaultSettle', 'unifiedMargin'])
173
175
  return params
174
176
 
177
+ async def create_order_ws(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
178
+ """
179
+ create a trade order
180
+ :see: https://bybit-exchange.github.io/docs/v5/order/create-order
181
+ :see: https://bybit-exchange.github.io/docs/v5/websocket/trade/guideline#createamendcancel-order
182
+ :param str symbol: unified symbol of the market to create an order in
183
+ :param str type: 'market' or 'limit'
184
+ :param str side: 'buy' or 'sell'
185
+ :param float amount: how much of currency you want to trade in units of base currency
186
+ :param float [price]: the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
187
+ :param dict [params]: extra parameters specific to the exchange API endpoint
188
+ :param str [params.timeInForce]: "GTC", "IOC", "FOK"
189
+ :param bool [params.postOnly]: True or False whether the order is post-only
190
+ :param bool [params.reduceOnly]: True or False whether the order is reduce-only
191
+ :param str [params.positionIdx]: *contracts only* 0 for one-way mode, 1 buy side of hedged mode, 2 sell side of hedged mode
192
+ :param boolean [params.isLeverage]: *unified spot only* False then spot trading True then margin trading
193
+ :param str [params.tpslMode]: *contract only* 'full' or 'partial'
194
+ :param str [params.mmp]: *option only* market maker protection
195
+ :param str [params.triggerDirection]: *contract only* the direction for trigger orders, 'above' or 'below'
196
+ :param float [params.triggerPrice]: The price at which a trigger order is triggered at
197
+ :param float [params.stopLossPrice]: The price at which a stop loss order is triggered at
198
+ :param float [params.takeProfitPrice]: The price at which a take profit order is triggered at
199
+ :param dict [params.takeProfit]: *takeProfit object in params* containing the triggerPrice at which the attached take profit order will be triggered
200
+ :param float [params.takeProfit.triggerPrice]: take profit trigger price
201
+ :param dict [params.stopLoss]: *stopLoss object in params* containing the triggerPrice at which the attached stop loss order will be triggered
202
+ :param float [params.stopLoss.triggerPrice]: stop loss trigger price
203
+ :param str [params.trailingAmount]: the quote amount to trail away from the current market price
204
+ :param str [params.trailingTriggerPrice]: the price to trigger a trailing order, default uses the price argument
205
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
206
+ """
207
+ await self.load_markets()
208
+ orderRequest = self.create_order_request(symbol, type, side, amount, price, params, True)
209
+ url = self.urls['api']['ws']['private']['trade']
210
+ await self.authenticate(url)
211
+ requestId = str(self.request_id())
212
+ request = {
213
+ 'op': 'order.create',
214
+ 'reqId': requestId,
215
+ 'args': [
216
+ orderRequest,
217
+ ],
218
+ 'header': {
219
+ 'X-BAPI-TIMESTAMP': str(self.milliseconds()),
220
+ 'X-BAPI-RECV-WINDOW': str(self.options['recvWindow']),
221
+ },
222
+ }
223
+ return await self.watch(url, requestId, request, requestId, True)
224
+
225
+ async def edit_order_ws(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: Num = None, price: Num = None, params={}):
226
+ """
227
+ edit a trade order
228
+ :see: https://bybit-exchange.github.io/docs/v5/order/amend-order
229
+ :see: https://bybit-exchange.github.io/docs/v5/websocket/trade/guideline#createamendcancel-order
230
+ :param str id: cancel order id
231
+ :param str symbol: unified symbol of the market to create an order in
232
+ :param str type: 'market' or 'limit'
233
+ :param str side: 'buy' or 'sell'
234
+ :param float amount: how much of currency you want to trade in units of base currency
235
+ :param float price: the price at which the order is to be fullfilled, in units of the base currency, ignored in market orders
236
+ :param dict [params]: extra parameters specific to the exchange API endpoint
237
+ :param float [params.triggerPrice]: The price that a trigger order is triggered at
238
+ :param float [params.stopLossPrice]: The price that a stop loss order is triggered at
239
+ :param float [params.takeProfitPrice]: The price that a take profit order is triggered at
240
+ :param dict [params.takeProfit]: *takeProfit object in params* containing the triggerPrice that the attached take profit order will be triggered
241
+ :param float [params.takeProfit.triggerPrice]: take profit trigger price
242
+ :param dict [params.stopLoss]: *stopLoss object in params* containing the triggerPrice that the attached stop loss order will be triggered
243
+ :param float [params.stopLoss.triggerPrice]: stop loss trigger price
244
+ :param str [params.triggerBy]: 'IndexPrice', 'MarkPrice' or 'LastPrice', default is 'LastPrice', required if no initial value for triggerPrice
245
+ :param str [params.slTriggerBy]: 'IndexPrice', 'MarkPrice' or 'LastPrice', default is 'LastPrice', required if no initial value for stopLoss
246
+ :param str [params.tpTriggerby]: 'IndexPrice', 'MarkPrice' or 'LastPrice', default is 'LastPrice', required if no initial value for takeProfit
247
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
248
+ """
249
+ await self.load_markets()
250
+ orderRequest = self.edit_order_request(id, symbol, type, side, amount, price, params)
251
+ url = self.urls['api']['ws']['private']['trade']
252
+ await self.authenticate(url)
253
+ requestId = str(self.request_id())
254
+ request = {
255
+ 'op': 'order.amend',
256
+ 'reqId': requestId,
257
+ 'args': [
258
+ orderRequest,
259
+ ],
260
+ 'header': {
261
+ 'X-BAPI-TIMESTAMP': str(self.milliseconds()),
262
+ 'X-BAPI-RECV-WINDOW': str(self.options['recvWindow']),
263
+ },
264
+ }
265
+ return await self.watch(url, requestId, request, requestId, True)
266
+
267
+ async def cancel_order_ws(self, id: str, symbol: Str = None, params={}):
268
+ """
269
+ cancels an open order
270
+ :see: https://bybit-exchange.github.io/docs/v5/order/cancel-order
271
+ :see: https://bybit-exchange.github.io/docs/v5/websocket/trade/guideline#createamendcancel-order
272
+ :param str id: order id
273
+ :param str symbol: unified symbol of the market the order was made in
274
+ :param dict [params]: extra parameters specific to the exchange API endpoint
275
+ :param boolean [params.stop]: *spot only* whether the order is a stop order
276
+ :param str [params.orderFilter]: *spot only* 'Order' or 'StopOrder' or 'tpslOrder'
277
+ :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
278
+ """
279
+ await self.load_markets()
280
+ orderRequest = self.cancelOrderRequest(id, symbol, params)
281
+ url = self.urls['api']['ws']['private']['trade']
282
+ await self.authenticate(url)
283
+ requestId = str(self.request_id())
284
+ del orderRequest['orderFilter']
285
+ request = {
286
+ 'op': 'order.cancel',
287
+ 'reqId': requestId,
288
+ 'args': [
289
+ orderRequest,
290
+ ],
291
+ 'header': {
292
+ 'X-BAPI-TIMESTAMP': str(self.milliseconds()),
293
+ 'X-BAPI-RECV-WINDOW': str(self.options['recvWindow']),
294
+ },
295
+ }
296
+ return await self.watch(url, requestId, request, requestId, True)
297
+
175
298
  async def watch_ticker(self, symbol: str, params={}) -> Ticker:
176
299
  """
177
300
  watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
@@ -1029,6 +1152,32 @@ class bybit(ccxt.async_support.bybit):
1029
1152
  limit = orders.getLimit(symbol, limit)
1030
1153
  return self.filter_by_symbol_since_limit(orders, symbol, since, limit, True)
1031
1154
 
1155
+ def handle_order_ws(self, client: Client, message):
1156
+ #
1157
+ # {
1158
+ # "reqId":"1",
1159
+ # "retCode":0,
1160
+ # "retMsg":"OK",
1161
+ # "op":"order.create",
1162
+ # "data":{
1163
+ # "orderId":"1673523595617593600",
1164
+ # "orderLinkId":"1673523595617593601"
1165
+ # },
1166
+ # "header":{
1167
+ # "X-Bapi-Limit":"20",
1168
+ # "X-Bapi-Limit-Status":"19",
1169
+ # "X-Bapi-Limit-Reset-Timestamp":"1714235558880",
1170
+ # "Traceid":"584a06d373f2fdcb3a4dfdd81d27df11",
1171
+ # "Timenow":"1714235558881"
1172
+ # },
1173
+ # "connId":"cojidqec0hv9fgvhtbt0-40e"
1174
+ # }
1175
+ #
1176
+ messageHash = self.safe_string(message, 'reqId')
1177
+ data = self.safe_dict(message, 'data')
1178
+ order = self.parse_order(data)
1179
+ client.resolve(order, messageHash)
1180
+
1032
1181
  def handle_order(self, client: Client, message):
1033
1182
  #
1034
1183
  # spot
@@ -1599,11 +1748,32 @@ class bybit(ccxt.async_support.bybit):
1599
1748
  #
1600
1749
  # {code: '-10009', desc: "Invalid period!"}
1601
1750
  #
1602
- code = self.safe_string_2(message, 'code', 'ret_code')
1751
+ # {
1752
+ # "reqId":"1",
1753
+ # "retCode":170131,
1754
+ # "retMsg":"Insufficient balance.",
1755
+ # "op":"order.create",
1756
+ # "data":{
1757
+ #
1758
+ # },
1759
+ # "header":{
1760
+ # "X-Bapi-Limit":"20",
1761
+ # "X-Bapi-Limit-Status":"19",
1762
+ # "X-Bapi-Limit-Reset-Timestamp":"1714236608944",
1763
+ # "Traceid":"3d7168a137bf32a947b7e5e6a575ac7f",
1764
+ # "Timenow":"1714236608946"
1765
+ # },
1766
+ # "connId":"cojifin88smerbj9t560-406"
1767
+ # }
1768
+ #
1769
+ code = self.safe_string_n(message, ['code', 'ret_code', 'retCode'])
1603
1770
  try:
1604
- if code is not None:
1771
+ if code is not None and code != '0':
1605
1772
  feedback = self.id + ' ' + self.json(message)
1606
1773
  self.throw_exactly_matched_exception(self.exceptions['exact'], code, feedback)
1774
+ msg = self.safe_string_2(message, 'retMsg', 'ret_msg')
1775
+ self.throw_broadly_matched_exception(self.exceptions['broad'], msg, feedback)
1776
+ raise ExchangeError(feedback)
1607
1777
  success = self.safe_value(message, 'success')
1608
1778
  if success is not None and not success:
1609
1779
  ret_msg = self.safe_string(message, 'ret_msg')
@@ -1621,7 +1791,8 @@ class bybit(ccxt.async_support.bybit):
1621
1791
  if messageHash in client.subscriptions:
1622
1792
  del client.subscriptions[messageHash]
1623
1793
  else:
1624
- client.reject(error)
1794
+ messageHash = self.safe_string(message, 'reqId')
1795
+ client.reject(error, messageHash)
1625
1796
  return True
1626
1797
 
1627
1798
  def handle_message(self, client: Client, message):
@@ -1638,15 +1809,11 @@ class bybit(ccxt.async_support.bybit):
1638
1809
  self.handle_pong(client, message)
1639
1810
  return
1640
1811
  # pong
1641
- op = self.safe_string(message, 'op')
1642
- if op == 'pong':
1643
- self.handle_pong(client, message)
1644
- return
1645
1812
  event = self.safe_string(message, 'event')
1646
1813
  if event == 'sub':
1647
1814
  self.handle_subscription_status(client, message)
1648
1815
  return
1649
- topic = self.safe_string(message, 'topic', '')
1816
+ topic = self.safe_string_2(message, 'topic', 'op')
1650
1817
  methods = {
1651
1818
  'orderbook': self.handle_order_book,
1652
1819
  'kline': self.handle_ohlcv,
@@ -1662,6 +1829,11 @@ class bybit(ccxt.async_support.bybit):
1662
1829
  'ticketInfo': self.handle_my_trades,
1663
1830
  'user.openapi.perp.trade': self.handle_my_trades,
1664
1831
  'position': self.handle_positions,
1832
+ 'pong': self.handle_pong,
1833
+ 'order.create': self.handle_order_ws,
1834
+ 'order.amend': self.handle_order_ws,
1835
+ 'order.cancel': self.handle_order_ws,
1836
+ 'auth': self.handle_authenticate,
1665
1837
  }
1666
1838
  exacMethod = self.safe_value(methods, topic)
1667
1839
  if exacMethod is not None:
@@ -1676,7 +1848,7 @@ class bybit(ccxt.async_support.bybit):
1676
1848
  return
1677
1849
  # unified auth acknowledgement
1678
1850
  type = self.safe_string(message, 'type')
1679
- if (op == 'auth') or (type == 'AUTH_RESP'):
1851
+ if type == 'AUTH_RESP':
1680
1852
  self.handle_authenticate(client, message)
1681
1853
 
1682
1854
  def ping(self, client):
@@ -1708,9 +1880,17 @@ class bybit(ccxt.async_support.bybit):
1708
1880
  # "conn_id": "ce3dpomvha7dha97tvp0-2xh"
1709
1881
  # }
1710
1882
  #
1883
+ # {
1884
+ # "retCode":0,
1885
+ # "retMsg":"OK",
1886
+ # "op":"auth",
1887
+ # "connId":"cojifin88smerbj9t560-404"
1888
+ # }
1889
+ #
1711
1890
  success = self.safe_value(message, 'success')
1891
+ code = self.safe_integer(message, 'retCode')
1712
1892
  messageHash = 'authenticated'
1713
- if success:
1893
+ if success or code == 0:
1714
1894
  future = self.safe_value(client.futures, messageHash)
1715
1895
  future.resolve(True)
1716
1896
  else: