ccxt 4.4.77__py2.py3-none-any.whl → 4.4.80__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 (133) hide show
  1. ccxt/__init__.py +3 -3
  2. ccxt/abstract/apex.py +31 -0
  3. ccxt/abstract/bitmart.py +1 -0
  4. ccxt/apex.py +1884 -0
  5. ccxt/ascendex.py +23 -6
  6. ccxt/async_support/__init__.py +3 -3
  7. ccxt/async_support/apex.py +1884 -0
  8. ccxt/async_support/ascendex.py +23 -6
  9. ccxt/async_support/base/exchange.py +5 -1
  10. ccxt/async_support/binance.py +9 -3
  11. ccxt/async_support/bingx.py +4 -4
  12. ccxt/async_support/bitfinex.py +61 -36
  13. ccxt/async_support/bitflyer.py +2 -2
  14. ccxt/async_support/bitget.py +186 -128
  15. ccxt/async_support/bitmart.py +9 -4
  16. ccxt/async_support/bitmex.py +14 -7
  17. ccxt/async_support/bitopro.py +5 -1
  18. ccxt/async_support/bitrue.py +2 -1
  19. ccxt/async_support/bitso.py +1 -1
  20. ccxt/async_support/bitteam.py +2 -0
  21. ccxt/async_support/bitvavo.py +25 -10
  22. ccxt/async_support/btcalpha.py +1 -1
  23. ccxt/async_support/btcmarkets.py +1 -1
  24. ccxt/async_support/btcturk.py +1 -1
  25. ccxt/async_support/bybit.py +27 -15
  26. ccxt/async_support/cex.py +1 -1
  27. ccxt/async_support/coinbase.py +17 -4
  28. ccxt/async_support/coincatch.py +66 -0
  29. ccxt/async_support/coinex.py +2 -1
  30. ccxt/async_support/coinlist.py +1 -0
  31. ccxt/async_support/coinone.py +1 -0
  32. ccxt/async_support/cryptocom.py +2 -2
  33. ccxt/async_support/defx.py +1 -1
  34. ccxt/async_support/delta.py +4 -1
  35. ccxt/async_support/deribit.py +3 -2
  36. ccxt/async_support/derive.py +2 -2
  37. ccxt/async_support/digifinex.py +2 -2
  38. ccxt/async_support/gate.py +1 -1
  39. ccxt/async_support/hitbtc.py +5 -2
  40. ccxt/async_support/hollaex.py +1 -0
  41. ccxt/async_support/htx.py +9 -5
  42. ccxt/async_support/huobijp.py +1 -0
  43. ccxt/async_support/hyperliquid.py +14 -6
  44. ccxt/async_support/kraken.py +4 -2
  45. ccxt/async_support/krakenfutures.py +2 -2
  46. ccxt/async_support/kucoinfutures.py +2 -2
  47. ccxt/async_support/mexc.py +50 -52
  48. ccxt/async_support/okx.py +2 -2
  49. ccxt/async_support/oxfun.py +2 -2
  50. ccxt/async_support/paradex.py +2 -2
  51. ccxt/async_support/phemex.py +4 -3
  52. ccxt/async_support/poloniex.py +4 -3
  53. ccxt/async_support/probit.py +1 -0
  54. ccxt/async_support/timex.py +2 -2
  55. ccxt/async_support/tradeogre.py +2 -1
  56. ccxt/async_support/upbit.py +243 -63
  57. ccxt/async_support/vertex.py +2 -2
  58. ccxt/async_support/whitebit.py +66 -12
  59. ccxt/async_support/woo.py +5 -3
  60. ccxt/async_support/woofipro.py +2 -2
  61. ccxt/async_support/xt.py +9 -2
  62. ccxt/base/exchange.py +69 -2
  63. ccxt/binance.py +9 -3
  64. ccxt/bingx.py +4 -4
  65. ccxt/bitfinex.py +61 -36
  66. ccxt/bitflyer.py +2 -2
  67. ccxt/bitget.py +186 -128
  68. ccxt/bitmart.py +9 -4
  69. ccxt/bitmex.py +14 -7
  70. ccxt/bitopro.py +5 -1
  71. ccxt/bitrue.py +2 -1
  72. ccxt/bitso.py +1 -1
  73. ccxt/bitteam.py +2 -0
  74. ccxt/bitvavo.py +25 -10
  75. ccxt/btcalpha.py +1 -1
  76. ccxt/btcmarkets.py +1 -1
  77. ccxt/btcturk.py +1 -1
  78. ccxt/bybit.py +27 -15
  79. ccxt/cex.py +1 -1
  80. ccxt/coinbase.py +17 -4
  81. ccxt/coincatch.py +66 -0
  82. ccxt/coinex.py +2 -1
  83. ccxt/coinlist.py +1 -0
  84. ccxt/coinone.py +1 -0
  85. ccxt/cryptocom.py +2 -2
  86. ccxt/defx.py +1 -1
  87. ccxt/delta.py +4 -1
  88. ccxt/deribit.py +3 -2
  89. ccxt/derive.py +2 -2
  90. ccxt/digifinex.py +2 -2
  91. ccxt/gate.py +1 -1
  92. ccxt/hitbtc.py +5 -2
  93. ccxt/hollaex.py +1 -0
  94. ccxt/htx.py +9 -5
  95. ccxt/huobijp.py +1 -0
  96. ccxt/hyperliquid.py +14 -6
  97. ccxt/kraken.py +4 -2
  98. ccxt/krakenfutures.py +2 -2
  99. ccxt/kucoinfutures.py +2 -2
  100. ccxt/mexc.py +50 -52
  101. ccxt/okx.py +2 -2
  102. ccxt/oxfun.py +2 -2
  103. ccxt/paradex.py +2 -2
  104. ccxt/phemex.py +4 -3
  105. ccxt/poloniex.py +4 -3
  106. ccxt/pro/__init__.py +5 -1
  107. ccxt/pro/apex.py +984 -0
  108. ccxt/pro/binance.py +3 -3
  109. ccxt/pro/coinbase.py +43 -57
  110. ccxt/pro/gate.py +22 -2
  111. ccxt/pro/hollaex.py +2 -2
  112. ccxt/pro/p2b.py +2 -2
  113. ccxt/pro/tradeogre.py +272 -0
  114. ccxt/pro/upbit.py +42 -0
  115. ccxt/probit.py +1 -0
  116. ccxt/test/tests_async.py +4 -1
  117. ccxt/test/tests_sync.py +4 -1
  118. ccxt/timex.py +2 -2
  119. ccxt/tradeogre.py +2 -1
  120. ccxt/upbit.py +243 -63
  121. ccxt/vertex.py +2 -2
  122. ccxt/whitebit.py +66 -12
  123. ccxt/woo.py +5 -3
  124. ccxt/woofipro.py +2 -2
  125. ccxt/xt.py +9 -2
  126. {ccxt-4.4.77.dist-info → ccxt-4.4.80.dist-info}/METADATA +9 -11
  127. {ccxt-4.4.77.dist-info → ccxt-4.4.80.dist-info}/RECORD +130 -128
  128. ccxt/abstract/ace.py +0 -15
  129. ccxt/ace.py +0 -1152
  130. ccxt/async_support/ace.py +0 -1152
  131. {ccxt-4.4.77.dist-info → ccxt-4.4.80.dist-info}/LICENSE.txt +0 -0
  132. {ccxt-4.4.77.dist-info → ccxt-4.4.80.dist-info}/WHEEL +0 -0
  133. {ccxt-4.4.77.dist-info → ccxt-4.4.80.dist-info}/top_level.txt +0 -0
