ccxt 4.4.88__py2.py3-none-any.whl → 4.4.91__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 (101) hide show
  1. ccxt/__init__.py +1 -3
  2. ccxt/abstract/bitget.py +58 -0
  3. ccxt/abstract/bitrue.py +65 -65
  4. ccxt/abstract/cryptocom.py +2 -0
  5. ccxt/abstract/luno.py +1 -0
  6. ccxt/async_support/__init__.py +1 -3
  7. ccxt/async_support/base/exchange.py +6 -3
  8. ccxt/async_support/base/ws/client.py +173 -64
  9. ccxt/async_support/base/ws/future.py +23 -50
  10. ccxt/async_support/binance.py +2 -2
  11. ccxt/async_support/bingx.py +55 -29
  12. ccxt/async_support/bitget.py +469 -147
  13. ccxt/async_support/bitmex.py +2 -1
  14. ccxt/async_support/bitrue.py +72 -66
  15. ccxt/async_support/bitvavo.py +34 -0
  16. ccxt/async_support/btcalpha.py +35 -0
  17. ccxt/async_support/btcbox.py +35 -0
  18. ccxt/async_support/btcmarkets.py +35 -0
  19. ccxt/async_support/btcturk.py +35 -0
  20. ccxt/async_support/bybit.py +9 -3
  21. ccxt/async_support/cex.py +61 -0
  22. ccxt/async_support/coinbase.py +1 -3
  23. ccxt/async_support/cryptocom.py +66 -2
  24. ccxt/async_support/cryptomus.py +1 -1
  25. ccxt/async_support/delta.py +2 -2
  26. ccxt/async_support/digifinex.py +39 -99
  27. ccxt/async_support/exmo.py +14 -7
  28. ccxt/async_support/gate.py +14 -7
  29. ccxt/async_support/hashkey.py +15 -28
  30. ccxt/async_support/hollaex.py +27 -22
  31. ccxt/async_support/hyperliquid.py +104 -53
  32. ccxt/async_support/kraken.py +54 -50
  33. ccxt/async_support/luno.py +87 -1
  34. ccxt/async_support/mexc.py +1 -0
  35. ccxt/async_support/modetrade.py +2 -2
  36. ccxt/async_support/okx.py +2 -1
  37. ccxt/async_support/paradex.py +1 -1
  38. ccxt/async_support/phemex.py +16 -8
  39. ccxt/async_support/tradeogre.py +3 -3
  40. ccxt/async_support/xt.py +1 -1
  41. ccxt/base/exchange.py +20 -8
  42. ccxt/binance.py +2 -2
  43. ccxt/bingx.py +55 -29
  44. ccxt/bitget.py +469 -147
  45. ccxt/bitmex.py +2 -1
  46. ccxt/bitrue.py +72 -66
  47. ccxt/bitvavo.py +34 -0
  48. ccxt/btcalpha.py +35 -0
  49. ccxt/btcbox.py +35 -0
  50. ccxt/btcmarkets.py +35 -0
  51. ccxt/btcturk.py +35 -0
  52. ccxt/bybit.py +9 -3
  53. ccxt/cex.py +61 -0
  54. ccxt/coinbase.py +1 -3
  55. ccxt/cryptocom.py +66 -2
  56. ccxt/cryptomus.py +1 -1
  57. ccxt/delta.py +2 -2
  58. ccxt/digifinex.py +39 -99
  59. ccxt/exmo.py +13 -7
  60. ccxt/gate.py +14 -7
  61. ccxt/hashkey.py +15 -28
  62. ccxt/hollaex.py +27 -22
  63. ccxt/hyperliquid.py +104 -53
  64. ccxt/kraken.py +53 -50
  65. ccxt/luno.py +87 -1
  66. ccxt/mexc.py +1 -0
  67. ccxt/modetrade.py +2 -2
  68. ccxt/okx.py +2 -1
  69. ccxt/paradex.py +1 -1
  70. ccxt/phemex.py +16 -8
  71. ccxt/pro/__init__.py +1 -127
  72. ccxt/pro/bitstamp.py +1 -1
  73. ccxt/pro/bybit.py +6 -136
  74. ccxt/pro/coinbase.py +2 -0
  75. ccxt/pro/cryptocom.py +27 -0
  76. ccxt/pro/kraken.py +249 -267
  77. ccxt/pro/mexc.py +0 -1
  78. ccxt/tradeogre.py +3 -3
  79. ccxt/xt.py +1 -1
  80. {ccxt-4.4.88.dist-info → ccxt-4.4.91.dist-info}/METADATA +64 -23
  81. {ccxt-4.4.88.dist-info → ccxt-4.4.91.dist-info}/RECORD +84 -101
  82. ccxt/abstract/coinlist.py +0 -57
  83. ccxt/async_support/base/ws/aiohttp_client.py +0 -147
  84. ccxt/async_support/bitcoincom.py +0 -18
  85. ccxt/async_support/bitfinex1.py +0 -1711
  86. ccxt/async_support/bitpanda.py +0 -17
  87. ccxt/async_support/coinlist.py +0 -2542
  88. ccxt/async_support/poloniexfutures.py +0 -1875
  89. ccxt/bitcoincom.py +0 -18
  90. ccxt/bitfinex1.py +0 -1710
  91. ccxt/bitpanda.py +0 -17
  92. ccxt/coinlist.py +0 -2542
  93. ccxt/poloniexfutures.py +0 -1875
  94. ccxt/pro/bitcoincom.py +0 -35
  95. ccxt/pro/bitfinex1.py +0 -635
  96. ccxt/pro/bitpanda.py +0 -16
  97. ccxt/pro/poloniexfutures.py +0 -1004
  98. ccxt/pro/wazirx.py +0 -766
  99. {ccxt-4.4.88.dist-info → ccxt-4.4.91.dist-info}/LICENSE.txt +0 -0
  100. {ccxt-4.4.88.dist-info → ccxt-4.4.91.dist-info}/WHEEL +0 -0
  101. {ccxt-4.4.88.dist-info → ccxt-4.4.91.dist-info}/top_level.txt +0 -0
