ccxt 4.2.20__py2.py3-none-any.whl → 4.2.22__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.

Potentially problematic release.


This version of ccxt might be problematic. Click here for more details.

Files changed (62) hide show
  1. ccxt/__init__.py +1 -1
  2. ccxt/abstract/binance.py +3 -0
  3. ccxt/abstract/binancecoinm.py +3 -0
  4. ccxt/abstract/binanceus.py +16 -12
  5. ccxt/abstract/binanceusdm.py +3 -0
  6. ccxt/abstract/gate.py +1 -0
  7. ccxt/abstract/gateio.py +1 -0
  8. ccxt/abstract/novadax.py +22 -18
  9. ccxt/abstract/phemex.py +1 -0
  10. ccxt/async_support/__init__.py +1 -1
  11. ccxt/async_support/base/exchange.py +16 -4
  12. ccxt/async_support/bigone.py +1 -0
  13. ccxt/async_support/binance.py +14 -3
  14. ccxt/async_support/bitget.py +11 -1
  15. ccxt/async_support/bitrue.py +1 -0
  16. ccxt/async_support/bitvavo.py +250 -152
  17. ccxt/async_support/blockchaincom.py +3 -1
  18. ccxt/async_support/bybit.py +49 -10
  19. ccxt/async_support/coinbasepro.py +1 -0
  20. ccxt/async_support/coinex.py +34 -12
  21. ccxt/async_support/deribit.py +145 -0
  22. ccxt/async_support/gate.py +30 -1
  23. ccxt/async_support/novadax.py +27 -23
  24. ccxt/async_support/okcoin.py +3 -0
  25. ccxt/async_support/phemex.py +7 -3
  26. ccxt/async_support/poloniex.py +1 -0
  27. ccxt/async_support/woo.py +1 -1
  28. ccxt/base/exchange.py +17 -5
  29. ccxt/bigone.py +1 -0
  30. ccxt/binance.py +14 -3
  31. ccxt/bitget.py +11 -1
  32. ccxt/bitrue.py +1 -0
  33. ccxt/bitvavo.py +250 -152
  34. ccxt/blockchaincom.py +3 -1
  35. ccxt/bybit.py +49 -10
  36. ccxt/coinbasepro.py +1 -0
  37. ccxt/coinex.py +34 -12
  38. ccxt/deribit.py +145 -0
  39. ccxt/gate.py +30 -1
  40. ccxt/novadax.py +27 -23
  41. ccxt/okcoin.py +3 -0
  42. ccxt/phemex.py +7 -3
  43. ccxt/poloniex.py +1 -0
  44. ccxt/pro/__init__.py +1 -1
  45. ccxt/pro/bequant.py +7 -1
  46. ccxt/pro/binance.py +7 -4
  47. ccxt/pro/binancecoinm.py +7 -1
  48. ccxt/pro/binanceus.py +7 -1
  49. ccxt/pro/bitcoincom.py +7 -1
  50. ccxt/pro/bitget.py +1 -1
  51. ccxt/pro/bitopro.py +7 -3
  52. ccxt/pro/bitrue.py +5 -1
  53. ccxt/pro/bitvavo.py +623 -19
  54. ccxt/pro/lbank.py +1 -1
  55. ccxt/pro/okx.py +10 -2
  56. ccxt/test/test_async.py +14 -1
  57. ccxt/test/test_sync.py +14 -1
  58. ccxt/woo.py +1 -1
  59. {ccxt-4.2.20.dist-info → ccxt-4.2.22.dist-info}/METADATA +4 -4
  60. {ccxt-4.2.20.dist-info → ccxt-4.2.22.dist-info}/RECORD +62 -62
  61. {ccxt-4.2.20.dist-info → ccxt-4.2.22.dist-info}/WHEEL +0 -0
  62. {ccxt-4.2.20.dist-info → ccxt-4.2.22.dist-info}/top_level.txt +0 -0
ccxt/pro/bitvavo.py CHANGED
@@ -6,9 +6,10 @@
6
6
  import ccxt.async_support
7
7
  from ccxt.async_support.base.ws.cache import ArrayCache, ArrayCacheBySymbolById, ArrayCacheByTimestamp
8
8
  import hashlib
9
- from ccxt.base.types import Int, Order, OrderBook, Str, Ticker, Trade
9
+ from ccxt.base.types import Balances, Int, Order, OrderBook, OrderSide, OrderType, Str, Ticker, Trade
10
10
  from ccxt.async_support.base.ws.client import Client
11
11
  from typing import List
12
+ from ccxt.base.errors import ExchangeError
12
13
  from ccxt.base.errors import ArgumentsRequired
13
14
  from ccxt.base.errors import AuthenticationError