@@ -1393,28 +1393,44 @@ class bitget(Exchange, ImplicitAPI):
1393
1393
  'fillResponseFromRequest': True,
1394
1394
  },
1395
1395
  'fetchOHLCV': {
1396
- 'spot': {
1397
- 'method': 'publicSpotGetV2SpotMarketCandles', # publicSpotGetV2SpotMarketCandles or publicSpotGetV2SpotMarketHistoryCandles
1398
- },
1399
- 'swap': {
1400
- 'method': 'publicMixGetV2MixMarketCandles', # publicMixGetV2MixMarketCandles or publicMixGetV2MixMarketHistoryCandles or publicMixGetV2MixMarketHistoryIndexCandles or publicMixGetV2MixMarketHistoryMarkCandles
1401
- },
1402
- 'maxDaysPerTimeframe': {
1396
+ # ### Timeframe settings ###
1397
+ # after testing, the below values are real ones, because the values provided by API DOCS are wrong
1398
+ # so, start timestamp should be within these thresholds to be able to call "recent" candles endpoint
1399
+ 'maxRecentDaysPerTimeframe': {
1403
1400
  '1m': 30,
1404
1401
  '3m': 30,
1405
1402
  '5m': 30,
1406
- '10m': 30,
1407
- '15m': 52,
1408
- '30m': 62,
1409
- '1h': 83,
1410
- '2h': 120,
1403
+ '15m': 30,
1404
+ '30m': 30,
1405
+ '1h': 60,
1411
1406
  '4h': 240,
1412
1407
  '6h': 360,
1413
- '12h': 360,
1414
- '1d': 300,
1415
- '3d': 300,
1416
- '1w': 300,
1417
- '1M': 300,
1408
+ '12h': 720,
1409
+ '1d': 1440,
1410
+ '3d': 1440 * 3,
1411
+ '1w': 1440 * 7,
1412
+ '1M': 1440 * 30,
1413
+ },
1414
+ 'spot': {
1415
+ 'maxLimitPerTimeframe': {
1416
+ '1d': 300,
1417
+ '3d': 100,
1418
+ '1w': 100,
1419
+ '1M': 100,
1420
+ },
1421
+ 'method': 'publicSpotGetV2SpotMarketCandles', # publicSpotGetV2SpotMarketCandles or publicSpotGetV2SpotMarketHistoryCandles
1422
+ },
1423
+ 'swap': {
1424
+ 'maxLimitPerTimeframe': {
1425
+ '4h': 540,
1426
+ '6h': 360,
1427
+ '12h': 180,
1428
+ '1d': 90,
1429
+ '3d': 30,
1430
+ '1w': 13,
1431
+ '1M': 4,
1432
+ },
1433
+ 'method': 'publicMixGetV2MixMarketCandles', # publicMixGetV2MixMarketCandles or publicMixGetV2MixMarketHistoryCandles or publicMixGetV2MixMarketHistoryIndexCandles or publicMixGetV2MixMarketHistoryMarkCandles
1418
1434
  },
1419
1435
  },
1420
1436
  'fetchTrades': {
@@ -1425,6 +1441,9 @@ class bitget(Exchange, ImplicitAPI):
1425
1441
  'method': 'publicMixGetV2MixMarketFillsHistory', # or publicMixGetV2MixMarketFills
1426
1442
  },
1427
1443
  },
