# -*- 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, ArrayCacheBySymbolBySide, ArrayCacheByTimestamp
from ccxt.base.types import Any, Int, Market, Order, OrderBook, Position, Str, Strings, Ticker, Trade
from ccxt.async_support.base.ws.client import Client
from typing import List
from ccxt.base.errors import ExchangeError
from ccxt.base.errors import BadRequest


class deepcoin(ccxt.async_support.deepcoin):

    def describe(self) -> Any:
        return self.deep_extend(super(deepcoin, self).describe(), {
            'has': {
                'ws': True,
                'watchTicker': True,
                'watchMarkPrice': False,
                'watchMarkPrices': False,
                'watchTickers': False,
                'watchBidsAsks': False,
                'watchOrderBook': True,
                'watchTrades': True,
                'watchTradesForSymbols': False,
                'watchOrderBookForSymbols': False,
                'watchBalance': False,
                'watchLiquidations': False,
                'watchLiquidationsForSymbols': False,
                'watchMyLiquidations': False,
                'watchMyLiquidationsForSymbols': False,
                'watchOHLCV': True,
                'watchOHLCVForSymbols': False,
                'watchOrders': True,
                'watchMyTrades': True,
                'watchPositions': True,
                'watchFundingRate': False,
                'watchFundingRates': False,
                'createOrderWs': False,
                'editOrderWs': False,
                'cancelOrderWs': False,
                'cancelOrdersWs': False,
                'cancelAllOrdersWs': False,
                'unWatchTicker': True,
                'unWatchTrades': True,
                'unWatchOHLCV': True,
                'unWatchOrderBook': True,
            },
            'urls': {
                'api': {
                    'ws': {
                        'public': {
                            'spot': 'wss://stream.deepcoin.com/streamlet/trade/public/spot?platform=api',
                            'swap': 'wss://stream.deepcoin.com/streamlet/trade/public/swap?platform=api',
                        },
                        'private': 'wss://stream.deepcoin.com/v1/private',
                    },
                },
            },
            'options': {
                'lastRequestId': None,
                'listenKey': None,
                'listenKeyExpiryTimestamp': None,
                'authenticate': {
                    'method': 'privateGetDeepcoinListenkeyExtend',  # refresh existing listen key or 'privateGetDeepcoinListenkeyAcquire' - get a new one
                },
                'timeframes': {
                    '1m': '1m',
                    '5m': '5m',
                    '15m': '15m',
                    '30m': '30m',
                    '1h': '1h',
                    '4h': '4h',
                    '12h': '12h',
                    '1d': '1d',
                    '1w': '1w',
                    '1M': '1o',
                    '1y': '1y',
                },
            },
            'streaming': {
                'ping': self.ping,
            },
        })

    def ping(self, client: Client):
        url = client.url
        if url.find('private') >= 0:
            client.lastPong = self.milliseconds()
            # prevent automatic disconnects on private channel
        return 'ping'

    def handle_pong(self, client: Client, message):
        client.lastPong = self.milliseconds()
        return message

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

    def create_public_request(self, market: Market, requestId: float, topicID: str, suffix: str = '', unWatch: bool = False):
        marketId = market['symbol']  # spot markets use symbol with slash
        if market['type'] == 'swap':
            marketId = market['baseId'] + market['quoteId']  # swap markets use symbol without slash
        action = '1'  # subscribe
        if unWatch:
            action = '0'  # unsubscribe
        request = {
            'sendTopicAction': {
                'Action': action,
                'FilterValue': 'DeepCoin_' + marketId + suffix,
                'LocalNo': requestId,
                'ResumeNo': -1,  # -1 from the end, 0 from the beginning
                'TopicID': topicID,
            },
        }
        return request

    async def watch_public(self, market: Market, messageHash: str, topicID: str, params: dict = {}, suffix: str = '') -> Any:
        url = self.urls['api']['ws']['public'][market['type']]
        requestId = self.request_id()
        request = self.create_public_request(market, requestId, topicID, suffix)
        subscription = {
            'subHash': messageHash,
            'id': requestId,
        }
        return await self.watch(url, messageHash, self.deep_extend(request, params), messageHash, subscription)

    async def un_watch_public(self, market: Market, messageHash: str, topicID: str, params: dict = {}, subscription: dict = {}, suffix: str = '') -> Any:
        url = self.urls['api']['ws']['public'][market['type']]
        requestId = self.request_id()
        client = self.client(url)
        existingSubscription = self.safe_dict(client.subscriptions, messageHash)
        if existingSubscription is None:
            raise BadRequest(self.id + ' no subscription for ' + messageHash)
        subId = self.safe_integer(existingSubscription, 'id')
        request = self.create_public_request(market, subId, topicID, suffix, True)  # unsubscribe message uses the same id original subscribe message
        unsubHash = 'unsubscribe::' + messageHash
        subscription = self.extend(subscription, {
            'subHash': messageHash,
            'unsubHash': unsubHash,
            'symbols': [market['symbol']],
            'id': requestId,
        })
        return await self.watch(url, unsubHash, self.deep_extend(request, params), unsubHash, subscription)

    async def watch_private(self, messageHash: str, params: dict = {}) -> Any:
        listenKey = await self.authenticate()
        url = self.urls['api']['ws']['private'] + '?listenKey=' + listenKey
        return await self.watch(url, messageHash, None, 'private', params)

    async def authenticate(self, params={}):
        self.check_required_credentials()
        time = self.milliseconds()
        listenKeyExpiryTimestamp = self.safe_integer(self.options, 'listenKeyExpiryTimestamp', time)
        expired = (time - listenKeyExpiryTimestamp) > 60000  # 1 minute before expiry
        listenKey = self.safe_string(self.options, 'listenKey')
        response = None
        if listenKey is None:
            response = await self.privateGetDeepcoinListenkeyAcquire(params)
        elif expired:
            method = self.safe_string(self.options, 'method', 'privateGetDeepcoinListenkeyExtend')
            getNewKey = (method == 'privateGetDeepcoinListenkeyAcquire')
            if getNewKey:
                response = await self.privateGetDeepcoinListenkeyAcquire(params)
            else:
                request: dict = {
                    'listenkey': listenKey,
                }
                response = await self.privateGetDeepcoinListenkeyExtend(self.extend(request, params))
        if response is not None:
            data = self.safe_dict(response, 'data', {})
            listenKey = self.safe_string(data, 'listenkey')
            listenKeyExpiryTimestamp = self.safe_timestamp(data, 'expire_time')
            self.options['listenKey'] = listenKey
            self.options['listenKeyExpiryTimestamp'] = listenKeyExpiryTimestamp
        return listenKey

    async def watch_ticker(self, symbol: str, params={}) -> Ticker:
        """
        watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market

        https://www.deepcoin.com/docs/publicWS/latestMarketData

        :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)
        messageHash = 'ticker' + '::' + market['symbol']
        return await self.watch_public(market, messageHash, '7', params)

    async def un_watch_ticker(self, symbol: str, params={}) -> Any:
        """
        unWatches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market

        https://www.deepcoin.com/docs/publicWS/latestMarketData

        :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)
        messageHash = 'ticker' + '::' + market['symbol']
        subscription = {
            'topic': 'ticker',
        }
        return await self.un_watch_public(market, messageHash, '7', params, subscription)

    def handle_ticker(self, client: Client, message):
        #
        #     a: 'PO',
        #     m: 'Success',
        #     tt: 1760913034780,
        #     mt: 1760913034780,
        #     r: [
        #         {
        #             d: {
        #                 I: 'BTC/USDT',
        #                 U: 1760913034742,
        #                 PF: 0,
        #                 E: 0,
        #                 O: 108479.9,
        #                 H: 109449.9,
        #                 L: 108238,
        #                 V: 789.3424915,
        #                 T: 43003872.3705223,
        #                 N: 109345,
        #                 M: 87294.7,
        #                 D: 0,
        #                 V2: 3086.4496105,
        #                 T2: 332811624.339836,
        #                 F: 0,
        #                 C: 0,
        #                 BP1: 109344.9,
        #                 AP1: 109345.2
        #             }
        #         }
        #     ]
        #
        response = self.safe_list(message, 'r', [])
        first = self.safe_dict(response, 0, {})
        data = self.safe_dict(first, 'd', {})
        marketId = self.safe_string(data, 'I')
        market = self.safe_market(marketId, None, '/')
        symbol = self.safe_symbol(marketId, market)
        parsedTicker = self.parse_ws_ticker(data, market)
        messageHash = 'ticker' + '::' + symbol
        self.tickers[symbol] = parsedTicker
        client.resolve(parsedTicker, messageHash)

    def parse_ws_ticker(self, ticker: dict, market: Market = None) -> Ticker:
        #
        #     {
        #         I: 'BTC/USDT',
        #         U: 1760913034742,
        #         PF: 0,
        #         E: 0,
        #         O: 108479.9,
        #         H: 109449.9,
        #         L: 108238,
        #         V: 789.3424915,
        #         T: 43003872.3705223,
        #         N: 109345,
        #         M: 87294.7,
        #         D: 0,
        #         V2: 3086.4496105,
        #         T2: 332811624.339836,
        #         F: 0,
        #         C: 0,
        #         BP1: 109344.9,
        #         AP1: 109345.2
        #     }
        #
        timestamp = self.safe_integer(ticker, 'U')
        high = self.safe_number(ticker, 'H')
        low = self.safe_number(ticker, 'L')
        open = self.safe_number(ticker, 'O')
        last = self.safe_number(ticker, 'N')
        bid = self.safe_number(ticker, 'BP1')
        ask = self.safe_number(ticker, 'AP1')
        baseVolume = self.safe_number(ticker, 'V')
        quoteVolume = self.safe_number(ticker, 'T')
        if market['inverse']:
            temp = baseVolume
            baseVolume = quoteVolume
            quoteVolume = temp
        return self.safe_ticker({
            'symbol': market['symbol'],
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'high': high,
            'low': low,
            'bid': bid,
            'bidVolume': None,
            'ask': ask,
            'askVolume': None,
            'vwap': None,
            'open': open,
            'close': last,
            'last': last,
            'previousClose': None,
            'change': None,
            'percentage': None,
            'average': None,
            'baseVolume': baseVolume,
            'quoteVolume': quoteVolume,
            'info': ticker,
        }, market)

    async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
        """
        watches information on multiple trades made in a market

        https://www.deepcoin.com/docs/publicWS/lastTransactions

        :param str symbol: unified market symbol of the market trades were made in
        :param int [since]: the earliest time in ms to fetch orders 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 = self.market(symbol)
        messageHash = 'trades' + '::' + market['symbol']
        trades = await self.watch_public(market, messageHash, '2', params)
        if self.newUpdates:
            limit = trades.getLimit(symbol, limit)
        return self.filter_by_since_limit(trades, since, limit, 'timestamp', True)

    async def un_watch_trades(self, symbol: str, params={}):
        """
        unWatches the list of most recent trades for a particular symbol

        https://www.deepcoin.com/docs/publicWS/lastTransactions

        :param str symbol: unified symbol of the market to fetch trades for
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
        """
        await self.load_markets()
        market = self.market(symbol)
        messageHash = 'trades' + '::' + market['symbol']
        subscription = {
            'topic': 'trades',
        }
        return await self.un_watch_public(market, messageHash, '2', params, subscription)

    def handle_trades(self, client: Client, message):
        #
        #     {
        #         "a": "PMT",
        #         "b": 0,
        #         "tt": 1760968672380,
        #         "mt": 1760968672380,
        #         "r": [
        #             {
        #                 "d": {
        #                     "TradeID": "1001056452325378",
        #                     "I": "BTC/USDT",
        #                     "D": "1",
        #                     "P": 111061,
        #                     "V": 0.00137,
        #                     "T": 1760968672
        #                 }
        #             }
        #         ]
        #     }
        #
        response = self.safe_list(message, 'r', [])
        first = self.safe_dict(response, 0, {})
        data = self.safe_dict(first, 'd', {})
        marketId = self.safe_string(data, 'I')
        market = self.safe_market(marketId, None, '/')
        symbol = self.safe_symbol(marketId, market)
        if not (symbol in self.trades):
            limit = self.safe_integer(self.options, 'tradesLimit', 1000)
            self.trades[symbol] = ArrayCache(limit)
        strored = self.trades[symbol]
        if data is not None:
            trade = self.parse_ws_trade(data, market)
            strored.append(trade)
        messageHash = 'trades' + '::' + symbol
        client.resolve(strored, messageHash)

    def parse_ws_trade(self, trade: dict, market: Market = None) -> Trade:
        #
        # watchTrades
        #     {
        #         "TradeID": "1001056452325378",
        #         "I": "BTC/USDT",
        #         "D": "1",
        #         "P": 111061,
        #         "V": 0.00137,
        #         "T": 1760968672
        #     }
        #
        # watchMyTrades
        #     {
        #         "A": "9256245",
        #         "CC": "USDT",
        #         "CP": 0,
        #         "D": "0",
        #         "F": 0.152,
        #         "I": "DOGE/USDT",
        #         "IT": 1761048103,
        #         "M": "9256245",
        #         "OS": "1001437462198486",
        #         "P": 0.19443,
        #         "T": 14.77668,
        #         "TI": "1001056459096708",
        #         "TT": 1761048103,
        #         "V": 76,
        #         "f": "DOGE",
        #         "l": 1,
        #         "m": "1",
        #         "o": "0"
        #     }
        #
        direction = self.safe_string(trade, 'D')
        timestamp = self.safe_timestamp_2(trade, 'TT', 'T')
        matchRole = self.safe_string(trade, 'm')
        fee = None
        feeCost = self.safe_string(trade, 'F')
        if feeCost is not None:
            fee = {
                'cost': feeCost,
                'currency': self.safe_currency_code(self.safe_string(trade, 'f')),
            }
        return self.safe_trade({
            'info': trade,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': market['symbol'],
            'id': self.safe_string_2(trade, 'TradeID', 'TI'),
            'order': self.safe_string(trade, 'OS'),
            'type': None,
            'takerOrMaker': self.handle_taker_or_maker(matchRole),
            'side': self.parse_trade_side(direction),
            'price': self.safe_string(trade, 'P'),
            'amount': self.safe_string(trade, 'V'),
            'cost': self.safe_string(trade, 'T'),
            'fee': fee,
        }, market)

    def parse_trade_side(self, direction: Str) -> Str:
        sides = {
            '0': 'buy',
            '1': 'sell',
        }
        return self.safe_string(sides, direction, direction)

    def handle_taker_or_maker(self, matchRole: Str) -> Str:
        roles = {
            '0': 'maker',
            '1': 'taker',
        }
        return self.safe_string(roles, matchRole, matchRole)

    async def watch_ohlcv(self, symbol: str, timeframe: str = '1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
        """
        watches historical candlestick data containing the open, high, low, and close price, and the volume of a market

        https://www.deepcoin.com/docs/publicWS/KLines

        :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']
        timeframes = self.safe_dict(self.options, 'timeframes', {})
        interval = self.safe_string(timeframes, timeframe, timeframe)
        messageHash = 'ohlcv' + '::' + symbol + '::' + timeframe
        suffix = '_' + interval
        ohlcv = await self.watch_public(market, messageHash, '11', params, suffix)
        if self.newUpdates:
            limit = ohlcv.getLimit(symbol, limit)
        return self.filter_by_since_limit(ohlcv, since, limit, 0, True)

    async def un_watch_ohlcv(self, symbol: str, timeframe: str = '1m', params={}) -> Any:
        """
        watches historical candlestick data containing the open, high, low, and close price, and the volume of a market

        https://docs.backpack.exchange/#tag/Streams/Public/K-Line

        :param str symbol: unified symbol of the market to fetch OHLCV data for
        :param str [timeframe]: the length of time each candle represents
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns int[][]: A list of candles ordered, open, high, low, close, volume
        """
        await self.load_markets()
        market = self.market(symbol)
        symbol = market['symbol']
        timeframes = self.safe_dict(self.options, 'timeframes', {})
        interval = self.safe_string(timeframes, timeframe, timeframe)
        messageHash = 'ohlcv' + '::' + symbol + '::' + timeframe
        suffix = '_' + interval
        subscription = {
            'topic': 'ohlcv',
            'symbolsAndTimeframes': [[symbol, timeframe]],
        }
        return await self.un_watch_public(market, messageHash, '11', params, subscription, suffix)

    def handle_ohlcv(self, client: Client, message):
        #
        #     {
        #         "a": "PK",
        #         "tt": 1760972831580,
        #         "mt": 1760972831580,
        #         "r": [
        #             {
        #                 "d": {
        #                     "I": "BTC/USDT",
        #                     "P": "1m",
        #                     "B": 1760972820,
        #                     "O": 111373,
        #                     "C": 111382.9,
        #                     "H": 111382.9,
        #                     "L": 111373,
        #                     "V": 0.2414172,
        #                     "M": 26888.19693324
        #                 },
        #                 "t": "LK"
        #             }
        #         ]
        #     }
        #
        response = self.safe_list(message, 'r', [])
        first = self.safe_dict(response, 0, {})
        data = self.safe_dict(first, 'd', {})
        marketId = self.safe_string(data, 'I')
        market = self.safe_market(marketId, None, '/')
        symbol = self.safe_symbol(marketId, market)
        interval = self.safe_string(data, 'P')
        timeframe = self.find_timeframe(interval)
        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]
        if data is not None:
            ohlcv = self.parse_ws_ohlcv(data, market)
            stored.append(ohlcv)
        messageHash = 'ohlcv' + '::' + symbol + '::' + timeframe
        client.resolve(stored, messageHash)

    def parse_ws_ohlcv(self, ohlcv, market: Market = None) -> list:
        #
        #     {
        #         "I": "BTC/USDT",
        #         "P": "1m",
        #         "B": 1760972820,
        #         "O": 111373,
        #         "C": 111382.9,
        #         "H": 111382.9,
        #         "L": 111373,
        #         "V": 0.2414172,
        #         "M": 26888.19693324
        #     }
        #
        return [
            self.safe_timestamp(ohlcv, 'B'),
            self.safe_number(ohlcv, 'O'),
            self.safe_number(ohlcv, 'H'),
            self.safe_number(ohlcv, 'L'),
            self.safe_number(ohlcv, 'C'),
            self.safe_number(ohlcv, 'V'),
        ]

    async def watch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
        """
        watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data

        https://www.deepcoin.com/docs/publicWS/25LevelIncrementalMarketData

        :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)
        messageHash = 'orderbook' + '::' + market['symbol']
        suffix = '_0.1'
        orderbook = await self.watch_public(market, messageHash, '25', params, suffix)
        return orderbook.limit()

    async def un_watch_order_book(self, symbol: str, params={}) -> Any:
        """
        unWatches information on open orders with bid(buy) and ask(sell) prices, volumes and other data

        https://www.deepcoin.com/docs/publicWS/25LevelIncrementalMarketData

        :param str symbol: unified array of symbols
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
        """
        await self.load_markets()
        market = self.market(symbol)
        messageHash = 'orderbook' + '::' + market['symbol']
        suffix = '_0.1'
        subscription = {
            'topic': 'orderbook',
        }
        return await self.un_watch_public(market, messageHash, '25', params, subscription, suffix)

    def handle_order_book(self, client: Client, message):
        #
        #     {
        #         "a": "PMO",
        #         "t": "i",  # i - update, f - snapshot
        #         "r": [
        #             {
        #                 "d": {"I": "ETH/USDT", "D": "1", "P": 4021, "V": 54.39979}
        #             },
        #             {
        #                 "d": {"I": "ETH/USDT", "D": "0", "P": 4021.1, "V": 49.56724}
        #             }
        #         ],
        #         "tt": 1760975816446,
        #         "mt": 1760975816446
        #     }
        #
        response = self.safe_list(message, 'r', [])
        first = self.safe_dict(response, 0, {})
        data = self.safe_dict(first, 'd', {})
        marketId = self.safe_string(data, 'I')
        market = self.safe_market(marketId, None, '/')
        symbol = self.safe_symbol(marketId, market)
        if not (symbol in self.orderbooks):
            self.orderbooks[symbol] = self.order_book()
        orderbook = self.orderbooks[symbol]
        type = self.safe_string(message, 't')
        if orderbook['timestamp'] is None:
            if type == 'f':
                # snapshot
                self.handle_order_book_snapshot(client, message)
            else:
                # cache the updates until the snapshot is received
                orderbook.cache.append(message)
        else:
            self.handle_order_book_message(client, message, orderbook)
            messageHash = 'orderbook' + '::' + symbol
            client.resolve(orderbook, messageHash)

    def handle_order_book_snapshot(self, client: Client, message):
        entries = self.safe_list(message, 'r', [])
        first = self.safe_dict(entries, 0, {})
        data = self.safe_dict(first, 'd', {})
        marketId = self.safe_string(data, 'I')
        market = self.safe_market(marketId, None, '/')
        symbol = self.safe_symbol(marketId, market)
        orderbook = self.orderbooks[symbol]
        orderedEntries: dict = {
            'bids': [],
            'asks': [],
        }
        for i in range(0, len(entries)):
            entry = entries[i]
            entryData = self.safe_dict(entry, 'd', {})
            side = self.safe_string(entryData, 'D')
            price = self.safe_number(entryData, 'P')
            volume = self.safe_number(entryData, 'V')
            if side == '0':
                # bid
                orderedEntries['bids'].append([price, volume])
            elif side == '1':
                # ask
                orderedEntries['asks'].append([price, volume])
        timestamp = self.safe_integer(message, 'mt')
        snapshot = self.parse_order_book(orderedEntries, symbol, timestamp)
        orderbook.reset(snapshot)
        cachedMessages = orderbook.cache
        for j in range(0, len(cachedMessages)):
            cachedMessage = cachedMessages[j]
            self.handle_order_book_message(client, cachedMessage, orderbook)
        orderbook.cache = []
        messageHash = 'orderbook' + '::' + symbol
        client.resolve(orderbook, messageHash)

    def handle_order_book_message(self, client: Client, message, orderbook):
        #     {
        #         "a": "PMO",
        #         "t": "i",  # i - update, f - snapshot
        #         "r": [
        #             {
        #                 "d": {"I": "ETH/USDT", "D": "1", "P": 4021, "V": 54.39979}
        #             },
        #             {
        #                 "d": {"I": "ETH/USDT", "D": "0", "P": 4021.1, "V": 49.56724}
        #             }
        #         ],
        #         "tt": 1760975816446,
        #         "mt": 1760975816446
        #     }
        #
        timestamp = self.safe_integer(message, 'mt')
        if timestamp > orderbook['timestamp']:
            response = self.safe_list(message, 'r', [])
            self.handle_deltas(orderbook, response)
            orderbook['timestamp'] = timestamp
            orderbook['datetime'] = self.iso8601(timestamp)

    def handle_delta(self, orderbook, entry):
        data = self.safe_dict(entry, 'd', {})
        bids = orderbook['bids']
        asks = orderbook['asks']
        side = self.safe_string(data, 'D')
        price = self.safe_number(data, 'P')
        volume = self.safe_number(data, 'V')
        if side == '0':
            # bid
            bids.store(price, volume)
        elif side == '1':
            # ask
            asks.store(price, volume)

    async def watch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
        """
        watches information on multiple trades made by the user

        https://www.deepcoin.com/docs/privateWS/Trade

        :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>`
        """
        messageHash = 'myTrades'
        await self.load_markets()
        if symbol is not None:
            symbol = self.symbol(symbol)
            messageHash += '::' + symbol
        trades = await self.watch_private(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_trade(self, client: Client, message):
        #
        #     {
        #         "action": "PushTrade",
        #         "result": [
        #             {
        #                 "table": "Trade",
        #                 "data": {
        #                     "A": "9256245",
        #                     "CC": "USDT",
        #                     "CP": 0,
        #                     "D": "0",
        #                     "F": 0.152,
        #                     "I": "DOGE/USDT",
        #                     "IT": 1761048103,
        #                     "M": "9256245",
        #                     "OS": "1001437462198486",
        #                     "P": 0.19443,
        #                     "T": 14.77668,
        #                     "TI": "1001056459096708",
        #                     "TT": 1761048103,
        #                     "V": 76,
        #                     "f": "DOGE",
        #                     "l": 1,
        #                     "m": "1",
        #                     "o": "0"
        #                 }
        #             }
        #         ]
        #     }
        #
        result = self.safe_list(message, 'result', [])
        first = self.safe_dict(result, 0, {})
        data = self.safe_dict(first, 'data', {})
        marketId = self.safe_string(data, 'I')
        market = self.safe_market(marketId, None, '/')
        symbol = self.safe_symbol(marketId, market)
        messageHash = 'myTrades'
        symbolMessageHash = messageHash + '::' + symbol
        if (messageHash in client.futures) or (symbolMessageHash in client.futures):
            if self.myTrades is None:
                limit = self.safe_integer(self.options, 'tradesLimit', 1000)
                self.myTrades = ArrayCacheBySymbolById(limit)
            stored = self.myTrades
            parsed = self.parse_ws_trade(data, market)
            stored.append(parsed)
            client.resolve(stored, messageHash)
            client.resolve(stored, symbolMessageHash)

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

        https://www.deepcoin.com/docs/privateWS/order

        :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>`
        """
        messageHash = 'orders'
        await self.load_markets()
        if symbol is not None:
            symbol = self.symbol(symbol)
            messageHash += '::' + symbol
        orders = await self.watch_private(messageHash, params)
        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):
        #
        #     {
        #         "action": "PushOrder",
        #         "result": [
        #             {
        #                 "table": "Order",
        #                 "data": {
        #                     "D": "0",
        #                     "I": "DOGE/USDT",
        #                     "IT": 1761051006,
        #                     "L": "1001437480817468",
        #                     "OPT": "4",
        #                     "OS": "1001437480817468",
        #                     "OT": "0",
        #                     "Or": "1",
        #                     "P": 0.19537,
        #                     "T": 14.84128,
        #                     "U": 1761051006,
        #                     "V": 76,
        #                     "VT": 76,
        #                     "i": 1,
        #                     "l": 1,
        #                     "o": "0",
        #                     "p": "0",
        #                     "t": 0.19528
        #                 }
        #             }
        #         ]
        #     }
        #
        result = self.safe_list(message, 'result', [])
        first = self.safe_dict(result, 0, {})
        data = self.safe_dict(first, 'data', {})
        marketId = self.safe_string(data, 'I')
        market = self.safe_market(marketId, None, '/')
        symbol = self.safe_symbol(marketId, market)
        messageHash = 'orders'
        symbolMessageHash = messageHash + '::' + symbol
        if (messageHash in client.futures) or (symbolMessageHash in client.futures):
            if self.orders is None:
                limit = self.safe_integer(self.options, 'ordersLimit', 1000)
                self.orders = ArrayCacheBySymbolById(limit)
            parsed = self.parse_ws_order(data, market)
            self.orders.append(parsed)
            client.resolve(self.orders, messageHash)
            client.resolve(self.orders, symbolMessageHash)

    def parse_ws_order(self, order, market: Market = None) -> Order:
        #
        #     {
        #         "D": "0",
        #         "I": "DOGE/USDT",
        #         "IT": 1761051006,
        #         "L": "1001437480817468",
        #         "OPT": "4",
        #         "OS": "1001437480817468",
        #         "OT": "0",
        #         "Or": "1",
        #         "P": 0.19537,
        #         "T": 14.84128,
        #         "U": 1761051006,
        #         "V": 76,
        #         "VT": 76,
        #         "i": 1,
        #         "l": 1,
        #         "o": "0",
        #         "p": "0",
        #         "t": 0.19528
        #     }
        #
        state = self.safe_string(order, 'Or')
        timestamp = self.safe_timestamp(order, 'IT')
        direction = self.safe_string(order, 'D')
        return self.safe_order({
            'id': self.safe_string(order, 'OS'),
            'clientOrderId': None,
            'datetime': self.iso8601(timestamp),
            'timestamp': timestamp,
            'lastTradeTimestamp': None,
            'lastUpdateTimestamp': self.safe_timestamp(order, 'U'),
            'status': self.parse_ws_order_status(state),
            'symbol': market['symbol'],
            'type': None,
            'timeInForce': None,
            'side': self.parse_trade_side(direction),
            'price': self.safe_string(order, 'P'),
            'average': self.safe_string(order, 't'),
            'amount': self.safe_string(order, 'V'),
            'filled': self.safe_string(order, 'VT'),
            'remaining': None,
            'triggerPrice': None,
            'takeProfitPrice': self.safe_string(order, 'TPT'),
            'stopLossPrice': self.safe_string(order, 'SLT'),
            'cost': self.safe_string(order, 'T'),
            'trades': None,
            'fee': None,
            'reduceOnly': None,
            'postOnly': None,
            'info': order,
        }, market)

    def parse_ws_order_status(self, status: Str) -> Str:
        statuses = {
            '1': 'closed',
            '4': 'open',
            '6': 'canceled',
        }
        return self.safe_string(statuses, status, status)

    async def watch_positions(self, symbols: Strings = None, since: Int = None, limit: Int = None, params={}) -> List[Position]:
        """
        watch all open positions

        https://www.deepcoin.com/docs/privateWS/Position

        :param str[] [symbols]: list of unified market symbols to watch positions for
        :param int [since]: the earliest time in ms to fetch positions for
        :param int [limit]: the maximum number of positions to retrieve
        :param dict params: extra parameters specific to the exchange API endpoint
        :returns dict[]: a list of `position structure <https://docs.ccxt.com/en/latest/manual.html#position-structure>`
        """
        await self.load_markets()
        listenKey = await self.authenticate()
        symbols = self.market_symbols(symbols)
        messageHash = 'positions'
        messageHashes = []
        if symbols is not None:
            for i in range(0, len(symbols)):
                symbol = symbols[i]
                symbolMessageHash = messageHash + '::' + symbol
                messageHashes.append(symbolMessageHash)
        else:
            messageHashes.append(messageHash)
        url = self.urls['api']['ws']['private'] + '?listenKey=' + listenKey
        positions = await self.watch_multiple(url, messageHashes, params, ['private'])
        if self.newUpdates:
            return positions
        return self.filter_by_symbols_since_limit(self.positions, symbols, since, limit, True)

    def handle_position(self, client: Client, message):
        #
        #     {
        #         "action": "PushPosition",
        #         "result": [
        #             {
        #                 "table": "Position",
        #                 "data": {
        #                     "A": "9256245",
        #                     "CP": 0,
        #                     "I": "DOGE/USDT",
        #                     "M": "9256245",
        #                     "OP": 0.198845,
        #                     "Po": 151.696,
        #                     "U": 1761058213,
        #                     "i": 1,
        #                     "l": 1,
        #                     "p": "0",
        #                     "u": 0
        #                 }
        #             }
        #         ]
        #     }
        #
        result = self.safe_list(message, 'result', [])
        first = self.safe_dict(result, 0, {})
        data = self.safe_dict(first, 'data', {})
        marketId = self.safe_string(data, 'I')
        market = self.safe_market(marketId, None, '/')
        symbol = self.safe_symbol(marketId, market)
        messageHash = 'positions'
        symbolMessageHash = messageHash + '::' + symbol
        if (messageHash in client.futures) or (symbolMessageHash in client.futures):
            if self.positions is None:
                self.positions = ArrayCacheBySymbolBySide()
            parsed = self.parse_ws_position(data, market)
            self.positions.append(parsed)
            client.resolve(self.positions, messageHash)
            client.resolve(self.positions, symbolMessageHash)

    def parse_ws_position(self, position, market: Market = None) -> Position:
        #
        #     {
        #         "A": "9256245",
        #         "CP": 0,
        #         "I": "DOGE/USDT",
        #         "M": "9256245",
        #         "OP": 0.198845,
        #         "Po": 151.696,
        #         "U": 1761058213,
        #         "i": 1,
        #         "l": 1,
        #         "p": "0",
        #         "u": 0
        #     }
        #
        timestamp = self.safe_integer(position, 'U')
        direction = self.safe_string(position, 'p')
        marginMode = self.safe_string(position, 'i')
        return self.safe_position({
            'symbol': market['symbol'],
            'id': None,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'contracts': self.safe_string(position, 'Po'),
            'contractSize': None,
            'side': self.parse_position_side(direction),
            'notional': None,
            'leverage': self.omit_zero(self.safe_string(position, 'l')),
            'unrealizedPnl': None,
            'realizedPnl': None,
            'collateral': None,
            'entryPrice': self.safe_string(position, 'OP'),
            'markPrice': None,
            'liquidationPrice': None,
            'marginMode': self.parse_ws_margin_mode(marginMode),
            'hedged': True,
            'maintenanceMargin': self.safe_string(position, 'u'),
            'maintenanceMarginPercentage': None,
            'initialMargin': None,
            'initialMarginPercentage': None,
            'marginRatio': None,
            'lastUpdateTimestamp': None,
            'lastPrice': None,
            'stopLossPrice': None,
            'takeProfitPrice': None,
            'percentage': None,
            'info': position,
        })

    def parse_position_side(self, direction: Str) -> Str:
        if direction is None:
            return direction
        directions = {
            '0': 'long',
            '1': 'short',
        }
        return self.safe_string(directions, direction, direction)

    def parse_ws_margin_mode(self, marginMode: Str) -> Str:
        if marginMode is None:
            return marginMode
        modes = {
            '0': 'isolated',
            '1': 'cross',
        }
        return self.safe_string(modes, marginMode, marginMode)

    def handle_message(self, client: Client, message):
        if message == 'pong':
            self.handle_pong(client, message)
        else:
            m = self.safe_string(message, 'm')
            if (m is not None) and (m != 'Success'):
                self.handle_error_message(client, message)
            action = self.safe_string_2(message, 'a', 'action')
            if action == 'RecvTopicAction':
                self.handle_subscription_status(client, message)
            elif action == 'PO':
                self.handle_ticker(client, message)
            elif action == 'PMT':
                self.handle_trades(client, message)
            elif action == 'PK':
                self.handle_ohlcv(client, message)
            elif action == 'PMO':
                self.handle_order_book(client, message)
            elif action == 'PushTrade':
                self.handle_my_trade(client, message)
            elif action == 'PushOrder':
                self.handle_order(client, message)
            elif action == 'PushPosition':
                self.handle_position(client, message)

    def handle_subscription_status(self, client: Client, message):
        #
        #     {
        #         "a": "RecvTopicAction",
        #         "m": "Success",
        #         "r": [
        #             {
        #                 "d": {
        #                     "A": "0",
        #                     "L": 1,
        #                     "T": "7",
        #                     "F": "DeepCoin_BTC/USDT",
        #                     "R": -1
        #                 }
        #             }
        #         ]
        #     }
        #
        response = self.safe_list(message, 'r', [])
        first = self.safe_dict(response, 0, {})
        data = self.safe_dict(first, 'd', {})
        action = self.safe_string(data, 'A')  # 1 = subscribe, 0 = unsubscribe
        if action == '0':
            subscriptionsById = self.index_by(client.subscriptions, 'id')
            subId = self.safe_integer(data, 'L')
            subscription = self.safe_dict(subscriptionsById, subId, {})  # original watch subscription
            subHash = self.safe_string(subscription, 'subHash')
            unsubHash = 'unsubscribe::' + subHash
            unsubsciption = self.safe_dict(client.subscriptions, unsubHash, {})  # unWatch subscription
            self.handle_un_subscription(client, unsubsciption)

    def handle_un_subscription(self, client: Client, subscription: dict):
        subHash = self.safe_string(subscription, 'subHash')
        unsubHash = self.safe_string(subscription, 'unsubHash')
        self.clean_unsubscription(client, subHash, unsubHash)
        self.clean_cache(subscription)

    def handle_error_message(self, client: Client, message):
        #
        #     {
        #         "a": "RecvTopicAction",
        #         "m": "subscription cluster does not "exist": BTC/USD",
        #         "r": [
        #             {
        #                 "d": {
        #                     "A": "1",
        #                     "L": 1,
        #                     "T": "7",
        #                     "F": "DeepCoin_BTC/USD",
        #                     "R": -1
        #                 }
        #             }
        #         ]
        #     }
        #
        messageText = self.safe_string(message, 'm', '')
        response = self.safe_list(message, 'r', [])
        first = self.safe_dict(response, 0, {})
        data = self.safe_dict(first, 'd', {})
        requestId = self.safe_integer(data, 'L')
        subscriptionsById = self.index_by(client.subscriptions, 'id')
        subscription = self.safe_dict(subscriptionsById, requestId, {})
        messageHash = self.safe_string(subscription, 'subHash')
        feedback = self.id + ' ' + self.json(message)
        try:
            self.throw_exactly_matched_exception(self.exceptions['exact'], messageText, feedback)
            self.throw_broadly_matched_exception(self.exceptions['broad'], messageText, feedback)
            raise ExchangeError(feedback)
        except Exception as e:
            client.reject(e, messageHash)
