<?php

namespace ccxt\async;

// 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

use Exception; // a common import
use ccxt\async\abstract\p2b as Exchange;
use ccxt\ArgumentsRequired;
use ccxt\BadRequest;
use \React\Async;
use \React\Promise\PromiseInterface;

class p2b extends Exchange {

    public function describe(): mixed {
        return $this->deep_extend(parent::describe(), array(
            'id' => 'p2b',
            'name' => 'p2b',
            'countries' => array( 'LT' ),
            'rateLimit' => 100,
            'version' => 'v2',
            'pro' => true,
            'has' => array(
                'CORS' => null,
                'spot' => true,
                'margin' => false,
                'swap' => false,
                'future' => false,
                'option' => false,
                'addMargin' => false,
                'borrowCrossMargin' => false,
                'borrowIsolatedMargin' => false,
                'borrowMargin' => 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,
                'fetchAllGreeks' => false,
                'fetchBalance' => true,
                'fetchBorrowInterest' => false,
                'fetchBorrowRate' => false,
                'fetchBorrowRateHistories' => false,
                'fetchBorrowRateHistory' => false,
                'fetchBorrowRates' => false,
                'fetchBorrowRatesPerSymbol' => false,
                'fetchClosedOrders' => true,
                'fetchCrossBorrowRate' => false,
                'fetchCrossBorrowRates' => false,
                'fetchDeposit' => false,
                'fetchDepositAddress' => false,
                'fetchDepositAddresses' => false,
                'fetchDepositAddressesByNetwork' => false,
                'fetchDeposits' => false,
                'fetchDepositsWithdrawals' => false,
                'fetchFundingHistory' => false,
                'fetchFundingInterval' => false,
                'fetchFundingIntervals' => false,
                'fetchFundingRate' => false,
                'fetchFundingRateHistory' => false,
                'fetchFundingRates' => false,
                'fetchGreeks' => false,
                'fetchIndexOHLCV' => false,
                'fetchIsolatedBorrowRate' => false,
                'fetchIsolatedBorrowRates' => false,
                'fetchIsolatedPositions' => false,
                'fetchLedger' => false,
                'fetchLedgerEntry' => false,
                'fetchLeverage' => false,
                'fetchLeverages' => false,
                'fetchLeverageTiers' => false,
                'fetchLiquidations' => false,
                'fetchLongShortRatio' => false,
                'fetchLongShortRatioHistory' => false,
                'fetchMarginAdjustmentHistory' => false,
                'fetchMarginMode' => false,
                'fetchMarginModes' => false,
                'fetchMarketLeverageTiers' => false,
                'fetchMarkets' => true,
                'fetchMarkOHLCV' => false,
                'fetchMarkPrice' => false,
                'fetchMarkPrices' => false,
                'fetchMyLiquidations' => false,
                'fetchMySettlementHistory' => false,
                'fetchMyTrades' => true,
                'fetchOHLCV' => true,
                'fetchOpenInterest' => false,
                'fetchOpenInterestHistory' => false,
                'fetchOpenInterests' => false,
                'fetchOpenOrders' => true,
                'fetchOption' => false,
                'fetchOptionChain' => false,
                'fetchOrderBook' => true,
                'fetchOrderBooks' => false,
                'fetchOrders' => false,
                'fetchOrderTrades' => true,
                'fetchPosition' => false,
                'fetchPositionHistory' => false,
                'fetchPositionMode' => false,
                'fetchPositions' => false,
                'fetchPositionsForSymbol' => false,
                'fetchPositionsHistory' => false,
                'fetchPositionsRisk' => false,
                'fetchPremiumIndexOHLCV' => false,
                'fetchSettlementHistory' => false,
                'fetchTicker' => true,
                'fetchTickers' => true,
                'fetchTrades' => true,
                'fetchTradingLimits' => false,
                'fetchTransactionFee' => false,
                'fetchTransactionFees' => false,
                'fetchTransactions' => false,
                'fetchTransfers' => false,
                'fetchUnderlyingAssets' => false,
                'fetchVolatilityHistory' => false,
                'fetchWithdrawAddresses' => false,
                'fetchWithdrawal' => false,
                'fetchWithdrawals' => false,
                'reduceMargin' => false,
                'repayCrossMargin' => false,
                'repayIsolatedMargin' => false,
                'setLeverage' => false,
                'setMargin' => false,
                'setMarginMode' => false,
                'setPositionMode' => false,
                'signIn' => false,
                'transfer' => false,
                'withdraw' => false,
            ),
            'timeframes' => array(
                '1m' => '1m',
                '1h' => '1h',
                '1d' => '1d',
            ),
            'urls' => array(
                'extension' => '.json',
                'referral' => 'https://p2pb2b.com?referral=ee784c53',
                'logo' => 'https://github.com/ccxt/ccxt/assets/43336371/8da13a80-1f0a-49be-bb90-ff8b25164755',
                'api' => array(
                    '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' => array(
                'public' => array(
                    'get' => array(
                        'markets' => 1,
                        'market' => 1,
                        'tickers' => 1,
                        'ticker' => 1,
                        'book' => 1,
                        'history' => 1,
                        'depth/result' => 1,
                        'market/kline' => 1,
                    ),
                ),
                'private' => array(
                    'post' => array(
                        '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' => array(
                'trading' => array(
                    'tierBased' => true,
                    'percentage' => true,
                    'taker' => array(
                        array( $this->parse_number('0'), $this->parse_number('0.2') ),
                        array( $this->parse_number('1'), $this->parse_number('0.19') ),
                        array( $this->parse_number('5'), $this->parse_number('0.18') ),
                        array( $this->parse_number('10'), $this->parse_number('0.17') ),
                        array( $this->parse_number('25'), $this->parse_number('0.16') ),
                        array( $this->parse_number('75'), $this->parse_number('0.15') ),
                        array( $this->parse_number('100'), $this->parse_number('0.14') ),
                        array( $this->parse_number('150'), $this->parse_number('0.13') ),
                        array( $this->parse_number('300'), $this->parse_number('0.12') ),
                        array( $this->parse_number('450'), $this->parse_number('0.11') ),
                        array( $this->parse_number('500'), $this->parse_number('0.1') ),
                    ),
                    'maker' => array(
                        array( $this->parse_number('0'), $this->parse_number('0.2') ),
                        array( $this->parse_number('1'), $this->parse_number('0.18') ),
                        array( $this->parse_number('5'), $this->parse_number('0.16') ),
                        array( $this->parse_number('10'), $this->parse_number('0.14') ),
                        array( $this->parse_number('25'), $this->parse_number('0.12') ),
                        array( $this->parse_number('75'), $this->parse_number('0.1') ),
                        array( $this->parse_number('100'), $this->parse_number('0.08') ),
                        array( $this->parse_number('150'), $this->parse_number('0.06') ),
                        array( $this->parse_number('300'), $this->parse_number('0.04') ),
                        array( $this->parse_number('450'), $this->parse_number('0.02') ),
                        array( $this->parse_number('500'), $this->parse_number('0.01') ),
                    ),
                ),
            ),
            'features' => array(
                'spot' => array(
                    'sandbox' => false,
                    'createOrder' => array(
                        'marginMode' => false,
                        'triggerPrice' => false,
                        'triggerDirection' => false,
                        'triggerPriceType' => null,
                        'stopLossPrice' => false,
                        'takeProfitPrice' => false,
                        'attachedStopLossTakeProfit' => null,
                        'timeInForce' => array(
                            'IOC' => true,
                            'FOK' => true,
                            'PO' => true,
                            'GTD' => false,
                        ),
                        'hedged' => false,
                        'trailing' => false,
                        'leverage' => false,
                        'marketBuyByCost' => false,
                        'marketBuyRequiresPrice' => false,
                        'selfTradePrevention' => false,
                        'iceberg' => false,
                    ),
                    'createOrders' => null,
                    'fetchMyTrades' => array(
                        'marginMode' => false,
                        'limit' => 100,
                        'daysBack' => 100000, // todo
                        'untilDays' => 1,
                        'symbolRequired' => true,
                    ),
                    'fetchOrder' => null, // todo
                    'fetchOpenOrders' => array(
                        'marginMode' => false,
                        'limit' => 100,
                        'trigger' => false,
                        'trailing' => false,
                        'symbolRequired' => true,
                    ),
                    'fetchOrders' => null, // todo
                    'fetchClosedOrders' => array(
                        'marginMode' => false,
                        'limit' => 100,
                        'daysBack' => 100000, // todo
                        'daysBackCanceled' => 1 / 12, // todo
                        'untilDays' => 1,
                        'trigger' => false,
                        'trailing' => false,
                        'symbolRequired' => false,
                    ),
                    'fetchOHLCV' => array(
                        'limit' => 500,
                    ),
                ),
                'swap' => array(
                    'linear' => null,
                    'inverse' => null,
                ),
                'future' => array(
                    'linear' => null,
                    'inverse' => null,
                ),
            ),
            'commonCurrencies' => array(
            ),
            'precisionMode' => TICK_SIZE,
            'exceptions' => array(
                '1001' => '\\ccxt\\AuthenticationError',    // Key not provided. X-TXC-APIKEY header is missing in the request or empty.
                '1002' => '\\ccxt\\AuthenticationError',    // Payload not provided. X-TXC-PAYLOAD header is missing in the request or empty.
                '1003' => '\\ccxt\\AuthenticationError',    // Signature not provided. X-TXC-SIGNATURE header is missing in the request or empty.
                '1004' => '\\ccxt\\AuthenticationError',    // Nonce and url not provided. Request body is empty. Missing required parameters "request", "nonce".
                '1005' => '\\ccxt\\AuthenticationError',    // Invalid body data. Invalid request body
                '1006' => '\\ccxt\\AuthenticationError',    // Nonce not provided. Request body missing required parameter "nonce".
                '1007' => '\\ccxt\\AuthenticationError',    // Request not provided. Request body missing required parameter "request".
                '1008' => '\\ccxt\\AuthenticationError',    // Invalid request in body. The passed request parameter does not match the URL of this request.
                '1009' => '\\ccxt\\AuthenticationError',    // Invalid payload. The transmitted payload value (X-TXC-PAYLOAD header) does not match the request body.
                '1010' => '\\ccxt\\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' => '\\ccxt\\AuthenticationError',    // This action is unauthorized. Please, enable two-factor authentication. Two-factor authentication is not activated for the user.
                '1012' => '\\ccxt\\AuthenticationError',    // Invalid nonce. Parameter "nonce" is not a number.
                '1013' => '\\ccxt\\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' => '\\ccxt\\AuthenticationError',    // Unauthorized request. Signature value passed (in the X-TXC-SIGNATURE header) does not match the request body.
                '1015' => '\\ccxt\\AuthenticationError',    // Temporary block. Temporary blocking. There is a cancellation of orders.
                '1016' => '\\ccxt\\AuthenticationError',    // Not unique nonce. The request was sent with a repeated parameter "nonce" within 10 seconds.
                '2010' => '\\ccxt\\BadRequest',             // Currency not found. Currency not found.
                '2020' => '\\ccxt\\BadRequest',             // Market is not available. Market is not available.
                '2021' => '\\ccxt\\BadRequest',             // Unknown market. Unknown market.
                '2030' => '\\ccxt\\BadRequest',             // Order not found. Order not found.
                '2040' => '\\ccxt\\InsufficientFunds',      // Balance not enough. Insufficient balance.
                '2050' => '\\ccxt\\BadRequest',             // Amount less than the permitted minimum. Amount less than the permitted minimum.
                '2051' => '\\ccxt\\BadRequest',             // Amount is greater than the maximum allowed. Amount exceeds the allowed maximum.
                '2052' => '\\ccxt\\BadRequest',             // Amount step size error. Amount step size error.
                '2060' => '\\ccxt\\BadRequest',             // Price less than the permitted minimum. Price is less than the permitted minimum.
                '2061' => '\\ccxt\\BadRequest',             // Price is greater than the maximum allowed. Price exceeds the allowed maximum.
                '2062' => '\\ccxt\\BadRequest',             // Price pick size error. Price pick size error.
                '2070' => '\\ccxt\\BadRequest',             // Total less than the permitted minimum. Total less than the permitted minimum.
                '3001' => '\\ccxt\\BadRequest',             // Validation exception. The given data was invalid.
                '3020' => '\\ccxt\\BadRequest',             // Invalid currency value. Incorrect parameter, check your request.
                '3030' => '\\ccxt\\BadRequest',             // Invalid market value. Incorrect "market" parameter, check your request.
                '3040' => '\\ccxt\\BadRequest',             // Invalid amount value. Incorrect "amount" parameter, check your request.
                '3050' => '\\ccxt\\BadRequest',             // Invalid price value. Incorrect "price" parameter, check your request.
                '3060' => '\\ccxt\\BadRequest',             // Invalid limit value. Incorrect "limit" parameter, check your request.
                '3070' => '\\ccxt\\BadRequest',             // Invalid offset value. Incorrect "offset" parameter, check your request.
                '3080' => '\\ccxt\\BadRequest',             // Invalid orderId value. Incorrect "orderId" parameter, check your request.
                '3090' => '\\ccxt\\BadRequest',             // Invalid lastId value. Incorrect "lastId" parameter, check your request.
                '3100' => '\\ccxt\\BadRequest',             // Invalid side value. Incorrect "side" parameter, check your request.
                '3110' => '\\ccxt\\BadRequest',             // Invalid interval value. Incorrect "interval" parameter, check your request.
                '4001' => '\\ccxt\\ExchangeNotAvailable',   // Service temporary unavailable. An unexpected system error has occurred. Try again after a while. If the error persists, please contact support.
                '6010' => '\\ccxt\\InsufficientFunds',      // Balance not enough. Insufficient balance.
            ),
            'options' => array(
            ),
        ));
    }

    public function fetch_markets($params = array ()): PromiseInterface {
        return Async\async(function () use ($params) {
            /**
             * retrieves data on all $markets for bigone
             *
             * @see https://github.com/P2B-team/p2b-api-docs/blob/master/api-doc.md#$markets
             *
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array[]} an array of objects representing market data
             */
            $response = Async\await($this->publicGetMarkets ($params));
            //
            //    {
            //        "success" => true,
            //        "errorCode" => "",
            //        "message" => "",
            //        "result" => array(
            //            {
            //                "name" => "ETH_BTC",
            //                "stock" => "ETH",
            //                "money" => "BTC",
            //                "precision" => array(
            //                    "money" => "6",
            //                    "stock" => "4",
            //                    "fee" => "4"
            //                ),
            //                "limits" => array(
            //                    "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 = $this->safe_value($response, 'result', array());
            return $this->parse_markets($markets);
        }) ();
    }

    public function parse_market(array $market): array {
        $marketId = $this->safe_string($market, 'name');
        $baseId = $this->safe_string($market, 'stock');
        $quoteId = $this->safe_string($market, 'money');
        $base = $this->safe_currency_code($baseId);
        $quote = $this->safe_currency_code($quoteId);
        $limits = $this->safe_value($market, 'limits');
        $maxAmount = $this->safe_string($limits, 'max_amount');
        $maxPrice = $this->safe_string($limits, 'max_price');
        return array(
            'id' => $marketId,
            'symbol' => $base . '/' . $quote,
            'base' => $base,
            'quote' => $quote,
            'settle' => null,
            'baseId' => $baseId,
            'quoteId' => $quoteId,
            'settleId' => null,
            'type' => 'spot',
            'spot' => true,
            'margin' => false,
            'swap' => false,
            'future' => false,
            'option' => false,
            'active' => true,
            'contract' => false,
            'linear' => null,
            'inverse' => null,
            'contractSize' => null,
            'expiry' => null,
            'expiryDatetime' => null,
            'strike' => null,
            'optionType' => null,
            'precision' => array(
                'amount' => $this->safe_number($limits, 'step_size'),
                'price' => $this->safe_number($limits, 'tick_size'),
            ),
            'limits' => array(
                'leverage' => array(
                    'min' => null,
                    'max' => null,
                ),
                'amount' => array(
                    'min' => $this->safe_number($limits, 'min_amount'),
                    'max' => $this->parse_number($this->omit_zero($maxAmount)),
                ),
                'price' => array(
                    'min' => $this->safe_number($limits, 'min_price'),
                    'max' => $this->parse_number($this->omit_zero($maxPrice)),
                ),
                'cost' => array(
                    'min' => null,
                    'max' => null,
                ),
            ),
            'created' => null,
            'info' => $market,
        );
    }

    public function fetch_tickers(?array $symbols = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbols, $params) {
            /**
             * 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 {string[]|null} $symbols unified $symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a dictionary of ~@link https://docs.ccxt.com/#/?id=ticker-structure ticker structures~
             */
            Async\await($this->load_markets());
            $response = Async\await($this->publicGetTickers ($params));
            //
            //    {
            //        success => true,
            //        errorCode => '',
            //        message => '',
            //        $result => {
            //            KNOLIX_BTC => array(
            //                at => '1699252631',
            //                ticker => array(
            //                    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 = $this->safe_value($response, 'result', array());
            return $this->parse_tickers($result, $symbols);
        }) ();
    }

    public function fetch_ticker(string $symbol, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $params) {
            /**
             * 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 {string} $symbol unified $symbol of the $market to fetch the ticker for
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=ticker-structure ticker structure~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'market' => $market['id'],
            );
            $response = Async\await($this->publicGetTicker ($this->extend($request, $params)));
            //
            //    {
            //        success => true,
            //        errorCode => '',
            //        message => '',
            //        $result => array(
            //            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 = $this->safe_value($response, 'result', array());
            $timestamp = $this->safe_integer_product($response, 'cache_time', 1000);
            return $this->extend(
                array( 'timestamp' => $timestamp, 'datetime' => $this->iso8601($timestamp) ),
                $this->parse_ticker($result, $market)
            );
        }) ();
    }

    public function parse_ticker($ticker, ?array $market = null) {
        //
        // 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 = $this->safe_integer_product($ticker, 'at', 1000);
        if (is_array($ticker) && array_key_exists('ticker', $ticker)) {
            $ticker = $this->safe_value($ticker, 'ticker');
        }
        $last = $this->safe_string($ticker, 'last');
        return $this->safe_ticker(array(
            'symbol' => $this->safe_string($market, 'symbol'),
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'high' => $this->safe_string($ticker, 'high'),
            'low' => $this->safe_string($ticker, 'low'),
            'bid' => $this->safe_string($ticker, 'bid'),
            'bidVolume' => null,
            'ask' => $this->safe_string($ticker, 'ask'),
            'askVolume' => null,
            'vwap' => null,
            'open' => $this->safe_string($ticker, 'open'),
            'close' => $last,
            'last' => $last,
            'previousClose' => null,
            'change' => null,
            'percentage' => $this->safe_string($ticker, 'change'),
            'average' => null,
            'baseVolume' => $this->safe_string_2($ticker, 'vol', 'volume'),
            'quoteVolume' => $this->safe_string($ticker, 'deal'),
            'info' => $ticker,
        ), $market);
    }

    public function fetch_order_book(string $symbol, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($symbol, $limit, $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 {string} $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 {array} [$params] extra parameters specific to the exchange API endpoint
             *
             * EXCHANGE SPECIFIC PARAMETERS
             * @param {string} [$params->interval] 0 (default), 0.00000001, 0.0000001, 0.000001, 0.00001, 0.0001, 0.001, 0.01, 0.1, 1
             * @return {array} A dictionary of ~@link https://docs.ccxt.com/#/?id=order-book-structure order book structures~ indexed by $market symbols
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'market' => $market['id'],
            );
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            $response = Async\await($this->publicGetDepthResult ($this->extend($request, $params)));
            //
            //    {
            //        "success" => true,
            //        "errorCode" => "",
            //        "message" => "",
            //        "result" => array(
            //            "asks" => array(
            //                array(
            //                    "4.53",     // Price
            //                    "523.95"    // Amount
            //                ),
            //                ...
            //            ),
            //            "bids" => array(
            //                array(
            //                    "4.51",
            //                    "244.75"
            //                ),
            //                ...
            //            )
            //        ),
            //        "cache_time" => 1698733470.469175,
            //        "current_time" => 1698733470.469274
            //    }
            //
            $result = $this->safe_value($response, 'result', array());
            $timestamp = $this->safe_integer_product($response, 'current_time', 1000);
            return $this->parse_order_book($result, $market['symbol'], $timestamp, 'bids', 'asks', 0, 1);
        }) ();
    }

    public function fetch_trades(string $symbol, ?int $since = null, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($symbol, $since, $limit, $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 {string} $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 {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {int} $params->lastId order id
             * @return {Trade[]} a list of ~@link https://docs.ccxt.com/#/?id=public-trades trade structures~
             */
            Async\await($this->load_markets());
            $lastId = $this->safe_integer($params, 'lastId');
            if ($lastId === null) {
                throw new ArgumentsRequired($this->id . ' fetchTrades () requires an extra parameter $params["lastId"]');
            }
            $market = $this->market($symbol);
            $request = array(
                'market' => $market['id'],
                'lastId' => $lastId,
            );
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            $response = Async\await($this->publicGetHistory ($this->extend($request, $params)));
            //
            //    {
            //        success => true,
            //        errorCode => '',
            //        message => '',
            //        $result => array(
            //            array(
            //                id => '7495738622',
            //                type => 'sell',
            //                time => '1699255565.445418',
            //                amount => '252.6',
            //                price => '0.3422'
            //            ),
            //            ...
            //        ),
            //        cache_time => '1699255571.413633',
            //        current_time => '1699255571.413828'
            //    }
            //
            $result = $this->safe_list($response, 'result', array());
            return $this->parse_trades($result, $market, $since, $limit);
        }) ();
    }

    public function parse_trade(array $trade, ?array $market = null) {
        //
        // 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 = $this->safe_integer_product_2($trade, 'time', 'deal_time', 1000);
        $takerOrMaker = $this->safe_string($trade, 'role');
        if ($takerOrMaker === '1') {
            $takerOrMaker = 'maker';
        } elseif ($takerOrMaker === '2') {
            $takerOrMaker = 'taker';
        }
        return $this->safe_trade(array(
            'info' => $trade,
            'id' => $this->safe_string_2($trade, 'id', 'deal_id'),
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'symbol' => $this->safe_string($market, 'symbol'),
            'order' => $this->safe_string_2($trade, 'dealOrderId', 'deal_order_id'),
            'type' => null,
            'side' => $this->safe_string_2($trade, 'type', 'side'),
            'takerOrMaker' => $takerOrMaker,
            'price' => $this->safe_string($trade, 'price'),
            'amount' => $this->safe_string($trade, 'amount'),
            'cost' => $this->safe_string($trade, 'deal'),
            'fee' => array(
                'currency' => $market['quote'],
                'cost' => $this->safe_string_2($trade, 'fee', 'deal_fee'),
            ),
        ), $market);
    }

    public function fetch_ohlcv(string $symbol, $timeframe = '1m', ?int $since = null, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($symbol, $timeframe, $since, $limit, $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 {string} $symbol unified $symbol of the $market to fetch OHLCV data for
             * @param {string} $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 {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {int} [$params->offset] default=0, with this value the last candles are returned
             * @return {int[][]} A list of candles ordered, open, high, low, close, volume
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'market' => $market['id'],
                'interval' => $timeframe,
            );
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            $response = Async\await($this->publicGetMarketKline ($this->extend($request, $params)));
            //
            //    {
            //        success => true,
            //        errorCode => '',
            //        message => '',
            //        $result => array(
            //            array(
            //                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 = $this->safe_list($response, 'result', array());
            return $this->parse_ohlcvs($result, $market, $timeframe, $since, $limit);
        }) ();
    }

    public function parse_ohlcv($ohlcv, ?array $market = null): array {
        //
        //    array(
        //        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 array(
            $this->safe_integer_product($ohlcv, 0, 1000),
            $this->safe_number($ohlcv, 1),
            $this->safe_number($ohlcv, 3),
            $this->safe_number($ohlcv, 4),
            $this->safe_number($ohlcv, 2),
            $this->safe_number($ohlcv, 5),
        );
    }

    public function fetch_balance($params = array ()) {
        return Async\async(function () use ($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 {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=balance-structure balance structure~
             */
            Async\await($this->load_markets());
            $response = Async\await($this->privatePostAccountBalances ($params));
            //
            //    {
            //        "success" => true,
            //        "errorCode" => "",
            //        "message" => "",
            //        "result" => {
            //            "USDT" => array(
            //              "available" => "71.81328046",
            //              "freeze" => "10.46103091"
            //            ),
            //            "BTC" => {
            //              "available" => "0.00135674",
            //              "freeze" => "0.00020003"
            //            }
            //        }
            //    }
            //
            $result = $this->safe_value($response, 'result', array());
            return $this->parse_balance($result);
        }) ();
    }

    public function parse_balance($response) {
        //
        //    {
        //        "USDT" => array(
        //            "available" => "71.81328046",
        //            "freeze" => "10.46103091"
        //        ),
        //        "BTC" => {
        //            "available" => "0.00135674",
        //            "freeze" => "0.00020003"
        //        }
        //    }
        //
        $result = array(
            'info' => $response,
        );
        $keys = is_array($response) ? array_keys($response) : array();
        for ($i = 0; $i < count($keys); $i++) {
            $currencyId = $keys[$i];
            $balance = $response[$currencyId];
            $code = $this->safe_currency_code($currencyId);
            $used = $this->safe_string($balance, 'freeze');
            $available = $this->safe_string($balance, 'available');
            $account = array(
                'free' => $available,
                'used' => $used,
            );
            $result[$code] = $account;
        }
        return $this->safe_balance($result);
    }

    public function create_order(string $symbol, string $type, string $side, float $amount, ?float $price = null, $params = array ()) {
        return Async\async(function () use ($symbol, $type, $side, $amount, $price, $params) {
            /**
             * create a trade order
             *
             * @see https://github.com/P2B-team/p2b-api-docs/blob/master/api-doc.md#create-order
             *
             * @param {string} $symbol unified $symbol of the $market to create an order in
             * @param {string} $type must be 'limit'
             * @param {string} $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 {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} an ~@link https://docs.ccxt.com/#/?id=order-structure order structure~
             */
            Async\await($this->load_markets());
            if ($type === 'market') {
                throw new BadRequest($this->id . ' createOrder () can only accept orders with $type "limit"');
            }
            $market = $this->market($symbol);
            $request = array(
                'market' => $market['id'],
                'side' => $side,
                'amount' => $this->amount_to_precision($symbol, $amount),
                'price' => $this->price_to_precision($symbol, $price),
            );
            $response = Async\await($this->privatePostOrderNew ($this->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 = $this->safe_dict($response, 'result');
            return $this->parse_order($result, $market);
        }) ();
    }

    public function cancel_order(string $id, ?string $symbol = null, $params = array ()) {
        return Async\async(function () use ($id, $symbol, $params) {
            /**
             * cancels an open order
             *
             * @see https://github.com/P2B-team/p2b-api-docs/blob/master/api-doc.md#cancel-order
             *
             * @param {string} $id order $id
             * @param {string} $symbol unified $symbol of the $market the order was made in
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} An ~@link https://docs.ccxt.com/#/?$id=order-structure order structure~
             */
            if ($symbol === null) {
                throw new ArgumentsRequired($this->id . ' cancelOrder() requires a $symbol argument');
            }
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'market' => $market['id'],
                'orderId' => $id,
            );
            $response = Async\await($this->privatePostOrderCancel ($this->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 = $this->safe_dict($response, 'result');
            return $this->parse_order($result);
        }) ();
    }

    public function fetch_open_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * fetch all unfilled currently open orders
             *
             * @see https://github.com/P2B-team/p2b-api-docs/blob/master/api-doc.md#open-orders
             *
             * @param {string} $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 {array} [$params] extra parameters specific to the exchange API endpoint
             *
             * EXCHANGE SPECIFIC PARAMETERS
             * @param {int} [$params->offset] 0-10000, default=0
             * @return {Order[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
             */
            if ($symbol === null) {
                throw new ArgumentsRequired($this->id . ' fetchOpenOrders () requires the $symbol argument');
            }
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'market' => $market['id'],
            );
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            $response = Async\await($this->privatePostOrders ($this->extend($request, $params)));
            //
            //    {
            //        "success" => true,
            //        "errorCode" => "",
            //        "message" => "",
            //        "result" => array(
            //            array(
            //                "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 = $this->safe_list($response, 'result', array());
            return $this->parse_orders($result, $market, $since, $limit);
        }) ();
    }

    public function fetch_order_trades(string $id, ?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($id, $symbol, $since, $limit, $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 {string} $id order $id
             * @param {string} $symbol unified $market $symbol
             * @param {int} [$since] the earliest time in ms to fetch trades for
             * @param {int} [$limit] 1-100, default=50
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             *
             * EXCHANGE SPECIFIC PARAMETERS
             * @param {int} [$params->offset] 0-10000, default=0
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?$id=trade-structure trade structures~
             */
            Async\await($this->load_markets());
            $market = $this->safe_market($symbol);
            $request = array(
                'orderId' => $id,
            );
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            $response = Async\await($this->privatePostAccountOrder ($this->extend($request, $params)));
            //
            //    {
            //        "success" => true,
            //        "errorCode" => "",
            //        "message" => "",
            //        "result" => {
            //            "offset" => 0,
            //            "limit" => 50,
            //            "records" => array(
            //                {
            //                    "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 = $this->safe_value($response, 'result', array());
            $records = $this->safe_list($result, 'records', array());
            return $this->parse_trades($records, $market, $since, $limit);
        }) ();
    }

    public function fetch_my_trades(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($symbol, $since, $limit, $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 {string} $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 {array} [$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
             * @return {Trade[]} a list of ~@link https://docs.ccxt.com/#/?id=public-trades trade structures~
             */
            if ($symbol === null) {
                throw new ArgumentsRequired($this->id . ' fetchMyTrades() requires a $symbol argument');
            }
            Async\await($this->load_markets());
            $until = $this->safe_integer($params, 'until');
            $params = $this->omit($params, 'until');
            if ($until === null) {
                if ($since === null) {
                    $until = $this->milliseconds();
                } else {
                    $until = $since + 86400000;
                }
            }
            if ($since === null) {
                $since = $until - 86400000;
            }
            if (($until - $since) > 86400000) {
                throw new BadRequest($this->id . ' fetchMyTrades () the time between $since and $params["until"] cannot be greater than 24 hours');
            }
            $market = $this->market($symbol);
            $request = array(
                'market' => $market['id'],
                'startTime' => $this->parse_to_int($since / 1000),
                'endTime' => $this->parse_to_int($until / 1000),
            );
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            $response = Async\await($this->privatePostAccountMarketDealHistory ($this->extend($request, $params)));
            //
            //    {
            //        "success" => true,
            //        "errorCode" => "",
            //        "message" => "",
            //        "result" => {
            //            "total" => 2,                                 // Total records in the queried range
            //            "deals" => array(
            //                array(
            //                    "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 = $this->safe_value($response, 'result', array());
            $deals = $this->safe_list($result, 'deals', array());
            return $this->parse_trades($deals, $market, $since, $limit);
        }) ();
    }

    public function fetch_closed_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * 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 {string} $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 {array} [$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
             * @return {Order[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
             */
            Async\await($this->load_markets());
            $until = $this->safe_integer($params, 'until');
            $params = $this->omit($params, 'until');
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
            }
            if ($until === null) {
                if ($since === null) {
                    $until = $this->milliseconds();
                } else {
                    $until = $since + 86400000;
                }
            }
            if ($since === null) {
                $since = $until - 86400000;
            }
            if (($until - $since) > 86400000) {
                throw new BadRequest($this->id . ' fetchClosedOrders () the time between $since and $params["until"] cannot be greater than 24 hours');
            }
            $request = array(
                'startTime' => $this->parse_to_int($since / 1000),
                'endTime' => $this->parse_to_int($until / 1000),
            );
            if ($market !== null) {
                $request['market'] = $market['id'];
            }
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            $response = Async\await($this->privatePostAccountOrderHistory ($this->extend($request, $params)));
            //
            //    {
            //        "success" => true,
            //        "errorCode" => "",
            //        "message" => "",
            //        "result" => {
            //            "LTC_USDT" => array(
            //                {
            //                    "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 = $this->safe_value($response, 'result');
            $orders = array();
            $keys = is_array($result) ? array_keys($result) : array();
            for ($i = 0; $i < count($keys); $i++) {
                $marketId = $keys[$i];
                $marketOrders = $result[$marketId];
                $parsedOrders = $this->parse_orders($marketOrders, $market, $since, $limit);
                $orders = $this->array_concat($orders, $parsedOrders);
            }
            return $orders;
        }) ();
    }

    public function parse_order(array $order, ?array $market = null): array {
        //
        // 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 = $this->safe_integer_product_2($order, 'timestamp', 'ctime', 1000);
        $marketId = $this->safe_string($order, 'market');
        $market = $this->safe_market($marketId, $market);
        return $this->safe_order(array(
            'info' => $order,
            'id' => $this->safe_string_2($order, 'id', 'orderId'),
            'clientOrderId' => null,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'lastTradeTimestamp' => null,
            'symbol' => $market['symbol'],
            'type' => $this->safe_string($order, 'type'),
            'timeInForce' => null,
            'postOnly' => null,
            'side' => $this->safe_string($order, 'side'),
            'price' => $this->safe_string($order, 'price'),
            'triggerPrice' => null,
            'amount' => $this->safe_string($order, 'amount'),
            'cost' => null,
            'average' => null,
            'filled' => $this->safe_string($order, 'dealStock'),
            'remaining' => $this->safe_string($order, 'left'),
            'status' => null,
            'fee' => array(
                'currency' => $market['quote'],
                'cost' => $this->safe_string($order, 'dealFee'),
            ),
            'trades' => null,
        ), $market);
    }

    public function sign($path, $api = 'public', $method = 'GET', $params = array (), $headers = null, $body = null) {
        $url = $this->urls['api'][$api] . '/' . $this->implode_params($path, $params);
        $params = $this->omit($params, $this->extract_params($path));
        if ($method === 'GET') {
            if ($params) {
                $url .= '?' . $this->urlencode($params);
            }
        }
        if ($api === 'private') {
            $params['request'] = '/api/v2/' . $path;
            $params['nonce'] = (string) $this->nonce();
            $payload = base64_encode($this->json($params));  // Body json encoded in base64
            $headers = array(
                'Content-Type' => 'application/json',
                'X-TXC-APIKEY' => $this->apiKey,
                'X-TXC-PAYLOAD' => $payload,
                'X-TXC-SIGNATURE' => $this->hmac($this->encode($payload), $this->encode($this->secret), 'sha512'),
            );
            $body = $this->json($params);
        }
        return array( 'url' => $url, 'method' => $method, 'body' => $body, 'headers' => $headers );
    }

    public function handle_errors(int $code, string $reason, string $url, string $method, array $headers, string $body, $response, $requestHeaders, $requestBody) {
        if ($response === null) {
            return null;
        }
        if ($code === 400) {
            $error = $this->safe_value($response, 'error');
            $errorCode = $this->safe_string($error, 'code');
            $feedback = $this->id . ' ' . $this->json($response);
            $this->throw_exactly_matched_exception($this->exceptions, $errorCode, $feedback);
            // fallback to default $error handler
        }
        return null;
    }
}
