ccxt 4.4.57__py2.py3-none-any.whl → 4.4.58__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/xt.py +1 -0
- ccxt/ascendex.py +2 -0
- ccxt/async_support/__init__.py +1 -1
- ccxt/async_support/ascendex.py +2 -0
- ccxt/async_support/base/exchange.py +1 -1
- ccxt/async_support/binance.py +19 -3
- ccxt/async_support/bitget.py +5 -0
- ccxt/async_support/bitmart.py +13 -3
- ccxt/async_support/blofin.py +1 -2
- ccxt/async_support/bybit.py +13 -2
- ccxt/async_support/coinbase.py +8 -1
- ccxt/async_support/hyperliquid.py +8 -3
- ccxt/async_support/timex.py +12 -2
- ccxt/async_support/vertex.py +1 -1
- ccxt/async_support/whitebit.py +2 -1
- ccxt/async_support/xt.py +54 -1
- ccxt/base/errors.py +0 -6
- ccxt/base/exchange.py +2 -2
- ccxt/binance.py +19 -3
- ccxt/bitget.py +5 -0
- ccxt/bitmart.py +13 -3
- ccxt/blofin.py +1 -2
- ccxt/bybit.py +13 -2
- ccxt/coinbase.py +8 -1
- ccxt/hyperliquid.py +8 -3
- ccxt/pro/__init__.py +1 -1
- ccxt/pro/bybit.py +4 -1
- ccxt/pro/mexc.py +3 -1
- ccxt/test/tests_init.py +2 -2
- ccxt/timex.py +12 -2
- ccxt/vertex.py +1 -1
- ccxt/whitebit.py +2 -1
- ccxt/xt.py +54 -1
- {ccxt-4.4.57.dist-info → ccxt-4.4.58.dist-info}/METADATA +5 -5
- {ccxt-4.4.57.dist-info → ccxt-4.4.58.dist-info}/RECORD +39 -39
- {ccxt-4.4.57.dist-info → ccxt-4.4.58.dist-info}/LICENSE.txt +0 -0
- {ccxt-4.4.57.dist-info → ccxt-4.4.58.dist-info}/WHEEL +0 -0
- {ccxt-4.4.57.dist-info → ccxt-4.4.58.dist-info}/top_level.txt +0 -0
ccxt/__init__.py
CHANGED
ccxt/abstract/xt.py
CHANGED
@@ -109,6 +109,7 @@ class ImplicitAPI:
|
|
109
109
|
private_linear_post_future_user_v1_position_margin = privateLinearPostFutureUserV1PositionMargin = Entry('future/user/v1/position/margin', ['private', 'linear'], 'POST', {'cost': 1})
|
110
110
|
private_linear_post_future_user_v1_user_collection_add = privateLinearPostFutureUserV1UserCollectionAdd = Entry('future/user/v1/user/collection/add', ['private', 'linear'], 'POST', {'cost': 1})
|
111
111
|
private_linear_post_future_user_v1_user_collection_cancel = privateLinearPostFutureUserV1UserCollectionCancel = Entry('future/user/v1/user/collection/cancel', ['private', 'linear'], 'POST', {'cost': 1})
|
112
|
+
private_linear_post_future_user_v1_position_change_type = privateLinearPostFutureUserV1PositionChangeType = Entry('future/user/v1/position/change-type', ['private', 'linear'], 'POST', {'cost': 1})
|
112
113
|
private_inverse_get_future_trade_v1_entrust_plan_detail = privateInverseGetFutureTradeV1EntrustPlanDetail = Entry('future/trade/v1/entrust/plan-detail', ['private', 'inverse'], 'GET', {'cost': 1})
|
113
114
|
private_inverse_get_future_trade_v1_entrust_plan_list = privateInverseGetFutureTradeV1EntrustPlanList = Entry('future/trade/v1/entrust/plan-list', ['private', 'inverse'], 'GET', {'cost': 1})
|
114
115
|
private_inverse_get_future_trade_v1_entrust_plan_list_history = privateInverseGetFutureTradeV1EntrustPlanListHistory = Entry('future/trade/v1/entrust/plan-list-history', ['private', 'inverse'], 'GET', {'cost': 1})
|
ccxt/ascendex.py
CHANGED
@@ -365,6 +365,7 @@ class ascendex(Exchange, ImplicitAPI):
|
|
365
365
|
'untilDays': 100000,
|
366
366
|
'trigger': False,
|
367
367
|
'trailing': False,
|
368
|
+
'symbolRequired': False,
|
368
369
|
},
|
369
370
|
},
|
370
371
|
'forDerivatives': {
|
@@ -388,6 +389,7 @@ class ascendex(Exchange, ImplicitAPI):
|
|
388
389
|
'untilDays': None,
|
389
390
|
'trigger': False,
|
390
391
|
'trailing': False,
|
392
|
+
'symbolRequired': False,
|
391
393
|
},
|
392
394
|
},
|
393
395
|
'swap': {
|
ccxt/async_support/__init__.py
CHANGED
ccxt/async_support/ascendex.py
CHANGED
@@ -366,6 +366,7 @@ class ascendex(Exchange, ImplicitAPI):
|
|
366
366
|
'untilDays': 100000,
|
367
367
|
'trigger': False,
|
368
368
|
'trailing': False,
|
369
|
+
'symbolRequired': False,
|
369
370
|
},
|
370
371
|
},
|
371
372
|
'forDerivatives': {
|
@@ -389,6 +390,7 @@ class ascendex(Exchange, ImplicitAPI):
|
|
389
390
|
'untilDays': None,
|
390
391
|
'trigger': False,
|
391
392
|
'trailing': False,
|
393
|
+
'symbolRequired': False,
|
392
394
|
},
|
393
395
|
},
|
394
396
|
'swap': {
|
ccxt/async_support/binance.py
CHANGED
@@ -1585,6 +1585,7 @@ class binance(Exchange, ImplicitAPI):
|
|
1585
1585
|
'legalMoneyCurrenciesById': {
|
1586
1586
|
'BUSD': 'USD',
|
1587
1587
|
},
|
1588
|
+
'defaultWithdrawPrecision': 0.00000001,
|
1588
1589
|
},
|
1589
1590
|
'features': {
|
1590
1591
|
'spot': {
|
@@ -1635,6 +1636,7 @@ class binance(Exchange, ImplicitAPI):
|
|
1635
1636
|
'limit': None,
|
1636
1637
|
'trigger': False,
|
1637
1638
|
'trailing': False,
|
1639
|
+
'symbolRequired': False,
|
1638
1640
|
},
|
1639
1641
|
'fetchOrders': {
|
1640
1642
|
'marginMode': True,
|
@@ -3097,6 +3099,7 @@ class binance(Exchange, ImplicitAPI):
|
|
3097
3099
|
id = self.safe_string(entry, 'coin')
|
3098
3100
|
name = self.safe_string(entry, 'name')
|
3099
3101
|
code = self.safe_currency_code(id)
|
3102
|
+
isFiat = self.safe_bool(entry, 'isLegalMoney')
|
3100
3103
|
minPrecision = None
|
3101
3104
|
isWithdrawEnabled = True
|
3102
3105
|
isDepositEnabled = True
|
@@ -3108,6 +3111,7 @@ class binance(Exchange, ImplicitAPI):
|
|
3108
3111
|
networkItem = networkList[j]
|
3109
3112
|
network = self.safe_string(networkItem, 'network')
|
3110
3113
|
networkCode = self.network_id_to_code(network)
|
3114
|
+
isETF = (network == 'ETF') # e.g. BTCUP, ETHDOWN
|
3111
3115
|
# name = self.safe_string(networkItem, 'name')
|
3112
3116
|
withdrawFee = self.safe_number(networkItem, 'withdrawFee')
|
3113
3117
|
depositEnable = self.safe_bool(networkItem, 'depositEnable')
|
@@ -3118,11 +3122,22 @@ class binance(Exchange, ImplicitAPI):
|
|
3118
3122
|
isDefault = self.safe_bool(networkItem, 'isDefault')
|
3119
3123
|
if isDefault or (fee is None):
|
3120
3124
|
fee = withdrawFee
|
3125
|
+
# todo: default networks in "setMarkets" overload
|
3126
|
+
# if isDefault:
|
3127
|
+
# self.options['defaultNetworkCodesForCurrencies'][code] = networkCode
|
3128
|
+
# }
|
3121
3129
|
precisionTick = self.safe_string(networkItem, 'withdrawIntegerMultiple')
|
3122
|
-
|
3123
|
-
#
|
3130
|
+
withdrawPrecision = precisionTick
|
3131
|
+
# avoid zero values, which are mostly from fiat or leveraged tokens or some abandoned coins : https://github.com/ccxt/ccxt/pull/14902#issuecomment-1271636731
|
3124
3132
|
if not Precise.string_eq(precisionTick, '0'):
|
3125
3133
|
minPrecision = precisionTick if (minPrecision is None) else Precise.string_min(minPrecision, precisionTick)
|
3134
|
+
else:
|
3135
|
+
if not isFiat and not isETF:
|
3136
|
+
# non-fiat and non-ETF currency, there are many cases when precision is set to zero(probably bug, we've reported to binance already)
|
3137
|
+
# in such cases, we can set default precision of 8(which is in UI for such coins)
|
3138
|
+
withdrawPrecision = self.omit_zero(self.safe_string(networkItem, 'withdrawInternalMin'))
|
3139
|
+
if withdrawPrecision is None:
|
3140
|
+
withdrawPrecision = self.safe_string(self.options, 'defaultWithdrawPrecision')
|
3126
3141
|
networks[networkCode] = {
|
3127
3142
|
'info': networkItem,
|
3128
3143
|
'id': network,
|
@@ -3131,7 +3146,7 @@ class binance(Exchange, ImplicitAPI):
|
|
3131
3146
|
'deposit': depositEnable,
|
3132
3147
|
'withdraw': withdrawEnable,
|
3133
3148
|
'fee': withdrawFee,
|
3134
|
-
'precision': self.parse_number(
|
3149
|
+
'precision': self.parse_number(withdrawPrecision),
|
3135
3150
|
'limits': {
|
3136
3151
|
'withdraw': {
|
3137
3152
|
'min': self.safe_number(networkItem, 'withdrawMin'),
|
@@ -3160,6 +3175,7 @@ class binance(Exchange, ImplicitAPI):
|
|
3160
3175
|
'id': id,
|
3161
3176
|
'name': name,
|
3162
3177
|
'code': code,
|
3178
|
+
'type': 'fiat' if isFiat else 'crypto',
|
3163
3179
|
'precision': self.parse_number(minPrecision),
|
3164
3180
|
'info': entry,
|
3165
3181
|
'active': active,
|
ccxt/async_support/bitget.py
CHANGED
@@ -8855,4 +8855,9 @@ class bitget(Exchange, ImplicitAPI):
|
|
8855
8855
|
}
|
8856
8856
|
if method == 'POST':
|
8857
8857
|
headers['Content-Type'] = 'application/json'
|
8858
|
+
sandboxMode = self.safe_bool(self.options, 'sandboxMode', False)
|
8859
|
+
if sandboxMode:
|
8860
|
+
if headers is None:
|
8861
|
+
headers = {}
|
8862
|
+
headers['PAPTRADING'] = 1
|
8858
8863
|
return {'url': url, 'method': method, 'body': body, 'headers': headers}
|
ccxt/async_support/bitmart.py
CHANGED
@@ -528,7 +528,10 @@ class bitmart(Exchange, ImplicitAPI):
|
|
528
528
|
'40049': InvalidOrder, # 403, The maximum length of clientOrderId cannot exceed 32
|
529
529
|
'40050': InvalidOrder, # 403, Client OrderId duplicated with existing orders
|
530
530
|
},
|
531
|
-
'broad': {
|
531
|
+
'broad': {
|
532
|
+
'You contract account available balance not enough': InsufficientFunds,
|
533
|
+
'you contract account available balance not enough': InsufficientFunds,
|
534
|
+
},
|
532
535
|
},
|
533
536
|
'commonCurrencies': {
|
534
537
|
'$GM': 'GOLDMINER',
|
@@ -968,6 +971,7 @@ class bitmart(Exchange, ImplicitAPI):
|
|
968
971
|
data = self.safe_dict(response, 'data', {})
|
969
972
|
symbols = self.safe_list(data, 'symbols', [])
|
970
973
|
result = []
|
974
|
+
fees = self.fees['trading']
|
971
975
|
for i in range(0, len(symbols)):
|
972
976
|
market = symbols[i]
|
973
977
|
id = self.safe_string(market, 'symbol')
|
@@ -1006,6 +1010,8 @@ class bitmart(Exchange, ImplicitAPI):
|
|
1006
1010
|
'expiryDatetime': None,
|
1007
1011
|
'strike': None,
|
1008
1012
|
'optionType': None,
|
1013
|
+
'maker': fees['maker'],
|
1014
|
+
'taker': fees['taker'],
|
1009
1015
|
'precision': {
|
1010
1016
|
'amount': baseMinSize,
|
1011
1017
|
'price': self.parse_number(self.parse_precision(self.safe_string(market, 'price_max_precision'))),
|
@@ -1077,6 +1083,7 @@ class bitmart(Exchange, ImplicitAPI):
|
|
1077
1083
|
data = self.safe_dict(response, 'data', {})
|
1078
1084
|
symbols = self.safe_list(data, 'symbols', [])
|
1079
1085
|
result = []
|
1086
|
+
fees = self.fees['trading']
|
1080
1087
|
for i in range(0, len(symbols)):
|
1081
1088
|
market = symbols[i]
|
1082
1089
|
id = self.safe_string(market, 'symbol')
|
@@ -1118,6 +1125,8 @@ class bitmart(Exchange, ImplicitAPI):
|
|
1118
1125
|
'expiryDatetime': self.iso8601(expiry),
|
1119
1126
|
'strike': None,
|
1120
1127
|
'optionType': None,
|
1128
|
+
'maker': fees['maker'],
|
1129
|
+
'taker': fees['taker'],
|
1121
1130
|
'precision': {
|
1122
1131
|
'amount': self.safe_number(market, 'vol_precision'),
|
1123
1132
|
'price': self.safe_number(market, 'price_precision'),
|
@@ -5085,6 +5094,7 @@ class bitmart(Exchange, ImplicitAPI):
|
|
5085
5094
|
# {"message":"Bad Request [from is empty]","code":50000,"trace":"579986f7-c93a-4559-926b-06ba9fa79d76","data":{}}
|
5086
5095
|
# {"message":"Kline size over 500","code":50004,"trace":"d625caa8-e8ca-4bd2-b77c-958776965819","data":{}}
|
5087
5096
|
# {"message":"Balance not enough","code":50020,"trace":"7c709d6a-3292-462c-98c5-32362540aeef","data":{}}
|
5097
|
+
# {"code":40012,"message":"You contract account available balance not enough.","trace":"..."}
|
5088
5098
|
#
|
5089
5099
|
# contract
|
5090
5100
|
#
|
@@ -5096,9 +5106,9 @@ class bitmart(Exchange, ImplicitAPI):
|
|
5096
5106
|
isErrorCode = (errorCode is not None) and (errorCode != '1000')
|
5097
5107
|
if isErrorCode or isErrorMessage:
|
5098
5108
|
feedback = self.id + ' ' + body
|
5099
|
-
self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback)
|
5100
|
-
self.throw_broadly_matched_exception(self.exceptions['broad'], errorCode, feedback)
|
5101
5109
|
self.throw_exactly_matched_exception(self.exceptions['exact'], message, feedback)
|
5102
5110
|
self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback)
|
5111
|
+
self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback)
|
5112
|
+
self.throw_broadly_matched_exception(self.exceptions['broad'], errorCode, feedback)
|
5103
5113
|
raise ExchangeError(feedback) # unknown message
|
5104
5114
|
return None
|
ccxt/async_support/blofin.py
CHANGED
ccxt/async_support/bybit.py
CHANGED
@@ -1421,8 +1421,8 @@ class bybit(Exchange, ImplicitAPI):
|
|
1421
1421
|
|
1422
1422
|
def create_expired_option_market(self, symbol: str):
|
1423
1423
|
# support expired option contracts
|
1424
|
-
quote =
|
1425
|
-
settle =
|
1424
|
+
quote = None
|
1425
|
+
settle = None
|
1426
1426
|
optionParts = symbol.split('-')
|
1427
1427
|
symbolBase = symbol.split('/')
|
1428
1428
|
base = None
|
@@ -1430,9 +1430,20 @@ class bybit(Exchange, ImplicitAPI):
|
|
1430
1430
|
if symbol.find('/') > -1:
|
1431
1431
|
base = self.safe_string(symbolBase, 0)
|
1432
1432
|
expiry = self.safe_string(optionParts, 1)
|
1433
|
+
symbolQuoteAndSettle = self.safe_string(symbolBase, 1)
|
1434
|
+
splitQuote = symbolQuoteAndSettle.split(':')
|
1435
|
+
quoteAndSettle = self.safe_string(splitQuote, 0)
|
1436
|
+
quote = quoteAndSettle
|
1437
|
+
settle = quoteAndSettle
|
1433
1438
|
else:
|
1434
1439
|
base = self.safe_string(optionParts, 0)
|
1435
1440
|
expiry = self.convert_market_id_expire_date(self.safe_string(optionParts, 1))
|
1441
|
+
if symbol.endswith('-USDT'):
|
1442
|
+
quote = 'USDT'
|
1443
|
+
settle = 'USDT'
|
1444
|
+
else:
|
1445
|
+
quote = 'USDC'
|
1446
|
+
settle = 'USDC'
|
1436
1447
|
strike = self.safe_string(optionParts, 2)
|
1437
1448
|
optionType = self.safe_string(optionParts, 3)
|
1438
1449
|
datetime = self.convert_expire_date(expiry)
|
ccxt/async_support/coinbase.py
CHANGED
@@ -4796,10 +4796,17 @@ class coinbase(Exchange, ImplicitAPI):
|
|
4796
4796
|
# }
|
4797
4797
|
# ]
|
4798
4798
|
# }
|
4799
|
+
# or
|
4800
|
+
# {
|
4801
|
+
# "error": "UNKNOWN_FAILURE_REASON",
|
4802
|
+
# "message": "",
|
4803
|
+
# "error_details": "",
|
4804
|
+
# "preview_failure_reason": "PREVIEW_STOP_PRICE_BELOW_LAST_TRADE_PRICE"
|
4805
|
+
# }
|
4799
4806
|
#
|
4800
4807
|
errorCode = self.safe_string(response, 'error')
|
4801
4808
|
if errorCode is not None:
|
4802
|
-
errorMessage = self.
|
4809
|
+
errorMessage = self.safe_string_2(response, 'error_description', 'preview_failure_reason')
|
4803
4810
|
self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback)
|
4804
4811
|
self.throw_broadly_matched_exception(self.exceptions['broad'], errorMessage, feedback)
|
4805
4812
|
raise ExchangeError(feedback)
|
@@ -204,7 +204,7 @@ class hyperliquid(Exchange, ImplicitAPI):
|
|
204
204
|
'broad': {
|
205
205
|
'Price must be divisible by tick size.': InvalidOrder,
|
206
206
|
'Order must have minimum value of $10': InvalidOrder,
|
207
|
-
'Insufficient margin to place order.':
|
207
|
+
'Insufficient margin to place order.': InsufficientFunds,
|
208
208
|
'Reduce only order would increase position.': InvalidOrder,
|
209
209
|
'Post only order would have immediately matched,': InvalidOrder,
|
210
210
|
'Order could not immediately match against any resting orders.': InvalidOrder,
|
@@ -602,7 +602,9 @@ class hyperliquid(Exchange, ImplicitAPI):
|
|
602
602
|
amountPrecisionStr = self.safe_string(innerBaseTokenInfo, 'szDecimals')
|
603
603
|
amountPrecision = int(amountPrecisionStr)
|
604
604
|
price = self.safe_number(extraData, 'midPx')
|
605
|
-
pricePrecision =
|
605
|
+
pricePrecision = 0
|
606
|
+
if price is not None:
|
607
|
+
pricePrecision = self.calculate_price_precision(price, amountPrecision, 8)
|
606
608
|
pricePrecisionStr = self.number_to_string(pricePrecision)
|
607
609
|
# quotePrecision = self.parse_number(self.parse_precision(self.safe_string(innerQuoteTokenInfo, 'szDecimals')))
|
608
610
|
baseId = self.number_to_string(index + 10000)
|
@@ -699,7 +701,9 @@ class hyperliquid(Exchange, ImplicitAPI):
|
|
699
701
|
amountPrecisionStr = self.safe_string(market, 'szDecimals')
|
700
702
|
amountPrecision = int(amountPrecisionStr)
|
701
703
|
price = self.safe_number(market, 'markPx', 0)
|
702
|
-
pricePrecision =
|
704
|
+
pricePrecision = 0
|
705
|
+
if price is not None:
|
706
|
+
pricePrecision = self.calculate_price_precision(price, amountPrecision, 6)
|
703
707
|
pricePrecisionStr = self.number_to_string(pricePrecision)
|
704
708
|
isDelisted = self.safe_bool(market, 'isDelisted')
|
705
709
|
active = True
|
@@ -3265,6 +3269,7 @@ class hyperliquid(Exchange, ImplicitAPI):
|
|
3265
3269
|
# status: 'ok',
|
3266
3270
|
# response: {type: 'order', data: {statuses: [{error: 'Insufficient margin to place order. asset=4'}]}}
|
3267
3271
|
# }
|
3272
|
+
# {"status":"ok","response":{"type":"order","data":{"statuses":[{"error":"Insufficient margin to place order. asset=84"}]}}}
|
3268
3273
|
#
|
3269
3274
|
status = self.safe_string(response, 'status', '')
|
3270
3275
|
message = None
|
ccxt/async_support/timex.py
CHANGED
@@ -729,6 +729,7 @@ class timex(Exchange, ImplicitAPI):
|
|
729
729
|
:param int [since]: timestamp in ms of the earliest candle to fetch
|
730
730
|
:param int [limit]: the maximum amount of candles to fetch
|
731
731
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
732
|
+
:param int [params.until]: timestamp in ms of the latest candle to fetch
|
732
733
|
:returns int[][]: A list of candles ordered, open, high, low, close, volume
|
733
734
|
"""
|
734
735
|
await self.load_markets()
|
@@ -739,15 +740,24 @@ class timex(Exchange, ImplicitAPI):
|
|
739
740
|
}
|
740
741
|
# if since and limit are not specified
|
741
742
|
duration = self.parse_timeframe(timeframe)
|
743
|
+
until = self.safe_integer(params, 'until')
|
742
744
|
if limit is None:
|
743
745
|
limit = 1000 # exchange provides tens of thousands of data, but we set generous default value
|
744
746
|
if since is not None:
|
745
747
|
request['from'] = self.iso8601(since)
|
746
|
-
|
748
|
+
if until is None:
|
749
|
+
request['till'] = self.iso8601(self.sum(since, self.sum(limit, 1) * duration * 1000))
|
750
|
+
else:
|
751
|
+
request['till'] = self.iso8601(until)
|
752
|
+
elif until is not None:
|
753
|
+
request['till'] = self.iso8601(until)
|
754
|
+
fromTimestamp = until - self.sum(limit, 1) * duration * 1000
|
755
|
+
request['from'] = self.iso8601(fromTimestamp)
|
747
756
|
else:
|
748
757
|
now = self.milliseconds()
|
749
758
|
request['till'] = self.iso8601(now)
|
750
|
-
request['from'] = self.iso8601(now - limit * duration * 1000 - 1)
|
759
|
+
request['from'] = self.iso8601(now - self.sum(limit, 1) * duration * 1000 - 1)
|
760
|
+
params = self.omit(params, 'until')
|
751
761
|
response = await self.publicGetCandles(self.extend(request, params))
|
752
762
|
#
|
753
763
|
# [
|
ccxt/async_support/vertex.py
CHANGED
@@ -1523,7 +1523,7 @@ class vertex(Exchange, ImplicitAPI):
|
|
1523
1523
|
marketId = base + '/' + quote
|
1524
1524
|
if base.find('PERP') > 0:
|
1525
1525
|
marketId = marketId.replace('-PERP', '') + ':USDC'
|
1526
|
-
market = self.
|
1526
|
+
market = self.safe_market(marketId, market)
|
1527
1527
|
last = self.safe_string(ticker, 'last_price')
|
1528
1528
|
return self.safe_ticker({
|
1529
1529
|
'symbol': market['symbol'],
|
ccxt/async_support/whitebit.py
CHANGED
@@ -2626,12 +2626,13 @@ class whitebit(Exchange, ImplicitAPI):
|
|
2626
2626
|
# For cases where we have a meaningful status
|
2627
2627
|
# {"response":null,"status":422,"errors":{"orderId":["Finished order id 435453454535 not found on your account"]},"notification":null,"warning":"Finished order id 435453454535 not found on your account","_token":null}
|
2628
2628
|
status = self.safe_string(response, 'status')
|
2629
|
+
errors = self.safe_value(response, 'errors')
|
2629
2630
|
# {"code":10,"message":"Unauthorized request."}
|
2630
2631
|
message = self.safe_string(response, 'message')
|
2631
2632
|
# For these cases where we have a generic code variable error key
|
2632
2633
|
# {"code":0,"message":"Validation failed","errors":{"amount":["Amount must be greater than 0"]}}
|
2633
2634
|
codeNew = self.safe_integer(response, 'code')
|
2634
|
-
hasErrorStatus = status is not None and status != '200'
|
2635
|
+
hasErrorStatus = status is not None and status != '200' and errors is not None
|
2635
2636
|
if hasErrorStatus or codeNew is not None:
|
2636
2637
|
feedback = self.id + ' ' + body
|
2637
2638
|
errorInfo = message
|
ccxt/async_support/xt.py
CHANGED
@@ -129,7 +129,7 @@ class xt(Exchange, ImplicitAPI):
|
|
129
129
|
'repayMargin': False,
|
130
130
|
'setLeverage': True,
|
131
131
|
'setMargin': False,
|
132
|
-
'setMarginMode':
|
132
|
+
'setMarginMode': True,
|
133
133
|
'setPositionMode': False,
|
134
134
|
'signIn': False,
|
135
135
|
'transfer': True,
|
@@ -287,6 +287,7 @@ class xt(Exchange, ImplicitAPI):
|
|
287
287
|
'future/user/v1/position/margin': 1,
|
288
288
|
'future/user/v1/user/collection/add': 1,
|
289
289
|
'future/user/v1/user/collection/cancel': 1,
|
290
|
+
'future/user/v1/position/change-type': 1,
|
290
291
|
},
|
291
292
|
},
|
292
293
|
'inverse': {
|
@@ -532,10 +533,12 @@ class xt(Exchange, ImplicitAPI):
|
|
532
533
|
'TRANSFER_012': PermissionDenied, # Currency transfer prohibited
|
533
534
|
'symbol_not_support_trading_via_api': BadSymbol, # {"returnCode":1,"msgInfo":"failure","error":{"code":"symbol_not_support_trading_via_api","msg":"The symbol does not support trading via API"},"result":null}
|
534
535
|
'open_order_min_nominal_value_limit': InvalidOrder, # {"returnCode":1,"msgInfo":"failure","error":{"code":"open_order_min_nominal_value_limit","msg":"Exceeds the minimum notional value of a single order"},"result":null}
|
536
|
+
'insufficient_balance': InsufficientFunds,
|
535
537
|
},
|
536
538
|
'broad': {
|
537
539
|
'The symbol does not support trading via API': BadSymbol, # {"returnCode":1,"msgInfo":"failure","error":{"code":"symbol_not_support_trading_via_api","msg":"The symbol does not support trading via API"},"result":null}
|
538
540
|
'Exceeds the minimum notional value of a single order': InvalidOrder, # {"returnCode":1,"msgInfo":"failure","error":{"code":"open_order_min_nominal_value_limit","msg":"Exceeds the minimum notional value of a single order"},"result":null}
|
541
|
+
'insufficient balance': InsufficientFunds,
|
539
542
|
},
|
540
543
|
},
|
541
544
|
'timeframes': {
|
@@ -4617,6 +4620,53 @@ class xt(Exchange, ImplicitAPI):
|
|
4617
4620
|
'status': None,
|
4618
4621
|
}
|
4619
4622
|
|
4623
|
+
async def set_margin_mode(self, marginMode: str, symbol: Str = None, params={}):
|
4624
|
+
"""
|
4625
|
+
set margin mode to 'cross' or 'isolated'
|
4626
|
+
|
4627
|
+
https://doc.xt.com/#futures_userchangePositionType
|
4628
|
+
|
4629
|
+
:param str marginMode: 'cross' or 'isolated'
|
4630
|
+
:param str [symbol]: required
|
4631
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
4632
|
+
:param str [params.positionSide]: *required* "long" or "short"
|
4633
|
+
:returns dict: response from the exchange
|
4634
|
+
"""
|
4635
|
+
if symbol is None:
|
4636
|
+
raise ArgumentsRequired(self.id + ' setMarginMode() requires a symbol argument')
|
4637
|
+
await self.load_markets()
|
4638
|
+
market = self.market(symbol)
|
4639
|
+
if market['spot']:
|
4640
|
+
raise BadSymbol(self.id + ' setMarginMode() supports contract markets only')
|
4641
|
+
marginMode = marginMode.lower()
|
4642
|
+
if marginMode != 'isolated' and marginMode != 'cross':
|
4643
|
+
raise BadRequest(self.id + ' setMarginMode() marginMode argument should be isolated or cross')
|
4644
|
+
if marginMode == 'cross':
|
4645
|
+
marginMode = 'CROSSED'
|
4646
|
+
else:
|
4647
|
+
marginMode = 'ISOLATED'
|
4648
|
+
posSide = self.safe_string_upper(params, 'positionSide')
|
4649
|
+
if posSide is None:
|
4650
|
+
raise ArgumentsRequired(self.id + ' setMarginMode() requires a positionSide parameter, either "LONG" or "SHORT"')
|
4651
|
+
request: dict = {
|
4652
|
+
'positionType': marginMode,
|
4653
|
+
'positionSide': posSide,
|
4654
|
+
'symbol': market['id'],
|
4655
|
+
}
|
4656
|
+
response = await self.privateLinearPostFutureUserV1PositionChangeType(self.extend(request, params))
|
4657
|
+
#
|
4658
|
+
# {
|
4659
|
+
# "error": {
|
4660
|
+
# "code": "",
|
4661
|
+
# "msg": ""
|
4662
|
+
# },
|
4663
|
+
# "msgInfo": "",
|
4664
|
+
# "result": {},
|
4665
|
+
# "returnCode": 0
|
4666
|
+
# }
|
4667
|
+
#
|
4668
|
+
return response # unify return type
|
4669
|
+
|
4620
4670
|
def handle_errors(self, code, reason, url, method, headers, body, response, requestHeaders, requestBody):
|
4621
4671
|
#
|
4622
4672
|
# spot: error
|
@@ -4667,6 +4717,9 @@ class xt(Exchange, ImplicitAPI):
|
|
4667
4717
|
# "result": {}
|
4668
4718
|
# }
|
4669
4719
|
#
|
4720
|
+
# {"returnCode":1,"msgInfo":"failure","error":{"code":"insufficient_balance","msg":"insufficient balance","args":[]},"result":null}
|
4721
|
+
#
|
4722
|
+
#
|
4670
4723
|
status = self.safe_string_upper_2(response, 'msgInfo', 'mc')
|
4671
4724
|
if status is not None and status != 'SUCCESS':
|
4672
4725
|
feedback = self.id + ' ' + body
|
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.58'
|
8
8
|
|
9
9
|
# -----------------------------------------------------------------------------
|
10
10
|
|
@@ -3657,7 +3657,7 @@ class Exchange(object):
|
|
3657
3657
|
change = self.omit_zero(self.safe_string(ticker, 'change'))
|
3658
3658
|
percentage = self.omit_zero(self.safe_string(ticker, 'percentage'))
|
3659
3659
|
average = self.omit_zero(self.safe_string(ticker, 'average'))
|
3660
|
-
vwap = self.
|
3660
|
+
vwap = self.safe_string(ticker, 'vwap')
|
3661
3661
|
baseVolume = self.safe_string(ticker, 'baseVolume')
|
3662
3662
|
quoteVolume = self.safe_string(ticker, 'quoteVolume')
|
3663
3663
|
if vwap is None:
|
ccxt/binance.py
CHANGED
@@ -1584,6 +1584,7 @@ class binance(Exchange, ImplicitAPI):
|
|
1584
1584
|
'legalMoneyCurrenciesById': {
|
1585
1585
|
'BUSD': 'USD',
|
1586
1586
|
},
|
1587
|
+
'defaultWithdrawPrecision': 0.00000001,
|
1587
1588
|
},
|
1588
1589
|
'features': {
|
1589
1590
|
'spot': {
|
@@ -1634,6 +1635,7 @@ class binance(Exchange, ImplicitAPI):
|
|
1634
1635
|
'limit': None,
|
1635
1636
|
'trigger': False,
|
1636
1637
|
'trailing': False,
|
1638
|
+
'symbolRequired': False,
|
1637
1639
|
},
|
1638
1640
|
'fetchOrders': {
|
1639
1641
|
'marginMode': True,
|
@@ -3096,6 +3098,7 @@ class binance(Exchange, ImplicitAPI):
|
|
3096
3098
|
id = self.safe_string(entry, 'coin')
|
3097
3099
|
name = self.safe_string(entry, 'name')
|
3098
3100
|
code = self.safe_currency_code(id)
|
3101
|
+
isFiat = self.safe_bool(entry, 'isLegalMoney')
|
3099
3102
|
minPrecision = None
|
3100
3103
|
isWithdrawEnabled = True
|
3101
3104
|
isDepositEnabled = True
|
@@ -3107,6 +3110,7 @@ class binance(Exchange, ImplicitAPI):
|
|
3107
3110
|
networkItem = networkList[j]
|
3108
3111
|
network = self.safe_string(networkItem, 'network')
|
3109
3112
|
networkCode = self.network_id_to_code(network)
|
3113
|
+
isETF = (network == 'ETF') # e.g. BTCUP, ETHDOWN
|
3110
3114
|
# name = self.safe_string(networkItem, 'name')
|
3111
3115
|
withdrawFee = self.safe_number(networkItem, 'withdrawFee')
|
3112
3116
|
depositEnable = self.safe_bool(networkItem, 'depositEnable')
|
@@ -3117,11 +3121,22 @@ class binance(Exchange, ImplicitAPI):
|
|
3117
3121
|
isDefault = self.safe_bool(networkItem, 'isDefault')
|
3118
3122
|
if isDefault or (fee is None):
|
3119
3123
|
fee = withdrawFee
|
3124
|
+
# todo: default networks in "setMarkets" overload
|
3125
|
+
# if isDefault:
|
3126
|
+
# self.options['defaultNetworkCodesForCurrencies'][code] = networkCode
|
3127
|
+
# }
|
3120
3128
|
precisionTick = self.safe_string(networkItem, 'withdrawIntegerMultiple')
|
3121
|
-
|
3122
|
-
#
|
3129
|
+
withdrawPrecision = precisionTick
|
3130
|
+
# avoid zero values, which are mostly from fiat or leveraged tokens or some abandoned coins : https://github.com/ccxt/ccxt/pull/14902#issuecomment-1271636731
|
3123
3131
|
if not Precise.string_eq(precisionTick, '0'):
|
3124
3132
|
minPrecision = precisionTick if (minPrecision is None) else Precise.string_min(minPrecision, precisionTick)
|
3133
|
+
else:
|
3134
|
+
if not isFiat and not isETF:
|
3135
|
+
# non-fiat and non-ETF currency, there are many cases when precision is set to zero(probably bug, we've reported to binance already)
|
3136
|
+
# in such cases, we can set default precision of 8(which is in UI for such coins)
|
3137
|
+
withdrawPrecision = self.omit_zero(self.safe_string(networkItem, 'withdrawInternalMin'))
|
3138
|
+
if withdrawPrecision is None:
|
3139
|
+
withdrawPrecision = self.safe_string(self.options, 'defaultWithdrawPrecision')
|
3125
3140
|
networks[networkCode] = {
|
3126
3141
|
'info': networkItem,
|
3127
3142
|
'id': network,
|
@@ -3130,7 +3145,7 @@ class binance(Exchange, ImplicitAPI):
|
|
3130
3145
|
'deposit': depositEnable,
|
3131
3146
|
'withdraw': withdrawEnable,
|
3132
3147
|
'fee': withdrawFee,
|
3133
|
-
'precision': self.parse_number(
|
3148
|
+
'precision': self.parse_number(withdrawPrecision),
|
3134
3149
|
'limits': {
|
3135
3150
|
'withdraw': {
|
3136
3151
|
'min': self.safe_number(networkItem, 'withdrawMin'),
|
@@ -3159,6 +3174,7 @@ class binance(Exchange, ImplicitAPI):
|
|
3159
3174
|
'id': id,
|
3160
3175
|
'name': name,
|
3161
3176
|
'code': code,
|
3177
|
+
'type': 'fiat' if isFiat else 'crypto',
|
3162
3178
|
'precision': self.parse_number(minPrecision),
|
3163
3179
|
'info': entry,
|
3164
3180
|
'active': active,
|
ccxt/bitget.py
CHANGED
@@ -8854,4 +8854,9 @@ class bitget(Exchange, ImplicitAPI):
|
|
8854
8854
|
}
|
8855
8855
|
if method == 'POST':
|
8856
8856
|
headers['Content-Type'] = 'application/json'
|
8857
|
+
sandboxMode = self.safe_bool(self.options, 'sandboxMode', False)
|
8858
|
+
if sandboxMode:
|
8859
|
+
if headers is None:
|
8860
|
+
headers = {}
|
8861
|
+
headers['PAPTRADING'] = 1
|
8857
8862
|
return {'url': url, 'method': method, 'body': body, 'headers': headers}
|