<?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\xt as Exchange;
use ccxt\ExchangeError;
use ccxt\ArgumentsRequired;
use ccxt\BadRequest;
use ccxt\BadSymbol;
use ccxt\InvalidOrder;
use ccxt\NotSupported;
use ccxt\Precise;
use \React\Async;
use \React\Promise;
use \React\Promise\PromiseInterface;

class xt extends Exchange {

    public function describe(): mixed {
        return $this->deep_extend(parent::describe(), array(
            'id' => 'xt',
            'name' => 'XT',
            'countries' => array( 'SC' ), // Seychelles
            // spot api ratelimits are null, 10/s/ip, 50/s/ip, 100/s/ip or 200/s/ip
            // futures 3 requests per second => 1000ms / (100 * 3.33) = 3.003 (get assets -> fetchMarkets & fetchCurrencies)
            // futures 10 requests per second => 1000ms / (100 * 1) = 10 (all other)
            // futures 1000 times per minute for each single IP -> Otherwise account locked for 10min
            'rateLimit' => 100,
            'version' => 'v4',
            'certified' => false,
            'pro' => true,
            'has' => array(
                'CORS' => false,
                'spot' => true,
                'margin' => true,
                'swap' => true,
                'future' => true,
                'option' => false,
                'addMargin' => true,
                'borrowMargin' => false,
                'cancelAllOrders' => true,
                'cancelOrder' => true,
                'cancelOrders' => true,
                'createDepositAddress' => false,
                'createMarketBuyOrderWithCost' => true,
                'createMarketSellOrderWithCost' => false,
                'createOrder' => true,
                'createPostOnlyOrder' => false,
                'createReduceOnlyOrder' => true,
                'editOrder' => true,
                'fetchAccounts' => false,
                'fetchBalance' => true,
                'fetchBidsAsks' => true,
                'fetchBorrowInterest' => false,
                'fetchBorrowRate' => false,
                'fetchBorrowRateHistories' => false,
                'fetchBorrowRateHistory' => false,
                'fetchBorrowRatesPerSymbol' => false,
                'fetchCanceledOrders' => true,
                'fetchClosedOrders' => true,
                'fetchCurrencies' => true,
                'fetchDeposit' => false,
                'fetchDepositAddress' => true,
                'fetchDepositAddresses' => false,
                'fetchDepositAddressesByNetwork' => false,
                'fetchDeposits' => true,
                'fetchDepositWithdrawals' => false,
                'fetchDepositWithdrawFee' => false,
                'fetchDepositWithdrawFees' => false,
                'fetchFundingHistory' => true,
                'fetchFundingInterval' => true,
                'fetchFundingIntervals' => false,
                'fetchFundingRate' => true,
                'fetchFundingRateHistory' => true,
                'fetchFundingRates' => false,
                'fetchIndexOHLCV' => false,
                'fetchL3OrderBook' => false,
                'fetchLedger' => true,
                'fetchLedgerEntry' => false,
                'fetchLeverage' => false,
                'fetchLeverageTiers' => true,
                'fetchMarketLeverageTiers' => true,
                'fetchMarkets' => true,
                'fetchMarkOHLCV' => false,
                'fetchMyTrades' => true,
                'fetchOHLCV' => true,
                'fetchOpenInterest' => false,
                'fetchOpenInterestHistory' => false,
                'fetchOpenOrders' => true,
                'fetchOrder' => true,
                'fetchOrderBook' => true,
                'fetchOrderBooks' => false,
                'fetchOrders' => true,
                'fetchOrdersByStatus' => true,
                'fetchOrderTrades' => false,
                'fetchPosition' => true,
                'fetchPositions' => true,
                'fetchPremiumIndexOHLCV' => false,
                'fetchSettlementHistory' => false,
                'fetchStatus' => false,
                'fetchTicker' => true,
                'fetchTickers' => true,
                'fetchTime' => true,
                'fetchTrades' => true,
                'fetchTradingFee' => false,
                'fetchTradingFees' => false,
                'fetchTradingLimits' => false,
                'fetchTransactionFee' => false,
                'fetchTransactionFees' => false,
                'fetchTransactions' => false,
                'fetchTransfer' => false,
                'fetchTransfers' => false,
                'fetchWithdrawal' => false,
                'fetchWithdrawals' => true,
                'fetchWithdrawalWhitelist' => false,
                'reduceMargin' => true,
                'repayMargin' => false,
                'setLeverage' => true,
                'setMargin' => false,
                'setMarginMode' => true,
                'setPositionMode' => false,
                'signIn' => false,
                'transfer' => true,
                'withdraw' => true,
            ),
            'precisionMode' => TICK_SIZE,
            'urls' => array(
                'logo' => 'https://user-images.githubusercontent.com/14319357/232636712-466df2fc-560a-4ca4-aab2-b1d954a58e24.jpg',
                'api' => array(
                    'spot' => 'https://sapi.xt.com',
                    'linear' => 'https://fapi.xt.com',
                    'inverse' => 'https://dapi.xt.com',
                    'user' => 'https://api.xt.com',
                ),
                'www' => 'https://xt.com',
                'referral' => 'https://www.xt.com/en/accounts/register?ref=9PTM9VW',
                'doc' => array(
                    'https://doc.xt.com/',
                    'https://github.com/xtpub/api-doc',
                ),
                'fees' => 'https://www.xt.com/en/rate',
            ),
            'api' => array(
                'public' => array(
                    'spot' => array(
                        'get' => array(
                            'currencies' => 1,
                            'depth' => 10,
                            'kline' => 1,
                            'symbol' => 1, // 1 for a single symbol
                            'ticker' => 1, // 1 for a single symbol
                            'ticker/book' => 1, // 1 for a single symbol
                            'ticker/price' => 1, // 1 for a single symbol
                            'ticker/24h' => 1, // 1 for a single symbol
                            'time' => 1,
                            'trade/history' => 1,
                            'trade/recent' => 1,
                            'wallet/support/currency' => 1,
                        ),
                    ),
                    'linear' => array(
                        'get' => array(
                            'future/market/v1/public/contract/risk-balance' => 1,
                            'future/market/v1/public/contract/open-interest' => 1,
                            'future/market/v1/public/leverage/bracket/detail' => 1,
                            'future/market/v1/public/leverage/bracket/list' => 1,
                            'future/market/v1/public/q/agg-ticker' => 1,
                            'future/market/v1/public/q/agg-tickers' => 1,
                            'future/market/v1/public/q/deal' => 1,
                            'future/market/v1/public/q/depth' => 1,
                            'future/market/v1/public/q/funding-rate' => 1,
                            'future/market/v1/public/q/funding-rate-record' => 1,
                            'future/market/v1/public/q/index-price' => 1,
                            'future/market/v1/public/q/kline' => 1,
                            'future/market/v1/public/q/mark-price' => 1,
                            'future/market/v1/public/q/symbol-index-price' => 1,
                            'future/market/v1/public/q/symbol-mark-price' => 1,
                            'future/market/v1/public/q/ticker' => 1,
                            'future/market/v1/public/q/tickers' => 1,
                            'future/market/v1/public/symbol/coins' => 3.33,
                            'future/market/v1/public/symbol/detail' => 3.33,
                            'future/market/v1/public/symbol/list' => 1,
                        ),
                    ),
                    'inverse' => array(
                        'get' => array(
                            'future/market/v1/public/contract/risk-balance' => 1,
                            'future/market/v1/public/contract/open-interest' => 1,
                            'future/market/v1/public/leverage/bracket/detail' => 1,
                            'future/market/v1/public/leverage/bracket/list' => 1,
                            'future/market/v1/public/q/agg-ticker' => 1,
                            'future/market/v1/public/q/agg-tickers' => 1,
                            'future/market/v1/public/q/deal' => 1,
                            'future/market/v1/public/q/depth' => 1,
                            'future/market/v1/public/q/funding-rate' => 1,
                            'future/market/v1/public/q/funding-rate-record' => 1,
                            'future/market/v1/public/q/index-price' => 1,
                            'future/market/v1/public/q/kline' => 1,
                            'future/market/v1/public/q/mark-price' => 1,
                            'future/market/v1/public/q/symbol-index-price' => 1,
                            'future/market/v1/public/q/symbol-mark-price' => 1,
                            'future/market/v1/public/q/ticker' => 1,
                            'future/market/v1/public/q/tickers' => 1,
                            'future/market/v1/public/symbol/coins' => 3.33,
                            'future/market/v1/public/symbol/detail' => 3.33,
                            'future/market/v1/public/symbol/list' => 1,
                        ),
                    ),
                ),
                'private' => array(
                    'spot' => array(
                        'get' => array(
                            'balance' => 1,
                            'balances' => 1,
                            'batch-order' => 1,
                            'deposit/address' => 1,
                            'deposit/history' => 1,
                            'history-order' => 1,
                            'open-order' => 1,
                            'order' => 1,
                            'order/{orderId}' => 1,
                            'trade' => 1,
                            'withdraw/history' => 1,
                        ),
                        'post' => array(
                            'order' => 0.2,
                            'withdraw' => 10,
                            'balance/transfer' => 1,
                            'balance/account/transfer' => 1,
                            'ws-token' => 1,
                        ),
                        'delete' => array(
                            'batch-order' => 1,
                            'open-order' => 1,
                            'order/{orderId}' => 1,
                        ),
                        'put' => array(
                            'order/{orderId}' => 1,
                        ),
                    ),
                    'linear' => array(
                        'get' => array(
                            'future/trade/v1/entrust/plan-detail' => 1,
                            'future/trade/v1/entrust/plan-list' => 1,
                            'future/trade/v1/entrust/plan-list-history' => 1,
                            'future/trade/v1/entrust/profit-detail' => 1,
                            'future/trade/v1/entrust/profit-list' => 1,
                            'future/trade/v1/order/detail' => 1,
                            'future/trade/v1/order/list' => 1,
                            'future/trade/v1/order/list-history' => 1,
                            'future/trade/v1/order/trade-list' => 1,
                            'future/user/v1/account/info' => 1,
                            'future/user/v1/balance/bills' => 1,
                            'future/user/v1/balance/detail' => 1,
                            'future/user/v1/balance/funding-rate-list' => 1,
                            'future/user/v1/balance/list' => 1,
                            'future/user/v1/position/adl' => 1,
                            'future/user/v1/position/list' => 1,
                            'future/user/v1/user/collection/list' => 1,
                            'future/user/v1/user/listen-key' => 1,
                        ),
                        'post' => array(
                            'future/trade/v1/entrust/cancel-all-plan' => 1,
                            'future/trade/v1/entrust/cancel-all-profit-stop' => 1,
                            'future/trade/v1/entrust/cancel-plan' => 1,
                            'future/trade/v1/entrust/cancel-profit-stop' => 1,
                            'future/trade/v1/entrust/create-plan' => 1,
                            'future/trade/v1/entrust/create-profit' => 1,
                            'future/trade/v1/entrust/update-profit-stop' => 1,
                            'future/trade/v1/order/cancel' => 1,
                            'future/trade/v1/order/cancel-all' => 1,
                            'future/trade/v1/order/create' => 1,
                            'future/trade/v1/order/create-batch' => 1,
                            'future/trade/v1/order/update' => 1,
                            'future/user/v1/account/open' => 1,
                            'future/user/v1/position/adjust-leverage' => 1,
                            'future/user/v1/position/auto-margin' => 1,
                            'future/user/v1/position/close-all' => 1,
                            'future/user/v1/position/margin' => 1,
                            'future/user/v1/user/collection/add' => 1,
                            'future/user/v1/user/collection/cancel' => 1,
                            'future/user/v1/position/change-type' => 1,
                        ),
                    ),
                    'inverse' => array(
                        'get' => array(
                            'future/trade/v1/entrust/plan-detail' => 1,
                            'future/trade/v1/entrust/plan-list' => 1,
                            'future/trade/v1/entrust/plan-list-history' => 1,
                            'future/trade/v1/entrust/profit-detail' => 1,
                            'future/trade/v1/entrust/profit-list' => 1,
                            'future/trade/v1/order/detail' => 1,
                            'future/trade/v1/order/list' => 1,
                            'future/trade/v1/order/list-history' => 1,
                            'future/trade/v1/order/trade-list' => 1,
                            'future/user/v1/account/info' => 1,
                            'future/user/v1/balance/bills' => 1,
                            'future/user/v1/balance/detail' => 1,
                            'future/user/v1/balance/funding-rate-list' => 1,
                            'future/user/v1/balance/list' => 1,
                            'future/user/v1/position/adl' => 1,
                            'future/user/v1/position/list' => 1,
                            'future/user/v1/user/collection/list' => 1,
                            'future/user/v1/user/listen-key' => 1,
                        ),
                        'post' => array(
                            'future/trade/v1/entrust/cancel-all-plan' => 1,
                            'future/trade/v1/entrust/cancel-all-profit-stop' => 1,
                            'future/trade/v1/entrust/cancel-plan' => 1,
                            'future/trade/v1/entrust/cancel-profit-stop' => 1,
                            'future/trade/v1/entrust/create-plan' => 1,
                            'future/trade/v1/entrust/create-profit' => 1,
                            'future/trade/v1/entrust/update-profit-stop' => 1,
                            'future/trade/v1/order/cancel' => 1,
                            'future/trade/v1/order/cancel-all' => 1,
                            'future/trade/v1/order/create' => 1,
                            'future/trade/v1/order/create-batch' => 1,
                            'future/trade/v1/order/update' => 1,
                            'future/user/v1/account/open' => 1,
                            'future/user/v1/position/adjust-leverage' => 1,
                            'future/user/v1/position/auto-margin' => 1,
                            'future/user/v1/position/close-all' => 1,
                            'future/user/v1/position/margin' => 1,
                            'future/user/v1/user/collection/add' => 1,
                            'future/user/v1/user/collection/cancel' => 1,
                        ),
                    ),
                    'user' => array(
                        'get' => array(
                            'user/account' => 1,
                            'user/account/api-key' => 1,
                        ),
                        'post' => array(
                            'user/account' => 1,
                            'user/account/api-key' => 1,
                        ),
                        'put' => array(
                            'user/account/api-key' => 1,
                        ),
                        'delete' => array(
                            'user/account/{apikeyId}' => 1,
                        ),
                    ),
                ),
            ),
            'fees' => array(
                'spot' => array(
                    'tierBased' => true,
                    'percentage' => true,
                    'maker' => $this->parse_number('0.002'),
                    'taker' => $this->parse_number('0.002'),
                    'tiers' => array(
                        'maker' => array(
                            array( $this->parse_number('0'), $this->parse_number('0.002') ),
                            array( $this->parse_number('5000'), $this->parse_number('0.0018') ),
                            array( $this->parse_number('10000'), $this->parse_number('0.0016') ),
                            array( $this->parse_number('20000'), $this->parse_number('0.0014') ),
                            array( $this->parse_number('50000'), $this->parse_number('0.0012') ),
                            array( $this->parse_number('150000'), $this->parse_number('0.0010') ),
                            array( $this->parse_number('300000'), $this->parse_number('0.0008') ),
                            array( $this->parse_number('600000'), $this->parse_number('0.0007') ),
                            array( $this->parse_number('1200000'), $this->parse_number('0.0006') ),
                            array( $this->parse_number('2500000'), $this->parse_number('0.0005') ),
                            array( $this->parse_number('6000000'), $this->parse_number('0.0004') ),
                            array( $this->parse_number('15000000'), $this->parse_number('0.0003') ),
                            array( $this->parse_number('30000000'), $this->parse_number('0.0002') ),
                        ),
                        'taker' => array(
                            array( $this->parse_number('0'), $this->parse_number('0.002') ),
                            array( $this->parse_number('5000'), $this->parse_number('0.0018') ),
                            array( $this->parse_number('10000'), $this->parse_number('0.0016') ),
                            array( $this->parse_number('20000'), $this->parse_number('0.0014') ),
                            array( $this->parse_number('50000'), $this->parse_number('0.0012') ),
                            array( $this->parse_number('150000'), $this->parse_number('0.0010') ),
                            array( $this->parse_number('300000'), $this->parse_number('0.0008') ),
                            array( $this->parse_number('600000'), $this->parse_number('0.0007') ),
                            array( $this->parse_number('1200000'), $this->parse_number('0.0006') ),
                            array( $this->parse_number('2500000'), $this->parse_number('0.0005') ),
                            array( $this->parse_number('6000000'), $this->parse_number('0.0004') ),
                            array( $this->parse_number('15000000'), $this->parse_number('0.0003') ),
                            array( $this->parse_number('30000000'), $this->parse_number('0.0002') ),
                        ),
                    ),
                ),
                'contract' => array(
                    'tierBased' => true,
                    'percentage' => true,
                    'maker' => $this->parse_number('0.0004'),
                    'taker' => $this->parse_number('0.0006'),
                    'tiers' => array(
                        'maker' => array(
                            array( $this->parse_number('0'), $this->parse_number('0.0004') ),
                            array( $this->parse_number('200000'), $this->parse_number('0.00038') ),
                            array( $this->parse_number('1000000'), $this->parse_number('0.00036') ),
                            array( $this->parse_number('5000000'), $this->parse_number('0.00034') ),
                            array( $this->parse_number('10000000'), $this->parse_number('0.00032') ),
                            array( $this->parse_number('15000000'), $this->parse_number('0.00028') ),
                            array( $this->parse_number('30000000'), $this->parse_number('0.00024') ),
                            array( $this->parse_number('50000000'), $this->parse_number('0.0002') ),
                            array( $this->parse_number('100000000'), $this->parse_number('0.00016') ),
                            array( $this->parse_number('300000000'), $this->parse_number('0.00012') ),
                            array( $this->parse_number('500000000'), $this->parse_number('0.00008') ),
                        ),
                        'taker' => array(
                            array( $this->parse_number('0'), $this->parse_number('0.0006') ),
                            array( $this->parse_number('200000'), $this->parse_number('0.000588') ),
                            array( $this->parse_number('1000000'), $this->parse_number('0.00057') ),
                            array( $this->parse_number('5000000'), $this->parse_number('0.00054') ),
                            array( $this->parse_number('10000000'), $this->parse_number('0.00051') ),
                            array( $this->parse_number('15000000'), $this->parse_number('0.00048') ),
                            array( $this->parse_number('30000000'), $this->parse_number('0.00045') ),
                            array( $this->parse_number('50000000'), $this->parse_number('0.00045') ),
                            array( $this->parse_number('100000000'), $this->parse_number('0.00036') ),
                            array( $this->parse_number('300000000'), $this->parse_number('0.00033') ),
                            array( $this->parse_number('500000000'), $this->parse_number('0.0003') ),
                        ),
                    ),
                ),
            ),
            'exceptions' => array(
                'exact' => array(
                    '400' => '\\ccxt\\NetworkError', // array("returnCode":1,"msgInfo":"failure","error":array("code":"400","msg":"Connection refused => /10.0.26.71:8080"),"result":null)
                    '404' => '\\ccxt\\ExchangeError', // interface does not exist
                    '429' => '\\ccxt\\RateLimitExceeded', // The request is too frequent, please control the request rate according to the speed limit requirement
                    '500' => '\\ccxt\\ExchangeError', // Service exception
                    '502' => '\\ccxt\\ExchangeError', // Gateway exception
                    '503' => '\\ccxt\\OnMaintenance', // Service unavailable, please try again later
                    'AUTH_001' => '\\ccxt\\AuthenticationError', // missing request header xt-validate-appkey
                    'AUTH_002' => '\\ccxt\\AuthenticationError', // missing request header xt-validate-timestamp
                    'AUTH_003' => '\\ccxt\\AuthenticationError', // missing request header xt-validate-recvwindow
                    'AUTH_004' => '\\ccxt\\AuthenticationError', // bad request header xt-validate-recvwindow
                    'AUTH_005' => '\\ccxt\\AuthenticationError', // missing request header xt-validate-algorithms
                    'AUTH_006' => '\\ccxt\\AuthenticationError', // bad request header xt-validate-algorithms
                    'AUTH_007' => '\\ccxt\\AuthenticationError', // missing request header xt-validate-signature
                    'AUTH_101' => '\\ccxt\\AuthenticationError', // ApiKey does not exist
                    'AUTH_102' => '\\ccxt\\AuthenticationError', // ApiKey is not activated
                    'AUTH_103' => '\\ccxt\\AuthenticationError', // Signature error, array("rc":1,"mc":"AUTH_103","ma":array(),"result":null)
                    'AUTH_104' => '\\ccxt\\AuthenticationError', // Unbound IP request
                    'AUTH_105' => '\\ccxt\\AuthenticationError', // outdated message
                    'AUTH_106' => '\\ccxt\\PermissionDenied', // Exceeded apikey permission
                    'SYMBOL_001' => '\\ccxt\\BadSymbol', // Symbol not exist
                    'SYMBOL_002' => '\\ccxt\\BadSymbol', // Symbol offline
                    'SYMBOL_003' => '\\ccxt\\BadSymbol', // Symbol suspend trading
                    'SYMBOL_004' => '\\ccxt\\BadSymbol', // Symbol country disallow trading
                    'SYMBOL_005' => '\\ccxt\\BadSymbol', // The symbol does not support trading via API
                    'ORDER_001' => '\\ccxt\\InvalidOrder', // Platform rejection
                    'ORDER_002' => '\\ccxt\\InsufficientFunds', // insufficient funds
                    'ORDER_003' => '\\ccxt\\InvalidOrder', // Trading Pair Suspended
                    'ORDER_004' => '\\ccxt\\InvalidOrder', // no transaction
                    'ORDER_005' => '\\ccxt\\InvalidOrder', // Order not exist
                    'ORDER_006' => '\\ccxt\\InvalidOrder', // Too many open orders
                    'ORDER_007' => '\\ccxt\\PermissionDenied', // The sub-account has no transaction authority
                    'ORDER_F0101' => '\\ccxt\\InvalidOrder', // Trigger Price Filter - Min
                    'ORDER_F0102' => '\\ccxt\\InvalidOrder', // Trigger Price Filter - Max
                    'ORDER_F0103' => '\\ccxt\\InvalidOrder', // Trigger Price Filter - Step Value
                    'ORDER_F0201' => '\\ccxt\\InvalidOrder', // Trigger Quantity Filter - Min
                    'ORDER_F0202' => '\\ccxt\\InvalidOrder', // Trigger Quantity Filter - Max
                    'ORDER_F0203' => '\\ccxt\\InvalidOrder', // Trigger Quantity Filter - Step Value
                    'ORDER_F0301' => '\\ccxt\\InvalidOrder', // Trigger QUOTE_QTY Filter - Min Value
                    'ORDER_F0401' => '\\ccxt\\InvalidOrder', // Trigger PROTECTION_ONLINE Filter
                    'ORDER_F0501' => '\\ccxt\\InvalidOrder', // Trigger PROTECTION_LIMIT Filter - Buy Max Deviation
                    'ORDER_F0502' => '\\ccxt\\InvalidOrder', // Trigger PROTECTION_LIMIT Filter - Sell Max Deviation
                    'ORDER_F0601' => '\\ccxt\\InvalidOrder', // Trigger PROTECTION_MARKET Filter
                    'COMMON_001' => '\\ccxt\\ExchangeError', // The user does not exist
                    'COMMON_002' => '\\ccxt\\ExchangeError', // System busy, please try it later
                    'COMMON_003' => '\\ccxt\\BadRequest', // Operation failed, please try it later
                    'CURRENCY_001' => '\\ccxt\\BadRequest', // Information of currency is abnormal
                    'DEPOSIT_001' => '\\ccxt\\BadRequest', // Deposit is not open
                    'DEPOSIT_002' => '\\ccxt\\PermissionDenied', // The current account security level is low, please bind any two security verifications in mobile phone/email/Google Authenticator before deposit
                    'DEPOSIT_003' => '\\ccxt\\BadRequest', // The format of address is incorrect, please enter again
                    'DEPOSIT_004' => '\\ccxt\\BadRequest', // The address is already exists, please enter again
                    'DEPOSIT_005' => '\\ccxt\\BadRequest', // Can not find the address of offline wallet
                    'DEPOSIT_006' => '\\ccxt\\BadRequest', // No deposit address, please try it later
                    'DEPOSIT_007' => '\\ccxt\\BadRequest', // Address is being generated, please try it later
                    'DEPOSIT_008' => '\\ccxt\\BadRequest', // Deposit is not available
                    'WITHDRAW_001' => '\\ccxt\\BadRequest', // Withdraw is not open
                    'WITHDRAW_002' => '\\ccxt\\BadRequest', // The withdrawal address is invalid
                    'WITHDRAW_003' => '\\ccxt\\PermissionDenied', // The current account security level is low, please bind any two security verifications in mobile phone/email/Google Authenticator before withdraw
                    'WITHDRAW_004' => '\\ccxt\\BadRequest', // The withdrawal address is not added
                    'WITHDRAW_005' => '\\ccxt\\BadRequest', // The withdrawal address cannot be empty
                    'WITHDRAW_006' => '\\ccxt\\BadRequest', // Memo cannot be empty
                    'WITHDRAW_008' => '\\ccxt\\PermissionDenied', // Risk control is triggered, withdraw of this currency is not currently supported
                    'WITHDRAW_009' => '\\ccxt\\PermissionDenied', // Withdraw failed, some property_exists($this, assets) withdraw are restricted by T+1 withdraw
                    'WITHDRAW_010' => '\\ccxt\\BadRequest', // The precision of withdrawal is invalid
                    'WITHDRAW_011' => '\\ccxt\\InsufficientFunds', // free balance is not enough
                    'WITHDRAW_012' => '\\ccxt\\PermissionDenied', // Withdraw failed, your remaining withdrawal limit today is not enough
                    'WITHDRAW_013' => '\\ccxt\\PermissionDenied', // Withdraw failed, your remaining withdrawal limit today is not enough, the withdrawal amount can be increased by completing a higher level of real-name authentication
                    'WITHDRAW_014' => '\\ccxt\\BadRequest', // This withdrawal address cannot be used in the internal transfer function, please cancel the internal transfer function before submitting
                    'WITHDRAW_015' => '\\ccxt\\BadRequest', // The withdrawal amount is not enough to deduct the handling fee
                    'WITHDRAW_016' => '\\ccxt\\BadRequest', // This withdrawal address is already exists
                    'WITHDRAW_017' => '\\ccxt\\BadRequest', // This withdrawal has been processed and cannot be canceled
                    'WITHDRAW_018' => '\\ccxt\\BadRequest', // Memo must be a number
                    'WITHDRAW_019' => '\\ccxt\\BadRequest', // Memo is incorrect, please enter again
                    'WITHDRAW_020' => '\\ccxt\\PermissionDenied', // Your withdrawal amount has reached the upper limit for today, please try it tomorrow
                    'WITHDRAW_021' => '\\ccxt\\PermissionDenied', // Your withdrawal amount has reached the upper limit for today, you can only withdraw up to {0} this time
                    'WITHDRAW_022' => '\\ccxt\\BadRequest', // Withdrawal amount must be greater than {0}
                    'WITHDRAW_023' => '\\ccxt\\BadRequest', // Withdrawal amount must be less than {0}
                    'WITHDRAW_024' => '\\ccxt\\BadRequest', // Withdraw is not supported
                    'WITHDRAW_025' => '\\ccxt\\BadRequest', // Please create a FIO address in the deposit page
                    'FUND_001' => '\\ccxt\\BadRequest', // Duplicate request (a bizId can only be requested once)
                    'FUND_002' => '\\ccxt\\InsufficientFunds', // Insufficient account balance
                    'FUND_003' => '\\ccxt\\BadRequest', // Transfer operations are not supported (for example, sub-accounts do not support financial transfers)
                    'FUND_004' => '\\ccxt\\ExchangeError', // Unfreeze failed
                    'FUND_005' => '\\ccxt\\PermissionDenied', // Transfer prohibited
                    'FUND_014' => '\\ccxt\\BadRequest', // The transfer-in account id and transfer-out account ID cannot be the same
                    'FUND_015' => '\\ccxt\\BadRequest', // From and to business types cannot be the same
                    'FUND_016' => '\\ccxt\\BadRequest', // Leverage transfer, symbol cannot be empty
                    'FUND_017' => '\\ccxt\\BadRequest', // Parameter error
                    'FUND_018' => '\\ccxt\\BadRequest', // Invalid freeze record
                    'FUND_019' => '\\ccxt\\BadRequest', // Freeze users not equal
                    'FUND_020' => '\\ccxt\\BadRequest', // Freeze currency are not equal
                    'FUND_021' => '\\ccxt\\BadRequest', // Operation not supported
                    'FUND_022' => '\\ccxt\\BadRequest', // Freeze record does not exist
                    'FUND_044' => '\\ccxt\\BadRequest', // The maximum length of the amount is 113 and cannot exceed the limit
                    'TRANSFER_001' => '\\ccxt\\BadRequest', // Duplicate request (a bizId can only be requested once)
                    'TRANSFER_002' => '\\ccxt\\InsufficientFunds', // Insufficient account balance
                    'TRANSFER_003' => '\\ccxt\\BadRequest', // User not registered
                    'TRANSFER_004' => '\\ccxt\\PermissionDenied', // The currency is not allowed to be transferred
                    'TRANSFER_005' => '\\ccxt\\PermissionDenied', // The user’s currency is not allowed to be transferred
                    'TRANSFER_006' => '\\ccxt\\PermissionDenied', // Transfer prohibited
                    'TRANSFER_007' => '\\ccxt\\RequestTimeout', // Request timed out
                    'TRANSFER_008' => '\\ccxt\\BadRequest', // Transferring to a leveraged account is abnormal
                    'TRANSFER_009' => '\\ccxt\\BadRequest', // Departing from a leveraged account is abnormal
                    'TRANSFER_010' => '\\ccxt\\PermissionDenied', // Leverage cleared, transfer prohibited
                    'TRANSFER_011' => '\\ccxt\\PermissionDenied', // Leverage with borrowing, transfer prohibited
                    'TRANSFER_012' => '\\ccxt\\PermissionDenied', // Currency transfer prohibited
                    'symbol_not_support_trading_via_api' => '\\ccxt\\BadSymbol', // array("returnCode":1,"msgInfo":"failure","error":array("code":"symbol_not_support_trading_via_api","msg":"The symbol does not support trading via API"),"result":null)
                    'open_order_min_nominal_value_limit' => '\\ccxt\\InvalidOrder', // array("returnCode":1,"msgInfo":"failure","error":array("code":"open_order_min_nominal_value_limit","msg":"Exceeds the minimum notional value of a single order"),"result":null)
                    'insufficient_balance' => '\\ccxt\\InsufficientFunds',
                ),
                'broad' => array(
                    'The symbol does not support trading via API' => '\\ccxt\\BadSymbol', // array("returnCode":1,"msgInfo":"failure","error":array("code":"symbol_not_support_trading_via_api","msg":"The symbol does not support trading via API"),"result":null)
                    'Exceeds the minimum notional value of a single order' => '\\ccxt\\InvalidOrder', // array("returnCode":1,"msgInfo":"failure","error":array("code":"open_order_min_nominal_value_limit","msg":"Exceeds the minimum notional value of a single order"),"result":null)
                    'insufficient balance' => '\\ccxt\\InsufficientFunds',
                ),
            ),
            'timeframes' => array(
                '1m' => '1m',
                '5m' => '5m',
                '15m' => '15m',
                '30m' => '30m',
                '1h' => '1h', // spot only
                '2h' => '2h', // spot only
                '4h' => '4h',
                '6h' => '6h', // spot only
                '8h' => '8h', // spot only
                '1d' => '1d',
                '3d' => '3d', // spot only
                '1w' => '1w',
                '1M' => '1M', // spot only
            ),
            'commonCurrencies' => array(),
            'options' => array(
                'adjustForTimeDifference' => false,
                'timeDifference' => 0,
                'accountsById' => array(
                    'spot' => 'SPOT',
                    'leverage' => 'LEVER',
                    'finance' => 'FINANCE',
                    'swap' => 'FUTURES_U',
                    'future' => 'FUTURES_U',
                    'linear' => 'FUTURES_U',
                    'inverse' => 'FUTURES_C',
                ),
                'networks' => array(
                    'ERC20' => 'Ethereum',
                    'TRC20' => 'Tron',
                    'BEP20' => 'BNB Smart Chain',
                    'BEP2' => 'BNB-BEP2',
                    'ETH' => 'Ethereum',
                    'TRON' => 'Tron',
                    'BNB' => 'BNB Smart Chain',
                    'AVAX' => 'AVAX C-Chain',
                    'GAL' => 'GAL(FT)',
                    'ALEO' => 'ALEO(IOU)',
                    'BTC' => 'Bitcoin',
                    'XT' => 'XT Smart Chain',
                    'ETC' => 'Ethereum Classic',
                    'MATIC' => 'Polygon',
                    'LTC' => 'Litecoin',
                    'BTS' => 'BitShares',
                    'XRP' => 'Ripple',
                    'XLM' => 'Stellar Network',
                    'ADA' => 'Cardano',
                    'XWC' => 'XWC-XWC',
                    'DOGE' => 'dogecoin',
                    'DCR' => 'Decred',
                    'SC' => 'Siacoin',
                    'XTZ' => 'Tezos',
                    'ZEC' => 'Zcash',
                    'XMR' => 'Monero',
                    'LSK' => 'Lisk',
                    'ATOM' => 'Cosmos',
                    'ONT' => 'Ontology',
                    'ALGO' => 'Algorand',
                    'SOL' => 'SOL-SOL',
                    'DOT' => 'Polkadot',
                    'ZEN' => 'Horizen',
                    'FIL' => 'Filecoin',
                    'CHZ' => 'chz',
                    'ICP' => 'Internet Computer',
                    'KSM' => 'Kusama',
                    'LUNA' => 'Terra',
                    'THETA' => 'Theta Token',
                    'FTM' => 'Fantom',
                    'VET' => 'VeChain',
                    'NEAR' => 'NEAR Protocol',
                    'ONE' => 'Harmony',
                    'KLAY' => 'Klaytn',
                    'AR' => 'Arweave',
                    'CELT' => 'OKT',
                    'EGLD' => 'Elrond eGold',
                    'CRO' => 'CRO-CRONOS',
                    'BCH' => 'Bitcoin Cash',
                    'GLMR' => 'Moonbeam',
                    'LOOP' => 'LOOP-LRC',
                    'REI' => 'REI Network',
                    'ASTR' => 'Astar Network',
                    'OP' => 'OPT',
                    'MMT' => 'MMT-MMT',
                    'TBC' => 'TBC-TBC',
                    'OMAX' => 'OMAX-OMAX CHAIN',
                    'GMMT' => 'GMMT chain',
                    'ZIL' => 'Zilliqa',
                ),
                'networksById' => array(
                    'Ethereum' => 'ERC20',
                    'Tron' => 'TRC20',
                    'BNB Smart Chain' => 'BEP20',
                    'BNB-BEP2' => 'BEP2',
                    'Bitcoin' => 'BTC',
                    'XT Smart Chain' => 'XT',
                    'Ethereum Classic' => 'ETC',
                    'Polygon' => 'MATIC',
                    'Litecoin' => 'LTC',
                    'BitShares' => 'BTS',
                    'Ripple' => 'XRP',
                    'Stellar Network' => 'XLM',
                    'Cardano' => 'ADA',
                    'XWC-XWC' => 'XWC',
                    'dogecoin' => 'DOGE',
                    'Decred' => 'DCR',
                    'Siacoin' => 'SC',
                    'Tezos' => 'XTZ',
                    'Zcash' => 'ZEC',
                    'Monero' => 'XMR',
                    'Lisk' => 'LSK',
                    'Cosmos' => 'ATOM',
                    'Ontology' => 'ONT',
                    'Algorand' => 'ALGO',
                    'SOL-SOL' => 'SOL',
                    'Polkadot' => 'DOT',
                    'Horizen' => 'ZEN',
                    'Filecoin' => 'FIL',
                    'chz' => 'CHZ',
                    'Internet Computer' => 'ICP',
                    'Kusama' => 'KSM',
                    'Terra' => 'LUNA',
                    'Theta Token' => 'THETA',
                    'Fantom' => 'FTM',
                    'VeChain' => 'VET',
                    'AVAX C-Chain' => 'AVAX',
                    'NEAR Protocol' => 'NEAR',
                    'Harmony' => 'ONE',
                    'Klaytn' => 'KLAY',
                    'Arweave' => 'AR',
                    'OKT' => 'CELT',
                    'Elrond eGold' => 'EGLD',
                    'CRO-CRONOS' => 'CRO',
                    'Bitcoin Cash' => 'BCH',
                    'Moonbeam' => 'GLMR',
                    'LOOP-LRC' => 'LOOP',
                    'REI Network' => 'REI',
                    'Astar Network' => 'ASTR',
                    'GAL(FT)' => 'GAL',
                    'ALEO(IOU)' => 'ALEO',
                    'OPT' => 'OP',
                    'MMT-MMT' => 'MMT',
                    'TBC-TBC' => 'TBC',
                    'OMAX-OMAX CHAIN' => 'OMAX',
                    'GMMT chain' => 'GMMT',
                    'Zilliqa' => 'ZIL',
                ),
                'createMarketBuyOrderRequiresPrice' => true,
                'recvWindow' => '5000', // in milliseconds, spot only
            ),
            'features' => array(
                'default' => 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' => true,
                        'marketBuyRequiresPrice' => false,
                        'selfTradePrevention' => false,
                        'iceberg' => false,
                    ),
                    'createOrders' => null,
                    'fetchMyTrades' => array(
                        'marginMode' => true,
                        'limit' => 100,
                        'daysBack' => 100000, // todo
                        'untilDays' => 100000, // todo
                        'marketType' => true,
                        'subType' => true,
                        'symbolRequired' => false,
                    ),
                    'fetchOrder' => array(
                        'marginMode' => false,
                        'trigger' => true, // todo TPSL kind
                        'trailing' => false,
                        'marketType' => true,
                        'subType' => true,
                        'symbolRequired' => false,
                    ),
                    'fetchOpenOrders' => array(
                        'marginMode' => true,
                        'limit' => 100,
                        'trigger' => true, // todo TPSL
                        'trailing' => false,
                        'marketType' => true,
                        'subType' => true,
                        'symbolRequired' => false,
                    ),
                    'fetchOrders' => array(
                        'marginMode' => true,
                        'limit' => 100,
                        'daysBack' => 100000, // todo
                        'untilDays' => 100000, // todo
                        'trigger' => true, // todo TPSL
                        'trailing' => false,
                        'marketType' => true,
                        'subType' => true,
                        'symbolRequired' => false,
                    ),
                    'fetchClosedOrders' => array(
                        'marginMode' => true,
                        'limit' => 100,
                        'daysBack' => 100000, // todo
                        'daysBackCanceled' => 1, // todo
                        'untilDays' => 100000, // todo
                        'trigger' => true, // todo TPSL
                        'trailing' => false,
                        'marketType' => true,
                        'subType' => true,
                        'symbolRequired' => false,
                    ),
                    'fetchOHLCV' => array(
                        'limit' => 1000, // todo for derivatives
                    ),
                ),
                'spot' => array(
                    'extends' => 'default',
                ),
                'forDerivatives' => array(
                    'extends' => 'default',
                    'createOrder' => array(
                        'triggerPrice' => true,
                        // todo
                        'triggerPriceType' => array(
                            'last' => true,
                            'mark' => true,
                            'index' => true,
                        ),
                        'stopLossPrice' => true,
                        'takeProfitPrice' => true,
                    ),
                    'fetchMyTrades' => array(
                        'daysBack' => null,
                        'untilDays' => null,
                    ),
                ),
                'swap' => array(
                    'linear' => array(
                        'extends' => 'forDerivatives',
                    ),
                    'inverse' => array(
                        'extends' => 'forDerivatives',
                    ),
                ),
                'future' => array(
                    'linear' => array(
                        'extends' => 'forDerivatives',
                    ),
                    'inverse' => array(
                        'extends' => 'forDerivatives',
                    ),
                ),
            ),
        ));
    }

    public function nonce() {
        return $this->milliseconds() - $this->options['timeDifference'];
    }

    public function fetch_time($params = array ()): PromiseInterface {
        return Async\async(function () use ($params) {
            /**
             * fetches the current integer timestamp in milliseconds from the xt server
             *
             * @see https://doc.xt.com/#market1serverInfo
             *
             * @param {array} $params extra parameters specific to the xt api endpoint
             * @return {int} the current integer timestamp in milliseconds from the xt server
             */
            $response = Async\await($this->publicSpotGetTime ($params));
            //
            //     {
            //         "rc" => 0,
            //         "mc" => "SUCCESS",
            //         "ma" => array(),
            //         "result" => {
            //             "serverTime" => 1677823301643
            //         }
            //     }
            //
            $data = $this->safe_value($response, 'result');
            return $this->safe_integer($data, 'serverTime');
        }) ();
    }

    public function fetch_currencies($params = array ()): PromiseInterface {
        return Async\async(function () use ($params) {
            /**
             * fetches all available currencies on an exchange
             *
             * @see https://doc.xt.com/#deposit_withdrawalsupportedCurrenciesGet
             *
             * @param {array} $params extra parameters specific to the xt api endpoint
             * @return {array} an associative dictionary of currencies
             */
            $promisesRaw = array( $this->publicSpotGetWalletSupportCurrency ($params), $this->publicSpotGetCurrencies ($params) );
            list($chainsResponse, $currenciesResponse) = Async\await(Promise\all($promisesRaw));
            //
            // currencies
            //
            //    {
            //        "time" => "1686626116145",
            //        "version" => "5dbbb2f2527c22b2b2e3b47187ef13d1",
            //        "currencies" => [
            //            array(
            //                "id" => "2",
            //                "currency" => "btc",
            //                "fullName" => "Bitcoin",
            //                "logo" => "https://a.static-global.com/1/currency/btc.png",
            //                "cmcLink" => "https://coinmarketcap.com/currencies/bitcoin/",
            //                "weight" => "99999",
            //                "maxPrecision" => "10",
            //                "depositStatus" => "1",
            //                "withdrawStatus" => "1",
            //                "convertEnabled" => "1",
            //                "transferEnabled" => "1",
            //                "isChainExist" => "1",
            //                "plates" => [152]
            //            ),
            //        ],
            //    }
            //
            //
            // chains
            //
            //     {
            //         "rc" => 0,
            //         "mc" => "SUCCESS",
            //         "ma" => array(),
            //         "result" => array(
            //             array(
            //                 "currency" => "btc",
            //                 "supportChains" => array(
            //                     array(
            //                         "chain" => "Bitcoin",
            //                         "depositEnabled" => true,
            //                         "withdrawEnabled" => true,
            //                         "withdrawFeeAmount" => 0.0009,
            //                         "withdrawMinAmount" => 0.0005,
            //                         "depositFeeRate" => 0
            //                     ),
            //                 )
            //             ),
            //         )
            //     }
            //
            // note => individual network's full data is available on per-currency endpoint => https://www.xt.com/sapi/v4/balance/public/currency/11
            //
            $chainsData = $this->safe_value($chainsResponse, 'result', array());
            $currenciesResult = $this->safe_value($currenciesResponse, 'result', array());
            $currenciesData = $this->safe_value($currenciesResult, 'currencies', array());
            $chainsDataIndexed = $this->index_by($chainsData, 'currency');
            $result = array();
            for ($i = 0; $i < count($currenciesData); $i++) {
                $entry = $currenciesData[$i];
                $currencyId = $this->safe_string($entry, 'currency');
                $code = $this->safe_currency_code($currencyId);
                $networkEntry = $this->safe_value($chainsDataIndexed, $currencyId, array());
                $rawNetworks = $this->safe_value($networkEntry, 'supportChains', array());
                $networks = array();
                for ($j = 0; $j < count($rawNetworks); $j++) {
                    $rawNetwork = $rawNetworks[$j];
                    $networkId = $this->safe_string($rawNetwork, 'chain');
                    $networkCode = $this->network_id_to_code($networkId, $code);
                    $networks[$networkCode] = array(
                        'info' => $rawNetwork,
                        'id' => $networkId,
                        'network' => $networkCode,
                        'name' => null,
                        'active' => null,
                        'fee' => $this->safe_number($rawNetwork, 'withdrawFeeAmount'),
                        'precision' => null,
                        'deposit' => $this->safe_bool($rawNetwork, 'depositEnabled'),
                        'withdraw' => $this->safe_bool($rawNetwork, 'withdrawEnabled'),
                        'limits' => array(
                            'amount' => array(
                                'min' => null,
                                'max' => null,
                            ),
                            'withdraw' => array(
                                'min' => $this->safe_number($rawNetwork, 'withdrawMinAmount'),
                                'max' => null,
                            ),
                            'deposit' => array(
                                'min' => null,
                                'max' => null,
                            ),
                        ),
                    );
                }
                $typeRaw = $this->safe_string($entry, 'type');
                $type = null;
                if ($typeRaw === 'FT') {
                    $type = 'crypto';
                } else {
                    $type = 'other';
                }
                $result[$code] = $this->safe_currency_structure(array(
                    'info' => $entry,
                    'id' => $currencyId,
                    'code' => $code,
                    'name' => $this->safe_string($entry, 'fullName'),
                    'active' => null,
                    'fee' => null,
                    'precision' => $this->parse_number($this->parse_precision($this->safe_string($entry, 'maxPrecision'))),
                    'deposit' => $this->safe_string($entry, 'depositStatus') === '1',
                    'withdraw' => $this->safe_string($entry, 'withdrawStatus') === '1',
                    'networks' => $networks,
                    'type' => $type,
                    'limits' => array(
                        'amount' => array(
                            'min' => null,
                            'max' => null,
                        ),
                        'withdraw' => array(
                            'min' => null,
                            'max' => null,
                        ),
                        'deposit' => array(
                            'min' => null,
                            'max' => null,
                        ),
                    ),
                ));
            }
            return $result;
        }) ();
    }

    public function fetch_markets($params = array ()): PromiseInterface {
        return Async\async(function () use ($params) {
            /**
             * retrieves data on all markets for xt
             *
             * @see https://doc.xt.com/#market2symbol
             * @see https://doc.xt.com/#futures_quotesgetSymbols
             *
             * @param {array} $params extra parameters specific to the xt api endpoint
             * @return {array[]} an array of objects representing market data
             */
            if ($this->options['adjustForTimeDifference']) {
                Async\await($this->load_time_difference());
            }
            $promisesUnresolved = array(
                $this->fetch_spot_markets($params),
                $this->fetch_swap_and_future_markets($params),
            );
            $promises = Async\await(Promise\all($promisesUnresolved));
            $spotMarkets = $promises[0];
            $swapAndFutureMarkets = $promises[1];
            return $this->array_concat($spotMarkets, $swapAndFutureMarkets);
        }) ();
    }

    public function fetch_spot_markets($params = array ()) {
        return Async\async(function () use ($params) {
            $response = Async\await($this->publicSpotGetSymbol ($params));
            //
            //     {
            //         "rc" => 0,
            //         "mc" => "SUCCESS",
            //         "ma" => array(),
            //         "result" => {
            //             "time" => 1677881368812,
            //             "version" => "abb101d1543e54bee40687b135411ba0",
            //             "symbols" => [
            //                 {
            //                     "id" => 640,
            //                     "symbol" => "xt_usdt",
            //                     "state" => "ONLINE",
            //                     "stateTime" => 1554048000000,
            //                     "tradingEnabled" => true,
            //                     "openapiEnabled" => true,
            //                     "nextStateTime" => null,
            //                     "nextState" => null,
            //                     "depthMergePrecision" => 5,
            //                     "baseCurrency" => "xt",
            //                     "baseCurrencyPrecision" => 8,
            //                     "baseCurrencyId" => 128,
            //                     "quoteCurrency" => "usdt",
            //                     "quoteCurrencyPrecision" => 8,
            //                     "quoteCurrencyId" => 11,
            //                     "pricePrecision" => 4,
            //                     "quantityPrecision" => 2,
            //                     "orderTypes" => ["LIMIT","MARKET"],
            //                     "timeInForces" => ["GTC","IOC"],
            //                     "displayWeight" => 10002,
            //                     "displayLevel" => "FULL",
            //                     "plates" => array(),
            //                     "filters":array(
            //                         array(
            //                             "filter" => "QUOTE_QTY",
            //                             "min" => "1"
            //                         ),
            //                         array(
            //                             "filter" => "PROTECTION_LIMIT",
            //                             "buyMaxDeviation" => "0.8",
            //                             "sellMaxDeviation" => "4"
            //                         ),
            //                         array(
            //                             "filter" => "PROTECTION_MARKET",
            //                             "maxDeviation" => "0.02"
            //                         }
            //                     )
            //                 ),
            //             ]
            //         }
            //     }
            //
            $data = $this->safe_value($response, 'result', array());
            $symbols = $this->safe_value($data, 'symbols', array());
            return $this->parse_markets($symbols);
        }) ();
    }

    public function fetch_swap_and_future_markets($params = array ()) {
        return Async\async(function () use ($params) {
            $markets = Async\await(Promise\all(array( $this->publicLinearGetFutureMarketV1PublicSymbolList ($params), $this->publicInverseGetFutureMarketV1PublicSymbolList ($params) )));
            //
            //     {
            //         "returnCode" => 0,
            //         "msgInfo" => "success",
            //         "error" => null,
            //         "result" => [
            //             array(
            //                 "id" => 52,
            //                 "symbolGroupId" => 71,
            //                 "symbol" => "xt_usdt",
            //                 "pair" => "xt_usdt",
            //                 "contractType" => "PERPETUAL",
            //                 "productType" => "perpetual",
            //                 "predictEventType" => null,
            //                 "underlyingType" => "U_BASED",
            //                 "contractSize" => "1",
            //                 "tradeSwitch" => true,
            //                 "isDisplay" => true,
            //                 "isOpenApi" => false,
            //                 "state" => 0,
            //                 "initLeverage" => 20,
            //                 "initPositionType" => "CROSSED",
            //                 "baseCoin" => "xt",
            //                 "quoteCoin" => "usdt",
            //                 "baseCoinPrecision" => 8,
            //                 "baseCoinDisplayPrecision" => 4,
            //                 "quoteCoinPrecision" => 8,
            //                 "quoteCoinDisplayPrecision" => 4,
            //                 "quantityPrecision" => 0,
            //                 "pricePrecision" => 4,
            //                 "supportOrderType" => "LIMIT,MARKET",
            //                 "supportTimeInForce" => "GTC,FOK,IOC,GTX",
            //                 "supportEntrustType" => "TAKE_PROFIT,STOP,TAKE_PROFIT_MARKET,STOP_MARKET,TRAILING_STOP_MARKET",
            //                 "supportPositionType" => "CROSSED,ISOLATED",
            //                 "minQty" => "1",
            //                 "minNotional" => "5",
            //                 "maxNotional" => "20000000",
            //                 "multiplierDown" => "0.1",
            //                 "multiplierUp" => "0.1",
            //                 "maxOpenOrders" => 200,
            //                 "maxEntrusts" => 200,
            //                 "makerFee" => "0.0004",
            //                 "takerFee" => "0.0006",
            //                 "liquidationFee" => "0.01",
            //                 "marketTakeBound" => "0.1",
            //                 "depthPrecisionMerge" => 5,
            //                 "labels" => ["HOT"],
            //                 "onboardDate" => 1657101601000,
            //                 "enName" => "XTUSDT ",
            //                 "cnName" => "XTUSDT",
            //                 "minStepPrice" => "0.0001",
            //                 "minPrice" => null,
            //                 "maxPrice" => null,
            //                 "deliveryDate" => 1669879634000,
            //                 "deliveryPrice" => null,
            //                 "deliveryCompletion" => false,
            //                 "cnDesc" => null,
            //                 "enDesc" => null
            //             ),
            //         ]
            //     }
            //
            $swapAndFutureMarkets = $this->array_concat($this->safe_value($markets[0], 'result', array()), $this->safe_value($markets[1], 'result', array()));
            return $this->parse_markets($swapAndFutureMarkets);
        }) ();
    }

    public function parse_markets($markets) {
        $result = array();
        for ($i = 0; $i < count($markets); $i++) {
            $result[] = $this->parse_market($markets[$i]);
        }
        return $result;
    }

    public function parse_market(array $market): array {
        //
        // $spot
        //
        //     {
        //         "id" => 640,
        //         "symbol" => "xt_usdt",
        //         "state" => "ONLINE",
        //         "stateTime" => 1554048000000,
        //         "tradingEnabled" => true,
        //         "openapiEnabled" => true,
        //         "nextStateTime" => null,
        //         "nextState" => null,
        //         "depthMergePrecision" => 5,
        //         "baseCurrency" => "xt",
        //         "baseCurrencyPrecision" => 8,
        //         "baseCurrencyId" => 128,
        //         "quoteCurrency" => "usdt",
        //         "quoteCurrencyPrecision" => 8,
        //         "quoteCurrencyId" => 11,
        //         "pricePrecision" => 4,
        //         "quantityPrecision" => 2,
        //         "orderTypes" => ["LIMIT","MARKET"],
        //         "timeInForces" => ["GTC","IOC"],
        //         "displayWeight" => 10002,
        //         "displayLevel" => "FULL",
        //         "plates" => array(),
        //         "filters":array(
        //             array(
        //                 "filter" => "QUOTE_QTY",
        //                 "min" => "1"
        //             ),
        //             array(
        //                 "filter" => "PRICE",
        //                 "min" => null,
        //                 "max" => null,
        //                 "tickSize" => null
        //             ),
        //             array(
        //                 "filter" => "QUANTITY",
        //                 "min" => null,
        //                 "max" => null,
        //                 "tickSize" => null
        //             ),
        //             array(
        //                 "filter" => "PROTECTION_LIMIT",
        //                 "buyMaxDeviation" => "0.8",
        //                 "sellMaxDeviation" => "4"
        //             ),
        //             array(
        //                 "filter" => "PROTECTION_MARKET",
        //                 "maxDeviation" => "0.02"
        //             ),
        //             array(
        //                  "filter" => "PROTECTION_ONLINE",
        //                  "durationSeconds" => "300",
        //                  "maxPriceMultiple" => "5"
        //             ),
        //         )
        //     }
        //
        // $swap and $future
        //
        //     {
        //         "id" => 52,
        //         "symbolGroupId" => 71,
        //         "symbol" => "xt_usdt",
        //         "pair" => "xt_usdt",
        //         "contractType" => "PERPETUAL",
        //         "productType" => "perpetual",
        //         "predictEventType" => null,
        //         "underlyingType" => "U_BASED",
        //         "contractSize" => "1",
        //         "tradeSwitch" => true,
        //         "isDisplay" => true,
        //         "isOpenApi" => false,
        //         "state" => 0,
        //         "initLeverage" => 20,
        //         "initPositionType" => "CROSSED",
        //         "baseCoin" => "xt",
        //         "quoteCoin" => "usdt",
        //         "baseCoinPrecision" => 8,
        //         "baseCoinDisplayPrecision" => 4,
        //         "quoteCoinPrecision" => 8,
        //         "quoteCoinDisplayPrecision" => 4,
        //         "quantityPrecision" => 0,
        //         "pricePrecision" => 4,
        //         "supportOrderType" => "LIMIT,MARKET",
        //         "supportTimeInForce" => "GTC,FOK,IOC,GTX",
        //         "supportEntrustType" => "TAKE_PROFIT,STOP,TAKE_PROFIT_MARKET,STOP_MARKET,TRAILING_STOP_MARKET",
        //         "supportPositionType" => "CROSSED,ISOLATED",
        //         "minQty" => "1",
        //         "minNotional" => "5",
        //         "maxNotional" => "20000000",
        //         "multiplierDown" => "0.1",
        //         "multiplierUp" => "0.1",
        //         "maxOpenOrders" => 200,
        //         "maxEntrusts" => 200,
        //         "makerFee" => "0.0004",
        //         "takerFee" => "0.0006",
        //         "liquidationFee" => "0.01",
        //         "marketTakeBound" => "0.1",
        //         "depthPrecisionMerge" => 5,
        //         "labels" => ["HOT"],
        //         "onboardDate" => 1657101601000,
        //         "enName" => "XTUSDT ",
        //         "cnName" => "XTUSDT",
        //         "minStepPrice" => "0.0001",
        //         "minPrice" => null,
        //         "maxPrice" => null,
        //         "deliveryDate" => 1669879634000,
        //         "deliveryPrice" => null,
        //         "deliveryCompletion" => false,
        //         "cnDesc" => null,
        //         "enDesc" => null
        //     }
        //
        $id = $this->safe_string($market, 'symbol');
        $baseId = $this->safe_string_2($market, 'baseCurrency', 'baseCoin');
        $quoteId = $this->safe_string_2($market, 'quoteCurrency', 'quoteCoin');
        $base = $this->safe_currency_code($baseId);
        $quote = $this->safe_currency_code($quoteId);
        $state = $this->safe_string($market, 'state');
        $symbol = $base . '/' . $quote;
        $filters = $this->safe_value($market, 'filters', array());
        $minAmount = null;
        $maxAmount = null;
        $minCost = null;
        $maxCost = null;
        $minPrice = null;
        $maxPrice = null;
        $amountPrecision = null;
        for ($i = 0; $i < count($filters); $i++) {
            $entry = $filters[$i];
            $filter = $this->safe_string($entry, 'filter');
            if ($filter === 'QUANTITY') {
                $minAmount = $this->safe_number($entry, 'min');
                $maxAmount = $this->safe_number($entry, 'max');
                $amountPrecision = $this->safe_number($entry, 'tickSize');
            }
            if ($filter === 'QUOTE_QTY') {
                $minCost = $this->safe_number($entry, 'min');
            }
            if ($filter === 'PRICE') {
                $minPrice = $this->safe_number($entry, 'min');
                $maxPrice = $this->safe_number($entry, 'max');
            }
        }
        if ($amountPrecision === null) {
            $amountPrecision = $this->parse_number($this->parse_precision($this->safe_string($market, 'quantityPrecision')));
        }
        $underlyingType = $this->safe_string($market, 'underlyingType');
        $linear = null;
        $inverse = null;
        $settleId = null;
        $settle = null;
        $expiry = null;
        $future = false;
        $swap = false;
        $contract = false;
        $spot = true;
        $type = 'spot';
        if ($underlyingType === 'U_BASED') {
            $symbol = $symbol . ':' . $quote;
            $settleId = $baseId;
            $settle = $quote;
            $linear = true;
            $inverse = false;
        } elseif ($underlyingType === 'COIN_BASED') {
            $symbol = $symbol . ':' . $base;
            $settleId = $baseId;
            $settle = $base;
            $linear = false;
            $inverse = true;
        }
        if ($underlyingType !== null) {
            $expiry = $this->safe_integer($market, 'deliveryDate');
            $productType = $this->safe_string($market, 'productType');
            if ($productType !== 'perpetual') {
                $symbol = $symbol . '-' . $this->yymmdd($expiry);
                $type = 'future';
                $future = true;
            } else {
                $type = 'swap';
                $swap = true;
            }
            $minAmount = $this->safe_number($market, 'minQty');
            $minCost = $this->safe_number($market, 'minNotional');
            $maxCost = $this->safe_number($market, 'maxNotional');
            $minPrice = $this->safe_number($market, 'minPrice');
            $maxPrice = $this->safe_number($market, 'maxPrice');
            $contract = true;
            $spot = false;
        }
        $isActive = false;
        if ($contract) {
            $isActive = $this->safe_value($market, 'isOpenApi', false);
        } else {
            if (($state === 'ONLINE') && ($this->safe_value($market, 'tradingEnabled')) && ($this->safe_value($market, 'openapiEnabled'))) {
                $isActive = true;
            }
        }
        return $this->safe_market_structure(array(
            'id' => $id,
            'symbol' => $symbol,
            'base' => $base,
            'quote' => $quote,
            'settle' => $settle,
            'baseId' => $baseId,
            'quoteId' => $quoteId,
            'settleId' => $settleId,
            'type' => $type,
            'spot' => $spot,
            'margin' => null,
            'swap' => $swap,
            'future' => $future,
            'option' => false,
            'active' => $isActive,
            'contract' => $contract,
            'linear' => $linear,
            'inverse' => $inverse,
            'taker' => $this->safe_number($market, 'takerFee'),
            'maker' => $this->safe_number($market, 'makerFee'),
            'contractSize' => $this->safe_number($market, 'contractSize'),
            'expiry' => $expiry,
            'expiryDatetime' => $this->iso8601($expiry),
            'strike' => null,
            'optionType' => null,
            'precision' => array(
                'price' => $this->parse_number($this->parse_precision($this->safe_string($market, 'pricePrecision'))),
                'amount' => $amountPrecision,
                'base' => $this->parse_number($this->parse_precision($this->safe_string($market, 'baseCoinPrecision'))),
                'quote' => $this->parse_number($this->parse_precision($this->safe_string($market, 'quoteCoinPrecision'))),
            ),
            'limits' => array(
                'leverage' => array(
                    'min' => $this->parse_number('1'),
                    'max' => null,
                ),
                'amount' => array(
                    'min' => $minAmount,
                    'max' => $maxAmount,
                ),
                'price' => array(
                    'min' => $minPrice,
                    'max' => $maxPrice,
                ),
                'cost' => array(
                    'min' => $minCost,
                    'max' => $maxCost,
                ),
            ),
            'info' => $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://doc.xt.com/#market4kline
             * @see https://doc.xt.com/#futures_quotesgetKLine
             *
             * @param {string} $symbol unified $symbol of the $market to fetch OHLCV data for
             * @param {string} $timeframe the length of time each candle represents
             * @param {int} [$since] timestamp in ms of the earliest candle to fetch
             * @param {int} [$limit] the maximum amount of candles to fetch
             * @param {array} $params extra parameters specific to the xt api endpoint
             * @param {int} [$params->until] timestamp in ms of the latest candle to fetch
             * @param {boolean} [$params->paginate] default false, when true will automatically $paginate by calling this endpoint multiple times. See in the docs all the [available parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-$params)
             * @return {int[][]} A list of candles ordered, open, high, low, close, volume
             */
            Async\await($this->load_markets());
            $paginate = false;
            list($paginate, $params) = $this->handle_option_and_params($params, 'fetchOHLCV', 'paginate', false);
            if ($paginate) {
                return Async\await($this->fetch_paginated_call_deterministic('fetchOHLCV', $symbol, $since, $limit, $timeframe, $params, 1000));
            }
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
                'interval' => $this->safe_string($this->timeframes, $timeframe, $timeframe),
            );
            if ($since !== null) {
                $request['startTime'] = $since;
            }
            if ($limit !== null) {
                $request['limit'] = $limit;
            } else {
                $request['limit'] = 1000;
            }
            $until = $this->safe_integer($params, 'until');
            $params = $this->omit($params, array( 'until' ));
            if ($until !== null) {
                $request['endTime'] = $until;
            }
            $response = null;
            if ($market['linear']) {
                $response = Async\await($this->publicLinearGetFutureMarketV1PublicQKline ($this->extend($request, $params)));
            } elseif ($market['inverse']) {
                $response = Async\await($this->publicInverseGetFutureMarketV1PublicQKline ($this->extend($request, $params)));
            } else {
                $response = Async\await($this->publicSpotGetKline ($this->extend($request, $params)));
            }
            //
            // spot
            //
            //     {
            //         "rc" => 0,
            //         "mc" => "SUCCESS",
            //         "ma" => array(),
            //         "result" => array(
            //             array(
            //                 "t" => 1678167720000,
            //                 "o" => "22467.85",
            //                 "c" => "22465.87",
            //                 "h" => "22468.86",
            //                 "l" => "22465.21",
            //                 "q" => "1.316656",
            //                 "v" => "29582.73018498"
            //             ),
            //         )
            //     }
            //
            // swap and future
            //
            //     {
            //         "returnCode" => 0,
            //         "msgInfo" => "success",
            //         "error" => null,
            //         "result" => array(
            //             array(
            //                 "s" => "btc_usdt",
            //                 "p" => "btc_usdt",
            //                 "t" => 1678168020000,
            //                 "o" => "22450.0",
            //                 "c" => "22441.5",
            //                 "h" => "22450.0",
            //                 "l" => "22441.5",
            //                 "a" => "312931",
            //                 "v" => "702461.58895"
            //             ),
            //         )
            //     }
            //
            $ohlcvs = $this->safe_value($response, 'result', array());
            return $this->parse_ohlcvs($ohlcvs, $market, $timeframe, $since, $limit);
        }) ();
    }

    public function parse_ohlcv($ohlcv, ?array $market = null): array {
        //
        // spot
        //
        //     {
        //         "t" => 1678167720000,
        //         "o" => "22467.85",
        //         "c" => "22465.87",
        //         "h" => "22468.86",
        //         "l" => "22465.21",
        //         "q" => "1.316656",
        //         "v" => "29582.73018498"
        //     }
        //
        // swap and future
        //
        //     {
        //         "s" => "btc_usdt",
        //         "p" => "btc_usdt",
        //         "t" => 1678168020000,
        //         "o" => "22450.0",
        //         "c" => "22441.5",
        //         "h" => "22450.0",
        //         "l" => "22441.5",
        //         "a" => "312931",
        //         "v" => "702461.58895"
        //     }
        //
        $volumeIndex = ($market['inverse']) ? 'v' : 'a';
        return array(
            $this->safe_integer($ohlcv, 't'),
            $this->safe_number($ohlcv, 'o'),
            $this->safe_number($ohlcv, 'h'),
            $this->safe_number($ohlcv, 'l'),
            $this->safe_number($ohlcv, 'c'),
            $this->safe_number_2($ohlcv, 'q', $volumeIndex),
        );
    }

    public function fetch_order_book(string $symbol, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($symbol, $limit, $params) {
            /**
             *
             * @see https://doc.xt.com/#market3depth
             * @see https://doc.xt.com/#futures_quotesgetDepth
             *
             * fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
             * @param {string} $symbol unified $market $symbol 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 xt api endpoint
             * @return {array} A dictionary of {@link https://docs.ccxt.com/en/latest/manual.html#order-book-structure order book structures} indexed by $market symbols
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
            );
            $response = null;
            if ($market['spot']) {
                if ($limit !== null) {
                    $request['limit'] = min ($limit, 500);
                }
                $response = Async\await($this->publicSpotGetDepth ($this->extend($request, $params)));
            } else {
                if ($limit !== null) {
                    $request['level'] = min ($limit, 50);
                } else {
                    $request['level'] = 50;
                }
                if ($market['linear']) {
                    $response = Async\await($this->publicLinearGetFutureMarketV1PublicQDepth ($this->extend($request, $params)));
                } elseif ($market['inverse']) {
                    $response = Async\await($this->publicInverseGetFutureMarketV1PublicQDepth ($this->extend($request, $params)));
                }
            }
            //
            // spot
            //
            //     {
            //         "rc" => 0,
            //         "mc" => "SUCCESS",
            //         "ma" => array(),
            //         "result" => {
            //             "timestamp" => 1678169975184,
            //             "lastUpdateId" => 1675333221812,
            //             "bids" => [
            //                 ["22444.51", "0.129887"],
            //                 ["22444.49", "0.114245"],
            //                 ["22444.30", "0.225956"]
            //             ],
            //             "asks" => [
            //                 ["22446.19", "0.095330"],
            //                 ["22446.24", "0.224413"],
            //                 ["22446.28", "0.329095"]
            //             ]
            //         }
            //     }
            //
            // swap and future
            //
            //     {
            //         "returnCode" => 0,
            //         "msgInfo" => "success",
            //         "error" => null,
            //         "result" => {
            //             "t" => 1678170311005,
            //             "s" => "btc_usdt",
            //             "u" => 471694545627,
            //             "b" => [
            //                 ["22426", "198623"],
            //                 ["22423.5", "80295"],
            //                 ["22423", "163580"]
            //             ],
            //             "a" => [
            //                 ["22427", "3417"],
            //                 ["22428.5", "43532"],
            //                 ["22429", "119"]
            //             ]
            //         }
            //     }
            //
            $orderBook = $this->safe_value($response, 'result', array());
            $timestamp = $this->safe_integer_2($orderBook, 'timestamp', 't');
            if ($market['spot']) {
                $ob = $this->parse_order_book($orderBook, $symbol, $timestamp);
                $ob['nonce'] = $this->safe_integer($orderBook, 'lastUpdateId');
                return $ob;
            }
            $swapOb = $this->parse_order_book($orderBook, $symbol, $timestamp, 'b', 'a');
            $swapOb['nonce'] = $this->safe_integer_2($orderBook, 'u', 'lastUpdateId');
            return $swapOb;
        }) ();
    }

    public function fetch_ticker(string $symbol, $params = array ()) {
        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://doc.xt.com/#market10ticker24h
             * @see https://doc.xt.com/#futures_quotesgetAggTicker
             *
             * @param {string} $symbol unified $market $symbol to fetch the $ticker for
             * @param {array} $params extra parameters specific to the xt api endpoint
             * @return {array} a {@link https://docs.ccxt.com/en/latest/manual.html#$ticker-structure $ticker structure}
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
            );
            $response = null;
            if ($market['linear']) {
                $response = Async\await($this->publicLinearGetFutureMarketV1PublicQAggTicker ($this->extend($request, $params)));
            } elseif ($market['inverse']) {
                $response = Async\await($this->publicInverseGetFutureMarketV1PublicQAggTicker ($this->extend($request, $params)));
            } else {
                $response = Async\await($this->publicSpotGetTicker24h ($this->extend($request, $params)));
            }
            //
            // spot
            //
            //     {
            //         "rc" => 0,
            //         "mc" => "SUCCESS",
            //         "ma" => array(),
            //         "result" => array(
            //             {
            //                 "s" => "btc_usdt",
            //                 "t" => 1678172693931,
            //                 "cv" => "34.00",
            //                 "cr" => "0.0015",
            //                 "o" => "22398.05",
            //                 "l" => "22323.72",
            //                 "h" => "22600.50",
            //                 "c" => "22432.05",
            //                 "q" => "7962.256931",
            //                 "v" => "178675209.47416856"
            //             }
            //         )
            //     }
            //
            // swap and future
            //
            //     {
            //         "returnCode" => 0,
            //         "msgInfo" => "success",
            //         "error" => null,
            //         "result" => {
            //             "t" => 1678172848572,
            //             "s" => "btc_usdt",
            //             "c" => "22415.5",
            //             "h" => "22590.0",
            //             "l" => "22310.0",
            //             "a" => "623654031",
            //             "v" => "1399166074.31675",
            //             "o" => "22381.5",
            //             "r" => "0.0015",
            //             "i" => "22424.5",
            //             "m" => "22416.5",
            //             "bp" => "22415",
            //             "ap" => "22415.5"
            //         }
            //     }
            //
            $ticker = $this->safe_value($response, 'result');
            if ($market['spot']) {
                return $this->parse_ticker($ticker[0], $market);
            }
            return $this->parse_ticker($ticker, $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 calculations with the information calculated over the past 24 hours each $market
             *
             * @see https://doc.xt.com/#market10ticker24h
             * @see https://doc.xt.com/#futures_quotesgetAggTickers
             *
             * @param {string} [$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 xt api endpoint
             * @return {array} an array of {@link https://docs.ccxt.com/en/latest/manual.html#$ticker-structure $ticker structures}
             */
            Async\await($this->load_markets());
            $market = null;
            if ($symbols !== null) {
                $symbols = $this->market_symbols($symbols);
                $market = $this->market($symbols[0]);
            }
            $request = array();
            $type = null;
            $subType = null;
            $response = null;
            list($type, $params) = $this->handle_market_type_and_params('fetchTickers', $market, $params);
            list($subType, $params) = $this->handle_sub_type_and_params('fetchTickers', $market, $params);
            if ($subType === 'inverse') {
                $response = Async\await($this->publicInverseGetFutureMarketV1PublicQAggTickers ($this->extend($request, $params)));
            } elseif (($subType === 'linear') || ($type === 'swap') || ($type === 'future')) {
                $response = Async\await($this->publicLinearGetFutureMarketV1PublicQAggTickers ($this->extend($request, $params)));
            } else {
                $response = Async\await($this->publicSpotGetTicker24h ($this->extend($request, $params)));
            }
            //
            // spot
            //
            //     {
            //         "rc" => 0,
            //         "mc" => "SUCCESS",
            //         "ma" => array(),
            //         "result" => array(
            //             {
            //                 "s" => "btc_usdt",
            //                 "t" => 1678172693931,
            //                 "cv" => "34.00",
            //                 "cr" => "0.0015",
            //                 "o" => "22398.05",
            //                 "l" => "22323.72",
            //                 "h" => "22600.50",
            //                 "c" => "22432.05",
            //                 "q" => "7962.256931",
            //                 "v" => "178675209.47416856"
            //             }
            //         )
            //     }
            //
            // swap and future
            //
            //     {
            //         "returnCode" => 0,
            //         "msgInfo" => "success",
            //         "error" => null,
            //         "result" => array(
            //             array(
            //                 "t" => 1680738775108,
            //                 "s" => "badger_usdt",
            //                 "c" => "2.7176",
            //                 "h" => "2.7917",
            //                 "l" => "2.6818",
            //                 "a" => "88332",
            //                 "v" => "242286.3520",
            //                 "o" => "2.7422",
            //                 "r" => "-0.0089",
            //                 "i" => "2.7155",
            //                 "m" => "2.7161",
            //                 "bp" => "2.7152",
            //                 "ap" => "2.7176"
            //             ),
            //         )
            //     }
            //
            $tickers = $this->safe_value($response, 'result', array());
            $result = array();
            for ($i = 0; $i < count($tickers); $i++) {
                $ticker = $this->parse_ticker($tickers[$i], $market);
                $symbol = $ticker['symbol'];
                $result[$symbol] = $ticker;
            }
            return $this->filter_by_array($result, 'symbol', $symbols);
        }) ();
    }

    public function fetch_bids_asks(?array $symbols = null, $params = array ()) {
        return Async\async(function () use ($symbols, $params) {
            /**
             * fetches the bid and ask price and volume for multiple markets
             *
             * @see https://doc.xt.com/#market9tickerBook
             *
             * @param {string} [$symbols] unified $symbols of the markets to fetch the bids and asks for, all markets are returned if not assigned
             * @param {array} $params extra parameters specific to the xt api endpoint
             * @return {array} a dictionary of {@link https://docs.ccxt.com/en/latest/manual.html#ticker-structure ticker structures}
             */
            Async\await($this->load_markets());
            $symbols = $this->market_symbols($symbols);
            $request = array();
            $market = null;
            if ($symbols !== null) {
                $market = $this->market($symbols[0]);
            }
            $subType = null;
            list($subType, $params) = $this->handle_sub_type_and_params('fetchBidsAsks', $market, $params);
            if ($subType !== null) {
                throw new NotSupported($this->id . ' fetchBidsAsks() is not available for swap and future markets, only spot markets are supported');
            }
            $response = Async\await($this->publicSpotGetTickerBook ($this->extend($request, $params)));
            //
            //     {
            //         "rc" => 0,
            //         "mc" => "SUCCESS",
            //         "ma" => array(),
            //         "result" => array(
            //             array(
            //                 "s" => "kas_usdt",
            //                 "t" => 1679539891853,
            //                 "ap" => "0.016298",
            //                 "aq" => "5119.09",
            //                 "bp" => "0.016290",
            //                 "bq" => "135.37"
            //             ),
            //         )
            //     }
            //
            $tickers = $this->safe_value($response, 'result', array());
            return $this->parse_tickers($tickers, $symbols);
        }) ();
    }

    public function parse_ticker($ticker, $market = null) {
        //
        // spot => fetchTicker, fetchTickers
        //
        //     {
        //         "s" => "btc_usdt",
        //         "t" => 1678172693931,
        //         "cv" => "34.00",
        //         "cr" => "0.0015",
        //         "o" => "22398.05",
        //         "l" => "22323.72",
        //         "h" => "22600.50",
        //         "c" => "22432.05",
        //         "q" => "7962.256931",
        //         "v" => "178675209.47416856"
        //     }
        //
        // swap and future => fetchTicker, fetchTickers
        //
        //     {
        //         "t" => 1678172848572,
        //         "s" => "btc_usdt",
        //         "c" => "22415.5",
        //         "h" => "22590.0",
        //         "l" => "22310.0",
        //         "a" => "623654031",
        //         "v" => "1399166074.31675",
        //         "o" => "22381.5",
        //         "r" => "0.0015",
        //         "i" => "22424.5",
        //         "m" => "22416.5",
        //         "bp" => "22415",
        //         "ap" => "22415.5"
        //     }
        //
        // fetchBidsAsks
        //
        //     {
        //         "s" => "kas_usdt",
        //         "t" => 1679539891853,
        //         "ap" => "0.016298",
        //         "aq" => "5119.09",
        //         "bp" => "0.016290",
        //         "bq" => "135.37"
        //     }
        //
        $marketId = $this->safe_string($ticker, 's');
        $marketType = ($market !== null) ? $market['type'] : null;
        $hasSpotKeys = (is_array($ticker) && array_key_exists('cv', $ticker)) || (is_array($ticker) && array_key_exists('aq', $ticker));
        if ($marketType === null) {
            $marketType = $hasSpotKeys ? 'spot' : 'contract';
        }
        $market = $this->safe_market($marketId, $market, '_', $marketType);
        $symbol = $market['symbol'];
        $timestamp = $this->safe_integer($ticker, 't');
        $percentage = $this->safe_string_2($ticker, 'cr', 'r');
        if ($percentage !== null) {
            $percentage = Precise::string_mul($percentage, '100');
        }
        return $this->safe_ticker(array(
            'symbol' => $symbol,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'high' => $this->safe_number($ticker, 'h'),
            'low' => $this->safe_number($ticker, 'l'),
            'bid' => $this->safe_number($ticker, 'bp'),
            'bidVolume' => $this->safe_number($ticker, 'bq'),
            'ask' => $this->safe_number($ticker, 'ap'),
            'askVolume' => $this->safe_number($ticker, 'aq'),
            'vwap' => null,
            'open' => $this->safe_string($ticker, 'o'),
            'close' => $this->safe_string($ticker, 'c'),
            'last' => $this->safe_string($ticker, 'c'),
            'previousClose' => null,
            'change' => $this->safe_number($ticker, 'cv'),
            'percentage' => $this->parse_number($percentage),
            'average' => null,
            'baseVolume' => null,
            'quoteVolume' => $this->safe_number_2($ticker, 'a', 'v'),
            'info' => $ticker,
        ), $market);
    }

    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://doc.xt.com/#market5tradeRecent
             * @see https://doc.xt.com/#futures_quotesgetDeal
             *
             * @param {string} $symbol unified $market $symbol to fetch $trades for
             * @param {int} [$since] timestamp in ms of the earliest trade to fetch
             * @param {int} [$limit] the maximum amount of $trades to fetch
             * @param {array} $params extra parameters specific to the xt api endpoint
             * @return {array[]} a list of ~@link https://docs.ccxt.com/en/latest/manual.html?#public-$trades trade structures~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
            );
            $response = null;
            if ($market['spot']) {
                if ($limit !== null) {
                    $request['limit'] = $limit;
                }
                $response = Async\await($this->publicSpotGetTradeRecent ($this->extend($request, $params)));
            } else {
                if ($limit !== null) {
                    $request['num'] = $limit;
                }
                if ($market['linear']) {
                    $response = Async\await($this->publicLinearGetFutureMarketV1PublicQDeal ($this->extend($request, $params)));
                } elseif ($market['inverse']) {
                    $response = Async\await($this->publicInverseGetFutureMarketV1PublicQDeal ($this->extend($request, $params)));
                }
            }
            //
            // spot
            //
            //     {
            //         "rc" => 0,
            //         "mc" => "SUCCESS",
            //         "ma" => array(),
            //         "result" => array(
            //             array(
            //                 "i" => 203530723141917063,
            //                 "t" => 1678227505815,
            //                 "p" => "22038.81",
            //                 "q" => "0.000978",
            //                 "v" => "21.55395618",
            //                 "b" => true
            //             ),
            //         )
            //     }
            //
            // swap and future
            //
            //     {
            //         "returnCode" => 0,
            //         "msgInfo" => "success",
            //         "error" => null,
            //         "result" => array(
            //             array(
            //                 "t" => 1678227683897,
            //                 "s" => "btc_usdt",
            //                 "p" => "22031",
            //                 "a" => "1067",
            //                 "m" => "BID"
            //             ),
            //         )
            //     }
            //
            $trades = $this->safe_value($response, 'result', array());
            return $this->parse_trades($trades, $market);
        }) ();
    }

    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
             *
             * @see https://doc.xt.com/#tradetradeGet
             * @see https://doc.xt.com/#futures_ordergetTrades
             *
             * @param {string} [$symbol] unified $market $symbol to fetch $trades for
             * @param {int} [$since] timestamp in ms of the earliest trade to fetch
             * @param {int} [$limit] the maximum amount of $trades to fetch
             * @param {array} $params extra parameters specific to the xt api endpoint
             * @return {array[]} a list of ~@link https://docs.ccxt.com/en/latest/manual.html?#public-$trades trade structures~
             */
            Async\await($this->load_markets());
            $request = array();
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
                $request['symbol'] = $market['id'];
            }
            if ($since !== null) {
                $request['startTime'] = $since;
            }
            $type = null;
            $subType = null;
            $response = null;
            list($type, $params) = $this->handle_market_type_and_params('fetchMyTrades', $market, $params);
            list($subType, $params) = $this->handle_sub_type_and_params('fetchMyTrades', $market, $params);
            if (($subType !== null) || ($type === 'swap') || ($type === 'future')) {
                if ($limit !== null) {
                    $request['size'] = $limit;
                }
                if ($subType === 'inverse') {
                    $response = Async\await($this->privateInverseGetFutureTradeV1OrderTradeList ($this->extend($request, $params)));
                } else {
                    $response = Async\await($this->privateLinearGetFutureTradeV1OrderTradeList ($this->extend($request, $params)));
                }
            } else {
                $marginMode = null;
                list($marginMode, $params) = $this->handle_margin_mode_and_params('fetchMyTrades', $params);
                $marginOrSpotRequest = ($marginMode !== null) ? 'LEVER' : 'SPOT';
                $request['bizType'] = $marginOrSpotRequest;
                if ($limit !== null) {
                    $request['limit'] = $limit;
                }
                $response = Async\await($this->privateSpotGetTrade ($this->extend($request, $params)));
            }
            //
            // spot and margin
            //
            //     {
            //         "rc" => 0,
            //         "mc" => "SUCCESS",
            //         "ma" => array(),
            //         "result" => {
            //             "hasPrev" => false,
            //             "hasNext" => false,
            //             "items" => array(
            //                 array(
            //                     "symbol" => "btc_usdt",
            //                     "tradeId" => "206906233569974658",
            //                     "orderId" => "206906233178463488",
            //                     "orderSide" => "SELL",
            //                     "orderType" => "MARKET",
            //                     "bizType" => "SPOT",
            //                     "time" => 1679032290215,
            //                     "price" => "25703.46",
            //                     "quantity" => "0.000099",
            //                     "quoteQty" => "2.54464254",
            //                     "baseCurrency" => "btc",
            //                     "quoteCurrency" => "usdt",
            //                     "fee" => "0.00508929",
            //                     "feeCurrency" => "usdt",
            //                     "takerMaker" => "TAKER"
            //                 ),
            //             )
            //         }
            //     }
            //
            // swap and future
            //
            //     {
            //         "returnCode" => 0,
            //         "msgInfo" => "success",
            //         "error" => null,
            //         "result" => {
            //             "page" => 1,
            //             "ps" => 10,
            //             "total" => 2,
            //             "items" => array(
            //                 array(
            //                     "orderId" => "207260566170987200",
            //                     "execId" => "207260566790603265",
            //                     "symbol" => "btc_usdt",
            //                     "quantity" => "13",
            //                     "price" => "27368",
            //                     "fee" => "0.02134704",
            //                     "feeCoin" => "usdt",
            //                     "timestamp" => 1679116769838,
            //                     "takerMaker" => "TAKER"
            //                 ),
            //             )
            //         }
            //     }
            //
            $data = $this->safe_value($response, 'result', array());
            $trades = $this->safe_value($data, 'items', array());
            return $this->parse_trades($trades, $market, $since, $limit);
        }) ();
    }

    public function parse_trade($trade, $market = null) {
        //
        // spot => fetchTrades
        //
        //     {
        //         "i" => 203530723141917063,
        //         "t" => 1678227505815,
        //         "p" => "22038.81",
        //         "q" => "0.000978",
        //         "v" => "21.55395618",
        //         "b" => true
        //     }
        //
        // spot => watchTrades
        //
        //    {
        //        s => 'btc_usdt',
        //        i => '228825383103928709',
        //        t => 1684258222702,
        //        p => '27003.65',
        //        q => '0.000796',
        //        b => true
        //    }
        //
        // spot => watchMyTrades
        //
        //    {
        //        "s" => "btc_usdt",                // symbol
        //        "t" => 1656043204763,             // time
        //        "i" => "6316559590087251233",     // tradeId
        //        "oi" => "6216559590087220004",    // orderId
        //        "p" => "30000",                   // $trade price
        //        "q" => "3",                       // qty $quantity
        //        "v" => "90000"                    // volume $trade $amount
        //    }
        //
        // swap and future => fetchTrades
        //
        //     {
        //         "t" => 1678227683897,
        //         "s" => "btc_usdt",
        //         "p" => "22031",
        //         "a" => "1067",
        //         "m" => "BID"
        //     }
        //
        // spot => fetchMyTrades
        //
        //     {
        //         "symbol" => "btc_usdt",
        //         "tradeId" => "206906233569974658",
        //         "orderId" => "206906233178463488",
        //         "orderSide" => "SELL",
        //         "orderType" => "MARKET",
        //         "bizType" => "SPOT",
        //         "time" => 1679032290215,
        //         "price" => "25703.46",
        //         "quantity" => "0.000099",
        //         "quoteQty" => "2.54464254",
        //         "baseCurrency" => "btc",
        //         "quoteCurrency" => "usdt",
        //         "fee" => "0.00508929",
        //         "feeCurrency" => "usdt",
        //         "takerMaker" => "TAKER"
        //     }
        //
        // swap and future => fetchMyTrades
        //
        //     {
        //         "orderId" => "207260566170987200",
        //         "execId" => "207260566790603265",
        //         "symbol" => "btc_usdt",
        //         "quantity" => "13",
        //         "price" => "27368",
        //         "fee" => "0.02134704",
        //         "feeCoin" => "usdt",
        //         "timestamp" => 1679116769838,
        //         "takerMaker" => "TAKER"
        //     }
        //
        // contract watchMyTrades
        //
        //    {
        //        "symbol" => 'btc_usdt',
        //        "orderSide" => 'SELL',
        //        "positionSide" => 'LONG',
        //        "orderId" => '231485367663419328',
        //        "price" => '27152.7',
        //        "quantity" => '33',
        //        "marginUnfrozen" => '2.85318000',
        //        "timestamp" => 1684892412565
        //    }
        //
        // watchMyTrades (ws, swap)
        //
        //    {
        //        'fee' => '0.04080840',
        //        'isMaker' => False,
        //        'marginUnfrozen' => '0.75711984',
        //        'orderId' => '376172779053188416',
        //        'orderSide' => 'BUY',
        //        'positionSide' => 'LONG',
        //        'price' => '3400.70',
        //        'quantity' => '2',
        //        'symbol' => 'eth_usdt',
        //        'timestamp' => 1719388579622
        //    }
        //
        $marketId = $this->safe_string_2($trade, 's', 'symbol');
        $marketType = ($market !== null) ? $market['type'] : null;
        $hasSpotKeys = (is_array($trade) && array_key_exists('b', $trade)) || (is_array($trade) && array_key_exists('bizType', $trade)) || (is_array($trade) && array_key_exists('oi', $trade));
        if ($marketType === null) {
            $marketType = $hasSpotKeys ? 'spot' : 'contract';
        }
        $market = $this->safe_market($marketId, $market, '_', $marketType);
        $side = null;
        $takerOrMaker = null;
        $isBuyerMaker = $this->safe_bool($trade, 'b');
        if ($isBuyerMaker !== null) {
            $side = $isBuyerMaker ? 'sell' : 'buy';
            $takerOrMaker = 'taker'; // public trades always taker
        } else {
            $takerMaker = $this->safe_string_lower($trade, 'takerMaker');
            if ($takerMaker !== null) {
                $takerOrMaker = $takerMaker;
            } else {
                $isMaker = $this->safe_bool($trade, 'isMaker');
                if ($isMaker !== null) {
                    $takerOrMaker = $isMaker ? 'maker' : 'taker';
                }
            }
            $orderSide = $this->safe_string_lower($trade, 'orderSide');
            if ($orderSide !== null) {
                $side = $orderSide;
            } else {
                $bidOrAsk = $this->safe_string($trade, 'm');
                if ($bidOrAsk !== null) {
                    $side = ($bidOrAsk === 'BID') ? 'buy' : 'sell';
                }
            }
        }
        $timestamp = $this->safe_integer_n($trade, array( 't', 'time', 'timestamp' ));
        $quantity = $this->safe_string_2($trade, 'q', 'quantity');
        $amount = null;
        if ($marketType === 'spot') {
            $amount = $quantity;
        } else {
            if ($quantity === null) {
                $amount = Precise::string_mul($this->safe_string($trade, 'a'), $this->number_to_string($market['contractSize']));
            } else {
                $amount = Precise::string_mul($quantity, $this->number_to_string($market['contractSize']));
            }
        }
        return $this->safe_trade(array(
            'info' => $trade,
            'id' => $this->safe_string_n($trade, array( 'i', 'tradeId', 'execId' )),
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'symbol' => $market['symbol'],
            'order' => $this->safe_string_2($trade, 'orderId', 'oi'),
            'type' => $this->safe_string_lower($trade, 'orderType'),
            'side' => $side,
            'takerOrMaker' => $takerOrMaker,
            'price' => $this->safe_string_2($trade, 'p', 'price'),
            'amount' => $amount,
            'cost' => null,
            'fee' => array(
                'currency' => $this->safe_currency_code($this->safe_string_2($trade, 'feeCurrency', 'feeCoin')),
                'cost' => $this->safe_string($trade, 'fee'),
            ),
        ), $market);
    }

    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://doc.xt.com/#balancebalancesGet
             * @see https://doc.xt.com/#futures_usergetBalances
             *
             * @param {array} $params extra parameters specific to the xt api endpoint
             * @return {array} a ~@link https://docs.ccxt.com/en/latest/manual.html?#balance-structure balance structure~
             */
            Async\await($this->load_markets());
            $type = null;
            $subType = null;
            $response = null;
            list($type, $params) = $this->handle_market_type_and_params('fetchBalance', null, $params);
            list($subType, $params) = $this->handle_sub_type_and_params('fetchBalance', null, $params);
            $isContractWallet = (($type === 'swap') || ($type === 'future'));
            if ($subType === 'inverse') {
                $response = Async\await($this->privateInverseGetFutureUserV1BalanceList ($params));
            } elseif (($subType === 'linear') || $isContractWallet) {
                $response = Async\await($this->privateLinearGetFutureUserV1BalanceList ($params));
            } else {
                $response = Async\await($this->privateSpotGetBalances ($params));
            }
            //
            // spot
            //
            //     {
            //         "rc" => 0,
            //         "mc" => "SUCCESS",
            //         "ma" => array(),
            //         "result" => {
            //             "totalUsdtAmount" => "31.75931133",
            //             "totalBtcAmount" => "0.00115951",
            //             "assets" => array(
            //                 array(
            //                     "currency" => "usdt",
            //                     "currencyId" => 11,
            //                     "frozenAmount" => "0.03834082",
            //                     "availableAmount" => "31.70995965",
            //                     "totalAmount" => "31.74830047",
            //                     "convertBtcAmount" => "0.00115911",
            //                     "convertUsdtAmount" => "31.74830047"
            //                 ),
            //             )
            //         }
            //     }
            //
            // swap and future
            //
            //     {
            //         "returnCode" => 0,
            //         "msgInfo" => "success",
            //         "error" => null,
            //         "result" => array(
            //             {
            //                 "coin" => "usdt",
            //                 "walletBalance" => "19.29849875",
            //                 "openOrderMarginFrozen" => "0",
            //                 "isolatedMargin" => "0.709475",
            //                 "crossedMargin" => "0",
            //                 "availableBalance" => "18.58902375",
            //                 "bonus" => "0",
            //                 "coupon":"0"
            //             }
            //         )
            //     }
            //
            $balances = null;
            if (($subType !== null) || $isContractWallet) {
                $balances = $this->safe_value($response, 'result', array());
            } else {
                $data = $this->safe_value($response, 'result', array());
                $balances = $this->safe_value($data, 'assets', array());
            }
            return $this->parse_balance($balances);
        }) ();
    }

    public function parse_balance($response) {
        //
        // spot
        //
        //     {
        //         "currency" => "usdt",
        //         "currencyId" => 11,
        //         "frozenAmount" => "0.03834082",
        //         "availableAmount" => "31.70995965",
        //         "totalAmount" => "31.74830047",
        //         "convertBtcAmount" => "0.00115911",
        //         "convertUsdtAmount" => "31.74830047"
        //     }
        //
        // swap and future
        //
        //     {
        //         "coin" => "usdt",
        //         "walletBalance" => "19.29849875",
        //         "openOrderMarginFrozen" => "0",
        //         "isolatedMargin" => "0.709475",
        //         "crossedMargin" => "0",
        //         "availableBalance" => "18.58902375",
        //         "bonus" => "0",
        //         "coupon":"0"
        //     }
        //
        $result = array( 'info' => $response );
        for ($i = 0; $i < count($response); $i++) {
            $balance = $response[$i];
            $currencyId = $this->safe_string_2($balance, 'currency', 'coin');
            $code = $this->safe_currency_code($currencyId);
            $account = $this->account();
            $free = $this->safe_string_2($balance, 'availableAmount', 'availableBalance');
            $used = $this->safe_string($balance, 'frozenAmount');
            $total = $this->safe_string_2($balance, 'totalAmount', 'walletBalance');
            if ($used === null) {
                $crossedAndIsolatedMargin = Precise::string_add($this->safe_string($balance, 'crossedMargin'), $this->safe_string($balance, 'isolatedMargin'));
                $used = Precise::string_add($this->safe_string($balance, 'openOrderMarginFrozen'), $crossedAndIsolatedMargin);
            }
            $account['free'] = $free;
            $account['used'] = $used;
            $account['total'] = $total;
            $result[$code] = $account;
        }
        return $this->safe_balance($result);
    }

    public function create_market_buy_order_with_cost(string $symbol, float $cost, $params = array ()) {
        return Async\async(function () use ($symbol, $cost, $params) {
            /**
             *
             * @see https://doc.xt.com/#orderorderPost
             *
             * create a $market buy order by providing the $symbol and $cost
             * @param {string} $symbol unified $symbol of the $market to create an order in
             * @param {float} $cost how much you want to trade 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());
            $market = $this->market($symbol);
            if (!$market['spot']) {
                throw new NotSupported($this->id . ' createMarketBuyOrderWithCost() supports spot orders only');
            }
            return Async\await($this->create_order($symbol, 'market', 'buy', $cost, 1, $params));
        }) ();
    }

    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://doc.xt.com/#orderorderPost
             * @see https://doc.xt.com/#futures_ordercreate
             * @see https://doc.xt.com/#futures_entrustcreatePlan
             * @see https://doc.xt.com/#futures_entrustcreateProfit
             *
             * @param {string} $symbol unified $symbol of the $market to create an order in
             * @param {string} $type 'market' or 'limit'
             * @param {string} $side 'buy' or 'sell'
             * @param {float} $amount how much you want to trade in units of the base currency
             * @param {float} [$price] the $price to fulfill the order, in units of the quote currency, can be ignored in $market orders
             * @param {array} $params extra parameters specific to the xt api endpoint
             * @param {string} [$params->timeInForce] 'GTC', 'IOC', 'FOK' or 'GTX'
             * @param {string} [$params->entrustType] 'TAKE_PROFIT', 'STOP', 'TAKE_PROFIT_MARKET', 'STOP_MARKET', 'TRAILING_STOP_MARKET', required if stopPrice is defined, currently isn't functioning on xt's $side
             * @param {string} [$params->triggerPriceType] 'INDEX_PRICE', 'MARK_PRICE', 'LATEST_PRICE', required if stopPrice is defined
             * @param {float} [$params->triggerPrice] $price to trigger a stop order
             * @param {float} [$params->stopPrice] alias for triggerPrice
             * @param {float} [$params->stopLoss] $price to set a stop-loss on an open position
             * @param {float} [$params->takeProfit] $price to set a take-profit on an open position
             * @return {array} an {@link https://docs.ccxt.com/en/latest/manual.html#order-structure order structure}
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $symbol = $market['symbol'];
            if ($market['spot']) {
                return Async\await($this->create_spot_order($symbol, $type, $side, $amount, $price, $params));
            } else {
                return Async\await($this->create_contract_order($symbol, $type, $side, $amount, $price, $params));
            }
        }) ();
    }

    public function create_spot_order(string $symbol, $type, $side, $amount, $price = null, $params = array ()) {
        return Async\async(function () use ($symbol, $type, $side, $amount, $price, $params) {
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
                'side' => strtoupper($side),
                'type' => strtoupper($type),
            );
            $timeInForce = null;
            $marginMode = null;
            list($marginMode, $params) = $this->handle_margin_mode_and_params('createOrder', $params);
            $marginOrSpotRequest = ($marginMode !== null) ? 'LEVER' : 'SPOT';
            $request['bizType'] = $marginOrSpotRequest;
            if ($type === 'market') {
                $timeInForce = $this->safe_string_upper($params, 'timeInForce', 'FOK');
                if ($side === 'buy') {
                    $cost = $this->safe_string($params, 'cost');
                    $params = $this->omit($params, 'cost');
                    $createMarketBuyOrderRequiresPrice = $this->safe_bool($this->options, 'createMarketBuyOrderRequiresPrice', true);
                    if ($createMarketBuyOrderRequiresPrice) {
                        if ($price === null && ($cost === null)) {
                            throw new InvalidOrder($this->id . ' createOrder() requires a $price argument or $cost in $params for $market buy orders on spot markets to calculate the total $amount to spend ($amount * $price), alternatively set the $createMarketBuyOrderRequiresPrice option to false and pass in the $cost to spend into the $amount parameter');
                        } else {
                            $amountString = $this->number_to_string($amount);
                            $priceString = $this->number_to_string($price);
                            $costCalculated = null;
                            if ($price !== null) {
                                $costCalculated = Precise::string_mul($amountString, $priceString);
                            } else {
                                $costCalculated = $cost;
                            }
                            $request['quoteQty'] = $this->cost_to_precision($symbol, $costCalculated);
                        }
                    } else {
                        $amountCost = ($cost !== null) ? $cost : $amount;
                        $request['quoteQty'] = $this->cost_to_precision($symbol, $amountCost);
                    }
                }
            } else {
                $timeInForce = $this->safe_string_upper($params, 'timeInForce', 'GTC');
                $request['price'] = $this->price_to_precision($symbol, $price);
            }
            if (($side === 'sell') || ($type === 'limit')) {
                $request['quantity'] = $this->amount_to_precision($symbol, $amount);
            }
            $request['timeInForce'] = $timeInForce;
            $response = Async\await($this->privateSpotPostOrder ($this->extend($request, $params)));
            //
            //     {
            //         "rc" => 0,
            //         "mc" => "SUCCESS",
            //         "ma" => array(),
            //         "result" => {
            //             "orderId" => "204371980095156544"
            //         }
            //     }
            //
            $order = $this->safe_value($response, 'result', array());
            return $this->parse_order($order, $market);
        }) ();
    }

    public function create_contract_order(string $symbol, $type, $side, $amount, $price = null, $params = array ()) {
        return Async\async(function () use ($symbol, $type, $side, $amount, $price, $params) {
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
                'origQty' => $this->amount_to_precision($symbol, $amount),
            );
            $timeInForce = $this->safe_string_upper($params, 'timeInForce');
            if ($timeInForce !== null) {
                $request['timeInForce'] = $timeInForce;
            }
            $reduceOnly = $this->safe_value($params, 'reduceOnly', false);
            if ($side === 'buy') {
                $requestType = ($reduceOnly) ? 'SHORT' : 'LONG';
                $request['positionSide'] = $requestType;
            } else {
                $requestType = ($reduceOnly) ? 'LONG' : 'SHORT';
                $request['positionSide'] = $requestType;
            }
            $response = null;
            $triggerPrice = $this->safe_number_2($params, 'triggerPrice', 'stopPrice');
            $stopLoss = $this->safe_number_2($params, 'stopLoss', 'triggerStopPrice');
            $takeProfit = $this->safe_number_2($params, 'takeProfit', 'triggerProfitPrice');
            $isTrigger = ($triggerPrice !== null);
            $isStopLoss = ($stopLoss !== null);
            $isTakeProfit = ($takeProfit !== null);
            if ($price !== null) {
                if (!($isStopLoss) && !($isTakeProfit)) {
                    $request['price'] = $this->price_to_precision($symbol, $price);
                }
            }
            if ($isTrigger) {
                $request['timeInForce'] = $this->safe_string_upper($params, 'timeInForce', 'GTC');
                $request['triggerPriceType'] = $this->safe_string($params, 'triggerPriceType', 'LATEST_PRICE');
                $request['orderSide'] = strtoupper($side);
                $request['stopPrice'] = $this->price_to_precision($symbol, $triggerPrice);
                $entrustType = ($type === 'market') ? 'STOP_MARKET' : 'STOP';
                $request['entrustType'] = $entrustType;
                $params = $this->omit($params, 'triggerPrice');
                if ($market['linear']) {
                    $response = Async\await($this->privateLinearPostFutureTradeV1EntrustCreatePlan ($this->extend($request, $params)));
                } elseif ($market['inverse']) {
                    $response = Async\await($this->privateInversePostFutureTradeV1EntrustCreatePlan ($this->extend($request, $params)));
                }
            } elseif ($isStopLoss || $isTakeProfit) {
                if ($isStopLoss) {
                    $request['triggerStopPrice'] = $this->price_to_precision($symbol, $stopLoss);
                } else {
                    $request['triggerProfitPrice'] = $this->price_to_precision($symbol, $takeProfit);
                }
                $params = $this->omit($params, array( 'stopLoss', 'takeProfit' ));
                if ($market['linear']) {
                    $response = Async\await($this->privateLinearPostFutureTradeV1EntrustCreateProfit ($this->extend($request, $params)));
                } elseif ($market['inverse']) {
                    $response = Async\await($this->privateInversePostFutureTradeV1EntrustCreateProfit ($this->extend($request, $params)));
                }
            } else {
                $request['orderSide'] = strtoupper($side);
                $request['orderType'] = strtoupper($type);
                if ($market['linear']) {
                    $response = Async\await($this->privateLinearPostFutureTradeV1OrderCreate ($this->extend($request, $params)));
                } elseif ($market['inverse']) {
                    $response = Async\await($this->privateInversePostFutureTradeV1OrderCreate ($this->extend($request, $params)));
                }
            }
            //
            //     {
            //         "returnCode" => 0,
            //         "msgInfo" => "success",
            //         "error" => null,
            //         "result" => "206410760006650176"
            //     }
            //
            return $this->parse_order($response, $market);
        }) ();
    }

    public function fetch_order(string $id, ?string $symbol = null, $params = array ()) {
        return Async\async(function () use ($id, $symbol, $params) {
            /**
             * fetches information on an $order made by the user
             *
             * @see https://doc.xt.com/#orderorderGet
             * @see https://doc.xt.com/#futures_ordergetById
             * @see https://doc.xt.com/#futures_entrustgetPlanById
             * @see https://doc.xt.com/#futures_entrustgetProfitById
             *
             * @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 xt api endpoint
             * @param {bool} [$params->trigger] if the $order is a $trigger $order or not
             * @param {bool} [$params->stopLossTakeProfit] if the $order is a stop-loss or take-profit $order
             * @return {array} An {@link https://docs.ccxt.com/en/latest/manual.html#$order-structure $order structure}
             */
            Async\await($this->load_markets());
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
            }
            $request = array();
            $type = null;
            $subType = null;
            $response = null;
            list($type, $params) = $this->handle_market_type_and_params('fetchOrder', $market, $params);
            list($subType, $params) = $this->handle_sub_type_and_params('fetchOrder', $market, $params);
            $trigger = $this->safe_value($params, 'stop');
            $stopLossTakeProfit = $this->safe_value($params, 'stopLossTakeProfit');
            if ($trigger) {
                $request['entrustId'] = $id;
            } elseif ($stopLossTakeProfit) {
                $request['profitId'] = $id;
            } else {
                $request['orderId'] = $id;
            }
            if ($trigger) {
                $params = $this->omit($params, 'stop');
                if ($subType === 'inverse') {
                    $response = Async\await($this->privateInverseGetFutureTradeV1EntrustPlanDetail ($this->extend($request, $params)));
                } else {
                    $response = Async\await($this->privateLinearGetFutureTradeV1EntrustPlanDetail ($this->extend($request, $params)));
                }
            } elseif ($stopLossTakeProfit) {
                $params = $this->omit($params, 'stopLossTakeProfit');
                if ($subType === 'inverse') {
                    $response = Async\await($this->privateInverseGetFutureTradeV1EntrustProfitDetail ($this->extend($request, $params)));
                } else {
                    $response = Async\await($this->privateLinearGetFutureTradeV1EntrustProfitDetail ($this->extend($request, $params)));
                }
            } elseif ($subType === 'inverse') {
                $response = Async\await($this->privateInverseGetFutureTradeV1OrderDetail ($this->extend($request, $params)));
            } elseif (($subType === 'linear') || ($type === 'swap') || ($type === 'future')) {
                $response = Async\await($this->privateLinearGetFutureTradeV1OrderDetail ($this->extend($request, $params)));
            } else {
                $response = Async\await($this->privateSpotGetOrderOrderId ($this->extend($request, $params)));
            }
            //
            // spot
            //
            //     {
            //         "rc" => 0,
            //         "mc" => "SUCCESS",
            //         "ma" => array(),
            //         "result" => {
            //             "symbol" => "btc_usdt",
            //             "orderId" => "207505997850909952",
            //             "clientOrderId" => null,
            //             "baseCurrency" => "btc",
            //             "quoteCurrency" => "usdt",
            //             "side" => "BUY",
            //             "type" => "LIMIT",
            //             "timeInForce" => "GTC",
            //             "price" => "20000.00",
            //             "origQty" => "0.001000",
            //             "origQuoteQty" => "20.00",
            //             "executedQty" => "0.000000",
            //             "leavingQty" => "0.001000",
            //             "tradeBase" => "0.000000",
            //             "tradeQuote" => "0.00",
            //             "avgPrice" => null,
            //             "fee" => null,
            //             "feeCurrency" => null,
            //             "closed" => false,
            //             "state" => "NEW",
            //             "time" => 1679175285162,
            //             "updatedTime" => 1679175285255
            //         }
            //     }
            //
            // swap and future
            //
            //     {
            //         "returnCode" => 0,
            //         "msgInfo" => "success",
            //         "error" => null,
            //         "result" => {
            //             "orderId" => "211451874783183936",
            //             "clientOrderId" => null,
            //             "symbol" => "btc_usdt",
            //             "orderType" => "LIMIT",
            //             "orderSide" => "BUY",
            //             "positionSide" => "LONG",
            //             "timeInForce" => "GTC",
            //             "closePosition" => false,
            //             "price" => "20000",
            //             "origQty" => "10",
            //             "avgPrice" => "0",
            //             "executedQty" => "0",
            //             "marginFrozen" => "1.34533334",
            //             "remark" => null,
            //             "triggerProfitPrice" => null,
            //             "triggerStopPrice" => null,
            //             "sourceId" => null,
            //             "sourceType" => "DEFAULT",
            //             "forceClose" => false,
            //             "closeProfit" => null,
            //             "state" => "NEW",
            //             "createdTime" => 1680116055693,
            //             "updatedTime" => 1680116055693
            //         }
            //     }
            //
            // $trigger
            //
            //     {
            //         "returnCode" => 0,
            //         "msgInfo" => "success",
            //         "error" => null,
            //         "result" => {
            //             "entrustId" => "216300248132756992",
            //             "symbol" => "btc_usdt",
            //             "entrustType" => "STOP",
            //             "orderSide" => "SELL",
            //             "positionSide" => "SHORT",
            //             "timeInForce" => "GTC",
            //             "closePosition" => null,
            //             "price" => "20000",
            //             "origQty" => "1",
            //             "stopPrice" => "19000",
            //             "triggerPriceType" => "LATEST_PRICE",
            //             "state" => "NOT_TRIGGERED",
            //             "marketOrderLevel" => null,
            //             "createdTime" => 1681271998064,
            //             "updatedTime" => 1681271998064,
            //             "ordinary" => false
            //         }
            //     }
            //
            // stop-loss and take-profit
            //
            //     {
            //         "returnCode" => 0,
            //         "msgInfo" => "success",
            //         "error" => null,
            //         "result" => {
            //             "profitId" => "216306213226230400",
            //             "symbol" => "btc_usdt",
            //             "positionSide" => "LONG",
            //             "origQty" => "1",
            //             "triggerPriceType" => "LATEST_PRICE",
            //             "triggerProfitPrice" => null,
            //             "triggerStopPrice" => "20000",
            //             "entryPrice" => null,
            //             "positionSize" => null,
            //             "isolatedMargin" => null,
            //             "executedQty" => null,
            //             "avgPrice" => null,
            //             "positionType" => "ISOLATED",
            //             "state" => "NOT_TRIGGERED",
            //             "createdTime" => 1681273420039
            //         }
            //     }
            //
            $order = $this->safe_value($response, 'result', array());
            return $this->parse_order($order, $market);
        }) ();
    }

    public function fetch_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * fetches information on multiple $orders made by the user
             *
             * @see https://doc.xt.com/#orderhistoryOrderGet
             * @see https://doc.xt.com/#futures_ordergetHistory
             * @see https://doc.xt.com/#futures_entrustgetPlanHistory
             *
             * @param {string} [$symbol] unified $market $symbol of the $market the $orders were made in
             * @param {int} [$since] timestamp in ms of the earliest order
             * @param {int} [$limit] the maximum number of order structures to retrieve
             * @param {array} $params extra parameters specific to the xt api endpoint
             * @param {bool} [$params->trigger] if the order is a $trigger order or not
             * @return {array[]} a list of {@link https://docs.ccxt.com/en/latest/manual.html#order-structure order structures}
             */
            Async\await($this->load_markets());
            $request = array();
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
                $request['symbol'] = $market['id'];
            }
            if ($since !== null) {
                $request['startTime'] = $since;
            }
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            $type = null;
            $subType = null;
            $response = null;
            list($type, $params) = $this->handle_market_type_and_params('fetchOrders', $market, $params);
            list($subType, $params) = $this->handle_sub_type_and_params('fetchOrders', $market, $params);
            $trigger = $this->safe_value_2($params, 'trigger', 'stop');
            if ($trigger) {
                $params = $this->omit($params, array( 'trigger', 'stop' ));
                if ($subType === 'inverse') {
                    $response = Async\await($this->privateInverseGetFutureTradeV1EntrustPlanListHistory ($this->extend($request, $params)));
                } else {
                    $response = Async\await($this->privateLinearGetFutureTradeV1EntrustPlanListHistory ($this->extend($request, $params)));
                }
            } elseif ($subType === 'inverse') {
                $response = Async\await($this->privateInverseGetFutureTradeV1OrderListHistory ($this->extend($request, $params)));
            } elseif (($subType === 'linear') || ($type === 'swap') || ($type === 'future')) {
                $response = Async\await($this->privateLinearGetFutureTradeV1OrderListHistory ($this->extend($request, $params)));
            } else {
                $marginMode = null;
                list($marginMode, $params) = $this->handle_margin_mode_and_params('fetchOrders', $params);
                $marginOrSpotRequest = ($marginMode !== null) ? 'LEVER' : 'SPOT';
                $request['bizType'] = $marginOrSpotRequest;
                $response = Async\await($this->privateSpotGetHistoryOrder ($this->extend($request, $params)));
            }
            //
            //  spot and margin
            //
            //     {
            //         "rc" => 0,
            //         "mc" => "SUCCESS",
            //         "ma" => array(),
            //         "result" => {
            //             "hasPrev" => false,
            //             "hasNext" => true,
            //             "items" => array(
            //                 array(
            //                     "symbol" => "btc_usdt",
            //                     "orderId" => "207505997850909952",
            //                     "clientOrderId" => null,
            //                     "baseCurrency" => "btc",
            //                     "quoteCurrency" => "usdt",
            //                     "side" => "BUY",
            //                     "type" => "LIMIT",
            //                     "timeInForce" => "GTC",
            //                     "price" => "20000.00",
            //                     "origQty" => "0.001000",
            //                     "origQuoteQty" => "20.00",
            //                     "executedQty" => "0.000000",
            //                     "leavingQty" => "0.000000",
            //                     "tradeBase" => "0.000000",
            //                     "tradeQuote" => "0.00",
            //                     "avgPrice" => null,
            //                     "fee" => null,
            //                     "feeCurrency" => null,
            //                     "closed" => true,
            //                     "state" => "CANCELED",
            //                     "time" => 1679175285162,
            //                     "updatedTime" => 1679175488492
            //                 ),
            //             )
            //         }
            //     }
            //
            // swap and future
            //
            //     {
            //         "returnCode" => 0,
            //         "msgInfo" => "success",
            //         "error" => null,
            //         "result" => {
            //             "hasPrev" => false,
            //             "hasNext" => true,
            //             "items" => array(
            //                 array(
            //                     "orderId" => "207519546930995456",
            //                     "clientOrderId" => null,
            //                     "symbol" => "btc_usdt",
            //                     "orderType" => "LIMIT",
            //                     "orderSide" => "BUY",
            //                     "positionSide" => "LONG",
            //                     "timeInForce" => "GTC",
            //                     "closePosition" => false,
            //                     "price" => "20000",
            //                     "origQty" => "100",
            //                     "avgPrice" => "0",
            //                     "executedQty" => "0",
            //                     "marginFrozen" => "4.12",
            //                     "remark" => null,
            //                     "triggerProfitPrice" => null,
            //                     "triggerStopPrice" => null,
            //                     "sourceId" => null,
            //                     "sourceType" => "DEFAULT",
            //                     "forceClose" => false,
            //                     "closeProfit" => null,
            //                     "state" => "CANCELED",
            //                     "createdTime" => 1679178515689,
            //                     "updatedTime" => 1679180096172
            //                 ),
            //             )
            //         }
            //     }
            //
            // stop
            //
            //     {
            //         "returnCode" => 0,
            //         "msgInfo" => "success",
            //         "error" => null,
            //         "result" => {
            //             "hasPrev" => false,
            //             "hasNext" => false,
            //             "items" => array(
            //                 array(
            //                     "entrustId" => "216300248132756992",
            //                     "symbol" => "btc_usdt",
            //                     "entrustType" => "STOP",
            //                     "orderSide" => "SELL",
            //                     "positionSide" => "SHORT",
            //                     "timeInForce" => "GTC",
            //                     "closePosition" => null,
            //                     "price" => "20000",
            //                     "origQty" => "1",
            //                     "stopPrice" => "19000",
            //                     "triggerPriceType" => "LATEST_PRICE",
            //                     "state" => "USER_REVOCATION",
            //                     "marketOrderLevel" => null,
            //                     "createdTime" => 1681271998064,
            //                     "updatedTime" => 1681273188674,
            //                     "ordinary" => false
            //                 ),
            //             )
            //         }
            //     }
            //
            $data = $this->safe_value($response, 'result', array());
            $orders = $this->safe_value($data, 'items', array());
            return $this->parse_orders($orders, $market, $since, $limit);
        }) ();
    }

    public function fetch_orders_by_status($status, ?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($status, $symbol, $since, $limit, $params) {
            Async\await($this->load_markets());
            $request = array();
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
                $request['symbol'] = $market['id'];
            }
            if ($limit !== null) {
                $request['size'] = $limit;
            }
            if ($since !== null) {
                $request['startTime'] = $since;
            }
            $type = null;
            $subType = null;
            $response = null;
            list($type, $params) = $this->handle_market_type_and_params('fetchOrdersByStatus', $market, $params);
            list($subType, $params) = $this->handle_sub_type_and_params('fetchOrdersByStatus', $market, $params);
            $trigger = $this->safe_bool_2($params, 'stop', 'trigger');
            $stopLossTakeProfit = $this->safe_value($params, 'stopLossTakeProfit');
            if ($status === 'open') {
                if ($trigger || $stopLossTakeProfit) {
                    $request['state'] = 'NOT_TRIGGERED';
                } elseif ($type === 'swap') {
                    $request['state'] = 'UNFINISHED'; // NEW & PARTIALLY_FILLED
                }
            } elseif ($status === 'closed') {
                if ($trigger || $stopLossTakeProfit) {
                    $request['state'] = 'TRIGGERED';
                } else {
                    $request['state'] = 'FILLED';
                }
            } elseif ($status === 'canceled') {
                if ($trigger || $stopLossTakeProfit) {
                    $request['state'] = 'USER_REVOCATION';
                } else {
                    $request['state'] = 'CANCELED';
                }
            } else {
                $request['state'] = $status;
            }
            if ($trigger || $stopLossTakeProfit || ($subType !== null) || ($type === 'swap') || ($type === 'future')) {
                if ($since !== null) {
                    $request['startTime'] = $since;
                }
                if ($limit !== null) {
                    $request['size'] = $limit;
                }
            }
            if ($trigger) {
                $params = $this->omit($params, array( 'stop', 'trigger' ));
                if ($subType === 'inverse') {
                    $response = Async\await($this->privateInverseGetFutureTradeV1EntrustPlanList ($this->extend($request, $params)));
                } else {
                    $response = Async\await($this->privateLinearGetFutureTradeV1EntrustPlanList ($this->extend($request, $params)));
                }
            } elseif ($stopLossTakeProfit) {
                $params = $this->omit($params, 'stopLossTakeProfit');
                if ($subType === 'inverse') {
                    $response = Async\await($this->privateInverseGetFutureTradeV1EntrustProfitList ($this->extend($request, $params)));
                } else {
                    $response = Async\await($this->privateLinearGetFutureTradeV1EntrustProfitList ($this->extend($request, $params)));
                }
            } elseif (($subType !== null) || ($type === 'swap') || ($type === 'future')) {
                if ($subType === 'inverse') {
                    $response = Async\await($this->privateInverseGetFutureTradeV1OrderList ($this->extend($request, $params)));
                } else {
                    $response = Async\await($this->privateLinearGetFutureTradeV1OrderList ($this->extend($request, $params)));
                }
            } else {
                $marginMode = null;
                list($marginMode, $params) = $this->handle_margin_mode_and_params('fetchOrdersByStatus', $params);
                $marginOrSpotRequest = ($marginMode !== null) ? 'LEVER' : 'SPOT';
                $request['bizType'] = $marginOrSpotRequest;
                if ($status !== 'open') {
                    if ($since !== null) {
                        $request['startTime'] = $since;
                    }
                    if ($limit !== null) {
                        $request = $this->omit($request, 'size');
                        $request['limit'] = $limit;
                    }
                    $response = Async\await($this->privateSpotGetHistoryOrder ($this->extend($request, $params)));
                } else {
                    $response = Async\await($this->privateSpotGetOpenOrder ($this->extend($request, $params)));
                }
            }
            //
            // spot and margin
            //
            //     {
            //         "rc" => 0,
            //         "mc" => "SUCCESS",
            //         "ma" => array(),
            //         "result" => {
            //             "hasPrev" => false,
            //             "hasNext" => true,
            //             "items" => array(
            //                 array(
            //                     "symbol" => "btc_usdt",
            //                     "orderId" => "207505997850909952",
            //                     "clientOrderId" => null,
            //                     "baseCurrency" => "btc",
            //                     "quoteCurrency" => "usdt",
            //                     "side" => "BUY",
            //                     "type" => "LIMIT",
            //                     "timeInForce" => "GTC",
            //                     "price" => "20000.00",
            //                     "origQty" => "0.001000",
            //                     "origQuoteQty" => "20.00",
            //                     "executedQty" => "0.000000",
            //                     "leavingQty" => "0.000000",
            //                     "tradeBase" => "0.000000",
            //                     "tradeQuote" => "0.00",
            //                     "avgPrice" => null,
            //                     "fee" => null,
            //                     "feeCurrency" => null,
            //                     "closed" => true,
            //                     "state" => "CANCELED",
            //                     "time" => 1679175285162,
            //                     "updatedTime" => 1679175488492
            //                 ),
            //             )
            //         }
            //     }
            //
            // spot and margin => fetchOpenOrders
            //
            //     {
            //         "rc" => 0,
            //         "mc" => "SUCCESS",
            //         "ma" => array(),
            //         "result" => array(
            //             array(
            //                 "symbol" => "eth_usdt",
            //                 "orderId" => "208249323222264320",
            //                 "clientOrderId" => null,
            //                 "baseCurrency" => "eth",
            //                 "quoteCurrency" => "usdt",
            //                 "side" => "BUY",
            //                 "type" => "LIMIT",
            //                 "timeInForce" => "GTC",
            //                 "price" => "1300.00",
            //                 "origQty" => "0.0032",
            //                 "origQuoteQty" => "4.16",
            //                 "executedQty" => "0.0000",
            //                 "leavingQty" => "0.0032",
            //                 "tradeBase" => "0.0000",
            //                 "tradeQuote" => "0.00",
            //                 "avgPrice" => null,
            //                 "fee" => null,
            //                 "feeCurrency" => null,
            //                 "closed" => false,
            //                 "state" => "NEW",
            //                 "time" => 1679352507741,
            //                 "updatedTime" => 1679352507869
            //             ),
            //         )
            //     }
            //
            // swap and future
            //
            //     {
            //         "returnCode" => 0,
            //         "msgInfo" => "success",
            //         "error" => null,
            //         "result" => {
            //             "page" => 1,
            //             "ps" => 10,
            //             "total" => 25,
            //             "items" => array(
            //                 array(
            //                     "orderId" => "207519546930995456",
            //                     "clientOrderId" => null,
            //                     "symbol" => "btc_usdt",
            //                     "orderType" => "LIMIT",
            //                     "orderSide" => "BUY",
            //                     "positionSide" => "LONG",
            //                     "timeInForce" => "GTC",
            //                     "closePosition" => false,
            //                     "price" => "20000",
            //                     "origQty" => "100",
            //                     "avgPrice" => "0",
            //                     "executedQty" => "0",
            //                     "marginFrozen" => "4.12",
            //                     "remark" => null,
            //                     "triggerProfitPrice" => null,
            //                     "triggerStopPrice" => null,
            //                     "sourceId" => null,
            //                     "sourceType" => "DEFAULT",
            //                     "forceClose" => false,
            //                     "closeProfit" => null,
            //                     "state" => "CANCELED",
            //                     "createdTime" => 1679178515689,
            //                     "updatedTime" => 1679180096172
            //                 ),
            //             )
            //         }
            //     }
            //
            // stop
            //
            //     {
            //         "returnCode" => 0,
            //         "msgInfo" => "success",
            //         "error" => null,
            //         "result" => {
            //             "page" => 1,
            //             "ps" => 3,
            //             "total" => 8,
            //             "items" => array(
            //                 array(
            //                     "entrustId" => "216300248132756992",
            //                     "symbol" => "btc_usdt",
            //                     "entrustType" => "STOP",
            //                     "orderSide" => "SELL",
            //                     "positionSide" => "SHORT",
            //                     "timeInForce" => "GTC",
            //                     "closePosition" => null,
            //                     "price" => "20000",
            //                     "origQty" => "1",
            //                     "stopPrice" => "19000",
            //                     "triggerPriceType" => "LATEST_PRICE",
            //                     "state" => "USER_REVOCATION",
            //                     "marketOrderLevel" => null,
            //                     "createdTime" => 1681271998064,
            //                     "updatedTime" => 1681273188674,
            //                     "ordinary" => false
            //                 ),
            //             )
            //         }
            //     }
            //
            // stop-loss and take-profit
            //
            //     {
            //         "returnCode" => 0,
            //         "msgInfo" => "success",
            //         "error" => null,
            //         "result" => {
            //             "page" => 1,
            //             "ps" => 3,
            //             "total" => 2,
            //             "items" => array(
            //                 array(
            //                     "profitId" => "216306213226230400",
            //                     "symbol" => "btc_usdt",
            //                     "positionSide" => "LONG",
            //                     "origQty" => "1",
            //                     "triggerPriceType" => "LATEST_PRICE",
            //                     "triggerProfitPrice" => null,
            //                     "triggerStopPrice" => "20000",
            //                     "entryPrice" => "0",
            //                     "positionSize" => "0",
            //                     "isolatedMargin" => "0",
            //                     "executedQty" => "0",
            //                     "avgPrice" => null,
            //                     "positionType" => "ISOLATED",
            //                     "state" => "USER_REVOCATION",
            //                     "createdTime" => 1681273420039
            //                 ),
            //             )
            //         }
            //     }
            //
            $orders = array();
            $resultDict = $this->safe_dict($response, 'result');
            if ($resultDict !== null) {
                $orders = $this->safe_list($resultDict, 'items', array());
            } else {
                $orders = $this->safe_list($response, 'result');
            }
            return $this->parse_orders($orders, $market, $since, $limit);
        }) ();
    }

    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://doc.xt.com/#orderopenOrderGet
             * @see https://doc.xt.com/#futures_ordergetOrders
             * @see https://doc.xt.com/#futures_entrustgetPlan
             * @see https://doc.xt.com/#futures_entrustgetProfit
             *
             * @param {string} [$symbol] unified market $symbol of the market the orders were made in
             * @param {int} [$since] timestamp in ms of the earliest order
             * @param {int} [$limit] the maximum number of open order structures to retrieve
             * @param {array} $params extra parameters specific to the xt api endpoint
             * @param {bool} [$params->trigger] if the order is a trigger order or not
             * @param {bool} [$params->stopLossTakeProfit] if the order is a stop-loss or take-profit order
             * @return {array[]} a list of {@link https://docs.ccxt.com/en/latest/manual.html#order-structure order structures}
             */
            return Async\await($this->fetch_orders_by_status('open', $symbol, $since, $limit, $params));
        }) ();
    }

    public function fetch_closed_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * fetches information on multiple closed orders made by the user
             *
             * @see https://doc.xt.com/#orderhistoryOrderGet
             * @see https://doc.xt.com/#futures_ordergetOrders
             * @see https://doc.xt.com/#futures_entrustgetPlan
             * @see https://doc.xt.com/#futures_entrustgetProfit
             *
             * @param {string} [$symbol] unified market $symbol of the market the orders were made in
             * @param {int} [$since] timestamp in ms of the earliest order
             * @param {int} [$limit] the maximum number of order structures to retrieve
             * @param {array} $params extra parameters specific to the xt api endpoint
             * @param {bool} [$params->trigger] if the order is a trigger order or not
             * @param {bool} [$params->stopLossTakeProfit] if the order is a stop-loss or take-profit order
             * @return {array[]} a list of {@link https://docs.ccxt.com/en/latest/manual.html#order-structure order structures}
             */
            return Async\await($this->fetch_orders_by_status('closed', $symbol, $since, $limit, $params));
        }) ();
    }

    public function fetch_canceled_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * fetches information on multiple canceled orders made by the user
             *
             * @see https://doc.xt.com/#orderhistoryOrderGet
             * @see https://doc.xt.com/#futures_ordergetOrders
             * @see https://doc.xt.com/#futures_entrustgetPlan
             * @see https://doc.xt.com/#futures_entrustgetProfit
             *
             * @param {string} [$symbol] unified market $symbol of the market the orders were made in
             * @param {int} [$since] timestamp in ms of the earliest order
             * @param {int} [$limit] the maximum number of order structures to retrieve
             * @param {array} $params extra parameters specific to the xt api endpoint
             * @param {bool} [$params->trigger] if the order is a trigger order or not
             * @param {bool} [$params->stopLossTakeProfit] if the order is a stop-loss or take-profit order
             * @return {array} a list of {@link https://docs.ccxt.com/en/latest/manual.html#order-structure order structures}
             */
            return Async\await($this->fetch_orders_by_status('canceled', $symbol, $since, $limit, $params));
        }) ();
    }

    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://doc.xt.com/#orderorderDel
             * @see https://doc.xt.com/#futures_ordercancel
             * @see https://doc.xt.com/#futures_entrustcancelPlan
             * @see https://doc.xt.com/#futures_entrustcancelProfit
             *
             * @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 xt api endpoint
             * @param {bool} [$params->trigger] if the $order is a $trigger $order or not
             * @param {bool} [$params->stopLossTakeProfit] if the $order is a stop-loss or take-profit $order
             * @return {array} An {@link https://docs.ccxt.com/en/latest/manual.html#$order-structure $order structure}
             */
            Async\await($this->load_markets());
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
            }
            $request = array();
            $type = null;
            $subType = null;
            $response = null;
            list($type, $params) = $this->handle_market_type_and_params('cancelOrder', $market, $params);
            list($subType, $params) = $this->handle_sub_type_and_params('cancelOrder', $market, $params);
            $trigger = $this->safe_value_2($params, 'trigger', 'stop');
            $stopLossTakeProfit = $this->safe_value($params, 'stopLossTakeProfit');
            if ($trigger) {
                $request['entrustId'] = $id;
            } elseif ($stopLossTakeProfit) {
                $request['profitId'] = $id;
            } else {
                $request['orderId'] = $id;
            }
            if ($trigger) {
                $params = $this->omit($params, array( 'trigger', 'stop' ));
                if ($subType === 'inverse') {
                    $response = Async\await($this->privateInversePostFutureTradeV1EntrustCancelPlan ($this->extend($request, $params)));
                } else {
                    $response = Async\await($this->privateLinearPostFutureTradeV1EntrustCancelPlan ($this->extend($request, $params)));
                }
            } elseif ($stopLossTakeProfit) {
                $params = $this->omit($params, 'stopLossTakeProfit');
                if ($subType === 'inverse') {
                    $response = Async\await($this->privateInversePostFutureTradeV1EntrustCancelProfitStop ($this->extend($request, $params)));
                } else {
                    $response = Async\await($this->privateLinearPostFutureTradeV1EntrustCancelProfitStop ($this->extend($request, $params)));
                }
            } elseif ($subType === 'inverse') {
                $response = Async\await($this->privateInversePostFutureTradeV1OrderCancel ($this->extend($request, $params)));
            } elseif (($subType === 'linear') || ($type === 'swap') || ($type === 'future')) {
                $response = Async\await($this->privateLinearPostFutureTradeV1OrderCancel ($this->extend($request, $params)));
            } else {
                $response = Async\await($this->privateSpotDeleteOrderOrderId ($this->extend($request, $params)));
            }
            //
            // spot
            //
            //     {
            //         "rc" => 0,
            //         "mc" => "SUCCESS",
            //         "ma" => array(),
            //         "result" => {
            //             "cancelId" => "208322474307982720"
            //         }
            //     }
            //
            // swap and future
            //
            //     {
            //         "returnCode" => 0,
            //         "msgInfo" => "success",
            //         "error" => null,
            //         "result" => "208319789679471616"
            //     }
            //
            $isContractResponse = (($subType !== null) || ($type === 'swap') || ($type === 'future'));
            $order = $isContractResponse ? $response : $this->safe_value($response, 'result', array());
            return $this->parse_order($order, $market);
        }) ();
    }

    public function cancel_all_orders(?string $symbol = null, $params = array ()) {
        return Async\async(function () use ($symbol, $params) {
            /**
             * cancel all open orders in a $market
             *
             * @see https://doc.xt.com/#orderopenOrderDel
             * @see https://doc.xt.com/#futures_ordercancelBatch
             * @see https://doc.xt.com/#futures_entrustcancelPlanBatch
             * @see https://doc.xt.com/#futures_entrustcancelProfitBatch
             *
             * @param {string} [$symbol] unified $market $symbol of the $market to cancel orders in
             * @param {array} $params extra parameters specific to the xt api endpoint
             * @param {bool} [$params->trigger] if the order is a $trigger order or not
             * @param {bool} [$params->stopLossTakeProfit] if the order is a stop-loss or take-profit order
             * @return {array[]} a list of {@link https://docs.ccxt.com/en/latest/manual.html#order-structure order structures}
             */
            Async\await($this->load_markets());
            $request = array();
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
                $request['symbol'] = $market['id'];
            }
            $type = null;
            $subType = null;
            $response = null;
            list($type, $params) = $this->handle_market_type_and_params('cancelAllOrders', $market, $params);
            list($subType, $params) = $this->handle_sub_type_and_params('cancelAllOrders', $market, $params);
            $trigger = $this->safe_value_2($params, 'trigger', 'stop');
            $stopLossTakeProfit = $this->safe_value($params, 'stopLossTakeProfit');
            if ($trigger) {
                $params = $this->omit($params, array( 'trigger', 'stop' ));
                if ($subType === 'inverse') {
                    $response = Async\await($this->privateInversePostFutureTradeV1EntrustCancelAllPlan ($this->extend($request, $params)));
                } else {
                    $response = Async\await($this->privateLinearPostFutureTradeV1EntrustCancelAllPlan ($this->extend($request, $params)));
                }
            } elseif ($stopLossTakeProfit) {
                $params = $this->omit($params, 'stopLossTakeProfit');
                if ($subType === 'inverse') {
                    $response = Async\await($this->privateInversePostFutureTradeV1EntrustCancelAllProfitStop ($this->extend($request, $params)));
                } else {
                    $response = Async\await($this->privateLinearPostFutureTradeV1EntrustCancelAllProfitStop ($this->extend($request, $params)));
                }
            } elseif ($subType === 'inverse') {
                $response = Async\await($this->privateInversePostFutureTradeV1OrderCancelAll ($this->extend($request, $params)));
            } elseif (($subType === 'linear') || ($type === 'swap') || ($type === 'future')) {
                $response = Async\await($this->privateLinearPostFutureTradeV1OrderCancelAll ($this->extend($request, $params)));
            } else {
                $marginMode = null;
                list($marginMode, $params) = $this->handle_margin_mode_and_params('cancelAllOrders', $params);
                $marginOrSpotRequest = ($marginMode !== null) ? 'LEVER' : 'SPOT';
                $request['bizType'] = $marginOrSpotRequest;
                $response = Async\await($this->privateSpotDeleteOpenOrder ($this->extend($request, $params)));
            }
            //
            // spot and margin
            //
            //     {
            //         "rc" => 0,
            //         "mc" => "SUCCESS",
            //         "ma" => array(),
            //         "result" => null
            //     }
            //
            // swap and future
            //
            //     {
            //         "returnCode" => 0,
            //         "msgInfo" => "success",
            //         "error" => null,
            //         "result" => true
            //     }
            //
            return array(
                $this->safe_order($response),
            );
        }) ();
    }

    public function cancel_orders(array $ids, ?string $symbol = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($ids, $symbol, $params) {
            /**
             * cancel multiple orders
             *
             * @see https://doc.xt.com/#orderbatchOrderDel
             *
             * @param {string[]} $ids order $ids
             * @param {string} [$symbol] unified $market $symbol of the $market to cancel orders in
             * @param {array} $params extra parameters specific to the xt api endpoint
             * @return {array[]} a list of {@link https://docs.ccxt.com/en/latest/manual.html#order-structure order structures}
             */
            Async\await($this->load_markets());
            $request = array(
                'orderIds' => $ids,
            );
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
            }
            $subType = null;
            list($subType, $params) = $this->handle_sub_type_and_params('cancelOrders', $market, $params);
            if ($subType !== null) {
                throw new NotSupported($this->id . ' cancelOrders() does not support swap and future orders, only spot orders are accepted');
            }
            $response = Async\await($this->privateSpotDeleteBatchOrder ($this->extend($request, $params)));
            //
            // spot
            //
            //     {
            //         "rc" => 0,
            //         "mc" => "SUCCESS",
            //         "ma" => array(),
            //         "result" => null
            //     }
            //
            return array(
                $this->safe_order($response),
            );
        }) ();
    }

    public function parse_order($order, $market = null) {
        //
        // spot => createOrder
        //
        //     {
        //         "orderId" => "204371980095156544"
        //     }
        //
        // spot => cancelOrder
        //
        //     {
        //         "cancelId" => "208322474307982720"
        //     }
        //
        // swap and future => createOrder, cancelOrder, editOrder
        //
        //     {
        //         "returnCode" => 0,
        //         "msgInfo" => "success",
        //         "error" => null,
        //         "result" => "206410760006650176"
        //     }
        //
        // spot => fetchOrder, fetchOrders, fetchOpenOrders, fetchClosedOrders, fetchCanceledOrders, fetchOrdersByStatus
        //
        //     {
        //         "symbol" => "btc_usdt",
        //         "orderId" => "207505997850909952",
        //         "clientOrderId" => null,
        //         "baseCurrency" => "btc",
        //         "quoteCurrency" => "usdt",
        //         "side" => "BUY",
        //         "type" => "LIMIT",
        //         "timeInForce" => "GTC",
        //         "price" => "20000.00",
        //         "origQty" => "0.001000",
        //         "origQuoteQty" => "20.00",
        //         "executedQty" => "0.000000",
        //         "leavingQty" => "0.001000",
        //         "tradeBase" => "0.000000",
        //         "tradeQuote" => "0.00",
        //         "avgPrice" => null,
        //         "fee" => null,
        //         "feeCurrency" => null,
        //         "closed" => false,
        //         "state" => "NEW",
        //         "time" => 1679175285162,
        //         "updatedTime" => 1679175285255
        //     }
        //
        // swap and future => fetchOrder, fetchOrders, fetchOpenOrders, fetchClosedOrders, fetchCanceledOrders, fetchOrdersByStatus
        //
        //     {
        //         "orderId" => "207519546930995456",
        //         "clientOrderId" => null,
        //         "symbol" => "btc_usdt",
        //         "orderType" => "LIMIT",
        //         "orderSide" => "BUY",
        //         "positionSide" => "LONG",
        //         "timeInForce" => "GTC",
        //         "closePosition" => false,
        //         "price" => "20000",
        //         "origQty" => "100",
        //         "avgPrice" => "0",
        //         "executedQty" => "0",
        //         "marginFrozen" => "4.12",
        //         "remark" => null,
        //         "triggerProfitPrice" => null,
        //         "triggerStopPrice" => null,
        //         "sourceId" => null,
        //         "sourceType" => "DEFAULT",
        //         "forceClose" => false,
        //         "closeProfit" => null,
        //         "state" => "CANCELED",
        //         "createdTime" => 1679178515689,
        //         "updatedTime" => 1679180096172
        //     }
        //
        // trigger => fetchOrder, fetchOrders, fetchOpenOrders, fetchClosedOrders, fetchCanceledOrders, fetchOrdersByStatus
        //
        //     {
        //         "entrustId" => "216300248132756992",
        //         "symbol" => "btc_usdt",
        //         "entrustType" => "STOP",
        //         "orderSide" => "SELL",
        //         "positionSide" => "SHORT",
        //         "timeInForce" => "GTC",
        //         "closePosition" => null,
        //         "price" => "20000",
        //         "origQty" => "1",
        //         "stopPrice" => "19000",
        //         "triggerPriceType" => "LATEST_PRICE",
        //         "state" => "NOT_TRIGGERED",
        //         "marketOrderLevel" => null,
        //         "createdTime" => 1681271998064,
        //         "updatedTime" => 1681271998064,
        //         "ordinary" => false
        //     }
        //
        // stop-loss and take-profit => fetchOrder, fetchOpenOrders, fetchClosedOrders, fetchCanceledOrders, fetchOrdersByStatus
        //
        //     {
        //         "profitId" => "216306213226230400",
        //         "symbol" => "btc_usdt",
        //         "positionSide" => "LONG",
        //         "origQty" => "1",
        //         "triggerPriceType" => "LATEST_PRICE",
        //         "triggerProfitPrice" => null,
        //         "triggerStopPrice" => "20000",
        //         "entryPrice" => null,
        //         "positionSize" => null,
        //         "isolatedMargin" => null,
        //         "executedQty" => null,
        //         "avgPrice" => null,
        //         "positionType" => "ISOLATED",
        //         "state" => "NOT_TRIGGERED",
        //         "createdTime" => 1681273420039
        //     }
        //
        // spot editOrder
        //
        //     {
        //         "orderId" => "484203027161892224",
        //         "modifyId" => "484203544105344000",
        //         "clientModifyId" => null
        //     }
        //
        $marketId = $this->safe_string($order, 'symbol');
        $marketType = (is_array($order) && array_key_exists('result', $order)) || (is_array($order) && array_key_exists('positionSide', $order)) ? 'contract' : 'spot';
        $market = $this->safe_market($marketId, $market, null, $marketType);
        $symbol = $this->safe_symbol($marketId, $market, null, $marketType);
        $timestamp = $this->safe_integer_2($order, 'time', 'createdTime');
        $quantity = $this->safe_number($order, 'origQty');
        $amount = ($marketType === 'spot') ? $quantity : Precise::string_mul($this->number_to_string($quantity), $this->number_to_string($market['contractSize']));
        $filledQuantity = $this->safe_number($order, 'executedQty');
        $filled = ($marketType === 'spot') ? $filledQuantity : Precise::string_mul($this->number_to_string($filledQuantity), $this->number_to_string($market['contractSize']));
        $lastUpdatedTimestamp = $this->safe_integer($order, 'updatedTime');
        return $this->safe_order(array(
            'info' => $order,
            'id' => $this->safe_string_n($order, array( 'orderId', 'result', 'cancelId', 'entrustId', 'profitId' )),
            'clientOrderId' => $this->safe_string_2($order, 'clientOrderId', 'clientModifyId'),
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'lastTradeTimestamp' => $lastUpdatedTimestamp,
            'lastUpdateTimestamp' => $lastUpdatedTimestamp,
            'symbol' => $symbol,
            'type' => $this->safe_string_lower_2($order, 'type', 'orderType'),
            'timeInForce' => $this->safe_string($order, 'timeInForce'),
            'postOnly' => null,
            'side' => $this->safe_string_lower_2($order, 'side', 'orderSide'),
            'price' => $this->safe_number($order, 'price'),
            'triggerPrice' => $this->safe_number($order, 'stopPrice'),
            'stopLoss' => $this->safe_number($order, 'triggerStopPrice'),
            'takeProfit' => $this->safe_number($order, 'triggerProfitPrice'),
            'amount' => $amount,
            'filled' => $filled,
            'remaining' => $this->safe_number($order, 'leavingQty'),
            'cost' => null,
            'average' => $this->safe_number($order, 'avgPrice'),
            'status' => $this->parse_order_status($this->safe_string($order, 'state')),
            'fee' => array(
                'currency' => $this->safe_currency_code($this->safe_string($order, 'feeCurrency')),
                'cost' => $this->safe_number($order, 'fee'),
            ),
            'trades' => null,
        ), $market);
    }

    public function parse_order_status($status) {
        $statuses = array(
            'NEW' => 'open',
            'PARTIALLY_FILLED' => 'open',
            'FILLED' => 'closed',
            'CANCELED' => 'canceled',
            'REJECTED' => 'rejected',
            'EXPIRED' => 'expired',
            'UNFINISHED' => 'open',
            'NOT_TRIGGERED' => 'open',
            'TRIGGERING' => 'open',
            'TRIGGERED' => 'closed',
            'USER_REVOCATION' => 'canceled',
            'PLATFORM_REVOCATION' => 'rejected',
            'HISTORY' => 'expired',
        );
        return $this->safe_string($statuses, $status, $status);
    }

    public function fetch_ledger(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $since, $limit, $params) {
            /**
             * fetch the history of changes, actions done by the user or operations that altered the balance of the user
             *
             * @see https://doc.xt.com/#futures_usergetBalanceBill
             *
             * @param {string} [$code] unified $currency $code
             * @param {int} [$since] timestamp in ms of the earliest $ledger entry
             * @param {int} [$limit] max number of $ledger entries to return
             * @param {array} $params extra parameters specific to the xt api endpoint
             * @return {array} a {@link https://docs.ccxt.com/en/latest/manual.html#$ledger-structure $ledger structure}
             */
            Async\await($this->load_markets());
            $request = array();
            $currency = null;
            if ($code !== null) {
                $currency = $this->currency($code);
            }
            if ($since !== null) {
                $request['startTime'] = $since;
            }
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            $type = null;
            $subType = null;
            $response = null;
            list($type, $params) = $this->handle_market_type_and_params('fetchLedger', null, $params);
            list($subType, $params) = $this->handle_sub_type_and_params('fetchLedger', null, $params);
            if ($subType === 'inverse') {
                $response = Async\await($this->privateInverseGetFutureUserV1BalanceBills ($this->extend($request, $params)));
            } elseif (($subType === 'linear') || ($type === 'swap') || ($type === 'future')) {
                $response = Async\await($this->privateLinearGetFutureUserV1BalanceBills ($this->extend($request, $params)));
            } else {
                throw new NotSupported($this->id . ' fetchLedger() does not support spot transactions, only swap and future wallet transactions are supported');
            }
            //
            //     {
            //         "returnCode" => 0,
            //         "msgInfo" => "success",
            //         "error" => null,
            //         "result" => {
            //             "hasPrev" => false,
            //             "hasNext" => false,
            //             "items" => array(
            //                 array(
            //                     "id" => "207260567109387524",
            //                     "coin" => "usdt",
            //                     "symbol" => "btc_usdt",
            //                     "type" => "FEE",
            //                     "amount" => "-0.0213",
            //                     "side" => "SUB",
            //                     "afterAmount" => null,
            //                     "createdTime" => 1679116769914
            //                 ),
            //             )
            //         }
            //     }
            //
            $data = $this->safe_value($response, 'result', array());
            $ledger = $this->safe_value($data, 'items', array());
            return $this->parse_ledger($ledger, $currency, $since, $limit);
        }) ();
    }

    public function parse_ledger_entry($item, $currency = null): array {
        //
        //     {
        //         "id" => "207260567109387524",
        //         "coin" => "usdt",
        //         "symbol" => "btc_usdt",
        //         "type" => "FEE",
        //         "amount" => "-0.0213",
        //         "side" => "SUB",
        //         "afterAmount" => null,
        //         "createdTime" => 1679116769914
        //     }
        //
        $side = $this->safe_string($item, 'side');
        $direction = ($side === 'ADD') ? 'in' : 'out';
        $currencyId = $this->safe_string($item, 'coin');
        $currency = $this->safe_currency($currencyId, $currency);
        $timestamp = $this->safe_integer($item, 'createdTime');
        return $this->safe_ledger_entry(array(
            'info' => $item,
            'id' => $this->safe_string($item, 'id'),
            'direction' => $direction,
            'account' => null,
            'referenceId' => null,
            'referenceAccount' => null,
            'type' => $this->parse_ledger_entry_type($this->safe_string($item, 'type')),
            'currency' => $this->safe_currency_code($currencyId, $currency),
            'amount' => $this->safe_number($item, 'amount'),
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'before' => null,
            'after' => $this->safe_number($item, 'afterAmount'),
            'status' => null,
            'fee' => array(
                'currency' => null,
                'cost' => null,
            ),
        ), $currency);
    }

    public function parse_ledger_entry_type($type) {
        $ledgerType = array(
            'EXCHANGE' => 'transfer',
            'CLOSE_POSITION' => 'trade',
            'TAKE_OVER' => 'trade',
            'MERGE' => 'trade',
            'QIANG_PING_MANAGER' => 'fee',
            'FUND' => 'fee',
            'FEE' => 'fee',
            'ADL' => 'auto-deleveraging',
        );
        return $this->safe_string($ledgerType, $type, $type);
    }

    public function fetch_deposit_address(string $code, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $params) {
            /**
             * fetch the deposit address for a $currency associated with this account
             *
             * @see https://doc.xt.com/#deposit_withdrawaldepositAddressGet
             *
             * @param {string} $code unified $currency $code
             * @param {array} $params extra parameters specific to the xt api endpoint
             * @param {string} $params->network required network id
             * @return {array} an {@link https://docs.ccxt.com/en/latest/manual.html#address-structure address structure}
             */
            Async\await($this->load_markets());
            $networkCode = null;
            list($networkCode, $params) = $this->handle_network_code_and_params($params);
            $currency = $this->currency($code);
            $networkId = $this->network_code_to_id($networkCode, $code);
            $this->check_required_argument('fetchDepositAddress', $networkId, 'network');
            $request = array(
                'currency' => $currency['id'],
                'chain' => $networkId,
            );
            $response = Async\await($this->privateSpotGetDepositAddress ($this->extend($request, $params)));
            //
            //     {
            //         "rc" => 0,
            //         "mc" => "SUCCESS",
            //         "ma" => array(),
            //         "result" => {
            //             "address" => "0x7f7173cf29d3846d20ca5a3aec1120b93dbd157a",
            //             "memo" => ""
            //         }
            //     }
            //
            $result = $this->safe_value($response, 'result', array());
            return $this->parse_deposit_address($result, $currency);
        }) ();
    }

    public function parse_deposit_address($depositAddress, $currency = null): array {
        //
        //     {
        //         "address" => "0x7f7173cf29d3846d20ca5a3aec1120b93dbd157a",
        //         "memo" => ""
        //     }
        //
        $address = $this->safe_string($depositAddress, 'address');
        $this->check_address($address);
        return array(
            'info' => $depositAddress,
            'currency' => $this->safe_currency_code(null, $currency),
            'network' => null,
            'address' => $address,
            'tag' => $this->safe_string($depositAddress, 'memo'),
        );
    }

    public function fetch_deposits(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($code, $since, $limit, $params) {
            /**
             * fetch all $deposits made to an account
             *
             * @see https://doc.xt.com/#deposit_withdrawalhistoryDepositGet
             *
             * @param {string} [$code] unified $currency $code
             * @param {int} [$since] the earliest time in ms to fetch $deposits for
             * @param {int} [$limit] the maximum number of transaction structures to retrieve
             * @param {array} $params extra parameters specific to the xt api endpoint
             * @return {array[]} a list of {@link https://docs.ccxt.com/en/latest/manual.html#transaction-structure transaction structures}
             */
            Async\await($this->load_markets());
            $request = array();
            $currency = null;
            if ($code !== null) {
                $currency = $this->currency($code);
                $request['currency'] = $currency['id'];
            }
            if ($since !== null) {
                $request['startTime'] = $since;
            }
            if ($limit !== null) {
                $request['limit'] = $limit; // default 10, max 200
            }
            $response = Async\await($this->privateSpotGetDepositHistory ($this->extend($request, $params)));
            //
            //     {
            //         "rc" => 0,
            //         "mc" => "SUCCESS",
            //         "ma" => array(),
            //         "result" => {
            //             "hasPrev" => false,
            //             "hasNext" => false,
            //             "items" => array(
            //                 array(
            //                     "id" => 170368702,
            //                     "currency" => "usdt",
            //                     "chain" => "Ethereum",
            //                     "memo" => "",
            //                     "status" => "SUCCESS",
            //                     "amount" => "31.792528",
            //                     "confirmations" => 12,
            //                     "transactionId" => "0x90b8487c258b81b85e15e461b1839c49d4d8e6e9de4c1adb658cd47d4f5c5321",
            //                     "address" => "0x7f7172cf29d3846d30ca5a3aec1120b92dbd150b",
            //                     "fromAddr" => "0x7830c87c02e56aff27fa9ab1241711331fa86f58",
            //                     "createdTime" => 1678491442000
            //                 ),
            //             )
            //         }
            //     }
            //
            $data = $this->safe_value($response, 'result', array());
            $deposits = $this->safe_value($data, 'items', array());
            return $this->parse_transactions($deposits, $currency, $since, $limit, $params);
        }) ();
    }

    public function fetch_withdrawals(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($code, $since, $limit, $params) {
            /**
             * fetch all $withdrawals made from an account
             *
             * @see https://doc.xt.com/#deposit_withdrawalwithdrawHistory
             *
             * @param {string} [$code] unified $currency $code
             * @param {int} [$since] the earliest time in ms to fetch $withdrawals for
             * @param {int} [$limit] the maximum number of transaction structures to retrieve
             * @param {array} $params extra parameters specific to the xt api endpoint
             * @return {array[]} a list of {@link https://docs.ccxt.com/en/latest/manual.html#transaction-structure transaction structures}
             */
            Async\await($this->load_markets());
            $request = array();
            $currency = null;
            if ($code !== null) {
                $currency = $this->currency($code);
                $request['currency'] = $currency['id'];
            }
            if ($since !== null) {
                $request['startTime'] = $since;
            }
            if ($limit !== null) {
                $request['limit'] = $limit; // default 10, max 200
            }
            $response = Async\await($this->privateSpotGetWithdrawHistory ($this->extend($request, $params)));
            //
            //     {
            //         "rc" => 0,
            //         "mc" => "SUCCESS",
            //         "ma" => array(),
            //         "result" => {
            //             "hasPrev" => false,
            //             "hasNext" => false,
            //             "items" => array(
            //                 {
            //                     "id" => 950898,
            //                     "currency" => "usdt",
            //                     "chain" => "Tron",
            //                     "address" => "TGB2vxTjiqraVZBy7YHXF8V3CSMVhQKcaf",
            //                     "memo" => "",
            //                     "status" => "SUCCESS",
            //                     "amount" => "5",
            //                     "fee" => "2",
            //                     "confirmations" => 6,
            //                     "transactionId" => "c36e230b879842b1d7afd19d15ee1a866e26eaa0626e367d6f545d2932a15156",
            //                     "createdTime" => 1680049062000
            //                 }
            //             )
            //         }
            //     }
            //
            $data = $this->safe_value($response, 'result', array());
            $withdrawals = $this->safe_value($data, 'items', array());
            return $this->parse_transactions($withdrawals, $currency, $since, $limit, $params);
        }) ();
    }

    public function withdraw(string $code, float $amount, string $address, ?string $tag = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $amount, $address, $tag, $params) {
            /**
             * make a withdrawal
             *
             * @see https://doc.xt.com/#deposit_withdrawalwithdraw
             *
             * @param {string} $code unified $currency $code
             * @param {float} $amount the $amount to withdraw
             * @param {string} $address the $address to withdraw to
             * @param {string} [$tag]
             * @param {array} $params extra parameters specific to the xt api endpoint
             * @return {array} a {@link https://docs.ccxt.com/en/latest/manual.html#transaction-structure transaction structure}
             */
            $this->check_address($address);
            Async\await($this->load_markets());
            $currency = $this->currency($code);
            list($tag, $params) = $this->handle_withdraw_tag_and_params($tag, $params);
            $networkCode = null;
            list($networkCode, $params) = $this->handle_network_code_and_params($params);
            $networkIdsByCodes = $this->safe_value($this->options, 'networks', array());
            $networkId = $this->safe_string_2($networkIdsByCodes, $networkCode, $code, $code);
            $request = array(
                'currency' => $currency['id'],
                'chain' => $networkId,
                'amount' => $this->currency_to_precision($code, $amount),
                'address' => $address,
            );
            if ($tag !== null) {
                $request['memo'] = $tag;
            }
            $response = Async\await($this->privateSpotPostWithdraw ($this->extend($request, $params)));
            //
            //     {
            //         "rc" => 0,
            //         "mc" => "SUCCESS",
            //         "ma" => array(),
            //         "result" => {
            //             "id" => 950898
            //         }
            //     }
            //
            $result = $this->safe_value($response, 'result', array());
            return $this->parse_transaction($result, $currency);
        }) ();
    }

    public function parse_transaction(array $transaction, ?array $currency = null): array {
        //
        // fetchDeposits
        //
        //     {
        //         "id" => 170368702,
        //         "currency" => "usdt",
        //         "chain" => "Ethereum",
        //         "memo" => "",
        //         "status" => "SUCCESS",
        //         "amount" => "31.792528",
        //         "confirmations" => 12,
        //         "transactionId" => "0x90b8487c258b81b85e15e461b1839c49d4d8e6e9de4c1adb658cd47d4f5c5321",
        //         "address" => "0x7f7172cf29d3846d30ca5a3aec1120b92dbd150b",
        //         "fromAddr" => "0x7830c87c02e56aff27fa9ab1241711331fa86f58",
        //         "createdTime" => 1678491442000
        //     }
        //
        // fetchWithdrawals
        //
        //     {
        //         "id" => 950898,
        //         "currency" => "usdt",
        //         "chain" => "Tron",
        //         "address" => "TGB2vxTjiqraVZBy7YHXF8V3CSMVhQKcaf",
        //         "memo" => "",
        //         "status" => "SUCCESS",
        //         "amount" => "5",
        //         "fee" => "2",
        //         "confirmations" => 6,
        //         "transactionId" => "c36e230b879842b1d7afd19d15ee1a866e26eaa0626e367d6f545d2932a15156",
        //         "createdTime" => 1680049062000
        //     }
        //
        // withdraw
        //
        //     {
        //         "id" => 950898
        //     }
        //
        $type = (is_array($transaction) && array_key_exists('fromAddr', $transaction)) ? 'deposit' : 'withdraw';
        $timestamp = $this->safe_integer($transaction, 'createdTime');
        $address = $this->safe_string($transaction, 'address');
        $memo = $this->safe_string($transaction, 'memo');
        $currencyCode = $this->safe_currency_code($this->safe_string($transaction, 'currency'), $currency);
        $fee = $this->safe_number($transaction, 'fee');
        $feeCurrency = ($fee !== null) ? $currencyCode : null;
        $networkId = $this->safe_string($transaction, 'chain');
        return array(
            'info' => $transaction,
            'id' => $this->safe_string($transaction, 'id'),
            'txid' => $this->safe_string($transaction, 'transactionId'),
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'updated' => null,
            'addressFrom' => $this->safe_string($transaction, 'fromAddr'),
            'addressTo' => $address,
            'address' => $address,
            'tagFrom' => null,
            'tagTo' => null,
            'tag' => $memo,
            'type' => $type,
            'amount' => $this->safe_number($transaction, 'amount'),
            'currency' => $currencyCode,
            'network' => $this->network_id_to_code($networkId, $currencyCode),
            'status' => $this->parse_transaction_status($this->safe_string($transaction, 'status')),
            'comment' => $memo,
            'fee' => array(
                'currency' => $feeCurrency,
                'cost' => $fee,
                'rate' => null,
            ),
            'internal' => null,
        );
    }

    public function parse_transaction_status($status) {
        $statuses = array(
            'SUBMIT' => 'pending',
            'REVIEW' => 'pending',
            'AUDITED' => 'pending',
            'PENDING' => 'pending',
            'CANCEL' => 'canceled',
            'FAIL' => 'failed',
            'SUCCESS' => 'ok',
        );
        return $this->safe_string($statuses, $status, $status);
    }

    public function set_leverage(int $leverage, ?string $symbol = null, $params = array ()) {
        return Async\async(function () use ($leverage, $symbol, $params) {
            /**
             * set the level of $leverage for a $market
             *
             * @see https://doc.xt.com/#futures_useradjustLeverage
             *
             * @param {float} $leverage the rate of $leverage
             * @param {string} $symbol unified $market $symbol
             * @param {array} $params extra parameters specific to the xt api endpoint
             * @param {string} $params->positionSide 'LONG' or 'SHORT'
             * @return {array} $response from the exchange
             */
            if ($symbol === null) {
                throw new ArgumentsRequired($this->id . ' setLeverage() requires a $symbol argument');
            }
            $positionSide = $this->safe_string($params, 'positionSide');
            $this->check_required_argument('setLeverage', $positionSide, 'positionSide', array( 'LONG', 'SHORT' ));
            if (($leverage < 1) || ($leverage > 125)) {
                throw new BadRequest($this->id . ' setLeverage() $leverage should be between 1 and 125');
            }
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            if (!($market['contract'])) {
                throw new BadSymbol($this->id . ' setLeverage() supports contract markets only');
            }
            $request = array(
                'symbol' => $market['id'],
                'positionSide' => $positionSide,
                'leverage' => $leverage,
            );
            $subType = null;
            list($subType, $params) = $this->handle_sub_type_and_params('setLeverage', $market, $params);
            $response = null;
            if ($subType === 'inverse') {
                $response = Async\await($this->privateInversePostFutureUserV1PositionAdjustLeverage ($this->extend($request, $params)));
            } else {
                $response = Async\await($this->privateLinearPostFutureUserV1PositionAdjustLeverage ($this->extend($request, $params)));
            }
            //
            //     {
            //         "returnCode" => 0,
            //         "msgInfo" => "success",
            //         "error" => null,
            //         "result" => null
            //     }
            //
            return $response;
        }) ();
    }

    public function add_margin(string $symbol, float $amount, $params = array ()) {
        return Async\async(function () use ($symbol, $amount, $params) {
            /**
             * add margin to a position
             *
             * @see https://doc.xt.com/#futures_useradjustMargin
             *
             * @param {string} $symbol unified market $symbol
             * @param {float} $amount amount of margin to add
             * @param {array} $params extra parameters specific to the xt api endpoint
             * @param {string} $params->positionSide 'LONG' or 'SHORT'
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=add-margin-structure margin structure~
             */
            return Async\await($this->modify_margin_helper($symbol, $amount, 'ADD', $params));
        }) ();
    }

    public function reduce_margin(string $symbol, float $amount, $params = array ()) {
        return Async\async(function () use ($symbol, $amount, $params) {
            /**
             * remove margin from a position
             *
             * @see https://doc.xt.com/#futures_useradjustMargin
             *
             * @param {string} $symbol unified market $symbol
             * @param {float} $amount the $amount of margin to remove
             * @param {array} $params extra parameters specific to the xt api endpoint
             * @param {string} $params->positionSide 'LONG' or 'SHORT'
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=reduce-margin-structure margin structure~
             */
            return Async\await($this->modify_margin_helper($symbol, $amount, 'SUB', $params));
        }) ();
    }

    public function modify_margin_helper(string $symbol, $amount, $addOrReduce, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $amount, $addOrReduce, $params) {
            $positionSide = $this->safe_string($params, 'positionSide');
            $this->check_required_argument('setLeverage', $positionSide, 'positionSide', array( 'LONG', 'SHORT' ));
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
                'margin' => $amount,
                'type' => $addOrReduce,
                'positionSide' => $positionSide,
            );
            $subType = null;
            list($subType, $params) = $this->handle_sub_type_and_params('modifyMarginHelper', $market, $params);
            $response = null;
            if ($subType === 'inverse') {
                $response = Async\await($this->privateInversePostFutureUserV1PositionMargin ($this->extend($request, $params)));
            } else {
                $response = Async\await($this->privateLinearPostFutureUserV1PositionMargin ($this->extend($request, $params)));
            }
            //
            //     {
            //         "returnCode" => 0,
            //         "msgInfo" => "success",
            //         "error" => null,
            //         "result" => null
            //     }
            //
            return $this->parse_margin_modification($response, $market);
        }) ();
    }

    public function parse_margin_modification($data, $market = null): array {
        return array(
            'info' => $data,
            'type' => null,
            'amount' => null,
            'code' => null,
            'symbol' => $this->safe_symbol(null, $market),
            'status' => null,
            'marginMode' => null,
            'total' => null,
            'timestamp' => null,
            'datetime' => null,
        );
    }

    public function fetch_leverage_tiers(?array $symbols = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbols, $params) {
            /**
             * retrieve information on the maximum leverage for different trade sizes
             *
             * @see https://doc.xt.com/#futures_quotesgetLeverageBrackets
             *
             * @param {string} [$symbols] a list of unified market $symbols
             * @param {array} $params extra parameters specific to the xt api endpoint
             * @return {array} a dictionary of ~@link https://docs.ccxt.com/#/?id=leverage-tiers-structure leverage tiers structures~
             */
            Async\await($this->load_markets());
            $subType = null;
            list($subType, $params) = $this->handle_sub_type_and_params('fetchLeverageTiers', null, $params);
            $response = null;
            if ($subType === 'inverse') {
                $response = Async\await($this->publicInverseGetFutureMarketV1PublicLeverageBracketList ($params));
            } else {
                $response = Async\await($this->publicLinearGetFutureMarketV1PublicLeverageBracketList ($params));
            }
            //
            //     {
            //         "returnCode" => 0,
            //         "msgInfo" => "success",
            //         "error" => null,
            //         "result" => array(
            //             array(
            //                 "symbol" => "rad_usdt",
            //                 "leverageBrackets" => array(
            //                     array(
            //                         "symbol" => "rad_usdt",
            //                         "bracket" => 1,
            //                         "maxNominalValue" => "5000",
            //                         "maintMarginRate" => "0.025",
            //                         "startMarginRate" => "0.05",
            //                         "maxStartMarginRate" => null,
            //                         "maxLeverage" => "20",
            //                         "minLeverage" => "1"
            //                     ),
            //                 )
            //             ),
            //         )
            //     }
            //
            $data = $this->safe_value($response, 'result', array());
            $symbols = $this->market_symbols($symbols);
            return $this->parse_leverage_tiers($data, $symbols, 'symbol');
        }) ();
    }

    public function parse_leverage_tiers($response, $symbols = null, $marketIdKey = null): array {
        //
        //     {
        //         "symbol" => "rad_usdt",
        //         "leverageBrackets" => array(
        //             array(
        //                 "symbol" => "rad_usdt",
        //                 "bracket" => 1,
        //                 "maxNominalValue" => "5000",
        //                 "maintMarginRate" => "0.025",
        //                 "startMarginRate" => "0.05",
        //                 "maxStartMarginRate" => null,
        //                 "maxLeverage" => "20",
        //                 "minLeverage" => "1"
        //             ),
        //         )
        //     }
        //
        $result = array();
        for ($i = 0; $i < count($response); $i++) {
            $entry = $response[$i];
            $marketId = $this->safe_string($entry, 'symbol');
            $market = $this->safe_market($marketId, null, '_', 'contract');
            $symbol = $this->safe_symbol($marketId, $market);
            if ($symbols !== null) {
                if ($this->in_array($symbol, $symbols)) {
                    $result[$symbol] = $this->parse_market_leverage_tiers($entry, $market);
                }
            } else {
                $result[$symbol] = $this->parse_market_leverage_tiers($response[$i], $market);
            }
        }
        return $result;
    }

    public function fetch_market_leverage_tiers(string $symbol, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $params) {
            /**
             * retrieve information on the maximum leverage for different trade sizes of a single $market
             *
             * @see https://doc.xt.com/#futures_quotesgetLeverageBracket
             *
             * @param {string} $symbol unified $market $symbol
             * @param {array} $params extra parameters specific to the xt api endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=leverage-tiers-structure leverage tiers structure~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
            );
            $subType = null;
            list($subType, $params) = $this->handle_sub_type_and_params('fetchMarketLeverageTiers', $market, $params);
            $response = null;
            if ($subType === 'inverse') {
                $response = Async\await($this->publicInverseGetFutureMarketV1PublicLeverageBracketDetail ($this->extend($request, $params)));
            } else {
                $response = Async\await($this->publicLinearGetFutureMarketV1PublicLeverageBracketDetail ($this->extend($request, $params)));
            }
            //
            //     {
            //         "returnCode" => 0,
            //         "msgInfo" => "success",
            //         "error" => null,
            //         "result" => {
            //             "symbol" => "btc_usdt",
            //             "leverageBrackets" => array(
            //                 array(
            //                     "symbol" => "btc_usdt",
            //                     "bracket" => 1,
            //                     "maxNominalValue" => "500000",
            //                     "maintMarginRate" => "0.004",
            //                     "startMarginRate" => "0.008",
            //                     "maxStartMarginRate" => null,
            //                     "maxLeverage" => "125",
            //                     "minLeverage" => "1"
            //                 ),
            //             )
            //         }
            //     }
            //
            $data = $this->safe_value($response, 'result', array());
            return $this->parse_market_leverage_tiers($data, $market);
        }) ();
    }

    public function parse_market_leverage_tiers($info, $market = null): array {
        //
        //     {
        //         "symbol" => "rad_usdt",
        //         "leverageBrackets" => array(
        //             array(
        //                 "symbol" => "rad_usdt",
        //                 "bracket" => 1,
        //                 "maxNominalValue" => "5000",
        //                 "maintMarginRate" => "0.025",
        //                 "startMarginRate" => "0.05",
        //                 "maxStartMarginRate" => null,
        //                 "maxLeverage" => "20",
        //                 "minLeverage" => "1"
        //             ),
        //         )
        //     }
        //
        $tiers = array();
        $brackets = $this->safe_value($info, 'leverageBrackets', array());
        for ($i = 0; $i < count($brackets); $i++) {
            $tier = $brackets[$i];
            $marketId = $this->safe_string($info, 'symbol');
            $market = $this->safe_market($marketId, $market, '_', 'contract');
            $tiers[] = array(
                'tier' => $this->safe_integer($tier, 'bracket'),
                'symbol' => $this->safe_symbol($marketId, $market, '_', 'contract'),
                'currency' => $market['settle'],
                'minNotional' => $this->safe_number($brackets[$i - 1], 'maxNominalValue', 0),
                'maxNotional' => $this->safe_number($tier, 'maxNominalValue'),
                'maintenanceMarginRate' => $this->safe_number($tier, 'maintMarginRate'),
                'maxLeverage' => $this->safe_number($tier, 'maxLeverage'),
                'info' => $tier,
            );
        }
        return $tiers;
    }

    public function fetch_funding_rate_history(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * fetches historical funding $rates
             *
             * @see https://doc.xt.com/#futures_quotesgetFundingRateRecord
             *
             * @param {string} [$symbol] unified $symbol of the $market to fetch the funding rate history for
             * @param {int} [$since] $timestamp in ms of the earliest funding rate to fetch
             * @param {int} [$limit] the maximum amount of [funding rate structures] to fetch
             * @param {array} $params extra parameters specific to the xt api endpoint
             * @param {bool} $params->paginate true/false whether to use the pagination helper to aumatically $paginate through the results
             * @return {array[]} a list of ~@link https://docs.ccxt.com/en/latest/manual.html?#funding-rate-history-structure funding rate structures~
             */
            if ($symbol === null) {
                throw new ArgumentsRequired($this->id . ' fetchFundingRateHistory() requires a $symbol argument');
            }
            Async\await($this->load_markets());
            $paginate = false;
            list($paginate, $params) = $this->handle_option_and_params($params, 'fetchFundingRateHistory', 'paginate');
            if ($paginate) {
                return Async\await($this->fetch_paginated_call_cursor('fetchFundingRateHistory', $symbol, $since, $limit, $params, 'id', 'id', 1, 200));
            }
            $market = $this->market($symbol);
            if (!$market['swap']) {
                throw new BadSymbol($this->id . ' fetchFundingRateHistory() supports swap contracts only');
            }
            $request = array(
                'symbol' => $market['id'],
            );
            if ($limit !== null) {
                $request['limit'] = $limit;
            } else {
                $request['limit'] = 200; // max
            }
            $subType = null;
            list($subType, $params) = $this->handle_sub_type_and_params('fetchFundingRateHistory', $market, $params);
            $response = null;
            if ($subType === 'inverse') {
                $response = Async\await($this->publicInverseGetFutureMarketV1PublicQFundingRateRecord ($this->extend($request, $params)));
            } else {
                $response = Async\await($this->publicLinearGetFutureMarketV1PublicQFundingRateRecord ($this->extend($request, $params)));
            }
            //
            //     {
            //         "returnCode" => 0,
            //         "msgInfo" => "success",
            //         "error" => null,
            //         "result" => {
            //             "hasPrev" => false,
            //             "hasNext" => true,
            //             "items" => array(
            //                 array(
            //                     "id" => "210441653482221888",
            //                     "symbol" => "btc_usdt",
            //                     "fundingRate" => "0.000057",
            //                     "createdTime" => 1679875200000,
            //                     "collectionInternal" => 28800
            //                 ),
            //             )
            //         }
            //     }
            //
            $result = $this->safe_value($response, 'result', array());
            $items = $this->safe_value($result, 'items', array());
            $rates = array();
            for ($i = 0; $i < count($items); $i++) {
                $entry = $items[$i];
                $marketId = $this->safe_string($entry, 'symbol');
                $symbolInner = $this->safe_symbol($marketId, $market);
                $timestamp = $this->safe_integer($entry, 'createdTime');
                $rates[] = array(
                    'info' => $entry,
                    'symbol' => $symbolInner,
                    'fundingRate' => $this->safe_number($entry, 'fundingRate'),
                    'timestamp' => $timestamp,
                    'datetime' => $this->iso8601($timestamp),
                );
            }
            $sorted = $this->sort_by($rates, 'timestamp');
            return $this->filter_by_symbol_since_limit($sorted, $market['symbol'], $since, $limit);
        }) ();
    }

    public function fetch_funding_interval(string $symbol, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $params) {
            /**
             * fetch the current funding rate interval
             *
             * @see https://doc.xt.com/#futures_quotesgetFundingRate
             *
             * @param {string} $symbol unified market $symbol
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=funding-rate-structure funding rate structure~
             */
            return Async\await($this->fetch_funding_rate($symbol, $params));
        }) ();
    }

    public function fetch_funding_rate(string $symbol, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $params) {
            /**
             * fetch the current funding rate
             *
             * @see https://doc.xt.com/#futures_quotesgetFundingRate
             *
             * @param {string} $symbol unified $market $symbol
             * @param {array} $params extra parameters specific to the xt api endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=funding-rate-structure funding rate structure~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            if (!$market['swap']) {
                throw new BadSymbol($this->id . ' fetchFundingRate() supports swap contracts only');
            }
            $request = array(
                'symbol' => $market['id'],
            );
            $subType = null;
            list($subType, $params) = $this->handle_sub_type_and_params('fetchFundingRate', $market, $params);
            $response = null;
            if ($subType === 'inverse') {
                $response = Async\await($this->publicInverseGetFutureMarketV1PublicQFundingRate ($this->extend($request, $params)));
            } else {
                $response = Async\await($this->publicLinearGetFutureMarketV1PublicQFundingRate ($this->extend($request, $params)));
            }
            //
            //     {
            //         "returnCode" => 0,
            //         "msgInfo" => "success",
            //         "error" => null,
            //         "result" => {
            //             "symbol" => "btc_usdt",
            //             "fundingRate" => "0.000086",
            //             "nextCollectionTime" => 1680307200000,
            //             "collectionInternal" => 8
            //         }
            //     }
            //
            $result = $this->safe_value($response, 'result', array());
            return $this->parse_funding_rate($result, $market);
        }) ();
    }

    public function parse_funding_rate($contract, $market = null): array {
        //
        //     {
        //         "symbol" => "btc_usdt",
        //         "fundingRate" => "0.000086",
        //         "nextCollectionTime" => 1680307200000,
        //         "collectionInternal" => 8
        //     }
        //
        $marketId = $this->safe_string($contract, 'symbol');
        $symbol = $this->safe_symbol($marketId, $market, '_', 'swap');
        $timestamp = $this->safe_integer($contract, 'nextCollectionTime');
        $interval = $this->safe_string($contract, 'collectionInternal');
        if ($interval !== null) {
            $interval = $interval . 'h';
        }
        return array(
            'info' => $contract,
            'symbol' => $symbol,
            'markPrice' => null,
            'indexPrice' => null,
            'interestRate' => null,
            'estimatedSettlePrice' => null,
            'timestamp' => null,
            'datetime' => null,
            'fundingRate' => $this->safe_number($contract, 'fundingRate'),
            'fundingTimestamp' => $timestamp,
            'fundingDatetime' => $this->iso8601($timestamp),
            'nextFundingRate' => null,
            'nextFundingTimestamp' => null,
            'nextFundingDatetime' => null,
            'previousFundingRate' => null,
            'previousFundingTimestamp' => null,
            'previousFundingDatetime' => null,
            'interval' => $interval,
        );
    }

    public function fetch_funding_history(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * fetch the funding history
             *
             * @see https://doc.xt.com/#futures_usergetFunding
             *
             * @param {string} $symbol unified $market $symbol
             * @param {int} [$since] the starting timestamp in milliseconds
             * @param {int} [$limit] the number of entries to return
             * @param {array} $params extra parameters specific to the xt api endpoint
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=funding-history-structure funding history structures~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            if (!$market['swap']) {
                throw new BadSymbol($this->id . ' fetchFundingHistory() supports swap contracts only');
            }
            $request = array(
                'symbol' => $market['id'],
            );
            if ($since !== null) {
                $request['startTime'] = $since;
            }
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            $subType = null;
            list($subType, $params) = $this->handle_sub_type_and_params('fetchFundingHistory', $market, $params);
            $response = null;
            if ($subType === 'inverse') {
                $response = Async\await($this->privateInverseGetFutureUserV1BalanceFundingRateList ($this->extend($request, $params)));
            } else {
                $response = Async\await($this->privateLinearGetFutureUserV1BalanceFundingRateList ($this->extend($request, $params)));
            }
            //
            //     {
            //         "returnCode" => 0,
            //         "msgInfo" => "success",
            //         "error" => null,
            //         "result" => {
            //             "hasPrev" => false,
            //             "hasNext" => false,
            //             "items" => array(
            //                 array(
            //                     "id" => "210804044057280512",
            //                     "symbol" => "btc_usdt",
            //                     "cast" => "-0.0013",
            //                     "coin" => "usdt",
            //                     "positionSide" => "SHORT",
            //                     "createdTime" => 1679961600653
            //                 ),
            //             )
            //         }
            //     }
            //
            $data = $this->safe_value($response, 'result', array());
            $items = $this->safe_value($data, 'items', array());
            $result = array();
            for ($i = 0; $i < count($items); $i++) {
                $entry = $items[$i];
                $result[] = $this->parse_funding_history($entry, $market);
            }
            $sorted = $this->sort_by($result, 'timestamp');
            return $this->filter_by_since_limit($sorted, $since, $limit);
        }) ();
    }

    public function parse_funding_history($contract, $market = null) {
        //
        //     {
        //         "id" => "210804044057280512",
        //         "symbol" => "btc_usdt",
        //         "cast" => "-0.0013",
        //         "coin" => "usdt",
        //         "positionSide" => "SHORT",
        //         "createdTime" => 1679961600653
        //     }
        //
        $marketId = $this->safe_string($contract, 'symbol');
        $symbol = $this->safe_symbol($marketId, $market, '_', 'swap');
        $currencyId = $this->safe_string($contract, 'coin');
        $code = $this->safe_currency_code($currencyId);
        $timestamp = $this->safe_integer($contract, 'createdTime');
        return array(
            'info' => $contract,
            'symbol' => $symbol,
            'code' => $code,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'id' => $this->safe_string($contract, 'id'),
            'amount' => $this->safe_number($contract, 'cast'),
        );
    }

    public function fetch_position(string $symbol, $params = array ()) {
        return Async\async(function () use ($symbol, $params) {
            /**
             * fetch data on a single open contract trade position
             *
             * @see https://doc.xt.com/#futures_usergetPosition
             *
             * @param {string} $symbol unified $market $symbol of the $market the position is held in
             * @param {array} $params extra parameters specific to the xt api endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=position-structure position structure~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
            );
            $subType = null;
            list($subType, $params) = $this->handle_sub_type_and_params('fetchPosition', $market, $params);
            $response = null;
            if ($subType === 'inverse') {
                $response = Async\await($this->privateInverseGetFutureUserV1PositionList ($this->extend($request, $params)));
            } else {
                $response = Async\await($this->privateLinearGetFutureUserV1PositionList ($this->extend($request, $params)));
            }
            //
            //     {
            //         "returnCode" => 0,
            //         "msgInfo" => "success",
            //         "error" => null,
            //         "result" => array(
            //             array(
            //                 "symbol" => "btc_usdt",
            //                 "positionType" => "ISOLATED",
            //                 "positionSide" => "SHORT",
            //                 "contractType" => "PERPETUAL",
            //                 "positionSize" => "10",
            //                 "closeOrderSize" => "0",
            //                 "availableCloseSize" => "10",
            //                 "entryPrice" => "27060",
            //                 "openOrderSize" => "0",
            //                 "isolatedMargin" => "1.0824",
            //                 "openOrderMarginFrozen" => "0",
            //                 "realizedProfit" => "-0.00130138",
            //                 "autoMargin" => false,
            //                 "leverage" => 25
            //             ),
            //         )
            //     }
            //
            $positions = $this->safe_value($response, 'result', array());
            for ($i = 0; $i < count($positions); $i++) {
                $entry = $positions[$i];
                $marketId = $this->safe_string($entry, 'symbol');
                $marketInner = $this->safe_market($marketId, null, null, 'contract');
                $positionSize = $this->safe_string($entry, 'positionSize');
                if ($positionSize !== '0') {
                    return $this->parse_position($entry, $marketInner);
                }
            }
            return null;
        }) ();
    }

    public function fetch_positions(?array $symbols = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbols, $params) {
            /**
             * fetch all open $positions
             *
             * @see https://doc.xt.com/#futures_usergetPosition
             *
             * @param {string} [$symbols] list of unified market $symbols, not supported with xt
             * @param {array} $params extra parameters specific to the xt api endpoint
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=position-structure position structure~
             */
            Async\await($this->load_markets());
            $subType = null;
            list($subType, $params) = $this->handle_sub_type_and_params('fetchPositions', null, $params);
            $response = null;
            if ($subType === 'inverse') {
                $response = Async\await($this->privateInverseGetFutureUserV1PositionList ($params));
            } else {
                $response = Async\await($this->privateLinearGetFutureUserV1PositionList ($params));
            }
            //
            //     {
            //         "returnCode" => 0,
            //         "msgInfo" => "success",
            //         "error" => null,
            //         "result" => array(
            //             array(
            //                 "symbol" => "btc_usdt",
            //                 "positionType" => "ISOLATED",
            //                 "positionSide" => "SHORT",
            //                 "contractType" => "PERPETUAL",
            //                 "positionSize" => "10",
            //                 "closeOrderSize" => "0",
            //                 "availableCloseSize" => "10",
            //                 "entryPrice" => "27060",
            //                 "openOrderSize" => "0",
            //                 "isolatedMargin" => "1.0824",
            //                 "openOrderMarginFrozen" => "0",
            //                 "realizedProfit" => "-0.00130138",
            //                 "autoMargin" => false,
            //                 "leverage" => 25
            //             ),
            //         )
            //     }
            //
            $positions = $this->safe_value($response, 'result', array());
            $result = array();
            for ($i = 0; $i < count($positions); $i++) {
                $entry = $positions[$i];
                $marketId = $this->safe_string($entry, 'symbol');
                $marketInner = $this->safe_market($marketId, null, null, 'contract');
                $result[] = $this->parse_position($entry, $marketInner);
            }
            return $this->filter_by_array_positions($result, 'symbol', $symbols, false);
        }) ();
    }

    public function parse_position($position, $market = null) {
        //
        //     {
        //         "symbol" => "btc_usdt",
        //         "positionType" => "ISOLATED",
        //         "positionSide" => "SHORT",
        //         "contractType" => "PERPETUAL",
        //         "positionSize" => "10",
        //         "closeOrderSize" => "0",
        //         "availableCloseSize" => "10",
        //         "entryPrice" => "27060",
        //         "openOrderSize" => "0",
        //         "isolatedMargin" => "1.0824",
        //         "openOrderMarginFrozen" => "0",
        //         "realizedProfit" => "-0.00130138",
        //         "autoMargin" => false,
        //         "leverage" => 25
        //     }
        //
        $marketId = $this->safe_string($position, 'symbol');
        $market = $this->safe_market($marketId, $market, null, 'contract');
        $symbol = $this->safe_symbol($marketId, $market, null, 'contract');
        $positionType = $this->safe_string($position, 'positionType');
        $marginMode = ($positionType === 'CROSSED') ? 'cross' : 'isolated';
        $collateral = $this->safe_number($position, 'isolatedMargin');
        return $this->safe_position(array(
            'info' => $position,
            'id' => null,
            'symbol' => $symbol,
            'timestamp' => null,
            'datetime' => null,
            'hedged' => null,
            'side' => $this->safe_string_lower($position, 'positionSide'),
            'contracts' => $this->safe_number($position, 'positionSize'),
            'contractSize' => $market['contractSize'],
            'entryPrice' => $this->safe_number($position, 'entryPrice'),
            'markPrice' => null,
            'notional' => null,
            'leverage' => $this->safe_integer($position, 'leverage'),
            'collateral' => $collateral,
            'initialMargin' => $collateral,
            'maintenanceMargin' => null,
            'initialMarginPercentage' => null,
            'maintenanceMarginPercentage' => null,
            'unrealizedPnl' => null,
            'liquidationPrice' => null,
            'marginMode' => $marginMode,
            'percentage' => null,
            'marginRatio' => null,
        ));
    }

    public function transfer(string $code, float $amount, string $fromAccount, string $toAccount, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $amount, $fromAccount, $toAccount, $params) {
            /**
             * transfer $currency internally between wallets on the same account
             *
             * @see https://doc.xt.com/#transfersubTransferPost
             *
             * @param {string} $code unified $currency $code
             * @param {float} $amount amount to transfer
             * @param {string} $fromAccount account to transfer from -  spot, swap, leverage, finance
             * @param {string} $toAccount account to transfer to - spot, swap, leverage, finance
             * @param {array} $params extra parameters specific to the whitebit api endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=transfer-structure transfer structure~
             */
            Async\await($this->load_markets());
            $currency = $this->currency($code);
            $accountsByType = $this->safe_value($this->options, 'accountsById');
            $fromAccountId = $this->safe_string($accountsByType, $fromAccount, $fromAccount);
            $toAccountId = $this->safe_string($accountsByType, $toAccount, $toAccount);
            $amountString = $this->currency_to_precision($code, $amount);
            $request = array(
                'bizId' => $this->uuid(),
                'currency' => $currency['id'],
                'amount' => $amountString,
                'from' => $fromAccountId,
                'to' => $toAccountId,
            );
            $response = Async\await($this->privateSpotPostBalanceTransfer ($this->extend($request, $params)));
            //
            //   {
            //       info => array( rc => '0', mc => 'SUCCESS', ma => array(), result => '226971333791398656' ),
            //       id => '226971333791398656',
            //       timestamp => null,
            //       datetime => null,
            //       $currency => null,
            //       $amount => null,
            //       $fromAccount => null,
            //       $toAccount => null,
            //       status => null
            //   }
            //
            return $this->parse_transfer($response, $currency);
        }) ();
    }

    public function parse_transfer($transfer, $currency = null) {
        return array(
            'info' => $transfer,
            'id' => $this->safe_string($transfer, 'result'),
            'timestamp' => null,
            'datetime' => null,
            'currency' => null,
            'amount' => null,
            'fromAccount' => null,
            'toAccount' => null,
            'status' => null,
        );
    }

    public function set_margin_mode(string $marginMode, ?string $symbol = null, $params = array ()) {
        return Async\async(function () use ($marginMode, $symbol, $params) {
            /**
             * set margin mode to 'cross' or 'isolated'
             *
             * @see https://doc.xt.com/#futures_userchangePositionType
             *
             * @param {string} $marginMode 'cross' or 'isolated'
             * @param {string} [$symbol] required
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->positionSide] *required* "long" or "short"
             * @return {array} $response from the exchange
             */
            if ($symbol === null) {
                throw new ArgumentsRequired($this->id . ' setMarginMode() requires a $symbol argument');
            }
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            if ($market['spot']) {
                throw new BadSymbol($this->id . ' setMarginMode() supports contract markets only');
            }
            $marginMode = strtolower($marginMode);
            if ($marginMode !== 'isolated' && $marginMode !== 'cross') {
                throw new BadRequest($this->id . ' setMarginMode() $marginMode argument should be isolated or cross');
            }
            if ($marginMode === 'cross') {
                $marginMode = 'CROSSED';
            } else {
                $marginMode = 'ISOLATED';
            }
            $posSide = $this->safe_string_upper($params, 'positionSide');
            if ($posSide === null) {
                throw new ArgumentsRequired($this->id . ' setMarginMode() requires a positionSide parameter, either "LONG" or "SHORT"');
            }
            $request = array(
                'positionType' => $marginMode,
                'positionSide' => $posSide,
                'symbol' => $market['id'],
            );
            $response = Async\await($this->privateLinearPostFutureUserV1PositionChangeType ($this->extend($request, $params)));
            //
            // {
            //     "error" => array(
            //       "code" => "",
            //       "msg" => ""
            //     ),
            //     "msgInfo" => "",
            //     "result" => array(),
            //     "returnCode" => 0
            // }
            //
            return $response; // unify return type
        }) ();
    }

    public function edit_order(string $id, string $symbol, string $type, string $side, ?float $amount = null, ?float $price = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($id, $symbol, $type, $side, $amount, $price, $params) {
            /**
             * cancels an order and places a new order
             *
             * @see https://doc.xt.com/#orderorderUpdate
             * @see https://doc.xt.com/#futures_orderupdate
             * @see https://doc.xt.com/#futures_entrustupdateProfit
             *
             * @param {string} $id order $id
             * @param {string} $symbol unified $symbol of the $market to create an order in
             * @param {string} $type 'market' or 'limit'
             * @param {string} $side 'buy' or 'sell'
             * @param {float} $amount how much of the currency you want to trade in units of the base currency
             * @param {float} [$price] the $price at which the order is to be fulfilled, in units of the quote currency, ignored in $market orders
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {float} [$params->stopLoss] $price to set a stop-loss on an open position
             * @param {float} [$params->takeProfit] $price to set a take-profit on an open position
             * @return {array} an ~@link https://docs.ccxt.com/#/?$id=order-structure order structure~
             */
            if ($amount === null) {
                throw new ArgumentsRequired($this->id . ' editOrder() requires an $amount argument');
            }
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array();
            $stopLoss = $this->safe_number_2($params, 'stopLoss', 'triggerStopPrice');
            $takeProfit = $this->safe_number_2($params, 'takeProfit', 'triggerProfitPrice');
            $params = $this->omit($params, array( 'stopLoss', 'takeProfit' ));
            $isStopLoss = ($stopLoss !== null);
            $isTakeProfit = ($takeProfit !== null);
            if ($isStopLoss || $isTakeProfit) {
                $request['profitId'] = $id;
            } else {
                $request['orderId'] = $id;
                $request['price'] = $this->price_to_precision($symbol, $price);
            }
            $response = null;
            if ($market['swap']) {
                if ($isStopLoss) {
                    $request['triggerStopPrice'] = $this->price_to_precision($symbol, $stopLoss);
                } elseif ($takeProfit !== null) {
                    $request['triggerProfitPrice'] = $this->price_to_precision($symbol, $takeProfit);
                } else {
                    $request['origQty'] = $this->amount_to_precision($symbol, $amount);
                }
                $subType = null;
                list($subType, $params) = $this->handle_sub_type_and_params('editOrder', $market, $params);
                if ($subType === 'inverse') {
                    if ($isStopLoss || $isTakeProfit) {
                        $response = Async\await($this->privateInversePostFutureTradeV1EntrustUpdateProfitStop ($this->extend($request, $params)));
                    } else {
                        $response = Async\await($this->privateInversePostFutureTradeV1OrderUpdate ($this->extend($request, $params)));
                        //
                        //     {
                        //         "returnCode" => 0,
                        //         "msgInfo" => "success",
                        //         "error" => null,
                        //         "result" => "483869474947826752"
                        //     }
                        //
                    }
                } else {
                    if ($isStopLoss || $isTakeProfit) {
                        $response = Async\await($this->privateLinearPostFutureTradeV1EntrustUpdateProfitStop ($this->extend($request, $params)));
                    } else {
                        $response = Async\await($this->privateLinearPostFutureTradeV1OrderUpdate ($this->extend($request, $params)));
                        //
                        //     {
                        //         "returnCode" => 0,
                        //         "msgInfo" => "success",
                        //         "error" => null,
                        //         "result" => "483869474947826752"
                        //     }
                        //
                    }
                }
            } else {
                $request['quantity'] = $this->amount_to_precision($symbol, $amount);
                $response = Async\await($this->privateSpotPutOrderOrderId ($this->extend($request, $params)));
                //
                //     {
                //         "rc" => 0,
                //         "mc" => "SUCCESS",
                //         "ma" => array(),
                //         "result" => {
                //             "orderId" => "484203027161892224",
                //             "modifyId" => "484203544105344000",
                //             "clientModifyId" => null
                //         }
                //     }
                //
            }
            $result = ($market['swap']) ? $response : $this->safe_dict($response, 'result', array());
            return $this->parse_order($result, $market);
        }) ();
    }

    public function handle_errors($code, $reason, $url, $method, $headers, $body, $response, $requestHeaders, $requestBody) {
        //
        // spot => $error
        //
        //     {
        //         "rc" => 1,
        //         "mc" => "AUTH_103",
        //         "ma" => array(),
        //         "result" => null
        //     }
        //
        // spot => success
        //
        //     {
        //         "returnCode" => 0,
        //         "msgInfo" => "success",
        //         "error" => null,
        //         "result" => array()
        //     }
        //
        // swap and future => $error
        //
        //     {
        //         "returnCode" => 1,
        //         "msgInfo" => "failure",
        //         "error" => array(
        //             "code" => "403",
        //             "msg" => "invalid signature"
        //         ),
        //         "result" => null
        //     }
        //
        // swap and future => success
        //
        //     {
        //         "returnCode" => 0,
        //         "msgInfo" => "success",
        //         "error" => null,
        //         "result" => null
        //     }
        //
        // other:
        //
        //     {
        //         "rc" => 0,
        //         "mc" => "SUCCESS",
        //         "ma" => array(),
        //         "result" => array()
        //     }
        //
        // array("returnCode":1,"msgInfo":"failure","error":array("code":"insufficient_balance","msg":"insufficient balance","args":array()),"result":null)
        //
        //
        $status = $this->safe_string_upper_2($response, 'msgInfo', 'mc');
        if ($status !== null && $status !== 'SUCCESS') {
            $feedback = $this->id . ' ' . $body;
            $error = $this->safe_value($response, 'error', array());
            $spotErrorCode = $this->safe_string($response, 'mc');
            $errorCode = $this->safe_string($error, 'code', $spotErrorCode);
            $spotMessage = $this->safe_string($response, 'msgInfo');
            $message = $this->safe_string($error, 'msg', $spotMessage);
            $this->throw_exactly_matched_exception($this->exceptions['exact'], $errorCode, $feedback);
            $this->throw_broadly_matched_exception($this->exceptions['broad'], $message, $feedback);
            throw new ExchangeError($feedback);
        }
        return null;
    }

    public function sign($path, $api = [], $method = 'GET', $params = array (), $headers = null, $body = null) {
        $signed = $api[0] === 'private';
        $endpoint = $api[1];
        $request = '/' . $this->implode_params($path, $params);
        $payload = null;
        if (($endpoint === 'spot') || ($endpoint === 'user')) {
            if ($signed) {
                $payload = '/' . $this->version . $request;
            } else {
                $payload = '/' . $this->version . '/public' . $request;
            }
        } else {
            $payload = $request;
        }
        $url = $this->urls['api'][$endpoint] . $payload;
        $query = $this->omit($params, $this->extract_params($path));
        $urlencoded = $this->urlencode($this->keysort($query));
        $headers = array(
            'Content-Type' => 'application/json',
        );
        if ($signed) {
            $this->check_required_credentials();
            $defaultRecvWindow = $this->safe_string($this->options, 'recvWindow');
            $recvWindow = $this->safe_string($query, 'recvWindow', $defaultRecvWindow);
            $timestamp = $this->number_to_string($this->nonce());
            $body = $query;
            if (($payload === '/v4/order') || ($payload === '/future/trade/v1/order/create') || ($payload === '/future/trade/v1/entrust/create-plan') || ($payload === '/future/trade/v1/entrust/create-profit') || ($payload === '/future/trade/v1/order/create-batch')) {
                $id = 'CCXT';
                if (mb_strpos($payload, 'future') > -1) {
                    $body['clientMedia'] = $id;
                } else {
                    $body['media'] = $id;
                }
            }
            $isUndefinedBody = (($method === 'GET') || ($path === 'order/{orderId}') || ($path === 'ws-token'));
            if (($method === 'PUT') && ($endpoint === 'spot')) {
                $isUndefinedBody = false;
            }
            $body = $isUndefinedBody ? null : $this->json($body);
            $payloadString = null;
            if (($endpoint === 'spot') || ($endpoint === 'user')) {
                $payloadString = 'xt-validate-algorithms=HmacSHA256&xt-validate-appkey=' . $this->apiKey . '&xt-validate-recvwindow=' . $recvWindow . '&xt-validate-t' . 'imestamp=' . $timestamp;
                if ($isUndefinedBody) {
                    if ($urlencoded) {
                        $url .= '?' . $urlencoded;
                        $payloadString .= '#' . $method . '#' . $payload . '#' . $this->rawencode($this->keysort($query));
                    } else {
                        $payloadString .= '#' . $method . '#' . $payload;
                    }
                } else {
                    $payloadString .= '#' . $method . '#' . $payload . '#' . $body;
                }
                $headers['xt-validate-algorithms'] = 'HmacSHA256';
                $headers['xt-validate-recvwindow'] = $recvWindow;
            } else {
                $payloadString = 'xt-validate-appkey=' . $this->apiKey . '&xt-validate-t' . 'imestamp=' . $timestamp; // we can't glue $timestamp, breaks in php
                if ($method === 'GET') {
                    if ($urlencoded) {
                        $url .= '?' . $urlencoded;
                        $payloadString .= '#' . $payload . '#' . $urlencoded;
                    } else {
                        $payloadString .= '#' . $payload;
                    }
                } else {
                    $payloadString .= '#' . $payload . '#' . $body;
                }
            }
            $signature = $this->hmac($this->encode($payloadString), $this->encode($this->secret), 'sha256');
            $headers['xt-validate-appkey'] = $this->apiKey;
            $headers['xt-validate-timestamp'] = $timestamp;
            $headers['xt-validate-signature'] = $signature;
        } else {
            if ($urlencoded) {
                $url .= '?' . $urlencoded;
            }
        }
        return array( 'url' => $url, 'method' => $method, 'body' => $body, 'headers' => $headers );
    }
}