14
15
 
@@ -19,21 +20,38 @@ class bitvavo(ccxt.async_support.bitvavo):
19
20
  return self.deep_extend(super(bitvavo, self).describe(), {
20
21
  'has': {
21
22
  'ws': True,
22
- 'createOrderWs': False,
23
- 'editOrderWs': False,
24
- 'fetchOpenOrdersWs': False,
25
- 'fetchOrderWs': False,
26
- 'cancelOrderWs': False,
27
23
  'cancelOrdersWs': False,
28
- 'cancelAllOrdersWs': False,
29
24
  'fetchTradesWs': False,
30
- 'fetchBalanceWs': False,
31
25
  'watchOrderBook': True,
32
26
  'watchTrades': True,
33
27
  'watchTicker': True,
34
28
  'watchOHLCV': True,
35
29
  'watchOrders': True,
36
30
  'watchMyTrades': True,
31
+ 'cancelAllOrdersWs': True,
32
+ 'cancelOrderWs': True,
33
+ 'createOrderWs': True,
34
+ 'createStopLimitOrderWs': True,
35
+ 'createStopMarketOrderWs': True,
36
+ 'createStopOrderWs': True,
37
+ 'editOrderWs': True,
38
+ 'fetchBalanceWs': True,
39
+ 'fetchCurrenciesWS': True,
40
+ 'fetchDepositAddressWs': True,
41
+ 'fetchDepositsWs': True,
42
+ 'fetchDepositWithdrawFeesWs': True,
43
+ 'fetchMyTradesWs': True,
44
+ 'fetchOHLCVWs': True,
45
+ 'fetchOpenOrdersWs': True,
46
+ 'fetchOrderWs': True,
47
+ 'fetchOrderBookWs': True,
48
+ 'fetchOrdersWs': True,
49
+ 'fetchTickerWs': True,
50
+ 'fetchTickersWs': True,
51
+ 'fetchTimeWs': True,
52
+ 'fetchTradingFeesWs': True,
53
+ 'fetchWithdrawalsWs': True,
54
+ 'withdrawWs': True,
37
55
  },
38
56
  'urls': {
39
57
  'api': {
@@ -41,6 +59,7 @@ class bitvavo(ccxt.async_support.bitvavo):
41
59
  },
42
60
  },
43
61
  'options': {
62
+ 'supressMultipleWsRequestsError': False, # if True, will not raise an error when using the same messageHash for more than one request. By making False you may receive responses from different requests on the same action
44
63
  'tradesLimit': 1000,
45
64
  'ordersLimit': 1000,
46
65
  'OHLCVLimit': 1000,
@@ -186,6 +205,22 @@ class bitvavo(ccxt.async_support.bitvavo):
186
205
  limit = ohlcv.getLimit(symbol, limit)
187
206
  return self.filter_by_since_limit(ohlcv, since, limit, 0, True)
188
207
 
208
+ def handle_fetch_ohlcv(self, client: Client, message):
209
+ #
210
+ # {
211
+ # action: 'getCandles',
212
+ # response: [
213
+ # [1690325820000, '26453', '26453', '26436', '26447', '0.01626246'],
214
+ # [1690325760000, '26454', '26454', '26453', '26453', '0.00037707']
215
+ # ]
216
+ # }
217
+ #
218
+ action = self.safe_string(message, 'action')
219
+ response = self.safe_value(message, 'response')
220
+ ohlcv = self.parse_ohlcvs(response, None, None, None)
221
+ messageHash = self.build_message_hash(action)
222
+ client.resolve(ohlcv, messageHash)
223
+
189
224
  def handle_ohlcv(self, client: Client, message):
190
225
  #
191
226
  # {
@@ -467,6 +502,542 @@ class bitvavo(ccxt.async_support.bitvavo):
467
502
  limit = trades.getLimit(symbol, limit)
468
503
  return self.filter_by_symbol_since_limit(trades, symbol, since, limit, True)
469
504
 
505
+ async def create_order_ws(self, symbol: str, type: OrderType, side: OrderSide, amount, price=None, params={}) -> Order:
506
+ """
507
+ create a trade order
508
+ :see: https://docs.bitvavo.com/#tag/Orders/paths/~1order/post
509
+ :param str symbol: unified symbol of the market to create an order in
510
+ :param str type: 'market' or 'limit'
511
+ :param str side: 'buy' or 'sell'
512
+ :param float amount: how much of currency you want to trade in units of base currency
513
+ :param float price: the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
514
+ :param dict [params]: extra parameters specific to the bitvavo api endpoint
515
+ :param str [params.timeInForce]: "GTC", "IOC", or "PO"
516
+ :param float [params.stopPrice]: The price at which a trigger order is triggered at
517
+ :param float [params.triggerPrice]: The price at which a trigger order is triggered at
518
+ :param bool [params.postOnly]: If True, the order will only be posted to the order book and not executed immediately
519
+ :param float [params.stopLossPrice]: The price at which a stop loss order is triggered at
520
+ :param float [params.takeProfitPrice]: The price at which a take profit order is triggered at
521
+ :param str [params.triggerType]: "price"
522
+ :param str [params.triggerReference]: "lastTrade", "bestBid", "bestAsk", "midPrice" Only for stop orders: Use self to determine which parameter will trigger the order
523
+ :param str [params.selfTradePrevention]: "decrementAndCancel", "cancelOldest", "cancelNewest", "cancelBoth"
524
+ :param bool [params.disableMarketProtection]: don't cancel if the next fill price is 10% worse than the best fill price
525
+ :param bool [params.responseRequired]: Set self to 'false' when only an acknowledgement of success or failure is required, self is faster.
526
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
527
+ """
528
+ await self.load_markets()
529
+ await self.authenticate()
530
+ request = self.create_order_request(symbol, type, side, amount, price, params)
531
+ return await self.watch_request('privateCreateOrder', request)
532
+
533
+ async def edit_order_ws(self, id: str, symbol, type, side, amount=None, price=None, params={}) -> Order:
534
+ """
535
+ edit a trade order
536
+ :see: https://docs.bitvavo.com/#tag/Orders/paths/~1order/put
537
+ :param str id: cancel order id
538
+ :param str symbol: unified symbol of the market to create an order in
539
+ :param str type: 'market' or 'limit'
540
+ :param str side: 'buy' or 'sell'
541
+ :param float [amount]: how much of currency you want to trade in units of base currency
542
+ :param float [price]: the price at which the order is to be fullfilled, in units of the base currency, ignored in market orders
543
+ :param dict [params]: extra parameters specific to the bitvavo api endpoint
544
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
545
+ """
546
+ await self.load_markets()
547
+ await self.authenticate()
548
+ request = self.edit_order_request(id, symbol, type, side, amount, price, params)
549
+ return await self.watch_request('privateUpdateOrder', request)
550
+
551
+ async def cancel_order_ws(self, id: str, symbol: str = None, params={}):
552
+ """
553
+ :see: https://docs.bitvavo.com/#tag/Orders/paths/~1order/delete
554
+ cancels an open order
555
+ :param str id: order id
556
+ :param str symbol: unified symbol of the market the order was made in
557
+ :param dict [params]: extra parameters specific to the bitvavo api endpoint
558
+ :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
559
+ """
560
+ await self.load_markets()
561
+ await self.authenticate()
562
+ request = self.cancelOrderRequest(id, symbol, params)
563
+ return await self.watch_request('privateCancelOrder', request)
564
+
565
+ async def cancel_all_orders_ws(self, symbol: str = None, params={}):
566
+ """
567
+ :see: https://docs.bitvavo.com/#tag/Orders/paths/~1orders/delete
568
+ cancel all open orders
569
+ :param str symbol: unified market symbol, only orders in the market of self symbol are cancelled when symbol is not None
570
+ :param dict [params]: extra parameters specific to the bitvavo api endpoint
571
+ :returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
572
+ """
573
+ await self.load_markets()
574
+ await self.authenticate()
575
+ request = {}
576
+ market = None
577
+ if symbol is not None:
578
+ market = self.market(symbol)
579
+ request['market'] = market['id']
580
+ return await self.watch_request('privateCancelOrders', self.extend(request, params))
581
+
582
+ def handle_multiple_orders(self, client: Client, message):
583
+ #
584
+ # {
585
+ # action: 'privateCancelOrders',
586
+ # response: [{
587
+ # orderId: 'd71df826-1130-478a-8741-d219128675b0'
588
+ # }]
589
+ # }
590
+ #
591
+ action = self.safe_string(message, 'action')
592
+ response = self.safe_value(message, 'response')
593
+ firstRawOrder = self.safe_value(response, 0, {})
594
+ marketId = self.safe_string(firstRawOrder, 'market')
595
+ orders = self.parse_orders(response)
596
+ messageHash = self.build_message_hash(action, {'market': marketId})
597
+ client.resolve(orders, messageHash)
598
+ messageHash = self.build_message_hash(action, message)
599
+ client.resolve(orders, messageHash)
600
+
601
+ async def fetch_order_ws(self, id: str, symbol: str = None, params={}) -> Order:
602
+ """
603
+ :see: https://docs.bitvavo.com/#tag/General/paths/~1assets/get
604
+ fetches information on an order made by the user
605
+ :param str symbol: unified symbol of the market the order was made in
606
+ :param dict [params]: extra parameters specific to the bitvavo api endpoint
607
+ :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
608
+ """
609
+ if symbol is None:
610
+ raise ArgumentsRequired(self.id + ' fetchOrder() requires a symbol argument')
611
+ await self.load_markets()
612
+ await self.authenticate()
613
+ market = self.market(symbol)
614
+ request = {
615
+ 'orderId': id,
616
+ 'market': market['id'],
617
+ }
618
+ return await self.watch_request('privateGetOrder', self.extend(request, params))
619
+
620
+ async def fetch_orders_ws(self, symbol: str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
621
+ """
622
+ :see: https://docs.bitvavo.com/#tag/Orders/paths/~1orders/get
623
+ fetches information on multiple orders made by the user
624
+ :param str symbol: unified market symbol of the market orders were made in
625
+ :param int [since]: the earliest time in ms to fetch orders for
626
+ :param int [limit]: the maximum number of orde structures to retrieve
627
+ :param dict [params]: extra parameters specific to the bitvavo api endpoint
628
+ :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
629
+ """
630
+ if symbol is None:
631
+ raise ArgumentsRequired(self.id + ' fetchOrdersWs() requires a symbol argument')
632
+ await self.load_markets()
633
+ await self.authenticate()
634
+ request = self.fetchOrdersRequest(symbol, since, limit, params)
635
+ orders = await self.watch_request('privateGetOrders', request)
636
+ return self.filter_by_symbol_since_limit(orders, symbol, since, limit)
637
+
638
+ async def watch_request(self, action, request):
639
+ request['action'] = action
640
+ messageHash = self.build_message_hash(action, request)
641
+ self.check_message_hash_does_not_exist(messageHash)
642
+ url = self.urls['api']['ws']
643
+ return await self.watch(url, messageHash, request, messageHash)
644
+
645
+ async def fetch_open_orders_ws(self, symbol: str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
646
+ """
647
+ fetch all unfilled currently open orders
648
+ :param str symbol: unified market symbol
649
+ :param int [since]: the earliest time in ms to fetch open orders for
650
+ :param int [limit]: the maximum number of open orders structures to retrieve
651
+ :param dict [params]: extra parameters specific to the bitvavo api endpoint
652
+ :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
653
+ """
654
+ await self.load_markets()
655
+ await self.authenticate()
656
+ request = {
657
+ # 'market': market['id'], # rate limit 25 without a market, 1 with market specified
658
+ }
659
+ market = None
660
+ if symbol is not None:
661
+ market = self.market(symbol)
662
+ request['market'] = market['id']
663
+ orders = await self.watch_request('privateGetOrdersOpen', self.extend(request, params))
664
+ return self.filter_by_symbol_since_limit(orders, symbol, since, limit)
665
+
666
+ async def fetch_my_trades_ws(self, symbol: str = None, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
667
+ """
668
+ :see: https://docs.bitvavo.com/#tag/Trades
669
+ fetch all trades made by the user
670
+ :param str symbol: unified market symbol
671
+ :param int [since]: the earliest time in ms to fetch trades for
672
+ :param int [limit]: the maximum number of trades structures to retrieve
673
+ :param dict [params]: extra parameters specific to the bitvavo api endpoint
674
+ :returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
675
+ """
676
+ if symbol is None:
677
+ raise ArgumentsRequired(self.id + ' fetchMyTradesWs() requires a symbol argument')
678
+ await self.load_markets()
679
+ await self.authenticate()
680
+ request = self.fetchMyTradesRequest(symbol, since, limit, params)
681
+ myTrades = await self.watch_request('privateGetTrades', request)
682
+ return self.filter_by_symbol_since_limit(myTrades, symbol, since, limit)
683
+
684
+ def handle_my_trades(self, client: Client, message):
685
+ #
686
+ # {
687
+ # action: 'privateGetTrades',
688
+ # response: [
689
+ # {
690
+ # "id": "108c3633-0276-4480-a902-17a01829deae",
691
+ # "orderId": "1d671998-3d44-4df4-965f-0d48bd129a1b",
692
+ # "timestamp": 1542967486256,
693
+ # "market": "BTC-EUR",
694
+ # "side": "buy",
695
+ # "amount": "0.005",
696
+ # "price": "5000.1",
697
+ # "taker": True,
698
+ # "fee": "0.03",
699
+ # "feeCurrency": "EUR",
700
+ # "settled": True
701
+ # }
702
+ # ]
703
+ # }
704
+ #
705
+ #
706
+ action = self.safe_string(message, 'action')
707
+ response = self.safe_value(message, 'response')
708
+ firstRawTrade = self.safe_value(response, 0, {})
709
+ marketId = self.safe_string(firstRawTrade, 'market')
710
+ trades = self.parse_trades(response, None, None, None)
711
+ messageHash = self.build_message_hash(action, {'market': marketId})
712
+ client.resolve(trades, messageHash)
713
+
714
+ async def withdraw_ws(self, code: str, amount, address, tag=None, params={}):
715
+ """
716
+ make a withdrawal
717
+ :param str code: unified currency code
718
+ :param float amount: the amount to withdraw
719
+ :param str address: the address to withdraw to
720
+ :param str tag:
721
+ :param dict [params]: extra parameters specific to the bitvavo api endpoint
722
+ :returns dict: a `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
723
+ """
724
+ tag, params = self.handle_withdraw_tag_and_params(tag, params)
725
+ self.check_address(address)
726
+ await self.load_markets()
727
+ await self.authenticate()
728
+ request = self.withdrawRequest(code, amount, address, tag, params)
729
+ return await self.watch_request('privateWithdrawAssets', request)
730
+
731
+ def handle_withdraw(self, client: Client, message):
732
+ #
733
+ # {
734
+ # action: 'privateWithdrawAssets',
735
+ # response: {
736
+ # "success": True,
737
+ # "symbol": "BTC",
738
+ # "amount": "1.5"
739
+ # }
740
+ # }
741
+ #
742
+ action = self.safe_string(message, 'action')
743
+ messageHash = self.build_message_hash(action, message)
744
+ response = self.safe_value(message, 'response')
745
+ withdraw = self.parse_transaction(response)
746
+ client.resolve(withdraw, messageHash)
747
+
748
+ async def fetch_withdrawals_ws(self, code: str = None, since: Int = None, limit: Int = None, params={}):
749
+ """
750
+ :see: https://docs.bitvavo.com/#tag/Account/paths/~1withdrawalHistory/get
751
+ fetch all withdrawals made from an account
752
+ :param str code: unified currency code
753
+ :param int [since]: the earliest time in ms to fetch withdrawals for
754
+ :param int [limit]: the maximum number of withdrawals structures to retrieve
755
+ :param dict [params]: extra parameters specific to the bitvavo api endpoint
756
+ :returns dict[]: a list of `transaction structures <https://docs.ccxt.com/#/?id=transaction-structure>`
757
+ """
758
+ await self.load_markets()
759
+ await self.authenticate()
760
+ request = self.fetchWithdrawalsRequest(code, since, limit, params)
761
+ withdraws = await self.watch_request('privateGetWithdrawalHistory', request)
762
+ return self.filter_by_currency_since_limit(withdraws, code, since, limit)
763
+
764
+ def handle_withdraws(self, client: Client, message):
765
+ #
766
+ # {
767
+ # action: 'privateGetWithdrawalHistory',
768
+ # response: [{
769
+ # timestamp: 1689792085000,
770
+ # symbol: 'BTC',
771
+ # amount: '0.0009',
772
+ # fee: '0',
773
+ # status: 'completed',
774
+ # txId: '7dbadc658d7d59c129de1332c55ee8e08d0ab74432faae03b417b9809c819d1f'
775
+ # },
776
+ # ...
777
+ # ]
778
+ # }
779
+ #
780
+ action = self.safe_string(message, 'action')
781
+ messageHash = self.build_message_hash(action, message)
782
+ response = self.safe_value(message, 'response')
783
+ withdrawals = self.parse_transactions(response, None, None, None, {'type': 'withdrawal'})
784
+ client.resolve(withdrawals, messageHash)
785
+
786
+ async def fetch_ohlcv_ws(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
787
+ """
788
+ :see: https://docs.bitvavo.com/#tag/Market-Data/paths/~1{market}~1candles/get
789
+ fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
790
+ :param str symbol: unified symbol of the market to fetch OHLCV data for
791
+ :param str timeframe: the length of time each candle represents
792
+ :param int [since]: timestamp in ms of the earliest candle to fetch
793
+ :param int [limit]: the maximum amount of candles to fetch
794
+ :param dict [params]: extra parameters specific to the bitvavo api endpoint
795
+ :returns int[][]: A list of candles ordered, open, high, low, close, volume
796
+ """
797
+ await self.load_markets()
798
+ request = self.fetchOHLCVRequest(symbol, timeframe, since, limit, params)
799
+ action = 'getCandles'
800
+ ohlcv = await self.watch_request(action, request)
801
+ return self.filter_by_since_limit(ohlcv, since, limit, 0, True)
802
+
803
+ async def fetch_deposits_ws(self, code: str = None, since: Int = None, limit: Int = None, params={}):
804
+ """
805
+ :see: https://docs.bitvavo.com/#tag/Account/paths/~1depositHistory/get
806
+ fetch all deposits made to an account
807
+ :param str code: unified currency code
808
+ :param int [since]: the earliest time in ms to fetch deposits for
809
+ :param int [limit]: the maximum number of deposits structures to retrieve
810
+ :param dict [params]: extra parameters specific to the bitvavo api endpoint
811
+ :returns dict[]: a list of `transaction structures <https://docs.ccxt.com/#/?id=transaction-structure>`
812
+ """
813
+ await self.load_markets()
814
+ await self.authenticate()
815
+ request = self.fetchDepositsRequest(code, since, limit, params)
816
+ deposits = await self.watch_request('privateGetDepositHistory', request)
817
+ return self.filter_by_currency_since_limit(deposits, code, since, limit)
818
+
819
+ def handle_deposits(self, client: Client, message):
820
+ #
821
+ # {
822
+ # action: 'privateGetDepositHistory',
823
+ # response: [{
824
+ # timestamp: 1689792085000,
825
+ # symbol: 'BTC',
826
+ # amount: '0.0009',
827
+ # fee: '0',
828
+ # status: 'completed',
829
+ # txId: '7dbadc658d7d59c129de1332c55ee8e08d0ab74432faae03b417b9809c819d1f'
830
+ # },
831
+ # ...
832
+ # ]
833
+ # }
834
+ #
835
+ action = self.safe_string(message, 'action')
836
+ messageHash = self.build_message_hash(action, message)
837
+ response = self.safe_value(message, 'response')
838
+ deposits = self.parse_transactions(response, None, None, None, {'type': 'deposit'})
839
+ client.resolve(deposits, messageHash)
840
+
841
+ async def fetch_trading_fees_ws(self, params={}):
842
+ """
843
+ :see: https://docs.bitvavo.com/#tag/Account/paths/~1account/get
844
+ fetch the trading fees for multiple markets
845
+ :param dict [params]: extra parameters specific to the bitvavo api endpoint
846
+ :returns dict: a dictionary of `fee structures <https://docs.ccxt.com/#/?id=fee-structure>` indexed by market symbols
847
+ """
848
+ await self.load_markets()
849
+ await self.authenticate()
850
+ return await self.watch_request('privateGetAccount', params)
851
+
852
+ async def fetch_markets_ws(self, params={}):
853
+ """
854
+ :see: https://docs.bitvavo.com/#tag/General/paths/~1markets/get
855
+ retrieves data on all markets for bitvavo
856
+ :param dict [params]: extra parameters specific to the exchange api endpoint
857
+ :returns dict[]: an array of objects representing market data
858
+ """
859
+ return await self.watch_request('getMarkets', params)
860
+
861
+ async def fetch_currencies_ws(self, params={}):
862
+ """
863
+ :see: https://docs.bitvavo.com/#tag/General/paths/~1assets/get
864
+ fetches all available currencies on an exchange
865
+ :param dict [params]: extra parameters specific to the bitvavo api endpoint
866
+ :returns dict: an associative dictionary of currencies
867
+ """
868
+ await self.load_markets()
869
+ return await self.watch_request('getAssets', params)
870
+
871
+ def handle_fetch_currencies(self, client: Client, message):
872
+ #
873
+ # {
874
+ # action: 'getAssets',
875
+ # response: [{
876
+ # symbol: '1INCH',
877
+ # name: '1inch',
878
+ # decimals: 8,
879
+ # depositFee: '0',
880
+ # depositConfirmations: 64,
881
+ # depositStatus: 'OK',
882
+ # withdrawalFee: '13',
883
+ # withdrawalMinAmount: '13',
884
+ # withdrawalStatus: 'OK',
885
+ # networks: [Array],
886
+ # message: ''
887
+ # },
888
+ # ...
889
+ # ]
890
+ # }
891
+ #
892
+ action = self.safe_string(message, 'action')
893
+ messageHash = self.build_message_hash(action, message)
894
+ response = self.safe_value(message, 'response')
895
+ currencies = self.parseCurrencies(response)
896
+ client.resolve(currencies, messageHash)
897
+
898
+ def handle_trading_fees(self, client, message):
899
+ #
900
+ # {
901
+ # action: 'privateGetAccount',
902
+ # response: {
903
+ # fees: {
904
+ # taker: '0.0025',
905
+ # maker: '0.0015',
906
+ # volume: '1693.74'
907
+ # }
908
+ # }
909
+ # }
910
+ #
911
+ action = self.safe_string(message, 'action')
912
+ messageHash = self.build_message_hash(action, message)
913
+ response = self.safe_value(message, 'response')
914
+ fees = self.parse_trading_fees(response)
915
+ client.resolve(fees, messageHash)
916
+
917
+ async def fetch_balance_ws(self, params={}) -> Balances:
918
+ """
919
+ :see: https://docs.bitvavo.com/#tag/Account/paths/~1balance/get
920
+ query for balance and get the amount of funds available for trading or funds locked in orders
921
+ :param dict [params]: extra parameters specific to the bitvavo api endpoint
922
+ :returns dict: a `balance structure <https://docs.ccxt.com/en/latest/manual.html?#balance-structure>`
923
+ """
924
+ await self.load_markets()
925
+ await self.authenticate()
926
+ return await self.watch_request('privateGetBalance', params)
927
+
928
+ def handle_fetch_balance(self, client: Client, message):
929
+ #
930
+ # {
931
+ # action: 'privateGetBalance',
932
+ # response: [{
933
+ # symbol: 'ADA',
934
+ # available: '0',
935
+ # inOrder: '0'
936
+ # },
937
+ # ...
938
+ # ]
939
+ # }
940
+ #
941
+ action = self.safe_string(message, 'action', 'privateGetBalance')
942
+ messageHash = self.build_message_hash(action, message)
943
+ response = self.safe_value(message, 'response', [])
944
+ balance = self.parse_balance(response)
945
+ client.resolve(balance, messageHash)
946
+
947
+ def handle_single_order(self, client: Client, message):
948
+ #
949
+ # {
950
+ # action: 'privateCreateOrder',
951
+ # response: {
952
+ # orderId: 'd71df826-1130-478a-8741-d219128675b0',
953
+ # market: 'BTC-EUR',
954
+ # created: 1689792749748,
955
+ # updated: 1689792749748,
956
+ # status: 'new',
957
+ # side: 'sell',
958
+ # orderType: 'limit',
959
+ # amount: '0.0002',
960
+ # amountRemaining: '0.0002',
961
+ # price: '37000',
962
+ # onHold: '0.0002',
963
+ # onHoldCurrency: 'BTC',
964
+ # filledAmount: '0',
965
+ # filledAmountQuote: '0',
966
+ # feePaid: '0',
967
+ # feeCurrency: 'EUR',
968
+ # fills: [],
969
+ # selfTradePrevention: 'decrementAndCancel',
970
+ # visible: True,
971
+ # timeInForce: 'GTC',
972
+ # postOnly: False
973
+ # }
974
+ # }
975
+ #
976
+ action = self.safe_string(message, 'action')
977
+ response = self.safe_value(message, 'response', {})
978
+ order = self.parse_order(response)
979
+ messageHash = self.build_message_hash(action, response)
980
+ client.resolve(order, messageHash)
981
+
982
+ def handle_markets(self, client: Client, message):
983
+ #
984
+ # {
985
+ # action: 'getMarkets',
986
+ # response: [{
987
+ # market: '1INCH-EUR',
988
+ # status: 'trading',
989
+ # base: '1INCH',
990
+ # quote: 'EUR',
991
+ # pricePrecision: 5,
992
+ # minOrderInBaseAsset: '2',
993
+ # minOrderInQuoteAsset: '5',
994
+ # maxOrderInBaseAsset: '1000000000',
995
+ # maxOrderInQuoteAsset: '1000000000',
996
+ # orderTypes: [Array]
997
+ # },
998
+ # ...
999
+ # ]
1000
+ # }
1001
+ #
1002
+ action = self.safe_string(message, 'action')
1003
+ response = self.safe_value(message, 'response', {})
1004
+ markets = self.parse_markets(response)
1005
+ messageHash = self.build_message_hash(action, response)
1006
+ client.resolve(markets, messageHash)
1007
+
1008
+ def build_message_hash(self, action, params={}):
1009
+ methods = {
1010
+ 'privateCreateOrder': self.action_and_market_message_hash,
1011
+ 'privateUpdateOrder': self.action_and_order_id_message_hash,
1012
+ 'privateCancelOrder': self.action_and_order_id_message_hash,
1013
+ 'privateGetOrder': self.action_and_order_id_message_hash,
1014
+ 'privateGetTrades': self.action_and_market_message_hash,
1015
+ }
1016
+ method = self.safe_value(methods, action)
1017
+ messageHash = action
1018
+ if method is not None:
1019
+ messageHash = method(action, params)
1020
+ return messageHash
1021
+
1022
+ def check_message_hash_does_not_exist(self, messageHash):
1023
+ supressMultipleWsRequestsError = self.safe_value(self.options, 'supressMultipleWsRequestsError', False)
1024
+ if not supressMultipleWsRequestsError:
1025
+ client = self.safe_value(self.clients, self.urls['api']['ws'])
1026
+ if client is not None:
1027
+ future = self.safe_value(client.futures, messageHash)
1028
+ if future is not None:
1029
+ raise ExchangeError(self.id + ' a similar request with messageHash ' + messageHash + ' is already pending, you must wait for a response, or turn off self error by setting supressMultipleWsRequestsError in the options to True')
1030
+
1031
+ def action_and_market_message_hash(self, action, params={}):
1032
+ symbol = self.safe_string(params, 'market', '')
1033
+ return action + symbol
1034
+
1035
+ def action_and_order_id_message_hash(self, action, params={}):
1036
+ orderId = self.safe_string(params, 'orderId')
1037
+ if orderId is None:
1038
+ raise ExchangeError(self.id + ' privateUpdateOrderMessageHash requires a orderId parameter')
1039
+ return action + orderId
1040
+
470
1041
  def handle_order(self, client: Client, message):
471
1042
  #
472
1043
  # {
@@ -592,6 +1163,28 @@ class bitvavo(ccxt.async_support.bitvavo):
592
1163
  if messageHash in client.subscriptions:
593
1164
  del client.subscriptions[messageHash]
594
1165
 
1166
+ def handle_error_message(self, client: Client, message):
1167
+ #
1168
+ # {
1169
+ # action: 'privateCreateOrder',
1170
+ # market: 'BTC-EUR',
1171
+ # errorCode: 217,
1172
+ # error: 'Minimum order size in quote currency is 5 EUR or 0.001 BTC.'
1173
+ # }
1174
+ #
1175
+ error = self.safe_string(message, 'error')
1176
+ code = self.safe_integer(error, 'errorCode')
1177
+ action = self.safe_string(message, 'action')
1178
+ messageHash = self.build_message_hash(action, message)
1179
+ rejected = False
1180
+ try:
1181
+ self.handle_errors(code, error, client.url, None, None, error, message, None, None)
1182
+ except Exception as e:
1183
+ rejected = True
1184
+ client.reject(e, messageHash)
1185
+ if not rejected:
1186
+ client.reject(message, messageHash)
1187
+
595
1188
  def handle_message(self, client: Client, message):
596
1189
  #
597
1190
  # {
@@ -601,7 +1194,6 @@ class bitvavo(ccxt.async_support.bitvavo):
601
1194
  # }
602
1195
  # }
603
1196
  #
604
- #
605
1197
  # {
606
1198
  # "event": "book",
607
1199
  # "market": "BTC-EUR",
@@ -637,6 +1229,9 @@ class bitvavo(ccxt.async_support.bitvavo):
637
1229
  # "authenticated": True
638
1230
  # }
639
1231
  #
1232
+ error = self.safe_string(message, 'error')
1233
+ if error is not None:
1234
+ self.handle_error_message(client, message)
640
1235
  methods = {
641
1236
  'subscribed': self.handle_subscription_status,
642
1237
  'book': self.handle_order_book,
@@ -647,15 +1242,24 @@ class bitvavo(ccxt.async_support.bitvavo):
647
1242
  'authenticate': self.handle_authentication_message,
648
1243
  'order': self.handle_order,
649
1244
  'fill': self.handle_my_trade,
1245
+ 'privateCreateOrder': self.handle_single_order,
1246
+ 'privateUpdateOrder': self.handle_single_order,
1247
+ 'privateGetBalance': self.handle_fetch_balance,
1248
+ 'privateCancelOrders': self.handle_multiple_orders,
1249
+ 'privateGetOrders': self.handle_multiple_orders,
1250
+ 'privateGetOrder': self.handle_single_order,
1251
+ 'privateCancelOrder': self.handle_single_order,
1252
+ 'privateGetOrdersOpen': self.handle_multiple_orders,
1253
+ 'privateGetAccount': self.handle_trading_fees,
1254
+ 'privateGetDepositHistory': self.handle_deposits,
1255
+ 'privateGetWithdrawalHistory': self.handle_withdraws,
1256
+ 'privateWithdrawAssets': self.handle_withdraw,
1257
+ 'privateGetTrades': self.handle_my_trades,
1258
+ 'getAssets': self.handle_fetch_currencies,
1259
+ 'getCandles': self.handle_fetch_ohlcv,
1260
+ 'getMarkets': self.handle_markets,
650
1261
  }
651
- event = self.safe_string(message, 'event')
1262
+ event = self.safe_string_2(message, 'event', 'action')
652
1263
  method = self.safe_value(methods, event)
653
- if method is None:
654
- action = self.safe_string(message, 'action')
655
- method = self.safe_value(methods, action)
656
- if method is None:
657
- return message
658
- else:
659
- return method(client, message)
660
- else:
661
- return method(client, message)
1264
+ if method is not None:
1265
+ method(client, message)