ccxt/pro/kraken.py CHANGED
@@ -56,6 +56,7 @@ class kraken(ccxt.async_support.kraken):
56
56
  'public': 'wss://ws.kraken.com',
57
57
  'private': 'wss://ws-auth.kraken.com',
58
58
  'privateV2': 'wss://ws-auth.kraken.com/v2',
59
+ 'publicV2': 'wss://ws.kraken.com/v2',
59
60
  'beta': 'wss://beta-ws.kraken.com',
60
61
  'beta-private': 'wss://beta-ws-auth.kraken.com',
61
62
  },
@@ -70,9 +71,13 @@ class kraken(ccxt.async_support.kraken):
70
71
  'ordersLimit': 1000,
71
72
  'symbolsByOrderId': {},
72
73
  'watchOrderBook': {
73
- 'checksum': True,
74
+ 'checksum': False,
74
75
  },
75
76
  },
77
+ 'streaming': {
78
+ 'ping': self.ping,
79
+ 'keepAlive': 6000,
80
+ },
76
81
  'exceptions': {
77
82
  'ws': {
78
83
  'exact': {
@@ -123,6 +128,7 @@ class kraken(ccxt.async_support.kraken):
123
128
  'EService:Market in post_only mode': NotSupported,
124
129
  'EService:Unavailable': ExchangeNotAvailable,
125
130
  'ETrade:Invalid request': BadRequest,
131
+ 'ESession:Invalid session': AuthenticationError,
126
132
  },
127
133
  },
128
134
  },
@@ -349,10 +355,10 @@ class kraken(ccxt.async_support.kraken):
349
355
 
350
356
  async def cancel_orders_ws(self, ids: List[str], symbol: Str = None, params={}):
351
357
  """
358
+ cancel multiple orders
352
359
 
353
- https://docs.kraken.com/api/docs/websocket-v1/cancelorder
360
+ https://docs.kraken.com/api/docs/websocket-v2/cancel_order
354
361
 
355
- cancel multiple orders
356
362
  :param str[] ids: order ids
357
363
  :param str [symbol]: unified market symbol, default is None
358
364
  :param dict [params]: extra parameters specific to the exchange API endpoint
@@ -377,10 +383,10 @@ class kraken(ccxt.async_support.kraken):
377
383
 
378
384
  async def cancel_order_ws(self, id: str, symbol: Str = None, params={}) -> Order:
379
385
  """
386
+ cancels an open order
380
387
 
381
- https://docs.kraken.com/api/docs/websocket-v1/cancelorder
388
+ https://docs.kraken.com/api/docs/websocket-v2/cancel_order
382
389
 
383
- cancels an open order
384
390
  :param str id: order id
385
391
  :param str [symbol]: unified symbol of the market the order was made in
386
392
  :param dict [params]: extra parameters specific to the exchange API endpoint
@@ -421,10 +427,10 @@ class kraken(ccxt.async_support.kraken):
421
427
 
422
428
  async def cancel_all_orders_ws(self, symbol: Str = None, params={}):
