ccxt 4.4.61__py2.py3-none-any.whl → 4.4.62__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 +1 -1
- ccxt/abstract/bybit.py +1 -0
- ccxt/async_support/__init__.py +1 -1
- ccxt/async_support/base/exchange.py +9 -7
- ccxt/async_support/bingx.py +5 -6
- ccxt/async_support/bybit.py +109 -2
- ccxt/async_support/kraken.py +2 -2
- ccxt/async_support/phemex.py +221 -5
- ccxt/async_support/whitebit.py +3 -1
- ccxt/base/exchange.py +53 -26
- ccxt/bingx.py +5 -6
- ccxt/bybit.py +109 -2
- ccxt/kraken.py +2 -2
- ccxt/phemex.py +221 -5
- ccxt/pro/__init__.py +1 -1
- ccxt/pro/binance.py +25 -11
- ccxt/pro/lbank.py +10 -4
- ccxt/pro/myokx.py +10 -1
- ccxt/test/tests_init.py +2 -1
- ccxt/whitebit.py +3 -1
- {ccxt-4.4.61.dist-info → ccxt-4.4.62.dist-info}/METADATA +4 -4
- {ccxt-4.4.61.dist-info → ccxt-4.4.62.dist-info}/RECORD +25 -25
- {ccxt-4.4.61.dist-info → ccxt-4.4.62.dist-info}/LICENSE.txt +0 -0
- {ccxt-4.4.61.dist-info → ccxt-4.4.62.dist-info}/WHEEL +0 -0
- {ccxt-4.4.61.dist-info → ccxt-4.4.62.dist-info}/top_level.txt +0 -0
ccxt/base/exchange.py
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
# -----------------------------------------------------------------------------
|
6
6
|
|
7
|
-
__version__ = '4.4.
|
7
|
+
__version__ = '4.4.62'
|
8
8
|
|
9
9
|
# -----------------------------------------------------------------------------
|
10
10
|
|
@@ -160,6 +160,7 @@ class Exchange(object):
|
|
160
160
|
symbols = None
|
161
161
|
codes = None
|
162
162
|
timeframes = {}
|
163
|
+
tokenBucket = None
|
163
164
|
|
164
165
|
fees = {
|
165
166
|
'trading': {
|
@@ -188,6 +189,7 @@ class Exchange(object):
|
|
188
189
|
urls = None
|
189
190
|
api = None
|
190
191
|
parseJsonResponse = True
|
192
|
+
throttler = None
|
191
193
|
|
192
194
|
# PROXY & USER-AGENTS (see "examples/proxy-usage" file for explanation)
|
193
195
|
proxy = None # for backwards compatibility
|
@@ -223,6 +225,7 @@ class Exchange(object):
|
|
223
225
|
}
|
224
226
|
headers = None
|
225
227
|
origin = '*' # CORS origin
|
228
|
+
MAX_VALUE = float('inf')
|
226
229
|
#
|
227
230
|
proxies = None
|
228
231
|
|
@@ -404,15 +407,8 @@ class Exchange(object):
|
|
404
407
|
else:
|
405
408
|
setattr(self, key, settings[key])
|
406
409
|
|
407
|
-
if self.markets:
|
408
|
-
self.set_markets(self.markets)
|
409
|
-
|
410
410
|
self.after_construct()
|
411
411
|
|
412
|
-
is_sandbox = self.safe_bool_2(self.options, 'sandbox', 'testnet', False)
|
413
|
-
if is_sandbox:
|
414
|
-
self.set_sandbox_mode(is_sandbox)
|
415
|
-
|
416
412
|
# convert all properties from underscore notation foo_bar to camelcase notation fooBar
|
417
413
|
cls = type(self)
|
418
414
|
for name in dir(self):
|
@@ -431,13 +427,6 @@ class Exchange(object):
|
|
431
427
|
else:
|
432
428
|
setattr(self, camelcase, attr)
|
433
429
|
|
434
|
-
self.tokenBucket = self.extend({
|
435
|
-
'refillRate': 1.0 / self.rateLimit if self.rateLimit > 0 else float('inf'),
|
436
|
-
'delay': 0.001,
|
437
|
-
'capacity': 1.0,
|
438
|
-
'defaultCost': 1.0,
|
439
|
-
}, getattr(self, 'tokenBucket', {}))
|
440
|
-
|
441
430
|
if not self.session and self.synchronous:
|
442
431
|
self.session = Session()
|
443
432
|
self.session.trust_env = self.requests_trust_env
|
@@ -456,6 +445,10 @@ class Exchange(object):
|
|
456
445
|
def __str__(self):
|
457
446
|
return self.name
|
458
447
|
|
448
|
+
def init_throttler(self, cost=None):
|
449
|
+
# stub in sync
|
450
|
+
pass
|
451
|
+
|
459
452
|
def throttle(self, cost=None):
|
460
453
|
now = float(self.milliseconds())
|
461
454
|
elapsed = now - self.lastRestRequestTimestamp
|
@@ -2745,8 +2738,35 @@ class Exchange(object):
|
|
2745
2738
|
return timestamp
|
2746
2739
|
|
2747
2740
|
def after_construct(self):
|
2741
|
+
# networks
|
2748
2742
|
self.create_networks_by_id_object()
|
2749
2743
|
self.features_generator()
|
2744
|
+
# init predefined markets if any
|
2745
|
+
if self.markets:
|
2746
|
+
self.set_markets(self.markets)
|
2747
|
+
# init the request rate limiter
|
2748
|
+
self.init_rest_rate_limiter()
|
2749
|
+
# sanbox mode
|
2750
|
+
isSandbox = self.safe_bool_2(self.options, 'sandbox', 'testnet', False)
|
2751
|
+
if isSandbox:
|
2752
|
+
self.set_sandbox_mode(isSandbox)
|
2753
|
+
|
2754
|
+
def init_rest_rate_limiter(self):
|
2755
|
+
if self.rateLimit is None or (self.id is not None and self.rateLimit == -1):
|
2756
|
+
raise ExchangeError(self.id + '.rateLimit property is not configured')
|
2757
|
+
refillRate = self.MAX_VALUE
|
2758
|
+
if self.rateLimit > 0:
|
2759
|
+
refillRate = 1 / self.rateLimit
|
2760
|
+
defaultBucket = {
|
2761
|
+
'delay': 0.001,
|
2762
|
+
'capacity': 1,
|
2763
|
+
'cost': 1,
|
2764
|
+
'maxCapacity': 1000,
|
2765
|
+
'refillRate': refillRate,
|
2766
|
+
}
|
2767
|
+
existingBucket = {} if (self.tokenBucket is None) else self.tokenBucket
|
2768
|
+
self.tokenBucket = self.extend(defaultBucket, existingBucket)
|
2769
|
+
self.init_throttler()
|
2750
2770
|
|
2751
2771
|
def features_generator(self):
|
2752
2772
|
#
|
@@ -4665,20 +4685,27 @@ class Exchange(object):
|
|
4665
4685
|
:param str [defaultValue]: assigned programatically in the method calling handleMarketTypeAndParams
|
4666
4686
|
:returns [str, dict]: the market type and params with type and defaultType omitted
|
4667
4687
|
"""
|
4668
|
-
|
4669
|
-
|
4670
|
-
|
4688
|
+
# type from param
|
4689
|
+
type = self.safe_string_2(params, 'defaultType', 'type')
|
4690
|
+
if type is not None:
|
4691
|
+
params = self.omit(params, ['defaultType', 'type'])
|
4692
|
+
return [type, params]
|
4693
|
+
# type from market
|
4694
|
+
if market is not None:
|
4695
|
+
return [market['type'], params]
|
4696
|
+
# type from default-argument
|
4697
|
+
if defaultValue is not None:
|
4698
|
+
return [defaultValue, params]
|
4671
4699
|
methodOptions = self.safe_dict(self.options, methodName)
|
4672
|
-
|
4673
|
-
if methodOptions is not None: # user defined methodType takes precedence over defaultValue
|
4700
|
+
if methodOptions is not None:
|
4674
4701
|
if isinstance(methodOptions, str):
|
4675
|
-
|
4702
|
+
return [methodOptions, params]
|
4676
4703
|
else:
|
4677
|
-
|
4678
|
-
|
4679
|
-
|
4680
|
-
|
4681
|
-
return [
|
4704
|
+
typeFromMethod = self.safe_string_2(methodOptions, 'defaultType', 'type')
|
4705
|
+
if typeFromMethod is not None:
|
4706
|
+
return [typeFromMethod, params]
|
4707
|
+
defaultType = self.safe_string_2(self.options, 'defaultType', 'type', 'spot')
|
4708
|
+
return [defaultType, params]
|
4682
4709
|
|
4683
4710
|
def handle_sub_type_and_params(self, methodName: str, market=None, params={}, defaultValue=None):
|
4684
4711
|
subType = None
|
ccxt/bingx.py
CHANGED
@@ -4454,13 +4454,12 @@ class bingx(Exchange, ImplicitAPI):
|
|
4454
4454
|
:param boolean [params.twap]: if fetching twap orders
|
4455
4455
|
:returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
|
4456
4456
|
"""
|
4457
|
-
if symbol is None:
|
4458
|
-
raise ArgumentsRequired(self.id + ' fetchClosedOrders() requires a symbol argument')
|
4459
4457
|
self.load_markets()
|
4460
|
-
market =
|
4461
|
-
request: dict = {
|
4462
|
-
|
4463
|
-
|
4458
|
+
market = None
|
4459
|
+
request: dict = {}
|
4460
|
+
if symbol is not None:
|
4461
|
+
market = self.market(symbol)
|
4462
|
+
request['symbol'] = market['id']
|
4464
4463
|
type = None
|
4465
4464
|
subType = None
|
4466
4465
|
standard = None
|
ccxt/bybit.py
CHANGED
@@ -73,7 +73,9 @@ class bybit(Exchange, ImplicitAPI):
|
|
73
73
|
'createTrailingAmountOrder': True,
|
74
74
|
'createTriggerOrder': True,
|
75
75
|
'editOrder': True,
|
76
|
+
'editOrders': True,
|
76
77
|
'fetchBalance': True,
|
78
|
+
'fetchBidsAsks': 'emulated',
|
77
79
|
'fetchBorrowInterest': False, # temporarily disabled, doesn't work
|
78
80
|
'fetchBorrowRateHistories': False,
|
79
81
|
'fetchBorrowRateHistory': False,
|
@@ -254,6 +256,7 @@ class bybit(Exchange, ImplicitAPI):
|
|
254
256
|
'v5/spot-lever-token/reference': 5,
|
255
257
|
# spot margin trade
|
256
258
|
'v5/spot-margin-trade/data': 5,
|
259
|
+
'v5/spot-margin-trade/collateral': 5,
|
257
260
|
'v5/spot-cross-margin-trade/data': 5,
|
258
261
|
'v5/spot-cross-margin-trade/pledge-token': 5,
|
259
262
|
'v5/spot-cross-margin-trade/borrow-token': 5,
|
@@ -1239,6 +1242,9 @@ class bybit(Exchange, ImplicitAPI):
|
|
1239
1242
|
'fetchOHLCV': {
|
1240
1243
|
'limit': 1000,
|
1241
1244
|
},
|
1245
|
+
'editOrders': {
|
1246
|
+
'max': 10,
|
1247
|
+
},
|
1242
1248
|
},
|
1243
1249
|
'spot': {
|
1244
1250
|
'extends': 'default',
|
@@ -2462,6 +2468,20 @@ class bybit(Exchange, ImplicitAPI):
|
|
2462
2468
|
tickerList = self.safe_list(result, 'list', [])
|
2463
2469
|
return self.parse_tickers(tickerList, parsedSymbols)
|
2464
2470
|
|
2471
|
+
def fetch_bids_asks(self, symbols: Strings = None, params={}):
|
2472
|
+
"""
|
2473
|
+
fetches the bid and ask price and volume for multiple markets
|
2474
|
+
|
2475
|
+
https://bybit-exchange.github.io/docs/v5/market/tickers
|
2476
|
+
|
2477
|
+
:param str[]|None symbols: unified symbols of the markets to fetch the bids and asks for, all markets are returned if not assigned
|
2478
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
2479
|
+
:param str [params.subType]: *contract only* 'linear', 'inverse'
|
2480
|
+
:param str [params.baseCoin]: *option only* base coin, default is 'BTC'
|
2481
|
+
:returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
|
2482
|
+
"""
|
2483
|
+
return self.fetch_tickers(symbols, params)
|
2484
|
+
|
2465
2485
|
def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
|
2466
2486
|
#
|
2467
2487
|
# [
|
@@ -3984,13 +4004,15 @@ class bybit(Exchange, ImplicitAPI):
|
|
3984
4004
|
price = self.safe_value(rawOrder, 'price')
|
3985
4005
|
orderParams = self.safe_dict(rawOrder, 'params', {})
|
3986
4006
|
orderRequest = self.create_order_request(marketId, type, side, amount, price, orderParams, isUta)
|
4007
|
+
del orderRequest['category']
|
3987
4008
|
ordersRequests.append(orderRequest)
|
3988
4009
|
symbols = self.market_symbols(orderSymbols, None, False, True, True)
|
3989
4010
|
market = self.market(symbols[0])
|
4011
|
+
unifiedMarginStatus = self.safe_integer(self.options, 'unifiedMarginStatus', 3)
|
3990
4012
|
category = None
|
3991
4013
|
category, params = self.get_bybit_type('createOrders', market, params)
|
3992
|
-
if category == 'inverse':
|
3993
|
-
raise NotSupported(self.id + ' createOrders does not allow inverse orders')
|
4014
|
+
if (category == 'inverse') and (unifiedMarginStatus < 5):
|
4015
|
+
raise NotSupported(self.id + ' createOrders does not allow inverse orders for non UTA2.0 account')
|
3994
4016
|
request: dict = {
|
3995
4017
|
'category': category,
|
3996
4018
|
'request': ordersRequests,
|
@@ -4157,6 +4179,91 @@ class bybit(Exchange, ImplicitAPI):
|
|
4157
4179
|
'id': self.safe_string(result, 'orderId'),
|
4158
4180
|
})
|
4159
4181
|
|
4182
|
+
def edit_orders(self, orders: List[OrderRequest], params={}):
|
4183
|
+
"""
|
4184
|
+
edit a list of trade orders
|
4185
|
+
|
4186
|
+
https://bybit-exchange.github.io/docs/v5/order/batch-amend
|
4187
|
+
|
4188
|
+
:param Array orders: list of orders to create, each object should contain the parameters required by createOrder, namely symbol, type, side, amount, price and params
|
4189
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
4190
|
+
:returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
4191
|
+
"""
|
4192
|
+
self.load_markets()
|
4193
|
+
ordersRequests = []
|
4194
|
+
orderSymbols = []
|
4195
|
+
for i in range(0, len(orders)):
|
4196
|
+
rawOrder = orders[i]
|
4197
|
+
symbol = self.safe_string(rawOrder, 'symbol')
|
4198
|
+
orderSymbols.append(symbol)
|
4199
|
+
id = self.safe_string(rawOrder, 'id')
|
4200
|
+
type = self.safe_string(rawOrder, 'type')
|
4201
|
+
side = self.safe_string(rawOrder, 'side')
|
4202
|
+
amount = self.safe_value(rawOrder, 'amount')
|
4203
|
+
price = self.safe_value(rawOrder, 'price')
|
4204
|
+
orderParams = self.safe_dict(rawOrder, 'params', {})
|
4205
|
+
orderRequest = self.edit_order_request(id, symbol, type, side, amount, price, orderParams)
|
4206
|
+
del orderRequest['category']
|
4207
|
+
ordersRequests.append(orderRequest)
|
4208
|
+
orderSymbols = self.market_symbols(orderSymbols, None, False, True, True)
|
4209
|
+
market = self.market(orderSymbols[0])
|
4210
|
+
unifiedMarginStatus = self.safe_integer(self.options, 'unifiedMarginStatus', 3)
|
4211
|
+
category = None
|
4212
|
+
category, params = self.get_bybit_type('editOrders', market, params)
|
4213
|
+
if (category == 'inverse') and (unifiedMarginStatus < 5):
|
4214
|
+
raise NotSupported(self.id + ' editOrders does not allow inverse orders for non UTA2.0 account')
|
4215
|
+
request: dict = {
|
4216
|
+
'category': category,
|
4217
|
+
'request': ordersRequests,
|
4218
|
+
}
|
4219
|
+
response = self.privatePostV5OrderAmendBatch(self.extend(request, params))
|
4220
|
+
result = self.safe_dict(response, 'result', {})
|
4221
|
+
data = self.safe_list(result, 'list', [])
|
4222
|
+
retInfo = self.safe_dict(response, 'retExtInfo', {})
|
4223
|
+
codes = self.safe_list(retInfo, 'list', [])
|
4224
|
+
# self.extend the error with the unsuccessful orders
|
4225
|
+
for i in range(0, len(codes)):
|
4226
|
+
code = codes[i]
|
4227
|
+
retCode = self.safe_integer(code, 'code')
|
4228
|
+
if retCode != 0:
|
4229
|
+
data[i] = self.extend(data[i], code)
|
4230
|
+
#
|
4231
|
+
# {
|
4232
|
+
# "retCode": 0,
|
4233
|
+
# "retMsg": "OK",
|
4234
|
+
# "result": {
|
4235
|
+
# "list": [
|
4236
|
+
# {
|
4237
|
+
# "category": "option",
|
4238
|
+
# "symbol": "ETH-30DEC22-500-C",
|
4239
|
+
# "orderId": "b551f227-7059-4fb5-a6a6-699c04dbd2f2",
|
4240
|
+
# "orderLinkId": ""
|
4241
|
+
# },
|
4242
|
+
# {
|
4243
|
+
# "category": "option",
|
4244
|
+
# "symbol": "ETH-30DEC22-700-C",
|
4245
|
+
# "orderId": "fa6a595f-1a57-483f-b9d3-30e9c8235a52",
|
4246
|
+
# "orderLinkId": ""
|
4247
|
+
# }
|
4248
|
+
# ]
|
4249
|
+
# },
|
4250
|
+
# "retExtInfo": {
|
4251
|
+
# "list": [
|
4252
|
+
# {
|
4253
|
+
# "code": 0,
|
4254
|
+
# "msg": "OK"
|
4255
|
+
# },
|
4256
|
+
# {
|
4257
|
+
# "code": 0,
|
4258
|
+
# "msg": "OK"
|
4259
|
+
# }
|
4260
|
+
# ]
|
4261
|
+
# },
|
4262
|
+
# "time": 1672222808060
|
4263
|
+
# }
|
4264
|
+
#
|
4265
|
+
return self.parse_orders(data)
|
4266
|
+
|
4160
4267
|
def cancel_order_request(self, id: str, symbol: Str = None, params={}):
|
4161
4268
|
market = self.market(symbol)
|
4162
4269
|
request: dict = {
|
ccxt/kraken.py
CHANGED
@@ -981,9 +981,9 @@ class kraken(Exchange, ImplicitAPI):
|
|
981
981
|
'high': self.safe_string(high, 1),
|
982
982
|
'low': self.safe_string(low, 1),
|
983
983
|
'bid': self.safe_string(bid, 0),
|
984
|
-
'bidVolume':
|
984
|
+
'bidVolume': self.safe_string(bid, 2),
|
985
985
|
'ask': self.safe_string(ask, 0),
|
986
|
-
'askVolume':
|
986
|
+
'askVolume': self.safe_string(ask, 2),
|
987
987
|
'vwap': vwap,
|
988
988
|
'open': self.safe_string(ticker, 'o'),
|
989
989
|
'close': last,
|
ccxt/phemex.py
CHANGED
@@ -7,7 +7,7 @@ from ccxt.base.exchange import Exchange
|
|
7
7
|
from ccxt.abstract.phemex import ImplicitAPI
|
8
8
|
import hashlib
|
9
9
|
import numbers
|
10
|
-
from ccxt.base.types import Any, Balances, Currencies, Currency, DepositAddress, Int, LeverageTier, LeverageTiers, MarginModification, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, FundingRate, Trade, Transaction, TransferEntry
|
10
|
+
from ccxt.base.types import Any, Balances, Conversion, Currencies, Currency, DepositAddress, Int, LeverageTier, LeverageTiers, MarginModification, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, FundingRate, Trade, Transaction, TransferEntry
|
11
11
|
from typing import List
|
12
12
|
from ccxt.base.errors import ExchangeError
|
13
13
|
from ccxt.base.errors import AuthenticationError
|
@@ -50,6 +50,7 @@ class phemex(Exchange, ImplicitAPI):
|
|
50
50
|
'cancelAllOrders': True,
|
51
51
|
'cancelOrder': True,
|
52
52
|
'closePosition': False,
|
53
|
+
'createConvertTrade': True,
|
53
54
|
'createOrder': True,
|
54
55
|
'createReduceOnlyOrder': True,
|
55
56
|
'createStopLimitOrder': True,
|
@@ -60,6 +61,9 @@ class phemex(Exchange, ImplicitAPI):
|
|
60
61
|
'fetchBorrowRateHistories': False,
|
61
62
|
'fetchBorrowRateHistory': False,
|
62
63
|
'fetchClosedOrders': True,
|
64
|
+
'fetchConvertQuote': True,
|
65
|
+
'fetchConvertTrade': False,
|
66
|
+
'fetchConvertTradeHistory': True,
|
63
67
|
'fetchCrossBorrowRate': False,
|
64
68
|
'fetchCrossBorrowRates': False,
|
65
69
|
'fetchCurrencies': True,
|
@@ -1078,7 +1082,7 @@ class phemex(Exchange, ImplicitAPI):
|
|
1078
1082
|
for i in range(0, len(products)):
|
1079
1083
|
market = products[i]
|
1080
1084
|
type = self.safe_string_lower(market, 'type')
|
1081
|
-
if (type == 'perpetual') or (type == 'perpetualv2') or (type == '
|
1085
|
+
if (type == 'perpetual') or (type == 'perpetualv2') or (type == 'perpetualpilot'):
|
1082
1086
|
id = self.safe_string(market, 'symbol')
|
1083
1087
|
riskLimitValues = self.safe_value(riskLimitsById, id, {})
|
1084
1088
|
market = self.extend(market, riskLimitValues)
|
@@ -1254,7 +1258,7 @@ class phemex(Exchange, ImplicitAPI):
|
|
1254
1258
|
precise.decimals = precise.decimals - scale
|
1255
1259
|
precise.reduce()
|
1256
1260
|
preciseString = str(precise)
|
1257
|
-
return self.
|
1261
|
+
return self.parse_to_numeric(preciseString)
|
1258
1262
|
|
1259
1263
|
def to_ev(self, amount, market: Market = None):
|
1260
1264
|
if (amount is None) or (market is None):
|
@@ -2547,7 +2551,6 @@ class phemex(Exchange, ImplicitAPI):
|
|
2547
2551
|
market = self.market(symbol)
|
2548
2552
|
requestSide = self.capitalize(side)
|
2549
2553
|
type = self.capitalize(type)
|
2550
|
-
reduceOnly = self.safe_bool(params, 'reduceOnly')
|
2551
2554
|
request: dict = {
|
2552
2555
|
# common
|
2553
2556
|
'symbol': market['id'],
|
@@ -2630,8 +2633,10 @@ class phemex(Exchange, ImplicitAPI):
|
|
2630
2633
|
posSide = self.safe_string_lower(params, 'posSide')
|
2631
2634
|
if posSide is None:
|
2632
2635
|
if hedged:
|
2636
|
+
reduceOnly = self.safe_bool(params, 'reduceOnly')
|
2633
2637
|
if reduceOnly:
|
2634
2638
|
side = 'sell' if (side == 'buy') else 'buy'
|
2639
|
+
params = self.omit(params, 'reduceOnly')
|
2635
2640
|
posSide = 'Long' if (side == 'buy') else 'Short'
|
2636
2641
|
else:
|
2637
2642
|
posSide = 'Merged'
|
@@ -2709,7 +2714,6 @@ class phemex(Exchange, ImplicitAPI):
|
|
2709
2714
|
else:
|
2710
2715
|
request['stopLossEp'] = self.to_ep(stopLossPrice, market)
|
2711
2716
|
params = self.omit(params, 'stopLossPrice')
|
2712
|
-
params = self.omit(params, 'reduceOnly')
|
2713
2717
|
response = None
|
2714
2718
|
if market['settle'] == 'USDT':
|
2715
2719
|
response = self.privatePostGOrders(self.extend(request, params))
|
@@ -4760,6 +4764,218 @@ class phemex(Exchange, ImplicitAPI):
|
|
4760
4764
|
'datetime': self.iso8601(timestamp),
|
4761
4765
|
}, market)
|
4762
4766
|
|
4767
|
+
def fetch_convert_quote(self, fromCode: str, toCode: str, amount: Num = None, params={}) -> Conversion:
|
4768
|
+
"""
|
4769
|
+
fetch a quote for converting from one currency to another
|
4770
|
+
|
4771
|
+
https://phemex-docs.github.io/#rfq-quote
|
4772
|
+
|
4773
|
+
:param str fromCode: the currency that you want to sell and convert from
|
4774
|
+
:param str toCode: the currency that you want to buy and convert into
|
4775
|
+
:param float amount: how much you want to trade in units of the from currency
|
4776
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
4777
|
+
:returns dict: a `conversion structure <https://docs.ccxt.com/#/?id=conversion-structure>`
|
4778
|
+
"""
|
4779
|
+
self.load_markets()
|
4780
|
+
fromCurrency = self.currency(fromCode)
|
4781
|
+
toCurrency = self.currency(toCode)
|
4782
|
+
valueScale = self.safe_integer(fromCurrency, 'valueScale')
|
4783
|
+
request: dict = {
|
4784
|
+
'fromCurrency': fromCode,
|
4785
|
+
'toCurrency': toCode,
|
4786
|
+
'fromAmountEv': self.to_en(amount, valueScale),
|
4787
|
+
}
|
4788
|
+
response = self.privateGetAssetsQuote(self.extend(request, params))
|
4789
|
+
#
|
4790
|
+
# {
|
4791
|
+
# "code": 0,
|
4792
|
+
# "msg": "OK",
|
4793
|
+
# "data": {
|
4794
|
+
# "code": "GIF...AAA",
|
4795
|
+
# "quoteArgs": {
|
4796
|
+
# "origin": 10,
|
4797
|
+
# "price": "0.00000939",
|
4798
|
+
# "proceeds": "0.00000000",
|
4799
|
+
# "ttlMs": 7000,
|
4800
|
+
# "expireAt": 1739875826009,
|
4801
|
+
# "requestAt": 1739875818009,
|
4802
|
+
# "quoteAt": 1739875816594
|
4803
|
+
# }
|
4804
|
+
# }
|
4805
|
+
# }
|
4806
|
+
#
|
4807
|
+
data = self.safe_dict(response, 'data', {})
|
4808
|
+
return self.parse_conversion(data, fromCurrency, toCurrency)
|
4809
|
+
|
4810
|
+
def create_convert_trade(self, id: str, fromCode: str, toCode: str, amount: Num = None, params={}) -> Conversion:
|
4811
|
+
"""
|
4812
|
+
convert from one currency to another
|
4813
|
+
|
4814
|
+
https://phemex-docs.github.io/#convert
|
4815
|
+
|
4816
|
+
:param str id: the id of the trade that you want to make
|
4817
|
+
:param str fromCode: the currency that you want to sell and convert from
|
4818
|
+
:param str toCode: the currency that you want to buy and convert into
|
4819
|
+
:param float [amount]: how much you want to trade in units of the from currency
|
4820
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
4821
|
+
:returns dict: a `conversion structure <https://docs.ccxt.com/#/?id=conversion-structure>`
|
4822
|
+
"""
|
4823
|
+
self.load_markets()
|
4824
|
+
fromCurrency = self.currency(fromCode)
|
4825
|
+
toCurrency = self.currency(toCode)
|
4826
|
+
valueScale = self.safe_integer(fromCurrency, 'valueScale')
|
4827
|
+
request: dict = {
|
4828
|
+
'code': id,
|
4829
|
+
'fromCurrency': fromCode,
|
4830
|
+
'toCurrency': toCode,
|
4831
|
+
}
|
4832
|
+
if amount is not None:
|
4833
|
+
request['fromAmountEv'] = self.to_en(amount, valueScale)
|
4834
|
+
response = self.privatePostAssetsConvert(self.extend(request, params))
|
4835
|
+
#
|
4836
|
+
# {
|
4837
|
+
# "code": 0,
|
4838
|
+
# "msg": "OK",
|
4839
|
+
# "data": {
|
4840
|
+
# "moveOp": 0,
|
4841
|
+
# "fromCurrency": "USDT",
|
4842
|
+
# "toCurrency": "BTC",
|
4843
|
+
# "fromAmountEv": 4000000000,
|
4844
|
+
# "toAmountEv": 41511,
|
4845
|
+
# "linkKey": "45c8ed8e-d3f4-472d-8262-e464e8c46247",
|
4846
|
+
# "status": 10
|
4847
|
+
# }
|
4848
|
+
# }
|
4849
|
+
#
|
4850
|
+
data = self.safe_dict(response, 'data', {})
|
4851
|
+
fromCurrencyId = self.safe_string(data, 'fromCurrency')
|
4852
|
+
fromResult = self.safe_currency(fromCurrencyId, fromCurrency)
|
4853
|
+
toCurrencyId = self.safe_string(data, 'toCurrency')
|
4854
|
+
to = self.safe_currency(toCurrencyId, toCurrency)
|
4855
|
+
return self.parse_conversion(data, fromResult, to)
|
4856
|
+
|
4857
|
+
def fetch_convert_trade_history(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Conversion]:
|
4858
|
+
"""
|
4859
|
+
fetch the users history of conversion trades
|
4860
|
+
|
4861
|
+
https://phemex-docs.github.io/#query-convert-history
|
4862
|
+
|
4863
|
+
:param str [code]: the unified currency code
|
4864
|
+
:param int [since]: the earliest time in ms to fetch conversions for
|
4865
|
+
:param int [limit]: the maximum number of conversion structures to retrieve, default 20, max 200
|
4866
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
4867
|
+
:param str [params.until]: the end time in ms
|
4868
|
+
:param str [params.fromCurrency]: the currency that you sold and converted from
|
4869
|
+
:param str [params.toCurrency]: the currency that you bought and converted into
|
4870
|
+
:returns dict[]: a list of `conversion structures <https://docs.ccxt.com/#/?id=conversion-structure>`
|
4871
|
+
"""
|
4872
|
+
self.load_markets()
|
4873
|
+
request: dict = {}
|
4874
|
+
if code is not None:
|
4875
|
+
request['fromCurrency'] = code
|
4876
|
+
if since is not None:
|
4877
|
+
request['startTime'] = since
|
4878
|
+
if limit is not None:
|
4879
|
+
request['limit'] = limit
|
4880
|
+
request, params = self.handle_until_option('endTime', request, params)
|
4881
|
+
response = self.privateGetAssetsConvert(self.extend(request, params))
|
4882
|
+
#
|
4883
|
+
# {
|
4884
|
+
# "code": 0,
|
4885
|
+
# "msg": "OK",
|
4886
|
+
# "data": {
|
4887
|
+
# "total": 2,
|
4888
|
+
# "rows": [
|
4889
|
+
# {
|
4890
|
+
# "linkKey": "45c8ed8e-d3f4-472d-8262-e464e8c46247",
|
4891
|
+
# "createTime": 1739882294000,
|
4892
|
+
# "fromCurrency": "USDT",
|
4893
|
+
# "toCurrency": "BTC",
|
4894
|
+
# "fromAmountEv": 4000000000,
|
4895
|
+
# "toAmountEv": 41511,
|
4896
|
+
# "status": 10,
|
4897
|
+
# "conversionRate": 1037,
|
4898
|
+
# "errorCode": 0
|
4899
|
+
# },
|
4900
|
+
# ]
|
4901
|
+
# }
|
4902
|
+
# }
|
4903
|
+
#
|
4904
|
+
data = self.safe_dict(response, 'data', {})
|
4905
|
+
rows = self.safe_list(data, 'rows', [])
|
4906
|
+
return self.parse_conversions(rows, code, 'fromCurrency', 'toCurrency', since, limit)
|
4907
|
+
|
4908
|
+
def parse_conversion(self, conversion: dict, fromCurrency: Currency = None, toCurrency: Currency = None) -> Conversion:
|
4909
|
+
#
|
4910
|
+
# fetchConvertQuote
|
4911
|
+
#
|
4912
|
+
# {
|
4913
|
+
# "code": "GIF...AAA",
|
4914
|
+
# "quoteArgs": {
|
4915
|
+
# "origin": 10,
|
4916
|
+
# "price": "0.00000939",
|
4917
|
+
# "proceeds": "0.00000000",
|
4918
|
+
# "ttlMs": 7000,
|
4919
|
+
# "expireAt": 1739875826009,
|
4920
|
+
# "requestAt": 1739875818009,
|
4921
|
+
# "quoteAt": 1739875816594
|
4922
|
+
# }
|
4923
|
+
# }
|
4924
|
+
#
|
4925
|
+
# createConvertTrade
|
4926
|
+
#
|
4927
|
+
# {
|
4928
|
+
# "moveOp": 0,
|
4929
|
+
# "fromCurrency": "USDT",
|
4930
|
+
# "toCurrency": "BTC",
|
4931
|
+
# "fromAmountEv": 4000000000,
|
4932
|
+
# "toAmountEv": 41511,
|
4933
|
+
# "linkKey": "45c8ed8e-d3f4-472d-8262-e464e8c46247",
|
4934
|
+
# "status": 10
|
4935
|
+
# }
|
4936
|
+
#
|
4937
|
+
# fetchConvertTradeHistory
|
4938
|
+
#
|
4939
|
+
# {
|
4940
|
+
# "linkKey": "45c8ed8e-d3f4-472d-8262-e464e8c46247",
|
4941
|
+
# "createTime": 1739882294000,
|
4942
|
+
# "fromCurrency": "USDT",
|
4943
|
+
# "toCurrency": "BTC",
|
4944
|
+
# "fromAmountEv": 4000000000,
|
4945
|
+
# "toAmountEv": 41511,
|
4946
|
+
# "status": 10,
|
4947
|
+
# "conversionRate": 1037,
|
4948
|
+
# "errorCode": 0
|
4949
|
+
# }
|
4950
|
+
#
|
4951
|
+
quoteArgs = self.safe_dict(conversion, 'quoteArgs', {})
|
4952
|
+
requestTime = self.safe_integer(quoteArgs, 'requestAt')
|
4953
|
+
timestamp = self.safe_integer(conversion, 'createTime', requestTime)
|
4954
|
+
fromCoin = self.safe_string(conversion, 'fromCurrency', self.safe_string(fromCurrency, 'code'))
|
4955
|
+
fromCode = self.safe_currency_code(fromCoin, fromCurrency)
|
4956
|
+
toCoin = self.safe_string(conversion, 'toCurrency', self.safe_string(toCurrency, 'code'))
|
4957
|
+
toCode = self.safe_currency_code(toCoin, toCurrency)
|
4958
|
+
fromValueScale = self.safe_integer(fromCurrency, 'valueScale')
|
4959
|
+
toValueScale = self.safe_integer(toCurrency, 'valueScale')
|
4960
|
+
fromAmount = self.from_en(self.safe_string(conversion, 'fromAmountEv'), fromValueScale)
|
4961
|
+
if fromAmount is None and quoteArgs is not None:
|
4962
|
+
fromAmount = self.from_en(self.safe_string(quoteArgs, 'origin'), fromValueScale)
|
4963
|
+
toAmount = self.from_en(self.safe_string(conversion, 'toAmountEv'), toValueScale)
|
4964
|
+
if toAmount is None and quoteArgs is not None:
|
4965
|
+
toAmount = self.from_en(self.safe_string(quoteArgs, 'proceeds'), toValueScale)
|
4966
|
+
return {
|
4967
|
+
'info': conversion,
|
4968
|
+
'timestamp': timestamp,
|
4969
|
+
'datetime': self.iso8601(timestamp),
|
4970
|
+
'id': self.safe_string(conversion, 'code'),
|
4971
|
+
'fromCurrency': fromCode,
|
4972
|
+
'fromAmount': self.parse_number(fromAmount),
|
4973
|
+
'toCurrency': toCode,
|
4974
|
+
'toAmount': self.parse_number(toAmount),
|
4975
|
+
'price': self.safe_number(quoteArgs, 'price'),
|
4976
|
+
'fee': None,
|
4977
|
+
}
|
4978
|
+
|
4763
4979
|
def handle_errors(self, httpCode: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
|
4764
4980
|
if response is None:
|
4765
4981
|
return None # fallback to default error handler
|