ccxt 4.4.82__py2.py3-none-any.whl → 4.4.86__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 (121) hide show
  1. ccxt/__init__.py +3 -9
  2. ccxt/abstract/blofin.py +8 -0
  3. ccxt/abstract/btcbox.py +1 -0
  4. ccxt/abstract/myokx.py +2 -0
  5. ccxt/abstract/okx.py +2 -0
  6. ccxt/apex.py +2 -1
  7. ccxt/ascendex.py +187 -151
  8. ccxt/async_support/__init__.py +3 -9
  9. ccxt/async_support/apex.py +2 -1
  10. ccxt/async_support/ascendex.py +187 -151
  11. ccxt/async_support/base/exchange.py +51 -24
  12. ccxt/async_support/base/ws/cache.py +6 -1
  13. ccxt/async_support/bequant.py +1 -1
  14. ccxt/async_support/bitget.py +5 -6
  15. ccxt/async_support/bitmart.py +1 -1
  16. ccxt/async_support/bitrue.py +14 -32
  17. ccxt/async_support/bitso.py +33 -0
  18. ccxt/async_support/bitstamp.py +33 -0
  19. ccxt/async_support/{huobijp.py → bittrade.py} +11 -11
  20. ccxt/async_support/blofin.py +145 -14
  21. ccxt/async_support/btcbox.py +25 -5
  22. ccxt/async_support/bybit.py +16 -37
  23. ccxt/async_support/cex.py +2 -4
  24. ccxt/async_support/coinbase.py +58 -47
  25. ccxt/async_support/coinbaseexchange.py +141 -32
  26. ccxt/async_support/coincatch.py +14 -67
  27. ccxt/async_support/coinex.py +28 -29
  28. ccxt/async_support/coinlist.py +17 -16
  29. ccxt/async_support/coinmetro.py +20 -11
  30. ccxt/async_support/coinone.py +8 -10
  31. ccxt/async_support/coinsph.py +124 -2
  32. ccxt/async_support/cryptocom.py +109 -2
  33. ccxt/async_support/cryptomus.py +42 -80
  34. ccxt/async_support/delta.py +75 -36
  35. ccxt/async_support/deribit.py +4 -5
  36. ccxt/async_support/derive.py +46 -10
  37. ccxt/async_support/ellipx.py +175 -77
  38. ccxt/async_support/gate.py +1 -1
  39. ccxt/async_support/gemini.py +3 -4
  40. ccxt/async_support/hitbtc.py +56 -65
  41. ccxt/async_support/hollaex.py +106 -49
  42. ccxt/async_support/htx.py +20 -43
  43. ccxt/async_support/hyperliquid.py +6 -6
  44. ccxt/async_support/kraken.py +27 -23
  45. ccxt/async_support/kucoinfutures.py +5 -0
  46. ccxt/async_support/lbank.py +1 -1
  47. ccxt/async_support/mexc.py +2 -2
  48. ccxt/async_support/ndax.py +25 -24
  49. ccxt/async_support/okcoin.py +12 -29
  50. ccxt/async_support/okx.py +9 -0
  51. ccxt/async_support/onetrading.py +10 -7
  52. ccxt/async_support/oxfun.py +40 -110
  53. ccxt/async_support/paradex.py +123 -4
  54. ccxt/base/exchange.py +21 -2
  55. ccxt/base/types.py +3 -0
  56. ccxt/bequant.py +1 -1
  57. ccxt/bitget.py +5 -6
  58. ccxt/bitmart.py +1 -1
  59. ccxt/bitrue.py +14 -32
  60. ccxt/bitso.py +33 -0
  61. ccxt/bitstamp.py +33 -0
  62. ccxt/{huobijp.py → bittrade.py} +11 -11
  63. ccxt/blofin.py +145 -14
  64. ccxt/btcbox.py +24 -5
  65. ccxt/bybit.py +16 -37
  66. ccxt/cex.py +2 -4
  67. ccxt/coinbase.py +58 -47
  68. ccxt/coinbaseexchange.py +141 -32
  69. ccxt/coincatch.py +14 -67
  70. ccxt/coinex.py +28 -29
  71. ccxt/coinlist.py +17 -16
  72. ccxt/coinmetro.py +20 -11
  73. ccxt/coinone.py +8 -10
  74. ccxt/coinsph.py +124 -2
  75. ccxt/cryptocom.py +109 -2
  76. ccxt/cryptomus.py +42 -80
  77. ccxt/delta.py +75 -36
  78. ccxt/deribit.py +4 -5
  79. ccxt/derive.py +46 -10
  80. ccxt/ellipx.py +175 -77
  81. ccxt/gate.py +1 -1
  82. ccxt/gemini.py +3 -4
  83. ccxt/hitbtc.py +56 -65
  84. ccxt/hollaex.py +106 -49
  85. ccxt/htx.py +20 -43
  86. ccxt/hyperliquid.py +6 -6
  87. ccxt/kraken.py +27 -23
  88. ccxt/kucoinfutures.py +5 -0
  89. ccxt/lbank.py +1 -1
  90. ccxt/mexc.py +2 -2
  91. ccxt/ndax.py +25 -24
  92. ccxt/okcoin.py +12 -29
  93. ccxt/okx.py +9 -0
  94. ccxt/onetrading.py +10 -7
  95. ccxt/oxfun.py +40 -110
  96. ccxt/paradex.py +123 -4
  97. ccxt/pro/__init__.py +109 -5
  98. ccxt/pro/binance.py +32 -33
  99. ccxt/pro/bithumb.py +5 -3
  100. ccxt/pro/{huobijp.py → bittrade.py} +3 -3
  101. ccxt/pro/kraken.py +249 -79
  102. ccxt/pro/luno.py +6 -5
  103. ccxt/pro/mexc.py +254 -7
  104. ccxt/pro/poloniex.py +6 -2
  105. {ccxt-4.4.82.dist-info → ccxt-4.4.86.dist-info}/METADATA +8 -11
  106. {ccxt-4.4.82.dist-info → ccxt-4.4.86.dist-info}/RECORD +110 -121
  107. ccxt/abstract/bl3p.py +0 -19
  108. ccxt/abstract/idex.py +0 -26
  109. ccxt/abstract/kuna.py +0 -182
  110. ccxt/async_support/base/ws/fast_client.py +0 -97
  111. ccxt/async_support/bl3p.py +0 -543
  112. ccxt/async_support/idex.py +0 -1889
  113. ccxt/async_support/kuna.py +0 -1935
  114. ccxt/bl3p.py +0 -543
  115. ccxt/idex.py +0 -1889
  116. ccxt/kuna.py +0 -1935
  117. ccxt/pro/idex.py +0 -687
  118. /ccxt/abstract/{huobijp.py → bittrade.py} +0 -0
  119. {ccxt-4.4.82.dist-info → ccxt-4.4.86.dist-info}/LICENSE.txt +0 -0
  120. {ccxt-4.4.82.dist-info → ccxt-4.4.86.dist-info}/WHEEL +0 -0
  121. {ccxt-4.4.82.dist-info → ccxt-4.4.86.dist-info}/top_level.txt +0 -0
