<?php

namespace ccxt;

// 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\abstract\bingx as Exchange;

class bingx extends Exchange {

    public function describe(): mixed {
        return $this->deep_extend(parent::describe(), array(
            'id' => 'bingx',
            'name' => 'BingX',
            'countries' => array( 'US' ), // North America, Canada, the EU, Hong Kong and Taiwan
            'rateLimit' => 100,
            'version' => 'v1',
            'certified' => true,
            'pro' => true,
            'has' => array(
                'CORS' => null,
                'spot' => true,
                'margin' => false,
                'swap' => true,
                'future' => false,
                'option' => false,
                'addMargin' => true,
                'cancelAllOrders' => true,
                'cancelAllOrdersAfter' => true,
                'cancelOrder' => true,
                'cancelOrders' => true,
                'closeAllPositions' => true,
                'closePosition' => true,
                'createMarketBuyOrderWithCost' => true,
                'createMarketOrderWithCost' => true,
                'createMarketSellOrderWithCost' => true,
                'createOrder' => true,
                'createOrders' => true,
                'createOrderWithTakeProfitAndStopLoss' => true,
                'createReduceOnlyOrder' => true,
                'createStopLossOrder' => true,
                'createStopOrder' => true,
                'createTakeProfitOrder' => true,
                'createTrailingAmountOrder' => true,
                'createTrailingPercentOrder' => true,
                'createTriggerOrder' => true,
                'editOrder' => true,
                'fetchBalance' => true,
                'fetchCanceledOrders' => true,
                'fetchClosedOrders' => true,
                'fetchCurrencies' => true,
                'fetchDepositAddress' => true,
                'fetchDepositAddresses' => false,
                'fetchDepositAddressesByNetwork' => true,
                'fetchDeposits' => true,
                'fetchDepositWithdrawFee' => 'emulated',
                'fetchDepositWithdrawFees' => true,
                'fetchFundingRate' => true,
                'fetchFundingRateHistory' => true,
                'fetchFundingRates' => true,
                'fetchLeverage' => true,
                'fetchLiquidations' => false,
                'fetchMarginAdjustmentHistory' => false,
                'fetchMarginMode' => true,
                'fetchMarkets' => true,
                'fetchMarkOHLCV' => true,
                'fetchMarkPrice' => true,
                'fetchMarkPrices' => true,
                'fetchMyLiquidations' => true,
                'fetchMyTrades' => true,
                'fetchOHLCV' => true,
                'fetchOpenInterest' => true,
                'fetchOpenOrders' => true,
                'fetchOrder' => true,
                'fetchOrderBook' => true,
                'fetchOrders' => true,
                'fetchPosition' => true,
                'fetchPositionHistory' => false,
                'fetchPositionMode' => true,
                'fetchPositions' => true,
                'fetchPositionsHistory' => true,
                'fetchTicker' => true,
                'fetchTickers' => true,
                'fetchTime' => true,
                'fetchTrades' => true,
                'fetchTradingFee' => true,
                'fetchTransfers' => true,
                'fetchWithdrawals' => true,
                'reduceMargin' => true,
                'sandbox' => true,
                'setLeverage' => true,
                'setMargin' => true,
                'setMarginMode' => true,
                'setPositionMode' => true,
                'transfer' => true,
            ),
            'hostname' => 'bingx.com',
            'urls' => array(
                'logo' => 'https://github-production-user-asset-6210df.s3.amazonaws.com/1294454/253675376-6983b72e-4999-4549-b177-33b374c195e3.jpg',
                'api' => array(
                    'fund' => 'https://open-api.{hostname}/openApi',
                    'spot' => 'https://open-api.{hostname}/openApi',
                    'swap' => 'https://open-api.{hostname}/openApi',
                    'contract' => 'https://open-api.{hostname}/openApi',
                    'wallets' => 'https://open-api.{hostname}/openApi',
                    'user' => 'https://open-api.{hostname}/openApi',
                    'subAccount' => 'https://open-api.{hostname}/openApi',
                    'account' => 'https://open-api.{hostname}/openApi',
                    'copyTrading' => 'https://open-api.{hostname}/openApi',
                    'cswap' => 'https://open-api.{hostname}/openApi',
                    'api' => 'https://open-api.{hostname}/openApi',
                ),
                'test' => array(
                    'swap' => 'https://open-api-vst.{hostname}/openApi', // only swap is really "test" but since the API keys are the same, we want to keep all the functionalities when the user enables the sandboxmode
                ),
                'www' => 'https://bingx.com/',
                'doc' => 'https://bingx-api.github.io/docs/',
                'referral' => 'https://bingx.com/invite/OHETOM',
            ),
            'fees' => array(
                'tierBased' => true,
                'spot' => array(
                    'feeSide' => 'get',
                    'maker' => $this->parse_number('0.001'),
                    'taker' => $this->parse_number('0.001'),
                ),
                'swap' => array(
                    'feeSide' => 'quote',
                    'maker' => $this->parse_number('0.0002'),
                    'taker' => $this->parse_number('0.0005'),
                ),
            ),
            'requiredCredentials' => array(
                'apiKey' => true,
                'secret' => true,
            ),
            'api' => array(
                'fund' => array(
                    'v1' => array(
                        'private' => array(
                            'get' => array(
                                'account/balance' => 1,
                            ),
                        ),
                    ),
                ),
                'spot' => array(
                    'v1' => array(
                        'public' => array(
                            'get' => array(
                                'server/time' => 1,
                                'common/symbols' => 1,
                                'market/trades' => 1,
                                'market/depth' => 1,
                                'market/kline' => 1,
                                'ticker/24hr' => 1,
                                'ticker/price' => 1,
                                'ticker/bookTicker' => 1,
                            ),
                        ),
                        'private' => array(
                            'get' => array(
                                'trade/query' => 1,
                                'trade/openOrders' => 1,
                                'trade/historyOrders' => 1,
                                'trade/myTrades' => 2,
                                'user/commissionRate' => 5,
                                'account/balance' => 2,
                            ),
                            'post' => array(
                                'trade/order' => 2,
                                'trade/cancel' => 2,
                                'trade/batchOrders' => 5,
                                'trade/order/cancelReplace' => 5,
                                'trade/cancelOrders' => 5,
                                'trade/cancelOpenOrders' => 5,
                                'trade/cancelAllAfter' => 5,
                            ),
                        ),
                    ),
                    'v2' => array(
                        'public' => array(
                            'get' => array(
                                'market/depth' => 1,
                                'market/kline' => 1,
                            ),
                        ),
                    ),
                    'v3' => array(
                        'private' => array(
                            'get' => array(
                                'get/asset/transfer' => 1,
                                'asset/transfer' => 1,
                                'capital/deposit/hisrec' => 1,
                                'capital/withdraw/history' => 1,
                            ),
                            'post' => array(
                                'post/asset/transfer' => 5,
                            ),
                        ),
                    ),
                ),
                'swap' => array(
                    'v1' => array(
                        'public' => array(
                            'get' => array(
                                'ticker/price' => 1,
                                'market/historicalTrades' => 1,
                                'market/markPriceKlines' => 1,
                                'trade/multiAssetsRules' => 1,
                            ),
                        ),
                        'private' => array(
                            'get' => array(
                                'positionSide/dual' => 5,
                                'trade/batchCancelReplace' => 5,
                                'trade/fullOrder' => 2,
                                'maintMarginRatio' => 2,
                                'trade/positionHistory' => 2,
                                'positionMargin/history' => 2,
                                'twap/openOrders' => 5,
                                'twap/historyOrders' => 5,
                                'twap/orderDetail' => 5,
                                'trade/assetMode' => 5,
                                'user/marginAssets' => 5,
                            ),
                            'post' => array(
                                'trade/cancelReplace' => 2,
                                'positionSide/dual' => 5,
                                'trade/batchCancelReplace' => 5,
                                'trade/closePosition' => 2,
                                'trade/getVst' => 5,
                                'twap/order' => 5,
                                'twap/cancelOrder' => 5,
                                'trade/assetMode' => 5,
                            ),
                        ),
                    ),
                    'v2' => array(
                        'public' => array(
                            'get' => array(
                                'server/time' => 1,
                                'quote/contracts' => 1,
                                'quote/price' => 1,
                                'quote/depth' => 1,
                                'quote/trades' => 1,
                                'quote/premiumIndex' => 1,
                                'quote/fundingRate' => 1,
                                'quote/klines' => 1,
                                'quote/openInterest' => 1,
                                'quote/ticker' => 1,
                                'quote/bookTicker' => 1,
                            ),
                        ),
                        'private' => array(
                            'get' => array(
                                'user/balance' => 2,
                                'user/positions' => 2,
                                'user/income' => 2,
                                'trade/openOrders' => 2,
                                'trade/openOrder' => 2,
                                'trade/order' => 2,
                                'trade/marginType' => 5,
                                'trade/leverage' => 2,
                                'trade/forceOrders' => 1,
                                'trade/allOrders' => 2,
                                'trade/allFillOrders' => 2,
                                'trade/fillHistory' => 2,
                                'user/income/export' => 2,
                                'user/commissionRate' => 2,
                                'quote/bookTicker' => 1,
                            ),
                            'post' => array(
                                'trade/order' => 2,
                                'trade/batchOrders' => 2,
                                'trade/closeAllPositions' => 2,
                                'trade/cancelAllAfter' => 5,
                                'trade/marginType' => 5,
                                'trade/leverage' => 5,
                                'trade/positionMargin' => 5,
                                'trade/order/test' => 2,
                            ),
                            'delete' => array(
                                'trade/order' => 2,
                                'trade/batchOrders' => 2,
                                'trade/allOpenOrders' => 2,
                            ),
                        ),
                    ),
                    'v3' => array(
                        'public' => array(
                            'get' => array(
                                'quote/klines' => 1,
                            ),
                        ),
                    ),
                ),
                'cswap' => array(
                    'v1' => array(
                        'public' => array(
                            'get' => array(
                                'market/contracts' => 1,
                                'market/premiumIndex' => 1,
                                'market/openInterest' => 1,
                                'market/klines' => 1,
                                'market/depth' => 1,
                                'market/ticker' => 1,
                            ),
                        ),
                        'private' => array(
                            'get' => array(
                                'trade/leverage' => 2,
                                'trade/forceOrders' => 2,
                                'trade/allFillOrders' => 2,
                                'trade/openOrders' => 2,
                                'trade/orderDetail' => 2,
                                'trade/orderHistory' => 2,
                                'trade/marginType' => 2,
                                'user/commissionRate' => 2,
                                'user/positions' => 2,
                                'user/balance' => 2,
                            ),
                            'post' => array(
                                'trade/order' => 2,
                                'trade/leverage' => 2,
                                'trade/allOpenOrders' => 2,
                                'trade/closeAllPositions' => 2,
                                'trade/marginType' => 2,
                                'trade/positionMargin' => 2,
                            ),
                            'delete' => array(
                                'trade/allOpenOrders' => 2, // post method in doc
                                'trade/cancelOrder' => 2,
                            ),
                        ),
                    ),
                ),
                'contract' => array(
                    'v1' => array(
                        'private' => array(
                            'get' => array(
                                'allPosition' => 2,
                                'allOrders' => 2,
                                'balance' => 2,
                            ),
                        ),
                    ),
                ),
                'wallets' => array(
                    'v1' => array(
                        'private' => array(
                            'get' => array(
                                'capital/config/getall' => 5,
                                'capital/deposit/address' => 5,
                                'capital/innerTransfer/records' => 1,
                                'capital/subAccount/deposit/address' => 5,
                                'capital/deposit/subHisrec' => 2,
                                'capital/subAccount/innerTransfer/records' => 1,
                                'capital/deposit/riskRecords' => 5,
                            ),
                            'post' => array(
                                'capital/withdraw/apply' => 5,
                                'capital/innerTransfer/apply' => 5,
                                'capital/subAccountInnerTransfer/apply' => 2,
                                'capital/deposit/createSubAddress' => 2,
                            ),
                        ),
                    ),
                ),
                'subAccount' => array(
                    'v1' => array(
                        'private' => array(
                            'get' => array(
                                'list' => 10,
                                'assets' => 2,
                                'allAccountBalance' => 2,
                            ),
                            'post' => array(
                                'create' => 10,
                                'apiKey/create' => 2,
                                'apiKey/edit' => 2,
                                'apiKey/del' => 2,
                                'updateStatus' => 10,
                            ),
                        ),
                    ),
                ),
                'account' => array(
                    'v1' => array(
                        'private' => array(
                            'get' => array(
                                'uid' => 1,
                                'apiKey/query' => 2,
                                'account/apiPermissions' => 5,
                                'allAccountBalance' => 2,
                            ),
                            'post' => array(
                                'innerTransfer/authorizeSubAccount' => 1,
                            ),
                        ),
                    ),
                    'transfer' => array(
                        'v1' => array(
                            'private' => array(
                                'get' => array(
                                    'subAccount/asset/transferHistory' => 1,
                                ),
                                'post' => array(
                                    'subAccount/transferAsset/supportCoins' => 1,
                                    'subAccount/transferAsset' => 1,
                                ),
                            ),
                        ),
                    ),
                ),
                'user' => array(
                    'auth' => array(
                        'private' => array(
                            'post' => array(
                                'userDataStream' => 2,
                            ),
                            'put' => array(
                                'userDataStream' => 2,
                            ),
                            'delete' => array(
                                'userDataStream' => 2,
                            ),
                        ),
                    ),
                ),
                'copyTrading' => array(
                    'v1' => array(
                        'private' => array(
                            'get' => array(
                                'swap/trace/currentTrack' => 2,
                            ),
                            'post' => array(
                                'swap/trace/closeTrackOrder' => 2,
                                'swap/trace/setTPSL' => 2,
                                'spot/trader/sellOrder' => 10,
                            ),
                        ),
                    ),
                ),
                'api' => array(
                    'v3' => array(
                        'private' => array(
                            'get' => array(
                                'asset/transfer' => 1,
                                'asset/transferRecord' => 5,
                                'capital/deposit/hisrec' => 1,
                                'capital/withdraw/history' => 1,
                            ),
                            'post' => array(
                                'post/asset/transfer' => 1,
                            ),
                        ),
                    ),
                    'asset' => array(
                        'v1' => array(
                            'private' => array(
                                'post' => array(
                                    'transfer' => 5,
                                ),
                            ),
                            'public' => array(
                                'get' => array(
                                    'transfer/supportCoins' => 5,
                                ),
                            ),
                        ),
                    ),
                ),
            ),
            'timeframes' => array(
                '1m' => '1m',
                '3m' => '3m',
                '5m' => '5m',
                '15m' => '15m',
                '30m' => '30m',
                '1h' => '1h',
                '2h' => '2h',
                '4h' => '4h',
                '6h' => '6h',
                '12h' => '12h',
                '1d' => '1d',
                '3d' => '3d',
                '1w' => '1w',
                '1M' => '1M',
            ),
            'precisionMode' => TICK_SIZE,
            'exceptions' => array(
                'exact' => array(
                    '400' => '\\ccxt\\BadRequest',
                    '401' => '\\ccxt\\AuthenticationError',
                    '403' => '\\ccxt\\PermissionDenied',
                    '404' => '\\ccxt\\BadRequest',
                    '429' => '\\ccxt\\DDoSProtection',
                    '418' => '\\ccxt\\PermissionDenied',
                    '500' => '\\ccxt\\ExchangeError',
                    '504' => '\\ccxt\\ExchangeError',
                    '100001' => '\\ccxt\\AuthenticationError',
                    '100412' => '\\ccxt\\AuthenticationError',
                    '100202' => '\\ccxt\\InsufficientFunds',
                    '100204' => '\\ccxt\\BadRequest',
                    '100400' => '\\ccxt\\BadRequest',
                    '100410' => '\\ccxt\\OperationFailed', // array("code":100410,"msg":"The current system is busy, please try again later")
                    '100421' => '\\ccxt\\BadSymbol', // array("code":100421,"msg":"This pair is currently restricted from API trading","debugMsg":"")
                    '100440' => '\\ccxt\\ExchangeError',
                    '100500' => '\\ccxt\\OperationFailed', // array("code":100500,"msg":"The current system is busy, please try again later","debugMsg":"")
                    '100503' => '\\ccxt\\ExchangeError',
                    '80001' => '\\ccxt\\BadRequest',
                    '80012' => '\\ccxt\\InsufficientFunds', // array("code":80012,"msg":"array(\"Code\":101253,\"Msg\":\"margin is not enough\"))
                    '80014' => '\\ccxt\\BadRequest',
                    '80016' => '\\ccxt\\OrderNotFound',
                    '80017' => '\\ccxt\\OrderNotFound',
                    '100414' => '\\ccxt\\AccountSuspended', // array("code":100414,"msg":"Code => 100414, Msg => risk control check fail,code(1)","debugMsg":"")
                    '100419' => '\\ccxt\\PermissionDenied', // array("code":100419,"msg":"IP does not match IP whitelist","success":false,"timestamp":1705274099347)
                    '100437' => '\\ccxt\\BadRequest', // array("code":100437,"msg":"The withdrawal amount is lower than the minimum limit, please re-enter.","timestamp":1689258588845)
                    '101204' => '\\ccxt\\InsufficientFunds', // array("code":101204,"msg":"","data":array())
                    '110425' => '\\ccxt\\InvalidOrder', // array("code":110425,"msg":"Please ensure that the minimum nominal value of the order placed must be greater than 2u","data":array())
                    'Insufficient assets' => '\\ccxt\\InsufficientFunds', // array("transferErrorMsg":"Insufficient assets")
                    'illegal transferType' => '\\ccxt\\BadRequest', // array("transferErrorMsg":"illegal transferType")
                ),
                'broad' => array(),
            ),
            'commonCurrencies' => array(
                'SNOW' => 'Snowman', // Snowman vs SnowSwap conflict
                'OMNI' => 'OmniCat',
                'NAP' => '$NAP', // NAP on SOL = SNAP
                'TRUMP' => 'TRUMPMAGA',
                'TRUMPSOL' => 'TRUMP',
            ),
            'options' => array(
                'defaultType' => 'spot',
                'accountsByType' => array(
                    'funding' => 'fund',
                    'spot' => 'spot',
                    'future' => 'stdFutures',
                    'swap' => 'USDTMPerp',
                    'linear' => 'USDTMPerp',
                    'inverse' => 'coinMPerp',
                ),
                'accountsById' => array(
                    'fund' => 'funding',
                    'spot' => 'spot',
                    'stdFutures' => 'future',
                    'USDTMPerp' => 'linear',
                    'coinMPerp' => 'inverse',
                ),
                'recvWindow' => 5 * 1000, // 5 sec
                'broker' => 'CCXT',
                'defaultNetworks' => array(
                    'ETH' => 'ETH',
                    'USDT' => 'ERC20',
                    'USDC' => 'ERC20',
                    'BTC' => 'BTC',
                    'LTC' => 'LTC',
                ),
                'networks' => array(
                    'ARBITRUM' => 'ARB',
                    'MATIC' => 'POLYGON',
                    'ZKSYNC' => 'ZKSYNCERA',
                    'AVAXC' => 'AVAX-C',
                    'HBAR' => 'HEDERA',
                ),
            ),
            'features' => array(
                'defaultForLinear' => array(
                    'sandbox' => true,
                    'createOrder' => array(
                        'marginMode' => false,
                        'triggerPrice' => true,
                        'triggerPriceType' => array(
                            'last' => true,
                            'mark' => true,
                            'index' => true,
                        ),
                        'triggerDirection' => false,
                        'stopLossPrice' => true,
                        'takeProfitPrice' => true,
                        'attachedStopLossTakeProfit' => array(
                            'triggerPriceType' => array(
                                'last' => true,
                                'mark' => true,
                                'index' => true,
                            ),
                            'price' => true,
                        ),
                        'timeInForce' => array(
                            'IOC' => true,
                            'FOK' => true,
                            'PO' => true,
                            'GTD' => false,
                        ),
                        'hedged' => true,
                        'trailing' => true,
                        'leverage' => false,
                        'marketBuyRequiresPrice' => false,
                        'marketBuyByCost' => true,
                        'selfTradePrevention' => false,
                        'iceberg' => false,
                    ),
                    'createOrders' => array(
                        'max' => 5,
                    ),
                    'fetchMyTrades' => array(
                        'marginMode' => false,
                        'limit' => 512, // 512 days for 'allFillOrders', 1000 days for 'fillOrders'
                        'daysBack' => 30, // 30 for 'allFillOrders', 7 for 'fillHistory'
                        'untilDays' => 30, // 30 for 'allFillOrders', 7 for 'fillHistory'
                        'symbolRequired' => true,
                    ),
                    'fetchOrder' => array(
                        'marginMode' => false,
                        'trigger' => false,
                        'trailing' => false,
                        'symbolRequired' => true,
                    ),
                    'fetchOpenOrders' => array(
                        'marginMode' => false,
                        'limit' => null,
                        'trigger' => false,
                        'trailing' => false,
                        'symbolRequired' => false,
                    ),
                    'fetchOrders' => array(
                        'marginMode' => false,
                        'limit' => 1000,
                        'daysBack' => 20000, // since epoch
                        'untilDays' => 7,
                        'trigger' => false,
                        'trailing' => false,
                        'symbolRequired' => true,
                    ),
                    'fetchClosedOrders' => array(
                        'marginMode' => false,
                        'limit' => 1000,
                        'daysBack' => null,
                        'daysBackCanceled' => null,
                        'untilDays' => 7,
                        'trigger' => false,
                        'trailing' => false,
                        'symbolRequired' => true,
                    ),
                    'fetchOHLCV' => array(
                        'limit' => 1440,
                    ),
                ),
                'defaultForInverse' => array(
                    'extends' => 'defaultForLinear',
                    'fetchMyTrades' => array(
                        'limit' => 1000,
                        'daysBack' => null,
                        'untilDays' => null,
                    ),
                    'fetchOrders' => null,
                ),
                //
                'spot' => array(
                    'extends' => 'defaultForLinear',
                    'fetchCurrencies' => array(
                        'private' => true,
                    ),
                    'createOrder' => array(
                        'triggerPriceType' => null,
                        'attachedStopLossTakeProfit' => null,
                        'trailing' => false,
                    ),
                    'fetchMyTrades' => array(
                        'limit' => 1000,
                        'daysBack' => 1,
                        'untilDays' => 1,
                    ),
                    'fetchOrders' => null,
                    'fetchClosedOrders' => array(
                        'limit' => 100,
                        'untilDays' => null,
                    ),
                ),
                'swap' => array(
                    'linear' => array(
                        'extends' => 'defaultForLinear',
                    ),
                    'inverse' => array(
                        'extends' => 'defaultForInverse',
                    ),
                ),
                'defaultForFuture' => array(
                    'extends' => 'defaultForLinear',
                    'fetchOrders' => null,
                ),
                'future' => array(
                    'linear' => array(
                        'extends' => 'defaultForFuture',
                    ),
                    'inverse' => array(
                        'extends' => 'defaultForFuture',
                    ),
                ),
            ),
        ));
    }

    public function fetch_time($params = array ()): ?int {
        /**
         * fetches the current integer timestamp in milliseconds from the bingx server
         *
         * @see https://bingx-api.github.io/docs/#/swapV2/base-info.html#Get%20Server%20Time
         *
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {int} the current integer timestamp in milliseconds from the bingx server
         */
        $response = $this->swapV2PublicGetServerTime ($params);
        //
        //    {
        //        "code" => 0,
        //        "msg" => "",
        //        "data" => {
        //            "serverTime" => 1675319535362
        //        }
        //    }
        //
        $data = $this->safe_dict($response, 'data');
        return $this->safe_integer($data, 'serverTime');
    }

    public function fetch_currencies($params = array ()): ?array {
        /**
         * fetches all available currencies on an exchange
         *
         * @see https://bingx-api.github.io/docs/#/common/account-api.html#All%20Coins
         *
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array} an associative dictionary of currencies
         */
        if (!$this->check_required_credentials(false)) {
            return array();
        }
        $isSandbox = $this->safe_bool($this->options, 'sandboxMode', false);
        if ($isSandbox) {
            return array();
        }
        $response = $this->walletsV1PrivateGetCapitalConfigGetall ($params);
        //
        //    {
        //      "code" => 0,
        //      "timestamp" => 1702623271476,
        //      "data" => array(
        //        {
        //          "coin" => "BTC",
        //          "name" => "BTC",
        //          "networkList" => array(
        //            array(
        //              "name" => "BTC",
        //              "network" => "BTC",
        //              "isDefault" => true,
        //              "minConfirm" => 2,
        //              "withdrawEnable" => true,
        //              "depositEnable" => true,
        //              "withdrawFee" => "0.0006",
        //              "withdrawMax" => "1.17522",
        //              "withdrawMin" => "0.0005",
        //              "depositMin" => "0.0002"
        //            ),
        //            {
        //              "name" => "BTC",
        //              "network" => "BEP20",
        //              "isDefault" => false,
        //              "minConfirm" => 15,
        //              "withdrawEnable" => true,
        //              "depositEnable" => true,
        //              "withdrawFee" => "0.0000066",
        //              "withdrawMax" => "1.17522",
        //              "withdrawMin" => "0.0000066",
        //              "depositMin" => "0.0002"
        //            }
        //          )
        //        }
        //      )
        //    }
        //
        $data = $this->safe_list($response, 'data', array());
        $result = array();
        for ($i = 0; $i < count($data); $i++) {
            $entry = $data[$i];
            $currencyId = $this->safe_string($entry, 'coin');
            $code = $this->safe_currency_code($currencyId);
            $name = $this->safe_string($entry, 'name');
            $networkList = $this->safe_list($entry, 'networkList');
            $networks = array();
            for ($j = 0; $j < count($networkList); $j++) {
                $rawNetwork = $networkList[$j];
                $network = $this->safe_string($rawNetwork, 'network');
                $networkCode = $this->network_id_to_code($network);
                $limits = array(
                    'withdraw' => array(
                        'min' => $this->safe_number($rawNetwork, 'withdrawMin'),
                        'max' => $this->safe_number($rawNetwork, 'withdrawMax'),
                    ),
                    'deposit' => array(
                        'min' => $this->safe_number($rawNetwork, 'depositMin'),
                        'max' => null,
                    ),
                );
                $precision = $this->parse_number($this->parse_precision($this->safe_string($rawNetwork, 'withdrawPrecision')));
                $networks[$networkCode] = array(
                    'info' => $rawNetwork,
                    'id' => $network,
                    'network' => $networkCode,
                    'fee' => $this->safe_number($rawNetwork, 'withdrawFee'),
                    'active' => null,
                    'deposit' => $this->safe_bool($rawNetwork, 'depositEnable'),
                    'withdraw' => $this->safe_bool($rawNetwork, 'withdrawEnable'),
                    'precision' => $precision,
                    'limits' => $limits,
                );
            }
            if (!(is_array($result) && array_key_exists($code, $result))) { // the exchange could return the same $currency with different $networks
                $result[$code] = array(
                    'info' => $entry,
                    'code' => $code,
                    'id' => $currencyId,
                    'precision' => null,
                    'name' => $name,
                    'active' => null,
                    'deposit' => null,
                    'withdraw' => null,
                    'networks' => $networks,
                    'fee' => null,
                    'limits' => null,
                    'type' => 'crypto', // only cryptos now
                );
            } else {
                $existing = $result[$code];
                $existingNetworks = $this->safe_dict($existing, 'networks', array());
                $newNetworkCodes = is_array($networks) ? array_keys($networks) : array();
                for ($j = 0; $j < count($newNetworkCodes); $j++) {
                    $newNetworkCode = $newNetworkCodes[$j];
                    if (!(is_array($existingNetworks) && array_key_exists($newNetworkCode, $existingNetworks))) {
                        $existingNetworks[$newNetworkCode] = $networks[$newNetworkCode];
                    }
                }
                $result[$code]['networks'] = $existingNetworks;
            }
        }
        $codes = is_array($result) ? array_keys($result) : array();
        for ($i = 0; $i < count($codes); $i++) {
            $code = $codes[$i];
            $currency = $result[$code];
            $result[$code] = $this->safe_currency_structure($currency);
        }
        return $result;
    }

    public function fetch_spot_markets($params): array {
        $response = $this->spotV1PublicGetCommonSymbols ($params);
        //
        //    {
        //        "code" => 0,
        //            "msg" => "",
        //            "debugMsg" => "",
        //            "data" => {
        //              "symbols" => array(
        //                  array(
        //                    "symbol" => "GEAR-USDT",
        //                    "minQty" => 735, // deprecated
        //                    "maxQty" => 2941177, // deprecated.
        //                    "minNotional" => 5,
        //                    "maxNotional" => 20000,
        //                    "status" => 1,
        //                    "tickSize" => 0.000001,
        //                    "stepSize" => 1,
        //                    "apiStateSell" => true,
        //                    "apiStateBuy" => true,
        //                    "timeOnline" => 0,
        //                    "offTime" => 0,
        //                    "maintainTime" => 0
        //                  ),
        //                  ...
        //              )
        //         }
        //    }
        //
        $data = $this->safe_dict($response, 'data');
        $markets = $this->safe_list($data, 'symbols', array());
        return $this->parse_markets($markets);
    }

