ccxt 4.4.61__py2.py3-none-any.whl → 4.4.63__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.
@@ -5,9 +5,10 @@
5
5
 
6
6
  from ccxt.async_support.base.exchange import Exchange
7
7
  from ccxt.abstract.phemex import ImplicitAPI
8
+ import asyncio
8
9
  import hashlib
9
10
  import numbers
10
- from ccxt.base.types import Any, Balances, Currencies, Currency, DepositAddress, Int, LeverageTier, LeverageTiers, MarginModification, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, FundingRate, Trade, Transaction, TransferEntry
11
+ from ccxt.base.types import Any, Balances, Conversion, Currencies, Currency, DepositAddress, Int, LeverageTier, LeverageTiers, MarginModification, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, FundingRate, Trade, Transaction, TransferEntry
11
12
  from typing import List
12
13
  from ccxt.base.errors import ExchangeError
13
14
  from ccxt.base.errors import AuthenticationError
@@ -50,6 +51,7 @@ class phemex(Exchange, ImplicitAPI):
50
51
  'cancelAllOrders': True,
51
52
  'cancelOrder': True,
52
53
  'closePosition': False,
54
+ 'createConvertTrade': True,
53
55
  'createOrder': True,
54
56
  'createReduceOnlyOrder': True,
55
57
  'createStopLimitOrder': True,
@@ -60,6 +62,9 @@ class phemex(Exchange, ImplicitAPI):
60
62
  'fetchBorrowRateHistories': False,
61
63
  'fetchBorrowRateHistory': False,
62
64
  'fetchClosedOrders': True,
65
+ 'fetchConvertQuote': True,
66
+ 'fetchConvertTrade': False,
67
+ 'fetchConvertTradeHistory': True,
63
68
  'fetchCrossBorrowRate': False,
64
69
  'fetchCrossBorrowRates': False,
65
70
  'fetchCurrencies': True,
@@ -681,7 +686,8 @@ class phemex(Exchange, ImplicitAPI):
681
686
  # }
682
687
  #
683
688
  id = self.safe_string(market, 'symbol')
684
- baseId = self.safe_string_2(market, 'baseCurrency', 'contractUnderlyingAssets')
689
+ contractUnderlyingAssets = self.safe_string(market, 'contractUnderlyingAssets')
690
+ baseId = self.safe_string(market, 'baseCurrency', contractUnderlyingAssets)
685
691
  quoteId = self.safe_string(market, 'quoteCurrency')
686
692
  settleId = self.safe_string(market, 'settleCurrency')
687
693
  base = self.safe_currency_code(baseId)
@@ -691,6 +697,9 @@ class phemex(Exchange, ImplicitAPI):
691
697
  inverse = False
692
698
  if settleId != quoteId:
693
699
  inverse = True
700
+ # some unhandled cases
701
+ if not ('baseCurrency' in market) and base == quote:
702
+ base = settle
694
703
  priceScale = self.safe_integer(market, 'priceScale')
695
704
  ratioScale = self.safe_integer(market, 'ratioScale')
696
705
  valueScale = self.safe_integer(market, 'valueScale')
@@ -876,7 +885,7 @@ class phemex(Exchange, ImplicitAPI):
876
885
  :param dict [params]: extra parameters specific to the exchange API endpoint
877
886
  :returns dict[]: an array of objects representing market data
