ccxt 4.4.85__py2.py3-none-any.whl → 4.4.87__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 (92) hide show
  1. ccxt/__init__.py +7 -5
  2. ccxt/abstract/modetrade.py +119 -0
  3. ccxt/abstract/myokx.py +2 -0
  4. ccxt/abstract/okx.py +2 -0
  5. ccxt/abstract/okxus.py +349 -0
  6. ccxt/ascendex.py +187 -151
  7. ccxt/async_support/__init__.py +7 -5
  8. ccxt/async_support/ascendex.py +187 -151
  9. ccxt/async_support/base/exchange.py +30 -26
  10. ccxt/async_support/bequant.py +1 -1
  11. ccxt/async_support/binance.py +1 -1
  12. ccxt/async_support/bitget.py +4 -4
  13. ccxt/async_support/bitmart.py +1 -1
  14. ccxt/async_support/bitteam.py +31 -0
  15. ccxt/async_support/{huobijp.py → bittrade.py} +11 -11
  16. ccxt/async_support/coinbase.py +2 -5
  17. ccxt/async_support/coinmetro.py +3 -0
  18. ccxt/async_support/deribit.py +4 -5
  19. ccxt/async_support/gate.py +91 -73
  20. ccxt/async_support/hollaex.py +106 -49
  21. ccxt/async_support/htx.py +30 -51
  22. ccxt/async_support/hyperliquid.py +36 -20
  23. ccxt/async_support/kraken.py +5 -8
  24. ccxt/async_support/mexc.py +2 -2
  25. ccxt/async_support/modetrade.py +2727 -0
  26. ccxt/async_support/ndax.py +25 -24
  27. ccxt/async_support/okcoin.py +12 -29
  28. ccxt/async_support/okx.py +99 -3
  29. ccxt/async_support/okxus.py +54 -0
  30. ccxt/async_support/onetrading.py +10 -7
  31. ccxt/async_support/oxfun.py +40 -110
  32. ccxt/async_support/paradex.py +6 -0
  33. ccxt/async_support/phemex.py +4 -6
  34. ccxt/async_support/poloniex.py +172 -159
  35. ccxt/async_support/probit.py +18 -47
  36. ccxt/async_support/timex.py +5 -10
  37. ccxt/async_support/vertex.py +3 -4
  38. ccxt/async_support/whitebit.py +41 -11
  39. ccxt/async_support/woo.py +101 -75
  40. ccxt/async_support/woofipro.py +25 -20
  41. ccxt/async_support/xt.py +31 -41
  42. ccxt/base/exchange.py +12 -9
  43. ccxt/bequant.py +1 -1
  44. ccxt/binance.py +1 -1
  45. ccxt/bitget.py +4 -4
  46. ccxt/bitmart.py +1 -1
  47. ccxt/bitteam.py +31 -0
  48. ccxt/{huobijp.py → bittrade.py} +11 -11
  49. ccxt/coinbase.py +2 -5
  50. ccxt/coinmetro.py +3 -0
  51. ccxt/deribit.py +4 -5
  52. ccxt/gate.py +91 -73
  53. ccxt/hollaex.py +106 -49
  54. ccxt/htx.py +30 -51
  55. ccxt/hyperliquid.py +36 -20
  56. ccxt/kraken.py +5 -8
  57. ccxt/mexc.py +2 -2
  58. ccxt/modetrade.py +2727 -0
  59. ccxt/ndax.py +25 -24
  60. ccxt/okcoin.py +12 -29
  61. ccxt/okx.py +99 -3
  62. ccxt/okxus.py +54 -0
  63. ccxt/onetrading.py +10 -7
  64. ccxt/oxfun.py +40 -110
  65. ccxt/paradex.py +6 -0
  66. ccxt/phemex.py +4 -6
  67. ccxt/poloniex.py +172 -159
  68. ccxt/pro/__init__.py +101 -3
  69. ccxt/pro/binance.py +1 -0
  70. ccxt/pro/{huobijp.py → bittrade.py} +3 -3
  71. ccxt/pro/luno.py +6 -5
  72. ccxt/pro/mexc.py +2 -0
  73. ccxt/pro/modetrade.py +1271 -0
  74. ccxt/pro/okxus.py +38 -0
  75. ccxt/probit.py +18 -47
  76. ccxt/test/tests_async.py +17 -1
  77. ccxt/test/tests_sync.py +17 -1
  78. ccxt/timex.py +5 -10
  79. ccxt/vertex.py +3 -4
  80. ccxt/whitebit.py +41 -11
  81. ccxt/woo.py +100 -75
  82. ccxt/woofipro.py +24 -20
  83. ccxt/xt.py +31 -41
  84. {ccxt-4.4.85.dist-info → ccxt-4.4.87.dist-info}/METADATA +19 -8
  85. {ccxt-4.4.85.dist-info → ccxt-4.4.87.dist-info}/RECORD +89 -84
  86. ccxt/abstract/kuna.py +0 -182
  87. ccxt/async_support/kuna.py +0 -1935
  88. ccxt/kuna.py +0 -1935
  89. /ccxt/abstract/{huobijp.py → bittrade.py} +0 -0
  90. {ccxt-4.4.85.dist-info → ccxt-4.4.87.dist-info}/LICENSE.txt +0 -0
  91. {ccxt-4.4.85.dist-info → ccxt-4.4.87.dist-info}/WHEEL +0 -0
  92. {ccxt-4.4.85.dist-info → ccxt-4.4.87.dist-info}/top_level.txt +0 -0
