ccxt 4.4.11__py2.py3-none-any.whl → 4.4.13__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/async_support/__init__.py +1 -1
- ccxt/async_support/base/exchange.py +4 -1
- ccxt/async_support/bigone.py +2 -0
- ccxt/async_support/binance.py +54 -3
- ccxt/async_support/bingx.py +84 -5
- ccxt/async_support/bitget.py +2 -0
- ccxt/async_support/bitmex.py +1 -0
- ccxt/async_support/bybit.py +4 -1
- ccxt/async_support/coinbaseinternational.py +2 -0
- ccxt/async_support/coinex.py +2 -0
- ccxt/async_support/delta.py +2 -0
- ccxt/async_support/deribit.py +2 -0
- ccxt/async_support/digifinex.py +2 -0
- ccxt/async_support/gate.py +2 -0
- ccxt/async_support/htx.py +8 -2
- ccxt/async_support/kraken.py +4 -4
- ccxt/async_support/krakenfutures.py +2 -0
- ccxt/async_support/kucoinfutures.py +2 -0
- ccxt/async_support/mexc.py +16 -4
- ccxt/async_support/okx.py +51 -20
- ccxt/async_support/oxfun.py +1 -0
- ccxt/async_support/paradex.py +1 -0
- ccxt/async_support/phemex.py +10 -3
- ccxt/async_support/poloniex.py +1 -0
- ccxt/base/exchange.py +7 -1
- ccxt/base/types.py +2 -0
- ccxt/bigone.py +2 -0
- ccxt/binance.py +54 -3
- ccxt/bingx.py +84 -5
- ccxt/bitget.py +2 -0
- ccxt/bitmex.py +1 -0
- ccxt/bybit.py +4 -1
- ccxt/coinbaseinternational.py +2 -0
- ccxt/coinex.py +2 -0
- ccxt/delta.py +2 -0
- ccxt/deribit.py +2 -0
- ccxt/digifinex.py +2 -0
- ccxt/gate.py +2 -0
- ccxt/htx.py +8 -2
- ccxt/kraken.py +4 -4
- ccxt/krakenfutures.py +2 -0
- ccxt/kucoinfutures.py +2 -0
- ccxt/mexc.py +16 -4
- ccxt/okx.py +51 -20
- ccxt/oxfun.py +1 -0
- ccxt/paradex.py +1 -0
- ccxt/phemex.py +10 -3
- ccxt/poloniex.py +1 -0
- ccxt/pro/__init__.py +1 -1
- ccxt/pro/binance.py +72 -5
- ccxt/pro/bitfinex.py +8 -8
- ccxt/pro/krakenfutures.py +2 -0
- ccxt/pro/phemex.py +2 -0
- ccxt/pro/woo.py +69 -0
- ccxt/test/tests_async.py +31 -3
- ccxt/test/tests_helpers.py +13 -36
- ccxt/test/tests_init.py +6 -2
- ccxt/test/tests_sync.py +31 -3
- {ccxt-4.4.11.dist-info → ccxt-4.4.13.dist-info}/METADATA +4 -5
- {ccxt-4.4.11.dist-info → ccxt-4.4.13.dist-info}/RECORD +64 -64
- {ccxt-4.4.11.dist-info → ccxt-4.4.13.dist-info}/LICENSE.txt +0 -0
- {ccxt-4.4.11.dist-info → ccxt-4.4.13.dist-info}/WHEEL +0 -0
- {ccxt-4.4.11.dist-info → ccxt-4.4.13.dist-info}/top_level.txt +0 -0
ccxt/kraken.py
CHANGED
@@ -1740,7 +1740,7 @@ class kraken(Exchange, ImplicitAPI):
|
|
1740
1740
|
request['volume'] = self.cost_to_precision(symbol, cost)
|
1741
1741
|
extendedOflags = flags + ',viqc' if (flags is not None) else 'viqc'
|
1742
1742
|
request['oflags'] = extendedOflags
|
1743
|
-
elif isLimitOrder and not isTrailingAmountOrder:
|
1743
|
+
elif isLimitOrder and not isTrailingAmountOrder and not isTrailingPercentOrder:
|
1744
1744
|
request['price'] = self.price_to_precision(symbol, price)
|
1745
1745
|
reduceOnly = self.safe_bool_2(params, 'reduceOnly', 'reduce_only')
|
1746
1746
|
if isStopLossOrTakeProfitTrigger:
|
@@ -1760,8 +1760,8 @@ class kraken(Exchange, ImplicitAPI):
|
|
1760
1760
|
request['price2'] = self.price_to_precision(symbol, price)
|
1761
1761
|
elif isTrailingAmountOrder or isTrailingPercentOrder:
|
1762
1762
|
trailingPercentString = None
|
1763
|
-
if
|
1764
|
-
trailingPercentString = trailingPercent if (trailingPercent.endswith('%')) else '+' +
|
1763
|
+
if trailingPercent is not None:
|
1764
|
+
trailingPercentString = ('+' + trailingPercent) if (trailingPercent.endswith('%')) else ('+' + trailingPercent + '%')
|
1765
1765
|
trailingAmountString = '+' + trailingAmount if (trailingAmount is not None) else None # must use + for self
|
1766
1766
|
offset = self.safe_string(params, 'offset', '-') # can use + or - for self
|
1767
1767
|
trailingLimitAmountString = offset + self.number_to_string(trailingLimitAmount) if (trailingLimitAmount is not None) else None
|
@@ -1770,7 +1770,7 @@ class kraken(Exchange, ImplicitAPI):
|
|
1770
1770
|
if isLimitOrder or (trailingLimitAmount is not None) or (trailingLimitPercent is not None):
|
1771
1771
|
request['ordertype'] = 'trailing-stop-limit'
|
1772
1772
|
if trailingLimitPercent is not None:
|
1773
|
-
trailingLimitPercentString = trailingLimitPercent if (trailingLimitPercent.endswith('%')) else (
|
1773
|
+
trailingLimitPercentString = (offset + trailingLimitPercent) if (trailingLimitPercent.endswith('%')) else (offset + trailingLimitPercent + '%')
|
1774
1774
|
request['price'] = trailingPercentString
|
1775
1775
|
request['price2'] = trailingLimitPercentString
|
1776
1776
|
elif trailingLimitAmount is not None:
|
ccxt/krakenfutures.py
CHANGED
@@ -611,6 +611,8 @@ class krakenfutures(Exchange, ImplicitAPI):
|
|
611
611
|
'average': average,
|
612
612
|
'baseVolume': baseVolume,
|
613
613
|
'quoteVolume': quoteVolume,
|
614
|
+
'markPrice': self.safe_string(ticker, 'markPrice'),
|
615
|
+
'indexPrice': self.safe_string(ticker, 'indexPrice'),
|
614
616
|
'info': ticker,
|
615
617
|
})
|
616
618
|
|
ccxt/kucoinfutures.py
CHANGED
@@ -944,6 +944,8 @@ class kucoinfutures(kucoin, ImplicitAPI):
|
|
944
944
|
'average': None,
|
945
945
|
'baseVolume': self.safe_string(ticker, 'volumeOf24h'),
|
946
946
|
'quoteVolume': self.safe_string(ticker, 'turnoverOf24h'),
|
947
|
+
'markPrice': self.safe_string(ticker, 'markPrice'),
|
948
|
+
'indexPrice': self.safe_string(ticker, 'indexPrice'),
|
947
949
|
'info': ticker,
|
948
950
|
}, market)
|
949
951
|
|
ccxt/mexc.py
CHANGED
@@ -2054,6 +2054,7 @@ class mexc(Exchange, ImplicitAPI):
|
|
2054
2054
|
:param float [params.triggerPrice]: The price at which a trigger order is triggered at
|
2055
2055
|
:param bool [params.postOnly]: if True, the order will only be posted if it will be a maker order
|
2056
2056
|
:param bool [params.reduceOnly]: *contract only* indicates if self order is to reduce the size of a position
|
2057
|
+
:param bool [params.hedged]: *swap only* True for hedged mode, False for one way mode, default is False
|
2057
2058
|
*
|
2058
2059
|
* EXCHANGE SPECIFIC PARAMETERS
|
2059
2060
|
:param int [params.leverage]: *contract only* leverage is necessary on isolated margin
|
@@ -2179,6 +2180,7 @@ class mexc(Exchange, ImplicitAPI):
|
|
2179
2180
|
:param float [params.triggerPrice]: The price at which a trigger order is triggered at
|
2180
2181
|
:param bool [params.postOnly]: if True, the order will only be posted if it will be a maker order
|
2181
2182
|
:param bool [params.reduceOnly]: indicates if self order is to reduce the size of a position
|
2183
|
+
:param bool [params.hedged]: *swap only* True for hedged mode, False for one way mode, default is False
|
2182
2184
|
*
|
2183
2185
|
* EXCHANGE SPECIFIC PARAMETERS
|
2184
2186
|
:param int [params.leverage]: leverage is necessary on isolated margin
|
@@ -2246,15 +2248,25 @@ class mexc(Exchange, ImplicitAPI):
|
|
2246
2248
|
if leverage is None:
|
2247
2249
|
raise ArgumentsRequired(self.id + ' createSwapOrder() requires a leverage parameter for isolated margin orders')
|
2248
2250
|
reduceOnly = self.safe_bool(params, 'reduceOnly', False)
|
2249
|
-
|
2250
|
-
|
2251
|
+
hedged = self.safe_bool(params, 'hedged', False)
|
2252
|
+
sideInteger = None
|
2253
|
+
if hedged:
|
2254
|
+
if reduceOnly:
|
2255
|
+
params = self.omit(params, 'reduceOnly') # hedged mode does not accept self parameter
|
2256
|
+
side = 'sell' if (side == 'buy') else 'buy'
|
2257
|
+
sideInteger = 1 if (side == 'buy') else 3
|
2258
|
+
request['positionMode'] = 1
|
2251
2259
|
else:
|
2252
|
-
|
2260
|
+
if reduceOnly:
|
2261
|
+
sideInteger = 2 if (side == 'buy') else 4
|
2262
|
+
else:
|
2263
|
+
sideInteger = 1 if (side == 'buy') else 3
|
2264
|
+
request['side'] = sideInteger
|
2253
2265
|
clientOrderId = self.safe_string_2(params, 'clientOrderId', 'externalOid')
|
2254
2266
|
if clientOrderId is not None:
|
2255
2267
|
request['externalOid'] = clientOrderId
|
2256
2268
|
stopPrice = self.safe_number_2(params, 'triggerPrice', 'stopPrice')
|
2257
|
-
params = self.omit(params, ['clientOrderId', 'externalOid', 'postOnly', 'stopPrice', 'triggerPrice'])
|
2269
|
+
params = self.omit(params, ['clientOrderId', 'externalOid', 'postOnly', 'stopPrice', 'triggerPrice', 'hedged'])
|
2258
2270
|
response = None
|
2259
2271
|
if stopPrice:
|
2260
2272
|
request['triggerPrice'] = self.price_to_precision(symbol, stopPrice)
|
ccxt/okx.py
CHANGED
@@ -120,6 +120,7 @@ class okx(Exchange, ImplicitAPI):
|
|
120
120
|
'fetchMarketLeverageTiers': True,
|
121
121
|
'fetchMarkets': True,
|
122
122
|
'fetchMarkOHLCV': True,
|
123
|
+
'fetchMarkPrices': True,
|
123
124
|
'fetchMySettlementHistory': False,
|
124
125
|
'fetchMyTrades': True,
|
125
126
|
'fetchOHLCV': True,
|
@@ -1784,6 +1785,13 @@ class okx(Exchange, ImplicitAPI):
|
|
1784
1785
|
return self.parse_order_book(first, symbol, timestamp)
|
1785
1786
|
|
1786
1787
|
def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
|
1788
|
+
#
|
1789
|
+
# {
|
1790
|
+
# "instType":"SWAP",
|
1791
|
+
# "instId":"BTC-USDT-SWAP",
|
1792
|
+
# "markPx":"200",
|
1793
|
+
# "ts":"1597026383085"
|
1794
|
+
# }
|
1787
1795
|
#
|
1788
1796
|
# {
|
1789
1797
|
# "instType": "SPOT",
|
@@ -1835,6 +1843,7 @@ class okx(Exchange, ImplicitAPI):
|
|
1835
1843
|
'average': None,
|
1836
1844
|
'baseVolume': baseVolume,
|
1837
1845
|
'quoteVolume': quoteVolume,
|
1846
|
+
'markPrice': self.safe_string(ticker, 'markPx'),
|
1838
1847
|
'info': ticker,
|
1839
1848
|
}, market)
|
1840
1849
|
|
@@ -1935,6 +1944,33 @@ class okx(Exchange, ImplicitAPI):
|
|
1935
1944
|
tickers = self.safe_list(response, 'data', [])
|
1936
1945
|
return self.parse_tickers(tickers, symbols)
|
1937
1946
|
|
1947
|
+
def fetch_mark_prices(self, symbols: Strings = None, params={}) -> Tickers:
|
1948
|
+
"""
|
1949
|
+
fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
|
1950
|
+
:see: https://www.okx.com/docs-v5/en/#public-data-rest-api-get-mark-price
|
1951
|
+
:param str[] [symbols]: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
|
1952
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
1953
|
+
:returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
|
1954
|
+
"""
|
1955
|
+
self.load_markets()
|
1956
|
+
symbols = self.market_symbols(symbols)
|
1957
|
+
market = self.get_market_from_symbols(symbols)
|
1958
|
+
marketType = None
|
1959
|
+
marketType, params = self.handle_market_type_and_params('fetchTickers', market, params, 'swap')
|
1960
|
+
request: dict = {
|
1961
|
+
'instType': self.convert_to_instrument_type(marketType),
|
1962
|
+
}
|
1963
|
+
if marketType == 'option':
|
1964
|
+
defaultUnderlying = self.safe_string(self.options, 'defaultUnderlying', 'BTC-USD')
|
1965
|
+
currencyId = self.safe_string_2(params, 'uly', 'marketId', defaultUnderlying)
|
1966
|
+
if currencyId is None:
|
1967
|
+
raise ArgumentsRequired(self.id + ' fetchTickers() requires an underlying uly or marketId parameter for options markets')
|
1968
|
+
else:
|
1969
|
+
request['uly'] = currencyId
|
1970
|
+
response = self.publicGetPublicMarkPrice(self.extend(request, params))
|
1971
|
+
tickers = self.safe_list(response, 'data', [])
|
1972
|
+
return self.parse_tickers(tickers, symbols)
|
1973
|
+
|
1938
1974
|
def parse_trade(self, trade: dict, market: Market = None) -> Trade:
|
1939
1975
|
#
|
1940
1976
|
# public fetchTrades
|
@@ -2794,7 +2830,7 @@ class okx(Exchange, ImplicitAPI):
|
|
2794
2830
|
:param str [params.positionSide]: if position mode is one-way: set to 'net', if position mode is hedge-mode: set to 'long' or 'short'
|
2795
2831
|
:param str [params.trailingPercent]: the percent to trail away from the current market price
|
2796
2832
|
:param str [params.tpOrdKind]: 'condition' or 'limit', the default is 'condition'
|
2797
|
-
:param
|
2833
|
+
:param bool [params.hedged]: *swap and future only* True for hedged mode, False for one way mode
|
2798
2834
|
:returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
2799
2835
|
"""
|
2800
2836
|
self.load_markets()
|
@@ -4527,32 +4563,27 @@ class okx(Exchange, ImplicitAPI):
|
|
4527
4563
|
:see: https://www.okx.com/docs-v5/en/#funding-account-rest-api-get-deposit-address
|
4528
4564
|
:param str code: unified currency code
|
4529
4565
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
4566
|
+
:param str [params.network]: the network name for the deposit address
|
4530
4567
|
:returns dict: an `address structure <https://docs.ccxt.com/#/?id=address-structure>`
|
4531
4568
|
"""
|
4569
|
+
self.load_markets()
|
4532
4570
|
rawNetwork = self.safe_string_upper(params, 'network')
|
4533
|
-
networks = self.safe_value(self.options, 'networks', {})
|
4534
|
-
network = self.safe_string(networks, rawNetwork, rawNetwork)
|
4535
4571
|
params = self.omit(params, 'network')
|
4572
|
+
code = self.safe_currency_code(code)
|
4573
|
+
network = self.network_id_to_code(rawNetwork, code)
|
4536
4574
|
response = self.fetch_deposit_addresses_by_network(code, params)
|
4537
|
-
|
4538
|
-
|
4539
|
-
result = self.safe_value(response, code)
|
4575
|
+
if network is not None:
|
4576
|
+
result = self.safe_dict(response, network)
|
4540
4577
|
if result is None:
|
4541
|
-
|
4542
|
-
result = self.safe_value(response, alias)
|
4543
|
-
if result is None:
|
4544
|
-
defaultNetwork = self.safe_string(self.options, 'defaultNetwork', 'ERC20')
|
4545
|
-
result = self.safe_value(response, defaultNetwork)
|
4546
|
-
if result is None:
|
4547
|
-
values = list(response.values())
|
4548
|
-
result = self.safe_value(values, 0)
|
4549
|
-
if result is None:
|
4550
|
-
raise InvalidAddress(self.id + ' fetchDepositAddress() cannot find deposit address for ' + code)
|
4578
|
+
raise InvalidAddress(self.id + ' fetchDepositAddress() cannot find ' + network + ' deposit address for ' + code)
|
4551
4579
|
return result
|
4552
|
-
|
4553
|
-
if
|
4554
|
-
|
4555
|
-
return
|
4580
|
+
codeNetwork = self.network_id_to_code(code, code)
|
4581
|
+
if codeNetwork in response:
|
4582
|
+
return response[codeNetwork]
|
4583
|
+
# if the network is not specified, return the first address
|
4584
|
+
keys = list(response.keys())
|
4585
|
+
first = self.safe_string(keys, 0)
|
4586
|
+
return self.safe_dict(response, first)
|
4556
4587
|
|
4557
4588
|
def withdraw(self, code: str, amount: float, address: str, tag=None, params={}):
|
4558
4589
|
"""
|
ccxt/oxfun.py
CHANGED
@@ -858,6 +858,7 @@ class oxfun(Exchange, ImplicitAPI):
|
|
858
858
|
'average': None,
|
859
859
|
'baseVolume': self.safe_string(ticker, 'currencyVolume24h'),
|
860
860
|
'quoteVolume': None, # the exchange returns cost in OX
|
861
|
+
'markPrice': self.safe_string(ticker, 'markPrice'),
|
861
862
|
'info': ticker,
|
862
863
|
}, market)
|
863
864
|
|
ccxt/paradex.py
CHANGED
ccxt/phemex.py
CHANGED
@@ -2402,6 +2402,8 @@ class phemex(Exchange, ImplicitAPI):
|
|
2402
2402
|
:param float [params.takeProfit.triggerPrice]: take profit trigger price
|
2403
2403
|
:param dict [params.stopLoss]: *swap only* *stopLoss object in params* containing the triggerPrice at which the attached stop loss order will be triggered(perpetual swap markets only)
|
2404
2404
|
:param float [params.stopLoss.triggerPrice]: stop loss trigger price
|
2405
|
+
:param str [params.posSide]: *swap only* "Merged" for one way mode, "Long" for buy side of hedged mode, "Short" for sell side of hedged mode
|
2406
|
+
:param bool [params.hedged]: *swap only* True for hedged mode, False for one way mode, default is False
|
2405
2407
|
:returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
2406
2408
|
"""
|
2407
2409
|
self.load_markets()
|
@@ -2486,13 +2488,18 @@ class phemex(Exchange, ImplicitAPI):
|
|
2486
2488
|
amountString = self.number_to_string(amount)
|
2487
2489
|
request['baseQtyEv'] = self.to_ev(amountString, market)
|
2488
2490
|
elif market['swap']:
|
2491
|
+
hedged = self.safe_bool(params, 'hedged', False)
|
2492
|
+
params = self.omit(params, 'hedged')
|
2489
2493
|
posSide = self.safe_string_lower(params, 'posSide')
|
2490
2494
|
if posSide is None:
|
2491
|
-
|
2495
|
+
if hedged:
|
2496
|
+
if reduceOnly:
|
2497
|
+
side = 'sell' if (side == 'buy') else 'buy'
|
2498
|
+
posSide = 'Long' if (side == 'buy') else 'Short'
|
2499
|
+
else:
|
2500
|
+
posSide = 'Merged'
|
2492
2501
|
posSide = self.capitalize(posSide)
|
2493
2502
|
request['posSide'] = posSide
|
2494
|
-
if reduceOnly is not None:
|
2495
|
-
request['reduceOnly'] = reduceOnly
|
2496
2503
|
if market['settle'] == 'USDT':
|
2497
2504
|
request['orderQtyRq'] = amount
|
2498
2505
|
else:
|
ccxt/poloniex.py
CHANGED
@@ -633,6 +633,7 @@ class poloniex(Exchange, ImplicitAPI):
|
|
633
633
|
'average': None,
|
634
634
|
'baseVolume': self.safe_string(ticker, 'quantity'),
|
635
635
|
'quoteVolume': self.safe_string(ticker, 'amount'),
|
636
|
+
'markPrice': self.safe_string(ticker, 'markPrice'),
|
636
637
|
'info': ticker,
|
637
638
|
}, market)
|
638
639
|
|
ccxt/pro/__init__.py
CHANGED
ccxt/pro/binance.py
CHANGED
@@ -43,6 +43,8 @@ class binance(ccxt.async_support.binance):
|
|
43
43
|
'watchPositions': True,
|
44
44
|
'watchTicker': True,
|
45
45
|
'watchTickers': True,
|
46
|
+
'watchMarkPrices': True,
|
47
|
+
'watchMarkPrice': True,
|
46
48
|
'watchTrades': True,
|
47
49
|
'watchTradesForSymbols': True,
|
48
50
|
'createOrderWs': True,
|
@@ -159,6 +161,7 @@ class binance(ccxt.async_support.binance):
|
|
159
161
|
'tickerChannelsMap': {
|
160
162
|
'24hrTicker': 'ticker',
|
161
163
|
'24hrMiniTicker': 'miniTicker',
|
164
|
+
'markPriceUpdate': 'markPrice',
|
162
165
|
# rolling window tickers
|
163
166
|
'1hTicker': 'ticker_1h',
|
164
167
|
'4hTicker': 'ticker_4h',
|
@@ -1641,6 +1644,39 @@ class binance(ccxt.async_support.binance):
|
|
1641
1644
|
tickers = await self.watch_tickers([symbol], self.extend(params, {'callerMethodName': 'watchTicker'}))
|
1642
1645
|
return tickers[symbol]
|
1643
1646
|
|
1647
|
+
async def watch_mark_price(self, symbol: str, params={}) -> Ticker:
|
1648
|
+
"""
|
1649
|
+
:see: https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/Mark-Price-Stream
|
1650
|
+
watches a mark price for a specific market
|
1651
|
+
:param str symbol: unified symbol of the market to fetch the ticker for
|
1652
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
1653
|
+
:param boolean [params.use1sFreq]: *default is True* if set to True, the mark price will be updated every second, otherwise every 3 seconds
|
1654
|
+
:returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
|
1655
|
+
"""
|
1656
|
+
await self.load_markets()
|
1657
|
+
symbol = self.symbol(symbol)
|
1658
|
+
tickers = await self.watch_mark_prices([symbol], self.extend(params, {'callerMethodName': 'watchMarkPrice'}))
|
1659
|
+
return tickers[symbol]
|
1660
|
+
|
1661
|
+
async def watch_mark_prices(self, symbols: Strings = None, params={}) -> Tickers:
|
1662
|
+
"""
|
1663
|
+
:see: https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams/Mark-Price-Stream-for-All-market
|
1664
|
+
watches the mark price for all markets
|
1665
|
+
:param str[] symbols: unified symbol of the market to fetch the ticker for
|
1666
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
1667
|
+
:param boolean [params.use1sFreq]: *default is True* if set to True, the mark price will be updated every second, otherwise every 3 seconds
|
1668
|
+
:returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
|
1669
|
+
"""
|
1670
|
+
channelName = None
|
1671
|
+
# for now watchmarkPrice uses the same messageHash
|
1672
|
+
# so it's impossible to watch both at the same time
|
1673
|
+
# refactor self to use different messageHashes
|
1674
|
+
channelName, params = self.handle_option_and_params(params, 'watchMarkPrices', 'name', 'markPrice')
|
1675
|
+
newTickers = await self.watch_multi_ticker_helper('watchMarkPrices', channelName, symbols, params)
|
1676
|
+
if self.newUpdates:
|
1677
|
+
return newTickers
|
1678
|
+
return self.filter_by_array(self.tickers, 'symbol', symbols)
|
1679
|
+
|
1644
1680
|
async def watch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
|
1645
1681
|
"""
|
1646
1682
|
:see: https://binance-docs.github.io/apidocs/spot/en/#individual-symbol-mini-ticker-stream
|
@@ -1775,12 +1811,16 @@ class binance(ccxt.async_support.binance):
|
|
1775
1811
|
async def watch_multi_ticker_helper(self, methodName, channelName: str, symbols: Strings = None, params={}):
|
1776
1812
|
await self.load_markets()
|
1777
1813
|
symbols = self.market_symbols(symbols, None, True, False, True)
|
1814
|
+
isBidAsk = (channelName == 'bookTicker')
|
1815
|
+
isMarkPrice = (channelName == 'markPrice')
|
1816
|
+
use1sFreq = self.safe_bool(params, 'use1sFreq', True)
|
1778
1817
|
firstMarket = None
|
1779
1818
|
marketType = None
|
1780
1819
|
symbolsDefined = (symbols is not None)
|
1781
1820
|
if symbolsDefined:
|
1782
1821
|
firstMarket = self.market(symbols[0])
|
1783
|
-
|
1822
|
+
defaultMarket = 'swap' if (isMarkPrice) else 'spot'
|
1823
|
+
marketType, params = self.handle_market_type_and_params(methodName, firstMarket, params, defaultMarket)
|
1784
1824
|
subType = None
|
1785
1825
|
subType, params = self.handle_sub_type_and_params(methodName, firstMarket, params)
|
1786
1826
|
rawMarketType = None
|
@@ -1792,20 +1832,24 @@ class binance(ccxt.async_support.binance):
|
|
1792
1832
|
rawMarketType = marketType
|
1793
1833
|
else:
|
1794
1834
|
raise NotSupported(self.id + ' ' + methodName + '() does not support options markets')
|
1795
|
-
isBidAsk = (channelName == 'bookTicker')
|
1796
1835
|
subscriptionArgs = []
|
1797
1836
|
messageHashes = []
|
1837
|
+
suffix = ''
|
1838
|
+
if isMarkPrice:
|
1839
|
+
suffix = '@1s' if (use1sFreq) else ''
|
1798
1840
|
if symbolsDefined:
|
1799
1841
|
for i in range(0, len(symbols)):
|
1800
1842
|
symbol = symbols[i]
|
1801
1843
|
market = self.market(symbol)
|
1802
|
-
subscriptionArgs.append(market['lowercaseId'] + '@' + channelName)
|
1844
|
+
subscriptionArgs.append(market['lowercaseId'] + '@' + channelName + suffix)
|
1803
1845
|
messageHashes.append(self.get_message_hash(channelName, market['symbol'], isBidAsk))
|
1804
1846
|
else:
|
1805
1847
|
if isBidAsk:
|
1806
1848
|
if marketType == 'spot':
|
1807
1849
|
raise ArgumentsRequired(self.id + ' ' + methodName + '() requires symbols for self channel for spot markets')
|
1808
1850
|
subscriptionArgs.append('!' + channelName)
|
1851
|
+
elif isMarkPrice:
|
1852
|
+
subscriptionArgs.append('!' + channelName + '@arr' + suffix)
|
1809
1853
|
else:
|
1810
1854
|
subscriptionArgs.append('!' + channelName + '@arr')
|
1811
1855
|
messageHashes.append(self.get_message_hash(channelName, None, isBidAsk))
|
@@ -1833,6 +1877,17 @@ class binance(ccxt.async_support.binance):
|
|
1833
1877
|
return newDict
|
1834
1878
|
|
1835
1879
|
def parse_ws_ticker(self, message, marketType):
|
1880
|
+
# markPrice
|
1881
|
+
# {
|
1882
|
+
# "e": "markPriceUpdate", # Event type
|
1883
|
+
# "E": 1562305380000, # Event time
|
1884
|
+
# "s": "BTCUSDT", # Symbol
|
1885
|
+
# "p": "11794.15000000", # Mark price
|
1886
|
+
# "i": "11784.62659091", # Index price
|
1887
|
+
# "P": "11784.25641265", # Estimated Settle Price, only useful in the last hour before the settlement starts
|
1888
|
+
# "r": "0.00038167", # Funding rate
|
1889
|
+
# "T": 1562306400000 # Next funding time
|
1890
|
+
# }
|
1836
1891
|
#
|
1837
1892
|
# ticker
|
1838
1893
|
# {
|
@@ -1890,9 +1945,21 @@ class binance(ccxt.async_support.binance):
|
|
1890
1945
|
# "time":1589437530011,
|
1891
1946
|
# }
|
1892
1947
|
#
|
1948
|
+
marketId = self.safe_string_2(message, 's', 'symbol')
|
1949
|
+
symbol = self.safe_symbol(marketId, None, None, marketType)
|
1893
1950
|
event = self.safe_string(message, 'e', 'bookTicker')
|
1894
1951
|
if event == '24hrTicker':
|
1895
1952
|
event = 'ticker'
|
1953
|
+
if event == 'markPriceUpdate':
|
1954
|
+
# handle self separately because some fields clash with the ticker fields
|
1955
|
+
return self.safe_ticker({
|
1956
|
+
'symbol': symbol,
|
1957
|
+
'timestamp': self.safe_integer(message, 'E'),
|
1958
|
+
'datetime': self.iso8601(self.safe_integer(message, 'E')),
|
1959
|
+
'info': message,
|
1960
|
+
'markPrice': self.safe_string(message, 'p'),
|
1961
|
+
'indexPrice': self.safe_string(message, 'i'),
|
1962
|
+
})
|
1896
1963
|
timestamp = None
|
1897
1964
|
if event == 'bookTicker':
|
1898
1965
|
# take the event timestamp, if available, for spot tickers it is not
|
@@ -1900,8 +1967,6 @@ class binance(ccxt.async_support.binance):
|
|
1900
1967
|
else:
|
1901
1968
|
# take the timestamp of the closing price for candlestick streams
|
1902
1969
|
timestamp = self.safe_integer_n(message, ['C', 'E', 'time'])
|
1903
|
-
marketId = self.safe_string_2(message, 's', 'symbol')
|
1904
|
-
symbol = self.safe_symbol(marketId, None, None, marketType)
|
1905
1970
|
market = self.safe_market(marketId, None, None, marketType)
|
1906
1971
|
last = self.safe_string_2(message, 'c', 'price')
|
1907
1972
|
return self.safe_ticker({
|
@@ -3864,6 +3929,8 @@ class binance(ccxt.async_support.binance):
|
|
3864
3929
|
'1dTicker': self.handle_tickers,
|
3865
3930
|
'24hrTicker': self.handle_tickers,
|
3866
3931
|
'24hrMiniTicker': self.handle_tickers,
|
3932
|
+
'markPriceUpdate': self.handle_tickers,
|
3933
|
+
'markPriceUpdate@arr': self.handle_tickers,
|
3867
3934
|
'bookTicker': self.handle_bids_asks, # there is no "bookTicker@arr" endpoint
|
3868
3935
|
'outboundAccountPosition': self.handle_balance,
|
3869
3936
|
'balanceUpdate': self.handle_balance,
|
ccxt/pro/bitfinex.py
CHANGED
@@ -213,15 +213,15 @@ class bitfinex(ccxt.async_support.bitfinex):
|
|
213
213
|
open = None
|
214
214
|
if (last is not None) and (change is not None):
|
215
215
|
open = Precise.string_sub(last, change)
|
216
|
-
result = {
|
216
|
+
result = self.safe_ticker({
|
217
217
|
'symbol': symbol,
|
218
218
|
'timestamp': None,
|
219
219
|
'datetime': None,
|
220
|
-
'high': self.
|
221
|
-
'low': self.
|
222
|
-
'bid': self.
|
220
|
+
'high': self.safe_string(message, 9),
|
221
|
+
'low': self.safe_string(message, 10),
|
222
|
+
'bid': self.safe_string(message, 1),
|
223
223
|
'bidVolume': None,
|
224
|
-
'ask': self.
|
224
|
+
'ask': self.safe_string(message, 3),
|
225
225
|
'askVolume': None,
|
226
226
|
'vwap': None,
|
227
227
|
'open': self.parse_number(open),
|
@@ -229,12 +229,12 @@ class bitfinex(ccxt.async_support.bitfinex):
|
|
229
229
|
'last': self.parse_number(last),
|
230
230
|
'previousClose': None,
|
231
231
|
'change': self.parse_number(change),
|
232
|
-
'percentage': self.
|
232
|
+
'percentage': self.safe_string(message, 6),
|
233
233
|
'average': None,
|
234
|
-
'baseVolume': self.
|
234
|
+
'baseVolume': self.safe_string(message, 8),
|
235
235
|
'quoteVolume': None,
|
236
236
|
'info': message,
|
237
|
-
}
|
237
|
+
})
|
238
238
|
self.tickers[symbol] = result
|
239
239
|
client.resolve(result, messageHash)
|
240
240
|
|
ccxt/pro/krakenfutures.py
CHANGED
@@ -998,6 +998,8 @@ class krakenfutures(ccxt.async_support.krakenfutures):
|
|
998
998
|
'average': None,
|
999
999
|
'baseVolume': self.safe_string(ticker, 'volume'),
|
1000
1000
|
'quoteVolume': self.safe_string(ticker, 'volumeQuote'),
|
1001
|
+
'markPrice': self.safe_string(ticker, 'markPrice'),
|
1002
|
+
'indexPrice': self.safe_string(ticker, 'index'),
|
1001
1003
|
})
|
1002
1004
|
|
1003
1005
|
def handle_order_book_snapshot(self, client: Client, message):
|
ccxt/pro/phemex.py
CHANGED
@@ -132,6 +132,8 @@ class phemex(ccxt.async_support.phemex):
|
|
132
132
|
'average': average,
|
133
133
|
'baseVolume': baseVolume,
|
134
134
|
'quoteVolume': quoteVolume,
|
135
|
+
'markPrice': self.parse_number(self.from_ep(self.safe_string(ticker, 'markPrice'), market)),
|
136
|
+
'indexPrice': self.parse_number(self.from_ep(self.safe_string(ticker, 'indexPrice'), market)),
|
135
137
|
'info': ticker,
|
136
138
|
}
|
137
139
|
return result
|
ccxt/pro/woo.py
CHANGED
@@ -27,6 +27,7 @@ class woo(ccxt.async_support.woo):
|
|
27
27
|
'watchOrders': True,
|
28
28
|
'watchTicker': True,
|
29
29
|
'watchTickers': True,
|
30
|
+
'watchBidsAsks': True,
|
30
31
|
'watchTrades': True,
|
31
32
|
'watchTradesForSymbols': False,
|
32
33
|
'watchPositions': True,
|
@@ -383,6 +384,73 @@ class woo(ccxt.async_support.woo):
|
|
383
384
|
result.append(ticker)
|
384
385
|
client.resolve(result, topic)
|
385
386
|
|
387
|
+
async def watch_bids_asks(self, symbols: Strings = None, params={}) -> Tickers:
|
388
|
+
"""
|
389
|
+
:see: https://docs.woox.io/#bbos
|
390
|
+
watches best bid & ask for symbols
|
391
|
+
:param str[] symbols: unified symbol of the market to fetch the ticker for
|
392
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
393
|
+
:returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
|
394
|
+
"""
|
395
|
+
await self.load_markets()
|
396
|
+
symbols = self.market_symbols(symbols, None, False)
|
397
|
+
name = 'bbos'
|
398
|
+
topic = name
|
399
|
+
request: dict = {
|
400
|
+
'event': 'subscribe',
|
401
|
+
'topic': topic,
|
402
|
+
}
|
403
|
+
message = self.extend(request, params)
|
404
|
+
tickers = await self.watch_public(topic, message)
|
405
|
+
if self.newUpdates:
|
406
|
+
return tickers
|
407
|
+
return self.filter_by_array(self.bidsasks, 'symbol', symbols)
|
408
|
+
|
409
|
+
def handle_bid_ask(self, client: Client, message):
|
410
|
+
#
|
411
|
+
# {
|
412
|
+
# "topic": "bbos",
|
413
|
+
# "ts": 1618822376000,
|
414
|
+
# "data": [
|
415
|
+
# {
|
416
|
+
# "symbol": "SPOT_FIL_USDT",
|
417
|
+
# "ask": 159.0318,
|
418
|
+
# "askSize": 370.43,
|
419
|
+
# "bid": 158.9158,
|
420
|
+
# "bidSize": 16
|
421
|
+
# }
|
422
|
+
# ]
|
423
|
+
# }
|
424
|
+
#
|
425
|
+
topic = self.safe_string(message, 'topic')
|
426
|
+
data = self.safe_list(message, 'data', [])
|
427
|
+
timestamp = self.safe_integer(message, 'ts')
|
428
|
+
result: dict = {}
|
429
|
+
for i in range(0, len(data)):
|
430
|
+
ticker = self.safe_dict(data, i)
|
431
|
+
ticker['ts'] = timestamp
|
432
|
+
parsedTicker = self.parse_ws_bid_ask(ticker)
|
433
|
+
symbol = parsedTicker['symbol']
|
434
|
+
self.bidsasks[symbol] = parsedTicker
|
435
|
+
result[symbol] = parsedTicker
|
436
|
+
client.resolve(result, topic)
|
437
|
+
|
438
|
+
def parse_ws_bid_ask(self, ticker, market=None):
|
439
|
+
marketId = self.safe_string(ticker, 'symbol')
|
440
|
+
market = self.safe_market(marketId, market)
|
441
|
+
symbol = self.safe_string(market, 'symbol')
|
442
|
+
timestamp = self.safe_integer(ticker, 'ts')
|
443
|
+
return self.safe_ticker({
|
444
|
+
'symbol': symbol,
|
445
|
+
'timestamp': timestamp,
|
446
|
+
'datetime': self.iso8601(timestamp),
|
447
|
+
'ask': self.safe_string(ticker, 'ask'),
|
448
|
+
'askVolume': self.safe_string(ticker, 'askSize'),
|
449
|
+
'bid': self.safe_string(ticker, 'bid'),
|
450
|
+
'bidVolume': self.safe_string(ticker, 'bidSize'),
|
451
|
+
'info': ticker,
|
452
|
+
}, market)
|
453
|
+
|
386
454
|
async def watch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
|
387
455
|
"""
|
388
456
|
watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
|
@@ -1101,6 +1169,7 @@ class woo(ccxt.async_support.woo):
|
|
1101
1169
|
'trade': self.handle_trade,
|
1102
1170
|
'balance': self.handle_balance,
|
1103
1171
|
'position': self.handle_positions,
|
1172
|
+
'bbos': self.handle_bid_ask,
|
1104
1173
|
}
|
1105
1174
|
event = self.safe_string(message, 'event')
|
1106
1175
|
method = self.safe_value(methods, event)
|
ccxt/test/tests_async.py
CHANGED
@@ -3,9 +3,37 @@
|
|
3
3
|
import asyncio
|
4
4
|
|
5
5
|
|
6
|
-
from tests_helpers import AuthenticationError, NotSupported, InvalidProxySettings, ExchangeNotAvailable, OperationFailed, OnMaintenance, get_cli_arg_value,
|
6
|
+
from tests_helpers import AuthenticationError, NotSupported, InvalidProxySettings, ExchangeNotAvailable, OperationFailed, OnMaintenance, get_cli_arg_value, ENV_VARS, NEW_LINE, LANG, EXT, ROOT_DIR, PROXY_TEST_FILE_NAME, IS_SYNCHRONOUS, dump, json_parse, json_stringify, convert_ascii, io_file_exists, io_file_read, io_dir_read, call_method, call_method_sync, call_exchange_method_dynamically, call_exchange_method_dynamically_sync, get_root_exception, exception_message, exit_script, get_exchange_prop, set_exchange_prop, init_exchange, get_test_files_sync, get_test_files, set_fetch_response, is_null_value, close # noqa: F401
|
7
|
+
|
8
|
+
class testMainClass:
|
9
|
+
is_synchronous = IS_SYNCHRONOUS
|
10
|
+
id_tests = False
|
11
|
+
request_tests_failed = False
|
12
|
+
response_tests_failed = False
|
13
|
+
request_tests = False
|
14
|
+
ws_tests = False
|
15
|
+
response_tests = False
|
16
|
+
static_tests = False
|
17
|
+
info = False
|
18
|
+
verbose = False
|
19
|
+
debug = False
|
20
|
+
private_test = False
|
21
|
+
private_test_only = False
|
22
|
+
load_keys = False
|
23
|
+
sandbox = False
|
24
|
+
proxy_test_file_name = PROXY_TEST_FILE_NAME
|
25
|
+
only_specific_tests = []
|
26
|
+
skipped_settings_for_exchange = {}
|
27
|
+
skipped_methods = {}
|
28
|
+
checked_public_tests = {}
|
29
|
+
test_files = {}
|
30
|
+
public_tests = {}
|
31
|
+
new_line = NEW_LINE
|
32
|
+
root_dir = ROOT_DIR
|
33
|
+
env_vars = ENV_VARS
|
34
|
+
ext = EXT
|
35
|
+
lang = LANG
|
7
36
|
|
8
|
-
class testMainClass(baseMainTestClass):
|
9
37
|
def parse_cli_args(self):
|
10
38
|
self.response_tests = get_cli_arg_value('--responseTests')
|
11
39
|
self.id_tests = get_cli_arg_value('--idTests')
|
@@ -117,7 +145,7 @@ class testMainClass(baseMainTestClass):
|
|
117
145
|
if self.load_keys:
|
118
146
|
self.load_credentials_from_env(exchange)
|
119
147
|
# skipped tests
|
120
|
-
skipped_file = self.
|
148
|
+
skipped_file = self.root_dir + 'skip-tests.json'
|
121
149
|
skipped_settings = io_file_read(skipped_file)
|
122
150
|
self.skipped_settings_for_exchange = exchange.safe_value(skipped_settings, exchange_id, {})
|
123
151
|
skipped_settings_for_exchange = self.skipped_settings_for_exchange
|