    public function fetch_swap_markets($params) {
        $response = $this->swapV2PublicGetQuoteContracts ($params);
        //
        //    {
        //        "code" => 0,
        //        "msg" => "",
        //        "data" => array(
        //            array(
        //                "contractId" => "100",
        //                "symbol" => "BTC-USDT",
        //                "size" => "0.0001",
        //                "quantityPrecision" => "4",
        //                "pricePrecision" => "1",
        //                "feeRate" => "0.0005",
        //                "makerFeeRate" => "0.0002",
        //                "takerFeeRate" => "0.0005",
        //                "tradeMinLimit" => "0",
        //                "tradeMinQuantity" => "0.0001",
        //                "tradeMinUSDT" => "2",
        //                "maxLongLeverage" => "125",
        //                "maxShortLeverage" => "125",
        //                "currency" => "USDT",
        //                "asset" => "BTC",
        //                "status" => "1",
        //                "apiStateOpen" => "true",
        //                "apiStateClose" => "true",
        //                "ensureTrigger" => true,
        //                "triggerFeeRate" => "0.00020000"
        //            ),
        //            ...
        //        )
        //    }
        //
        $markets = $this->safe_list($response, 'data', array());
        return $this->parse_markets($markets);
    }

    public function fetch_inverse_swap_markets($params) {
        $response = $this->cswapV1PublicGetMarketContracts ($params);
        //
        //     {
        //         "code" => 0,
        //         "msg" => "",
        //         "timestamp" => 1720074487610,
        //         "data" => array(
        //             array(
        //                 "symbol" => "BNB-USD",
        //                 "pricePrecision" => 2,
        //                 "minTickSize" => "10",
        //                 "minTradeValue" => "10",
        //                 "minQty" => "1.00000000",
        //                 "status" => 1,
        //                 "timeOnline" => 1713175200000
        //             ),
        //         )
        //     }
        //
        $markets = $this->safe_list($response, 'data', array());
        return $this->parse_markets($markets);
    }

    public function parse_market(array $market): array {
        $id = $this->safe_string($market, 'symbol');
        $symbolParts = explode('-', $id);
        $baseId = $symbolParts[0];
        $quoteId = $symbolParts[1];
        $base = $this->safe_currency_code($baseId);
        $quote = $this->safe_currency_code($quoteId);
        $currency = $this->safe_string($market, 'currency');
        $checkIsInverse = false;
        $checkIsLinear = true;
        $minTickSize = $this->safe_number($market, 'minTickSize');
        if ($minTickSize !== null) {
            // inverse $swap $market
            $currency = $baseId;
            $checkIsInverse = true;
            $checkIsLinear = false;
        }
        $settle = $this->safe_currency_code($currency);
        $pricePrecision = $this->safe_number($market, 'tickSize');
        if ($pricePrecision === null) {
            $pricePrecision = $this->parse_number($this->parse_precision($this->safe_string($market, 'pricePrecision')));
        }
        $quantityPrecision = $this->safe_number($market, 'stepSize');
        if ($quantityPrecision === null) {
            $quantityPrecision = $this->parse_number($this->parse_precision($this->safe_string($market, 'quantityPrecision')));
        }
        $type = ($settle !== null) ? 'swap' : 'spot';
        $spot = $type === 'spot';
        $swap = $type === 'swap';
        $symbol = $base . '/' . $quote;
        if ($settle !== null) {
            $symbol .= ':' . $settle;
        }
        $fees = $this->safe_dict($this->fees, $type, array());
        $contractSize = ($swap) ? $this->parse_number('1') : null;
        $isActive = false;
        if (($this->safe_string($market, 'apiStateOpen') === 'true') && ($this->safe_string($market, 'apiStateClose') === 'true')) {
            $isActive = true; // $swap active
        } elseif ($this->safe_bool($market, 'apiStateSell') && $this->safe_bool($market, 'apiStateBuy') && ($this->safe_string($market, 'status') === '1')) {
            $isActive = true; // $spot active
        }
        $isInverse = ($spot) ? null : $checkIsInverse;
        $isLinear = ($spot) ? null : $checkIsLinear;
        $minAmount = null;
        if (!$spot) {
            $minAmount = $this->safe_number_2($market, 'minQty', 'tradeMinQuantity');
        }
        $timeOnline = $this->safe_integer($market, 'timeOnline');
        if ($timeOnline === 0) {
            $timeOnline = null;
        }
        return $this->safe_market_structure(array(
            'id' => $id,
            'symbol' => $symbol,
            'base' => $base,
            'quote' => $quote,
            'settle' => $settle,
            'baseId' => $baseId,
            'quoteId' => $quoteId,
            'settleId' => $currency,
            'type' => $type,
            'spot' => $spot,
            'margin' => false,
            'swap' => $swap,
            'future' => false,
            'option' => false,
            'active' => $isActive,
            'contract' => $swap,
            'linear' => $isLinear,
            'inverse' => $isInverse,
            'taker' => $this->safe_number($fees, 'taker'),
            'maker' => $this->safe_number($fees, 'maker'),
            'feeSide' => $this->safe_string($fees, 'feeSide'),
            'contractSize' => $contractSize,
            'expiry' => null,
            'expiryDatetime' => null,
            'strike' => null,
            'optionType' => null,
            'precision' => array(
                'amount' => $quantityPrecision,
                'price' => $pricePrecision,
            ),
            'limits' => array(
                'leverage' => array(
                    'min' => null,
                    'max' => null,
                ),
                'amount' => array(
                    'min' => $minAmount,
                    'max' => null,
                ),
                'price' => array(
                    'min' => $minTickSize,
                    'max' => null,
                ),
                'cost' => array(
                    'min' => $this->safe_number_n($market, array( 'minNotional', 'tradeMinUSDT', 'minTradeValue' )),
                    'max' => $this->safe_number($market, 'maxNotional'),
                ),
            ),
            'created' => $timeOnline,
            'info' => $market,
        ));
    }

    public function fetch_markets($params = array ()): array {
        /**
         * retrieves data on all markets for bingx
         *
         * @see https://bingx-api.github.io/docs/#/spot/market-api.html#Query%20Symbols
         * @see https://bingx-api.github.io/docs/#/swapV2/market-api.html#Contract%20Information
         * @see https://bingx-api.github.io/docs/#/en-us/cswap/market-api.html#Contract%20Information
         *
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array[]} an array of objects representing market data
         */
        $requests = array( $this->fetch_swap_markets($params) );
        $isSandbox = $this->safe_bool($this->options, 'sandboxMode', false);
        if (!$isSandbox) {
            $requests[] = $this->fetch_inverse_swap_markets($params);
            $requests[] = $this->fetch_spot_markets($params); // sandbox is swap only
        }
        $promises = $requests;
        $linearSwapMarkets = $this->safe_list($promises, 0, array());
        $inverseSwapMarkets = $this->safe_list($promises, 1, array());
        $spotMarkets = $this->safe_list($promises, 2, array());
        $swapMarkets = $this->array_concat($linearSwapMarkets, $inverseSwapMarkets);
        return $this->array_concat($spotMarkets, $swapMarkets);
    }

    public function fetch_ohlcv(string $symbol, $timeframe = '1m', ?int $since = null, ?int $limit = null, $params = array ()): array {
        /**
         * fetches historical candlestick data containing the open, high, low, and close $price, and the volume of a $market
         *
         * @see https://bingx-api.github.io/docs/#/swapV2/market-api.html#K-Line%20Data
         * @see https://bingx-api.github.io/docs/#/spot/market-api.html#Candlestick%20chart%20data
         * @see https://bingx-api.github.io/docs/#/swapV2/market-api.html#%20K-Line%20Data
         * @see https://bingx-api.github.io/docs/#/en-us/swapV2/market-api.html#Mark%20Price%20Kline/Candlestick%20Data
         * @see https://bingx-api.github.io/docs/#/en-us/cswap/market-api.html#Get%20K-line%20Data
         *
         * @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 exchange 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
         */
        $this->load_markets();
        $paginate = false;
        list($paginate, $params) = $this->handle_option_and_params($params, 'fetchOHLCV', 'paginate', false);
        if ($paginate) {
            return $this->fetch_paginated_call_deterministic('fetchOHLCV', $symbol, $since, $limit, $timeframe, $params, 1440);
        }
        $market = $this->market($symbol);
        $request = array(
            'symbol' => $market['id'],
        );
        $request['interval'] = $this->safe_string($this->timeframes, $timeframe, $timeframe);
        if ($since !== null) {
            $request['startTime'] = max ($since - 1, 0);
        }
        if ($limit !== null) {
            $request['limit'] = $limit;
        }
        $until = $this->safe_integer_2($params, 'until', 'endTime');
        if ($until !== null) {
            $params = $this->omit($params, array( 'until' ));
            $request['endTime'] = $until;
        }
        $response = null;
        if ($market['spot']) {
            $response = $this->spotV1PublicGetMarketKline ($this->extend($request, $params));
        } else {
            if ($market['inverse']) {
                $response = $this->cswapV1PublicGetMarketKlines ($this->extend($request, $params));
            } else {
                $price = $this->safe_string($params, 'price');
                $params = $this->omit($params, 'price');
                if ($price === 'mark') {
                    $response = $this->swapV1PublicGetMarketMarkPriceKlines ($this->extend($request, $params));
                } else {
                    $response = $this->swapV3PublicGetQuoteKlines ($this->extend($request, $params));
                }
            }
        }
        //
        //    {
        //        "code" => 0,
        //        "msg" => "",
        //        "data" => array(
        //          array(
        //            "open" => "19396.8",
        //            "close" => "19394.4",
        //            "high" => "19397.5",
        //            "low" => "19385.7",
        //            "volume" => "110.05",
        //            "time" => 1666583700000
        //          ),
        //          ...
        //        )
        //    }
        //
        // fetchMarkOHLCV
        //
        //    {
        //        "code" => 0,
        //        "msg" => "",
        //        "data" => array(
        //            {
        //                "open" => "42191.7",
        //                "close" => "42189.5",
        //                "high" => "42196.5",
        //                "low" => "42189.5",
        //                "volume" => "0.00",
        //                "openTime" => 1706508840000,
        //                "closeTime" => 1706508840000
        //            }
        //        )
        //    }
        //
        $ohlcvs = $this->safe_value($response, 'data', array());
        if (gettype($ohlcvs) !== 'array' || array_keys($ohlcvs) !== array_keys(array_keys($ohlcvs))) {
            $ohlcvs = array( $ohlcvs );
        }
        return $this->parse_ohlcvs($ohlcvs, $market, $timeframe, $since, $limit);
    }

    public function parse_ohlcv($ohlcv, ?array $market = null): array {
        //
        //    {
        //        "open" => "19394.4",
        //        "close" => "19379.0",
        //        "high" => "19394.4",
        //        "low" => "19368.3",
        //        "volume" => "167.44",
        //        "time" => 1666584000000
        //    }
        //
        // fetchMarkOHLCV
        //
        //    {
        //        "open" => "42191.7",
        //        "close" => "42189.5",
        //        "high" => "42196.5",
        //        "low" => "42189.5",
        //        "volume" => "0.00",
        //        "openTime" => 1706508840000,
        //        "closeTime" => 1706508840000
        //    }
        // spot
        //    array(
        //        1691402580000,
        //        29093.61,
        //        29093.93,
        //        29087.73,
        //        29093.24,
        //        0.59,
        //        1691402639999,
        //        17221.07
        //    )
        //
        if (gettype($ohlcv) === 'array' && array_keys($ohlcv) === array_keys(array_keys($ohlcv))) {
            return array(
                $this->safe_integer($ohlcv, 0),
                $this->safe_number($ohlcv, 1),
                $this->safe_number($ohlcv, 2),
                $this->safe_number($ohlcv, 3),
                $this->safe_number($ohlcv, 4),
                $this->safe_number($ohlcv, 5),
            );
        }
        return array(
            $this->safe_integer_2($ohlcv, 'time', 'closeTime'),
            $this->safe_number($ohlcv, 'open'),
            $this->safe_number($ohlcv, 'high'),
            $this->safe_number($ohlcv, 'low'),
            $this->safe_number($ohlcv, 'close'),
            $this->safe_number($ohlcv, 'volume'),
        );
    }

    public function fetch_trades(string $symbol, ?int $since = null, ?int $limit = null, $params = array ()): array {
        /**
         * get the list of most recent $trades for a particular $symbol
         *
         * @see https://bingx-api.github.io/docs/#/spot/market-api.html#Query%20transaction%20records
         * @see https://bingx-api.github.io/docs/#/swapV2/market-api.html#The%20latest%20Trade%20of%20a%20Trading%20Pair
         *
         * @param {string} $symbol unified $symbol of the $market to fetch $trades for
         * @param {int} [$since] timestamp in ms of the earliest trade to fetch
         * @param {int} [$limit] the maximum amount of $trades to fetch
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=public-$trades trade structures~
         */
        $this->load_markets();
        $market = $this->market($symbol);
        $request = array(
            'symbol' => $market['id'],
        );
        if ($limit !== null) {
            $request['limit'] = min ($limit, 100); // avoid API exception "limit should less than 100"
        }
        $response = null;
        $marketType = null;
        list($marketType, $params) = $this->handle_market_type_and_params('fetchTrades', $market, $params);
        if ($marketType === 'spot') {
            $response = $this->spotV1PublicGetMarketTrades ($this->extend($request, $params));
        } else {
            $response = $this->swapV2PublicGetQuoteTrades ($this->extend($request, $params));
        }
        //
        // spot
        //
        //    {
        //        "code" => 0,
        //        "data" => array(
        //            {
        //                "id" => 43148253,
        //                "price" => 25714.71,
        //                "qty" => 1.674571,
        //                "time" => 1655085975589,
        //                "buyerMaker" => false
        //            }
        //        )
        //    }
        //
        // swap
        //
        //    {
        //      "code":0,
        //      "msg":"",
        //      "data":array(
        //        array(
        //          "time" => 1672025549368,
        //          "isBuyerMaker" => true,
        //          "price" => "16885.0",
        //          "qty" => "3.3002",
        //          "quoteQty" => "55723.87"
        //        ),
        //        ...
        //      )
        //    }
        //
        $trades = $this->safe_list($response, 'data', array());
        return $this->parse_trades($trades, $market, $since, $limit);
    }

    public function parse_trade(array $trade, ?array $market = null): array {
        //
        // spot fetchTrades
        //
        //    {
        //        "id" => 43148253,
        //        "price" => 25714.71,
        //        "qty" => 1.674571,
        //        "time" => 1655085975589,
        //        "buyerMaker" => false
        //    }
        //
        // spot fetchMyTrades
        //
        //     {
        //         "symbol" => "LTC-USDT",
        //         "id" => 36237072,
        //         "orderId" => 1674069326895775744,
        //         "price" => "85.891",
        //         "qty" => "0.0582",
        //         "quoteQty" => "4.9988562000000005",
        //         "commission" => -0.00005820000000000001,
        //         "commissionAsset" => "LTC",
        //         "time" => 1687964205000,
        //         "isBuyer" => true,
        //         "isMaker" => false
        //     }
        //
        // swap fetchTrades
        //
        //    {
        //        "time" => 1672025549368,
        //        "isBuyerMaker" => true,
        //        "price" => "16885.0",
        //        "qty" => "3.3002",
        //        "quoteQty" => "55723.87"
        //    }
        //
        // swap fetchMyTrades
        //
        //    {
        //        "volume" => "0.1",
        //        "price" => "106.75",
        //        "amount" => "10.6750",
        //        "commission" => "-0.0053",
        //        "currency" => "USDT",
        //        "orderId" => "1676213270274379776",
        //        "liquidatedPrice" => "0.00",
        //        "liquidatedMarginRatio" => "0.00",
        //        "filledTime" => "2023-07-04T20:56:01.000+0800"
        //    }
        //
        // ws spot
        //
        //    {
        //        "E" => 1690214529432,
        //        "T" => 1690214529386,
        //        "e" => "trade",
        //        "m" => true,
        //        "p" => "29110.19",
        //        "q" => "0.1868",
        //        "s" => "BTC-USDT",
        //        "t" => "57903921"
        //    }
        //
        // ws linear swap
        //
        //    {
        //        "q" => "0.0421",
        //        "p" => "29023.5",
        //        "T" => 1690221401344,
        //        "m" => false,
        //        "s" => "BTC-USDT"
        //    }
        //
        // ws inverse swap
        //
        //     {
        //         "e" => "trade",
        //         "E" => 1722920589665,
        //         "s" => "BTC-USD",
        //         "t" => "39125001",
        //         "p" => "55360.0",
        //         "q" => "1",
        //         "T" => 1722920589582,
        //         "m" => false
        //     }
        //
        // inverse swap fetchMyTrades
        //
        //     {
        //         "orderId" => "1817441228670648320",
        //         "symbol" => "SOL-USD",
        //         "type" => "MARKET",
        //         "side" => "BUY",
        //         "positionSide" => "LONG",
        //         "tradeId" => "97244554",
        //         "volume" => "2",
        //         "tradePrice" => "182.652",
        //         "amount" => "20.00000000",
        //         "realizedPnl" => "0.00000000",
        //         "commission" => "-0.00005475",
        //         "currency" => "SOL",
        //         "buyer" => true,
        //         "maker" => false,
        //         "tradeTime" => 1722146730000
        //     }
        //
        $time = $this->safe_integer_n($trade, array( 'time', 'filledTm', 'T', 'tradeTime' ));
        $datetimeId = $this->safe_string($trade, 'filledTm');
        if ($datetimeId !== null) {
            $time = $this->parse8601($datetimeId);
        }
        if ($time === 0) {
            $time = null;
        }
        $cost = $this->safe_string($trade, 'quoteQty');
        // $type = ($cost === null) ? 'spot' : 'swap'; this is not reliable
        $currencyId = $this->safe_string_n($trade, array( 'currency', 'N', 'commissionAsset' ));
        $currencyCode = $this->safe_currency_code($currencyId);
        $m = $this->safe_bool($trade, 'm');
        $marketId = $this->safe_string_2($trade, 's', 'symbol');
        $isBuyerMaker = $this->safe_bool_n($trade, array( 'buyerMaker', 'isBuyerMaker', 'maker' ));
        $takeOrMaker = null;
        if (($isBuyerMaker !== null) || ($m !== null)) {
            $takeOrMaker = ($isBuyerMaker || $m) ? 'maker' : 'taker';
        }
        $side = $this->safe_string_lower_2($trade, 'side', 'S');
        if ($side === null) {
            if (($isBuyerMaker !== null) || ($m !== null)) {
                $side = ($isBuyerMaker || $m) ? 'sell' : 'buy';
                $takeOrMaker = 'taker';
            }
        }
        $isBuyer = $this->safe_bool($trade, 'isBuyer');
        if ($isBuyer !== null) {
            $side = $isBuyer ? 'buy' : 'sell';
        }
        $isMaker = $this->safe_bool($trade, 'isMaker');
        if ($isMaker !== null) {
            $takeOrMaker = $isMaker ? 'maker' : 'taker';
        }
        $amount = $this->safe_string_n($trade, array( 'qty', 'amount', 'q' ));
        if (($market !== null) && $market['swap'] && (is_array($trade) && array_key_exists('volume', $trade))) {
            // private $trade returns num of contracts instead of base currency (as the order-related methods do)
            $contractSize = $this->safe_string($market['info'], 'tradeMinQuantity');
            $volume = $this->safe_string($trade, 'volume');
            $amount = Precise::string_mul($volume, $contractSize);
        }
        return $this->safe_trade(array(
            'id' => $this->safe_string_n($trade, array( 'id', 't' )),
            'info' => $trade,
            'timestamp' => $time,
            'datetime' => $this->iso8601($time),
            'symbol' => $this->safe_symbol($marketId, $market, '-'),
            'order' => $this->safe_string_2($trade, 'orderId', 'i'),
            'type' => $this->safe_string_lower($trade, 'o'),
            'side' => $this->parse_order_side($side),
            'takerOrMaker' => $takeOrMaker,
            'price' => $this->safe_string_n($trade, array( 'price', 'p', 'tradePrice' )),
            'amount' => $amount,
            'cost' => $cost,
            'fee' => array(
                'cost' => $this->parse_number(Precise::string_abs($this->safe_string_2($trade, 'commission', 'n'))),
                'currency' => $currencyCode,
            ),
        ), $market);
    }

    public function fetch_order_book(string $symbol, ?int $limit = null, $params = array ()): array {
        /**
         * fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
         *
         * @see https://bingx-api.github.io/docs/#/spot/market-api.html#Query%20depth%20information
         * @see https://bingx-api.github.io/docs/#/swapV2/market-api.html#Get%20Market%20Depth
         * @see https://bingx-api.github.io/docs/#/en-us/cswap/market-api.html#Query%20Depth%20Data
         *
         * @param {string} $symbol unified $symbol of the $market to fetch the order book for
         * @param {int} [$limit] the maximum amount of order book entries to return
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array} A dictionary of ~@link https://docs.ccxt.com/#/?id=order-book-structure order book structures~ indexed by $market symbols
         */
        $this->load_markets();
        $market = $this->market($symbol);
        $request = array(
            'symbol' => $market['id'],
        );
        if ($limit !== null) {
            $request['limit'] = $limit;
        }
        $response = null;
        $marketType = null;
        list($marketType, $params) = $this->handle_market_type_and_params('fetchOrderBook', $market, $params);
        if ($marketType === 'spot') {
            $response = $this->spotV1PublicGetMarketDepth ($this->extend($request, $params));
        } else {
            if ($market['inverse']) {
                $response = $this->cswapV1PublicGetMarketDepth ($this->extend($request, $params));
            } else {
                $response = $this->swapV2PublicGetQuoteDepth ($this->extend($request, $params));
            }
        }
        //
        // spot
        //
        //     {
        //         "code":0,
        //         "timestamp":1743240504535,
        //         "data":{
        //             "bids":[
        //                 ["83775.39","1.981875"],
        //                 ["83775.38","0.001076"],
        //                 ["83775.34","0.254716"],
        //             ],
        //             "asks":[
        //                 ["83985.40","0.000013"],
        //                 ["83980.00","0.000011"],
        //                 ["83975.70","0.000061000000000000005"],
        //             ],
        //             "ts":1743240504535,
        //             "lastUpdateId":13565639906
        //         }
        //     }
        //
        //
        // linear swap
        //
        //     {
        //         "code":0,
        //         "msg":"",
        //         "data":{
        //             "T":1743240836255,
        //             "bids":[
        //                 ["83760.7","7.0861"],
        //                 ["83760.6","0.0044"],
        //                 ["83757.7","1.9526"],
        //             ],
        //             "asks":[
        //                 ["83784.3","8.3531"],
        //                 ["83782.8","23.7289"],
        //                 ["83780.1","18.0617"],
        //             ],
        //             "bidsCoin":[
        //                 ["83760.7","0.0007"],
        //                 ["83760.6","0.0000"],
        //                 ["83757.7","0.0002"],
        //             ],
        //             "asksCoin":[
        //                 ["83784.3","0.0008"],
        //                 ["83782.8","0.0024"],
        //                 ["83780.1","0.0018"],
        //             ]
        //         }
        //     }
        //
        // inverse swap
        //
        //     {
        //         "code":0,
        //         "msg":"",
        //         "timestamp":1743240979146,
        //         "data":{
        //             "T":1743240978691,
        //             "bids":[
        //                 ["83611.4","241.0"],
        //                 ["83611.3","1.0"],
        //                 ["83602.9","666.0"],
        //             ],
        //             "asks":[
        //                 ["83645.0","4253.0"],
        //                 ["83640.5","3188.0"],
        //                 ["83636.0","5540.0"],
        //             ]
        //         }
        //     }
        //
        $orderbook = $this->safe_dict($response, 'data', array());
        $nonce = $this->safe_integer($orderbook, 'lastUpdateId');
        $timestamp = $this->safe_integer_2($orderbook, 'T', 'ts');
        $result = $this->parse_order_book($orderbook, $market['symbol'], $timestamp, 'bids', 'asks', 0, 1);
        $result['nonce'] = $nonce;
        return $result;
    }

    public function fetch_funding_rate(string $symbol, $params = array ()): array {
        /**
         * fetch the current funding rate
         *
         * @see https://bingx-api.github.io/docs/#/swapV2/market-api.html#Current%20Funding%20Rate
         * @see https://bingx-api.github.io/docs/#/en-us/cswap/market-api.html#Price%20&%20Current%20Funding%20Rate
         *
         * @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~
         */
        $this->load_markets();
        $market = $this->market($symbol);
        $request = array(
            'symbol' => $market['id'],
        );
        $response = null;
        if ($market['inverse']) {
            $response = $this->cswapV1PublicGetMarketPremiumIndex ($this->extend($request, $params));
        } else {
            $response = $this->swapV2PublicGetQuotePremiumIndex ($this->extend($request, $params));
        }
        //
        //    {
        //        "code":0,
        //        "msg":"",
        //        "data":array(
        //          array(
        //            "symbol" => "BTC-USDT",
        //            "markPrice" => "16884.5",
        //            "indexPrice" => "16886.9",
        //            "lastFundingRate" => "0.0001",
        //            "nextFundingTime" => 1672041600000
        //          ),
        //          ...
        //        )
        //    }
        //
        $data = $this->safe_dict($response, 'data');
        return $this->parse_funding_rate($data, $market);
    }

    public function fetch_funding_rates(?array $symbols = null, $params = array ()): array {
        /**
         * fetch the current funding rate for multiple $symbols
         *
         * @see https://bingx-api.github.io/docs/#/swapV2/market-api.html#Current%20Funding%20Rate
         *
         * @param {string[]} [$symbols] list of unified market $symbols
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=funding-rate-structure funding rate structures~
         */
        $this->load_markets();
        $symbols = $this->market_symbols($symbols, 'swap', true);
        $response = $this->swapV2PublicGetQuotePremiumIndex ($this->extend($params));
        $data = $this->safe_list($response, 'data', array());
        return $this->parse_funding_rates($data, $symbols);
    }

    public function parse_funding_rate($contract, ?array $market = null): array {
        //
        //     {
        //         "symbol" => "BTC-USDT",
        //         "markPrice" => "16884.5",
        //         "indexPrice" => "16886.9",
        //         "lastFundingRate" => "0.0001",
        //         "nextFundingTime" => 1672041600000
        //     }
        //
        $marketId = $this->safe_string($contract, 'symbol');
        $nextFundingTimestamp = $this->safe_integer($contract, 'nextFundingTime');
        return array(
            'info' => $contract,
            'symbol' => $this->safe_symbol($marketId, $market, '-', 'swap'),
            'markPrice' => $this->safe_number($contract, 'markPrice'),
            'indexPrice' => $this->safe_number($contract, 'indexPrice'),
            'interestRate' => null,
            'estimatedSettlePrice' => null,
            'timestamp' => null,
            'datetime' => null,
            'fundingRate' => $this->safe_number($contract, 'lastFundingRate'),
            'fundingTimestamp' => null,
            'fundingDatetime' => null,
            'nextFundingRate' => null,
            'nextFundingTimestamp' => $nextFundingTimestamp,
            'nextFundingDatetime' => $this->iso8601($nextFundingTimestamp),
            'previousFundingRate' => null,
            'previousFundingTimestamp' => null,
            'previousFundingDatetime' => null,
            'interval' => null,
        );
    }

    public function fetch_funding_rate_history(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) {
        /**
         * fetches historical funding rate prices
         *
         * @see https://bingx-api.github.io/docs/#/swapV2/market-api.html#Funding%20Rate%20History
         *
         * @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 ~@link https://docs.ccxt.com/#/?id=funding-rate-history-structure funding rate structures~ to fetch
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @param {int} [$params->until] timestamp in ms of the latest funding rate 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 [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-$params)
         * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=funding-rate-history-structure funding rate structures~
         */
        if ($symbol === null) {
            throw new ArgumentsRequired($this->id . ' fetchFundingRateHistory() requires a $symbol argument');
        }
        $this->load_markets();
        $paginate = false;
        list($paginate, $params) = $this->handle_option_and_params($params, 'fetchFundingRateHistory', 'paginate');
        if ($paginate) {
            return $this->fetch_paginated_call_deterministic('fetchFundingRateHistory', $symbol, $since, $limit, '8h', $params);
        }
        $market = $this->market($symbol);
        $request = array(
            'symbol' => $market['id'],
        );
        if ($since !== null) {
            $request['startTime'] = $since;
        }
        if ($limit !== null) {
            $request['limit'] = $limit;
        }
        $until = $this->safe_integer_2($params, 'until', 'startTime');
        if ($until !== null) {
            $params = $this->omit($params, array( 'until' ));
            $request['startTime'] = $until;
        }
        $response = $this->swapV2PublicGetQuoteFundingRate ($this->extend($request, $params));
        //
        //    {
        //        "code":0,
        //        "msg":"",
        //        "data":array(
        //          array(
        //            "symbol" => "BTC-USDT",
        //            "fundingRate" => "0.0001",
        //            "fundingTime" => 1585684800000
        //          ),
        //          ...
        //        )
        //    }
        //
        $data = $this->safe_list($response, 'data', array());
        return $this->parse_funding_rate_histories($data, $market, $since, $limit);
    }

    public function parse_funding_rate_history($contract, ?array $market = null) {
        //
        //     {
        //         "symbol" => "BTC-USDT",
        //         "fundingRate" => "0.0001",
        //         "fundingTime" => 1585684800000
        //     }
        //
        $timestamp = $this->safe_integer($contract, 'fundingTime');
        return array(
            'info' => $contract,
            'symbol' => $this->safe_symbol($this->safe_string($contract, 'symbol'), $market, '-', 'swap'),
            'fundingRate' => $this->safe_number($contract, 'fundingRate'),
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
        );
    }

    public function fetch_open_interest(string $symbol, $params = array ()) {
        /**
         * retrieves the open interest of a trading pair
         *
         * @see https://bingx-api.github.io/docs/#/swapV2/market-api.html#Get%20Swap%20Open%20Positions
         * @see https://bingx-api.github.io/docs/#/en-us/cswap/market-api.html#Get%20Swap%20Open%20Positions
         *
         * @param {string} $symbol unified CCXT $market $symbol
         * @param {array} [$params] exchange specific parameters
         * @return {array} an open interest structurearray(@link https://docs.ccxt.com/#/?id=open-interest-structure)
         */
        $this->load_markets();
        $market = $this->market($symbol);
        $request = array(
            'symbol' => $market['id'],
        );
        $response = null;
        if ($market['inverse']) {
            $response = $this->cswapV1PublicGetMarketOpenInterest ($this->extend($request, $params));
        } else {
            $response = $this->swapV2PublicGetQuoteOpenInterest ($this->extend($request, $params));
        }
        //
        // linear swap
        //
        //     {
        //         "code" => 0,
        //         "msg" => "",
        //         "data" => {
        //           "openInterest" => "3289641547.10",
        //           "symbol" => "BTC-USDT",
        //           "time" => 1672026617364
        //         }
        //     }
        //
        // inverse swap
        //
        //     {
        //         "code" => 0,
        //         "msg" => "",
        //         "timestamp" => 1720328247986,
        //         "data" => array(
        //             {
        //                 "symbol" => "BTC-USD",
        //                 "openInterest" => "749.1160",
        //                 "timestamp" => 1720310400000
        //             }
        //         )
        //     }
        //
        $result = array();
        if ($market['inverse']) {
            $data = $this->safe_list($response, 'data', array());
            $result = $this->safe_dict($data, 0, array());
        } else {
            $result = $this->safe_dict($response, 'data', array());
        }
        return $this->parse_open_interest($result, $market);
    }

    public function parse_open_interest($interest, ?array $market = null) {
        //
        // linear swap
        //
        //     {
        //         "openInterest" => "3289641547.10",
        //         "symbol" => "BTC-USDT",
        //         "time" => 1672026617364
        //     }
        //
        // inverse swap
        //
        //     {
        //         "symbol" => "BTC-USD",
        //         "openInterest" => "749.1160",
        //         "timestamp" => 1720310400000
        //     }
        //
        $timestamp = $this->safe_integer_2($interest, 'time', 'timestamp');
        $id = $this->safe_string($interest, 'symbol');
        $symbol = $this->safe_symbol($id, $market, '-', 'swap');
        $openInterest = $this->safe_number($interest, 'openInterest');
        return $this->safe_open_interest(array(
            'symbol' => $symbol,
            'baseVolume' => null,
            'quoteVolume' => null,  // deprecated
            'openInterestAmount' => null,
            'openInterestValue' => $openInterest,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'info' => $interest,
        ), $market);
    }

    public function fetch_ticker(string $symbol, $params = array ()): array {
        /**
         * fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific $market
         *
         * @see https://bingx-api.github.io/docs/#/en-us/swapV2/market-api.html#Get%20Ticker
         * @see https://bingx-api.github.io/docs/#/en-us/spot/market-api.html#24-hour%20price%20changes
         * @see https://bingx-api.github.io/docs/#/en-us/cswap/market-api.html#Query%2024-Hour%20Price%20Change
         *
         * @param {string} $symbol unified $symbol of the $market to fetch the ticker for
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array} a ~@link https://docs.ccxt.com/#/?id=ticker-structure ticker structure~
         */
        $this->load_markets();
        $market = $this->market($symbol);
        $request = array(
            'symbol' => $market['id'],
        );
        $response = null;
        if ($market['spot']) {
            $response = $this->spotV1PublicGetTicker24hr ($this->extend($request, $params));
        } else {
            if ($market['inverse']) {
                $response = $this->cswapV1PublicGetMarketTicker ($this->extend($request, $params));
            } else {
                $response = $this->swapV2PublicGetQuoteTicker ($this->extend($request, $params));
            }
        }
        //
        // spot and swap
        //
        //     {
        //         "code" => 0,
        //         "msg" => "",
        //         "timestamp" => 1720647285296,
        //         "data" => array(
        //             {
        //                 "symbol" => "SOL-USD",
        //                 "priceChange" => "-2.418",
        //                 "priceChangePercent" => "-1.6900%",
        //                 "lastPrice" => "140.574",
        //                 "lastQty" => "1",
        //                 "highPrice" => "146.190",
        //                 "lowPrice" => "138.586",
        //                 "volume" => "1464648.00",
        //                 "quoteVolume" => "102928.12",
        //                 "openPrice" => "142.994",
        //                 "closeTime" => "1720647284976",
        //                 "bidPrice" => "140.573",
        //                 "bidQty" => "372",
        //                 "askPrice" => "140.577",
        //                 "askQty" => "58"
        //             }
        //         )
        //     }
        //
        $data = $this->safe_list($response, 'data');
        if ($data !== null) {
            $first = $this->safe_dict($data, 0, array());
            return $this->parse_ticker($first, $market);
        }
        $dataDict = $this->safe_dict($response, 'data', array());
        return $this->parse_ticker($dataDict, $market);
    }

    public function fetch_tickers(?array $symbols = null, $params = array ()): array {
        /**
         * fetches price $tickers for multiple markets, statistical information calculated over the past 24 hours for each $market
         *
         * @see https://bingx-api.github.io/docs/#/en-us/swapV2/market-api.html#Get%20Ticker
         * @see https://bingx-api.github.io/docs/#/en-us/spot/market-api.html#24-hour%20price%20changes
         * @see https://bingx-api.github.io/docs/#/en-us/cswap/market-api.html#Query%2024-Hour%20Price%20Change
         *
         * @param {string[]|null} $symbols unified $symbols of the markets to fetch the ticker for, all $market $tickers are returned if not assigned
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array} a dictionary of ~@link https://docs.ccxt.com/#/?id=ticker-structure ticker structures~
         */
        $this->load_markets();
        $market = null;
        if ($symbols !== null) {
            $symbols = $this->market_symbols($symbols);
            $firstSymbol = $this->safe_string($symbols, 0);
            if ($firstSymbol !== null) {
                $market = $this->market($firstSymbol);
            }
        }
        $type = null;
        list($type, $params) = $this->handle_market_type_and_params('fetchTickers', $market, $params);
        $subType = null;
        list($subType, $params) = $this->handle_sub_type_and_params('fetchTickers', $market, $params);
        $response = null;
        if ($type === 'spot') {
            $response = $this->spotV1PublicGetTicker24hr ($params);
        } else {
            if ($subType === 'inverse') {
                $response = $this->cswapV1PublicGetMarketTicker ($params);
            } else {
                $response = $this->swapV2PublicGetQuoteTicker ($params);
            }
        }
        //
        // spot and swap
        //
        //     {
        //         "code" => 0,
        //         "msg" => "",
        //         "timestamp" => 1720647285296,
        //         "data" => array(
        //             array(
        //                 "symbol" => "SOL-USD",
        //                 "priceChange" => "-2.418",
        //                 "priceChangePercent" => "-1.6900%",
        //                 "lastPrice" => "140.574",
        //                 "lastQty" => "1",
        //                 "highPrice" => "146.190",
        //                 "lowPrice" => "138.586",
        //                 "volume" => "1464648.00",
        //                 "quoteVolume" => "102928.12",
        //                 "openPrice" => "142.994",
        //                 "closeTime" => "1720647284976",
        //                 "bidPrice" => "140.573",
        //                 "bidQty" => "372",
        //                 "askPrice" => "140.577",
        //                 "askQty" => "58"
        //             ),
        //             ...
        //         )
        //     }
        //
        $tickers = $this->safe_list($response, 'data');
        return $this->parse_tickers($tickers, $symbols);
    }

    public function fetch_mark_price(string $symbol, $params = array ()): array {
        /**
         * fetches mark prices for the $market
         *
         * @see https://bingx-api.github.io/docs/#/en-us/swapV2/market-api.html#Mark%20Price%20and%20Funding%20Rate
         *
         * @param {string} $symbol unified $symbol of the $market to fetch the ticker for
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array} a dictionary of ~@link https://docs.ccxt.com/#/?id=ticker-structure ticker structures~
         */
        $this->load_markets();
        $market = $this->market($symbol);
        $subType = null;
        list($subType, $params) = $this->handle_sub_type_and_params('fetchMarkPrice', $market, $params, 'linear');
        $request = array(
            'symbol' => $market['id'],
        );
        $response = null;
        if ($subType === 'inverse') {
            $response = $this->cswapV1PublicGetMarketPremiumIndex ($this->extend($request, $params));
            //
            // {
            //     "code" => 0,
            //     "msg" => "",
            //     "timestamp" => 1728577213289,
            //     "data" => array(
            //         {
            //             "symbol" => "ETH-USD",
            //             "lastFundingRate" => "0.0001",
            //             "markPrice" => "2402.68",
            //             "indexPrice" => "2404.92",
            //             "nextFundingTime" => 1728604800000
            //         }
            //     )
            // }
            //
        } else {
            $response = $this->swapV2PublicGetQuotePremiumIndex ($this->extend($request, $params));
            //
            // {
            //     "code" => 0,
            //     "msg" => "",
            //     "data" => {
            //         "symbol" => "ETH-USDT",
            //         "markPrice" => "2408.40",
            //         "indexPrice" => "2409.62",
            //         "lastFundingRate" => "0.00009900",
            //         "nextFundingTime" => 1728604800000
            //     }
            // }
            //
        }
        if (gettype($response['data']) === 'array' && array_keys($response['data']) === array_keys(array_keys($response['data']))) {
            return $this->parse_ticker($this->safe_dict($response['data'], 0, array()), $market);
        }
        return $this->parse_ticker($response['data'], $market);
    }

    public function fetch_mark_prices(?array $symbols = null, $params = array ()): array {
        /**
         * fetches mark prices for multiple markets
         *
         * @see https://bingx-api.github.io/docs/#/en-us/swapV2/market-api.html#Mark%20Price%20and%20Funding%20Rate
         *
         * @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 exchange API endpoint
         * @return {array} a dictionary of ~@link https://docs.ccxt.com/#/?id=ticker-structure ticker structures~
         */
        $this->load_markets();
        $market = null;
        if ($symbols !== null) {
            $symbols = $this->market_symbols($symbols);
            $firstSymbol = $this->safe_string($symbols, 0);
            if ($firstSymbol !== null) {
                $market = $this->market($firstSymbol);
            }
        }
        $subType = null;
        list($subType, $params) = $this->handle_sub_type_and_params('fetchMarkPrices', $market, $params, 'linear');
        $response = null;
        if ($subType === 'inverse') {
            $response = $this->cswapV1PublicGetMarketPremiumIndex ($params);
        } else {
            $response = $this->swapV2PublicGetQuotePremiumIndex ($params);
        }
        //
        // spot and swap
        //
        //     {
        //         "code" => 0,
        //         "msg" => "",
        //         "timestamp" => 1720647285296,
        //         "data" => array(
        //             array(
        //                 "symbol" => "SOL-USD",
        //                 "priceChange" => "-2.418",
        //                 "priceChangePercent" => "-1.6900%",
        //                 "lastPrice" => "140.574",
        //                 "lastQty" => "1",
        //                 "highPrice" => "146.190",
        //                 "lowPrice" => "138.586",
        //                 "volume" => "1464648.00",
        //                 "quoteVolume" => "102928.12",
        //                 "openPrice" => "142.994",
        //                 "closeTime" => "1720647284976",
        //                 "bidPrice" => "140.573",
        //                 "bidQty" => "372",
        //                 "askPrice" => "140.577",
        //                 "askQty" => "58"
        //             ),
        //             ...
        //         )
        //     }
        //
        $tickers = $this->safe_list($response, 'data');
        return $this->parse_tickers($tickers, $symbols);
    }

    public function parse_ticker(array $ticker, ?array $market = null): array {
        //
        // mark price
        // {
        //     "symbol" => "string",
        //     "lastFundingRate" => "string",
        //     "markPrice" => "string",
        //     "indexPrice" => "string",
        //     "nextFundingTime" => "int64"
        // }
        //
        // spot
        //    {
        //        "symbol" => "BTC-USDT",
        //        "openPrice" => "26032.08",
        //        "highPrice" => "26178.86",
        //        "lowPrice" => "25968.18",
        //        "lastPrice" => "26113.60",
        //        "volume" => "1161.79",
        //        "quoteVolume" => "30288466.44",
        //        "openTime" => "1693081020762",
        //        "closeTime" => "1693167420762",
        //  added 2023-11-10:
        //        "bidPrice" => 16726.0,
        //        "bidQty" => 0.05,
        //        "askPrice" => 16726.0,
        //        "askQty" => 0.05,
        //    }
        // swap
        //
        //    {
        //        "symbol" => "BTC-USDT",
        //        "priceChange" => "52.5",
        //        "priceChangePercent" => "0.31%", // they started to add the percent sign in value
        //        "lastPrice" => "16880.5",
        //        "lastQty" => "2.2238",          // only present in swap!
        //        "highPrice" => "16897.5",
        //        "lowPrice" => "16726.0",
        //        "volume" => "245870.1692",
        //        "quoteVolume" => "4151395117.73",
        //        "openPrice" => "16832.0",
        //        "openTime" => 1672026667803,
        //        "closeTime" => 1672026648425,
        //  added 2023-11-10:
        //        "bidPrice" => 16726.0,
        //        "bidQty" => 0.05,
        //        "askPrice" => 16726.0,
        //        "askQty" => 0.05,
        //    }
        //
        $marketId = $this->safe_string($ticker, 'symbol');
        $lastQty = $this->safe_string($ticker, 'lastQty');
        // in spot markets, $lastQty is not present
        // it's (bad, but) the only way we can check the tickers origin
        $type = ($lastQty === null) ? 'spot' : 'swap';
        $market = $this->safe_market($marketId, $market, null, $type);
        $symbol = $market['symbol'];
        $open = $this->safe_string($ticker, 'openPrice');
        $high = $this->safe_string($ticker, 'highPrice');
        $low = $this->safe_string($ticker, 'lowPrice');
        $close = $this->safe_string($ticker, 'lastPrice');
        $quoteVolume = $this->safe_string($ticker, 'quoteVolume');
        $baseVolume = $this->safe_string($ticker, 'volume');
        $percentage = $this->safe_string($ticker, 'priceChangePercent');
        if ($percentage !== null) {
            $percentage = str_replace('%', '', $percentage);
        }
        $change = $this->safe_string($ticker, 'priceChange');
        $ts = $this->safe_integer($ticker, 'closeTime');
        if ($ts === 0) {
            $ts = null;
        }
        $datetime = $this->iso8601($ts);
        $bid = $this->safe_string($ticker, 'bidPrice');
        $bidVolume = $this->safe_string($ticker, 'bidQty');
        $ask = $this->safe_string($ticker, 'askPrice');
        $askVolume = $this->safe_string($ticker, 'askQty');
        return $this->safe_ticker(array(
            'symbol' => $symbol,
            'timestamp' => $ts,
            'datetime' => $datetime,
            'high' => $high,
            'low' => $low,
            'bid' => $bid,
            'bidVolume' => $bidVolume,
            'ask' => $ask,
            'askVolume' => $askVolume,
            'vwap' => null,
            'open' => $open,
            'close' => $close,
            'last' => null,
            'previousClose' => null,
            'change' => $change,
            'percentage' => $percentage,
            'average' => null,
            'baseVolume' => $baseVolume,
            'quoteVolume' => $quoteVolume,
            'markPrice' => $this->safe_string($ticker, 'markPrice'),
            'indexPrice' => $this->safe_string($ticker, 'indexPrice'),
            'info' => $ticker,
        ), $market);
    }

    public function fetch_balance($params = array ()): array {
        /**
         * query for balance and get the amount of funds available for trading or funds locked in orders
         *
         * @see https://bingx-api.github.io/docs/#/spot/trade-api.html#Query%20Assets
         * @see https://bingx-api.github.io/docs/#/swapV2/account-api.html#Get%20Perpetual%20Swap%20Account%20Asset%20Information
         * @see https://bingx-api.github.io/docs/#/standard/contract-interface.html#Query%20standard%20contract%20balance
         * @see https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#Query%20Account%20Assets
         *
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @param {boolean} [$params->standard] whether to fetch $standard contract balances
         * @param {string} [$params->type] the type of balance to fetch (spot, swap, funding) default is `spot`
         * @return {array} a ~@link https://docs.ccxt.com/#/?id=balance-structure balance structure~
         */
        $this->load_markets();
        $response = null;
        $standard = null;
        list($standard, $params) = $this->handle_option_and_params($params, 'fetchBalance', 'standard', false);
        $subType = null;
        list($subType, $params) = $this->handle_sub_type_and_params('fetchBalance', null, $params);
        list($marketType, $marketTypeQuery) = $this->handle_market_type_and_params('fetchBalance', null, $params);
        if ($standard) {
            $response = $this->contractV1PrivateGetBalance ($marketTypeQuery);
            //
            //     {
            //         "code" => 0,
            //         "timestamp" => 1721192833454,
            //         "data" => array(
            //             array(
            //                 "asset" => "USDT",
            //                 "balance" => "4.72644300000000000000",
            //                 "crossWalletBalance" => "4.72644300000000000000",
            //                 "crossUnPnl" => "0",
            //                 "availableBalance" => "4.72644300000000000000",
            //                 "maxWithdrawAmount" => "4.72644300000000000000",
            //                 "marginAvailable" => false,
            //                 "updateTime" => 1721192833443
            //             ),
            //         )
            //     }
            //
        } elseif (($marketType === 'funding') || ($marketType === 'fund')) {
            $response = $this->fundV1PrivateGetAccountBalance ($marketTypeQuery);
            // {
            //     code => '0',
            //     timestamp => '1754906016631',
            //     data => {
            //         assets => array(
            //             {
            //                 asset => 'USDT',
            //                 free => '44.37692200000000237300',
            //                 locked => '0.00000000000000000000'
            //             }
            //         )
            //     }
            // }
        } elseif ($marketType === 'spot') {
            $response = $this->spotV1PrivateGetAccountBalance ($marketTypeQuery);
            //
            //     {
            //         "code" => 0,
            //         "msg" => "",
            //         "debugMsg" => "",
            //         "data" => {
            //             "balances" => array(
            //                 array(
            //                     "asset" => "USDT",
            //                     "free" => "45.733046995800514",
            //                     "locked" => "0"
            //                 ),
            //             )
            //         }
            //     }
            //
        } else {
            if ($subType === 'inverse') {
                $response = $this->cswapV1PrivateGetUserBalance ($marketTypeQuery);
                //
                //     {
                //         "code" => 0,
                //         "msg" => "",
                //         "timestamp" => 1721191833813,
                //         "data" => array(
                //             {
                //                 "asset" => "SOL",
                //                 "balance" => "0.35707951",
                //                 "equity" => "0.35791051",
                //                 "unrealizedProfit" => "0.00083099",
                //                 "availableMargin" => "0.35160653",
                //                 "usedMargin" => "0.00630397",
                //                 "freezedMargin" => "0",
                //                 "shortUid" => "12851936"
                //             }
                //         )
                //     }
                //
            } else {
                $response = $this->swapV2PrivateGetUserBalance ($marketTypeQuery);
                //
                //     {
                //         "code" => 0,
                //         "msg" => "",
                //         "data" => {
                //             "balance" => {
                //                 "userId" => "1177064765068660742",
                //                 "asset" => "USDT",
                //                 "balance" => "51.5198",
                //                 "equity" => "50.5349",
                //                 "unrealizedProfit" => "-0.9849",
                //                 "realisedProfit" => "-0.2134",
                //                 "availableMargin" => "49.1428",
                //                 "usedMargin" => "1.3922",
                //                 "freezedMargin" => "0.0000",
                //                 "shortUid" => "12851936"
                //             }
                //         }
                //     }
                //
            }
        }
        return $this->parse_balance($response);
    }

    public function parse_balance($response): array {
        //
        // standard
        //
        //     {
        //         "code" => 0,
        //         "timestamp" => 1721192833454,
        //         "data" => array(
        //             array(
        //                 "asset" => "USDT",
        //                 "balance" => "4.72644300000000000000",
        //                 "crossWalletBalance" => "4.72644300000000000000",
        //                 "crossUnPnl" => "0",
        //                 "availableBalance" => "4.72644300000000000000",
        //                 "maxWithdrawAmount" => "4.72644300000000000000",
        //                 "marginAvailable" => false,
        //                 "updateTime" => 1721192833443
        //             ),
        //         )
        //     }
        //
        // spot
        //
        //     {
        //         "code" => 0,
        //         "msg" => "",
        //         "debugMsg" => "",
        //         "data" => {
        //             "balances" => array(
        //                 array(
        //                     "asset" => "USDT",
        //                     "free" => "45.733046995800514",
        //                     "locked" => "0"
        //                 ),
        //             )
        //         }
        //     }
        //
        // inverse swap
        //
        //     {
        //         "code" => 0,
        //         "msg" => "",
        //         "timestamp" => 1721191833813,
        //         "data" => array(
        //             {
        //                 "asset" => "SOL",
        //                 "balance" => "0.35707951",
        //                 "equity" => "0.35791051",
        //                 "unrealizedProfit" => "0.00083099",
        //                 "availableMargin" => "0.35160653",
        //                 "usedMargin" => "0.00630397",
        //                 "freezedMargin" => "0",
        //                 "shortUid" => "12851936"
        //             }
        //         )
        //     }
        //
        // linear swap
        //
        //     {
        //         "code" => 0,
        //         "msg" => "",
        //         "data" => {
        //             "balance" => {
        //                 "userId" => "1177064765068660742",
        //                 "asset" => "USDT",
        //                 "balance" => "51.5198",
        //                 "equity" => "50.5349",
        //                 "unrealizedProfit" => "-0.9849",
        //                 "realisedProfit" => "-0.2134",
        //                 "availableMargin" => "49.1428",
        //                 "usedMargin" => "1.3922",
        //                 "freezedMargin" => "0.0000",
        //                 "shortUid" => "12851936"
        //             }
        //         }
        //     }
        //
        $result = array( 'info' => $response );
        $standardAndInverseBalances = $this->safe_list($response, 'data');
        $firstStandardOrInverse = $this->safe_dict($standardAndInverseBalances, 0);
        $isStandardOrInverse = $firstStandardOrInverse !== null;
        $spotData = $this->safe_dict($response, 'data', array());
        $spotBalances = $this->safe_list_2($spotData, 'balances', 'assets', array());
        $firstSpot = $this->safe_dict($spotBalances, 0);
        $isSpot = $firstSpot !== null;
        if ($isStandardOrInverse) {
            for ($i = 0; $i < count($standardAndInverseBalances); $i++) {
                $balance = $standardAndInverseBalances[$i];
                $currencyId = $this->safe_string($balance, 'asset');
                $code = $this->safe_currency_code($currencyId);
                $account = $this->account();
                $account['free'] = $this->safe_string_2($balance, 'availableMargin', 'availableBalance');
                $account['used'] = $this->safe_string($balance, 'usedMargin');
                $account['total'] = $this->safe_string($balance, 'maxWithdrawAmount');
                $result[$code] = $account;
            }
        } elseif ($isSpot) {
            for ($i = 0; $i < count($spotBalances); $i++) {
                $balance = $spotBalances[$i];
                $currencyId = $this->safe_string($balance, 'asset');
                $code = $this->safe_currency_code($currencyId);
                $account = $this->account();
                $account['free'] = $this->safe_string($balance, 'free');
                $account['used'] = $this->safe_string($balance, 'locked');
                $result[$code] = $account;
            }
        } else {
            $linearSwapData = $this->safe_dict($response, 'data', array());
            $linearSwapBalance = $this->safe_dict($linearSwapData, 'balance');
            if ($linearSwapBalance) {
                $currencyId = $this->safe_string($linearSwapBalance, 'asset');
                $code = $this->safe_currency_code($currencyId);
                $account = $this->account();
                $account['free'] = $this->safe_string($linearSwapBalance, 'availableMargin');
                $account['used'] = $this->safe_string($linearSwapBalance, 'usedMargin');
                $result[$code] = $account;
            }
        }
        return $this->safe_balance($result);
    }

    public function fetch_position_history(string $symbol, ?int $since = null, ?int $limit = null, $params = array ()): array {
        /**
         * fetches historical $positions
         *
         * @see https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Query%20Position%20History
         *
         * @param {string} $symbol unified contract $symbol
         * @param {int} [$since] the earliest time in ms to fetch $positions for
         * @param {int} [$limit] the maximum amount of $records to fetch
         * @param {array} [$params] extra parameters specific to the exchange api endpoint
         * @param {int} [$params->until] the latest time in ms to fetch $positions for
         * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=position-structure position structures~
         */
        $this->load_markets();
        $market = $this->market($symbol);
        $request = array(
            'symbol' => $market['id'],
        );
        if ($limit !== null) {
            $request['pageSize'] = $limit;
        }
        if ($since !== null) {
            $request['startTs'] = $since;
        }
        list($request, $params) = $this->handle_until_option('endTs', $request, $params);
        $response = null;
        if ($market['linear']) {
            $response = $this->swapV1PrivateGetTradePositionHistory ($this->extend($request, $params));
        } else {
            throw new NotSupported($this->id . ' fetchPositionHistory() is not supported for inverse swap positions');
        }
        //
        //     {
        //         "code" => 0,
        //         "msg" => "",
        //         "data" => {
        //             "positionHistory" => array(
        //                 array(
        //                     "positionId" => "1861675561156571136",
        //                     "symbol" => "LTC-USDT",
        //                     "isolated" => false,
        //                     "positionSide" => "LONG",
        //                     "openTime" => 1732693017000,
        //                     "updateTime" => 1733310292000,
        //                     "avgPrice" => "95.18",
        //                     "avgClosePrice" => "129.48",
        //                     "realisedProfit" => "102.89",
        //                     "netProfit" => "99.63",
        //                     "positionAmt" => "30.0",
        //                     "closePositionAmt" => "30.0",
        //                     "leverage" => 6,
        //                     "closeAllPositions" => true,
        //                     "positionCommission" => "-0.33699650000000003",
        //                     "totalFunding" => "-2.921461693902908"
        //                 ),
        //             )
        //         }
        //     }
        //
        $data = $this->safe_dict($response, 'data', array());
        $records = $this->safe_list($data, 'positionHistory', array());
        $positions = $this->parse_positions($records);
        return $this->filter_by_symbol_since_limit($positions, $symbol, $since, $limit);
    }

    public function fetch_positions(?array $symbols = null, $params = array ()): array {
        /**
         * fetch all open $positions
         *
         * @see https://bingx-api.github.io/docs/#/en-us/swapV2/account-api.html#Query%20position%20data
         * @see https://bingx-api.github.io/docs/#/en-us/standard/contract-interface.html#position
         * @see https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#Query%20warehouse
         *
         * @param {string[]|null} $symbols list of unified $market $symbols
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @param {boolean} [$params->standard] whether to fetch $standard contract $positions
         * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=position-structure position structures~
         */
        $this->load_markets();
        $symbols = $this->market_symbols($symbols);
        $standard = null;
        list($standard, $params) = $this->handle_option_and_params($params, 'fetchPositions', 'standard', false);
        $response = null;
        if ($standard) {
            $response = $this->contractV1PrivateGetAllPosition ($params);
        } else {
            $market = null;
            if ($symbols !== null) {
                $symbols = $this->market_symbols($symbols);
                $firstSymbol = $this->safe_string($symbols, 0);
                if ($firstSymbol !== null) {
                    $market = $this->market($firstSymbol);
                }
            }
            $subType = null;
            list($subType, $params) = $this->handle_sub_type_and_params('fetchPositions', $market, $params);
            if ($subType === 'inverse') {
                $response = $this->cswapV1PrivateGetUserPositions ($params);
                //
                //     {
                //         "code" => 0,
                //         "msg" => "",
                //         "timestamp" => 0,
                //         "data" => array(
                //             {
                //                 "symbol" => "SOL-USD",
                //                 "positionId" => "1813080351385337856",
                //                 "positionSide" => "LONG",
                //                 "isolated" => false,
                //                 "positionAmt" => "1",
                //                 "availableAmt" => "1",
                //                 "unrealizedProfit" => "-0.00009074",
                //                 "initialMargin" => "0.00630398",
                //                 "liquidationPrice" => 23.968303426677032,
                //                 "avgPrice" => "158.63",
                //                 "leverage" => 10,
                //                 "markPrice" => "158.402",
                //                 "riskRate" => "0.00123783",
                //                 "maxMarginReduction" => "0",
                //                 "updateTime" => 1721107015848
                //             }
                //         )
                //     }
                //
            } else {
                $response = $this->swapV2PrivateGetUserPositions ($params);
                //
                //     {
                //         "code" => 0,
                //         "msg" => "",
                //         "data" => array(
                //             {
                //                 "positionId" => "1792480725958881280",
                //                 "symbol" => "LTC-USDT",
                //                 "currency" => "USDT",
                //                 "positionAmt" => "0.1",
                //                 "availableAmt" => "0.1",
                //                 "positionSide" => "LONG",
                //                 "isolated" => false,
                //                 "avgPrice" => "83.53",
                //                 "initialMargin" => "1.3922",
                //                 "margin" => "0.3528",
                //                 "leverage" => 6,
                //                 "unrealizedProfit" => "-1.0393",
                //                 "realisedProfit" => "-0.2119",
                //                 "liquidationPrice" => 0,
                //                 "pnlRatio" => "-0.7465",
                //                 "maxMarginReduction" => "0.0000",
                //                 "riskRate" => "0.0008",
                //                 "markPrice" => "73.14",
                //                 "positionValue" => "7.3136",
                //                 "onlyOnePosition" => true,
                //                 "updateTime" => 1721088016688
                //             }
                //         )
                //     }
                //
            }
        }
        $positions = $this->safe_list($response, 'data', array());
        return $this->parse_positions($positions, $symbols);
    }

    public function fetch_position(string $symbol, $params = array ()) {
        /**
         * fetch $data on a single open contract trade position
         *
         * @see https://bingx-api.github.io/docs/#/en-us/swapV2/account-api.html#Query%20position%20data
         * @see https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#Query%20warehouse
         *
         * @param {string} $symbol unified $market $symbol of the $market the position is held in
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array} a ~@link https://docs.ccxt.com/#/?id=position-structure position structure~
         */
        $this->load_markets();
        $market = $this->market($symbol);
        if (!$market['swap']) {
            throw new BadRequest($this->id . ' fetchPosition() supports swap markets only');
        }
        $request = array(
            'symbol' => $market['id'],
        );
        $response = null;
        if ($market['inverse']) {
            $response = $this->cswapV1PrivateGetUserPositions ($this->extend($request, $params));
            //
            //     {
            //         "code" => 0,
            //         "msg" => "",
            //         "timestamp" => 0,
            //         "data" => array(
            //             {
            //                 "symbol" => "SOL-USD",
            //                 "positionId" => "1813080351385337856",
            //                 "positionSide" => "LONG",
            //                 "isolated" => false,
            //                 "positionAmt" => "1",
            //                 "availableAmt" => "1",
            //                 "unrealizedProfit" => "-0.00009074",
            //                 "initialMargin" => "0.00630398",
            //                 "liquidationPrice" => 23.968303426677032,
            //                 "avgPrice" => "158.63",
            //                 "leverage" => 10,
            //                 "markPrice" => "158.402",
            //                 "riskRate" => "0.00123783",
            //                 "maxMarginReduction" => "0",
            //                 "updateTime" => 1721107015848
            //             }
            //         )
            //     }
            //
        } else {
            $response = $this->swapV2PrivateGetUserPositions ($this->extend($request, $params));
            //
            //     {
            //         "code" => 0,
            //         "msg" => "",
            //         "data" => array(
            //             {
            //                 "positionId" => "1792480725958881280",
            //                 "symbol" => "LTC-USDT",
            //                 "currency" => "USDT",
            //                 "positionAmt" => "0.1",
            //                 "availableAmt" => "0.1",
            //                 "positionSide" => "LONG",
            //                 "isolated" => false,
            //                 "avgPrice" => "83.53",
            //                 "initialMargin" => "1.3922",
            //                 "margin" => "0.3528",
            //                 "leverage" => 6,
            //                 "unrealizedProfit" => "-1.0393",
            //                 "realisedProfit" => "-0.2119",
            //                 "liquidationPrice" => 0,
            //                 "pnlRatio" => "-0.7465",
            //                 "maxMarginReduction" => "0.0000",
            //                 "riskRate" => "0.0008",
            //                 "markPrice" => "73.14",
            //                 "positionValue" => "7.3136",
            //                 "onlyOnePosition" => true,
            //                 "updateTime" => 1721088016688
            //             }
            //         )
            //     }
            //
        }
        $data = $this->safe_list($response, 'data', array());
        $first = $this->safe_dict($data, 0, array());
        return $this->parse_position($first, $market);
    }

    public function parse_position(array $position, ?array $market = null) {
        //
        // inverse swap
        //
        //     {
        //         "symbol" => "SOL-USD",
        //         "positionId" => "1813080351385337856",
        //         "positionSide" => "LONG",
        //         "isolated" => false,
        //         "positionAmt" => "1",
        //         "availableAmt" => "1",
        //         "unrealizedProfit" => "-0.00009074",
        //         "initialMargin" => "0.00630398",
        //         "liquidationPrice" => 23.968303426677032,
        //         "avgPrice" => "158.63",
        //         "leverage" => 10,
        //         "markPrice" => "158.402",
        //         "riskRate" => "0.00123783",
        //         "maxMarginReduction" => "0",
        //         "updateTime" => 1721107015848
        //     }
        //
        // linear swap
        //
        //     {
        //         "positionId" => "1792480725958881280",
        //         "symbol" => "LTC-USDT",
        //         "currency" => "USDT",
        //         "positionAmt" => "0.1",
        //         "availableAmt" => "0.1",
        //         "positionSide" => "LONG",
        //         "isolated" => false,
        //         "avgPrice" => "83.53",
        //         "initialMargin" => "1.3922",
        //         "margin" => "0.3528",
        //         "leverage" => 6,
        //         "unrealizedProfit" => "-1.0393",
        //         "realisedProfit" => "-0.2119",
        //         "liquidationPrice" => 0,
        //         "pnlRatio" => "-0.7465",
        //         "maxMarginReduction" => "0.0000",
        //         "riskRate" => "0.0008",
        //         "markPrice" => "73.14",
        //         "positionValue" => "7.3136",
        //         "onlyOnePosition" => true,
        //         "updateTime" => 1721088016688
        //     }
        //
        // standard $position
        //
        //     {
        //         "currentPrice" => "82.91",
        //         "symbol" => "LTC/USDT",
        //         "initialMargin" => "5.00000000000000000000",
        //         "unrealizedProfit" => "-0.26464500",
        //         "leverage" => "20.000000000",
        //         "isolated" => true,
        //         "entryPrice" => "83.13",
        //         "positionSide" => "LONG",
        //         "positionAmt" => "1.20365912",
        //     }
        //
        // linear swap fetchPositionHistory
        //
        //     {
        //         "positionId" => "1861675561156571136",
        //         "symbol" => "LTC-USDT",
        //         "isolated" => false,
        //         "positionSide" => "LONG",
        //         "openTime" => 1732693017000,
        //         "updateTime" => 1733310292000,
        //         "avgPrice" => "95.18",
        //         "avgClosePrice" => "129.48",
        //         "realisedProfit" => "102.89",
        //         "netProfit" => "99.63",
        //         "positionAmt" => "30.0",
        //         "closePositionAmt" => "30.0",
        //         "leverage" => 6,
        //         "closeAllPositions" => true,
        //         "positionCommission" => "-0.33699650000000003",
        //         "totalFunding" => "-2.921461693902908"
        //     }
        //
        $marketId = $this->safe_string($position, 'symbol', '');
        $marketId = str_replace('/', '-', $marketId); // standard return different format
        $isolated = $this->safe_bool($position, 'isolated');
        $marginMode = null;
        if ($isolated !== null) {
            $marginMode = $isolated ? 'isolated' : 'cross';
        }
        $timestamp = $this->safe_integer($position, 'openTime');
        return $this->safe_position(array(
            'info' => $position,
            'id' => $this->safe_string($position, 'positionId'),
            'symbol' => $this->safe_symbol($marketId, $market, '-', 'swap'),
            'notional' => $this->safe_number($position, 'positionValue'),
            'marginMode' => $marginMode,
            'liquidationPrice' => null,
            'entryPrice' => $this->safe_number_2($position, 'avgPrice', 'entryPrice'),
            'unrealizedPnl' => $this->safe_number($position, 'unrealizedProfit'),
            'realizedPnl' => $this->safe_number($position, 'realisedProfit'),
            'percentage' => null,
            'contracts' => $this->safe_number($position, 'positionAmt'),
            'contractSize' => null,
            'markPrice' => $this->safe_number($position, 'markPrice'),
            'lastPrice' => null,
            'side' => $this->safe_string_lower($position, 'positionSide'),
            'hedged' => null,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'lastUpdateTimestamp' => $this->safe_integer($position, 'updateTime'),
            'maintenanceMargin' => null,
            'maintenanceMarginPercentage' => null,
            'collateral' => null,
            'initialMargin' => $this->safe_number($position, 'initialMargin'),
            'initialMarginPercentage' => null,
            'leverage' => $this->safe_number($position, 'leverage'),
            'marginRatio' => null,
            'stopLossPrice' => null,
            'takeProfitPrice' => null,
        ));
    }

    public function create_market_order_with_cost(string $symbol, string $side, float $cost, $params = array ()) {
        /**
         * create a market order by providing the $symbol, $side and $cost
         * @param {string} $symbol unified $symbol of the market to create an order in
         * @param {string} $side 'buy' or 'sell'
         * @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~
         */
        $params['quoteOrderQty'] = $cost;
        return $this->create_order($symbol, 'market', $side, $cost, null, $params);
    }

    public function create_market_buy_order_with_cost(string $symbol, float $cost, $params = array ()) {
        /**
         * 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~
         */
        $params['quoteOrderQty'] = $cost;
        return $this->create_order($symbol, 'market', 'buy', $cost, null, $params);
    }

    public function create_market_sell_order_with_cost(string $symbol, float $cost, $params = array ()) {
        /**
         * create a market sell 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~
         */
        $params['quoteOrderQty'] = $cost;
        return $this->create_order($symbol, 'market', 'sell', $cost, null, $params);
    }

    public function create_order_request(string $symbol, string $type, string $side, float $amount, ?float $price = null, $params = array ()) {
        /**
         * @ignore
         * helper function to build $request
         * @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 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
         * @return {array} $request to be sent to the exchange
         */
        $market = $this->market($symbol);
        $postOnly = null;
        $marketType = null;
        list($marketType, $params) = $this->handle_market_type_and_params('createOrder', $market, $params);
        $type = strtoupper($type);
        $request = array(
            'symbol' => $market['id'],
            'type' => $type,
            'side' => strtoupper($side),
        );
        $isMarketOrder = $type === 'MARKET';
        $isSpot = $marketType === 'spot';
        $isTwapOrder = $type === 'TWAP';
        if ($isTwapOrder && $isSpot) {
            throw new BadSymbol($this->id . ' createOrder() twap order supports swap contracts only');
        }
        $stopLossPrice = $this->safe_string($params, 'stopLossPrice');
        $takeProfitPrice = $this->safe_string($params, 'takeProfitPrice');
        $triggerPrice = $this->safe_string_2($params, 'stopPrice', 'triggerPrice');
        $isTriggerOrder = $triggerPrice !== null;
        $isStopLossPriceOrder = $stopLossPrice !== null;
        $isTakeProfitPriceOrder = $takeProfitPrice !== null;
        $exchangeClientOrderId = $isSpot ? 'newClientOrderId' : 'clientOrderID';
        $clientOrderId = $this->safe_string_2($params, $exchangeClientOrderId, 'clientOrderId');
        if ($clientOrderId !== null) {
            $request[$exchangeClientOrderId] = $clientOrderId;
        }
        $timeInForce = $this->safe_string_upper($params, 'timeInForce');
        list($postOnly, $params) = $this->handle_post_only($isMarketOrder, $timeInForce === 'PostOnly', $params);
        if ($postOnly || ($timeInForce === 'PostOnly')) {
            $request['timeInForce'] = 'PostOnly';
        } elseif ($timeInForce === 'IOC') {
            $request['timeInForce'] = 'IOC';
        } elseif ($timeInForce === 'GTC') {
            $request['timeInForce'] = 'GTC';
        }
        if ($isSpot) {
            $cost = $this->safe_string_2($params, 'cost', 'quoteOrderQty');
            $params = $this->omit($params, 'cost');
            if ($cost !== null) {
                $request['quoteOrderQty'] = $this->parse_to_numeric($this->cost_to_precision($symbol, $cost));
            } else {
                if ($isMarketOrder && ($price !== null)) {
                    // keep the legacy behavior, to avoid  breaking the old spot-$market-buying code
                    $calculatedCost = Precise::string_mul($this->number_to_string($amount), $this->number_to_string($price));
                    $request['quoteOrderQty'] = $this->parse_to_numeric($calculatedCost);
                } else {
                    $request['quantity'] = $this->parse_to_numeric($this->amount_to_precision($symbol, $amount));
                }
            }
            if (!$isMarketOrder) {
                $request['price'] = $this->parse_to_numeric($this->price_to_precision($symbol, $price));
            }
            if ($triggerPrice !== null) {
                if ($isMarketOrder && $this->safe_string($request, 'quoteOrderQty') === null) {
                    throw new ArgumentsRequired($this->id . ' createOrder() requires the $cost parameter (or the $amount . $price) for placing spot $market-buy trigger orders');
                }
                $request['stopPrice'] = $this->price_to_precision($symbol, $triggerPrice);
                if ($type === 'LIMIT') {
                    $request['type'] = 'TRIGGER_LIMIT';
                } elseif ($type === 'MARKET') {
                    $request['type'] = 'TRIGGER_MARKET';
                }
            } elseif (($stopLossPrice !== null) || ($takeProfitPrice !== null)) {
                $stopTakePrice = ($stopLossPrice !== null) ? $stopLossPrice : $takeProfitPrice;
                if ($type === 'LIMIT') {
                    $request['type'] = 'TAKE_STOP_LIMIT';
                } elseif ($type === 'MARKET') {
                    $request['type'] = 'TAKE_STOP_MARKET';
                }
                $request['stopPrice'] = $this->parse_to_numeric($this->price_to_precision($symbol, $stopTakePrice));
            }
        } else {
            if ($isTwapOrder) {
                $twapRequest = array(
                    'symbol' => $request['symbol'],
                    'side' => $request['side'],
                    'positionSide' => ($side === 'buy') ? 'LONG' : 'SHORT',
                    'triggerPrice' => $this->parse_to_numeric($this->price_to_precision($symbol, $triggerPrice)),
                    'totalAmount' => $this->parse_to_numeric($this->amount_to_precision($symbol, $amount)),
                );
                //     {
                //         "symbol" => "LTC-USDT",
                //         "side" => "BUY",
                //         "positionSide" => "LONG",
                //         "priceType" => "constant",
                //         "priceVariance" => "10",
                //         "triggerPrice" => "120",
                //         "interval" => 8,
                //         "amountPerOrder" => "0.5",
                //         "totalAmount" => "1"
                //     }
                return $this->extend($twapRequest, $params);
            }
            if ($timeInForce === 'FOK') {
                $request['timeInForce'] = 'FOK';
            }
            $trailingAmount = $this->safe_string($params, 'trailingAmount');
            $trailingPercent = $this->safe_string_2($params, 'trailingPercent', 'priceRate');
            $trailingType = $this->safe_string($params, 'trailingType', 'TRAILING_STOP_MARKET');
            $isTrailingAmountOrder = $trailingAmount !== null;
            $isTrailingPercentOrder = $trailingPercent !== null;
            $isTrailing = $isTrailingAmountOrder || $isTrailingPercentOrder;
            $stopLoss = $this->safe_value($params, 'stopLoss');
            $takeProfit = $this->safe_value($params, 'takeProfit');
            $isStopLoss = $stopLoss !== null;
            $isTakeProfit = $takeProfit !== null;
            if ((($type === 'LIMIT') || ($type === 'TRIGGER_LIMIT') || ($type === 'STOP') || ($type === 'TAKE_PROFIT')) && !$isTrailing) {
                $request['price'] = $this->parse_to_numeric($this->price_to_precision($symbol, $price));
            }
            $reduceOnly = $this->safe_bool($params, 'reduceOnly', false);
            if ($isTriggerOrder) {
                $request['stopPrice'] = $this->parse_to_numeric($this->price_to_precision($symbol, $triggerPrice));
                if ($isMarketOrder || ($type === 'TRIGGER_MARKET')) {
                    $request['type'] = 'TRIGGER_MARKET';
                } elseif (($type === 'LIMIT') || ($type === 'TRIGGER_LIMIT')) {
                    $request['type'] = 'TRIGGER_LIMIT';
                }
            } elseif ($isStopLossPriceOrder || $isTakeProfitPriceOrder) {
                // This can be used to set the stop loss and take profit, but the position needs to be opened first
                $reduceOnly = true;
                if ($isStopLossPriceOrder) {
                    $request['stopPrice'] = $this->parse_to_numeric($this->price_to_precision($symbol, $stopLossPrice));
                    if ($isMarketOrder || ($type === 'STOP_MARKET')) {
                        $request['type'] = 'STOP_MARKET';
                    } elseif (($type === 'LIMIT') || ($type === 'STOP')) {
                        $request['type'] = 'STOP';
                    }
                } elseif ($isTakeProfitPriceOrder) {
                    $request['stopPrice'] = $this->parse_to_numeric($this->price_to_precision($symbol, $takeProfitPrice));
                    if ($isMarketOrder || ($type === 'TAKE_PROFIT_MARKET')) {
                        $request['type'] = 'TAKE_PROFIT_MARKET';
                    } elseif (($type === 'LIMIT') || ($type === 'TAKE_PROFIT')) {
                        $request['type'] = 'TAKE_PROFIT';
                    }
                }
            } elseif ($isTrailing) {
                $request['type'] = $trailingType;
                if ($isTrailingAmountOrder) {
                    $request['price'] = $this->parse_to_numeric($trailingAmount);
                } elseif ($isTrailingPercentOrder) {
                    $requestTrailingPercent = Precise::string_div($trailingPercent, '100');
                    $request['priceRate'] = $this->parse_to_numeric($requestTrailingPercent);
                }
            }
            if ($isStopLoss || $isTakeProfit) {
                $stringifiedAmount = $this->number_to_string($amount);
                if ($isStopLoss) {
                    $slTriggerPrice = $this->safe_string_2($stopLoss, 'triggerPrice', 'stopPrice', $stopLoss);
                    $slWorkingType = $this->safe_string($stopLoss, 'workingType', 'MARK_PRICE');
                    $slType = $this->safe_string($stopLoss, 'type', 'STOP_MARKET');
                    $slRequest = array(
                        'stopPrice' => $this->parse_to_numeric($this->price_to_precision($symbol, $slTriggerPrice)),
                        'workingType' => $slWorkingType,
                        'type' => $slType,
                    );
                    $slPrice = $this->safe_string($stopLoss, 'price');
                    if ($slPrice !== null) {
                        $slRequest['price'] = $this->parse_to_numeric($this->price_to_precision($symbol, $slPrice));
                    }
                    $slQuantity = $this->safe_string($stopLoss, 'quantity', $stringifiedAmount);
                    $slRequest['quantity'] = $this->parse_to_numeric($this->amount_to_precision($symbol, $slQuantity));
                    $request['stopLoss'] = $this->json($slRequest);
                }
                if ($isTakeProfit) {
                    $tkTriggerPrice = $this->safe_string_2($takeProfit, 'triggerPrice', 'stopPrice', $takeProfit);
                    $tkWorkingType = $this->safe_string($takeProfit, 'workingType', 'MARK_PRICE');
                    $tpType = $this->safe_string($takeProfit, 'type', 'TAKE_PROFIT_MARKET');
                    $tpRequest = array(
                        'stopPrice' => $this->parse_to_numeric($this->price_to_precision($symbol, $tkTriggerPrice)),
                        'workingType' => $tkWorkingType,
                        'type' => $tpType,
                    );
                    $slPrice = $this->safe_string($takeProfit, 'price');
                    if ($slPrice !== null) {
                        $tpRequest['price'] = $this->parse_to_numeric($this->price_to_precision($symbol, $slPrice));
                    }
                    $tkQuantity = $this->safe_string($takeProfit, 'quantity', $stringifiedAmount);
                    $tpRequest['quantity'] = $this->parse_to_numeric($this->amount_to_precision($symbol, $tkQuantity));
                    $request['takeProfit'] = $this->json($tpRequest);
                }
            }
            $positionSide = null;
            $hedged = $this->safe_bool($params, 'hedged', false);
            if ($hedged) {
                $params = $this->omit($params, 'reduceOnly');
                if ($reduceOnly) {
                    $positionSide = ($side === 'buy') ? 'SHORT' : 'LONG';
                } else {
                    $positionSide = ($side === 'buy') ? 'LONG' : 'SHORT';
                }
            } else {
                $positionSide = 'BOTH';
            }
            $request['positionSide'] = $positionSide;
            $amountReq = $amount;
            if (!$market['inverse']) {
                $amountReq = $this->parse_to_numeric($this->amount_to_precision($symbol, $amount));
            }
            $request['quantity'] = $amountReq; // precision not available for inverse contracts
        }
        $params = $this->omit($params, array( 'hedged', 'triggerPrice', 'stopLossPrice', 'takeProfitPrice', 'trailingAmount', 'trailingPercent', 'trailingType', 'takeProfit', 'stopLoss', 'clientOrderId' ));
        return $this->extend($request, $params);
    }

    public function create_order(string $symbol, string $type, string $side, float $amount, ?float $price = null, $params = array ()) {
        /**
         * create a trade order
         *
         * @see https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Trade%20order
         * @see https://bingx-api.github.io/docs/#/en-us/spot/trade-api.html#Create%20an%20Order
         * @see https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#Trade%20order
         * @see https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Place%20TWAP%20Order
         *
         * @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 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 {string} [$params->clientOrderId] a unique id for the order
         * @param {bool} [$params->postOnly] true to place a post only order
         * @param {string} [$params->timeInForce] spot supports 'PO', 'GTC' and 'IOC', swap supports 'PO', 'GTC', 'IOC' and 'FOK'
         * @param {bool} [$params->reduceOnly] *swap only* true or false whether the order is reduce only
         * @param {float} [$params->triggerPrice] triggerPrice at which the attached take profit / stop loss order will be triggered
         * @param {float} [$params->stopLossPrice] stop loss trigger $price
         * @param {float} [$params->takeProfitPrice] take profit trigger $price
         * @param {float} [$params->cost] the quote quantity that can be used alternative for the $amount
         * @param {float} [$params->trailingAmount] *swap only* the quote $amount to trail away from the current $market $price
         * @param {float} [$params->trailingPercent] *swap only* the percent to trail away from the current $market $price
         * @param {array} [$params->takeProfit] *takeProfit object in $params* containing the triggerPrice at which the attached take profit order will be triggered
         * @param {float} [$params->takeProfit.triggerPrice] take profit trigger $price
         * @param {array} [$params->stopLoss] *stopLoss object in $params* containing the triggerPrice at which the attached stop loss order will be triggered
         * @param {float} [$params->stopLoss.triggerPrice] stop loss trigger $price
         * @param {boolean} [$params->test] *swap only* whether to use the $test endpoint or not, default is false
         * @param {string} [$params->positionSide] *contracts only* "BOTH" for one way mode, "LONG" for buy $side of hedged mode, "SHORT" for sell $side of hedged mode
         * @param {boolean} [$params->hedged] *swap only* whether the order is in hedged mode or one way mode
         * @return {array} an ~@link https://docs.ccxt.com/#/?id=order-structure order structure~
         */
        $this->load_markets();
        $market = $this->market($symbol);
        $test = $this->safe_bool($params, 'test', false);
        $params = $this->omit($params, 'test');
        $request = $this->create_order_request($symbol, $type, $side, $amount, $price, $params);
        $response = null;
        if ($market['swap']) {
            if ($test) {
                $response = $this->swapV2PrivatePostTradeOrderTest ($request);
            } elseif ($market['inverse']) {
                $response = $this->cswapV1PrivatePostTradeOrder ($request);
            } elseif ($type === 'twap') {
                $response = $this->swapV1PrivatePostTwapOrder ($request);
            } else {
                $response = $this->swapV2PrivatePostTradeOrder ($request);
            }
        } else {
            $response = $this->spotV1PrivatePostTradeOrder ($request);
        }
        //
        // spot
        //
        //    {
        //        "code" => 0,
        //        "msg" => "",
        //        "data" => {
        //            "symbol" => "XRP-USDT",
        //            "orderId" => 1514090846268424192,
        //            "transactTime" => 1649822362855,
        //            "price" => "0.5",
        //            "origQty" => "10",
        //            "executedQty" => "0",
        //            "cummulativeQuoteQty" => "0",
        //            "status" => "PENDING",
        //            "type" => "LIMIT",
        //            "side" => "BUY"
        //        }
        //    }
        //
        // linear swap
        //
        //     {
        //         "code" => 0,
        //         "msg" => "",
        //         "data" => {
        //             "order" => {
        //                 "symbol" => "BTC-USDT",
        //                 "orderId" => 1709036527545438208,
        //                 "side" => "BUY",
        //                 "positionSide" => "LONG",
        //                 "type" => "TRIGGER_LIMIT",
        //                 "clientOrderID" => "",
        //                 "workingType" => ""
        //             }
        //         }
        //     }
        //
        // inverse swap
        //
        //     {
        //         "orderId" => 1809841379603398656,
        //         "symbol" => "SOL-USD",
        //         "positionSide" => "LONG",
        //         "side" => "BUY",
        //         "type" => "LIMIT",
        //         "price" => 100,
        //         "quantity" => 1,
        //         "stopPrice" => 0,
        //         "workingType" => "",
        //         "timeInForce" => ""
        //     }
        //
        // twap order
        //
        //     {
        //         "code" => 0,
        //         "msg" => "",
        //         "timestamp" => 1732693774386,
        //         "data" => {
        //             "mainOrderId" => "4633860139993029715"
        //         }
        //     }
        //
        if (gettype($response) === 'string') {
            // broken api engine : order-ids are too long numbers (i.e. 1742930526912864656)
            // and JSON.parse can not handle them in JS, so we have to use .parseJson
            // however, when order has an attached SL/TP, their value types need extra parsing
            $response = $this->fix_stringified_json_members($response);
            $response = $this->parse_json($response);
        }
        $data = $this->safe_dict($response, 'data', array());
        $result = array();
        if ($market['swap']) {
            if ($market['inverse']) {
                $result = $response;
            } else {
                $result = $this->safe_dict($data, 'order', $data);
            }
        } else {
            $result = $data;
        }
        return $this->parse_order($result, $market);
    }

    public function create_orders(array $orders, $params = array ()) {
        /**
         * create a list of trade $orders
         *
         * @see https://bingx-api.github.io/docs/#/spot/trade-api.html#Batch%20Placing%20Orders
         * @see https://bingx-api.github.io/docs/#/swapV2/trade-api.html#Bulk%20order
         *
         * @param {Array} $orders list of $orders to create, each object should contain the parameters required by createOrder, namely symbol, $type, $side, $amount, $price and $params
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @param {boolean} [$params->sync] *spot only* if true, multiple $orders are ordered serially and all $orders do not require the same symbol/side/type
         * @return {array} an ~@link https://docs.ccxt.com/#/?id=order-structure order structure~
         */
        $this->load_markets();
        $ordersRequests = array();
        $marketIds = array();
        for ($i = 0; $i < count($orders); $i++) {
            $rawOrder = $orders[$i];
            $marketId = $this->safe_string($rawOrder, 'symbol');
            $type = $this->safe_string($rawOrder, 'type');
            $marketIds[] = $marketId;
            $side = $this->safe_string($rawOrder, 'side');
            $amount = $this->safe_number($rawOrder, 'amount');
            $price = $this->safe_number($rawOrder, 'price');
            $orderParams = $this->safe_dict($rawOrder, 'params', array());
            $orderRequest = $this->create_order_request($marketId, $type, $side, $amount, $price, $orderParams);
            $ordersRequests[] = $orderRequest;
        }
        $symbols = $this->market_symbols($marketIds, null, false, true, true);
        $symbolsLength = count($symbols);
        $market = $this->market($symbols[0]);
        $request = array();
        $response = null;
        if ($market['swap']) {
            if ($symbolsLength > 5) {
                throw new InvalidOrder($this->id . ' createOrders() can not create more than 5 $orders at once for swap markets');
            }
            $request['batchOrders'] = $this->json($ordersRequests);
            $response = $this->swapV2PrivatePostTradeBatchOrders ($request);
        } else {
            $sync = $this->safe_bool($params, 'sync', false);
            if ($sync) {
                $request['sync'] = true;
            }
            $request['data'] = $this->json($ordersRequests);
            $response = $this->spotV1PrivatePostTradeBatchOrders ($request);
        }
        //
        // spot
        //
        //     {
        //         "code" => 0,
        //         "msg" => "",
        //         "debugMsg" => "",
        //         "data" => {
        //             "orders" => array(
        //                 array(
        //                     "symbol" => "BTC-USDT",
        //                     "orderId" => 1720661389564968960,
        //                     "transactTime" => 1699072618272,
        //                     "price" => "25000",
        //                     "origQty" => "0.0002",
        //                     "executedQty" => "0",
        //                     "cummulativeQuoteQty" => "0",
        //                     "status" => "PENDING",
        //                     "type" => "LIMIT",
        //                     "side" => "BUY"
        //                 ),
        //             )
        //         }
        //     }
        //
        // swap
        //
        //     {
        //         "code" => 0,
        //         "msg" => "",
        //         "data" => {
        //             "orders" => array(
        //                 array(
        //                     "symbol" => "BTC-USDT",
        //                     "orderId" => 1720657081994006528,
        //                     "side" => "BUY",
        //                     "positionSide" => "LONG",
        //                     "type" => "LIMIT",
        //                     "clientOrderID" => "",
        //                     "workingType" => ""
        //                 ),
        //             )
        //         }
        //     }
        //
        if (gettype($response) === 'string') {
            // broken api engine : order-ids are too long numbers ($i->e. 1742930526912864656)
            // and JSON.parse can not handle them in JS, so we have to use .parseJson
            // however, when order has an attached SL/TP, their value types need extra parsing
            $response = $this->fix_stringified_json_members($response);
            $response = $this->parse_json($response);
        }
        $data = $this->safe_dict($response, 'data', array());
        $result = $this->safe_list($data, 'orders', array());
        return $this->parse_orders($result, $market);
    }

