ccxt 4.4.96__py2.py3-none-any.whl → 4.4.98__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. ccxt/__init__.py +2 -1
  2. ccxt/abstract/binance.py +3 -0
  3. ccxt/abstract/binancecoinm.py +3 -0
  4. ccxt/abstract/binanceus.py +3 -0
  5. ccxt/abstract/binanceusdm.py +3 -0
  6. ccxt/abstract/phemex.py +1 -0
  7. ccxt/async_support/__init__.py +2 -1
  8. ccxt/async_support/base/exchange.py +6 -3
  9. ccxt/async_support/binance.py +90 -34
  10. ccxt/async_support/binancecoinm.py +5 -1
  11. ccxt/async_support/binanceus.py +3 -1
  12. ccxt/async_support/binanceusdm.py +3 -1
  13. ccxt/async_support/bingx.py +1 -1
  14. ccxt/async_support/bitget.py +14 -2
  15. ccxt/async_support/coinmetro.py +2 -3
  16. ccxt/async_support/cryptocom.py +77 -2
  17. ccxt/async_support/exmo.py +1 -1
  18. ccxt/async_support/foxbit.py +1 -1
  19. ccxt/async_support/gate.py +1 -2
  20. ccxt/async_support/hashkey.py +39 -0
  21. ccxt/async_support/hyperliquid.py +40 -25
  22. ccxt/async_support/independentreserve.py +35 -0
  23. ccxt/async_support/indodax.py +34 -0
  24. ccxt/async_support/kucoin.py +2 -1
  25. ccxt/async_support/latoken.py +42 -0
  26. ccxt/async_support/luno.py +36 -0
  27. ccxt/async_support/mercado.py +34 -0
  28. ccxt/async_support/mexc.py +3 -19
  29. ccxt/async_support/ndax.py +8 -0
  30. ccxt/async_support/okx.py +2 -0
  31. ccxt/async_support/phemex.py +36 -31
  32. ccxt/base/decimal_to_precision.py +14 -10
  33. ccxt/base/errors.py +6 -0
  34. ccxt/base/exchange.py +55 -15
  35. ccxt/binance.py +90 -34
  36. ccxt/binancecoinm.py +5 -1
  37. ccxt/binanceus.py +3 -1
  38. ccxt/binanceusdm.py +3 -1
  39. ccxt/bingx.py +1 -1
  40. ccxt/bitget.py +14 -2
  41. ccxt/coinmetro.py +2 -3
  42. ccxt/cryptocom.py +77 -2
  43. ccxt/exmo.py +1 -1
  44. ccxt/foxbit.py +1 -1
  45. ccxt/gate.py +1 -2
  46. ccxt/hashkey.py +39 -0
  47. ccxt/hyperliquid.py +40 -25
  48. ccxt/independentreserve.py +35 -0
  49. ccxt/indodax.py +34 -0
  50. ccxt/kucoin.py +2 -1
  51. ccxt/latoken.py +42 -0
  52. ccxt/luno.py +36 -0
  53. ccxt/mercado.py +34 -0
  54. ccxt/mexc.py +3 -19
  55. ccxt/ndax.py +8 -0
  56. ccxt/okx.py +2 -0
  57. ccxt/phemex.py +36 -31
  58. ccxt/pro/__init__.py +2 -1
  59. ccxt/pro/binancecoinm.py +3 -1
  60. ccxt/pro/binanceus.py +3 -1
  61. ccxt/pro/binanceusdm.py +3 -1
  62. ccxt/pro/bybit.py +33 -1
  63. {ccxt-4.4.96.dist-info → ccxt-4.4.98.dist-info}/METADATA +16 -16
  64. {ccxt-4.4.96.dist-info → ccxt-4.4.98.dist-info}/RECORD +67 -67
  65. {ccxt-4.4.96.dist-info → ccxt-4.4.98.dist-info}/LICENSE.txt +0 -0
  66. {ccxt-4.4.96.dist-info → ccxt-4.4.98.dist-info}/WHEEL +0 -0
  67. {ccxt-4.4.96.dist-info → ccxt-4.4.98.dist-info}/top_level.txt +0 -0
@@ -449,6 +449,7 @@ class mexc(Exchange, ImplicitAPI):
449
449
  },
450
450
  },
451
451
  },