878
887
  """
879
- v2Products = await self.v2GetPublicProducts(params)
888
+ v2ProductsPromise = self.v2GetPublicProducts(params)
880
889
  #
881
890
  # {
882
891
  # "code":0,
@@ -1026,7 +1035,8 @@ class phemex(Exchange, ImplicitAPI):
1026
1035
  # }
1027
1036
  # }
1028
1037
  #
1029
- v1Products = await self.v1GetExchangePublicProducts(params)
1038
+ v1ProductsPromise = self.v1GetExchangePublicProducts(params)
1039
+ v2Products, v1Products = await asyncio.gather(*[v2ProductsPromise, v1ProductsPromise])
1030
1040
  v1ProductsData = self.safe_value(v1Products, 'data', [])
1031
1041
  #
1032
1042
  # {
@@ -1063,14 +1073,14 @@ class phemex(Exchange, ImplicitAPI):
1063
1073
  # ]
1064
1074
  # }
1065
1075
  #
1066
- v2ProductsData = self.safe_value(v2Products, 'data', {})
1067
- products = self.safe_value(v2ProductsData, 'products', [])
1068
- perpetualProductsV2 = self.safe_value(v2ProductsData, 'perpProductsV2', [])
1076
+ v2ProductsData = self.safe_dict(v2Products, 'data', {})
1077
+ products = self.safe_list(v2ProductsData, 'products', [])
1078
+ perpetualProductsV2 = self.safe_list(v2ProductsData, 'perpProductsV2', [])
1069
1079
  products = self.array_concat(products, perpetualProductsV2)
1070
- riskLimits = self.safe_value(v2ProductsData, 'riskLimits', [])
1071
- riskLimitsV2 = self.safe_value(v2ProductsData, 'riskLimitsV2', [])
1080
+ riskLimits = self.safe_list(v2ProductsData, 'riskLimits', [])
1081
+ riskLimitsV2 = self.safe_list(v2ProductsData, 'riskLimitsV2', [])
1072
1082
  riskLimits = self.array_concat(riskLimits, riskLimitsV2)
1073
- currencies = self.safe_value(v2ProductsData, 'currencies', [])
1083
+ currencies = self.safe_list(v2ProductsData, 'currencies', [])
1074
1084
  riskLimitsById = self.index_by(riskLimits, 'symbol')
1075
1085
  v1ProductsById = self.index_by(v1ProductsData, 'symbol')
1076
1086
  currenciesByCode = self.index_by(currencies, 'currency')
@@ -1078,16 +1088,16 @@ class phemex(Exchange, ImplicitAPI):
1078
1088
  for i in range(0, len(products)):
1079
1089
  market = products[i]
1080
1090
  type = self.safe_string_lower(market, 'type')
1081
- if (type == 'perpetual') or (type == 'perpetualv2') or (type == 'PerpetualPilot'):
1091
+ if (type == 'perpetual') or (type == 'perpetualv2') or (type == 'perpetualpilot'):
1082
1092
  id = self.safe_string(market, 'symbol')
1083
- riskLimitValues = self.safe_value(riskLimitsById, id, {})
1093
+ riskLimitValues = self.safe_dict(riskLimitsById, id, {})
1084
1094
  market = self.extend(market, riskLimitValues)
1085
- v1ProductsValues = self.safe_value(v1ProductsById, id, {})
1095
+ v1ProductsValues = self.safe_dict(v1ProductsById, id, {})
1086
1096
  market = self.extend(market, v1ProductsValues)
1087
1097
  market = self.parse_swap_market(market)
1088
1098
  else:
1089
1099
  baseCurrency = self.safe_string(market, 'baseCurrency')
1090
- currencyValues = self.safe_value(currenciesByCode, baseCurrency, {})
1100
+ currencyValues = self.safe_dict(currenciesByCode, baseCurrency, {})
1091
1101
  valueScale = self.safe_string(currencyValues, 'valueScale', '8')
1092
1102
  market = self.extend(market, {'valueScale': valueScale})
1093
1103
  market = self.parse_spot_market(market)
@@ -1254,7 +1264,7 @@ class phemex(Exchange, ImplicitAPI):
1254
1264
  precise.decimals = precise.decimals - scale
1255
1265
  precise.reduce()
1256
1266
  preciseString = str(precise)
1257
- return self.parse_to_int(preciseString)
1267
+ return self.parse_to_numeric(preciseString)
1258
1268
 
1259
1269
  def to_ev(self, amount, market: Market = None):
1260
1270
  if (amount is None) or (market is None):
@@ -2547,7 +2557,6 @@ class phemex(Exchange, ImplicitAPI):
2547
2557
  market = self.market(symbol)
2548
2558
  requestSide = self.capitalize(side)
2549
2559
  type = self.capitalize(type)
2550
- reduceOnly = self.safe_bool(params, 'reduceOnly')
2551
2560
  request: dict = {
2552
2561
  # common
2553
2562
  'symbol': market['id'],
@@ -2630,8 +2639,10 @@ class phemex(Exchange, ImplicitAPI):
2630
2639
  posSide = self.safe_string_lower(params, 'posSide')
2631
2640
  if posSide is None:
2632
2641
  if hedged:
2642
+ reduceOnly = self.safe_bool(params, 'reduceOnly')
2633
2643
  if reduceOnly:
2634
2644
  side = 'sell' if (side == 'buy') else 'buy'
2645
+ params = self.omit(params, 'reduceOnly')
2635
2646
  posSide = 'Long' if (side == 'buy') else 'Short'
2636
2647
  else:
2637
2648
  posSide = 'Merged'
@@ -2709,7 +2720,6 @@ class phemex(Exchange, ImplicitAPI):
2709
2720
  else:
2710
2721
  request['stopLossEp'] = self.to_ep(stopLossPrice, market)
2711
2722
  params = self.omit(params, 'stopLossPrice')
2712
- params = self.omit(params, 'reduceOnly')
2713
2723
  response = None
2714
2724
  if market['settle'] == 'USDT':
2715
2725
  response = await self.privatePostGOrders(self.extend(request, params))
@@ -4760,6 +4770,218 @@ class phemex(Exchange, ImplicitAPI):
4760
4770
  'datetime': self.iso8601(timestamp),
4761
4771
  }, market)
4762
4772
 
4773
+ async def fetch_convert_quote(self, fromCode: str, toCode: str, amount: Num = None, params={}) -> Conversion:
4774
+ """
4775
+ fetch a quote for converting from one currency to another
4776
+
4777
+ https://phemex-docs.github.io/#rfq-quote
4778
+
4779
+ :param str fromCode: the currency that you want to sell and convert from
4780
+ :param str toCode: the currency that you want to buy and convert into
4781
+ :param float amount: how much you want to trade in units of the from currency
4782
+ :param dict [params]: extra parameters specific to the exchange API endpoint
4783
+ :returns dict: a `conversion structure <https://docs.ccxt.com/#/?id=conversion-structure>`
4784
+ """
4785
+ await self.load_markets()
4786
+ fromCurrency = self.currency(fromCode)
4787
+ toCurrency = self.currency(toCode)
4788
+ valueScale = self.safe_integer(fromCurrency, 'valueScale')
4789
+ request: dict = {
4790
+ 'fromCurrency': fromCode,
4791
+ 'toCurrency': toCode,
4792
+ 'fromAmountEv': self.to_en(amount, valueScale),
4793
+ }
4794
+ response = await self.privateGetAssetsQuote(self.extend(request, params))
4795
+ #
4796
+ # {
4797
+ # "code": 0,
4798
+ # "msg": "OK",
4799
+ # "data": {
4800
+ # "code": "GIF...AAA",
4801
+ # "quoteArgs": {
4802
+ # "origin": 10,
4803
+ # "price": "0.00000939",
4804
+ # "proceeds": "0.00000000",
4805
+ # "ttlMs": 7000,
4806
+ # "expireAt": 1739875826009,
4807
+ # "requestAt": 1739875818009,
4808
+ # "quoteAt": 1739875816594
4809
+ # }
4810
+ # }
4811
+ # }
4812
+ #
4813
+ data = self.safe_dict(response, 'data', {})
4814
+ return self.parse_conversion(data, fromCurrency, toCurrency)
4815
+
4816
+ async def create_convert_trade(self, id: str, fromCode: str, toCode: str, amount: Num = None, params={}) -> Conversion:
4817
+ """
4818
+ convert from one currency to another
4819
+
4820
+ https://phemex-docs.github.io/#convert
4821
+
4822
+ :param str id: the id of the trade that you want to make
4823
+ :param str fromCode: the currency that you want to sell and convert from
4824
+ :param str toCode: the currency that you want to buy and convert into
4825
+ :param float [amount]: how much you want to trade in units of the from currency
4826
+ :param dict [params]: extra parameters specific to the exchange API endpoint
4827
+ :returns dict: a `conversion structure <https://docs.ccxt.com/#/?id=conversion-structure>`
4828
+ """
4829
+ await self.load_markets()
4830
+ fromCurrency = self.currency(fromCode)
4831
+ toCurrency = self.currency(toCode)
4832
+ valueScale = self.safe_integer(fromCurrency, 'valueScale')
4833
+ request: dict = {
4834
+ 'code': id,
4835
+ 'fromCurrency': fromCode,
4836
+ 'toCurrency': toCode,
4837
+ }
4838
+ if amount is not None:
4839
+ request['fromAmountEv'] = self.to_en(amount, valueScale)
4840
+ response = await self.privatePostAssetsConvert(self.extend(request, params))
4841
+ #
4842
+ # {
4843
+ # "code": 0,
4844
+ # "msg": "OK",
4845
+ # "data": {
4846
+ # "moveOp": 0,
4847
+ # "fromCurrency": "USDT",
4848
+ # "toCurrency": "BTC",
4849
+ # "fromAmountEv": 4000000000,
4850
+ # "toAmountEv": 41511,
4851
+ # "linkKey": "45c8ed8e-d3f4-472d-8262-e464e8c46247",
4852
+ # "status": 10
4853
+ # }
4854
+ # }
4855
+ #
4856
+ data = self.safe_dict(response, 'data', {})
4857
+ fromCurrencyId = self.safe_string(data, 'fromCurrency')
4858
+ fromResult = self.safe_currency(fromCurrencyId, fromCurrency)
4859
+ toCurrencyId = self.safe_string(data, 'toCurrency')
4860
+ to = self.safe_currency(toCurrencyId, toCurrency)
4861
+ return self.parse_conversion(data, fromResult, to)
4862
+
4863
+ async def fetch_convert_trade_history(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Conversion]:
4864
+ """
4865
+ fetch the users history of conversion trades
4866
+
4867
+ https://phemex-docs.github.io/#query-convert-history
4868
+
4869
+ :param str [code]: the unified currency code
4870
+ :param int [since]: the earliest time in ms to fetch conversions for
4871
+ :param int [limit]: the maximum number of conversion structures to retrieve, default 20, max 200
4872
+ :param dict [params]: extra parameters specific to the exchange API endpoint
4873
+ :param str [params.until]: the end time in ms
4874
+ :param str [params.fromCurrency]: the currency that you sold and converted from
4875
+ :param str [params.toCurrency]: the currency that you bought and converted into
4876
+ :returns dict[]: a list of `conversion structures <https://docs.ccxt.com/#/?id=conversion-structure>`
4877
+ """
4878
+ await self.load_markets()
4879
+ request: dict = {}
4880
+ if code is not None:
4881
+ request['fromCurrency'] = code
4882
+ if since is not None:
4883
+ request['startTime'] = since
4884
+ if limit is not None:
4885
+ request['limit'] = limit
4886
+ request, params = self.handle_until_option('endTime', request, params)
4887
+ response = await self.privateGetAssetsConvert(self.extend(request, params))
4888
+ #
4889
+ # {
4890
+ # "code": 0,
4891
+ # "msg": "OK",
4892
+ # "data": {
4893
+ # "total": 2,
4894
+ # "rows": [
4895
+ # {
4896
+ # "linkKey": "45c8ed8e-d3f4-472d-8262-e464e8c46247",
4897
+ # "createTime": 1739882294000,
4898
+ # "fromCurrency": "USDT",
4899
+ # "toCurrency": "BTC",
4900
+ # "fromAmountEv": 4000000000,
4901
+ # "toAmountEv": 41511,
4902
+ # "status": 10,
4903
+ # "conversionRate": 1037,
4904
+ # "errorCode": 0
4905
+ # },
4906
+ # ]
4907
+ # }
4908
+ # }
4909
+ #
4910
+ data = self.safe_dict(response, 'data', {})
4911
+ rows = self.safe_list(data, 'rows', [])
4912
+ return self.parse_conversions(rows, code, 'fromCurrency', 'toCurrency', since, limit)
4913
+
4914
+ def parse_conversion(self, conversion: dict, fromCurrency: Currency = None, toCurrency: Currency = None) -> Conversion:
4915
+ #
4916
+ # fetchConvertQuote
4917
+ #
4918
+ # {
4919
+ # "code": "GIF...AAA",
4920
+ # "quoteArgs": {
4921
+ # "origin": 10,
4922
+ # "price": "0.00000939",
4923
+ # "proceeds": "0.00000000",
4924
+ # "ttlMs": 7000,
4925
+ # "expireAt": 1739875826009,
4926
+ # "requestAt": 1739875818009,
4927
+ # "quoteAt": 1739875816594
4928
+ # }
4929
+ # }
4930
+ #
4931
+ # createConvertTrade
4932
+ #
4933
+ # {
4934
+ # "moveOp": 0,
4935
+ # "fromCurrency": "USDT",
4936
+ # "toCurrency": "BTC",
4937
+ # "fromAmountEv": 4000000000,
4938
+ # "toAmountEv": 41511,
4939
+ # "linkKey": "45c8ed8e-d3f4-472d-8262-e464e8c46247",
4940
+ # "status": 10
4941
+ # }
4942
+ #
4943
+ # fetchConvertTradeHistory
4944
+ #
4945
+ # {
4946
+ # "linkKey": "45c8ed8e-d3f4-472d-8262-e464e8c46247",
4947
+ # "createTime": 1739882294000,
4948
+ # "fromCurrency": "USDT",
4949
+ # "toCurrency": "BTC",
4950
+ # "fromAmountEv": 4000000000,
4951
+ # "toAmountEv": 41511,
4952
+ # "status": 10,
4953
+ # "conversionRate": 1037,
4954
+ # "errorCode": 0
4955
+ # }
4956
+ #
4957
+ quoteArgs = self.safe_dict(conversion, 'quoteArgs', {})
4958
+ requestTime = self.safe_integer(quoteArgs, 'requestAt')
4959
+ timestamp = self.safe_integer(conversion, 'createTime', requestTime)
4960
+ fromCoin = self.safe_string(conversion, 'fromCurrency', self.safe_string(fromCurrency, 'code'))
4961
+ fromCode = self.safe_currency_code(fromCoin, fromCurrency)
4962
+ toCoin = self.safe_string(conversion, 'toCurrency', self.safe_string(toCurrency, 'code'))
4963
+ toCode = self.safe_currency_code(toCoin, toCurrency)
4964
+ fromValueScale = self.safe_integer(fromCurrency, 'valueScale')
4965
+ toValueScale = self.safe_integer(toCurrency, 'valueScale')
4966
+ fromAmount = self.from_en(self.safe_string(conversion, 'fromAmountEv'), fromValueScale)
4967
+ if fromAmount is None and quoteArgs is not None:
4968
+ fromAmount = self.from_en(self.safe_string(quoteArgs, 'origin'), fromValueScale)
4969
+ toAmount = self.from_en(self.safe_string(conversion, 'toAmountEv'), toValueScale)
4970
+ if toAmount is None and quoteArgs is not None:
4971
+ toAmount = self.from_en(self.safe_string(quoteArgs, 'proceeds'), toValueScale)
4972
+ return {
4973
+ 'info': conversion,
4974
+ 'timestamp': timestamp,
4975
+ 'datetime': self.iso8601(timestamp),
4976
+ 'id': self.safe_string(conversion, 'code'),
4977
+ 'fromCurrency': fromCode,
4978
+ 'fromAmount': self.parse_number(fromAmount),
4979
+ 'toCurrency': toCode,
4980
+ 'toAmount': self.parse_number(toAmount),
4981
+ 'price': self.safe_number(quoteArgs, 'price'),
4982
+ 'fee': None,
4983
+ }
4984
+
4763
4985
  def handle_errors(self, httpCode: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
4764
4986
  if response is None:
4765
4987
  return None # fallback to default error handler
@@ -511,11 +511,11 @@ class tradeogre(Exchange, ImplicitAPI):
511
511
  if type == 'market':
512
512
  raise BadRequest(self.id + ' createOrder does not support market orders')
513
513
  if price is None:
514
- raise ArgumentsRequired(self.id + ' createOrder requires a limit parameter')
514
+ raise ArgumentsRequired(self.id + ' createOrder requires a price parameter')
515
515
  request: dict = {
516
516
  'market': market['id'],
517
- 'quantity': self.parse_to_numeric(self.amount_to_precision(symbol, amount)),
518
- 'price': self.parse_to_numeric(self.price_to_precision(symbol, price)),
517
+ 'quantity': self.amount_to_precision(symbol, amount),
518
+ 'price': self.price_to_precision(symbol, price),
519
519
  }
520
520
  response = None
521
521
  if side == 'buy':
@@ -1150,6 +1150,7 @@ class whitebit(Exchange, ImplicitAPI):
1150
1150
  # "clientOrderId": "customId11",
1151
1151
  # "role": 2, # 1 = maker, 2 = taker
1152
1152
  # "deal": "0.00419198" # amount in money
1153
+ # "feeAsset": "USDT"
1153
1154
  # }
1154
1155
  #
1155
1156
  # fetchMyTrades
@@ -1165,6 +1166,7 @@ class whitebit(Exchange, ImplicitAPI):
1165
1166
  # "deal": "9.981007",
1166
1167
  # "fee": "0.009981007",
1167
1168
  # "orderId": 58166729555,
1169
+ # "feeAsset": "USDT"
1168
1170
  # }
1169
1171
  #
1170
1172
  market = self.safe_market(None, market)
@@ -1185,7 +1187,7 @@ class whitebit(Exchange, ImplicitAPI):
1185
1187
  if feeCost is not None:
1186
1188
  fee = {
1187
1189
  'cost': feeCost,
1188
- 'currency': market['quote'],
1190
+ 'currency': self.safe_currency_code(self.safe_string(trade, 'feeAsset')),
1189
1191
  }
1190
1192
  return self.safe_trade({
1191
1193
  'info': trade,
@@ -2601,12 +2603,10 @@ class whitebit(Exchange, ImplicitAPI):
2601
2603
  url += '?' + self.urlencode(query)
2602
2604
  if accessibility == 'private':
2603
2605
  self.check_required_credentials()
2604
- nonce = self.nonce()
2605
- timestamp = self.parse_to_int(nonce / 1000)
2606
- timestampString = str(timestamp)
2606
+ nonce = str(self.nonce())
2607
2607
  secret = self.encode(self.secret)
2608
2608
  request = '/' + 'api' + '/' + version + pathWithParams
2609
- body = self.json(self.extend({'request': request, 'nonce': timestampString}, params))
2609
+ body = self.json(self.extend({'request': request, 'nonce': nonce}, params))
2610
2610
  payload = self.string_to_base64(body)
2611
2611
  signature = self.hmac(self.encode(payload), secret, hashlib.sha512)
2612
2612
  headers = {
ccxt/base/errors.py CHANGED
@@ -1,9 +1,3 @@
1
- # ----------------------------------------------------------------------------
2
-
3
- # PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
4
- # https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
5
- # EDIT THE CORRESPONDENT .ts FILE INSTEAD
6
-
7
1
  error_hierarchy = {
8
2
  'BaseError': {
9
3
  'ExchangeError': {
ccxt/base/exchange.py CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  # -----------------------------------------------------------------------------
6
6
 
7
- __version__ = '4.4.61'
7
+ __version__ = '4.4.63'
8
8
 
9
9
  # -----------------------------------------------------------------------------
10
10
 
@@ -160,6 +160,7 @@ class Exchange(object):
160
160
  symbols = None
161
161
  codes = None
162
162
  timeframes = {}
163
+ tokenBucket = None
163
164
 
164
165
  fees = {
165
166
  'trading': {
@@ -188,6 +189,7 @@ class Exchange(object):
188
189
  urls = None
189
190
  api = None
190
191
  parseJsonResponse = True
192
+ throttler = None
191
193
 
192
194
  # PROXY & USER-AGENTS (see "examples/proxy-usage" file for explanation)
193
195
  proxy = None # for backwards compatibility
@@ -223,6 +225,7 @@ class Exchange(object):
223
225
  }
224
226
  headers = None
225
227
  origin = '*' # CORS origin
228
+ MAX_VALUE = float('inf')
226
229
  #
227
230
  proxies = None
228
231
 
@@ -404,15 +407,8 @@ class Exchange(object):
404
407
  else:
405
408
  setattr(self, key, settings[key])
406
409
 
407
- if self.markets:
408
- self.set_markets(self.markets)
409
-
410
410
  self.after_construct()
411
411
 
412
- is_sandbox = self.safe_bool_2(self.options, 'sandbox', 'testnet', False)
413
- if is_sandbox:
414
- self.set_sandbox_mode(is_sandbox)
415
-
416
412
  # convert all properties from underscore notation foo_bar to camelcase notation fooBar
417
413
  cls = type(self)
418
414
  for name in dir(self):
@@ -431,13 +427,6 @@ class Exchange(object):
431
427
  else:
432
428
  setattr(self, camelcase, attr)
433
429
 
434
- self.tokenBucket = self.extend({
435
- 'refillRate': 1.0 / self.rateLimit if self.rateLimit > 0 else float('inf'),
436
- 'delay': 0.001,
437
- 'capacity': 1.0,
438
- 'defaultCost': 1.0,
439
- }, getattr(self, 'tokenBucket', {}))
440
-
441
430
  if not self.session and self.synchronous:
442
431
  self.session = Session()
443
432
  self.session.trust_env = self.requests_trust_env
@@ -456,6 +445,10 @@ class Exchange(object):
456
445
  def __str__(self):
457
446
  return self.name
458
447
 
448
+ def init_throttler(self, cost=None):
449
+ # stub in sync
450
+ pass
451
+
459
452
  def throttle(self, cost=None):
460
453
  now = float(self.milliseconds())
461
454
  elapsed = now - self.lastRestRequestTimestamp
@@ -1457,11 +1450,11 @@ class Exchange(object):
1457
1450
 
1458
1451
  @staticmethod
1459
1452
  def encode(string):
1460
- return string.encode('latin-1')
1453
+ return string.encode('utf-8')
1461
1454
 
1462
1455
  @staticmethod
1463
1456
  def decode(string):
1464
- return string.decode('latin-1')
1457
+ return string.decode('utf-8')
1465
1458
 
1466
1459
  @staticmethod
1467
1460
  def to_array(value):
@@ -2745,8 +2738,35 @@ class Exchange(object):
2745
2738
  return timestamp
2746
2739
 
2747
2740
  def after_construct(self):
2741
+ # networks
2748
2742
  self.create_networks_by_id_object()
2749
2743
  self.features_generator()
2744
+ # init predefined markets if any
2745
+ if self.markets:
2746
+ self.set_markets(self.markets)
2747
+ # init the request rate limiter
2748
+ self.init_rest_rate_limiter()
2749
+ # sanbox mode
2750
+ isSandbox = self.safe_bool_2(self.options, 'sandbox', 'testnet', False)
2751
+ if isSandbox:
2752
+ self.set_sandbox_mode(isSandbox)
2753
+
2754
+ def init_rest_rate_limiter(self):
2755
+ if self.rateLimit is None or (self.id is not None and self.rateLimit == -1):
2756
+ raise ExchangeError(self.id + '.rateLimit property is not configured')
2757
+ refillRate = self.MAX_VALUE
2758
+ if self.rateLimit > 0:
2759
+ refillRate = 1 / self.rateLimit
2760
+ defaultBucket = {
2761
+ 'delay': 0.001,
2762
+ 'capacity': 1,
2763
+ 'cost': 1,
2764
+ 'maxCapacity': 1000,
2765
+ 'refillRate': refillRate,
2766
+ }
2767
+ existingBucket = {} if (self.tokenBucket is None) else self.tokenBucket
2768
+ self.tokenBucket = self.extend(defaultBucket, existingBucket)
2769
+ self.init_throttler()
2750
2770
 
2751
2771
  def features_generator(self):
2752
2772
  #
@@ -2883,6 +2903,9 @@ class Exchange(object):
2883
2903
 
2884
2904
  def safe_currency_structure(self, currency: object):
2885
2905
  # derive data from networks: deposit, withdraw, active, fee, limits, precision
2906
+ currencyDeposit = self.safe_bool(currency, 'deposit')
2907
+ currencyWithdraw = self.safe_bool(currency, 'withdraw')
2908
+ currencyActive = self.safe_bool(currency, 'active')
2886
2909
  networks = self.safe_dict(currency, 'networks', {})
2887
2910
  keys = list(networks.keys())
2888
2911
  length = len(keys)
@@ -2890,13 +2913,13 @@ class Exchange(object):
2890
2913
  for i in range(0, length):
2891
2914
  network = networks[keys[i]]
2892
2915
  deposit = self.safe_bool(network, 'deposit')
2893
- if currency['deposit'] is None or deposit:
2916
+ if currencyDeposit is None or deposit:
2894
2917
  currency['deposit'] = deposit
2895
2918
  withdraw = self.safe_bool(network, 'withdraw')
2896
- if currency['withdraw'] is None or withdraw:
2919
+ if currencyWithdraw is None or withdraw:
2897
2920
  currency['withdraw'] = withdraw
2898
2921
  active = self.safe_bool(network, 'active')
2899
- if currency['active'] is None or active:
2922
+ if currencyActive is None or active:
2900
2923
  currency['active'] = active
2901
2924
  # find lowest fee(which is more desired)
2902
2925
  fee = self.safe_string(network, 'fee')
@@ -4045,7 +4068,9 @@ class Exchange(object):
4045
4068
  # if networkCode was not provided by user, then we try to use the default network(if it was defined in "defaultNetworks"), otherwise, we just return the first network entry
4046
4069
  defaultNetworkCode = self.default_network_code(currencyCode)
4047
4070
  defaultNetworkId = defaultNetworkCode if isIndexedByUnifiedNetworkCode else self.network_code_to_id(defaultNetworkCode, currencyCode)
4048
- chosenNetworkId = defaultNetworkId if (defaultNetworkId in indexedNetworkEntries) else availableNetworkIds[0]
4071
+ if defaultNetworkId in indexedNetworkEntries:
4072
+ return defaultNetworkId
4073
+ raise NotSupported(self.id + ' - can not determine the default network, please pass param["network"] one from : ' + ', '.join(availableNetworkIds))
4049
4074
  return chosenNetworkId
4050
4075
 
4051
4076
  def safe_number_2(self, dictionary: object, key1: IndexType, key2: IndexType, d=None):
@@ -4665,20 +4690,27 @@ class Exchange(object):
4665
4690
  :param str [defaultValue]: assigned programatically in the method calling handleMarketTypeAndParams
4666
4691
  :returns [str, dict]: the market type and params with type and defaultType omitted
4667
4692
  """