1444
+ 'fetchFundingRate': {
1445
+ 'method': 'publicMixGetV2MixMarketCurrentFundRate', # or publicMixGetV2MixMarketFundingTime
1446
+ },
1428
1447
  'accountsByType': {
1429
1448
  'spot': 'spot',
1430
1449
  'cross': 'crossed_margin',
@@ -1548,6 +1567,8 @@ class bitget(Exchange, ImplicitAPI):
1548
1567
  'method': 'privateMixGetV2MixPositionAllPosition', # or privateMixGetV2MixPositionHistoryPosition
1549
1568
  },
1550
1569
  'defaultTimeInForce': 'GTC', # 'GTC' = Good To Cancel(default), 'IOC' = Immediate Or Cancel
1570
+ # fiat currencies on deposit page
1571
+ 'fiatCurrencies': ['EUR', 'VND', 'PLN', 'CZK', 'HUF', 'DKK', 'AUD', 'CAD', 'NOK', 'SEK', 'CHF', 'MXN', 'COP', 'ARS', 'GBP', 'BRL', 'UAH', 'ZAR'],
1551
1572
  },
1552
1573
  'features': {
1553
1574
  'spot': {
@@ -1622,7 +1643,7 @@ class bitget(Exchange, ImplicitAPI):
1622
1643
  'symbolRequired': False,
1623
1644
  },
1624
1645
  'fetchOHLCV': {
1625
- 'limit': 1000, # variable timespans for recent endpoint, 200 for historical
1646
+ 'limit': 200, # variable timespans for recent endpoint, 200 for historical
1626
1647
  },
1627
1648
  },