    public function parse_order_side($side) {
        $sides = array(
            'BUY' => 'buy',
            'SELL' => 'sell',
            'SHORT' => 'sell',
            'LONG' => 'buy',
            'ask' => 'sell',
            'bid' => 'buy',
        );
        return $this->safe_string($sides, $side, $side);
    }

    public function parse_order_type(?string $type) {
        $types = array(
            'trigger_market' => 'market',
            'trigger_limit' => 'limit',
            'stop_limit' => 'limit',
            'stop_market' => 'market',
            'take_profit_market' => 'market',
            'stop' => 'limit',
        );
        return $this->safe_string($types, $type, $type);
    }

    public function parse_order(array $order, ?array $market = null): array {
        //
        // spot
        // createOrder, createOrders, cancelOrder
        //
        //    {
        //        "symbol" => "XRP-USDT",
        //        "orderId" => 1514090846268424192,
        //        "transactTime" => 1649822362855,
        //        "price" => "0.5",
        //        "origQty" => "10",
        //        "executedQty" => "0",
        //        "cummulativeQuoteQty" => "0",
        //        "status" => "PENDING",
        //        "type" => "LIMIT",
        //        "side" => "BUY"
        //    }
        //
        // fetchOrder
        //
        //    {
        //        "symbol" => "ETH-USDT",
        //        "orderId" => "1660602123001266176",
        //        "price" => "1700",
        //        "origQty" => "0.003",
        //        "executedQty" => "0",
        //        "cummulativeQuoteQty" => "0",
        //        "status" => "PENDING",
        //        "type" => "LIMIT",
        //        "side" => "BUY",
        //        "time" => "1684753373276",
        //        "updateTime" => "1684753373276",
        //        "origQuoteOrderQty" => "0",
        //        "fee" => "0",
        //        "feeAsset" => "ETH"
        //    }
        //
        // fetchOpenOrders, fetchClosedOrders
        //
        //   {
        //       "symbol" => "XRP-USDT",
        //       "orderId" => 1514073325788200960,
        //       "price" => "0.5",
        //       "StopPrice" => "0",
        //       "origQty" => "20",
        //       "executedQty" => "10",
        //       "cummulativeQuoteQty" => "5",
        //       "status" => "PENDING",
        //       "type" => "LIMIT",
        //       "side" => "BUY",
        //       "time" => 1649818185647,
        //       "updateTime" => 1649818185647,
        //       "origQuoteOrderQty" => "0"
        //       "fee" => "-0.01"
        //   }
        //
        //
        // linear swap
        // createOrder, createOrders
        //
        //    {
        //      "symbol" => "BTC-USDT",
        //      "orderId" => 1590973236294713344,
        //      "side" => "BUY",
        //      "positionSide" => "LONG",
        //      "type" => "LIMIT"
        //    }
        //
        // inverse swap createOrder
        //
        //     {
        //         "orderId" => 1809841379603398656,
        //         "symbol" => "SOL-USD",
        //         "positionSide" => "LONG",
        //         "side" => "BUY",
        //         "type" => "LIMIT",
        //         "price" => 100,
        //         "quantity" => 1,
        //         "stopPrice" => 0,
        //         "workingType" => "",
        //         "timeInForce" => ""
        //     }
        //
        // fetchOrder, fetchOpenOrders, fetchClosedOrders
        //
        //     {
        //         "symbol" => "BTC-USDT",
        //         "orderId" => 1709036527545438208,
        //         "side" => "BUY",
        //         "positionSide" => "LONG",
        //         "type" => "TRIGGER_LIMIT",
        //         "origQty" => "0.0010",
        //         "price" => "22000.0",
        //         "executedQty" => "0.0000",
        //         "avgPrice" => "0.0",
        //         "cumQuote" => "",
        //         "stopPrice" => "23000.0",
        //         "profit" => "",
        //         "commission" => "",
        //         "status" => "NEW",
        //         "time" => 1696301035187,
        //         "updateTime" => 1696301035187,
        //         "clientOrderId" => "",
        //         "leverage" => "",
        //         "takeProfit" => "",
        //         "stopLoss" => "",
        //         "advanceAttr" => 0,
        //         "positionID" => 0,
        //         "takeProfitEntrustPrice" => 0,
        //         "stopLossEntrustPrice" => 0,
        //         "orderType" => "",
        //         "workingType" => "MARK_PRICE"
        //     }
        // with tp and sl
        //    {
        //        orderId => 1741440894764281900,
        //        symbol => 'LTC-USDT',
        //        $positionSide => 'LONG',
        //        $side => 'BUY',
        //        type => 'MARKET',
        //        price => 0,
        //        quantity => 1,
        //        $stopPrice => 0,
        //        workingType => 'MARK_PRICE',
        //        clientOrderID => '',
        //        timeInForce => 'GTC',
        //        priceRate => 0,
        //        $stopLoss => 'array("stopPrice":50,"workingType":"MARK_PRICE","type":"STOP_MARKET","quantity":1)',
        //        $takeProfit => 'array("stopPrice":150,"workingType":"MARK_PRICE","type":"TAKE_PROFIT_MARKET","quantity":1)',
        //        reduceOnly => false
        //    }
        //
        // editOrder (swap)
        //
        //    {
        //        cancelResult => 'true',
        //        cancelMsg => '',
        //        cancelResponse => array(
        //            cancelClientOrderId => '',
        //            cancelOrderId => '1755336244265705472',
        //            symbol => 'SOL-USDT',
        //            orderId => '1755336244265705472',
        //            $side => 'SELL',
        //            $positionSide => 'SHORT',
        //            type => 'LIMIT',
        //            origQty => '1',
        //            price => '100.000',
        //            executedQty => '0',
        //            avgPrice => '0.000',
        //            cumQuote => '0',
        //            $stopPrice => '',
        //            profit => '0.0000',
        //            commission => '0.000000',
        //            status => 'PENDING',
        //            time => '1707339747860',
        //            updateTime => '1707339747860',
        //            clientOrderId => '',
        //            leverage => '20X',
        //            workingType => 'MARK_PRICE',
        //            onlyOnePosition => false,
        //            reduceOnly => false
        //        ),
        //        replaceResult => 'true',
        //        replaceMsg => '',
        //        newOrderResponse => {
        //            orderId => '1755338440612995072',
        //            symbol => 'SOL-USDT',
        //            $positionSide => 'SHORT',
        //            $side => 'SELL',
        //            type => 'LIMIT',
        //            price => '99',
        //            quantity => '2',
        //            $stopPrice => '0',
        //            workingType => 'MARK_PRICE',
        //            clientOrderID => '',
        //            timeInForce => 'GTC',
        //            priceRate => '0',
        //            $stopLoss => '',
        //            $takeProfit => '',
        //            reduceOnly => false
        //        }
        //    }
        //
        // editOrder (spot)
        //
        //    {
        //        cancelResult => array( code => '0', msg => '', result => true ),
        //        openResult => array( code => '0', msg => '', result => true ),
        //        orderOpenResponse => array(
        //            symbol => 'SOL-USDT',
        //            orderId => '1755334007697866752',
        //            transactTime => '1707339214620',
        //            price => '99',
        //            $stopPrice => '0',
        //            origQty => '0.2',
        //            executedQty => '0',
        //            cummulativeQuoteQty => '0',
        //            status => 'PENDING',
        //            type => 'LIMIT',
        //            $side => 'SELL',
        //            clientOrderID => ''
        //        ),
        //        orderCancelResponse => {
        //            symbol => 'SOL-USDT',
        //            orderId => '1755117055251480576',
        //            price => '100',
        //            $stopPrice => '0',
        //            origQty => '0.2',
        //            executedQty => '0',
        //            cummulativeQuoteQty => '0',
        //            status => 'CANCELED',
        //            type => 'LIMIT',
        //            $side => 'SELL'
        //        }
        //    }
        //
        // stop loss $order
        //
        //    {
        //        "symbol" => "ETH-USDT",
        //        "orderId" => "1792461744476422144",
        //        "price" => "2775.65",
        //        "StopPrice" => "2778.42",
        //        "origQty" => "0.032359",
        //        "executedQty" => "0",
        //        "cummulativeQuoteQty" => "0",
        //        "status" => "NEW",
        //        "type" => "TAKE_STOP_LIMIT",
        //        "side" => "SELL",
        //        "time" => "1716191156868",
        //        "updateTime" => "1716191156868",
        //        "origQuoteOrderQty" => "0",
        //        "fee" => "0",
        //        "feeAsset" => "USDT",
        //        "clientOrderID" => ""
        //    }
        //
        // inverse swap cancelAllOrders, cancelOrder, fetchOrder, fetchOpenOrders, fetchClosedOrders, fetchCanceledOrders
        //
        //     {
        //         "symbol" => "SOL-USD",
        //         "orderId" => "1809845251327672320",
        //         "side" => "BUY",
        //         "positionSide" => "LONG",
        //         "type" => "LIMIT",
        //         "quantity" => 1,
        //         "origQty" => "0",
        //         "price" => "90",
        //         "executedQty" => "0",
        //         "avgPrice" => "0",
        //         "cumQuote" => "0",
        //         "stopPrice" => "",
        //         "profit" => "0.0000",
        //         "commission" => "0.000000",
        //         "status" => "CANCELLED",
        //         "time" => 1720335707872,
        //         "updateTime" => 1720335707912,
        //         "clientOrderId" => "",
        //         "leverage" => "",
        //         "takeProfit" => array(
        //             "type" => "",
        //             "quantity" => 0,
        //             "stopPrice" => 0,
        //             "price" => 0,
        //             "workingType" => "",
        //             "stopGuaranteed" => ""
        //         ),
        //         "stopLoss" => array(
        //             "type" => "",
        //             "quantity" => 0,
        //             "stopPrice" => 0,
        //             "price" => 0,
        //             "workingType" => "",
        //             "stopGuaranteed" => ""
        //         ),
        //         "advanceAttr" => 0,
        //         "positionID" => 0,
        //         "takeProfitEntrustPrice" => 0,
        //         "stopLossEntrustPrice" => 0,
        //         "orderType" => "",
        //         "workingType" => ""
        //     }
        //
        $info = $order;
        $newOrder = $this->safe_dict_2($order, 'newOrderResponse', 'orderOpenResponse');
        if ($newOrder !== null) {
            $order = $newOrder;
        }
        $positionSide = $this->safe_string_2($order, 'positionSide', 'ps');
        $marketType = ($positionSide === null) ? 'spot' : 'swap';
        $marketId = $this->safe_string_2($order, 'symbol', 's');
        if ($market === null) {
            $market = $this->safe_market($marketId, null, null, $marketType);
        }
        $side = $this->safe_string_lower_2($order, 'side', 'S');
        $timestamp = $this->safe_integer_n($order, array( 'time', 'transactTime', 'E', 'createdTime' ));
        $lastTradeTimestamp = $this->safe_integer_2($order, 'updateTime', 'T');
        $statusId = $this->safe_string_upper_n($order, array( 'status', 'X', 'orderStatus' ));
        $feeCurrencyCode = $this->safe_string_2($order, 'feeAsset', 'N');
        $feeCost = $this->safe_string_n($order, array( 'fee', 'commission', 'n' ));
        if (($feeCurrencyCode === null)) {
            if ($market['spot']) {
                if ($side === 'buy') {
                    $feeCurrencyCode = $market['base'];
                } else {
                    $feeCurrencyCode = $market['quote'];
                }
            } else {
                $feeCurrencyCode = $market['quote'];
            }
        }
        $stopLoss = $this->safe_value($order, 'stopLoss');
        $stopLossPrice = null;
        if (($stopLoss !== null) && ($stopLoss !== '')) {
            $stopLossPrice = $this->omit_zero($this->safe_string($stopLoss, 'stopLoss'));
        }
        if (($stopLoss !== null) && (!(is_float($stopLoss) || is_int($stopLoss))) && ($stopLoss !== '')) {
            //  $stopLoss => 'array("stopPrice":50,"workingType":"MARK_PRICE","type":"STOP_MARKET","quantity":1)',
            if (gettype($stopLoss) === 'string') {
                $stopLoss = $this->parse_json($stopLoss);
            }
            $stopLossPrice = $this->omit_zero($this->safe_string($stopLoss, 'stopPrice'));
        }
        $takeProfit = $this->safe_value($order, 'takeProfit');
        $takeProfitPrice = null;
        if ($takeProfit !== null && ($takeProfit !== '')) {
            $takeProfitPrice = $this->omit_zero($this->safe_string($takeProfit, 'takeProfit'));
        }
        if (($takeProfit !== null) && (!(is_float($takeProfit) || is_int($takeProfit))) && ($takeProfit !== '')) {
            //  $takeProfit => 'array("stopPrice":150,"workingType":"MARK_PRICE","type":"TAKE_PROFIT_MARKET","quantity":1)',
            if (gettype($takeProfit) === 'string') {
                $takeProfit = $this->parse_json($takeProfit);
            }
            $takeProfitPrice = $this->omit_zero($this->safe_string($takeProfit, 'stopPrice'));
        }
        $rawType = $this->safe_string_lower_2($order, 'type', 'o');
        $stopPrice = $this->omit_zero($this->safe_string_2($order, 'StopPrice', 'stopPrice'));
        $triggerPrice = $stopPrice;
        if ($stopPrice !== null) {
            if ((mb_strpos($rawType, 'stop') > -1) && ($stopLossPrice === null)) {
                $stopLossPrice = $stopPrice;
                $triggerPrice = null;
            }
            if ((mb_strpos($rawType, 'take') > -1) && ($takeProfitPrice === null)) {
                $takeProfitPrice = $stopPrice;
                $triggerPrice = null;
            }
        }
        return $this->safe_order(array(
            'info' => $info,
            'id' => $this->safe_string_n($order, array( 'orderId', 'i', 'mainOrderId' )),
            'clientOrderId' => $this->safe_string_n($order, array( 'clientOrderID', 'clientOrderId', 'origClientOrderId', 'c' )),
            'symbol' => $this->safe_symbol($marketId, $market, '-', $marketType),
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'lastTradeTimestamp' => $lastTradeTimestamp,
            'lastUpdateTimestamp' => $this->safe_integer($order, 'updateTime'),
            'type' => $this->parse_order_type($rawType),
            'timeInForce' => $this->safe_string($order, 'timeInForce'),
            'postOnly' => null,
            'side' => $this->parse_order_side($side),
            'price' => $this->safe_string_2($order, 'price', 'p'),
            'triggerPrice' => $triggerPrice,
            'stopLossPrice' => $stopLossPrice,
            'takeProfitPrice' => $takeProfitPrice,
            'average' => $this->safe_string_2($order, 'avgPrice', 'ap'),
            'cost' => $this->safe_string($order, 'cummulativeQuoteQty'),
            'amount' => $this->safe_string_n($order, array( 'origQty', 'q', 'quantity', 'totalAmount' )),
            'filled' => $this->safe_string_2($order, 'executedQty', 'z'),
            'remaining' => null,
            'status' => $this->parse_order_status($statusId),
            'fee' => array(
                'currency' => $feeCurrencyCode,
                'cost' => Precise::string_abs($feeCost),
            ),
            'trades' => null,
            'reduceOnly' => $this->safe_bool_2($order, 'reduceOnly', 'ro'),
        ), $market);
    }

    public function parse_order_status(?string $status) {
        $statuses = array(
            'NEW' => 'open',
            'PENDING' => 'open',
            'PARTIALLY_FILLED' => 'open',
            'RUNNING' => 'open',
            'FILLED' => 'closed',
            'CANCELED' => 'canceled',
            'CANCELLED' => 'canceled',
            'FAILED' => 'canceled',
        );
        return $this->safe_string($statuses, $status, $status);
    }