452
+ 'useCcxtTradeId': True,
452
453
  'timeframes': {
453
454
  'spot': {
454
455
  '1m': '1m',
@@ -1695,8 +1696,8 @@ class mexc(Exchange, ImplicitAPI):
1695
1696
  'cost': self.safe_string(trade, 'commission'),
1696
1697
  'currency': self.safe_currency_code(feeAsset),
1697
1698
  }
1698
- if id is None:
1699
- id = self.synthetic_trade_id(market, timestamp, side, amountString, priceString, type, takerOrMaker)
1699
+ if id is None and self.safe_bool(self.options, 'useCcxtTradeId', True):
1700
+ id = self.create_ccxt_trade_id(timestamp, side, amountString, priceString, takerOrMaker)
1700
1701
  return self.safe_trade({
1701
1702
  'id': id,
1702
1703
  'order': orderId,
@@ -1713,23 +1714,6 @@ class mexc(Exchange, ImplicitAPI):
1713
1714
  'info': trade,
1714
1715
  }, market)
1715
1716
 
1716
- def synthetic_trade_id(self, market=None, timestamp=None, side=None, amount=None, price=None, orderType=None, takerOrMaker=None):
1717
- # TODO: can be unified method? self approach is being used by multiple exchanges(mexc, woo-coinsbit, dydx, ...)
1718
- id = ''
1719
- if timestamp is not None:
1720
- id = self.number_to_string(timestamp) + '-' + self.safe_string(market, 'id', '_')
1721
- if side is not None:
1722
- id += '-' + side
1723
- if amount is not None:
1724
- id += '-' + self.number_to_string(amount)
1725
- if price is not None:
1726
- id += '-' + self.number_to_string(price)
1727
- if takerOrMaker is not None:
1728
- id += '-' + takerOrMaker
1729
- if orderType is not None:
1730
- id += '-' + orderType
1731
- return id
1732
-
1733
1717
  async def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
1734
1718
  """
1735
1719
 
@@ -35,6 +35,9 @@ class ndax(Exchange, ImplicitAPI):
35
35
  'future': False,
36
36
  'option': False,
37
37
  'addMargin': False,
38
+ 'borrowCrossMargin': False,
39
+ 'borrowIsolatedMargin': False,
40
+ 'borrowMargin': False,
38
41
  'cancelAllOrders': True,
39
42
  'cancelOrder': True,
40
43
  'closeAllPositions': False,
@@ -47,6 +50,7 @@ class ndax(Exchange, ImplicitAPI):
47
50
  'createStopOrder': True,
48
51
  'editOrder': True,
49
52
  'fetchAccounts': True,
53
+ 'fetchAllGreeks': False,
50
54
  'fetchBalance': True,
51
55
  'fetchBorrowInterest': False,
52
56
  'fetchBorrowRate': False,
@@ -77,12 +81,15 @@ class ndax(Exchange, ImplicitAPI):
77
81
  'fetchLeverages': False,
78
82
  'fetchLeverageTiers': False,
79
83
  'fetchLiquidations': False,
84
+ 'fetchLongShortRatio': False,
85
+ 'fetchLongShortRatioHistory': False,
80
86
  'fetchMarginAdjustmentHistory': False,
81
87
  'fetchMarginMode': False,
82
88
  'fetchMarginModes': False,
83
89
  'fetchMarketLeverageTiers': False,
84
90
  'fetchMarkets': True,
85
91
  'fetchMarkOHLCV': False,
92
+ 'fetchMarkPrice': False,
86
93
  'fetchMarkPrices': False,
87
94
  'fetchMyLiquidations': False,
88
95
  'fetchMySettlementHistory': False,
@@ -90,6 +97,7 @@ class ndax(Exchange, ImplicitAPI):
90
97
  'fetchOHLCV': True,
91
98
  'fetchOpenInterest': False,
92
99
  'fetchOpenInterestHistory': False,
100
+ 'fetchOpenInterests': False,
93
101
  'fetchOpenOrders': True,
94
102
  'fetchOption': False,
95
103
  'fetchOptionChain': False,
ccxt/async_support/okx.py CHANGED
@@ -19,6 +19,7 @@ from ccxt.base.errors import BadRequest
19
19
  from ccxt.base.errors import BadSymbol
20
20
  from ccxt.base.errors import OperationRejected
21
21
  from ccxt.base.errors import ManualInteractionNeeded
22
+ from ccxt.base.errors import RestrictedLocation
22
23
  from ccxt.base.errors import InsufficientFunds
23
24
  from ccxt.base.errors import InvalidAddress
24
25
  from ccxt.base.errors import InvalidOrder
@@ -762,6 +763,7 @@ class okx(Exchange, ImplicitAPI):
762
763
  '51137': InvalidOrder, # Your opening price has triggered the limit price, and the max buy price is {0}
763
764
  '51138': InvalidOrder, # Your opening price has triggered the limit price, and the min sell price is {0}
764
765
  '51139': InvalidOrder, # Reduce-only feature is unavailable for the spot transactions by simple account
766
+ '51155': RestrictedLocation, # {"code":"1","data":[{"clOrdId":"e847xxx","ordId":"","sCode":"51155","sMsg":"You can't trade self pair or borrow self crypto due to local compliance restrictions. ","tag":"e847xxx","ts":"1753979177157"}],"inTime":"1753979177157408","msg":"All operations failed","outTime":"1753979177157874"}
765
767
  '51156': BadRequest, # You're leading trades in long/short mode and can't use self API endpoint to close positions
766
768
  '51159': BadRequest, # You're leading trades in buy/sell mode. If you want to place orders using self API endpoint, the orders must be in the same direction existing positions and open orders.
767
769
  '51162': InvalidOrder, # You have {instrument} open orders. Cancel these orders and try again
@@ -282,6 +282,7 @@ class phemex(Exchange, ImplicitAPI):
282
282
  # swap
283
283
  'orders/replace': 1, # ?symbol=<symbol>&orderID=<orderID>&origClOrdID=<origClOrdID>&clOrdID=<clOrdID>&price=<price>&priceEp=<priceEp>&orderQty=<orderQty>&stopPx=<stopPx>&stopPxEp=<stopPxEp>&takeProfit=<takeProfit>&takeProfitEp=<takeProfitEp>&stopLoss=<stopLoss>&stopLossEp=<stopLossEp>&pegOffsetValueEp=<pegOffsetValueEp>&pegPriceType=<pegPriceType>
284
284
  'g-orders/replace': 1, # ?symbol=<symbol>&orderID=<orderID>&origClOrdID=<origClOrdID>&clOrdID=<clOrdID>&price=<price>&priceEp=<priceEp>&orderQty=<orderQty>&stopPx=<stopPx>&stopPxEp=<stopPxEp>&takeProfit=<takeProfit>&takeProfitEp=<takeProfitEp>&stopLoss=<stopLoss>&stopLossEp=<stopLossEp>&pegOffsetValueEp=<pegOffsetValueEp>&pegPriceType=<pegPriceType>
285
+ 'g-orders/create': 1,
285
286
  'positions/leverage': 5, # ?symbol=<symbol>&leverage=<leverage>&leverageEr=<leverageEr>
286
287
  'g-positions/leverage': 5, # ?symbol=<symbol>&leverage=<leverage>&leverageEr=<leverageEr>
287
288
  'g-positions/switch-pos-mode-sync': 5, # ?symbol=<symbol>&targetPosMode=<targetPosMode>
@@ -1219,7 +1220,8 @@ class phemex(Exchange, ImplicitAPI):
1219
1220
  # 'id': 123456789, # optional request id
1220
1221
  }
1221
1222
  response = None
1222
- if market['linear'] and market['settle'] == 'USDT':
1223
+ isStableSettled = (market['settle'] == 'USDT') or (market['settle'] == 'USDC')
1224
+ if market['linear'] and isStableSettled:
1223
1225
  response = await self.v2GetMdV2Orderbook(self.extend(request, params))
1224
1226
  else:
1225
1227
  if (limit is not None) and (limit <= 30):
@@ -1351,7 +1353,8 @@ class phemex(Exchange, ImplicitAPI):
1351
1353
  }
1352
1354
  until = self.safe_integer_2(params, 'until', 'to')
1353
1355
  params = self.omit(params, ['until'])
1354
- usesSpecialFromToEndpoint = ((market['linear'] or market['settle'] == 'USDT')) and ((since is not None) or (until is not None))
1356
+ isStableSettled = (market['settle'] == 'USDT') or (market['settle'] == 'USDC')
1357
+ usesSpecialFromToEndpoint = ((market['linear'] or isStableSettled)) and ((since is not None) or (until is not None))
1355
1358
  maxLimit = 1000
1356
1359
  if usesSpecialFromToEndpoint:
1357
1360
  maxLimit = 2000
@@ -1359,7 +1362,7 @@ class phemex(Exchange, ImplicitAPI):
1359
1362
  limit = maxLimit
1360
1363
  request['limit'] = min(limit, maxLimit)
1361
1364
  response = None
1362
- if market['linear'] or market['settle'] == 'USDT':
1365
+ if market['linear'] or isStableSettled:
1363
1366
  if (until is not None) or (since is not None):
1364
1367
  candleDuration = self.parse_timeframe(timeframe)
1365
1368
  if since is not None:
@@ -1615,7 +1618,8 @@ class phemex(Exchange, ImplicitAPI):
1615
1618
  # 'id': 123456789, # optional request id
1616
1619
  }
1617
1620
  response = None
1618
- if market['linear'] and market['settle'] == 'USDT':
1621
+ isStableSettled = (market['settle'] == 'USDT') or (market['settle'] == 'USDC')
1622
+ if market['linear'] and isStableSettled:
1619
1623
  response = await self.v2GetMdV2Trade(self.extend(request, params))
1620
1624
  else:
1621
1625
  response = await self.v1GetMdTrade(self.extend(request, params))
@@ -1854,7 +1858,7 @@ class phemex(Exchange, ImplicitAPI):
1854
1858
  timestamp = self.safe_integer(trade, 'createdAt')
1855
1859
  id = self.safe_string_2(trade, 'execId', 'execID')
1856
1860
  orderId = self.safe_string(trade, 'orderID')
1857
- if market['settle'] == 'USDT':
1861
+ if market['settle'] == 'USDT' or market['settle'] == 'USDC':
1858
1862
  sideId = self.safe_string_lower(trade, 'side')
1859
1863
  if (sideId == 'buy') or (sideId == 'sell'):
1860
1864
  side = sideId
@@ -2590,6 +2594,7 @@ class phemex(Exchange, ImplicitAPI):
2590
2594
  stopLossDefined = (stopLoss is not None)
2591
2595
  takeProfit = self.safe_value(params, 'takeProfit')
2592
2596
  takeProfitDefined = (takeProfit is not None)
2597
+ isStableSettled = (market['settle'] == 'USDT') or (market['settle'] == 'USDC')
2593
2598
  if clientOrderId is None:
2594
2599
  brokerId = self.safe_string(self.options, 'brokerId', 'CCXT123456')
2595
2600
  if brokerId is not None:
@@ -2599,7 +2604,7 @@ class phemex(Exchange, ImplicitAPI):
2599
2604
  params = self.omit(params, ['clOrdID', 'clientOrderId'])
2600
2605
  triggerPrice = self.safe_string_n(params, ['stopPx', 'stopPrice', 'triggerPrice'])
2601
2606
  if triggerPrice is not None:
2602
- if market['settle'] == 'USDT':
2607
+ if isStableSettled:
2603
2608
  request['stopPxRp'] = self.price_to_precision(symbol, triggerPrice)
2604
2609
  else:
2605
2610
  request['stopPxEp'] = self.to_ep(triggerPrice, market)
@@ -2648,7 +2653,7 @@ class phemex(Exchange, ImplicitAPI):
2648
2653
  posSide = 'Merged'
2649
2654
  posSide = self.capitalize(posSide)
2650
2655
  request['posSide'] = posSide
2651
- if market['settle'] == 'USDT':
2656
+ if isStableSettled:
2652
2657
  request['orderQtyRq'] = amount
2653
2658
  else:
2654
2659
  request['orderQty'] = self.parse_to_int(amount)
@@ -2676,7 +2681,7 @@ class phemex(Exchange, ImplicitAPI):
2676
2681
  stopLossTriggerPrice = self.safe_value_2(stopLoss, 'triggerPrice', 'stopPrice')
2677
2682
  if stopLossTriggerPrice is None:
2678
2683
  raise InvalidOrder(self.id + ' createOrder() requires a trigger price in params["stopLoss"]["triggerPrice"] for a stop loss order')
2679
- if market['settle'] == 'USDT':
2684
+ if isStableSettled:
2680
2685
  request['stopLossRp'] = self.price_to_precision(symbol, stopLossTriggerPrice)
2681
2686
  else:
2682
2687
  request['stopLossEp'] = self.to_ep(stopLossTriggerPrice, market)
@@ -2690,7 +2695,7 @@ class phemex(Exchange, ImplicitAPI):
2690
2695
  takeProfitTriggerPrice = self.safe_value_2(takeProfit, 'triggerPrice', 'stopPrice')
2691
2696
  if takeProfitTriggerPrice is None:
2692
2697
  raise InvalidOrder(self.id + ' createOrder() requires a trigger price in params["takeProfit"]["triggerPrice"] for a take profit order')
2693
- if market['settle'] == 'USDT':
2698
+ if isStableSettled:
2694
2699
  request['takeProfitRp'] = self.price_to_precision(symbol, takeProfitTriggerPrice)
2695
2700
  else:
2696
2701
  request['takeProfitEp'] = self.to_ep(takeProfitTriggerPrice, market)
@@ -2701,27 +2706,27 @@ class phemex(Exchange, ImplicitAPI):
2701
2706
  if tpLimitPrice is not None:
2702
2707
  request['tpPxRp'] = self.price_to_precision(symbol, tpLimitPrice)
2703
2708
  if (type == 'Limit') or (type == 'StopLimit') or (type == 'LimitIfTouched'):
2704
- if market['settle'] == 'USDT':
2709
+ if isStableSettled:
2705
2710
  request['priceRp'] = self.price_to_precision(symbol, price)
2706
2711
  else:
2707
2712
  priceString = self.number_to_string(price)
2708
2713
  request['priceEp'] = self.to_ep(priceString, market)
2709
2714
  takeProfitPrice = self.safe_string(params, 'takeProfitPrice')
2710
2715
  if takeProfitPrice is not None:
2711
- if market['settle'] == 'USDT':
2716
+ if isStableSettled:
2712
2717
  request['takeProfitRp'] = self.price_to_precision(symbol, takeProfitPrice)
2713
2718
  else:
2714
2719
  request['takeProfitEp'] = self.to_ep(takeProfitPrice, market)
2715
2720
  params = self.omit(params, 'takeProfitPrice')
2716
2721
  stopLossPrice = self.safe_string(params, 'stopLossPrice')
2717
2722
  if stopLossPrice is not None:
2718
- if market['settle'] == 'USDT':
2723
+ if isStableSettled:
2719
2724
  request['stopLossRp'] = self.price_to_precision(symbol, stopLossPrice)
2720
2725
  else:
2721
2726
  request['stopLossEp'] = self.to_ep(stopLossPrice, market)
2722
2727
  params = self.omit(params, 'stopLossPrice')
2723
2728
  response = None
2724
- if market['settle'] == 'USDT':
2729
+ if isStableSettled:
2725
2730
  response = await self.privatePostGOrders(self.extend(request, params))
2726
2731
  elif market['contract']:
2727
2732
  response = await self.privatePostOrders(self.extend(request, params))
@@ -2829,13 +2834,13 @@ class phemex(Exchange, ImplicitAPI):
2829
2834
  }
2830
2835
  clientOrderId = self.safe_string_2(params, 'clientOrderId', 'clOrdID')
2831
2836
  params = self.omit(params, ['clientOrderId', 'clOrdID'])
2832
- isUSDTSettled = (market['settle'] == 'USDT')
2837
+ isStableSettled = (market['settle'] == 'USDT') or (market['settle'] == 'USDC')
2833
2838
  if clientOrderId is not None:
2834
2839
  request['clOrdID'] = clientOrderId
2835
2840
  else:
2836
2841
  request['orderID'] = id
2837
2842
  if price is not None:
2838
- if isUSDTSettled:
2843
+ if isStableSettled:
2839
2844
  request['priceRp'] = self.price_to_precision(market['symbol'], price)
2840
2845
  else:
2841
2846
  request['priceEp'] = self.to_ep(price, market)
@@ -2845,19 +2850,19 @@ class phemex(Exchange, ImplicitAPI):
2845
2850
  if finalQty is not None:
2846
2851
  request['baseQtyEV'] = finalQty
2847
2852
  elif amount is not None:
2848
- if isUSDTSettled:
2853
+ if isStableSettled:
2849
2854
  request['orderQtyRq'] = self.amount_to_precision(market['symbol'], amount)
2850
2855
  else:
2851
2856
  request['baseQtyEV'] = self.to_ev(amount, market)
2852
2857
  triggerPrice = self.safe_string_n(params, ['triggerPrice', 'stopPx', 'stopPrice'])
2853
2858
  if triggerPrice is not None:
2854
- if isUSDTSettled:
2859
+ if isStableSettled:
2855
2860
  request['stopPxRp'] = self.price_to_precision(symbol, triggerPrice)
2856
2861
  else:
2857
2862
  request['stopPxEp'] = self.to_ep(triggerPrice, market)
2858
2863
  params = self.omit(params, ['triggerPrice', 'stopPx', 'stopPrice'])
2859
2864
  response = None
2860
- if isUSDTSettled:
2865
+ if isStableSettled:
2861
2866
  posSide = self.safe_string(params, 'posSide')
2862
2867
  if posSide is None:
2863
2868
  request['posSide'] = 'Merged'
@@ -2895,7 +2900,7 @@ class phemex(Exchange, ImplicitAPI):
2895
2900
  else:
2896
2901
  request['orderID'] = id
2897
2902
  response = None
2898
- if market['settle'] == 'USDT':
2903
+ if market['settle'] == 'USDT' or market['settle'] == 'USDC':
2899
2904
  posSide = self.safe_string(params, 'posSide')
2900
2905
  if posSide is None:
2901
2906
  request['posSide'] = 'Merged'
@@ -2931,7 +2936,7 @@ class phemex(Exchange, ImplicitAPI):
2931
2936
  if trigger:
2932
2937
  request['untriggerred'] = trigger
2933
2938
  response = None
2934
- if market['settle'] == 'USDT':
2939
+ if market['settle'] == 'USDT' or market['settle'] == 'USDC':
2935
2940
  response = await self.privateDeleteGOrdersAll(self.extend(request, params))
2936
2941
  #
2937
2942
  # {
@@ -2991,7 +2996,7 @@ class phemex(Exchange, ImplicitAPI):
2991
2996
  else:
2992
2997
  request['orderID'] = id
2993
2998
  response = None
2994
- if market['settle'] == 'USDT':
2999
+ if market['settle'] == 'USDT' or market['settle'] == 'USDC':
2995
3000
  response = await self.privateGetApiDataGFuturesOrdersByOrderId(self.extend(request, params))
2996
3001
  elif market['spot']:
2997
3002
  response = await self.privateGetApiDataSpotsOrdersByOrderId(self.extend(request, params))
@@ -3036,7 +3041,7 @@ class phemex(Exchange, ImplicitAPI):
3036
3041
  if limit is not None:
3037
3042
  request['limit'] = limit
3038
3043
  response = None
3039
- if market['settle'] == 'USDT':
3044
+ if market['settle'] == 'USDT' or market['settle'] == 'USDC':
3040
3045
  request['currency'] = market['settle']
3041
3046
  response = await self.privateGetExchangeOrderV2OrderList(self.extend(request, params))
3042
3047
  elif market['swap']:
@@ -3071,7 +3076,7 @@ class phemex(Exchange, ImplicitAPI):
3071
3076
  }
3072
3077
  response = None
3073
3078
  try:
3074
- if market['settle'] == 'USDT':
3079
+ if market['settle'] == 'USDT' or market['settle'] == 'USDC':
3075
3080
  response = await self.privateGetGOrdersActiveList(self.extend(request, params))
3076
3081
  elif market['swap']:
3077
3082
  response = await self.privateGetOrdersActiveList(self.extend(request, params))
@@ -3876,8 +3881,8 @@ class phemex(Exchange, ImplicitAPI):
3876
3881
  raise BadRequest(self.id + ' fetchFundingHistory() limit argument cannot exceed 200')
3877
3882
  request['limit'] = limit
3878
3883
  response = None
3879
- isUsdt = market['settle'] == 'USDT'
3880
- if isUsdt:
3884
+ isStableSettled = market['settle'] == 'USDT' or market['settle'] == 'USDC'
3885
+ if isStableSettled:
3881
3886
  response = await self.privateGetApiDataGFuturesFundingFees(self.extend(request, params))
3882
3887
  else:
3883
3888
  response = await self.privateGetApiDataFuturesFundingFees(self.extend(request, params))
@@ -3926,8 +3931,8 @@ class phemex(Exchange, ImplicitAPI):
3926
3931
  if value is None or currencyCode is None:
3927
3932
  return value
3928
3933
  # it was confirmed by phemex support, that USDT contracts use direct amounts in funding fees, while USD & INVERSE needs 'valueScale'
3929
- isUsdt = market['settle'] == 'USDT'
3930
- if not isUsdt:
3934
+ isStableSettled = market['settle'] == 'USDT' or market['settle'] == 'USDC'
3935
+ if not isStableSettled:
3931
3936
  currency = self.safe_currency(currencyCode)
3932
3937
  scale = self.safe_string(currency['info'], 'valueScale')
3933
3938
  tickPrecision = self.parse_precision(scale)
@@ -4119,8 +4124,8 @@ class phemex(Exchange, ImplicitAPI):
4119
4124
  raise ArgumentsRequired(self.id + ' setMarginMode() requires a symbol argument')
4120
4125
  await self.load_markets()
4121
4126
  market = self.market(symbol)
4122
- if not market['swap'] or market['settle'] == 'USDT':
4123
- raise BadSymbol(self.id + ' setMarginMode() supports swap(non USDT based) contracts only')
4127
+ if not market['swap'] or market['settle'] == 'USDT' or market['settle'] == 'USDC':
4128
+ raise BadSymbol(self.id + ' setMarginMode() supports swap(non USDT/USDC based) contracts only')
4124
4129
  marginMode = marginMode.lower()
4125
4130
  if marginMode != 'isolated' and marginMode != 'cross':
4126
4131
  raise BadRequest(self.id + ' setMarginMode() marginMode argument should be isolated or cross')
@@ -4356,7 +4361,7 @@ class phemex(Exchange, ImplicitAPI):
4356
4361
  'symbol': market['id'],
4357
4362
  }
4358
4363
  response = None
4359
- if market['settle'] == 'USDT':
4364
+ if market['settle'] == 'USDT' or market['settle'] == 'USDC':
4360
4365
  if not isHedged and longLeverageRr is None and shortLeverageRr is None:
4361
4366
  request['leverageRr'] = leverage
4362
4367
  else:
@@ -4579,7 +4584,7 @@ class phemex(Exchange, ImplicitAPI):
4579
4584
  raise ArgumentsRequired(self.id + ' fetchFundingRateHistory() requires a symbol argument')
4580
4585
  await self.load_markets()
4581
4586
  market = self.market(symbol)
4582
- isUsdtSettled = market['settle'] == 'USDT'
4587
+ isUsdtSettled = market['settle'] == 'USDT' or market['settle'] == 'USDC'
4583
4588
  if not market['swap']:
4584
4589
  raise BadRequest(self.id + ' fetchFundingRateHistory() supports swap contracts only')
4585
4590
  paginate = False
@@ -34,17 +34,21 @@ PAD_WITH_ZERO = 6
34
34
 
35
35
 
36
36
  def decimal_to_precision(n, rounding_mode=ROUND, precision=None, counting_mode=DECIMAL_PLACES, padding_mode=NO_PADDING):
37
- assert precision is not None
37
+ assert precision is not None, 'precision should not be None'
38
+
39
+ if isinstance(precision, str):
40
+ precision = float(precision)
41
+ assert isinstance(precision, float) or isinstance(precision, decimal.Decimal) or isinstance(precision, numbers.Integral), 'precision has an invalid number'
42
+
38
43
  if counting_mode == TICK_SIZE:
39
- assert(isinstance(precision, float) or isinstance(precision, decimal.Decimal) or isinstance(precision, numbers.Integral) or isinstance(precision, str))
44
+ assert precision > 0, 'negative or zero precision can not be used with TICK_SIZE precisionMode'
40
45
  else:
41
- assert(isinstance(precision, numbers.Integral))
46
+ assert isinstance(precision, numbers.Integral)
47
+
42
48
  assert rounding_mode in [TRUNCATE, ROUND]
43
49
  assert counting_mode in [DECIMAL_PLACES, SIGNIFICANT_DIGITS, TICK_SIZE]
44
50
  assert padding_mode in [NO_PADDING, PAD_WITH_ZERO]
45
-
46
- if isinstance(precision, str):
47
- precision = float(precision)
51
+ # end of checks
48
52
 
49
53
  context = decimal.getcontext()
50
54
 
@@ -78,12 +82,12 @@ def decimal_to_precision(n, rounding_mode=ROUND, precision=None, counting_mode=D
78
82
  if missing != 0:
79
83
  if rounding_mode == ROUND:
80
84
  if dec > 0:
81
- if missing >= precision / 2:
85
+ if missing >= precision_dec / 2:
82
86
  dec = dec - missing + precision_dec
83
87
  else:
84
88
  dec = dec - missing
85
89
  else:
86
- if missing >= precision / 2:
90
+ if missing >= precision_dec / 2:
87
91
  dec = dec + missing - precision_dec
88
92
  else:
89
93
  dec = dec + missing
@@ -117,7 +121,7 @@ def decimal_to_precision(n, rounding_mode=ROUND, precision=None, counting_mode=D
117
121
  precise = '{:f}'.format(min((below, above), key=lambda x: abs(x - dec)))
118
122
  else:
119
123
  precise = '{:f}'.format(dec.quantize(sigfig))
120
- if precise == ('-0.' + len(precise) * '0')[:2] or precise == '-0':
124
+ if precise.startswith('-0') and all(c in '0.' for c in precise[1:]):
121
125
  precise = precise[1:]
122
126
 
123
127
  elif rounding_mode == TRUNCATE:
@@ -138,7 +142,7 @@ def decimal_to_precision(n, rounding_mode=ROUND, precision=None, counting_mode=D
138
142
  precise = string
139
143
  else:
140
144
  precise = string[:end].ljust(dot, '0')
141
- if precise == ('-0.' + len(precise) * '0')[:3] or precise == '-0':
145
+ if precise.startswith('-0') and all(c in '0.' for c in precise[1:]):
142
146
  precise = precise[1:]
143
147
  precise = precise.rstrip('.')
144
148
 
ccxt/base/errors.py CHANGED
@@ -23,6 +23,7 @@ error_hierarchy = {
23
23
  },
24
24
  'MarketClosed': {},
25
25
  'ManualInteractionNeeded': {},
26
+ 'RestrictedLocation': {},
26
27
  },
27
28
  'InsufficientFunds': {},
28
29
  'InvalidAddress': {
@@ -118,6 +119,10 @@ class ManualInteractionNeeded(OperationRejected):
118
119
  pass
119
120
 
120
121
 
122
+ class RestrictedLocation(OperationRejected):
123
+ pass
124
+
125
+
121
126
  class InsufficientFunds(ExchangeError):
122
127
  pass
123
128
 
@@ -238,6 +243,7 @@ __all__ = [
238
243
  'MarginModeAlreadySet',
239
244
  'MarketClosed',
240
245
  'ManualInteractionNeeded',
246
+ 'RestrictedLocation',
241
247
  'InsufficientFunds',
242
248
  'InvalidAddress',
243
249
  'AddressPending',
ccxt/base/exchange.py CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  # -----------------------------------------------------------------------------
6
6
 
7
- __version__ = '4.4.96'
7
+ __version__ = '4.4.98'
8
8
 
9
9
  # -----------------------------------------------------------------------------
10
10
 
@@ -2133,6 +2133,17 @@ class Exchange(object):
2133
2133
  'watchLiquidations': None,
2134
2134
  'watchLiquidationsForSymbols': None,
2135
2135
  'watchMyLiquidations': None,
2136
+ 'unWatchOrders': None,
2137
+ 'unWatchTrades': None,
2138
+ 'unWatchTradesForSymbols': None,
2139
+ 'unWatchOHLCVForSymbols': None,
2140
+ 'unWatchOrderBookForSymbols': None,
2141
+ 'unWatchPositions': None,
2142
+ 'unWatchOrderBook': None,
2143
+ 'unWatchTickers': None,
2144
+ 'unWatchMyTrades': None,
2145
+ 'unWatchTicker': None,
2146
+ 'unWatchOHLCV': None,
2136
2147
  'watchMyLiquidationsForSymbols': None,
2137
2148
  'withdraw': None,
2138
2149
  'ws': None,
@@ -2619,6 +2630,9 @@ class Exchange(object):
2619
2630
  def un_watch_order_book_for_symbols(self, symbols: List[str], params={}):
2620
2631
  raise NotSupported(self.id + ' unWatchOrderBookForSymbols() is not supported yet')
2621
2632
 
2633
+ def un_watch_positions(self, symbols: Strings = None, params={}):
2634
+ raise NotSupported(self.id + ' unWatchPositions() is not supported yet')
2635
+
2622
2636
  def fetch_deposit_addresses(self, codes: Strings = None, params={}):
2623
2637
  raise NotSupported(self.id + ' fetchDepositAddresses() is not supported yet')
2624
2638
 
@@ -3587,18 +3601,7 @@ class Exchange(object):
3587
3601
  symbol = market['symbol'] if (market is not None) else None
3588
3602
  return self.filter_by_symbol_since_limit(results, symbol, since, limit)
3589
3603
 
3590
- def calculate_fee(self, symbol: str, type: str, side: str, amount: float, price: float, takerOrMaker='taker', params={}):
3591
- """
3592
- calculates the presumptive fee that would be charged for an order
3593
- :param str symbol: unified market symbol
3594
- :param str type: 'market' or 'limit'
3595
- :param str side: 'buy' or 'sell'
3596
- :param float amount: how much you want to trade, in units of the base currency on most exchanges, or number of contracts
3597
- :param float price: the price for the order to be filled at, in units of the quote currency
3598
- :param str takerOrMaker: 'taker' or 'maker'
3599
- :param dict params:
3600
- :returns dict: contains the rate, the percentage multiplied to the order amount to obtain the fee amount, and cost, the total value of the fee in units of the quote currency, for the order
3601
- """
3604
+ def calculate_fee_with_rate(self, symbol: str, type: str, side: str, amount: float, price: float, takerOrMaker='taker', feeRate: Num = None, params={}):
3602
3605
  if type == 'market' and takerOrMaker == 'maker':
3603
3606
  raise ArgumentsRequired(self.id + ' calculateFee() - you have provided incompatible arguments - "market" type order can not be "maker". Change either the "type" or the "takerOrMaker" argument to calculate the fee.')
3604
3607
  market = self.markets[symbol]
@@ -3627,7 +3630,7 @@ class Exchange(object):
3627
3630
  # even if `takerOrMaker` argument was set to 'maker', for 'market' orders we should forcefully override it to 'taker'
3628
3631
  if type == 'market':
3629
3632
  takerOrMaker = 'taker'
3630
- rate = self.safe_string(market, takerOrMaker)
3633
+ rate = self.number_to_string(feeRate) if (feeRate is not None) else self.safe_string(market, takerOrMaker)
3631
3634
  cost = Precise.string_mul(cost, rate)
3632
3635
  return {
3633
3636
  'type': takerOrMaker,
@@ -3636,6 +3639,20 @@ class Exchange(object):
3636
3639
  'cost': self.parse_number(cost),
3637
3640
  }
3638
3641
 
3642
+ def calculate_fee(self, symbol: str, type: str, side: str, amount: float, price: float, takerOrMaker='taker', params={}):
3643
+ """
3644
+ calculates the presumptive fee that would be charged for an order
3645
+ :param str symbol: unified market symbol
3646
+ :param str type: 'market' or 'limit'
3647
+ :param str side: 'buy' or 'sell'
3648
+ :param float amount: how much you want to trade, in units of the base currency on most exchanges, or number of contracts
3649
+ :param float price: the price for the order to be filled at, in units of the quote currency
3650
+ :param str takerOrMaker: 'taker' or 'maker'
3651
+ :param dict params:
3652
+ :returns dict: contains the rate, the percentage multiplied to the order amount to obtain the fee amount, and cost, the total value of the fee in units of the quote currency, for the order
3653
+ """
3654
+ return self.calculate_fee_with_rate(symbol, type, side, amount, price, takerOrMaker, None, params)
3655
+
3639
3656
  def safe_liquidation(self, liquidation: dict, market: Market = None):
3640
3657
  contracts = self.safe_string(liquidation, 'contracts')
3641
3658
  contractSize = self.safe_string(market, 'contractSize')
@@ -3675,6 +3692,21 @@ class Exchange(object):
3675
3692
  trade['cost'] = self.parse_number(cost)
3676
3693
  return trade
3677
3694
 
3695
+ def create_ccxt_trade_id(self, timestamp=None, side=None, amount=None, price=None, takerOrMaker=None):
3696
+ # self approach is being used by multiple exchanges(mexc, woo, coinsbit, dydx, ...)
3697
+ id = None
3698
+ if timestamp is not None:
3699
+ id = self.number_to_string(timestamp)
3700
+ if side is not None:
3701
+ id += '-' + side
3702
+ if amount is not None:
3703
+ id += '-' + self.number_to_string(amount)
3704
+ if price is not None:
3705
+ id += '-' + self.number_to_string(price)
3706
+ if takerOrMaker is not None:
3707
+ id += '-' + takerOrMaker
3708
+ return id
3709
+
3678
3710
  def parsed_fee_and_fees(self, container: Any):
3679
3711
  fee = self.safe_dict(container, 'fee')
3680
3712
  fees = self.safe_list(container, 'fees')
@@ -6429,7 +6461,7 @@ class Exchange(object):
6429
6461
  calls = 0
6430
6462
  result = []
6431
6463
  errors = 0
6432
- until = self.safe_integer_2(params, 'untill', 'till') # do not omit it from params here
6464
+ until = self.safe_integer_n(params, ['until', 'untill', 'till']) # do not omit it from params here
6433
6465
  maxEntriesPerRequest, params = self.handle_max_entries_per_request_and_params(method, maxEntriesPerRequest, params)
6434
6466
  if (paginationDirection == 'forward'):
6435
6467
  if since is None:
@@ -6991,6 +7023,14 @@ class Exchange(object):
6991
7023
  self.myTrades = None
6992
7024
  elif topic == 'orders' and (self.orders is not None):
6993
7025
  self.orders = None
7026
+ elif topic == 'positions' and (self.positions is not None):
7027
+ self.positions = None
7028
+ clients = list(self.clients.values())
7029
+ for i in range(0, len(clients)):
7030
+ client = clients[i]
7031
+ futures = self.safe_dict(client, 'futures')
7032
+ if (futures is not None) and ('fetchPositionsSnapshot' in futures):
7033
+ del futures['fetchPositionsSnapshot']
6994
7034
  elif topic == 'ticker' and (self.tickers is not None):
6995
7035
  tickerSymbols = list(self.tickers.keys())
6996
7036
  for i in range(0, len(tickerSymbols)):