ccxt 4.2.89__py2.py3-none-any.whl → 4.2.90__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.

Potentially problematic release.


This version of ccxt might be problematic. Click here for more details.

Files changed (64) hide show
  1. ccxt/__init__.py +1 -1
  2. ccxt/abstract/bingx.py +2 -0
  3. ccxt/abstract/bybit.py +2 -0
  4. ccxt/ascendex.py +1 -0
  5. ccxt/async_support/__init__.py +1 -1
  6. ccxt/async_support/ascendex.py +1 -0
  7. ccxt/async_support/base/exchange.py +13 -1
  8. ccxt/async_support/binance.py +81 -9
  9. ccxt/async_support/bingx.py +94 -1
  10. ccxt/async_support/bitfinex2.py +1 -0
  11. ccxt/async_support/bitget.py +2 -0
  12. ccxt/async_support/bitmex.py +1 -0
  13. ccxt/async_support/bitrue.py +1 -0
  14. ccxt/async_support/bybit.py +50 -0
  15. ccxt/async_support/coinbase.py +40 -20
  16. ccxt/async_support/coinbaseinternational.py +1 -0
  17. ccxt/async_support/coinex.py +96 -8
  18. ccxt/async_support/cryptocom.py +1 -0
  19. ccxt/async_support/delta.py +1 -0
  20. ccxt/async_support/digifinex.py +1 -0
  21. ccxt/async_support/exmo.py +1 -0
  22. ccxt/async_support/gate.py +2 -0
  23. ccxt/async_support/gemini.py +10 -9
  24. ccxt/async_support/hitbtc.py +1 -0
  25. ccxt/async_support/htx.py +1 -0
  26. ccxt/async_support/hyperliquid.py +1 -0
  27. ccxt/async_support/kucoin.py +1 -0
  28. ccxt/async_support/kucoinfutures.py +32 -4
  29. ccxt/async_support/mexc.py +1 -0
  30. ccxt/async_support/okx.py +143 -8
  31. ccxt/async_support/phemex.py +1 -0
  32. ccxt/async_support/woo.py +1 -0
  33. ccxt/base/exchange.py +52 -6
  34. ccxt/base/types.py +1 -0
  35. ccxt/binance.py +81 -9
  36. ccxt/bingx.py +94 -1
  37. ccxt/bitfinex2.py +1 -0
  38. ccxt/bitget.py +2 -0
  39. ccxt/bitmex.py +1 -0
  40. ccxt/bitrue.py +1 -0
  41. ccxt/bybit.py +50 -0
  42. ccxt/coinbase.py +40 -20
  43. ccxt/coinbaseinternational.py +1 -0
  44. ccxt/coinex.py +96 -8
  45. ccxt/cryptocom.py +1 -0
  46. ccxt/delta.py +1 -0
  47. ccxt/digifinex.py +1 -0
  48. ccxt/exmo.py +1 -0
  49. ccxt/gate.py +2 -0
  50. ccxt/gemini.py +10 -9
  51. ccxt/hitbtc.py +1 -0
  52. ccxt/htx.py +1 -0
  53. ccxt/hyperliquid.py +1 -0
  54. ccxt/kucoin.py +1 -0
  55. ccxt/kucoinfutures.py +32 -4
  56. ccxt/mexc.py +1 -0
  57. ccxt/okx.py +143 -8
  58. ccxt/phemex.py +1 -0
  59. ccxt/pro/__init__.py +1 -1
  60. ccxt/woo.py +1 -0
  61. {ccxt-4.2.89.dist-info → ccxt-4.2.90.dist-info}/METADATA +4 -4
  62. {ccxt-4.2.89.dist-info → ccxt-4.2.90.dist-info}/RECORD +64 -64
  63. {ccxt-4.2.89.dist-info → ccxt-4.2.90.dist-info}/WHEEL +0 -0
  64. {ccxt-4.2.89.dist-info → ccxt-4.2.90.dist-info}/top_level.txt +0 -0
ccxt/__init__.py CHANGED
@@ -22,7 +22,7 @@
22
22
 
23
23
  # ----------------------------------------------------------------------------
24
24
 
25
- __version__ = '4.2.89'
25
+ __version__ = '4.2.90'
26
26
 
27
27
  # ----------------------------------------------------------------------------
28
28
 
ccxt/abstract/bingx.py CHANGED
@@ -30,6 +30,7 @@ class ImplicitAPI:
30
30
  swap_v1_private_get_positionside_dual = swapV1PrivateGetPositionSideDual = Entry('positionSide/dual', ['swap', 'v1', 'private'], 'GET', {'cost': 1})
31
31
  swap_v1_private_get_market_markpriceklines = swapV1PrivateGetMarketMarkPriceKlines = Entry('market/markPriceKlines', ['swap', 'v1', 'private'], 'GET', {'cost': 1})
32
32
  swap_v1_private_get_trade_batchcancelreplace = swapV1PrivateGetTradeBatchCancelReplace = Entry('trade/batchCancelReplace', ['swap', 'v1', 'private'], 'GET', {'cost': 1})
33
+ swap_v1_private_get_trade_fullorder = swapV1PrivateGetTradeFullOrder = Entry('trade/fullOrder', ['swap', 'v1', 'private'], 'GET', {'cost': 1})
33
34
  swap_v1_private_post_trade_cancelreplace = swapV1PrivatePostTradeCancelReplace = Entry('trade/cancelReplace', ['swap', 'v1', 'private'], 'POST', {'cost': 1})
34
35
  swap_v1_private_post_positionside_dual = swapV1PrivatePostPositionSideDual = Entry('positionSide/dual', ['swap', 'v1', 'private'], 'POST', {'cost': 1})
35
36
  swap_v1_private_post_trade_closeposition = swapV1PrivatePostTradeClosePosition = Entry('trade/closePosition', ['swap', 'v1', 'private'], 'POST', {'cost': 1})
@@ -97,6 +98,7 @@ class ImplicitAPI:
97
98
  copytrading_v1_private_get_swap_trace_currenttrack = copyTradingV1PrivateGetSwapTraceCurrentTrack = Entry('swap/trace/currentTrack', ['copyTrading', 'v1', 'private'], 'GET', {'cost': 1})
98
99
  copytrading_v1_private_post_swap_trace_closetrackorder = copyTradingV1PrivatePostSwapTraceCloseTrackOrder = Entry('swap/trace/closeTrackOrder', ['copyTrading', 'v1', 'private'], 'POST', {'cost': 1})
99
100
  copytrading_v1_private_post_swap_trace_settpsl = copyTradingV1PrivatePostSwapTraceSetTPSL = Entry('swap/trace/setTPSL', ['copyTrading', 'v1', 'private'], 'POST', {'cost': 1})
101
+ copytrading_v1_private_post_spot_trader_sellorder = copyTradingV1PrivatePostSpotTraderSellOrder = Entry('spot/trader/sellOrder', ['copyTrading', 'v1', 'private'], 'POST', {'cost': 1})
100
102
  api_v3_private_get_asset_transfer = apiV3PrivateGetAssetTransfer = Entry('asset/transfer', ['api', 'v3', 'private'], 'GET', {'cost': 1})
101
103
  api_v3_private_get_capital_deposit_hisrec = apiV3PrivateGetCapitalDepositHisrec = Entry('capital/deposit/hisrec', ['api', 'v3', 'private'], 'GET', {'cost': 1})
102
104
  api_v3_private_get_capital_withdraw_history = apiV3PrivateGetCapitalWithdrawHistory = Entry('capital/withdraw/history', ['api', 'v3', 'private'], 'GET', {'cost': 1})
ccxt/abstract/bybit.py CHANGED
@@ -155,6 +155,7 @@ class ImplicitAPI:
155
155
  private_get_v5_user_get_member_type = privateGetV5UserGetMemberType = Entry('v5/user/get-member-type', 'private', 'GET', {'cost': 5})
156
156
  private_get_v5_user_aff_customer_info = privateGetV5UserAffCustomerInfo = Entry('v5/user/aff-customer-info', 'private', 'GET', {'cost': 5})
157
157
  private_get_v5_user_del_submember = privateGetV5UserDelSubmember = Entry('v5/user/del-submember', 'private', 'GET', {'cost': 5})
158
+ private_get_v5_user_submembers = privateGetV5UserSubmembers = Entry('v5/user/submembers', 'private', 'GET', {'cost': 5})
158
159
  private_get_v5_spot_lever_token_order_record = privateGetV5SpotLeverTokenOrderRecord = Entry('v5/spot-lever-token/order-record', 'private', 'GET', {'cost': 1})
159
160
  private_get_v5_spot_margin_trade_state = privateGetV5SpotMarginTradeState = Entry('v5/spot-margin-trade/state', 'private', 'GET', {'cost': 5})
160
161
  private_get_v5_spot_cross_margin_trade_loan_info = privateGetV5SpotCrossMarginTradeLoanInfo = Entry('v5/spot-cross-margin-trade/loan-info', 'private', 'GET', {'cost': 1})
@@ -291,3 +292,4 @@ class ImplicitAPI:
291
292
  private_post_v5_lending_redeem_cancel = privatePostV5LendingRedeemCancel = Entry('v5/lending/redeem-cancel', 'private', 'POST', {'cost': 5})
292
293
  private_post_v5_account_set_collateral_switch = privatePostV5AccountSetCollateralSwitch = Entry('v5/account/set-collateral-switch', 'private', 'POST', {'cost': 5})
293
294
  private_post_v5_account_set_collateral_switch_batch = privatePostV5AccountSetCollateralSwitchBatch = Entry('v5/account/set-collateral-switch-batch', 'private', 'POST', {'cost': 5})
295
+ private_post_v5_account_demo_apply_money = privatePostV5AccountDemoApplyMoney = Entry('v5/account/demo-apply-money', 'private', 'POST', {'cost': 5})
ccxt/ascendex.py CHANGED
@@ -2744,6 +2744,7 @@ class ascendex(Exchange, ImplicitAPI):
2744
2744
  'info': data,
2745
2745
  'symbol': market['symbol'],
2746
2746
  'type': None,
2747
+ 'marginMode': 'isolated',
2747
2748
  'amount': None,
2748
2749
  'total': None,
2749
2750
  'code': market['quote'],
@@ -4,7 +4,7 @@
4
4
 
5
5
  # -----------------------------------------------------------------------------
6
6
 
7
- __version__ = '4.2.89'
7
+ __version__ = '4.2.90'
8
8
 
9
9
  # -----------------------------------------------------------------------------
10
10
 
@@ -2744,6 +2744,7 @@ class ascendex(Exchange, ImplicitAPI):
2744
2744
  'info': data,
2745
2745
  'symbol': market['symbol'],
2746
2746
  'type': None,
2747
+ 'marginMode': 'isolated',
2747
2748
  'amount': None,
2748
2749
  'total': None,
2749
2750
  'code': market['quote'],
@@ -2,7 +2,7 @@
2
2
 
3
3
  # -----------------------------------------------------------------------------
4
4
 
5
- __version__ = '4.2.89'
5
+ __version__ = '4.2.90'
6
6
 
7
7
  # -----------------------------------------------------------------------------
8
8
 
@@ -677,6 +677,18 @@ class Exchange(BaseExchange):
677
677
  async def set_margin(self, symbol: str, amount: float, params={}):
678
678
  raise NotSupported(self.id + ' setMargin() is not supported yet')
679
679
 
680
+ async def fetch_margin_adjustment_history(self, symbol: Str = None, type: Str = None, since: Num = None, limit: Num = None, params={}):
681
+ """
682
+ fetches the history of margin added or reduced from contract isolated positions
683
+ :param str [symbol]: unified market symbol
684
+ :param str [type]: "add" or "reduce"
685
+ :param int [since]: timestamp in ms of the earliest change to fetch
686
+ :param int [limit]: the maximum amount of changes to fetch
687
+ :param dict params: extra parameters specific to the exchange api endpoint
688
+ :returns dict[]: a list of `margin structures <https://docs.ccxt.com/#/?id=margin-loan-structure>`
689
+ """
690
+ raise NotSupported(self.id + ' fetchMarginAdjustmentHistory() is not supported yet')
691
+
680
692
  async def set_margin_mode(self, marginMode: str, symbol: Str = None, params={}):
681
693
  raise NotSupported(self.id + ' setMarginMode() is not supported yet')
682
694
 
@@ -121,6 +121,7 @@ class binance(Exchange, ImplicitAPI):
121
121
  'fetchLeverages': True,
122
122
  'fetchLeverageTiers': True,
123
123
  'fetchLiquidations': False,
124
+ 'fetchMarginAdjustmentHistory': True,
124
125
  'fetchMarginMode': 'emulated',
125
126
  'fetchMarginModes': True,
126
127
  'fetchMarketLeverageTiers': 'emulated',
@@ -10330,21 +10331,37 @@ class binance(Exchange, ImplicitAPI):
10330
10331
  # "type": 1
10331
10332
  # }
10332
10333
  #
10334
+ # fetchMarginAdjustmentHistory
10335
+ #
10336
+ # {
10337
+ # symbol: "XRPUSDT",
10338
+ # type: "1",
10339
+ # deltaType: "TRADE",
10340
+ # amount: "2.57148240",
10341
+ # asset: "USDT",
10342
+ # time: "1711046271555",
10343
+ # positionSide: "BOTH",
10344
+ # clientTranId: ""
10345
+ # }
10346
+ #
10333
10347
  rawType = self.safe_integer(data, 'type')
10334
- resultType = 'add' if (rawType == 1) else 'reduce'
10335
- resultAmount = self.safe_number(data, 'amount')
10336
10348
  errorCode = self.safe_string(data, 'code')