@@ -4,7 +4,7 @@
4
4
 
5
5
  # -----------------------------------------------------------------------------
6
6
 
7
- __version__ = '4.4.82'
7
+ __version__ = '4.4.86'
8
8
 
9
9
  # -----------------------------------------------------------------------------
10
10
 
@@ -87,8 +87,8 @@ from ccxt.async_support.bitrue import bitrue
87
87
  from ccxt.async_support.bitso import bitso # noqa: F401
88
88
  from ccxt.async_support.bitstamp import bitstamp # noqa: F401
89
89
  from ccxt.async_support.bitteam import bitteam # noqa: F401
90
+ from ccxt.async_support.bittrade import bittrade # noqa: F401
90
91
  from ccxt.async_support.bitvavo import bitvavo # noqa: F401
91
- from ccxt.async_support.bl3p import bl3p # noqa: F401
92
92
  from ccxt.async_support.blockchaincom import blockchaincom # noqa: F401
93
93
  from ccxt.async_support.blofin import blofin # noqa: F401
94
94
  from ccxt.async_support.btcalpha import btcalpha # noqa: F401
@@ -128,16 +128,13 @@ from ccxt.async_support.hitbtc import hitbtc
128
128
  from ccxt.async_support.hollaex import hollaex # noqa: F401