1628
1649
  'forPerps': {
@@ -1697,11 +1718,12 @@ class bitget(Exchange, ImplicitAPI):
1697
1718
  defaultProductType = None
1698
1719
  if (subType is not None) and (market is None):
1699
1720
  # set default only if subType is defined and market is not defined, since there is also USDC productTypes which are also linear
1700
- sandboxMode = self.safe_bool(self.options, 'sandboxMode', False)
1701
- if sandboxMode:
1702
- defaultProductType = 'SUSDT-FUTURES' if (subType == 'linear') else 'SCOIN-FUTURES'
1703
- else:
1704
- defaultProductType = 'USDT-FUTURES' if (subType == 'linear') else 'COIN-FUTURES'
1721
+ # sandboxMode = self.safe_bool(self.options, 'sandboxMode', False)
1722
+ # if sandboxMode:
1723
+ # defaultProductType = 'SUSDT-FUTURES' if (subType == 'linear') else 'SCOIN-FUTURES'
1724
+ # else:
1725
+ defaultProductType = 'USDT-FUTURES' if (subType == 'linear') else 'COIN-FUTURES'
1726
+ # }
1705
1727
  productType = self.safe_string(params, 'productType', defaultProductType)
1706
1728
  if (productType is None) and (market is not None):
1707
1729
  settle = market['settle']
@@ -2005,99 +2027,85 @@ class bitget(Exchange, ImplicitAPI):
2005
2027
  """
2006
2028
  response = await self.publicSpotGetV2SpotPublicCoins(params)
2007
2029
  #
2008
- # {
2009
- # "code": "00000",
2010
- # "data": [
2011
- # {
2012
- # "chains": [
2013
- # {
2014
- # "browserUrl": "https://blockchair.com/bitcoin/transaction/",
2015
- # "chain": "BTC",
2016
- # "depositConfirm": "1",
2017
- # "extraWithdrawFee": "0",
2018
- # "minDepositAmount": "0.0001",
2019
- # "minWithdrawAmount": "0.005",
2020
- # "needTag": "false",
2021
- # "rechargeable": "true",
2022
- # "withdrawConfirm": "1",
2023
- # "withdrawFee": "0.0004",
2024
- # "withdrawable": "true"
2025
- # },
2026
- # ],
2027
- # "coin": "BTC",
2028
- # "coinId": "1",
2029
- # "transfer": "true""
2030
- # }
2031
- # ],
2032
- # "msg": "success",
2033
- # "requestTime": "1700120731773"
2034
- # }
2030
+ # {
2031
+ # "code": "00000",
2032
+ # "msg": "success",
2033
+ # "requestTime": "1746195617812",
2034
+ # "data": [
2035
+ # {
2036
+ # "coinId": "1456",
2037
+ # "coin": "NEIROETH",
2038
+ # "transfer": "false",
2039
+ # "chains": [
2040
+ # {
2041
+ # "chain": "ERC20",
2042
+ # "needTag": "false",
2043
+ # "withdrawable": "true",
2044
+ # "rechargeable": "true",
2045
+ # "withdrawFee": "44.91017965",
2046
+ # "extraWithdrawFee": "0",
2047
+ # "depositConfirm": "12",
2048
+ # "withdrawConfirm": "64",
2049
+ # "minDepositAmount": "0.06",
2050
+ # "minWithdrawAmount": "60",
2051
+ # "browserUrl": "https://etherscan.io/tx/",
2052
+ # "contractAddress": "0xee2a03aa6dacf51c18679c516ad5283d8e7c2637",
2053
+ # "withdrawStep": "0",
2054
+ # "withdrawMinScale": "8",
2055
+ # "congestion": "normal"
2056
+ # }
2057
+ # ],
2058
+ # "areaCoin": "no"
2059
+ # },
2060
+ # ...
2035
2061
  #
2036
2062
  result: dict = {}
2037
2063
  data = self.safe_value(response, 'data', [])
2064
+ fiatCurrencies = self.safe_list(self.options, 'fiatCurrencies', [])
2038
2065
  for i in range(0, len(data)):
2039
2066
  entry = data[i]
2040
2067
  id = self.safe_string(entry, 'coin') # we don't use 'coinId' has no use. it is 'coin' field that needs to be used in currency related endpoints(deposit, withdraw, etc..)
2041
2068
  code = self.safe_currency_code(id)
2042
2069
  chains = self.safe_value(entry, 'chains', [])
2043
2070
  networks: dict = {}
2044
- deposit = False
2045
- withdraw = False
2046
- minWithdrawString = None
2047
- minDepositString = None
2048
- minWithdrawFeeString = None
2049
2071
  for j in range(0, len(chains)):
2050
2072
  chain = chains[j]
2051
2073
  networkId = self.safe_string(chain, 'chain')
2052
2074
  network = self.network_id_to_code(networkId, code)
2053
2075
  if network is not None:
2054
2076
  network = network.upper()
2055
- withdrawEnabled = self.safe_string(chain, 'withdrawable')
2056
- canWithdraw = withdrawEnabled == 'true'
2057
- withdraw = canWithdraw if (canWithdraw) else withdraw
2058
- depositEnabled = self.safe_string(chain, 'rechargeable')
2059
- canDeposit = depositEnabled == 'true'
2060
- deposit = canDeposit if (canDeposit) else deposit
2061
- networkWithdrawFeeString = self.safe_string(chain, 'withdrawFee')
2062
- if networkWithdrawFeeString is not None:
2063
- minWithdrawFeeString = networkWithdrawFeeString if (minWithdrawFeeString is None) else Precise.string_min(networkWithdrawFeeString, minWithdrawFeeString)
2064
- networkMinWithdrawString = self.safe_string(chain, 'minWithdrawAmount')
2065
- if networkMinWithdrawString is not None:
2066
- minWithdrawString = networkMinWithdrawString if (minWithdrawString is None) else Precise.string_min(networkMinWithdrawString, minWithdrawString)
2067
- networkMinDepositString = self.safe_string(chain, 'minDepositAmount')
2068
- if networkMinDepositString is not None:
2069
- minDepositString = networkMinDepositString if (minDepositString is None) else Precise.string_min(networkMinDepositString, minDepositString)
2070
2077
  networks[network] = {
2071
2078
  'info': chain,
2072
2079
  'id': networkId,
2073
2080
  'network': network,
2074
2081
  'limits': {
2075
2082
  'withdraw': {
2076
- 'min': self.parse_number(networkMinWithdrawString),
2083
+ 'min': self.safe_number(chain, 'minWithdrawAmount'),
2077
2084
  'max': None,
2078
2085
  },
2079
2086
  'deposit': {
2080
- 'min': self.parse_number(networkMinDepositString),
2087
+ 'min': self.safe_number(chain, 'minDepositAmount'),
2081
2088
  'max': None,
2082
2089
  },
2083
2090
  },
2084
- 'active': canWithdraw and canDeposit,
2085
- 'withdraw': canWithdraw,
2086
- 'deposit': canDeposit,
2087
- 'fee': self.parse_number(networkWithdrawFeeString),
2088
- 'precision': None,
2091
+ 'active': None,
2092
+ 'withdraw': self.safe_string(chain, 'withdrawable') == 'true',
2093
+ 'deposit': self.safe_string(chain, 'rechargeable') == 'true',
2094
+ 'fee': self.safe_number(chain, 'withdrawFee'),
2095
+ 'precision': self.parse_number(self.parse_precision(self.safe_string(chain, 'withdrawMinScale'))),
2089
2096
  }
2090
- result[code] = {
2097
+ isFiat = self.in_array(code, fiatCurrencies)
2098
+ result[code] = self.safe_currency_structure({
2091
2099
  'info': entry,
2092
2100
  'id': id,
2093
2101
  'code': code,
2094
2102
  'networks': networks,
2095
- 'type': None,
2103
+ 'type': 'fiat' if isFiat else 'crypto',
2096
2104
  'name': None,
2097
- 'active': deposit and withdraw,
2098
- 'deposit': deposit,
2099
- 'withdraw': withdraw,
2100
- 'fee': self.parse_number(minWithdrawFeeString),
2105
+ 'active': None,
2106
+ 'deposit': None,
2107
+ 'withdraw': None,
2108
+ 'fee': None,
2101
2109
  'precision': None,
2102
2110
  'limits': {
2103
2111
  'amount': {
@@ -2105,16 +2113,16 @@ class bitget(Exchange, ImplicitAPI):
2105
2113
  'max': None,
2106
2114
  },
2107
2115
  'withdraw': {
2108
- 'min': self.parse_number(minWithdrawString),
2116
+ 'min': None,
2109
2117
  'max': None,
2110
2118
  },
2111
2119
  'deposit': {
2112
- 'min': self.parse_number(minDepositString),
2120
+ 'min': None,
2113
2121
  'max': None,
2114
2122
  },
2115
2123
  },
2116
2124
  'created': None,
2117
- }
2125
+ })
2118
2126
  return result
2119
2127
 
2120
2128
  async def fetch_market_leverage_tiers(self, symbol: str, params={}) -> List[LeverageTier]:
@@ -2733,7 +2741,7 @@ class bitget(Exchange, ImplicitAPI):
2733
2741
  close = self.safe_string(ticker, 'lastPr')
2734
2742
  timestamp = self.safe_integer_omit_zero(ticker, 'ts') # exchange bitget provided 0
2735
2743
  change = self.safe_string(ticker, 'change24h')
2736
- open24 = self.safe_string(ticker, 'open24')
2744
+ open24 = self.safe_string_2(ticker, 'open24', 'open24h')
2737
2745
  open = self.safe_string(ticker, 'open')
2738
2746
  symbol: str
2739
2747
  openValue: str
@@ -3416,29 +3424,31 @@ class bitget(Exchange, ImplicitAPI):
3416
3424
  market = self.market(symbol)
3417
3425
  marketType = 'spot' if market['spot'] else 'swap'
3418
3426
  timeframes = self.options['timeframes'][marketType]
3419
- msInDay = 86400000
3420
- duration = self.parse_timeframe(timeframe) * 1000
3421
3427
  request: dict = {
3422
3428
  'symbol': market['id'],
3423
3429
  'granularity': self.safe_string(timeframes, timeframe, timeframe),
3424
3430
  }
3431
+ msInDay = 86400000
3432
+ now = self.milliseconds()
3433
+ duration = self.parse_timeframe(timeframe) * 1000
3425
3434
  until = self.safe_integer(params, 'until')
3426
3435
  limitDefined = limit is not None
3427
3436
  sinceDefined = since is not None
3428
3437
  untilDefined = until is not None
3429
3438
  params = self.omit(params, ['until'])
3430
- response = None
3431
- now = self.milliseconds()
3432
3439
  # retrievable periods listed here:
3433
3440
  # - https://www.bitget.com/api-doc/spot/market/Get-Candle-Data#request-parameters
3434
3441
  # - https://www.bitget.com/api-doc/contract/market/Get-Candle-Data#description
3435
- ohlcOptions = self.safe_dict(self.options, 'fetchOHLCV', {})
3436
- retrievableDaysMap = self.safe_dict(ohlcOptions, 'maxDaysPerTimeframe', {})
3437
- maxRetrievableDaysForRecent = self.safe_integer(retrievableDaysMap, timeframe, 30) # default to safe minimum
3438
- endpointTsBoundary = now - (maxRetrievableDaysForRecent - 1) * msInDay
3442
+ key = 'spot' if market['spot'] else 'swap'
3443
+ ohlcOptions = self.safe_dict(self.options['fetchOHLCV'], key, {})
3444
+ maxLimitPerTimeframe = self.safe_dict(ohlcOptions, 'maxLimitPerTimeframe', {})
3445
+ maxLimitForThisTimeframe = self.safe_integer(maxLimitPerTimeframe, timeframe, limit)
3446
+ recentEndpointDaysMap = self.safe_dict(self.options['fetchOHLCV'], 'maxRecentDaysPerTimeframe', {})
3447
+ recentEndpointAvailableDays = self.safe_integer(recentEndpointDaysMap, timeframe)
3448
+ recentEndpointBoundaryTs = now - (recentEndpointAvailableDays - 1) * msInDay
3439
3449
  if limitDefined:
3440
3450
  limit = min(limit, maxLimitForRecentEndpoint)
3441
- request['limit'] = limit
3451
+ limit = min(limit, maxLimitForThisTimeframe)
3442
3452
  else:
3443
3453
  limit = defaultLimit
3444
3454
  limitMultipliedDuration = limit * duration
@@ -3458,26 +3468,33 @@ class bitget(Exchange, ImplicitAPI):
3458
3468
  if not sinceDefined:
3459
3469
  calculatedStartTime = calculatedEndTime - limitMultipliedDuration
3460
3470
  # we do not need to set "startTime" here
3461
- historicalEndpointNeeded = (calculatedStartTime is not None) and (calculatedStartTime <= endpointTsBoundary)
3462
- if historicalEndpointNeeded:
3471
+ # if historical endpoint is needed, we should re-set the variables
3472
+ historicalEndpointNeeded = False
3473
+ if (calculatedStartTime is not None and calculatedStartTime <= recentEndpointBoundaryTs) or useHistoryEndpoint:
3474
+ historicalEndpointNeeded = True
3463
3475
  # only for "historical-candles" - ensure we use correct max limit
3464
- if limitDefined:
3465
- request['limit'] = min(limit, maxLimitForHistoryEndpoint)
3476
+ limit = min(limit, maxLimitForHistoryEndpoint)
3477
+ limitMultipliedDuration = limit * duration
3478
+ calculatedStartTime = calculatedEndTime - limitMultipliedDuration
3479
+ request['startTime'] = calculatedStartTime
3480
+ # for contract, maximum 90 days allowed between start-end times
3481
+ if not market['spot']:
3482
+ maxDistanceDaysForContracts = 90
3483
+ # only correct if request is larger
3484
+ if calculatedEndTime - calculatedStartTime > maxDistanceDaysForContracts * msInDay:
3485
+ calculatedEndTime = self.sum(calculatedStartTime, maxDistanceDaysForContracts * msInDay)
3486
+ request['endTime'] = calculatedEndTime
3487
+ # we need to set limit to safely cover the period
3488
+ request['limit'] = limit
3466
3489
  # make request
3490
+ response = None
3467
3491
  if market['spot']:
3468
3492
  # checks if we need history endpoint
3469
- if historicalEndpointNeeded or useHistoryEndpoint:
3493
+ if historicalEndpointNeeded:
3470
3494
  response = await self.publicSpotGetV2SpotMarketHistoryCandles(self.extend(request, params))
3471
3495
  else:
3472
3496
  response = await self.publicSpotGetV2SpotMarketCandles(self.extend(request, params))
3473
3497
  else:
3474
- maxDistanceDaysForContracts = 90 # for contract, maximum 90 days allowed between start-end times
3475
- # only correct the request to fix 90 days if until was auto-calculated
3476
- if sinceDefined:
3477
- if not untilDefined:
3478
- request['endTime'] = min(calculatedEndTime, self.sum(since, maxDistanceDaysForContracts * msInDay))
3479
- elif calculatedEndTime - calculatedStartTime > maxDistanceDaysForContracts * msInDay:
3480
- raise BadRequest(self.id + ' fetchOHLCV() between start and end must be less than ' + str(maxDistanceDaysForContracts) + ' days')
3481
3498
  priceType = None
3482
3499
  priceType, params = self.handle_param_string(params, 'price')
3483
3500
  productType = None
@@ -3490,7 +3507,7 @@ class bitget(Exchange, ImplicitAPI):
3490
3507
  elif priceType == 'index':
3491
3508
  response = await self.publicMixGetV2MixMarketHistoryIndexCandles(extended)
3492
3509
  else:
3493
- if historicalEndpointNeeded or useHistoryEndpoint:
3510
+ if historicalEndpointNeeded:
3494
3511
  response = await self.publicMixGetV2MixMarketHistoryCandles(extended)
3495
3512
  else:
3496
3513
  response = await self.publicMixGetV2MixMarketCandles(extended)
@@ -6444,9 +6461,11 @@ class bitget(Exchange, ImplicitAPI):
6444
6461
  fetch the current funding rate
6445
6462
 
6446
6463
  https://www.bitget.com/api-doc/contract/market/Get-Current-Funding-Rate
6464
+ https://www.bitget.com/api-doc/contract/market/Get-Symbol-Next-Funding-Time
6447
6465
 
6448
6466
  :param str symbol: unified market symbol
6449
6467
  :param dict [params]: extra parameters specific to the exchange API endpoint
6468
+ :param str [params.method]: either(default) 'publicMixGetV2MixMarketCurrentFundRate' or 'publicMixGetV2MixMarketFundingTime'
6450
6469
  :returns dict: a `funding rate structure <https://docs.ccxt.com/#/?id=funding-rate-structure>`
6451
6470
  """
6452
6471
  await self.load_markets()
@@ -6459,21 +6478,45 @@ class bitget(Exchange, ImplicitAPI):
6459
6478
  'symbol': market['id'],
6460
6479
  'productType': productType,
6461
6480
  }
6462
- response = await self.publicMixGetV2MixMarketCurrentFundRate(self.extend(request, params))
6463
- #
6464
- # {
6465
- # "code": "00000",
6466
- # "msg": "success",
6467
- # "requestTime": 1700811542124,
6468
- # "data": [
6469
- # {
6470
- # "symbol": "BTCUSDT",
6471
- # "fundingRate": "0.000106"
6472
- # }
6473
- # ]
6474
- # }
6475
- #
6476
- data = self.safe_value(response, 'data', [])
6481
+ method = None
6482
+ method, params = self.handle_option_and_params(params, 'fetchFundingRate', 'method', 'publicMixGetV2MixMarketCurrentFundRate')
6483
+ response = None
6484
+ if method == 'publicMixGetV2MixMarketCurrentFundRate':
6485
+ response = await self.publicMixGetV2MixMarketCurrentFundRate(self.extend(request, params))
6486
+ #
6487
+ # {
6488
+ # "code": "00000",
6489
+ # "msg": "success",
6490
+ # "requestTime": 1745500709429,
6491
+ # "data": [
6492
+ # {
6493
+ # "symbol": "BTCUSDT",
6494
+ # "fundingRate": "-0.000013",
6495
+ # "fundingRateInterval": "8",
6496
+ # "nextUpdate": "1745510400000",
6497
+ # "minFundingRate": "-0.003",
6498
+ # "maxFundingRate": "0.003"
6499
+ # }
6500
+ # ]
6501
+ # }
6502
+ #
6503
+ elif method == 'publicMixGetV2MixMarketFundingTime':
6504
+ response = await self.publicMixGetV2MixMarketFundingTime(self.extend(request, params))
6505
+ #
6506
+ # {
6507
+ # "code": "00000",
6508
+ # "msg": "success",
6509
+ # "requestTime": 1745402092428,
6510
+ # "data": [
6511
+ # {
6512
+ # "symbol": "BTCUSDT",
6513
+ # "nextFundingTime": "1745424000000",
6514
+ # "ratePeriod": "8"
6515
+ # }
6516
+ # ]
6517
+ # }
6518
+ #
6519
+ data = self.safe_list(response, 'data', [])
6477
6520
  return self.parse_funding_rate(data[0], market)