10337
- status = 'ok' if (errorCode == '200') else 'failed'
10349
+ marketId = self.safe_string(data, 'symbol')
10350
+ timestamp = self.safe_integer(data, 'time')
10351
+ market = self.safe_market(marketId, market, None, 'swap')
10352
+ noErrorCode = errorCode is None
10353
+ success = errorCode == '200'
10338
10354
  return {
10339
10355
  'info': data,
10340
10356
  'symbol': market['symbol'],
10341
- 'type': resultType,
10342
- 'amount': resultAmount,
10357
+ 'type': 'add' if (rawType == 1) else 'reduce',
10358
+ 'marginMode': 'isolated',
10359
+ 'amount': self.safe_number(data, 'amount'),
10360
+ 'code': self.safe_string(data, 'asset'),
10343
10361
  'total': None,
10344
- 'code': None,
10345
- 'status': status,
10346
- 'timestamp': None,
10347
- 'datetime': None,
10362
+ 'status': 'ok' if (success or noErrorCode) else 'failed',
10363
+ 'timestamp': timestamp,
10364
+ 'datetime': self.iso8601(timestamp),
10348
10365
  }
10349
10366
 
10350
10367
  async def reduce_margin(self, symbol: str, amount, params={}) -> MarginModification:
@@ -11506,3 +11523,58 @@ class binance(Exchange, ImplicitAPI):
11506
11523
  'baseVolume': self.safe_number(chain, 'volume'),
11507
11524
  'quoteVolume': None,
11508
11525
  }
11526
+
11527
+ async def fetch_margin_adjustment_history(self, symbol: Str = None, type: Str = None, since: Num = None, limit: Num = None, params={}) -> List[MarginModification]:
11528
+ """
11529
+ fetches the history of margin added or reduced from contract isolated positions
11530
+ :see: https://binance-docs.github.io/apidocs/futures/en/#get-position-margin-change-history-trade
11531
+ :see: https://binance-docs.github.io/apidocs/delivery/en/#get-position-margin-change-history-trade
11532
+ :param str symbol: unified market symbol
11533
+ :param str [type]: "add" or "reduce"
11534
+ :param int [since]: timestamp in ms of the earliest change to fetch
11535
+ :param int [limit]: the maximum amount of changes to fetch
11536
+ :param dict params: extra parameters specific to the exchange api endpoint
11537
+ :param int [params.until]: timestamp in ms of the latest change to fetch
11538
+ :returns dict[]: a list of `margin structures <https://docs.ccxt.com/#/?id=margin-loan-structure>`
11539
+ """
11540
+ await self.load_markets()
11541
+ if symbol is None:
11542
+ raise ArgumentsRequired(self.id + ' fetchMarginAdjustmentHistory() requires a symbol argument')
11543
+ market = self.market(symbol)
11544
+ until = self.safe_integer(params, 'until')
11545
+ params = self.omit(params, 'until')
11546
+ request = {
11547
+ 'symbol': market['id'],
11548
+ }
11549
+ if type is not None:
11550
+ request['type'] = 1 if (type == 'add') else 2
11551
+ if since is not None:
11552
+ request['startTime'] = since
11553
+ if limit is not None:
11554
+ request['limit'] = limit
11555
+ if until is not None:
11556
+ request['endTime'] = until
11557
+ response = None
11558
+ if market['linear']:
11559
+ response = await self.fapiPrivateGetPositionMarginHistory(self.extend(request, params))
11560
+ elif market['inverse']:
11561
+ response = await self.dapiPrivateGetPositionMarginHistory(self.extend(request, params))
11562
+ else:
11563
+ raise BadRequest(self.id + 'fetchMarginAdjustmentHistory() is not supported for markets of type ' + market['type'])
11564
+ #
11565
+ # [
11566
+ # {
11567
+ # symbol: "XRPUSDT",
11568
+ # type: "1",
11569
+ # deltaType: "TRADE",
11570
+ # amount: "2.57148240",
11571
+ # asset: "USDT",
11572
+ # time: "1711046271555",
11573
+ # positionSide: "BOTH",
11574
+ # clientTranId: ""
11575
+ # }
11576
+ # ...
11577
+ # ]
11578
+ #
11579
+ modifications = self.parse_margin_modifications(response)
11580
+ return self.filter_by_symbol_since_limit(modifications, symbol, since, limit)
@@ -74,6 +74,7 @@ class bingx(Exchange, ImplicitAPI):
74
74
  'fetchFundingRates': True,
75
75
  'fetchLeverage': True,
76
76
  'fetchLiquidations': False,
77
+ 'fetchMarginAdjustmentHistory': False,
77
78
  'fetchMarginMode': True,
78
79
  'fetchMarkets': True,
79
80
  'fetchMarkOHLCV': True,
@@ -83,6 +84,7 @@ class bingx(Exchange, ImplicitAPI):
83
84
  'fetchOpenOrders': True,
84
85
  'fetchOrder': True,
85
86
  'fetchOrderBook': True,
87
+ 'fetchOrders': True,
86
88
  'fetchPositionMode': True,
87
89
  'fetchPositions': True,
88
90
  'fetchTicker': True,
@@ -194,6 +196,7 @@ class bingx(Exchange, ImplicitAPI):
194
196
  'positionSide/dual': 1,
195
197
  'market/markPriceKlines': 1,
196
198
  'trade/batchCancelReplace': 1,
199
+ 'trade/fullOrder': 1,
197
200
  },
198
201
  'post': {
199
202
  'trade/cancelReplace': 1,
@@ -341,6 +344,7 @@ class bingx(Exchange, ImplicitAPI):
341
344
  'post': {
342
345
  'swap/trace/closeTrackOrder': 1,
343
346
  'swap/trace/setTPSL': 1,
347
+ 'spot/trader/sellOrder': 1,
344
348
  },
345
349
  },
346
350
  },
@@ -2587,7 +2591,7 @@ class bingx(Exchange, ImplicitAPI):
2587
2591
  :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
2588
2592
  """
2589
2593
  if symbol is None:
2590
- raise ArgumentsRequired(self.id + ' fetchOrders() requires a symbol argument')
2594
+ raise ArgumentsRequired(self.id + ' fetchOrder() requires a symbol argument')
2591
2595
  await self.load_markets()
2592
2596
  market = self.market(symbol)
2593
2597
  request: dict = {
@@ -2655,6 +2659,94 @@ class bingx(Exchange, ImplicitAPI):
2655
2659
  first = self.safe_dict(data, 'order', data)
2656
2660
  return self.parse_order(first, market)
2657
2661
 
2662
+ async def fetch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
2663
+ """
2664
+ fetches information on multiple orders made by the user
2665
+ :see: https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#User's%20All%20Orders
2666
+ :param str symbol: unified market symbol of the market orders were made in
2667
+ :param int [since]: the earliest time in ms to fetch orders for
2668
+ :param int [limit]: the maximum number of order structures to retrieve
2669
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2670
+ :param int [params.until]: the latest time in ms to fetch entries for
2671
+ :param int [params.orderId]: Only return subsequent orders, and return the latest order by default
2672
+ :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
2673
+ """
2674
+ await self.load_markets()
2675
+ request = {}
2676
+ market = None
2677
+ if symbol is not None:
2678
+ market = self.market(symbol)
2679
+ request['symbol'] = market['id']
2680
+ type = None
2681
+ type, params = self.handle_market_type_and_params('fetchOrders', market, params)
2682
+ if type != 'swap':
2683
+ raise NotSupported(self.id + ' fetchOrders() is only supported for swap markets')
2684
+ if limit is not None:
2685
+ request['limit'] = limit
2686
+ if since is not None:
2687
+ request['startTime'] = since
2688
+ until = self.safe_integer_2(params, 'until', 'till') # unified in milliseconds
2689
+ endTime = self.safe_integer(params, 'endTime', until) # exchange-specific in milliseconds
2690
+ params = self.omit(params, ['endTime', 'till', 'until'])
2691
+ if endTime is not None:
2692
+ request['endTime'] = endTime
2693
+ response = await self.swapV1PrivateGetTradeFullOrder(self.extend(request, params))
2694
+ #
2695
+ # {
2696
+ # "code": 0,
2697
+ # "msg": "",
2698
+ # "data": {
2699
+ # "orders": [
2700
+ # {
2701
+ # "symbol": "PYTH-USDT",
2702
+ # "orderId": 1736007506620112100,
2703
+ # "side": "SELL",
2704
+ # "positionSide": "SHORT",
2705
+ # "type": "LIMIT",
2706
+ # "origQty": "33",
2707
+ # "price": "0.3916",
2708
+ # "executedQty": "33",
2709
+ # "avgPrice": "0.3916",
2710
+ # "cumQuote": "13",
2711
+ # "stopPrice": "",
2712
+ # "profit": "0.0000",
2713
+ # "commission": "-0.002585",
2714
+ # "status": "FILLED",
2715
+ # "time": 1702731418000,
2716
+ # "updateTime": 1702731470000,
2717
+ # "clientOrderId": "",
2718
+ # "leverage": "15X",
2719
+ # "takeProfit": {
2720
+ # "type": "TAKE_PROFIT",
2721
+ # "quantity": 0,
2722
+ # "stopPrice": 0,
2723
+ # "price": 0,
2724
+ # "workingType": ""
2725
+ # },
2726
+ # "stopLoss": {
2727
+ # "type": "STOP",
2728
+ # "quantity": 0,
2729
+ # "stopPrice": 0,
2730
+ # "price": 0,
2731
+ # "workingType": ""
2732
+ # },
2733
+ # "advanceAttr": 0,
2734
+ # "positionID": 0,
2735
+ # "takeProfitEntrustPrice": 0,
2736
+ # "stopLossEntrustPrice": 0,
2737
+ # "orderType": "",
2738
+ # "workingType": "MARK_PRICE",
2739
+ # "stopGuaranteed": False,
2740
+ # "triggerOrderId": 1736012449498123500
2741
+ # }
2742
+ # ]
2743
+ # }
2744
+ # }
2745
+ #
2746
+ data = self.safe_dict(response, 'data', {})
2747
+ orders = self.safe_list(data, 'orders', [])
2748
+ return self.parse_orders(orders, market, since, limit)
2749
+
2658
2750
  async def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
2659
2751
  """
2660
2752
  :see: https://bingx-api.github.io/docs/#/spot/trade-api.html#Query%20Open%20Orders
@@ -3307,6 +3399,7 @@ class bingx(Exchange, ImplicitAPI):
3307
3399
  'info': data,
3308
3400
  'symbol': self.safe_string(market, 'symbol'),
3309
3401
  'type': 'add' if (type == '1') else 'reduce',
3402
+ 'marginMode': 'isolated',
3310
3403
  'amount': self.safe_number(data, 'amount'),
3311
3404
  'total': self.safe_number(data, 'margin'),
3312
3405
  'code': self.safe_string(market, 'settle'),
@@ -3325,6 +3325,7 @@ class bitfinex2(Exchange, ImplicitAPI):
3325
3325
  'info': data,
3326
3326
  'symbol': market['symbol'],
3327
3327
  'type': None,
3328
+ 'marginMode': 'isolated',
3328
3329
  'amount': None,
3329
3330
  'total': None,
3330
3331
  'code': None,
@@ -106,6 +106,7 @@ class bitget(Exchange, ImplicitAPI):
106
106
  'fetchLeverage': True,
107
107
  'fetchLeverageTiers': False,
108
108
  'fetchLiquidations': False,
109
+ 'fetchMarginAdjustmentHistory': False,
109
110
  'fetchMarginMode': True,
110
111
  'fetchMarketLeverageTiers': True,
111
112
  'fetchMarkets': True,
@@ -6486,6 +6487,7 @@ class bitget(Exchange, ImplicitAPI):
6486
6487
  'info': data,
6487
6488
  'symbol': market['symbol'],
6488
6489
  'type': None,
6490
+ 'marginMode': 'isolated',
6489
6491
  'amount': None,
6490
6492
  'total': None,
6491
6493
  'code': market['settle'],
@@ -75,6 +75,7 @@ class bitmex(Exchange, ImplicitAPI):
75
75
  'fetchLeverages': True,
76
76
  'fetchLeverageTiers': False,
77
77
  'fetchLiquidations': True,
78
+ 'fetchMarginAdjustmentHistory': False,
78
79
  'fetchMarketLeverageTiers': False,
79
80
  'fetchMarkets': True,
80
81
  'fetchMarkOHLCV': False,
@@ -2853,6 +2853,7 @@ class bitrue(Exchange, ImplicitAPI):
2853
2853
  'info': data,
2854
2854
  'symbol': market['symbol'],
2855
2855
  'type': None,
2856
+ 'marginMode': 'isolated',
2856
2857
  'amount': None,
2857
2858
  'total': None,
2858
2859
  'code': None,
@@ -96,6 +96,7 @@ class bybit(Exchange, ImplicitAPI):
96
96
  'fetchLedger': True,
97
97
  'fetchLeverage': True,
98
98
  'fetchLeverageTiers': True,
99
+ 'fetchMarginAdjustmentHistory': False,
99
100
  'fetchMarketLeverageTiers': True,
100
101
  'fetchMarkets': True,
101
102
  'fetchMarkOHLCV': True,
@@ -166,6 +167,13 @@ class bybit(Exchange, ImplicitAPI):
166
167
  'public': 'https://api.{hostname}',
167
168
  'private': 'https://api.{hostname}',
168
169
  },
170
+ 'demotrading': {
171
+ 'spot': 'https://api-demo.{hostname}',
172
+ 'futures': 'https://api-demo.{hostname}',
173
+ 'v2': 'https://api-demo.{hostname}',
174
+ 'public': 'https://api-demo.{hostname}',
175
+ 'private': 'https://api-demo.{hostname}',
176
+ },
169
177
  'www': 'https://www.bybit.com',
170
178
  'doc': [
171
179
  'https://bybit-exchange.github.io/docs/inverse/',
@@ -354,6 +362,7 @@ class bybit(Exchange, ImplicitAPI):
354
362
  'v5/user/get-member-type': 5,
355
363
  'v5/user/aff-customer-info': 5,
356
364
  'v5/user/del-submember': 5,
365
+ 'v5/user/submembers': 5,
357
366
  # spot leverage token
358
367
  'v5/spot-lever-token/order-record': 1, # 50/s => cost = 50 / 50 = 1
359
368
  # spot margin trade
@@ -514,6 +523,8 @@ class bybit(Exchange, ImplicitAPI):
514
523
  'v5/lending/redeem-cancel': 5,
515
524
  'v5/account/set-collateral-switch': 5,
516
525
  'v5/account/set-collateral-switch-batch': 5,
526
+ # demo trading
527
+ 'v5/account/demo-apply-money': 5,
517
528
  },
518
529
  },
519
530
  },
@@ -980,6 +991,8 @@ class bybit(Exchange, ImplicitAPI):
980
991
  },
981
992
  'precisionMode': TICK_SIZE,
982
993
  'options': {
994
+ 'sandboxMode': False,
995
+ 'enableDemoTrading': False,
983
996
  'fetchMarkets': ['spot', 'linear', 'inverse', 'option'],
984
997
  'createOrder': {
985
998
  'method': 'privatePostV5OrderCreate', # 'privatePostV5PositionTradingStop'
@@ -1063,6 +1076,32 @@ class bybit(Exchange, ImplicitAPI):
1063
1076
  },
1064
1077
  })
1065
1078
 
1079
+ def set_sandbox_mode(self, enable: bool):
1080
+ """
1081
+ enables or disables sandbox mode
1082
+ :param boolean [enable]: True if demo trading should be enabled, False otherwise
1083
+ """
1084
+ super(bybit, self).set_sandbox_mode(enable)
1085
+ self.options['sandboxMode'] = enable
1086
+
1087
+ def enable_demo_trading(self, enable: bool):
1088
+ """
1089
+ enables or disables demo trading mode
1090
+ :see: https://bybit-exchange.github.io/docs/v5/demo
1091
+ :param boolean [enable]: True if demo trading should be enabled, False otherwise
1092
+ """
1093
+ if self.options['sandboxMode']:
1094
+ raise NotSupported(self.id + ' demo trading does not support in sandbox environment')
1095
+ # enable demo trading in bybit, see: https://bybit-exchange.github.io/docs/v5/demo
1096
+ if enable:
1097
+ self.urls['apiBackupDemoTrading'] = self.urls['api']
1098
+ self.urls['api'] = self.urls['demotrading']
1099
+ elif 'apiBackupDemoTrading' in self.urls:
1100
+ self.urls['api'] = self.urls['apiBackupDemoTrading']
1101
+ newUrls = self.omit(self.urls, 'apiBackupDemoTrading')
1102
+ self.urls = newUrls
1103
+ self.options['enableDemoTrading'] = enable
1104
+
1066
1105
  def nonce(self):
1067
1106
  return self.milliseconds() - self.options['timeDifference']
1068
1107
 
@@ -1078,12 +1117,21 @@ class bybit(Exchange, ImplicitAPI):
1078
1117
  return data
1079
1118
 
1080
1119
  async def is_unified_enabled(self, params={}):
1120
+ """
1121
+ returns [enableUnifiedMargin, enableUnifiedAccount] so the user can check if unified account is enabled
1122
+ """
1081
1123
  # The API key of user id must own one of permissions will be allowed to call following API endpoints.
1082
1124
  # SUB UID: "Account Transfer"
1083
1125
  # MASTER UID: "Account Transfer", "Subaccount Transfer", "Withdrawal"
1084
1126
  enableUnifiedMargin = self.safe_value(self.options, 'enableUnifiedMargin')
1085
1127
  enableUnifiedAccount = self.safe_value(self.options, 'enableUnifiedAccount')
1086
1128
  if enableUnifiedMargin is None or enableUnifiedAccount is None:
1129
+ if self.options['enableDemoTrading']:
1130
+ # info endpoint is not available in demo trading
1131
+ # so we're assuming UTA is enabled
1132
+ self.options['enableUnifiedMargin'] = False
1133
+ self.options['enableUnifiedAccount'] = True
1134
+ return [self.options['enableUnifiedMargin'], self.options['enableUnifiedAccount']]
1087
1135
  response = await self.privateGetV5UserQueryApi(params)
1088
1136
  #
1089
1137
  # {
@@ -1243,6 +1291,8 @@ class bybit(Exchange, ImplicitAPI):
1243
1291
  """
1244
1292
  if not self.check_required_credentials(False):
1245
1293
  return None
1294
+ if self.options['enableDemoTrading']:
1295
+ return None
1246
1296
  response = await self.privateGetV5AssetCoinQueryInfo(params)
1247
1297
  #
1248
1298
  # {
@@ -3574,25 +3574,14 @@ class coinbase(Exchange, ImplicitAPI):
3574
3574
  url = self.urls['api']['rest'] + fullPath
3575
3575
  if signed:
3576
3576
  authorization = self.safe_string(self.headers, 'Authorization')
3577
+ authorizationString = None
3577
3578
  if authorization is not None:
3578
- headers = {
3579
- 'Authorization': authorization,
3580
- 'Content-Type': 'application/json',
3581
- }
3582
- if method != 'GET':
3583
- if query:
3584
- body = self.json(query)
3579
+ authorizationString = authorization
3585
3580
  elif self.token and not self.check_required_credentials(False):
3586
- headers = {
3587
- 'Authorization': 'Bearer ' + self.token,
3588
- 'Content-Type': 'application/json',
3589
- }
3590
- if method != 'GET':
3591
- if query:
3592
- body = self.json(query)
3581
+ authorizationString = 'Bearer ' + self.token
3593
3582
  else:
3594
3583
  self.check_required_credentials()
3595
- timestampString = str(self.seconds())
3584
+ seconds = self.seconds()
3596
3585
  payload = ''
3597
3586
  if method != 'GET':
3598
3587
  if query:
@@ -3606,14 +3595,45 @@ class coinbase(Exchange, ImplicitAPI):
3606
3595
  # https://docs.cloud.coinbase.com/advanced-trade-api/docs/auth#example-request
3607
3596
  # v2: 'GET' require payload in the signature
3608
3597
  # https://docs.cloud.coinbase.com/sign-in-with-coinbase/docs/api-key-authentication
3609
- auth = timestampString + method + savedPath + payload
3610
- signature = self.hmac(self.encode(auth), self.encode(self.secret), hashlib.sha256)
3598
+ isCloudAPiKey = (self.apiKey.find('organizations/') >= 0) or (self.secret.startswith('-----BEGIN'))
3599
+ if isCloudAPiKey:
3600
+ if self.apiKey.startswith('-----BEGIN'):
3601
+ raise ArgumentsRequired(self.id + ' apiKey should contain the name(eg: organizations/3b910e93....) and not the public key')
3602
+ # it may not work for v2
3603
+ uri = method + ' ' + url.replace('https://', '')
3604
+ quesPos = uri.find('?')
3605
+ if quesPos >= 0:
3606
+ uri = uri[0:quesPos]
3607
+ nonce = self.random_bytes(16)
3608
+ request = {
3609
+ 'aud': ['retail_rest_api_proxy'],
3610
+ 'iss': 'coinbase-cloud',
3611
+ 'nbf': seconds,
3612
+ 'exp': seconds + 120,
3613
+ 'sub': self.apiKey,
3614
+ 'uri': uri,
3615
+ 'iat': seconds,
3616
+ }
3617
+ token = self.jwt(request, self.encode(self.secret), 'sha256', False, {'kid': self.apiKey, 'nonce': nonce, 'alg': 'ES256'})
3618
+ authorizationString = 'Bearer ' + token
3619
+ else:
3620
+ timestampString = str(self.seconds())
3621
+ auth = timestampString + method + savedPath + payload
3622
+ signature = self.hmac(self.encode(auth), self.encode(self.secret), hashlib.sha256)
3623
+ headers = {
3624
+ 'CB-ACCESS-KEY': self.apiKey,
3625
+ 'CB-ACCESS-SIGN': signature,
3626
+ 'CB-ACCESS-TIMESTAMP': timestampString,
3627
+ 'Content-Type': 'application/json',
3628
+ }
3629
+ if authorizationString is not None:
3611
3630
  headers = {
3612
- 'CB-ACCESS-KEY': self.apiKey,
3613
- 'CB-ACCESS-SIGN': signature,
3614
- 'CB-ACCESS-TIMESTAMP': timestampString,
3631
+ 'Authorization': authorizationString,
3615
3632
  'Content-Type': 'application/json',
3616
3633
  }
3634
+ if method != 'GET':
3635
+ if query:
3636
+ body = self.json(query)
3617
3637
  return {'url': url, 'method': method, 'body': body, 'headers': headers}
3618
3638
 
3619
3639
  def handle_errors(self, code, reason, url, method, headers, body, response, requestHeaders, requestBody):
@@ -85,6 +85,7 @@ class coinbaseinternational(Exchange, ImplicitAPI):
85
85
  'fetchLedger': False,
86
86
  'fetchLeverage': False,
87
87
  'fetchLeverageTiers': False,
88
+ 'fetchMarginAdjustmentHistory': False,
88
89
  'fetchMarginMode': False,
89
90
  'fetchMarkets': True,
90
91
  'fetchMarkOHLCV': False,