4668
- defaultType = self.safe_string_2(self.options, 'defaultType', 'type', 'spot')
4669
- if defaultValue is None: # defaultValue takes precendence over exchange wide defaultType
4670
- defaultValue = defaultType
4693
+ # type from param
4694
+ type = self.safe_string_2(params, 'defaultType', 'type')
4695
+ if type is not None:
4696
+ params = self.omit(params, ['defaultType', 'type'])
4697
+ return [type, params]
4698
+ # type from market
4699
+ if market is not None:
4700
+ return [market['type'], params]
4701
+ # type from default-argument
4702
+ if defaultValue is not None:
4703
+ return [defaultValue, params]
4671
4704
  methodOptions = self.safe_dict(self.options, methodName)
4672
- methodType = defaultValue
4673
- if methodOptions is not None: # user defined methodType takes precedence over defaultValue
4705
+ if methodOptions is not None:
4674
4706
  if isinstance(methodOptions, str):
4675
- methodType = methodOptions
4707
+ return [methodOptions, params]
4676
4708
  else:
4677
- methodType = self.safe_string_2(methodOptions, 'defaultType', 'type', methodType)
4678
- marketType = methodType if (market is None) else market['type']
4679
- type = self.safe_string_2(params, 'defaultType', 'type', marketType)
4680
- params = self.omit(params, ['defaultType', 'type'])
4681
- return [type, params]
4709
+ typeFromMethod = self.safe_string_2(methodOptions, 'defaultType', 'type')
4710
+ if typeFromMethod is not None:
4711
+ return [typeFromMethod, params]
4712
+ defaultType = self.safe_string_2(self.options, 'defaultType', 'type', 'spot')
4713
+ return [defaultType, params]
4682
4714
 
4683
4715
  def handle_sub_type_and_params(self, methodName: str, market=None, params={}, defaultValue=None):
4684
4716
  subType = None
ccxt/bingx.py CHANGED
@@ -4454,13 +4454,12 @@ class bingx(Exchange, ImplicitAPI):
4454
4454
  :param boolean [params.twap]: if fetching twap orders
4455
4455
  :returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
4456
4456
  """
4457
- if symbol is None:
4458
- raise ArgumentsRequired(self.id + ' fetchClosedOrders() requires a symbol argument')
4459
4457
  self.load_markets()
4460
- market = self.market(symbol)
4461
- request: dict = {
4462
- 'symbol': market['id'],
4463
- }
4458
+ market = None
4459
+ request: dict = {}
4460
+ if symbol is not None:
4461
+ market = self.market(symbol)
4462
+ request['symbol'] = market['id']
4464
4463
  type = None
4465
4464
  subType = None
4466
4465
  standard = None
@@ -4472,7 +4471,7 @@ class bingx(Exchange, ImplicitAPI):
4472
4471
  response = self.contractV1PrivateGetAllOrders(self.extend(request, params))
4473
4472
  elif type == 'spot':
4474
4473
  if limit is not None:
4475
- request['limit'] = limit
4474
+ request['pageSize'] = limit
4476
4475
  response = self.spotV1PrivateGetTradeHistoryOrders(self.extend(request, params))
4477
4476
  #
4478
4477
  # {