ccxt 4.4.40__py2.py3-none-any.whl → 4.4.41__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
ccxt/__init__.py CHANGED
@@ -22,7 +22,7 @@
22
22
 
23
23
  # ----------------------------------------------------------------------------
24
24
 
25
- __version__ = '4.4.40'
25
+ __version__ = '4.4.41'
26
26
 
27
27
  # ----------------------------------------------------------------------------
28
28
 
ccxt/abstract/bitmart.py CHANGED
@@ -25,6 +25,7 @@ class ImplicitAPI:
25
25
  public_get_contract_public_depth = publicGetContractPublicDepth = Entry('contract/public/depth', 'public', 'GET', {'cost': 5})
26
26
  public_get_contract_public_open_interest = publicGetContractPublicOpenInterest = Entry('contract/public/open-interest', 'public', 'GET', {'cost': 30})
27
27
  public_get_contract_public_funding_rate = publicGetContractPublicFundingRate = Entry('contract/public/funding-rate', 'public', 'GET', {'cost': 30})
28
+ public_get_contract_public_funding_rate_history = publicGetContractPublicFundingRateHistory = Entry('contract/public/funding-rate-history', 'public', 'GET', {'cost': 30})
28
29
  public_get_contract_public_kline = publicGetContractPublicKline = Entry('contract/public/kline', 'public', 'GET', {'cost': 6})
29
30
  public_get_account_v1_currencies = publicGetAccountV1Currencies = Entry('account/v1/currencies', 'public', 'GET', {'cost': 30})
30
31
  private_get_account_sub_account_v1_transfer_list = privateGetAccountSubAccountV1TransferList = Entry('account/sub-account/v1/transfer-list', 'private', 'GET', {'cost': 7.5})
@@ -64,6 +65,7 @@ class ImplicitAPI:
64
65
  private_get_contract_private_position_risk = privateGetContractPrivatePositionRisk = Entry('contract/private/position-risk', 'private', 'GET', {'cost': 10})
65
66
  private_get_contract_private_affilate_rebate_list = privateGetContractPrivateAffilateRebateList = Entry('contract/private/affilate/rebate-list', 'private', 'GET', {'cost': 10})
66
67
  private_get_contract_private_affilate_trade_list = privateGetContractPrivateAffilateTradeList = Entry('contract/private/affilate/trade-list', 'private', 'GET', {'cost': 10})
68
+ private_get_contract_private_transaction_history = privateGetContractPrivateTransactionHistory = Entry('contract/private/transaction-history', 'private', 'GET', {'cost': 10})
67
69
  private_post_account_sub_account_main_v1_sub_to_main = privatePostAccountSubAccountMainV1SubToMain = Entry('account/sub-account/main/v1/sub-to-main', 'private', 'POST', {'cost': 30})
68
70
  private_post_account_sub_account_sub_v1_sub_to_main = privatePostAccountSubAccountSubV1SubToMain = Entry('account/sub-account/sub/v1/sub-to-main', 'private', 'POST', {'cost': 30})
69
71
  private_post_account_sub_account_main_v1_main_to_sub = privatePostAccountSubAccountMainV1MainToSub = Entry('account/sub-account/main/v1/main-to-sub', 'private', 'POST', {'cost': 30})
ccxt/abstract/okx.py CHANGED
@@ -68,6 +68,7 @@ class ImplicitAPI:
68
68
  public_get_tradingbot_public_rsi_back_testing = publicGetTradingBotPublicRsiBackTesting = Entry('tradingBot/public/rsi-back-testing', 'public', 'GET', {'cost': 1})
69
69
  public_get_asset_exchange_list = publicGetAssetExchangeList = Entry('asset/exchange-list', 'public', 'GET', {'cost': 1.6666666666666667})
70
70
  public_get_finance_staking_defi_eth_apy_history = publicGetFinanceStakingDefiEthApyHistory = Entry('finance/staking-defi/eth/apy-history', 'public', 'GET', {'cost': 1.6666666666666667})
71
+ public_get_finance_staking_defi_sol_apy_history = publicGetFinanceStakingDefiSolApyHistory = Entry('finance/staking-defi/sol/apy-history', 'public', 'GET', {'cost': 1.6666666666666667})
71
72
  public_get_finance_savings_lending_rate_summary = publicGetFinanceSavingsLendingRateSummary = Entry('finance/savings/lending-rate-summary', 'public', 'GET', {'cost': 1.6666666666666667})
72
73
  public_get_finance_savings_lending_rate_history = publicGetFinanceSavingsLendingRateHistory = Entry('finance/savings/lending-rate-history', 'public', 'GET', {'cost': 1.6666666666666667})
73
74
  public_get_finance_fixed_loan_lending_offers = publicGetFinanceFixedLoanLendingOffers = Entry('finance/fixed-loan/lending-offers', 'public', 'GET', {'cost': 3.3333333333333335})