423
429
  """
430
+ cancel all open orders
424
431
 
425
- https://docs.kraken.com/api/docs/websocket-v1/cancelall
432
+ https://docs.kraken.com/api/docs/websocket-v2/cancel_all
426
433
 
427
- cancel all open orders
428
434
  :param str [symbol]: unified market symbol, only orders in the market of self symbol are cancelled when symbol is not None
429
435
  :param dict [params]: extra parameters specific to the exchange API endpoint
430
436
  :returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
@@ -461,53 +467,56 @@ class kraken(ccxt.async_support.kraken):
461
467
  reqId = self.safe_value(message, 'req_id')
462
468
  client.resolve(message, reqId)
463
469
 
464
- def handle_ticker(self, client, message, subscription):
470
+ def handle_ticker(self, client, message):
465
471
  #
466
- # [
467
- # 0, # channelID
468
- # {
469
- # "a": ["5525.40000", 1, "1.000"], # ask, wholeAskVolume, askVolume
470
- # "b": ["5525.10000", 1, "1.000"], # bid, wholeBidVolume, bidVolume
471
- # "c": ["5525.10000", "0.00398963"], # closing price, volume
472
- # "h": ["5783.00000", "5783.00000"], # high price today, high price 24h ago
473
- # "l": ["5505.00000", "5505.00000"], # low price today, low price 24h ago
474
- # "o": ["5760.70000", "5763.40000"], # open price today, open price 24h ago
475
- # "p": ["5631.44067", "5653.78939"], # vwap today, vwap 24h ago
476
- # "t": [11493, 16267], # number of trades today, 24 hours ago
477
- # "v": ["2634.11501494", "3591.17907851"], # volume today, volume 24 hours ago
478
- # },
479
- # "ticker",
480
- # "XBT/USD"
481
- # ]
472
+ # {
473
+ # "channel": "ticker",
474
+ # "type": "snapshot",
475
+ # "data": [
476
+ # {
477
+ # "symbol": "BTC/USD",
478
+ # "bid": 108359.8,
479
+ # "bid_qty": 0.01362603,
480
+ # "ask": 108359.9,
481
+ # "ask_qty": 17.17988863,
482
+ # "last": 108359.8,
483
+ # "volume": 2158.32346723,
484
+ # "vwap": 108894.5,
485
+ # "low": 106824,
486
+ # "high": 111300,
487
+ # "change": -2679.9,
488
+ # "change_pct": -2.41
489
+ # }
490
+ # ]
491
+ # }
482
492
  #
483
- wsName = message[3]
484
- market = self.safe_value(self.options['marketsByWsName'], wsName)
485
- symbol = market['symbol']
493
+ data = self.safe_list(message, 'data', [])
494
+ ticker = data[0]
495
+ symbol = self.safe_string(ticker, 'symbol')
486
496
  messageHash = self.get_message_hash('ticker', None, symbol)
487
- ticker = message[1]
488
- vwap = self.safe_string(ticker['p'], 0)
497
+ vwap = self.safe_string(ticker, 'vwap')
489
498
  quoteVolume = None
490
- baseVolume = self.safe_string(ticker['v'], 0)
499
+ baseVolume = self.safe_string(ticker, 'volume')
491
500
  if baseVolume is not None and vwap is not None:
492
501
  quoteVolume = Precise.string_mul(baseVolume, vwap)
493
- last = self.safe_string(ticker['c'], 0)
502
+ last = self.safe_string(ticker, 'last')
494
503
  result = self.safe_ticker({
495
504
  'symbol': symbol,
496
505
  'timestamp': None,
497
506
  'datetime': None,
498
- 'high': self.safe_string(ticker['h'], 0),
499
- 'low': self.safe_string(ticker['l'], 0),
500
- 'bid': self.safe_string(ticker['b'], 0),
501
- 'bidVolume': self.safe_string(ticker['b'], 2),
502
- 'ask': self.safe_string(ticker['a'], 0),
503
- 'askVolume': self.safe_string(ticker['a'], 2),
507
+ 'high': self.safe_string(ticker, 'high'),
508
+ 'low': self.safe_string(ticker, 'low'),
509
+ 'bid': self.safe_string(ticker, 'bid'),
510
+ 'bidVolume': self.safe_string(ticker, 'bid_qty'),
511
+ 'ask': self.safe_string(ticker, 'ask'),
512
+ 'askVolume': self.safe_string(ticker, 'ask_qty'),
504
513
  'vwap': vwap,
505
- 'open': self.safe_string(ticker['o'], 0),
514
+ 'open': None,
506
515
  'close': last,
507
516
  'last': last,
508
517
  'previousClose': None,
509
- 'change': None,
510
- 'percentage': None,
518
+ 'change': self.safe_string(ticker, 'change'),
519
+ 'percentage': self.safe_string(ticker, 'change_pct'),
511
520
  'average': None,
512
521
  'baseVolume': baseVolume,
513
522
  'quoteVolume': quoteVolume,
@@ -516,30 +525,35 @@ class kraken(ccxt.async_support.kraken):
516
525
  self.tickers[symbol] = result
517
526
  client.resolve(result, messageHash)
518
527
 
519
- def handle_trades(self, client: Client, message, subscription):
528
+ def handle_trades(self, client: Client, message):
520
529
  #
521
- # [
522
- # 0, # channelID
523
- # [ # price volume time side type misc
524
- # ["5541.20000", "0.15850568", "1534614057.321596", "s", "l", ""],
525
- # ["6060.00000", "0.02455000", "1534614057.324998", "b", "l", ""],
526
- # ],
527
- # "trade",
528
- # "XBT/USD"
529
- # ]
530
+ # {
531
+ # "channel": "trade",
532
+ # "type": "update",
533
+ # "data": [
534
+ # {
535
+ # "symbol": "MATIC/USD",
536
+ # "side": "sell",
537
+ # "price": 0.5117,
538
+ # "qty": 40.0,
539
+ # "ord_type": "market",
540
+ # "trade_id": 4665906,
541
+ # "timestamp": "2023-09-25T07:49:37.708706Z"
542
+ # }
543
+ # ]
544
+ # }
530
545
  #
531
- wsName = self.safe_string(message, 3)
532
- name = self.safe_string(message, 2)
533
- market = self.safe_value(self.options['marketsByWsName'], wsName)
534
- symbol = market['symbol']
535
- messageHash = self.get_message_hash(name, None, symbol)
546
+ data = self.safe_list(message, 'data', [])
547
+ trade = data[0]
548
+ symbol = self.safe_string(trade, 'symbol')
549
+ messageHash = self.get_message_hash('trade', None, symbol)
536
550
  stored = self.safe_value(self.trades, symbol)
537
551
  if stored is None:
538
552
  limit = self.safe_integer(self.options, 'tradesLimit', 1000)
539
553
  stored = ArrayCache(limit)
540
554
  self.trades[symbol] = stored
541
- trades = self.safe_value(message, 1, [])
542
- parsed = self.parse_trades(trades, market)
555
+ market = self.market(symbol)
556
+ parsed = self.parse_trades(data, market)
543
557
  for i in range(0, len(parsed)):
544
558
  stored.append(parsed[i])
545
559
  client.resolve(stored, messageHash)
@@ -624,7 +638,7 @@ class kraken(ccxt.async_support.kraken):
624
638
  """
625
639
  watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
626
640
 
627
- https://docs.kraken.com/api/docs/websocket-v1/ticker
641
+ https://docs.kraken.com/api/docs/websocket-v2/ticker
628
642
 
629
643
  :param str symbol: unified symbol of the market to fetch the ticker for
630
644
  :param dict [params]: extra parameters specific to the exchange API endpoint
@@ -639,7 +653,7 @@ class kraken(ccxt.async_support.kraken):
639
653
  """
640
654
  watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
641
655
 
642
- https://docs.kraken.com/api/docs/websocket-v1/ticker
656
+ https://docs.kraken.com/api/docs/websocket-v2/ticker
643
657
 
644
658
  :param str[] symbols:
645
659
  :param dict [params]: extra parameters specific to the exchange API endpoint
@@ -656,66 +670,29 @@ class kraken(ccxt.async_support.kraken):
656
670
 
657
671
  async def watch_bids_asks(self, symbols: Strings = None, params={}) -> Tickers:
658
672
  """
673
+ watches best bid & ask for symbols
659
674
 
660
- https://docs.kraken.com/api/docs/websocket-v1/spread
675
+ https://docs.kraken.com/api/docs/websocket-v2/ticker
661
676
 
662
- watches best bid & ask for symbols
663
677
  :param str[] symbols: unified symbol of the market to fetch the ticker for
664
678
  :param dict [params]: extra parameters specific to the exchange API endpoint
665
679
  :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
666
680
  """
667
681
  await self.load_markets()
668
682
  symbols = self.market_symbols(symbols, None, False)
669
- ticker = await self.watch_multi_helper('bidask', 'spread', symbols, None, params)
683
+ params['event_trigger'] = 'bbo'
684
+ ticker = await self.watch_multi_helper('bidask', 'ticker', symbols, None, params)
670
685
  if self.newUpdates:
671
686
  result: dict = {}
672
687
  result[ticker['symbol']] = ticker
673
688
  return result
674
689
  return self.filter_by_array(self.bidsasks, 'symbol', symbols)
675
690
 
676
- def handle_bid_ask(self, client: Client, message, subscription):
677
- #
678
- # [
679
- # 7208974, # channelID
680
- # [
681
- # "63758.60000", # bid
682
- # "63759.10000", # ask
683
- # "1726814731.089778", # timestamp
684
- # "0.00057917", # bid_volume
685
- # "0.15681688" # ask_volume
686
- # ],
687
- # "spread",
688
- # "XBT/USDT"
689
- # ]
690
- #
691
- parsedTicker = self.parse_ws_bid_ask(message)
692
- symbol = parsedTicker['symbol']
693
- self.bidsasks[symbol] = parsedTicker
694
- messageHash = self.get_message_hash('bidask', None, symbol)
695
- client.resolve(parsedTicker, messageHash)
696
-
697
- def parse_ws_bid_ask(self, ticker, market=None):
698
- data = self.safe_list(ticker, 1, [])
699
- marketId = self.safe_string(ticker, 3)
700
- market = self.safe_value(self.options['marketsByWsName'], marketId)
701
- symbol = self.safe_string(market, 'symbol')
702
- timestamp = self.parse_to_int(self.safe_integer(data, 2)) * 1000
703
- return self.safe_ticker({
704
- 'symbol': symbol,
705
- 'timestamp': timestamp,
706
- 'datetime': self.iso8601(timestamp),
707
- 'ask': self.safe_string(data, 1),
708
- 'askVolume': self.safe_string(data, 4),
709
- 'bid': self.safe_string(data, 0),
710
- 'bidVolume': self.safe_string(data, 3),
711
- 'info': ticker,
712
- }, market)
713
-
714
691
  async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
715
692
  """
716
693
  get the list of most recent trades for a particular symbol
717
694
 
718
- https://docs.kraken.com/api/docs/websocket-v1/trade
695
+ https://docs.kraken.com/api/docs/websocket-v2/trade
719
696
 
