1810 lines
79 KiB
Python
1810 lines
79 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
|
|
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
|
|
|
|
import ccxt.async_support
|
|
from ccxt.async_support.base.ws.cache import ArrayCache, ArrayCacheBySymbolById, ArrayCacheByTimestamp
|
|
import hashlib
|
|
from ccxt.base.types import Any, Balances, Int, Market, Order, OrderBook, Position, Str, Strings, Ticker, Tickers, Trade
|
|
from ccxt.async_support.base.ws.client import Client
|
|
from typing import List
|
|
from ccxt.base.errors import ExchangeError
|
|
from ccxt.base.errors import BadRequest
|
|
from ccxt.base.errors import NotSupported
|
|
|
|
|
|
class weex(ccxt.async_support.weex):
|
|
|
|
def describe(self) -> Any:
|
|
return self.deep_extend(super(weex, self).describe(), {
|
|
'has': {
|
|
'watchBalance': True,
|
|
'watchBidsAsks': True,
|
|
'watchMyTrades': True,
|
|
'watchOHLCV': True,
|
|
'watchOHLCVForSymbols': True,
|
|
'watchOrderBook': False,
|
|
'watchOrderBookForSymbols': False,
|
|
'watchOrders': True,
|
|
'watchPositions': True,
|
|
'watchTicker': True,
|
|
'watchTickers': True,
|
|
'watchTrades': True,
|
|
'watchTradesForSymbols': True,
|
|
'unWatchBidsAsks': True,
|
|
'unWatchMyTrades': True,
|
|
'unWatchOHLCV': True,
|
|
'unWatchOHLCVForSymbols': True,
|
|
'unWatchOrderBook': False,
|
|
'unWatchOrderBookForSymbols': False,
|
|
'unWatchOrders': True,
|
|
'unWatchPositions': True,
|
|
'unWatchTicker': True,
|
|
'unWatchTickers': True,
|
|
'unWatchTrades': True,
|
|
'unWatchTradesForSymbols': True,
|
|
},
|
|
'urls': {
|
|
'api': {
|
|
'ws': {
|
|
'spot': 'wss://ws-spot.weex.com/v3/ws',
|
|
'contract': 'wss://ws-contract.weex.com/v3/ws',
|
|
},
|
|
},
|
|
},
|
|
'options': {
|
|
'ws': {
|
|
'options': {
|
|
'headers': {
|
|
'User-Agent': 'ccxt', # the exchange requires headers
|
|
},
|
|
},
|
|
},
|
|
'watchOHLCV': {
|
|
'priceType': 'LAST_PRICE', # or 'MARK_PRICE' for swap markets
|
|
},
|
|
'watchOHLCVForSymbols': {
|
|
'priceType': 'LAST_PRICE', # or 'MARK_PRICE' for swap markets
|
|
},
|
|
'watchOrderBook': {
|
|
'depth': '200', # or '15'
|
|
},
|
|
'watchOrderBookForSymbols': {
|
|
'depth': '200', # or '15'
|
|
},
|
|
'watchBalance': {
|
|
'fetchBalanceSnapshot': True,
|
|
'awaitBalanceSnapshot': True,
|
|
},
|
|
'watchPositions': {
|
|
'fetchPositionsSnapshot': True,
|
|
'awaitPositionsSnapshot': True,
|
|
},
|
|
},
|
|
'streaming': {},
|
|
})
|
|
|
|
def request_id(self):
|
|
self.lock_id()
|
|
requestId = self.sum(self.safe_integer(self.options, 'requestId', 0), 1)
|
|
self.options['requestId'] = requestId
|
|
self.unlock_id()
|
|
return self.number_to_string(requestId)
|
|
|
|
async def subscribe_public(self, messageHashes, channels, isContract=False, params={}, subscription={}):
|
|
id = self.request_id()
|
|
method = 'SUBSCRIBE'
|
|
unsubscribe = self.safe_bool(subscription, 'unsubscribe', False)
|
|
if unsubscribe:
|
|
method = 'UNSUBSCRIBE'
|
|
message: dict = {
|
|
'id': id,
|
|
'method': method,
|
|
'params': channels,
|
|
}
|
|
subscription = self.extend(subscription, {'id': id})
|
|
type = 'contract' if isContract else 'spot'
|
|
url = self.urls['api']['ws'][type] + '/public'
|
|
return await self.watch_multiple(url, messageHashes, self.deep_extend(message, params), messageHashes, subscription)
|
|
|
|
async def subscribe_private(self, messageHash, subscribeHash, channel, isContract=False, params={}, subscription={}):
|
|
type = 'contract' if isContract else 'spot'
|
|
url = self.urls['api']['ws'][type] + '/private'
|
|
self.authenticate(url)
|
|
method = 'SUBSCRIBE'
|
|
unsubscribe = self.safe_bool(subscription, 'unsubscribe', False)
|
|
if unsubscribe:
|
|
method = 'UNSUBSCRIBE'
|
|
id = self.request_id()
|
|
message: dict = {
|
|
'id': id,
|
|
'method': method,
|
|
'params': [channel],
|
|
}
|
|
subscription = self.extend(subscription, {'id': id})
|
|
return await self.watch(url, messageHash, self.deep_extend(message, params), subscribeHash, subscription)
|
|
|
|
def authenticate(self, url):
|
|
self.check_required_credentials()
|
|
if (self.clients is not None) and (url in self.clients):
|
|
return
|
|
timestamp = self.nonce()
|
|
payload = str(timestamp) + '/v3/ws/private'
|
|
signature = self.hmac(self.encode(payload), self.encode(self.secret), hashlib.sha256, 'base64')
|
|
originalHeaders = self.options['ws']['options']['headers']
|
|
userAgent = self.safe_string(originalHeaders, 'User-Agent', 'ccxt')
|
|
extendedOptions: dict = {
|
|
'ws': {
|
|
'options': {
|
|
'headers': {
|
|
'User-Agent': userAgent,
|
|
'ACCESS-KEY': self.apiKey,
|
|
'ACCESS-SIGN': signature,
|
|
'ACCESS-PASSPHRASE': self.password,
|
|
'ACCESS-TIMESTAMP': self.number_to_string(timestamp),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
self.extend_exchange_options(extendedOptions)
|
|
# instantiate client
|
|
self.client(url)
|
|
# return headers to original state
|
|
defaultOptions: dict = {
|
|
'ws': {
|
|
'options': {
|
|
'headers': {
|
|
'User-Agent': userAgent,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
self.extend_exchange_options(defaultOptions)
|
|
|
|
async def watch_ticker(self, symbol: str, params={}) -> Ticker:
|
|
"""
|
|
watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
|
|
|
|
https://www.weex.com/api-doc/spot/Websocket/public/Tickers-Channel
|
|
https://www.weex.com/api-doc/contract/Websocket/public/Tickers-Channel
|
|
|
|
:param str symbol: unified symbol of the market to fetch the ticker for
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param str [params.name]: stream to use can be ticker or miniTicker
|
|
:returns dict: a `ticker structure <https://docs.ccxt.com/?id=ticker-structure>`
|
|
"""
|
|
await self.load_markets()
|
|
symbol = self.symbol(symbol)
|
|
tickers = await self.watch_tickers([symbol], params)
|
|
return tickers[symbol]
|
|
|
|
async def watch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
|
|
"""
|
|
watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
|
|
|
|
https://www.weex.com/api-doc/spot/Websocket/public/Tickers-Channel
|
|
https://www.weex.com/api-doc/contract/Websocket/public/Tickers-Channel
|
|
|
|
:param str[] symbols: unified symbol of the market to fetch the ticker for
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: a `ticker structure <https://docs.ccxt.com/?id=ticker-structure>`
|
|
"""
|
|
await self.load_markets()
|
|
symbols = self.market_symbols(symbols, None, False, True)
|
|
firstMarket = self.get_market_from_symbols(symbols)
|
|
isContract = firstMarket['contract']
|
|
topic = 'ticker'
|
|
messageHashes = []
|
|
channels = []
|
|
for i in range(0, len(symbols)):
|
|
symbol = symbols[i]
|
|
market = self.market(symbol)
|
|
channelName = market['id'] + '@' + topic
|
|
messageHash = topic + '::' + symbol
|
|
messageHashes.append(messageHash)
|
|
channels.append(channelName)
|
|
newTicker = await self.subscribe_public(messageHashes, channels, isContract, params)
|
|
if self.newUpdates:
|
|
result: dict = {}
|
|
result[newTicker['symbol']] = newTicker
|
|
return result
|
|
return self.filter_by_array(self.tickers, 'symbol', symbols)
|
|
|
|
async def un_watch_ticker(self, symbol: str, params={}) -> Any:
|
|
"""
|
|
unWatches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
|
|
|
|
https://www.weex.com/api-doc/spot/Websocket/public/Tickers-Channel
|
|
https://www.weex.com/api-doc/contract/Websocket/public/Tickers-Channel
|
|
|
|
:param str symbol: unified symbol of the market to fetch the ticker for
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: a `ticker structure <https://docs.ccxt.com/?id=ticker-structure>`
|
|
"""
|
|
return await self.un_watch_tickers([symbol], params)
|
|
|
|
async def un_watch_tickers(self, symbols: Strings = None, params={}) -> Any:
|
|
"""
|
|
unWatches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
|
|
|
|
https://www.weex.com/api-doc/spot/Websocket/public/Tickers-Channel
|
|
https://www.weex.com/api-doc/contract/Websocket/public/Tickers-Channel
|
|
|
|
:param str[] symbols: unified symbol of the market to fetch the ticker for
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: a `ticker structure <https://docs.ccxt.com/?id=ticker-structure>`
|
|
"""
|
|
await self.load_markets()
|
|
symbols = self.market_symbols(symbols, None, False, True)
|
|
firstMarket = self.get_market_from_symbols(symbols)
|
|
isContract = firstMarket['contract']
|
|
topic = 'ticker'
|
|
subHashes = []
|
|
channels = []
|
|
unSubHashes = []
|
|
for i in range(0, len(symbols)):
|
|
symbol = symbols[i]
|
|
market = self.market(symbol)
|
|
channelName = market['id'] + '@' + topic
|
|
messageHash = topic + '::' + symbol
|
|
unSubMessageHash = 'unsubscribe::' + messageHash
|
|
subHashes.append(messageHash)
|
|
channels.append(channelName)
|
|
unSubHashes.append(unSubMessageHash)
|
|
subscription = {
|
|
'unsubscribe': True,
|
|
'symbols': symbols,
|
|
'messageHashes': unSubHashes,
|
|
'subMessageHashes': subHashes,
|
|
'topic': topic,
|
|
}
|
|
return await self.subscribe_public(unSubHashes, channels, isContract, params, subscription)
|
|
|
|
def handle_ticker(self, client: Client, message):
|
|
#
|
|
# {
|
|
# "e": "ticker",
|
|
# "E": 1776081628845,
|
|
# "s": "ETHUSDT",
|
|
# "d": [
|
|
# {
|
|
# "p": "-18.93",
|
|
# "P": "-0.008592",
|
|
# "w": "2192.40298388",
|
|
# "c": "2184.20",
|
|
# "o": "2203.13",
|
|
# "h": "2217.34",
|
|
# "l": "2173.32",
|
|
# "v": "359395.800",
|
|
# "q": "787940424.31399",
|
|
# "O": 1775995200000,
|
|
# "C": 1776081600000,
|
|
# "n": 485169,
|
|
# "m": "2184.28",
|
|
# "i": "2185.2025"
|
|
# }
|
|
# ]
|
|
# }
|
|
#
|
|
market = self.get_market_from_client_and_message(client, message)
|
|
tickers = self.safe_list(message, 'd', [])
|
|
data = self.safe_dict(tickers, 0, {})
|
|
ticker = self.parse_ws_ticker(data, market)
|
|
symbol = market['symbol']
|
|
messageHash = 'ticker::' + symbol
|
|
self.tickers[symbol] = ticker
|
|
client.resolve(self.tickers[symbol], messageHash)
|
|
|
|
def parse_ws_ticker(self, ticker: dict, market: Market = None) -> Ticker:
|
|
#
|
|
# {
|
|
# "p": "-18.93",
|
|
# "P": "-0.008592",
|
|
# "w": "2192.40298388",
|
|
# "c": "2184.20",
|
|
# "o": "2203.13",
|
|
# "h": "2217.34",
|
|
# "l": "2173.32",
|
|
# "v": "359395.800",
|
|
# "q": "787940424.31399",
|
|
# "O": 1775995200000,
|
|
# "C": 1776081600000,
|
|
# "n": 485169,
|
|
# "m": "2184.28",
|
|
# "i": "2185.2025"
|
|
# }
|
|
#
|
|
timestamp = self.safe_integer(ticker, 'C')
|
|
close = self.safe_string(ticker, 'c')
|
|
return self.safe_ticker({
|
|
'symbol': market['symbol'],
|
|
'timestamp': timestamp,
|
|
'datetime': self.iso8601(timestamp),
|
|
'high': self.safe_string(ticker, 'h'),
|
|
'low': self.safe_string(ticker, 'l'),
|
|
'bid': self.safe_string(ticker, 'b'),
|
|
'bidVolume': self.safe_string(ticker, 'B'),
|
|
'ask': self.safe_string(ticker, 'a'),
|
|
'askVolume': self.safe_string(ticker, 'A'),
|
|
'vwap': self.safe_string(ticker, 'w'),
|
|
'open': self.safe_string(ticker, 'o'),
|
|
'close': close,
|
|
'last': close,
|
|
'previousClose': self.safe_string(ticker, 'x'),
|
|
'change': self.safe_string(ticker, 'p'),
|
|
'percentage': self.safe_string(ticker, 'P'),
|
|
'average': self.safe_string(ticker, 'w'),
|
|
'baseVolume': self.safe_string(ticker, 'v'),
|
|
'quoteVolume': self.safe_string(ticker, 'q'),
|
|
'markPrice': self.safe_string(ticker, 'm'),
|
|
'indexPrice': self.safe_string(ticker, 'i'),
|
|
'info': ticker,
|
|
}, market)
|
|
|
|
async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
|
|
"""
|
|
get the list of most recent trades for a particular symbol
|
|
|
|
https://www.weex.com/api-doc/spot/Websocket/public/Trades-Channel
|
|
https://www.weex.com/api-doc/contract/Websocket/public/Trades-Channel
|
|
|
|
:param str symbol: unified symbol of the market to fetch trades for
|
|
:param int [since]: timestamp in ms of the earliest trade to fetch
|
|
:param int [limit]: the maximum amount of trades to fetch
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict[]: a list of `trade structures <https://docs.ccxt.com/?id=public-trades>`
|
|
"""
|
|
return await self.watch_trades_for_symbols([symbol], since, limit, params)
|
|
|
|
async def watch_trades_for_symbols(self, symbols: List[str], since: Int = None, limit: Int = None, params={}) -> List[Trade]:
|
|
"""
|
|
get the list of most recent trades for a list of symbols
|
|
|
|
https://www.weex.com/api-doc/spot/Websocket/public/Trades-Channel
|
|
https://www.weex.com/api-doc/contract/Websocket/public/Trades-Channel
|
|
|
|
:param str[] symbols: unified symbol of the market to fetch trades for
|
|
:param int [since]: timestamp in ms of the earliest trade to fetch
|
|
:param int [limit]: the maximum amount of trades to fetch
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict[]: a list of `trade structures <https://docs.ccxt.com/?id=public-trades>`
|
|
"""
|
|
await self.load_markets()
|
|
symbols = self.market_symbols(symbols, None, False, True)
|
|
firstMarket = self.get_market_from_symbols(symbols)
|
|
isContract = firstMarket['contract']
|
|
topic = 'trade'
|
|
messageHashes = []
|
|
channels = []
|
|
for i in range(0, len(symbols)):
|
|
symbol = symbols[i]
|
|
market = self.market(symbol)
|
|
channelName = market['id'] + '@' + topic
|
|
messageHash = topic + '::' + symbol
|
|
messageHashes.append(messageHash)
|
|
channels.append(channelName)
|
|
trades = await self.subscribe_public(messageHashes, channels, isContract, params)
|
|
if self.newUpdates:
|
|
first = self.safe_value(trades, 0)
|
|
tradeSymbol = self.safe_string(first, 'symbol')
|
|
limit = trades.getLimit(tradeSymbol, limit)
|
|
return self.filter_by_since_limit(trades, since, limit, 'timestamp', True)
|
|
|
|
async def un_watch_trades(self, symbol: str, params={}) -> Any:
|
|
"""
|
|
unsubscribes from the trades channel
|
|
|
|
https://www.weex.com/api-doc/spot/Websocket/public/Trades-Channel
|
|
https://www.weex.com/api-doc/contract/Websocket/public/Trades-Channel
|
|
|
|
:param str symbol: unified symbol of the market to fetch trades for
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict[]: a list of `trade structures <https://docs.ccxt.com/?id=public-trades>`
|
|
"""
|
|
await self.load_markets()
|
|
return await self.un_watch_trades_for_symbols([symbol], params)
|
|
|
|
async def un_watch_trades_for_symbols(self, symbols: List[str], params={}) -> Any:
|
|
"""
|
|
unsubscribes from the trades channel
|
|
|
|
https://www.weex.com/api-doc/spot/Websocket/public/Trades-Channel
|
|
https://www.weex.com/api-doc/contract/Websocket/public/Trades-Channel
|
|
|
|
:param str[] symbols: unified symbol of the market to fetch trades for
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict[]: a list of `trade structures <https://docs.ccxt.com/?id=public-trades>`
|
|
"""
|
|
await self.load_markets()
|
|
symbols = self.market_symbols(symbols, None, False, True)
|
|
firstMarket = self.get_market_from_symbols(symbols)
|
|
isContract = firstMarket['contract']
|
|
topic = 'trade'
|
|
subHashes = []
|
|
channels = []
|
|
unSubHashes = []
|
|
for i in range(0, len(symbols)):
|
|
symbol = symbols[i]
|
|
market = self.market(symbol)
|
|
channelName = market['id'] + '@' + topic
|
|
messageHash = topic + '::' + symbol
|
|
unSubMessageHash = 'unsubscribe::' + messageHash
|
|
subHashes.append(messageHash)
|
|
channels.append(channelName)
|
|
unSubHashes.append(unSubMessageHash)
|
|
subscription = {
|
|
'unsubscribe': True,
|
|
'symbols': symbols,
|
|
'messageHashes': unSubHashes,
|
|
'subMessageHashes': subHashes,
|
|
'topic': 'trades',
|
|
}
|
|
return await self.subscribe_public(unSubHashes, channels, isContract, params, subscription)
|
|
|
|
def handle_trade(self, client: Client, message):
|
|
#
|
|
# {
|
|
# "e": "trade",
|
|
# "E": 1776104608321,
|
|
# "s": "ETHUSDT",
|
|
# "d": [
|
|
# {
|
|
# "T": 1776104608298,
|
|
# "t": "41099265-7985-4f4c-af93-2cc3bc1cf13b",
|
|
# "p": "2225.15",
|
|
# "q": "0.02525",
|
|
# "v": "56.1850375",
|
|
# "m": False
|
|
# }
|
|
# ]
|
|
# }
|
|
#
|
|
market = self.get_market_from_client_and_message(client, message)
|
|
symbol = market['symbol']
|
|
messageHash = 'trade::' + symbol
|
|
if not (symbol in self.trades):
|
|
limit = self.safe_integer(self.options, 'tradesLimit', 1000)
|
|
self.trades[symbol] = ArrayCache(limit)
|
|
tradesArray = self.trades[symbol]
|
|
data = self.safe_list(message, 'd', [])
|
|
newTrades = []
|
|
for i in range(0, len(data)):
|
|
rawTrade = self.safe_dict(data, i, {})
|
|
trade = self.parse_ws_trade(rawTrade, market)
|
|
newTrades.append(trade)
|
|
sorted = self.sort_by(newTrades, 'timestamp')
|
|
for j in range(0, len(sorted)):
|
|
sortedTrade = sorted[j]
|
|
tradesArray.append(sortedTrade)
|
|
self.trades[symbol] = tradesArray
|
|
client.resolve(tradesArray, messageHash)
|
|
|
|
def parse_ws_trade(self, trade, market=None):
|
|
#
|
|
# {
|
|
# "T": 1776089287762,
|
|
# "t": "df4d1af1-71e8-400d-9571-f2cee2e6bea8",
|
|
# "p": "2203.73",
|
|
# "q": "7.214",
|
|
# "v": "15897.70822",
|
|
# "m": False
|
|
# }
|
|
#
|
|
timestamp = self.safe_integer(trade, 'T')
|
|
return self.safe_trade({
|
|
'info': trade,
|
|
'id': self.safe_string(trade, 't'),
|
|
'timestamp': timestamp,
|
|
'datetime': self.iso8601(timestamp),
|
|
'symbol': market['symbol'],
|
|
'order': None,
|
|
'type': None,
|
|
'side': None,
|
|
'takerOrMaker': None,
|
|
'price': self.safe_string(trade, 'p'),
|
|
'amount': self.safe_string(trade, 'q'),
|
|
'cost': self.safe_string(trade, 'v'),
|
|
'fee': None,
|
|
}, market)
|
|
|
|
async def watch_ohlcv(self, symbol: str, timeframe: str = '1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
|
|
"""
|
|
watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
|
|
|
|
https://www.weex.com/api-doc/spot/Websocket/public/Candlesticks-Channel
|
|
https://www.weex.com/api-doc/contract/Websocket/public/Candlesticks-Channel
|
|
|
|
:param str symbol: unified symbol of the market to fetch OHLCV data for
|
|
:param str timeframe: the length of time each candle represents
|
|
:param int [since]: timestamp in ms of the earliest candle to fetch
|
|
:param int [limit]: the maximum amount of candles to fetch
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns int[][]: A list of candles ordered, open, high, low, close, volume
|
|
"""
|
|
extendedParams = self.extend(params, {
|
|
'callerMethodName': 'watchOHLCV',
|
|
})
|
|
result = await self.watch_ohlcv_for_symbols([[symbol, timeframe]], since, limit, extendedParams)
|
|
return result[symbol][timeframe]
|
|
|
|
async def watch_ohlcv_for_symbols(self, symbolsAndTimeframes: List[List[str]], since: Int = None, limit: Int = None, params={}):
|
|
"""
|
|
watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
|
|
|
|
https://www.weex.com/api-doc/spot/Websocket/public/Candlesticks-Channel
|
|
https://www.weex.com/api-doc/contract/Websocket/public/Candlesticks-Channel
|
|
|
|
:param str[][] symbolsAndTimeframes: array of arrays containing unified symbols and timeframes to fetch OHLCV data for, example [['BTC/USDT', '1m'], ['LTC/USDT', '5m']]
|
|
:param int [since]: timestamp in ms of the earliest candle to fetch
|
|
:param int [limit]: the maximum amount of candles to fetch
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: A list of candles ordered, open, high, low, close, volume
|
|
"""
|
|
await self.load_markets()
|
|
callerMethodName = self.safe_string(params, 'callerMethodName', 'watchOHLCVForSymbols')
|
|
params = self.omit(params, 'callerMethodName')
|
|
channels = []
|
|
messageHashes = []
|
|
firstEntry = self.safe_list(symbolsAndTimeframes, 0, [])
|
|
firstSymbol = self.safe_string(firstEntry, 0)
|
|
firstMarket = self.market(firstSymbol)
|
|
isContract = firstMarket['contract']
|
|
priceType = 'LAST_PRICE'
|
|
if isContract:
|
|
priceType, params = self.handle_option_and_params_2(params, callerMethodName, 'price', 'priceType', priceType)
|
|
for i in range(0, len(symbolsAndTimeframes)):
|
|
data = self.safe_list(symbolsAndTimeframes, i)
|
|
symbolString = self.safe_string(data, 0)
|
|
market = self.market(symbolString)
|
|
if market['type'] != firstMarket['type']:
|
|
raise BadRequest(self.id + ' ' + callerMethodName + ' market symbols must be of the same type')
|
|
symbolString = market['symbol']
|
|
unifiedTimeframe = self.safe_string(data, 1, '1')
|
|
interval = self.safe_string(self.timeframes, unifiedTimeframe, unifiedTimeframe)
|
|
channel = market['id'] + '@kline_' + interval + '_' + priceType
|
|
messageHash = 'ohlcv::' + symbolString + '::' + unifiedTimeframe
|
|
channels.append(channel)
|
|
messageHashes.append(messageHash)
|
|
symbol, timeframe, stored = await self.subscribe_public(messageHashes, channels, isContract, params)
|
|
if self.newUpdates:
|
|
limit = stored.getLimit(symbol, limit)
|
|
filtered = self.filter_by_since_limit(stored, since, limit, 0, True)
|
|
return self.create_ohlcv_object(symbol, timeframe, filtered)
|
|
|
|
async def un_watch_ohlcv(self, symbol: str, timeframe='1m', params={}) -> Any:
|
|
"""
|
|
unWatches historical candlestick data containing the open, high, low, and close price, and the volume of a market
|
|
|
|
https://www.weex.com/api-doc/spot/Websocket/public/Candlesticks-Channel
|
|
https://www.weex.com/api-doc/contract/Websocket/public/Candlesticks-Channel
|
|
|
|
:param str symbol: unified symbol of the market to fetch OHLCV data for
|
|
:param str timeframe: the length of time each candle represents
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns int[][]: A list of candles ordered, open, high, low, close, volume
|
|
"""
|
|
params['callerMethodName'] = 'unWatchOHLCV'
|
|
return await self.un_watch_ohlcv_for_symbols([[symbol, timeframe]], params)
|
|
|
|
async def un_watch_ohlcv_for_symbols(self, symbolsAndTimeframes: List[List[str]], params={}) -> Any:
|
|
"""
|
|
unWatches historical candlestick data containing the open, high, low, and close price, and the volume of a market
|
|
|
|
https://www.weex.com/api-doc/spot/Websocket/public/Candlesticks-Channel
|
|
https://www.weex.com/api-doc/contract/Websocket/public/Candlesticks-Channel
|
|
|
|
:param str[][] symbolsAndTimeframes: array of arrays containing unified symbols and timeframes to fetch OHLCV data for, example [['BTC/USDT', '1m'], ['LTC/USDT', '5m']]
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns int[][]: A list of candles ordered, open, high, low, close, volume
|
|
"""
|
|
await self.load_markets()
|
|
callerMethodName = self.safe_string(params, 'callerMethodName', 'unWatchOHLCVForSymbols')
|
|
params = self.omit(params, 'callerMethodName')
|
|
channels = []
|
|
subHashes = []
|
|
unSubHashes = []
|
|
firstEntry = self.safe_list(symbolsAndTimeframes, 0, [])
|
|
firstSymbol = self.safe_string(firstEntry, 0)
|
|
firstMarket = self.market(firstSymbol)
|
|
isContract = firstMarket['contract']
|
|
priceType = 'LAST_PRICE'
|
|
if isContract:
|
|
priceType, params = self.handle_option_and_params_2(params, callerMethodName, 'price', 'priceType', priceType)
|
|
for i in range(0, len(symbolsAndTimeframes)):
|
|
data = self.safe_list(symbolsAndTimeframes, i)
|
|
symbolString = self.safe_string(data, 0)
|
|
market = self.market(symbolString)
|
|
if market['type'] != firstMarket['type']:
|
|
raise BadRequest(self.id + ' ' + callerMethodName + ' market symbols must be of the same type')
|
|
symbolString = market['symbol']
|
|
unifiedTimeframe = self.safe_string(data, 1, '1')
|
|
interval = self.safe_string(self.timeframes, unifiedTimeframe, unifiedTimeframe)
|
|
channel = market['id'] + '@kline_' + interval + '_' + priceType
|
|
messageHash = 'ohlcv::' + symbolString + '::' + unifiedTimeframe
|
|
unSubMessageHash = 'unsubscribe::' + messageHash
|
|
channels.append(channel)
|
|
subHashes.append(messageHash)
|
|
unSubHashes.append(unSubMessageHash)
|
|
subscription = {
|
|
'unsubscribe': True,
|
|
'symbolsAndTimeframes': symbolsAndTimeframes,
|
|
'messageHashes': unSubHashes,
|
|
'subMessageHashes': subHashes,
|
|
'topic': 'ohlcv',
|
|
}
|
|
return await self.subscribe_public(unSubHashes, channels, isContract, params, subscription)
|
|
|
|
def handle_ohlcv(self, client: Client, message):
|
|
#
|
|
# {
|
|
# e: 'kline',
|
|
# E: 1776095535012,
|
|
# s: 'ETHUSDT',
|
|
# p: 'LAST_PRICE',
|
|
# d: [
|
|
# {
|
|
# t: 1776092400000,
|
|
# T: 1776096000000,
|
|
# s: 'ETHUSDT',
|
|
# i: '1h',
|
|
# o: '2234.18',
|
|
# c: '2205.15',
|
|
# h: '2236.43',
|
|
# l: '2199.53',
|
|
# v: '12505.60574',
|
|
# n: 3381,
|
|
# q: '27682528.6655305',
|
|
# V: '6420.47929',
|
|
# Q: '14213680.1906424'
|
|
# }
|
|
# ]
|
|
# }
|
|
#
|
|
market = self.get_market_from_client_and_message(client, message)
|
|
symbol = market['symbol']
|
|
if not (symbol in self.ohlcvs):
|
|
self.ohlcvs[symbol] = {}
|
|
data = self.safe_list(message, 'd', [])
|
|
firstEntry = self.safe_dict(data, 0, {})
|
|
interval = self.safe_string(firstEntry, 'i')
|
|
timeframe = self.find_timeframe(interval)
|
|
if not (timeframe in self.ohlcvs[symbol]):
|
|
limit = self.safe_integer(self.options, 'OHLCVLimit', 1000)
|
|
self.ohlcvs[symbol][timeframe] = ArrayCacheByTimestamp(limit)
|
|
stored = self.ohlcvs[symbol][timeframe]
|
|
for i in range(0, len(data)):
|
|
entry = self.safe_dict(data, i, {})
|
|
parsed = self.parse_ws_ohlcv(entry)
|
|
stored.append(parsed)
|
|
messageHash = 'ohlcv::' + symbol + '::' + timeframe
|
|
resolveData = [symbol, timeframe, stored]
|
|
client.resolve(resolveData, messageHash)
|
|
|
|
def parse_ws_ohlcv(self, ohlcv, market=None) -> list:
|
|
#
|
|
# {
|
|
# t: 1776092400000,
|
|
# T: 1776096000000,
|
|
# s: 'ETHUSDT',
|
|
# i: '1h',
|
|
# o: '2234.18',
|
|
# c: '2205.15',
|
|
# h: '2236.43',
|
|
# l: '2199.53',
|
|
# v: '12505.60574',
|
|
# n: 3381,
|
|
# q: '27682528.6655305',
|
|
# V: '6420.47929',
|
|
# Q: '14213680.1906424'
|
|
# }
|
|
#
|
|
return [
|
|
self.safe_integer(ohlcv, 't'),
|
|
self.safe_number(ohlcv, 'o'),
|
|
self.safe_number(ohlcv, 'h'),
|
|
self.safe_number(ohlcv, 'l'),
|
|
self.safe_number(ohlcv, 'c'),
|
|
self.safe_number(ohlcv, 'v'),
|
|
]
|
|
|
|
async def watch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
|
|
"""
|
|
watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
|
|
|
|
https://www.weex.com/api-doc/spot/Websocket/public/Depth-Channel
|
|
https://www.weex.com/api-doc/contract/Websocket/public/Depth-Channel
|
|
|
|
:param str symbol: unified symbol of the market to fetch the order book for
|
|
:param int [limit]: the maximum amount of order book entries to return
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: A dictionary of `order book structures <https://docs.ccxt.com/?id=order-book-structure>` indexed by market symbols
|
|
"""
|
|
params = self.extend(params, {
|
|
'callerMethodName': 'watchOrderBook',
|
|
})
|
|
return await self.watch_order_book_for_symbols([symbol], limit, params)
|
|
|
|
async def watch_order_book_for_symbols(self, symbols: List[str], limit: Int = None, params={}) -> OrderBook:
|
|
"""
|
|
watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
|
|
|
|
https://www.weex.com/api-doc/spot/Websocket/public/Depth-Channel
|
|
https://www.weex.com/api-doc/contract/Websocket/public/Depth-Channel
|
|
|
|
:param str[] symbols: unified array of symbols
|
|
:param int [limit]: the maximum amount of order book entries to return
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: A dictionary of `order book structures <https://docs.ccxt.com/?id=order-book-structure>` indexed by market symbols
|
|
"""
|
|
await self.load_markets()
|
|
symbols = self.market_symbols(symbols, None, False, True)
|
|
firstMarket = self.get_market_from_symbols(symbols)
|
|
isContract = firstMarket['contract']
|
|
callerMethodName = self.safe_string(params, 'callerMethodName', 'watchOrderBookForSymbols')
|
|
params = self.omit(params, 'callerMethodName')
|
|
depth = '200'
|
|
depth, params = self.handle_option_and_params(params, callerMethodName, 'depth', depth)
|
|
messageHashes = []
|
|
channels = []
|
|
for i in range(0, len(symbols)):
|
|
symbol = symbols[i]
|
|
market = self.market(symbol)
|
|
messageHash = 'orderbook::' + symbol
|
|
channel = market['id'] + '@depth' + depth
|
|
messageHashes.append(messageHash)
|
|
channels.append(channel)
|
|
subscription: dict = {
|
|
'limit': limit,
|
|
}
|
|
orderbook = await self.subscribe_public(messageHashes, channels, isContract, params, subscription)
|
|
return orderbook.limit()
|
|
|
|
async def un_watch_order_book(self, symbol: str, params={}) -> Any:
|
|
"""
|
|
unWatches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
|
|
|
|
https://www.weex.com/api-doc/spot/Websocket/public/Depth-Channel
|
|
https://www.weex.com/api-doc/contract/Websocket/public/Depth-Channel
|
|
|
|
:param str symbol: unified array of symbols
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: A dictionary of `order book structures <https://docs.ccxt.com/?id=order-book-structure>` indexed by market symbols
|
|
"""
|
|
params = self.extend(params, {
|
|
'callerMethodName': 'unWatchOrderBook',
|
|
})
|
|
return await self.un_watch_order_book_for_symbols([symbol], params)
|
|
|
|
async def un_watch_order_book_for_symbols(self, symbols: List[str], params={}) -> Any:
|
|
"""
|
|
unWatches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
|
|
|
|
https://www.weex.com/api-doc/spot/Websocket/public/Depth-Channel
|
|
https://www.weex.com/api-doc/contract/Websocket/public/Depth-Channel
|
|
|
|
:param str[] symbols: unified array of symbols
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: A dictionary of `order book structures <https://docs.ccxt.com/?id=order-book-structure>` indexed by market symbols
|
|
"""
|
|
await self.load_markets()
|
|
symbols = self.market_symbols(symbols, None, False, True)
|
|
firstMarket = self.get_market_from_symbols(symbols)
|
|
isContract = firstMarket['contract']
|
|
callerMethodName = self.safe_string(params, 'callerMethodName', 'unWatchOrderBookForSymbols')
|
|
params = self.omit(params, 'callerMethodName')
|
|
depth = '200'
|
|
depth, params = self.handle_option_and_params(params, callerMethodName, 'depth', depth)
|
|
subHashes = []
|
|
channels = []
|
|
unSubHashes = []
|
|
for i in range(0, len(symbols)):
|
|
symbol = symbols[i]
|
|
market = self.market(symbol)
|
|
messageHash = 'orderbook::' + symbol
|
|
channel = market['id'] + '@depth' + depth
|
|
unSubMessageHash = 'unsubscribe::' + messageHash
|
|
subHashes.append(messageHash)
|
|
channels.append(channel)
|
|
unSubHashes.append(unSubMessageHash)
|
|
subscription = {
|
|
'unsubscribe': True,
|
|
'symbols': symbols,
|
|
'messageHashes': unSubHashes,
|
|
'subMessageHashes': subHashes,
|
|
'topic': 'orderbook',
|
|
}
|
|
return await self.subscribe_public(unSubHashes, channels, isContract, params, subscription)
|
|
|
|
def handle_order_book(self, client: Client, message):
|
|
#
|
|
# {
|
|
# "e": "depth",
|
|
# "E": 1776098967972,
|
|
# "s": "ETHUSDT",
|
|
# "U": 14181847790,
|
|
# "u": 14181847802,
|
|
# "l": 200,
|
|
# "d": "CHANGED",
|
|
# "b": [["2227.21", "0"], ["2227.20", "46.519"]],
|
|
# "a": [["2227.21", "44.092"], ["2227.26", "0"]]
|
|
# }
|
|
#
|
|
market = self.get_market_from_client_and_message(client, message)
|
|
symbol = market['symbol']
|
|
messageHash = 'orderbook::' + symbol
|
|
if not (symbol in self.orderbooks):
|
|
subscription = self.safe_dict(client.subscriptions, messageHash, {})
|
|
limit = self.safe_integer(subscription, 'limit')
|
|
if limit is not None:
|
|
self.orderbooks[symbol] = self.order_book({}, limit)
|
|
else:
|
|
self.orderbooks[symbol] = self.order_book({})
|
|
orderbook = self.orderbooks[symbol]
|
|
timestamp = self.safe_integer(message, 'E')
|
|
event = self.safe_string(message, 'e')
|
|
nonce = self.safe_integer(message, 'u')
|
|
if event == 'depthSnapshot':
|
|
parsed = self.parse_order_book(message, symbol, timestamp, 'b', 'a')
|
|
parsed['nonce'] = nonce
|
|
orderbook.reset(parsed)
|
|
else:
|
|
asks = self.safe_list(message, 'a', [])
|
|
bids = self.safe_list(message, 'b', [])
|
|
self.handle_deltas(orderbook['asks'], asks)
|
|
self.handle_deltas(orderbook['bids'], bids)
|
|
orderbook['timestamp'] = timestamp
|
|
orderbook['datetime'] = self.iso8601(timestamp)
|
|
orderbook['nonce'] = nonce
|
|
client.resolve(orderbook, messageHash)
|
|
|
|
def handle_delta(self, bookside, delta):
|
|
bidAsk = self.parse_bid_ask(delta)
|
|
bookside.storeArray(bidAsk)
|
|
|
|
async def watch_bids_asks(self, symbols: Strings = None, params={}) -> Tickers:
|
|
"""
|
|
watches best bid & ask for spot symbols
|
|
|
|
https://www.weex.com/api-doc/spot/Websocket/public/BookTicker-Channel
|
|
|
|
:param str[] symbols: unified symbol of the market to fetch the ticker for
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: a `ticker structure <https://docs.ccxt.com/?id=ticker-structure>`
|
|
"""
|
|
await self.load_markets()
|
|
symbols = self.market_symbols(symbols, None, False, True)
|
|
firstMarket = self.get_market_from_symbols(symbols)
|
|
if firstMarket['contract']:
|
|
raise NotSupported(self.id + ' watchBidsAsks is supported for spot markets only')
|
|
messageHashes = []
|
|
channels = []
|
|
for i in range(0, len(symbols)):
|
|
symbol = symbols[i]
|
|
market = self.market(symbol)
|
|
channelName = market['id'] + '@' + 'bookTicker'
|
|
messageHash = 'bidask::' + symbol
|
|
messageHashes.append(messageHash)
|
|
channels.append(channelName)
|
|
newTicker = await self.subscribe_public(messageHashes, channels, False, params)
|
|
if self.newUpdates:
|
|
result = {}
|
|
result[newTicker['symbol']] = newTicker
|
|
return result
|
|
return self.filter_by_array(self.bidsasks, 'symbol', symbols)
|
|
|
|
async def un_watch_bids_asks(self, symbols: Strings = None, params={}) -> Any:
|
|
"""
|
|
unWatches best bid & ask for spot symbols
|
|
|
|
https://www.weex.com/api-doc/spot/Websocket/public/BookTicker-Channel
|
|
|
|
:param str[] symbols: unified symbol of the market to fetch the ticker for
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: a `ticker structure <https://docs.ccxt.com/?id=ticker-structure>`
|
|
"""
|
|
await self.load_markets()
|
|
symbols = self.market_symbols(symbols, None, False, True)
|
|
firstMarket = self.get_market_from_symbols(symbols)
|
|
if firstMarket['contract']:
|
|
raise NotSupported(self.id + ' unWatchBidsAsks is supported for spot markets only')
|
|
subHashes = []
|
|
channels = []
|
|
unSubHashes = []
|
|
for i in range(0, len(symbols)):
|
|
symbol = symbols[i]
|
|
market = self.market(symbol)
|
|
channelName = market['id'] + '@' + 'bookTicker'
|
|
messageHash = 'bidask::' + symbol
|
|
unSubMessageHash = 'unsubscribe::' + messageHash
|
|
subHashes.append(messageHash)
|
|
channels.append(channelName)
|
|
unSubHashes.append(unSubMessageHash)
|
|
subscription = {
|
|
'unsubscribe': True,
|
|
'symbols': symbols,
|
|
'messageHashes': unSubHashes,
|
|
'subMessageHashes': subHashes,
|
|
'topic': 'bidsasks',
|
|
}
|
|
return await self.subscribe_public(unSubHashes, channels, False, params, subscription)
|
|
|
|
def handle_bid_ask(self, client: Client, message):
|
|
#
|
|
# {
|
|
# "e": "bookTicker",
|
|
# "E": 1776103547551,
|
|
# "s": "ETHUSDT",
|
|
# "u": 1776103547547,
|
|
# "b": "2227.39",
|
|
# "B": "1.05512",
|
|
# "a": "2227.40",
|
|
# "A": "6.30889"
|
|
# }
|
|
#
|
|
market = self.get_market_from_client_and_message(client, message)
|
|
ticker = self.parse_ws_bid_ask(message, market)
|
|
symbol = ticker['symbol']
|
|
self.bidsasks[symbol] = ticker
|
|
messageHash = 'bidask::' + symbol
|
|
client.resolve(ticker, messageHash)
|
|
|
|
def parse_ws_bid_ask(self, message, market=None):
|
|
timestamp = self.safe_integer(message, 'E')
|
|
return self.safe_ticker({
|
|
'symbol': market['symbol'],
|
|
'timestamp': timestamp,
|
|
'datetime': self.iso8601(timestamp),
|
|
'ask': self.safe_string(message, 'a'),
|
|
'askVolume': self.safe_string(message, 'A'),
|
|
'bid': self.safe_string(message, 'b'),
|
|
'bidVolume': self.safe_string(message, 'B'),
|
|
'info': message,
|
|
}, market)
|
|
|
|
async def watch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
|
|
"""
|
|
watches information on multiple trades made by the user
|
|
|
|
https://www.weex.com/api-doc/spot/Websocket/private/Fill-Channel
|
|
https://www.weex.com/api-doc/contract/Websocket/private/Fill-Channel
|
|
|
|
:param str symbol: unified market symbol of the market trades were made in
|
|
:param int [since]: the earliest time in ms to fetch trades for
|
|
:param int [limit]: the maximum number of trade structures to retrieve
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param str [params.type]: spot or swap, default is spot if symbol is not provided
|
|
:returns dict[]: a list of `trade structures <https://docs.ccxt.com/?id=trade-structure>`
|
|
"""
|
|
await self.load_markets()
|
|
marketType = None
|
|
market = None
|
|
if symbol is not None:
|
|
market = self.market(symbol)
|
|
symbol = market['symbol']
|
|
marketType, params = self.handle_market_type_and_params('watchMyTrades', market, params)
|
|
isContract = (marketType != 'spot')
|
|
messageHash = 'myContractTrades' if isContract else 'myTrades'
|
|
subscriptionHash = messageHash
|
|
if symbol is not None:
|
|
messageHash += '::' + symbol
|
|
channel = 'fill'
|
|
trades = await self.subscribe_private(messageHash, subscriptionHash, channel, isContract, params)
|
|
if self.newUpdates:
|
|
limit = trades.getLimit(symbol, limit)
|
|
return self.filter_by_symbol_since_limit(trades, symbol, since, limit, True)
|
|
|
|
async def un_watch_my_trades(self, symbol: Str = None, params={}) -> Any:
|
|
"""
|
|
unWatches information on multiple trades made by the user
|
|
|
|
https://www.weex.com/api-doc/spot/Websocket/private/Fill-Channel
|
|
https://www.weex.com/api-doc/contract/Websocket/private/Fill-Channel
|
|
|
|
:param str [symbol]: not used by the exchange
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param str [params.type]: spot or swap, default is spot
|
|
:returns dict[]: a list of `order structures <https://docs.ccxt.com/?id=order-structure>`
|
|
"""
|
|
if symbol is not None:
|
|
raise NotSupported(self.id + ' unWatchMyTrades does not support a symbol argument. Unsubscribing from myTrades is global for all symbols.')
|
|
marketType = None
|
|
marketType, params = self.handle_market_type_and_params('unWatchMyTrades', None, params)
|
|
isContract = (marketType != 'spot')
|
|
subHash = 'myContractTrades' if isContract else 'myTrades'
|
|
unSubHash = 'unsubscribe::' + subHash
|
|
channel = 'fill'
|
|
subscription = {
|
|
'unsubscribe': True,
|
|
'messageHashes': [unSubHash],
|
|
'subMessageHashes': [subHash],
|
|
'topic': 'myTrades',
|
|
'subHashIsPrefix': True,
|
|
}
|
|
return await self.subscribe_private(unSubHash, unSubHash, channel, isContract, params, subscription)
|
|
|
|
def handle_my_trades(self, client: Client, message):
|
|
#
|
|
# spot
|
|
# {
|
|
# e: 'fill',
|
|
# E: 1776174283564,
|
|
# v: 83,
|
|
# msgEvent: 'OrderUpdate',
|
|
# d: [
|
|
# {
|
|
# id: '738928502249620072',
|
|
# symbol: 'DOGEUSDT',
|
|
# baseCoin: 'DOGE',
|
|
# quoteCoin: 'USDT',
|
|
# orderId: '738928502174122600',
|
|
# orderSide: 'SELL',
|
|
# fillSize: '200.0',
|
|
# fillValue: '19.098000',
|
|
# fillFee: '0.01909800',
|
|
# direction: 'TAKER',
|
|
# createdTime: '1776174283564',
|
|
# updatedTime: '1776174283564'
|
|
# }
|
|
# ]
|
|
# }
|
|
#
|
|
# swap
|
|
# {
|
|
# "id": "738957755401896296",
|
|
# "coin": "USDT",
|
|
# "symbol": "DOGEUSDT",
|
|
# "orderId": "738957755376730472",
|
|
# "marginMode": "CROSSED",
|
|
# "separatedMode": "COMBINED",
|
|
# "separatedOpenOrderId": "0",
|
|
# "positionSide": "LONG",
|
|
# "orderSide": "BUY",
|
|
# "fillSize": "100",
|
|
# "fillValue": "9.59500",
|
|
# "fillFee": "0.00767600",
|
|
# "liquidateFee": "0",
|
|
# "realizePnl": "0",
|
|
# "direction": "TAKER",
|
|
# "createdTime": "1776181258059",
|
|
# "updatedTime": "1776181258059"
|
|
# }
|
|
#
|
|
if self.myTrades is None:
|
|
limit = self.safe_integer(self.options, 'tradesLimit', 1000)
|
|
self.myTrades = ArrayCacheBySymbolById(limit)
|
|
trades = self.myTrades
|
|
data = self.safe_list(message, 'd', [])
|
|
symbols: dict = {}
|
|
for i in range(0, len(data)):
|
|
trade = self.safe_dict(data, i, {})
|
|
parsed = self.parse_ws_my_trade(trade)
|
|
symbol = parsed['symbol']
|
|
symbols[symbol] = True
|
|
trades.append(parsed)
|
|
messageHash = 'myTrades'
|
|
symbolKeys = list(symbols.keys())
|
|
market = self.get_market_from_symbols(symbolKeys)
|
|
if market['contract']:
|
|
messageHash = 'myContractTrades'
|
|
for j in range(0, len(symbolKeys)):
|
|
symbol = symbolKeys[j]
|
|
symbolMessageHash = messageHash + '::' + symbol
|
|
client.resolve(trades, symbolMessageHash)
|
|
client.resolve(trades, messageHash)
|
|
|
|
def parse_ws_my_trade(self, trade, market=None):
|
|
#
|
|
# spot
|
|
# {
|
|
# id: '738928502249620072',
|
|
# symbol: 'DOGEUSDT',
|
|
# baseCoin: 'DOGE',
|
|
# quoteCoin: 'USDT',
|
|
# orderId: '738928502174122600',
|
|
# orderSide: 'SELL',
|
|
# fillSize: '200.0',
|
|
# fillValue: '19.098000',
|
|
# fillFee: '0.01909800',
|
|
# direction: 'TAKER',
|
|
# createdTime: '1776174283564',
|
|
# updatedTime: '1776174283564'
|
|
# }
|
|
#
|
|
timestamp = self.safe_integer(trade, 'createdTime')
|
|
marketId = self.safe_string(trade, 'symbol')
|
|
marketType = 'spot'
|
|
positionSide = self.safe_string(trade, 'positionSide')
|
|
if positionSide is not None:
|
|
marketType = 'swap'
|
|
market = self.safe_market(marketId, None, None, marketType)
|
|
side = self.safe_string_lower(trade, 'orderSide')
|
|
fee = None
|
|
commission = self.safe_string(trade, 'fillFee')
|
|
if commission is not None:
|
|
commissionAsset = self.safe_string(trade, 'coin')
|
|
feeCurrency = self.safe_currency_code(commissionAsset)
|
|
if marketType == 'spot':
|
|
if side == 'buy':
|
|
feeCurrency = market['base']
|
|
else:
|
|
feeCurrency = market['quote']
|
|
fee = {
|
|
'cost': commission,
|
|
'currency': feeCurrency,
|
|
}
|
|
return self.safe_trade({
|
|
'info': trade,
|
|
'id': self.safe_string(trade, 'id'),
|
|
'timestamp': timestamp,
|
|
'datetime': self.iso8601(timestamp),
|
|
'symbol': market['symbol'],
|
|
'order': self.safe_string(trade, 'orderId'),
|
|
'type': self.safe_string(trade, 'type'),
|
|
'side': side,
|
|
'takerOrMaker': self.safe_string_lower(trade, 'direction'),
|
|
'price': None,
|
|
'amount': self.safe_string(trade, 'fillSize'),
|
|
'cost': self.safe_string(trade, 'fillValue'),
|
|
'fee': fee,
|
|
})
|
|
|
|
async def watch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
|
|
"""
|
|
watches information on multiple orders made by the user
|
|
|
|
https://www.weex.com/api-doc/spot/Websocket/private/Order-Channel
|
|
https://www.weex.com/api-doc/contract/Websocket/private/Order-Channel
|
|
|
|
:param str symbol: unified market symbol of the market orders were made in
|
|
:param int [since]: the earliest time in ms to fetch orders for
|
|
:param int [limit]: the maximum number of order structures to retrieve
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param str [params.type]: spot or swap, default is spot if symbol is not provided
|
|
:returns dict[]: a list of `order structures <https://docs.ccxt.com/?id=order-structure>`
|
|
"""
|
|
await self.load_markets()
|
|
market = None
|
|
if symbol is not None:
|
|
market = self.market(symbol)
|
|
symbol = market['symbol']
|
|
marketType = None
|
|
marketType, params = self.handle_market_type_and_params('watchOrders', market, params)
|
|
isContract = (marketType != 'spot')
|
|
messageHash = 'contractOrders' if isContract else 'orders'
|
|
subscriptionHash = messageHash
|
|
if symbol is not None:
|
|
messageHash += '::' + symbol
|
|
channel = 'orders'
|
|
orders = await self.subscribe_private(messageHash, subscriptionHash, channel, isContract, params)
|
|
if self.newUpdates:
|
|
limit = orders.getLimit(symbol, limit)
|
|
return self.filter_by_symbol_since_limit(orders, symbol, since, limit, True)
|
|
|
|
async def un_watch_orders(self, symbol: Str = None, params={}) -> Any:
|
|
"""
|
|
unWatches information on multiple orders made by the user
|
|
|
|
https://www.weex.com/api-doc/spot/Websocket/private/Order-Channel
|
|
https://www.weex.com/api-doc/contract/Websocket/private/Order-Channel
|
|
|
|
:param str [symbol]: not used by the exchange
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict[]: a list of `order structures <https://docs.ccxt.com/?id=order-structure>`
|
|
"""
|
|
if symbol is not None:
|
|
raise NotSupported(self.id + ' unWatchOrders does not support a symbol argument. Unsubscribing from orders is global for all symbols.')
|
|
marketType = None
|
|
marketType, params = self.handle_market_type_and_params('unWatchOrders', None, params)
|
|
isContract = (marketType != 'spot')
|
|
subHash = 'contractOrders' if isContract else 'orders'
|
|
unSubHash = 'unsubscribe::' + subHash
|
|
channel = 'orders'
|
|
subscription = {
|
|
'unsubscribe': True,
|
|
'messageHashes': [unSubHash],
|
|
'subMessageHashes': [subHash],
|
|
'topic': 'orders',
|
|
'subHashIsPrefix': True,
|
|
}
|
|
return await self.subscribe_private(unSubHash, unSubHash, channel, isContract, params, subscription)
|
|
|
|
def handle_orders(self, client: Client, message):
|
|
#
|
|
# {
|
|
# "e": "orders",
|
|
# "E": 1776184415058,
|
|
# "v": 153,
|
|
# "msgEvent": "OrderUpdate",
|
|
# "d": [
|
|
# {
|
|
# "id": "738970996765098600",
|
|
# "symbol": "DOGEUSDT",
|
|
# "baseCoin": "DOGE",
|
|
# "quoteCoin": "USDT",
|
|
# "orderSide": "SELL",
|
|
# "price": "0",
|
|
# "size": "200.0",
|
|
# "value": "0",
|
|
# "clientOrderId": "b-WEEX111125-bf78d975ca38422bb6ea65",
|
|
# "type": "MARKET",
|
|
# "timeInForce": "IOC",
|
|
# "reduceOnly": False,
|
|
# "triggerPrice": "0",
|
|
# "orderSource": "API",
|
|
# "openTpslParentOrderId": "0",
|
|
# "setOpenTp": False,
|
|
# "setOpenSl": False,
|
|
# "takerFeeRate": "0.001",
|
|
# "makerFeeRate": "0.001",
|
|
# "feeDiscount": "1",
|
|
# "takerFeeDiscount": "1",
|
|
# "makerFeeDiscount": "1",
|
|
# "status": "FILLED",
|
|
# "triggerTime": "0",
|
|
# "triggerPriceTime": "0",
|
|
# "triggerPriceValue": "0",
|
|
# "cancelReason": "UNKNOWN_ORDER_CANCEL_REASON",
|
|
# "latestFillPrice": "0.09571",
|
|
# "maxFillPrice": "0.09571",
|
|
# "minFillPrice": "0.09571",
|
|
# "cumFillSize": "200.0",
|
|
# "cumFillValue": "19.142000",
|
|
# "cumFillFee": "0.01914200",
|
|
# "createdTime": "1776184415046",
|
|
# "updatedTime": "1776184415058"
|
|
# }
|
|
# ]
|
|
# }
|
|
#
|
|
data = self.safe_list(message, 'd', [])
|
|
symbols: dict = {}
|
|
if self.orders is None:
|
|
limit = self.safe_integer(self.options, 'ordersLimit', 1000)
|
|
self.orders = ArrayCacheBySymbolById(limit)
|
|
orders = self.orders
|
|
for i in range(0, len(data)):
|
|
rawOrder = self.safe_dict(data, i, {})
|
|
parsed = self.parse_ws_order(rawOrder)
|
|
orders.append(parsed)
|
|
symbol = parsed['symbol']
|
|
symbols[symbol] = True
|
|
messageHash = 'orders'
|
|
symbolKeys = list(symbols.keys())
|
|
market = self.get_market_from_symbols(symbolKeys)
|
|
if market['contract']:
|
|
messageHash = 'contractOrders'
|
|
for i in range(0, len(symbolKeys)):
|
|
symbol = symbolKeys[i]
|
|
symbolMessageHash = messageHash + '::' + symbol
|
|
client.resolve(orders, symbolMessageHash)
|
|
client.resolve(self.orders, messageHash)
|
|
|
|
def parse_ws_order(self, order, market=None):
|
|
#
|
|
# spot
|
|
# {
|
|
# "id": "738970996765098600",
|
|
# "symbol": "DOGEUSDT",
|
|
# "baseCoin": "DOGE",
|
|
# "quoteCoin": "USDT",
|
|
# "orderSide": "SELL",
|
|
# "price": "0",
|
|
# "size": "200.0",
|
|
# "value": "0",
|
|
# "clientOrderId": "b-WEEX111125-bf78d975ca38422bb6ea65",
|
|
# "type": "MARKET",
|
|
# "timeInForce": "IOC",
|
|
# "reduceOnly": False,
|
|
# "triggerPrice": "0",
|
|
# "orderSource": "API",
|
|
# "openTpslParentOrderId": "0",
|
|
# "setOpenTp": False,
|
|
# "setOpenSl": False,
|
|
# "takerFeeRate": "0.001",
|
|
# "makerFeeRate": "0.001",
|
|
# "feeDiscount": "1",
|
|
# "takerFeeDiscount": "1",
|
|
# "makerFeeDiscount": "1",
|
|
# "status": "FILLED",
|
|
# "triggerTime": "0",
|
|
# "triggerPriceTime": "0",
|
|
# "triggerPriceValue": "0",
|
|
# "cancelReason": "UNKNOWN_ORDER_CANCEL_REASON",
|
|
# "latestFillPrice": "0.09571",
|
|
# "maxFillPrice": "0.09571",
|
|
# "minFillPrice": "0.09571",
|
|
# "cumFillSize": "200.0",
|
|
# "cumFillValue": "19.142000",
|
|
# "cumFillFee": "0.01914200",
|
|
# "createdTime": "1776184415046",
|
|
# "updatedTime": "1776184415058"
|
|
# }
|
|
#
|
|
# swap
|
|
# {
|
|
# "id": "617414920861909658",
|
|
# "coin": "USDT",
|
|
# "symbol": "BTCUSDT",
|
|
# "marginMode": "CROSSED",
|
|
# "separatedMode": "COMBINED",
|
|
# "separatedOpenOrderId": "0",
|
|
# "positionSide": "LONG",
|
|
# "orderSide": "BUY",
|
|
# "price": "0.0",
|
|
# "size": "0.10000",
|
|
# "clientOrderId": "1747203186927FPIZRP",
|
|
# "type": "MARKET",
|
|
# "timeInForce": "IOC",
|
|
# "reduceOnly": False,
|
|
# "triggerPrice": "0",
|
|
# "triggerPriceType": "CONTRACT_PRICE",
|
|
# "orderSource": "WEB",
|
|
# "openTpslParentOrderId": "0",
|
|
# "positionTpsl": False,
|
|
# "setOpenTp": False,
|
|
# "setOpenSl": False,
|
|
# "leverage": "20",
|
|
# "takerFeeRate": "0.0006",
|
|
# "makerFeeRate": "0.0002",
|
|
# "feeDiscount": "1",
|
|
# "liquidateFeeRate": "0.01",
|
|
# "status": "PENDING",
|
|
# "triggerTime": "0",
|
|
# "triggerPriceTime": "0",
|
|
# "triggerPriceValue": "0",
|
|
# "cancelReason": "UNKNOWN_ORDER_CANCEL_REASON",
|
|
# "latestFillPrice": "0",
|
|
# "maxFillPrice": "0",
|
|
# "minFillPrice": "0",
|
|
# "cumFillSize": "0",
|
|
# "cumFillValue": "0",
|
|
# "cumFillFee": "0",
|
|
# "cumLiquidateFee": "0",
|
|
# "cumRealizePnl": "0",
|
|
# "createdTime": "1747203188148",
|
|
# "updatedTime": "1747203188148"
|
|
# }
|
|
#
|
|
timestamp = self.safe_integer(order, 'createdTime')
|
|
marketId = self.safe_string(order, 'symbol')
|
|
marketType = 'spot'
|
|
positionSide = self.safe_string(order, 'positionSide')
|
|
if positionSide is not None:
|
|
marketType = 'swap'
|
|
market = self.safe_market(marketId, None, None, marketType)
|
|
side = self.safe_string_lower(order, 'orderSide')
|
|
fee = None
|
|
commission = self.safe_string(order, 'cumFillFee')
|
|
if commission is not None:
|
|
commissionAsset = self.safe_string(order, 'coin')
|
|
feeCurrency = self.safe_currency_code(commissionAsset)
|
|
if marketType == 'spot':
|
|
if side == 'buy':
|
|
feeCurrency = market['base']
|
|
else:
|
|
feeCurrency = market['quote']
|
|
fee = {
|
|
'cost': commission,
|
|
'currency': feeCurrency,
|
|
}
|
|
rawStatus = self.safe_string_lower(order, 'status')
|
|
rawType = self.safe_string(order, 'type')
|
|
triggerPrice = self.omit_zero(self.safe_string(order, 'triggerPrice'))
|
|
stopLossPrice = None
|
|
takeProfitPrice = None
|
|
if rawType == 'TAKE_PROFIT_MARKET' or rawType == 'TAKE_PROFIT':
|
|
takeProfitPrice = triggerPrice
|
|
elif rawType == 'STOP_LOSS' or rawType == 'STOP' or rawType == 'STOP_MARKET':
|
|
stopLossPrice = triggerPrice
|
|
return self.safe_order({
|
|
'id': self.safe_string(order, 'id'),
|
|
'clientOrderId': self.safe_string(order, 'clientOrderId'),
|
|
'symbol': market['symbol'],
|
|
'type': self.parseOrderType(rawType),
|
|
'timeInForce': self.safe_string(order, 'timeInForce'),
|
|
'postOnly': None,
|
|
'reduceOnly': self.safe_bool(order, 'reduceOnly'),
|
|
'side': side,
|
|
'amount': self.safe_string(order, 'size'),
|
|
'price': self.safe_string(order, 'price'),
|
|
'triggerPrice': triggerPrice,
|
|
'cost': self.safe_string(order, 'cumFillValue'),
|
|
'filled': self.safe_string(order, 'cumFillSize'),
|
|
'remaining': None,
|
|
'timestamp': timestamp,
|
|
'datetime': self.iso8601(timestamp),
|
|
'fee': fee,
|
|
'status': self.parse_order_status(rawStatus),
|
|
'lastTradeTimestamp': None,
|
|
'lastUpdateTimestamp': self.safe_integer(order, 'updatedTime'),
|
|
'average': None,
|
|
'trades': None,
|
|
'stopLossPrice': stopLossPrice,
|
|
'takeProfitPrice': takeProfitPrice,
|
|
'info': order,
|
|
}, market)
|
|
|
|
async def watch_balance(self, params={}) -> Balances:
|
|
"""
|
|
query for balance and get the amount of funds available for trading or funds locked in orders
|
|
|
|
https://www.weex.com/api-doc/spot/Websocket/private/Account-Channel
|
|
https://www.weex.com/api-doc/contract/Websocket/private/Account-Channel
|
|
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:param str [params.type]: 'spot' or 'swap', default is 'spot'
|
|
:returns dict: a `balance structure <https://docs.ccxt.com/?id=balance-structure>`
|
|
"""
|
|
await self.load_markets()
|
|
type = None
|
|
type, params = self.handle_market_type_and_params('watchBalance', None, params)
|
|
isContract = (type != 'spot')
|
|
urlType = 'contract' if isContract else 'spot'
|
|
url = self.urls['api']['ws'][urlType] + '/private'
|
|
self.authenticate(url)
|
|
client = self.client(url)
|
|
self.set_balance_cache(client, type)
|
|
options = self.safe_dict(self.options, 'watchBalance')
|
|
fetchBalanceSnapshot = self.safe_bool(options, 'fetchBalanceSnapshot', False)
|
|
awaitBalanceSnapshot = self.safe_bool(options, 'awaitBalanceSnapshot', True)
|
|
if fetchBalanceSnapshot and awaitBalanceSnapshot:
|
|
await client.future(type + ':fetchBalanceSnapshot')
|
|
messageHash = type + ':' + 'balance'
|
|
return await self.subscribe_private(messageHash, type, 'account', isContract, params)
|
|
|
|
def set_balance_cache(self, client: Client, type):
|
|
if (type in client.subscriptions) and (type in self.balance):
|
|
return
|
|
options = self.safe_dict(self.options, 'watchBalance')
|
|
fetchBalanceSnapshot = self.safe_bool(options, 'fetchBalanceSnapshot', False)
|
|
if fetchBalanceSnapshot:
|
|
messageHash = type + ':fetchBalanceSnapshot'
|
|
if not (messageHash in client.futures):
|
|
client.future(messageHash)
|
|
self.spawn(self.load_balance_snapshot, client, messageHash, type)
|
|
else:
|
|
self.balance[type] = {}
|
|
|
|
async def load_balance_snapshot(self, client, messageHash, type):
|
|
params: dict = {
|
|
'type': type,
|
|
}
|
|
response = await self.fetch_balance(params)
|
|
self.balance[type] = self.extend(response, self.safe_value(self.balance, type, {}))
|
|
# don't remove the future from the .futures cache
|
|
if messageHash in client.futures:
|
|
future = client.futures[messageHash]
|
|
future.resolve()
|
|
client.resolve(self.balance[type], type + ':balance')
|
|
|
|
def handle_balance(self, client: Client, message):
|
|
#
|
|
# spot
|
|
# {
|
|
# "e": "account",
|
|
# "E": 1776187844633,
|
|
# "v": 178,
|
|
# "msgEvent": "DepositUpdate",
|
|
# "d": [
|
|
# {
|
|
# "coin": "USDT",
|
|
# "equity": "47.98428060",
|
|
# "available": "47.98428060",
|
|
# "frozen": "0"
|
|
# }
|
|
# ]
|
|
# }
|
|
#
|
|
# coontract
|
|
# {
|
|
# "e": "account",
|
|
# "E": 1776189629849,
|
|
# "v": 281,
|
|
# "msgEvent": "DepositUpdate",
|
|
# "d": [
|
|
# {
|
|
# "coin": "USDT",
|
|
# "marginMode": "CROSSED",
|
|
# "crossSymbol": "0",
|
|
# "isolatedPositionId": "0",
|
|
# "amount": "0.00000000",
|
|
# "pendingDepositAmount": "20.00000000",
|
|
# "pendingWithdrawAmount": "0.00000000",
|
|
# "pendingTransferInAmount": "0",
|
|
# "pendingTransferOutAmount": "0",
|
|
# "liquidating": False,
|
|
# "legacyAmount": "0.00000000",
|
|
# "cumDepositAmount": "167.50000925",
|
|
# "cumWithdrawAmount": "166.94609514",
|
|
# "cumTransferInAmount": "0",
|
|
# "cumTransferOutAmount": "0",
|
|
# "cumMarginMoveInAmount": "10.86162763",
|
|
# "cumMarginMoveOutAmount": "10.83205378",
|
|
# "cumPositionOpenLongAmount": "305.59400",
|
|
# "cumPositionOpenShortAmount": "238.95700",
|
|
# "cumPositionCloseLongAmount": "305.86600000",
|
|
# "cumPositionCloseShortAmount": "238.94700000",
|
|
# "cumPositionFillFeeAmount": "0.00761040",
|
|
# "cumPositionLiquidateFeeAmount": "0",
|
|
# "cumPositionFundingAmount": "0.00049824",
|
|
# "cumOrderFillFeeIncomeAmount": "0",
|
|
# "cumOrderLiquidateFeeIncomeAmount": "0",
|
|
# "createdTime": "1775605824300",
|
|
# "updatedTime": "1776189629849"
|
|
# }
|
|
# ]
|
|
# }
|
|
#
|
|
url = client.url
|
|
accountType = 'spot'
|
|
if url.find('contract') >= 0:
|
|
accountType = 'swap'
|
|
messageHash = accountType + ':balance'
|
|
if self.balance[accountType] is None:
|
|
self.balance[accountType] = {}
|
|
self.balance[accountType]['info'] = message
|
|
balanceUpdates = self.safe_list(message, 'd', [])
|
|
for i in range(0, len(balanceUpdates)):
|
|
entry = self.safe_dict(balanceUpdates, i)
|
|
currencyId = self.safe_string(entry, 'coin')
|
|
code = self.safe_currency_code(currencyId)
|
|
account = self.account()
|
|
account['free'] = self.safe_string_2(entry, 'available', 'amount')
|
|
account['used'] = self.safe_string(entry, 'frozen')
|
|
account['total'] = self.safe_string_2(entry, 'equity', 'legacyAmount')
|
|
self.balance[accountType][code] = account
|
|
timestamp = self.safe_integer(message, 'E')
|
|
self.balance[accountType]['timestamp'] = timestamp
|
|
self.balance[accountType]['datetime'] = self.iso8601(timestamp)
|
|
self.balance[accountType] = self.safe_balance(self.balance[accountType])
|
|
client.resolve(self.balance[accountType], messageHash)
|
|
|
|
async def watch_positions(self, symbols: Strings = None, since: Int = None, limit: Int = None, params={}) -> List[Position]:
|
|
"""
|
|
|
|
https://www.weex.com/api-doc/contract/Websocket/private/Positions-Channel
|
|
|
|
watch all open positions
|
|
:param str[]|None symbols: list of unified market symbols
|
|
:param int [since]: the earliest time in ms to fetch positions for
|
|
:param int [limit]: the maximum number of position structures to retrieve
|
|
:param dict params: extra parameters specific to the exchange API endpoint
|
|
:param int [params.accountNumber]: account number to query orders for, required
|
|
:returns dict[]: a list of `position structure <https://docs.ccxt.com/en/latest/manual.html#position-structure>`
|
|
"""
|
|
await self.load_markets()
|
|
url = self.urls['api']['ws']['contract'] + '/private'
|
|
self.authenticate(url)
|
|
client = self.client(url)
|
|
symbols = self.market_symbols(symbols, 'swap', True)
|
|
messageHash = 'positions'
|
|
subscriptionHash = messageHash
|
|
if symbols is not None:
|
|
messageHash += '::' + ','.join(symbols)
|
|
channel = 'positions'
|
|
self.set_positions_cache(client, params)
|
|
fetchPositionsSnapshot = self.handle_option('watchPositions', 'fetchPositionsSnapshot', True)
|
|
awaitPositionsSnapshot = self.handle_option('watchPositions', 'awaitPositionsSnapshot', True)
|
|
if fetchPositionsSnapshot and awaitPositionsSnapshot and self.positions is None:
|
|
snapshot = await client.future('fetchPositionsSnapshot')
|
|
return self.filter_by_symbols_since_limit(snapshot, symbols, since, limit, True)
|
|
newPositions = await self.subscribe_private(messageHash, subscriptionHash, channel, True, params)
|
|
if self.newUpdates:
|
|
return newPositions
|
|
return self.filter_by_symbols_since_limit(self.positions, symbols, since, limit, True)
|
|
|
|
def set_positions_cache(self, client: Client, params: dict = {}):
|
|
fetchPositionsSnapshot = self.handle_option('watchPositions', 'fetchPositionsSnapshot', False)
|
|
if fetchPositionsSnapshot:
|
|
messageHash = 'fetchPositionsSnapshot'
|
|
if not (messageHash in client.futures):
|
|
client.future(messageHash)
|
|
self.spawn(self.load_positions_snapshot, client, messageHash, params)
|
|
else:
|
|
self.positions = ArrayCacheBySymbolById()
|
|
|
|
async def load_positions_snapshot(self, client, messageHash, params):
|
|
positions = await self.fetch_positions(None, params)
|
|
self.positions = ArrayCacheBySymbolById()
|
|
cache = self.positions
|
|
for i in range(0, len(positions)):
|
|
position = positions[i]
|
|
cache.append(position)
|
|
# don't remove the future from the .futures cache
|
|
future = client.futures[messageHash]
|
|
future.resolve(cache)
|
|
client.resolve(cache, 'positions')
|
|
|
|
async def un_watch_positions(self, symbols: Strings = None, params={}) -> Any:
|
|
"""
|
|
unWatches all open positions
|
|
|
|
https://www.weex.com/api-doc/contract/Websocket/private/Positions-Channel
|
|
|
|
:param str[] [symbols]: not used by the exchange, unsubscription from positions is global for all symbols
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: status of the unwatch request
|
|
"""
|
|
if symbols is not None:
|
|
raise NotSupported(self.id + ' unWatchPositions does not support a symbols argument. Unsubscribing from positions is global for all symbols.')
|
|
subHash = 'positions'
|
|
unSubHash = 'unsubscribe::' + subHash
|
|
channel = 'positions'
|
|
subscription = {
|
|
'unsubscribe': True,
|
|
'messageHashes': [unSubHash],
|
|
'subMessageHashes': [subHash],
|
|
'topic': 'positions',
|
|
'subHashIsPrefix': True,
|
|
}
|
|
return await self.subscribe_private(unSubHash, unSubHash, channel, True, params, subscription)
|
|
|
|
def handle_positions(self, client, message):
|
|
#
|
|
# {
|
|
# "e": "positions",
|
|
# "E": 1776192398399,
|
|
# "v": 319,
|
|
# "msgEvent": "OrderUpdate",
|
|
# "d": [
|
|
# {
|
|
# "id": "739004481374519656",
|
|
# "coin": "USDT",
|
|
# "symbol": "DOGEUSDT",
|
|
# "side": "LONG",
|
|
# "marginMode": "CROSSED",
|
|
# "separatedMode": "COMBINED",
|
|
# "separatedOpenOrderId": "0",
|
|
# "leverage": "11",
|
|
# "size": "100",
|
|
# "openValue": "9.31100",
|
|
# "openFee": "0.00744880",
|
|
# "fundingFee": "0",
|
|
# "isolatedMargin": "0",
|
|
# "autoAppendIsolatedMargin": False,
|
|
# "cumOpenSize": "100",
|
|
# "cumOpenValue": "9.31100",
|
|
# "cumOpenFee": "0.00744880",
|
|
# "cumCloseSize": "0",
|
|
# "cumCloseValue": "0",
|
|
# "cumCloseFee": "0",
|
|
# "cumFundingFee": "0",
|
|
# "cumLiquidateFee": "0",
|
|
# "createdMatchSequenceId": "5792711540",
|
|
# "updatedMatchSequenceId": "5792711540",
|
|
# "createdTime": "1776192398399",
|
|
# "updatedTime": "1776192398399"
|
|
# }
|
|
# ]
|
|
# }
|
|
#
|
|
if self.positions is None:
|
|
self.positions = ArrayCacheBySymbolById()
|
|
cache = self.positions
|
|
newPositions = []
|
|
data = self.safe_list(message, 'd', [])
|
|
for i in range(0, len(data)):
|
|
rawPosition = self.safe_dict(data, i, {})
|
|
position = self.parse_ws_position(rawPosition)
|
|
cache.append(position)
|
|
newPositions.append(position)
|
|
messageHashes = self.find_message_hashes(client, 'positions::')
|
|
for i in range(0, len(messageHashes)):
|
|
messageHash = messageHashes[i]
|
|
parts = messageHash.split('::')
|
|
symbolsString = parts[1]
|
|
symbols = symbolsString.split(',')
|
|
positions = self.filter_by_array(newPositions, 'symbol', symbols, False)
|
|
if not self.is_empty(positions):
|
|
client.resolve(positions, messageHash)
|
|
client.resolve(newPositions, 'positions')
|
|
|
|
def parse_ws_position(self, position, market=None):
|
|
# same api
|
|
return self.parse_position(position, market)
|
|
|
|
def get_market_from_client_and_message(self, client: Client, message):
|
|
url = client.url
|
|
marketType = 'spot'
|
|
if url.find('contract') >= 0:
|
|
marketType = 'swap'
|
|
marketId = self.safe_string(message, 's')
|
|
market = self.safe_market(marketId, None, None, marketType)
|
|
return market
|
|
|
|
async def pong(self, client: Client, message):
|
|
#
|
|
# {"event": "ping", "time": "1776078750000"} - public
|
|
#
|
|
# {"type": "ping", "time": "1776172740000"} - private
|
|
#
|
|
response: dict = {
|
|
'id': self.request_id(),
|
|
'method': 'PONG',
|
|
}
|
|
await client.send(response)
|
|
|
|
def handle_ping(self, client: Client, message):
|
|
self.spawn(self.pong, client, message)
|
|
|
|
def handle_subscription_status(self, client: Client, message):
|
|
#
|
|
# {"result": True, "id": 2}
|
|
#
|
|
id = self.safe_string(message, 'id')
|
|
subscriptionsById = self.index_by(client.subscriptions, 'id')
|
|
subscription = self.safe_dict(subscriptionsById, id, {})
|
|
unsubscribe = self.safe_bool(subscription, 'unsubscribe', False)
|
|
if unsubscribe:
|
|
subHashIsPrefix = self.safe_bool(subscription, 'subHashIsPrefix', False)
|
|
messageHashes = self.safe_list(subscription, 'messageHashes', [])
|
|
subHashes = self.safe_list(subscription, 'subMessageHashes', [])
|
|
for i in range(0, len(messageHashes)):
|
|
unSubHash = self.safe_string(messageHashes, i)
|
|
subHash = self.safe_string(subHashes, i)
|
|
self.clean_unsubscription(client, subHash, unSubHash, subHashIsPrefix)
|
|
self.clean_cache(subscription)
|
|
return message
|
|
|
|
def handle_error_message(self, client: Client, message):
|
|
#
|
|
# {
|
|
# "result": False,
|
|
# "id": 1,
|
|
# "msg": "INVALID_ARGUMENT: invalid symbol : ASDFS_SPBL"
|
|
# }
|
|
#
|
|
result = self.safe_bool(message, 'result', True)
|
|
if not result:
|
|
msg = self.safe_string(message, 'msg', '')
|
|
feedback = self.id + ' ' + self.json(message)
|
|
try:
|
|
self.throw_exactly_matched_exception(self.exceptions['exact'], msg, feedback)
|
|
self.throw_broadly_matched_exception(self.exceptions['broad'], msg, feedback)
|
|
raise ExchangeError(feedback)
|
|
except Exception as error:
|
|
client.reject(error)
|
|
return True
|
|
return False
|
|
|
|
def handle_message(self, client: Client, message):
|
|
#
|
|
# {"id": "5", "method": "PONG"}
|
|
#
|
|
# {"result": True, "id": 2}
|
|
#
|
|
# {
|
|
# "result": False,
|
|
# "id": 1,
|
|
# "msg": "INVALID_ARGUMENT: invalid symbol : ASDFS_SPBL"
|
|
# }
|
|
#
|
|
if self.handle_error_message(client, message):
|
|
return
|
|
id = self.safe_string(message, 'id')
|
|
if id is not None:
|
|
self.handle_subscription_status(client, message)
|
|
return
|
|
event = self.safe_string_n(message, ['e', 'event', 'type'])
|
|
if event == 'ping':
|
|
self.handle_ping(client, message)
|
|
elif event == 'ticker':
|
|
self.handle_ticker(client, message)
|
|
elif (event == 'trade') or (event == 'tradeSnapshot'):
|
|
self.handle_trade(client, message)
|
|
elif (event == 'kline') or (event == 'klineSnapshot'):
|
|
self.handle_ohlcv(client, message)
|
|
elif (event == 'depth') or (event == 'depthSnapshot'):
|
|
self.handle_order_book(client, message)
|
|
elif event == 'bookTicker':
|
|
self.handle_bid_ask(client, message)
|
|
elif event == 'fill':
|
|
self.handle_my_trades(client, message)
|
|
elif event == 'orders':
|
|
self.handle_orders(client, message)
|
|
elif event == 'account':
|
|
self.handle_balance(client, message)
|
|
elif event == 'positions':
|
|
self.handle_positions(client, message)
|