@@ -191,6 +192,8 @@ class ImplicitAPI:
191
192
  private_get_finance_staking_defi_eth_balance = privateGetFinanceStakingDefiEthBalance = Entry('finance/staking-defi/eth/balance', 'private', 'GET', {'cost': 1.6666666666666667})
192
193
  private_get_finance_staking_defi_eth_purchase_redeem_history = privateGetFinanceStakingDefiEthPurchaseRedeemHistory = Entry('finance/staking-defi/eth/purchase-redeem-history', 'private', 'GET', {'cost': 1.6666666666666667})
193
194
  private_get_finance_staking_defi_eth_product_info = privateGetFinanceStakingDefiEthProductInfo = Entry('finance/staking-defi/eth/product-info', 'private', 'GET', {'cost': 3})
195
+ private_get_finance_staking_defi_sol_balance = privateGetFinanceStakingDefiSolBalance = Entry('finance/staking-defi/sol/balance', 'private', 'GET', {'cost': 1.6666666666666667})
196
+ private_get_finance_staking_defi_sol_purchase_redeem_history = privateGetFinanceStakingDefiSolPurchaseRedeemHistory = Entry('finance/staking-defi/sol/purchase-redeem-history', 'private', 'GET', {'cost': 1.6666666666666667})
194
197
  private_get_copytrading_current_subpositions = privateGetCopytradingCurrentSubpositions = Entry('copytrading/current-subpositions', 'private', 'GET', {'cost': 1})
195
198
  private_get_copytrading_subpositions_history = privateGetCopytradingSubpositionsHistory = Entry('copytrading/subpositions-history', 'private', 'GET', {'cost': 1})
196
199
  private_get_copytrading_instruments = privateGetCopytradingInstruments = Entry('copytrading/instruments', 'private', 'GET', {'cost': 4})
@@ -311,6 +314,8 @@ class ImplicitAPI:
311
314
  private_post_finance_staking_defi_cancel = privatePostFinanceStakingDefiCancel = Entry('finance/staking-defi/cancel', 'private', 'POST', {'cost': 3})
312
315
  private_post_finance_staking_defi_eth_purchase = privatePostFinanceStakingDefiEthPurchase = Entry('finance/staking-defi/eth/purchase', 'private', 'POST', {'cost': 5})
313
316
  private_post_finance_staking_defi_eth_redeem = privatePostFinanceStakingDefiEthRedeem = Entry('finance/staking-defi/eth/redeem', 'private', 'POST', {'cost': 5})
317
+ private_post_finance_staking_defi_sol_purchase = privatePostFinanceStakingDefiSolPurchase = Entry('finance/staking-defi/sol/purchase', 'private', 'POST', {'cost': 5})
318
+ private_post_finance_staking_defi_sol_redeem = privatePostFinanceStakingDefiSolRedeem = Entry('finance/staking-defi/sol/redeem', 'private', 'POST', {'cost': 5})
314
319
  private_post_copytrading_algo_order = privatePostCopytradingAlgoOrder = Entry('copytrading/algo-order', 'private', 'POST', {'cost': 1})
315
320
  private_post_copytrading_close_subposition = privatePostCopytradingCloseSubposition = Entry('copytrading/close-subposition', 'private', 'POST', {'cost': 1})
316
321
  private_post_copytrading_set_instruments = privatePostCopytradingSetInstruments = Entry('copytrading/set-instruments', 'private', 'POST', {'cost': 4})
@@ -4,7 +4,7 @@
4
4
 
5
5
  # -----------------------------------------------------------------------------
6
6
 
7
- __version__ = '4.4.40'
7
+ __version__ = '4.4.41'
8
8
 
9
9
  # -----------------------------------------------------------------------------
10
10
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  # -----------------------------------------------------------------------------
4
4
 
5
- __version__ = '4.4.40'
5
+ __version__ = '4.4.41'
6
6
 
7
7
  # -----------------------------------------------------------------------------
8
8
 
@@ -755,6 +755,9 @@ class Exchange(BaseExchange):
755
755
  async def fetch_open_interest(self, symbol: str, params={}):
756
756
  raise NotSupported(self.id + ' fetchOpenInterest() is not supported yet')
757
757
 
758
+ async def fetch_open_interests(self, symbols: Strings = None, params={}):
759
+ raise NotSupported(self.id + ' fetchOpenInterests() is not supported yet')
760
+
758
761
  async def sign_in(self, params={}):
759
762
  raise NotSupported(self.id + ' signIn() is not supported yet')
760
763
 
@@ -1809,7 +1812,7 @@ class Exchange(BaseExchange):
1809
1812
  else:
1810
1813
  raise NotSupported(self.id + ' fetchTransactions() is not supported yet')
1811
1814
 
1812
- async def fetch_paginated_call_dynamic(self, method: str, symbol: Str = None, since: Int = None, limit: Int = None, params={}, maxEntriesPerRequest: Int = None):
1815
+ async def fetch_paginated_call_dynamic(self, method: str, symbol: Str = None, since: Int = None, limit: Int = None, params={}, maxEntriesPerRequest: Int = None, removeRepeated=True):
1813
1816
  maxCalls = None
