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
ccxt/bitget.py CHANGED
@@ -1392,28 +1392,44 @@ class bitget(Exchange, ImplicitAPI):
1392
1392
  'fillResponseFromRequest': True,
1393
1393
  },
1394
1394
  'fetchOHLCV': {
1395
- 'spot': {
1396
- 'method': 'publicSpotGetV2SpotMarketCandles', # publicSpotGetV2SpotMarketCandles or publicSpotGetV2SpotMarketHistoryCandles
1397
- },
1398
- 'swap': {
1399
- 'method': 'publicMixGetV2MixMarketCandles', # publicMixGetV2MixMarketCandles or publicMixGetV2MixMarketHistoryCandles or publicMixGetV2MixMarketHistoryIndexCandles or publicMixGetV2MixMarketHistoryMarkCandles
1400
- },
1401
- 'maxDaysPerTimeframe': {
1395
+ # ### Timeframe settings ###
1396
+ # after testing, the below values are real ones, because the values provided by API DOCS are wrong
1397
+ # so, start timestamp should be within these thresholds to be able to call "recent" candles endpoint
1398
+ 'maxRecentDaysPerTimeframe': {
1402
1399
  '1m': 30,
1403
1400
  '3m': 30,
1404
1401
  '5m': 30,
1405
- '10m': 30,
1406
- '15m': 52,
1407
- '30m': 62,
1408
- '1h': 83,
1409
- '2h': 120,
1402
+ '15m': 30,
1403
+ '30m': 30,
1404
+ '1h': 60,
1410
1405
  '4h': 240,
1411
1406
  '6h': 360,
1412
- '12h': 360,
1413
- '1d': 300,
1414
- '3d': 300,
1415
- '1w': 300,
1416
- '1M': 300,
1407
+ '12h': 720,
1408
+ '1d': 1440,
1409
+ '3d': 1440 * 3,
1410
+ '1w': 1440 * 7,
1411
+ '1M': 1440 * 30,
1412
+ },
1413
+ 'spot': {
1414
+ 'maxLimitPerTimeframe': {
1415
+ '1d': 300,
1416
+ '3d': 100,
1417
+ '1w': 100,
1418
+ '1M': 100,
1419
+ },
1420
+ 'method': 'publicSpotGetV2SpotMarketCandles', # publicSpotGetV2SpotMarketCandles or publicSpotGetV2SpotMarketHistoryCandles
1421
+ },
1422
+ 'swap': {
1423
+ 'maxLimitPerTimeframe': {
1424
+ '4h': 540,
1425
+ '6h': 360,
1426
+ '12h': 180,
1427
+ '1d': 90,
1428
+ '3d': 30,
1429
+ '1w': 13,
1430
+ '1M': 4,
1431
+ },
1432
+ 'method': 'publicMixGetV2MixMarketCandles', # publicMixGetV2MixMarketCandles or publicMixGetV2MixMarketHistoryCandles or publicMixGetV2MixMarketHistoryIndexCandles or publicMixGetV2MixMarketHistoryMarkCandles
1417
1433
  },
1418
1434
  },
1419
1435
  'fetchTrades': {
@@ -1424,6 +1440,9 @@ class bitget(Exchange, ImplicitAPI):
1424
1440
  'method': 'publicMixGetV2MixMarketFillsHistory', # or publicMixGetV2MixMarketFills
1425
1441
  },
1426
1442
  },
1443
+ 'fetchFundingRate': {
1444
+ 'method': 'publicMixGetV2MixMarketCurrentFundRate', # or publicMixGetV2MixMarketFundingTime
1445
+ },
1427
1446
  'accountsByType': {
1428
1447
  'spot': 'spot',
1429
1448
  'cross': 'crossed_margin',
@@ -1547,6 +1566,8 @@ class bitget(Exchange, ImplicitAPI):
1547
1566
  'method': 'privateMixGetV2MixPositionAllPosition', # or privateMixGetV2MixPositionHistoryPosition
1548
1567
  },