129
129
  from ccxt.async_support.htx import htx # noqa: F401
130
130
  from ccxt.async_support.huobi import huobi # noqa: F401
131
- from ccxt.async_support.huobijp import huobijp # noqa: F401
132
131
  from ccxt.async_support.hyperliquid import hyperliquid # noqa: F401
133
- from ccxt.async_support.idex import idex # noqa: F401
134
132
  from ccxt.async_support.independentreserve import independentreserve # noqa: F401
135
133
  from ccxt.async_support.indodax import indodax # noqa: F401
136
134
  from ccxt.async_support.kraken import kraken # noqa: F401
137
135
  from ccxt.async_support.krakenfutures import krakenfutures # noqa: F401
138
136
  from ccxt.async_support.kucoin import kucoin # noqa: F401
139
137
  from ccxt.async_support.kucoinfutures import kucoinfutures # noqa: F401
140
- from ccxt.async_support.kuna import kuna # noqa: F401
141
138
  from ccxt.async_support.latoken import latoken # noqa: F401
142
139
  from ccxt.async_support.lbank import lbank # noqa: F401
143
140
  from ccxt.async_support.luno import luno # noqa: F401
@@ -196,8 +193,8 @@ exchanges = [
196
193
  'bitso',
197
194
  'bitstamp',
198
195
  'bitteam',
196
+ 'bittrade',
199
197
  'bitvavo',
200
- 'bl3p',
201
198
  'blockchaincom',
202
199
  'blofin',
203
200
  'btcalpha',
@@ -237,16 +234,13 @@ exchanges = [
237
234
  'hollaex',
238
235
  'htx',
239
236
  'huobi',
240
- 'huobijp',
241
237
  'hyperliquid',
242
- 'idex',
243
238
  'independentreserve',
244
239
  'indodax',
245
240
  'kraken',
246
241
  'krakenfutures',
247
242
  'kucoin',
248
243
  'kucoinfutures',
249
- 'kuna',
250
244
  'latoken',
251
245
  'lbank',
252
246
  'luno',
@@ -1077,9 +1077,10 @@ class apex(Exchange, ImplicitAPI):
1077
1077
  for i in range(0, len(resultList)):
1078
1078
  entry = resultList[i]
1079
1079
  timestamp = self.safe_integer(entry, 'fundingTimestamp')
1080
+ marketId = self.safe_string(entry, 'symbol')
1080
1081
  rates.append({
1081
1082
  'info': entry,
1082
- 'symbol': self.safe_string(entry, 'symbol'),
1083
+ 'symbol': self.safe_symbol(marketId, market),
1083
1084
  'fundingRate': self.safe_number(entry, 'rate'),
1084
1085
  'timestamp': timestamp,
1085
1086
  'datetime': self.iso8601(timestamp),
@@ -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.82'
5
+ __version__ = '4.4.86'
6
6
 
7
7
  # -----------------------------------------------------------------------------
8
8
 
@@ -34,7 +34,7 @@ from ccxt.base.exchange import Exchange as BaseExchange, ArgumentsRequired
34
34
  # -----------------------------------------------------------------------------
35
35
 
36
36
  from ccxt.async_support.base.ws.functions import inflate, inflate64, gunzip
37
- from ccxt.async_support.base.ws.fast_client import FastClient
37
+ from ccxt.async_support.base.ws.aiohttp_client import AiohttpClient
38
38
  from ccxt.async_support.base.ws.future import Future
39
39
  from ccxt.async_support.base.ws.order_book import OrderBook, IndexedOrderBook, CountedOrderBook
40
40
 
@@ -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:
@@ -279,7 +285,28 @@ class Exchange(BaseExchange):
279
285
  markets = await self.fetch_markets(params)
280
286
  return self.set_markets(markets, currencies)
281
287
 
288
+
282
289
  async def load_markets(self, reload=False, params={}):
290
+ """
291
+ Loads and prepares the markets for trading.
292
+
293
+ Args:
294
+ reload (bool): If True, the markets will be reloaded from the exchange.
295
+ params (dict): Additional exchange-specific parameters for the request.
296
+
297
+ Returns:
298
+ dict: A dictionary of markets.
299
+
300
+ Raises:
301
+ Exception: If the markets cannot be loaded or prepared.
302
+
303
+ Notes:
304
+ This method is asynchronous.
305
+ It ensures that the markets are only loaded once, even if called multiple times.
306
+ If the markets are already loaded and `reload` is False or not provided, it returns the existing markets.
307
+ If a reload is in progress, it waits for completion before returning.
308
+ If an error occurs during loading or preparation, an exception is raised.
309
+ """
283
310
  if (reload and not self.reloading_markets) or not self.markets_loading:
284
311
  self.reloading_markets = True
285
312
  coroutine = self.load_markets_helper(reload, params)
@@ -387,20 +414,15 @@ class Exchange(BaseExchange):
387
414
  'throttle': Throttler(self.tokenBucket, self.asyncio_loop),
388
415
  'asyncio_loop': self.asyncio_loop,
389
416
  }, ws_options)
390
- self.clients[url] = FastClient(url, on_message, on_error, on_close, on_connected, options)
391
- self.clients[url].proxy = self.get_ws_proxy()
417
+ # we use aiohttp instead of fastClient now because of this
418
+ # https://github.com/ccxt/ccxt/pull/25995
419
+ self.clients[url] = AiohttpClient(url, on_message, on_error, on_close, on_connected, options)
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
392
424
  return self.clients[url]
393
425
 
394
- def get_ws_proxy(self):
395
- httpProxy, httpsProxy, socksProxy = self.check_ws_proxy_settings()
396
- if httpProxy:
397
- return httpProxy
398
- elif httpsProxy:
399
- return httpsProxy
400
- elif socksProxy:
401
- return socksProxy
402
- return None
403
-
404
426
  def delay(self, timeout, method, *args):
405
427
  return self.asyncio_loop.call_later(timeout / 1000, self.spawn, method, *args)
406
428
 
@@ -463,8 +485,13 @@ class Exchange(BaseExchange):
463
485
  if not subscribed:
464
486
  client.subscriptions[subscribe_hash] = subscription or True
465
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)
466
493
  connected = client.connected if client.connected.done() \
467
- else asyncio.ensure_future(client.connect(self.session, backoff_delay))
494
+ else asyncio.ensure_future(client.connect(selected_session, backoff_delay))
468
495
 
469
496
  def after(fut):
470
497
  # todo: decouple signing from subscriptions
@@ -1,5 +1,7 @@
1
1
  import collections
2
+ import logging
2
3
 
4
+ logger = logging.getLogger(__name__)
3
5
 
4
6
  class Delegate:
5
7
  def __init__(self, name, delegated):
@@ -151,7 +153,10 @@ class ArrayCacheBySymbolById(ArrayCache):
151
153
  if len(self._deque) == self._deque.maxlen:
152
154
  delete_item = self._deque.popleft()
153
155
  self._index.popleft()
154
- del self.hashmap[delete_item['symbol']][delete_item['id']]
156
+ try:
157
+ del self.hashmap[delete_item['symbol']][delete_item['id']]
158
+ except Exception as e:
159
+ logger.error(f"Error deleting item from hashmap: {delete_item}. Error:{e}")
155
160
  self._deque.append(item)
156
161
  self._index.append(item['id'])
157
162
  if self._clear_all_updates: