ccxt 4.4.19__py2.py3-none-any.whl → 4.4.21__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.19'
25
+ __version__ = '4.4.21'
26
26
 
27
27
  # ----------------------------------------------------------------------------
28
28
 
ccxt/abstract/bybit.py CHANGED
@@ -163,6 +163,7 @@ class ImplicitAPI:
163
163
  private_get_v5_user_del_submember = privateGetV5UserDelSubmember = Entry('v5/user/del-submember', 'private', 'GET', {'cost': 5})
164
164
  private_get_v5_user_submembers = privateGetV5UserSubmembers = Entry('v5/user/submembers', 'private', 'GET', {'cost': 5})
165
165
  private_get_v5_spot_lever_token_order_record = privateGetV5SpotLeverTokenOrderRecord = Entry('v5/spot-lever-token/order-record', 'private', 'GET', {'cost': 1})
166
+ private_get_v5_spot_margin_trade_interest_rate_history = privateGetV5SpotMarginTradeInterestRateHistory = Entry('v5/spot-margin-trade/interest-rate-history', 'private', 'GET', {'cost': 5})
166
167
  private_get_v5_spot_margin_trade_state = privateGetV5SpotMarginTradeState = Entry('v5/spot-margin-trade/state', 'private', 'GET', {'cost': 5})
167
168
  private_get_v5_spot_cross_margin_trade_loan_info = privateGetV5SpotCrossMarginTradeLoanInfo = Entry('v5/spot-cross-margin-trade/loan-info', 'private', 'GET', {'cost': 1})
168
169
  private_get_v5_spot_cross_margin_trade_account = privateGetV5SpotCrossMarginTradeAccount = Entry('v5/spot-cross-margin-trade/account', 'private', 'GET', {'cost': 1})
ccxt/abstract/kucoin.py CHANGED
@@ -20,6 +20,7 @@ class ImplicitAPI:
20
20
  public_get_mark_price_symbol_current = publicGetMarkPriceSymbolCurrent = Entry('mark-price/{symbol}/current', 'public', 'GET', {'cost': 3})
21
21
  public_get_mark_price_all_symbols = publicGetMarkPriceAllSymbols = Entry('mark-price/all-symbols', 'public', 'GET', {'cost': 3})
22
22
  public_get_margin_config = publicGetMarginConfig = Entry('margin/config', 'public', 'GET', {'cost': 25})
23
+ public_get_announcements = publicGetAnnouncements = Entry('announcements', 'public', 'GET', {'cost': 20})
23
24
  public_post_bullet_public = publicPostBulletPublic = Entry('bullet-public', 'public', 'POST', {'cost': 15})
24
25
  private_get_user_info = privateGetUserInfo = Entry('user-info', 'private', 'GET', {'cost': 30})
25
26
  private_get_accounts = privateGetAccounts = Entry('accounts', 'private', 'GET', {'cost': 7.5})
@@ -20,6 +20,7 @@ class ImplicitAPI:
20
20
  public_get_mark_price_symbol_current = publicGetMarkPriceSymbolCurrent = Entry('mark-price/{symbol}/current', 'public', 'GET', {'cost': 3})
21
21
  public_get_mark_price_all_symbols = publicGetMarkPriceAllSymbols = Entry('mark-price/all-symbols', 'public', 'GET', {'cost': 3})
22
22
  public_get_margin_config = publicGetMarginConfig = Entry('margin/config', 'public', 'GET', {'cost': 25})
23
+ public_get_announcements = publicGetAnnouncements = Entry('announcements', 'public', 'GET', {'cost': 20})
23
24
  public_post_bullet_public = publicPostBulletPublic = Entry('bullet-public', 'public', 'POST', {'cost': 15})
24
25
  private_get_user_info = privateGetUserInfo = Entry('user-info', 'private', 'GET', {'cost': 30})
25
26
  private_get_accounts = privateGetAccounts = Entry('accounts', 'private', 'GET', {'cost': 7.5})
ccxt/abstract/okx.py CHANGED
@@ -153,6 +153,9 @@ class ImplicitAPI:
153
153
  private_get_account_fixed_loan_borrowing_limit = privateGetAccountFixedLoanBorrowingLimit = Entry('account/fixed-loan/borrowing-limit', 'private', 'GET', {'cost': 4})
154
154
  private_get_account_fixed_loan_borrowing_quote = privateGetAccountFixedLoanBorrowingQuote = Entry('account/fixed-loan/borrowing-quote', 'private', 'GET', {'cost': 5})
155
155
  private_get_account_fixed_loan_borrowing_orders_list = privateGetAccountFixedLoanBorrowingOrdersList = Entry('account/fixed-loan/borrowing-orders-list', 'private', 'GET', {'cost': 5})
156
+ private_get_account_spot_manual_borrow_repay = privateGetAccountSpotManualBorrowRepay = Entry('account/spot-manual-borrow-repay', 'private', 'GET', {'cost': 10})
157
+ private_get_account_set_auto_repay = privateGetAccountSetAutoRepay = Entry('account/set-auto-repay', 'private', 'GET', {'cost': 4})
158
+ private_get_account_spot_borrow_repay_history = privateGetAccountSpotBorrowRepayHistory = Entry('account/spot-borrow-repay-history', 'private', 'GET', {'cost': 4})
156
159
  private_get_users_subaccount_list = privateGetUsersSubaccountList = Entry('users/subaccount/list', 'private', 'GET', {'cost': 10})
157
160
  private_get_account_subaccount_balances = privateGetAccountSubaccountBalances = Entry('account/subaccount/balances', 'private', 'GET', {'cost': 3.3333333333333335})
158
161
  private_get_asset_subaccount_balances = privateGetAssetSubaccountBalances = Entry('asset/subaccount/balances', 'private', 'GET', {'cost': 3.3333333333333335})
@@ -4,7 +4,7 @@
4
4
 
5
5
  # -----------------------------------------------------------------------------
6
6
 
7
- __version__ = '4.4.19'
7
+ __version__ = '4.4.21'
8
8
 
9
9
  # -----------------------------------------------------------------------------
10
10
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  # -----------------------------------------------------------------------------
4
4
 
5
- __version__ = '4.4.19'
5
+ __version__ = '4.4.21'
6
6
 
7
7
  # -----------------------------------------------------------------------------
8
8
 
@@ -1908,6 +1908,8 @@ class Exchange(BaseExchange):
1908
1908
  i = 0
1909
1909
  errors = 0
1910
1910
  result = []
1911
+ timeframe = self.safe_string(params, 'timeframe')
1912
+ params = self.omit(params, 'timeframe') # reading the timeframe from the method arguments to avoid changing the signature
1911
1913
  while(i < maxCalls):
1912
1914
  try:
1913
1915
  if cursorValue is not None:
@@ -1919,6 +1921,8 @@ class Exchange(BaseExchange):
1919
1921
  response = await getattr(self, method)(params)
1920
1922
  elif method == 'getLeverageTiersPaginated' or method == 'fetchPositions':
1921
1923
  response = await getattr(self, method)(symbol, params)
1924
+ elif method == 'fetchOpenInterestHistory':
1925
+ response = await getattr(self, method)(symbol, timeframe, since, maxEntriesPerRequest, params)
1922
1926
  else:
1923
1927
  response = await getattr(self, method)(symbol, since, maxEntriesPerRequest, params)
1924
1928
  errors = 0
@@ -11138,15 +11138,6 @@ class binance(Exchange, ImplicitAPI):
11138
11138
  #
11139
11139
  return self.parse_borrow_rate_history(response, code, since, limit)
11140
11140
 
11141
- def parse_borrow_rate_history(self, response, code, since, limit):
11142
- result = []
11143
- for i in range(0, len(response)):
11144
- item = response[i]
11145
- borrowRate = self.parse_borrow_rate(item)
11146
- result.append(borrowRate)
11147
- sorted = self.sort_by(result, 'timestamp')
11148
- return self.filter_by_currency_since_limit(sorted, code, since, limit)
11149
-
11150
11141
  def parse_borrow_rate(self, info, currency: Currency = None):
11151
11142
  #
11152
11143
  # {
@@ -4981,7 +4981,7 @@ class bingx(Exchange, ImplicitAPI):
4981
4981
  async def withdraw(self, code: str, amount: float, address: str, tag=None, params={}):
4982
4982
  """
4983
4983
  make a withdrawal
4984
- :see: https://bingx-api.github.io/docs/#/common/account-api.html#Withdraw
4984
+ :see: https://bingx-api.github.io/docs/#/en-us/spot/wallet-api.html#Withdraw
4985
4985
  :param str code: unified currency code
4986
4986
  :param float amount: the amount to withdraw
4987
4987
  :param str address: the address to withdraw to
@@ -4990,6 +4990,8 @@ class bingx(Exchange, ImplicitAPI):
4990
4990
  :param int [params.walletType]: 1 fund account, 2 standard account, 3 perpetual account
4991
4991
  :returns dict: a `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
4992
4992
  """
4993
+ tag, params = self.handle_withdraw_tag_and_params(tag, params)
4994
+ self.check_address(address)
4993
4995
  await self.load_markets()
4994
4996
  currency = self.currency(code)
4995
4997
  walletType = self.safe_integer(params, 'walletType')
@@ -5006,6 +5008,8 @@ class bingx(Exchange, ImplicitAPI):
5006
5008
  network = self.safe_string_upper(params, 'network')
5007
5009
  if network is not None:
5008
5010
  request['network'] = self.network_code_to_id(network)
5011
+ if tag is not None:
5012
+ request['addressTag'] = tag
5009
5013
  params = self.omit(params, ['walletType', 'network'])
5010
5014
  response = await self.walletsV1PrivatePostCapitalWithdrawApply(self.extend(request, params))
5011
5015
  data = self.safe_value(response, 'data')
@@ -385,6 +385,7 @@ class bybit(Exchange, ImplicitAPI):
385
385
  # spot leverage token
386
386
  'v5/spot-lever-token/order-record': 1, # 50/s => cost = 50 / 50 = 1
387
387
  # spot margin trade
388
+ 'v5/spot-margin-trade/interest-rate-history': 5,
388
389
  'v5/spot-margin-trade/state': 5,
389
390
  'v5/spot-cross-margin-trade/loan-info': 1, # 50/s => cost = 50 / 50 = 1
390
391
  'v5/spot-cross-margin-trade/account': 1, # 50/s => cost = 50 / 50 = 1
@@ -6703,7 +6704,8 @@ class bybit(Exchange, ImplicitAPI):
6703
6704
  paginate = self.safe_bool(params, 'paginate')
6704
6705
  if paginate:
6705
6706
  params = self.omit(params, 'paginate')
6706
- return await self.fetch_paginated_call_deterministic('fetchOpenInterestHistory', symbol, since, limit, timeframe, params, 500)
6707
+ params['timeframe'] = timeframe
6708
+ return await self.fetch_paginated_call_cursor('fetchOpenInterestHistory', symbol, since, limit, params, 'nextPageCursor', 'cursor', None, 200)
6707
6709
  market = self.market(symbol)
6708
6710
  if market['spot'] or market['option']:
6709
6711
  raise BadRequest(self.id + ' fetchOpenInterestHistory() symbol does not support market ' + symbol)
@@ -6775,12 +6777,22 @@ class bybit(Exchange, ImplicitAPI):
6775
6777
  # "timestamp": 1666734490778
6776
6778
  # }
6777
6779
  #
6780
+ # fetchBorrowRateHistory
6781
+ # {
6782
+ # "timestamp": 1721469600000,
6783
+ # "currency": "USDC",
6784
+ # "hourlyBorrowRate": "0.000014621596",
6785
+ # "vipLevel": "No VIP"
6786
+ # }
6787
+ #
6778
6788
  timestamp = self.safe_integer(info, 'timestamp')
6779
- currencyId = self.safe_string(info, 'coin')
6789
+ currencyId = self.safe_string_2(info, 'coin', 'currency')
6790
+ hourlyBorrowRate = self.safe_number(info, 'hourlyBorrowRate')
6791
+ period = 3600000 if (hourlyBorrowRate is not None) else 86400000 # 1h or 1d
6780
6792
  return {
6781
6793
  'currency': self.safe_currency_code(currencyId, currency),
6782
- 'rate': self.safe_number(info, 'interestRate'),
6783
- 'period': 86400000, # Daily
6794
+ 'rate': self.safe_number(info, 'interestRate', hourlyBorrowRate),
6795
+ 'period': period, # Daily
6784
6796
  'timestamp': timestamp,
6785
6797
  'datetime': self.iso8601(timestamp),
6786
6798
  'info': info,
@@ -6830,6 +6842,53 @@ class bybit(Exchange, ImplicitAPI):
6830
6842
  interest = self.parse_borrow_interests(rows, None)
6831
6843
  return self.filter_by_currency_since_limit(interest, code, since, limit)
6832
6844
 
6845
+ async def fetch_borrow_rate_history(self, code: str, since: Int = None, limit: Int = None, params={}):
6846
+ """
6847
+ retrieves a history of a currencies borrow interest rate at specific time slots
6848
+ :see: https://bybit-exchange.github.io/docs/v5/spot-margin-uta/historical-interest
6849
+ :param str code: unified currency code
6850
+ :param int [since]: timestamp for the earliest borrow rate
6851
+ :param int [limit]: the maximum number of `borrow rate structures <https://docs.ccxt.com/#/?id=borrow-rate-structure>` to retrieve
6852
+ :param dict [params]: extra parameters specific to the exchange API endpoint
6853
+ :param int [params.until]: the latest time in ms to fetch entries for
6854
+ :returns dict[]: an array of `borrow rate structures <https://docs.ccxt.com/#/?id=borrow-rate-structure>`
6855
+ """
6856
+ await self.load_markets()
6857
+ currency = self.currency(code)
6858
+ request: dict = {
6859
+ 'currency': currency['id'],
6860
+ }
6861
+ if since is None:
6862
+ since = self.milliseconds() - 86400000 * 30 # last 30 days
6863
+ request['startTime'] = since
6864
+ endTime = self.safe_integer_2(params, 'until', 'endTime')
6865
+ params = self.omit(params, ['until'])
6866
+ if endTime is None:
6867
+ endTime = since + 86400000 * 30 # since + 30 days
6868
+ request['endTime'] = endTime
6869
+ response = await self.privateGetV5SpotMarginTradeInterestRateHistory(self.extend(request, params))
6870
+ #
6871
+ # {
6872
+ # "retCode": 0,
6873
+ # "retMsg": "OK",
6874
+ # "result": {
6875
+ # "list": [
6876
+ # {
6877
+ # "timestamp": 1721469600000,
6878
+ # "currency": "USDC",
6879
+ # "hourlyBorrowRate": "0.000014621596",
6880
+ # "vipLevel": "No VIP"
6881
+ # }
6882
+ # ]
6883
+ # },
6884
+ # "retExtInfo": "{}",
6885
+ # "time": 1721899048991
6886
+ # }
6887
+ #
6888
+ data = self.safe_dict(response, 'result')
6889
+ rows = self.safe_list(data, 'list', [])
6890
+ return self.parse_borrow_rate_history(rows, code, since, limit)
6891
+
6833
6892
  def parse_borrow_interest(self, info: dict, market: Market = None):
6834
6893
  #
6835
6894
  # {
@@ -4421,6 +4421,7 @@ class gate(Exchange, ImplicitAPI):
4421
4421
  """
4422
4422
  fetch all unfilled currently open orders
4423
4423
  :see: https://www.gate.io/docs/developers/apiv4/en/#list-all-open-orders
4424
+ :see: https://www.gate.io/docs/developers/apiv4/en/#retrieve-running-auto-order-list
4424
4425
  :param str symbol: unified market symbol
4425
4426
  :param int [since]: the earliest time in ms to fetch open orders for
4426
4427
  :param int [limit]: the maximum number of open orders structures to retrieve
ccxt/async_support/htx.py CHANGED
@@ -1628,6 +1628,10 @@ class htx(Exchange, ImplicitAPI):
1628
1628
  async def fetch_markets(self, params={}) -> List[Market]:
1629
1629
  """
1630
1630
  retrieves data on all markets for huobi
1631
+ :see: https://huobiapi.github.io/docs/spot/v1/en/#get-all-supported-trading-symbol-v1-deprecated
1632
+ :see: https://huobiapi.github.io/docs/dm/v1/en/#get-contract-info
1633
+ :see: https://huobiapi.github.io/docs/coin_margined_swap/v1/en/#query-swap-info
1634
+ :see: https://huobiapi.github.io/docs/usdt_swap/v1/en/#general-query-swap-info
1631
1635
  :param dict [params]: extra parameters specific to the exchange API endpoint
1632
1636
  :returns dict[]: an array of objects representing market data
1633
1637
  """
@@ -1651,7 +1655,19 @@ class htx(Exchange, ImplicitAPI):
1651
1655
  allMarkets = self.array_concat(allMarkets, promises[i])
1652
1656
  return allMarkets
1653
1657
 
1654
- async def fetch_markets_by_type_and_sub_type(self, type, subType, params={}):
1658
+ async def fetch_markets_by_type_and_sub_type(self, type: Str, subType: Str, params={}):
1659
+ """
1660
+ * @ignore
1661
+ retrieves data on all markets of a certain type and/or subtype
1662
+ :see: https://huobiapi.github.io/docs/spot/v1/en/#get-all-supported-trading-symbol-v1-deprecated
1663
+ :see: https://huobiapi.github.io/docs/dm/v1/en/#get-contract-info
1664
+ :see: https://huobiapi.github.io/docs/coin_margined_swap/v1/en/#query-swap-info
1665
+ :see: https://huobiapi.github.io/docs/usdt_swap/v1/en/#general-query-swap-info
1666
+ :param str [type]: 'spot', 'swap' or 'future'
1667
+ :param str [subType]: 'linear' or 'inverse'
1668
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1669
+ :returns dict[]: an array of objects representing market data
1670
+ """
1655
1671
  isSpot = (type == 'spot')
1656
1672
  request: dict = {}
1657
1673
  response = None
@@ -3015,7 +3031,15 @@ class htx(Exchange, ImplicitAPI):
3015
3031
  'code': None,
3016
3032
  }
3017
3033
 
3018
- async def fetch_account_id_by_type(self, type, marginMode=None, symbol=None, params={}):
3034
+ async def fetch_account_id_by_type(self, type: str, marginMode: Str = None, symbol: Str = None, params={}):
3035
+ """
3036
+ fetch all the accounts by a type and marginModeassociated with a profile
3037
+ :see: https://huobiapi.github.io/docs/spot/v1/en/#get-all-accounts-of-the-current-user
3038
+ :param str type: 'spot', 'swap' or 'future
3039
+ :param str [marginMode]: 'cross' or 'isolated'
3040
+ :param dict [params]: extra parameters specific to the exchange API endpoint
3041
+ :returns dict: a dictionary of `account structures <https://docs.ccxt.com/#/?id=account-structure>` indexed by the account type
3042
+ """
3019
3043
  accounts = await self.load_accounts()
3020
3044
  accountId = self.safe_value_2(params, 'accountId', 'account-id')
3021
3045
  if accountId is not None:
@@ -639,17 +639,17 @@ class hyperliquid(Exchange, ImplicitAPI):
639
639
  code = self.safe_currency_code(self.safe_string(balance, 'coin'))
640
640
  account = self.account()
641
641
  total = self.safe_string(balance, 'total')
642
- free = self.safe_string(balance, 'hold')
642
+ used = self.safe_string(balance, 'hold')
643
643
  account['total'] = total
644
- account['used'] = free
644
+ account['used'] = used
645
645
  spotBalances[code] = account
646
646
  return self.safe_balance(spotBalances)
647
647
  data = self.safe_dict(response, 'marginSummary', {})
648
648
  result: dict = {
649
649
  'info': response,
650
650
  'USDC': {
651
- 'total': self.safe_float(data, 'accountValue'),
652
- 'used': self.safe_float(data, 'totalMarginUsed'),
651
+ 'total': self.safe_number(data, 'accountValue'),
652
+ 'free': self.safe_number(response, 'withdrawable'),
653
653
  },
654
654
  }
655
655
  timestamp = self.safe_integer(response, 'time')
@@ -2113,10 +2113,11 @@ class hyperliquid(Exchange, ImplicitAPI):
2113
2113
  leverage = self.safe_dict(entry, 'leverage', {})
2114
2114
  marginMode = self.safe_string(leverage, 'type')
2115
2115
  isIsolated = (marginMode == 'isolated')
2116
- size = self.safe_number(entry, 'szi')
2116
+ size = self.safe_string(entry, 'szi')
2117
2117
  side = None
2118
2118
  if size is not None:
2119
- side = 'long' if (size > 0) else 'short'
2119
+ side = 'long' if Precise.string_gt(size, '0') else 'short'
2120
+ size = Precise.string_abs(size)
2120
2121
  unrealizedPnl = self.safe_number(entry, 'unrealizedPnl')
2121
2122
  initialMargin = self.safe_number(entry, 'marginUsed')
2122
2123
  percentage = unrealizedPnl / initialMargin * 100
@@ -184,6 +184,7 @@ class kucoin(Exchange, ImplicitAPI):
184
184
  'mark-price/{symbol}/current': 3, # 2PW
185
185
  'mark-price/all-symbols': 3,
186
186
  'margin/config': 25, # 25SW
187
+ 'announcements': 20, # 20W
187
188
  },
188
189
  'post': {
189
190
  # ws
@@ -473,6 +474,7 @@ class kucoin(Exchange, ImplicitAPI):
473
474
  'precisionMode': TICK_SIZE,
474
475
  'exceptions': {
475
476
  'exact': {
477
+ 'The order does not exist.': OrderNotFound,
476
478
  'order not exist': OrderNotFound,
477
479
  'order not exist.': OrderNotFound, # duplicated error temporarily
478
480
  'order_not_exist': OrderNotFound, # {"code":"order_not_exist","msg":"order_not_exist"} ¯\_(ツ)_/¯
@@ -673,6 +675,7 @@ class kucoin(Exchange, ImplicitAPI):
673
675
  'currencies/{currency}': 'v3',
674
676
  'symbols': 'v2',
675
677
  'mark-price/all-symbols': 'v3',
678
+ 'announcements': 'v3',
676
679
  },
677
680
  },
678
681
  'private': {
@@ -1530,38 +1533,28 @@ class kucoin(Exchange, ImplicitAPI):
1530
1533
  # "chain": "ERC20"
1531
1534
  # }
1532
1535
  #
1536
+ minWithdrawFee = self.safe_number(fee, 'withdrawMinFee')
1533
1537
  result: dict = {
1534
1538
  'info': fee,
1535
1539
  'withdraw': {
1540
+ 'fee': minWithdrawFee,
1541
+ 'percentage': False,
1542
+ },
1543
+ 'deposit': {
1536
1544
  'fee': None,
1537
1545
  'percentage': None,
1538
1546
  },
1547
+ 'networks': {},
1548
+ }
1549
+ networkId = self.safe_string(fee, 'chain')
1550
+ networkCode = self.network_id_to_code(networkId, self.safe_string(currency, 'code'))
1551
+ result['networks'][networkCode] = {
1552
+ 'withdraw': minWithdrawFee,
1539
1553
  'deposit': {
1540
1554
  'fee': None,
1541
1555
  'percentage': None,
1542
1556
  },
1543
- 'networks': {},
1544
1557
  }
1545
- isWithdrawEnabled = self.safe_bool(fee, 'isWithdrawEnabled', True)
1546
- minFee = None
1547
- if isWithdrawEnabled:
1548
- result['withdraw']['percentage'] = False
1549
- chains = self.safe_list(fee, 'chains', [])
1550
- for i in range(0, len(chains)):
1551
- chain = chains[i]
1552
- networkId = self.safe_string(chain, 'chainId')
1553
- networkCode = self.network_id_to_code(networkId, self.safe_string(currency, 'code'))
1554
- withdrawFee = self.safe_string(chain, 'withdrawalMinFee')
1555
- if minFee is None or (Precise.string_lt(withdrawFee, minFee)):
1556
- minFee = withdrawFee
1557
- result['networks'][networkCode] = {
1558
- 'withdraw': self.parse_number(withdrawFee),
1559
- 'deposit': {
1560
- 'fee': None,
1561
- 'percentage': None,
1562
- },
1563
- }
1564
- result['withdraw']['fee'] = self.parse_number(minFee)
1565
1558
  return result
1566
1559
 
1567
1560
  def is_futures_method(self, methodName, params):
@@ -2921,7 +2914,7 @@ class kucoin(Exchange, ImplicitAPI):
2921
2914
  },
2922
2915
  'status': status,
2923
2916
  'lastTradeTimestamp': None,
2924
- 'average': None,
2917
+ 'average': self.safe_string(order, 'avgDealPrice'),
2925
2918
  'trades': None,
2926
2919
  }, market)
2927
2920
 
@@ -4154,15 +4147,6 @@ class kucoin(Exchange, ImplicitAPI):
4154
4147
  return config['v1']
4155
4148
  return self.safe_value(config, 'cost', 1)
4156
4149
 
4157
- def parse_borrow_rate_history(self, response, code, since, limit):
4158
- result = []
4159
- for i in range(0, len(response)):
4160
- item = response[i]
4161
- borrowRate = self.parse_borrow_rate(item)
4162
- result.append(borrowRate)
4163
- sorted = self.sort_by(result, 'timestamp')
4164
- return self.filter_by_currency_since_limit(sorted, code, since, limit)
4165
-
4166
4150
  def parse_borrow_rate(self, info, currency: Currency = None):
4167
4151
  #
4168
4152
  # {
@@ -2103,8 +2103,8 @@ class kucoinfutures(kucoin, ImplicitAPI):
2103
2103
  amount = self.safe_string(order, 'size')
2104
2104
  filled = self.safe_string(order, 'filledSize')
2105
2105
  cost = self.safe_string(order, 'filledValue')
2106
- average = None
2107
- if Precise.string_gt(filled, '0'):
2106
+ average = self.safe_string(order, 'avgDealPrice')
2107
+ if (average is None) and Precise.string_gt(filled, '0'):
2108
2108
  contractSize = self.safe_string(market, 'contractSize')
2109
2109
  if market['linear']:
2110
2110
  average = Precise.string_div(cost, Precise.string_mul(contractSize, filled))
@@ -976,15 +976,15 @@ class lbank(Exchange, ImplicitAPI):
976
976
  limit = min(limit, 2000)
977
977
  if since is None:
978
978
  duration = self.parse_timeframe(timeframe)
979
- since = self.milliseconds() - duration * 1000 * limit
979
+ since = self.milliseconds() - (duration * 1000 * limit)
980
980
  request: dict = {
981
981
  'symbol': market['id'],
982
982
  'type': self.safe_string(self.timeframes, timeframe, timeframe),
983
983
  'time': self.parse_to_int(since / 1000),
984
- 'size': limit, # max 2000
984
+ 'size': min(limit + 1, 2000), # max 2000
985
985
  }
986
986
  response = await self.spotPublicGetKline(self.extend(request, params))
987
- ohlcvs = self.safe_value(response, 'data', [])
987
+ ohlcvs = self.safe_list(response, 'data', [])
988
988
  #
989
989
  #
990
990
  # [
ccxt/async_support/okx.py CHANGED
@@ -18,6 +18,7 @@ from ccxt.base.errors import AccountSuspended
18
18
  from ccxt.base.errors import ArgumentsRequired
19
19
  from ccxt.base.errors import BadRequest
20
20
  from ccxt.base.errors import BadSymbol
21
+ from ccxt.base.errors import OperationRejected
21
22
  from ccxt.base.errors import ManualInteractionNeeded
22
23
  from ccxt.base.errors import InsufficientFunds
23
24
  from ccxt.base.errors import InvalidAddress
@@ -379,6 +380,9 @@ class okx(Exchange, ImplicitAPI):
379
380
  'account/fixed-loan/borrowing-limit': 4,
380
381
  'account/fixed-loan/borrowing-quote': 5,
381
382
  'account/fixed-loan/borrowing-orders-list': 5,
383
+ 'account/spot-manual-borrow-repay': 10,
384
+ 'account/set-auto-repay': 4,
385
+ 'account/spot-borrow-repay-history': 4,
382
386
  # subaccount
383
387
  'users/subaccount/list': 10,
384
388
  'account/subaccount/balances': 10 / 3,
@@ -910,6 +914,11 @@ class okx(Exchange, ImplicitAPI):
910
914
  '59301': ExchangeError, # Margin adjustment failed for exceeding the max limit
911
915
  '59313': ExchangeError, # Unable to repay. You haven't borrowed any {ccy} {ccyPair} in Quick margin mode.
912
916
  '59401': ExchangeError, # Holdings already reached the limit
917
+ '59410': OperationRejected, # You can only borrow self crypto if it supports borrowing and borrowing is enabled.
918
+ '59411': InsufficientFunds, # Manual borrowing failed. Your account's free margin is insufficient
919
+ '59412': OperationRejected, # Manual borrowing failed. The amount exceeds your borrowing limit.
920
+ '59413': OperationRejected, # You didn't borrow self crypto. No repayment needed.
921
+ '59414': BadRequest, # Manual borrowing failed. The minimum borrowing limit is {param0}.needed.
913
922
  '59500': ExchangeError, # Only the APIKey of the main account has permission
914
923
  '59501': ExchangeError, # Only 50 APIKeys can be created per account
915
924
  '59502': ExchangeError, # Note name cannot be duplicate with the currently created APIKey note name
@@ -6231,15 +6240,6 @@ class okx(Exchange, ImplicitAPI):
6231
6240
  borrowRateHistories[code] = self.filter_by_currency_since_limit(borrowRateHistories[code], code, since, limit)
6232
6241
  return borrowRateHistories
6233
6242
 
6234
- def parse_borrow_rate_history(self, response, code, since, limit):
6235
- result = []
6236
- for i in range(0, len(response)):
6237
- item = response[i]
6238
- borrowRate = self.parse_borrow_rate(item)
6239
- result.append(borrowRate)
6240
- sorted = self.sort_by(result, 'timestamp')
6241
- return self.filter_by_currency_since_limit(sorted, code, since, limit)
6242
-
6243
6243
  async def fetch_borrow_rate_histories(self, codes=None, since: Int = None, limit: Int = None, params={}):
6244
6244
  """
6245
6245
  retrieves a history of a multiple currencies borrow interest rate at specific time slots, returns all currencies if no symbols passed, default is None
@@ -82,6 +82,7 @@ class phemex(Exchange, ImplicitAPI):
82
82
  'fetchMarkOHLCV': False,
83
83
  'fetchMyTrades': True,
84
84
  'fetchOHLCV': True,
85
+ 'fetchOpenInterest': True,
85
86
  'fetchOpenOrders': True,
86
87
  'fetchOrder': True,
87
88
  'fetchOrderBook': True,
@@ -4494,6 +4495,77 @@ class phemex(Exchange, ImplicitAPI):
4494
4495
  data = self.safe_dict(response, 'data', {})
4495
4496
  return self.parse_transaction(data, currency)
4496
4497
 
4498
+ async def fetch_open_interest(self, symbol: str, params={}):
4499
+ """
4500
+ retrieves the open interest of a trading pair
4501
+ :see: https://phemex-docs.github.io/#query-24-hours-ticker
4502
+ :param str symbol: unified CCXT market symbol
4503
+ :param dict [params]: exchange specific parameters
4504
+ :returns dict} an open interest structure{@link https://docs.ccxt.com/#/?id=open-interest-structure:
4505
+ """
4506
+ await self.load_markets()
4507
+ market = self.market(symbol)
4508
+ if not market['contract']:
4509
+ raise BadRequest(self.id + ' fetchOpenInterest is only supported for contract markets.')
4510
+ request: dict = {
4511
+ 'symbol': market['id'],
4512
+ }
4513
+ response = await self.v2GetMdV2Ticker24hr(self.extend(request, params))
4514
+ #
4515
+ # {
4516
+ # error: null,
4517
+ # id: '0',
4518
+ # result: {
4519
+ # closeRp: '67550.1',
4520
+ # fundingRateRr: '0.0001',
4521
+ # highRp: '68400',
4522
+ # indexPriceRp: '67567.15389794',
4523
+ # lowRp: '66096.4',
4524
+ # markPriceRp: '67550.1',
4525
+ # openInterestRv: '1848.1144186',
4526
+ # openRp: '66330',
4527
+ # predFundingRateRr: '0.0001',
4528
+ # symbol: 'BTCUSDT',
4529
+ # timestamp: '1729114315443343001',
4530
+ # turnoverRv: '228863389.3237532',
4531
+ # volumeRq: '3388.5600312'
4532
+ # }
4533
+ # }
4534
+ #
4535
+ result = self.safe_dict(response, 'result')
4536
+ return self.parse_open_interest(result, market)
4537
+
4538
+ def parse_open_interest(self, interest, market: Market = None):
4539
+ #
4540
+ # {
4541
+ # closeRp: '67550.1',
4542
+ # fundingRateRr: '0.0001',
4543
+ # highRp: '68400',
4544
+ # indexPriceRp: '67567.15389794',
4545
+ # lowRp: '66096.4',
4546
+ # markPriceRp: '67550.1',
4547
+ # openInterestRv: '1848.1144186',
4548
+ # openRp: '66330',
4549
+ # predFundingRateRr: '0.0001',
4550
+ # symbol: 'BTCUSDT',
4551
+ # timestamp: '1729114315443343001',
4552
+ # turnoverRv: '228863389.3237532',
4553
+ # volumeRq: '3388.5600312'
4554
+ # }
4555
+ #
4556
+ timestamp = self.safe_integer(interest, 'timestamp') / 1000000
4557
+ id = self.safe_string(interest, 'symbol')
4558
+ return self.safe_open_interest({
4559
+ 'info': interest,
4560
+ 'symbol': self.safe_symbol(id, market),
4561
+ 'baseVolume': self.safe_string(interest, 'volumeRq'),
4562
+ 'quoteVolume': None, # deprecated
4563
+ 'openInterestAmount': self.safe_string(interest, 'openInterestRv'),
4564
+ 'openInterestValue': None,
4565
+ 'timestamp': timestamp,
4566
+ 'datetime': self.iso8601(timestamp),
4567
+ }, market)
4568
+
4497
4569
  def handle_errors(self, httpCode: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
4498
4570
  if response is None:
4499
4571
  return None # fallback to default error handler
ccxt/base/exchange.py CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  # -----------------------------------------------------------------------------
6
6
 
7
- __version__ = '4.4.19'
7
+ __version__ = '4.4.21'
8
8
 
9
9
  # -----------------------------------------------------------------------------
10
10
 
@@ -5445,6 +5445,18 @@ class Exchange(object):
5445
5445
  interests.append(self.parse_borrow_interest(row, market))
5446
5446
  return interests
5447
5447
 
5448
+ def parse_borrow_rate(self, info, currency: Currency = None):
5449
+ raise NotSupported(self.id + ' parseBorrowRate() is not supported yet')
5450
+
5451
+ def parse_borrow_rate_history(self, response, code: Str, since: Int, limit: Int):
5452
+ result = []
5453
+ for i in range(0, len(response)):
5454
+ item = response[i]
5455
+ borrowRate = self.parse_borrow_rate(item)
5456
+ result.append(borrowRate)
5457
+ sorted = self.sort_by(result, 'timestamp')
5458
+ return self.filter_by_currency_since_limit(sorted, code, since, limit)
5459
+
5448
5460
  def parse_isolated_borrow_rates(self, info: Any):
5449
5461
  result = {}
5450
5462
  for i in range(0, len(info)):
@@ -5981,6 +5993,8 @@ class Exchange(object):
5981
5993
  i = 0
5982
5994
  errors = 0
5983
5995
  result = []
5996
+ timeframe = self.safe_string(params, 'timeframe')
5997
+ params = self.omit(params, 'timeframe') # reading the timeframe from the method arguments to avoid changing the signature
5984
5998
  while(i < maxCalls):
5985
5999
  try:
5986
6000
  if cursorValue is not None:
@@ -5992,6 +6006,8 @@ class Exchange(object):
5992
6006
  response = getattr(self, method)(params)
5993
6007
  elif method == 'getLeverageTiersPaginated' or method == 'fetchPositions':
5994
6008
  response = getattr(self, method)(symbol, params)
6009
+ elif method == 'fetchOpenInterestHistory':
6010
+ response = getattr(self, method)(symbol, timeframe, since, maxEntriesPerRequest, params)
5995
6011
  else:
5996
6012
  response = getattr(self, method)(symbol, since, maxEntriesPerRequest, params)
5997
6013
  errors = 0