1549
1568
  'defaultTimeInForce': 'GTC', # 'GTC' = Good To Cancel(default), 'IOC' = Immediate Or Cancel
1569
+ # fiat currencies on deposit page
1570
+ 'fiatCurrencies': ['EUR', 'VND', 'PLN', 'CZK', 'HUF', 'DKK', 'AUD', 'CAD', 'NOK', 'SEK', 'CHF', 'MXN', 'COP', 'ARS', 'GBP', 'BRL', 'UAH', 'ZAR'],
1550
1571
  },
1551
1572
  'features': {
1552
1573
  'spot': {
@@ -1621,7 +1642,7 @@ class bitget(Exchange, ImplicitAPI):
1621
1642
  'symbolRequired': False,
1622
1643
  },
1623
1644
  'fetchOHLCV': {
1624
- 'limit': 1000, # variable timespans for recent endpoint, 200 for historical
1645
+ 'limit': 200, # variable timespans for recent endpoint, 200 for historical
1625
1646
  },
1626
1647
  },
1627
1648
  'forPerps': {
@@ -1696,11 +1717,12 @@ class bitget(Exchange, ImplicitAPI):
1696
1717
  defaultProductType = None
1697
1718
  if (subType is not None) and (market is None):
1698
1719
  # set default only if subType is defined and market is not defined, since there is also USDC productTypes which are also linear
1699
- sandboxMode = self.safe_bool(self.options, 'sandboxMode', False)
1700
- if sandboxMode:
1701
- defaultProductType = 'SUSDT-FUTURES' if (subType == 'linear') else 'SCOIN-FUTURES'
1702
- else:
1703
- defaultProductType = 'USDT-FUTURES' if (subType == 'linear') else 'COIN-FUTURES'
1720
+ # sandboxMode = self.safe_bool(self.options, 'sandboxMode', False)
1721
+ # if sandboxMode:
1722
+ # defaultProductType = 'SUSDT-FUTURES' if (subType == 'linear') else 'SCOIN-FUTURES'
1723
+ # else:
1724
+ defaultProductType = 'USDT-FUTURES' if (subType == 'linear') else 'COIN-FUTURES'
1725
+ # }
1704
1726
  productType = self.safe_string(params, 'productType', defaultProductType)
1705
1727
  if (productType is None) and (market is not None):
1706
1728
  settle = market['settle']
@@ -2004,99 +2026,85 @@ class bitget(Exchange, ImplicitAPI):
2004
2026
  """
2005
2027
  response = self.publicSpotGetV2SpotPublicCoins(params)
2006
2028
  #
2007
- # {
2008
- # "code": "00000",
2009
- # "data": [
2010
- # {
2011
- # "chains": [
2012
- # {
2013
- # "browserUrl": "https://blockchair.com/bitcoin/transaction/",
2014
- # "chain": "BTC",
2015
- # "depositConfirm": "1",
2016
- # "extraWithdrawFee": "0",
2017
- # "minDepositAmount": "0.0001",
2018
- # "minWithdrawAmount": "0.005",
2019
- # "needTag": "false",
2020
- # "rechargeable": "true",
2021
- # "withdrawConfirm": "1",
2022
- # "withdrawFee": "0.0004",
2023
- # "withdrawable": "true"
2024
- # },
2025
- # ],
2026
- # "coin": "BTC",
2027
- # "coinId": "1",
2028
- # "transfer": "true""
2029
- # }
2030
- # ],
2031
- # "msg": "success",
2032
- # "requestTime": "1700120731773"
2033
- # }
2029
+ # {
2030
+ # "code": "00000",
2031
+ # "msg": "success",
2032
+ # "requestTime": "1746195617812",
2033
+ # "data": [
2034
+ # {
2035
+ # "coinId": "1456",
2036
+ # "coin": "NEIROETH",
2037
+ # "transfer": "false",
2038
+ # "chains": [
2039
+ # {
2040
+ # "chain": "ERC20",
2041
+ # "needTag": "false",
2042
+ # "withdrawable": "true",
2043
+ # "rechargeable": "true",
2044
+ # "withdrawFee": "44.91017965",
2045
+ # "extraWithdrawFee": "0",
2046
+ # "depositConfirm": "12",
2047
+ # "withdrawConfirm": "64",
2048
+ # "minDepositAmount": "0.06",
2049
+ # "minWithdrawAmount": "60",
2050
+ # "browserUrl": "https://etherscan.io/tx/",
2051
+ # "contractAddress": "0xee2a03aa6dacf51c18679c516ad5283d8e7c2637",
2052
+ # "withdrawStep": "0",
2053
+ # "withdrawMinScale": "8",
2054
+ # "congestion": "normal"
2055
+ # }
2056
+ # ],
2057
+ # "areaCoin": "no"
2058
+ # },
2059
+ # ...
2034
2060
  #
2035
2061
  result: dict = {}
2036
2062
  data = self.safe_value(response, 'data', [])
2063
+ fiatCurrencies = self.safe_list(self.options, 'fiatCurrencies', [])
2037
2064
  for i in range(0, len(data)):
2038
2065
  entry = data[i]
2039
2066
  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..)
2040
2067
  code = self.safe_currency_code(id)
2041
2068
  chains = self.safe_value(entry, 'chains', [])
2042
2069
  networks: dict = {}
2043
- deposit = False
2044
- withdraw = False
2045
- minWithdrawString = None
2046
- minDepositString = None
2047
- minWithdrawFeeString = None
2048
2070
  for j in range(0, len(chains)):
2049
2071
  chain = chains[j]
2050
2072
  networkId = self.safe_string(chain, 'chain')
2051
2073
  network = self.network_id_to_code(networkId, code)
2052
2074
  if network is not None:
2053
2075
  network = network.upper()
2054
- withdrawEnabled = self.safe_string(chain, 'withdrawable')
2055
- canWithdraw = withdrawEnabled == 'true'
2056
- withdraw = canWithdraw if (canWithdraw) else withdraw
2057
- depositEnabled = self.safe_string(chain, 'rechargeable')
2058
- canDeposit = depositEnabled == 'true'
2059
- deposit = canDeposit if (canDeposit) else deposit
2060
- networkWithdrawFeeString = self.safe_string(chain, 'withdrawFee')
2061
- if networkWithdrawFeeString is not None:
2062
- minWithdrawFeeString = networkWithdrawFeeString if (minWithdrawFeeString is None) else Precise.string_min(networkWithdrawFeeString, minWithdrawFeeString)
2063
- networkMinWithdrawString = self.safe_string(chain, 'minWithdrawAmount')
2064
- if networkMinWithdrawString is not None:
2065
- minWithdrawString = networkMinWithdrawString if (minWithdrawString is None) else Precise.string_min(networkMinWithdrawString, minWithdrawString)
2066
- networkMinDepositString = self.safe_string(chain, 'minDepositAmount')
2067
- if networkMinDepositString is not None:
2068
- minDepositString = networkMinDepositString if (minDepositString is None) else Precise.string_min(networkMinDepositString, minDepositString)
2069
2076
  networks[network] = {
2070
2077
  'info': chain,
2071
2078
  'id': networkId,
2072
2079
  'network': network,
2073
2080
  'limits': {
2074
2081
  'withdraw': {
2075
- 'min': self.parse_number(networkMinWithdrawString),
2082
+ 'min': self.safe_number(chain, 'minWithdrawAmount'),
2076
2083
  'max': None,
2077
2084
  },
2078
2085
  'deposit': {
2079
- 'min': self.parse_number(networkMinDepositString),
2086
+ 'min': self.safe_number(chain, 'minDepositAmount'),
2080
2087
  'max': None,
2081
2088
  },
2082
2089
  },
2083
- 'active': canWithdraw and canDeposit,
2084
- 'withdraw': canWithdraw,
2085
- 'deposit': canDeposit,
2086
- 'fee': self.parse_number(networkWithdrawFeeString),
2087
- 'precision': None,
2090
+ 'active': None,
2091
+ 'withdraw': self.safe_string(chain, 'withdrawable') == 'true',
2092
+ 'deposit': self.safe_string(chain, 'rechargeable') == 'true',
2093
+ 'fee': self.safe_number(chain, 'withdrawFee'),
2094
+ 'precision': self.parse_number(self.parse_precision(self.safe_string(chain, 'withdrawMinScale'))),
2088
2095
  }
2089
- result[code] = {
2096
+ isFiat = self.in_array(code, fiatCurrencies)
2097
+ result[code] = self.safe_currency_structure({
2090
2098
  'info': entry,
2091
2099
  'id': id,
2092
2100
  'code': code,
2093
2101
  'networks': networks,
2094
- 'type': None,
2102
+ 'type': 'fiat' if isFiat else 'crypto',
2095
2103
  'name': None,
2096
- 'active': deposit and withdraw,
2097
- 'deposit': deposit,
2098
- 'withdraw': withdraw,
2099
- 'fee': self.parse_number(minWithdrawFeeString),
2104
+ 'active': None,
2105
+ 'deposit': None,
2106
+ 'withdraw': None,
2107
+ 'fee': None,
2100
2108
  'precision': None,
2101
2109
  'limits': {
2102
2110
  'amount': {
@@ -2104,16 +2112,16 @@ class bitget(Exchange, ImplicitAPI):
2104
2112
  'max': None,
2105
2113
  },
2106
2114
  'withdraw': {
2107
- 'min': self.parse_number(minWithdrawString),
2115
+ 'min': None,
2108
2116
  'max': None,
2109
2117
  },
2110
2118
  'deposit': {
2111
- 'min': self.parse_number(minDepositString),
2119
+ 'min': None,
2112
2120
  'max': None,
2113
2121
  },
2114
2122
  },
2115
2123
  'created': None,
2116
- }
2124
+ })
2117
2125
  return result
2118
2126
 
2119
2127
  def fetch_market_leverage_tiers(self, symbol: str, params={}) -> List[LeverageTier]:
@@ -2732,7 +2740,7 @@ class bitget(Exchange, ImplicitAPI):
2732
2740
  close = self.safe_string(ticker, 'lastPr')
2733
2741
  timestamp = self.safe_integer_omit_zero(ticker, 'ts') # exchange bitget provided 0
2734
2742
  change = self.safe_string(ticker, 'change24h')
2735
- open24 = self.safe_string(ticker, 'open24')
2743
+ open24 = self.safe_string_2(ticker, 'open24', 'open24h')
2736
2744
  open = self.safe_string(ticker, 'open')
2737
2745
  symbol: str
2738
2746
  openValue: str
@@ -3415,29 +3423,31 @@ class bitget(Exchange, ImplicitAPI):
3415
3423
  market = self.market(symbol)
3416
3424
  marketType = 'spot' if market['spot'] else 'swap'
3417
3425
  timeframes = self.options['timeframes'][marketType]
3418
- msInDay = 86400000
3419
- duration = self.parse_timeframe(timeframe) * 1000
3420
3426
  request: dict = {
3421
3427
  'symbol': market['id'],
3422
3428
  'granularity': self.safe_string(timeframes, timeframe, timeframe),
3423
3429
  }
3430
+ msInDay = 86400000
3431
+ now = self.milliseconds()
3432
+ duration = self.parse_timeframe(timeframe) * 1000
3424
3433
  until = self.safe_integer(params, 'until')
3425
3434
  limitDefined = limit is not None
3426
3435
  sinceDefined = since is not None
3427
3436
  untilDefined = until is not None
3428
3437
  params = self.omit(params, ['until'])
3429
- response = None
3430
- now = self.milliseconds()
3431
3438
  # retrievable periods listed here:
3432
3439
  # - https://www.bitget.com/api-doc/spot/market/Get-Candle-Data#request-parameters
3433
3440
  # - https://www.bitget.com/api-doc/contract/market/Get-Candle-Data#description
3434
- ohlcOptions = self.safe_dict(self.options, 'fetchOHLCV', {})
3435
- retrievableDaysMap = self.safe_dict(ohlcOptions, 'maxDaysPerTimeframe', {})
3436
- maxRetrievableDaysForRecent = self.safe_integer(retrievableDaysMap, timeframe, 30) # default to safe minimum
3437
- endpointTsBoundary = now - (maxRetrievableDaysForRecent - 1) * msInDay
3441
+ key = 'spot' if market['spot'] else 'swap'
3442
+ ohlcOptions = self.safe_dict(self.options['fetchOHLCV'], key, {})
3443
+ maxLimitPerTimeframe = self.safe_dict(ohlcOptions, 'maxLimitPerTimeframe', {})
3444
+ maxLimitForThisTimeframe = self.safe_integer(maxLimitPerTimeframe, timeframe, limit)
3445
+ recentEndpointDaysMap = self.safe_dict(self.options['fetchOHLCV'], 'maxRecentDaysPerTimeframe', {})
3446
+ recentEndpointAvailableDays = self.safe_integer(recentEndpointDaysMap, timeframe)
3447
+ recentEndpointBoundaryTs = now - (recentEndpointAvailableDays - 1) * msInDay
3438
3448
  if limitDefined:
3439
3449
  limit = min(limit, maxLimitForRecentEndpoint)
3440
- request['limit'] = limit
3450
+ limit = min(limit, maxLimitForThisTimeframe)
3441
3451
  else:
3442
3452
  limit = defaultLimit
3443
3453
  limitMultipliedDuration = limit * duration
@@ -3457,26 +3467,33 @@ class bitget(Exchange, ImplicitAPI):
3457
3467
  if not sinceDefined:
3458
3468
  calculatedStartTime = calculatedEndTime - limitMultipliedDuration
3459
3469
  # we do not need to set "startTime" here
3460
- historicalEndpointNeeded = (calculatedStartTime is not None) and (calculatedStartTime <= endpointTsBoundary)
3461
- if historicalEndpointNeeded:
3470
+ # if historical endpoint is needed, we should re-set the variables
3471
+ historicalEndpointNeeded = False
3472
+ if (calculatedStartTime is not None and calculatedStartTime <= recentEndpointBoundaryTs) or useHistoryEndpoint:
3473
+ historicalEndpointNeeded = True
3462
3474
  # only for "historical-candles" - ensure we use correct max limit
3463
- if limitDefined:
3464
- request['limit'] = min(limit, maxLimitForHistoryEndpoint)
3475
+ limit = min(limit, maxLimitForHistoryEndpoint)
3476
+ limitMultipliedDuration = limit * duration
3477
+ calculatedStartTime = calculatedEndTime - limitMultipliedDuration
3478
+ request['startTime'] = calculatedStartTime
3479
+ # for contract, maximum 90 days allowed between start-end times
3480
+ if not market['spot']:
3481
+ maxDistanceDaysForContracts = 90
3482
+ # only correct if request is larger
3483
+ if calculatedEndTime - calculatedStartTime > maxDistanceDaysForContracts * msInDay:
3484
+ calculatedEndTime = self.sum(calculatedStartTime, maxDistanceDaysForContracts * msInDay)
3485
+ request['endTime'] = calculatedEndTime
3486
+ # we need to set limit to safely cover the period
3487
+ request['limit'] = limit
3465
3488
  # make request
3489
+ response = None
3466
3490
  if market['spot']:
3467
3491
  # checks if we need history endpoint
3468
- if historicalEndpointNeeded or useHistoryEndpoint:
3492
+ if historicalEndpointNeeded:
3469
3493
  response = self.publicSpotGetV2SpotMarketHistoryCandles(self.extend(request, params))
3470
3494
  else:
3471
3495
  response = self.publicSpotGetV2SpotMarketCandles(self.extend(request, params))
3472
3496
  else:
3473
- maxDistanceDaysForContracts = 90 # for contract, maximum 90 days allowed between start-end times
3474
- # only correct the request to fix 90 days if until was auto-calculated
3475
- if sinceDefined:
3476
- if not untilDefined:
3477
- request['endTime'] = min(calculatedEndTime, self.sum(since, maxDistanceDaysForContracts * msInDay))
3478
- elif calculatedEndTime - calculatedStartTime > maxDistanceDaysForContracts * msInDay:
3479
- raise BadRequest(self.id + ' fetchOHLCV() between start and end must be less than ' + str(maxDistanceDaysForContracts) + ' days')
3480
3497
  priceType = None
3481
3498
  priceType, params = self.handle_param_string(params, 'price')
3482
3499
  productType = None
@@ -3489,7 +3506,7 @@ class bitget(Exchange, ImplicitAPI):
3489
3506
  elif priceType == 'index':
3490
3507
  response = self.publicMixGetV2MixMarketHistoryIndexCandles(extended)
3491
3508
  else:
3492
- if historicalEndpointNeeded or useHistoryEndpoint:
3509
+ if historicalEndpointNeeded:
3493
3510
  response = self.publicMixGetV2MixMarketHistoryCandles(extended)
3494
3511
  else:
3495
3512
  response = self.publicMixGetV2MixMarketCandles(extended)
@@ -6443,9 +6460,11 @@ class bitget(Exchange, ImplicitAPI):
6443
6460
  fetch the current funding rate
6444
6461
 
6445
6462
  https://www.bitget.com/api-doc/contract/market/Get-Current-Funding-Rate
6463
+ https://www.bitget.com/api-doc/contract/market/Get-Symbol-Next-Funding-Time
6446
6464
 
6447
6465
  :param str symbol: unified market symbol
6448
6466
  :param dict [params]: extra parameters specific to the exchange API endpoint
6467
+ :param str [params.method]: either(default) 'publicMixGetV2MixMarketCurrentFundRate' or 'publicMixGetV2MixMarketFundingTime'
6449
6468
  :returns dict: a `funding rate structure <https://docs.ccxt.com/#/?id=funding-rate-structure>`
6450
6469
  """
6451
6470
  self.load_markets()
@@ -6458,21 +6477,45 @@ class bitget(Exchange, ImplicitAPI):
6458
6477
  'symbol': market['id'],
6459
6478
  'productType': productType,
6460
6479
  }