6478
6521
 
6479
6522
  async def fetch_funding_rates(self, symbols: Strings = None, params={}) -> FundingRates:
@@ -6536,11 +6579,23 @@ class bitget(Exchange, ImplicitAPI):
6536
6579
 
6537
6580
  def parse_funding_rate(self, contract, market: Market = None) -> FundingRate:
6538
6581
  #
6539
- # fetchFundingRate
6582
+ # fetchFundingRate: publicMixGetV2MixMarketCurrentFundRate
6540
6583
  #
6541
6584
  # {
6542
6585
  # "symbol": "BTCUSDT",
6543
- # "fundingRate": "-0.000182"
6586
+ # "fundingRate": "-0.000013",
6587
+ # "fundingRateInterval": "8",
6588
+ # "nextUpdate": "1745510400000",
6589
+ # "minFundingRate": "-0.003",
6590
+ # "maxFundingRate": "0.003"
6591
+ # }
6592
+ #
6593
+ # fetchFundingRate: publicMixGetV2MixMarketFundingTime
6594
+ #
6595
+ # {
6596
+ # "symbol": "BTCUSDT",
6597
+ # "nextFundingTime": "1745424000000",
6598
+ # "ratePeriod": "8"
6544
6599
  # }
6545
6600
  #
6546
6601
  # fetchFundingInterval