720
697
  :param str symbol: unified symbol of the market to fetch trades for
721
698
  :param int [since]: timestamp in ms of the earliest trade to fetch
@@ -727,10 +704,10 @@ class kraken(ccxt.async_support.kraken):
727
704
 
728
705
  async def watch_trades_for_symbols(self, symbols: List[str], since: Int = None, limit: Int = None, params={}) -> List[Trade]:
729
706
  """
707
+ get the list of most recent trades for a list of symbols
730
708
 
731
- https://docs.kraken.com/api/docs/websocket-v1/trade
709
+ https://docs.kraken.com/api/docs/websocket-v2/trade
732
710
 
733
- get the list of most recent trades for a list of symbols
734
711
  :param str[] symbols: unified symbol of the market to fetch trades for
735
712
  :param int [since]: timestamp in ms of the earliest trade to fetch
736
713
  :param int [limit]: the maximum amount of trades to fetch
@@ -748,7 +725,7 @@ class kraken(ccxt.async_support.kraken):
748
725
  """
749
726
  watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
750
727
 
751
- https://docs.kraken.com/api/docs/websocket-v1/book
728
+ https://docs.kraken.com/api/docs/websocket-v2/book
752
729
 
753
730
  :param str symbol: unified symbol of the market to fetch the order book for
754
731
  :param int [limit]: the maximum amount of order book entries to return