1814
1817
  maxCalls, params = self.handle_option_and_params(params, method, 'paginationCalls', 10)
1815
1818
  maxRetries = None
@@ -1817,6 +1820,8 @@ class Exchange(BaseExchange):
1817
1820
  paginationDirection = None
1818
1821
  paginationDirection, params = self.handle_option_and_params(params, method, 'paginationDirection', 'backward')
1819
1822
  paginationTimestamp = None
1823
+ removeRepeatedOption = removeRepeated
1824
+ removeRepeatedOption, params = self.handle_option_and_params(params, method, 'removeRepeated', removeRepeated)
1820
1825
  calls = 0
1821
1826
  result = []
1822
1827
  errors = 0
@@ -1870,7 +1875,9 @@ class Exchange(BaseExchange):
1870
1875
  errors += 1
1871
1876
  if errors > maxRetries:
1872
1877
  raise e
1873
- uniqueResults = self.remove_repeated_elements_from_array(result)
1878
+ uniqueResults = result
1879
+ if removeRepeatedOption:
1880
+ uniqueResults = self.remove_repeated_elements_from_array(result)
1874
1881
  key = 0 if (method == 'fetchOHLCV') else 'timestamp'
1875
1882
  return self.filter_by_since_limit(uniqueResults, since, limit, key)
1876
1883
 
@@ -5063,8 +5063,8 @@ class binance(Exchange, ImplicitAPI):
5063
5063
  if postOnly:
5064
5064
  uppercaseType = 'LIMIT_MAKER'
5065
5065
  request['type'] = uppercaseType
5066
- stopPrice = self.safe_number_2(params, 'stopPrice', 'triggerPrice')
5067
- if stopPrice is not None:
5066
+ triggerPrice = self.safe_number_2(params, 'stopPrice', 'triggerPrice')
5067
+ if triggerPrice is not None:
5068
5068
  if uppercaseType == 'MARKET':
5069
5069
  uppercaseType = 'STOP_LOSS'
5070
5070
  elif uppercaseType == 'LIMIT':
@@ -5072,7 +5072,7 @@ class binance(Exchange, ImplicitAPI):
5072
5072
  validOrderTypes = self.safe_list(market['info'], 'orderTypes')
5073
5073
  if not self.in_array(uppercaseType, validOrderTypes):
5074
5074
  if initialUppercaseType != uppercaseType:
5075
- raise InvalidOrder(self.id + ' stopPrice parameter is not allowed for ' + symbol + ' ' + type + ' orders')
5075
+ raise InvalidOrder(self.id + ' triggerPrice parameter is not allowed for ' + symbol + ' ' + type + ' orders')
5076
5076
  else:
5077
5077
  raise InvalidOrder(self.id + ' ' + type + ' is not a valid order type for the ' + symbol + ' market')
5078
5078
  if clientOrderId is None:
@@ -5086,7 +5086,7 @@ class binance(Exchange, ImplicitAPI):
5086
5086
  request['newOrderRespType'] = self.safe_value(self.options['newOrderRespType'], type, 'RESULT') # 'ACK' for order id, 'RESULT' for full order or 'FULL' for order with fills
5087
5087
  timeInForceIsRequired = False
5088
5088
  priceIsRequired = False
5089
- stopPriceIsRequired = False
5089
+ triggerPriceIsRequired = False
5090
5090
  quantityIsRequired = False
5091
5091
  if uppercaseType == 'MARKET':
5092
5092
  quoteOrderQty = self.safe_bool(self.options, 'quoteOrderQty', True)
@@ -5109,11 +5109,11 @@ class binance(Exchange, ImplicitAPI):
5109
5109
  timeInForceIsRequired = True
5110
5110
  quantityIsRequired = True
5111
5111
  elif (uppercaseType == 'STOP_LOSS') or (uppercaseType == 'TAKE_PROFIT'):
5112
- stopPriceIsRequired = True
5112
+ triggerPriceIsRequired = True
5113
5113
  quantityIsRequired = True
5114
5114
  elif (uppercaseType == 'STOP_LOSS_LIMIT') or (uppercaseType == 'TAKE_PROFIT_LIMIT'):
5115
5115
  quantityIsRequired = True
5116
- stopPriceIsRequired = True
5116
+ triggerPriceIsRequired = True
5117
5117
  priceIsRequired = True
5118
5118
  timeInForceIsRequired = True
5119
5119
  elif uppercaseType == 'LIMIT_MAKER':
@@ -5127,11 +5127,11 @@ class binance(Exchange, ImplicitAPI):
5127
5127
  request['price'] = self.price_to_precision(symbol, price)
5128
5128
  if timeInForceIsRequired and (self.safe_string(params, 'timeInForce') is None):
5129
5129
  request['timeInForce'] = self.options['defaultTimeInForce'] # 'GTC' = Good To Cancel(default), 'IOC' = Immediate Or Cancel
5130
- if stopPriceIsRequired:
5131
- if stopPrice is None:
5132
- raise InvalidOrder(self.id + ' editOrder() requires a stopPrice extra param for a ' + type + ' order')
5130
+ if triggerPriceIsRequired:
5131
+ if triggerPrice is None:
5132
+ raise InvalidOrder(self.id + ' editOrder() requires a triggerPrice extra param for a ' + type + ' order')
5133
5133
  else:
5134
- request['stopPrice'] = self.price_to_precision(symbol, stopPrice)
5134
+ request['stopPrice'] = self.price_to_precision(symbol, triggerPrice)
5135
5135
  request['cancelReplaceMode'] = 'STOP_ON_FAILURE' # If the cancel request fails, the new order placement will not be attempted.
5136
5136
  cancelId = self.safe_string_2(params, 'cancelNewClientOrderId', 'cancelOrigClientOrderId')
5137
5137
  if cancelId is None:
@@ -5776,7 +5776,7 @@ class binance(Exchange, ImplicitAPI):
5776
5776
  if type == 'limit_maker':
5777
5777
  type = 'limit'
5778
5778
  stopPriceString = self.safe_string(order, 'stopPrice')
5779
- stopPrice = self.parse_number(self.omit_zero(stopPriceString))
5779
+ triggerPrice = self.parse_number(self.omit_zero(stopPriceString))
5780
5780
  feeCost = self.safe_number(order, 'fee')
5781
5781
  fee = None
5782
5782
  if feeCost is not None:
@@ -5800,7 +5800,7 @@ class binance(Exchange, ImplicitAPI):
5800
5800
  'reduceOnly': self.safe_bool(order, 'reduceOnly'),
5801
5801
  'side': side,
5802
5802
  'price': price,
5803
- 'triggerPrice': stopPrice,
5803
+ 'triggerPrice': triggerPrice,
5804
5804
  'amount': amount,
5805
5805
  'cost': cost,
5806
5806
  'average': average,
@@ -6072,7 +6072,7 @@ class binance(Exchange, ImplicitAPI):
6072
6072
  validOrderTypes = self.safe_list(market['info'], 'orderTypes')
6073
6073
  if not self.in_array(uppercaseType, validOrderTypes):
6074
6074
  if initialUppercaseType != uppercaseType:
6075
- raise InvalidOrder(self.id + ' stopPrice parameter is not allowed for ' + symbol + ' ' + type + ' orders')
6075
+ raise InvalidOrder(self.id + ' triggerPrice parameter is not allowed for ' + symbol + ' ' + type + ' orders')
6076
6076
  else:
6077
6077
  raise InvalidOrder(self.id + ' ' + type + ' is not a valid order type for the ' + symbol + ' market')
6078
6078
  clientOrderIdRequest = 'newClientStrategyId' if isPortfolioMarginConditional else 'newClientOrderId'
@@ -6111,7 +6111,7 @@ class binance(Exchange, ImplicitAPI):
6111
6111
  closePosition = self.safe_bool(params, 'closePosition', False)
6112
6112
  timeInForceIsRequired = False
6113
6113
  priceIsRequired = False
6114
- stopPriceIsRequired = False
6114
+ triggerPriceIsRequired = False
6115
6115
  quantityIsRequired = False
6116
6116
  #
6117
6117
  # spot/margin
@@ -6157,13 +6157,13 @@ class binance(Exchange, ImplicitAPI):
6157
6157
  timeInForceIsRequired = True
6158
6158
  quantityIsRequired = True
6159
6159
  elif (uppercaseType == 'STOP_LOSS') or (uppercaseType == 'TAKE_PROFIT'):
6160
- stopPriceIsRequired = True
6160
+ triggerPriceIsRequired = True
6161
6161
  quantityIsRequired = True
6162
6162
  if market['linear'] or market['inverse']:
6163
6163
  priceIsRequired = True
6164
6164
  elif (uppercaseType == 'STOP_LOSS_LIMIT') or (uppercaseType == 'TAKE_PROFIT_LIMIT'):
6165
6165
  quantityIsRequired = True
6166
- stopPriceIsRequired = True
6166
+ triggerPriceIsRequired = True
6167
6167
  priceIsRequired = True
6168
6168
  timeInForceIsRequired = True
6169
6169
  elif uppercaseType == 'LIMIT_MAKER':
@@ -6171,12 +6171,12 @@ class binance(Exchange, ImplicitAPI):
6171
6171
  quantityIsRequired = True
6172
6172
  elif uppercaseType == 'STOP':
6173
6173
  quantityIsRequired = True
6174
- stopPriceIsRequired = True
6174
+ triggerPriceIsRequired = True
6175
6175
  priceIsRequired = True
6176
6176
  elif (uppercaseType == 'STOP_MARKET') or (uppercaseType == 'TAKE_PROFIT_MARKET'):
6177
6177
  if not closePosition:
6178
6178
  quantityIsRequired = True
6179
- stopPriceIsRequired = True
6179
+ triggerPriceIsRequired = True
6180
6180
  elif uppercaseType == 'TRAILING_STOP_MARKET':
6181
6181
  if not closePosition:
6182
6182
  quantityIsRequired = True
@@ -6202,14 +6202,14 @@ class binance(Exchange, ImplicitAPI):
6202
6202
  request['price'] = self.price_to_precision(symbol, price)
6203
6203
  else:
6204
6204
  request['price'] = self.parse_to_numeric(price) # some options don't have the precision available
6205
- if stopPriceIsRequired:
6205
+ if triggerPriceIsRequired:
6206
6206
  if market['contract']:
6207
6207
  if stopPrice is None:
6208
- raise InvalidOrder(self.id + ' createOrder() requires a stopPrice extra param for a ' + type + ' order')
6208
+ raise InvalidOrder(self.id + ' createOrder() requires a triggerPrice extra param for a ' + type + ' order')
6209
6209
  else:
6210
6210
  # check for delta price
6211
6211
  if trailingDelta is None and stopPrice is None and trailingPercent is None:
6212
- raise InvalidOrder(self.id + ' createOrder() requires a stopPrice, trailingDelta or trailingPercent param for a ' + type + ' order')
6212
+ raise InvalidOrder(self.id + ' createOrder() requires a triggerPrice, trailingDelta or trailingPercent param for a ' + type + ' order')
6213
6213
  if stopPrice is not None:
6214
6214
  request['stopPrice'] = self.price_to_precision(symbol, stopPrice)
6215
6215
  if timeInForceIsRequired and (self.safe_string(params, 'timeInForce') is None) and (self.safe_string(request, 'timeInForce') is None):
@@ -10888,7 +10888,7 @@ class binance(Exchange, ImplicitAPI):
10888
10888
  paginate = False
10889
10889
  paginate, params = self.handle_option_and_params(params, 'fetchLedger', 'paginate')
10890
10890
  if paginate:
10891
- return await self.fetch_paginated_call_dynamic('fetchLedger', code, since, limit, params)
10891
+ return await self.fetch_paginated_call_dynamic('fetchLedger', code, since, limit, params, None, False)
10892
10892
  type = None
10893
10893
  subType = None
10894
10894
  currency = None
@@ -11963,7 +11963,7 @@ class binance(Exchange, ImplicitAPI):
11963
11963
  # ...
11964
11964
  # ]
11965
11965
  #
11966
- return self.parse_open_interests(response, market, since, limit)
11966
+ return self.parse_open_interests_history(response, market, since, limit)
11967
11967
 
11968
11968
  async def fetch_open_interest(self, symbol: str, params={}):
