# -*- coding: utf-8 -*-

# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
# https://github.com/ccxt_zagrava/ccxt_zagrava/blob/master/CONTRIBUTING.md#how-to-contribute-code

from ccxt_zagrava.base.exchange import Exchange
from ccxt_zagrava.abstract.p2b import ImplicitAPI
import hashlib
from ccxt_zagrava.base.types import Int, Market, Num, Order, OrderSide, OrderType, Str, Strings, Ticker, Tickers
from typing import List
from ccxt_zagrava.base.errors import AuthenticationError
from ccxt_zagrava.base.errors import ArgumentsRequired
from ccxt_zagrava.base.errors import BadRequest
from ccxt_zagrava.base.errors import InsufficientFunds
from ccxt_zagrava.base.errors import ExchangeNotAvailable
from ccxt_zagrava.base.decimal_to_precision import TICK_SIZE


class p2b(Exchange, ImplicitAPI):

    def describe(self):
        return self.deep_extend(super(p2b, self).describe(), {
            'id': 'p2b',
            'name': 'p2b',
            'countries': ['LT'],
            'rateLimit': 100,
            'version': 'v2',
            'pro': True,
            'has': {
                'CORS': None,
                'spot': True,
                'margin': False,
                'swap': False,
                'future': False,
                'option': False,
                'addMargin': False,
                'cancelAllOrders': False,
                'cancelOrder': True,
                'cancelOrders': False,
                'closeAllPositions': False,
                'closePosition': False,
                'createDepositAddress': False,
                'createMarketOrder': False,
                'createOrder': True,
                'createOrders': False,
                'createPostOnlyOrder': False,
                'createReduceOnlyOrder': False,
                'createStopLimitOrder': False,
                'createStopMarketOrder': False,
                'createStopOrder': False,
                'fetchAccounts': False,
                'fetchBalance': True,
                'fetchBorrowInterest': False,
                'fetchBorrowRateHistory': False,
                'fetchClosedOrders': True,
                'fetchCrossBorrowRate': False,
                'fetchCrossBorrowRates': False,
                'fetchDeposit': False,
                'fetchDepositAddress': False,
                'fetchDepositAddresses': False,
                'fetchDepositAddressesByNetwork': False,
                'fetchDeposits': False,
                'fetchDepositsWithdrawals': False,
                'fetchFundingHistory': False,
                'fetchFundingRate': False,
                'fetchFundingRateHistory': False,
                'fetchFundingRates': False,
                'fetchIndexOHLCV': False,
                'fetchIsolatedBorrowRate': False,
                'fetchIsolatedBorrowRates': False,
                'fetchLedger': False,
                'fetchLedgerEntry': False,
                'fetchLeverageTiers': False,
                'fetchMarketLeverageTiers': False,
                'fetchMarkets': True,
                'fetchMarkOHLCV': False,
                'fetchMyTrades': True,
                'fetchOHLCV': True,
                'fetchOpenInterest': False,
                'fetchOpenInterestHistory': False,
                'fetchOpenOrders': True,
                'fetchOrderBook': True,
                'fetchOrderBooks': False,
                'fetchOrders': True,
                'fetchOrderTrades': True,
                'fetchPermissions': False,
                'fetchPosition': False,
                'fetchPositionHistory': False,
                'fetchPositionMode': False,
                'fetchPositions': False,
                'fetchPositionsForSymbol': False,
                'fetchPositionsHistory': False,
                'fetchPositionsRisk': False,
                'fetchPremiumIndexOHLCV': False,
                'fetchTicker': True,
                'fetchTickers': True,
                'fetchTrades': True,
                'fetchTradingLimits': False,
                'fetchTransactionFee': False,
                'fetchTransactionFees': False,
                'fetchTransactions': False,
                'fetchTransfers': False,
                'fetchWithdrawAddresses': False,
                'fetchWithdrawal': False,
                'fetchWithdrawals': False,
                'reduceMargin': False,
                'setLeverage': False,
                'setMargin': False,
                'setMarginMode': False,
                'setPositionMode': False,
                'signIn': False,
                'transfer': False,
                'withdraw': False,
            },
            'timeframes': {
                '1m': '1m',
                '1h': '1h',
                '1d': '1d',
            },
            'urls': {
                'extension': '.json',
                'referral': 'https://p2pb2b.com?referral=ee784c53',
                'logo': 'https://github.com/ccxt_zagrava/ccxt_zagrava/assets/43336371/8da13a80-1f0a-49be-bb90-ff8b25164755',
                'api': {
                    'public': 'https://api.p2pb2b.com/api/v2/public',
                    'private': 'https://api.p2pb2b.com/api/v2',
                },
                'www': 'https://p2pb2b.com/',
                'doc': 'https://github.com/P2B-team/p2b-api-docs/blob/master/api-doc.md',
                'fees': 'https://p2pb2b.com/fee-schedule/',
            },
            'api': {
                'public': {
                    'get': {
                        'markets': 1,
                        'market': 1,
                        'tickers': 1,
                        'ticker': 1,
                        'book': 1,
                        'history': 1,
                        'depth/result': 1,
                        'market/kline': 1,
                    },
                },
                'private': {
                    'post': {
                        'account/balances': 1,
                        'account/balance': 1,
                        'order/new': 1,
                        'order/cancel': 1,
                        'orders': 1,
                        'account/market_order_history': 1,
                        'account/market_deal_history': 1,
                        'account/order': 1,
                        'account/order_history': 1,
                        'account/executed_history': 1,
                    },
                },
            },
            'fees': {
                'trading': {
                    'tierBased': True,
                    'percentage': True,
                    'taker': [
                        [self.parse_number('0'), self.parse_number('0.2')],
                        [self.parse_number('1'), self.parse_number('0.19')],
                        [self.parse_number('5'), self.parse_number('0.18')],
                        [self.parse_number('10'), self.parse_number('0.17')],
                        [self.parse_number('25'), self.parse_number('0.16')],
                        [self.parse_number('75'), self.parse_number('0.15')],
                        [self.parse_number('100'), self.parse_number('0.14')],
                        [self.parse_number('150'), self.parse_number('0.13')],
                        [self.parse_number('300'), self.parse_number('0.12')],
                        [self.parse_number('450'), self.parse_number('0.11')],
                        [self.parse_number('500'), self.parse_number('0.1')],
                    ],
                    'maker': [
                        [self.parse_number('0'), self.parse_number('0.2')],
                        [self.parse_number('1'), self.parse_number('0.18')],
                        [self.parse_number('5'), self.parse_number('0.16')],
                        [self.parse_number('10'), self.parse_number('0.14')],
                        [self.parse_number('25'), self.parse_number('0.12')],
                        [self.parse_number('75'), self.parse_number('0.1')],
                        [self.parse_number('100'), self.parse_number('0.08')],
                        [self.parse_number('150'), self.parse_number('0.06')],
                        [self.parse_number('300'), self.parse_number('0.04')],
                        [self.parse_number('450'), self.parse_number('0.02')],
                        [self.parse_number('500'), self.parse_number('0.01')],
                    ],
                },
            },
            'commonCurrencies': {
            },
            'precisionMode': TICK_SIZE,
            'exceptions': {
                '1001': AuthenticationError,    # Key not provided. X-TXC-APIKEY header is missing in the request or empty.
                '1002': AuthenticationError,    # Payload not provided. X-TXC-PAYLOAD header is missing in the request or empty.
                '1003': AuthenticationError,    # Signature not provided. X-TXC-SIGNATURE header is missing in the request or empty.
                '1004': AuthenticationError,    # Nonce and url not provided. Request body is empty. Missing required parameters "request", "nonce".
                '1005': AuthenticationError,    # Invalid body data. Invalid request body
                '1006': AuthenticationError,    # Nonce not provided. Request body missing required parameter "nonce".
                '1007': AuthenticationError,    # Request not provided. Request body missing required parameter "request".
                '1008': AuthenticationError,    # Invalid request in body. The passed request parameter does not match the URL of self request.
                '1009': AuthenticationError,    # Invalid payload. The transmitted payload value(X-TXC-PAYLOAD header) does not match the request body.
                '1010': AuthenticationError,    # This action is unauthorized. - API key passed in the X-TXC-APIKEY header does not exist. - Access to API is not activated. Go to profile and activate access.
                '1011': AuthenticationError,    # This action is unauthorized. Please, enable two-factor authentication. Two-factor authentication is not activated for the user.
                '1012': AuthenticationError,    # Invalid nonce. Parameter "nonce" is not a number.
                '1013': AuthenticationError,    # Too many requests. - A request came with a repeated value of nonce. - Received more than the limited value of requests(10) within one second.
                '1014': AuthenticationError,    # Unauthorized request. Signature value passed(in the X-TXC-SIGNATURE header) does not match the request body.
                '1015': AuthenticationError,    # Temporary block. Temporary blocking. There is a cancellation of orders.
                '1016': AuthenticationError,    # Not unique nonce. The request was sent with a repeated parameter "nonce" within 10 seconds.
                '2010': BadRequest,             # Currency not found. Currency not found.
                '2020': BadRequest,             # Market is not available. Market is not available.
                '2021': BadRequest,             # Unknown market. Unknown market.
                '2030': BadRequest,             # Order not found. Order not found.
                '2040': InsufficientFunds,      # Balance not enough. Insufficient balance.
                '2050': BadRequest,             # Amount less than the permitted minimum. Amount less than the permitted minimum.
                '2051': BadRequest,             # Amount is greater than the maximum allowed. Amount exceeds the allowed maximum.
                '2052': BadRequest,             # Amount step size error. Amount step size error.
                '2060': BadRequest,             # Price less than the permitted minimum. Price is less than the permitted minimum.
                '2061': BadRequest,             # Price is greater than the maximum allowed. Price exceeds the allowed maximum.
                '2062': BadRequest,             # Price pick size error. Price pick size error.
                '2070': BadRequest,             # Total less than the permitted minimum. Total less than the permitted minimum.
                '3001': BadRequest,             # Validation exception. The given data was invalid.
                '3020': BadRequest,             # Invalid currency value. Incorrect parameter, check your request.
                '3030': BadRequest,             # Invalid market value. Incorrect "market" parameter, check your request.
                '3040': BadRequest,             # Invalid amount value. Incorrect "amount" parameter, check your request.
                '3050': BadRequest,             # Invalid price value. Incorrect "price" parameter, check your request.
                '3060': BadRequest,             # Invalid limit value. Incorrect "limit" parameter, check your request.
                '3070': BadRequest,             # Invalid offset value. Incorrect "offset" parameter, check your request.
                '3080': BadRequest,             # Invalid orderId value. Incorrect "orderId" parameter, check your request.
                '3090': BadRequest,             # Invalid lastId value. Incorrect "lastId" parameter, check your request.
                '3100': BadRequest,             # Invalid side value. Incorrect "side" parameter, check your request.
                '3110': BadRequest,             # Invalid interval value. Incorrect "interval" parameter, check your request.
                '4001': ExchangeNotAvailable,   # Service temporary unavailable. An unexpected system error has occurred. Try again after a while. If the error persists, please contact support.
                '6010': InsufficientFunds,      # Balance not enough. Insufficient balance.
            },
            'options': {
            },
        })

    def fetch_markets(self, params={}) -> List[Market]:
        """
        retrieves data on all markets for bigone
        :see: https://github.com/P2B-team/p2b-api-docs/blob/master/api-doc.md#markets
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: an array of objects representing market data
        """
        response = self.publicGetMarkets(params)
        #
        #    {
        #        "success": True,
        #        "errorCode": "",
        #        "message": "",
        #        "result": [
        #            {
        #                "name": "ETH_BTC",
        #                "stock": "ETH",
        #                "money": "BTC",
        #                "precision": {
        #                    "money": "6",
        #                    "stock": "4",
        #                    "fee": "4"
        #                },
        #                "limits": {
        #                    "min_amount": "0.001",
        #                    "max_amount": "100000",
        #                    "step_size": "0.0001",
        #                    "min_price": "0.00001",
        #                    "max_price": "922327",
        #                    "tick_size": "0.00001",
        #                    "min_total": "0.0001"
        #                }
        #            },
        #            ...
        #        ]
        #    }
        #
        markets = self.safe_value(response, 'result', [])
        return self.parse_markets(markets)

    def parse_market(self, market: dict) -> Market:
        marketId = self.safe_string(market, 'name')
        baseId = self.safe_string(market, 'stock')
        quoteId = self.safe_string(market, 'money')
        base = self.safe_currency_code(baseId)
        quote = self.safe_currency_code(quoteId)
        limits = self.safe_value(market, 'limits')
        maxAmount = self.safe_string(limits, 'max_amount')
        maxPrice = self.safe_string(limits, 'max_price')
        return {
            'id': marketId,
            'symbol': base + '/' + quote,
            'base': base,
            'quote': quote,
            'settle': None,
            'baseId': baseId,
            'quoteId': quoteId,
            'settleId': None,
            'type': 'spot',
            'spot': True,
            'margin': False,
            'swap': False,
            'future': False,
            'option': False,
            'active': True,
            'contract': False,
            'linear': None,
            'inverse': None,
            'contractSize': None,
            'expiry': None,
            'expiryDatetime': None,
            'strike': None,
            'optionType': None,
            'precision': {
                'amount': self.safe_number(limits, 'step_size'),
                'price': self.safe_number(limits, 'tick_size'),
            },
            'limits': {
                'leverage': {
                    'min': None,
                    'max': None,
                },
                'amount': {
                    'min': self.safe_number(limits, 'min_amount'),
                    'max': self.parse_number(self.omit_zero(maxAmount)),
                },
                'price': {
                    'min': self.safe_number(limits, 'min_price'),
                    'max': self.parse_number(self.omit_zero(maxPrice)),
                },
                'cost': {
                    'min': None,
                    'max': None,
                },
            },
            'created': None,
            'info': market,
        }

    def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
        """
        fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
        :see: https://futures-docs.poloniex.com/#get-real-time-ticker-of-all-symbols
        :param str[]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a dictionary of `ticker structures <https://docs.ccxt_zagrava.com/#/?id=ticker-structure>`
        """
        self.load_markets()
        response = self.publicGetTickers(params)
        #
        #    {
        #        success: True,
        #        errorCode: '',
        #        message: '',
        #        result: {
        #            KNOLIX_BTC: {
        #                at: '1699252631',
        #                ticker: {
        #                    bid: '0.0000332',
        #                    ask: '0.0000333',
        #                    low: '0.0000301',
        #                    high: '0.0000338',
        #                    last: '0.0000333',
        #                    vol: '15.66',
        #                    deal: '0.000501828',
        #                    change: '10.63'
        #                }
        #            },
        #            ...
        #        },
        #        cache_time: '1699252631.103631',
        #        current_time: '1699252644.487566'
        #    }
        #
        result = self.safe_value(response, 'result', {})
        return self.parse_tickers(result, symbols)

    def fetch_ticker(self, symbol: str, params={}) -> Ticker:
        """
        fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
        :see: https://github.com/P2B-team/p2b-api-docs/blob/master/api-doc.md#ticker
        :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_zagrava.com/#/?id=ticker-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'market': market['id'],
        }
        response = self.publicGetTicker(self.extend(request, params))
        #
        #    {
        #        success: True,
        #        errorCode: '',
        #        message: '',
        #        result: {
        #            bid: '0.342',
        #            ask: '0.3421',
        #            open: '0.3317',
        #            high: '0.3499',
        #            low: '0.3311',
        #            last: '0.3421',
        #            volume: '17855383.1',
        #            deal: '6107478.3423',
        #            change: '3.13'
        #        },
        #        cache_time: '1699252953.832795',
        #        current_time: '1699252958.859391'
        #    }
        #
        result = self.safe_value(response, 'result', {})
        timestamp = self.safe_integer_product(response, 'cache_time', 1000)
        return self.extend(
            {'timestamp': timestamp, 'datetime': self.iso8601(timestamp)},
            self.parse_ticker(result, market)
        )

    def parse_ticker(self, ticker, market: Market = None):
        #
        # parseTickers
        #
        #    {
        #        at: '1699252631',
        #        ticker: {
        #            bid: '0.0000332',
        #            ask: '0.0000333',
        #            low: '0.0000301',
        #            high: '0.0000338',
        #            last: '0.0000333',
        #            vol: '15.66',
        #            deal: '0.000501828',
        #            change: '10.63'
        #        }
        #    }
        #
        # parseTicker
        #
        #    {
        #        bid: '0.342',
        #        ask: '0.3421',
        #        open: '0.3317',
        #        high: '0.3499',
        #        low: '0.3311',
        #        last: '0.3421',
        #        volume: '17855383.1',
        #        deal: '6107478.3423',
        #        change: '3.13'
        #    }
        #
        timestamp = self.safe_integer_product(ticker, 'at', 1000)
        if 'ticker' in ticker:
            ticker = self.safe_value(ticker, 'ticker')
        last = self.safe_string(ticker, 'last')
        return self.safe_ticker({
            'symbol': self.safe_string(market, 'symbol'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'high': self.safe_string(ticker, 'high'),
            'low': self.safe_string(ticker, 'low'),
            'bid': self.safe_string(ticker, 'bid'),
            'bidVolume': None,
            'ask': self.safe_string(ticker, 'ask'),
            'askVolume': None,
            'vwap': None,
            'open': self.safe_string(ticker, 'open'),
            'close': last,
            'last': last,
            'previousClose': None,
            'change': None,
            'percentage': self.safe_string(ticker, 'change'),
            'average': None,
            'baseVolume': self.safe_string_2(ticker, 'vol', 'volume'),
            'quoteVolume': self.safe_string(ticker, 'deal'),
            'info': ticker,
        }, market)

    def fetch_order_book(self, symbol: str, limit: Int = None, params={}):
        """
        fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
        :see: https://github.com/P2B-team/p2b-api-docs/blob/master/api-doc.md#depth-result
        :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
         *
         * EXCHANGE SPECIFIC PARAMETERS
        :param str [params.interval]: 0(default), 0.00000001, 0.0000001, 0.000001, 0.00001, 0.0001, 0.001, 0.01, 0.1, 1
        :returns dict: A dictionary of `order book structures <https://docs.ccxt_zagrava.com/#/?id=order-book-structure>` indexed by market symbols
        """
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'market': market['id'],
        }
        if limit is not None:
            request['limit'] = limit
        response = self.publicGetDepthResult(self.extend(request, params))
        #
        #    {
        #        "success": True,
        #        "errorCode": "",
        #        "message": "",
        #        "result": {
        #            "asks": [
        #                [
        #                    "4.53",     # Price
        #                    "523.95"    # Amount
        #                ],
        #                ...
        #            ],
        #            "bids": [
        #                [
        #                    "4.51",
        #                    "244.75"
        #                ],
        #                ...
        #            ]
        #        },
        #        "cache_time": 1698733470.469175,
        #        "current_time": 1698733470.469274
        #    }
        #
        result = self.safe_value(response, 'result', {})
        timestamp = self.safe_integer_product(response, 'current_time', 1000)
        return self.parse_order_book(result, market['symbol'], timestamp, 'bids', 'asks', 0, 1)

    def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}):
        """
        get the list of most recent trades for a particular symbol
        :see: https://github.com/P2B-team/p2b-api-docs/blob/master/api-doc.md#history
        :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]: 1-100, default=50
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int params['lastId']: order id
        :returns Trade[]: a list of `trade structures <https://docs.ccxt_zagrava.com/#/?id=public-trades>`
        """
        self.load_markets()
        lastId = self.safe_integer(params, 'lastId')
        if lastId is None:
            raise ArgumentsRequired(self.id + ' fetchTrades() requires an extra parameter params["lastId"]')
        market = self.market(symbol)
        request: dict = {
            'market': market['id'],
            'lastId': lastId,
        }
        if limit is not None:
            request['limit'] = limit
        response = self.publicGetHistory(self.extend(request, params))
        #
        #    {
        #        success: True,
        #        errorCode: '',
        #        message: '',
        #        result: [
        #            {
        #                id: '7495738622',
        #                type: 'sell',
        #                time: '1699255565.445418',
        #                amount: '252.6',
        #                price: '0.3422'
        #            },
        #            ...
        #        ],
        #        cache_time: '1699255571.413633',
        #        current_time: '1699255571.413828'
        #    }
        #
        result = self.safe_list(response, 'result', [])
        return self.parse_trades(result, market, since, limit)

    def parse_trade(self, trade: dict, market: Market = None):
        #
        # fetchTrades
        #
        #    {
        #        id: '7495738622',
        #        type: 'sell',
        #        time: '1699255565.445418',
        #        amount: '252.6',
        #        price: '0.3422'
        #    }
        #
        # fetchMyTrades
        #
        #    {
        #        "deal_id": 7450617292,              # Deal id
        #        "deal_time": 1698506956.66224,      # Deal execution time
        #        "deal_order_id": 171955225751,      # Deal order id
        #        "opposite_order_id": 171955110512,  # Opposite order id
        #        "side": "sell",                     # Deal side
        #        "price": "0.05231",                 # Deal price
        #        "amount": "0.002",                  # Deal amount
        #        "deal": "0.00010462",               # Total(price * amount)
        #        "deal_fee": "0.000000188316",       # Deal fee
        #        "role": "taker",                    # Role. Taker or maker
        #        "isSelfTrade": False                # is self trade
        #    }
        #
        # fetchOrderTrades
        #
        #    {
        #        "id": 7429883128,             # Deal id
        #        "time": 1698237535.41196,     # Deal execution time
        #        "fee": "0.01755848704",       # Deal fee
        #        "price": "34293.92",          # Deal price
        #        "amount": "0.00032",          # Deal amount
        #        "dealOrderId": 171366551416,  # Deal order id
        #        "role": 1,                    # Deal role(1 - maker, 2 - taker)
        #        "deal": "10.9740544"          # Total(price * amount)
        #    }
        #
        timestamp = self.safe_integer_product_2(trade, 'time', 'deal_time', 1000)
        takerOrMaker = self.safe_string(trade, 'role')
        if takerOrMaker == '1':
            takerOrMaker = 'maker'
        elif takerOrMaker == '2':
            takerOrMaker = 'taker'
        return self.safe_trade({
            'info': trade,
            'id': self.safe_string_2(trade, 'id', 'deal_id'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': self.safe_string(market, 'symbol'),
            'order': self.safe_string_2(trade, 'dealOrderId', 'deal_order_id'),
            'type': None,
            'side': self.safe_string_2(trade, 'type', 'side'),
            'takerOrMaker': takerOrMaker,
            'price': self.safe_string(trade, 'price'),
            'amount': self.safe_string(trade, 'amount'),
            'cost': self.safe_string(trade, 'deal'),
            'fee': {
                'currency': market['quote'],
                'cost': self.safe_string_2(trade, 'fee', 'deal_fee'),
            },
        }, market)

    def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}):
        """
        fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
        :see: https://github.com/P2B-team/p2b-api-docs/blob/master/api-doc.md#kline
        :param str symbol: unified symbol of the market to fetch OHLCV data for
        :param str timeframe: 1m, 1h, or 1d
        :param int [since]: timestamp in ms of the earliest candle to fetch
        :param int [limit]: 1-500, default=50
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.offset]: default=0, with self value the last candles are returned
        :returns int[][]: A list of candles ordered, open, high, low, close, volume
        """
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'market': market['id'],
            'interval': timeframe,
        }
        if limit is not None:
            request['limit'] = limit
        response = self.publicGetMarketKline(self.extend(request, params))
        #
        #    {
        #        success: True,
        #        errorCode: '',
        #        message: '',
        #        result: [
        #            [
        #                1699253400,       # Kline open time
        #                '0.3429',         # Open price
        #                '0.3427',         # Close price
        #                '0.3429',         # Highest price
        #                '0.3427',         # Lowest price
        #                '1900.4',         # Volume for stock currency
        #                '651.46278',      # Volume for money currency
        #                'ADA_USDT'        # Market name
        #            ],
        #            ...
        #        ],
        #        cache_time: '1699256375.030292',
        #        current_time: '1699256375.030494'
        #    }
        #
        result = self.safe_list(response, 'result', [])
        return self.parse_ohlcvs(result, market, timeframe, since, limit)

    def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
        #
        #    [
        #        1699253400,       # Kline open time
        #        '0.3429',         # Open price
        #        '0.3427',         # Close price
        #        '0.3429',         # Highest price
        #        '0.3427',         # Lowest price
        #        '1900.4',         # Volume for stock currency
        #        '651.46278',      # Volume for money currency
        #        'ADA_USDT'        # Market name
        #    ],
        #
        return [
            self.safe_integer_product(ohlcv, 0, 1000),
            self.safe_number(ohlcv, 1),
            self.safe_number(ohlcv, 3),
            self.safe_number(ohlcv, 4),
            self.safe_number(ohlcv, 2),
            self.safe_number(ohlcv, 5),
        ]

    def fetch_balance(self, params={}):
        """
        query for balance and get the amount of funds available for trading or funds locked in orders
        :see: https://github.com/P2B-team/p2b-api-docs/blob/master/api-doc.md#all-balances
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `balance structure <https://docs.ccxt_zagrava.com/#/?id=balance-structure>`
        """
        self.load_markets()
        response = self.privatePostAccountBalances(params)
        #
        #    {
        #        "success": True,
        #        "errorCode": "",
        #        "message": "",
        #        "result": {
        #            "USDT": {
        #              "available": "71.81328046",
        #              "freeze": "10.46103091"
        #            },
        #            "BTC": {
        #              "available": "0.00135674",
        #              "freeze": "0.00020003"
        #            }
        #        }
        #    }
        #
        result = self.safe_value(response, 'result', {})
        return self.parse_balance(result)

    def parse_balance(self, response):
        #
        #    {
        #        "USDT": {
        #            "available": "71.81328046",
        #            "freeze": "10.46103091"
        #        },
        #        "BTC": {
        #            "available": "0.00135674",
        #            "freeze": "0.00020003"
        #        }
        #    }
        #
        result: dict = {
            'info': response,
        }
        keys = list(response.keys())
        for i in range(0, len(keys)):
            currencyId = keys[i]
            balance = response[currencyId]
            code = self.safe_currency_code(currencyId)
            used = self.safe_string(balance, 'freeze')
            available = self.safe_string(balance, 'available')
            account: dict = {
                'free': available,
                'used': used,
            }
            result[code] = account
        return self.safe_balance(result)

    def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
        """
        create a trade order
        :see: https://github.com/P2B-team/p2b-api-docs/blob/master/api-doc.md#create-order
        :param str symbol: unified symbol of the market to create an order in
        :param str type: must be 'limit'
        :param str side: 'buy' or 'sell'
        :param float amount: how much of currency you want to trade in units of base currency
        :param float price: the price at which the order is to be fulfilled, in units of the quote currency
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        self.load_markets()
        if type == 'market':
            raise BadRequest(self.id + ' createOrder() can only accept orders with type "limit"')
        market = self.market(symbol)
        request: dict = {
            'market': market['id'],
            'side': side,
            'amount': self.amount_to_precision(symbol, amount),
            'price': self.price_to_precision(symbol, price),
        }
        response = self.privatePostOrderNew(self.extend(request, params))
        #
        #    {
        #        "success": True,
        #        "errorCode": "",
        #        "message": "",
        #        "result": {
        #            "orderId": 171906478744,          # Order id
        #            "market": "ETH_BTC",              # Market name
        #            "price": "0.04348",               # Price
        #            "side": "buy",                    # Side
        #            "type": "limit",                  # Order type
        #            "timestamp": 1698484861.746517,   # Order creation time
        #            "dealMoney": "0",                 # Filled total
        #            "dealStock": "0",                 # Filled amount
        #            "amount": "0.0277",               # Original amount
        #            "takerFee": "0.002",              # taker fee
        #            "makerFee": "0.002",              # maker fee
        #            "left": "0.0277",                 # Unfilled amount
        #            "dealFee": "0"                    # Filled fee
        #        }
        #    }
        #
        result = self.safe_dict(response, 'result')
        return self.parse_order(result, market)

    def cancel_order(self, id: str, symbol: Str = None, params={}):
        """
        cancels an open order
        :see: https://github.com/P2B-team/p2b-api-docs/blob/master/api-doc.md#cancel-order
        :param str id: order id
        :param str symbol: unified symbol of the market the order was made in
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: An `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelOrder() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'market': market['id'],
            'orderId': id,
        }
        response = self.privatePostOrderCancel(self.extend(request, params))
        #
        #    {
        #        "success": True,
        #        "errorCode": "",
        #        "message": "",
        #        "result": {
        #            "orderId": 171906478744,
        #            "market": "ETH_BTC",
        #            "price": "0.04348",
        #            "side": "buy",
        #            "type": "limit",
        #            "timestamp": 1698484861.746517,
        #            "dealMoney": "0",
        #            "dealStock": "0",
        #            "amount": "0.0277",
        #            "takerFee": "0.002",
        #            "makerFee": "0.002",
        #            "left": "0.0277",
        #            "dealFee": "0"
        #        }
        #    }
        #
        result = self.safe_dict(response, 'result')
        return self.parse_order(result)

    def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        fetch all unfilled currently open orders
        :see: https://github.com/P2B-team/p2b-api-docs/blob/master/api-doc.md#open-orders
        :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
         *
         * EXCHANGE SPECIFIC PARAMETERS
        :param int [params.offset]: 0-10000, default=0
        :returns Order[]: a list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOpenOrders() requires the symbol argument')
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'market': market['id'],
        }
        if limit is not None:
            request['limit'] = limit
        response = self.privatePostOrders(self.extend(request, params))
        #
        #    {
        #        "success": True,
        #        "errorCode": "",
        #        "message": "",
        #        "result": [
        #            {
        #                "orderId": 171913325964,
        #                "market": "ETH_BTC",
        #                "price": "0.06534",
        #                "side": "sell",
        #                "type": "limit",
        #                "timestamp": 1698487986.836821,
        #                "dealMoney": "0",
        #                "dealStock": "0",
        #                "amount": "0.0018",
        #                "takerFee": "0.0018",
        #                "makerFee": "0.0016",
        #                "left": "0.0018",
        #                "dealFee": "0"
        #            },
        #            ...
        #        ]
        #    }
        #
        result = self.safe_list(response, 'result', [])
        return self.parse_orders(result, market, since, limit)

    def fetch_order_trades(self, id: str, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        fetch all the trades made from a single order
        :see: https://github.com/P2B-team/p2b-api-docs/blob/master/api-doc.md#deals-by-order-id
        :param str id: order id
        :param str symbol: unified market symbol
        :param int [since]: the earliest time in ms to fetch trades for
        :param int [limit]: 1-100, default=50
        :param dict [params]: extra parameters specific to the exchange API endpoint
         *
         * EXCHANGE SPECIFIC PARAMETERS
        :param int [params.offset]: 0-10000, default=0
        :returns dict[]: a list of `trade structures <https://docs.ccxt_zagrava.com/#/?id=trade-structure>`
        """
        self.load_markets()
        market = self.safe_market(symbol)
        request: dict = {
            'orderId': id,
        }
        if limit is not None:
            request['limit'] = limit
        response = self.privatePostAccountOrder(self.extend(request, params))
        #
        #    {
        #        "success": True,
        #        "errorCode": "",
        #        "message": "",
        #        "result": {
        #            "offset": 0,
        #            "limit": 50,
        #            "records": [
        #                {
        #                    "id": 7429883128,             # Deal id
        #                    "time": 1698237535.41196,     # Deal execution time
        #                    "fee": "0.01755848704",       # Deal fee
        #                    "price": "34293.92",          # Deal price
        #                    "amount": "0.00032",          # Deal amount
        #                    "dealOrderId": 171366551416,  # Deal order id
        #                    "role": 1,                    # Deal role(1 - maker, 2 - taker)
        #                    "deal": "10.9740544"          # Total(price * amount)
        #                }
        #            ]
        #        }
        #    }
        #
        result = self.safe_value(response, 'result', {})
        records = self.safe_list(result, 'records', [])
        return self.parse_trades(records, market, since, limit)

    def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        fetch all trades made by the user, only the transaction records in the past 3 month can be queried, the time between since and params["until"] cannot be longer than 24 hours
        :see: https://github.com/P2B-team/p2b-api-docs/blob/master/api-doc.md#deals-history-by-market
        :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, default = params["until"] - 86400000
        :param int [limit]: 1-100, default=50
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: the latest time in ms to fetch orders for, default = current timestamp or since + 86400000
         *
         * EXCHANGE SPECIFIC PARAMETERS
        :param int [params.offset]: 0-10000, default=0
        :returns Trade[]: a list of `trade structures <https://docs.ccxt_zagrava.com/#/?id=public-trades>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchMyTrades() requires a symbol argument')
        self.load_markets()
        until = self.safe_integer(params, 'until')
        params = self.omit(params, 'until')
        if until is None:
            if since is None:
                until = self.milliseconds()
            else:
                until = since + 86400000
        if since is None:
            since = until - 86400000
        if (until - since) > 86400000:
            raise BadRequest(self.id + ' fetchMyTrades() the time between since and params["until"] cannot be greater than 24 hours')
        market = self.market(symbol)
        request: dict = {
            'market': market['id'],
            'startTime': self.parse_to_int(since / 1000),
            'endTime': self.parse_to_int(until / 1000),
        }
        if limit is not None:
            request['limit'] = limit
        response = self.privatePostAccountMarketDealHistory(self.extend(request, params))
        #
        #    {
        #        "success": True,
        #        "errorCode": "",
        #        "message": "",
        #        "result": {
        #            "total": 2,                                 # Total records in the queried range
        #            "deals": [
        #                {
        #                    "deal_id": 7450617292,              # Deal id
        #                    "deal_time": 1698506956.66224,      # Deal execution time
        #                    "deal_order_id": 171955225751,      # Deal order id
        #                    "opposite_order_id": 171955110512,  # Opposite order id
        #                    "side": "sell",                     # Deal side
        #                    "price": "0.05231",                 # Deal price
        #                    "amount": "0.002",                  # Deal amount
        #                    "deal": "0.00010462",               # Total(price * amount)
        #                    "deal_fee": "0.000000188316",       # Deal fee
        #                    "role": "taker",                    # Role. Taker or maker
        #                    "isSelfTrade": False                # is self trade
        #                },
        #                ...
        #            ]
        #        }
        #    }
        #
        result = self.safe_value(response, 'result', {})
        deals = self.safe_list(result, 'deals', [])
        return self.parse_trades(deals, market, since, limit)

    def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
        """
        fetches information on multiple closed orders made by the user, the time between since and params["untnil"] cannot be longer than 24 hours
        :see: https://github.com/P2B-team/p2b-api-docs/blob/master/api-doc.md#orders-history-by-market
        :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, default = params["until"] - 86400000
        :param int [limit]: 1-100, default=50
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: the latest time in ms to fetch orders for, default = current timestamp or since + 86400000
         *
         * EXCHANGE SPECIFIC PARAMETERS
        :param int [params.offset]: 0-10000, default=0
        :returns Order[]: a list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        self.load_markets()
        until = self.safe_integer(params, 'until')
        params = self.omit(params, 'until')
        market: Market = None
        if symbol is not None:
            market = self.market(symbol)
        if until is None:
            if since is None:
                until = self.milliseconds()
            else:
                until = since + 86400000
        if since is None:
            since = until - 86400000
        if (until - since) > 86400000:
            raise BadRequest(self.id + ' fetchClosedOrders() the time between since and params["until"] cannot be greater than 24 hours')
        request: dict = {
            'startTime': self.parse_to_int(since / 1000),
            'endTime': self.parse_to_int(until / 1000),
        }
        if market is not None:
            request['market'] = market['id']
        if limit is not None:
            request['limit'] = limit
        response = self.privatePostAccountOrderHistory(self.extend(request, params))
        #
        #    {
        #        "success": True,
        #        "errorCode": "",
        #        "message": "",
        #        "result": {
        #            "LTC_USDT": [
        #                {
        #                    "id": 173985944395,
        #                    "amount": "0.1",
        #                    "price": "73",
        #                    "type": "limit",
        #                    "side": "sell",
        #                    "ctime": 1699436194.390845,
        #                    "ftime": 1699436194.390847,
        #                    "market": "LTC_USDT",
        #                    "takerFee": "0.002",
        #                    "makerFee": "0.002",
        #                    "dealFee": "0.01474",
        #                    "dealStock": "0.1",
        #                    "dealMoney": "7.37"
        #                }
        #            ]
        #        }
        #    }
        #
        result = self.safe_value(response, 'result')
        orders = []
        keys = list(result.keys())
        for i in range(0, len(keys)):
            marketId = keys[i]
            marketOrders = result[marketId]
            parsedOrders = self.parse_orders(marketOrders, market, since, limit)
            orders = self.array_concat(orders, parsedOrders)
        return orders

    def parse_order(self, order: dict, market: Market = None) -> Order:
        #
        # cancelOrder, fetchOpenOrders, createOrder
        #
        #    {
        #        "orderId": 171906478744,
        #        "market": "ETH_BTC",
        #        "price": "0.04348",
        #        "side": "buy",
        #        "type": "limit",
        #        "timestamp": 1698484861.746517,
        #        "dealMoney": "0",
        #        "dealStock": "0",
        #        "amount": "0.0277",
        #        "takerFee": "0.002",
        #        "makerFee": "0.002",
        #        "left": "0.0277",
        #        "dealFee": "0"
        #    }
        #
        # fetchClosedOrders
        #
        #    {
        #        "id": 171366547790,           # Order id
        #        "amount": "0.00032",          # Original amount
        #        "price": "34293.92",          # Order price
        #        "type": "limit",              # Order type
        #        "side": "sell",               # Order side
        #        "ctime": 1698237533.497241,   # Order creation time
        #        "ftime": 1698237535.41196,    # Order fill time
        #        "market": "BTC_USDT",         # Market name
        #        "takerFee": "0.0018",         # Taker fee
        #        "makerFee": "0.0016",         # Market fee
        #        "dealFee": "0.01755848704",   # Deal fee
        #        "dealStock": "0.00032",       # Filled amount
        #        "dealMoney": "10.9740544"     # Filled total
        #    }
        #
        timestamp = self.safe_integer_product_2(order, 'timestamp', 'ctime', 1000)
        marketId = self.safe_string(order, 'market')
        market = self.safe_market(marketId, market)
        return self.safe_order({
            'info': order,
            'id': self.safe_string_2(order, 'id', 'orderId'),
            'clientOrderId': None,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': None,
            'symbol': market['symbol'],
            'type': self.safe_string(order, 'type'),
            'timeInForce': None,
            'postOnly': None,
            'side': self.safe_string(order, 'side'),
            'price': self.safe_string(order, 'price'),
            'stopPrice': None,
            'amount': self.safe_string(order, 'amount'),
            'cost': None,
            'average': None,
            'filled': self.safe_string(order, 'dealStock'),
            'remaining': self.safe_string(order, 'left'),
            'status': None,
            'fee': {
                'currency': market['quote'],
                'cost': self.safe_string(order, 'dealFee'),
            },
            'trades': None,
        }, market)

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        url = self.urls['api'][api] + '/' + self.implode_params(path, params)
        params = self.omit(params, self.extract_params(path))
        if method == 'GET':
            if params:
                url += '?' + self.urlencode(params)
        if api == 'private':
            params['request'] = '/api/v2/' + path
            params['nonce'] = str(self.nonce())
            payload = self.string_to_base64(self.json(params))  # Body json encoded in base64
            headers = {
                'Content-Type': 'application/json',
                'X-TXC-APIKEY': self.apiKey,
                'X-TXC-PAYLOAD': payload,
                'X-TXC-SIGNATURE': self.hmac(self.encode(payload), self.encode(self.secret), hashlib.sha512),
            }
            body = self.json(params)
        return {'url': url, 'method': method, 'body': body, 'headers': headers}

    def handle_errors(self, code: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
        if response is None:
            return None
        if code == 400:
            error = self.safe_value(response, 'error')
            errorCode = self.safe_string(error, 'code')
            feedback = self.id + ' ' + self.json(response)
            self.throw_exactly_matched_exception(self.exceptions, errorCode, feedback)
            # fallback to default error handler
        return None