@@ -6550,7 +6605,9 @@ class bitget(Exchange, ImplicitAPI):
6550
6605
  # "nextFundingTime": "1727942400000",
6551
6606
  # "ratePeriod": "8"
6552
6607
  # }
6608
+ #
6553
6609
  # fetchFundingRates
6610
+ #
6554
6611
  # {
6555
6612
  # "symbol": "BTCUSD",
6556
6613
  # "lastPr": "29904.5",
@@ -6576,10 +6633,11 @@ class bitget(Exchange, ImplicitAPI):
6576
6633
  # "open24h": "0",
6577
6634
  # "markPrice": "12345"
6578
6635
  # }
6636
+ #
6579
6637
  marketId = self.safe_string(contract, 'symbol')
6580
6638
  symbol = self.safe_symbol(marketId, market, None, 'swap')
6581
- fundingTimestamp = self.safe_integer(contract, 'nextFundingTime')
6582
- interval = self.safe_string(contract, 'ratePeriod')
6639
+ fundingTimestamp = self.safe_integer_2(contract, 'nextFundingTime', 'nextUpdate')
6640
+ interval = self.safe_string_2(contract, 'ratePeriod', 'fundingRateInterval')
6583
6641
  timestamp = self.safe_integer(contract, 'ts')
6584
6642
  markPrice = self.safe_number(contract, 'markPrice')
6585
6643
  indexPrice = self.safe_number(contract, 'indexPrice')
@@ -6,7 +6,7 @@
6
6
  from ccxt.async_support.base.exchange import Exchange
7
7
  from ccxt.abstract.bitmart import ImplicitAPI
8
8
  import hashlib
9
- from ccxt.base.types import Any, Balances, BorrowInterest, Currencies, Currency, DepositAddress, FundingHistory, Int, IsolatedBorrowRate, IsolatedBorrowRates, LedgerEntry, Market, Num, Order, OrderBook, OrderRequest, OrderSide, OrderType, Str, Strings, Ticker, Tickers, FundingRate, Trade, TradingFeeInterface, Transaction, MarketInterface, TransferEntry
9
+ from ccxt.base.types import Any, Balances, BorrowInterest, Currencies, Currency, DepositAddress, FundingHistory, Int, IsolatedBorrowRate, IsolatedBorrowRates, LedgerEntry, Market, Num, Order, OrderBook, OrderRequest, OrderSide, OrderType, Position, Str, Strings, Ticker, Tickers, FundingRate, Trade, TradingFeeInterface, Transaction, MarketInterface, TransferEntry
10
10
  from typing import List
11
11
  from ccxt.base.errors import ExchangeError
12
12
  from ccxt.base.errors import AuthenticationError
@@ -225,6 +225,7 @@ class bitmart(Exchange, ImplicitAPI):
225
225
  'contract/private/order': 1.2,
226
226
  'contract/private/order-history': 10,
227
227
  'contract/private/position': 10,
228
+ 'contract/private/position-v2': 10,
228
229
  'contract/private/get-open-orders': 1.2,
229
230
  'contract/private/current-plan-order': 1.2,
230
231
  'contract/private/trades': 10,
@@ -1214,6 +1215,7 @@ class bitmart(Exchange, ImplicitAPI):
1214
1215
  # {
1215
1216
  # "currency": "BTC",
1216
1217
  # "name": "Bitcoin",
1218
+ # "recharge_minsize": '0.00000001',
1217
1219
  # "contract_address": null,
1218
1220
  # "network": "BTC",
1219
1221
  # "withdraw_enabled": True,
@@ -1235,7 +1237,8 @@ class bitmart(Exchange, ImplicitAPI):
1235
1237
  fullId = self.safe_string(currency, 'currency')
1236
1238
  currencyId = fullId
1237
1239
  networkId = self.safe_string(currency, 'network')
1238
- if fullId.find('NFT') < 0:
1240
+ isNtf = (fullId.find('NFT') >= 0)
1241
+ if not isNtf:
1239
1242
  parts = fullId.split('-')
1240
1243
  currencyId = self.safe_string(parts, 0)
1241
1244
  second = self.safe_string(parts, 1)
@@ -1254,6 +1257,7 @@ class bitmart(Exchange, ImplicitAPI):
1254
1257
  'withdraw': None,
1255
1258
  'active': None,
1256
1259
  'networks': {},
1260
+ 'type': 'other' if isNtf else 'crypto',
1257
1261
  }
1258
1262
  networkCode = self.network_id_to_code(networkId)
1259
1263
  withdraw = self.safe_bool(currency, 'withdraw_enabled')
@@ -4654,11 +4658,12 @@ class bitmart(Exchange, ImplicitAPI):
4654
4658
  first = self.safe_dict(data, 0, {})
4655
4659
  return self.parse_position(first, market)
4656
4660
 