11969
11969
  """
@@ -12026,7 +12026,7 @@ class binance(Exchange, ImplicitAPI):
12026
12026
  #
12027
12027
  if market['option']:
12028
12028
  symbol = market['symbol']
12029
- result = self.parse_open_interests(response, market)
12029
+ result = self.parse_open_interests_history(response, market)
12030
12030
  for i in range(0, len(result)):
12031
12031
  item = result[i]
12032
12032
  if item['symbol'] == symbol:
@@ -64,6 +64,7 @@ class bingx(Exchange, ImplicitAPI):
64
64
  'createTrailingAmountOrder': True,
65
65
  'createTrailingPercentOrder': True,
66
66
  'createTriggerOrder': True,
67
+ 'editOrder': True,
67
68
  'fetchBalance': True,
68
69
  'fetchCanceledOrders': True,
69
70
  'fetchClosedOrders': True,
@@ -86,6 +87,7 @@ class bingx(Exchange, ImplicitAPI):
86
87
  'fetchMarkPrice': True,
87
88
  'fetchMarkPrices': True,
88
89
  'fetchMyLiquidations': True,
90
+ 'fetchMyTrades': True,
89
91
  'fetchOHLCV': True,
90
92
  'fetchOpenInterest': True,
91
93
  'fetchOpenOrders': True,
@@ -1037,7 +1039,7 @@ class bingx(Exchange, ImplicitAPI):
1037
1039
  }
1038
1040
  request['interval'] = self.safe_string(self.timeframes, timeframe, timeframe)
1039
1041
  if since is not None:
1040
- request['startTime'] = since
1042
+ request['startTime'] = max(since - 1, 0)
1041
1043
  if limit is not None:
1042
1044
  request['limit'] = limit
1043
1045
  until = self.safe_integer_2(params, 'until', 'endTime')
@@ -3225,7 +3225,7 @@ class bitfinex(Exchange, ImplicitAPI):
3225
3225
  # ],
3226
3226
  # ]
3227
3227
  #
3228
- return self.parse_open_interests(response, market, since, limit)
3228
+ return self.parse_open_interests_history(response, market, since, limit)
3229
3229
 
3230
3230
  def parse_open_interest(self, interest, market: Market = None):
3231
3231
  #
@@ -1265,6 +1265,7 @@ class bitget(Exchange, ImplicitAPI):
1265
1265
  '41103': InvalidOrder, # {"code":"41103","msg":"param price scale error error","requestTime":1725635883561,"data":null}
1266
1266
  '41114': OnMaintenance, # {"code":"41114","msg":"The current trading pair is under maintenance, please refer to the official announcement for the opening time","requestTime":1679196062544,"data":null}
1267
1267
  '43011': InvalidOrder, # The parameter does not meet the specification executePrice <= 0
1268
+ '43001': OrderNotFound,
1268
1269
  '43012': InsufficientFunds, # {"code":"43012","msg":"Insufficient balance","requestTime":1711648951774,"data":null}
1269
1270
  '43025': InvalidOrder, # Plan order does not exist
1270
1271
  '43115': OnMaintenance, # {"code":"43115","msg":"The current trading pair is opening soon, please refer to the official announcement for the opening time","requestTime":1688907202434,"data":null}
@@ -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 Balances, BorrowInterest, Currencies, Currency, DepositAddress, Int, IsolatedBorrowRate, IsolatedBorrowRates, Market, Num, Order, OrderBook, OrderRequest, OrderSide, OrderType, Str, Strings, Ticker, Tickers, FundingRate, Trade, TradingFeeInterface, Transaction, TransferEntry
9
+ from ccxt.base.types import 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, TransferEntry
10
10
  from typing import List
11
11
  from ccxt.base.errors import ExchangeError
12
12
  from ccxt.base.errors import AuthenticationError
@@ -81,12 +81,13 @@ class bitmart(Exchange, ImplicitAPI):
81
81
  'fetchDeposits': True,
82
82
  'fetchDepositWithdrawFee': True,
83
83
  'fetchDepositWithdrawFees': False,
84
- 'fetchFundingHistory': None,
84
+ 'fetchFundingHistory': True,
85
85
  'fetchFundingRate': True,
86
86
  'fetchFundingRateHistory': False,
87
87
  'fetchFundingRates': False,
88
88
  'fetchIsolatedBorrowRate': True,
89
89
  'fetchIsolatedBorrowRates': True,
90
+ 'fetchLedger': True,
90
91
  'fetchLiquidations': False,
91
92
  'fetchMarginMode': False,
92
93
  'fetchMarkets': True,
@@ -173,6 +174,7 @@ class bitmart(Exchange, ImplicitAPI):
173
174
  'contract/public/depth': 5,
174
175
  'contract/public/open-interest': 30,
175
176
  'contract/public/funding-rate': 30,
177
+ 'contract/public/funding-rate-history': 30,
176
178
  'contract/public/kline': 6, # should be 5 but errors
177
179
  'account/v1/currencies': 30,
178
180
  },
@@ -223,6 +225,7 @@ class bitmart(Exchange, ImplicitAPI):
223
225
  'contract/private/position-risk': 10,
224
226
  'contract/private/affilate/rebate-list': 10,
225
227
  'contract/private/affilate/trade-list': 10,
228
+ 'contract/private/transaction-history': 10,
226
229
  },
227
230
  'post': {
228
231
  # sub-account endpoints
@@ -4375,6 +4378,62 @@ class bitmart(Exchange, ImplicitAPI):
4375
4378
  data = self.safe_dict(response, 'data', {})
4376
4379
  return self.parse_funding_rate(data, market)
4377
4380
 
4381
+ async def fetch_funding_rate_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
4382
+ """
4383
+ fetches historical funding rate prices
4384
+
4385
+ https://developer-pro.bitmart.com/en/futuresv2/#get-funding-rate-history
4386
+
4387
+ :param str symbol: unified symbol of the market to fetch the funding rate history for
4388
+ :param int [since]: timestamp in ms of the earliest funding rate to fetch
4389
+ :param int [limit]: the maximum amount of funding rate structures to fetch
4390
+ :param dict [params]: extra parameters specific to the exchange API endpoint
4391
+ :returns dict[]: a list of `funding rate structures <https://docs.ccxt.com/#/?id=funding-rate-history-structure>`
4392
+ """
4393
+ if symbol is None:
4394
+ raise ArgumentsRequired(self.id + ' fetchFundingRateHistory() requires a symbol argument')
4395
+ await self.load_markets()
4396
+ market = self.market(symbol)
4397
+ request: dict = {
4398
+ 'symbol': market['id'],
4399
+ }
4400
+ if limit is not None:
4401
+ request['limit'] = limit
4402
+ response = await self.publicGetContractPublicFundingRateHistory(self.extend(request, params))
4403
+ #
4404
+ # {
4405
+ # "code": 1000,
4406
+ # "message": "Ok",
4407
+ # "data": {
4408
+ # "list": [
4409
+ # {
4410
+ # "symbol": "BTCUSDT",
4411
+ # "funding_rate": "0.000091412174",
4412
+ # "funding_time": "1734336000000"
4413
+ # },
4414
+ # ]
4415
+ # },
4416
+ # "trace": "fg73d949fgfdf6a40c8fc7f5ae6738.54.345345345345"
4417
+ # }
4418
+ #
4419
+ data = self.safe_dict(response, 'data', {})
4420
+ result = self.safe_list(data, 'list', [])
4421
+ rates = []
4422
+ for i in range(0, len(result)):
4423
+ entry = result[i]
4424
+ marketId = self.safe_string(entry, 'symbol')
4425
+ symbolInner = self.safe_symbol(marketId, market, '-', 'swap')
4426
+ timestamp = self.safe_integer(entry, 'funding_time')
4427
+ rates.append({
4428
+ 'info': entry,
4429
+ 'symbol': symbolInner,
4430
+ 'fundingRate': self.safe_number(entry, 'funding_rate'),
4431
+ 'timestamp': timestamp,
4432
+ 'datetime': self.iso8601(timestamp),
4433
+ })
4434
+ sorted = self.sort_by(rates, 'timestamp')
4435
+ return self.filter_by_symbol_since_limit(sorted, market['symbol'], since, limit)
4436
+
4378
4437
  def parse_funding_rate(self, contract, market: Market = None) -> FundingRate:
4379
4438
  #
4380
4439
  # {
@@ -4788,6 +4847,188 @@ class bitmart(Exchange, ImplicitAPI):
4788
4847
  data = self.safe_dict(response, 'data', {})
4789
4848
  return self.parse_order(data, market)
4790
4849
 
4850
+ async def fetch_ledger(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[LedgerEntry]:
4851
+ """
4852
+ fetch the history of changes, actions done by the user or operations that altered the balance of the user
4853
+
4854
+ https://developer-pro.bitmart.com/en/futuresv2/#get-transaction-history-keyed
4855
+
4856
+ :param str [code]: unified currency code
4857
+ :param int [since]: timestamp in ms of the earliest ledger entry
4858
+ :param int [limit]: max number of ledger entries to return
4859
+ :param dict [params]: extra parameters specific to the exchange API endpoint
4860
+ :param int [params.until]: timestamp in ms of the latest ledger entry
4861
+ :returns dict[]: a list of `ledger structures <https://docs.ccxt.com/#/?id=ledger>`
4862
+ """
4863
+ await self.load_markets()
4864
+ currency = None
4865
+ if code is not None:
4866
+ currency = self.currency(code)
4867
+ request: dict = {}
4868
+ request, params = self.handle_until_option('end_time', request, params)
4869
+ transactionsRequest = self.fetch_transactions_request(0, None, since, limit, params)
4870
+ response = await self.privateGetContractPrivateTransactionHistory(transactionsRequest)
4871
+ #
4872
+ # {
4873
+ # "code": 1000,
4874
+ # "message": "Ok",
4875
+ # "data": [
4876
+ # {
4877
+ # "time": "1734422402121",
4878
+ # "type": "Funding Fee",
4879
+ # "amount": "-0.00008253",
4880
+ # "asset": "USDT",
4881
+ # "symbol": "LTCUSDT",
4882
+ # "tran_id": "1734422402121",
4883
+ # "flow_type": 3
4884
+ # },
4885
+ # ],
4886
+ # "trace": "4cd11f83c71egfhfgh842790f07241e.23.173442343427772866"
4887
+ # }
4888
+ #
4889
+ data = self.safe_list(response, 'data', [])
4890
+ return self.parse_ledger(data, currency, since, limit)
4891
+
4892
+ def parse_ledger_entry(self, item: dict, currency: Currency = None) -> LedgerEntry:
4893
+ #
4894
+ # {
4895
+ # "time": "1734422402121",
4896
+ # "type": "Funding Fee",
4897
+ # "amount": "-0.00008253",
4898
+ # "asset": "USDT",
4899
+ # "symbol": "LTCUSDT",
4900
+ # "tran_id": "1734422402121",
4901
+ # "flow_type": 3
4902
+ # }
4903
+ #
4904
+ amount = self.safe_string(item, 'amount')
4905
+ direction = None
4906
+ if Precise.string_le(amount, '0'):
4907
+ direction = 'out'
4908
+ amount = Precise.string_mul('-1', amount)
4909
+ else:
4910
+ direction = 'in'
4911
+ currencyId = self.safe_string(item, 'asset')
4912
+ timestamp = self.safe_integer(item, 'time')
4913
+ type = self.safe_string(item, 'type')
4914
+ return self.safe_ledger_entry({
4915
+ 'info': item,
4916
+ 'id': self.safe_string(item, 'tran_id'),
4917
+ 'direction': direction,
4918
+ 'account': None,
4919
+ 'referenceAccount': None,
4920
+ 'referenceId': self.safe_string(item, 'tradeId'),
4921
+ 'type': self.parse_ledger_entry_type(type),
4922
+ 'currency': self.safe_currency_code(currencyId, currency),
4923
+ 'amount': self.parse_number(amount),
4924
+ 'timestamp': timestamp,
4925
+ 'datetime': self.iso8601(timestamp),
4926
+ 'before': None,
4927
+ 'after': None,
4928
+ 'status': None,
4929
+ 'fee': None,
4930
+ }, currency)
4931
+
4932
+ def parse_ledger_entry_type(self, type):
4933
+ ledgerType: dict = {
4934
+ 'Commission Fee': 'fee',
4935
+ 'Funding Fee': 'fee',
4936
+ 'Realized PNL': 'trade',
4937
+ 'Transfer': 'transfer',
4938
+ 'Liquidation Clearance': 'settlement',
4939
+ }
4940
+ return self.safe_string(ledgerType, type, type)
4941
+
4942
+ def fetch_transactions_request(self, flowType: Int = None, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
4943
+ request: dict = {}
4944
+ if flowType is not None:
4945
+ request['flow_type'] = flowType
4946
+ market = None
4947
+ if symbol is not None:
4948
+ market = self.market(symbol)
4949
+ request['symbol'] = market['id']
4950
+ if since is not None:
4951
+ request['start_time'] = since
4952
+ if limit is not None:
4953
+ request['page_size'] = limit
4954
+ request, params = self.handle_until_option('end_time', request, params)
4955
+ return self.extend(request, params)
4956
+
4957
+ async def fetch_funding_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[FundingHistory]:
4958
+ """
4959
+ fetch the history of funding payments paid and received on self account
4960
+
4961
+ https://developer-pro.bitmart.com/en/futuresv2/#get-transaction-history-keyed
4962
+
4963
+ :param str [symbol]: unified market symbol
4964
+ :param int [since]: the starting timestamp in milliseconds
4965
+ :param int [limit]: the number of entries to return
4966
+ :param dict [params]: extra parameters specific to the exchange API endpoint
4967
+ :param int [params.until]: the latest time in ms to fetch funding history for
4968
+ :returns dict[]: a list of `funding history structures <https://docs.ccxt.com/#/?id=funding-history-structure>`
4969
+ """
4970
+ await self.load_markets()
4971
+ market = None
4972
+ if symbol is not None:
4973
+ market = self.market(symbol)
4974
+ request: dict = {}
4975
+ request, params = self.handle_until_option('end_time', request, params)
4976
+ transactionsRequest = self.fetch_transactions_request(3, symbol, since, limit, params)
4977
+ response = await self.privateGetContractPrivateTransactionHistory(transactionsRequest)
4978
+ #
4979
+ # {
4980
+ # "code": 1000,
4981
+ # "message": "Ok",
4982
+ # "data": [
4983
+ # {
4984
+ # "time": "1734422402121",
4985
+ # "type": "Funding Fee",
4986
+ # "amount": "-0.00008253",
4987
+ # "asset": "USDT",
4988
+ # "symbol": "LTCUSDT",
4989
+ # "tran_id": "1734422402121",
4990
+ # "flow_type": 3
4991
+ # },
4992
+ # ],
4993
+ # "trace": "4cd11f83c71egfhfgh842790f07241e.23.173442343427772866"
4994
+ # }
4995
+ #
4996
+ data = self.safe_list(response, 'data', [])
4997
+ return self.parse_funding_histories(data, market, since, limit)
4998
+
4999
+ def parse_funding_history(self, contract, market: Market = None):
5000
+ #
5001
+ # {
5002
+ # "time": "1734422402121",
5003
+ # "type": "Funding Fee",
5004
+ # "amount": "-0.00008253",
5005
+ # "asset": "USDT",
5006
+ # "symbol": "LTCUSDT",
5007
+ # "tran_id": "1734422402121",
5008
+ # "flow_type": 3
5009
+ # }
5010
+ #
5011
+ marketId = self.safe_string(contract, 'symbol')
5012
+ currencyId = self.safe_string(contract, 'asset')
5013
+ timestamp = self.safe_integer(contract, 'time')
5014
+ return {
5015
+ 'info': contract,
5016
+ 'symbol': self.safe_symbol(marketId, market, None, 'swap'),
5017
+ 'code': self.safe_currency_code(currencyId),
5018
+ 'timestamp': timestamp,
5019
+ 'datetime': self.iso8601(timestamp),
5020
+ 'id': self.safe_string(contract, 'tran_id'),
5021
+ 'amount': self.safe_number(contract, 'amount'),
5022
+ }
5023
+
5024
+ def parse_funding_histories(self, contracts, market=None, since: Int = None, limit: Int = None) -> List[FundingHistory]:
5025
+ result = []
5026
+ for i in range(0, len(contracts)):
5027
+ contract = contracts[i]
5028
+ result.append(self.parse_funding_history(contract, market))
5029
+ sorted = self.sort_by(result, 'timestamp')
5030
+ return self.filter_by_since_limit(sorted, since, limit)
5031
+
4791
5032
  def nonce(self):
4792
5033
  return self.milliseconds()
4793
5034