@@ -761,7 +738,7 @@ class kraken(ccxt.async_support.kraken):
761
738
  """
762
739
  watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
763
740
 
764
- https://docs.kraken.com/api/docs/websocket-v1/book
741
+ https://docs.kraken.com/api/docs/websocket-v2/book
765
742
 
766
743
  :param str[] symbols: unified array of symbols
767
744
  :param int [limit]: the maximum amount of order book entries to return
@@ -771,7 +748,7 @@ class kraken(ccxt.async_support.kraken):
771
748
  request: dict = {}
772
749
  if limit is not None:
773
750
  if self.in_array(limit, [10, 25, 100, 500, 1000]):
774
- request['subscription'] = {
751
+ request['params'] = {
775
752
  'depth': limit, # default 10, valid options 10, 25, 100, 500, 1000
776
753
  }
777
754
  else:
@@ -825,18 +802,25 @@ class kraken(ccxt.async_support.kraken):
825
802
  for i in range(0, len(self.symbols)):
826
803
  symbol = self.symbols[i]
827
804
  market = self.markets[symbol]
828
- if market['darkpool']:
829
- info = self.safe_value(market, 'info', {})
830
- altname = self.safe_string(info, 'altname')
831
- wsName = altname[0:3] + '/' + altname[3:]
832
- marketsByWsName[wsName] = market
833
- else:
834
- info = self.safe_value(market, 'info', {})
835
- wsName = self.safe_string(info, 'wsname')
836
- marketsByWsName[wsName] = market
805
+ info = self.safe_value(market, 'info', {})
806
+ wsName = self.safe_string(info, 'wsname')
807
+ marketsByWsName[wsName] = market
837
808
  self.options['marketsByWsName'] = marketsByWsName
838
809
  return markets
839
810
 
811
+ def ping(self, client: Client):
812
+ url = client.url
813
+ request = {}
814
+ if url.find('v2') >= 0:
815
+ request['method'] = 'ping'
816
+ else:
817
+ request['event'] = 'ping'
818
+ return request
819
+
820
+ def handle_pong(self, client: Client, message):
821
+ client.lastPong = self.milliseconds()
822
+ return message
823
+
840
824
  async def watch_heartbeat(self, params={}):
841
825
  await self.load_markets()
842
826
  event = 'heartbeat'
@@ -852,157 +836,151 @@ class kraken(ccxt.async_support.kraken):
852
836
  event = self.safe_string(message, 'event')
853
837
  client.resolve(message, event)
854
838
 
855
- def handle_order_book(self, client: Client, message, subscription):
839
+ def handle_order_book(self, client: Client, message):
856
840
  #
857
841
  # first message(snapshot)
858
842
  #
859
- # [
860
- # 1234, # channelID
861
- # {
862
- # "as": [
863
- # ["5541.30000", "2.50700000", "1534614248.123678"],
864
- # ["5541.80000", "0.33000000", "1534614098.345543"],
865
- # ["5542.70000", "0.64700000", "1534614244.654432"]
866
- # ],
867
- # "bs": [
868
- # ["5541.20000", "1.52900000", "1534614248.765567"],
869
- # ["5539.90000", "0.30000000", "1534614241.769870"],
870
- # ["5539.50000", "5.00000000", "1534613831.243486"]
871
- # ]
872
- # },
873
- # "book-10",
874
- # "XBT/USD"
875
- # ]
843
+ # {
844
+ # "channel": "book",
845
+ # "type": "snapshot",
846
+ # "data": [
847
+ # {
848
+ # "symbol": "MATIC/USD",
849
+ # "bids": [
850
+ # {
851
+ # "price": 0.5666,
852
+ # "qty": 4831.75496356
853
+ # },
854
+ # {
855
+ # "price": 0.5665,
856
+ # "qty": 6658.22734739
857
+ # }
858
+ # ],
859
+ # "asks": [
860
+ # {
861
+ # "price": 0.5668,
862
+ # "qty": 4410.79769741
863
+ # },
864
+ # {
865
+ # "price": 0.5669,
866
+ # "qty": 4655.40412487
867
+ # }
868
+ # ],
869
+ # "checksum": 2439117997
870
+ # }
871
+ # ]
872
+ # }
876
873
  #
877
874
  # subsequent updates
878
875
  #
879
- # [
880
- # 1234,
881
- # { # optional
882
- # "a": [
883
- # ["5541.30000", "2.50700000", "1534614248.456738"],
884
- # ["5542.50000", "0.40100000", "1534614248.456738"]
885
- # ]
886
- # },
887
- # { # optional
888
- # "b": [
889
- # ["5541.30000", "0.00000000", "1534614335.345903"]
890
- # ]
891
- # },
892
- # "book-10",
893
- # "XBT/USD"
894
- # ]
876
+ # {
877
+ # "channel": "book",
878
+ # "type": "update",
879
+ # "data": [
880
+ # {
881
+ # "symbol": "MATIC/USD",
882
+ # "bids": [
883
+ # {
884
+ # "price": 0.5657,
885
+ # "qty": 1098.3947558
886
+ # }
887
+ # ],
888
+ # "asks": [],
889
+ # "checksum": 2114181697,
890
+ # "timestamp": "2023-10-06T17:35:55.440295Z"
891
+ # }
892
+ # ]
893
+ # }
895
894
  #
896
- messageLength = len(message)
897
- wsName = message[messageLength - 1]
898
- bookDepthString = message[messageLength - 2]
899
- parts = bookDepthString.split('-')
900
- depth = self.safe_integer(parts, 1, 10)
901
- market = self.safe_value(self.options['marketsByWsName'], wsName)
902
- symbol = market['symbol']
903
- timestamp = None
895
+ type = self.safe_string(message, 'type')
896
+ data = self.safe_list(message, 'data', [])
897
+ first = self.safe_dict(data, 0, {})
898
+ symbol = self.safe_string(first, 'symbol')
899
+ a = self.safe_value(first, 'asks', [])
900
+ b = self.safe_value(first, 'bids', [])
901
+ c = self.safe_integer(first, 'checksum')
904
902
  messageHash = self.get_message_hash('orderbook', None, symbol)
905
- # if self is a snapshot
906
- if 'as' in message[1]:
907
- # todo get depth from marketsByWsName
908
- self.orderbooks[symbol] = self.order_book({}, depth)
903
+ orderbook = None
904
+ if type == 'update':
909
905
  orderbook = self.orderbooks[symbol]
910
- sides: dict = {
911
- 'as': 'asks',
912
- 'bs': 'bids',
913
- }
914
- keys = list(sides.keys())
915
- for i in range(0, len(keys)):
916
- key = keys[i]
917
- side = sides[key]
918
- bookside = orderbook[side]
919
- deltas = self.safe_value(message[1], key, [])
920
- timestamp = self.custom_handle_deltas(bookside, deltas, timestamp)
921
- orderbook['symbol'] = symbol
922
- orderbook['timestamp'] = timestamp
923
- orderbook['datetime'] = self.iso8601(timestamp)
924
- client.resolve(orderbook, messageHash)
925
- else:
926
- orderbook = self.orderbooks[symbol]
927
- # else, if self is an orderbook update
928
- a = None
929
- b = None
930
- c = None
931
- if messageLength == 5:
932
- a = self.safe_value(message[1], 'a', [])
933
- b = self.safe_value(message[2], 'b', [])
934
- c = self.safe_integer(message[1], 'c')
935
- c = self.safe_integer(message[2], 'c', c)
936
- else:
937
- c = self.safe_integer(message[1], 'c')
938
- if 'a' in message[1]:
939
- a = self.safe_value(message[1], 'a', [])
940
- else:
941
- b = self.safe_value(message[1], 'b', [])
942
906
  storedAsks = orderbook['asks']
943
907
  storedBids = orderbook['bids']
944
- example = None
945
908
  if a is not None:
946
- timestamp = self.custom_handle_deltas(storedAsks, a, timestamp)
947
- example = self.safe_value(a, 0)
909
+ self.custom_handle_deltas(storedAsks, a)
948
910
  if b is not None:
949
- timestamp = self.custom_handle_deltas(storedBids, b, timestamp)
950
- example = self.safe_value(b, 0)
951
- # don't remove self line or I will poop on your face
952
- orderbook.limit()
953
- checksum = self.handle_option('watchOrderBook', 'checksum', True)
954
- if checksum:
955
- priceString = self.safe_string(example, 0)
956
- amountString = self.safe_string(example, 1)
957
- priceParts = priceString.split('.')
958
- amountParts = amountString.split('.')
959
- priceLength = len(priceParts[1]) - 0
960
- amountLength = len(amountParts[1]) - 0
961
- payloadArray = []
962
- if c is not None:
963
- for i in range(0, 10):
964
- formatted = self.format_number(storedAsks[i][0], priceLength) + self.format_number(storedAsks[i][1], amountLength)
965
- payloadArray.append(formatted)
966
- for i in range(0, 10):
967
- formatted = self.format_number(storedBids[i][0], priceLength) + self.format_number(storedBids[i][1], amountLength)
968
- payloadArray.append(formatted)
969
- payload = ''.join(payloadArray)
970
- localChecksum = self.crc32(payload, False)
971
- if localChecksum != c:
972
- error = ChecksumError(self.id + ' ' + self.orderbook_checksum_message(symbol))
973
- del client.subscriptions[messageHash]
974
- del self.orderbooks[symbol]
975
- client.reject(error, messageHash)
976
- return
911
+ self.custom_handle_deltas(storedBids, b)
912
+ datetime = self.safe_string(first, 'timestamp')
977
913
  orderbook['symbol'] = symbol
978
- orderbook['timestamp'] = timestamp
979
- orderbook['datetime'] = self.iso8601(timestamp)
980
- client.resolve(orderbook, messageHash)
981
-
982
- def format_number(self, n, length):
983
- stringNumber = self.number_to_string(n)
984
- parts = stringNumber.split('.')
914
+ orderbook['timestamp'] = self.parse8601(datetime)
915
+ orderbook['datetime'] = datetime
916
+ else:
917
+ # snapshot
918
+ depth = len(a)
919
+ self.orderbooks[symbol] = self.order_book({}, depth)
920
+ orderbook = self.orderbooks[symbol]
921
+ keys = ['asks', 'bids']
922
+ for i in range(0, len(keys)):
923
+ key = keys[i]
924
+ bookside = orderbook[key]
925
+ deltas = self.safe_value(first, key, [])
926
+ if len(deltas) > 0:
927
+ self.custom_handle_deltas(bookside, deltas)
928
+ orderbook['symbol'] = symbol
929
+ orderbook.limit()
930
+ # checksum temporarily disabled because the exchange checksum was not reliable
931
+ checksum = self.handle_option('watchOrderBook', 'checksum', False)
932
+ if checksum:
933
+ payloadArray = []
934
+ if c is not None:
935
+ checkAsks = orderbook['asks']
936
+ checkBids = orderbook['bids']
937
+ # checkAsks = asks.map((elem) => [elem['price'], elem['qty']])
938
+ # checkBids = bids.map((elem) => [elem['price'], elem['qty']])
939
+ for i in range(0, 10):
940
+ currentAsk = self.safe_value(checkAsks, i, {})
941
+ formattedAsk = self.format_number(currentAsk[0]) + self.format_number(currentAsk[1])
942
+ payloadArray.append(formattedAsk)
943
+ for i in range(0, 10):
944
+ currentBid = self.safe_value(checkBids, i, {})
945
+ formattedBid = self.format_number(currentBid[0]) + self.format_number(currentBid[1])
946
+ payloadArray.append(formattedBid)
947
+ payload = ''.join(payloadArray)
948
+ localChecksum = self.crc32(payload, False)
949
+ if localChecksum != c:
950
+ error = ChecksumError(self.id + ' ' + self.orderbook_checksum_message(symbol))
951
+ del client.subscriptions[messageHash]
952
+ del self.orderbooks[symbol]
953
+ client.reject(error, messageHash)
954
+ return
955
+ client.resolve(orderbook, messageHash)
956
+
957
+ def custom_handle_deltas(self, bookside, deltas):
958
+ # sortOrder = True if (key == 'bids') else False
959
+ for j in range(0, len(deltas)):
960
+ delta = deltas[j]
961
+ price = self.safe_number(delta, 'price')
962
+ amount = self.safe_number(delta, 'qty')
963
+ bookside.store(price, amount)
964
+ # if amount == 0:
965
+ # index = bookside.findIndex((x: Int) => x[0] == price)
966
+ # bookside.splice(index, 1)
967
+ # else:
968
+ # bookside.store(price, amount)
969
+ # }
970
+ # bookside = self.sort_by(bookside, 0, sortOrder)
971
+ # bookside[0:9]
972
+
973
+ def format_number(self, data):
974
+ parts = data.split('.')
985
975
  integer = self.safe_string(parts, 0)
986
976
  decimals = self.safe_string(parts, 1, '')
987
- paddedDecimals = decimals.ljust(length, '0')
988
- joined = integer + paddedDecimals
977
+ joinedResult = integer + decimals
989
978
  i = 0
990
- while(joined[i] == '0'):
979
+ while(joinedResult[i] == '0'):
991
980
  i += 1
992
981
  if i > 0:
993
- return joined[i:]
994
- else:
995
- return joined
996
-
997
- def custom_handle_deltas(self, bookside, deltas, timestamp=None):
998
- for j in range(0, len(deltas)):
999
- delta = deltas[j]
1000
- price = self.parse_number(delta[0])
1001
- amount = self.parse_number(delta[1])
1002
- oldTimestamp = timestamp if timestamp else 0
1003
- timestamp = max(oldTimestamp, self.parse_to_int(float(delta[2]) * 1000))
1004
- bookside.store(price, amount)
1005
- return timestamp
982
+ joinedResult = joinedResult[i:]
983
+ return joinedResult
1006
984
 
1007
985
  def handle_system_status(self, client: Client, message):
1008
986
  #
@@ -1038,7 +1016,11 @@ class kraken(ccxt.async_support.kraken):
1038
1016
  client = self.client(url)
1039
1017
  authenticated = 'authenticated'
1040
1018
  subscription = self.safe_value(client.subscriptions, authenticated)
1041
- if subscription is None:
1019
+ now = self.seconds()
1020
+ start = self.safe_integer(subscription, 'start')
1021
+ expires = self.safe_integer(subscription, 'expires')
1022
+ if (subscription is None) or ((subscription is not None) and (start + expires) <= now):
1023
+ # https://docs.kraken.com/api/docs/rest-api/get-websockets-token
1042
1024
  response = await self.privatePostGetWebSocketsToken(params)
1043
1025
  #
1044
1026
  # {
@@ -1049,7 +1031,8 @@ class kraken(ccxt.async_support.kraken):
1049
1031
  # }
1050
1032
  # }
1051
1033
  #
1052
- subscription = self.safe_value(response, 'result')
1034
+ subscription = self.safe_dict(response, 'result')
1035
+ subscription['start'] = now
1053
1036
  client.subscriptions[authenticated] = subscription
1054
1037
  return self.safe_string(subscription, 'token')
1055
1038
 
@@ -1483,23 +1466,22 @@ class kraken(ccxt.async_support.kraken):
1483
1466
  symbols = self.market_symbols(symbols, None, False, True, False)
1484
1467
  messageHashes = []
1485
1468
  for i in range(0, len(symbols)):
1486
- messageHashes.append(self.get_message_hash(unifiedName, None, self.symbol(symbols[i])))
1487
- # for WS subscriptions, we can't use .marketIds(symbols), instead a custom is field needed
1488
- markets = self.markets_for_symbols(symbols)
1489
- wsMarketIds = []
1490
- for i in range(0, len(markets)):
1491
- wsMarketId = self.safe_string(markets[i]['info'], 'wsname')
1492
- wsMarketIds.append(wsMarketId)
1469
+ eventTrigger = self.safe_string(params, 'event_trigger')
1470
+ if eventTrigger is not None:
1471
+ messageHashes.append(self.get_message_hash(channelName, None, self.symbol(symbols[i])))
1472
+ else:
1473
+ messageHashes.append(self.get_message_hash(unifiedName, None, self.symbol(symbols[i])))
1493
1474
  request: dict = {
1494
- 'event': 'subscribe',
1495
- 'reqid': self.request_id(),
1496
- 'pair': wsMarketIds,
1497
- 'subscription': {
1498
- 'name': channelName,
1475
+ 'method': 'subscribe',
1476
+ 'params': {
1477
+ 'channel': channelName,
1478
+ 'symbol': symbols,
1499
1479
  },
1480
+ 'req_id': self.request_id(),
1500
1481
  }
1501
- url = self.urls['api']['ws']['public']
1502
- return await self.watch_multiple(url, messageHashes, self.deep_extend(request, params), messageHashes, subscriptionArgs)
1482
+ request['params'] = self.deep_extend(request['params'], params)
1483
+ url = self.urls['api']['ws']['publicV2']
1484
+ return await self.watch_multiple(url, messageHashes, request, messageHashes, subscriptionArgs)
1503
1485
 
1504
1486
  async def watch_balance(self, params={}) -> Balances:
1505
1487
  """