@@ -7,7 +7,7 @@ from ccxt.async_support.base.exchange import Exchange
7
7
  from ccxt.abstract.ascendex import ImplicitAPI
8
8
  import asyncio
9
9
  import hashlib
10
- from ccxt.base.types import Account, Any, Balances, Bool, Currencies, Currency, DepositAddress, Int, Leverage, Leverages, LeverageTier, LeverageTiers, MarginMode, MarginModes, MarginModification, Market, Num, Order, OrderBook, OrderRequest, OrderSide, OrderType, Position, Str, Strings, Ticker, Tickers, FundingRate, FundingRates, Trade, TradingFees, Transaction, TransferEntry
10
+ from ccxt.base.types import Account, Any, Balances, Currencies, Currency, DepositAddress, Int, Leverage, Leverages, LeverageTier, LeverageTiers, MarginMode, MarginModes, MarginModification, Market, Num, Order, OrderBook, OrderRequest, OrderSide, OrderType, Position, Str, Strings, Ticker, Tickers, FundingRate, FundingRates, Trade, TradingFees, Transaction, TransferEntry
11
11
  from typing import List
12
12
  from ccxt.base.errors import ExchangeError
13
13
  from ccxt.base.errors import AuthenticationError
@@ -478,6 +478,7 @@ class ascendex(Exchange, ImplicitAPI):
478
478
  'broad': {},
479
479
  },
