Files
ccxt_with_mt5/ccxt/pro/phemex.py
lz_db 0fab423a18 add
2025-11-16 12:31:03 +08:00

1497 lines
62 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, Order, OrderBook, Str, Strings, Ticker, Tickers, Trade
from ccxt.async_support.base.ws.client import Client
from typing import List
from ccxt.base.errors import AuthenticationError
from ccxt.base.precise import Precise
class phemex(ccxt.async_support.phemex):
def describe(self) -> Any:
return self.deep_extend(super(phemex, self).describe(), {
'has': {
'ws': True,
'watchTicker': True,
'watchTickers': True,
'watchTrades': True,
'watchMyTrades': True,
'watchOrders': True,
'watchOrderBook': True,
'watchOHLCV': True,
'watchPositions': None, # TODO
# mutli-endpoints are not supported: https://github.com/ccxt/ccxt/pull/21490
'watchOrderBookForSymbols': False,
'watchTradesForSymbols': False,
'watchOHLCVForSymbols': False,
'watchBalance': True,
},
'urls': {
'test': {
'ws': 'wss://testnet-api.phemex.com/ws',
},
'api': {
'ws': 'wss://ws.phemex.com',
},
},
'options': {
'tradesLimit': 1000,
'OHLCVLimit': 1000,
},
'streaming': {
'keepAlive': 9000,
},
})
def from_en(self, en, scale):
if en is None:
return None
precise = Precise(en)
precise.decimals = self.sum(precise.decimals, scale)
precise.reduce()
return str(precise)
def from_ep(self, ep, market=None):
if (ep is None) or (market is None):
return ep
return self.from_en(ep, self.safe_integer(market, 'priceScale'))
def from_ev(self, ev, market=None):
if (ev is None) or (market is None):
return ev
return self.from_en(ev, self.safe_integer(market, 'valueScale'))
def from_er(self, er, market=None):
if (er is None) or (market is None):
return er
return self.from_en(er, self.safe_integer(market, 'ratioScale'))
def request_id(self):
requestId = self.sum(self.safe_integer(self.options, 'requestId', 0), 1)
self.options['requestId'] = requestId
return requestId
def parse_swap_ticker(self, ticker, market=None):
#
# {
# "close": 442800,
# "fundingRate": 10000,
# "high": 445400,
# "indexPrice": 442621,
# "low": 428400,
# "markPrice": 442659,
# "open": 432200,
# "openInterest": 744183,
# "predFundingRate": 10000,
# "symbol": "LTCUSD",
# "turnover": 8133238294,
# "volume": 934292
# }
#
marketId = self.safe_string(ticker, 'symbol')
market = self.safe_market(marketId, market)
symbol = market['symbol']
timestamp = self.safe_integer_product(ticker, 'timestamp', 0.000001)
lastString = self.from_ep(self.safe_string(ticker, 'close'), market)
last = self.parse_number(lastString)
quoteVolume = self.parse_number(self.from_ev(self.safe_string(ticker, 'turnover'), market))
baseVolume = self.parse_number(self.from_ev(self.safe_string(ticker, 'volume'), market))
change = None
percentage = None
average = None
openString = self.omit_zero(self.from_ep(self.safe_string(ticker, 'open'), market))
open = self.parse_number(openString)
if (openString is not None) and (lastString is not None):
change = self.parse_number(Precise.string_sub(lastString, openString))
average = self.parse_number(Precise.string_div(Precise.string_add(lastString, openString), '2'))
percentage = self.parse_number(Precise.string_mul(Precise.string_sub(Precise.string_div(lastString, openString), '1'), '100'))
return self.safe_ticker({
'symbol': symbol,
'timestamp': timestamp,
'datetime': self.iso8601(timestamp),
'high': self.parse_number(self.from_ep(self.safe_string(ticker, 'high'), market)),
'low': self.parse_number(self.from_ep(self.safe_string(ticker, 'low'), market)),
'bid': None,
'bidVolume': None,
'ask': None,
'askVolume': None,
'vwap': None,
'open': open,
'close': last,
'last': last,
'previousClose': None, # previous day close
'change': change,
'percentage': percentage,
'average': average,
'baseVolume': baseVolume,
'quoteVolume': quoteVolume,
'markPrice': self.parse_number(self.from_ep(self.safe_string(ticker, 'markPrice'), market)),
'indexPrice': self.parse_number(self.from_ep(self.safe_string(ticker, 'indexPrice'), market)),
'info': ticker,
})
def parse_perpetual_ticker(self, ticker, market=None):
#
# [
# "STXUSDT",
# "0.64649",
# "0.8628",
# "0.61215",
# "0.71737",
# "4519387",
# "3210827.98166",
# "697635",
# "0.71720205",
# "0.71720205",
# "0.0001",
# "0.0001",
# ]
#
marketId = self.safe_string(ticker, 0)
market = self.safe_market(marketId, market)
symbol = market['symbol']
lastString = self.from_ep(self.safe_string(ticker, 4), market)
last = self.parse_number(lastString)
quoteVolume = self.parse_number(self.from_ev(self.safe_string(ticker, 6), market))
baseVolume = self.parse_number(self.from_ev(self.safe_string(ticker, 5), market))
change = None
percentage = None
average = None
openString = self.omit_zero(self.from_ep(self.safe_string(ticker, 1), market))
open = self.parse_number(openString)
if (openString is not None) and (lastString is not None):
change = self.parse_number(Precise.string_sub(lastString, openString))
average = self.parse_number(Precise.string_div(Precise.string_add(lastString, openString), '2'))
percentage = self.parse_number(Precise.string_mul(Precise.string_sub(Precise.string_div(lastString, openString), '1'), '100'))
return self.safe_ticker({
'symbol': symbol,
'timestamp': None,
'datetime': None,
'high': self.parse_number(self.from_ep(self.safe_string(ticker, 2), market)),
'low': self.parse_number(self.from_ep(self.safe_string(ticker, 3), market)),
'bid': None,
'bidVolume': None,
'ask': None,
'askVolume': None,
'vwap': None,
'open': open,
'close': last,
'last': last,
'previousClose': None, # previous day close
'change': change,
'percentage': percentage,
'average': average,
'baseVolume': baseVolume,
'quoteVolume': quoteVolume,
'info': ticker,
})
def handle_ticker(self, client: Client, message):
#
# {
# "spot_market24h": {
# "askEp": 958148000000,
# "bidEp": 957884000000,
# "highEp": 962000000000,
# "lastEp": 958220000000,
# "lowEp": 928049000000,
# "openEp": 935597000000,
# "symbol": "sBTCUSDT",
# "turnoverEv": 146074214388978,
# "volumeEv": 15492228900
# },
# "timestamp": 1592847265888272100
# }
#
# swap
#
# {
# "market24h": {
# "close": 442800,
# "fundingRate": 10000,
# "high": 445400,
# "indexPrice": 442621,
# "low": 428400,
# "markPrice": 442659,
# "open": 432200,
# "openInterest": 744183,
# "predFundingRate": 10000,
# "symbol": "LTCUSD",
# "turnover": 8133238294,
# "volume": 934292
# },
# "timestamp": 1592845585373374500
# }
#
# perpetual
#
# {
# "data": [
# [
# "STXUSDT",
# "0.64649",
# "0.8628",
# "0.61215",
# "0.71737",
# "4519387",
# "3210827.98166",
# "697635",
# "0.71720205",
# "0.71720205",
# "0.0001",
# "0.0001",
# ],
# ...
# ],
# "fields": [
# "symbol",
# "openRp",
# "highRp",
# "lowRp",
# "lastRp",
# "volumeRq",
# "turnoverRv",
# "openInterestRv",
# "indexRp",
# "markRp",
# "fundingRateRr",
# "predFundingRateRr",
# ],
# "method": "perp_market24h_pack_p.update",
# "timestamp": "1677094918686806209",
# "type": "snapshot",
# }
#
tickers = []
if 'market24h' in message:
ticker = self.safe_value(message, 'market24h')
tickers.append(self.parse_swap_ticker(ticker))
elif 'spot_market24h' in message:
ticker = self.safe_value(message, 'spot_market24h')
tickers.append(self.parse_ticker(ticker))
elif 'data' in message:
data = self.safe_value(message, 'data', [])
for i in range(0, len(data)):
tickers.append(self.parse_perpetual_ticker(data[i]))
for i in range(0, len(tickers)):
ticker = tickers[i]
symbol = ticker['symbol']
messageHash = 'ticker:' + symbol
timestamp = self.safe_integer_product(message, 'timestamp', 0.000001)
ticker['timestamp'] = timestamp
ticker['datetime'] = self.iso8601(timestamp)
self.tickers[symbol] = ticker
client.resolve(ticker, messageHash)
async def watch_balance(self, params={}) -> Balances:
"""
https://github.com/phemex/phemex-api-docs/blob/master/Public-Hedged-Perpetual-API.md#subscribe-account-order-position-aop
https://github.com/phemex/phemex-api-docs/blob/master/Public-Contract-API-en.md#subscribe-account-order-position-aop
https://github.com/phemex/phemex-api-docs/blob/master/Public-Spot-API-en.md#subscribe-wallet-order-messages
watch balance and get the amount of funds available for trading or funds locked in orders
:param dict [params]: extra parameters specific to the exchange API endpoint
:param str [params.settle]: set to USDT to use hedged perpetual api
: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)
usePerpetualApi = self.safe_string(params, 'settle') == 'USDT'
messageHash = ':balance'
messageHash = 'perpetual' + messageHash if usePerpetualApi else type + messageHash
return await self.subscribe_private(type, messageHash, params)
def handle_balance(self, type, client, message):
# spot
# [
# {
# "balanceEv": 0,
# "currency": "BTC",
# "lastUpdateTimeNs": "1650442638722099092",
# "lockedTradingBalanceEv": 0,
# "lockedWithdrawEv": 0,
# "userID": 2647224
# },
# {
# "balanceEv": 1154232337,
# "currency": "USDT",
# "lastUpdateTimeNs": "1650442617610017597",
# "lockedTradingBalanceEv": 0,
# "lockedWithdrawEv": 0,
# "userID": 2647224
# }
# ]
# swap
# [
# {
# "accountBalanceEv": 0,
# "accountID": 26472240001,
# "bonusBalanceEv": 0,
# "currency": "BTC",
# "totalUsedBalanceEv": 0,
# "userID": 2647224
# }
# ]
# perpetual
# [
# {
# "accountBalanceRv": "1508.452588802237",
# "accountID": 9328670003,
# "bonusBalanceRv": "0",
# "currency": "USDT",
# "totalUsedBalanceRv": "343.132599666883",
# "userID": 932867
# }
# ]
#
self.balance['info'] = message
for i in range(0, len(message)):
balance = message[i]
currencyId = self.safe_string(balance, 'currency')
code = self.safe_currency_code(currencyId)
currency = self.safe_value(self.currencies, code, {})
scale = self.safe_integer(currency, 'valueScale', 8)
account = self.account()
used = self.safe_string(balance, 'totalUsedBalanceRv')
if used is None:
usedEv = self.safe_string(balance, 'totalUsedBalanceEv')
if usedEv is None:
lockedTradingBalanceEv = self.safe_string(balance, 'lockedTradingBalanceEv')
lockedWithdrawEv = self.safe_string_2(balance, 'lockedWithdrawEv', 'lockedWithdrawRv')
usedEv = Precise.string_add(lockedTradingBalanceEv, lockedWithdrawEv)
used = self.from_en(usedEv, scale)
total = self.safe_string(balance, 'accountBalanceRv')
if total is None:
totalEv = self.safe_string_2(balance, 'accountBalanceEv', 'balanceEv')
total = self.from_en(totalEv, scale)
account['used'] = used
account['total'] = total
self.balance[code] = account
self.balance = self.safe_balance(self.balance)
messageHash = type + ':balance'
client.resolve(self.balance, messageHash)
def handle_trades(self, client: Client, message):
#
# {
# "sequence": 1795484727,
# "symbol": "sBTCUSDT",
# "trades": [
# [1592891002064516600, "Buy", 964020000000, 1431000],
# [1592890978987934500, "Sell", 963704000000, 1401800],
# [1592890972918701800, "Buy", 963938000000, 2018600],
# ],
# "type": "snapshot"
# }
# perpetual
# {
# "sequence": 1230197759,
# "symbol": "BTCUSDT",
# "trades_p": [
# [
# 1677094244729433000,
# "Buy",
# "23800.4",
# "2.455",
# ],
# ],
# "type": "snapshot",
# }
#
name = 'trade'
marketId = self.safe_string(message, 'symbol')
market = self.safe_market(marketId)
symbol = market['symbol']
messageHash = name + ':' + symbol
stored = self.safe_value(self.trades, symbol)
if stored is None:
limit = self.safe_integer(self.options, 'tradesLimit', 1000)
stored = ArrayCache(limit)
self.trades[symbol] = stored
trades = self.safe_value_2(message, 'trades', 'trades_p', [])
parsed = self.parse_trades(trades, market)
for i in range(0, len(parsed)):
stored.append(parsed[i])
client.resolve(stored, messageHash)
def handle_ohlcv(self, client: Client, message):
#
# {
# "kline": [
# [1592905200, 60, 960688000000, 960709000000, 960709000000, 960400000000, 960400000000, 848100, 8146756046],
# [1592905140, 60, 960718000000, 960716000000, 960717000000, 960560000000, 960688000000, 4284900, 41163743512],
# [1592905080, 60, 960513000000, 960684000000, 960718000000, 960684000000, 960718000000, 4880500, 46887494349],
# ],
# "sequence": 1804401474,
# "symbol": "sBTCUSDT",
# "type": "snapshot"
# }
# perpetual
# {
# "kline_p": [
# [
# 1677094560,
# 60,
# "23746.2",
# "23746.1",
# "23757.6",
# "23736.9",
# "23754.8",
# "34.273",
# "813910.208",
# ],
# ],
# "sequence": 1230786017,
# "symbol": "BTCUSDT",
# "type": "incremental",
# }
#
marketId = self.safe_string(message, 'symbol')
market = self.safe_market(marketId)
symbol = market['symbol']
candles = self.safe_value_2(message, 'kline', 'kline_p', [])
first = self.safe_value(candles, 0, [])
interval = self.safe_string(first, 1)
timeframe = self.find_timeframe(interval)
if timeframe is not None:
messageHash = 'kline:' + timeframe + ':' + symbol
ohlcvs = self.parse_ohlcvs(candles, market)
self.ohlcvs[symbol] = self.safe_value(self.ohlcvs, symbol, {})
stored = self.safe_value(self.ohlcvs[symbol], timeframe)
if stored is None:
limit = self.safe_integer(self.options, 'OHLCVLimit', 1000)
stored = ArrayCacheByTimestamp(limit)
self.ohlcvs[symbol][timeframe] = stored
for i in range(0, len(ohlcvs)):
candle = ohlcvs[i]
stored.append(candle)
client.resolve(stored, messageHash)
async def watch_ticker(self, symbol: str, params={}) -> Ticker:
"""
https://github.com/phemex/phemex-api-docs/blob/master/Public-Hedged-Perpetual-API.md#subscribe-24-hours-ticker
https://github.com/phemex/phemex-api-docs/blob/master/Public-Contract-API-en.md#subscribe-24-hours-ticker
https://github.com/phemex/phemex-api-docs/blob/master/Public-Spot-API-en.md#subscribe-24-hours-ticker
watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
: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>`
"""
await self.load_markets()
market = self.market(symbol)
symbol = market['symbol']
isSwap = market['swap']
settleIsUSDT = market['settle'] == 'USDT'
name = 'spot_market24h'
if isSwap:
name = 'perp_market24h_pack_p' if settleIsUSDT else 'market24h'
url = self.urls['api']['ws']
requestId = self.request_id()
subscriptionHash = name + '.subscribe'
messageHash = 'ticker:' + symbol
subscribe: dict = {
'method': subscriptionHash,
'id': requestId,
'params': [],
}
request = self.deep_extend(subscribe, params)
return await self.watch(url, messageHash, request, subscriptionHash)
async def watch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
"""
https://github.com/phemex/phemex-api-docs/blob/master/Public-Hedged-Perpetual-API.md#subscribe-24-hours-ticker
https://github.com/phemex/phemex-api-docs/blob/master/Public-Contract-API-en.md#subscribe-24-hours-ticker
https://github.com/phemex/phemex-api-docs/blob/master/Public-Spot-API-en.md#subscribe-24-hours-ticker
watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
:param str[] [symbols]: unified symbol of the market to fetch the ticker for
:param dict [params]: extra parameters specific to the exchange API endpoint
:param str [params.channel]: the channel to subscribe to, tickers by default. Can be tickers, sprd-tickers, index-tickers, block-tickers
:returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
"""
await self.load_markets()
symbols = self.market_symbols(symbols, None, False)
first = symbols[0]
market = self.market(first)
isSwap = market['swap']
settleIsUSDT = market['settle'] == 'USDT'
name = 'spot_market24h'
if isSwap:
name = 'perp_market24h_pack_p' if settleIsUSDT else 'market24h'
url = self.urls['api']['ws']
requestId = self.request_id()
subscriptionHash = name + '.subscribe'
messageHashes = []
for i in range(0, len(symbols)):
messageHashes.append('ticker:' + symbols[i])
subscribe: dict = {
'method': subscriptionHash,
'id': requestId,
'params': [],
}
request = self.deep_extend(subscribe, params)
ticker = await self.watch_multiple(url, messageHashes, request, messageHashes)
if self.newUpdates:
result: dict = {}
result[ticker['symbol']] = ticker
return result
return self.filter_by_array(self.tickers, 'symbol', symbols)
async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
"""
https://github.com/phemex/phemex-api-docs/blob/master/Public-Hedged-Perpetual-API.md#subscribe-trade
https://github.com/phemex/phemex-api-docs/blob/master/Public-Contract-API-en.md#subscribe-trade
https://github.com/phemex/phemex-api-docs/blob/master/Public-Spot-API-en.md#subscribe-trade
get the list of most recent trades for a particular symbol
: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>`
"""
await self.load_markets()
market = self.market(symbol)
symbol = market['symbol']
url = self.urls['api']['ws']
requestId = self.request_id()
isSwap = market['swap']
settleIsUSDT = market['settle'] == 'USDT'
name = 'trade_p' if (isSwap and settleIsUSDT) else 'trade'
messageHash = 'trade:' + symbol
method = name + '.subscribe'
subscribe: dict = {
'method': method,
'id': requestId,
'params': [
market['id'],
],
}
request = self.deep_extend(subscribe, params)
trades = await self.watch(url, messageHash, request, messageHash)
if self.newUpdates:
limit = trades.getLimit(symbol, limit)
return self.filter_by_since_limit(trades, since, limit, 'timestamp', True)
async def watch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
"""
https://github.com/phemex/phemex-api-docs/blob/master/Public-Spot-API-en.md#subscribe-orderbook
https://github.com/phemex/phemex-api-docs/blob/master/Public-Hedged-Perpetual-API.md#subscribe-orderbook-for-new-model
https://github.com/phemex/phemex-api-docs/blob/master/Public-Contract-API-en.md#subscribe-30-levels-orderbook
https://github.com/phemex/phemex-api-docs/blob/master/Public-Contract-API-en.md#subscribe-full-orderbook
watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
: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
"""
await self.load_markets()
market = self.market(symbol)
symbol = market['symbol']
url = self.urls['api']['ws']
requestId = self.request_id()
isSwap = market['swap']
settleIsUSDT = market['settle'] == 'USDT'
name = 'orderbook_p' if (isSwap and settleIsUSDT) else 'orderbook'
messageHash = 'orderbook:' + symbol
method = name + '.subscribe'
subscribe: dict = {
'method': method,
'id': requestId,
'params': [
market['id'],
],
}
request = self.deep_extend(subscribe, params)
orderbook = await self.watch(url, messageHash, request, messageHash)
return orderbook.limit()
async def watch_ohlcv(self, symbol: str, timeframe: str = '1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
"""
https://github.com/phemex/phemex-api-docs/blob/master/Public-Hedged-Perpetual-API.md#subscribe-kline
https://github.com/phemex/phemex-api-docs/blob/master/Public-Contract-API-en.md#subscribe-kline
https://github.com/phemex/phemex-api-docs/blob/master/Public-Spot-API-en.md#subscribe-kline
watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
: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
"""
await self.load_markets()
market = self.market(symbol)
symbol = market['symbol']
url = self.urls['api']['ws']
requestId = self.request_id()
isSwap = market['swap']
settleIsUSDT = market['settle'] == 'USDT'
name = 'kline_p' if (isSwap and settleIsUSDT) else 'kline'
messageHash = 'kline:' + timeframe + ':' + symbol
method = name + '.subscribe'
subscribe: dict = {
'method': method,
'id': requestId,
'params': [
market['id'],
self.safe_integer(self.timeframes, timeframe),
],
}
request = self.deep_extend(subscribe, params)
ohlcv = await self.watch(url, messageHash, request, messageHash)
if self.newUpdates:
limit = ohlcv.getLimit(symbol, limit)
return self.filter_by_since_limit(ohlcv, since, limit, 0, True)
def custom_handle_delta(self, bookside, delta, market=None):
bidAsk = self.custom_parse_bid_ask(delta, 0, 1, market)
bookside.storeArray(bidAsk)
def custom_handle_deltas(self, bookside, deltas, market=None):
for i in range(0, len(deltas)):
self.custom_handle_delta(bookside, deltas[i], market)
def handle_order_book(self, client: Client, message):
#
# {
# "book": {
# "asks": [
# [960316000000, 6993800],
# [960318000000, 13183000],
# [960319000000, 9170200],
# ],
# "bids": [
# [959941000000, 8385300],
# [959939000000, 10296600],
# [959930000000, 3672400],
# ]
# },
# "depth": 30,
# "sequence": 1805784701,
# "symbol": "sBTCUSDT",
# "timestamp": 1592908460404461600,
# "type": "snapshot"
# }
# perpetual
# {
# "depth": 30,
# "orderbook_p": {
# "asks": [
# [
# "23788.5",
# "0.13",
# ],
# ],
# "bids": [
# [
# "23787.8",
# "1.836",
# ],
# ],
# },
# "sequence": 1230347368,
# "symbol": "BTCUSDT",
# "timestamp": "1677093457306978852",
# "type": "snapshot",
# }
#
marketId = self.safe_string(message, 'symbol')
market = self.safe_market(marketId)
symbol = market['symbol']
type = self.safe_string(message, 'type')
depth = self.safe_integer(message, 'depth')
name = 'orderbook'
messageHash = name + ':' + symbol
nonce = self.safe_integer(message, 'sequence')
timestamp = self.safe_integer_product(message, 'timestamp', 0.000001)
if type == 'snapshot':
book = self.safe_value_2(message, 'book', 'orderbook_p', {})
snapshot = self.custom_parse_order_book(book, symbol, timestamp, 'bids', 'asks', 0, 1, market)
snapshot['nonce'] = nonce
orderbook = self.order_book(snapshot, depth)
self.orderbooks[symbol] = orderbook
client.resolve(orderbook, messageHash)
else:
if symbol in self.orderbooks:
orderbook = self.orderbooks[symbol]
changes = self.safe_dict_2(message, 'book', 'orderbook_p', {})
asks = self.safe_list(changes, 'asks', [])
bids = self.safe_list(changes, 'bids', [])
self.custom_handle_deltas(orderbook['asks'], asks, market)
self.custom_handle_deltas(orderbook['bids'], bids, market)
orderbook['nonce'] = nonce
orderbook['timestamp'] = timestamp
orderbook['datetime'] = self.iso8601(timestamp)
self.orderbooks[symbol] = orderbook
client.resolve(orderbook, messageHash)
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
: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
:returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
"""
await self.load_markets()
market = None
type = None
messageHash = 'trades:'
if symbol is not None:
market = self.market(symbol)
symbol = market['symbol']
messageHash = messageHash + market['symbol']
if market['settle'] == 'USDT':
params = self.extend(params)
params['settle'] = 'USDT'
type, params = self.handle_market_type_and_params('watchMyTrades', market, params)
if symbol is None:
settle = self.safe_string(params, 'settle')
messageHash = (messageHash + 'perpetual') if (settle == 'USDT') else (messageHash + type)
trades = await self.subscribe_private(type, messageHash, params)
if self.newUpdates:
limit = trades.getLimit(symbol, limit)
return self.filter_by_symbol_since_limit(trades, symbol, since, limit, True)
def handle_my_trades(self, client: Client, message):
#
# swap
# [
# {
# "avgPriceEp":4138763000000,
# "baseCurrency":"BTC",
# "baseQtyEv":0,
# "clOrdID":"7956e0be-e8be-93a0-2887-ca504d85cda2",
# "execBaseQtyEv":30100,
# "execFeeEv":31,
# "execID":"d3b10cfa-84e3-5752-828e-78a79617e598",
# "execPriceEp":4138763000000,
# "execQuoteQtyEv":1245767663,
# "feeCurrency":"BTC",
# "lastLiquidityInd":"RemovedLiquidity",
# "ordType":"Market",
# "orderID":"34a4b1a8-ac3a-4580-b3e6-a6d039f27195",
# "priceEp":4549022000000,
# "qtyType":"ByQuote",
# "quoteCurrency":"USDT",
# "quoteQtyEv":1248000000,
# "side":"Buy",
# "symbol":"sBTCUSDT",
# "tradeType":"Trade",
# "transactTimeNs":"1650442617609928764",
# "userID":2647224
# }
# ]
# perpetual
# [
# {
# "accountID": 9328670003,
# "action": "New",
# "actionBy": "ByUser",
# "actionTimeNs": 1666858780876924611,
# "addedSeq": 77751555,
# "apRp": "0",
# "bonusChangedAmountRv": "0",
# "bpRp": "0",
# "clOrdID": "c0327a7d-9064-62a9-28f6-2db9aaaa04e0",
# "closedPnlRv": "0",
# "closedSize": "0",
# "code": 0,
# "cumFeeRv": "0",
# "cumQty": "0",
# "cumValueRv": "0",
# "curAccBalanceRv": "1508.489893982237",
# "curAssignedPosBalanceRv": "24.62786650928",
# "curBonusBalanceRv": "0",
# "curLeverageRr": "-10",
# "curPosSide": "Buy",
# "curPosSize": "0.043",
# "curPosTerm": 1,
# "curPosValueRv": "894.0689",
# "curRiskLimitRv": "1000000",
# "currency": "USDT",
# "cxlRejReason": 0,
# "displayQty": "0.003",
# "execFeeRv": "0",
# "execID": "00000000-0000-0000-0000-000000000000",
# "execPriceRp": "20723.7",
# "execQty": "0",
# "execSeq": 77751555,
# "execStatus": "New",
# "execValueRv": "0",
# "feeRateRr": "0",
# "leavesQty": "0.003",
# "leavesValueRv": "63.4503",
# "message": "No error",
# "ordStatus": "New",
# "ordType": "Market",
# "orderID": "fa64c6f2-47a4-4929-aab4-b7fa9bbc4323",
# "orderQty": "0.003",
# "pegOffsetValueRp": "0",
# "posSide": "Long",
# "priceRp": "21150.1",
# "relatedPosTerm": 1,
# "relatedReqNum": 11,
# "side": "Buy",
# "slTrigger": "ByMarkPrice",
# "stopLossRp": "0",
# "stopPxRp": "0",
# "symbol": "BTCUSDT",
# "takeProfitRp": "0",
# "timeInForce": "ImmediateOrCancel",
# "tpTrigger": "ByLastPrice",
# "tradeType": "Amend",
# "transactTimeNs": 1666858780881545305,
# "userID": 932867
# },
# ...
# ]
#
channel = 'trades'
tradesLength = len(message)
if tradesLength == 0:
return
cachedTrades = self.myTrades
if cachedTrades is None:
limit = self.safe_integer(self.options, 'tradesLimit', 1000)
cachedTrades = ArrayCacheBySymbolById(limit)
marketIds: dict = {}
type = None
for i in range(0, len(message)):
rawTrade = message[i]
marketId = self.safe_string(rawTrade, 'symbol')
market = self.safe_market(marketId)
parsed = self.parse_trade(rawTrade)
cachedTrades.append(parsed)
symbol = parsed['symbol']
if type is None:
type = 'perpetual' if (market['settle'] == 'USDT') else market['type']
marketIds[symbol] = True
keys = list(marketIds.keys())
for i in range(0, len(keys)):
market = keys[i]
hash = channel + ':' + market
client.resolve(cachedTrades, hash)
# generic subscription
messageHash = channel + ':' + type
client.resolve(cachedTrades, messageHash)
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
: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
:returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
"""
await self.load_markets()
messageHash = 'orders:'
market = None
type = None
if symbol is not None:
market = self.market(symbol)
symbol = market['symbol']
messageHash = messageHash + market['symbol']
if market['settle'] == 'USDT':
params = self.extend(params)
params['settle'] = 'USDT'
type, params = self.handle_market_type_and_params('watchOrders', market, params)
isUSDTSettled = self.safe_string(params, 'settle') == 'USDT'
if symbol is None:
messageHash = (messageHash + 'perpetual') if (isUSDTSettled) else (messageHash + type)
orders = await self.subscribe_private(type, messageHash, params)
if self.newUpdates:
limit = orders.getLimit(symbol, limit)
return self.filter_by_symbol_since_limit(orders, symbol, since, limit, True)
def handle_orders(self, client: Client, message):
# spot update
# {
# "closed":[
# {
# "action":"New",
# "avgPriceEp":4138763000000,
# "baseCurrency":"BTC",
# "baseQtyEv":0,
# "bizError":0,
# "clOrdID":"7956e0be-e8be-93a0-2887-ca504d85cda2",
# "createTimeNs":"1650442617606017583",
# "cumBaseQtyEv":30100,
# "cumFeeEv":31,
# "cumQuoteQtyEv":1245767663,
# "cxlRejReason":0,
# "feeCurrency":"BTC",
# "leavesBaseQtyEv":0,
# "leavesQuoteQtyEv":0,
# "ordStatus":"Filled",
# "ordType":"Market",
# "orderID":"34a4b1a8-ac3a-4580-b3e6-a6d039f27195",
# "pegOffsetValueEp":0,
# "priceEp":4549022000000,
# "qtyType":"ByQuote",
# "quoteCurrency":"USDT",
# "quoteQtyEv":1248000000,
# "side":"Buy",
# "stopPxEp":0,
# "symbol":"sBTCUSDT",
# "timeInForce":"ImmediateOrCancel",
# "tradeType":"Trade",
# "transactTimeNs":"1650442617609928764",
# "triggerTimeNs":0,
# "userID":2647224
# }
# ],
# "fills":[
# {
# "avgPriceEp":4138763000000,
# "baseCurrency":"BTC",
# "baseQtyEv":0,
# "clOrdID":"7956e0be-e8be-93a0-2887-ca504d85cda2",
# "execBaseQtyEv":30100,
# "execFeeEv":31,
# "execID":"d3b10cfa-84e3-5752-828e-78a79617e598",
# "execPriceEp":4138763000000,
# "execQuoteQtyEv":1245767663,
# "feeCurrency":"BTC",
# "lastLiquidityInd":"RemovedLiquidity",
# "ordType":"Market",
# "orderID":"34a4b1a8-ac3a-4580-b3e6-a6d039f27195",
# "priceEp":4549022000000,
# "qtyType":"ByQuote",
# "quoteCurrency":"USDT",
# "quoteQtyEv":1248000000,
# "side":"Buy",
# "symbol":"sBTCUSDT",
# "tradeType":"Trade",
# "transactTimeNs":"1650442617609928764",
# "userID":2647224
# }
# ],
# "open":[
# {
# "action":"New",
# "avgPriceEp":0,
# "baseCurrency":"LTC",
# "baseQtyEv":0,
# "bizError":0,
# "clOrdID":"2c0e5eb5-efb7-60d3-2e5f-df175df412ef",
# "createTimeNs":"1650446670073853755",
# "cumBaseQtyEv":0,
# "cumFeeEv":0,
# "cumQuoteQtyEv":0,
# "cxlRejReason":0,
# "feeCurrency":"LTC",
# "leavesBaseQtyEv":0,
# "leavesQuoteQtyEv":1000000000,
# "ordStatus":"New",
# "ordType":"Limit",
# "orderID":"d2aad92f-50f5-441a-957b-8184b146e3fb",
# "pegOffsetValueEp":0,
# "priceEp":5000000000,
# "qtyType":"ByQuote",
# "quoteCurrency":"USDT",
# "quoteQtyEv":1000000000,
# "side":"Buy",
# }
# ]
# },
# perpetual
# [
# {
# "accountID": 40183400003,
# "action": "New",
# "actionBy": "ByUser",
# "actionTimeNs": "1674110665380190869",
# "addedSeq": 678760103,
# "apRp": "0",
# "bonusChangedAmountRv": "0",
# "bpRp": "0",
# "clOrdID": '',
# "cl_req_code": 0,
# "closedPnlRv": "0",
# "closedSize": "0",
# "code": 0,
# "cumFeeRv": "0",
# "cumQty": "0.001",
# "cumValueRv": "20.849",
# "curAccBalanceRv": "19.9874906",
# "curAssignedPosBalanceRv": "0",
# "curBonusBalanceRv": "0",
# "curLeverageRr": "-10",
# "curPosSide": "Buy",
# "curPosSize": "0.001",
# "curPosTerm": 1,
# "curPosValueRv": "20.849",
# "curRiskLimitRv": "1000000",
# "currency": "USDT",
# "cxlRejReason": 0,
# "displayQty": "0.001",
# "execFeeRv": "0.0125094",
# "execID": "b88d2950-04a2-52d8-8927-346059900242",
# "execPriceRp": "20849",
# "execQty": "0.001",
# "execSeq": 678760103,
# "execStatus": "TakerFill",
# "execValueRv": "20.849",
# "feeRateRr": "0.0006",
# "lastLiquidityInd": "RemovedLiquidity",
# "leavesQty": "0",
# "leavesValueRv": "0",
# "message": "No error",
# "ordStatus": "Filled",
# "ordType": "Market",
# "orderID": "79620ed2-54c6-4645-a35c-7057e687c576",
# "orderQty": "0.001",
# "pegOffsetProportionRr": "0",
# "pegOffsetValueRp": "0",
# "posSide": "Long",
# "priceRp": "21476.3",
# "relatedPosTerm": 1,
# "relatedReqNum": 4,
# "side": "Buy",
# "slTrigger": "ByMarkPrice",
# "stopLossRp": "0",
# "stopPxRp": "0",
# "symbol": "BTCUSDT",
# "takeProfitRp": "0",
# "timeInForce": "ImmediateOrCancel",
# "tpTrigger": "ByLastPrice",
# "tradeType": "Trade",
# "transactTimeNs": "1674110665387882268",
# "userID": 4018340
# },
# ...
# ]
#
trades = []
parsedOrders = []
if ('closed' in message) or ('fills' in message) or ('open' in message):
closed = self.safe_value(message, 'closed', [])
open = self.safe_value(message, 'open', [])
orders = self.array_concat(open, closed)
ordersLength = len(orders)
if ordersLength == 0:
return
trades = self.safe_value(message, 'fills', [])
for i in range(0, len(orders)):
rawOrder = orders[i]
parsedOrder = self.parse_order(rawOrder)
parsedOrders.append(parsedOrder)
else:
messageLength = len(message)
if messageLength == 0:
return
for i in range(0, len(message)):
update = message[i]
action = self.safe_string(update, 'action')
if (action is not None) and (action != 'Cancel'):
# order + trade info together
trades.append(update)
parsedOrder = self.parse_ws_swap_order(update)
parsedOrders.append(parsedOrder)
self.handle_my_trades(client, trades)
limit = self.safe_integer(self.options, 'ordersLimit', 1000)
marketIds: dict = {}
if self.orders is None:
self.orders = ArrayCacheBySymbolById(limit)
type = None
stored = self.orders
for i in range(0, len(parsedOrders)):
parsed = parsedOrders[i]
stored.append(parsed)
symbol = parsed['symbol']
market = self.market(symbol)
if type is None:
isUsdt = market['settle'] == 'USDT'
type = 'perpetual' if isUsdt else market['type']
marketIds[symbol] = True
keys = list(marketIds.keys())
for i in range(0, len(keys)):
currentMessageHash = 'orders' + ':' + keys[i]
client.resolve(self.orders, currentMessageHash)
# resolve generic subscription(spot or swap)
messageHash = 'orders:' + type
client.resolve(self.orders, messageHash)
def parse_ws_swap_order(self, order, market=None):
#
# swap
# {
# "accountID":26472240002,
# "action":"Cancel",
# "actionBy":"ByUser",
# "actionTimeNs":"1650450096104760797",
# "addedSeq":26975849309,
# "bonusChangedAmountEv":0,
# "clOrdID":"d9675963-5e4e-6fc8-898a-ec8b934c1c61",
# "closedPnlEv":0,
# "closedSize":0,
# "code":0,
# "cumQty":0,
# "cumValueEv":0,
# "curAccBalanceEv":400079,
# "curAssignedPosBalanceEv":0,
# "curBonusBalanceEv":0,
# "curLeverageEr":0,
# "curPosSide":"None",
# "curPosSize":0,
# "curPosTerm":1,
# "curPosValueEv":0,
# "curRiskLimitEv":5000000000,
# "currency":"USD",
# "cxlRejReason":0,
# "displayQty":0,
# "execFeeEv":0,
# "execID":"00000000-0000-0000-0000-000000000000",
# "execPriceEp":0,
# "execQty":1,
# "execSeq":26975862338,
# "execStatus":"Canceled",
# "execValueEv":0,
# "feeRateEr":0,
# "leavesQty":0,
# "leavesValueEv":0,
# "message":"No error",
# "ordStatus":"Canceled",
# "ordType":"Limit",
# "orderID":"8141deb9-8f94-48f6-9421-a4e3a791537b",
# "orderQty":1,
# "pegOffsetValueEp":0,
# "priceEp":9521,
# "relatedPosTerm":1,
# "relatedReqNum":4,
# "side":"Buy",
# "slTrigger":"ByMarkPrice",
# "stopLossEp":0,
# "stopPxEp":0,
# "symbol":"ADAUSD",
# "takeProfitEp":0,
# "timeInForce":"GoodTillCancel",
# "tpTrigger":"ByLastPrice",
# "transactTimeNs":"1650450096108143014",
# "userID":2647224
# }
# perpetual
# {
# "accountID": 40183400003,
# "action": "New",
# "actionBy": "ByUser",
# "actionTimeNs": "1674110665380190869",
# "addedSeq": 678760103,
# "apRp": "0",
# "bonusChangedAmountRv": "0",
# "bpRp": "0",
# "clOrdID": '',
# "cl_req_code": 0,
# "closedPnlRv": "0",
# "closedSize": "0",
# "code": 0,
# "cumFeeRv": "0",
# "cumQty": "0.001",
# "cumValueRv": "20.849",
# "curAccBalanceRv": "19.9874906",
# "curAssignedPosBalanceRv": "0",
# "curBonusBalanceRv": "0",
# "curLeverageRr": "-10",
# "curPosSide": "Buy",
# "curPosSize": "0.001",
# "curPosTerm": 1,
# "curPosValueRv": "20.849",
# "curRiskLimitRv": "1000000",
# "currency": "USDT",
# "cxlRejReason": 0,
# "displayQty": "0.001",
# "execFeeRv": "0.0125094",
# "execID": "b88d2950-04a2-52d8-8927-346059900242",
# "execPriceRp": "20849",
# "execQty": "0.001",
# "execSeq": 678760103,
# "execStatus": "TakerFill",
# "execValueRv": "20.849",
# "feeRateRr": "0.0006",
# "lastLiquidityInd": "RemovedLiquidity",
# "leavesQty": "0",
# "leavesValueRv": "0",
# "message": "No error",
# "ordStatus": "Filled",
# "ordType": "Market",
# "orderID": "79620ed2-54c6-4645-a35c-7057e687c576",
# "orderQty": "0.001",
# "pegOffsetProportionRr": "0",
# "pegOffsetValueRp": "0",
# "posSide": "Long",
# "priceRp": "21476.3",
# "relatedPosTerm": 1,
# "relatedReqNum": 4,
# "side": "Buy",
# "slTrigger": "ByMarkPrice",
# "stopLossRp": "0",
# "stopPxRp": "0",
# "symbol": "BTCUSDT",
# "takeProfitRp": "0",
# "timeInForce": "ImmediateOrCancel",
# "tpTrigger": "ByLastPrice",
# "tradeType": "Trade",
# "transactTimeNs": "1674110665387882268",
# "userID": 4018340
# }
#
id = self.safe_string(order, 'orderID')
clientOrderId = self.safe_string(order, 'clOrdID')
if (clientOrderId is not None) and (len(clientOrderId) < 1):
clientOrderId = None
marketId = self.safe_string(order, 'symbol')
market = self.safe_market(marketId, market)
symbol = market['symbol']
status = self.parse_order_status(self.safe_string(order, 'ordStatus'))
side = self.safe_string_lower(order, 'side')
type = self.parseOrderType(self.safe_string(order, 'ordType'))
price = self.safe_string(order, 'priceRp', self.from_ep(self.safe_string(order, 'priceEp'), market))
amount = self.safe_string(order, 'orderQty')
filled = self.safe_string(order, 'cumQty')
remaining = self.safe_string(order, 'leavesQty')
timestamp = self.safe_integer_product(order, 'actionTimeNs', 0.000001)
cost = self.safe_string(order, 'cumValueRv', self.from_ev(self.safe_string(order, 'cumValueEv'), market))
lastTradeTimestamp = self.safe_integer_product(order, 'transactTimeNs', 0.000001)
if lastTradeTimestamp == 0:
lastTradeTimestamp = None
timeInForce = self.parse_time_in_force(self.safe_string(order, 'timeInForce'))
stopPrice = self.safe_string(order, 'stopPx')
postOnly = (timeInForce == 'PO')
return self.safe_order({
'info': order,
'id': id,
'clientOrderId': clientOrderId,
'datetime': self.iso8601(timestamp),
'timestamp': timestamp,
'lastTradeTimestamp': lastTradeTimestamp,
'symbol': symbol,
'type': type,
'timeInForce': timeInForce,
'postOnly': postOnly,
'side': side,
'price': price,
'stopPrice': stopPrice,
'triggerPrice': stopPrice,
'amount': amount,
'filled': filled,
'remaining': remaining,
'cost': cost,
'average': None,
'status': status,
'fee': None,
'trades': None,
}, market)
def handle_message(self, client: Client, message):
# private spot update
# {
# "orders": {closed: [], fills: [], open: []},
# "sequence": 40435835,
# "timestamp": "1650443245600839241",
# "type": "snapshot",
# "wallets": [
# {
# "balanceEv": 0,
# "currency": "BTC",
# "lastUpdateTimeNs": "1650442638722099092",
# "lockedTradingBalanceEv": 0,
# "lockedWithdrawEv": 0,
# "userID": 2647224
# },
# {
# "balanceEv": 1154232337,
# "currency": "USDT",
# "lastUpdateTimeNs": "1650442617610017597",
# "lockedTradingBalanceEv": 0,
# "lockedWithdrawEv": 0,
# "userID": 2647224
# }
# ]
# }
# private swap update
# {
# "sequence": 83839628,
# "timestamp": "1650382581827447829",
# "type": "snapshot",
# "accounts": [
# {
# "accountBalanceEv": 0,
# "accountID": 26472240001,
# "bonusBalanceEv": 0,
# "currency": "BTC",
# "totalUsedBalanceEv": 0,
# "userID": 2647224
# }
# ],
# "orders": [],
# "positions": [
# {
# "accountID": 26472240001,
# "assignedPosBalanceEv": 0,
# "avgEntryPriceEp": 0,
# "bankruptCommEv": 0,
# "bankruptPriceEp": 0,
# "buyLeavesQty": 0,
# "buyLeavesValueEv": 0,
# "buyValueToCostEr": 1150750,
# "createdAtNs": 0,
# "crossSharedBalanceEv": 0,
# "cumClosedPnlEv": 0,
# "cumFundingFeeEv": 0,
# "cumTransactFeeEv": 0,
# "curTermRealisedPnlEv": 0,
# "currency": "BTC",
# "dataVer": 2,
# "deleveragePercentileEr": 0,
# "displayLeverageEr": 10000000000,
# "estimatedOrdLossEv": 0,
# "execSeq": 0,
# "freeCostEv": 0,
# "freeQty": 0,
# "initMarginReqEr": 1000000,
# "lastFundingTime": "1640601827712091793",
# "lastTermEndTime": 0,
# "leverageEr": 0,
# "liquidationPriceEp": 0,
# "maintMarginReqEr": 500000,
# "makerFeeRateEr": 0,
# "markPriceEp": 507806777,
# "orderCostEv": 0,
# "posCostEv": 0,
# "positionMarginEv": 0,
# "positionStatus": "Normal",
# "riskLimitEv": 10000000000,
# "sellLeavesQty": 0,
# "sellLeavesValueEv": 0,
# "sellValueToCostEr": 1149250,
# "side": "None",
# "size": 0,
# "symbol": "BTCUSD",
# "takerFeeRateEr": 0,
# "term": 1,
# "transactTimeNs": 0,
# "unrealisedPnlEv": 0,
# "updatedAtNs": 0,
# "usedBalanceEv": 0,
# "userID": 2647224,
# "valueEv": 0
# }
# ]
# }
id = self.safe_string(message, 'id')
if id in client.subscriptions:
method = client.subscriptions[id]
del client.subscriptions[id]
if method is not True:
method(client, message)
return
methodName = self.safe_string(message, 'method', '')
if ('market24h' in message) or ('spot_market24h' in message) or (methodName.find('perp_market24h_pack_p') >= 0):
self.handle_ticker(client, message)
return
elif ('trades' in message) or ('trades_p' in message):
self.handle_trades(client, message)
return
elif ('kline' in message) or ('kline_p' in message):
self.handle_ohlcv(client, message)
return
elif ('book' in message) or ('orderbook_p' in message):
self.handle_order_book(client, message)
return
if ('orders' in message) or ('orders_p' in message):
orders = self.safe_value_2(message, 'orders', 'orders_p', {})
self.handle_orders(client, orders)
if ('accounts' in message) or ('accounts_p' in message) or ('wallets' in message):
type = 'swap' if ('accounts' in message) else 'spot'
if 'accounts_p' in message:
type = 'perpetual'
accounts = self.safe_value_n(message, ['accounts', 'accounts_p', 'wallets'], [])
self.handle_balance(type, client, accounts)
def handle_authenticate(self, client: Client, message):
#
# {
# "error": null,
# "id": 1234,
# "result": {
# "status": "success"
# }
# }
#
result = self.safe_value(message, 'result')
status = self.safe_string(result, 'status')
messageHash = 'authenticated'
if status == 'success':
client.resolve(message, messageHash)
else:
error = AuthenticationError(self.id + ' ' + self.json(message))
client.reject(error, messageHash)
if messageHash in client.subscriptions:
del client.subscriptions[messageHash]
async def subscribe_private(self, type, messageHash, params={}):
await self.load_markets()
await self.authenticate()
url = self.urls['api']['ws']
requestId = self.seconds()
settleIsUSDT = (self.safe_value(params, 'settle', '') == 'USDT')
params = self.omit(params, 'settle')
channel = 'aop.subscribe'
if type == 'spot':
channel = 'wo.subscribe'
if settleIsUSDT:
channel = 'aop_p.subscribe'
request = {
'id': requestId,
'method': channel,
'params': [],
}
request = self.extend(request, params)
return await self.watch(url, messageHash, request, channel)
async def authenticate(self, params={}):
self.check_required_credentials()
url = self.urls['api']['ws']
client = self.client(url)
requestId = self.request_id()
messageHash = 'authenticated'
future = self.safe_value(client.subscriptions, messageHash)
if future is None:
expiryDelta = self.safe_integer(self.options, 'expires', 120)
expiration = self.seconds() + expiryDelta
payload = self.apiKey + str(expiration)
signature = self.hmac(self.encode(payload), self.encode(self.secret), hashlib.sha256)
method = 'user.auth'
request: dict = {
'method': method,
'params': ['API', self.apiKey, signature, expiration],
'id': requestId,
}
subscriptionHash = str(requestId)
message = self.extend(request, params)
if not (messageHash in client.subscriptions):
client.subscriptions[subscriptionHash] = self.handle_authenticate
future = await self.watch(url, messageHash, message, messageHash)
client.subscriptions[messageHash] = future
return future