    public function cancel_order(string $id, ?string $symbol = null, $params = array ()) {
        /**
         * cancels an open $order
         *
         * @see https://bingx-api.github.io/docs/#/en-us/spot/trade-api.html#Cancel%20Order
         * @see https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Cancel%20Order
         * @see https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#Cancel%20an%20Order
         * @see https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Cancel%20TWAP%20Order
         *
         * @param {string} $id $order $id
         * @param {string} $symbol unified $symbol of the $market the $order was made in
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @param {string} [$params->clientOrderId] a unique $id for the $order
         * @return {array} an ~@link https://docs.ccxt.com/#/?$id=$order-structure $order structure~
         */
        $this->load_markets();
        $isTwapOrder = $this->safe_bool($params, 'twap', false);
        $params = $this->omit($params, 'twap');
        $response = null;
        $market = null;
        if ($isTwapOrder) {
            $twapRequest = array(
                'mainOrderId' => $id,
            );
            $response = $this->swapV1PrivatePostTwapCancelOrder ($this->extend($twapRequest, $params));
            //
            //     {
            //         "code" => 0,
            //         "msg" => "",
            //         "timestamp" => 1702731661854,
            //         "data" => {
            //             "symbol" => "BNB-USDT",
            //             "side" => "BUY",
            //             "positionSide" => "LONG",
            //             "priceType" => "constant",
            //             "priceVariance" => "2000",
            //             "triggerPrice" => "68000",
            //             "interval" => 8,
            //             "amountPerOrder" => "0.111",
            //             "totalAmount" => "0.511",
            //             "orderStatus" => "Running",
            //             "executedQty" => "0.1",
            //             "duration" => 800,
            //             "maxDuration" => 9000,
            //             "createdTime" => 1702731661854,
            //             "updateTime" => 1702731661854
            //         }
            //     }
            //
        } else {
            if ($symbol === null) {
                throw new ArgumentsRequired($this->id . ' cancelOrder() requires a $symbol argument');
            }
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
            );
            $clientOrderId = $this->safe_string_2($params, 'clientOrderId', 'clientOrderID');
            $params = $this->omit($params, array( 'clientOrderId' ));
            if ($clientOrderId !== null) {
                $request['clientOrderID'] = $clientOrderId;
            } else {
                $request['orderId'] = $id;
            }
            $type = null;
            $subType = 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);
            if ($type === 'spot') {
                $response = $this->spotV1PrivatePostTradeCancel ($this->extend($request, $params));
            } else {
                if ($subType === 'inverse') {
                    $response = $this->cswapV1PrivateDeleteTradeCancelOrder ($this->extend($request, $params));
                } else {
                    $response = $this->swapV2PrivateDeleteTradeOrder ($this->extend($request, $params));
                }
            }
        }
        //
        // spot
        //
        //   {
        //       "code" => 0,
        //       "msg" => "",
        //       "data" => {
        //           "symbol" => "XRP-USDT",
        //           "orderId" => 1514090846268424192,
        //           "price" => "0.5",
        //           "origQty" => "10",
        //           "executedQty" => "0",
        //           "cummulativeQuoteQty" => "0",
        //           "status" => "CANCELED",
        //           "type" => "LIMIT",
        //           "side" => "BUY"
        //       }
        //   }
        //
        // inverse swap
        //
        //     {
        //         "code" => 0,
        //         "msg" => "",
        //         "data" => {
        //             "order" => {
        //                 "symbol" => "SOL-USD",
        //                 "orderId" => "1816002957423951872",
        //                 "side" => "BUY",
        //                 "positionSide" => "Long",
        //                 "type" => "Pending",
        //                 "quantity" => 0,
        //                 "origQty" => "0",
        //                 "price" => "150",
        //                 "executedQty" => "0",
        //                 "avgPrice" => "0",
        //                 "cumQuote" => "0",
        //                 "stopPrice" => "",
        //                 "profit" => "0.0000",
        //                 "commission" => "0.000000",
        //                 "status" => "CANCELLED",
        //                 "time" => 1721803819410,
        //                 "updateTime" => 1721803819427,
        //                 "clientOrderId" => "",
        //                 "leverage" => "",
        //                 "takeProfit" => array(
        //                     "type" => "",
        //                     "quantity" => 0,
        //                     "stopPrice" => 0,
        //                     "price" => 0,
        //                     "workingType" => "",
        //                     "stopGuaranteed" => ""
        //                 ),
        //                 "stopLoss" => array(
        //                     "type" => "",
        //                     "quantity" => 0,
        //                     "stopPrice" => 0,
        //                     "price" => 0,
        //                     "workingType" => "",
        //                     "stopGuaranteed" => ""
        //                 ),
        //                 "advanceAttr" => 0,
        //                 "positionID" => 0,
        //                 "takeProfitEntrustPrice" => 0,
        //                 "stopLossEntrustPrice" => 0,
        //                 "orderType" => "",
        //                 "workingType" => ""
        //             }
        //         }
        //     }
        //
        // linear swap
        //
        //    {
        //        "code" => 0,
        //        "msg" => "",
        //        "data" => {
        //          "order" => {
        //            "symbol" => "LINK-USDT",
        //            "orderId" => 1597783850786750464,
        //            "side" => "BUY",
        //            "positionSide" => "LONG",
        //            "type" => "TRIGGER_MARKET",
        //            "origQty" => "5.0",
        //            "price" => "5.0000",
        //            "executedQty" => "0.0",
        //            "avgPrice" => "0.0000",
        //            "cumQuote" => "0",
        //            "stopPrice" => "5.0000",
        //            "profit" => "",
        //            "commission" => "",
        //            "status" => "CANCELLED",
        //            "time" => 1669776330000,
        //            "updateTime" => 1669776330000
        //          }
        //        }
        //    }
        //
        $data = $this->safe_dict($response, 'data', array());
        $order = $this->safe_dict($data, 'order', $data);
        return $this->parse_order($order, $market);
    }

    public function cancel_all_orders(?string $symbol = null, $params = array ()) {
        /**
         * cancel all open $orders
         *
         * @see https://bingx-api.github.io/docs/#/en-us/spot/trade-api.html#Cancel%20orders%20by%20symbol
         * @see https://bingx-api.github.io/docs/#/swapV2/trade-api.html#Cancel%20All%20Orders
         * @see https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#Cancel%20all%20orders
         *
         * @param {string} [$symbol] unified $market $symbol, only $orders in the $market of this $symbol are cancelled when $symbol is not null
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
         */
        if ($symbol === null) {
            throw new ArgumentsRequired($this->id . ' cancelAllOrders() requires a $symbol argument');
        }
        $this->load_markets();
        $market = $this->market($symbol);
        $request = array(
            'symbol' => $market['id'],
        );
        $response = null;
        if ($market['spot']) {
            $response = $this->spotV1PrivatePostTradeCancelOpenOrders ($this->extend($request, $params));
            //
            //     {
            //         "code" => 0,
            //         "msg" => "",
            //         "debugMsg" => "",
            //         "data" => {
            //             "orders" => [array(
            //                 "symbol" => "ADA-USDT",
            //                 "orderId" => 1740659971369992192,
            //                 "transactTime" => 1703840651730,
            //                 "price" => 5,
            //                 "stopPrice" => 0,
            //                 "origQty" => 10,
            //                 "executedQty" => 0,
            //                 "cummulativeQuoteQty" => 0,
            //                 "status" => "CANCELED",
            //                 "type" => "LIMIT",
            //                 "side" => "SELL"
            //             )]
            //         }
            //     }
            //
        } elseif ($market['swap']) {
            if ($market['inverse']) {
                $response = $this->cswapV1PrivateDeleteTradeAllOpenOrders ($this->extend($request, $params));
                //
                //     {
                //         "code" => 0,
                //         "msg" => "",
                //         "timestamp" => 1720501468364,
                //         "data" => {
                //             "success" => array(
                //                 {
                //                     "symbol" => "SOL-USD",
                //                     "orderId" => "1809845251327672320",
                //                     "side" => "BUY",
                //                     "positionSide" => "LONG",
                //                     "type" => "LIMIT",
                //                     "quantity" => 1,
                //                     "origQty" => "0",
                //                     "price" => "90",
                //                     "executedQty" => "0",
                //                     "avgPrice" => "0",
                //                     "cumQuote" => "0",
                //                     "stopPrice" => "",
                //                     "profit" => "0.0000",
                //                     "commission" => "0.000000",
                //                     "status" => "CANCELLED",
                //                     "time" => 1720335707872,
                //                     "updateTime" => 1720335707912,
                //                     "clientOrderId" => "",
                //                     "leverage" => "",
                //                     "takeProfit" => array(
                //                         "type" => "",
                //                         "quantity" => 0,
                //                         "stopPrice" => 0,
                //                         "price" => 0,
                //                         "workingType" => "",
                //                         "stopGuaranteed" => ""
                //                     ),
                //                     "stopLoss" => array(
                //                         "type" => "",
                //                         "quantity" => 0,
                //                         "stopPrice" => 0,
                //                         "price" => 0,
                //                         "workingType" => "",
                //                         "stopGuaranteed" => ""
                //                     ),
                //                     "advanceAttr" => 0,
                //                     "positionID" => 0,
                //                     "takeProfitEntrustPrice" => 0,
                //                     "stopLossEntrustPrice" => 0,
                //                     "orderType" => "",
                //                     "workingType" => ""
                //                 }
                //             ),
                //             "failed" => null
                //         }
                //     }
                //
            } else {
                $response = $this->swapV2PrivateDeleteTradeAllOpenOrders ($this->extend($request, $params));
                //
                //    {
                //        "code" => 0,
                //        "msg" => "",
                //        "data" => {
                //          "success" => array(
                //            {
                //              "symbol" => "LINK-USDT",
                //              "orderId" => 1597783835095859200,
                //              "side" => "BUY",
                //              "positionSide" => "LONG",
                //              "type" => "TRIGGER_LIMIT",
                //              "origQty" => "5.0",
                //              "price" => "9.0000",
                //              "executedQty" => "0.0",
                //              "avgPrice" => "0.0000",
                //              "cumQuote" => "0",
                //              "stopPrice" => "9.5000",
                //              "profit" => "",
                //              "commission" => "",
                //              "status" => "NEW",
                //              "time" => 1669776326000,
                //              "updateTime" => 1669776326000
                //            }
                //          ),
                //          "failed" => null
                //        }
                //    }
                //
            }
        } else {
            throw new BadRequest($this->id . ' cancelAllOrders is only supported for spot and swap markets.');
        }
        $data = $this->safe_dict($response, 'data', array());
        $orders = $this->safe_list_2($data, 'success', 'orders', array());
        return $this->parse_orders($orders);
    }

    public function cancel_orders(array $ids, ?string $symbol = null, $params = array ()) {
        /**
         * cancel multiple orders
         *
         * @see https://bingx-api.github.io/docs/#/swapV2/trade-api.html#Cancel%20a%20Batch%20of%20Orders
         * @see https://bingx-api.github.io/docs/#/spot/trade-api.html#Cancel%20a%20Batch%20of%20Orders
         *
         * @param {string[]} $ids order $ids
         * @param {string} $symbol unified $market $symbol, default is null
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @param {string[]} [$params->clientOrderIds] client order $ids
         * @return {array} an list of ~@link https://docs.ccxt.com/#/?$id=order-structure order structures~
         */
        if ($symbol === null) {
            throw new ArgumentsRequired($this->id . ' cancelOrders() requires a $symbol argument');
        }
        $this->load_markets();
        $market = $this->market($symbol);
        $request = array(
            'symbol' => $market['id'],
        );
        $clientOrderIds = $this->safe_value($params, 'clientOrderIds');
        $params = $this->omit($params, 'clientOrderIds');
        $idsToParse = $ids;
        $areClientOrderIds = ($clientOrderIds !== null);
        if ($areClientOrderIds) {
            $idsToParse = $clientOrderIds;
        }
        $parsedIds = array();
        for ($i = 0; $i < count($idsToParse); $i++) {
            $id = $idsToParse[$i];
            $stringId = (string) $id;
            $parsedIds[] = $stringId;
        }
        $response = null;
        if ($market['spot']) {
            $spotReqKey = $areClientOrderIds ? 'clientOrderIDs' : 'orderIds';
            $request[$spotReqKey] = implode(',', $parsedIds);
            $response = $this->spotV1PrivatePostTradeCancelOrders ($this->extend($request, $params));
            //
            //    {
            //       "code" => 0,
            //       "msg" => "",
            //       "debugMsg" => "",
            //       "data" => {
            //           "orders" => array(
            //                array(
            //                    "symbol" => "SOL-USDT",
            //                    "orderId" => 1795970045910614016,
            //                    "transactTime" => 1717027601111,
            //                    "price" => "180.25",
            //                    "stopPrice" => "0",
            //                    "origQty" => "0.03",
            //                    "executedQty" => "0",
            //                    "cummulativeQuoteQty" => "0",
            //                    "status" => "CANCELED",
            //                    "type" => "LIMIT",
            //                    "side" => "SELL",
            //                    "clientOrderID" => ""
            //                ),
            //                ...
            //            )
            //        }
            //    }
            //
        } else {
            if ($areClientOrderIds) {
                $request['clientOrderIDList'] = $this->json($parsedIds);
            } else {
                $request['orderIdList'] = $parsedIds;
            }
            $response = $this->swapV2PrivateDeleteTradeBatchOrders ($this->extend($request, $params));
            //
            //    {
            //        "code" => 0,
            //        "msg" => "",
            //        "data" => {
            //          "success" => array(
            //            {
            //              "symbol" => "LINK-USDT",
            //              "orderId" => 1597783850786750464,
            //              "side" => "BUY",
            //              "positionSide" => "LONG",
            //              "type" => "TRIGGER_MARKET",
            //              "origQty" => "5.0",
            //              "price" => "5.5710",
            //              "executedQty" => "0.0",
            //              "avgPrice" => "0.0000",
            //              "cumQuote" => "0",
            //              "stopPrice" => "5.0000",
            //              "profit" => "0.0000",
            //              "commission" => "0.000000",
            //              "status" => "CANCELLED",
            //              "time" => 1669776330000,
            //              "updateTime" => 1672370837000
            //            }
            //          ),
            //          "failed" => null
            //        }
            //    }
            //
        }
        $data = $this->safe_dict($response, 'data', array());
        $success = $this->safe_list_2($data, 'success', 'orders', array());
        return $this->parse_orders($success);
    }

    public function cancel_all_orders_after(?int $timeout, $params = array ()) {
        /**
         * dead man's switch, cancel all orders after the given $timeout
         *
         * @see https://bingx-api.github.io/docs/#/en-us/spot/trade-api.html#Cancel%20all%20orders%20in%20countdown
         * @see https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Cancel%20all%20orders%20in%20countdown
         *
         * @param {number} $timeout time in milliseconds, 0 represents cancel the timer
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @param {string} [$params->type] spot or swap market
         * @return {array} the api result
         */
        $this->load_markets();
        $isActive = ($timeout > 0);
        $request = array(
            'type' => ($isActive) ? 'ACTIVATE' : 'CLOSE',
            'timeOut' => ($isActive) ? ($this->parse_to_int($timeout / 1000)) : 0,
        );
        $response = null;
        $type = null;
        list($type, $params) = $this->handle_market_type_and_params('cancelAllOrdersAfter', null, $params);
        if ($type === 'spot') {
            $response = $this->spotV1PrivatePostTradeCancelAllAfter ($this->extend($request, $params));
        } elseif ($type === 'swap') {
            $response = $this->swapV2PrivatePostTradeCancelAllAfter ($this->extend($request, $params));
        } else {
            throw new NotSupported($this->id . ' cancelAllOrdersAfter() is not supported for ' . $type . ' markets');
        }
        //
        //     {
        //         code => '0',
        //         msg => '',
        //         data => {
        //             triggerTime => '1712645434',
        //             status => 'ACTIVATED',
        //             note => 'All your perpetual pending orders will be closed automatically at 2024-04-09 06:50:34 UTC(+0),before that you can cancel the timer, or extend triggerTime time by this request'
        //         }
        //     }
        //
        return $response;
    }

    public function fetch_order(string $id, ?string $symbol = null, $params = array ()) {
        /**
         * fetches information on an $order made by the user
         *
         * @see https://bingx-api.github.io/docs/#/en-us/spot/trade-api.html#Query%20Order%20details
         * @see https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Query%20Order%20details
         * @see https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#Query%20Order
         * @see https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#TWAP%20Order%20Details
         *
         * @param {string} $id the $order $id
         * @param {string} $symbol unified $symbol of the $market the $order was made in
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @param {boolean} [$params->twap] if fetching twap $order
         * @return {array} an ~@link https://docs.ccxt.com/#/?$id=$order-structure $order structure~
         */
        $this->load_markets();
        $isTwapOrder = $this->safe_bool($params, 'twap', false);
        $params = $this->omit($params, 'twap');
        $response = null;
        $market = null;
        if ($isTwapOrder) {
            $twapRequest = array(
                'mainOrderId' => $id,
            );
            $response = $this->swapV1PrivateGetTwapOrderDetail ($this->extend($twapRequest, $params));
            //
            //     {
            //         "code" => 0,
            //         "msg" => "success cancel $order",
            //         "timestamp" => 1732760856617,
            //         "data" => {
            //             "symbol" => "LTC-USDT",
            //             "mainOrderId" => "5596903086063901779",
            //             "side" => "BUY",
            //             "positionSide" => "LONG",
            //             "priceType" => "constant",
            //             "priceVariance" => "10.00",
            //             "triggerPrice" => "120.00",
            //             "interval" => 8,
            //             "amountPerOrder" => "0.5",
            //             "totalAmount" => "1.0",
            //             "orderStatus" => "Filled",
            //             "executedQty" => "1.0",
            //             "duration" => 16,
            //             "maxDuration" => 86400,
            //             "createdTime" => 1732693017000,
            //             "updateTime" => 1732693033000
            //         }
            //     }
            //
        } else {
            if ($symbol === null) {
                throw new ArgumentsRequired($this->id . ' fetchOrder() requires a $symbol argument');
            }
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
                'orderId' => $id,
            );
            $type = null;
            $subType = 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);
            if ($type === 'spot') {
                $response = $this->spotV1PrivateGetTradeQuery ($this->extend($request, $params));
                //
                //     {
                //         "code" => 0,
                //         "msg" => "",
                //         "data" => {
                //             "symbol" => "XRP-USDT",
                //             "orderId" => 1514087361158316032,
                //             "price" => "0.5",
                //             "origQty" => "10",
                //             "executedQty" => "0",
                //             "cummulativeQuoteQty" => "0",
                //             "status" => "CANCELED",
                //             "type" => "LIMIT",
                //             "side" => "BUY",
                //             "time" => 1649821532000,
                //             "updateTime" => 1649821543000,
                //             "origQuoteOrderQty" => "0",
                //             "fee" => "0",
                //             "feeAsset" => "XRP"
                //         }
                //     }
                //
            } else {
                if ($subType === 'inverse') {
                    $response = $this->cswapV1PrivateGetTradeOrderDetail ($this->extend($request, $params));
                    //
                    //     {
                    //         "code" => 0,
                    //         "msg" => "",
                    //         "data" => {
                    //             "order" => {
                    //                 "symbol" => "SOL-USD",
                    //                 "orderId" => "1816342420721254400",
                    //                 "side" => "BUY",
                    //                 "positionSide" => "Long",
                    //                 "type" => "LIMIT",
                    //                 "quantity" => 1,
                    //                 "origQty" => "",
                    //                 "price" => "150",
                    //                 "executedQty" => "0",
                    //                 "avgPrice" => "0.000",
                    //                 "cumQuote" => "",
                    //                 "stopPrice" => "",
                    //                 "profit" => "0.0000",
                    //                 "commission" => "0.0000",
                    //                 "status" => "Pending",
                    //                 "time" => 1721884753767,
                    //                 "updateTime" => 1721884753786,
                    //                 "clientOrderId" => "",
                    //                 "leverage" => "",
                    //                 "takeProfit" => array(
                    //                     "type" => "TAKE_PROFIT",
                    //                     "quantity" => 0,
                    //                     "stopPrice" => 0,
                    //                     "price" => 0,
                    //                     "workingType" => "MARK_PRICE",
                    //                     "stopGuaranteed" => ""
                    //                 ),
                    //                 "stopLoss" => array(
                    //                     "type" => "STOP",
                    //                     "quantity" => 0,
                    //                     "stopPrice" => 0,
                    //                     "price" => 0,
                    //                     "workingType" => "MARK_PRICE",
                    //                     "stopGuaranteed" => ""
                    //                 ),
                    //                 "advanceAttr" => 0,
                    //                 "positionID" => 0,
                    //                 "takeProfitEntrustPrice" => 0,
                    //                 "stopLossEntrustPrice" => 0,
                    //                 "orderType" => "",
                    //                 "workingType" => "MARK_PRICE"
                    //             }
                    //         }
                    //     }
                    //
                } else {
                    $response = $this->swapV2PrivateGetTradeOrder ($this->extend($request, $params));
                    //
                    //     {
                    //         "code" => 0,
                    //         "msg" => "",
                    //         "data" => {
                    //             "order" => {
                    //                 "symbol" => "BTC-USDT",
                    //                 "orderId" => 1597597642269917184,
                    //                 "side" => "SELL",
                    //                 "positionSide" => "LONG",
                    //                 "type" => "TAKE_PROFIT_MARKET",
                    //                 "origQty" => "1.0000",
                    //                 "price" => "0.0",
                    //                 "executedQty" => "0.0000",
                    //                 "avgPrice" => "0.0",
                    //                 "cumQuote" => "",
                    //                 "stopPrice" => "16494.0",
                    //                 "profit" => "",
                    //                 "commission" => "",
                    //                 "status" => "FILLED",
                    //                 "time" => 1669731935000,
                    //                 "updateTime" => 1669752524000
                    //             }
                    //         }
                    //     }
                    //
                }
            }
        }
        $data = $this->safe_dict($response, 'data', array());
        $order = $this->safe_dict($data, 'order', $data);
        return $this->parse_order($order, $market);
    }

    public function fetch_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): array {
        /**
         * fetches information on multiple $orders made by the user
         *
         * @see https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#All%20Orders
         * @see https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Query%20Order%20history (returns less fields than above)
         *
         * @param {string} $symbol unified $market $symbol of the $market $orders were made in
         * @param {int} [$since] the earliest time in ms to fetch $orders for
         * @param {int} [$limit] the maximum number of order structures to retrieve
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @param {int} [$params->until] the latest time in ms to fetch entries for
         * @param {int} [$params->orderId] Only return subsequent $orders, and return the latest order by default
         * @return {Order[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
         */
        $this->load_markets();
        $request = array();
        $market = null;
        if ($symbol !== null) {
            $market = $this->market($symbol);
            $request['symbol'] = $market['id'];
        }
        $type = null;
        list($type, $params) = $this->handle_market_type_and_params('fetchOrders', $market, $params);
        if ($type !== 'swap') {
            throw new NotSupported($this->id . ' fetchOrders() is only supported for swap markets');
        }
        if ($limit !== null) {
            $request['limit'] = $limit;
        }
        if ($since !== null) {
            $request['startTime'] = $since;
        }
        list($request, $params) = $this->handle_until_option('endTime', $request, $params);
        $response = $this->swapV1PrivateGetTradeFullOrder ($this->extend($request, $params));
        //
        //     {
        //         "code" => 0,
        //         "msg" => "",
        //         "data" => {
        //         "orders" => array(
        //           {
        //             "symbol" => "PYTH-USDT",
        //             "orderId" => 1736007506620112100,
        //             "side" => "SELL",
        //             "positionSide" => "SHORT",
        //             "type" => "LIMIT",
        //             "origQty" => "33",
        //             "price" => "0.3916",
        //             "executedQty" => "33",
        //             "avgPrice" => "0.3916",
        //             "cumQuote" => "13",
        //             "stopPrice" => "",
        //             "profit" => "0.0000",
        //             "commission" => "-0.002585",
        //             "status" => "FILLED",
        //             "time" => 1702731418000,
        //             "updateTime" => 1702731470000,
        //             "clientOrderId" => "",
        //             "leverage" => "15X",
        //             "takeProfit" => array(
        //                 "type" => "TAKE_PROFIT",
        //                 "quantity" => 0,
        //                 "stopPrice" => 0,
        //                 "price" => 0,
        //                 "workingType" => ""
        //             ),
        //             "stopLoss" => array(
        //                 "type" => "STOP",
        //                 "quantity" => 0,
        //                 "stopPrice" => 0,
        //                 "price" => 0,
        //                 "workingType" => ""
        //             ),
        //             "advanceAttr" => 0,
        //             "positionID" => 0,
        //             "takeProfitEntrustPrice" => 0,
        //             "stopLossEntrustPrice" => 0,
        //             "orderType" => "",
        //             "workingType" => "MARK_PRICE",
        //             "stopGuaranteed" => false,
        //             "triggerOrderId" => 1736012449498123500
        //           }
        //         )
        //       }
        //     }
        //
        $data = $this->safe_dict($response, 'data', array());
        $orders = $this->safe_list($data, 'orders', array());
        return $this->parse_orders($orders, $market, $since, $limit);
    }

    public function fetch_open_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): array {
        /**
         * fetch all unfilled currently open $orders
         *
         * @see https://bingx-api.github.io/docs/#/en-us/spot/trade-api.html#Current%20Open%20Orders
         * @see https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Current%20All%20Open%20Orders
         * @see https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#Query%20all%20current%20pending%20orders
         * @see https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Query%20TWAP%20Entrusted%20Order
         *
         * @param {string} $symbol unified $market $symbol
         * @param {int} [$since] the earliest time in ms to fetch open $orders for
         * @param {int} [$limit] the maximum number of open order structures to retrieve
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @param {boolean} [$params->twap] if fetching twap open $orders
         * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
         */
        $this->load_markets();
        $market = null;
        $request = array();
        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('fetchOpenOrders', $market, $params);
        list($subType, $params) = $this->handle_sub_type_and_params('fetchOpenOrders', $market, $params);
        if ($type === 'spot') {
            $response = $this->spotV1PrivateGetTradeOpenOrders ($this->extend($request, $params));
        } else {
            $isTwapOrder = $this->safe_bool($params, 'twap', false);
            $params = $this->omit($params, 'twap');
            if ($isTwapOrder) {
                $response = $this->swapV1PrivateGetTwapOpenOrders ($this->extend($request, $params));
            } elseif ($subType === 'inverse') {
                $response = $this->cswapV1PrivateGetTradeOpenOrders ($this->extend($request, $params));
            } else {
                $response = $this->swapV2PrivateGetTradeOpenOrders ($this->extend($request, $params));
            }
        }
        //
        //  spot
        //
        //    {
        //        "code" => 0,
        //        "msg" => "",
        //        "data" => {
        //            "orders" => array(
        //                {
        //                    "symbol" => "XRP-USDT",
        //                    "orderId" => 1514073325788200960,
        //                    "price" => "0.5",
        //                    "origQty" => "20",
        //                    "executedQty" => "0",
        //                    "cummulativeQuoteQty" => "0",
        //                    "status" => "PENDING",
        //                    "type" => "LIMIT",
        //                    "side" => "BUY",
        //                    "time" => 1649818185647,
        //                    "updateTime" => 1649818185647,
        //                    "origQuoteOrderQty" => "0"
        //                }
        //            )
        //        }
        //    }
        //
        // inverse swap
        //
        //     {
        //         "code" => 0,
        //         "msg" => "",
        //         "data" => {
        //             "orders" => array(
        //                 {
        //                     "symbol" => "SOL-USD",
        //                     "orderId" => "1816013900044320768",
        //                     "side" => "BUY",
        //                     "positionSide" => "Long",
        //                     "type" => "LIMIT",
        //                     "quantity" => 1,
        //                     "origQty" => "",
        //                     "price" => "150",
        //                     "executedQty" => "0",
        //                     "avgPrice" => "0.000",
        //                     "cumQuote" => "",
        //                     "stopPrice" => "",
        //                     "profit" => "0.0000",
        //                     "commission" => "0.0000",
        //                     "status" => "Pending",
        //                     "time" => 1721806428334,
        //                     "updateTime" => 1721806428352,
        //                     "clientOrderId" => "",
        //                     "leverage" => "",
        //                     "takeProfit" => array(
        //                         "type" => "TAKE_PROFIT",
        //                         "quantity" => 0,
        //                         "stopPrice" => 0,
        //                         "price" => 0,
        //                         "workingType" => "MARK_PRICE",
        //                         "stopGuaranteed" => ""
        //                     ),
        //                     "stopLoss" => array(
        //                         "type" => "STOP",
        //                         "quantity" => 0,
        //                         "stopPrice" => 0,
        //                         "price" => 0,
        //                         "workingType" => "MARK_PRICE",
        //                         "stopGuaranteed" => ""
        //                     ),
        //                     "advanceAttr" => 0,
        //                     "positionID" => 0,
        //                     "takeProfitEntrustPrice" => 0,
        //                     "stopLossEntrustPrice" => 0,
        //                     "orderType" => "",
        //                     "workingType" => "MARK_PRICE"
        //                 }
        //             )
        //         }
        //     }
        //
        // linear swap
        //
        //    {
        //        "code" => 0,
        //        "msg" => "",
        //        "data" => {
        //          "orders" => array(
        //            array(
        //              "symbol" => "LINK-USDT",
        //              "orderId" => 1585839271162413056,
        //              "side" => "BUY",
        //              "positionSide" => "LONG",
        //              "type" => "TRIGGER_MARKET",
        //              "origQty" => "5.0",
        //              "price" => "9",
        //              "executedQty" => "0.0",
        //              "avgPrice" => "0",
        //              "cumQuote" => "0",
        //              "stopPrice" => "5",
        //              "profit" => "0.0000",
        //              "commission" => "0.000000",
        //              "status" => "CANCELLED",
        //              "time" => 1667631605000,
        //              "updateTime" => 1667631605000
        //            ),
        //          )
        //        }
        //    }
        //
        // twap
        //
        //     {
        //         "code" => 0,
        //         "msg" => "",
        //         "timestamp" => 1702731661854,
        //         "data" => {
        //             "list" => array(
        //                 {
        //                     "symbol" => "BNB-USDT",
        //                     "side" => "BUY",
        //                     "positionSide" => "LONG",
        //                     "priceType" => "constant",
        //                     "priceVariance" => "2000",
        //                     "triggerPrice" => "68000",
        //                     "interval" => 8,
        //                     "amountPerOrder" => "0.111",
        //                     "totalAmount" => "0.511",
        //                     "orderStatus" => "Running",
        //                     "executedQty" => "0.1",
        //                     "duration" => 800,
        //                     "maxDuration" => 9000,
        //                     "createdTime" => 1702731661854,
        //                     "updateTime" => 1702731661854
        //                 }
        //             ),
        //             "total" => 1
        //         }
        //     }
        //
        $data = $this->safe_dict($response, 'data', array());
        $orders = $this->safe_list_2($data, 'orders', 'list', array());
        return $this->parse_orders($orders, $market, $since, $limit);
    }

    public function fetch_closed_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): array {
        /**
         * fetches information on multiple closed $orders made by the user
         *
         * @see https://bingx-api.github.io/docs/#/en-us/spot/trade-api.html#Query%20Order%20history
         * @see https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Query%20Order%20history
         * @see https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#User's%20History%20Orders
         * @see https://bingx-api.github.io/docs/#/standard/contract-interface.html#Historical%20order
         *
         * @param {string} $symbol unified market $symbol of the closed $orders
         * @param {int} [$since] timestamp in ms of the earliest order
         * @param {int} [$limit] the max number of closed $orders to return
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @param {int} [$params->until] the latest time in ms to fetch $orders for
         * @param {boolean} [$params->standard] whether to fetch standard contract $orders
         * @return {Order[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
         */
        $this->load_markets();
        $orders = $this->fetch_canceled_and_closed_orders($symbol, $since, $limit, $params);
        return $this->filter_by($orders, 'status', 'closed');
    }

    public function fetch_canceled_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) {
        /**
         * fetches information on multiple canceled $orders made by the user
         *
         * @see https://bingx-api.github.io/docs/#/en-us/spot/trade-api.html#Query%20Order%20history
         * @see https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Query%20Order%20history
         * @see https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#User's%20History%20Orders
         * @see https://bingx-api.github.io/docs/#/standard/contract-interface.html#Historical%20order
         *
         * @param {string} $symbol unified market $symbol of the canceled $orders
         * @param {int} [$since] timestamp in ms of the earliest order
         * @param {int} [$limit] the max number of canceled $orders to return
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @param {int} [$params->until] the latest time in ms to fetch $orders for
         * @param {boolean} [$params->standard] whether to fetch standard contract $orders
         * @return {array} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
         */
        $this->load_markets();
        $orders = $this->fetch_canceled_and_closed_orders($symbol, $since, $limit, $params);
        return $this->filter_by($orders, 'status', 'canceled');
    }

    public function fetch_canceled_and_closed_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) {
        /**
         * fetches information on multiple closed $orders made by the user
         *
         * @see https://bingx-api.github.io/docs/#/en-us/spot/trade-api.html#Query%20Order%20history
         * @see https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Query%20Order%20history
         * @see https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#User's%20History%20Orders
         * @see https://bingx-api.github.io/docs/#/standard/contract-interface.html#Historical%20order
         * @see https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Query%20TWAP%20Historical%20Orders
         *
         * @param {string} [$symbol] unified $market $symbol of the $market $orders were made in
         * @param {int} [$since] the earliest time in ms to fetch $orders for
         * @param {int} [$limit] the maximum number of order structures to retrieve
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @param {int} [$params->until] the latest time in ms to fetch $orders for
         * @param {boolean} [$params->standard] whether to fetch $standard contract $orders
         * @param {boolean} [$params->twap] if fetching twap $orders
         * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
         */
        $this->load_markets();
        $market = null;
        $request = array();
        if ($symbol !== null) {
            $market = $this->market($symbol);
            $request['symbol'] = $market['id'];
        }
        $type = null;
        $subType = null;
        $standard = null;
        $response = null;
        list($type, $params) = $this->handle_market_type_and_params('fetchClosedOrders', $market, $params);
        list($subType, $params) = $this->handle_sub_type_and_params('fetchClosedOrders', $market, $params);
        list($standard, $params) = $this->handle_option_and_params($params, 'fetchClosedOrders', 'standard', false);
        if ($standard) {
            $response = $this->contractV1PrivateGetAllOrders ($this->extend($request, $params));
        } elseif ($type === 'spot') {
            if ($limit !== null) {
                $request['pageSize'] = $limit;
            }
            $response = $this->spotV1PrivateGetTradeHistoryOrders ($this->extend($request, $params));
            //
            //    {
            //        "code" => 0,
            //        "msg" => "",
            //        "data" => {
            //            "orders" => array(
            //                {
            //                    "symbol" => "XRP-USDT",
            //                    "orderId" => 1514073325788200960,
            //                    "price" => "0.5",
            //                    "origQty" => "20",
            //                    "executedQty" => "0",
            //                    "cummulativeQuoteQty" => "0",
            //                    "status" => "PENDING",
            //                    "type" => "LIMIT",
            //                    "side" => "BUY",
            //                    "time" => 1649818185647,
            //                    "updateTime" => 1649818185647,
            //                    "origQuoteOrderQty" => "0"
            //                }
            //            )
            //        }
            //    }
            //
        } else {
            $isTwapOrder = $this->safe_bool($params, 'twap', false);
            $params = $this->omit($params, 'twap');
            if ($isTwapOrder) {
                $request['pageIndex'] = 1;
                $request['pageSize'] = ($limit === null) ? 100 : $limit;
                $request['startTime'] = ($since === null) ? 1 : $since;
                $until = $this->safe_integer($params, 'until', $this->milliseconds());
                $params = $this->omit($params, 'until');
                $request['endTime'] = $until;
                $response = $this->swapV1PrivateGetTwapHistoryOrders ($this->extend($request, $params));
                //
                //     {
                //         "code" => 0,
                //         "msg" => "",
                //         "timestamp" => 1702731661854,
                //         "data" => {
                //             "list" => array(
                //                 {
                //                     "symbol" => "BNB-USDT",
                //                     "side" => "BUY",
                //                     "positionSide" => "LONG",
                //                     "priceType" => "constant",
                //                     "priceVariance" => "2000",
                //                     "triggerPrice" => "68000",
                //                     "interval" => 8,
                //                     "amountPerOrder" => "0.111",
                //                     "totalAmount" => "0.511",
                //                     "orderStatus" => "Running",
                //                     "executedQty" => "0.1",
                //                     "duration" => 800,
                //                     "maxDuration" => 9000,
                //                     "createdTime" => 1702731661854,
                //                     "updateTime" => 1702731661854
                //                 }
                //             ),
                //             "total" => 1
                //         }
                //     }
                //
            } elseif ($subType === 'inverse') {
                $response = $this->cswapV1PrivateGetTradeOrderHistory ($this->extend($request, $params));
                //
                //     {
                //         "code" => 0,
                //         "msg" => "",
                //         "data" => {
                //             "orders" => array(
                //                 array(
                //                     "symbol" => "SOL-USD",
                //                     "orderId" => "1816002957423951872",
                //                     "side" => "BUY",
                //                     "positionSide" => "LONG",
                //                     "type" => "LIMIT",
                //                     "quantity" => 1,
                //                     "origQty" => "10.00000000",
                //                     "price" => "150.000",
                //                     "executedQty" => "0.00000000",
                //                     "avgPrice" => "0.000",
                //                     "cumQuote" => "",
                //                     "stopPrice" => "0.000",
                //                     "profit" => "0.0000",
                //                     "commission" => "0.000000",
                //                     "status" => "Filled",
                //                     "time" => 1721803819000,
                //                     "updateTime" => 1721803856000,
                //                     "clientOrderId" => "",
                //                     "leverage" => "",
                //                     "takeProfit" => array(
                //                         "type" => "",
                //                         "quantity" => 0,
                //                         "stopPrice" => 0,
                //                         "price" => 0,
                //                         "workingType" => "",
                //                         "stopGuaranteed" => ""
                //                     ),
                //                     "stopLoss" => array(
                //                         "type" => "",
                //                         "quantity" => 0,
                //                         "stopPrice" => 0,
                //                         "price" => 0,
                //                         "workingType" => "",
                //                         "stopGuaranteed" => ""
                //                     ),
                //                     "advanceAttr" => 0,
                //                     "positionID" => 0,
                //                     "takeProfitEntrustPrice" => 0,
                //                     "stopLossEntrustPrice" => 0,
                //                     "orderType" => "",
                //                     "workingType" => "MARK_PRICE"
                //                 ),
                //             )
                //         }
                //     }
                //
            } else {
                $response = $this->swapV2PrivateGetTradeAllOrders ($this->extend($request, $params));
                //
                //     {
                //         "code" => 0,
                //         "msg" => "",
                //         "data" => {
                //             "orders" => array(
                //                 array(
                //                     "symbol" => "LINK-USDT",
                //                     "orderId" => 1585839271162413056,
                //                     "side" => "BUY",
                //                     "positionSide" => "LONG",
                //                     "type" => "TRIGGER_MARKET",
                //                     "origQty" => "5.0",
                //                     "price" => "9",
                //                     "executedQty" => "0.0",
                //                     "avgPrice" => "0",
                //                     "cumQuote" => "0",
                //                     "stopPrice" => "5",
                //                     "profit" => "0.0000",
                //                     "commission" => "0.000000",
                //                     "status" => "CANCELLED",
                //                     "time" => 1667631605000,
                //                     "updateTime" => 1667631605000
                //                 ),
                //             )
                //         }
                //     }
                //
            }
        }
        $data = $this->safe_dict($response, 'data', array());
        $orders = $this->safe_list_2($data, 'orders', 'list', array());
        return $this->parse_orders($orders, $market, $since, $limit);
    }

    public function transfer(string $code, float $amount, string $fromAccount, string $toAccount, $params = array ()): array {
        /**
         * transfer $currency internally between wallets on the same account
         *
         * @see https://bingx-api.github.io/docs/#/en-us/common/account-api.html#Asset%20Transfer%20New
         *
         * @param {string} $code unified $currency $code
         * @param {float} $amount amount to transfer
         * @param {string} $fromAccount account to transfer from (spot, swap, futures, or funding)
         * @param {string} $toAccount account to transfer to (spot, swap (linear or inverse), future, or funding)
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array} a ~@link https://docs.ccxt.com/#/?id=transfer-structure transfer structure~
         */
        $this->load_markets();
        $currency = $this->currency($code);
        $accountsByType = $this->safe_dict($this->options, 'accountsByType', array());
        $subType = null;
        list($subType, $params) = $this->handle_sub_type_and_params('transfer', null, $params);
        $fromId = $this->safe_string($accountsByType, $fromAccount, $fromAccount);
        $toId = $this->safe_string($accountsByType, $toAccount, $toAccount);
        if ($fromId === 'swap') {
            if ($subType === 'inverse') {
                $fromId = 'coinMPerp';
            } else {
                $fromId = 'USDTMPerp';
            }
        }
        if ($toId === 'swap') {
            if ($subType === 'inverse') {
                $toId = 'coinMPerp';
            } else {
                $toId = 'USDTMPerp';
            }
        }
        $request = array(
            'fromAccount' => $fromId,
            'toAccount' => $toId,
            'asset' => $currency['id'],
            'amount' => $this->currency_to_precision($code, $amount),
        );
        $response = $this->apiAssetV1PrivatePostTransfer ($this->extend($request, $params));
        //
        //     {
        //         "tranId" => 1933130865269936128,
        //         "transferId" => "1051450703949464903736"
        //     }
        //
        return array(
            'info' => $response,
            'id' => $this->safe_string($response, 'transferId'),
            'timestamp' => null,
            'datetime' => null,
            'currency' => $code,
            'amount' => $amount,
            'fromAccount' => $fromAccount,
            'toAccount' => $toAccount,
            'status' => null,
        );
    }

    public function fetch_transfers(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()): array {
        /**
         * fetch a history of internal transfers made on an account
         *
         * @see https://bingx-api.github.io/docs/#/en-us/common/account-api.html#Asset%20transfer%20records%20new
         *
         * @param {string} [$code] unified $currency $code of the $currency transferred
         * @param {int} [$since] the earliest time in ms to fetch transfers for
         * @param {int} [$limit] the maximum number of transfers structures to retrieve (default 10, max 100)
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @param {string} $params->fromAccount (mandatory) transfer from (spot, swap (linear or inverse), future, or funding)
         * @param {string} $params->toAccount (mandatory) transfer to (spot, swap(linear or inverse), future, or funding)
         * @param {boolean} [$params->paginate] whether to $paginate the results (default false)
         * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=transfer-structure transfer structures~
         */
        $this->load_markets();
        $request = array();
        $currency = null;
        if ($code !== null) {
            $currency = $this->currency($code);
        }
        $accountsByType = $this->safe_dict($this->options, 'accountsByType', array());
        $fromAccount = $this->safe_string($params, 'fromAccount');
        $toAccount = $this->safe_string($params, 'toAccount');
        $fromId = $this->safe_string($accountsByType, $fromAccount, $fromAccount);
        $toId = $this->safe_string($accountsByType, $toAccount, $toAccount);
        if ($fromId === null || $toId === null) {
            throw new ExchangeError($this->id . ' $fromAccount & $toAccount parameters are required');
        }
        if ($fromAccount !== null) {
            $request['fromAccount'] = $fromId;
        }
        if ($toAccount !== null) {
            $request['toAccount'] = $toId;
        }
        $params = $this->omit($params, array( 'fromAccount', 'toAccount' ));
        $maxLimit = 100;
        $paginate = false;
        list($paginate, $params) = $this->handle_option_and_params($params, 'fetchTransfers', 'paginate', false);
        if ($paginate) {
            return $this->fetch_paginated_call_dynamic('fetchTransfers', null, $since, $limit, $params, $maxLimit);
        }
        if ($since !== null) {
            $request['startTime'] = $since;
        }
        if ($limit !== null) {
            $request['pageSize'] = $limit;
        }
        list($request, $params) = $this->handle_until_option('endTime', $request, $params);
        $response = $this->apiV3PrivateGetAssetTransferRecord ($this->extend($request, $params));
        //
        //     {
        //         "total" => 2,
        //         "rows" => array(
        //             {
        //                 "asset" => "LTC",
        //                 "amount" => "0.05000000000000000000",
        //                 "status" => "CONFIRMED",
        //                 "transferId" => "1051461075661819338791",
        //                 "timestamp" => 1752202092000,
        //                 "fromAccount" => "spot",
        //                 "toAccount" => "USDTMPerp"
        //             }
        //         )
        //     }
        //
        $rows = $this->safe_list($response, 'rows', array());
        return $this->parse_transfers($rows, $currency, $since, $limit);
    }

    public function parse_transfer(array $transfer, ?array $currency = null): array {
        $tranId = $this->safe_string($transfer, 'transferId');
        $timestamp = $this->safe_integer($transfer, 'timestamp');
        $currencyId = $this->safe_string($transfer, 'asset');
        $currencyCode = $this->safe_currency_code($currencyId, $currency);
        $status = $this->safe_string($transfer, 'status');
        $accountsById = $this->safe_dict($this->options, 'accountsById', array());
        $fromId = $this->safe_string($transfer, 'fromAccount');
        $toId = $this->safe_string($transfer, 'toAccount');
        $fromAccount = $this->safe_string($accountsById, $fromId, $fromId);
        $toAccount = $this->safe_string($accountsById, $toId, $toId);
        return array(
            'info' => $transfer,
            'id' => $tranId,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'currency' => $currencyCode,
            'amount' => $this->safe_number($transfer, 'amount'),
            'fromAccount' => $fromAccount,
            'toAccount' => $toAccount,
            'status' => $this->parse_transfer_status($status),
        );
    }

    public function parse_transfer_status(?string $status): string {
        $statuses = array(
            'CONFIRMED' => 'ok',
        );
        return $this->safe_string($statuses, $status, $status);
    }

    public function fetch_deposit_addresses_by_network(string $code, $params = array ()): array {
        /**
         * fetch the deposit addresses for a $currency associated with this account
         *
         * @see https://bingx-api.github.io/docs/#/en-us/common/wallet-api.html#Query%20Main%20Account%20Deposit%20Address
         *
         * @param {string} $code unified $currency $code
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array} a dictionary ~@link https://docs.ccxt.com/#/?id=address-structure address structures~, indexed by the network
         */
        $this->load_markets();
        $currency = $this->currency($code);
        $defaultRecvWindow = $this->safe_integer($this->options, 'recvWindow');
        $recvWindow = $this->safe_integer(array($this, 'parse_params'), 'recvWindow', $defaultRecvWindow);
        $request = array(
            'coin' => $currency['id'],
            'offset' => 0,
            'limit' => 1000,
            'recvWindow' => $recvWindow,
        );
        $response = $this->walletsV1PrivateGetCapitalDepositAddress ($this->extend($request, $params));
        //
        //     {
        //         "code" => "0",
        //         "timestamp" => "1695200226859",
        //         "data" => {
        //           "data" => array(
        //             {
        //               "coinId" => "799",
        //               "coin" => "USDT",
        //               "network" => "BEP20",
        //               "address" => "6a7eda2817462dabb6493277a2cfe0f5c3f2550b",
        //               "tag" => ''
        //             }
        //           ),
        //           "total" => "1"
        //         }
        //     }
        //
        $data = $this->safe_list($this->safe_dict($response, 'data'), 'data');
        $parsed = $this->parse_deposit_addresses($data, [ $currency['code'] ], false);
        return $this->index_by($parsed, 'network');
    }

    public function fetch_deposit_address(string $code, $params = array ()): array {
        /**
         * fetch the deposit address for a currency associated with this account
         *
         * @see https://bingx-api.github.io/docs/#/en-us/common/wallet-api.html#Query%20Main%20Account%20Deposit%20Address
         *
         * @param {string} $code unified currency $code
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @param {string} [$params->network] The chain of currency. This only apply for multi-chain currency, and there is no need for single chain currency
         * @return {array} an ~@link https://docs.ccxt.com/#/?id=address-structure address structure~
         */
        $network = $this->safe_string($params, 'network');
        $params = $this->omit($params, array( 'network' ));
        $addressStructures = $this->fetch_deposit_addresses_by_network($code, $params);
        if ($network !== null) {
            return $this->safe_dict($addressStructures, $network);
        } else {
            $options = $this->safe_dict($this->options, 'defaultNetworks');
            $defaultNetworkForCurrency = $this->safe_string($options, $code);
            if ($defaultNetworkForCurrency !== null) {
                return $this->safe_dict($addressStructures, $defaultNetworkForCurrency);
            } else {
                $keys = is_array($addressStructures) ? array_keys($addressStructures) : array();
                $key = $this->safe_string($keys, 0);
                return $this->safe_dict($addressStructures, $key);
            }
        }
    }

    public function parse_deposit_address($depositAddress, ?array $currency = null): array {
        //
        // {
        //     "coinId":"4",
        //     "coin":"USDT",
        //     "network":"OMNI",
        //     "address":"1HXyx8HVQRY7Nhqz63nwnRB7SpS9xQPzLN",
        //     "addressWithPrefix":"1HXyx8HVQRY7Nhqz63nwnRB7SpS9xQPzLN"
        // }
        //
        $tag = $this->safe_string($depositAddress, 'tag');
        $currencyId = $this->safe_string($depositAddress, 'coin');
        $currency = $this->safe_currency($currencyId, $currency);
        $code = $currency['code'];
        $address = $this->safe_string($depositAddress, 'addressWithPrefix');
        $networkdId = $this->safe_string($depositAddress, 'network');
        $networkCode = $this->network_id_to_code($networkdId, $code);
        $this->check_address($address);
        return array(
            'info' => $depositAddress,
            'currency' => $code,
            'network' => $networkCode,
            'address' => $address,
            'tag' => $tag,
        );
    }

    public function fetch_deposits(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()): array {
        /**
         * fetch all deposits made to an account
         *
         * @see https://bingx-api.github.io/docs/#/spot/account-api.html#Deposit%20History(supporting%20network)
         *
         * @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 deposits structures to retrieve
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=transaction-structure transaction structures~
         */
        $this->load_markets();
        $request = array(
        );
        $currency = null;
        if ($code !== null) {
            $currency = $this->currency($code);
            $request['coin'] = $currency['id'];
        }
        if ($since !== null) {
            $request['startTime'] = $since;
        }
        if ($limit !== null) {
            $request['limit'] = $limit; // default 1000
        }
        $response = $this->spotV3PrivateGetCapitalDepositHisrec ($this->extend($request, $params));
        //
        //    array(
        //        array(
        //            "amount":"0.00999800",
        //            "coin":"PAXG",
        //            "network":"ETH",
        //            "status":1,
        //            "address":"0x788cabe9236ce061e5a892e1a59395a81fc8d62c",
        //            "addressTag":"",
        //            "txId":"0xaad4654a3234aa6118af9b4b335f5ae81c360b2394721c019b5d1e75328b09f3",
        //            "insertTime":1599621997000,
        //            "transferType":0,
        //            "unlockConfirm":"12/12", // confirm times for unlocking
        //            "confirmTimes":"12/12"
        //        ),
        //    )
        //
        return $this->parse_transactions($response, $currency, $since, $limit);
    }

    public function fetch_withdrawals(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()): array {
        /**
         * fetch all withdrawals made from an account
         *
         * @see https://bingx-api.github.io/docs/#/spot/account-api.html#Withdraw%20History%20(supporting%20network)
         *
         * @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 withdrawals structures to retrieve
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=transaction-structure transaction structures~
         */
        $this->load_markets();
        $request = array(
        );
        $currency = null;
        if ($code !== null) {
            $currency = $this->currency($code);
            $request['coin'] = $currency['id'];
        }
        if ($since !== null) {
            $request['startTime'] = $since;
        }
        if ($limit !== null) {
            $request['limit'] = $limit; // default 1000
        }
        $response = $this->spotV3PrivateGetCapitalWithdrawHistory ($this->extend($request, $params));
        //
        //    array(
        //        array(
        //            "address" => "0x94df8b352de7f46f64b01d3666bf6e936e44ce60",
        //            "amount" => "8.91000000",
        //            "applyTime" => "2019-10-12 11:12:02",
        //            "coin" => "USDT",
        //            "id" => "b6ae22b3aa844210a7041aee7589627c",
        //            "withdrawOrderId" => "WITHDRAWtest123",
        //            "network" => "ETH",
        //            "transferType" => 0
        //            "status" => 6,
        //            "transactionFee" => "0.004",
        //            "confirmNo":3,
        //            "info" => "The address is not valid. Please confirm with the recipient",
        //            "txId" => "0xb5ef8c13b968a406cc62a93a8bd80f9e9a906ef1b3fcf20a2e48573c17659268"
        //        ),
        //    )
        //
        return $this->parse_transactions($response, $currency, $since, $limit);
    }

    public function parse_transaction(array $transaction, ?array $currency = null): array {
        //
        // fetchDeposits
        //
        //    {
        //        "amount":"0.00999800",
        //        "coin":"PAXG",
        //        "network":"ETH",
        //        "status":1,
        //        "address":"0x788cabe9236ce061e5a892e1a59395a81fc8d62c",
        //        "addressTag":"",
        //        "txId":"0xaad4654a3234aa6118af9b4b335f5ae81c360b2394721c019b5d1e75328b09f3",
        //        "insertTime":1599621997000,
        //        "transferType":0,
        //        "unlockConfirm":"12/12", // confirm times for unlocking
        //        "confirmTimes":"12/12"
        //    }
        //
        // fetchWithdrawals
        //
        //    {
        //        "address" => "0x94df8b352de7f46f64b01d3666bf6e936e44ce60",
        //        "amount" => "8.91000000",
        //        "applyTime" => "2019-10-12 11:12:02",
        //        "coin" => "USDT",
        //        "id" => "b6ae22b3aa844210a7041aee7589627c",
        //        "withdrawOrderId" => "WITHDRAWtest123",
        //        "network" => "ETH",
        //        "transferType" => 0
        //        "status" => 6,
        //        "transactionFee" => "0.004",
        //        "confirmNo":3,
        //        "info" => "The $address is not valid. Please confirm with the recipient",
        //        "txId" => "0xb5ef8c13b968a406cc62a93a8bd80f9e9a906ef1b3fcf20a2e48573c17659268"
        //    }
        //
        // withdraw
        //
        //     {
        //         "code":0,
        //         "timestamp":1705274263621,
        //         "data":{
        //             "id":"1264246141278773252"
        //         }
        //     }
        //
        // parse withdraw-$type output first...
        //
        $data = $this->safe_value($transaction, 'data');
        $dataId = ($data === null) ? null : $this->safe_string($data, 'id');
        $id = $this->safe_string($transaction, 'id', $dataId);
        $address = $this->safe_string($transaction, 'address');
        $tag = $this->safe_string($transaction, 'addressTag');
        $timestamp = $this->safe_integer_2($transaction, 'insertTime', 'timestamp');
        $datetime = $this->iso8601($timestamp);
        if ($timestamp === null) {
            $datetime = $this->safe_string($transaction, 'applyTime');
            $timestamp = $this->parse8601($datetime);
        }
        $network = $this->safe_string($transaction, 'network');
        $currencyId = $this->safe_string($transaction, 'coin');
        $code = $this->safe_currency_code($currencyId, $currency);
        if (($code !== null) && ($code !== $network) && mb_strpos($code, $network) !== false) {
            if ($network !== null) {
                $code = str_replace($network, '', $code);
            }
        }
        $rawType = $this->safe_string($transaction, 'transferType');
        $type = ($rawType === '0') ? 'deposit' : 'withdrawal';
        return array(
            'info' => $transaction,
            'id' => $id,
            'txid' => $this->safe_string($transaction, 'txId'),
            'type' => $type,
            'currency' => $code,
            'network' => $this->network_id_to_code($network),
            'amount' => $this->safe_number($transaction, 'amount'),
            'status' => $this->parse_transaction_status($this->safe_string($transaction, 'status')),
            'timestamp' => $timestamp,
            'datetime' => $datetime,
            'address' => $address,
            'addressFrom' => null,
            'addressTo' => $address,
            'tag' => $tag,
            'tagFrom' => $tag,
            'tagTo' => null,
            'updated' => null,
            'comment' => $this->safe_string($transaction, 'info'),
            'fee' => array(
                'currency' => $code,
                'cost' => $this->safe_number($transaction, 'transactionFee'),
                'rate' => null,
            ),
            'internal' => null,
        );
    }

    public function parse_transaction_status(string $status) {
        $statuses = array(
            '0' => 'pending',
            '1' => 'ok',
            '10' => 'pending',
            '20' => 'rejected',
            '30' => 'ok',
            '40' => 'rejected',
            '50' => 'ok',
            '60' => 'pending',
            '70' => 'rejected',
            '2' => 'pending',
            '3' => 'rejected',
            '4' => 'pending',
            '5' => 'rejected',
            '6' => 'ok',
        );
        return $this->safe_string($statuses, $status, $status);
    }

    public function set_margin_mode(string $marginMode, ?string $symbol = null, $params = array ()) {
        /**
         * set margin mode to 'cross' or 'isolated'
         *
         * @see https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Change%20Margin%20Type
         * @see https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#Set%20Margin%20Type
         *
         * @param {string} $marginMode 'cross' or 'isolated'
         * @param {string} $symbol unified $market $symbol
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array} response from the exchange
         */
        if ($symbol === null) {
            throw new ArgumentsRequired($this->id . ' setMarginMode() requires a $symbol argument');
        }
        $this->load_markets();
        $market = $this->market($symbol);
        if ($market['type'] !== 'swap') {
            throw new BadSymbol($this->id . ' setMarginMode() supports swap contracts only');
        }
        $marginMode = strtoupper($marginMode);
        if ($marginMode === 'CROSS') {
            $marginMode = 'CROSSED';
        }
        if ($marginMode !== 'ISOLATED' && $marginMode !== 'CROSSED') {
            throw new BadRequest($this->id . ' setMarginMode() $marginMode argument should be isolated or cross');
        }
        $request = array(
            'symbol' => $market['id'],
            'marginType' => $marginMode,
        );
        $subType = null;
        list($subType, $params) = $this->handle_sub_type_and_params('setMarginMode', $market, $params);
        if ($subType === 'inverse') {
            return $this->cswapV1PrivatePostTradeMarginType ($this->extend($request, $params));
        } else {
            return $this->swapV2PrivatePostTradeMarginType ($this->extend($request, $params));
        }
    }

    public function add_margin(string $symbol, float $amount, $params = array ()): array {
        $request = array(
            'type' => 1,
        );
        return $this->set_margin($symbol, $amount, $this->extend($request, $params));
    }

    public function reduce_margin(string $symbol, float $amount, $params = array ()): array {
        $request = array(
            'type' => 2,
        );
        return $this->set_margin($symbol, $amount, $this->extend($request, $params));
    }

    public function set_margin(string $symbol, float $amount, $params = array ()): array {
        /**
         * Either adds or reduces margin in an isolated position in order to set the margin to a specific value
         *
         * @see https://bingx-api.github.io/docs/#/swapV2/trade-api.html#Adjust%20isolated%20margin
         *
         * @param {string} $symbol unified $market $symbol of the $market to set margin in
         * @param {float} $amount the $amount to set the margin to
         * @param {array} [$params] parameters specific to the bingx api endpoint
         * @return {array} A ~@link https://docs.ccxt.com/#/?id=add-margin-structure margin structure~
         */
        $type = $this->safe_integer($params, 'type'); // 1 increase margin 2 decrease margin
        if ($type === null) {
            throw new ArgumentsRequired($this->id . ' setMargin() requires a $type parameter either 1 (increase margin) or 2 (decrease margin)');
        }
        if (!$this->in_array($type, array( 1, 2 ))) {
            throw new ArgumentsRequired($this->id . ' setMargin() requires a $type parameter either 1 (increase margin) or 2 (decrease margin)');
        }
        $this->load_markets();
        $market = $this->market($symbol);
        $request = array(
            'symbol' => $market['id'],
            'amount' => $this->amount_to_precision($market['symbol'], $amount),
            'type' => $type,
        );
        $response = $this->swapV2PrivatePostTradePositionMargin ($this->extend($request, $params));
        //
        //    {
        //        "code" => 0,
        //        "msg" => "",
        //        "amount" => 1,
        //        "type" => 1
        //    }
        //
        return $this->parse_margin_modification($response, $market);
    }

    public function parse_margin_modification(array $data, ?array $market = null): array {
        //
        //    {
        //        "code" => 0,
        //        "msg" => "",
        //        "amount" => 1,
        //        "type" => 1
        //    }
        //
        $type = $this->safe_string($data, 'type');
        return array(
            'info' => $data,
            'symbol' => $this->safe_string($market, 'symbol'),
            'type' => ($type === '1') ? 'add' : 'reduce',
            'marginMode' => 'isolated',
            'amount' => $this->safe_number($data, 'amount'),
            'total' => $this->safe_number($data, 'margin'),
            'code' => $this->safe_string($market, 'settle'),
            'status' => null,
            'timestamp' => null,
            'datetime' => null,
        );
    }

    public function fetch_leverage(string $symbol, $params = array ()): array {
        /**
         * fetch the set leverage for a $market
         *
         * @see https://bingx-api.github.io/docs/#/swapV2/trade-api.html#Query%20Leverage
         * @see https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#Query%20Leverage
         *
         * @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=leverage-structure leverage structure~
         */
        $this->load_markets();
        $market = $this->market($symbol);
        $request = array(
            'symbol' => $market['id'],
        );
        $response = null;
        if ($market['inverse']) {
            $response = $this->cswapV1PrivateGetTradeLeverage ($this->extend($request, $params));
            //
            //     {
            //         "code" => 0,
            //         "msg" => "",
            //         "timestamp" => 1720683803391,
            //         "data" => {
            //             "symbol" => "SOL-USD",
            //             "longLeverage" => 5,
            //             "shortLeverage" => 5,
            //             "maxLongLeverage" => 50,
            //             "maxShortLeverage" => 50,
            //             "availableLongVol" => "4000000",
            //             "availableShortVol" => "4000000"
            //         }
            //     }
            //
        } else {
            $response = $this->swapV2PrivateGetTradeLeverage ($this->extend($request, $params));
            //
            //     {
            //         "code" => 0,
            //         "msg" => "",
            //         "data" => {
            //             "longLeverage" => 5,
            //             "shortLeverage" => 5,
            //             "maxLongLeverage" => 125,
            //             "maxShortLeverage" => 125,
            //             "availableLongVol" => "0.0000",
            //             "availableShortVol" => "0.0000",
            //             "availableLongVal" => "0.0",
            //             "availableShortVal" => "0.0",
            //             "maxPositionLongVal" => "0.0",
            //             "maxPositionShortVal" => "0.0"
            //         }
            //     }
            //
        }
        $data = $this->safe_dict($response, 'data', array());
        return $this->parse_leverage($data, $market);
    }

    public function parse_leverage(array $leverage, ?array $market = null): array {
        //
        // linear swap
        //
        //     {
        //         "longLeverage" => 5,
        //         "shortLeverage" => 5,
        //         "maxLongLeverage" => 125,
        //         "maxShortLeverage" => 125,
        //         "availableLongVol" => "0.0000",
        //         "availableShortVol" => "0.0000",
        //         "availableLongVal" => "0.0",
        //         "availableShortVal" => "0.0",
        //         "maxPositionLongVal" => "0.0",
        //         "maxPositionShortVal" => "0.0"
        //     }
        //
        // inverse swap
        //
        //     {
        //         "symbol" => "SOL-USD",
        //         "longLeverage" => 5,
        //         "shortLeverage" => 5,
        //         "maxLongLeverage" => 50,
        //         "maxShortLeverage" => 50,
        //         "availableLongVol" => "4000000",
        //         "availableShortVol" => "4000000"
        //     }
        //
        $marketId = $this->safe_string($leverage, 'symbol');
        return array(
            'info' => $leverage,
            'symbol' => $this->safe_symbol($marketId, $market),
            'marginMode' => null,
            'longLeverage' => $this->safe_integer($leverage, 'longLeverage'),
            'shortLeverage' => $this->safe_integer($leverage, 'shortLeverage'),
        );
    }

    public function set_leverage(int $leverage, ?string $symbol = null, $params = array ()) {
        /**
         * set the level of $leverage for a $market
         *
         * @see https://bingx-api.github.io/docs/#/swapV2/trade-api.html#Switch%20Leverage
         * @see https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#Modify%20Leverage
         *
         * @param {float} $leverage the rate of $leverage
         * @param {string} $symbol unified $market $symbol
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @param {string} [$params->side] hedged => ['long' or 'short']. one way => ['both']
         * @return {array} response from the exchange
         */
        if ($symbol === null) {
            throw new ArgumentsRequired($this->id . ' setLeverage() requires a $symbol argument');
        }
        $side = $this->safe_string_upper($params, 'side');
        $this->check_required_argument('setLeverage', $side, 'side', array( 'LONG', 'SHORT', 'BOTH' ));
        $this->load_markets();
        $market = $this->market($symbol);
        $request = array(
            'symbol' => $market['id'],
            'side' => $side,
            'leverage' => $leverage,
        );
        if ($market['inverse']) {
            return $this->cswapV1PrivatePostTradeLeverage ($this->extend($request, $params));
            //
            //     {
            //         "code" => 0,
            //         "msg" => "",
            //         "timestamp" => 1720725058059,
            //         "data" => {
            //             "symbol" => "SOL-USD",
            //             "longLeverage" => 10,
            //             "shortLeverage" => 5,
            //             "maxLongLeverage" => 50,
            //             "maxShortLeverage" => 50,
            //             "availableLongVol" => "4000000",
            //             "availableShortVol" => "4000000"
            //         }
            //     }
            //
        } else {
            return $this->swapV2PrivatePostTradeLeverage ($this->extend($request, $params));
            //
            //     {
            //         "code" => 0,
            //         "msg" => "",
            //         "data" => {
            //             "leverage" => 10,
            //             "symbol" => "BTC-USDT",
            //             "availableLongVol" => "0.0000",
            //             "availableShortVol" => "0.0000",
            //             "availableLongVal" => "0.0",
            //             "availableShortVal" => "0.0",
            //             "maxPositionLongVal" => "0.0",
            //             "maxPositionShortVal" => "0.0"
            //         }
            //     }
            //
        }
    }

    public function fetch_my_trades(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) {
        /**
         * fetch all trades made by the user
         *
         * @see https://bingx-api.github.io/docs/#/en-us/spot/trade-api.html#Query%20transaction%20details
         * @see https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Query%20historical%20transaction%20orders
         * @see https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Query%20historical%20transaction%20details
         * @see https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#Query%20Order%20Trade%20Detail
         *
         * @param {string} [$symbol] unified $market $symbol
         * @param {int} [$since] the earliest time in ms to fetch trades for
         * @param {int} [$limit] the maximum number of trades structures to retrieve
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @param {int} [$params->until] timestamp in ms for the ending date filter, default is null
         * @param {string} $params->trandingUnit COIN (directly represent assets such and ETH) or CONT (represents the number of contract sheets)
         * @param {string} $params->orderId the order id required for inverse swap
         * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=trade-structure trade structures~
         */
        if ($symbol === null) {
            throw new ArgumentsRequired($this->id . ' fetchMyTrades() requires a $symbol argument');
        }
        $this->load_markets();
        $market = $this->market($symbol);
        $request = array();
        $fills = null;
        $response = null;
        $subType = null;
        list($subType, $params) = $this->handle_sub_type_and_params('fetchMyTrades', $market, $params);
        if ($subType === 'inverse') {
            $orderId = $this->safe_string($params, 'orderId');
            if ($orderId === null) {
                throw new ArgumentsRequired($this->id . ' fetchMyTrades() requires an $orderId argument for inverse swap trades');
            }
            $response = $this->cswapV1PrivateGetTradeAllFillOrders ($this->extend($request, $params));
            $fills = $this->safe_list($response, 'data', array());
            //
            //     {
            //         "code" => 0,
            //         "msg" => "",
            //         "timestamp" => 1722147756019,
            //         "data" => array(
            //             {
            //                 "orderId" => "1817441228670648320",
            //                 "symbol" => "SOL-USD",
            //                 "type" => "MARKET",
            //                 "side" => "BUY",
            //                 "positionSide" => "LONG",
            //                 "tradeId" => "97244554",
            //                 "volume" => "2",
            //                 "tradePrice" => "182.652",
            //                 "amount" => "20.00000000",
            //                 "realizedPnl" => "0.00000000",
            //                 "commission" => "-0.00005475",
            //                 "currency" => "SOL",
            //                 "buyer" => true,
            //                 "maker" => false,
            //                 "tradeTime" => 1722146730000
            //             }
            //         )
            //     }
            //
        } else {
            $request['symbol'] = $market['id'];
            $now = $this->milliseconds();
            if ($since !== null) {
                $startTimeReq = $market['spot'] ? 'startTime' : 'startTs';
                $request[$startTimeReq] = $since;
            } elseif ($market['swap']) {
                $request['startTs'] = $now - 30 * 24 * 60 * 60 * 1000; // 30 days for swap
            }
            $until = $this->safe_integer($params, 'until');
            $params = $this->omit($params, 'until');
            if ($until !== null) {
                $endTimeReq = $market['spot'] ? 'endTime' : 'endTs';
                $request[$endTimeReq] = $until;
            } elseif ($market['swap']) {
                $request['endTs'] = $now;
            }
            if ($market['spot']) {
                if ($limit !== null) {
                    $request['limit'] = $limit; // default 500, maximum 1000
                }
                $response = $this->spotV1PrivateGetTradeMyTrades ($this->extend($request, $params));
                $data = $this->safe_dict($response, 'data', array());
                $fills = $this->safe_list($data, 'fills', array());
                //
                //     {
                //         "code" => 0,
                //         "msg" => "",
                //         "debugMsg" => "",
                //         "data" => {
                //             "fills" => array(
                //                 {
                //                     "symbol" => "LTC-USDT",
                //                     "id" => 36237072,
                //                     "orderId" => 1674069326895775744,
                //                     "price" => "85.891",
                //                     "qty" => "0.0582",
                //                     "quoteQty" => "4.9988562000000005",
                //                     "commission" => -0.00005820000000000001,
                //                     "commissionAsset" => "LTC",
                //                     "time" => 1687964205000,
                //                     "isBuyer" => true,
                //                     "isMaker" => false
                //                 }
                //             )
                //         }
                //     }
                //
            } else {
                $tradingUnit = $this->safe_string_upper($params, 'tradingUnit', 'CONT');
                $params = $this->omit($params, 'tradingUnit');
                $request['tradingUnit'] = $tradingUnit;
                $response = $this->swapV2PrivateGetTradeAllFillOrders ($this->extend($request, $params));
                $data = $this->safe_dict($response, 'data', array());
                $fills = $this->safe_list($data, 'fill_orders', array());
                //
                //    {
                //       "code" => "0",
                //       "msg" => '',
                //       "data" => { fill_orders => array(
                //          {
                //              "volume" => "0.1",
                //              "price" => "106.75",
                //              "amount" => "10.6750",
                //              "commission" => "-0.0053",
                //              "currency" => "USDT",
                //              "orderId" => "1676213270274379776",
                //              "liquidatedPrice" => "0.00",
                //              "liquidatedMarginRatio" => "0.00",
                //              "filledTime" => "2023-07-04T20:56:01.000+0800"
                //          }
                //        )
                //      }
                //    }
                //
            }
        }
        return $this->parse_trades($fills, $market, $since, $limit, $params);
    }

    public function parse_deposit_withdraw_fee($fee, ?array $currency = null) {
        //
        // currencie structure
        //
        $networks = $this->safe_dict($fee, 'networks', array());
        $networkCodes = is_array($networks) ? array_keys($networks) : array();
        $networksLength = count($networkCodes);
        $result = array(
            'info' => $networks,
            'withdraw' => array(
                'fee' => null,
                'percentage' => null,
            ),
            'deposit' => array(
                'fee' => null,
                'percentage' => null,
            ),
            'networks' => array(),
        );
        if ($networksLength !== 0) {
            for ($i = 0; $i < $networksLength; $i++) {
                $networkCode = $networkCodes[$i];
                $network = $networks[$networkCode];
                $result['networks'][$networkCode] = array(
                    'deposit' => array( 'fee' => null, 'percentage' => null ),
                    'withdraw' => array( 'fee' => $this->safe_number($network, 'fee'), 'percentage' => false ),
                );
                if ($networksLength === 1) {
                    $result['withdraw']['fee'] = $this->safe_number($network, 'withdrawFee');
                    $result['withdraw']['percentage'] = false;
                }
            }
        }
        return $result;
    }

    public function fetch_deposit_withdraw_fees(?array $codes = null, $params = array ()) {
        /**
         * fetch deposit and withdraw fees
         *
         * @see https://bingx-api.github.io/docs/#/common/account-api.html#All%20Coins'%20Information
         *
         * @param {string[]|null} $codes list of unified currency $codes
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array} a list of ~@link https://docs.ccxt.com/#/?id=fee-structure fee structures~
         */
        $this->load_markets();
        $response = $this->fetch_currencies($params);
        $depositWithdrawFees = array();
        $responseCodes = is_array($response) ? array_keys($response) : array();
        for ($i = 0; $i < count($responseCodes); $i++) {
            $code = $responseCodes[$i];
            if (($codes === null) || ($this->in_array($code, $codes))) {
                $entry = $response[$code];
                $depositWithdrawFees[$code] = $this->parse_deposit_withdraw_fee($entry);
            }
        }
        return $depositWithdrawFees;
    }

    public function withdraw(string $code, float $amount, string $address, ?string $tag = null, $params = array ()): array {
        /**
         * make a withdrawal
         *
         * @see https://bingx-api.github.io/docs/#/en-us/spot/wallet-api.html#Withdraw
         *
         * @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 exchange API endpoint
         * @param {int} [$params->walletType] 1 fund account, 2 standard account, 3 perpetual account, 15 spot account
         * @return {array} a ~@link https://docs.ccxt.com/#/?id=transaction-structure transaction structure~
         */
        list($tag, $params) = $this->handle_withdraw_tag_and_params($tag, $params);
        $this->check_address($address);
        $this->load_markets();
        $currency = $this->currency($code);
        $walletType = $this->safe_integer($params, 'walletType', 1);
        $request = array(
            'coin' => $currency['id'],
            'address' => $address,
            'amount' => $this->currency_to_precision($code, $amount),
            'walletType' => $walletType,
        );
        $network = $this->safe_string_upper($params, 'network');
        if ($network !== null) {
            $request['network'] = $this->network_code_to_id($network);
        }
        if ($tag !== null) {
            $request['addressTag'] = $tag;
        }
        $params = $this->omit($params, array( 'walletType', 'network' ));
        $response = $this->walletsV1PrivatePostCapitalWithdrawApply ($this->extend($request, $params));
        $data = $this->safe_value($response, 'data');
        //    {
        //        "code":0,
        //        "timestamp":1689258953651,
        //        "data":{
        //           "id":"1197073063359000577"
        //        }
        //    }
        return $this->parse_transaction($data);
    }

    public function parse_params($params) {
        // $sortedParams = $this->keysort($params);
        $rawKeys = is_array($params) ? array_keys($params) : array();
        $keys = $this->sort($rawKeys);
        for ($i = 0; $i < count($keys); $i++) {
            $key = $keys[$i];
            $value = $params[$key];
            if (gettype($value) === 'array' && array_keys($value) === array_keys(array_keys($value))) {
                $arrStr = '[';
                for ($j = 0; $j < count($value); $j++) {
                    $arrayElement = $value[$j];
                    if ($j > 0) {
                        $arrStr .= ',';
                    }
                    $arrStr .= (string) $arrayElement;
                }
                $arrStr .= ']';
                $params[$key] = $arrStr;
            }
        }
        return $params;
    }

    public function fetch_my_liquidations(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) {
        /**
         * retrieves the users liquidated positions
         *
         * @see https://bingx-api.github.io/docs/#/swapV2/trade-api.html#User's%20Force%20Orders
         * @see https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#Query%20force%20orders
         *
         * @param {string} [$symbol] unified CCXT $market $symbol
         * @param {int} [$since] the earliest time in ms to fetch $liquidations for
         * @param {int} [$limit] the maximum number of liquidation structures to retrieve
         * @param {array} [$params] exchange specific parameters for the bingx api endpoint
         * @param {int} [$params->until] timestamp in ms of the latest liquidation
         * @return {array} an array of ~@link https://docs.ccxt.com/#/?id=liquidation-structure liquidation structures~
         */
        $this->load_markets();
        $request = array(
            'autoCloseType' => 'LIQUIDATION',
        );
        list($request, $params) = $this->handle_until_option('endTime', $request, $params);
        $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;
        }
        $subType = null;
        list($subType, $params) = $this->handle_sub_type_and_params('fetchMyLiquidations', $market, $params);
        $response = null;
        $liquidations = null;
        if ($subType === 'inverse') {
            $response = $this->cswapV1PrivateGetTradeForceOrders ($this->extend($request, $params));
            //
            //     {
            //         "code" => 0,
            //         "msg" => "",
            //         "timestamp" => 1721280071678,
            //         "data" => array(
            //             {
            //                 "orderId" => "string",
            //                 "symbol" => "string",
            //                 "type" => "string",
            //                 "side" => "string",
            //                 "positionSide" => "string",
            //                 "price" => "string",
            //                 "quantity" => "float64",
            //                 "stopPrice" => "string",
            //                 "workingType" => "string",
            //                 "status" => "string",
            //                 "time" => "int64",
            //                 "avgPrice" => "string",
            //                 "executedQty" => "string",
            //                 "profit" => "string",
            //                 "commission" => "string",
            //                 "updateTime" => "string"
            //             }
            //         )
            //     }
            //
            $liquidations = $this->safe_list($response, 'data', array());
        } else {
            $response = $this->swapV2PrivateGetTradeForceOrders ($this->extend($request, $params));
            //
            //     {
            //         "code" => 0,
            //         "msg" => "",
            //         "data" => {
            //             "orders" => array(
            //                 array(
            //                     "time" => "int64",
            //                     "symbol" => "string",
            //                     "side" => "string",
            //                     "type" => "string",
            //                     "positionSide" => "string",
            //                     "cumQuote" => "string",
            //                     "status" => "string",
            //                     "stopPrice" => "string",
            //                     "price" => "string",
            //                     "origQty" => "string",
            //                     "avgPrice" => "string",
            //                     "executedQty" => "string",
            //                     "orderId" => "int64",
            //                     "profit" => "string",
            //                     "commission" => "string",
            //                     "workingType" => "string",
            //                     "updateTime" => "int64"
            //                 ),
            //             )
            //         }
            //     }
            //
            $data = $this->safe_dict($response, 'data', array());
            $liquidations = $this->safe_list($data, 'orders', array());
        }
        return $this->parse_liquidations($liquidations, $market, $since, $limit);
    }

    public function parse_liquidation($liquidation, ?array $market = null) {
        //
        //     {
        //         "time" => "int64",
        //         "symbol" => "string",
        //         "side" => "string",
        //         "type" => "string",
        //         "positionSide" => "string",
        //         "cumQuote" => "string",
        //         "status" => "string",
        //         "stopPrice" => "string",
        //         "price" => "string",
        //         "origQty" => "string",
        //         "avgPrice" => "string",
        //         "executedQty" => "string",
        //         "orderId" => "int64",
        //         "profit" => "string",
        //         "commission" => "string",
        //         "workingType" => "string",
        //         "updateTime" => "int64"
        //     }
        //
        $marketId = $this->safe_string($liquidation, 'symbol');
        $timestamp = $this->safe_integer($liquidation, 'time');
        $contractsString = $this->safe_string($liquidation, 'executedQty');
        $contractSizeString = $this->safe_string($market, 'contractSize');
        $priceString = $this->safe_string($liquidation, 'avgPrice');
        $baseValueString = Precise::string_mul($contractsString, $contractSizeString);
        $quoteValueString = Precise::string_mul($baseValueString, $priceString);
        return $this->safe_liquidation(array(
            'info' => $liquidation,
            'symbol' => $this->safe_symbol($marketId, $market),
            'contracts' => $this->parse_number($contractsString),
            'contractSize' => $this->parse_number($contractSizeString),
            'price' => $this->parse_number($priceString),
            'baseValue' => $this->parse_number($baseValueString),
            'quoteValue' => $this->parse_number($quoteValueString),
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
        ));
    }

    public function close_position(string $symbol, ?string $side = null, $params = array ()): array {
        /**
         * closes open positions for a $market
         *
         * @see https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#One-Click%20Close%20All%20Positions
         * @see https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#Close%20all%20positions%20in%20bulk
         *
         * @param {string} $symbol Unified CCXT $market $symbol
         * @param {string} [$side] not used by bingx
         * @param {array} [$params] extra parameters specific to the bingx api endpoint
         * @param {string|null} [$params->positionId] the id of the position you would like to close
         * @return {array} an ~@link https://docs.ccxt.com/#/?id=order-structure order structure~
         */
        $this->load_markets();
        $market = $this->market($symbol);
        $positionId = $this->safe_string($params, 'positionId');
        $request = array();
        $response = null;
        if ($positionId !== null) {
            $response = $this->swapV1PrivatePostTradeClosePosition ($this->extend($request, $params));
            //
            //    {
            //        "code" => 0,
            //        "msg" => "",
            //        "timestamp" => 1710992264190,
            //        "data" => {
            //            "orderId" => 1770656007907930112,
            //            "positionId" => "1751667128353910784",
            //            "symbol" => "LTC-USDT",
            //            "side" => "Ask",
            //            "type" => "MARKET",
            //            "positionSide" => "Long",
            //            "origQty" => "0.2"
            //        }
            //    }
            //
        } else {
            $request['symbol'] = $market['id'];
            if ($market['inverse']) {
                $response = $this->cswapV1PrivatePostTradeCloseAllPositions ($this->extend($request, $params));
                //
                //     {
                //         "code" => 0,
                //         "msg" => "",
                //         "timestamp" => 1720771601428,
                //         "data" => {
                //             "success" => ["1811673520637231104"],
                //             "failed" => null
                //         }
                //     }
                //
            } else {
                $response = $this->swapV2PrivatePostTradeCloseAllPositions ($this->extend($request, $params));
                //
                //    {
                //        "code" => 0,
                //        "msg" => "",
                //        "data" => {
                //            "success" => array(
                //                1727686766700486656,
                //            ),
                //            "failed" => null
                //        }
                //    }
                //
            }
        }
        $data = $this->safe_dict($response, 'data');
        return $this->parse_order($data, $market);
    }

    public function close_all_positions($params = array ()): array {
        /**
         * closes open $positions for a market
         *
         * @see https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#One-Click%20Close%20All%20Positions
         * @see https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#Close%20all%20positions%20in%20bulk
         *
         * @param {array} [$params] extra parameters specific to the bingx api endpoint
         * @param {string} [$params->recvWindow] $request valid time window value
         * @return {array[]} ~@link https://docs.ccxt.com/#/?id=$position-structure a list of $position structures~
         */
        $this->load_markets();
        $defaultRecvWindow = $this->safe_integer($this->options, 'recvWindow');
        $recvWindow = $this->safe_integer(array($this, 'parse_params'), 'recvWindow', $defaultRecvWindow);
        $marketType = null;
        list($marketType, $params) = $this->handle_market_type_and_params('closeAllPositions', null, $params);
        $subType = null;
        list($subType, $params) = $this->handle_sub_type_and_params('closeAllPositions', null, $params);
        if ($marketType === 'margin') {
            throw new BadRequest($this->id . ' closePositions () cannot be used for ' . $marketType . ' markets');
        }
        $request = array(
            'recvWindow' => $recvWindow,
        );
        $response = null;
        if ($subType === 'inverse') {
            $response = $this->cswapV1PrivatePostTradeCloseAllPositions ($this->extend($request, $params));
            //
            //     {
            //         "code" => 0,
            //         "msg" => "",
            //         "timestamp" => 1720771601428,
            //         "data" => {
            //             "success" => ["1811673520637231104"],
            //             "failed" => null
            //         }
            //     }
            //
        } else {
            $response = $this->swapV2PrivatePostTradeCloseAllPositions ($this->extend($request, $params));
            //
            //    {
            //        "code" => 0,
            //        "msg" => "",
            //        "data" => {
            //            "success" => array(
            //                1727686766700486656,
            //                1727686767048613888
            //            ),
            //            "failed" => null
            //        }
            //    }
            //
        }
        $data = $this->safe_dict($response, 'data', array());
        $success = $this->safe_list($data, 'success', array());
        $positions = array();
        for ($i = 0; $i < count($success); $i++) {
            $position = $this->parse_position(array( 'positionId' => $success[$i] ));
            $positions[] = $position;
        }
        return $positions;
    }

    public function fetch_position_mode(?string $symbol = null, $params = array ()) {
        /**
         * fetchs the position mode, hedged or one way, hedged for binance is set identically for all linear markets or all inverse markets
         *
         * @see https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Get%20Position%20Mode
         *
         * @param {string} $symbol unified $symbol of the market to fetch the order book for
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array} an object detailing whether the market is in hedged or one-way mode
         */
        $response = $this->swapV1PrivateGetPositionSideDual ($params);
        //
        //     {
        //         "code" => "0",
        //         "msg" => "",
        //         "timeStamp" => "1709002057516",
        //         "data" => {
        //             "dualSidePosition" => "false"
        //         }
        //     }
        //
        $data = $this->safe_dict($response, 'data', array());
        $dualSidePosition = $this->safe_string($data, 'dualSidePosition');
        return array(
            'info' => $response,
            'hedged' => ($dualSidePosition === 'true'),
        );
    }

    public function set_position_mode(bool $hedged, ?string $symbol = null, $params = array ()) {
        /**
         * set $hedged to true or false for a market
         *
         * @see https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Set%20Position%20Mode
         *
         * @param {bool} $hedged set to true to use $dualSidePosition
         * @param {string} $symbol not used by bingx setPositionMode ()
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array} response from the exchange
         */
        $dualSidePosition = null;
        if ($hedged) {
            $dualSidePosition = 'true';
        } else {
            $dualSidePosition = 'false';
        }
        $request = array(
            'dualSidePosition' => $dualSidePosition,
        );
        //
        //     {
        //         code => '0',
        //         msg => '',
        //         timeStamp => '1703327432734',
        //         data => array( $dualSidePosition => 'false' )
        //     }
        //
        return $this->swapV1PrivatePostPositionSideDual ($this->extend($request, $params));
    }

    public function edit_order(string $id, string $symbol, string $type, string $side, ?float $amount = null, ?float $price = null, $params = array ()): array {
        /**
         * cancels an order and places a new order
         *
         * @see https://bingx-api.github.io/docs/#/en-us/spot/trade-api.html#Cancel%20order%20and%20place%20a%20new%20order  // spot
         * @see https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Cancel%20an%20order%20and%20then%20Place%20a%20new%20order  // swap
         *
         * @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 {string} [$params->triggerPrice] Trigger $price used for TAKE_STOP_LIMIT, TAKE_STOP_MARKET, TRIGGER_LIMIT, TRIGGER_MARKET order types.
         * @param {array} [$params->takeProfit] *takeProfit object in $params* containing the triggerPrice at which the attached take profit order will be triggered
         * @param {float} [$params->takeProfit.triggerPrice] take profit trigger $price
         * @param {array} [$params->stopLoss] *stopLoss object in $params* containing the triggerPrice at which the attached stop loss order will be triggered
         * @param {float} [$params->stopLoss.triggerPrice] stop loss trigger $price
         *
         * EXCHANGE SPECIFIC PARAMETERS
         * @param {string} [$params->cancelClientOrderID] the user-defined $id of the order to be canceled, 1-40 characters, different orders cannot use the same clientOrderID, only supports a query range of 2 hours
         * @param {string} [$params->cancelRestrictions] cancel orders with specified status, NEW => New order, PENDING => Pending order, PARTIALLY_FILLED => Partially filled
         * @param {string} [$params->cancelReplaceMode] STOP_ON_FAILURE - if the cancel order fails, it will not continue to place a new order, ALLOW_FAILURE - regardless of whether the cancel order succeeds or fails, it will continue to place a new order
         * @param {float} [$params->quoteOrderQty] order $amount
         * @param {string} [$params->newClientOrderId] custom order $id consisting of letters, numbers, and _, 1-40 characters, different orders cannot use the same newClientOrderId.
         * @param {string} [$params->positionSide] *contract only* position direction, required for single position, for both long and short positions only LONG or SHORT can be chosen, defaults to LONG if empty
         * @param {string} [$params->reduceOnly] *contract only* true or false, default=false for single position mode. this parameter is not accepted for both long and short positions mode
         * @param {float} [$params->priceRate] *contract only* for $type TRAILING_STOP_Market or TRAILING_TP_SL, Max = 1
         * @param {string} [$params->workingType] *contract only* StopPrice trigger $price types, MARK_PRICE (default), CONTRACT_PRICE, or INDEX_PRICE
         * @return {array} an ~@link https://docs.ccxt.com/#/?$id=order-structure order structure~
         */
        $this->load_markets();
        $market = $this->market($symbol);
        $request = $this->create_order_request($symbol, $type, $side, $amount, $price, $params);
        $request['cancelOrderId'] = $id;
        $request['cancelReplaceMode'] = 'STOP_ON_FAILURE';
        $response = null;
        if ($market['swap']) {
            $response = $this->swapV1PrivatePostTradeCancelReplace ($this->extend($request, $params));
            //
            //    {
            //        code => '0',
            //        msg => '',
            //        $data => {
            //            cancelResult => 'true',
            //            cancelMsg => '',
            //            cancelResponse => array(
            //                cancelClientOrderId => '',
            //                cancelOrderId => '1755336244265705472',
            //                $symbol => 'SOL-USDT',
            //                orderId => '1755336244265705472',
            //                $side => 'SELL',
            //                positionSide => 'SHORT',
            //                $type => 'LIMIT',
            //                origQty => '1',
            //                $price => '100.000',
            //                executedQty => '0',
            //                avgPrice => '0.000',
            //                cumQuote => '0',
            //                stopPrice => '',
            //                profit => '0.0000',
            //                commission => '0.000000',
            //                status => 'PENDING',
            //                time => '1707339747860',
            //                updateTime => '1707339747860',
            //                clientOrderId => '',
            //                leverage => '20X',
            //                workingType => 'MARK_PRICE',
            //                onlyOnePosition => false,
            //                reduceOnly => false
            //            ),
            //            replaceResult => 'true',
            //            replaceMsg => '',
            //            newOrderResponse => {
            //                orderId => '1755338440612995072',
            //                $symbol => 'SOL-USDT',
            //                positionSide => 'SHORT',
            //                $side => 'SELL',
            //                $type => 'LIMIT',
            //                $price => '99',
            //                quantity => '2',
            //                stopPrice => '0',
            //                workingType => 'MARK_PRICE',
            //                clientOrderID => '',
            //                timeInForce => 'GTC',
            //                priceRate => '0',
            //                stopLoss => '',
            //                takeProfit => '',
            //                reduceOnly => false
            //            }
            //        }
            //    }
            //
        } else {
            $response = $this->spotV1PrivatePostTradeOrderCancelReplace ($this->extend($request, $params));
            //
            //    {
            //        code => '0',
            //        msg => '',
            //        debugMsg => '',
            //        $data => {
            //            cancelResult => array( code => '0', msg => '', result => true ),
            //            openResult => array( code => '0', msg => '', result => true ),
            //            orderOpenResponse => array(
            //                $symbol => 'SOL-USDT',
            //                orderId => '1755334007697866752',
            //                transactTime => '1707339214620',
            //                $price => '99',
            //                stopPrice => '0',
            //                origQty => '0.2',
            //                executedQty => '0',
            //                cummulativeQuoteQty => '0',
            //                status => 'PENDING',
            //                $type => 'LIMIT',
            //                $side => 'SELL',
            //                clientOrderID => ''
            //            ),
            //            orderCancelResponse => {
            //                $symbol => 'SOL-USDT',
            //                orderId => '1755117055251480576',
            //                $price => '100',
            //                stopPrice => '0',
            //                origQty => '0.2',
            //                executedQty => '0',
            //                cummulativeQuoteQty => '0',
            //                status => 'CANCELED',
            //                $type => 'LIMIT',
            //                $side => 'SELL'
            //            }
            //        }
            //    }
            //
        }
        $data = $this->safe_dict($response, 'data');
        return $this->parse_order($data, $market);
    }

    public function fetch_margin_mode(string $symbol, $params = array ()): array {
        /**
         * fetches the margin mode of the trading pair
         *
         * @see https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Query%20Margin%20Type
         * @see https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#Query%20Margin%20Type
         *
         * @param {string} $symbol unified $symbol of the $market to fetch the margin mode for
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array} a ~@link https://docs.ccxt.com/#/?id=margin-mode-structure margin mode structure~
         */
        $this->load_markets();
        $market = $this->market($symbol);
        $request = array(
            'symbol' => $market['id'],
        );
        $subType = null;
        $response = null;
        list($subType, $params) = $this->handle_sub_type_and_params('fetchMarginMode', $market, $params);
        if ($subType === 'inverse') {
            $response = $this->cswapV1PrivateGetTradeMarginType ($this->extend($request, $params));
            //
            //     {
            //         "code" => 0,
            //         "msg" => "",
            //         "timestamp" => 1721966069132,
            //         "data" => {
            //             "symbol" => "SOL-USD",
            //             "marginType" => "CROSSED"
            //         }
            //     }
            //
        } else {
            $response = $this->swapV2PrivateGetTradeMarginType ($this->extend($request, $params));
            //
            //     {
            //         "code" => 0,
            //         "msg" => "",
            //         "data" => {
            //             "marginType" => "CROSSED"
            //         }
            //     }
            //
        }
        $data = $this->safe_dict($response, 'data', array());
        return $this->parse_margin_mode($data, $market);
    }

    public function parse_margin_mode(array $marginMode, $market = null): array {
        $marketId = $this->safe_string($marginMode, 'symbol');
        $marginType = $this->safe_string_lower($marginMode, 'marginType');
        $marginType = ($marginType === 'crossed') ? 'cross' : $marginType;
        return array(
            'info' => $marginMode,
            'symbol' => $this->safe_symbol($marketId, $market, '-', 'swap'),
            'marginMode' => $marginType,
        );
    }

    public function fetch_trading_fee(string $symbol, $params = array ()): array {
        /**
         * fetch the trading fees for a $market
         *
         * @see https://bingx-api.github.io/docs/#/en-us/spot/trade-api.html#Query%20Trading%20Commission%20Rate
         * @see https://bingx-api.github.io/docs/#/en-us/swapV2/account-api.html#Query%20Trading%20Commission%20Rate
         * @see https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#Query%20Trade%20Commission%20Rate
         *
         * @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=fee-structure fee structure~
         */
        $this->load_markets();
        $market = $this->market($symbol);
        $request = array(
            'symbol' => $market['id'],
        );
        $response = null;
        $commission = array();
        $data = $this->safe_dict($response, 'data', array());
        if ($market['spot']) {
            $response = $this->spotV1PrivateGetUserCommissionRate ($this->extend($request, $params));
            //
            //     {
            //         "code" => 0,
            //         "msg" => "",
            //         "debugMsg" => "",
            //         "data" => {
            //             "takerCommissionRate" => 0.001,
            //             "makerCommissionRate" => 0.001
            //         }
            //     }
            //
            $commission = $data;
        } else {
            if ($market['inverse']) {
                $response = $this->cswapV1PrivateGetUserCommissionRate ($params);
                //
                //     {
                //         "code" => 0,
                //         "msg" => "",
                //         "timestamp" => 1721365261438,
                //         "data" => {
                //             "takerCommissionRate" => "0.0005",
                //             "makerCommissionRate" => "0.0002"
                //         }
                //     }
                //
                $commission = $data;
            } else {
                $response = $this->swapV2PrivateGetUserCommissionRate ($params);
                //
                //     {
                //         "code" => 0,
                //         "msg" => "",
                //         "data" => {
                //             "commission" => {
                //                 "takerCommissionRate" => 0.0005,
                //                 "makerCommissionRate" => 0.0002
                //             }
                //         }
                //     }
                //
                $commission = $this->safe_dict($data, 'commission', array());
            }
        }
        return $this->parse_trading_fee($commission, $market);
    }

    public function parse_trading_fee(array $fee, ?array $market = null): array {
        //
        //     {
        //         "takerCommissionRate" => 0.001,
        //         "makerCommissionRate" => 0.001
        //     }
        //
        $symbol = ($market !== null) ? $market['symbol'] : null;
        return array(
            'info' => $fee,
            'symbol' => $symbol,
            'maker' => $this->safe_number($fee, 'makerCommissionRate'),
            'taker' => $this->safe_number($fee, 'takerCommissionRate'),
            'percentage' => false,
            'tierBased' => false,
        );
    }

    public function custom_encode($params) {
        // $sortedParams = $this->keysort($params);
        $rawKeys = is_array($params) ? array_keys($params) : array();
        $keys = $this->sort($rawKeys);
        $adjustedValue = null;
        $result = null;
        for ($i = 0; $i < count($keys); $i++) {
            $key = $keys[$i];
            $value = $params[$key];
            if (gettype($value) === 'array' && array_keys($value) === array_keys(array_keys($value))) {
                $arrStr = null;
                for ($j = 0; $j < count($value); $j++) {
                    $arrayElement = $value[$j];
                    $isString = (gettype($arrayElement) === 'string');
                    if ($isString) {
                        if ($j > 0) {
                            $arrStr .= ',' . '"' . (string) $arrayElement . '"';
                        } else {
                            $arrStr = '"' . (string) $arrayElement . '"';
                        }
                    } else {
                        if ($j > 0) {
                            $arrStr .= ',' . (string) $arrayElement;
                        } else {
                            $arrStr = (string) $arrayElement;
                        }
                    }
                }
                $adjustedValue = '[' . $arrStr . ']';
                $value = $adjustedValue;
            }
            if ($i === 0) {
                $result = $key . '=' . $value;
            } else {
                $result .= '&' . $key . '=' . $value;
            }
        }
        return $result;
    }

    public function sign($path, $section = 'public', $method = 'GET', $params = array (), $headers = null, $body = null) {
        $type = $section[0];
        $version = $section[1];
        $access = $section[2];
        $isSandbox = $this->safe_bool($this->options, 'sandboxMode', false);
        if ($isSandbox && ($type !== 'swap')) {
            throw new NotSupported($this->id . ' does not have a testnet/sandbox URL for ' . $type . ' endpoints');
        }
        $url = $this->implode_hostname($this->urls['api'][$type]);
        $path = $this->implode_params($path, $params);
        $versionIsTransfer = ($version === 'transfer');
        $versionIsAsset = ($version === 'asset');
        if ($versionIsTransfer || $versionIsAsset) {
            if ($versionIsTransfer) {
                $type = 'account/transfer';
            } else {
                $type = 'api/asset';
            }
            $version = $section[2];
            $access = $section[3];
        }
        if ($path !== 'account/apiPermissions') {
            if ($type === 'spot' && $version === 'v3') {
                $url .= '/api';
            } else {
                $url .= '/' . $type;
            }
        }
        $url .= '/' . $version . '/' . $path;
        $params = $this->omit($params, $this->extract_params($path));
        $params['timestamp'] = $this->nonce();
        $params = $this->keysort($params);
        if ($access === 'public') {
            if ($params) {
                $url .= '?' . $this->urlencode($params);
            }
        } elseif ($access === 'private') {
            $this->check_required_credentials();
            $isJsonContentType = ((($type === 'subAccount') || ($type === 'account/transfer')) && ($method === 'POST'));
            $parsedParams = null;
            $encodeRequest = null;
            if ($isJsonContentType) {
                $encodeRequest = $this->custom_encode($params);
            } else {
                $parsedParams = $this->parse_params($params);
                $encodeRequest = $this->rawencode($parsedParams, true);
            }
            $signature = $this->hmac($this->encode($encodeRequest), $this->encode($this->secret), 'sha256');
            $headers = array(
                'X-BX-APIKEY' => $this->apiKey,
                'X-SOURCE-KEY' => $this->safe_string($this->options, 'broker', 'CCXT'),
            );
            if ($isJsonContentType) {
                $headers['Content-Type'] = 'application/json';
                $params['signature'] = $signature;
                $body = $this->json($params);
            } else {
                $query = $this->urlencode($parsedParams, true);
                $url .= '?' . $query . '&' . 'signature=' . $signature;
            }
        }
        return array( 'url' => $url, 'method' => $method, 'body' => $body, 'headers' => $headers );
    }

    public function nonce() {
        return $this->milliseconds();
    }

    public function set_sandbox_mode(bool $enable) {
        parent::set_sandbox_mode($enable);
        $this->options['sandboxMode'] = $enable;
    }

    public function handle_errors(int $httpCode, string $reason, string $url, string $method, array $headers, string $body, $response, $requestHeaders, $requestBody) {
        if ($response === null) {
            return null; // fallback to default error handler
        }
        //
        //    {
        //        "code" => 80014,
        //        "msg" => "Invalid parameters, err:Key => 'GetTickerRequest.Symbol' Error:Field validation for "Symbol" failed on the "len=0|endswith=-USDT" tag",
        //        "data" => {
        //        }
        //    }
        //
        $code = $this->safe_string($response, 'code');
        $message = $this->safe_string($response, 'msg');
        $transferErrorMsg = $this->safe_string($response, 'transferErrorMsg'); // handling with errors from transfer endpoint
        if (($transferErrorMsg !== null) || ($code !== null && $code !== '0')) {
            if ($transferErrorMsg !== null) {
                $message = $transferErrorMsg;
            }
            $feedback = $this->id . ' ' . $body;
            $this->throw_exactly_matched_exception($this->exceptions['exact'], $message, $feedback);
            $this->throw_exactly_matched_exception($this->exceptions['exact'], $code, $feedback);
            $this->throw_broadly_matched_exception($this->exceptions['broad'], $message, $feedback);
            throw new ExchangeError($feedback); // unknown $message
        }
        return null;
    }
}