480
480
  'commonCurrencies': {
481
+ 'XBT': 'XBT', # self is not BTC ! just another token
481
482
  'BOND': 'BONDED',
482
483
  'BTCBEAR': 'BEAR',
483
484
  'BTCBULL': 'BULL',
@@ -498,112 +499,82 @@ class ascendex(Exchange, ImplicitAPI):
498
499
  :param dict [params]: extra parameters specific to the exchange API endpoint
499
500
  :returns dict: an associative dictionary of currencies
500
501
  """
501
- assetsPromise = self.v1PublicGetAssets(params)
502
- #
503
- # {
504
- # "code":0,
505
- # "data":[
506
- # {
507
- # "assetCode" : "LTCBULL",
508
- # "assetName" : "3X Long LTC Token",
509
- # "precisionScale" : 9,
510
- # "nativeScale" : 4,
511
- # "withdrawalFee" : "0.2",
512
- # "minWithdrawalAmt" : "1.0",
513
- # "status" : "Normal"
514
- # },
515
- # ]
516
- # }
517
- #
518
- marginPromise = self.v1PublicGetMarginAssets(params)
519
- #
520
- # {
521
- # "code":0,
522
- # "data":[
523
- # {
524
- # "assetCode":"BTT",
525
- # "displayName": "BTT",
526
- # "borrowAssetCode":"BTT-B",
527
- # "interestAssetCode":"BTT-I",
528
- # "nativeScale":0,
529
- # "numConfirmations":1,
530
- # "withdrawFee":"100.0",
531
- # "minWithdrawalAmt":"1000.0",
532
- # "statusCode":"Normal",
533
- # "statusMessage":"",
534
- # "interestRate":"0.001"
535
- # }
536
- # ]
537
- # }
538
- #
539
- cashPromise = self.v1PublicGetCashAssets(params)
502
+ response = await self.v2PublicGetAssets(params)
540
503
  #
541
- # {
542
- # "code":0,
543
- # "data":[
544
- # {
545
- # "assetCode":"LTCBULL",
546
- # "displayName": "LTCBULL",
547
- # "nativeScale":4,
548
- # "numConfirmations":20,
549
- # "withdrawFee":"0.2",
550
- # "minWithdrawalAmt":"1.0",
551
- # "statusCode":"Normal",
552
- # "statusMessage":"" # hideFromWalletTx
553
- # }
504
+ # {
505
+ # "code": "0",
506
+ # "data": [
507
+ # {
508
+ # "assetCode": "USDT",
509
+ # "assetName": "Tether",
510
+ # "precisionScale": 9,
511
+ # "nativeScale": 4,
512
+ # "blockChain": [
513
+ # {
514
+ # "chainName": "Solana",
515
+ # "withdrawFee": "2.0",
516
+ # "allowDeposit": True,
517
+ # "allowWithdraw": True,
518
+ # "minDepositAmt": "0.01",
519
+ # "minWithdrawal": "4.0",
520
+ # "numConfirmations": 1
521
+ # },
522
+ # ...
523
+ # ]
524
+ # },
554
525
  # ]
555
- # }
526
+ # }
556
527
  #
557
- assets, margin, cash = await asyncio.gather(*[assetsPromise, marginPromise, cashPromise])
558
- assetsData = self.safe_list(assets, 'data', [])
559
- marginData = self.safe_list(margin, 'data', [])
560
- cashData = self.safe_list(cash, 'data', [])
561
- assetsById = self.index_by(assetsData, 'assetCode')
562
- marginById = self.index_by(marginData, 'assetCode')
563
- cashById = self.index_by(cashData, 'assetCode')
564
- dataById = self.deep_extend(assetsById, marginById, cashById)
565
- ids = list(dataById.keys())
528
+ data = self.safe_list(response, 'data', [])
566
529
  result: dict = {}
567
- for i in range(0, len(ids)):
568
- id = self.safe_string(ids, i)
569
- currency = dataById[id]
530
+ for i in range(0, len(data)):
531
+ currency = data[i]
532
+ id = self.safe_string(currency, 'assetCode')
570
533
  code = self.safe_currency_code(id)
571
- scale = self.safe_string_2(currency, 'precisionScale', 'nativeScale')
572
- precision = self.parse_number(self.parse_precision(scale))
573
- fee = self.safe_number_2(currency, 'withdrawFee', 'withdrawalFee')
574
- status = self.safe_string(currency, 'status')
575
- statusCode = self.safe_string(currency, 'statusCode')
576
- active = (status == 'Normal')
577
- depositEnabled: Bool = None
578
- withdrawEnabled: Bool = None
579
- if status == 'Delisted' or statusCode == 'hideFromWalletTx':
580
- depositEnabled = False
581
- withdrawEnabled = False
582
- elif status == 'Normal':
583
- depositEnabled = True
584
- withdrawEnabled = True
585
- elif status == 'NoTransaction' or statusCode == 'NoTransaction':
586
- depositEnabled = True
587
- withdrawEnabled = False
588
- elif status == 'NoDeposit':
589
- depositEnabled = False
590
- withdrawEnabled = True
591
- marginInside = ('borrowAssetCode' in currency)
592
- result[code] = {
534
+ chains = self.safe_list(currency, 'blockChain', [])
535
+ precision = self.parse_number(self.parse_precision(self.safe_string(currency, 'nativeScale')))
536
+ networks = {}
537
+ for j in range(0, len(chains)):
538
+ networkEtnry = chains[j]
539
+ networkId = self.safe_string(networkEtnry, 'chainName')
540
+ networkCode = self.network_code_to_id(networkId)
541
+ networks[networkCode] = {
542
+ 'fee': self.safe_number(networkEtnry, 'withdrawFee'),
543
+ 'active': None,
544
+ 'withdraw': self.safe_bool(networkEtnry, 'allowWithdraw'),
545
+ 'deposit': self.safe_bool(networkEtnry, 'allowDeposit'),
546
+ 'precision': precision,
547
+ 'limits': {
548
+ 'amount': {
549
+ 'min': None,
550
+ 'max': None,
551
+ },
552
+ 'withdraw': {
553
+ 'min': self.safe_number(networkEtnry, 'minWithdrawal'),
554
+ 'max': None,
555
+ },
556
+ 'deposit': {
557
+ 'min': self.safe_number(networkEtnry, 'minDepositAmt'),
558
+ 'max': None,
559
+ },
560
+ },
561
+ }
562
+ # todo type: if chainsLength == 0 and (assetName.endswith(' Staking') or assetName.find(' Reward ') >= 0 or assetName.find('Slot Auction') >= 0 or assetName.find(' Freeze Asset') >= 0):
563
+ result[code] = self.safe_currency_structure({
593
564
  'id': id,
594
565
  'code': code,
595
566
  'info': currency,
596
567
  'type': None,
597
- 'margin': marginInside,
568
+ 'margin': None,
598
569
  'name': self.safe_string(currency, 'assetName'),
599
- 'active': active,
600
- 'deposit': depositEnabled,
601
- 'withdraw': withdrawEnabled,
602
- 'fee': fee,
570
+ 'active': None,
571
+ 'deposit': None,
572
+ 'withdraw': None,
573
+ 'fee': None,
603
574
  'precision': precision,
604
575
  'limits': {
605
576
  'amount': {
606
- 'min': precision,
577
+ 'min': None,
607
578
  'max': None,
608
579
  },
609
580
  'withdraw': {
@@ -611,8 +582,8 @@ class ascendex(Exchange, ImplicitAPI):
611
582
  'max': None,
612
583
  },
613
584
  },
614
- 'networks': {}, # todo
615
- }
585
+ 'networks': networks,
586
+ })
616
587
  return result
617
588
 
618
589
  async def fetch_markets(self, params={}) -> List[Market]:
@@ -621,6 +592,12 @@ class ascendex(Exchange, ImplicitAPI):
621
592
  :param dict [params]: extra parameters specific to the exchange API endpoint
622
593
  :returns dict[]: an array of objects representing market data
623
594
  """
595
+ spotPromise = self.fetch_spot_markets(params)
596
+ contractPromise = self.fetch_contract_markets(params)
597
+ spotMarkets, contractMarkets = await asyncio.gather(*[spotPromise, contractPromise])
598
+ return self.array_concat(spotMarkets, contractMarkets)
599
+
600
+ async def fetch_spot_markets(self, params={}) -> List[Market]:
624
601
  productsPromise = self.v1PublicGetProducts(params)
625
602
  #
626
603
  # {
@@ -672,7 +649,91 @@ class ascendex(Exchange, ImplicitAPI):
672
649
  # ]
673
650
  # }
674
651
  #
675
- perpetualsPromise = self.v2PublicGetFuturesContract(params)
652
+ products, cash = await asyncio.gather(*[productsPromise, cashPromise])
653
+ productsData = self.safe_list(products, 'data', [])
654
+ productsById = self.index_by(productsData, 'symbol')
655
+ cashData = self.safe_list(cash, 'data', [])
656
+ cashAndPerpetualsById = self.index_by(cashData, 'symbol')
657
+ dataById = self.deep_extend(productsById, cashAndPerpetualsById)
658
+ ids = list(dataById.keys())
659
+ result = []
660
+ for i in range(0, len(ids)):
661
+ id = ids[i]
662
+ if id.find('-PERP') >= 0:
663
+ continue # skip perpetuals, endpoint returns them
664
+ market = dataById[id]
665
+ status = self.safe_string(market, 'status')
666
+ domain = self.safe_string(market, 'domain')
667
+ active = False
668
+ if ((status == 'Normal') or (status == 'InternalTrading')) and (domain != 'LeveragedETF'):
669
+ active = True
670
+ minQty = self.safe_number(market, 'minQty')
671
+ maxQty = self.safe_number(market, 'maxQty')
672
+ minPrice = self.safe_number(market, 'tickSize')
673
+ maxPrice: Num = None
674
+ underlying = self.safe_string_2(market, 'underlying', 'symbol')
675
+ parts = underlying.split('/')
676
+ baseId = self.safe_string(parts, 0)
677
+ quoteId = self.safe_string(parts, 1)
678
+ base = self.safe_currency_code(baseId)
679
+ quote = self.safe_currency_code(quoteId)
680
+ fee = self.safe_number(market, 'commissionReserveRate')
681
+ marginTradable = self.safe_bool(market, 'marginTradable', False)
682
+ result.append({
683
+ 'id': id,
684
+ 'symbol': base + '/' + quote,
685
+ 'base': base,
686
+ 'baseId': baseId,
687
+ 'quote': quote,
688
+ 'quoteId': quoteId,
689
+ 'settle': None,
690
+ 'settleId': None,
691
+ 'type': 'spot',
692
+ 'spot': True,
693
+ 'margin': marginTradable,
694
+ 'swap': False,
695
+ 'future': False,
696
+ 'option': False,
697
+ 'active': active,
698
+ 'contract': False,
699
+ 'linear': None,
700
+ 'inverse': None,
701
+ 'taker': fee,
702
+ 'maker': fee,
703
+ 'contractSize': None,
704
+ 'expiry': None,
705
+ 'expiryDatetime': None,
706
+ 'strike': None,
707
+ 'optionType': None,
708
+ 'precision': {
709
+ 'amount': self.safe_number(market, 'lotSize'),
710
+ 'price': self.safe_number(market, 'tickSize'),
711
+ },
712
+ 'limits': {
713
+ 'leverage': {
714
+ 'min': None,
715
+ 'max': None,
716
+ },
717
+ 'amount': {
718
+ 'min': minQty,
719
+ 'max': maxQty,
720
+ },
721
+ 'price': {
722
+ 'min': minPrice,
723
+ 'max': maxPrice,
724
+ },
725
+ 'cost': {
726
+ 'min': self.safe_number(market, 'minNotional'),
727
+ 'max': self.safe_number(market, 'maxNotional'),
728
+ },
729
+ },
730
+ 'created': self.safe_integer(market, 'tradingStartTime'),
731
+ 'info': market,
732
+ })
733
+ return result
734
+
735
+ async def fetch_contract_markets(self, params={}) -> List[Market]:
736
+ contracts = await self.v2PublicGetFuturesContract(params)
676
737
  #
677
738
  # {
678
739
  # "code": 0,
@@ -685,9 +746,9 @@ class ascendex(Exchange, ImplicitAPI):
685
746
  # "underlying": "BTC/USDT",
686
747
  # "tradingStartTime": 1579701600000,
687
748
  # "priceFilter": {
688
- # "minPrice": "1",
749
+ # "minPrice": "0.1",
689
750
  # "maxPrice": "1000000",
690
- # "tickSize": "1"
751
+ # "tickSize": "0.1"
691
752
  # },
692
753
  # "lotSizeFilter": {
693
754
  # "minQty": "0.0001",
@@ -710,50 +771,25 @@ class ascendex(Exchange, ImplicitAPI):
710
771
  # ]
711
772
  # }
712
773
  #
713
- products, cash, perpetuals = await asyncio.gather(*[productsPromise, cashPromise, perpetualsPromise])
714
- productsData = self.safe_list(products, 'data', [])
715
- productsById = self.index_by(productsData, 'symbol')
716
- cashData = self.safe_list(cash, 'data', [])
717
- perpetualsData = self.safe_list(perpetuals, 'data', [])
718
- cashAndPerpetualsData = self.array_concat(cashData, perpetualsData)
719
- cashAndPerpetualsById = self.index_by(cashAndPerpetualsData, 'symbol')
720
- dataById = self.deep_extend(productsById, cashAndPerpetualsById)
721
- ids = list(dataById.keys())
774
+ data = self.safe_list(contracts, 'data', [])
722
775
  result = []
723
- for i in range(0, len(ids)):
724
- id = ids[i]
725
- market = dataById[id]
726
- settleId = self.safe_string(market, 'settlementAsset')
727
- settle = self.safe_currency_code(settleId)
728
- status = self.safe_string(market, 'status')
729
- domain = self.safe_string(market, 'domain')
730
- active = False
731
- if ((status == 'Normal') or (status == 'InternalTrading')) and (domain != 'LeveragedETF'):
732
- active = True
733
- spot = settle is None
734
- swap = not spot
735
- linear = True if swap else None
736
- minQty = self.safe_number(market, 'minQty')
737
- maxQty = self.safe_number(market, 'maxQty')
738
- minPrice = self.safe_number(market, 'tickSize')
739
- maxPrice: Num = None
740
- underlying = self.safe_string_2(market, 'underlying', 'symbol')
776
+ for i in range(0, len(data)):
777
+ market = data[i]
778
+ id = self.safe_string(market, 'symbol')
779
+ underlying = self.safe_string(market, 'underlying')
741
780
  parts = underlying.split('/')
742
781
  baseId = self.safe_string(parts, 0)
743
- quoteId = self.safe_string(parts, 1)
744
782
  base = self.safe_currency_code(baseId)
783
+ quoteId = self.safe_string(parts, 1)
745
784
  quote = self.safe_currency_code(quoteId)
746
- symbol = base + '/' + quote
747
- if swap:
748
- lotSizeFilter = self.safe_dict(market, 'lotSizeFilter')
749
- minQty = self.safe_number(lotSizeFilter, 'minQty')
750
- maxQty = self.safe_number(lotSizeFilter, 'maxQty')
751
- priceFilter = self.safe_dict(market, 'priceFilter')
752
- minPrice = self.safe_number(priceFilter, 'minPrice')
753
- maxPrice = self.safe_number(priceFilter, 'maxPrice')
754
- symbol = base + '/' + quote + ':' + settle
785
+ settleId = self.safe_string(market, 'settlementAsset')
786
+ settle = self.safe_currency_code(settleId)
787
+ linear = settle == quote
788
+ inverse = settle == base
789
+ symbol = base + '/' + quote + ':' + settle
790
+ priceFilter = self.safe_dict(market, 'priceFilter')
791
+ lotSizeFilter = self.safe_dict(market, 'lotSizeFilter')
755
792
  fee = self.safe_number(market, 'commissionReserveRate')
756
- marginTradable = self.safe_bool(market, 'marginTradable', False)
757
793
  result.append({
758
794
  'id': id,
759
795
  'symbol': symbol,
@@ -763,26 +799,26 @@ class ascendex(Exchange, ImplicitAPI):
763
799
  'baseId': baseId,
764
800
  'quoteId': quoteId,
765
801
  'settleId': settleId,
766
- 'type': 'swap' if swap else 'spot',
767
- 'spot': spot,
768
- 'margin': marginTradable if spot else None,
769
- 'swap': swap,
802
+ 'type': 'swap',
803
+ 'spot': False,
804
+ 'margin': None,
805
+ 'swap': True,
770
806
  'future': False,
771
807
  'option': False,
772
- 'active': active,
773
- 'contract': swap,
808
+ 'active': self.safe_string(market, 'status') == 'Normal',
809
+ 'contract': True,
774
810
  'linear': linear,
775
- 'inverse': not linear if swap else None,
811
+ 'inverse': inverse,
776
812
  'taker': fee,
777
813
  'maker': fee,
778
- 'contractSize': self.parse_number('1') if swap else None,
814
+ 'contractSize': self.parse_number('1'),
779
815
  'expiry': None,
780
816
  'expiryDatetime': None,
781
817
  'strike': None,
782
818
  'optionType': None,
783
819
  'precision': {
784
- 'amount': self.safe_number(market, 'lotSize'),
785
- 'price': self.safe_number(market, 'tickSize'),
820
+ 'amount': self.safe_number(lotSizeFilter, 'lotSize'),
821
+ 'price': self.safe_number(priceFilter, 'tickSize'),
786
822
  },
787
823
  'limits': {
788
824
  'leverage': {
@@ -790,12 +826,12 @@ class ascendex(Exchange, ImplicitAPI):
790
826
  'max': None,
791
827
  },
792
828
  'amount': {
793
- 'min': minQty,
794
- 'max': maxQty,
829
+ 'min': self.safe_number(lotSizeFilter, 'minQty'),
830
+ 'max': self.safe_number(lotSizeFilter, 'maxQty'),
795
831
  },
796
832
  'price': {
797
- 'min': minPrice,
798
- 'max': maxPrice,
833
+ 'min': self.safe_number(priceFilter, 'minPrice'),
834
+ 'max': self.safe_number(priceFilter, 'maxPrice'),
799
835
  },
800
836
  'cost': {
801
837
  'min': self.safe_number(market, 'minNotional'),
@@ -2,7 +2,7 @@
2
2
 
3
3
  # -----------------------------------------------------------------------------
4
4
 
5
- __version__ = '4.4.85'
5
+ __version__ = '4.4.87'
6
6
 
7
7
  # -----------------------------------------------------------------------------
8
8
 
@@ -176,15 +176,7 @@ class Exchange(BaseExchange):
176
176
  if (socksProxy not in self.socks_proxy_sessions):
177
177
  # Create our SSL context object with our CA cert file
178
178
  self.open() # ensure `asyncio_loop` is set
179
- self.aiohttp_socks_connector = ProxyConnector.from_url(
180
- socksProxy,
181
- # extra args copied from self.open()
182
- ssl=self.ssl_context,
183
- loop=self.asyncio_loop,
184
- enable_cleanup_closed=True
185
- )
186
- self.socks_proxy_sessions[socksProxy] = aiohttp.ClientSession(loop=self.asyncio_loop, connector=self.aiohttp_socks_connector, trust_env=self.aiohttp_trust_env)
187
- proxy_session = self.socks_proxy_sessions[socksProxy]
179
+ proxy_session = self.get_socks_proxy_session(socksProxy)
188
180
  # add aiohttp_proxy for python as exclusion
189
181
  elif self.aiohttp_proxy:
190
182
  final_proxy = self.aiohttp_proxy
@@ -267,6 +259,20 @@ class Exchange(BaseExchange):
267
259
  return http_response
268
260
  return response.content
269
261
 
262
+ def get_socks_proxy_session(self, socksProxy):
263
+ if (self.socks_proxy_sessions is None):
264
+ self.socks_proxy_sessions = {}
265
+ if (socksProxy not in self.socks_proxy_sessions):
266
+ self.aiohttp_socks_connector = ProxyConnector.from_url(
267
+ socksProxy,
268
+ # extra args copied from self.open()
269
+ ssl=self.ssl_context,
270
+ loop=self.asyncio_loop,
271
+ enable_cleanup_closed=True
272
+ )
273
+ self.socks_proxy_sessions[socksProxy] = aiohttp.ClientSession(loop=self.asyncio_loop, connector=self.aiohttp_socks_connector, trust_env=self.aiohttp_trust_env)
274
+ return self.socks_proxy_sessions[socksProxy]
275
+
270
276
  async def load_markets_helper(self, reload=False, params={}):
271
277
  if not reload:
272
278
  if self.markets:
@@ -411,19 +417,12 @@ class Exchange(BaseExchange):
411
417
  # we use aiohttp instead of fastClient now because of this
412
418
  # https://github.com/ccxt/ccxt/pull/25995
413
419
  self.clients[url] = AiohttpClient(url, on_message, on_error, on_close, on_connected, options)
414
- self.clients[url].proxy = self.get_ws_proxy()
420
+ # set http/s proxy (socks proxy should be set in other place)
421
+ httpProxy, httpsProxy, socksProxy = self.check_ws_proxy_settings()
422
+ if (httpProxy or httpsProxy):
423
+ self.clients[url].proxy = httpProxy if httpProxy else httpsProxy
415
424
  return self.clients[url]
416
425
 
417
- def get_ws_proxy(self):
418
- httpProxy, httpsProxy, socksProxy = self.check_ws_proxy_settings()
419
- if httpProxy:
420
- return httpProxy
421
- elif httpsProxy:
422
- return httpsProxy
423
- elif socksProxy:
424
- return socksProxy
425
- return None
426
-
427
426
  def delay(self, timeout, method, *args):
428
427
  return self.asyncio_loop.call_later(timeout / 1000, self.spawn, method, *args)
429
428
 
@@ -486,8 +485,13 @@ class Exchange(BaseExchange):
486
485
  if not subscribed:
487
486
  client.subscriptions[subscribe_hash] = subscription or True
488
487
 
488
+ selected_session = self.session
489
+ # http/s proxy is being set in other places
490
+ httpProxy, httpsProxy, socksProxy = self.check_ws_proxy_settings()
491
+ if (socksProxy):
492
+ selected_session = self.get_socks_proxy_session(socksProxy)
489
493
  connected = client.connected if client.connected.done() \
490
- else asyncio.ensure_future(client.connect(self.session, backoff_delay))
494
+ else asyncio.ensure_future(client.connect(selected_session, backoff_delay))
491
495
 
492
496
  def after(fut):
493
497
  # todo: decouple signing from subscriptions
@@ -899,15 +903,15 @@ class Exchange(BaseExchange):
899
903
  if self.enableRateLimit:
900
904
  cost = self.calculate_rate_limiter_cost(api, method, path, params, config)
901
905
  await self.throttle(cost)
906
+ retries = None
907
+ retries, params = self.handle_option_and_params(params, path, 'maxRetriesOnFailure', 0)
908
+ retryDelay = None
909
+ retryDelay, params = self.handle_option_and_params(params, path, 'maxRetriesOnFailureDelay', 0)
902
910
  self.lastRestRequestTimestamp = self.milliseconds()
903
911
  request = self.sign(path, api, method, params, headers, body)
904
912
  self.last_request_headers = request['headers']
905
913
  self.last_request_body = request['body']
906
914
  self.last_request_url = request['url']
907
- retries = None
908
- retries, params = self.handle_option_and_params(params, path, 'maxRetriesOnFailure', 0)
909
- retryDelay = None
910
- retryDelay, params = self.handle_option_and_params(params, path, 'maxRetriesOnFailureDelay', 0)
911
915
  for i in range(0, retries + 1):
912
916
  try:
913
917
  return await self.fetch(request['url'], request['method'], request['headers'], request['body'])
@@ -14,8 +14,8 @@ class bequant(hitbtc, ImplicitAPI):
14
14
  return self.deep_extend(super(bequant, self).describe(), {
15
15
  'id': 'bequant',
16
16
  'name': 'Bequant',
17
- 'countries': ['MT'], # Malta
18
17
  'pro': True,
18
+ 'countries': ['MT'], # Malta
19
19
  'urls': {
20
20
  'logo': 'https://github.com/user-attachments/assets/0583ef1f-29fe-4b7c-8189-63565a0e2867',
21
21
  'api': {
@@ -5024,13 +5024,13 @@ class binance(Exchange, ImplicitAPI):
5024
5024
  postOnly = self.is_post_only(initialUppercaseType == 'MARKET', initialUppercaseType == 'LIMIT_MAKER', params)
5025
5025
  if postOnly:
5026
5026
  uppercaseType = 'LIMIT_MAKER'
5027
- request['type'] = uppercaseType
5028
5027
  triggerPrice = self.safe_number_2(params, 'stopPrice', 'triggerPrice')
5029
5028
  if triggerPrice is not None:
5030
5029
  if uppercaseType == 'MARKET':
5031
5030
  uppercaseType = 'STOP_LOSS'
5032
5031
  elif uppercaseType == 'LIMIT':
5033
5032
  uppercaseType = 'STOP_LOSS_LIMIT'
5033
+ request['type'] = uppercaseType
5034
5034
  validOrderTypes = self.safe_list(market['info'], 'orderTypes')
5035
5035
  if not self.in_array(uppercaseType, validOrderTypes):
5036
5036
  if initialUppercaseType != uppercaseType:
@@ -2371,7 +2371,7 @@ class bitget(Exchange, ImplicitAPI):
2371
2371
  'coin': currency['id'],
2372
2372
  'address': address,
2373
2373
  'chain': networkId,
2374
- 'size': amount,
2374
+ 'size': self.currency_to_precision(code, amount, networkCode),
2375
2375
  'transferType': 'on_chain',
2376
2376
  }
2377
2377
  if tag is not None:
@@ -2395,8 +2395,6 @@ class bitget(Exchange, ImplicitAPI):
2395
2395
  fillResponseFromRequest = self.safe_bool(withdrawOptions, 'fillResponseFromRequest', True)
2396
2396
  if fillResponseFromRequest:
2397
2397
  result['currency'] = code
2398
- result['timestamp'] = self.milliseconds()
2399
- result['datetime'] = self.iso8601(self.milliseconds())
2400
2398
  result['amount'] = amount
2401
2399
  result['tag'] = tag
2402
2400
  result['address'] = address
@@ -2514,7 +2512,9 @@ class bitget(Exchange, ImplicitAPI):
2514
2512
  status = self.safe_string(transaction, 'status')
2515
2513
  tag = self.safe_string(transaction, 'tag')
2516
2514
  feeCostString = self.safe_string(transaction, 'fee')
2517
- feeCostAbsString = Precise.string_abs(feeCostString)
2515
+ feeCostAbsString = None
2516
+ if feeCostString is not None:
2517
+ feeCostAbsString = Precise.string_abs(feeCostString)
2518
2518
  fee = None
2519
2519
  amountString = self.safe_string(transaction, 'size')
2520
2520
  if feeCostAbsString is not None:
@@ -2306,7 +2306,7 @@ class bitmart(Exchange, ImplicitAPI):
2306
2306
  code = self.safe_currency_code(currencyId)
2307
2307
  account = self.account()
2308
2308
  account['free'] = self.safe_string_2(balance, 'available', 'available_balance')
2309
- account['used'] = self.safe_string_2(balance, 'frozen', 'frozen_balance')
2309
+ account['used'] = self.safe_string_n(balance, ['unAvailable', 'frozen', 'frozen_balance'])
2310
2310
  result[code] = account
2311
2311
  return self.safe_balance(result)
2312
2312