4657
- async def fetch_positions(self, symbols: Strings = None, params={}):
4661
+ async def fetch_positions(self, symbols: Strings = None, params={}) -> List[Position]:
4658
4662
  """
4659
4663
  fetch all open contract positions
4660
4664
 
4661
4665
  https://developer-pro.bitmart.com/en/futuresv2/#get-current-position-keyed
4666
+ https://developer-pro.bitmart.com/en/futuresv2/#get-current-position-v2-keyed
4662
4667
 
4663
4668
  :param str[]|None symbols: list of unified market symbols
4664
4669
  :param dict [params]: extra parameters specific to the exchange API endpoint
@@ -4675,7 +4680,7 @@ class bitmart(Exchange, ImplicitAPI):
4675
4680
  if symbolsLength == 1:
4676
4681
  # only supports symbols or sending one symbol
4677
4682
  request['symbol'] = market['id']
4678
- response = await self.privateGetContractPrivatePosition(self.extend(request, params))
4683
+ response = await self.privateGetContractPrivatePositionV2(self.extend(request, params))
4679
4684
  #
4680
4685
  # {
4681
4686
  # "code": 1000,
@@ -6,7 +6,7 @@
6
6
  from ccxt.async_support.base.exchange import Exchange
7
7
  from ccxt.abstract.bitmex import ImplicitAPI
8
8
  import hashlib
9
- from ccxt.base.types import Any, Balances, Currencies, Currency, DepositAddress, Int, LedgerEntry, Leverage, Leverages, Market, MarketType, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, FundingRate, FundingRates, Trade, Transaction
9
+ from ccxt.base.types import Any, Balances, Currencies, Currency, DepositAddress, Int, LedgerEntry, Leverage, Leverages, Market, MarketType, Num, Order, OrderBook, OrderSide, OrderType, Position, Str, Strings, Ticker, Tickers, FundingRate, FundingRates, Trade, Transaction
10
10
  from typing import List
11
11
  from ccxt.base.errors import ExchangeError
12
12
  from ccxt.base.errors import AuthenticationError
@@ -503,6 +503,7 @@ class bitmex(Exchange, ImplicitAPI):
503
503
  maxWithdrawal = self.parse_number(Precise.string_mul(maxWithdrawalString, precisionString))
504
504
  minDepositString = self.safe_string(currency, 'minDepositAmount')
505
505
  minDeposit = self.parse_number(Precise.string_mul(minDepositString, precisionString))
506
+ isCrypto = self.safe_string(currency, 'currencyType') == 'Crypto'
506
507
  result[code] = {
507
508
  'id': id,
508
509
  'code': code,
@@ -528,6 +529,7 @@ class bitmex(Exchange, ImplicitAPI):
528
529
  },
529
530
  },
530
531
  'networks': networks,
532
+ 'type': 'crypto' if isCrypto else 'other',
531
533
  }
532
534
  return result
533
535
 
@@ -729,7 +731,7 @@ class bitmex(Exchange, ImplicitAPI):
729
731
  isQuanto = self.safe_value(market, 'isQuanto') # self is True when BASE and SETTLE are different, i.e. AXS/XXX:BTC
730
732
  linear = (not isInverse and not isQuanto) if contract else None
731
733
  status = self.safe_string(market, 'state')
732
- active = status != 'Unlisted'
734
+ active = status == 'Open' # Open, Settled, Unlisted
733
735
  expiry = None
734
736
  expiryDatetime = None
735
737
  symbol = None
@@ -743,9 +745,9 @@ class bitmex(Exchange, ImplicitAPI):
743
745
  else:
744
746
  multiplierString = Precise.string_abs(self.safe_string(market, 'multiplier'))
745
747
  contractSize = self.parse_number(multiplierString)
746
- if future:
747
- expiryDatetime = self.safe_string(market, 'expiry')
748
- expiry = self.parse8601(expiryDatetime)
748
+ expiryDatetime = self.safe_string(market, 'expiry')
749
+ expiry = self.parse8601(expiryDatetime)
750
+ if expiry is not None:
749
751
  symbol = symbol + '-' + self.yymmdd(expiry)
750
752
  else:
751
753
  # for index/exotic markets, default to id
@@ -756,6 +758,11 @@ class bitmex(Exchange, ImplicitAPI):
756
758
  maxOrderQty = self.safe_number(market, 'maxOrderQty')
757
759
  initMargin = self.safe_string(market, 'initMargin', '1')
758
760
  maxLeverage = self.parse_number(Precise.string_div('1', initMargin))
761
+ # subtype should be None for spot markets
762
+ if spot:
763
+ isInverse = None
764
+ isQuanto = None
765
+ linear = None
759
766
  return {
760
767
  'id': id,
761
768
  'symbol': symbol,
@@ -805,7 +812,7 @@ class bitmex(Exchange, ImplicitAPI):
805
812
  'max': maxOrderQty if positionIsQuote else None,
806
813
  },
807
814
  },
808
- 'created': self.parse8601(self.safe_string(market, 'listing')),
815
+ 'created': None, # 'listing' field is buggy, e.g. 2200-02-01T00:00:00.000Z
809
816
  'info': market,
810
817
  }
811
818
 
@@ -2182,7 +2189,7 @@ class bitmex(Exchange, ImplicitAPI):
2182
2189
  'shortLeverage': self.safe_integer(leverage, 'leverage'),
2183
2190
  }
2184
2191
 
2185
- async def fetch_positions(self, symbols: Strings = None, params={}):
2192
+ async def fetch_positions(self, symbols: Strings = None, params={}) -> List[Position]:
2186
2193
  """
2187
2194
  fetch all open positions
2188
2195