@@ -1655,11 +1637,7 @@ class kraken(ccxt.async_support.kraken):
1655
1637
  name = self.safe_string(info, 'name')
1656
1638
  methods: dict = {
1657
1639
  # public
1658
- 'book': self.handle_order_book,
1659
1640
  'ohlc': self.handle_ohlcv,
1660
- 'ticker': self.handle_ticker,
1661
- 'spread': self.handle_bid_ask,
1662
- 'trade': self.handle_trades,
1663
1641
  # private
1664
1642
  'openOrders': self.handle_orders,
1665
1643
  'ownTrades': self.handle_my_trades,
@@ -1672,6 +1650,9 @@ class kraken(ccxt.async_support.kraken):
1672
1650
  if channel is not None:
1673
1651
  methods: dict = {
1674
1652
  'balances': self.handle_balance,
1653
+ 'book': self.handle_order_book,
1654
+ 'ticker': self.handle_ticker,
1655
+ 'trade': self.handle_trades,
1675
1656
  }
1676
1657
  method = self.safe_value(methods, channel)
1677
1658
  if method is not None:
@@ -1686,6 +1667,7 @@ class kraken(ccxt.async_support.kraken):
1686
1667
  'amend_order': self.handle_create_edit_order,
1687
1668
  'cancel_order': self.handle_cancel_order,
1688
1669
  'cancel_all': self.handle_cancel_all_orders,
1670
+ 'pong': self.handle_pong,
1689
1671
  }
1690
1672
  method = self.safe_value(methods, event)
1691
1673
  if method is not None: