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.
- ccxt/__init__.py +1 -1
- ccxt/abstract/bitmart.py +5 -1
- ccxt/abstract/bybit.py +1 -0
- ccxt/async_support/__init__.py +1 -1
- ccxt/async_support/base/exchange.py +9 -7
- ccxt/async_support/bingx.py +6 -7
- ccxt/async_support/bitmart.py +155 -54
- ccxt/async_support/bybit.py +112 -5
- ccxt/async_support/gate.py +106 -145
- ccxt/async_support/kraken.py +2 -2
- ccxt/async_support/phemex.py +239 -17
- ccxt/async_support/tradeogre.py +3 -3
- ccxt/async_support/whitebit.py +5 -5
- ccxt/base/errors.py +0 -6
- ccxt/base/exchange.py +64 -32
- ccxt/bingx.py +6 -7
- ccxt/bitmart.py +155 -54
- ccxt/bybit.py +112 -5
- ccxt/gate.py +106 -145
- ccxt/kraken.py +2 -2
- ccxt/phemex.py +238 -17
- ccxt/pro/__init__.py +1 -1
- ccxt/pro/binance.py +25 -11
- ccxt/pro/bitmart.py +64 -53
- ccxt/pro/lbank.py +10 -4
- ccxt/pro/myokx.py +10 -1
- ccxt/test/tests_init.py +2 -1
- ccxt/tradeogre.py +3 -3
- ccxt/whitebit.py +5 -5
- {ccxt-4.4.61.dist-info → ccxt-4.4.63.dist-info}/METADATA +4 -4
- {ccxt-4.4.61.dist-info → ccxt-4.4.63.dist-info}/RECORD +34 -34
- {ccxt-4.4.61.dist-info → ccxt-4.4.63.dist-info}/LICENSE.txt +0 -0
- {ccxt-4.4.61.dist-info → ccxt-4.4.63.dist-info}/WHEEL +0 -0
- {ccxt-4.4.61.dist-info → ccxt-4.4.63.dist-info}/top_level.txt +0 -0
ccxt/async_support/phemex.py
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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.
|
1067
|
-
products = self.
|
1068
|
-
perpetualProductsV2 = self.
|
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.
|
1071
|
-
riskLimitsV2 = self.
|
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.
|
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 == '
|
1091
|
+
if (type == 'perpetual') or (type == 'perpetualv2') or (type == 'perpetualpilot'):
|
1082
1092
|
id = self.safe_string(market, 'symbol')
|
1083
|
-
riskLimitValues = self.
|
1093
|
+
riskLimitValues = self.safe_dict(riskLimitsById, id, {})
|
1084
1094
|
market = self.extend(market, riskLimitValues)
|
1085
|
-
v1ProductsValues = self.
|
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.
|
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.
|
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
|
ccxt/async_support/tradeogre.py
CHANGED
@@ -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
|
514
|
+
raise ArgumentsRequired(self.id + ' createOrder requires a price parameter')
|
515
515
|
request: dict = {
|
516
516
|
'market': market['id'],
|
517
|
-
'quantity': self.
|
518
|
-
'price': self.
|
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':
|
ccxt/async_support/whitebit.py
CHANGED
@@ -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':
|
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':
|
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.
|
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('
|
1453
|
+
return string.encode('utf-8')
|
1461
1454
|
|
1462
1455
|
@staticmethod
|
1463
1456
|
def decode(string):
|
1464
|
-
return string.decode('
|
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
|
2916
|
+
if currencyDeposit is None or deposit:
|
2894
2917
|
currency['deposit'] = deposit
|
2895
2918
|
withdraw = self.safe_bool(network, 'withdraw')
|
2896
|
-
if
|
2919
|
+
if currencyWithdraw is None or withdraw:
|
2897
2920
|
currency['withdraw'] = withdraw
|
2898
2921
|
active = self.safe_bool(network, 'active')
|
2899
|
-
if
|
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
|
-
|
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
|
-
|
4669
|
-
|
4670
|
-
|
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
|
-
|
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
|
-
|
4707
|
+
return [methodOptions, params]
|
4676
4708
|
else:
|
4677
|
-
|
4678
|
-
|
4679
|
-
|
4680
|
-
|
4681
|
-
return [
|
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 =
|
4461
|
-
request: dict = {
|
4462
|
-
|
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['
|
4474
|
+
request['pageSize'] = limit
|
4476
4475
|
response = self.spotV1PrivateGetTradeHistoryOrders(self.extend(request, params))
|
4477
4476
|
#
|
4478
4477
|
# {
|