6461
- response = self.publicMixGetV2MixMarketCurrentFundRate(self.extend(request, params))
6462
- #
6463
- # {
6464
- # "code": "00000",
6465
- # "msg": "success",
6466
- # "requestTime": 1700811542124,
6467
- # "data": [
6468
- # {
6469
- # "symbol": "BTCUSDT",
6470
- # "fundingRate": "0.000106"
6471
- # }
6472
- # ]
6473
- # }
6474
- #
6475
- data = self.safe_value(response, 'data', [])
6480
+ method = None
6481
+ method, params = self.handle_option_and_params(params, 'fetchFundingRate', 'method', 'publicMixGetV2MixMarketCurrentFundRate')
6482
+ response = None
6483
+ if method == 'publicMixGetV2MixMarketCurrentFundRate':
6484
+ response = self.publicMixGetV2MixMarketCurrentFundRate(self.extend(request, params))
6485
+ #
6486
+ # {
6487
+ # "code": "00000",
6488
+ # "msg": "success",
6489
+ # "requestTime": 1745500709429,
6490
+ # "data": [
6491
+ # {
6492
+ # "symbol": "BTCUSDT",
6493
+ # "fundingRate": "-0.000013",
6494
+ # "fundingRateInterval": "8",
6495
+ # "nextUpdate": "1745510400000",
6496
+ # "minFundingRate": "-0.003",
6497
+ # "maxFundingRate": "0.003"
6498
+ # }
6499
+ # ]
6500
+ # }
6501
+ #
6502
+ elif method == 'publicMixGetV2MixMarketFundingTime':
6503
+ response = self.publicMixGetV2MixMarketFundingTime(self.extend(request, params))
6504
+ #
6505
+ # {
6506
+ # "code": "00000",
6507
+ # "msg": "success",
6508
+ # "requestTime": 1745402092428,
6509
+ # "data": [
6510
+ # {
6511
+ # "symbol": "BTCUSDT",
6512
+ # "nextFundingTime": "1745424000000",
6513
+ # "ratePeriod": "8"
6514
+ # }
6515
+ # ]
6516
+ # }
6517
+ #
6518
+ data = self.safe_list(response, 'data', [])
6476
6519
  return self.parse_funding_rate(data[0], market)
6477
6520
 
6478
6521
  def fetch_funding_rates(self, symbols: Strings = None, params={}) -> FundingRates:
@@ -6535,11 +6578,23 @@ class bitget(Exchange, ImplicitAPI):
6535
6578
 
6536
6579
  def parse_funding_rate(self, contract, market: Market = None) -> FundingRate:
6537
6580
  #
6538
- # fetchFundingRate
6581
+ # fetchFundingRate: publicMixGetV2MixMarketCurrentFundRate
6539
6582
  #
6540
6583
  # {
6541
6584
  # "symbol": "BTCUSDT",
6542
- # "fundingRate": "-0.000182"
6585
+ # "fundingRate": "-0.000013",
6586
+ # "fundingRateInterval": "8",
6587
+ # "nextUpdate": "1745510400000",
6588
+ # "minFundingRate": "-0.003",
6589
+ # "maxFundingRate": "0.003"
6590
+ # }
6591
+ #
6592
+ # fetchFundingRate: publicMixGetV2MixMarketFundingTime
6593
+ #
6594
+ # {
6595
+ # "symbol": "BTCUSDT",
6596
+ # "nextFundingTime": "1745424000000",
6597
+ # "ratePeriod": "8"
6543
6598
  # }
6544
6599
  #
6545
6600
  # fetchFundingInterval
@@ -6549,7 +6604,9 @@ class bitget(Exchange, ImplicitAPI):
6549
6604
  # "nextFundingTime": "1727942400000",
6550
6605
  # "ratePeriod": "8"
6551
6606
  # }
6607
+ #
6552
6608
  # fetchFundingRates
6609
+ #
6553
6610
  # {
6554
6611
  # "symbol": "BTCUSD",
6555
6612
  # "lastPr": "29904.5",
@@ -6575,10 +6632,11 @@ class bitget(Exchange, ImplicitAPI):
6575
6632
  # "open24h": "0",
6576
6633
  # "markPrice": "12345"
6577
6634
  # }
6635
+ #
6578
6636
  marketId = self.safe_string(contract, 'symbol')
6579
6637
  symbol = self.safe_symbol(marketId, market, None, 'swap')
6580
- fundingTimestamp = self.safe_integer(contract, 'nextFundingTime')
6581
- interval = self.safe_string(contract, 'ratePeriod')
6638
+ fundingTimestamp = self.safe_integer_2(contract, 'nextFundingTime', 'nextUpdate')
6639
+ interval = self.safe_string_2(contract, 'ratePeriod', 'fundingRateInterval')
6582
6640
  timestamp = self.safe_integer(contract, 'ts')
6583
6641
  markPrice = self.safe_number(contract, 'markPrice')
6584
6642
  indexPrice = self.safe_number(contract, 'indexPrice')
ccxt/bitmart.py CHANGED
@@ -6,7 +6,7 @@
6
6
  from ccxt.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
- def fetch_positions(self, symbols: Strings = None, params={}):
4661
+ 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 = self.privateGetContractPrivatePosition(self.extend(request, params))
4683
+ response = self.privateGetContractPrivatePositionV2(self.extend(request, params))
4679
4684
  #
4680
4685
  # {
4681
4686
  # "code": 1000,
ccxt/bitmex.py CHANGED
@@ -6,7 +6,7 @@
6
6
  from ccxt.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
- def fetch_positions(self, symbols: Strings = None, params={}):
2192
+ def fetch_positions(self, symbols: Strings = None, params={}) -> List[Position]:
2186
2193
  """
2187
2194
  fetch all open positions
2188
2195