# -*- 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
from ccxt.base.types import 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 ExchangeError
from ccxt.base.errors import AuthenticationError
from ccxt.base.errors import BadRequest
from ccxt.base.errors import NotSupported
from ccxt.base.errors import ExchangeNotAvailable
from ccxt.base.errors import RequestTimeout
from ccxt.base.precise import Precise


class coinex(ccxt.async_support.coinex):

    def describe(self):
        return self.deep_extend(super(coinex, self).describe(), {
            'has': {
                'ws': True,
                'watchBalance': True,
                'watchTicker': True,
                'watchTickers': True,
                'watchTrades': True,
                'watchMyTrades': False,  # can query but can't subscribe
                'watchOrders': True,
                'watchOrderBook': True,
                'watchOHLCV': True,  # only for swap markets
                'fetchOHLCVWs': True,
            },
            'urls': {
                'api': {
                    'ws': {
                        'spot': 'wss://socket.coinex.com/',
                        'swap': 'wss://perpetual.coinex.com/',
                    },
                },
            },
            'options': {
                'watchOHLCVWarning': True,
                'timeframes': {
                    '1m': 60,
                    '3m': 180,
                    '5m': 300,
                    '15m': 900,
                    '30m': 1800,
                    '1h': 3600,
                    '2h': 7200,
                    '4h': 14400,
                    '6h': 21600,
                    '12h': 43200,
                    '1d': 86400,
                    '3d': 259200,
                    '1w': 604800,
                },
                'account': 'spot',
                'watchOrderBook': {
                    'limits': [5, 10, 20, 50],
                    'defaultLimit': 50,
                    'aggregations': ['10', '1', '0', '0.1', '0.01'],
                    'defaultAggregation': '0',
                },
            },
            'streaming': {
            },
            'exceptions': {
                'codes': {
                    '1': BadRequest,  # Parameter error
                    '2': ExchangeError,  # Internal error
                    '3': ExchangeNotAvailable,  # Service unavailable
                    '4': NotSupported,  # Method unavailable
                    '5': RequestTimeout,  # Service timeout
                    '6': AuthenticationError,  # Permission denied
                },
            },
        })

    def request_id(self):
        requestId = self.sum(self.safe_integer(self.options, 'requestId', 0), 1)
        self.options['requestId'] = requestId
        return requestId

    def handle_ticker(self, client: Client, message):
        #
        #  spot
        #
        #     {
        #         "method": "state.update",
        #         "params": [{
        #             "BTCUSDT": {
        #                 "last": "31577.89",
        #                 "open": "29318.36",
        #                 "close": "31577.89",
        #                 "high": "32222.19",
        #                 "low": "29317.21",
        #                 "volume": "630.43024965",
        #                 "sell_total": "13.66143951",
        #                 "buy_total": "2.76410939",
        #                 "period": 86400,
        #                 "deal": "19457487.84611409070000000000"
        #             }
        #         }]
        #     }
        #
        #  swap
        #
        #     {
        #         "method": "state.update",
        #         "params": [{
        #             "BTCUSDT": {
        #                 "period": 86400,
        #                 "funding_time": 422,
        #                 "position_amount": "285.6246",
        #                 "funding_rate_last": "-0.00097933",
        #                 "funding_rate_next": "0.00022519",
        #                 "funding_rate_predict": "0.00075190",
        #                 "insurance": "17474289.49925859030905338270",
        #                 "last": "31570.08",
        #                 "sign_price": "31568.09",
        #                 "index_price": "31561.85000000",
        #                 "open": "29296.11",
        #                 "close": "31570.08",
        #                 "high": "32463.40",
        #                 "low": "29296.11",
        #                 "volume": "8774.7318",
        #                 "deal": "270675177.827928219109030017258398",
        #                 "sell_total": "19.2230",
        #                 "buy_total": "25.7814"
        #             }
        #         }]
        #     }
        #
        defaultType = self.safe_string(self.options, 'defaultType')
        params = self.safe_value(message, 'params', [])
        rawTickers = self.safe_value(params, 0, {})
        keys = list(rawTickers.keys())
        newTickers = []
        for i in range(0, len(keys)):
            marketId = keys[i]
            rawTicker = rawTickers[marketId]
            symbol = self.safe_symbol(marketId, None, None, defaultType)
            market = self.safe_market(marketId, None, None, defaultType)
            parsedTicker = self.parse_ws_ticker(rawTicker, market)
            self.tickers[symbol] = parsedTicker
            newTickers.append(parsedTicker)
        messageHashes = self.find_message_hashes(client, 'tickers::')
        for i in range(0, len(messageHashes)):
            messageHash = messageHashes[i]
            parts = messageHash.split('::')
            symbolsString = parts[1]
            symbols = symbolsString.split(',')
            tickers = self.filter_by_array(newTickers, 'symbol', symbols)
            tickersSymbols = list(tickers.keys())
            numTickers = len(tickersSymbols)
            if numTickers > 0:
                client.resolve(tickers, messageHash)
        client.resolve(newTickers, 'tickers')

    def parse_ws_ticker(self, ticker, market=None):
        #
        #  spot
        #
        #     {
        #         "last": "31577.89",
        #         "open": "29318.36",
        #         "close": "31577.89",
        #         "high": "32222.19",
        #         "low": "29317.21",
        #         "volume": "630.43024965",
        #         "sell_total": "13.66143951",
        #         "buy_total": "2.76410939",
        #         "period": 86400,
        #         "deal": "19457487.84611409070000000000"
        #     }
        #
        #  swap
        #
        #     {
        #         "period": 86400,
        #         "funding_time": 422,
        #         "position_amount": "285.6246",
        #         "funding_rate_last": "-0.00097933",
        #         "funding_rate_next": "0.00022519",
        #         "funding_rate_predict": "0.00075190",
        #         "insurance": "17474289.49925859030905338270",
        #         "last": "31570.08",
        #         "sign_price": "31568.09",
        #         "index_price": "31561.85000000",
        #         "open": "29296.11",
        #         "close": "31570.08",
        #         "high": "32463.40",
        #         "low": "29296.11",
        #         "volume": "8774.7318",
        #         "deal": "270675177.827928219109030017258398",
        #         "sell_total": "19.2230",
        #         "buy_total": "25.7814"
        #     }
        #
        defaultType = self.safe_string(self.options, 'defaultType')
        return self.safe_ticker({
            'symbol': self.safe_symbol(None, market, None, defaultType),
            'timestamp': None,
            'datetime': None,
            'high': self.safe_string(ticker, 'high'),
            'low': self.safe_string(ticker, 'low'),
            'bid': None,
            'bidVolume': self.safe_string(ticker, 'buy_total'),
            'ask': None,
            'askVolume': self.safe_string(ticker, 'sell_total'),
            'vwap': None,
            'open': self.safe_string(ticker, 'open'),
            'close': self.safe_string(ticker, 'close'),
            'last': self.safe_string(ticker, 'last'),
            'previousClose': None,
            'change': None,
            'percentage': None,
            'average': None,
            'baseVolume': self.safe_string(ticker, 'volume'),
            'quoteVolume': self.safe_string(ticker, 'deal'),
            'info': ticker,
        }, market)

    async def watch_balance(self, params={}) -> Balances:
        """
        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
        :returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
        """
        await self.load_markets()
        await self.authenticate(params)
        messageHash = 'balance'
        type = None
        type, params = self.handle_market_type_and_params('watchBalance', None, params)
        url = self.urls['api']['ws'][type]
        currencies = list(self.currencies_by_id.keys())
        subscribe = {
            'method': 'asset.subscribe',
            'params': currencies,
            'id': self.request_id(),
        }
        request = self.deep_extend(subscribe, params)
        return await self.watch(url, messageHash, request, messageHash)

    def handle_balance(self, client: Client, message):
        #
        #     {
        #         "method": "asset.update",
        #         "params": [
        #             {
        #                 "BTC": {
        #                     "available": "250",
        #                     "frozen": "10",
        #                 }
        #             }
        #         ],
        #         "id": null
        #     }
        #
        params = self.safe_value(message, 'params', [])
        first = self.safe_value(params, 0, {})
        self.balance['info'] = first
        currencies = list(first.keys())
        for i in range(0, len(currencies)):
            currencyId = currencies[i]
            code = self.safe_currency_code(currencyId)
            available = self.safe_string(first[currencyId], 'available')
            frozen = self.safe_string(first[currencyId], 'frozen')
            account = self.account()
            account['free'] = available
            account['used'] = frozen
            self.balance[code] = account
            self.balance = self.safe_balance(self.balance)
        messageHash = 'balance'
        client.resolve(self.balance, messageHash)

    def handle_trades(self, client: Client, message):
        #
        #     {
        #         "method": "deals.update",
        #         "params": [
        #             "BTCUSD",
        #             [{
        #                 "type": "sell",
        #                 "time": 1496458040.059284,
        #                 "price ": "46444.74",
        #                 "id": 29433,
        #                 "amount": "0.00120000"
        #             }]
        #         ],
        #         "id": null
        #     }
        #
        params = self.safe_value(message, 'params', [])
        marketId = self.safe_string(params, 0)
        trades = self.safe_value(params, 1, [])
        defaultType = self.safe_string(self.options, 'defaultType')
        market = self.safe_market(marketId, None, None, defaultType)
        symbol = market['symbol']
        messageHash = 'trades:' + 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
        for i in range(0, len(trades)):
            trade = trades[i]
            parsed = self.parse_ws_trade(trade, market)
            stored.append(parsed)
        self.trades[symbol] = stored
        client.resolve(self.trades[symbol], messageHash)

    def parse_ws_trade(self, trade, market=None):
        #
        #     {
        #         "type": "sell",
        #         "time": 1496458040.059284,
        #         "price ": "46444.74",
        #         "id": 29433,
        #         "amount": "0.00120000"
        #     }
        #
        timestamp = self.safe_timestamp(trade, 'time')
        defaultType = self.safe_string(self.options, 'defaultType')
        return self.safe_trade({
            'id': self.safe_string(trade, 'id'),
            'info': trade,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': self.safe_symbol(None, market, None, defaultType),
            'order': None,
            'type': None,
            'side': self.safe_string(trade, 'type'),
            'takerOrMaker': None,
            'price': self.safe_string(trade, 'price'),
            'amount': self.safe_string(trade, 'amount'),
            'cost': None,
            'fee': None,
        }, market)

    def handle_ohlcv(self, client: Client, message):
        #
        #  spot
        #     {
        #         "error": null,
        #         "result": [
        #           [
        #             1673846940,
        #             "21148.74",
        #             "21148.38",
        #             "21148.75",
        #             "21138.66",
        #             "1.57060173",
        #             "33214.9138778914"
        #           ],
        #         ]
        #         "id": 1,
        #     }
        #  swap
        #     {
        #         "method": "kline.update",
        #         "params": [
        #             [
        #                 1654019640,   # timestamp
        #                 "32061.99",   # open
        #                 "32061.28",   # close
        #                 "32061.99",   # high
        #                 "32061.28",   # low
        #                 "0.1285",     # amount base
        #                 "4119.943736"  # amount quote
        #             ]
        #         ],
        #         "id": null
        #     }
        #
        candles = self.safe_value_2(message, 'params', 'result', [])
        messageHash = 'ohlcv'
        id = self.safe_string(message, 'id')
        ohlcvs = self.parse_ohlcvs(candles)
        if id is not None:
            # spot subscription response
            client.resolve(ohlcvs, messageHash)
            return
        keys = list(self.ohlcvs.keys())
        keysLength = len(keys)
        if keysLength == 0:
            self.ohlcvs['unknown'] = {}
            limit = self.safe_integer(self.options, 'OHLCVLimit', 1000)
            stored = ArrayCacheByTimestamp(limit)
            self.ohlcvs['unknown']['unknown'] = stored
        ohlcv = self.ohlcvs['unknown']['unknown']
        for i in range(0, len(ohlcvs)):
            candle = ohlcvs[i]
            ohlcv.append(candle)
        client.resolve(ohlcv, messageHash)

    async def watch_ticker(self, symbol: str, params={}) -> Ticker:
        """
        :see: https://viabtc.github.io/coinex_api_en_doc/spot/#docsspot004_websocket007_state_subscribe
        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>`
        """
        tickers = await self.watch_tickers([symbol], params)
        return self.safe_value(tickers, symbol)

    async def watch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
        """
        :see: https://viabtc.github.io/coinex_api_en_doc/spot/#docsspot004_websocket007_state_subscribe
        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
        :returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
        """
        await self.load_markets()
        symbols = self.market_symbols(symbols)
        type = None
        type, params = self.handle_market_type_and_params('watchTickers', None, params)
        url = self.urls['api']['ws'][type]
        messageHash = 'tickers'
        if symbols is not None:
            messageHash = 'tickers::' + ','.join(symbols)
        subscribe = {
            'method': 'state.subscribe',
            'id': self.request_id(),
            'params': [],
        }
        request = self.deep_extend(subscribe, params)
        newTickers = await self.watch(url, messageHash, request, messageHash)
        if self.newUpdates:
            return newTickers
        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]:
        """
        :see: https://viabtc.github.io/coinex_api_en_doc/spot/#docsspot004_websocket012_deal_subcribe
        :see: https://viabtc.github.io/coinex_api_en_doc/futures/#docsfutures002_websocket019_deal_subcribe
        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)
        type = None
        type, params = self.handle_market_type_and_params('watchTrades', market, params)
        url = self.urls['api']['ws'][type]
        messageHash = 'trades:' + symbol
        subscriptionHash = 'trades'
        subscribedSymbols = self.safe_value(self.options, 'watchTradesSubscriptions', [])
        subscribedSymbols.append(market['id'])
        message = {
            'method': 'deals.subscribe',
            'params': subscribedSymbols,
            'id': self.request_id(),
        }
        self.options['watchTradesSubscriptions'] = subscribedSymbols
        request = self.deep_extend(message, params)
        trades = await self.watch(url, messageHash, request, subscriptionHash)
        return self.filter_by_since_limit(trades, since, limit, 'timestamp', True)

    async def watch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
        """
        :see: https://viabtc.github.io/coinex_api_en_doc/spot/#docsspot004_websocket017_depth_subscribe_multi
        :see: https://viabtc.github.io/coinex_api_en_doc/futures/#docsfutures002_websocket011_depth_subscribe_multi
        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']
        type = None
        type, params = self.handle_market_type_and_params('watchOrderBook', market, params)
        url = self.urls['api']['ws'][type]
        name = 'orderbook'
        messageHash = name + ':' + symbol
        options = self.safe_value(self.options, 'watchOrderBook', {})
        limits = self.safe_value(options, 'limits', [])
        if limit is None:
            limit = self.safe_value(options, 'defaultLimit', 50)
        if not self.in_array(limit, limits):
            raise NotSupported(self.id + ' watchOrderBook() limit must be one of ' + ', '.join(limits))
        defaultAggregation = self.safe_string(options, 'defaultAggregation', '0')
        aggregations = self.safe_value(options, 'aggregations', [])
        aggregation = self.safe_string(params, 'aggregation', defaultAggregation)
        if not self.in_array(aggregation, aggregations):
            raise NotSupported(self.id + ' watchOrderBook() aggregation must be one of ' + ', '.join(aggregations))
        params = self.omit(params, 'aggregation')
        watchOrderBookSubscriptions = self.safe_value(self.options, 'watchOrderBookSubscriptions', {})
        watchOrderBookSubscriptions[symbol] = [market['id'], limit, aggregation, True]
        subscribe = {
            'method': 'depth.subscribe_multi',
            'id': self.request_id(),
            'params': list(watchOrderBookSubscriptions.values()),
        }
        self.options['watchOrderBookSubscriptions'] = watchOrderBookSubscriptions
        subscriptionHash = self.hash(self.encode(self.json(watchOrderBookSubscriptions)), 'sha256')
        request = self.deep_extend(subscribe, params)
        orderbook = await self.watch(url, messageHash, request, subscriptionHash, request)
        return orderbook.limit()

    async def watch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
        """
        :see: https://viabtc.github.io/coinex_api_en_doc/futures/#docsfutures002_websocket023_kline_subscribe
        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']
        type = None
        type, params = self.handle_market_type_and_params('watchOHLCV', market, params)
        if type != 'swap':
            raise NotSupported(self.id + ' watchOHLCV() is only supported for swap markets. Try using fetchOHLCV() instead')
        url = self.urls['api']['ws'][type]
        messageHash = 'ohlcv'
        watchOHLCVWarning = self.safe_bool(self.options, 'watchOHLCVWarning', True)
        client = self.safe_value(self.clients, url, {})
        clientSub = self.safe_value(client, 'subscriptions', {})
        existingSubscription = self.safe_value(clientSub, messageHash)
        subSymbol = self.safe_string(existingSubscription, 'symbol')
        subTimeframe = self.safe_string(existingSubscription, 'timeframe')
        # due to nature of coinex response can only watch one symbol at a time
        if watchOHLCVWarning and existingSubscription is not None and (subSymbol != symbol or subTimeframe != timeframe):
            raise ExchangeError(self.id + ' watchOHLCV() can only watch one symbol and timeframe at a time. To supress self warning set watchOHLCVWarning to False in options')
        timeframes = self.safe_value(self.options, 'timeframes', {})
        subscribe = {
            'method': 'kline.subscribe',
            'id': self.request_id(),
            'params': [
                market['id'],
                self.safe_integer(timeframes, timeframe),
            ],
        }
        subscription = {
            'symbol': symbol,
            'timeframe': timeframe,
        }
        request = self.deep_extend(subscribe, params)
        ohlcvs = await self.watch(url, messageHash, request, messageHash, subscription)
        if self.newUpdates:
            limit = ohlcvs.getLimit(symbol, limit)
        return self.filter_by_since_limit(ohlcvs, since, limit, 0)

    async def fetch_ohlcv_ws(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
        """
        :see: https://viabtc.github.io/coinex_api_en_doc/spot/#docsspot004_websocket005_kline_query
        query 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 query OHLCV data for
        :param str timeframe: the length of time each candle represents
        :param int|None since: timestamp in ms of the earliest candle to fetch
        :param int|None limit: the maximum amount of candles to fetch
        :param dict params: extra parameters specific to the exchange API endpoint
        :param int|None params['end']: the end time for spot markets, self.seconds() is set
        :returns int[][]: A list of candles ordered, open, high, low, close, volume
        """
        await self.load_markets()
        market = self.market(symbol)
        type, query = self.handle_market_type_and_params('fetchOHLCV', market, params)
        url = self.urls['api']['ws'][type]
        symbol = market['symbol']
        messageHash = 'ohlcv'
        timeframes = self.safe_value(self.options, 'timeframes', {})
        timeframe = self.safe_string(timeframes, timeframe, timeframe)
        if since is None:
            since = 1640995200  # January 1, 2022
        id = self.request_id()
        subscribe = {
            'method': 'kline.query',
            'params': [
                market['id'],
                self.parse_to_int(since / 1000),
                self.safe_integer(params, 'end', self.seconds()),
                self.parse_to_int(timeframe),
            ],
            'id': id,
        }
        subscription = {
            'id': id,
            'future': messageHash,
        }
        subscriptionHash = id
        request = self.deep_extend(subscribe, query)
        ohlcvs = await self.watch(url, messageHash, request, subscriptionHash, subscription)
        return self.filter_by_since_limit(ohlcvs, since, limit, 0)

    def handle_delta(self, bookside, delta):
        bidAsk = self.parse_bid_ask(delta, 0, 1)
        bookside.storeArray(bidAsk)

    def handle_deltas(self, bookside, deltas):
        for i in range(0, len(deltas)):
            self.handle_delta(bookside, deltas[i])

    def handle_order_book(self, client: Client, message):
        #
        #     {
        #         "method": "depth.update",
        #         "params": [
        #             False,
        #             {
        #                 "asks": [
        #                     ["46350.52", "1.07871851"],
        #                     ...
        #                 ],
        #                 "bids": [
        #                     ["46349.61", "0.04000000"],
        #                     ...
        #                 ],
        #                 "last": "46349.93",
        #                 "time": 1639987469166,
        #                 "checksum": 1533284725
        #             },
        #             "BTCUSDT"
        #         ],
        #         "id": null
        #     }
        #
        params = self.safe_value(message, 'params', [])
        fullOrderBook = self.safe_value(params, 0)
        orderbook = self.safe_value(params, 1)
        marketId = self.safe_string(params, 2)
        defaultType = self.safe_string(self.options, 'defaultType')
        market = self.safe_market(marketId, None, None, defaultType)
        symbol = market['symbol']
        name = 'orderbook'
        messageHash = name + ':' + symbol
        timestamp = self.safe_integer(orderbook, 'time')
        currentOrderBook = self.safe_value(self.orderbooks, symbol)
        if fullOrderBook:
            snapshot = self.parse_order_book(orderbook, symbol, timestamp)
            if currentOrderBook is None:
                orderbook = self.order_book(snapshot)
                self.orderbooks[symbol] = orderbook
            else:
                orderbook = self.orderbooks[symbol]
                orderbook.reset(snapshot)
        else:
            asks = self.safe_value(orderbook, 'asks', [])
            bids = self.safe_value(orderbook, 'bids', [])
            self.handle_deltas(currentOrderBook['asks'], asks)
            self.handle_deltas(currentOrderBook['bids'], bids)
            currentOrderBook['nonce'] = timestamp
            currentOrderBook['timestamp'] = timestamp
            currentOrderBook['datetime'] = self.iso8601(timestamp)
            self.orderbooks[symbol] = currentOrderBook
        # self.checkOrderBookChecksum(self.orderbooks[symbol])
        client.resolve(self.orderbooks[symbol], messageHash)

    async def watch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
        await self.load_markets()
        await self.authenticate(params)
        messageHash = 'orders'
        market = None
        type, query = self.handle_market_type_and_params('watchOrders', market, params)
        message = {
            'method': 'order.subscribe',
            'id': self.request_id(),
        }
        if symbol is not None:
            market = self.market(symbol)
            symbol = market['symbol']
            message['params'] = [market['id']]
            messageHash += ':' + symbol
        else:
            message['params'] = []
        url = self.urls['api']['ws'][type]
        request = self.deep_extend(message, query)
        orders = await self.watch(url, messageHash, request, messageHash, request)
        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
        #
        #      {
        #          "method": "order.update",
        #          "params": [
        #              1,
        #              {
        #                  "id": 77782469357,
        #                  "type": 1,
        #                  "side": 2,
        #                  "user": 1849116,
        #                  "account": 0,
        #                  "option": 2,
        #                  "ctime": 1653961043.048967,
        #                  "mtime": 1653961043.048967,
        #                  "market": "BTCUSDT",
        #                  "source": "web",
        #                  "client_id": '',
        #                  "price": "1.00",
        #                  "amount": "1.00000000",
        #                  "taker_fee": "0.0020",
        #                  "maker_fee": "0.0020",
        #                  "left": "1.00000000",
        #                  "deal_stock": "0",
        #                  "deal_money": "0",
        #                  "money_fee": "0",
        #                  "stock_fee": "0",
        #                  "asset_fee": "0",
        #                  "fee_discount": "1",
        #                  "last_deal_amount": "0",
        #                  "last_deal_price": "0",
        #                  "last_deal_time": 0,
        #                  "last_deal_id": 0,
        #                  "last_role": 0,
        #                  "fee_asset": null,
        #                  "stop_id": 0
        #              }
        #          ],
        #          "id": null
        #      }
        #
        #  swap
        #
        #      {
        #          "method": "order.update",
        #          "params": [
        #              1,
        #              {
        #                  "order_id": 23423462821,
        #                  "position_id": 0,
        #                  "stop_id": 0,
        #                  "market": "BTCUSDT",
        #                  "type": 1,
        #                  "side": 2,
        #                  "target": 0,
        #                  "effect_type": 1,
        #                  "user_id": 1849116,
        #                  "create_time": 1653961509.25049,
        #                  "update_time": 1653961509.25049,
        #                  "source": "web",
        #                  "price": "1.00",
        #                  "amount": "1.0000",
        #                  "taker_fee": "0.00050",
        #                  "maker_fee": "0.00030",
        #                  "left": "1.0000",
        #                  "deal_stock": "0.00000000000000000000",
        #                  "deal_fee": "0.00000000000000000000",
        #                  "deal_profit": "0.00000000000000000000",
        #                  "last_deal_amount": "0.00000000000000000000",
        #                  "last_deal_price": "0.00000000000000000000",
        #                  "last_deal_time": 0,
        #                  "last_deal_id": 0,
        #                  "last_deal_type": 0,
        #                  "last_deal_role": 0,
        #                  "client_id": '',
        #                  "fee_asset": '',
        #                  "fee_discount": "0.00000000000000000000",
        #                  "deal_asset_fee": "0.00000000000000000000",
        #                  "leverage": "3",
        #                  "position_type": 2
        #              }
        #          ],
        #          "id": null
        #      }
        #
        params = self.safe_value(message, 'params', [])
        order = self.safe_value(params, 1, {})
        parsedOrder = self.parse_ws_order(order)
        if self.orders is None:
            limit = self.safe_integer(self.options, 'ordersLimit', 1000)
            self.orders = ArrayCacheBySymbolById(limit)
        orders = self.orders
        orders.append(parsedOrder)
        messageHash = 'orders'
        client.resolve(self.orders, messageHash)
        messageHash += ':' + parsedOrder['symbol']
        client.resolve(self.orders, messageHash)

    def parse_ws_order(self, order, market=None):
        #
        #  spot
        #
        #       {
        #           "id": 77782469357,
        #           "type": 1,
        #           "side": 2,
        #           "user": 1849116,
        #           "account": 0,
        #           "option": 2,
        #           "ctime": 1653961043.048967,
        #           "mtime": 1653961043.048967,
        #           "market": "BTCUSDT",
        #           "source": "web",
        #           "client_id": '',
        #           "price": "1.00",
        #           "amount": "1.00000000",
        #           "taker_fee": "0.0020",
        #           "maker_fee": "0.0020",
        #           "left": "1.00000000",
        #           "deal_stock": "0",
        #           "deal_money": "0",
        #           "money_fee": "0",
        #           "stock_fee": "0",
        #           "asset_fee": "0",
        #           "fee_discount": "1",
        #           "last_deal_amount": "0",
        #           "last_deal_price": "0",
        #           "last_deal_time": 0,
        #           "last_deal_id": 0,
        #           "last_role": 0,
        #           "fee_asset": null,
        #           "stop_id": 0
        #       }
        #
        #  swap
        #
        #      {
        #          "order_id": 23423462821,
        #          "position_id": 0,
        #          "stop_id": 0,
        #          "market": "BTCUSDT",
        #          "type": 1,
        #          "side": 2,
        #          "target": 0,
        #          "effect_type": 1,
        #          "user_id": 1849116,
        #          "create_time": 1653961509.25049,
        #          "update_time": 1653961509.25049,
        #          "source": "web",
        #          "price": "1.00",
        #          "amount": "1.0000",
        #          "taker_fee": "0.00050",
        #          "maker_fee": "0.00030",
        #          "left": "1.0000",
        #          "deal_stock": "0.00000000000000000000",
        #          "deal_fee": "0.00000000000000000000",
        #          "deal_profit": "0.00000000000000000000",
        #          "last_deal_amount": "0.00000000000000000000",
        #          "last_deal_price": "0.00000000000000000000",
        #          "last_deal_time": 0,
        #          "last_deal_id": 0,
        #          "last_deal_type": 0,
        #          "last_deal_role": 0,
        #          "client_id": '',
        #          "fee_asset": '',
        #          "fee_discount": "0.00000000000000000000",
        #          "deal_asset_fee": "0.00000000000000000000",
        #          "leverage": "3",
        #          "position_type": 2
        #      }
        #
        #  order.update_stop
        #
        #       {
        #           "id": 78006745870,
        #           "type": 1,
        #           "side": 2,
        #           "user": 1849116,
        #           "account": 1,
        #           "option": 70,
        #           "direction": 1,
        #           "ctime": 1654171725.131976,
        #           "mtime": 1654171725.131976,
        #           "market": "BTCUSDT",
        #           "source": "web",
        #           "client_id": '',
        #           "stop_price": "1.00",
        #           "price": "1.00",
        #           "amount": "1.00000000",
        #           "taker_fee": "0.0020",
        #           "maker_fee": "0.0020",
        #           "fee_discount": "1",
        #           "fee_asset": null,
        #           "status": 0
        #       }
        #
        timestamp = self.safe_timestamp_2(order, 'update_time', 'mtime')
        marketId = self.safe_string(order, 'market')
        typeCode = self.safe_string(order, 'type')
        type = self.safe_string({
            '1': 'limit',
            '2': 'market',
        }, typeCode)
        sideCode = self.safe_string(order, 'side')
        side = self.safe_string({
            '1': 'sell',
            '2': 'buy',
        }, sideCode)
        remaining = self.safe_string(order, 'left')
        amount = self.safe_string(order, 'amount')
        status = self.safe_string(order, 'status')
        defaultType = self.safe_string(self.options, 'defaultType')
        market = self.safe_market(marketId, market, None, defaultType)
        cost = self.safe_string(order, 'deal_money')
        filled = self.safe_string(order, 'deal_stock')
        average = None
        if market['swap']:
            leverage = self.safe_string(order, 'leverage')
            cost = Precise.string_div(filled, leverage)
            average = Precise.string_div(filled, amount)
            filled = None
        fee = None
        feeCost = self.omit_zero(self.safe_string(order, 'money_fee'))
        if feeCost is not None:
            feeCurrencyId = self.safe_string(order, 'fee_asset', market['quote'])
            fee = {
                'currency': self.safe_currency_code(feeCurrencyId),
                'cost': feeCost,
            }
        return self.safe_order({
            'info': order,
            'id': self.safe_string_2(order, 'order_id', 'id'),
            'clientOrderId': self.safe_string(order, 'client_id'),
            'datetime': self.iso8601(timestamp),
            'timestamp': timestamp,
            'lastTradeTimestamp': self.safe_timestamp(order, 'last_deal_time'),
            'symbol': market['symbol'],
            'type': type,
            'timeInForce': None,
            'postOnly': None,
            'side': side,
            'price': self.safe_string(order, 'price'),
            'stopPrice': self.safe_string(order, 'stop_price'),
            'triggerPrice': self.safe_string(order, 'stop_price'),
            'amount': amount,
            'filled': filled,
            'remaining': remaining,
            'cost': cost,
            'average': average,
            'status': self.parse_ws_order_status(status),
            'fee': fee,
            'trades': None,
        }, market)

    def parse_ws_order_status(self, status):
        statuses = {
            '0': 'pending',
            '1': 'ok',
        }
        return self.safe_string(statuses, status, status)

    def handle_message(self, client: Client, message):
        error = self.safe_value(message, 'error')
        if error is not None:
            raise ExchangeError(self.id + ' ' + self.json(error))
        method = self.safe_string(message, 'method')
        handlers = {
            'state.update': self.handle_ticker,
            'asset.update': self.handle_balance,
            'deals.update': self.handle_trades,
            'depth.update': self.handle_order_book,
            'order.update': self.handle_orders,
            'kline.update': self.handle_ohlcv,
            'order.update_stop': self.handle_orders,
        }
        handler = self.safe_value(handlers, method)
        if handler is not None:
            handler(client, message)
            return
        self.handle_subscription_status(client, message)

    def handle_authentication_message(self, client: Client, message):
        #
        #     {
        #         "error": null,
        #         "result": {
        #             "status": "success"
        #         },
        #         "id": 1
        #     }
        #
        messageHashSpot = 'authenticated:spot'
        messageHashSwap = 'authenticated:swap'
        # client.resolve(message, messageHashSpot)
        # client.resolve(message, messageHashSwap)
        spotFuture = self.safe_value(client.futures, messageHashSpot)
        spotFuture.resolve(True)
        swapFutures = self.safe_value(client.futures, messageHashSwap)
        swapFutures.resolve(True)
        return message

    def handle_subscription_status(self, client: Client, message):
        id = self.safe_integer(message, 'id')
        subscription = self.safe_value(client.subscriptions, id)
        if subscription is not None:
            futureIndex = self.safe_string(subscription, 'future')
            if futureIndex == 'ohlcv':
                self.handle_ohlcv(client, message)
                return
            future = self.safe_value(client.futures, futureIndex)
            if future is not None:
                future.resolve(True)
            del client.subscriptions[id]

    async def authenticate(self, params={}):
        type = None
        type, params = self.handle_market_type_and_params('authenticate', None, params)
        url = self.urls['api']['ws'][type]
        client = self.client(url)
        time = self.milliseconds()
        isSpot = (type == 'spot')
        spotMessageHash = 'authenticated:spot'
        swapMessageHash = 'authenticated:swap'
        messageHash = spotMessageHash if isSpot else swapMessageHash
        future = client.future(messageHash)
        authenticated = self.safe_value(client.subscriptions, messageHash)
        if type == 'spot':
            if authenticated is not None:
                return await future
            requestId = self.request_id()
            subscribe = {
                'id': requestId,
                'future': spotMessageHash,
            }
            signData = 'access_id=' + self.apiKey + '&tonce=' + self.number_to_string(time) + '&secret_key=' + self.secret
            hash = self.hash(self.encode(signData), 'md5')
            request = {
                'method': 'server.sign',
                'params': [
                    self.apiKey,
                    hash.upper(),
                    time,
                ],
                'id': requestId,
            }
            self.watch(url, messageHash, request, requestId, subscribe)
            client.subscriptions[messageHash] = True
            return await future
        else:
            if authenticated is not None:
                return await future
            requestId = self.request_id()
            subscribe = {
                'id': requestId,
                'future': swapMessageHash,
            }
            signData = 'access_id=' + self.apiKey + '&timestamp=' + self.number_to_string(time) + '&secret_key=' + self.secret
            hash = self.hash(self.encode(signData), 'sha256', 'hex')
            request = {
                'method': 'server.sign',
                'params': [
                    self.apiKey,
                    hash.lower(),
                    time,
                ],
                'id': requestId,
            }
            self.watch(url, messageHash, request, requestId, subscribe)
            client.subscriptions[messageHash] = True
            return await future
