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 CHANGED
@@ -22,7 +22,7 @@
22
22
 
23
23
  # ----------------------------------------------------------------------------
24
24
 
25
- __version__ = '4.4.57'
25
+ __version__ = '4.4.58'
26
26
 
27
27
  # ----------------------------------------------------------------------------
28
28
 
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': {
@@ -4,7 +4,7 @@
4
4
 
5
5
  # -----------------------------------------------------------------------------
6
6
 
7
- __version__ = '4.4.57'
7
+ __version__ = '4.4.58'
8
8
 
9
9
  # -----------------------------------------------------------------------------
10
10
 
@@ -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': {
@@ -2,7 +2,7 @@
2
2
 
3
3
  # -----------------------------------------------------------------------------
4
4
 
5
- __version__ = '4.4.57'
5
+ __version__ = '4.4.58'
6
6
 
7
7
  # -----------------------------------------------------------------------------
8
8
 
@@ -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
- # avoid zero values, which are mostly from fiat or leveraged tokens : https://github.com/ccxt/ccxt/pull/14902#issuecomment-1271636731
3123
- # so, when there is zero instead of i.e. 0.001, then we skip those cases, because we don't know the precision - it might be because of network is suspended or other reasons
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(precisionTick),
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,
@@ -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}
@@ -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
@@ -328,8 +328,7 @@ class blofin(Exchange, ImplicitAPI):
328
328
  'takeProfitPrice': True,
329
329
  'attachedStopLossTakeProfit': {
330
330
  'triggerPriceType': None,
331
- 'limit': True,
332
- 'price': None,
331
+ 'price': True,
333
332
  },
334
333
  'hedged': True,
335
334
  },
@@ -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 = 'USD'
1425
- settle = 'USDC'
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)
@@ -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.safe_string(response, 'error_description')
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.': InvalidOrder,
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 = self.calculate_price_precision(price, amountPrecision, 8)
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 = self.calculate_price_precision(price, amountPrecision, 6)
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
@@ -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
- request['till'] = self.iso8601(self.sum(since, self.sum(limit, 1) * duration * 1000))
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
  # [
@@ -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.market(marketId)
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'],
@@ -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': False,
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.57'
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.omit_zero(self.safe_string(ticker, 'vwap'))
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
- # avoid zero values, which are mostly from fiat or leveraged tokens : https://github.com/ccxt/ccxt/pull/14902#issuecomment-1271636731
3122
- # so, when there is zero instead of i.e. 0.001, then we skip those cases, because we don't know the precision - it might be because of network is suspended or other reasons
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(precisionTick),
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}