# -*- 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 Any, Balances, Int, Order, OrderBook, Str, Ticker, Trade
from ccxt.async_support.base.ws.client import Client
from typing import List
from ccxt.base.errors import NotSupported


class bitrue(ccxt.async_support.bitrue):

    def describe(self) -> Any:
        return self.deep_extend(super(bitrue, self).describe(), {
            'has': {
                'ws': True,
                'watchBalance': True,
                'watchTicker': True,
                'watchTickers': False,
                'watchTrades': True,
                'watchMyTrades': False,
                'watchOrders': True,
                'watchOrderBook': True,
                'watchOHLCV': True,
            },
            'urls': {
                'api': {
                    'open': 'https://open.bitrue.com',
                    'ws': {
                        'public': 'wss://ws.bitrue.com/market/ws',
                        'futurePublic': 'wss://fmarket-ws.bitrue.com/kline-api/ws',
                        'private': 'wss://wsapi.bitrue.com',
                    },
                },
            },
            'api': {
                'open': {
                    'v1': {
                        'private': {
                            'post': {
                                'poseidon/api/v1/listenKey': 1,
                            },
                            'put': {
                                'poseidon/api/v1/listenKey/{listenKey}': 1,
                            },
                            'delete': {
                                'poseidon/api/v1/listenKey/{listenKey}': 1,
                            },
                        },
                    },
                },
            },
            'options': {
                'listenKeyRefreshRate': 1800000,  # 30 mins
                'ws': {
                    'gunzip': True,
                },
                'futuresTimeframes': {
                    '1m': '1min',
                    '5m': '5min',
                    '15m': '15min',
                    '30m': '30min',
                    '1h': '60min',
                    '2h': '2h',
                    '4h': '4h',
                    '1d': '1day',
                    '1w': '1week',
                },
            },
        })

    async def watch_balance(self, params={}) -> Balances:
        """
        watch balance and get the amount of funds available for trading or funds locked in orders

        https://github.com/Bitrue-exchange/Spot-official-api-docs#balance-update

        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `balance structure <https://docs.ccxt.com/?id=balance-structure>`
        """
        url = await self.authenticate()
        messageHash = 'balance'
        message: dict = {
            'event': 'sub',
            'params': {
                'channel': 'user_balance_update',
            },
        }
        request = self.deep_extend(message, params)
        return await self.watch(url, messageHash, request, messageHash)

    def handle_balance(self, client: Client, message):
        #
        #     {
        #         "e": "BALANCE",
        #         "x": "OutboundAccountPositionTradeEvent",
        #         "E": 1657799510175,
        #         "I": "302274978401288200",
        #         "i": 1657799510175,
        #         "B": [{
        #                 "a": "btc",
        #                 "F": "0.0006000000000000",
        #                 "T": 1657799510000,
        #                 "f": "0.0006000000000000",
        #                 "t": 0
        #             },
        #             {
        #                 "a": "usdt",
        #                 "T": 0,
        #                 "L": "0.0000000000000000",
        #                 "l": "-11.8705317318000000",
        #                 "t": 1657799510000
        #             }
        #         ],
        #         "u": 1814396
        #     }
        #
        #     {
        #      "e": "BALANCE",
        #      "x": "OutboundAccountPositionOrderEvent",
        #      "E": 1670051332478,
        #      "I": "353662845694083072",
        #      "i": 1670051332478,
        #      "B": [
        #        {
        #          "a": "eth",
        #          "F": "0.0400000000000000",
        #          "T": 1670051332000,
        #          "f": "-0.0100000000000000",
        #          "L": "0.0100000000000000",
        #          "l": "0.0100000000000000",
        #          "t": 1670051332000
        #        }
        #      ],
        #      "u": 2285311
        #    }
        #
        balances = self.safe_value(message, 'B', [])
        self.parse_ws_balances(balances)
        messageHash = 'balance'
        client.resolve(self.balance, messageHash)

    def parse_ws_balances(self, balances):
        #
        #    [{
        #         "a": "btc",
        #         "F": "0.0006000000000000",
        #         "T": 1657799510000,
        #         "f": "0.0006000000000000",
        #         "t": 0
        #     },
        #     {
        #         "a": "usdt",
        #         "T": 0,
        #         "L": "0.0000000000000000",
        #         "l": "-11.8705317318000000",
        #         "t": 1657799510000
        #     }]
        #
        self.balance['info'] = balances
        for i in range(0, len(balances)):
            balance = balances[i]
            currencyId = self.safe_string(balance, 'a')
            code = self.safe_currency_code(currencyId)
            account = self.account()
            free = self.safe_string(balance, 'F')
            used = self.safe_string(balance, 'L')
            balanceUpdateTime = self.safe_integer(balance, 'T', 0)
            lockBalanceUpdateTime = self.safe_integer(balance, 't', 0)
            updateFree = balanceUpdateTime != 0
            updateUsed = lockBalanceUpdateTime != 0
            if updateFree or updateUsed:
                if updateFree:
                    account['free'] = free
                if updateUsed:
                    account['used'] = used
                self.balance[code] = account
        self.balance = self.safe_balance(self.balance)

    async def watch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
        """
        watches information on user orders

        https://github.com/Bitrue-exchange/Spot-official-api-docs#order-update

        :param str symbol:
        :param int [since]: timestamp in ms of the earliest order
        :param int [limit]: the maximum amount of orders to return
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: A dictionary of `order structure <https://docs.ccxt.com/?id=order-structure>` indexed by market symbols
        """
        await self.load_markets()
        if symbol is not None:
            market = self.market(symbol)
            symbol = market['symbol']
        url = await self.authenticate()
        messageHash = 'orders'
        message: dict = {
            'event': 'sub',
            'params': {
                'channel': 'user_order_update',
            },
        }
        request = self.deep_extend(message, params)
        orders = await self.watch(url, messageHash, request, messageHash)
        if self.newUpdates:
            limit = orders.getLimit(symbol, limit)
        return self.filter_by_symbol_since_limit(orders, symbol, since, limit, True)

    def handle_order(self, client: Client, message):
        #
        #    {
        #        "e": "ORDER",
        #        "i": 16122802798,
        #        "E": 1657882521876,
        #        "I": "302623154710888464",
        #        "u": 1814396,
        #        "s": "btcusdt",
        #        "S": 2,
        #        "o": 1,
        #        "q": "0.0005",
        #        "p": "60000",
        #        "X": 0,
        #        "x": 1,
        #        "z": "0",
        #        "n": "0",
        #        "N": "usdt",
        #        "O": 1657882521876,
        #        "L": "0",
        #        "l": "0",
        #        "Y": "0"
        #    }
        #
        parsed = self.parse_ws_order(message)
        if self.orders is None:
            limit = self.safe_integer(self.options, 'ordersLimit', 1000)
            self.orders = ArrayCacheBySymbolById(limit)
        orders = self.orders
        orders.append(parsed)
        messageHash = 'orders'
        client.resolve(self.orders, messageHash)

    def parse_ws_order(self, order, market=None):
        #
        #    {
        #        "e": "ORDER",
        #        "i": 16122802798,
        #        "E": 1657882521876,
        #        "I": "302623154710888464",
        #        "u": 1814396,
        #        "s": "btcusdt",
        #        "S": 2,
        #        "o": 1,
        #        "q": "0.0005",
        #        "p": "60000",
        #        "X": 0,
        #        "x": 1,
        #        "z": "0",
        #        "n": "0",
        #        "N": "usdt",
        #        "O": 1657882521876,
        #        "L": "0",
        #        "l": "0",
        #        "Y": "0"
        #    }
        #
        timestamp = self.safe_integer(order, 'E')
        marketId = self.safe_string_upper(order, 's')
        typeId = self.safe_string(order, 'o')
        sideId = self.safe_integer(order, 'S')
        # 1: buy
        # 2: sell
        side = 'buy' if (sideId == 1) else 'sell'
        statusId = self.safe_string(order, 'X')
        feeCurrencyId = self.safe_string(order, 'N')
        return self.safe_order({
            'info': order,
            'id': self.safe_string(order, 'i'),
            'clientOrderId': self.safe_string(order, 'c'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': self.safe_integer(order, 'T'),
            'symbol': self.safe_symbol(marketId, market),
            'type': self.parse_ws_order_type(typeId),
            'timeInForce': None,
            'postOnly': None,
            'side': side,
            'price': self.safe_string(order, 'p'),
            'triggerPrice': None,
            'amount': self.safe_string(order, 'q'),
            'cost': self.safe_string(order, 'Y'),
            'average': None,
            'filled': self.safe_string(order, 'z'),
            'remaining': None,
            'status': self.parse_ws_order_status(statusId),
            'fee': {
                'currency': self.safe_currency_code(feeCurrencyId),
                'cost': self.safe_number(order, 'n'),
            },
        }, market)

    async def watch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
        await self.load_markets()
        market = self.market(symbol)
        symbol = market['symbol']
        messageHash = 'orderbook:' + symbol
        url = None
        channel = None
        cbId = None
        if market['swap']:
            baseIdLower = self.safe_string_lower(market, 'baseId')
            quoteIdLower = self.safe_string_lower(market, 'quoteId')
            wsId = 'e_' + baseIdLower + quoteIdLower
            channel = 'market_' + wsId + '_depth_step0'
            cbId = wsId
            url = self.urls['api']['ws']['futurePublic']
        else:
            marketIdLowercase = market['id'].lower()
            channel = 'market_' + marketIdLowercase + '_simple_depth_step0'
            cbId = marketIdLowercase
            url = self.urls['api']['ws']['public']
        message: dict = {
            'event': 'sub',
            'params': {
                'cb_id': cbId,
                'channel': channel,
            },
        }
        request = self.deep_extend(message, params)
        return await self.watch(url, messageHash, request, messageHash)

    def handle_order_book(self, client: Client, message):
        #
        #     {
        #         "channel": "market_ethbtc_simple_depth_step0",
        #         "ts": 1670056708670,
        #         "tick": {
        #             "buys": [
        #                 [
        #                     "0.075170",
        #                     "67.153"
        #                 ],
        #                 [
        #                     "0.075169",
        #                     "17.195"
        #                 ],
        #                 [
        #                     "0.075166",
        #                     "29.788"
        #                 ],
        #             ]
        #              "asks": [
        #                 [
        #                     "0.075171",
        #                     "0.256"
        #                 ],
        #                 [
        #                     "0.075172",
        #                     "0.160"
        #                 ],
        #             ]
        #         }
        #     }
        #
        channel = self.safe_string(message, 'channel')
        parts = channel.split('_')
        channelKind = self.safe_string(parts, 1)
        isFutures = (channelKind == 'e')
        market = None
        if isFutures:
            wsBaseQuote = self.safe_string_lower(parts, 2)
            market = self.find_swap_market_by_ws_base_quote(wsBaseQuote)
        else:
            marketId = self.safe_string_upper(parts, 1)
            market = self.safe_market(marketId)
        symbol = market['symbol']
        timestamp = self.safe_integer(message, 'ts')
        tick = self.safe_value(message, 'tick', {})
        parseable = tick
        if isFutures:
            rawAsks = self.safe_list(tick, 'asks', [])
            rawBuys = self.safe_list(tick, 'buys', [])
            parseable = {
                'asks': self.parse_contract_bids_asks(rawAsks, symbol),
                'buys': self.parse_contract_bids_asks(rawBuys, symbol),
            }
        if not (symbol in self.orderbooks):
            self.orderbooks[symbol] = self.order_book()
        orderbook = self.orderbooks[symbol]
        snapshot = self.parse_order_book(parseable, symbol, timestamp, 'buys', 'asks')
        orderbook.reset(snapshot)
        messageHash = 'orderbook:' + symbol
        client.resolve(orderbook, messageHash)

    def find_swap_market_by_ws_base_quote(self, wsBaseQuote: str):
        symbols = list(self.markets.keys())
        for i in range(0, len(symbols)):
            candidate = self.markets[symbols[i]]
            if not candidate['swap']:
                continue
            baseId = self.safe_string_lower(candidate, 'baseId', '')
            quoteId = self.safe_string_lower(candidate, 'quoteId', '')
            if baseId + quoteId == wsBaseQuote:
                return candidate
        return None

    def parse_contract_bids_asks(self, bidsAsks, symbol: str):
        result = []
        for i in range(0, len(bidsAsks)):
            level = bidsAsks[i]
            price = self.safe_number(level, 0)
            rawAmount = self.safe_number(level, 1)
            amount = self.convert_from_raw_quantity(symbol, rawAmount)
            result.append([price, amount])
        return result

    def convert_from_raw_quantity(self, symbol: str, rawQuantity):
        if rawQuantity is None:
            return None
        market = self.market(symbol)
        if not market['contract']:
            return rawQuantity
        contractSize = self.safe_number(market, 'contractSize', 1)
        return rawQuantity * contractSize

    async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
        """
        watches public trades for a swap(futures) market

        https://www.bitrue.com/api_docs_includes_file/futures/index.html#websocket-market-data

        :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']
        if not market['swap']:
            raise NotSupported(self.id + ' watchTrades is only supported for swap markets')
        baseIdLower = self.safe_string_lower(market, 'baseId')
        quoteIdLower = self.safe_string_lower(market, 'quoteId')
        wsId = 'e_' + baseIdLower + quoteIdLower
        channel = 'market_' + wsId + '_trade_ticker'
        messageHash = 'trades:' + symbol
        url = self.urls['api']['ws']['futurePublic']
        message: dict = {
            'event': 'sub',
            'params': {
                'cb_id': wsId,
                'channel': channel,
            },
        }
        request = self.deep_extend(message, 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)

    def handle_trades(self, client: Client, message):
        #
        #     {
        #         "event_rep": "",
        #         "channel": "market_e_btcusdt_trade_ticker",
        #         "ts": 1721743391000,
        #         "status": "ok",
        #         "tick": {
        #             "data": [
        #                 {
        #                     "amount": "1666656191.2",
        #                     "ds": "2024-07-23 22:03:11",
        #                     "price": "66008.8",
        #                     "side": "SELL",
        #                     "ts": 1721743391398,
        #                     "vol": "25249"
        #                 }
        #             ]
        #         }
        #     }
        #
        channel = self.safe_string(message, 'channel')
        parts = channel.split('_')
        wsBaseQuote = self.safe_string_lower(parts, 2)
        market = self.find_swap_market_by_ws_base_quote(wsBaseQuote)
        if market is None:
            return
        symbol = market['symbol']
        tick = self.safe_value(message, 'tick', {})
        data = self.safe_list(tick, 'data', [])
        appended = False
        stored = self.safe_value(self.trades, symbol)
        for i in range(0, len(data)):
            if stored is None:
                limit = self.safe_integer(self.options, 'tradesLimit', 1000)
                stored = ArrayCache(limit)
                self.trades[symbol] = stored
            trade = self.parse_ws_trade(data[i], market)
            stored.append(trade)
            appended = True
        if appended:
            messageHash = 'trades:' + symbol
            client.resolve(stored, messageHash)

    def parse_ws_trade(self, trade, market=None):
        symbol = market['symbol']
        timestamp = self.safe_integer(trade, 'ts')
        sideLower = self.safe_string_lower(trade, 'side')
        priceString = self.safe_string(trade, 'price')
        rawVol = self.safe_number(trade, 'vol')
        baseAmount = self.convert_from_raw_quantity(symbol, rawVol)
        return self.safe_trade({
            'info': trade,
            'id': None,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': symbol,
            'order': None,
            'type': None,
            'side': sideLower,
            'takerOrMaker': 'taker',
            'price': priceString,
            'amount': self.number_to_string(baseAmount),
            'cost': None,
            'fee': None,
        }, market)

    async def watch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
        """
        watches OHLCV candles for a swap(futures) market

        https://www.bitrue.com/api_docs_includes_file/futures/index.html#websocket-market-data

        :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']
        if not market['swap']:
            raise NotSupported(self.id + ' watchOHLCV is only supported for swap markets')
        futuresTimeframes = self.safe_dict(self.options, 'futuresTimeframes', {})
        interval = self.safe_string(futuresTimeframes, timeframe)
        if interval is None:
            raise NotSupported(self.id + ' watchOHLCV does not support timeframe ' + timeframe)
        baseIdLower = self.safe_string_lower(market, 'baseId')
        quoteIdLower = self.safe_string_lower(market, 'quoteId')
        wsId = 'e_' + baseIdLower + quoteIdLower
        channel = 'market_' + wsId + '_kline_' + interval
        messageHash = 'ohlcv:' + symbol + ':' + timeframe
        url = self.urls['api']['ws']['futurePublic']
        message: dict = {
            'event': 'sub',
            'params': {
                'cb_id': wsId,
                'channel': channel,
            },
        }
        request = self.deep_extend(message, 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 handle_ohlcv(self, client: Client, message):
        #
        #     {
        #         "channel": "market_e_btcusdt_kline_1min",
        #         "data": [],
        #         "tick": {
        #             "amount": 396539282326.3,
        #             "close": 19517.1,
        #             "ds": "2022-07-13 14:00:00",
        #             "high": 19556.5,
        #             "id": 1657692000,
        #             "low": 19465.1,
        #             "open": 19507.3,
        #             "vol": 20325940
        #         },
        #         "ts": 1657696418000,
        #         "status": "ok"
        #     }
        #
        channel = self.safe_string(message, 'channel')
        parts = channel.split('_')
        wsBaseQuote = self.safe_string_lower(parts, 2)
        market = self.find_swap_market_by_ws_base_quote(wsBaseQuote)
        if market is None:
            return
        symbol = market['symbol']
        wsInterval = self.safe_string(parts, 4)
        futuresTimeframes = self.safe_dict(self.options, 'futuresTimeframes', {})
        timeframe = self.find_timeframe(wsInterval, futuresTimeframes)
        tick = self.safe_value(message, 'tick')
        if tick is None:
            return
        parsed = self.parse_ws_ohlcv(tick, market)
        if not (symbol in self.ohlcvs):
            self.ohlcvs[symbol] = {}
        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]
        stored.append(parsed)
        messageHash = 'ohlcv:' + symbol + ':' + timeframe
        client.resolve(stored, messageHash)

    def parse_ws_ohlcv(self, tick, market=None) -> list:
        symbol = market['symbol']
        idSeconds = self.safe_integer(tick, 'id')
        timestamp = None if (idSeconds is None) else idSeconds * 1000
        open = self.safe_number(tick, 'open')
        high = self.safe_number(tick, 'high')
        low = self.safe_number(tick, 'low')
        close = self.safe_number(tick, 'close')
        rawVol = self.safe_number(tick, 'vol')
        baseVolume = self.convert_from_raw_quantity(symbol, rawVol)
        return [timestamp, open, high, low, close, baseVolume]

    async def watch_ticker(self, symbol: str, params={}) -> Ticker:
        """
        watches a 24h ticker for a swap(futures) market

        https://www.bitrue.com/api_docs_includes_file/futures/index.html#websocket-market-data

        :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']
        if not market['swap']:
            raise NotSupported(self.id + ' watchTicker is only supported for swap markets')
        baseIdLower = self.safe_string_lower(market, 'baseId')
        quoteIdLower = self.safe_string_lower(market, 'quoteId')
        wsId = 'e_' + baseIdLower + quoteIdLower
        channel = 'market_' + wsId + '_ticker'
        messageHash = 'ticker:' + symbol
        url = self.urls['api']['ws']['futurePublic']
        message: dict = {
            'event': 'sub',
            'params': {
                'cb_id': wsId,
                'channel': channel,
            },
        }
        request = self.deep_extend(message, params)
        return await self.watch(url, messageHash, request, messageHash)

    def handle_ticker(self, client: Client, message):
        #
        #     {
        #         "channel": "market_e_btcusdt_ticker",
        #         "ts": 1506584998239,
        #         "tick": {
        #             "amount": 123.1221,
        #             "vol": 1212.12211,
        #             "open": 2233.22,
        #             "close": 1221.11,
        #             "high": 22322.22,
        #             "low": 2321.22,
        #             "rose": -0.2922
        #         },
        #         "status": "ok"
        #     }
        #
        channel = self.safe_string(message, 'channel')
        parts = channel.split('_')
        wsBaseQuote = self.safe_string_lower(parts, 2)
        market = self.find_swap_market_by_ws_base_quote(wsBaseQuote)
        if market is None:
            return
        symbol = market['symbol']
        tick = self.safe_value(message, 'tick')
        if tick is None:
            return
        timestamp = self.safe_integer(message, 'ts')
        parsed = self.parse_ws_ticker(tick, market, timestamp)
        self.tickers[symbol] = parsed
        messageHash = 'ticker:' + symbol
        client.resolve(parsed, messageHash)

    def parse_ws_ticker(self, tick, market, timestamp: Int = None) -> Ticker:
        symbol = market['symbol']
        rawVol = self.safe_number(tick, 'vol')
        rawAmount = self.safe_number(tick, 'amount')
        baseVolume = self.convert_from_raw_quantity(symbol, rawVol)
        quoteVolume = self.convert_from_raw_quantity(symbol, rawAmount)
        close = self.safe_number(tick, 'close')
        rose = self.safe_number(tick, 'rose')
        percentage = None if (rose is None) else rose * 100
        return self.safe_ticker({
            'info': tick,
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'high': self.safe_number(tick, 'high'),
            'low': self.safe_number(tick, 'low'),
            'bid': None,
            'bidVolume': None,
            'ask': None,
            'askVolume': None,
            'vwap': None,
            'open': self.safe_number(tick, 'open'),
            'close': close,
            'last': close,
            'previousClose': None,
            'change': None,
            'percentage': percentage,
            'average': None,
            'baseVolume': baseVolume,
            'quoteVolume': quoteVolume,
        }, market)

    def parse_ws_order_type(self, typeId):
        types: dict = {
            '1': 'limit',
            '2': 'market',
            '3': 'limit',
        }
        return self.safe_string(types, typeId, typeId)

    def parse_ws_order_status(self, status):
        statuses: dict = {
            '0': 'open',  # The order has not been accepted by the engine.
            '1': 'open',  # The order has been accepted by the engine.
            '2': 'closed',  # The order has been completed.
            '3': 'open',  # A part of the order has been filled.
            '4': 'canceled',  # The order has been canceled.
            '7': 'open',  # Stop order placed.
        }
        return self.safe_string(statuses, status, status)

    def handle_ping(self, client: Client, message):
        self.spawn(self.pong, client, message)

    async def pong(self, client, message):
        #
        #     {
        #         "ping": 1670057540627
        #     }
        #
        time = self.safe_integer(message, 'ping')
        pong: dict = {
            'pong': time,
        }
        await client.send(pong)

    def handle_message(self, client: Client, message):
        if 'channel' in message:
            channel = self.safe_string(message, 'channel')
            if channel.find('_depth_step') > -1:
                self.handle_order_book(client, message)
            elif channel.find('_trade_ticker') > -1:
                self.handle_trades(client, message)
            elif channel.find('_kline_') > -1:
                self.handle_ohlcv(client, message)
            elif channel.find('_ticker') > -1:
                self.handle_ticker(client, message)
        elif 'ping' in message:
            self.handle_ping(client, message)
        else:
            event = self.safe_string(message, 'e')
            handlers: dict = {
                'BALANCE': self.handle_balance,
                'ORDER': self.handle_order,
            }
            handler = self.safe_value(handlers, event)
            if handler is not None:
                handler(client, message)

    async def authenticate(self, params={}):
        listenKey = self.safe_value(self.options, 'listenKey')
        if listenKey is None:
            response = await self.openV1PrivatePostPoseidonApiV1ListenKey(params)
            #
            #     {
            #         "msg": "succ",
            #         "code": 200,
            #         "data": {
            #             "listenKey": "7d1ec51340f499d85bb33b00a96ef680bda28869d5c3374a444c5ca4847d1bf0"
            #         }
            #     }
            #
            data = self.safe_value(response, 'data', {})
            key = self.safe_string(data, 'listenKey')
            self.options['listenKey'] = key
            self.options['listenKeyUrl'] = self.urls['api']['ws']['private'] + '/stream?listenKey=' + key
            refreshTimeout = self.safe_integer(self.options, 'listenKeyRefreshRate', 1800000)
            self.delay(refreshTimeout, self.keep_alive_listen_key)
        return self.options['listenKeyUrl']

    async def keep_alive_listen_key(self, params={}):
        listenKey = self.safe_string(self.options, 'listenKey')
        request: dict = {
            'listenKey': listenKey,
        }
        try:
            await self.openV1PrivatePutPoseidonApiV1ListenKeyListenKey(self.extend(request, params))
            #
            # ಠ_ಠ
            #     {
            #         "msg": "succ",
            #         "code": "200"
            #     }
            #
        except Exception as error:
            self.options['listenKey'] = None
            self.options['listenKeyUrl'] = None
            return
        refreshTimeout = self.safe_integer(self.options, 'listenKeyRefreshRate', 1800000)
        self.delay(refreshTimeout, self.keep_alive_listen_key)
