<?php

namespace ccxt\async;

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

use Exception; // a common import
use ccxt\async\abstract\bybit as Exchange;
use ccxt\ExchangeError;
use ccxt\ArgumentsRequired;
use ccxt\BadRequest;
use ccxt\InvalidOrder;
use ccxt\OrderNotFound;
use ccxt\NotSupported;
use ccxt\Precise;
use \React\Async;
use \React\Promise;
use \React\Promise\PromiseInterface;

class bybit extends Exchange {

    public function describe(): mixed {
        return $this->deep_extend(parent::describe(), array(
            'id' => 'bybit',
            'name' => 'Bybit',
            'countries' => array( 'VG' ), // British Virgin Islands
            'version' => 'v5',
            'userAgent' => null,
            'rateLimit' => 20,
            'hostname' => 'bybit.com', // bybit.com, bytick.com, bybit.nl, bybit.com.hk
            'pro' => true,
            'certified' => true,
            'has' => array(
                'CORS' => true,
                'spot' => true,
                'margin' => true,
                'swap' => true,
                'future' => true,
                'option' => true,
                'borrowCrossMargin' => true,
                'cancelAllOrders' => true,
                'cancelAllOrdersAfter' => true,
                'cancelOrder' => true,
                'cancelOrders' => true,
                'cancelOrdersForSymbols' => true,
                'closeAllPositions' => false,
                'closePosition' => false,
                'createConvertTrade' => true,
                'createMarketBuyOrderWithCost' => true,
                'createMarketSellOrderWithCost' => true,
                'createOrder' => true,
                'createOrders' => true,
                'createOrderWithTakeProfitAndStopLoss' => true,
                'createPostOnlyOrder' => true,
                'createReduceOnlyOrder' => true,
                'createStopLimitOrder' => true,
                'createStopLossOrder' => true,
                'createStopMarketOrder' => true,
                'createStopOrder' => true,
                'createTakeProfitOrder' => true,
                'createTrailingAmountOrder' => true,
                'createTriggerOrder' => true,
                'editOrder' => true,
                'editOrders' => true,
                'fetchAllGreeks' => true,
                'fetchBalance' => true,
                'fetchBidsAsks' => 'emulated',
                'fetchBorrowInterest' => false, // temporarily disabled, doesn't work
                'fetchBorrowRateHistories' => false,
                'fetchBorrowRateHistory' => false,
                'fetchCanceledAndClosedOrders' => true,
                'fetchCanceledOrders' => true,
                'fetchClosedOrder' => true,
                'fetchClosedOrders' => true,
                'fetchConvertCurrencies' => true,
                'fetchConvertQuote' => true,
                'fetchConvertTrade' => true,
                'fetchConvertTradeHistory' => true,
                'fetchCrossBorrowRate' => true,
                'fetchCrossBorrowRates' => false,
                'fetchCurrencies' => true,
                'fetchDeposit' => false,
                'fetchDepositAddress' => true,
                'fetchDepositAddresses' => false,
                'fetchDepositAddressesByNetwork' => true,
                'fetchDeposits' => true,
                'fetchDepositWithdrawFee' => 'emulated',
                'fetchDepositWithdrawFees' => true,
                'fetchFundingHistory' => true,
                'fetchFundingRate' => 'emulated', // emulated in exchange
                'fetchFundingRateHistory' => true,
                'fetchFundingRates' => true,
                'fetchGreeks' => true,
                'fetchIndexOHLCV' => true,
                'fetchIsolatedBorrowRate' => false,
                'fetchIsolatedBorrowRates' => false,
                'fetchLedger' => true,
                'fetchLeverage' => true,
                'fetchLeverageTiers' => true,
                'fetchLongShortRatio' => false,
                'fetchLongShortRatioHistory' => true,
                'fetchMarginAdjustmentHistory' => false,
                'fetchMarketLeverageTiers' => true,
                'fetchMarkets' => true,
                'fetchMarkOHLCV' => true,
                'fetchMyLiquidations' => true,
                'fetchMySettlementHistory' => true,
                'fetchMyTrades' => true,
                'fetchOHLCV' => true,
                'fetchOpenInterest' => true,
                'fetchOpenInterestHistory' => true,
                'fetchOpenOrder' => true,
                'fetchOpenOrders' => true,
                'fetchOption' => true,
                'fetchOptionChain' => true,
                'fetchOrder' => true,
                'fetchOrderBook' => true,
                'fetchOrders' => false,
                'fetchOrderTrades' => true,
                'fetchPosition' => true,
                'fetchPositionHistory' => 'emulated',
                'fetchPositions' => true,
                'fetchPositionsHistory' => true,
                'fetchPremiumIndexOHLCV' => true,
                'fetchSettlementHistory' => true,
                'fetchTicker' => true,
                'fetchTickers' => true,
                'fetchTime' => true,
                'fetchTrades' => true,
                'fetchTradingFee' => true,
                'fetchTradingFees' => true,
                'fetchTransactions' => false,
                'fetchTransfers' => true,
                'fetchUnderlyingAssets' => false,
                'fetchVolatilityHistory' => true,
                'fetchWithdrawals' => true,
                'repayCrossMargin' => true,
                'sandbox' => true,
                'setLeverage' => true,
                'setMarginMode' => true,
                'setPositionMode' => true,
                'transfer' => true,
                'withdraw' => true,
            ),
            'timeframes' => array(
                '1m' => '1',
                '3m' => '3',
                '5m' => '5',
                '15m' => '15',
                '30m' => '30',
                '1h' => '60',
                '2h' => '120',
                '4h' => '240',
                '6h' => '360',
                '12h' => '720',
                '1d' => 'D',
                '1w' => 'W',
                '1M' => 'M',
            ),
            'urls' => array(
                'test' => array(
                    'spot' => 'https://api-testnet.{hostname}',
                    'futures' => 'https://api-testnet.{hostname}',
                    'v2' => 'https://api-testnet.{hostname}',
                    'public' => 'https://api-testnet.{hostname}',
                    'private' => 'https://api-testnet.{hostname}',
                ),
                'logo' => 'https://github.com/user-attachments/assets/97a5d0b3-de10-423d-90e1-6620960025ed',
                'api' => array(
                    'spot' => 'https://api.{hostname}',
                    'futures' => 'https://api.{hostname}',
                    'v2' => 'https://api.{hostname}',
                    'public' => 'https://api.{hostname}',
                    'private' => 'https://api.{hostname}',
                ),
                'demotrading' => array(
                    'spot' => 'https://api-demo.{hostname}',
                    'futures' => 'https://api-demo.{hostname}',
                    'v2' => 'https://api-demo.{hostname}',
                    'public' => 'https://api-demo.{hostname}',
                    'private' => 'https://api-demo.{hostname}',
                ),
                'www' => 'https://www.bybit.com',
                'doc' => array(
                    'https://bybit-exchange.github.io/docs/inverse/',
                    'https://bybit-exchange.github.io/docs/linear/',
                    'https://github.com/bybit-exchange',
                ),
                'fees' => 'https://help.bybit.com/hc/en-us/articles/360039261154',
                'referral' => 'https://www.bybit.com/register?affiliate_id=35953',
            ),
            'api' => array(
                'public' => array(
                    'get' => array(
                        // spot
                        'spot/v3/public/symbols' => 1,
                        'spot/v3/public/quote/depth' => 1,
                        'spot/v3/public/quote/depth/merged' => 1,
                        'spot/v3/public/quote/trades' => 1,
                        'spot/v3/public/quote/kline' => 1,
                        'spot/v3/public/quote/ticker/24hr' => 1,
                        'spot/v3/public/quote/ticker/price' => 1,
                        'spot/v3/public/quote/ticker/bookTicker' => 1,
                        'spot/v3/public/server-time' => 1,
                        'spot/v3/public/infos' => 1,
                        'spot/v3/public/margin-product-infos' => 1,
                        'spot/v3/public/margin-ensure-tokens' => 1,
                        // data
                        'v3/public/time' => 1,
                        'contract/v3/public/copytrading/symbol/list' => 1,
                        // derivative
                        'derivatives/v3/public/order-book/L2' => 1,
                        'derivatives/v3/public/kline' => 1,
                        'derivatives/v3/public/tickers' => 1,
                        'derivatives/v3/public/instruments-info' => 1,
                        'derivatives/v3/public/mark-price-kline' => 1,
                        'derivatives/v3/public/index-price-kline' => 1,
                        'derivatives/v3/public/funding/history-funding-rate' => 1,
                        'derivatives/v3/public/risk-limit/list' => 1,
                        'derivatives/v3/public/delivery-price' => 1,
                        'derivatives/v3/public/recent-trade' => 1,
                        'derivatives/v3/public/open-interest' => 1,
                        'derivatives/v3/public/insurance' => 1,
                        // v5
                        'v5/announcements/index' => 5, // 10/s = 1000 / (20 * 5)
                        // market
                        'v5/market/time' => 5,
                        'v5/market/kline' => 5,
                        'v5/market/mark-price-kline' => 5,
                        'v5/market/index-price-kline' => 5,
                        'v5/market/premium-index-price-kline' => 5,
                        'v5/market/instruments-info' => 5,
                        'v5/market/orderbook' => 5,
                        'v5/market/tickers' => 5,
                        'v5/market/funding/history' => 5,
                        'v5/market/recent-trade' => 5,
                        'v5/market/open-interest' => 5,
                        'v5/market/historical-volatility' => 5,
                        'v5/market/insurance' => 5,
                        'v5/market/risk-limit' => 5,
                        'v5/market/delivery-price' => 5,
                        'v5/market/account-ratio' => 5,
                        // spot leverage token
                        'v5/spot-lever-token/info' => 5,
                        'v5/spot-lever-token/reference' => 5,
                        // spot margin trade
                        'v5/spot-margin-trade/data' => 5,
                        'v5/spot-margin-trade/collateral' => 5,
                        'v5/spot-cross-margin-trade/data' => 5,
                        'v5/spot-cross-margin-trade/pledge-token' => 5,
                        'v5/spot-cross-margin-trade/borrow-token' => 5,
                        // crypto loan
                        'v5/crypto-loan/collateral-data' => 5,
                        'v5/crypto-loan/loanable-data' => 5,
                        // institutional lending
                        'v5/ins-loan/product-infos' => 5,
                        'v5/ins-loan/ensure-tokens-convert' => 5,
                        // earn
                        'v5/earn/product' => 5,
                    ),
                ),
                'private' => array(
                    'get' => array(
                        'v5/market/instruments-info' => 5,
                        // Legacy inverse swap
                        'v2/private/wallet/fund/records' => 25, // 120 per minute = 2 per second => cost = 50 / 2 = 25
                        // spot
                        'spot/v3/private/order' => 2.5,
                        'spot/v3/private/open-orders' => 2.5,
                        'spot/v3/private/history-orders' => 2.5,
                        'spot/v3/private/my-trades' => 2.5,
                        'spot/v3/private/account' => 2.5,
                        'spot/v3/private/reference' => 2.5,
                        'spot/v3/private/record' => 2.5,
                        'spot/v3/private/cross-margin-orders' => 10,
                        'spot/v3/private/cross-margin-account' => 10,
                        'spot/v3/private/cross-margin-loan-info' => 10,
                        'spot/v3/private/cross-margin-repay-history' => 10,
                        'spot/v3/private/margin-loan-infos' => 10,
                        'spot/v3/private/margin-repaid-infos' => 10,
                        'spot/v3/private/margin-ltv' => 10,
                        // account
                        'asset/v3/private/transfer/inter-transfer/list/query' => 50, // 60 per minute = 1 per second => cost = 50 / 1 = 50
                        'asset/v3/private/transfer/sub-member/list/query' => 50,
                        'asset/v3/private/transfer/sub-member-transfer/list/query' => 50,
                        'asset/v3/private/transfer/universal-transfer/list/query' => 25,
                        'asset/v3/private/coin-info/query' => 25, // 2/s
                        'asset/v3/private/deposit/address/query' => 10,
                        'contract/v3/private/copytrading/order/list' => 30, // 100 req/min = 1000 / (20 * 30) = 1.66666666667/s
                        'contract/v3/private/copytrading/position/list' => 40, // 75 req/min = 1000 / (20 * 40) = 1.25/s
                        'contract/v3/private/copytrading/wallet/balance' => 25, // 120 req/min = 1000 / (20 * 25) = 2/s
                        'contract/v3/private/position/limit-info' => 25, // 120 per minute = 2 per second => cost = 50 / 2 = 25
                        'contract/v3/private/order/unfilled-orders' => 1,
                        'contract/v3/private/order/list' => 1,
                        'contract/v3/private/position/list' => 1,
                        'contract/v3/private/execution/list' => 1,
                        'contract/v3/private/position/closed-pnl' => 1,
                        'contract/v3/private/account/wallet/balance' => 1,
                        'contract/v3/private/account/fee-rate' => 1,
                        'contract/v3/private/account/wallet/fund-records' => 1,
                        // derivative
                        'unified/v3/private/order/unfilled-orders' => 1,
                        'unified/v3/private/order/list' => 1,
                        'unified/v3/private/position/list' => 1,
                        'unified/v3/private/execution/list' => 1,
                        'unified/v3/private/delivery-record' => 1,
                        'unified/v3/private/settlement-record' => 1,
                        'unified/v3/private/account/wallet/balance' => 1,
                        'unified/v3/private/account/transaction-log' => 1,
                        'unified/v3/private/account/borrow-history' => 1,
                        'unified/v3/private/account/borrow-rate' => 1,
                        'unified/v3/private/account/info' => 1,
                        'user/v3/private/frozen-sub-member' => 10, // 5/s
                        'user/v3/private/query-sub-members' => 5, // 10/s
                        'user/v3/private/query-api' => 5, // 10/s
                        'user/v3/private/get-member-type' => 1,
                        'asset/v3/private/transfer/transfer-coin/list/query' => 50,
                        'asset/v3/private/transfer/account-coin/balance/query' => 50,
                        'asset/v3/private/transfer/account-coins/balance/query' => 25,
                        'asset/v3/private/transfer/asset-info/query' => 50,
                        'asset/v3/public/deposit/allowed-deposit-list/query' => 0.17, // 300/s
                        'asset/v3/private/deposit/record/query' => 10,
                        'asset/v3/private/withdraw/record/query' => 10,
                        // v5
                        // trade
                        'v5/order/realtime' => 5, // 10/s => cost = 50 / 10 = 5
                        'v5/order/history' => 5, // 10/s => cost = 50 / 10 = 5
                        'v5/order/spot-borrow-check' => 1, // 50/s = 1000 / (20 * 1)
                        // position
                        'v5/position/list' => 5, // 10/s => cost = 50 / 10 = 5
                        'v5/execution/list' => 5, // 10/s => cost = 50 / 10 = 5
                        'v5/position/closed-pnl' => 5, // 10/s => cost = 50 / 10 = 5
                        'v5/position/move-history' => 5, // 10/s => cost = 50 / 10 = 5
                        // pre-upgrade
                        'v5/pre-upgrade/order/history' => 5,
                        'v5/pre-upgrade/execution/list' => 5,
                        'v5/pre-upgrade/position/closed-pnl' => 5,
                        'v5/pre-upgrade/account/transaction-log' => 5,
                        'v5/pre-upgrade/asset/delivery-record' => 5,
                        'v5/pre-upgrade/asset/settlement-record' => 5,
                        // account
                        'v5/account/wallet-balance' => 1,
                        'v5/account/borrow-history' => 1,
                        'v5/account/collateral-info' => 1,
                        'v5/asset/coin-greeks' => 1,
                        'v5/account/fee-rate' => 10, // 5/s = 1000 / (20 * 10)
                        'v5/account/info' => 5,
                        'v5/account/transaction-log' => 1,
                        'v5/account/contract-transaction-log' => 1,
                        'v5/account/smp-group' => 1,
                        'v5/account/mmp-state' => 5,
                        'v5/account/withdrawal' => 5,
                        // asset
                        'v5/asset/exchange/query-coin-list' => 0.5, // 100/s => cost = 50 / 100 = 0.5
                        'v5/asset/exchange/convert-result-query' => 0.5, // 100/s => cost = 50 / 100 = 0.5
                        'v5/asset/exchange/query-convert-history' => 0.5, // 100/s => cost = 50 / 100 = 0.5
                        'v5/asset/exchange/order-record' => 5, // 10/s => cost = 50 / 10 = 5
                        'v5/asset/delivery-record' => 5,
                        'v5/asset/settlement-record' => 5,
                        'v5/asset/transfer/query-asset-info' => 50, // 1/s => cost = 50 / 1 = 50
                        'v5/asset/transfer/query-account-coins-balance' => 25, // 2/s => cost = 50 / 2 = 25
                        'v5/asset/transfer/query-account-coin-balance' => 50, // 1/s => cost = 50 / 1 = 50
                        'v5/asset/transfer/query-transfer-coin-list' => 50, // 1/s => cost = 50 / 1 = 50
                        'v5/asset/transfer/query-inter-transfer-list' => 50, // 1/s => cost = 50 / 1 = 50
                        'v5/asset/transfer/query-sub-member-list' => 50, // 1/s => cost = 50 / 1 = 50
                        'v5/asset/transfer/query-universal-transfer-list' => 25, // 2/s => cost = 50 / 2 = 25
                        'v5/asset/deposit/query-allowed-list' => 5,
                        'v5/asset/deposit/query-record' => 10, // 5/s => cost = 50 / 5 = 10
                        'v5/asset/deposit/query-sub-member-record' => 10, // 5/s => cost = 50 / 5 = 10
                        'v5/asset/deposit/query-internal-record' => 5,
                        'v5/asset/deposit/query-address' => 10, // 5/s => cost = 50 / 5 = 10
                        'v5/asset/deposit/query-sub-member-address' => 10, // 5/s => cost = 50 / 5 = 10
                        'v5/asset/coin/query-info' => 28, // should be 25 but exceeds ratelimit unless the weight is 28 or higher
                        'v5/asset/withdraw/query-record' => 10, // 5/s => cost = 50 / 5 = 10
                        'v5/asset/withdraw/withdrawable-amount' => 5,
                        'v5/asset/withdraw/vasp/list' => 5,
                        // user
                        'v5/user/query-sub-members' => 5, // 10/s => cost = 50 / 10 = 5
                        'v5/user/query-api' => 5, // 10/s => cost = 50 / 10 = 5
                        'v5/user/sub-apikeys' => 5,
                        'v5/user/get-member-type' => 5,
                        'v5/user/aff-customer-info' => 5,
                        'v5/user/del-submember' => 5,
                        'v5/user/submembers' => 5,
                        // affilate
                        'v5/affiliate/aff-user-list' => 5,
                        // spot leverage token
                        'v5/spot-lever-token/order-record' => 1, // 50/s => cost = 50 / 50 = 1
                        // spot margin trade
                        'v5/spot-margin-trade/interest-rate-history' => 5,
                        'v5/spot-margin-trade/state' => 5,
                        'v5/spot-cross-margin-trade/loan-info' => 1, // 50/s => cost = 50 / 50 = 1
                        'v5/spot-cross-margin-trade/account' => 1, // 50/s => cost = 50 / 50 = 1
                        'v5/spot-cross-margin-trade/orders' => 1, // 50/s => cost = 50 / 50 = 1
                        'v5/spot-cross-margin-trade/repay-history' => 1, // 50/s => cost = 50 / 50 = 1
                        // crypto loan
                        'v5/crypto-loan/borrowable-collateralisable-number' => 5,
                        'v5/crypto-loan/ongoing-orders' => 5,
                        'v5/crypto-loan/repayment-history' => 5,
                        'v5/crypto-loan/borrow-history' => 5,
                        'v5/crypto-loan/max-collateral-amount' => 5,
                        'v5/crypto-loan/adjustment-history' => 5,
                        // institutional lending
                        'v5/ins-loan/product-infos' => 5,
                        'v5/ins-loan/ensure-tokens-convert' => 5,
                        'v5/ins-loan/loan-order' => 5,
                        'v5/ins-loan/repaid-history' => 5,
                        'v5/ins-loan/ltv-convert' => 5,
                        // c2c lending
                        'v5/lending/info' => 5,
                        'v5/lending/history-order' => 5,
                        'v5/lending/account' => 5,
                        // broker
                        'v5/broker/earning-record' => 5, // deprecated
                        'v5/broker/earnings-info' => 5,
                        'v5/broker/account-info' => 5,
                        'v5/broker/asset/query-sub-member-deposit-record' => 10,
                        // earn
                        'v5/earn/order' => 5,
                        'v5/earn/position' => 5,
                    ),
                    'post' => array(
                        // spot
                        'spot/v3/private/order' => 2.5,
                        'spot/v3/private/cancel-order' => 2.5,
                        'spot/v3/private/cancel-orders' => 2.5,
                        'spot/v3/private/cancel-orders-by-ids' => 2.5,
                        'spot/v3/private/purchase' => 2.5,
                        'spot/v3/private/redeem' => 2.5,
                        'spot/v3/private/cross-margin-loan' => 10,
                        'spot/v3/private/cross-margin-repay' => 10,
                        // account
                        'asset/v3/private/transfer/inter-transfer' => 150, // 20 per minute = 0.333 per second => cost = 50 / 0.3333 = 150
                        'asset/v3/private/withdraw/create' => 300,
                        'asset/v3/private/withdraw/cancel' => 50,
                        'asset/v3/private/transfer/sub-member-transfer' => 150,
                        'asset/v3/private/transfer/transfer-sub-member-save' => 150,
                        'asset/v3/private/transfer/universal-transfer' => 10, // 5/s
                        'user/v3/private/create-sub-member' => 10, // 5/s
                        'user/v3/private/create-sub-api' => 10, // 5/s
                        'user/v3/private/update-api' => 10, // 5/s
                        'user/v3/private/delete-api' => 10, // 5/s
                        'user/v3/private/update-sub-api' => 10, // 5/s
                        'user/v3/private/delete-sub-api' => 10, // 5/s
                        // contract
                        'contract/v3/private/copytrading/order/create' => 30, // 100 req/min = 1000 / (20 * 30) = 1.66666666667/s
                        'contract/v3/private/copytrading/order/cancel' => 30,
                        'contract/v3/private/copytrading/order/close' => 30,
                        'contract/v3/private/copytrading/position/close' => 40, // 75 req/min = 1000 / (20 * 40) = 1.25/s
                        'contract/v3/private/copytrading/position/set-leverage' => 40,
                        'contract/v3/private/copytrading/wallet/transfer' => 25, // 120 req/min = 1000 / (20 * 25) = 2/s
                        'contract/v3/private/copytrading/order/trading-stop' => 2.5,
                        'contract/v3/private/order/create' => 1,
                        'contract/v3/private/order/cancel' => 1,
                        'contract/v3/private/order/cancel-all' => 1,
                        'contract/v3/private/order/replace' => 1,
                        'contract/v3/private/position/set-auto-add-margin' => 1,
                        'contract/v3/private/position/switch-isolated' => 1,
                        'contract/v3/private/position/switch-mode' => 1,
                        'contract/v3/private/position/switch-tpsl-mode' => 1,
                        'contract/v3/private/position/set-leverage' => 1,
                        'contract/v3/private/position/trading-stop' => 1,
                        'contract/v3/private/position/set-risk-limit' => 1,
                        'contract/v3/private/account/setMarginMode' => 1,
                        // derivative
                        'unified/v3/private/order/create' => 30, // 100 req/min (shared) = 1000 / (20 * 30) = 1.66666666667/s
                        'unified/v3/private/order/replace' => 30,
                        'unified/v3/private/order/cancel' => 30,
                        'unified/v3/private/order/create-batch' => 30,
                        'unified/v3/private/order/replace-batch' => 30,
                        'unified/v3/private/order/cancel-batch' => 30,
                        'unified/v3/private/order/cancel-all' => 30,
                        'unified/v3/private/position/set-leverage' => 2.5,
                        'unified/v3/private/position/tpsl/switch-mode' => 2.5,
                        'unified/v3/private/position/set-risk-limit' => 2.5,
                        'unified/v3/private/position/trading-stop' => 2.5,
                        'unified/v3/private/account/upgrade-unified-account' => 2.5,
                        'unified/v3/private/account/setMarginMode' => 2.5,
                        // tax
                        'fht/compliance/tax/v3/private/registertime' => 50,
                        'fht/compliance/tax/v3/private/create' => 50,
                        'fht/compliance/tax/v3/private/status' => 50,
                        'fht/compliance/tax/v3/private/url' => 50,
                        // v5
                        // trade
                        'v5/order/create' => 2.5, // 20/s = 1000 / (20 * 2.5)
                        'v5/order/amend' => 5, // 10/s => cost = 50 / 10 = 5
                        'v5/order/cancel' => 2.5,
                        'v5/order/cancel-all' => 50, // 1/s = 1000 / (20 * 50)
                        'v5/order/create-batch' => 5, // 10/s => cost = 50 / 10 = 5
                        'v5/order/amend-batch' => 5, // 10/s => cost = 50 / 10 = 5
                        'v5/order/cancel-batch' => 5, // 10/s => cost = 50 / 10 = 5
                        'v5/order/disconnected-cancel-all' => 5,
                        // position
                        'v5/position/set-leverage' => 5, // 10/s => cost = 50 / 10 = 5
                        'v5/position/switch-isolated' => 5,
                        'v5/position/set-tpsl-mode' => 5, // 10/s => cost = 50 / 10 = 5
                        'v5/position/switch-mode' => 5,
                        'v5/position/set-risk-limit' => 5, // 10/s => cost = 50 / 10 = 5
                        'v5/position/trading-stop' => 5, // 10/s => cost = 50 / 10 = 5
                        'v5/position/set-auto-add-margin' => 5,
                        'v5/position/add-margin' => 5,
                        'v5/position/move-positions' => 5,
                        'v5/position/confirm-pending-mmr' => 5,
                        // account
                        'v5/account/upgrade-to-uta' => 5,
                        'v5/account/quick-repayment' => 5,
                        'v5/account/set-margin-mode' => 5,
                        'v5/account/set-hedging-mode' => 5,
                        'v5/account/mmp-modify' => 5,
                        'v5/account/mmp-reset' => 5,
                        // asset
                        'v5/asset/exchange/quote-apply' => 1, // 50/s
                        'v5/asset/exchange/convert-execute' => 1, // 50/s
                        'v5/asset/transfer/inter-transfer' => 50, // 1/s => cost = 50 / 1 = 50
                        'v5/asset/transfer/save-transfer-sub-member' => 150, // 1/3/s => cost = 50 / 1/3 = 150
                        'v5/asset/transfer/universal-transfer' => 10, // 5/s => cost = 50 / 5 = 10
                        'v5/asset/deposit/deposit-to-account' => 5,
                        'v5/asset/withdraw/create' => 50, // 1/s => cost = 50 / 1 = 50
                        'v5/asset/withdraw/cancel' => 50, // 1/s => cost = 50 / 1 = 50
                        // user
                        'v5/user/create-sub-member' => 10, // 5/s => cost = 50 / 5 = 10
                        'v5/user/create-sub-api' => 10, // 5/s => cost = 50 / 5 = 10
                        'v5/user/frozen-sub-member' => 10, // 5/s => cost = 50 / 5 = 10
                        'v5/user/update-api' => 10, // 5/s => cost = 50 / 5 = 10
                        'v5/user/update-sub-api' => 10, // 5/s => cost = 50 / 5 = 10
                        'v5/user/delete-api' => 10, // 5/s => cost = 50 / 5 = 10
                        'v5/user/delete-sub-api' => 10, // 5/s => cost = 50 / 5 = 10
                        // spot leverage token
                        'v5/spot-lever-token/purchase' => 2.5, // 20/s => cost = 50 / 20 = 2.5
                        'v5/spot-lever-token/redeem' => 2.5, // 20/s => cost = 50 / 20 = 2.5
                        // spot margin trade
                        'v5/spot-margin-trade/switch-mode' => 5,
                        'v5/spot-margin-trade/set-leverage' => 5,
                        'v5/spot-cross-margin-trade/loan' => 2.5, // 20/s => cost = 50 / 20 = 2.5
                        'v5/spot-cross-margin-trade/repay' => 2.5, // 20/s => cost = 50 / 20 = 2.5
                        'v5/spot-cross-margin-trade/switch' => 2.5, // 20/s => cost = 50 / 20 = 2.5
                        // crypto loan
                        'v5/crypto-loan/borrow' => 5,
                        'v5/crypto-loan/repay' => 5,
                        'v5/crypto-loan/adjust-ltv' => 5,
                        // institutional lending
                        'v5/ins-loan/association-uid' => 5,
                        // c2c lending
                        'v5/lending/purchase' => 5,
                        'v5/lending/redeem' => 5,
                        'v5/lending/redeem-cancel' => 5,
                        'v5/account/set-collateral-switch' => 5,
                        'v5/account/set-collateral-switch-batch' => 5,
                        // demo trading
                        'v5/account/demo-apply-money' => 5,
                        // broker
                        'v5/broker/award/info' => 5,
                        'v5/broker/award/distribute-award' => 5,
                        'v5/broker/award/distribution-record' => 5,
                        // earn
                        'v5/earn/place-order' => 5,
                    ),
                ),
            ),
            'httpExceptions' => array(
                '403' => '\\ccxt\\RateLimitExceeded', // Forbidden -- You request too many times
            ),
            'exceptions' => array(
                // Uncodumented explanation of error strings:
                // - oc_diff => order cost needed to place this order
                // - new_oc => total order cost of open orders including the order you are trying to open
                // - ob => order balance - the total cost of current open orders
                // - ab => available balance
                'exact' => array(
                    '-10009' => '\\ccxt\\BadRequest', // array("ret_code":-10009,"ret_msg":"Invalid period!","result":null,"token":null)
                    '-1004' => '\\ccxt\\BadRequest', // array("ret_code":-1004,"ret_msg":"Missing required parameter \u0027symbol\u0027","ext_code":null,"ext_info":null,"result":null)
                    '-1021' => '\\ccxt\\BadRequest', // array("ret_code":-1021,"ret_msg":"Timestamp for this request is outside of the recvWindow.","ext_code":null,"ext_info":null,"result":null)
                    '-1103' => '\\ccxt\\BadRequest', // An unknown parameter was sent.
                    '-1140' => '\\ccxt\\InvalidOrder', // array("ret_code":-1140,"ret_msg":"Transaction amount lower than the minimum.","result":array(),"ext_code":"","ext_info":null,"time_now":"1659204910.248576")
                    '-1197' => '\\ccxt\\InvalidOrder', // array("ret_code":-1197,"ret_msg":"Your order quantity to buy is too large. The filled price may deviate significantly from the market price. Please try again","result":array(),"ext_code":"","ext_info":null,"time_now":"1659204531.979680")
                    '-2013' => '\\ccxt\\InvalidOrder', // array("ret_code":-2013,"ret_msg":"Order does not exist.","ext_code":null,"ext_info":null,"result":null)
                    '-2015' => '\\ccxt\\AuthenticationError', // Invalid API-key, IP, or permissions for action.
                    '-6017' => '\\ccxt\\BadRequest', // Repayment amount has exceeded the total liability
                    '-6025' => '\\ccxt\\BadRequest', // Amount to borrow cannot be lower than the min. amount to borrow (per transaction)
                    '-6029' => '\\ccxt\\BadRequest', // Amount to borrow has exceeded the user's estimated max amount to borrow
                    '5004' => '\\ccxt\\ExchangeError', // array("retCode":5004,"retMsg":"Server Timeout","result":null,"retExtInfo":array(),"time":1667577060106)
                    '7001' => '\\ccxt\\BadRequest', // array("retCode":7001,"retMsg":"request params type error")
                    '10001' => '\\ccxt\\BadRequest', // parameter error
                    '10002' => '\\ccxt\\InvalidNonce', // request expired, check your timestamp and recv_window
                    '10003' => '\\ccxt\\AuthenticationError', // Invalid apikey
                    '10004' => '\\ccxt\\AuthenticationError', // invalid sign
                    '10005' => '\\ccxt\\PermissionDenied', // permission denied for current apikey
                    '10006' => '\\ccxt\\RateLimitExceeded', // too many requests
                    '10007' => '\\ccxt\\AuthenticationError', // api_key not found in your request parameters
                    '10008' => '\\ccxt\\AccountSuspended', // User had been banned
                    '10009' => '\\ccxt\\AuthenticationError', // IP had been banned
                    '10010' => '\\ccxt\\PermissionDenied', // request ip mismatch
                    '10014' => '\\ccxt\\BadRequest', // Request is duplicate
                    '10016' => '\\ccxt\\ExchangeError', // array("retCode":10016,"retMsg":"System error. Please try again later.")
                    '10017' => '\\ccxt\\BadRequest', // request path not found or request method is invalid
                    '10018' => '\\ccxt\\RateLimitExceeded', // exceed ip rate limit
                    '10020' => '\\ccxt\\PermissionDenied', // array("retCode":10020,"retMsg":"your account is not a unified margin account, please update your account","result":null,"retExtInfo":null,"time":1664783731123)
                    '10024' => '\\ccxt\\PermissionDenied', // Compliance rules triggered
                    '10027' => '\\ccxt\\PermissionDenied', // Trading Banned
                    '10028' => '\\ccxt\\PermissionDenied', // The API can only be accessed by unified account users.
                    '10029' => '\\ccxt\\PermissionDenied', // The requested symbol is invalid, please check symbol whitelist
                    '12137' => '\\ccxt\\InvalidOrder', // array("retCode":12137,"retMsg":"Order quantity has too many decimals.","result":array(),"retExtInfo":array(),"time":1695900943033)
                    '12201' => '\\ccxt\\BadRequest', // array("retCode":12201,"retMsg":"Invalid orderCategory parameter.","result":array(),"retExtInfo":null,"time":1666699391220)
                    '12141' => '\\ccxt\\BadRequest', // "retCode":12141,"retMsg":"Duplicate clientOrderId.","result":array(),"retExtInfo":array(),"time":1686134298989}
                    '100028' => '\\ccxt\\PermissionDenied', // The API cannot be accessed by unified account users.
                    '110001' => '\\ccxt\\OrderNotFound', // Order does not exist
                    '110003' => '\\ccxt\\InvalidOrder', // Order price is out of permissible range
                    '110004' => '\\ccxt\\InsufficientFunds', // Insufficient wallet balance
                    '110005' => '\\ccxt\\InvalidOrder', // position status
                    '110006' => '\\ccxt\\InsufficientFunds', // cannot afford estimated position_margin
                    '110007' => '\\ccxt\\InsufficientFunds', // array("retCode":110007,"retMsg":"ab not enough for new order","result":array(),"retExtInfo":array(),"time":1668838414793)
                    '110008' => '\\ccxt\\InvalidOrder', // Order has been finished or canceled
                    '110009' => '\\ccxt\\InvalidOrder', // The number of stop orders exceeds maximum limit allowed
                    '110010' => '\\ccxt\\InvalidOrder', // Order already cancelled
                    '110011' => '\\ccxt\\InvalidOrder', // Any adjustments made will trigger immediate liquidation
                    '110012' => '\\ccxt\\InsufficientFunds', // Available balance not enough
                    '110013' => '\\ccxt\\BadRequest', // Due to risk limit, cannot set leverage
                    '110014' => '\\ccxt\\InsufficientFunds', // Available balance not enough to add margin
                    '110015' => '\\ccxt\\BadRequest', // the position is in cross_margin
                    '110016' => '\\ccxt\\InvalidOrder', // Requested quantity of contracts exceeds risk limit, please adjust your risk limit level before trying again
                    '110017' => '\\ccxt\\InvalidOrder', // Reduce-only rule not satisfied
                    '110018' => '\\ccxt\\BadRequest', // userId illegal
                    '110019' => '\\ccxt\\InvalidOrder', // orderId illegal
                    '110020' => '\\ccxt\\InvalidOrder', // number of active orders greater than 500
                    '110021' => '\\ccxt\\InvalidOrder', // Open Interest exceeded
                    '110022' => '\\ccxt\\InvalidOrder', // qty has been limited, cannot modify the order to add qty
                    '110023' => '\\ccxt\\InvalidOrder', // This contract only supports position reduction operation, please contact customer service for details
                    '110024' => '\\ccxt\\BadRequest', // You have an existing position, so position mode cannot be switched
                    '110025' => '\\ccxt\\NoChange', // Position mode is not modified
                    '110026' => '\\ccxt\\MarginModeAlreadySet', // Cross/isolated margin mode is not modified
                    '110027' => '\\ccxt\\NoChange', // Margin is not modified
                    '110028' => '\\ccxt\\BadRequest', // Open orders exist, so you cannot change position mode
                    '110029' => '\\ccxt\\BadRequest', // Hedge mode is not available for this symbol
                    '110030' => '\\ccxt\\InvalidOrder', // Duplicate orderId
                    '110031' => '\\ccxt\\InvalidOrder', // risk limit info does not exists
                    '110032' => '\\ccxt\\InvalidOrder', // Illegal order
                    '110033' => '\\ccxt\\InvalidOrder', // Margin cannot be set without open position
                    '110034' => '\\ccxt\\InvalidOrder', // There is no net position
                    '110035' => '\\ccxt\\InvalidOrder', // Cancel order is not completed before liquidation
                    '110036' => '\\ccxt\\InvalidOrder', // Cross margin mode is not allowed to change leverage
                    '110037' => '\\ccxt\\InvalidOrder', // User setting list does not have this symbol
                    '110038' => '\\ccxt\\InvalidOrder', // Portfolio margin mode is not allowed to change leverage
                    '110039' => '\\ccxt\\InvalidOrder', // Maintain margin rate is too high, which may trigger liquidation
                    '110040' => '\\ccxt\\InvalidOrder', // Order will trigger forced liquidation, please resubmit the order
                    '110041' => '\\ccxt\\InvalidOrder', // Skip liquidation is not allowed when a position or maker order exists
                    '110042' => '\\ccxt\\InvalidOrder', // Pre-delivery status can only reduce positions
                    '110043' => '\\ccxt\\BadRequest', // Set leverage not modified
                    '110044' => '\\ccxt\\InsufficientFunds', // Insufficient available margin
                    '110045' => '\\ccxt\\InsufficientFunds', // Insufficient wallet balance
                    '110046' => '\\ccxt\\BadRequest', // Any adjustments made will trigger immediate liquidation
                    '110047' => '\\ccxt\\BadRequest', // Risk limit cannot be adjusted due to insufficient available margin
                    '110048' => '\\ccxt\\BadRequest', // Risk limit cannot be adjusted current/expected position value held exceeds the revised risk limit
                    '110049' => '\\ccxt\\BadRequest', // Tick notes can only be numbers
                    '110050' => '\\ccxt\\BadRequest', // Coin is not in the range of selected
                    '110051' => '\\ccxt\\InsufficientFunds', // The user's available balance cannot cover the lowest price of the current market
                    '110052' => '\\ccxt\\InsufficientFunds', // User's available balance is insufficient to set a price
                    '110053' => '\\ccxt\\InsufficientFunds', // The user's available balance cannot cover the current market price and upper limit price
                    '110054' => '\\ccxt\\InvalidOrder', // This position has at least one take profit link order, so the take profit and stop loss mode cannot be switched
                    '110055' => '\\ccxt\\InvalidOrder', // This position has at least one stop loss link order, so the take profit and stop loss mode cannot be switched
                    '110056' => '\\ccxt\\InvalidOrder', // This position has at least one trailing stop link order, so the take profit and stop loss mode cannot be switched
                    '110057' => '\\ccxt\\InvalidOrder', // Conditional order or limit order contains TP/SL related params
                    '110058' => '\\ccxt\\InvalidOrder', // Insufficient number of remaining position size to set take profit and stop loss
                    '110059' => '\\ccxt\\InvalidOrder', // In the case of partial filled of the open order, it is not allowed to modify the take profit and stop loss settings of the open order
                    '110060' => '\\ccxt\\BadRequest', // Under full TP/SL mode, it is not allowed to modify TP/SL
                    '110061' => '\\ccxt\\BadRequest', // Under partial TP/SL mode, TP/SL set more than 20
                    '110062' => '\\ccxt\\BadRequest', // Institution MMP profile not found.
                    '110063' => '\\ccxt\\ExchangeError', // Settlement in progress! xxx not available for trades.
                    '110064' => '\\ccxt\\InvalidOrder', // The number of contracts modified cannot be less than or equal to the filled quantity
                    '110065' => '\\ccxt\\PermissionDenied', // MMP hasn't yet been enabled for your account. Please contact your BD manager.
                    '110066' => '\\ccxt\\ExchangeError', // No trading is allowed at the current time
                    '110067' => '\\ccxt\\PermissionDenied', // unified account is not support
                    '110068' => '\\ccxt\\PermissionDenied', // Leveraged user trading is not allowed
                    '110069' => '\\ccxt\\PermissionDenied', // Do not allow OTC lending users to trade
                    '110070' => '\\ccxt\\InvalidOrder', // ETP symbols are not allowed to be traded
                    '110071' => '\\ccxt\\ExchangeError', // Sorry, we're revamping the Unified Margin Account! Currently, new upgrades are not supported. If you have any questions, please contact our 24/7 customer support.
                    '110072' => '\\ccxt\\InvalidOrder', // OrderLinkedID is duplicate
                    '110073' => '\\ccxt\\ExchangeError', // Set margin mode failed
                    '110092' => '\\ccxt\\InvalidOrder', // expect Rising, but trigger_price[XXXXX] <= current[XXXXX]
                    '110093' => '\\ccxt\\InvalidOrder', // expect Falling, but trigger_price[XXXXX] >= current[XXXXX]
                    '110094' => '\\ccxt\\InvalidOrder', // Order notional value below the lower limit
                    '130006' => '\\ccxt\\InvalidOrder', // array("ret_code":130006,"ret_msg":"The number of contracts exceeds maximum limit allowed => too large","ext_code":"","ext_info":"","result":null,"time_now":"1658397095.099030","rate_limit_status":99,"rate_limit_reset_ms":1658397095097,"rate_limit":100)
                    '130021' => '\\ccxt\\InsufficientFunds', // array("ret_code":130021,"ret_msg":"orderfix price failed for CannotAffordOrderCost.","ext_code":"","ext_info":"","result":null,"time_now":"1644588250.204878","rate_limit_status":98,"rate_limit_reset_ms":1644588250200,"rate_limit":100) |  array("ret_code":130021,"ret_msg":"oc_diff[1707966351], new_oc[1707966351] with ob[....]+AB[....]","ext_code":"","ext_info":"","result":null,"time_now":"1658395300.872766","rate_limit_status":99,"rate_limit_reset_ms":1658395300855,"rate_limit":100) caused issues/9149#issuecomment-1146559498
                    '130074' => '\\ccxt\\InvalidOrder', // array("ret_code":130074,"ret_msg":"expect Rising, but trigger_price[190000000] \u003c= current[211280000]??LastPrice","ext_code":"","ext_info":"","result":null,"time_now":"1655386638.067076","rate_limit_status":97,"rate_limit_reset_ms":1655386638065,"rate_limit":100)
                    '131001' => '\\ccxt\\InsufficientFunds', // array("retCode":131001,"retMsg":"the available balance is not sufficient to cover the handling fee","result":array(),"retExtInfo":array(),"time":1666892821245)
                    '131084' => '\\ccxt\\ExchangeError', // Withdraw failed because of Uta Upgrading
                    '131200' => '\\ccxt\\ExchangeError', // Service error
                    '131201' => '\\ccxt\\ExchangeError', // Internal error
                    '131202' => '\\ccxt\\BadRequest', // Invalid memberId
                    '131203' => '\\ccxt\\BadRequest', // Request parameter error
                    '131204' => '\\ccxt\\BadRequest', // Account info error
                    '131205' => '\\ccxt\\BadRequest', // Query transfer error
                    '131206' => '\\ccxt\\ExchangeError', // Fail to transfer
                    '131207' => '\\ccxt\\BadRequest', // Account not exist
                    '131208' => '\\ccxt\\ExchangeError', // Forbid transfer
                    '131209' => '\\ccxt\\BadRequest', // Get subMember relation error
                    '131210' => '\\ccxt\\BadRequest', // Amount accuracy error
                    '131211' => '\\ccxt\\BadRequest', // fromAccountType can't be the same
                    '131212' => '\\ccxt\\InsufficientFunds', // Insufficient balance
                    '131213' => '\\ccxt\\BadRequest', // TransferLTV check error
                    '131214' => '\\ccxt\\BadRequest', // TransferId exist
                    '131215' => '\\ccxt\\BadRequest', // Amount error
                    '131216' => '\\ccxt\\ExchangeError', // Query balance error
                    '131217' => '\\ccxt\\ExchangeError', // Risk check error
                    '131231' => '\\ccxt\\NotSupported', // Transfers into this account are not supported
                    '131232' => '\\ccxt\\NotSupported', // Transfers out this account are not supported
                    '131002' => '\\ccxt\\BadRequest', // Parameter error
                    '131003' => '\\ccxt\\ExchangeError', // Interal error
                    '131004' => '\\ccxt\\AuthenticationError', // KYC needed
                    '131085' => '\\ccxt\\InsufficientFunds', // Withdrawal amount is greater than your availale balance (the deplayed withdrawal is triggered)
                    '131086' => '\\ccxt\\BadRequest', // Withdrawal amount exceeds risk limit (the risk limit of margin trade is triggered)
                    '131088' => '\\ccxt\\BadRequest', // The withdrawal amount exceeds the remaining withdrawal limit of your identity verification level. The current available amount for withdrawal : %s
                    '131089' => '\\ccxt\\BadRequest', // User sensitive operation, withdrawal is prohibited within 24 hours
                    '131090' => '\\ccxt\\ExchangeError', // User withdraw has been banned
                    '131091' => '\\ccxt\\ExchangeError', // Blocked login status does not allow withdrawals
                    '131092' => '\\ccxt\\ExchangeError', // User status is abnormal
                    '131093' => '\\ccxt\\ExchangeError', // The withdrawal address is not in the whitelist
                    '131094' => '\\ccxt\\BadRequest', // UserId is not in the whitelist
                    '131095' => '\\ccxt\\BadRequest', // Withdrawl amount exceeds the 24 hour platform limit
                    '131096' => '\\ccxt\\BadRequest', // Withdraw amount does not satify the lower limit or upper limit
                    '131097' => '\\ccxt\\ExchangeError', // Withdrawal of this currency has been closed
                    '131098' => '\\ccxt\\ExchangeError', // Withdrawal currently is not availble from new address
                    '131099' => '\\ccxt\\ExchangeError', // Hot wallet status can cancel the withdraw
                    '140001' => '\\ccxt\\OrderNotFound', // Order does not exist
                    '140003' => '\\ccxt\\InvalidOrder', // Order price is out of permissible range
                    '140004' => '\\ccxt\\InsufficientFunds', // Insufficient wallet balance
                    '140005' => '\\ccxt\\InvalidOrder', // position status
                    '140006' => '\\ccxt\\InsufficientFunds', // cannot afford estimated position_margin
                    '140007' => '\\ccxt\\InsufficientFunds', // Insufficient available balance
                    '140008' => '\\ccxt\\InvalidOrder', // Order has been finished or canceled
                    '140009' => '\\ccxt\\InvalidOrder', // The number of stop orders exceeds maximum limit allowed
                    '140010' => '\\ccxt\\InvalidOrder', // Order already cancelled
                    '140011' => '\\ccxt\\InvalidOrder', // Any adjustments made will trigger immediate liquidation
                    '140012' => '\\ccxt\\InsufficientFunds', // Available balance not enough
                    '140013' => '\\ccxt\\BadRequest', // Due to risk limit, cannot set leverage
                    '140014' => '\\ccxt\\InsufficientFunds', // Available balance not enough to add margin
                    '140015' => '\\ccxt\\InvalidOrder', // the position is in cross_margin
                    '140016' => '\\ccxt\\InvalidOrder', // Requested quantity of contracts exceeds risk limit, please adjust your risk limit level before trying again
                    '140017' => '\\ccxt\\InvalidOrder', // Reduce-only rule not satisfied
                    '140018' => '\\ccxt\\BadRequest', // userId illegal
                    '140019' => '\\ccxt\\InvalidOrder', // orderId illegal
                    '140020' => '\\ccxt\\InvalidOrder', // number of active orders greater than 500
                    '140021' => '\\ccxt\\InvalidOrder', // Open Interest exceeded
                    '140022' => '\\ccxt\\InvalidOrder', // qty has been limited, cannot modify the order to add qty
                    '140023' => '\\ccxt\\InvalidOrder', // This contract only supports position reduction operation, please contact customer service for details
                    '140024' => '\\ccxt\\BadRequest', // You have an existing position, so position mode cannot be switched
                    '140025' => '\\ccxt\\BadRequest', // Position mode is not modified
                    '140026' => '\\ccxt\\BadRequest', // Cross/isolated margin mode is not modified
                    '140027' => '\\ccxt\\BadRequest', // Margin is not modified
                    '140028' => '\\ccxt\\InvalidOrder', // Open orders exist, so you cannot change position mode
                    '140029' => '\\ccxt\\BadRequest', // Hedge mode is not available for this symbol
                    '140030' => '\\ccxt\\InvalidOrder', // Duplicate orderId
                    '140031' => '\\ccxt\\BadRequest', // risk limit info does not exists
                    '140032' => '\\ccxt\\InvalidOrder', // Illegal order
                    '140033' => '\\ccxt\\InvalidOrder', // Margin cannot be set without open position
                    '140034' => '\\ccxt\\InvalidOrder', // There is no net position
                    '140035' => '\\ccxt\\InvalidOrder', // Cancel order is not completed before liquidation
                    '140036' => '\\ccxt\\BadRequest', // Cross margin mode is not allowed to change leverage
                    '140037' => '\\ccxt\\InvalidOrder', // User setting list does not have this symbol
                    '140038' => '\\ccxt\\BadRequest', // Portfolio margin mode is not allowed to change leverage
                    '140039' => '\\ccxt\\BadRequest', // Maintain margin rate is too high, which may trigger liquidation
                    '140040' => '\\ccxt\\InvalidOrder', // Order will trigger forced liquidation, please resubmit the order
                    '140041' => '\\ccxt\\InvalidOrder', // Skip liquidation is not allowed when a position or maker order exists
                    '140042' => '\\ccxt\\InvalidOrder', // Pre-delivery status can only reduce positions
                    '140043' => '\\ccxt\\BadRequest', // Set leverage not modified
                    '140044' => '\\ccxt\\InsufficientFunds', // Insufficient available margin
                    '140045' => '\\ccxt\\InsufficientFunds', // Insufficient wallet balance
                    '140046' => '\\ccxt\\BadRequest', // Any adjustments made will trigger immediate liquidation
                    '140047' => '\\ccxt\\BadRequest', // Risk limit cannot be adjusted due to insufficient available margin
                    '140048' => '\\ccxt\\BadRequest', // Risk limit cannot be adjusted current/expected position value held exceeds the revised risk limit
                    '140049' => '\\ccxt\\BadRequest', // Tick notes can only be numbers
                    '140050' => '\\ccxt\\InvalidOrder', // Coin is not in the range of selected
                    '140051' => '\\ccxt\\InsufficientFunds', // The user's available balance cannot cover the lowest price of the current market
                    '140052' => '\\ccxt\\InsufficientFunds', // User's available balance is insufficient to set a price
                    '140053' => '\\ccxt\\InsufficientFunds', // The user's available balance cannot cover the current market price and upper limit price
                    '140054' => '\\ccxt\\InvalidOrder', // This position has at least one take profit link order, so the take profit and stop loss mode cannot be switched
                    '140055' => '\\ccxt\\InvalidOrder', // This position has at least one stop loss link order, so the take profit and stop loss mode cannot be switched
                    '140056' => '\\ccxt\\InvalidOrder', // This position has at least one trailing stop link order, so the take profit and stop loss mode cannot be switched
                    '140057' => '\\ccxt\\InvalidOrder', // Conditional order or limit order contains TP/SL related params
                    '140058' => '\\ccxt\\InvalidOrder', // Insufficient number of remaining position size to set take profit and stop loss
                    '140059' => '\\ccxt\\InvalidOrder', // In the case of partial filled of the open order, it is not allowed to modify the take profit and stop loss settings of the open order
                    '140060' => '\\ccxt\\BadRequest', // Under full TP/SL mode, it is not allowed to modify TP/SL
                    '140061' => '\\ccxt\\BadRequest', // Under partial TP/SL mode, TP/SL set more than 20
                    '140062' => '\\ccxt\\BadRequest', // Institution MMP profile not found.
                    '140063' => '\\ccxt\\ExchangeError', // Settlement in progress! xxx not available for trades.
                    '140064' => '\\ccxt\\InvalidOrder', // The number of contracts modified cannot be less than or equal to the filled quantity
                    '140065' => '\\ccxt\\PermissionDenied', // MMP hasn't yet been enabled for your account. Please contact your BD manager.
                    '140066' => '\\ccxt\\ExchangeError', // No trading is allowed at the current time
                    '140067' => '\\ccxt\\PermissionDenied', // unified account is not support
                    '140068' => '\\ccxt\\PermissionDenied', // Leveraged user trading is not allowed
                    '140069' => '\\ccxt\\PermissionDenied', // Do not allow OTC lending users to trade
                    '140070' => '\\ccxt\\InvalidOrder', // ETP symbols are not allowed to be traded
                    '170001' => '\\ccxt\\ExchangeError', // Internal error.
                    '170005' => '\\ccxt\\InvalidOrder', // Too many new orders; current limit is %s orders per %s.
                    '170007' => '\\ccxt\\RequestTimeout', // Timeout waiting for response from backend server.
                    '170010' => '\\ccxt\\InvalidOrder', // Purchase failed => Exceed the maximum position limit of leveraged tokens, the current available limit is %s USDT
                    '170011' => '\\ccxt\\InvalidOrder', // "Purchase failed => Exceed the maximum position limit of innovation tokens,
                    '170019' => '\\ccxt\\InvalidOrder', // the current available limit is replaceKey0 USDT"
                    '170031' => '\\ccxt\\ExchangeError', // The feature has been suspended
                    '170032' => '\\ccxt\\ExchangeError', // Network error. Please try again later
                    '170033' => '\\ccxt\\InsufficientFunds', // margin Insufficient account balance
                    '170034' => '\\ccxt\\InsufficientFunds', // Liability over flow in spot leverage trade!
                    '170035' => '\\ccxt\\BadRequest', // Submitted to the system for processing!
                    '170036' => '\\ccxt\\BadRequest', // You haven't enabled Cross Margin Trading yet. To do so, please head to the PC trading site or the Bybit app
                    '170037' => '\\ccxt\\BadRequest', // Cross Margin Trading not yet supported by the selected coin
                    '170105' => '\\ccxt\\BadRequest', // Parameter '%s' was empty.
                    '170115' => '\\ccxt\\InvalidOrder', // Invalid timeInForce.
                    '170116' => '\\ccxt\\InvalidOrder', // Invalid orderType.
                    '170117' => '\\ccxt\\InvalidOrder', // Invalid side.
                    '170121' => '\\ccxt\\InvalidOrder', // Invalid symbol.
                    '170124' => '\\ccxt\\InvalidOrder', // Order amount too large.
                    '170130' => '\\ccxt\\BadRequest', // Data sent for paramter '%s' is not valid.
                    '170131' => '\\ccxt\\InsufficientFunds', // Balance insufficient
                    '170132' => '\\ccxt\\InvalidOrder', // Order price too high.
                    '170133' => '\\ccxt\\InvalidOrder', // Order price lower than the minimum.
                    '170134' => '\\ccxt\\InvalidOrder', // Order price decimal too long.
                    '170135' => '\\ccxt\\InvalidOrder', // Order quantity too large.
                    '170136' => '\\ccxt\\InvalidOrder', // Order quantity lower than the minimum.
                    '170137' => '\\ccxt\\InvalidOrder', // Order volume decimal too long
                    '170139' => '\\ccxt\\InvalidOrder', // Order has been filled.
                    '170140' => '\\ccxt\\InvalidOrder', // Transaction amount lower than the minimum.
                    '170141' => '\\ccxt\\InvalidOrder', // Duplicate clientOrderId
                    '170142' => '\\ccxt\\InvalidOrder', // Order has been canceled
                    '170143' => '\\ccxt\\InvalidOrder', // Cannot be found on order book
                    '170144' => '\\ccxt\\InvalidOrder', // Order has been locked
                    '170145' => '\\ccxt\\InvalidOrder', // This order type does not support cancellation
                    '170146' => '\\ccxt\\InvalidOrder', // Order creation timeout
                    '170147' => '\\ccxt\\InvalidOrder', // Order cancellation timeout
                    '170148' => '\\ccxt\\InvalidOrder', // Market order amount decimal too long
                    '170149' => '\\ccxt\\ExchangeError', // Create order failed
                    '170150' => '\\ccxt\\ExchangeError', // Cancel order failed
                    '170151' => '\\ccxt\\InvalidOrder', // The trading pair is not open yet
                    '170157' => '\\ccxt\\InvalidOrder', // The trading pair is not available for api trading
                    '170159' => '\\ccxt\\InvalidOrder', // Market Order is not supported within the first %s minutes of newly launched pairs due to risk control.
                    '170190' => '\\ccxt\\InvalidOrder', // Cancel order has been finished
                    '170191' => '\\ccxt\\InvalidOrder', // Can not cancel order, please try again later
                    '170192' => '\\ccxt\\InvalidOrder', // Order price cannot be higher than %s .
                    '170193' => '\\ccxt\\InvalidOrder', // Buy order price cannot be higher than %s.
                    '170194' => '\\ccxt\\InvalidOrder', // Sell order price cannot be lower than %s.
                    '170195' => '\\ccxt\\InvalidOrder', // Please note that your order may not be filled
                    '170196' => '\\ccxt\\InvalidOrder', // Please note that your order may not be filled
                    '170197' => '\\ccxt\\InvalidOrder', // Your order quantity to buy is too large. The filled price may deviate significantly from the market price. Please try again
                    '170198' => '\\ccxt\\InvalidOrder', // Your order quantity to sell is too large. The filled price may deviate significantly from the market price. Please try again
                    '170199' => '\\ccxt\\InvalidOrder', // Your order quantity to buy is too large. The filled price may deviate significantly from the nav. Please try again.
                    '170200' => '\\ccxt\\InvalidOrder', // Your order quantity to sell is too large. The filled price may deviate significantly from the nav. Please try again.
                    '170201' => '\\ccxt\\PermissionDenied', // Your account has been restricted for trades. If you have any questions, please email us at support@bybit.com
                    '170202' => '\\ccxt\\InvalidOrder', // Invalid orderFilter parameter.
                    '170203' => '\\ccxt\\InvalidOrder', // Please enter the TP/SL price.
                    '170204' => '\\ccxt\\InvalidOrder', // trigger price cannot be higher than 110% price.
                    '170206' => '\\ccxt\\InvalidOrder', // trigger price cannot be lower than 90% of qty.
                    '170210' => '\\ccxt\\InvalidOrder', // New order rejected.
                    '170213' => '\\ccxt\\OrderNotFound', // Order does not exist.
                    '170217' => '\\ccxt\\InvalidOrder', // Only LIMIT-MAKER order is supported for the current pair.
                    '170218' => '\\ccxt\\InvalidOrder', // The LIMIT-MAKER order is rejected due to invalid price.
                    '170221' => '\\ccxt\\BadRequest', // This coin does not exist.
                    '170222' => '\\ccxt\\RateLimitExceeded', // Too many property_exists($this, requests) time frame.
                    '170223' => '\\ccxt\\InsufficientFunds', // Your Spot Account with Institutional Lending triggers an alert or liquidation.
                    '170224' => '\\ccxt\\PermissionDenied', // You're not a user of the Innovation Zone.
                    '170226' => '\\ccxt\\InsufficientFunds', // Your Spot Account for Margin Trading is being liquidated.
                    '170227' => '\\ccxt\\ExchangeError', // This feature is not supported.
                    '170228' => '\\ccxt\\InvalidOrder', // The purchase amount of each order exceeds the estimated maximum purchase amount.
                    '170229' => '\\ccxt\\InvalidOrder', // The sell quantity per order exceeds the estimated maximum sell quantity.
                    '170234' => '\\ccxt\\ExchangeError', // System Error
                    '170241' => '\\ccxt\\ManualInteractionNeeded', // To proceed with trading, users must read through and confirm that they fully understand the project's risk disclosure document.
                    '175000' => '\\ccxt\\InvalidOrder', // The serialNum is already in use.
                    '175001' => '\\ccxt\\InvalidOrder', // Daily purchase limit has been exceeded. Please try again later.
                    '175002' => '\\ccxt\\InvalidOrder', // There's a large number of purchase orders. Please try again later.
                    '175003' => '\\ccxt\\InsufficientFunds', // Insufficient available balance. Please make a deposit and try again.
                    '175004' => '\\ccxt\\InvalidOrder', // Daily redemption limit has been exceeded. Please try again later.
                    '175005' => '\\ccxt\\InvalidOrder', // There's a large number of redemption orders. Please try again later.
                    '175006' => '\\ccxt\\InsufficientFunds', // Insufficient available balance. Please make a deposit and try again.
                    '175007' => '\\ccxt\\InvalidOrder', // Order not found.
                    '175008' => '\\ccxt\\InvalidOrder', // Purchase period hasn't started yet.
                    '175009' => '\\ccxt\\InvalidOrder', // Purchase amount has exceeded the upper limit.
                    '175010' => '\\ccxt\\PermissionDenied', // You haven't passed the quiz yet! To purchase and/or redeem an LT, please complete the quiz first.
                    '175012' => '\\ccxt\\InvalidOrder', // Redemption period hasn't started yet.
                    '175013' => '\\ccxt\\InvalidOrder', // Redemption amount has exceeded the upper limit.
                    '175014' => '\\ccxt\\InvalidOrder', // Purchase of the LT has been temporarily suspended.
                    '175015' => '\\ccxt\\InvalidOrder', // Redemption of the LT has been temporarily suspended.
                    '175016' => '\\ccxt\\InvalidOrder', // Invalid format. Please check the length and numeric precision.
                    '175017' => '\\ccxt\\InvalidOrder', // Failed to place order：Exceed the maximum position limit of leveraged tokens, the current available limit is XXXX USDT
                    '175027' => '\\ccxt\\ExchangeError', // Subscriptions and redemptions are temporarily unavailable while account upgrade is in progress
                    '176002' => '\\ccxt\\BadRequest', // Query user account info error
                    '176004' => '\\ccxt\\BadRequest', // Query order history start time exceeds end time
                    '176003' => '\\ccxt\\BadRequest', // Query user loan history error
                    '176006' => '\\ccxt\\BadRequest', // Repayment Failed
                    '176005' => '\\ccxt\\BadRequest', // Failed to borrow
                    '176008' => '\\ccxt\\BadRequest', // You haven't enabled Cross Margin Trading yet. To do so
                    '176007' => '\\ccxt\\BadRequest', // User not found
                    '176010' => '\\ccxt\\BadRequest', // Failed to locate the coins to borrow
                    '176009' => '\\ccxt\\BadRequest', // You haven't enabled Cross Margin Trading yet. To do so
                    '176012' => '\\ccxt\\BadRequest', // Pair not available
                    '176011' => '\\ccxt\\BadRequest', // Cross Margin Trading not yet supported by the selected coin
                    '176014' => '\\ccxt\\BadRequest', // Repeated repayment requests
                    '176013' => '\\ccxt\\BadRequest', // Cross Margin Trading not yet supported by the selected pair
                    '176015' => '\\ccxt\\InsufficientFunds', // Insufficient available balance
                    '176016' => '\\ccxt\\BadRequest', // No repayment required
                    '176017' => '\\ccxt\\BadRequest', // Repayment amount has exceeded the total liability
                    '176018' => '\\ccxt\\BadRequest', // Settlement in progress
                    '176019' => '\\ccxt\\BadRequest', // Liquidation in progress
                    '176020' => '\\ccxt\\BadRequest', // Failed to locate repayment history
                    '176021' => '\\ccxt\\BadRequest', // Repeated borrowing requests
                    '176022' => '\\ccxt\\BadRequest', // Coins to borrow not generally available yet
                    '176023' => '\\ccxt\\BadRequest', // Pair to borrow not generally available yet
                    '176024' => '\\ccxt\\BadRequest', // Invalid user status
                    '176025' => '\\ccxt\\BadRequest', // Amount to borrow cannot be lower than the min. amount to borrow (per transaction)
                    '176026' => '\\ccxt\\BadRequest', // Amount to borrow cannot be larger than the max. amount to borrow (per transaction)
                    '176027' => '\\ccxt\\BadRequest', // Amount to borrow cannot be higher than the max. amount to borrow per user
                    '176028' => '\\ccxt\\BadRequest', // Amount to borrow has exceeded Bybit's max. amount to borrow
                    '176029' => '\\ccxt\\BadRequest', // Amount to borrow has exceeded the user's estimated max. amount to borrow
                    '176030' => '\\ccxt\\BadRequest', // Query user loan info error
                    '176031' => '\\ccxt\\BadRequest', // Number of decimals has exceeded the maximum precision
                    '176034' => '\\ccxt\\BadRequest', // The leverage ratio is out of range
                    '176035' => '\\ccxt\\PermissionDenied', // Failed to close the leverage switch during liquidation
                    '176036' => '\\ccxt\\PermissionDenied', // Failed to adjust leverage switch during forced liquidation
                    '176037' => '\\ccxt\\PermissionDenied', // For non-unified transaction users, the operation failed
                    '176038' => '\\ccxt\\BadRequest', // The spot leverage is closed and the current operation is not allowed
                    '176039' => '\\ccxt\\BadRequest', // Borrowing, current operation is not allowed
                    '176040' => '\\ccxt\\BadRequest', // There is a spot leverage order, and the adjustment of the leverage switch failed!
                    '181000' => '\\ccxt\\BadRequest', // category is null
                    '181001' => '\\ccxt\\BadRequest', // category only support linear or option or spot.
                    '181002' => '\\ccxt\\InvalidOrder', // symbol is null.
                    '181003' => '\\ccxt\\InvalidOrder', // side is null.
                    '181004' => '\\ccxt\\InvalidOrder', // side only support Buy or Sell.
                    '182000' => '\\ccxt\\InvalidOrder', // symbol related quote price is null
                    '181017' => '\\ccxt\\BadRequest', // OrderStatus must be final status
                    '20001' => '\\ccxt\\OrderNotFound', // Order not exists
                    '20003' => '\\ccxt\\InvalidOrder', // missing parameter side
                    '20004' => '\\ccxt\\InvalidOrder', // invalid parameter side
                    '20005' => '\\ccxt\\InvalidOrder', // missing parameter symbol
                    '20006' => '\\ccxt\\InvalidOrder', // invalid parameter symbol
                    '20007' => '\\ccxt\\InvalidOrder', // missing parameter order_type
                    '20008' => '\\ccxt\\InvalidOrder', // invalid parameter order_type
                    '20009' => '\\ccxt\\InvalidOrder', // missing parameter qty
                    '20010' => '\\ccxt\\InvalidOrder', // qty must be greater than 0
                    '20011' => '\\ccxt\\InvalidOrder', // qty must be an integer
                    '20012' => '\\ccxt\\InvalidOrder', // qty must be greater than zero and less than 1 million
                    '20013' => '\\ccxt\\InvalidOrder', // missing parameter price
                    '20014' => '\\ccxt\\InvalidOrder', // price must be greater than 0
                    '20015' => '\\ccxt\\InvalidOrder', // missing parameter time_in_force
                    '20016' => '\\ccxt\\InvalidOrder', // invalid value for parameter time_in_force
                    '20017' => '\\ccxt\\InvalidOrder', // missing parameter order_id
                    '20018' => '\\ccxt\\InvalidOrder', // invalid date format
                    '20019' => '\\ccxt\\InvalidOrder', // missing parameter stop_px
                    '20020' => '\\ccxt\\InvalidOrder', // missing parameter base_price
                    '20021' => '\\ccxt\\InvalidOrder', // missing parameter stop_order_id
                    '20022' => '\\ccxt\\BadRequest', // missing parameter leverage
                    '20023' => '\\ccxt\\BadRequest', // leverage must be a number
                    '20031' => '\\ccxt\\BadRequest', // leverage must be greater than zero
                    '20070' => '\\ccxt\\BadRequest', // missing parameter margin
                    '20071' => '\\ccxt\\BadRequest', // margin must be greater than zero
                    '20084' => '\\ccxt\\BadRequest', // order_id or order_link_id is required
                    '30001' => '\\ccxt\\BadRequest', // order_link_id is repeated
                    '30003' => '\\ccxt\\InvalidOrder', // qty must be more than the minimum allowed
                    '30004' => '\\ccxt\\InvalidOrder', // qty must be less than the maximum allowed
                    '30005' => '\\ccxt\\InvalidOrder', // price exceeds maximum allowed
                    '30007' => '\\ccxt\\InvalidOrder', // price exceeds minimum allowed
                    '30008' => '\\ccxt\\InvalidOrder', // invalid order_type
                    '30009' => '\\ccxt\\ExchangeError', // no position found
                    '30010' => '\\ccxt\\InsufficientFunds', // insufficient wallet balance
                    '30011' => '\\ccxt\\PermissionDenied', // operation not allowed is undergoing liquidation
                    '30012' => '\\ccxt\\PermissionDenied', // operation not allowed is undergoing ADL
                    '30013' => '\\ccxt\\PermissionDenied', // position is in liq or adl status
                    '30014' => '\\ccxt\\InvalidOrder', // invalid closing order, qty should not greater than size
                    '30015' => '\\ccxt\\InvalidOrder', // invalid closing order, side should be opposite
                    '30016' => '\\ccxt\\ExchangeError', // TS and SL must be cancelled first while closing position
                    '30017' => '\\ccxt\\InvalidOrder', // estimated fill price cannot be lower than current Buy liq_price
                    '30018' => '\\ccxt\\InvalidOrder', // estimated fill price cannot be higher than current Sell liq_price
                    '30019' => '\\ccxt\\InvalidOrder', // cannot attach TP/SL params for non-zero position when placing non-opening position order
                    '30020' => '\\ccxt\\InvalidOrder', // position already has TP/SL params
                    '30021' => '\\ccxt\\InvalidOrder', // cannot afford estimated position_margin
                    '30022' => '\\ccxt\\InvalidOrder', // estimated buy liq_price cannot be higher than current mark_price
                    '30023' => '\\ccxt\\InvalidOrder', // estimated sell liq_price cannot be lower than current mark_price
                    '30024' => '\\ccxt\\InvalidOrder', // cannot set TP/SL/TS for zero-position
                    '30025' => '\\ccxt\\InvalidOrder', // trigger price should bigger than 10% of last price
                    '30026' => '\\ccxt\\InvalidOrder', // price too high
                    '30027' => '\\ccxt\\InvalidOrder', // price set for Take profit should be higher than Last Traded Price
                    '30028' => '\\ccxt\\InvalidOrder', // price set for Stop loss should be between Liquidation price and Last Traded Price
                    '30029' => '\\ccxt\\InvalidOrder', // price set for Stop loss should be between Last Traded Price and Liquidation price
                    '30030' => '\\ccxt\\InvalidOrder', // price set for Take profit should be lower than Last Traded Price
                    '30031' => '\\ccxt\\InsufficientFunds', // insufficient available balance for order cost
                    '30032' => '\\ccxt\\InvalidOrder', // order has been filled or cancelled
                    '30033' => '\\ccxt\\RateLimitExceeded', // The number of stop orders exceeds maximum limit allowed
                    '30034' => '\\ccxt\\OrderNotFound', // no order found
                    '30035' => '\\ccxt\\RateLimitExceeded', // too fast to cancel
                    '30036' => '\\ccxt\\ExchangeError', // the expected position value after order execution exceeds the current risk limit
                    '30037' => '\\ccxt\\InvalidOrder', // order already cancelled
                    '30041' => '\\ccxt\\ExchangeError', // no position found
                    '30042' => '\\ccxt\\InsufficientFunds', // insufficient wallet balance
                    '30043' => '\\ccxt\\InvalidOrder', // operation not allowed is undergoing liquidation
                    '30044' => '\\ccxt\\InvalidOrder', // operation not allowed is undergoing AD
                    '30045' => '\\ccxt\\InvalidOrder', // operation not allowed is not normal status
                    '30049' => '\\ccxt\\InsufficientFunds', // insufficient available balance
                    '30050' => '\\ccxt\\ExchangeError', // any adjustments made will trigger immediate liquidation
                    '30051' => '\\ccxt\\ExchangeError', // due to risk limit, cannot adjust leverage
                    '30052' => '\\ccxt\\ExchangeError', // leverage can not less than 1
                    '30054' => '\\ccxt\\ExchangeError', // position margin is invalid
                    '30057' => '\\ccxt\\ExchangeError', // requested quantity of contracts exceeds risk limit
                    '30063' => '\\ccxt\\ExchangeError', // reduce-only rule not satisfied
                    '30067' => '\\ccxt\\InsufficientFunds', // insufficient available balance
                    '30068' => '\\ccxt\\ExchangeError', // exit value must be positive
                    '30074' => '\\ccxt\\InvalidOrder', // can't create the stop order, because you expect the order will be triggered when the LastPrice(or IndexPrice、 MarkPrice, determined by trigger_by) is raising to stop_px, but the LastPrice(or IndexPrice、 MarkPrice) is already equal to or greater than stop_px, please adjust base_price or stop_px
                    '30075' => '\\ccxt\\InvalidOrder', // can't create the stop order, because you expect the order will be triggered when the LastPrice(or IndexPrice、 MarkPrice, determined by trigger_by) is falling to stop_px, but the LastPrice(or IndexPrice、 MarkPrice) is already equal to or less than stop_px, please adjust base_price or stop_px
                    '30078' => '\\ccxt\\ExchangeError', // array("ret_code":30078,"ret_msg":"","ext_code":"","ext_info":"","result":null,"time_now":"1644853040.916000","rate_limit_status":73,"rate_limit_reset_ms":1644853040912,"rate_limit":75)
                    // '30084' => '\\ccxt\\BadRequest', // Isolated not modified, see handleErrors below
                    '33004' => '\\ccxt\\AuthenticationError', // apikey already expired
                    '34026' => '\\ccxt\\ExchangeError', // the limit is no change
                    '34036' => '\\ccxt\\BadRequest', // array("ret_code":34036,"ret_msg":"leverage not modified","ext_code":"","ext_info":"","result":null,"time_now":"1652376449.258918","rate_limit_status":74,"rate_limit_reset_ms":1652376449255,"rate_limit":75)
                    '35015' => '\\ccxt\\BadRequest', // array("ret_code":35015,"ret_msg":"Qty not in range","ext_code":"","ext_info":"","result":null,"time_now":"1652277215.821362","rate_limit_status":99,"rate_limit_reset_ms":1652277215819,"rate_limit":100)
                    '340099' => '\\ccxt\\ExchangeError', // Server error
                    '3400045' => '\\ccxt\\ExchangeError', // Set margin mode failed
                    '3100116' => '\\ccxt\\BadRequest', // array("retCode":3100116,"retMsg":"Order quantity below the lower limit 0.01.","result":null,"retExtMap":array("key0":"0.01"))
                    '3100198' => '\\ccxt\\BadRequest', // array("retCode":3100198,"retMsg":"orderLinkId can not be empty.","result":null,"retExtMap":array())
                    '3200300' => '\\ccxt\\InsufficientFunds', // array("retCode":3200300,"retMsg":"Insufficient margin balance.","result":null,"retExtMap":array())
                ),
                'broad' => array(
                    'Not supported symbols' => '\\ccxt\\BadSymbol', // array("retCode":10001,"retMsg":"Not supported symbols","result":array(),"retExtInfo":array(),"time":1726147060461)
                    'Request timeout' => '\\ccxt\\RequestTimeout', // array("retCode":10016,"retMsg":"Request timeout, please try again later","result":array(),"retExtInfo":array(),"time":1675307914985)
                    'unknown orderInfo' => '\\ccxt\\OrderNotFound', // array("ret_code":-1,"ret_msg":"unknown orderInfo","ext_code":"","ext_info":"","result":null,"time_now":"1584030414.005545","rate_limit_status":99,"rate_limit_reset_ms":1584030414003,"rate_limit":100)
                    'invalid api_key' => '\\ccxt\\AuthenticationError', // array("ret_code":10003,"ret_msg":"invalid api_key","ext_code":"","ext_info":"","result":null,"time_now":"1599547085.415797")
                    // the below two issues are caused => issues/9149#issuecomment-1146559498, when response is such =>  array("ret_code":130021,"ret_msg":"oc_diff[1707966351], new_oc[1707966351] with ob[....]+AB[....]","ext_code":"","ext_info":"","result":null,"time_now":"1658395300.872766","rate_limit_status":99,"rate_limit_reset_ms":1658395300855,"rate_limit":100)
                    'oc_diff' => '\\ccxt\\InsufficientFunds',
                    'new_oc' => '\\ccxt\\InsufficientFunds',
                    'openapi sign params error!' => '\\ccxt\\AuthenticationError', // array("retCode":10001,"retMsg":"empty value => apiTimestamparray() apiKeyarray() apiSignature[xxxxxxxxxxxxxxxxxxxxxxx] => openapi sign params error!","result":null,"retExtInfo":null,"time":1664789597123)
                ),
            ),
            'precisionMode' => TICK_SIZE,
            'options' => array(
                'usePrivateInstrumentsInfo' => false,
                'enableDemoTrading' => false,
                'fetchMarkets' => array(
                    'types' => array( 'spot', 'linear', 'inverse', 'option' ),
                ),
                'enableUnifiedMargin' => null,
                'enableUnifiedAccount' => null,
                'unifiedMarginStatus' => null,
                'createMarketBuyOrderRequiresPrice' => false, // only true for classic accounts
                'createUnifiedMarginAccount' => false,
                'defaultType' => 'swap',  // 'swap', 'future', 'option', 'spot'
                'defaultSubType' => 'linear',  // 'linear', 'inverse'
                'defaultSettle' => 'USDT', // USDC for USDC settled markets
                'code' => 'BTC',
                'recvWindow' => 5 * 1000, // 5 sec default
                'timeDifference' => 0, // the difference between system clock and exchange server clock
                'adjustForTimeDifference' => false, // controls the adjustment logic upon instantiation
                'loadAllOptions' => false, // load all possible option markets, adds signficant load time
                'loadExpiredOptions' => false, // loads expired options, to load all possible expired options set loadAllOptions to true
                'brokerId' => 'CCXT',
                'accountsByType' => array(
                    'spot' => 'SPOT',
                    'margin' => 'SPOT',
                    'future' => 'CONTRACT',
                    'swap' => 'CONTRACT',
                    'option' => 'OPTION',
                    'investment' => 'INVESTMENT',
                    'unified' => 'UNIFIED',
                    'funding' => 'FUND',
                    'fund' => 'FUND',
                    'contract' => 'CONTRACT',
                ),
                'accountsById' => array(
                    'SPOT' => 'spot',
                    'MARGIN' => 'spot',
                    'CONTRACT' => 'contract',
                    'OPTION' => 'option',
                    'INVESTMENT' => 'investment',
                    'UNIFIED' => 'unified',
                    'FUND' => 'fund',
                ),
                'networks' => array(
                    'ERC20' => 'ETH',
                    'TRC20' => 'TRX',
                    'BEP20' => 'BSC',
                    'SOL' => 'SOL',
                    'ACA' => 'ACA',
                    'ADA' => 'ADA',
                    'ALGO' => 'ALGO',
                    'APT' => 'APTOS',
                    'AR' => 'AR',
                    'ARBONE' => 'ARBI',
                    'AVAXC' => 'CAVAX',
                    'AVAXX' => 'XAVAX',
                    'ATOM' => 'ATOM',
                    'BCH' => 'BCH',
                    'BEP2' => 'BNB',
                    'CHZ' => 'CHZ',
                    'DCR' => 'DCR',
                    'DGB' => 'DGB',
                    'DOGE' => 'DOGE',
                    'DOT' => 'DOT',
                    'EGLD' => 'EGLD',
                    'EOS' => 'EOS',
                    'ETC' => 'ETC',
                    'ETHF' => 'ETHF',
                    'ETHW' => 'ETHW',
                    'FIL' => 'FIL',
                    'STEP' => 'FITFI',
                    'FLOW' => 'FLOW',
                    'FTM' => 'FTM',
                    'GLMR' => 'GLMR',
                    'HBAR' => 'HBAR',
                    'HNT' => 'HNT',
                    'ICP' => 'ICP',
                    'ICX' => 'ICX',
                    'KDA' => 'KDA',
                    'KLAY' => 'KLAY',
                    'KMA' => 'KMA',
                    'KSM' => 'KSM',
                    'LTC' => 'LTC',
                    // 'TERRA' => 'LUNANEW',
                    // 'TERRACLASSIC' => 'LUNA',
                    'MATIC' => 'MATIC',
                    'MINA' => 'MINA',
                    'MOVR' => 'MOVR',
                    'NEAR' => 'NEAR',
                    'NEM' => 'NEM',
                    'OASYS' => 'OAS',
                    'OASIS' => 'ROSE',
                    'OMNI' => 'OMNI',
                    'ONE' => 'ONE',
                    'OPTIMISM' => 'OP',
                    'POKT' => 'POKT',
                    'QTUM' => 'QTUM',
                    'RVN' => 'RVN',
                    'SC' => 'SC',
                    'SCRT' => 'SCRT',
                    'STX' => 'STX',
                    'THETA' => 'THETA',
                    'TON' => 'TON',
                    'WAVES' => 'WAVES',
                    'WAX' => 'WAXP',
                    'XDC' => 'XDC',
                    'XEC' => 'XEC',
                    'XLM' => 'XLM',
                    'XRP' => 'XRP',
                    'XTZ' => 'XTZ',
                    'XYM' => 'XYM',
                    'ZEN' => 'ZEN',
                    'ZIL' => 'ZIL',
                    'ZKSYNC' => 'ZKSYNC',
                    // todo => uncomment after consensus
                    // 'CADUCEUS' => 'CMP',
                    // 'KON' => 'KON', // konpay, "konchain"
                    // 'AURORA' => 'AURORA',
                    // 'BITCOINGOLD' => 'BTG',
                ),
                'networksById' => array(
                    'ETH' => 'ERC20',
                    'TRX' => 'TRC20',
                    'BSC' => 'BEP20',
                    'OMNI' => 'OMNI',
                    'SPL' => 'SOL',
                ),
                'defaultNetwork' => 'ERC20',
                'defaultNetworks' => array(
                    'USDT' => 'TRC20',
                ),
                'intervals' => array(
                    '5m' => '5min',
                    '15m' => '15min',
                    '30m' => '30min',
                    '1h' => '1h',
                    '4h' => '4h',
                    '1d' => '1d',
                ),
                'useMarkPriceForPositionCollateral' => false, // use mark price for position collateral
            ),
            'features' => array(
                'default' => array(
                    'sandbox' => true,
                    'createOrder' => array(
                        'marginMode' => false,
                        'triggerPrice' => true,
                        'triggerPriceType' => array(
                            'last' => true,
                            'mark' => true,
                            'index' => true,
                        ),
                        'triggerDirection' => true,
                        '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,
                        'selfTradePrevention' => true, // todo => implement
                        'trailing' => true,
                        'iceberg' => false,
                        'leverage' => false,
                        'marketBuyRequiresPrice' => false,
                        'marketBuyByCost' => true,
                    ),
                    'createOrders' => array(
                        'max' => 10,
                    ),
                    'fetchMyTrades' => array(
                        'marginMode' => false,
                        'limit' => 100,
                        'daysBack' => 365 * 2, // 2 years
                        'untilDays' => 7, // days between start-end
                        'symbolRequired' => false,
                    ),
                    'fetchOrder' => array(
                        'marginMode' => false,
                        'trigger' => true,
                        'trailing' => false,
                        'symbolRequired' => true,
                    ),
                    'fetchOpenOrders' => array(
                        'marginMode' => false,
                        'limit' => 50,
                        'trigger' => true,
                        'trailing' => false,
                        'symbolRequired' => false,
                    ),
                    'fetchOrders' => null,
                    'fetchClosedOrders' => array(
                        'marginMode' => false,
                        'limit' => 50,
                        'daysBack' => 365 * 2, // 2 years
                        'daysBackCanceled' => 1,
                        'untilDays' => 7,
                        'trigger' => true,
                        'trailing' => false,
                        'symbolRequired' => false,
                    ),
                    'fetchOHLCV' => array(
                        'limit' => 1000,
                    ),
                    'editOrders' => array(
                        'max' => 10,
                    ),
                ),
                'spot' => array(
                    'extends' => 'default',
                    'fetchCurrencies' => array(
                        'private' => true,
                    ),
                    'createOrder' => array(
                        'triggerPriceType' => null,
                        'triggerDirection' => false,
                        'attachedStopLossTakeProfit' => array(
                            'triggerPriceType' => null,
                            'price' => true,
                        ),
                        'marketBuyRequiresPrice' => true,
                    ),
                ),
                'swap' => array(
                    'linear' => array(
                        'extends' => 'default',
                    ),
                    'inverse' => array(
                        'extends' => 'default',
                    ),
                ),
                'future' => array(
                    'linear' => array(
                        'extends' => 'default',
                    ),
                    'inverse' => array(
                        'extends' => 'default',
                    ),
                ),
            ),
            'fees' => array(
                'trading' => array(
                    'feeSide' => 'get',
                    'tierBased' => true,
                    'percentage' => true,
                    'taker' => 0.00075,
                    'maker' => 0.0001,
                ),
                'funding' => array(
                    'tierBased' => false,
                    'percentage' => false,
                    'withdraw' => array(),
                    'deposit' => array(),
                ),
            ),
        ));
    }

    public function enable_demo_trading(bool $enable) {
        /**
         * enables or disables demo trading mode
         *
         * @see https://bybit-exchange.github.io/docs/v5/demo
         *
         * @param {boolean} [$enable] true if demo trading should be enabled, false otherwise
         */
        if ($this->isSandboxModeEnabled) {
            throw new NotSupported($this->id . ' demo trading does not support in sandbox environment');
        }
        // $enable demo trading in bybit, see => https://bybit-exchange.github.io/docs/v5/demo
        if ($enable) {
            $this->urls['apiBackupDemoTrading'] = $this->urls['api'];
            $this->urls['api'] = $this->urls['demotrading'];
        } elseif (is_array($this->urls) && array_key_exists('apiBackupDemoTrading', $this->urls)) {
            $this->urls['api'] = $this->urls['apiBackupDemoTrading'];
            $newUrls = $this->omit($this->urls, 'apiBackupDemoTrading');
            $this->urls = $newUrls;
        }
        $this->options['enableDemoTrading'] = $enable;
    }

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

    public function add_pagination_cursor_to_result($response) {
        $result = $this->safe_dict($response, 'result', array());
        $data = $this->safe_list_n($result, array( 'list', 'rows', 'data', 'dataList' ), array());
        $paginationCursor = $this->safe_string_2($result, 'nextPageCursor', 'cursor');
        $dataLength = count($data);
        if (($paginationCursor !== null) && ($dataLength > 0)) {
            $first = $data[0];
            $first['nextPageCursor'] = $paginationCursor;
            $data[0] = $first;
        }
        return $data;
    }

    public function is_unified_enabled($params = array ()) {
        return Async\async(function () use ($params) {
            /**
             *
             * @see https://bybit-exchange.github.io/docs/v5/user/apikey-info#http-request
             * @see https://bybit-exchange.github.io/docs/v5/account/account-info
             *
             * returns [$enableUnifiedMargin, $enableUnifiedAccount] so the user can check if unified account is enabled
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {any} [$enableUnifiedMargin, $enableUnifiedAccount]
             */
            // The API key of user id must own one of permissions will be allowed to call following API endpoints:
            // SUB UID => "Account Transfer"
            // MASTER UID => "Account Transfer", "Subaccount Transfer", "Withdrawal"
            $enableUnifiedMargin = $this->safe_bool($this->options, 'enableUnifiedMargin');
            $enableUnifiedAccount = $this->safe_bool($this->options, 'enableUnifiedAccount');
            if ($enableUnifiedMargin === null || $enableUnifiedAccount === null) {
                if ($this->options['enableDemoTrading']) {
                    // info endpoint is not available in demo trading
                    // so we're assuming UTA is enabled
                    $this->options['enableUnifiedMargin'] = false;
                    $this->options['enableUnifiedAccount'] = true;
                    $this->options['unifiedMarginStatus'] = 6;
                    return [ $this->options['enableUnifiedMargin'], $this->options['enableUnifiedAccount'] ];
                }
                $rawPromises = array( $this->privateGetV5UserQueryApi ($params), $this->privateGetV5AccountInfo ($params) );
                $promises = Async\await(Promise\all($rawPromises));
                $response = $promises[0];
                $accountInfo = $promises[1];
                //
                //     {
                //         "retCode" => 0,
                //         "retMsg" => "",
                //         "result" => array(
                //             "id" => "13770661",
                //             "note" => "XXXXXX",
                //             "apiKey" => "XXXXXX",
                //             "readOnly" => 0,
                //             "secret" => "",
                //             "permissions" => array(
                //                 "ContractTrade" => [...],
                //                 "Spot" => [...],
                //                 "Wallet" => [...],
                //                 "Options" => [...],
                //                 "Derivatives" => [...],
                //                 "CopyTrading" => [...],
                //                 "BlockTrade" => [...],
                //                 "Exchange" => [...],
                //                 "NFT" => [...],
                //             ),
                //             "ips" => [...],
                //             "type" => 1,
                //             "deadlineDay" => 83,
                //             "expiredAt" => "2023-05-15T03:21:05Z",
                //             "createdAt" => "2022-10-16T02:24:40Z",
                //             "unified" => 0,
                //             "uta" => 0,
                //             "userID" => 24600000,
                //             "inviterID" => 0,
                //             "vipLevel" => "No VIP",
                //             "mktMakerLevel" => "0",
                //             "affiliateID" => 0,
                //             "rsaPublicKey" => "",
                //             "isMaster" => false
                //         ),
                //         "retExtInfo" => array(),
                //         "time" => 1676891757649
                //     }
                // account info
                //     {
                //         "retCode" => 0,
                //         "retMsg" => "OK",
                //         "result" => {
                //             "marginMode" => "REGULAR_MARGIN",
                //             "updatedTime" => "1697078946000",
                //             "unifiedMarginStatus" => 4,
                //             "dcpStatus" => "OFF",
                //             "timeWindow" => 10,
                //             "smpGroup" => 0,
                //             "isMasterTrader" => false,
                //             "spotHedgingStatus" => "OFF"
                //         }
                //     }
                //
                $result = $this->safe_dict($response, 'result', array());
                $accountResult = $this->safe_dict($accountInfo, 'result', array());
                $this->options['enableUnifiedMargin'] = $this->safe_integer($result, 'unified') === 1;
                $this->options['enableUnifiedAccount'] = $this->safe_integer($result, 'uta') === 1;
                $this->options['unifiedMarginStatus'] = $this->safe_integer($accountResult, 'unifiedMarginStatus', 6); // default to uta 2.0 pro if not found
            }
            return [ $this->options['enableUnifiedMargin'], $this->options['enableUnifiedAccount'] ];
        }) ();
    }

    public function upgrade_unified_trade_account($params = array ()) {
        return Async\async(function () use ($params) {
            /**
             * upgrades the account to unified trade account *warning* this is irreversible
             *
             * @see https://bybit-exchange.github.io/docs/v5/account/upgrade-unified-account
             *
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {any} nothing
             */
            return Async\await($this->privatePostV5AccountUpgradeToUta ($params));
        }) ();
    }

    public function create_expired_option_market(string $symbol) {
        // support expired option contracts
        $quote = null;
        $settle = null;
        $optionParts = explode('-', $symbol);
        $symbolBase = explode('/', $symbol);
        $base = null;
        $expiry = null;
        if (mb_strpos($symbol, '/') > -1) {
            $base = $this->safe_string($symbolBase, 0);
            $expiry = $this->safe_string($optionParts, 1);
            $symbolQuoteAndSettle = $this->safe_string($symbolBase, 1);
            $splitQuote = explode(':', $symbolQuoteAndSettle);
            $quoteAndSettle = $this->safe_string($splitQuote, 0);
            $quote = $quoteAndSettle;
            $settle = $quoteAndSettle;
        } else {
            $base = $this->safe_string($optionParts, 0);
            $expiry = $this->convert_market_id_expire_date($this->safe_string($optionParts, 1));
            if (str_ends_with($symbol, '-USDT')) {
                $quote = 'USDT';
                $settle = 'USDT';
            } else {
                $quote = 'USDC';
                $settle = 'USDC';
            }
        }
        $strike = $this->safe_string($optionParts, 2);
        $optionType = $this->safe_string($optionParts, 3);
        $datetime = $this->convert_expire_date($expiry);
        $timestamp = $this->parse8601($datetime);
        $amountPrecision = null;
        $pricePrecision = null;
        // hard coded amount and price precisions from fetchOptionMarkets
        if ($base === 'BTC') {
            $amountPrecision = $this->parse_number('0.01');
            $pricePrecision = $this->parse_number('5');
        } elseif ($base === 'ETH') {
            $amountPrecision = $this->parse_number('0.1');
            $pricePrecision = $this->parse_number('0.1');
        } elseif ($base === 'SOL') {
            $amountPrecision = $this->parse_number('1');
            $pricePrecision = $this->parse_number('0.01');
        }
        return array(
            'id' => $base . '-' . $this->convert_expire_date_to_market_id_date($expiry) . '-' . $strike . '-' . $optionType,
            'symbol' => $base . '/' . $quote . ':' . $settle . '-' . $expiry . '-' . $strike . '-' . $optionType,
            'base' => $base,
            'quote' => $quote,
            'settle' => $settle,
            'baseId' => $base,
            'quoteId' => $quote,
            'settleId' => $settle,
            'active' => false,
            'type' => 'option',
            'linear' => null,
            'inverse' => null,
            'spot' => false,
            'swap' => false,
            'future' => false,
            'option' => true,
            'margin' => false,
            'contract' => true,
            'contractSize' => $this->parse_number('1'),
            'expiry' => $timestamp,
            'expiryDatetime' => $datetime,
            'optionType' => ($optionType === 'C') ? 'call' : 'put',
            'strike' => $this->parse_number($strike),
            'precision' => array(
                'amount' => $amountPrecision,
                'price' => $pricePrecision,
            ),
            'limits' => array(
                'amount' => array(
                    'min' => null,
                    'max' => null,
                ),
                'price' => array(
                    'min' => null,
                    'max' => null,
                ),
                'cost' => array(
                    'min' => null,
                    'max' => null,
                ),
            ),
            'info' => null,
        );
    }

    public function safe_market(?string $marketId = null, ?array $market = null, ?string $delimiter = null, ?string $marketType = null): array {
        $isOption = ($marketId !== null) && ((mb_strpos($marketId, '-C') > -1) || (mb_strpos($marketId, '-P') > -1));
        if ($isOption && !(is_array($this->markets_by_id) && array_key_exists($marketId, $this->markets_by_id))) {
            // handle expired option contracts
            return $this->create_expired_option_market($marketId);
        }
        return parent::safe_market($marketId, $market, $delimiter, $marketType);
    }

    public function get_bybit_type($method, $market, $params = array ()) {
        $type = null;
        list($type, $params) = $this->handle_market_type_and_params($method, $market, $params);
        $subType = null;
        list($subType, $params) = $this->handle_sub_type_and_params($method, $market, $params);
        if ($type === 'option' || $type === 'spot') {
            return array( $type, $params );
        }
        return array( $subType, $params );
    }

    public function get_amount(string $symbol, float $amount) {
        // some markets like options might not have the precision available
        // and we shouldn't crash in those cases
        $market = $this->market($symbol);
        $emptyPrecisionAmount = ($market['precision']['amount'] === null);
        $amountString = $this->number_to_string($amount);
        if (!$emptyPrecisionAmount && ($amountString !== '0')) {
            return $this->amount_to_precision($symbol, $amount);
        }
        return $amountString;
    }

    public function get_price(string $symbol, string $price) {
        if ($price === null) {
            return $price;
        }
        $market = $this->market($symbol);
        $emptyPrecisionPrice = ($market['precision']['price'] === null);
        if (!$emptyPrecisionPrice) {
            return $this->price_to_precision($symbol, $price);
        }
        return $price;
    }

    public function get_cost(string $symbol, string $cost) {
        $market = $this->market($symbol);
        $emptyPrecisionPrice = ($market['precision']['price'] === null);
        if (!$emptyPrecisionPrice) {
            return $this->cost_to_precision($symbol, $cost);
        }
        return $cost;
    }

    public function fetch_time($params = array ()): PromiseInterface {
        return Async\async(function () use ($params) {
            /**
             * fetches the current integer timestamp in milliseconds from the exchange server
             *
             * @see https://bybit-exchange.github.io/docs/v5/market/time
             *
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {int} the current integer timestamp in milliseconds from the exchange server
             */
            $response = Async\await($this->publicGetV5MarketTime ($params));
            //
            //    {
            //         "retCode" => "0",
            //         "retMsg" => "OK",
            //         "result" => array(
            //             "timeSecond" => "1666879482",
            //             "timeNano" => "1666879482792685914"
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => "1666879482792"
            //     }
            //
            return $this->safe_integer($response, 'time');
        }) ();
    }

    public function fetch_currencies($params = array ()): PromiseInterface {
        return Async\async(function () use ($params) {
            /**
             * fetches all available currencies on an exchange
             *
             * @see https://bybit-exchange.github.io/docs/v5/asset/coin-info
             *
             * @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();
            }
            if ($this->options['enableDemoTrading']) {
                return array();
            }
            $response = Async\await($this->privateGetV5AssetCoinQueryInfo ($params));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "",
            //         "result" => {
            //             "rows" => array(
            //                 {
            //                     "name" => "BTC",
            //                     "coin" => "BTC",
            //                     "remainAmount" => "150",
            //                     "chains" => array(
            //                         array(
            //                             "chainType" => "BTC",
            //                             "confirmation" => "10000",
            //                             "withdrawFee" => "0.0005",
            //                             "depositMin" => "0.0005",
            //                             "withdrawMin" => "0.001",
            //                             "chain" => "BTC",
            //                             "chainDeposit" => "1",
            //                             "chainWithdraw" => "1",
            //                             "minAccuracy" => "8"
            //                         }
            //                     )
            //                 }
            //             )
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1672194582264
            //     }
            //
            $data = $this->safe_dict($response, 'result', array());
            $rows = $this->safe_list($data, 'rows', array());
            $result = array();
            for ($i = 0; $i < count($rows); $i++) {
                $currency = $rows[$i];
                $currencyId = $this->safe_string($currency, 'coin');
                $code = $this->safe_currency_code($currencyId);
                $name = $this->safe_string($currency, 'name');
                $chains = $this->safe_list($currency, 'chains', array());
                $networks = array();
                for ($j = 0; $j < count($chains); $j++) {
                    $chain = $chains[$j];
                    $networkId = $this->safe_string($chain, 'chain');
                    $networkCode = $this->network_id_to_code($networkId);
                    $networks[$networkCode] = array(
                        'info' => $chain,
                        'id' => $networkId,
                        'network' => $networkCode,
                        'active' => null,
                        'deposit' => $this->safe_integer($chain, 'chainDeposit') === 1,
                        'withdraw' => $this->safe_integer($chain, 'chainWithdraw') === 1,
                        'fee' => $this->safe_number($chain, 'withdrawFee'),
                        'precision' => $this->parse_number($this->parse_precision($this->safe_string($chain, 'minAccuracy'))),
                        'limits' => array(
                            'withdraw' => array(
                                'min' => $this->safe_number($chain, 'withdrawMin'),
                                'max' => null,
                            ),
                            'deposit' => array(
                                'min' => $this->safe_number($chain, 'depositMin'),
                                'max' => null,
                            ),
                        ),
                    );
                }
                $result[$code] = $this->safe_currency_structure(array(
                    'info' => $currency,
                    'code' => $code,
                    'id' => $currencyId,
                    'name' => $name,
                    'active' => null,
                    'deposit' => null,
                    'withdraw' => null,
                    'fee' => null,
                    'precision' => null,
                    'limits' => array(
                        'amount' => array(
                            'min' => null,
                            'max' => null,
                        ),
                        'withdraw' => array(
                            'min' => null,
                            'max' => null,
                        ),
                        'deposit' => array(
                            'min' => null,
                            'max' => null,
                        ),
                    ),
                    'networks' => $networks,
                    'type' => 'crypto', // atm exchange api provides only cryptos
                ));
            }
            return $result;
        }) ();
    }

    public function fetch_markets($params = array ()): PromiseInterface {
        return Async\async(function () use ($params) {
            /**
             * retrieves data on all markets for bybit
             *
             * @see https://bybit-exchange.github.io/docs/v5/market/instrument
             *
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array[]} an array of objects representing market data
             */
            if ($this->options['adjustForTimeDifference']) {
                Async\await($this->load_time_difference());
            }
            $promisesUnresolved = array();
            $types = null;
            $defaultTypes = array( 'spot', 'linear', 'inverse', 'option' );
            $fetchMarketsOptions = $this->safe_dict($this->options, 'fetchMarkets');
            if ($fetchMarketsOptions !== null) {
                $types = $this->safe_list($fetchMarketsOptions, 'types', $defaultTypes);
            } else {
                // for backward-compatibility
                $types = $this->safe_list($this->options, 'fetchMarkets', $defaultTypes);
            }
            for ($i = 0; $i < count($types); $i++) {
                $marketType = $types[$i];
                if ($marketType === 'spot') {
                    $promisesUnresolved[] = $this->fetch_spot_markets($params);
                } elseif ($marketType === 'linear') {
                    $promisesUnresolved[] = $this->fetch_future_markets(array( 'category' => 'linear' ));
                } elseif ($marketType === 'inverse') {
                    $promisesUnresolved[] = $this->fetch_future_markets(array( 'category' => 'inverse' ));
                } elseif ($marketType === 'option') {
                    $promisesUnresolved[] = $this->fetch_option_markets(array( 'baseCoin' => 'BTC' ));
                    $promisesUnresolved[] = $this->fetch_option_markets(array( 'baseCoin' => 'ETH' ));
                    $promisesUnresolved[] = $this->fetch_option_markets(array( 'baseCoin' => 'SOL' ));
                } else {
                    throw new ExchangeError($this->id . ' fetchMarkets() $this->options fetchMarkets "' . $marketType . '" is not a supported market type');
                }
            }
            $promises = Async\await(Promise\all($promisesUnresolved));
            $spotMarkets = $this->safe_list($promises, 0, array());
            $linearMarkets = $this->safe_list($promises, 1, array());
            $inverseMarkets = $this->safe_list($promises, 2, array());
            $btcOptionMarkets = $this->safe_list($promises, 3, array());
            $ethOptionMarkets = $this->safe_list($promises, 4, array());
            $solOptionMarkets = $this->safe_list($promises, 5, array());
            $futureMarkets = $this->array_concat($linearMarkets, $inverseMarkets);
            $optionMarkets = $this->array_concat($btcOptionMarkets, $ethOptionMarkets);
            $optionMarkets = $this->array_concat($optionMarkets, $solOptionMarkets);
            $derivativeMarkets = $this->array_concat($futureMarkets, $optionMarkets);
            return $this->array_concat($spotMarkets, $derivativeMarkets);
        }) ();
    }

    public function fetch_spot_markets($params): PromiseInterface {
        return Async\async(function () use ($params) {
            $request = array(
                'category' => 'spot',
            );
            $usePrivateInstrumentsInfo = $this->safe_bool($this->options, 'usePrivateInstrumentsInfo', false);
            $response = null;
            if ($usePrivateInstrumentsInfo) {
                $response = Async\await($this->privateGetV5MarketInstrumentsInfo ($this->extend($request, $params)));
            } else {
                $response = Async\await($this->publicGetV5MarketInstrumentsInfo ($this->extend($request, $params)));
            }
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "OK",
            //         "result" => {
            //             "category" => "spot",
            //             "list" => array(
            //                 {
            //                     "symbol" => "BTCUSDT",
            //                     "baseCoin" => "BTC",
            //                     "quoteCoin" => "USDT",
            //                     "innovation" => "0",
            //                     "status" => "Trading",
            //                     "marginTrading" => "both",
            //                     "lotSizeFilter" => array(
            //                         "basePrecision" => "0.000001",
            //                         "quotePrecision" => "0.00000001",
            //                         "minOrderQty" => "0.00004",
            //                         "maxOrderQty" => "63.01197227",
            //                         "minOrderAmt" => "1",
            //                         "maxOrderAmt" => "100000"
            //                     ),
            //                     "priceFilter" => array(
            //                         "tickSize" => "0.01"
            //                     }
            //                 }
            //             )
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1672712468011
            //     }
            //
            $responseResult = $this->safe_dict($response, 'result', array());
            $markets = $this->safe_list($responseResult, 'list', array());
            $result = array();
            $takerFee = $this->parse_number('0.001');
            $makerFee = $this->parse_number('0.001');
            for ($i = 0; $i < count($markets); $i++) {
                $market = $markets[$i];
                $id = $this->safe_string($market, 'symbol');
                $baseId = $this->safe_string($market, 'baseCoin');
                $quoteId = $this->safe_string($market, 'quoteCoin');
                $base = $this->safe_currency_code($baseId);
                $quote = $this->safe_currency_code($quoteId);
                $symbol = $base . '/' . $quote;
                $status = $this->safe_string($market, 'status');
                $active = ($status === 'Trading');
                $lotSizeFilter = $this->safe_dict($market, 'lotSizeFilter');
                $priceFilter = $this->safe_dict($market, 'priceFilter');
                $quotePrecision = $this->safe_number($lotSizeFilter, 'quotePrecision');
                $marginTrading = $this->safe_string($market, 'marginTrading', 'none');
                $allowsMargin = $marginTrading !== 'none';
                $result[] = $this->safe_market_structure(array(
                    'id' => $id,
                    'symbol' => $symbol,
                    'base' => $base,
                    'quote' => $quote,
                    'settle' => null,
                    'baseId' => $baseId,
                    'quoteId' => $quoteId,
                    'settleId' => null,
                    'type' => 'spot',
                    'spot' => true,
                    'margin' => $allowsMargin,
                    'swap' => false,
                    'future' => false,
                    'option' => false,
                    'active' => $active,
                    'contract' => false,
                    'linear' => null,
                    'inverse' => null,
                    'taker' => $takerFee,
                    'maker' => $makerFee,
                    'contractSize' => null,
                    'expiry' => null,
                    'expiryDatetime' => null,
                    'strike' => null,
                    'optionType' => null,
                    'precision' => array(
                        'amount' => $this->safe_number($lotSizeFilter, 'basePrecision'),
                        'price' => $this->safe_number($priceFilter, 'tickSize', $quotePrecision),
                    ),
                    'limits' => array(
                        'leverage' => array(
                            'min' => $this->parse_number('1'),
                            'max' => null,
                        ),
                        'amount' => array(
                            'min' => $this->safe_number($lotSizeFilter, 'minOrderQty'),
                            'max' => $this->safe_number($lotSizeFilter, 'maxOrderQty'),
                        ),
                        'price' => array(
                            'min' => null,
                            'max' => null,
                        ),
                        'cost' => array(
                            'min' => $this->safe_number($lotSizeFilter, 'minOrderAmt'),
                            'max' => $this->safe_number($lotSizeFilter, 'maxOrderAmt'),
                        ),
                    ),
                    'created' => null,
                    'info' => $market,
                ));
            }
            return $result;
        }) ();
    }

    public function fetch_future_markets($params): PromiseInterface {
        return Async\async(function () use ($params) {
            $params = $this->extend($params);
            $params['limit'] = 1000; // minimize number of requests
            $preLaunchMarkets = array();
            $usePrivateInstrumentsInfo = $this->safe_bool($this->options, 'usePrivateInstrumentsInfo', false);
            $response = null;
            if ($usePrivateInstrumentsInfo) {
                $response = Async\await($this->privateGetV5MarketInstrumentsInfo ($params));
            } else {
                $linearPromises = array(
                    $this->publicGetV5MarketInstrumentsInfo ($params),
                    $this->publicGetV5MarketInstrumentsInfo ($this->extend($params, array( 'status' => 'PreLaunch' ))),
                );
                $promises = Async\await(Promise\all($linearPromises));
                $response = $this->safe_dict($promises, 0, array());
                $preLaunchMarkets = $this->safe_dict($promises, 1, array());
            }
            $data = $this->safe_dict($response, 'result', array());
            $markets = $this->safe_list($data, 'list', array());
            $paginationCursor = $this->safe_string($data, 'nextPageCursor');
            if ($paginationCursor !== null) {
                while ($paginationCursor !== null) {
                    $params['cursor'] = $paginationCursor;
                    $responseInner = null;
                    if ($usePrivateInstrumentsInfo) {
                        $responseInner = Async\await($this->privateGetV5MarketInstrumentsInfo ($params));
                    } else {
                        $responseInner = Async\await($this->publicGetV5MarketInstrumentsInfo ($params));
                    }
                    $dataNew = $this->safe_dict($responseInner, 'result', array());
                    $rawMarkets = $this->safe_list($dataNew, 'list', array());
                    $rawMarketsLength = count($rawMarkets);
                    if ($rawMarketsLength === 0) {
                        break;
                    }
                    $markets = $this->array_concat($rawMarkets, $markets);
                    $paginationCursor = $this->safe_string($dataNew, 'nextPageCursor');
                }
            }
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "OK",
            //         "result" => {
            //             "category" => "linear",
            //             "list" => array(
            //                 array(
            //                     "symbol" => "BTCUSDT",
            //                     "contractType" => "LinearPerpetual",
            //                     "status" => "Trading",
            //                     "baseCoin" => "BTC",
            //                     "quoteCoin" => "USDT",
            //                     "launchTime" => "1585526400000",
            //                     "deliveryTime" => "0",
            //                     "deliveryFeeRate" => "",
            //                     "priceScale" => "2",
            //                     "leverageFilter" => array(
            //                         "minLeverage" => "1",
            //                         "maxLeverage" => "100.00",
            //                         "leverageStep" => "0.01"
            //                     ),
            //                     "priceFilter" => array(
            //                         "minPrice" => "0.50",
            //                         "maxPrice" => "999999.00",
            //                         "tickSize" => "0.50"
            //                     ),
            //                     "lotSizeFilter" => array(
            //                         "maxOrderQty" => "100.000",
            //                         "minOrderQty" => "0.001",
            //                         "qtyStep" => "0.001",
            //                         "postOnlyMaxOrderQty" => "1000.000"
            //                     ),
            //                     "unifiedMarginTrade" => true,
            //                     "fundingInterval" => 480,
            //                     "settleCoin" => "USDT"
            //                 }
            //             ),
            //             "nextPageCursor" => ""
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1672712495660
            //     }
            //
            $preLaunchData = $this->safe_dict($preLaunchMarkets, 'result', array());
            $preLaunchMarketsList = $this->safe_list($preLaunchData, 'list', array());
            $markets = $this->array_concat($markets, $preLaunchMarketsList);
            $result = array();
            $category = $this->safe_string($data, 'category');
            for ($i = 0; $i < count($markets); $i++) {
                $market = $markets[$i];
                if ($category === null) {
                    $category = $this->safe_string($market, 'category');
                }
                $linear = ($category === 'linear');
                $inverse = ($category === 'inverse');
                $contractType = $this->safe_string($market, 'contractType');
                $inverseFutures = ($contractType === 'InverseFutures');
                $linearFutures = ($contractType === 'LinearFutures');
                $linearPerpetual = ($contractType === 'LinearPerpetual');
                $inversePerpetual = ($contractType === 'InversePerpetual');
                $id = $this->safe_string($market, 'symbol');
                $baseId = $this->safe_string($market, 'baseCoin');
                $quoteId = $this->safe_string($market, 'quoteCoin');
                $defaultSettledId = $linear ? $quoteId : $baseId;
                $settleId = $this->safe_string($market, 'settleCoin', $defaultSettledId);
                $base = $this->safe_currency_code($baseId);
                $quote = $this->safe_currency_code($quoteId);
                $settle = null;
                if ($linearPerpetual && ($settleId === 'USD')) {
                    $settle = 'USDC';
                } else {
                    $settle = $this->safe_currency_code($settleId);
                }
                $symbol = $base . '/' . $quote;
                $lotSizeFilter = $this->safe_dict($market, 'lotSizeFilter', array());
                $priceFilter = $this->safe_dict($market, 'priceFilter', array());
                $leverage = $this->safe_dict($market, 'leverageFilter', array());
                $status = $this->safe_string($market, 'status');
                $swap = $linearPerpetual || $inversePerpetual;
                $future = $inverseFutures || $linearFutures;
                $type = null;
                if ($swap) {
                    $type = 'swap';
                } elseif ($future) {
                    $type = 'future';
                }
                $expiry = null;
                // some swaps have deliveryTime meaning delisting time
                if (!$swap) {
                    $expiry = $this->omit_zero($this->safe_string($market, 'deliveryTime'));
                    if ($expiry !== null) {
                        $expiry = intval($expiry);
                    }
                }
                $expiryDatetime = $this->iso8601($expiry);
                $symbol = $symbol . ':' . $settle;
                if ($expiry !== null) {
                    $symbol = $symbol . '-' . $this->yymmdd($expiry);
                }
                $contractSize = $inverse ? $this->safe_number_2($lotSizeFilter, 'minTradingQty', 'minOrderQty') : $this->parse_number('1');
                $result[] = $this->safe_market_structure(array(
                    'id' => $id,
                    'symbol' => $symbol,
                    'base' => $base,
                    'quote' => $quote,
                    'settle' => $settle,
                    'baseId' => $baseId,
                    'quoteId' => $quoteId,
                    'settleId' => $settleId,
                    'type' => $type,
                    'spot' => false,
                    'margin' => null,
                    'swap' => $swap,
                    'future' => $future,
                    'option' => false,
                    'active' => ($status === 'Trading'),
                    'contract' => true,
                    'linear' => $linear,
                    'inverse' => $inverse,
                    'taker' => $this->safe_number($market, 'takerFee', $this->parse_number('0.0006')),
                    'maker' => $this->safe_number($market, 'makerFee', $this->parse_number('0.0001')),
                    'contractSize' => $contractSize,
                    'expiry' => $expiry,
                    'expiryDatetime' => $expiryDatetime,
                    'strike' => null,
                    'optionType' => null,
                    'precision' => array(
                        'amount' => $this->safe_number($lotSizeFilter, 'qtyStep'),
                        'price' => $this->safe_number($priceFilter, 'tickSize'),
                    ),
                    'limits' => array(
                        'leverage' => array(
                            'min' => $this->safe_number($leverage, 'minLeverage'),
                            'max' => $this->safe_number($leverage, 'maxLeverage'),
                        ),
                        'amount' => array(
                            'min' => $this->safe_number_2($lotSizeFilter, 'minTradingQty', 'minOrderQty'),
                            'max' => $this->safe_number_2($lotSizeFilter, 'maxTradingQty', 'maxOrderQty'),
                        ),
                        'price' => array(
                            'min' => $this->safe_number($priceFilter, 'minPrice'),
                            'max' => $this->safe_number($priceFilter, 'maxPrice'),
                        ),
                        'cost' => array(
                            'min' => null,
                            'max' => null,
                        ),
                    ),
                    'created' => $this->safe_integer($market, 'launchTime'),
                    'info' => $market,
                ));
            }
            return $result;
        }) ();
    }

    public function fetch_option_markets($params): PromiseInterface {
        return Async\async(function () use ($params) {
            $request = array(
                'category' => 'option',
            );
            $usePrivateInstrumentsInfo = $this->safe_bool($this->options, 'usePrivateInstrumentsInfo', false);
            $response = null;
            if ($usePrivateInstrumentsInfo) {
                $response = Async\await($this->privateGetV5MarketInstrumentsInfo ($this->extend($request, $params)));
            } else {
                $response = Async\await($this->publicGetV5MarketInstrumentsInfo ($this->extend($request, $params)));
            }
            $data = $this->safe_dict($response, 'result', array());
            $markets = $this->safe_list($data, 'list', array());
            if ($this->options['loadAllOptions']) {
                $request['limit'] = 1000;
                $paginationCursor = $this->safe_string($data, 'nextPageCursor');
                if ($paginationCursor !== null) {
                    while ($paginationCursor !== null) {
                        $request['cursor'] = $paginationCursor;
                        $responseInner = null;
                        if ($usePrivateInstrumentsInfo) {
                            $responseInner = Async\await($this->privateGetV5MarketInstrumentsInfo ($this->extend($request, $params)));
                        } else {
                            $responseInner = Async\await($this->publicGetV5MarketInstrumentsInfo ($this->extend($request, $params)));
                        }
                        $dataNew = $this->safe_dict($responseInner, 'result', array());
                        $rawMarkets = $this->safe_list($dataNew, 'list', array());
                        $rawMarketsLength = count($rawMarkets);
                        if ($rawMarketsLength === 0) {
                            break;
                        }
                        $markets = $this->array_concat($rawMarkets, $markets);
                        $paginationCursor = $this->safe_string($dataNew, 'nextPageCursor');
                    }
                }
            }
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "success",
            //         "result" => {
            //             "category" => "option",
            //             "nextPageCursor" => "0%2C2",
            //             "list" => array(
            //                 array(
            //                     "symbol" => "BTC-29DEC23-80000-C",
            //                     "status" => "Trading",
            //                     "baseCoin" => "BTC",
            //                     "quoteCoin" => "USD",
            //                     "settleCoin" => "USDC",
            //                     "optionsType" => "Call",
            //                     "launchTime" => "1688630400000",
            //                     "deliveryTime" => "1703836800000",
            //                     "deliveryFeeRate" => "0.00015",
            //                     "priceFilter" => array(
            //                         "minPrice" => "5",
            //                         "maxPrice" => "10000000",
            //                         "tickSize" => "5"
            //                     ),
            //                     "lotSizeFilter" => array(
            //                         "maxOrderQty" => "500",
            //                         "minOrderQty" => "0.01",
            //                         "qtyStep" => "0.01"
            //                     }
            //                 ),
            //             )
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1688873094448
            //     }
            //
            $result = array();
            for ($i = 0; $i < count($markets); $i++) {
                $market = $markets[$i];
                $id = $this->safe_string($market, 'symbol');
                $baseId = $this->safe_string($market, 'baseCoin');
                $quoteId = $this->safe_string($market, 'quoteCoin');
                $settleId = $this->safe_string($market, 'settleCoin');
                $base = $this->safe_currency_code($baseId);
                $quote = $this->safe_currency_code($quoteId);
                $settle = $this->safe_currency_code($settleId);
                $lotSizeFilter = $this->safe_dict($market, 'lotSizeFilter', array());
                $priceFilter = $this->safe_dict($market, 'priceFilter', array());
                $status = $this->safe_string($market, 'status');
                $expiry = $this->safe_integer($market, 'deliveryTime');
                $splitId = explode('-', $id);
                $strike = $this->safe_string($splitId, 2);
                $optionLetter = $this->safe_string($splitId, 3);
                $isActive = ($status === 'Trading');
                $isInverse = $base === $settle;
                if ($isActive || ($this->options['loadAllOptions']) || ($this->options['loadExpiredOptions'])) {
                    $result[] = $this->safe_market_structure(array(
                        'id' => $id,
                        'symbol' => $base . '/' . $quote . ':' . $settle . '-' . $this->yymmdd($expiry) . '-' . $strike . '-' . $optionLetter,
                        'base' => $base,
                        'quote' => $quote,
                        'settle' => $settle,
                        'baseId' => $baseId,
                        'quoteId' => $quoteId,
                        'settleId' => $settleId,
                        'type' => 'option',
                        'subType' => null,
                        'spot' => false,
                        'margin' => false,
                        'swap' => false,
                        'future' => false,
                        'option' => true,
                        'active' => $isActive,
                        'contract' => true,
                        'linear' => !$isInverse,
                        'inverse' => $isInverse,
                        'taker' => $this->safe_number($market, 'takerFee', $this->parse_number('0.0006')),
                        'maker' => $this->safe_number($market, 'makerFee', $this->parse_number('0.0001')),
                        'contractSize' => $this->parse_number('1'),
                        'expiry' => $expiry,
                        'expiryDatetime' => $this->iso8601($expiry),
                        'strike' => $this->parse_number($strike),
                        'optionType' => $this->safe_string_lower($market, 'optionsType'),
                        'precision' => array(
                            'amount' => $this->safe_number($lotSizeFilter, 'qtyStep'),
                            'price' => $this->safe_number($priceFilter, 'tickSize'),
                        ),
                        'limits' => array(
                            'leverage' => array(
                                'min' => null,
                                'max' => null,
                            ),
                            'amount' => array(
                                'min' => $this->safe_number($lotSizeFilter, 'minOrderQty'),
                                'max' => $this->safe_number($lotSizeFilter, 'maxOrderQty'),
                            ),
                            'price' => array(
                                'min' => $this->safe_number($priceFilter, 'minPrice'),
                                'max' => $this->safe_number($priceFilter, 'maxPrice'),
                            ),
                            'cost' => array(
                                'min' => null,
                                'max' => null,
                            ),
                        ),
                        'created' => $this->safe_integer($market, 'launchTime'),
                        'info' => $market,
                    ));
                }
            }
            return $result;
        }) ();
    }

    public function parse_ticker(array $ticker, ?array $market = null): array {
        //
        // spot
        //
        //     {
        //         "symbol" => "BTCUSDT",
        //         "bid1Price" => "20517.96",
        //         "bid1Size" => "2",
        //         "ask1Price" => "20527.77",
        //         "ask1Size" => "1.862172",
        //         "lastPrice" => "20533.13",
        //         "prevPrice24h" => "20393.48",
        //         "price24hPcnt" => "0.0068",
        //         "highPrice24h" => "21128.12",
        //         "lowPrice24h" => "20318.89",
        //         "turnover24h" => "243765620.65899866",
        //         "volume24h" => "11801.27771",
        //         "usdIndexPrice" => "20784.12009279"
        //     }
        //
        // linear/inverse
        //
        //     {
        //         "symbol" => "BTCUSD",
        //         "lastPrice" => "16597.00",
        //         "indexPrice" => "16598.54",
        //         "markPrice" => "16596.00",
        //         "prevPrice24h" => "16464.50",
        //         "price24hPcnt" => "0.008047",
        //         "highPrice24h" => "30912.50",
        //         "lowPrice24h" => "15700.00",
        //         "prevPrice1h" => "16595.50",
        //         "openInterest" => "373504107",
        //         "openInterestValue" => "22505.67",
        //         "turnover24h" => "2352.94950046",
        //         "volume24h" => "49337318",
        //         "fundingRate" => "-0.001034",
        //         "nextFundingTime" => "1672387200000",
        //         "predictedDeliveryPrice" => "",
        //         "basisRate" => "",
        //         "deliveryFeeRate" => "",
        //         "deliveryTime" => "0",
        //         "ask1Size" => "1",
        //         "bid1Price" => "16596.00",
        //         "ask1Price" => "16597.50",
        //         "bid1Size" => "1"
        //     }
        //
        // option
        //
        //     {
        //         "symbol" => "BTC-30DEC22-18000-C",
        //         "bid1Price" => "0",
        //         "bid1Size" => "0",
        //         "bid1Iv" => "0",
        //         "ask1Price" => "435",
        //         "ask1Size" => "0.66",
        //         "ask1Iv" => "5",
        //         "lastPrice" => "435",
        //         "highPrice24h" => "435",
        //         "lowPrice24h" => "165",
        //         "markPrice" => "0.00000009",
        //         "indexPrice" => "16600.55",
        //         "markIv" => "0.7567",
        //         "underlyingPrice" => "16590.42",
        //         "openInterest" => "6.3",
        //         "turnover24h" => "2482.73",
        //         "volume24h" => "0.15",
        //         "totalVolume" => "99",
        //         "totalTurnover" => "1967653",
        //         "delta" => "0.00000001",
        //         "gamma" => "0.00000001",
        //         "vega" => "0.00000004",
        //         "theta" => "-0.00000152",
        //         "predictedDeliveryPrice" => "0",
        //         "change24h" => "86"
        //     }
        //
        $isSpot = $this->safe_string($ticker, 'openInterestValue') === null;
        $timestamp = $this->safe_integer($ticker, 'time');
        $marketId = $this->safe_string($ticker, 'symbol');
        $type = $isSpot ? 'spot' : 'contract';
        $market = $this->safe_market($marketId, $market, null, $type);
        $symbol = $this->safe_symbol($marketId, $market, null, $type);
        $last = $this->safe_string($ticker, 'lastPrice');
        $open = $this->safe_string($ticker, 'prevPrice24h');
        $percentage = $this->safe_string($ticker, 'price24hPcnt');
        $percentage = Precise::string_mul($percentage, '100');
        $quoteVolume = $this->safe_string($ticker, 'turnover24h');
        $baseVolume = $this->safe_string($ticker, 'volume24h');
        $bid = $this->safe_string($ticker, 'bid1Price');
        $ask = $this->safe_string($ticker, 'ask1Price');
        $high = $this->safe_string($ticker, 'highPrice24h');
        $low = $this->safe_string($ticker, 'lowPrice24h');
        return $this->safe_ticker(array(
            'symbol' => $symbol,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'high' => $high,
            'low' => $low,
            'bid' => $bid,
            'bidVolume' => $this->safe_string_2($ticker, 'bidSize', 'bid1Size'),
            'ask' => $ask,
            'askVolume' => $this->safe_string_2($ticker, 'askSize', 'ask1Size'),
            'vwap' => null,
            'open' => $open,
            'close' => $last,
            'last' => $last,
            'previousClose' => null,
            'change' => null,
            '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_ticker(string $symbol, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $params) {
            /**
             * fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific $market
             *
             * @see https://bybit-exchange.github.io/docs/v5/market/tickers
             *
             * @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~
             */
            if ($symbol === null) {
                throw new ArgumentsRequired($this->id . ' fetchTicker() requires a $symbol argument');
            }
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
                // 'baseCoin' => '', Base coin. For option only
                // 'expDate' => '', Expiry date. e.g., 25DEC22. For option only
            );
            $category = null;
            list($category, $params) = $this->get_bybit_type('fetchTicker', $market, $params);
            $request['category'] = $category;
            $response = Async\await($this->publicGetV5MarketTickers ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "OK",
            //         "result" => {
            //             "category" => "inverse",
            //             "list" => array(
            //                 array(
            //                     "symbol" => "BTCUSD",
            //                     "lastPrice" => "16597.00",
            //                     "indexPrice" => "16598.54",
            //                     "markPrice" => "16596.00",
            //                     "prevPrice24h" => "16464.50",
            //                     "price24hPcnt" => "0.008047",
            //                     "highPrice24h" => "30912.50",
            //                     "lowPrice24h" => "15700.00",
            //                     "prevPrice1h" => "16595.50",
            //                     "openInterest" => "373504107",
            //                     "openInterestValue" => "22505.67",
            //                     "turnover24h" => "2352.94950046",
            //                     "volume24h" => "49337318",
            //                     "fundingRate" => "-0.001034",
            //                     "nextFundingTime" => "1672387200000",
            //                     "predictedDeliveryPrice" => "",
            //                     "basisRate" => "",
            //                     "deliveryFeeRate" => "",
            //                     "deliveryTime" => "0",
            //                     "ask1Size" => "1",
            //                     "bid1Price" => "16596.00",
            //                     "ask1Price" => "16597.50",
            //                     "bid1Size" => "1"
            //                 }
            //             )
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1672376496682
            //     }
            //
            $result = $this->safe_dict($response, 'result', array());
            $tickers = $this->safe_list($result, 'list', array());
            $rawTicker = $this->safe_dict($tickers, 0);
            return $this->parse_ticker($rawTicker, $market);
        }) ();
    }

    public function fetch_tickers(?array $symbols = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbols, $params) {
            /**
             * fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each $market
             *
             * @see https://bybit-exchange.github.io/docs/v5/market/tickers
             *
             * @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
             * @param {string} [$params->subType] *contract only* 'linear', 'inverse'
             * @param {string} [$params->baseCoin] *option only* base coin, default is 'BTC'
             * @return {array} an array of ~@link https://docs.ccxt.com/#/?id=ticker-structure ticker structures~
             */
            Async\await($this->load_markets());
            $code = $this->safe_string_n($params, array( 'code', 'currency', 'baseCoin' ));
            $market = null;
            $parsedSymbols = null;
            if ($symbols !== null) {
                $parsedSymbols = array();
                $marketTypeInfo = $this->handle_market_type_and_params('fetchTickers', null, $params);
                $defaultType = $marketTypeInfo[0]; // don't omit here
                // we can't use marketSymbols here due to the conflicing ids between markets
                $currentType = null;
                for ($i = 0; $i < count($symbols); $i++) {
                    $symbol = $symbols[$i];
                    // using safeMarket here because if the user provides for instance BTCUSDT and "type" => "spot" in $params we should
                    // infer the $market type from the type provided and not from the conflicting id (BTCUSDT might be swap or spot)
                    $isExchangeSpecificSymbol = (mb_strpos($symbol, '/') === -1);
                    if ($isExchangeSpecificSymbol) {
                        $market = $this->safe_market($symbol, null, null, $defaultType);
                    } else {
                        $market = $this->market($symbol);
                    }
                    if ($currentType === null) {
                        $currentType = $market['type'];
                    } elseif ($market['type'] !== $currentType) {
                        throw new BadRequest($this->id . ' fetchTickers can only accept a list of $symbols of the same type');
                    }
                    if ($market['option']) {
                        if ($code !== null && $code !== $market['base']) {
                            throw new BadRequest($this->id . ' fetchTickers the base currency must be the same for all $symbols, this endpoint only supports one base currency at a time. Read more about it here => https://bybit-exchange.github.io/docs/v5/market/tickers');
                        }
                        if ($code === null) {
                            $code = $market['base'];
                        }
                        $params = $this->omit($params, array( 'code', 'currency' ));
                    }
                    $parsedSymbols[] = $market['symbol'];
                }
            }
            $request = array(
                // 'symbol' => $market['id'],
                // 'baseCoin' => '', // Base coin. For option only
                // 'expDate' => '', // Expiry date. e.g., 25DEC22. For option only
            );
            $category = null;
            list($category, $params) = $this->get_bybit_type('fetchTickers', $market, $params);
            $request['category'] = $category;
            if ($category === 'option') {
                $request['category'] = 'option';
                if ($code === null) {
                    $code = 'BTC';
                }
                $request['baseCoin'] = $code;
            }
            $response = Async\await($this->publicGetV5MarketTickers ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "OK",
            //         "result" => {
            //             "category" => "inverse",
            //             "list" => array(
            //                 array(
            //                     "symbol" => "BTCUSD",
            //                     "lastPrice" => "16597.00",
            //                     "indexPrice" => "16598.54",
            //                     "markPrice" => "16596.00",
            //                     "prevPrice24h" => "16464.50",
            //                     "price24hPcnt" => "0.008047",
            //                     "highPrice24h" => "30912.50",
            //                     "lowPrice24h" => "15700.00",
            //                     "prevPrice1h" => "16595.50",
            //                     "openInterest" => "373504107",
            //                     "openInterestValue" => "22505.67",
            //                     "turnover24h" => "2352.94950046",
            //                     "volume24h" => "49337318",
            //                     "fundingRate" => "-0.001034",
            //                     "nextFundingTime" => "1672387200000",
            //                     "predictedDeliveryPrice" => "",
            //                     "basisRate" => "",
            //                     "deliveryFeeRate" => "",
            //                     "deliveryTime" => "0",
            //                     "ask1Size" => "1",
            //                     "bid1Price" => "16596.00",
            //                     "ask1Price" => "16597.50",
            //                     "bid1Size" => "1"
            //                 }
            //             )
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1672376496682
            //     }
            //
            $result = $this->safe_dict($response, 'result', array());
            $tickerList = $this->safe_list($result, 'list', array());
            return $this->parse_tickers($tickerList, $parsedSymbols);
        }) ();
    }

    public function fetch_bids_asks(?array $symbols = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbols, $params) {
            /**
             * fetches the bid and ask price and volume for multiple markets
             *
             * @see https://bybit-exchange.github.io/docs/v5/market/tickers
             *
             * @param {string[]|null} $symbols unified $symbols of the markets to fetch the bids and asks for, all markets are returned if not assigned
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->subType] *contract only* 'linear', 'inverse'
             * @param {string} [$params->baseCoin] *option only* base coin, default is 'BTC'
             * @return {array} a dictionary of ~@link https://docs.ccxt.com/#/?id=ticker-structure ticker structures~
             */
            return Async\await($this->fetch_tickers($symbols, $params));
        }) ();
    }

    public function parse_ohlcv($ohlcv, ?array $market = null): array {
        //
        //     array(
        //         "1621162800",
        //         "49592.43",
        //         "49644.91",
        //         "49342.37",
        //         "49349.42",
        //         "1451.59",
        //         "2.4343353100000003"
        //     )
        //
        $volumeIndex = ($market['inverse']) ? 6 : 5;
        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, $volumeIndex),
        );
    }

    public function fetch_ohlcv(string $symbol, $timeframe = '1m', ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $timeframe, $since, $limit, $params) {
            /**
             * fetches historical candlestick data containing the open, high, low, and close $price, and the volume of a $market
             *
             * @see https://bybit-exchange.github.io/docs/v5/market/kline
             * @see https://bybit-exchange.github.io/docs/v5/market/mark-kline
             * @see https://bybit-exchange.github.io/docs/v5/market/index-kline
             * @see https://bybit-exchange.github.io/docs/v5/market/preimum-index-kline
             *
             * @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] the latest time in ms to fetch orders for
             * @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 {int[][]} A list of candles ordered, open, high, low, close, volume
             */
            if ($symbol === null) {
                throw new ArgumentsRequired($this->id . ' fetchOHLCV() requires a $symbol argument');
            }
            Async\await($this->load_markets());
            $paginate = false;
            list($paginate, $params) = $this->handle_option_and_params($params, 'fetchOHLCV', 'paginate');
            if ($paginate) {
                return Async\await($this->fetch_paginated_call_deterministic('fetchOHLCV', $symbol, $since, $limit, $timeframe, $params, 1000));
            }
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
            );
            if ($limit === null) {
                $limit = 200; // default is 200 when requested with `$since`
            }
            if ($since !== null) {
                $request['start'] = $since;
            }
            if ($limit !== null) {
                $request['limit'] = $limit; // max 1000, default 1000
            }
            list($request, $params) = $this->handle_until_option('end', $request, $params);
            $request['interval'] = $this->safe_string($this->timeframes, $timeframe, $timeframe);
            $response = null;
            if ($market['spot']) {
                $request['category'] = 'spot';
                $response = Async\await($this->publicGetV5MarketKline ($this->extend($request, $params)));
            } else {
                $price = $this->safe_string($params, 'price');
                $params = $this->omit($params, 'price');
                if ($market['linear']) {
                    $request['category'] = 'linear';
                } elseif ($market['inverse']) {
                    $request['category'] = 'inverse';
                } else {
                    throw new NotSupported($this->id . ' fetchOHLCV() is not supported for option markets');
                }
                if ($price === 'mark') {
                    $response = Async\await($this->publicGetV5MarketMarkPriceKline ($this->extend($request, $params)));
                } elseif ($price === 'index') {
                    $response = Async\await($this->publicGetV5MarketIndexPriceKline ($this->extend($request, $params)));
                } elseif ($price === 'premiumIndex') {
                    $response = Async\await($this->publicGetV5MarketPremiumIndexPriceKline ($this->extend($request, $params)));
                } else {
                    $response = Async\await($this->publicGetV5MarketKline ($this->extend($request, $params)));
                }
            }
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "OK",
            //         "result" => array(
            //             "symbol" => "BTCUSD",
            //             "category" => "inverse",
            //             "list" => array(
            //                 array(
            //                     "1670608800000",
            //                     "17071",
            //                     "17073",
            //                     "17027",
            //                     "17055.5",
            //                     "268611",
            //                     "15.74462667"
            //                 ),
            //                 array(
            //                     "1670605200000",
            //                     "17071.5",
            //                     "17071.5",
            //                     "17061",
            //                     "17071",
            //                     "4177",
            //                     "0.24469757"
            //                 ),
            //                 array(
            //                     "1670601600000",
            //                     "17086.5",
            //                     "17088",
            //                     "16978",
            //                     "17071.5",
            //                     "6356",
            //                     "0.37288112"
            //                 )
            //             )
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1672025956592
            //     }
            //
            $result = $this->safe_dict($response, 'result', array());
            $ohlcvs = $this->safe_list($result, 'list', array());
            return $this->parse_ohlcvs($ohlcvs, $market, $timeframe, $since, $limit);
        }) ();
    }

    public function parse_funding_rate($ticker, ?array $market = null): array {
        //
        //     {
        //         "symbol" => "BTCUSDT",
        //         "bidPrice" => "19255",
        //         "askPrice" => "19255.5",
        //         "lastPrice" => "19255.50",
        //         "lastTickDirection" => "ZeroPlusTick",
        //         "prevPrice24h" => "18634.50",
        //         "price24hPcnt" => "0.033325",
        //         "highPrice24h" => "19675.00",
        //         "lowPrice24h" => "18610.00",
        //         "prevPrice1h" => "19278.00",
        //         "markPrice" => "19255.00",
        //         "indexPrice" => "19260.68",
        //         "openInterest" => "48069.549",
        //         "turnover24h" => "4686694853.047006",
        //         "volume24h" => "243730.252",
        //         "fundingRate" => "0.0001",
        //         "nextFundingTime" => "1663689600000",
        //         "predictedDeliveryPrice" => "",
        //         "basisRate" => "",
        //         "deliveryFeeRate" => "",
        //         "deliveryTime" => "0"
        //     }
        //
        $timestamp = $this->safe_integer($ticker, 'timestamp'); // added artificially to avoid changing the signature
        $ticker = $this->omit($ticker, 'timestamp');
        $marketId = $this->safe_string($ticker, 'symbol');
        $symbol = $this->safe_symbol($marketId, $market, null, 'swap');
        $fundingRate = $this->safe_number($ticker, 'fundingRate');
        $fundingTimestamp = $this->safe_integer($ticker, 'nextFundingTime');
        $markPrice = $this->safe_number($ticker, 'markPrice');
        $indexPrice = $this->safe_number($ticker, 'indexPrice');
        $info = $this->safe_dict($this->safe_market($marketId, $market, null, 'swap'), 'info');
        $fundingInterval = $this->safe_integer($info, 'fundingInterval');
        $intervalString = null;
        if ($fundingInterval !== null) {
            $interval = $this->parse_to_int($fundingInterval / 60);
            $intervalString = (string) $interval . 'h';
        }
        return array(
            'info' => $ticker,
            'symbol' => $symbol,
            'markPrice' => $markPrice,
            'indexPrice' => $indexPrice,
            'interestRate' => null,
            'estimatedSettlePrice' => null,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'fundingRate' => $fundingRate,
            'fundingTimestamp' => $fundingTimestamp,
            'fundingDatetime' => $this->iso8601($fundingTimestamp),
            'nextFundingRate' => null,
            'nextFundingTimestamp' => null,
            'nextFundingDatetime' => null,
            'previousFundingRate' => null,
            'previousFundingTimestamp' => null,
            'previousFundingDatetime' => null,
            'interval' => $intervalString,
        );
    }

    public function fetch_funding_rates(?array $symbols = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbols, $params) {
            /**
             * fetches funding rates for multiple markets
             *
             * @see https://bybit-exchange.github.io/docs/v5/market/tickers
             *
             * @param {string[]} $symbols unified $symbols of the markets to fetch the funding rates for, all $market funding rates are returned if not assigned
             * @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~
             */
            Async\await($this->load_markets());
            $market = null;
            $request = array();
            if ($symbols !== null) {
                $symbols = $this->market_symbols($symbols);
                $market = $this->market($symbols[0]);
                $symbolsLength = count($symbols);
                if ($symbolsLength === 1) {
                    $request['symbol'] = $market['id'];
                }
            }
            $type = null;
            list($type, $params) = $this->handle_market_type_and_params('fetchFundingRates', $market, $params);
            if ($type !== 'swap') {
                throw new NotSupported($this->id . ' fetchFundingRates() does not support ' . $type . ' markets');
            } else {
                $subType = null;
                list($subType, $params) = $this->handle_sub_type_and_params('fetchFundingRates', $market, $params, 'linear');
                $request['category'] = $subType;
            }
            $response = Async\await($this->publicGetV5MarketTickers ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "OK",
            //         "result" => {
            //             "category" => "linear",
            //             "list" => array(
            //                 array(
            //                     "symbol" => "BTCUSDT",
            //                     "bidPrice" => "19255",
            //                     "askPrice" => "19255.5",
            //                     "lastPrice" => "19255.50",
            //                     "lastTickDirection" => "ZeroPlusTick",
            //                     "prevPrice24h" => "18634.50",
            //                     "price24hPcnt" => "0.033325",
            //                     "highPrice24h" => "19675.00",
            //                     "lowPrice24h" => "18610.00",
            //                     "prevPrice1h" => "19278.00",
            //                     "markPrice" => "19255.00",
            //                     "indexPrice" => "19260.68",
            //                     "openInterest" => "48069.549",
            //                     "turnover24h" => "4686694853.047006",
            //                     "volume24h" => "243730.252",
            //                     "fundingRate" => "0.0001",
            //                     "nextFundingTime" => "1663689600000",
            //                     "predictedDeliveryPrice" => "",
            //                     "basisRate" => "",
            //                     "deliveryFeeRate" => "",
            //                     "deliveryTime" => "0"
            //                 }
            //             )
            //         ),
            //         "retExtInfo" => null,
            //         "time" => 1663670053454
            //     }
            //
            $data = $this->safe_dict($response, 'result', array());
            $tickerList = $this->safe_list($data, 'list', array());
            $timestamp = $this->safe_integer($response, 'time');
            for ($i = 0; $i < count($tickerList); $i++) {
                $tickerList[$i]['timestamp'] = $timestamp; // will be removed inside the parser
            }
            return $this->parse_funding_rates($tickerList, $symbols);
        }) ();
    }

    public function fetch_funding_rate_history(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * fetches historical funding rate prices
             *
             * @see https://bybit-exchange.github.io/docs/v5/market/history-fund-rate
             *
             * @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
             * @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');
            }
            Async\await($this->load_markets());
            $paginate = false;
            list($paginate, $params) = $this->handle_option_and_params($params, 'fetchFundingRateHistory', 'paginate');
            if ($paginate) {
                return Async\await($this->fetch_paginated_call_deterministic('fetchFundingRateHistory', $symbol, $since, $limit, '8h', $params, 200));
            }
            if ($limit === null) {
                $limit = 200;
            }
            $request = array(
                // 'category' => '', // Product $type-> linear,inverse
                // 'symbol' => '', // Symbol name
                // 'startTime' => 0, // The start $timestamp (ms)
                // 'endTime' => 0, // The end $timestamp (ms)
                'limit' => $limit, // Limit for data size per page. [1, 200]. Default => 200
            );
            $market = $this->market($symbol);
            $symbol = $market['symbol'];
            $request['symbol'] = $market['id'];
            $type = null;
            list($type, $params) = $this->get_bybit_type('fetchFundingRateHistory', $market, $params);
            if ($type === 'spot' || $type === 'option') {
                throw new NotSupported($this->id . ' fetchFundingRateHistory() only support linear and inverse market');
            }
            $request['category'] = $type;
            if ($since !== null) {
                $request['startTime'] = $since;
            }
            $until = $this->safe_integer($params, 'until'); // unified in milliseconds
            $endTime = $this->safe_integer($params, 'endTime', $until); // exchange-specific in milliseconds
            $params = $this->omit($params, array( 'endTime', 'until' ));
            if ($endTime !== null) {
                $request['endTime'] = $endTime;
            } else {
                if ($since !== null) {
                    // end time is required when $since is not empty
                    $fundingInterval = 60 * 60 * 8 * 1000;
                    $request['endTime'] = $since . $limit * $fundingInterval;
                }
            }
            $response = Async\await($this->publicGetV5MarketFundingHistory ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "OK",
            //         "result" => {
            //             "category" => "linear",
            //             "list" => array(
            //                 array(
            //                     "symbol" => "ETHPERP",
            //                     "fundingRate" => "0.0001",
            //                     "fundingRateTimestamp" => "1672041600000"
            //                 }
            //             )
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1672051897447
            //     }
            //
            $rates = array();
            $result = $this->safe_dict($response, 'result');
            $resultList = $this->safe_list($result, 'list');
            for ($i = 0; $i < count($resultList); $i++) {
                $entry = $resultList[$i];
                $timestamp = $this->safe_integer($entry, 'fundingRateTimestamp');
                $rates[] = array(
                    'info' => $entry,
                    'symbol' => $this->safe_symbol($this->safe_string($entry, 'symbol'), null, null, 'swap'),
                    'fundingRate' => $this->safe_number($entry, 'fundingRate'),
                    'timestamp' => $timestamp,
                    'datetime' => $this->iso8601($timestamp),
                );
            }
            $sorted = $this->sort_by($rates, 'timestamp');
            return $this->filter_by_symbol_since_limit($sorted, $symbol, $since, $limit);
        }) ();
    }

    public function parse_trade(array $trade, ?array $market = null): array {
        //
        // public https://bybit-exchange.github.io/docs/v5/market/recent-$trade
        //
        //     {
        //         "execId" => "666042b4-50c6-58f3-bd9c-89b2088663ff",
        //         "symbol" => "ETHUSD",
        //         "price" => "1162.95",
        //         "size" => "1",
        //         "side" => "Sell",
        //         "time" => "1669191277315",
        //         "isBlockTrade" => false
        //     }
        //
        // private trades classic spot https://bybit-exchange.github.io/docs/v5/position/execution
        //
        //     {
        //         "symbol" => "QNTUSDT",
        //         "orderId" => "1538686353240339712",
        //         "orderLinkId" => "",
        //         "side" => "Sell",
        //         "orderPrice" => "",
        //         "orderQty" => "",
        //         "leavesQty" => "",
        //         "orderType" => "Limit",
        //         "stopOrderType" => "",
        //         "execFee" => "0.040919",
        //         "execId" => "2210000000097330907",
        //         "execPrice" => "98.6",
        //         "execQty" => "0.415",
        //         "execType" => "",
        //         "execValue" => "",
        //         "execTime" => "1698161716634",
        //         "isMaker" => true,
        //         "feeRate" => "",
        //         "tradeIv" => "",
        //         "markIv" => "",
        //         "markPrice" => "",
        //         "indexPrice" => "",
        //         "underlyingPrice" => "",
        //         "blockTradeId" => ""
        //     }
        //
        // private trades unified https://bybit-exchange.github.io/docs/v5/position/execution
        //
        //     array(
        //         "symbol" => "QNTUSDT",
        //         "orderType" => "Limit",
        //         "underlyingPrice" => "",
        //         "orderLinkId" => "1549452573428424449",
        //         "orderId" => "1549452573428424448",
        //         "stopOrderType" => "",
        //         "execTime" => "1699445151998",
        //         "feeRate" => "0.00025",
        //         "tradeIv" => "",
        //         "blockTradeId" => "",
        //         "markPrice" => "",
        //         "execPrice" => "102.8",
        //         "markIv" => "",
        //         "orderQty" => "3.652",
        //         "orderPrice" => "102.8",
        //         "execValue" => "1.028",
        //         "closedSize" => "",
        //         "execType" => "Trade",
        //         "seq" => "19157444346",
        //         "side" => "Buy",
        //         "indexPrice" => "",
        //         "leavesQty" => "3.642",
        //         "isMaker" => true,
        //         "execFee" => "0.0000025",
        //         "execId" => "2210000000101610464",
        //         "execQty" => "0.01",
        //         "nextPageCursor" => "267951%3A0%2C38567%3A0"
        //     ),
        //
        // private USDC settled trades
        //
        //     {
        //         "symbol" => "ETHPERP",
        //         "orderLinkId" => "",
        //         "side" => "Buy",
        //         "orderId" => "aad0ee44-ce12-4112-aeee-b7829f6c3a26",
        //         "execFee" => "0.0210",
        //         "feeRate" => "0.000600",
        //         "blockTradeId" => "",
        //         "tradeTime" => "1669196417930",
        //         "execPrice" => "1162.15",
        //         "lastLiquidityInd" => "TAKER",
        //         "execValue" => "34.8645",
        //         "execType" => "Trade",
        //         "execQty" => "0.030",
        //         "tradeId" => "0e94eaf5-b08e-5505-b43f-7f1f30b1ca80"
        //     }
        //
        // watchMyTrades execution.fast
        //
        //     {
        //         "category" => "linear",
        //         "symbol" => "ICPUSDT",
        //         "execId" => "3510f361-0add-5c7b-a2e7-9679810944fc",
        //         "execPrice" => "12.015",
        //         "execQty" => "3000",
        //         "orderId" => "443d63fa-b4c3-4297-b7b1-23bca88b04dc",
        //         "isMaker" => false,
        //         "orderLinkId" => "test-00001",
        //         "side" => "Sell",
        //         "execTime" => "1716800399334",
        //         "seq" => 34771365464
        //     }
        //
        // watchMyTrades execution
        //
        //     {
        //         "category" => "linear",
        //         "symbol" => "BTCUSDT",
        //         "closedSize" => "0",
        //         "execFee" => "0.0679239",
        //         "execId" => "135dbae5-cbed-5275-9290-3956bb2ed907",
        //         "execPrice" => "123498",
        //         "execQty" => "0.001",
        //         "execType" => "Trade",
        //         "execValue" => "123.498",
        //         "feeRate" => "0.00055",
        //         "tradeIv" => "",
        //         "markIv" => "",
        //         "blockTradeId" => "",
        //         "markPrice" => "122392",
        //         "indexPrice" => "",
        //         "underlyingPrice" => "",
        //         "leavesQty" => "0",
        //         "orderId" => "aee7453a-a100-465f-857a-3db780e9329a",
        //         "orderLinkId" => "",
        //         "orderPrice" => "123615.9",
        //         "orderQty" => "0.001",
        //         "orderType" => "Market",
        //         "stopOrderType" => "UNKNOWN",
        //         "side" => "Buy",
        //         "execTime" => "1757837580469",
        //         "isLeverage" => "0",
        //         "isMaker" => false,
        //         "seq" => 9517074055,
        //         "marketUnit" => "",
        //         "execPnl" => "0",
        //         "createType" => "CreateByUser",
        //         "extraFees" => array(),
        //         "feeCoin" => "USDT"
        //  }
        //
        $id = $this->safe_string_n($trade, array( 'execId', 'id', 'tradeId' ));
        $marketId = $this->safe_string($trade, 'symbol');
        $marketType = (is_array($trade) && array_key_exists('createType', $trade)) ? 'contract' : 'spot';
        $category = $this->safe_string($trade, 'category');
        if ($category !== null) {
            $marketType = ($category === 'spot') ? 'spot' : 'contract';
        }
        if ($market !== null) {
            $marketType = $market['type'];
        }
        $market = $this->safe_market($marketId, $market, null, $marketType);
        $symbol = $market['symbol'];
        $amountString = $this->safe_string_n($trade, array( 'execQty', 'orderQty', 'size' ));
        $priceString = $this->safe_string_n($trade, array( 'execPrice', 'orderPrice', 'price' ));
        $costString = $this->safe_string($trade, 'execValue');
        $timestamp = $this->safe_integer_n($trade, array( 'time', 'execTime', 'tradeTime' ));
        $side = $this->safe_string_lower($trade, 'side');
        if ($side === null) {
            $isBuyer = $this->safe_integer($trade, 'isBuyer');
            if ($isBuyer !== null) {
                $side = $isBuyer ? 'buy' : 'sell';
            }
        }
        $isMaker = $this->safe_bool($trade, 'isMaker');
        $takerOrMaker = null;
        if ($isMaker !== null) {
            $takerOrMaker = $isMaker ? 'maker' : 'taker';
        } else {
            $lastLiquidityInd = $this->safe_string($trade, 'lastLiquidityInd');
            if ($lastLiquidityInd === 'UNKNOWN') {
                $lastLiquidityInd = null;
            }
            if ($lastLiquidityInd !== null) {
                if (($lastLiquidityInd === 'TAKER') || ($lastLiquidityInd === 'MAKER')) {
                    $takerOrMaker = strtolower($lastLiquidityInd);
                } else {
                    $takerOrMaker = ($lastLiquidityInd === 'AddedLiquidity') ? 'maker' : 'taker';
                }
            }
        }
        $orderType = $this->safe_string_lower($trade, 'orderType');
        if ($orderType === 'unknown') {
            $orderType = null;
        }
        $feeCostString = $this->safe_string($trade, 'execFee');
        $fee = null;
        if ($feeCostString !== null) {
            $feeRateString = $this->safe_string($trade, 'feeRate');
            $feeCurrencyCode = null;
            if ($market['spot']) {
                if (Precise::string_gt($feeCostString, '0')) {
                    if ($side === 'buy') {
                        $feeCurrencyCode = $market['base'];
                    } else {
                        $feeCurrencyCode = $market['quote'];
                    }
                } else {
                    if ($side === 'buy') {
                        $feeCurrencyCode = $market['quote'];
                    } else {
                        $feeCurrencyCode = $market['base'];
                    }
                }
            } else {
                $feeCurrencyCode = $market['inverse'] ? $market['base'] : $market['settle'];
            }
            $fee = array(
                'cost' => $feeCostString,
                'currency' => $this->safe_string($trade, 'feeCoin', $feeCurrencyCode),
                'rate' => $feeRateString,
            );
        }
        return $this->safe_trade(array(
            'id' => $id,
            'info' => $trade,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'symbol' => $symbol,
            'order' => $this->safe_string($trade, 'orderId'),
            'type' => $orderType,
            'side' => $side,
            'takerOrMaker' => $takerOrMaker,
            'price' => $priceString,
            'amount' => $amountString,
            'cost' => $costString,
            'fee' => $fee,
        ), $market);
    }

    public function fetch_trades(string $symbol, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * get the list of most recent $trades for a particular $symbol
             *
             * @see https://bybit-exchange.github.io/docs/v5/market/recent-trade
             *
             * @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
             * @param {string} [$params->type] $market $type, ['swap', 'option', 'spot']
             * @param {string} [$params->subType] $market subType, ['linear', 'inverse']
             * @return {Trade[]} a list of ~@link https://docs.ccxt.com/#/?id=public-$trades trade structures~
             */
            if ($symbol === null) {
                throw new ArgumentsRequired($this->id . ' fetchTrades() requires a $symbol argument');
            }
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
                // 'baseCoin' => '', // Base coin. For option only. If not passed, return BTC data by default
                // 'optionType' => 'Call', // Option $type-> Call or Put. For option only
            );
            if ($limit !== null) {
                // spot => [1,60], default => 60.
                // others => [1,1000], default => 500
                $request['limit'] = $limit;
            }
            $type = null;
            list($type, $params) = $this->get_bybit_type('fetchTrades', $market, $params);
            $request['category'] = $type;
            $response = Async\await($this->publicGetV5MarketRecentTrade ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "OK",
            //         "result" => {
            //             "category" => "spot",
            //             "list" => array(
            //                 array(
            //                     "execId" => "2100000000007764263",
            //                     "symbol" => "BTCUSDT",
            //                     "price" => "16618.49",
            //                     "size" => "0.00012",
            //                     "side" => "Buy",
            //                     "time" => "1672052955758",
            //                     "isBlockTrade" => false
            //                 }
            //             )
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1672053054358
            //     }
            //
            $result = $this->safe_dict($response, 'result', array());
            $trades = $this->safe_list($result, 'list', array());
            return $this->parse_trades($trades, $market, $since, $limit);
        }) ();
    }

    public function fetch_order_book(string $symbol, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $limit, $params) {
            /**
             * fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
             *
             * @see https://bybit-exchange.github.io/docs/v5/market/orderbook
             *
             * @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
             */
            if ($symbol === null) {
                throw new ArgumentsRequired($this->id . ' fetchOrderBook() requires a $symbol argument');
            }
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
            );
            $defaultLimit = 25;
            if ($market['spot']) {
                // $limit => [1, 50]. Default => 1
                $defaultLimit = 50;
                $request['category'] = 'spot';
            } else {
                if ($market['option']) {
                    // $limit => [1, 25]. Default => 1
                    $request['category'] = 'option';
                } elseif ($market['linear']) {
                    // $limit => [1, 500]. Default => 25
                    $request['category'] = 'linear';
                } elseif ($market['inverse']) {
                    // $limit => [1, 500]. Default => 25
                    $request['category'] = 'inverse';
                }
            }
            $request['limit'] = ($limit !== null) ? $limit : $defaultLimit;
            $response = Async\await($this->publicGetV5MarketOrderbook ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "OK",
            //         "result" => array(
            //             "s" => "BTCUSDT",
            //             "a" => array(
            //                 array(
            //                     "16638.64",
            //                     "0.008479"
            //                 )
            //             ),
            //             "b" => array(
            //                 array(
            //                     "16638.27",
            //                     "0.305749"
            //                 )
            //             ),
            //             "ts" => 1672765737733,
            //             "u" => 5277055
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1672765737734
            //     }
            //
            $result = $this->safe_dict($response, 'result', array());
            $timestamp = $this->safe_integer($result, 'ts');
            return $this->parse_order_book($result, $symbol, $timestamp, 'b', 'a');
        }) ();
    }

    public function parse_balance($response): array {
        //
        // cross
        //     {
        //         "retCode" => 0,
        //         "retMsg" => "success",
        //         "result" => array(
        //             "acctBalanceSum" => "0.122995614474732872",
        //             "debtBalanceSum" => "0.011734191124529754",
        //             "loanAccountList" => array(
        //                 array(
        //                     "free" => "0.001143855",
        //                     "interest" => "0",
        //                     "loan" => "0",
        //                     "locked" => "0",
        //                     "tokenId" => "BTC",
        //                     "total" => "0.001143855"
        //                 ),
        //                 array(
        //                     "free" => "200.00005568",
        //                     "interest" => "0.0008391",
        //                     "loan" => "200",
        //                     "locked" => "0",
        //                     "tokenId" => "USDT",
        //                     "total" => "200.00005568"
        //                 ),
        //             ),
        //             "riskRate" => "0.0954",
        //             "status" => 1
        //         ),
        //         "retExtInfo" => array(),
        //         "time" => 1669843584123
        //     }
        //
        // funding
        //     {
        //         "retCode" => 0,
        //         "retMsg" => "success",
        //         "result" => {
        //             "memberId" => "533285",
        //             "accountType" => "FUND",
        //             "balance" => array(
        //                 array(
        //                     "coin" => "USDT",
        //                     "transferBalance" => "1010",
        //                     "walletBalance" => "1010",
        //                     "bonus" => ""
        //                 ),
        //                 array(
        //                     "coin" => "USDC",
        //                     "transferBalance" => "0",
        //                     "walletBalance" => "0",
        //                     "bonus" => ""
        //                 }
        //             )
        //         ),
        //         "retExtInfo" => array(),
        //         "time" => 1675865290069
        //     }
        //
        //  spot & swap
        //     {
        //         "retCode" => 0,
        //         "retMsg" => "OK",
        //         "result" => {
        //             "list" => array(
        //                 {
        //                     "totalEquity" => "18070.32797922",
        //                     "accountIMRate" => "0.0101",
        //                     "totalMarginBalance" => "18070.32797922",
        //                     "totalInitialMargin" => "182.60183684",
        //                     "accountType" => "UNIFIED",
        //                     "totalAvailableBalance" => "17887.72614237",
        //                     "accountMMRate" => "0",
        //                     "totalPerpUPL" => "-0.11001349",
        //                     "totalWalletBalance" => "18070.43799271",
        //                     "accountLTV" => "0.017",
        //                     "totalMaintenanceMargin" => "0.38106773",
        //                     "coin" => array(
        //                         array(
        //                             "availableToBorrow" => "2.5",
        //                             "bonus" => "0",
        //                             "accruedInterest" => "0",
        //                             "availableToWithdraw" => "0.805994",
        //                             "totalOrderIM" => "0",
        //                             "equity" => "0.805994",
        //                             "totalPositionMM" => "0",
        //                             "usdValue" => "12920.95352538",
        //                             "unrealisedPnl" => "0",
        //                             "borrowAmount" => "0",
        //                             "totalPositionIM" => "0",
        //                             "walletBalance" => "0.805994",
        //                             "cumRealisedPnl" => "0",
        //                             "coin" => "BTC"
        //                         }
        //                     )
        //                 }
        //             )
        //         ),
        //         "retExtInfo" => array(),
        //         "time" => 1672125441042
        //     }
        //
        $timestamp = $this->safe_integer($response, 'time');
        $result = array(
            'info' => $response,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
        );
        $responseResult = $this->safe_dict($response, 'result', array());
        $currencyList = $this->safe_list_n($responseResult, array( 'loanAccountList', 'list', 'balance' ));
        if ($currencyList === null) {
            // usdc wallet
            $code = 'USDC';
            $account = $this->account();
            $account['free'] = $this->safe_string($responseResult, 'availableBalance');
            $account['total'] = $this->safe_string($responseResult, 'walletBalance');
            $result[$code] = $account;
        } else {
            for ($i = 0; $i < count($currencyList); $i++) {
                $entry = $currencyList[$i];
                $accountType = $this->safe_string($entry, 'accountType');
                if ($accountType === 'UNIFIED' || $accountType === 'CONTRACT' || $accountType === 'SPOT') {
                    $coins = $this->safe_list($entry, 'coin');
                    for ($j = 0; $j < count($coins); $j++) {
                        $account = $this->account();
                        $coinEntry = $coins[$j];
                        $loan = $this->safe_string($coinEntry, 'borrowAmount');
                        $interest = $this->safe_string($coinEntry, 'accruedInterest');
                        if (($loan !== null) && ($interest !== null)) {
                            $account['debt'] = Precise::string_add($loan, $interest);
                        }
                        $account['total'] = $this->safe_string($coinEntry, 'walletBalance');
                        $free = $this->safe_string_2($coinEntry, 'availableToWithdraw', 'free');
                        if ($free !== null) {
                            $account['free'] = $free;
                        } else {
                            $locked = $this->safe_string($coinEntry, 'locked', '0');
                            $totalPositionIm = $this->safe_string($coinEntry, 'totalPositionIM', '0');
                            $totalOrderIm = $this->safe_string($coinEntry, 'totalOrderIM', '0');
                            $totalUsed = Precise::string_add($locked, $totalPositionIm);
                            $totalUsed = Precise::string_add($totalUsed, $totalOrderIm);
                            $account['used'] = $totalUsed;
                        }
                        // $account['used'] = $this->safe_string($coinEntry, 'locked');
                        $currencyId = $this->safe_string($coinEntry, 'coin');
                        $code = $this->safe_currency_code($currencyId);
                        $result[$code] = $account;
                    }
                } else {
                    $account = $this->account();
                    $loan = $this->safe_string($entry, 'loan');
                    $interest = $this->safe_string($entry, 'interest');
                    if (($loan !== null) && ($interest !== null)) {
                        $account['debt'] = Precise::string_add($loan, $interest);
                    }
                    $account['total'] = $this->safe_string_2($entry, 'total', 'walletBalance');
                    $account['free'] = $this->safe_string_n($entry, array( 'free', 'availableBalanceWithoutConvert', 'availableBalance', 'transferBalance' ));
                    $account['used'] = $this->safe_string($entry, 'locked');
                    $currencyId = $this->safe_string_n($entry, array( 'tokenId', 'coin', 'currencyCoin' ));
                    $code = $this->safe_currency_code($currencyId);
                    $result[$code] = $account;
                }
            }
        }
        return $this->safe_balance($result);
    }

    public function fetch_balance($params = array ()): PromiseInterface {
        return Async\async(function () use ($params) {
            /**
             * query for balance and get the amount of funds available for trading or funds locked in orders
             *
             * @see https://bybit-exchange.github.io/docs/v5/spot-margin-normal/account-info
             * @see https://bybit-exchange.github.io/docs/v5/asset/all-balance
             * @see https://bybit-exchange.github.io/docs/v5/account/wallet-balance
             *
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->type] wallet $type, ['spot', 'swap', 'funding']
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=balance-structure balance structure~
             */
            Async\await($this->load_markets());
            $request = array();
            list($enableUnifiedMargin, $enableUnifiedAccount) = Async\await($this->is_unified_enabled());
            $isUnifiedAccount = ($enableUnifiedMargin || $enableUnifiedAccount);
            $type = null;
            // don't use getBybitType here
            list($type, $params) = $this->handle_market_type_and_params('fetchBalance', null, $params);
            $subType = null;
            list($subType, $params) = $this->handle_sub_type_and_params('fetchBalance', null, $params);
            if (($type === 'swap') || ($type === 'future')) {
                $type = $subType;
            }
            $lowercaseRawType = ($type !== null) ? strtolower($type) : null;
            $isSpot = ($type === 'spot');
            $isLinear = ($type === 'linear');
            $isInverse = ($type === 'inverse');
            $isFunding = ($lowercaseRawType === 'fund') || ($lowercaseRawType === 'funding');
            if ($isUnifiedAccount) {
                $unifiedMarginStatus = $this->safe_integer($this->options, 'unifiedMarginStatus', 6);
                if ($unifiedMarginStatus < 5) {
                    // it's not uta.20 where inverse are unified
                    if ($isInverse) {
                        $type = 'contract';
                    } else {
                        $type = 'unified';
                    }
                } else {
                    $type = 'unified'; // uta.20 where inverse are unified
                }
            } else {
                if ($isLinear || $isInverse) {
                    $type = 'contract';
                }
            }
            $accountTypes = $this->safe_dict($this->options, 'accountsByType', array());
            $unifiedType = $this->safe_string_upper($accountTypes, $type, $type);
            $marginMode = null;
            list($marginMode, $params) = $this->handle_margin_mode_and_params('fetchBalance', $params);
            $response = null;
            if ($isSpot && ($marginMode !== null)) {
                $response = Async\await($this->privateGetV5SpotCrossMarginTradeAccount ($this->extend($request, $params)));
            } elseif ($isFunding) {
                // use this endpoint only we have no other choice
                // because it requires transfer permission
                $request['accountType'] = 'FUND';
                $response = Async\await($this->privateGetV5AssetTransferQueryAccountCoinsBalance ($this->extend($request, $params)));
            } else {
                $request['accountType'] = $unifiedType;
                $response = Async\await($this->privateGetV5AccountWalletBalance ($this->extend($request, $params)));
            }
            //
            // cross
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "success",
            //         "result" => array(
            //             "acctBalanceSum" => "0.122995614474732872",
            //             "debtBalanceSum" => "0.011734191124529754",
            //             "loanAccountList" => array(
            //                 array(
            //                     "free" => "0.001143855",
            //                     "interest" => "0",
            //                     "loan" => "0",
            //                     "locked" => "0",
            //                     "tokenId" => "BTC",
            //                     "total" => "0.001143855"
            //                 ),
            //                 array(
            //                     "free" => "200.00005568",
            //                     "interest" => "0.0008391",
            //                     "loan" => "200",
            //                     "locked" => "0",
            //                     "tokenId" => "USDT",
            //                     "total" => "200.00005568"
            //                 ),
            //             ),
            //             "riskRate" => "0.0954",
            //             "status" => 1
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1669843584123
            //     }
            //
            // funding
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "success",
            //         "result" => {
            //             "memberId" => "533285",
            //             "accountType" => "FUND",
            //             "balance" => array(
            //                 array(
            //                     "coin" => "USDT",
            //                     "transferBalance" => "1010",
            //                     "walletBalance" => "1010",
            //                     "bonus" => ""
            //                 ),
            //                 array(
            //                     "coin" => "USDC",
            //                     "transferBalance" => "0",
            //                     "walletBalance" => "0",
            //                     "bonus" => ""
            //                 }
            //             )
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1675865290069
            //     }
            //
            //  spot & swap
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "OK",
            //         "result" => {
            //             "list" => array(
            //                 {
            //                     "totalEquity" => "18070.32797922",
            //                     "accountIMRate" => "0.0101",
            //                     "totalMarginBalance" => "18070.32797922",
            //                     "totalInitialMargin" => "182.60183684",
            //                     "accountType" => "UNIFIED",
            //                     "totalAvailableBalance" => "17887.72614237",
            //                     "accountMMRate" => "0",
            //                     "totalPerpUPL" => "-0.11001349",
            //                     "totalWalletBalance" => "18070.43799271",
            //                     "accountLTV" => "0.017",
            //                     "totalMaintenanceMargin" => "0.38106773",
            //                     "coin" => array(
            //                         array(
            //                             "availableToBorrow" => "2.5",
            //                             "bonus" => "0",
            //                             "accruedInterest" => "0",
            //                             "availableToWithdraw" => "0.805994",
            //                             "totalOrderIM" => "0",
            //                             "equity" => "0.805994",
            //                             "totalPositionMM" => "0",
            //                             "usdValue" => "12920.95352538",
            //                             "unrealisedPnl" => "0",
            //                             "borrowAmount" => "0",
            //                             "totalPositionIM" => "0",
            //                             "walletBalance" => "0.805994",
            //                             "cumRealisedPnl" => "0",
            //                             "coin" => "BTC"
            //                         }
            //                     )
            //                 }
            //             )
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1672125441042
            //     }
            //
            return $this->parse_balance($response);
        }) ();
    }

    public function parse_order_status(?string $status) {
        $statuses = array(
            // v3 spot
            'NEW' => 'open',
            'PARTIALLY_FILLED' => 'open',
            'FILLED' => 'closed',
            'CANCELED' => 'canceled',
            'PENDING_CANCEL' => 'open',
            'PENDING_NEW' => 'open',
            'REJECTED' => 'rejected',
            'PARTIALLY_FILLED_CANCELLED' => 'closed', // context => https://github.com/ccxt/ccxt/issues/18685
            // v3 contract / unified margin / unified account
            'Created' => 'open',
            'New' => 'open',
            'Rejected' => 'rejected', // order is triggered but failed upon being placed
            'PartiallyFilled' => 'open',
            'PartiallyFilledCanceled' => 'closed', // context => https://github.com/ccxt/ccxt/issues/18685
            'Filled' => 'closed',
            'PendingCancel' => 'open',
            'Cancelled' => 'canceled',
            // below this line the $status only pertains to conditional orders
            'Untriggered' => 'open',
            'Deactivated' => 'canceled',
            'Triggered' => 'open',
            'Active' => 'open',
        );
        return $this->safe_string($statuses, $status, $status);
    }

    public function parse_time_in_force(?string $timeInForce) {
        $timeInForces = array(
            'GoodTillCancel' => 'GTC',
            'ImmediateOrCancel' => 'IOC',
            'FillOrKill' => 'FOK',
            'PostOnly' => 'PO',
        );
        return $this->safe_string($timeInForces, $timeInForce, $timeInForce);
    }

    public function parse_order(array $order, ?array $market = null): array {
        //
        // v1 for usdc normal account
        //     {
        //         "symbol" => "BTCPERP",
        //         "orderType" => "Market",
        //         "orderLinkId" => "",
        //         "orderId" => "36190ad3-de08-4b83-9ad3-56942f684b79",
        //         "cancelType" => "UNKNOWN",
        //         "stopOrderType" => "UNKNOWN",
        //         "orderStatus" => "Filled",
        //         "updateTimeStamp" => "1692769133267",
        //         "takeProfit" => "0.0000",
        //         "cumExecValue" => "259.6830",
        //         "createdAt" => "1692769133261",
        //         "blockTradeId" => "",
        //         "orderPnl" => "",
        //         "price" => "24674.7",
        //         "tpTriggerBy" => "UNKNOWN",
        //         "timeInForce" => "ImmediateOrCancel",
        //         "updatedAt" => "1692769133267",
        //         "basePrice" => "0.0",
        //         "realisedPnl" => "0.0000",
        //         "side" => "Sell",
        //         "triggerPrice" => "0.0",
        //         "cumExecFee" => "0.1429",
        //         "leavesQty" => "0.000",
        //         "cashFlow" => "",
        //         "slTriggerBy" => "UNKNOWN",
        //         "iv" => "",
        //         "closeOnTrigger" => "UNKNOWN",
        //         "cumExecQty" => "0.010",
        //         "reduceOnly" => 0,
        //         "qty" => "0.010",
        //         "stopLoss" => "0.0000",
        //         "triggerBy" => "UNKNOWN",
        //         "orderIM" => ""
        //     }
        //
        // v5
        //     {
        //         "symbol" => "BTCUSDT",
        //         "orderType" => "Market",
        //         "orderLinkId" => "",
        //         "slLimitPrice" => "0",
        //         "orderId" => "f5f2d355-9a11-4af3-9b83-aa1d6ab6ddfe",
        //         "cancelType" => "UNKNOWN",
        //         "avgPrice" => "122529.9",
        //         "stopOrderType" => "",
        //         "lastPriceOnCreated" => "123747.9",
        //         "orderStatus" => "Filled",
        //         "createType" => "CreateByUser",
        //         "takeProfit" => "",
        //         "cumExecValue" => "122.5299",
        //         "tpslMode" => "",
        //         "smpType" => "None",
        //         "triggerDirection" => 0,
        //         "blockTradeId" => "",
        //         "cumFeeDetail" => array(
        //             "USDT" => "0.06739145"
        //         ),
        //         "rejectReason" => "EC_NoError",
        //         "isLeverage" => "",
        //         "price" => "120518",
        //         "orderIv" => "",
        //         "createdTime" => "1757837618905",
        //         "tpTriggerBy" => "",
        //         "positionIdx" => 0,
        //         "timeInForce" => "IOC",
        //         "leavesValue" => "0",
        //         "updatedTime" => "1757837618909",
        //         "side" => "Sell",
        //         "smpGroup" => 0,
        //         "triggerPrice" => "",
        //         "tpLimitPrice" => "0",
        //         "cumExecFee" => "0.06739145",
        //         "slTriggerBy" => "",
        //         "leavesQty" => "0",
        //         "closeOnTrigger" => false,
        //         "slippageToleranceType" => "UNKNOWN",
        //         "placeType" => "",
        //         "cumExecQty" => "0.001",
        //         "reduceOnly" => true,
        //         "qty" => "0.001",
        //         "stopLoss" => "",
        //         "smpOrderId" => "",
        //         "slippageTolerance" => "0",
        //         "triggerBy" => "",
        //         "extraFees" => ""
        //     }
        //
        // createOrders failed $order
        //    {
        //        "category" => "linear",
        //        "symbol" => "LTCUSDT",
        //        "orderId" => '',
        //        "orderLinkId" => '',
        //        "createAt" => '',
        //        "code" => "10001",
        //        "msg" => "The number of contracts exceeds maximum limit allowed => too large"
        //    }
        //
        $code = $this->safe_string($order, 'code');
        if ($code !== null) {
            if ($code !== '0') {
                $category = $this->safe_string($order, 'category');
                $inferredMarketType = ($category === 'spot') ? 'spot' : 'contract';
                return $this->safe_order(array(
                    'info' => $order,
                    'status' => 'rejected',
                    'id' => $this->safe_string($order, 'orderId'),
                    'clientOrderId' => $this->safe_string($order, 'orderLinkId'),
                    'symbol' => $this->safe_symbol($this->safe_string($order, 'symbol'), null, null, $inferredMarketType),
                ));
            }
        }
        $marketId = $this->safe_string($order, 'symbol');
        $isContract = (is_array($order) && array_key_exists('tpslMode', $order));
        $marketType = null;
        if ($market !== null) {
            $marketType = $market['type'];
        } else {
            $marketType = $isContract ? 'contract' : 'spot';
        }
        $market = $this->safe_market($marketId, $market, null, $marketType);
        $symbol = $market['symbol'];
        $timestamp = $this->safe_integer_2($order, 'createdTime', 'createdAt');
        $marketUnit = $this->safe_string($order, 'marketUnit', 'baseCoin');
        $id = $this->safe_string($order, 'orderId');
        $type = $this->safe_string_lower($order, 'orderType');
        $price = $this->safe_string($order, 'price');
        $amount = null;
        $cost = null;
        if ($marketUnit === 'baseCoin') {
            $amount = $this->safe_string($order, 'qty');
            $cost = $this->safe_string($order, 'cumExecValue');
        } else {
            $cost = $this->safe_string($order, 'cumExecValue');
        }
        $filled = $this->safe_string($order, 'cumExecQty');
        $remaining = $this->safe_string($order, 'leavesQty');
        $lastTradeTimestamp = $this->safe_integer_2($order, 'updatedTime', 'updatedAt');
        $rawStatus = $this->safe_string($order, 'orderStatus');
        $status = $this->parse_order_status($rawStatus);
        $side = $this->safe_string_lower($order, 'side');
        $fee = null;
        $cumFeeDetail = $this->safe_dict($order, 'cumFeeDetail', array());
        $feeCoins = is_array($cumFeeDetail) ? array_keys($cumFeeDetail) : array();
        $feeCoinId = $this->safe_string($feeCoins, 0);
        if ($feeCoinId !== null) {
            $fee = array(
                'cost' => $this->safe_number($cumFeeDetail, $feeCoinId),
                'currency' => $feeCoinId,
            );
        }
        $clientOrderId = $this->safe_string($order, 'orderLinkId');
        if (($clientOrderId !== null) && (strlen($clientOrderId) < 1)) {
            $clientOrderId = null;
        }
        $avgPrice = $this->omit_zero($this->safe_string($order, 'avgPrice'));
        $rawTimeInForce = $this->safe_string($order, 'timeInForce');
        $timeInForce = $this->parse_time_in_force($rawTimeInForce);
        $triggerPrice = $this->omit_zero($this->safe_string($order, 'triggerPrice'));
        $reduceOnly = $this->safe_bool($order, 'reduceOnly');
        $takeProfitPrice = $this->omit_zero($this->safe_string($order, 'takeProfit'));
        $stopLossPrice = $this->omit_zero($this->safe_string($order, 'stopLoss'));
        $triggerDirection = $this->safe_string($order, 'triggerDirection');
        $isAscending = ($triggerDirection === '1');
        $isStopOrderType2 = ($triggerPrice !== null) && $reduceOnly;
        if (($stopLossPrice === null) && $isStopOrderType2) {
            // check if $order is stop $order $type 2 - $stopLossPrice
            if ($isAscending && ($side === 'buy')) {
                // stopLoss $order against short position
                $stopLossPrice = $triggerPrice;
            }
            if (!$isAscending && ($side === 'sell')) {
                // stopLoss $order against a long position
                $stopLossPrice = $triggerPrice;
            }
        }
        if (($takeProfitPrice === null) && $isStopOrderType2) {
            // check if $order is stop $order $type 2 - $takeProfitPrice
            if ($isAscending && ($side === 'sell')) {
                // takeprofit $order against a long position
                $takeProfitPrice = $triggerPrice;
            }
            if (!$isAscending && ($side === 'buy')) {
                // takeprofit $order against a short position
                $takeProfitPrice = $triggerPrice;
            }
        }
        return $this->safe_order(array(
            'info' => $order,
            'id' => $id,
            'clientOrderId' => $clientOrderId,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'lastTradeTimestamp' => $lastTradeTimestamp,
            'lastUpdateTimestamp' => $lastTradeTimestamp,
            'symbol' => $symbol,
            'type' => $type,
            'timeInForce' => $timeInForce,
            'postOnly' => null,
            'reduceOnly' => $this->safe_bool($order, 'reduceOnly'),
            'side' => $side,
            'price' => $price,
            'triggerPrice' => $triggerPrice,
            'takeProfitPrice' => $takeProfitPrice,
            'stopLossPrice' => $stopLossPrice,
            'amount' => $amount,
            'cost' => $cost,
            'average' => $avgPrice,
            'filled' => $filled,
            'remaining' => $remaining,
            'status' => $status,
            'fee' => $fee,
            'trades' => null,
        ), $market);
    }

    public function create_market_buy_order_with_cost(string $symbol, float $cost, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $cost, $params) {
            /**
             * create a $market buy order by providing the $symbol and $cost
             *
             * @see https://bybit-exchange.github.io/docs/v5/order/create-order
             *
             * @param {string} $symbol unified $symbol of the $market to create an order in
             * @param {float} $cost how much you want to trade in units of the quote currency
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} an ~@link https://docs.ccxt.com/#/?id=order-structure order structure~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            if (!$market['spot']) {
                throw new NotSupported($this->id . ' createMarketBuyOrderWithCost() supports spot orders only');
            }
            $req = array(
                'cost' => $cost,
            );
            return Async\await($this->create_order($symbol, 'market', 'buy', -1, null, $this->extend($req, $params)));
        }) ();
    }

    public function create_market_sell_order_with_cost(string $symbol, float $cost, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $cost, $params) {
            /**
             * create a $market sell order by providing the $symbol and $cost
             *
             * @see https://bybit-exchange.github.io/docs/v5/order/create-order
             *
             * @param {string} $symbol unified $symbol of the $market to create an order in
             * @param {float} $cost how much you want to trade in units of the quote currency
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} an ~@link https://docs.ccxt.com/#/?id=order-structure order structure~
             */
            Async\await($this->load_markets());
            $types = Async\await($this->is_unified_enabled());
            $enableUnifiedAccount = $types[1];
            if (!$enableUnifiedAccount) {
                throw new NotSupported($this->id . ' createMarketSellOrderWithCost() supports UTA accounts only');
            }
            $market = $this->market($symbol);
            if (!$market['spot']) {
                throw new NotSupported($this->id . ' createMarketSellOrderWithCost() supports spot orders only');
            }
            $req = array(
                'cost' => $cost,
            );
            return Async\await($this->create_order($symbol, 'market', 'sell', -1, null, $this->extend($req, $params)));
        }) ();
    }

    public function create_order(string $symbol, string $type, string $side, float $amount, ?float $price = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $type, $side, $amount, $price, $params) {
            /**
             * create a trade $order
             *
             * @see https://bybit-exchange.github.io/docs/v5/order/create-$order
             * @see https://bybit-exchange.github.io/docs/v5/position/trading-stop
             *
             * @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 currency you want to trade in units of base currency
             * @param {float} [$price] the $price at which the $order is to be fulfilled, in units of the quote currency, ignored in $market orders
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->timeInForce] "GTC", "IOC", "FOK"
             * @param {bool} [$params->postOnly] true or false whether the $order is post-only
             * @param {bool} [$params->reduceOnly] true or false whether the $order is reduce-only
             * @param {string} [$params->positionIdx] *contracts only* 0 for one-way mode, 1 buy $side of hedged mode, 2 sell $side of hedged mode
             * @param {bool} [$params->hedged] *contracts only* true for hedged mode, false for one way mode, default is false
             * @param {int} [$params->isLeverage] *unified spot only* false then spot trading true then margin trading
             * @param {string} [$params->tpslMode] *contract only* 'full' or 'partial'
             * @param {string} [$params->mmp] *option only* $market maker protection
             * @param {string} [$params->triggerDirection] *contract only* the direction for trigger orders, 'ascending' or 'descending'
             * @param {float} [$params->triggerPrice] The $price at which a trigger $order is triggered at
             * @param {float} [$params->stopLossPrice] The $price at which a stop loss $order is triggered at
             * @param {float} [$params->takeProfitPrice] The $price at which a take profit $order is triggered at
             * @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 {string} [$params->trailingAmount] the quote $amount to trail away from the current $market $price
             * @param {string} [$params->trailingTriggerPrice] the $price to trigger a trailing $order, default uses the $price argument
             * @return {array} an ~@link https://docs.ccxt.com/#/?id=$order-structure $order structure~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $parts = Async\await($this->is_unified_enabled());
            $enableUnifiedAccount = $parts[1];
            $trailingAmount = $this->safe_string_2($params, 'trailingAmount', 'trailingStop');
            $stopLossPrice = $this->safe_string($params, 'stopLossPrice');
            $takeProfitPrice = $this->safe_string($params, 'takeProfitPrice');
            $isTrailingAmountOrder = $trailingAmount !== null;
            $isStopLoss = $stopLossPrice !== null;
            $isTakeProfit = $takeProfitPrice !== null;
            $orderRequest = $this->create_order_request($symbol, $type, $side, $amount, $price, $params, $enableUnifiedAccount);
            $defaultMethod = null;
            if (($isTrailingAmountOrder || $isStopLoss || $isTakeProfit) && !$market['spot']) {
                $defaultMethod = 'privatePostV5PositionTradingStop';
            } else {
                $defaultMethod = 'privatePostV5OrderCreate';
            }
            $method = null;
            list($method, $params) = $this->handle_option_and_params($params, 'createOrder', 'method', $defaultMethod);
            $response = null;
            if ($method === 'privatePostV5PositionTradingStop') {
                $response = Async\await($this->privatePostV5PositionTradingStop ($orderRequest));
            } else {
                $response = Async\await($this->privatePostV5OrderCreate ($orderRequest)); // already extended inside createOrderRequest
            }
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "OK",
            //         "result" => array(
            //             "orderId" => "1321003749386327552",
            //             "orderLinkId" => "spot-test-postonly"
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1672211918471
            //     }
            //
            $order = $this->safe_dict($response, 'result', array());
            return $this->parse_order($order, $market);
        }) ();
    }

    public function create_order_request(string $symbol, string $type, string $side, float $amount, ?float $price = null, $params = array (), $isUTA = true) {
        $market = $this->market($symbol);
        $symbol = $market['symbol'];
        $lowerCaseType = strtolower($type);
        if (($price === null) && ($lowerCaseType === 'limit')) {
            throw new ArgumentsRequired($this->id . ' createOrder requires a $price argument for limit orders');
        }
        $request = array(
            'symbol' => $market['id'],
            // 'side' => $this->capitalize($side),
            // 'orderType' => $this->capitalize($lowerCaseType), // limit or $market
            // 'timeInForce' => 'GTC', // IOC, FOK, PostOnly
            // 'takeProfit' => 123.45, // take profit $price, only take effect upon opening the position
            // 'stopLoss' => 123.45, // stop loss $price, only take effect upon opening the position
            // 'reduceOnly' => false, // reduce only, required for linear orders
            // when creating a closing order, bybit recommends a True value for
            //  closeOnTrigger to avoid failing due to insufficient available margin
            // 'closeOnTrigger' => false, required for linear orders
            // 'orderLinkId' => 'string', // unique client order id, max 36 characters
            // 'triggerPrice' => 123.46, // trigger $price, required for conditional orders
            // 'triggerBy' => 'MarkPrice', // IndexPrice, MarkPrice, LastPrice
            // 'tpTriggerby' => 'MarkPrice', // IndexPrice, MarkPrice, LastPrice
            // 'slTriggerBy' => 'MarkPrice', // IndexPrice, MarkPrice, LastPrice
            // 'mmp' => false // $market maker protection
            // 'positionIdx' => 0, // Position mode. Unified account has one-way mode only (0)
            // 'triggerDirection' => 1, // Conditional order param. Used to identify the expected direction of the conditional order. 1 => triggered when $market $price rises to $triggerPrice 2 => triggered when $market $price falls to $triggerPrice
            // Valid for spot only.
            // 'isLeverage' => 0, // Whether to borrow. 0(default) => false, 1 => true
            // 'orderFilter' => 'Order' // Order,tpslOrder. If not passed, Order by default
            // Valid for option only.
            // 'orderIv' => '0', // Implied volatility; parameters are passed according to the real value; for example, for 10%, 0.1 is passed
        );
        $hedged = $this->safe_bool($params, 'hedged', false);
        $reduceOnly = $this->safe_bool($params, 'reduceOnly');
        $triggerPrice = $this->safe_value_2($params, 'triggerPrice', 'stopPrice');
        $stopLossTriggerPrice = $this->safe_value($params, 'stopLossPrice');
        $takeProfitTriggerPrice = $this->safe_value($params, 'takeProfitPrice');
        $stopLoss = $this->safe_value($params, 'stopLoss');
        $takeProfit = $this->safe_value($params, 'takeProfit');
        $trailingTriggerPrice = $this->safe_string_2($params, 'trailingTriggerPrice', 'activePrice', $this->number_to_string($price));
        $trailingAmount = $this->safe_string_2($params, 'trailingAmount', 'trailingStop');
        $isTrailingAmountOrder = $trailingAmount !== null;
        $isTriggerOrder = $triggerPrice !== null;
        $isStopLossTriggerOrder = $stopLossTriggerPrice !== null;
        $isTakeProfitTriggerOrder = $takeProfitTriggerPrice !== null;
        $isStopLoss = $stopLoss !== null;
        $isTakeProfit = $takeProfit !== null;
        $isMarket = $lowerCaseType === 'market';
        $isLimit = $lowerCaseType === 'limit';
        $isBuy = $side === 'buy';
        $defaultMethod = null;
        if (($isTrailingAmountOrder || $isStopLossTriggerOrder || $isTakeProfitTriggerOrder) && !$market['spot']) {
            $defaultMethod = 'privatePostV5PositionTradingStop';
        } else {
            $defaultMethod = 'privatePostV5OrderCreate';
        }
        $method = null;
        list($method, $params) = $this->handle_option_and_params($params, 'createOrder', 'method', $defaultMethod);
        $isAlternativeEndpoint = $method === 'privatePostV5PositionTradingStop';
        $amountString = $this->get_amount($symbol, $amount);
        $priceString = ($price !== null) ? $this->get_price($symbol, $this->number_to_string($price)) : null;
        if ($isTrailingAmountOrder || $isAlternativeEndpoint) {
            if ($isStopLoss || $isTakeProfit || $isTriggerOrder || $market['spot']) {
                throw new InvalidOrder($this->id . ' the API endpoint used only supports contract $trailingAmount, stopLossPrice and takeProfitPrice orders');
            }
            if ($isStopLossTriggerOrder || $isTakeProfitTriggerOrder) {
                if ($isStopLossTriggerOrder) {
                    $request['stopLoss'] = $this->get_price($symbol, $stopLossTriggerPrice);
                    if ($isLimit) {
                        $request['tpslMode'] = 'Partial';
                        $request['slOrderType'] = 'Limit';
                        $request['slLimitPrice'] = $priceString;
                        $request['slSize'] = $amountString;
                    }
                } elseif ($isTakeProfitTriggerOrder) {
                    $request['takeProfit'] = $this->get_price($symbol, $takeProfitTriggerPrice);
                    if ($isLimit) {
                        $request['tpslMode'] = 'Partial';
                        $request['tpOrderType'] = 'Limit';
                        $request['tpLimitPrice'] = $priceString;
                        $request['tpSize'] = $amountString;
                    }
                }
            }
        } else {
            $request['side'] = $this->capitalize($side);
            $request['orderType'] = $this->capitalize($lowerCaseType);
            $timeInForce = $this->safe_string_lower($params, 'timeInForce'); // this is same specific param
            $postOnly = null;
            list($postOnly, $params) = $this->handle_post_only($isMarket, $timeInForce === 'postonly', $params);
            if ($postOnly) {
                $request['timeInForce'] = 'PostOnly';
            } elseif ($timeInForce === 'gtc') {
                $request['timeInForce'] = 'GTC';
            } elseif ($timeInForce === 'fok') {
                $request['timeInForce'] = 'FOK';
            } elseif ($timeInForce === 'ioc') {
                $request['timeInForce'] = 'IOC';
            }
            if ($market['spot']) {
                // only works for spot $market
                if ($triggerPrice !== null) {
                    $request['orderFilter'] = 'StopOrder';
                } elseif ($stopLossTriggerPrice !== null || $takeProfitTriggerPrice !== null || $isStopLoss || $isTakeProfit) {
                    $request['orderFilter'] = 'tpslOrder';
                }
            }
            $clientOrderId = $this->safe_string($params, 'clientOrderId');
            if ($clientOrderId !== null) {
                $request['orderLinkId'] = $clientOrderId;
            } elseif ($market['option']) {
                // mandatory field for options
                $request['orderLinkId'] = $this->uuid16();
            }
            if ($isLimit) {
                $request['price'] = $priceString;
            }
        }
        $category = null;
        list($category, $params) = $this->get_bybit_type('createOrderRequest', $market, $params);
        $request['category'] = $category;
        $cost = $this->safe_string($params, 'cost');
        $params = $this->omit($params, 'cost');
        // if the $cost is inferable, let's keep the old logic and ignore marketUnit, to minimize the impact of the changes
        $isMarketBuyAndCostInferable = ($lowerCaseType === 'market') && ($side === 'buy') && (($price !== null) || ($cost !== null));
        if ($market['spot'] && ($type === 'market') && $isUTA && !$isMarketBuyAndCostInferable) {
            // UTA account can specify the $cost of the order on both sides
            if (($cost !== null) || ($price !== null)) {
                $request['marketUnit'] = 'quoteCoin';
                $orderCost = null;
                if ($cost !== null) {
                    $orderCost = $cost;
                } else {
                    $quoteAmount = Precise::string_mul($amountString, $priceString);
                    $orderCost = $quoteAmount;
                }
                $request['qty'] = $this->get_cost($symbol, $orderCost);
            } else {
                $request['marketUnit'] = 'baseCoin';
                $request['qty'] = $amountString;
            }
        } elseif ($market['spot'] && ($type === 'market') && ($side === 'buy')) {
            // classic accounts
            // for $market buy it requires the $amount of quote currency to spend
            $createMarketBuyOrderRequiresPrice = true;
            list($createMarketBuyOrderRequiresPrice, $params) = $this->handle_option_and_params($params, 'createOrder', 'createMarketBuyOrderRequiresPrice');
            if ($createMarketBuyOrderRequiresPrice) {
                if (($price === null) && ($cost === null)) {
                    throw new InvalidOrder($this->id . ' createOrder() requires the $price argument for $market buy orders to calculate the total $cost to spend ($amount * $price), alternatively set the $createMarketBuyOrderRequiresPrice option or param to false and pass the $cost to spend in the $amount argument');
                } else {
                    $quoteAmount = Precise::string_mul($this->number_to_string($amount), $priceString);
                    $costRequest = ($cost !== null) ? $cost : $quoteAmount;
                    $request['qty'] = $this->get_cost($symbol, $costRequest);
                }
            } else {
                if ($cost !== null) {
                    $request['qty'] = $this->get_cost($symbol, $this->number_to_string($cost));
                } elseif ($price !== null) {
                    $request['qty'] = $this->get_cost($symbol, Precise::string_mul($amountString, $priceString));
                } else {
                    $request['qty'] = $amountString;
                }
            }
        } else {
            if (!$isTrailingAmountOrder && !$isAlternativeEndpoint) {
                $request['qty'] = $amountString;
            }
        }
        if ($isTrailingAmountOrder) {
            if ($trailingTriggerPrice !== null) {
                $request['activePrice'] = $this->get_price($symbol, $trailingTriggerPrice);
            }
            $request['trailingStop'] = $trailingAmount;
        } elseif ($isTriggerOrder && !$isAlternativeEndpoint) {
            $triggerDirection = $this->safe_string($params, 'triggerDirection');
            $params = $this->omit($params, array( 'triggerPrice', 'stopPrice', 'triggerDirection' ));
            if ($market['spot']) {
                if ($triggerDirection !== null) {
                    throw new NotSupported($this->id . ' createOrder() : trigger order does not support $triggerDirection for spot markets yet');
                }
            } else {
                if ($triggerDirection === null) {
                    throw new ArgumentsRequired($this->id . ' stop/trigger orders require a $triggerDirection parameter, either "ascending" or "descending" to determine the direction of the trigger.');
                }
                $isAsending = (($triggerDirection === 'ascending') || ($triggerDirection === 'above') || ($triggerDirection === '1'));
                $request['triggerDirection'] = $isAsending ? 1 : 2;
            }
            $request['triggerPrice'] = $this->get_price($symbol, $triggerPrice);
        } elseif (($isStopLossTriggerOrder || $isTakeProfitTriggerOrder) && !$isAlternativeEndpoint) {
            if ($isBuy) {
                $request['triggerDirection'] = $isStopLossTriggerOrder ? 1 : 2;
            } else {
                $request['triggerDirection'] = $isStopLossTriggerOrder ? 2 : 1;
            }
            $triggerPrice = $isStopLossTriggerOrder ? $stopLossTriggerPrice : $takeProfitTriggerPrice;
            $request['triggerPrice'] = $this->get_price($symbol, $triggerPrice);
            $request['reduceOnly'] = true;
        }
        if (($isStopLoss || $isTakeProfit) && !$isAlternativeEndpoint) {
            if ($isStopLoss) {
                $slTriggerPrice = $this->safe_value_2($stopLoss, 'triggerPrice', 'stopPrice', $stopLoss);
                $request['stopLoss'] = $this->get_price($symbol, $slTriggerPrice);
                $slLimitPrice = $this->safe_value($stopLoss, 'price');
                if ($slLimitPrice !== null) {
                    $request['tpslMode'] = 'Partial';
                    $request['slOrderType'] = 'Limit';
                    $request['slLimitPrice'] = $this->get_price($symbol, $slLimitPrice);
                }
            }
            if ($isTakeProfit) {
                $tpTriggerPrice = $this->safe_value_2($takeProfit, 'triggerPrice', 'stopPrice', $takeProfit);
                $request['takeProfit'] = $this->get_price($symbol, $tpTriggerPrice);
                $tpLimitPrice = $this->safe_value($takeProfit, 'price');
                if ($tpLimitPrice !== null) {
                    $request['tpslMode'] = 'Partial';
                    $request['tpOrderType'] = 'Limit';
                    $request['tpLimitPrice'] = $this->get_price($symbol, $tpLimitPrice);
                }
            }
        }
        if (!$market['spot'] && $hedged) {
            if ($reduceOnly) {
                $params = $this->omit($params, 'reduceOnly');
                $side = ($side === 'buy') ? 'sell' : 'buy';
            }
            $request['positionIdx'] = ($side === 'buy') ? 1 : 2;
        }
        $params = $this->omit($params, array( 'stopPrice', 'timeInForce', 'stopLossPrice', 'takeProfitPrice', 'postOnly', 'clientOrderId', 'triggerPrice', 'stopLoss', 'takeProfit', 'trailingAmount', 'trailingTriggerPrice', 'hedged' ));
        return $this->extend($request, $params);
    }

    public function create_orders(array $orders, $params = array ()): PromiseInterface {
        return Async\async(function () use ($orders, $params) {
            /**
             * create a list of trade $orders
             *
             * @see https://bybit-exchange.github.io/docs/v5/order/batch-place
             *
             * @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
             * @return {array} an ~@link https://docs.ccxt.com/#/?id=order-structure order structure~
             */
            Async\await($this->load_markets());
            $accounts = Async\await($this->is_unified_enabled());
            $isUta = $accounts[1];
            $ordersRequests = array();
            $orderSymbols = array();
            for ($i = 0; $i < count($orders); $i++) {
                $rawOrder = $orders[$i];
                $marketId = $this->safe_string($rawOrder, 'symbol');
                $orderSymbols[] = $marketId;
                $type = $this->safe_string($rawOrder, 'type');
                $side = $this->safe_string($rawOrder, 'side');
                $amount = $this->safe_value($rawOrder, 'amount');
                $price = $this->safe_value($rawOrder, 'price');
                $orderParams = $this->safe_dict($rawOrder, 'params', array());
                $orderRequest = $this->create_order_request($marketId, $type, $side, $amount, $price, $orderParams, $isUta);
                unset($orderRequest['category']);
                $ordersRequests[] = $orderRequest;
            }
            $symbols = $this->market_symbols($orderSymbols, null, false, true, true);
            $market = $this->market($symbols[0]);
            $unifiedMarginStatus = $this->safe_integer($this->options, 'unifiedMarginStatus', 6);
            $category = null;
            list($category, $params) = $this->get_bybit_type('createOrders', $market, $params);
            if (($category === 'inverse') && ($unifiedMarginStatus < 5)) {
                throw new NotSupported($this->id . ' createOrders does not allow inverse $orders for non UTA2.0 account');
            }
            $request = array(
                'category' => $category,
                'request' => $ordersRequests,
            );
            $response = Async\await($this->privatePostV5OrderCreateBatch ($this->extend($request, $params)));
            $result = $this->safe_dict($response, 'result', array());
            $data = $this->safe_list($result, 'list', array());
            $retInfo = $this->safe_dict($response, 'retExtInfo', array());
            $codes = $this->safe_list($retInfo, 'list', array());
            // extend the error with the unsuccessful $orders
            for ($i = 0; $i < count($codes); $i++) {
                $code = $codes[$i];
                $retCode = $this->safe_integer($code, 'code');
                if ($retCode !== 0) {
                    $data[$i] = $this->extend($data[$i], $code);
                }
            }
            //
            // {
            //     "retCode":0,
            //     "retMsg":"OK",
            //     "result":{
            //        "list":array(
            //           array(
            //              "category":"linear",
            //              "symbol":"LTCUSDT",
            //              "orderId":"",
            //              "orderLinkId":"",
            //              "createAt":""
            //           ),
            //           array(
            //              "category":"linear",
            //              "symbol":"LTCUSDT",
            //              "orderId":"3c9f65b6-01ad-4ac0-9741-df17e02a4223",
            //              "orderLinkId":"",
            //              "createAt":"1698075516029"
            //           }
            //        )
            //     ),
            //     "retExtInfo":{
            //        "list":array(
            //           array(
            //              "code":10001,
            //              "msg":"The number of contracts exceeds maximum limit allowed => too large"
            //           ),
            //           array(
            //              "code":0,
            //              "msg":"OK"
            //           }
            //        )
            //     ),
            //     "time":1698075516029
            // }
            //
            return $this->parse_orders($data);
        }) ();
    }

    public function edit_order_request(string $id, string $symbol, string $type, string $side, ?float $amount = null, ?float $price = null, $params = array ()) {
        $market = $this->market($symbol);
        $request = array(
            'symbol' => $market['id'],
            'orderId' => $id,
            // 'orderLinkId' => 'string', // unique client order $id, max 36 characters
            // 'takeProfit' => 123.45, // take profit $price, only take effect upon opening the position
            // 'stopLoss' => 123.45, // stop loss $price, only take effect upon opening the position
            // 'triggerPrice' => 123.45, // trigger $price, required for conditional orders
            // 'triggerBy' => 'MarkPrice', // IndexPrice, MarkPrice, LastPrice
            // 'tpTriggerby' => 'MarkPrice', // IndexPrice, MarkPrice, LastPrice
            // 'slTriggerBy' => 'MarkPrice', // IndexPrice, MarkPrice, LastPrice
            // Valid for option only.
            // 'orderIv' => '0', // Implied volatility; parameters are passed according to the real value; for example, for 10%, 0.1 is passed
        );
        $category = null;
        list($category, $params) = $this->get_bybit_type('editOrderRequest', $market, $params);
        $request['category'] = $category;
        if ($amount !== null) {
            $request['qty'] = $this->get_amount($symbol, $amount);
        }
        if ($price !== null) {
            $request['price'] = $this->get_price($symbol, $this->number_to_string($price));
        }
        $triggerPrice = $this->safe_string_2($params, 'triggerPrice', 'stopPrice');
        $stopLossTriggerPrice = $this->safe_string($params, 'stopLossPrice');
        $takeProfitTriggerPrice = $this->safe_string($params, 'takeProfitPrice');
        $stopLoss = $this->safe_value($params, 'stopLoss');
        $takeProfit = $this->safe_value($params, 'takeProfit');
        $isStopLossTriggerOrder = $stopLossTriggerPrice !== null;
        $isTakeProfitTriggerOrder = $takeProfitTriggerPrice !== null;
        $isStopLoss = $stopLoss !== null;
        $isTakeProfit = $takeProfit !== null;
        if ($isStopLossTriggerOrder || $isTakeProfitTriggerOrder) {
            $triggerPrice = $isStopLossTriggerOrder ? $stopLossTriggerPrice : $takeProfitTriggerPrice;
        }
        if ($triggerPrice !== null) {
            $triggerPriceRequest = ($triggerPrice === '0') ? $triggerPrice : $this->get_price($symbol, $triggerPrice);
            $request['triggerPrice'] = $triggerPriceRequest;
            $triggerBy = $this->safe_string($params, 'triggerBy', 'LastPrice');
            $request['triggerBy'] = $triggerBy;
        }
        if ($isStopLoss || $isTakeProfit) {
            if ($isStopLoss) {
                $slTriggerPrice = $this->safe_string_2($stopLoss, 'triggerPrice', 'stopPrice', $stopLoss);
                $stopLossRequest = ($slTriggerPrice === '0') ? $slTriggerPrice : $this->get_price($symbol, $slTriggerPrice);
                $request['stopLoss'] = $stopLossRequest;
                $slTriggerBy = $this->safe_string($params, 'slTriggerBy', 'LastPrice');
                $request['slTriggerBy'] = $slTriggerBy;
            }
            if ($isTakeProfit) {
                $tpTriggerPrice = $this->safe_string_2($takeProfit, 'triggerPrice', 'stopPrice', $takeProfit);
                $takeProfitRequest = ($tpTriggerPrice === '0') ? $tpTriggerPrice : $this->get_price($symbol, $tpTriggerPrice);
                $request['takeProfit'] = $takeProfitRequest;
                $tpTriggerBy = $this->safe_string($params, 'tpTriggerBy', 'LastPrice');
                $request['tpTriggerBy'] = $tpTriggerBy;
            }
        }
        $clientOrderId = $this->safe_string($params, 'clientOrderId');
        if ($clientOrderId !== null) {
            $request['orderLinkId'] = $clientOrderId;
        }
        $params = $this->omit($params, array( 'stopPrice', 'stopLossPrice', 'takeProfitPrice', 'triggerPrice', 'clientOrderId', 'stopLoss', 'takeProfit' ));
        return $request;
    }

    public function edit_order(string $id, string $symbol, string $type, string $side, ?float $amount = null, ?float $price = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($id, $symbol, $type, $side, $amount, $price, $params) {
            /**
             * edit a trade order
             *
             * @see https://bybit-exchange.github.io/docs/v5/order/amend-order
             * @see https://bybit-exchange.github.io/docs/derivatives/unified/replace-order
             * @see https://bybit-exchange.github.io/docs/api-explorer/derivatives/trade/contract/replace-order
             *
             * @param {string} $id cancel 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 currency you want to trade in units of base currency
             * @param {float} $price the $price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {float} [$params->triggerPrice] The $price that a trigger order is triggered at
             * @param {float} [$params->stopLossPrice] The $price that a stop loss order is triggered at
             * @param {float} [$params->takeProfitPrice] The $price that a take profit order is triggered at
             * @param {array} [$params->takeProfit] *takeProfit object in $params* containing the triggerPrice that 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 that the attached stop loss order will be triggered
             * @param {float} [$params->stopLoss.triggerPrice] stop loss trigger $price
             * @param {string} [$params->triggerBy] 'IndexPrice', 'MarkPrice' or 'LastPrice', default is 'LastPrice', required if no initial value for triggerPrice
             * @param {string} [$params->slTriggerBy] 'IndexPrice', 'MarkPrice' or 'LastPrice', default is 'LastPrice', required if no initial value for stopLoss
             * @param {string} [$params->tpTriggerby] 'IndexPrice', 'MarkPrice' or 'LastPrice', default is 'LastPrice', required if no initial value for takeProfit
             * @return {array} an ~@link https://docs.ccxt.com/#/?$id=order-structure order structure~
             */
            Async\await($this->load_markets());
            if ($symbol === null) {
                throw new ArgumentsRequired($this->id . ' editOrder() requires a $symbol argument');
            }
            $request = $this->edit_order_request($id, $symbol, $type, $side, $amount, $price, $params);
            $response = Async\await($this->privatePostV5OrderAmend ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "OK",
            //         "result" => array(
            //             "orderId" => "c6f055d9-7f21-4079-913d-e6523a9cfffa",
            //             "orderLinkId" => "linear-004"
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1672217093461
            //     }
            //
            $result = $this->safe_dict($response, 'result', array());
            return $this->safe_order(array(
                'info' => $response,
                'id' => $this->safe_string($result, 'orderId'),
            ));
        }) ();
    }

    public function edit_orders(array $orders, $params = array ()): PromiseInterface {
        return Async\async(function () use ($orders, $params) {
            /**
             * edit a list of trade $orders
             *
             * @see https://bybit-exchange.github.io/docs/v5/order/batch-amend
             *
             * @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
             * @return {array} an ~@link https://docs.ccxt.com/#/?$id=order-structure order structure~
             */
            Async\await($this->load_markets());
            $ordersRequests = array();
            $orderSymbols = array();
            for ($i = 0; $i < count($orders); $i++) {
                $rawOrder = $orders[$i];
                $symbol = $this->safe_string($rawOrder, 'symbol');
                $orderSymbols[] = $symbol;
                $id = $this->safe_string($rawOrder, 'id');
                $type = $this->safe_string($rawOrder, 'type');
                $side = $this->safe_string($rawOrder, 'side');
                $amount = $this->safe_value($rawOrder, 'amount');
                $price = $this->safe_value($rawOrder, 'price');
                $orderParams = $this->safe_dict($rawOrder, 'params', array());
                $orderRequest = $this->edit_order_request($id, $symbol, $type, $side, $amount, $price, $orderParams);
                unset($orderRequest['category']);
                $ordersRequests[] = $orderRequest;
            }
            $orderSymbols = $this->market_symbols($orderSymbols, null, false, true, true);
            $market = $this->market($orderSymbols[0]);
            $unifiedMarginStatus = $this->safe_integer($this->options, 'unifiedMarginStatus', 6);
            $category = null;
            list($category, $params) = $this->get_bybit_type('editOrders', $market, $params);
            if (($category === 'inverse') && ($unifiedMarginStatus < 5)) {
                throw new NotSupported($this->id . ' editOrders does not allow inverse $orders for non UTA2.0 account');
            }
            $request = array(
                'category' => $category,
                'request' => $ordersRequests,
            );
            $response = Async\await($this->privatePostV5OrderAmendBatch ($this->extend($request, $params)));
            $result = $this->safe_dict($response, 'result', array());
            $data = $this->safe_list($result, 'list', array());
            $retInfo = $this->safe_dict($response, 'retExtInfo', array());
            $codes = $this->safe_list($retInfo, 'list', array());
            // extend the error with the unsuccessful $orders
            for ($i = 0; $i < count($codes); $i++) {
                $code = $codes[$i];
                $retCode = $this->safe_integer($code, 'code');
                if ($retCode !== 0) {
                    $data[$i] = $this->extend($data[$i], $code);
                }
            }
            //
            // {
            //     "retCode" => 0,
            //     "retMsg" => "OK",
            //     "result" => {
            //         "list" => array(
            //             array(
            //                 "category" => "option",
            //                 "symbol" => "ETH-30DEC22-500-C",
            //                 "orderId" => "b551f227-7059-4fb5-a6a6-699c04dbd2f2",
            //                 "orderLinkId" => ""
            //             ),
            //             array(
            //                 "category" => "option",
            //                 "symbol" => "ETH-30DEC22-700-C",
            //                 "orderId" => "fa6a595f-1a57-483f-b9d3-30e9c8235a52",
            //                 "orderLinkId" => ""
            //             }
            //         )
            //     ),
            //     "retExtInfo" => {
            //         "list" => array(
            //             array(
            //                 "code" => 0,
            //                 "msg" => "OK"
            //             ),
            //             array(
            //                 "code" => 0,
            //                 "msg" => "OK"
            //             }
            //         )
            //     ),
            //     "time" => 1672222808060
            // }
            //
            return $this->parse_orders($data);
        }) ();
    }

    public function cancel_order_request(string $id, ?string $symbol = null, $params = array ()) {
        $market = $this->market($symbol);
        $request = array(
            'symbol' => $market['id'],
            // 'orderLinkId' => 'string',
            // 'orderId' => $id,
            // conditional orders
            // 'orderFilter' => '', // Valid for spot only. Order,tpslOrder. If not passed, Order by default
        );
        if ($market['spot']) {
            // only works for spot $market
            $isTrigger = $this->safe_bool_2($params, 'stop', 'trigger', false);
            $params = $this->omit($params, array( 'stop', 'trigger' ));
            $request['orderFilter'] = $isTrigger ? 'StopOrder' : 'Order';
        }
        if ($id !== null) { // The user can also use argument $params["orderLinkId"]
            $request['orderId'] = $id;
        }
        $category = null;
        list($category, $params) = $this->get_bybit_type('cancelOrderRequest', $market, $params);
        $request['category'] = $category;
        return $this->extend($request, $params);
    }

    public function cancel_order(string $id, ?string $symbol = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($id, $symbol, $params) {
            /**
             * cancels an open order
             *
             * @see https://bybit-exchange.github.io/docs/v5/order/cancel-order
             *
             * @param {string} $id order $id
             * @param {string} $symbol unified $symbol of the $market the order was made in
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {boolean} [$params->trigger] *spot only* whether the order is a trigger order
             * @param {boolean} [$params->stop] alias for trigger
             * @param {string} [$params->orderFilter] *spot only* 'Order' or 'StopOrder' or 'tpslOrder'
             * @return {array} An ~@link https://docs.ccxt.com/#/?$id=order-structure order structure~
             */
            if ($symbol === null) {
                throw new ArgumentsRequired($this->id . ' cancelOrder() requires a $symbol argument');
            }
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $requestExtended = $this->cancel_order_request($id, $symbol, $params);
            $response = Async\await($this->privatePostV5OrderCancel ($requestExtended));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "OK",
            //         "result" => array(
            //             "orderId" => "c6f055d9-7f21-4079-913d-e6523a9cfffa",
            //             "orderLinkId" => "linear-004"
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1672217377164
            //     }
            //
            $result = $this->safe_dict($response, 'result', array());
            return $this->parse_order($result, $market);
        }) ();
    }

    public function cancel_orders($ids, ?string $symbol = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($ids, $symbol, $params) {
            /**
             * cancel multiple orders
             *
             * @see https://bybit-exchange.github.io/docs/v5/order/batch-cancel
             *
             * @param {string[]} $ids order $ids
             * @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->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');
            }
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $types = Async\await($this->is_unified_enabled());
            $enableUnifiedAccount = $types[1];
            if (!$enableUnifiedAccount) {
                throw new NotSupported($this->id . ' cancelOrders() supports UTA accounts only');
            }
            $category = null;
            list($category, $params) = $this->get_bybit_type('cancelOrders', $market, $params);
            if ($category === 'inverse') {
                throw new NotSupported($this->id . ' cancelOrders does not allow inverse orders');
            }
            $ordersRequests = array();
            $clientOrderIds = $this->safe_list_2($params, 'clientOrderIds', 'clientOids', array());
            $params = $this->omit($params, array( 'clientOrderIds', 'clientOids' ));
            for ($i = 0; $i < count($clientOrderIds); $i++) {
                $ordersRequests[] = array(
                    'symbol' => $market['id'],
                    'orderLinkId' => $this->safe_string($clientOrderIds, $i),
                );
            }
            for ($i = 0; $i < count($ids); $i++) {
                $ordersRequests[] = array(
                    'symbol' => $market['id'],
                    'orderId' => $this->safe_string($ids, $i),
                );
            }
            $request = array(
                'category' => $category,
                'request' => $ordersRequests,
            );
            $response = Async\await($this->privatePostV5OrderCancelBatch ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => "0",
            //         "retMsg" => "OK",
            //         "result" => {
            //             "list" => array(
            //                 array(
            //                     "category" => "spot",
            //                     "symbol" => "BTCUSDT",
            //                     "orderId" => "1636282505818800896",
            //                     "orderLinkId" => "1636282505818800897"
            //                 ),
            //                 array(
            //                     "category" => "spot",
            //                     "symbol" => "BTCUSDT",
            //                     "orderId" => "1636282505818800898",
            //                     "orderLinkId" => "1636282505818800899"
            //                 }
            //             )
            //         ),
            //         "retExtInfo" => {
            //             "list" => array(
            //                 array(
            //                     "code" => "0",
            //                     "msg" => "OK"
            //                 ),
            //                 array(
            //                     "code" => "0",
            //                     "msg" => "OK"
            //                 }
            //             )
            //         ),
            //         "time" => "1709796158501"
            //     }
            //
            $result = $this->safe_dict($response, 'result', array());
            $row = $this->safe_list($result, 'list', array());
            return $this->parse_orders($row, $market);
        }) ();
    }

    public function cancel_all_orders_after(?int $timeout, $params = array ()) {
        return Async\async(function () use ($timeout, $params) {
            /**
             * dead man's switch, cancel all orders after the given $timeout
             *
             * @see https://bybit-exchange.github.io/docs/v5/order/dcp
             *
             * @param {number} $timeout time in milliseconds
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->product] OPTIONS, DERIVATIVES, SPOT, default is 'DERIVATIVES'
             * @return {array} the api result
             */
            Async\await($this->load_markets());
            $request = array(
                'timeWindow' => $this->parse_to_int($timeout / 1000),
            );
            $type = null;
            list($type, $params) = $this->handle_market_type_and_params('cancelAllOrdersAfter', null, $params, 'swap');
            $productMap = array(
                'spot' => 'SPOT',
                'swap' => 'DERIVATIVES',
                'option' => 'OPTIONS',
            );
            $product = $this->safe_string($productMap, $type, $type);
            $request['product'] = $product;
            $response = Async\await($this->privatePostV5OrderDisconnectedCancelAll ($this->extend($request, $params)));
            //
            // {
            //     "retCode" => 0,
            //     "retMsg" => "success"
            // }
            //
            return $response;
        }) ();
    }

    public function cancel_orders_for_symbols(array $orders, $params = array ()) {
        return Async\async(function () use ($orders, $params) {
            /**
             * cancel multiple $orders for multiple symbols
             *
             * @see https://bybit-exchange.github.io/docs/v5/order/batch-cancel
             *
             * @param {CancellationRequest[]} $orders list of $order ids with $symbol, example [array("id" => "a", "symbol" => "BTC/USDT"), array("id" => "b", "symbol" => "ETH/USDT")]
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} an list of ~@link https://docs.ccxt.com/#/?$id=$order-structure $order structures~
             */
            Async\await($this->load_markets());
            $types = Async\await($this->is_unified_enabled());
            $enableUnifiedAccount = $types[1];
            if (!$enableUnifiedAccount) {
                throw new NotSupported($this->id . ' cancelOrdersForSymbols() supports UTA accounts only');
            }
            $ordersRequests = array();
            $category = null;
            for ($i = 0; $i < count($orders); $i++) {
                $order = $orders[$i];
                $symbol = $this->safe_string($order, 'symbol');
                $market = $this->market($symbol);
                $currentCategory = null;
                list($currentCategory, $params) = $this->get_bybit_type('cancelOrders', $market, $params);
                if ($currentCategory === 'inverse') {
                    throw new NotSupported($this->id . ' cancelOrdersForSymbols does not allow inverse orders');
                }
                if (($category !== null) && ($category !== $currentCategory)) {
                    throw new ExchangeError($this->id . ' cancelOrdersForSymbols requires all $orders to be of the same $category (linear, spot or option))');
                }
                $category = $currentCategory;
                $id = $this->safe_string($order, 'id');
                $clientOrderId = $this->safe_string($order, 'clientOrderId');
                $idKey = 'orderId';
                if ($clientOrderId !== null) {
                    $idKey = 'orderLinkId';
                }
                $orderItem = array(
                    'symbol' => $market['id'],
                );
                $orderItem[$idKey] = ($idKey === 'orderId') ? $id : $clientOrderId;
                $ordersRequests[] = $orderItem;
            }
            $request = array(
                'category' => $category,
                'request' => $ordersRequests,
            );
            $response = Async\await($this->privatePostV5OrderCancelBatch ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => "0",
            //         "retMsg" => "OK",
            //         "result" => {
            //             "list" => array(
            //                 array(
            //                     "category" => "spot",
            //                     "symbol" => "BTCUSDT",
            //                     "orderId" => "1636282505818800896",
            //                     "orderLinkId" => "1636282505818800897"
            //                 ),
            //                 array(
            //                     "category" => "spot",
            //                     "symbol" => "BTCUSDT",
            //                     "orderId" => "1636282505818800898",
            //                     "orderLinkId" => "1636282505818800899"
            //                 }
            //             )
            //         ),
            //         "retExtInfo" => {
            //             "list" => array(
            //                 array(
            //                     "code" => "0",
            //                     "msg" => "OK"
            //                 ),
            //                 array(
            //                     "code" => "0",
            //                     "msg" => "OK"
            //                 }
            //             )
            //         ),
            //         "time" => "1709796158501"
            //     }
            //
            $result = $this->safe_dict($response, 'result', array());
            $row = $this->safe_list($result, 'list', array());
            return $this->parse_orders($row, null);
        }) ();
    }

    public function cancel_all_orders(?string $symbol = null, $params = array ()) {
        return Async\async(function () use ($symbol, $params) {
            /**
             * cancel all open $orders
             *
             * @see https://bybit-exchange.github.io/docs/v5/order/cancel-all
             *
             * @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
             * @param {boolean} [$params->trigger] true if trigger order
             * @param {boolean} [$params->stop] alias for trigger
             * @param {string} [$params->type] $market $type, ['swap', 'option', 'spot']
             * @param {string} [$params->subType] $market subType, ['linear', 'inverse']
             * @param {string} [$params->baseCoin] Base coin. Supports linear, inverse & option
             * @param {string} [$params->settleCoin] Settle coin. Supports linear, inverse & option
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
             */
            Async\await($this->load_markets());
            list($enableUnifiedMargin, $enableUnifiedAccount) = Async\await($this->is_unified_enabled());
            $isUnifiedAccount = ($enableUnifiedMargin || $enableUnifiedAccount);
            $market = null;
            $request = array();
            if ($symbol !== null) {
                $market = $this->market($symbol);
                $request['symbol'] = $market['id'];
            }
            $type = null;
            list($type, $params) = $this->get_bybit_type('cancelAllOrders', $market, $params);
            $request['category'] = $type;
            if (($type === 'option') && !$isUnifiedAccount) {
                throw new NotSupported($this->id . ' cancelAllOrders() Normal Account not support ' . $type . ' market');
            }
            if (($type === 'linear') || ($type === 'inverse')) {
                $baseCoin = $this->safe_string($params, 'baseCoin');
                if ($symbol === null && $baseCoin === null) {
                    $defaultSettle = $this->safe_string($this->options, 'defaultSettle', 'USDT');
                    $request['settleCoin'] = $this->safe_string($params, 'settleCoin', $defaultSettle);
                }
            }
            $isTrigger = $this->safe_bool_2($params, 'stop', 'trigger', false);
            $params = $this->omit($params, array( 'stop', 'trigger' ));
            if ($isTrigger) {
                $request['orderFilter'] = 'StopOrder';
            }
            $response = Async\await($this->privatePostV5OrderCancelAll ($this->extend($request, $params)));
            //
            // linear / inverse / option
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "OK",
            //         "result" => {
            //             "list" => array(
            //                 array(
            //                     "orderId" => "f6a73e1f-39b5-4dee-af21-1460b2e3b27c",
            //                     "orderLinkId" => "a001"
            //                 }
            //             )
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1672219780463
            //     }
            //
            // spot
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "OK",
            //         "result" => array(
            //             "success" => "1"
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1676962409398
            //     }
            //
            $result = $this->safe_dict($response, 'result', array());
            $orders = $this->safe_list($result, 'list');
            if (gettype($orders) !== 'array' || array_keys($orders) !== array_keys(array_keys($orders))) {
                return array( $this->safe_order(array( 'info' => $response )) );
            }
            return $this->parse_orders($orders, $market);
        }) ();
    }

    public function fetch_order_classic(string $id, ?string $symbol = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($id, $symbol, $params) {
            /**
             * fetches information on an order made by the user *classic accounts only*
             *
             * @see https://bybit-exchange.github.io/docs/v5/order/order-list
             *
             * @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
             * @return {array} An ~@link https://docs.ccxt.com/#/?$id=order-structure order structure~
             */
            if ($symbol === null) {
                throw new ArgumentsRequired($this->id . ' fetchOrder() requires a $symbol argument');
            }
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            if ($market['spot']) {
                throw new NotSupported($this->id . ' fetchOrder() is not supported for spot markets');
            }
            $request = array(
                'orderId' => $id,
            );
            $result = Async\await($this->fetch_orders($symbol, null, null, $this->extend($request, $params)));
            $length = count($result);
            if ($length === 0) {
                $isTrigger = $this->safe_bool_n($params, array( 'trigger', 'stop' ), false);
                $extra = $isTrigger ? '' : ' If you are trying to fetch SL/TP conditional order, you might try setting $params["trigger"] = true';
                throw new OrderNotFound('Order ' . (string) $id . ' was not found.' . $extra);
            }
            if ($length > 1) {
                throw new InvalidOrder($this->id . ' returned more than one order');
            }
            return $this->safe_value($result, 0);
        }) ();
    }

    public function fetch_order(string $id, ?string $symbol = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($id, $symbol, $params) {
            /**
             *  *classic accounts only/ spot not supported*  fetches information on an $order made by the user *classic accounts only*
             *
             * @see https://bybit-exchange.github.io/docs/v5/order/order-list
             *
             * @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 {array} [$params->acknowledged] to suppress the warning, set to true
             * @return {array} An ~@link https://docs.ccxt.com/#/?$id=$order-structure $order structure~
             */
            Async\await($this->load_markets());
            list($enableUnifiedMargin, $enableUnifiedAccount) = Async\await($this->is_unified_enabled());
            $isUnifiedAccount = ($enableUnifiedMargin || $enableUnifiedAccount);
            if (!$isUnifiedAccount) {
                return Async\await($this->fetch_order_classic($id, $symbol, $params));
            }
            $acknowledge = false;
            list($acknowledge, $params) = $this->handle_option_and_params($params, 'fetchOrder', 'acknowledged');
            if (!$acknowledge) {
                throw new ArgumentsRequired($this->id . ' fetchOrder() can only access an $order if it is in last 500 orders (of any status) for your account. Set $params["acknowledged"] = true to hide this warning. Alternatively, we suggest to use fetchOpenOrder or fetchClosedOrder');
            }
            $market = $this->market($symbol);
            $marketType = null;
            list($marketType, $params) = $this->get_bybit_type('fetchOrder', $market, $params);
            $request = array(
                'symbol' => $market['id'],
                'orderId' => $id,
                'category' => $marketType,
            );
            $isTrigger = null;
            list($isTrigger, $params) = $this->handle_param_bool_2($params, 'trigger', 'stop', false);
            if ($isTrigger) {
                $request['orderFilter'] = 'StopOrder';
            }
            $response = Async\await($this->privateGetV5OrderRealtime ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "OK",
            //         "result" => {
            //             "nextPageCursor" => "1321052653536515584%3A1672217748287%2C1321052653536515584%3A1672217748287",
            //             "category" => "spot",
            //             "list" => array(
            //                 array(
            //                     "symbol" => "ETHUSDT",
            //                     "orderType" => "Limit",
            //                     "orderLinkId" => "1672217748277652",
            //                     "orderId" => "1321052653536515584",
            //                     "cancelType" => "UNKNOWN",
            //                     "avgPrice" => "",
            //                     "stopOrderType" => "tpslOrder",
            //                     "lastPriceOnCreated" => "",
            //                     "orderStatus" => "Cancelled",
            //                     "takeProfit" => "",
            //                     "cumExecValue" => "0",
            //                     "triggerDirection" => 0,
            //                     "isLeverage" => "0",
            //                     "rejectReason" => "",
            //                     "price" => "1000",
            //                     "orderIv" => "",
            //                     "createdTime" => "1672217748287",
            //                     "tpTriggerBy" => "",
            //                     "positionIdx" => 0,
            //                     "timeInForce" => "GTC",
            //                     "leavesValue" => "500",
            //                     "updatedTime" => "1672217748287",
            //                     "side" => "Buy",
            //                     "triggerPrice" => "1500",
            //                     "cumExecFee" => "0",
            //                     "leavesQty" => "0",
            //                     "slTriggerBy" => "",
            //                     "closeOnTrigger" => false,
            //                     "cumExecQty" => "0",
            //                     "reduceOnly" => false,
            //                     "qty" => "0.5",
            //                     "stopLoss" => "",
            //                     "triggerBy" => "1192.5"
            //                 }
            //             )
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1672219526294
            //     }
            //
            $result = $this->safe_dict($response, 'result', array());
            $innerList = $this->safe_list($result, 'list', array());
            if (strlen($innerList) === 0) {
                $extra = $isTrigger ? '' : ' If you are trying to fetch SL/TP conditional $order, you might try setting $params["trigger"] = true';
                throw new OrderNotFound('Order ' . (string) $id . ' was not found.' . $extra);
            }
            $order = $this->safe_dict($innerList, 0, array());
            return $this->parse_order($order, $market);
        }) ();
    }

    public function fetch_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            $res = Async\await($this->is_unified_enabled());
            /**
             * *classic accounts only/ spot not supported* fetches information on multiple orders made by the user *classic accounts only/ spot not supported*
             * @see https://bybit-exchange.github.io/docs/v5/order/order-list
             * @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 {boolean} [$params->trigger] true if trigger order
             * @param {boolean} [$params->stop] alias for trigger
             * @param {string} [$params->type] market type, ['swap', 'option']
             * @param {string} [$params->subType] market subType, ['linear', 'inverse']
             * @param {string} [$params->orderFilter] 'Order' or 'StopOrder' or 'tpslOrder'
             * @param {int} [$params->until] the latest time in ms to fetch entries for
             * @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 {Order[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
             */
            $enableUnifiedAccount = $this->safe_bool($res, 1);
            if ($enableUnifiedAccount) {
                throw new NotSupported($this->id . ' fetchOrders() is not supported after the 5/02 update for UTA accounts, please use fetchOpenOrders, fetchClosedOrders or fetchCanceledOrders');
            }
            return Async\await($this->fetch_orders_classic($symbol, $since, $limit, $params));
        }) ();
    }

    public function fetch_orders_classic(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * fetches information on multiple orders made by the user *classic accounts only*
             *
             * @see https://bybit-exchange.github.io/docs/v5/order/order-list
             *
             * @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 {boolean} [$params->trigger] true if trigger order
             * @param {boolean} [$params->stop] alias for trigger
             * @param {string} [$params->type] $market $type, ['swap', 'option', 'spot']
             * @param {string} [$params->subType] $market subType, ['linear', 'inverse']
             * @param {string} [$params->orderFilter] 'Order' or 'StopOrder' or 'tpslOrder'
             * @param {int} [$params->until] the latest time in ms to fetch entries for
             * @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 {Order[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
             */
            Async\await($this->load_markets());
            $paginate = false;
            list($paginate, $params) = $this->handle_option_and_params($params, 'fetchOrders', 'paginate');
            if ($paginate) {
                return Async\await($this->fetch_paginated_call_cursor('fetchOrders', $symbol, $since, $limit, $params, 'nextPageCursor', 'cursor', null, 50));
            }
            $request = array();
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
                $request['symbol'] = $market['id'];
            }
            $type = null;
            list($type, $params) = $this->get_bybit_type('fetchOrders', $market, $params);
            if ($type === 'spot') {
                throw new NotSupported($this->id . ' fetchOrders() is not supported for spot markets');
            }
            $request['category'] = $type;
            $isTrigger = $this->safe_bool_n($params, array( 'trigger', 'stop' ), false);
            $params = $this->omit($params, array( 'trigger', 'stop' ));
            if ($isTrigger) {
                $request['orderFilter'] = 'StopOrder';
            }
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            if ($since !== null) {
                $request['startTime'] = $since;
            }
            $until = $this->safe_integer($params, 'until'); // unified in milliseconds
            $endTime = $this->safe_integer($params, 'endTime', $until); // exchange-specific in milliseconds
            $params = $this->omit($params, array( 'endTime', 'until' ));
            if ($endTime !== null) {
                $request['endTime'] = $endTime;
            }
            $response = Async\await($this->privateGetV5OrderHistory ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "OK",
            //         "result" => {
            //             "nextPageCursor" => "03234de9-1332-41eb-b805-4a9f42c136a3%3A1672220109387%2C03234de9-1332-41eb-b805-4a9f42c136a3%3A1672220109387",
            //             "category" => "linear",
            //             "list" => array(
            //                 array(
            //                     "symbol" => "BTCUSDT",
            //                     "orderType" => "Limit",
            //                     "orderLinkId" => "test-001",
            //                     "orderId" => "03234de9-1332-41eb-b805-4a9f42c136a3",
            //                     "cancelType" => "CancelByUser",
            //                     "avgPrice" => "0",
            //                     "stopOrderType" => "UNKNOWN",
            //                     "lastPriceOnCreated" => "16656.5",
            //                     "orderStatus" => "Cancelled",
            //                     "takeProfit" => "",
            //                     "cumExecValue" => "0",
            //                     "triggerDirection" => 0,
            //                     "blockTradeId" => "",
            //                     "rejectReason" => "EC_PerCancelRequest",
            //                     "isLeverage" => "",
            //                     "price" => "18000",
            //                     "orderIv" => "",
            //                     "createdTime" => "1672220109387",
            //                     "tpTriggerBy" => "UNKNOWN",
            //                     "positionIdx" => 0,
            //                     "timeInForce" => "GoodTillCancel",
            //                     "leavesValue" => "0",
            //                     "updatedTime" => "1672220114123",
            //                     "side" => "Sell",
            //                     "triggerPrice" => "",
            //                     "cumExecFee" => "0",
            //                     "slTriggerBy" => "UNKNOWN",
            //                     "leavesQty" => "0",
            //                     "closeOnTrigger" => false,
            //                     "cumExecQty" => "0",
            //                     "reduceOnly" => false,
            //                     "qty" => "0.1",
            //                     "stopLoss" => "",
            //                     "triggerBy" => "UNKNOWN"
            //                 }
            //             )
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1672221263862
            //     }
            //
            $data = $this->add_pagination_cursor_to_result($response);
            return $this->parse_orders($data, $market, $since, $limit);
        }) ();
    }

    public function fetch_closed_order(string $id, ?string $symbol = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($id, $symbol, $params) {
            /**
             * fetches information on a closed order made by the user
             *
             * @see https://bybit-exchange.github.io/docs/v5/order/order-list
             *
             * @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 {boolean} [$params->trigger] set to true for fetching a closed trigger order
             * @param {boolean} [$params->stop] alias for trigger
             * @param {string} [$params->type] market type, ['swap', 'option', 'spot']
             * @param {string} [$params->subType] market subType, ['linear', 'inverse']
             * @param {string} [$params->orderFilter] 'Order' or 'StopOrder' or 'tpslOrder'
             * @return {array} an ~@link https://docs.ccxt.com/#/?$id=order-structure order structure~
             */
            Async\await($this->load_markets());
            $request = array(
                'orderId' => $id,
            );
            $result = Async\await($this->fetch_closed_orders($symbol, null, null, $this->extend($request, $params)));
            $length = count($result);
            if ($length === 0) {
                $isTrigger = $this->safe_bool_n($params, array( 'trigger', 'stop' ), false);
                $extra = $isTrigger ? '' : ' If you are trying to fetch SL/TP conditional order, you might try setting $params["trigger"] = true';
                throw new OrderNotFound('Order ' . (string) $id . ' was not found.' . $extra);
            }
            if ($length > 1) {
                throw new InvalidOrder($this->id . ' returned more than one order');
            }
            return $this->safe_value($result, 0);
        }) ();
    }

    public function fetch_open_order(string $id, ?string $symbol = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($id, $symbol, $params) {
            /**
             * fetches information on an open order made by the user
             *
             * @see https://bybit-exchange.github.io/docs/v5/order/open-order
             *
             * @param {string} $id order $id
             * @param {string} [$symbol] unified $symbol of the market the order was made in
             * @param {array} [$params] $extra parameters specific to the exchange API endpoint
             * @param {boolean} [$params->trigger] set to true for fetching an open trigger order
             * @param {boolean} [$params->stop] alias for trigger
             * @param {string} [$params->type] market type, ['swap', 'option', 'spot']
             * @param {string} [$params->subType] market subType, ['linear', 'inverse']
             * @param {string} [$params->baseCoin] Base coin. Supports linear, inverse & option
             * @param {string} [$params->settleCoin] Settle coin. Supports linear, inverse & option
             * @param {string} [$params->orderFilter] 'Order' or 'StopOrder' or 'tpslOrder'
             * @return {array} an ~@link https://docs.ccxt.com/#/?$id=order-structure order structure~
             */
            Async\await($this->load_markets());
            $request = array(
                'orderId' => $id,
            );
            $result = Async\await($this->fetch_open_orders($symbol, null, null, $this->extend($request, $params)));
            $length = count($result);
            if ($length === 0) {
                $isTrigger = $this->safe_bool_n($params, array( 'trigger', 'stop' ), false);
                $extra = $isTrigger ? '' : ' If you are trying to fetch SL/TP conditional order, you might try setting $params["trigger"] = true';
                throw new OrderNotFound('Order ' . (string) $id . ' was not found.' . $extra);
            }
            if ($length > 1) {
                throw new InvalidOrder($this->id . ' returned more than one order');
            }
            return $this->safe_value($result, 0);
        }) ();
    }

    public function fetch_canceled_and_closed_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * fetches information on multiple canceled and closed orders made by the user
             *
             * @see https://bybit-exchange.github.io/docs/v5/order/order-list
             *
             * @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 {boolean} [$params->trigger] set to true for fetching trigger orders
             * @param {boolean} [$params->stop] alias for trigger
             * @param {string} [$params->type] $market $type, ['swap', 'option', 'spot']
             * @param {string} [$params->subType] $market subType, ['linear', 'inverse']
             * @param {string} [$params->orderFilter] 'Order' or 'StopOrder' or 'tpslOrder'
             * @param {int} [$params->until] the latest time in ms to fetch entries for
             * @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 {Order[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
             */
            Async\await($this->load_markets());
            $paginate = false;
            list($paginate, $params) = $this->handle_option_and_params($params, 'fetchCanceledAndClosedOrders', 'paginate');
            if ($paginate) {
                return Async\await($this->fetch_paginated_call_cursor('fetchCanceledAndClosedOrders', $symbol, $since, $limit, $params, 'nextPageCursor', 'cursor', null, 50));
            }
            $request = array();
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
                $request['symbol'] = $market['id'];
            }
            $type = null;
            list($type, $params) = $this->get_bybit_type('fetchCanceledAndClosedOrders', $market, $params);
            $request['category'] = $type;
            $isTrigger = $this->safe_bool_n($params, array( 'trigger', 'stop' ), false);
            $params = $this->omit($params, array( 'trigger', 'stop' ));
            if ($isTrigger) {
                $request['orderFilter'] = 'StopOrder';
            }
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            if ($since !== null) {
                $request['startTime'] = $since;
            }
            $until = $this->safe_integer($params, 'until'); // unified in milliseconds
            $endTime = $this->safe_integer($params, 'endTime', $until); // exchange-specific in milliseconds
            $params = $this->omit($params, array( 'endTime', 'until' ));
            if ($endTime !== null) {
                $request['endTime'] = $endTime;
            }
            $response = Async\await($this->privateGetV5OrderHistory ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "OK",
            //         "result" => array(
            //             "nextPageCursor" => "f5f2d355-9a11-4af3-9b83-aa1d6ab6ddfe%3A1757837618905%2Caee7453a-a100-465f-857a-3db780e9329a%3A1757837580469",
            //             "category" => "linear",
            //             "list" => array(
            //                 array(
            //                     "symbol" => "BTCUSDT",
            //                     "orderType" => "Market",
            //                     "orderLinkId" => "",
            //                     "slLimitPrice" => "0",
            //                     "orderId" => "f5f2d355-9a11-4af3-9b83-aa1d6ab6ddfe",
            //                     "cancelType" => "UNKNOWN",
            //                     "avgPrice" => "122529.9",
            //                     "stopOrderType" => "",
            //                     "lastPriceOnCreated" => "123747.9",
            //                     "orderStatus" => "Filled",
            //                     "createType" => "CreateByUser",
            //                     "takeProfit" => "",
            //                     "cumExecValue" => "122.5299",
            //                     "tpslMode" => "",
            //                     "smpType" => "None",
            //                     "triggerDirection" => 0,
            //                     "blockTradeId" => "",
            //                     "cumFeeDetail" => array(
            //                         "USDT" => "0.06739145"
            //                     ),
            //                     "rejectReason" => "EC_NoError",
            //                     "isLeverage" => "",
            //                     "price" => "120518",
            //                     "orderIv" => "",
            //                     "createdTime" => "1757837618905",
            //                     "tpTriggerBy" => "",
            //                     "positionIdx" => 0,
            //                     "timeInForce" => "IOC",
            //                     "leavesValue" => "0",
            //                     "updatedTime" => "1757837618909",
            //                     "side" => "Sell",
            //                     "smpGroup" => 0,
            //                     "triggerPrice" => "",
            //                     "tpLimitPrice" => "0",
            //                     "cumExecFee" => "0.06739145",
            //                     "slTriggerBy" => "",
            //                     "leavesQty" => "0",
            //                     "closeOnTrigger" => false,
            //                     "slippageToleranceType" => "UNKNOWN",
            //                     "placeType" => "",
            //                     "cumExecQty" => "0.001",
            //                     "reduceOnly" => true,
            //                     "qty" => "0.001",
            //                     "stopLoss" => "",
            //                     "smpOrderId" => "",
            //                     "slippageTolerance" => "0",
            //                     "triggerBy" => "",
            //                     "extraFees" => ""
            //                 ),
            //             )
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1758187806376
            //     }
            //
            $data = $this->add_pagination_cursor_to_result($response);
            return $this->parse_orders($data, $market, $since, $limit);
        }) ();
    }

    public function fetch_closed_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * fetches information on multiple closed orders made by the user
             *
             * @see https://bybit-exchange.github.io/docs/v5/order/order-list
             *
             * @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 {boolean} [$params->trigger] set to true for fetching closed trigger orders
             * @param {boolean} [$params->stop] alias for trigger
             * @param {string} [$params->type] market type, ['swap', 'option', 'spot']
             * @param {string} [$params->subType] market subType, ['linear', 'inverse']
             * @param {string} [$params->orderFilter] 'Order' or 'StopOrder' or 'tpslOrder'
             * @param {int} [$params->until] the latest time in ms to fetch entries for
             * @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 {Order[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
             */
            Async\await($this->load_markets());
            $request = array(
                'orderStatus' => 'Filled',
            );
            return Async\await($this->fetch_canceled_and_closed_orders($symbol, $since, $limit, $this->extend($request, $params)));
        }) ();
    }

    public function fetch_canceled_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * fetches information on multiple canceled orders made by the user
             *
             * @see https://bybit-exchange.github.io/docs/v5/order/order-list
             *
             * @param {string} [$symbol] unified market $symbol of the market orders were made in
             * @param {int} [$since] timestamp in ms of the earliest order, default is null
             * @param {int} [$limit] max number of orders to return, default is null
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {boolean} [$params->trigger] true if trigger order
             * @param {boolean} [$params->stop] alias for trigger
             * @param {string} [$params->type] market type, ['swap', 'option', 'spot']
             * @param {string} [$params->subType] market subType, ['linear', 'inverse']
             * @param {string} [$params->orderFilter] 'Order' or 'StopOrder' or 'tpslOrder'
             * @param {int} [$params->until] the latest time in ms to fetch entries for
             * @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 {array} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
             */
            Async\await($this->load_markets());
            $request = array(
                'orderStatus' => 'Cancelled',
            );
            return Async\await($this->fetch_canceled_and_closed_orders($symbol, $since, $limit, $this->extend($request, $params)));
        }) ();
    }

    public function fetch_open_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * fetch all unfilled currently open orders
             *
             * @see https://bybit-exchange.github.io/docs/v5/order/open-order
             *
             * @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 orders structures to retrieve
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {boolean} [$params->trigger] set to true for fetching open trigger orders
             * @param {boolean} [$params->stop] alias for trigger
             * @param {string} [$params->type] $market $type, ['swap', 'option', 'spot']
             * @param {string} [$params->subType] $market subType, ['linear', 'inverse']
             * @param {string} [$params->baseCoin] Base coin. Supports linear, inverse & option
             * @param {string} [$params->settleCoin] Settle coin. Supports linear, inverse & option
             * @param {string} [$params->orderFilter] 'Order' or 'StopOrder' or 'tpslOrder'
             * @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 {Order[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
             */
            Async\await($this->load_markets());
            $paginate = false;
            list($paginate, $params) = $this->handle_option_and_params($params, 'fetchOpenOrders', 'paginate');
            if ($paginate) {
                return Async\await($this->fetch_paginated_call_cursor('fetchOpenOrders', $symbol, $since, $limit, $params, 'nextPageCursor', 'cursor', null, 50));
            }
            $request = array();
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
                $request['symbol'] = $market['id'];
            }
            $type = null;
            list($type, $params) = $this->get_bybit_type('fetchOpenOrders', $market, $params);
            if ($type === 'linear' || $type === 'inverse') {
                $baseCoin = $this->safe_string($params, 'baseCoin');
                if ($symbol === null && $baseCoin === null) {
                    $defaultSettle = $this->safe_string($this->options, 'defaultSettle', 'USDT');
                    $settleCoin = $this->safe_string($params, 'settleCoin', $defaultSettle);
                    $request['settleCoin'] = $settleCoin;
                }
            }
            $request['category'] = $type;
            $isTrigger = $this->safe_bool_2($params, 'stop', 'trigger', false);
            $params = $this->omit($params, array( 'stop', 'trigger' ));
            if ($isTrigger) {
                $request['orderFilter'] = 'StopOrder';
            }
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            $response = Async\await($this->privateGetV5OrderRealtime ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "OK",
            //         "result" => array(
            //             "nextPageCursor" => "f5f2d355-9a11-4af3-9b83-aa1d6ab6ddfe%3A1757837618905%2Caee7453a-a100-465f-857a-3db780e9329a%3A1757837580469",
            //             "category" => "linear",
            //             "list" => array(
            //                 array(
            //                     "symbol" => "BTCUSDT",
            //                     "orderType" => "Market",
            //                     "orderLinkId" => "",
            //                     "slLimitPrice" => "0",
            //                     "orderId" => "f5f2d355-9a11-4af3-9b83-aa1d6ab6ddfe",
            //                     "cancelType" => "UNKNOWN",
            //                     "avgPrice" => "122529.9",
            //                     "stopOrderType" => "",
            //                     "lastPriceOnCreated" => "123747.9",
            //                     "orderStatus" => "Filled",
            //                     "createType" => "CreateByUser",
            //                     "takeProfit" => "",
            //                     "cumExecValue" => "122.5299",
            //                     "tpslMode" => "",
            //                     "smpType" => "None",
            //                     "triggerDirection" => 0,
            //                     "blockTradeId" => "",
            //                     "cumFeeDetail" => array(
            //                         "USDT" => "0.06739145"
            //                     ),
            //                     "rejectReason" => "EC_NoError",
            //                     "isLeverage" => "",
            //                     "price" => "120518",
            //                     "orderIv" => "",
            //                     "createdTime" => "1757837618905",
            //                     "tpTriggerBy" => "",
            //                     "positionIdx" => 0,
            //                     "timeInForce" => "IOC",
            //                     "leavesValue" => "0",
            //                     "updatedTime" => "1757837618909",
            //                     "side" => "Sell",
            //                     "smpGroup" => 0,
            //                     "triggerPrice" => "",
            //                     "tpLimitPrice" => "0",
            //                     "cumExecFee" => "0.06739145",
            //                     "slTriggerBy" => "",
            //                     "leavesQty" => "0",
            //                     "closeOnTrigger" => false,
            //                     "slippageToleranceType" => "UNKNOWN",
            //                     "placeType" => "",
            //                     "cumExecQty" => "0.001",
            //                     "reduceOnly" => true,
            //                     "qty" => "0.001",
            //                     "stopLoss" => "",
            //                     "smpOrderId" => "",
            //                     "slippageTolerance" => "0",
            //                     "triggerBy" => "",
            //                     "extraFees" => ""
            //                 ),
            //             )
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1758187806376
            //     }
            //
            $data = $this->add_pagination_cursor_to_result($response);
            return $this->parse_orders($data, $market, $since, $limit);
        }) ();
    }

    public function fetch_order_trades(string $id, ?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($id, $symbol, $since, $limit, $params) {
            /**
             * fetch all the trades made from a single order
             *
             * @see https://bybit-exchange.github.io/docs/v5/position/execution
             *
             * @param {string} $id order $id
             * @param {string} $symbol unified market $symbol
             * @param {int} [$since] the earliest time in ms to fetch trades for
             * @param {int} [$limit] the maximum number of trades to retrieve
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?$id=trade-structure trade structures~
             */
            $request = array();
            $clientOrderId = $this->safe_string_2($params, 'clientOrderId', 'orderLinkId');
            if ($clientOrderId !== null) {
                $request['orderLinkId'] = $clientOrderId;
            } else {
                $request['orderId'] = $id;
            }
            $params = $this->omit($params, array( 'clientOrderId', 'orderLinkId' ));
            return Async\await($this->fetch_my_trades($symbol, $since, $limit, $this->extend($request, $params)));
        }) ();
    }

    public function fetch_my_trades(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * fetch all $trades made by the user
             *
             * @see https://bybit-exchange.github.io/docs/api-explorer/v5/position/execution
             *
             * @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 {string} [$params->type] $market $type, ['swap', 'option', 'spot']
             * @param {string} [$params->subType] $market subType, ['linear', 'inverse']
             * @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 {Trade[]} a list of ~@link https://docs.ccxt.com/#/?id=trade-structure trade structures~
             */
            Async\await($this->load_markets());
            $paginate = false;
            list($paginate, $params) = $this->handle_option_and_params($params, 'fetchMyTrades', 'paginate');
            if ($paginate) {
                return Async\await($this->fetch_paginated_call_cursor('fetchMyTrades', $symbol, $since, $limit, $params, 'nextPageCursor', 'cursor', null, 100));
            }
            $request = array(
                'execType' => 'Trade',
            );
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
                $request['symbol'] = $market['id'];
            }
            $type = null;
            list($type, $params) = $this->get_bybit_type('fetchMyTrades', $market, $params);
            $request['category'] = $type;
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            if ($since !== null) {
                $request['startTime'] = $since;
            }
            list($request, $params) = $this->handle_until_option('endTime', $request, $params);
            $response = Async\await($this->privateGetV5ExecutionList ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "OK",
            //         "result" => {
            //             "nextPageCursor" => "132766%3A2%2C132766%3A2",
            //             "category" => "linear",
            //             "list" => array(
            //                 array(
            //                     "symbol" => "ETHPERP",
            //                     "orderType" => "Market",
            //                     "underlyingPrice" => "",
            //                     "orderLinkId" => "",
            //                     "side" => "Buy",
            //                     "indexPrice" => "",
            //                     "orderId" => "8c065341-7b52-4ca9-ac2c-37e31ac55c94",
            //                     "stopOrderType" => "UNKNOWN",
            //                     "leavesQty" => "0",
            //                     "execTime" => "1672282722429",
            //                     "isMaker" => false,
            //                     "execFee" => "0.071409",
            //                     "feeRate" => "0.0006",
            //                     "execId" => "e0cbe81d-0f18-5866-9415-cf319b5dab3b",
            //                     "tradeIv" => "",
            //                     "blockTradeId" => "",
            //                     "markPrice" => "1183.54",
            //                     "execPrice" => "1190.15",
            //                     "markIv" => "",
            //                     "orderQty" => "0.1",
            //                     "orderPrice" => "1236.9",
            //                     "execValue" => "119.015",
            //                     "execType" => "Trade",
            //                     "execQty" => "0.1"
            //                 }
            //             )
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1672283754510
            //     }
            //
            $trades = $this->add_pagination_cursor_to_result($response);
            return $this->parse_trades($trades, $market, $since, $limit);
        }) ();
    }

    public function parse_deposit_address($depositAddress, ?array $currency = null): array {
        //
        //     {
        //         "chainType" => "ERC20",
        //         "addressDeposit" => "0xf56297c6717c1d1c42c30324468ed50a9b7402ee",
        //         "tagDeposit" => '',
        //         "chain" => "ETH"
        //     }
        //
        $address = $this->safe_string($depositAddress, 'addressDeposit');
        $tag = $this->safe_string($depositAddress, 'tagDeposit');
        $code = $this->safe_string($currency, 'code');
        $this->check_address($address);
        return array(
            'info' => $depositAddress,
            'currency' => $code,
            'network' => $this->network_id_to_code($this->safe_string($depositAddress, 'chain'), $code),
            'address' => $address,
            'tag' => $tag,
        );
    }

    public function fetch_deposit_addresses_by_network(string $code, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $params) {
            /**
             * fetch a dictionary of addresses for a $currency, indexed by network
             *
             * @see https://bybit-exchange.github.io/docs/v5/asset/master-deposit-addr
             *
             * @param {string} $code unified $currency $code of the $currency for the deposit address
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a dictionary of ~@link https://docs.ccxt.com/#/?id=address-structure address structures~ indexed by the network
             */
            Async\await($this->load_markets());
            $currency = $this->currency($code);
            $request = array(
                'coin' => $currency['id'],
            );
            $networkCode = null;
            list($networkCode, $params) = $this->handle_network_code_and_params($params);
            if ($networkCode !== null) {
                $request['chainType'] = $this->network_code_to_id($networkCode, $code);
            }
            $response = Async\await($this->privateGetV5AssetDepositQueryAddress ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "success",
            //         "result" => {
            //             "coin" => "USDT",
            //             "chains" => array(
            //                 array(
            //                     "chainType" => "ERC20",
            //                     "addressDeposit" => "0xd9e1cd77afa0e50b452a62fbb68a3340602286c3",
            //                     "tagDeposit" => "",
            //                     "chain" => "ETH"
            //                 }
            //             )
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1672192792860
            //     }
            //
            $result = $this->safe_dict($response, 'result', array());
            $chains = $this->safe_list($result, 'chains', array());
            $coin = $this->safe_string($result, 'coin');
            $currency = $this->currency($coin);
            $parsed = $this->parse_deposit_addresses($chains, [ $currency['code'] ], false, array(
                'currency' => $currency['code'],
            ));
            return $this->index_by($parsed, 'network');
        }) ();
    }

    public function fetch_deposit_address(string $code, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $params) {
            /**
             * fetch the deposit address for a $currency associated with this account
             *
             * @see https://bybit-exchange.github.io/docs/v5/asset/master-deposit-addr
             *
             * @param {string} $code unified $currency $code
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} an ~@link https://docs.ccxt.com/#/?id=address-structure address structure~
             */
            Async\await($this->load_markets());
            $currency = $this->currency($code);
            list($networkCode, $paramsOmited) = $this->handle_network_code_and_params($params);
            $indexedAddresses = Async\await($this->fetch_deposit_addresses_by_network($code, $paramsOmited));
            $selectedNetworkCode = $this->select_network_code_from_unified_networks($currency['code'], $networkCode, $indexedAddresses);
            return $indexedAddresses[$selectedNetworkCode];
        }) ();
    }

    public function fetch_deposits(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $since, $limit, $params) {
            /**
             * fetch all deposits made to an account
             *
             * @see https://bybit-exchange.github.io/docs/v5/asset/deposit-record
             *
             * @param {string} $code unified $currency $code
             * @param {int} [$since] the earliest time in ms to fetch deposits for, default = 30 days before the current time
             * @param {int} [$limit] the maximum number of deposits structures to retrieve, default = 50, max = 50
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {int} [$params->until] the latest time in ms to fetch deposits for, default = 30 days after $since
             * EXCHANGE SPECIFIC PARAMETERS
             * @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)
             * @param {string} [$params->cursor] used for pagination
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=transaction-structure transaction structures~
             */
            Async\await($this->load_markets());
            $paginate = false;
            list($paginate, $params) = $this->handle_option_and_params($params, 'fetchDeposits', 'paginate');
            if ($paginate) {
                return Async\await($this->fetch_paginated_call_cursor('fetchDeposits', $code, $since, $limit, $params, 'nextPageCursor', 'cursor', null, 50));
            }
            $request = array(
                // 'coin' => $currency['id'],
                // 'limit' => 20, // max 50
                // 'cursor' => '',
            );
            $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;
            }
            list($request, $params) = $this->handle_until_option('endTime', $request, $params);
            $response = Async\await($this->privateGetV5AssetDepositQueryRecord ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "success",
            //         "result" => {
            //             "rows" => array(
            //                 array(
            //                     "coin" => "USDT",
            //                     "chain" => "ETH",
            //                     "amount" => "10000",
            //                     "txID" => "skip-notification-scene-test-amount-202212270944-533285-USDT",
            //                     "status" => 3,
            //                     "toAddress" => "test-amount-address",
            //                     "tag" => "",
            //                     "depositFee" => "",
            //                     "successAt" => "1672134274000",
            //                     "confirmations" => "10000",
            //                     "txIndex" => "",
            //                     "blockHash" => ""
            //                 }
            //             ),
            //             "nextPageCursor" => "eyJtaW5JRCI6MTA0NjA0MywibWF4SUQiOjEwNDYwNDN9"
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1672191992512
            //     }
            //
            $data = $this->add_pagination_cursor_to_result($response);
            return $this->parse_transactions($data, $currency, $since, $limit);
        }) ();
    }

    public function fetch_withdrawals(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $since, $limit, $params) {
            /**
             * fetch all withdrawals made from an account
             *
             * @see https://bybit-exchange.github.io/docs/v5/asset/withdraw-record
             *
             * @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
             * @param {int} [$params->until] the latest time in ms to fetch entries for
             * @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=transaction-structure transaction structures~
             */
            Async\await($this->load_markets());
            $paginate = false;
            list($paginate, $params) = $this->handle_option_and_params($params, 'fetchWithdrawals', 'paginate');
            if ($paginate) {
                return Async\await($this->fetch_paginated_call_cursor('fetchWithdrawals', $code, $since, $limit, $params, 'nextPageCursor', 'cursor', null, 50));
            }
            $request = array(
                // 'coin' => $currency['id'],
                // 'limit' => 20, // max 50
                // 'cusor' => '',
            );
            $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;
            }
            list($request, $params) = $this->handle_until_option('endTime', $request, $params);
            $response = Async\await($this->privateGetV5AssetWithdrawQueryRecord ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "success",
            //         "result" => {
            //             "rows" => array(
            //                 array(
            //                     "coin" => "USDT",
            //                     "chain" => "ETH",
            //                     "amount" => "77",
            //                     "txID" => "",
            //                     "status" => "SecurityCheck",
            //                     "toAddress" => "0x99ced129603abc771c0dabe935c326ff6c86645d",
            //                     "tag" => "",
            //                     "withdrawFee" => "10",
            //                     "createTime" => "1670922217000",
            //                     "updateTime" => "1670922217000",
            //                     "withdrawId" => "9976",
            //                     "withdrawType" => 0
            //                 ),
            //                 array(
            //                     "coin" => "USDT",
            //                     "chain" => "ETH",
            //                     "amount" => "26",
            //                     "txID" => "",
            //                     "status" => "success",
            //                     "toAddress" => "15638072681@163.com",
            //                     "tag" => "",
            //                     "withdrawFee" => "0",
            //                     "createTime" => "1669711121000",
            //                     "updateTime" => "1669711380000",
            //                     "withdrawId" => "9801",
            //                     "withdrawType" => 1
            //                 }
            //             ),
            //             "nextPageCursor" => "eyJtaW5JRCI6OTgwMSwibWF4SUQiOjk5NzZ9"
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1672194949928
            //     }
            //
            $data = $this->add_pagination_cursor_to_result($response);
            return $this->parse_transactions($data, $currency, $since, $limit);
        }) ();
    }

    public function parse_transaction_status(?string $status) {
        $statuses = array(
            // v3 deposit $status
            '0' => 'unknown',
            '1' => 'pending',
            '2' => 'processing',
            '3' => 'ok',
            '4' => 'fail',
            // v3 withdrawal $status
            'SecurityCheck' => 'pending',
            'Pending' => 'pending',
            'success' => 'ok',
            'CancelByUser' => 'canceled',
            'Reject' => 'rejected',
            'Fail' => 'failed',
            'BlockchainConfirmed' => 'ok',
        );
        return $this->safe_string($statuses, $status, $status);
    }

    public function parse_transaction(array $transaction, ?array $currency = null): array {
        //
        // fetchWithdrawals
        //
        //     {
        //         "coin" => "USDT",
        //         "chain" => "TRX",
        //         "amount" => "12.34",
        //         "txID" => "de5ea0a2f2e59dc9a714837dd3ddc6d5e151b56ec5d786d351c4f52336f80d3c",
        //         "status" => "success",
        //         "toAddress" => "TQdmFKUoe1Lk2iwZuwRJEHJreTUBoN3BAw",
        //         "tag" => "",
        //         "withdrawFee" => "0.5",
        //         "createTime" => "1665144183000",
        //         "updateTime" => "1665144256000",
        //         "withdrawId" => "8839035"
        //     }
        //
        // fetchDeposits
        //
        //     {
        //         "coin" => "USDT",
        //         "chain" => "TRX",
        //         "amount" => "44",
        //         "txID" => "0b038ea12fa1575e2d66693db3c346b700d4b28347afc39f80321cf089acc960",
        //         "status" => "3",
        //         "toAddress" => "TC6NCAC5WSVCCiaD3kWZXyW91ZKKhLm53b",
        //         "tag" => "",
        //         "depositFee" => "",
        //         "successAt" => "1665142507000",
        //         "confirmations" => "100",
        //         "txIndex" => "0",
        //         "blockHash" => "0000000002ac3b1064aee94bca1bd0b58c4c09c65813b084b87a2063d961129e"
        //     }
        //
        // withdraw
        //
        //     {
        //         "id" => "9377266"
        //     }
        //
        $currencyId = $this->safe_string($transaction, 'coin');
        $code = $this->safe_currency_code($currencyId, $currency);
        $timestamp = $this->safe_integer_2($transaction, 'createTime', 'successAt');
        $updated = $this->safe_integer($transaction, 'updateTime');
        $status = $this->parse_transaction_status($this->safe_string($transaction, 'status'));
        $feeCost = $this->safe_number_2($transaction, 'depositFee', 'withdrawFee');
        $type = (is_array($transaction) && array_key_exists('depositFee', $transaction)) ? 'deposit' : 'withdrawal';
        $fee = null;
        if ($feeCost !== null) {
            $fee = array(
                'cost' => $feeCost,
                'currency' => $code,
            );
        }
        $toAddress = $this->safe_string($transaction, 'toAddress');
        return array(
            'info' => $transaction,
            'id' => $this->safe_string_2($transaction, 'id', 'withdrawId'),
            'txid' => $this->safe_string($transaction, 'txID'),
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'network' => $this->network_id_to_code($this->safe_string($transaction, 'chain')),
            'address' => null,
            'addressTo' => $toAddress,
            'addressFrom' => null,
            'tag' => $this->safe_string($transaction, 'tag'),
            'tagTo' => null,
            'tagFrom' => null,
            'type' => $type,
            'amount' => $this->safe_number($transaction, 'amount'),
            'currency' => $code,
            'status' => $status,
            'updated' => $updated,
            'fee' => $fee,
            'internal' => null,
            'comment' => null,
        );
    }

    public function fetch_ledger(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $since, $limit, $params) {
            /**
             * fetch the history of changes, actions done by the user or operations that altered the balance of the user
             *
             * @see https://bybit-exchange.github.io/docs/v5/account/transaction-log
             * @see https://bybit-exchange.github.io/docs/v5/account/contract-transaction-log
             *
             * @param {string} [$code] unified $currency $code, default is null
             * @param {int} [$since] timestamp in ms of the earliest ledger entry, default is null
             * @param {int} [$limit] max number of ledger entries to return, default is null
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @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)
             * @param {string} [$params->subType] if inverse will use v5/account/contract-transaction-log
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=ledger ledger structure~
             */
            Async\await($this->load_markets());
            $paginate = false;
            list($paginate, $params) = $this->handle_option_and_params($params, 'fetchLedger', 'paginate');
            if ($paginate) {
                return Async\await($this->fetch_paginated_call_cursor('fetchLedger', $code, $since, $limit, $params, 'nextPageCursor', 'cursor', null, 50));
            }
            $request = array(
                // 'coin' => $currency['id'],
                // 'currency' => $currency['id'], // alias
                // 'start_date' => $this->iso8601($since),
                // 'end_date' => $this->iso8601(until),
                // 'wallet_fund_type' => 'Deposit', // Withdraw, RealisedPNL, Commission, Refund, Prize, ExchangeOrderWithdraw, ExchangeOrderDeposit
                // 'page' => 1,
                // 'limit' => 20, // max 50
                // v5 transaction log
                // 'accountType' => '', Account Type. UNIFIED
                // 'category' => '', Product type. spot,linear,option
                // 'currency' => '', Currency
                // 'baseCoin' => '', BaseCoin. e.g., BTC of BTCPERP
                // 'type' => '', Types of transaction logs
                // 'startTime' => 0, The start timestamp (ms)
                // 'endTime' => 0, The end timestamp (ms)
                // 'limit' => 0, Limit for $data size per page. [1, 50]. Default => 20
                // 'cursor' => '', Cursor. Used for pagination
            );
            $enableUnified = Async\await($this->is_unified_enabled());
            $currency = null;
            $currencyKey = 'coin';
            if ($enableUnified[1]) {
                $currencyKey = 'currency';
                if ($since !== null) {
                    $request['startTime'] = $since;
                }
            } else {
                if ($since !== null) {
                    $request['start_date'] = $this->yyyymmdd($since);
                }
            }
            if ($code !== null) {
                $currency = $this->currency($code);
                $request[$currencyKey] = $currency['id'];
            }
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            $subType = null;
            list($subType, $params) = $this->handle_sub_type_and_params('fetchLedger', null, $params);
            $response = null;
            if ($enableUnified[1]) {
                $unifiedMarginStatus = $this->safe_integer($this->options, 'unifiedMarginStatus', 5); // 3/4 uta 1.0, 5/6 uta 2.0
                if ($subType === 'inverse' && ($unifiedMarginStatus < 5)) {
                    $response = Async\await($this->privateGetV5AccountContractTransactionLog ($this->extend($request, $params)));
                } else {
                    $response = Async\await($this->privateGetV5AccountTransactionLog ($this->extend($request, $params)));
                }
            } else {
                $response = Async\await($this->privateGetV5AccountContractTransactionLog ($this->extend($request, $params)));
            }
            //
            //     {
            //         "ret_code" => 0,
            //         "ret_msg" => "ok",
            //         "ext_code" => "",
            //         "result" => {
            //             "data" => array(
            //                 array(
            //                     "id" => 234467,
            //                     "user_id" => 1,
            //                     "coin" => "BTC",
            //                     "wallet_id" => 27913,
            //                     "type" => "Realized P&L",
            //                     "amount" => "-0.00000006",
            //                     "tx_id" => "",
            //                     "address" => "BTCUSD",
            //                     "wallet_balance" => "0.03000330",
            //                     "exec_time" => "2019-12-09T00:00:25.000Z",
            //                     "cross_seq" => 0
            //                 }
            //             )
            //         ),
            //         "ext_info" => null,
            //         "time_now" => "1577481867.115552",
            //         "rate_limit_status" => 119,
            //         "rate_limit_reset_ms" => 1577481867122,
            //         "rate_limit" => 120
            //     }
            //
            // v5 transaction log
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "OK",
            //         "result" => {
            //             "nextPageCursor" => "21963%3A1%2C14954%3A1",
            //             "list" => array(
            //                 array(
            //                     "symbol" => "XRPUSDT",
            //                     "side" => "Buy",
            //                     "funding" => "-0.003676",
            //                     "orderLinkId" => "",
            //                     "orderId" => "1672128000-8-592324-1-2",
            //                     "fee" => "0.00000000",
            //                     "change" => "-0.003676",
            //                     "cashFlow" => "0",
            //                     "transactionTime" => "1672128000000",
            //                     "type" => "SETTLEMENT",
            //                     "feeRate" => "0.0001",
            //                     "size" => "100",
            //                     "qty" => "100",
            //                     "cashBalance" => "5086.55825002",
            //                     "currency" => "USDT",
            //                     "category" => "linear",
            //                     "tradePrice" => "0.3676",
            //                     "tradeId" => "534c0003-4bf7-486f-aa02-78cee36825e4"
            //                 ),
            //                 array(
            //                     "symbol" => "XRPUSDT",
            //                     "side" => "Buy",
            //                     "funding" => "",
            //                     "orderLinkId" => "linear-order",
            //                     "orderId" => "592b7e41-78fd-42e2-9aa3-91e1835ef3e1",
            //                     "fee" => "0.01908720",
            //                     "change" => "-0.0190872",
            //                     "cashFlow" => "0",
            //                     "transactionTime" => "1672121182224",
            //                     "type" => "TRADE",
            //                     "feeRate" => "0.0006",
            //                     "size" => "100",
            //                     "qty" => "88",
            //                     "cashBalance" => "5086.56192602",
            //                     "currency" => "USDT",
            //                     "category" => "linear",
            //                     "tradePrice" => "0.3615",
            //                     "tradeId" => "5184f079-88ec-54c7-8774-5173cafd2b4e"
            //                 ),
            //                 array(
            //                     "symbol" => "XRPUSDT",
            //                     "side" => "Buy",
            //                     "funding" => "",
            //                     "orderLinkId" => "linear-order",
            //                     "orderId" => "592b7e41-78fd-42e2-9aa3-91e1835ef3e1",
            //                     "fee" => "0.00260280",
            //                     "change" => "-0.0026028",
            //                     "cashFlow" => "0",
            //                     "transactionTime" => "1672121182224",
            //                     "type" => "TRADE",
            //                     "feeRate" => "0.0006",
            //                     "size" => "12",
            //                     "qty" => "12",
            //                     "cashBalance" => "5086.58101322",
            //                     "currency" => "USDT",
            //                     "category" => "linear",
            //                     "tradePrice" => "0.3615",
            //                     "tradeId" => "8569c10f-5061-5891-81c4-a54929847eb3"
            //                 }
            //             )
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1672132481405
            //     }
            //
            $data = $this->add_pagination_cursor_to_result($response);
            return $this->parse_ledger($data, $currency, $since, $limit);
        }) ();
    }

    public function parse_ledger_entry(array $item, ?array $currency = null): array {
        //
        //     {
        //         "id" => 234467,
        //         "user_id" => 1,
        //         "coin" => "BTC",
        //         "wallet_id" => 27913,
        //         "type" => "Realized P&L",
        //         "amount" => "-0.00000006",
        //         "tx_id" => "",
        //         "address" => "BTCUSD",
        //         "wallet_balance" => "0.03000330",
        //         "exec_time" => "2019-12-09T00:00:25.000Z",
        //         "cross_seq" => 0
        //     }
        //
        //     {
        //         "symbol" => "XRPUSDT",
        //         "side" => "Buy",
        //         "funding" => "",
        //         "orderLinkId" => "linear-order",
        //         "orderId" => "592b7e41-78fd-42e2-9aa3-91e1835ef3e1",
        //         "fee" => "0.00260280",
        //         "change" => "-0.0026028",
        //         "cashFlow" => "0",
        //         "transactionTime" => "1672121182224",
        //         "type" => "TRADE",
        //         "feeRate" => "0.0006",
        //         "size" => "12",
        //         "qty" => "12",
        //         "cashBalance" => "5086.58101322",
        //         "currency" => "USDT",
        //         "category" => "linear",
        //         "tradePrice" => "0.3615",
        //         "tradeId" => "8569c10f-5061-5891-81c4-a54929847eb3"
        //     }
        //
        $currencyId = $this->safe_string_2($item, 'coin', 'currency');
        $code = $this->safe_currency_code($currencyId, $currency);
        $currency = $this->safe_currency($currencyId, $currency);
        $amountString = $this->safe_string_2($item, 'amount', 'change');
        $afterString = $this->safe_string_2($item, 'wallet_balance', 'cashBalance');
        $direction = Precise::string_lt($amountString, '0') ? 'out' : 'in';
        $before = null;
        $after = null;
        $amount = null;
        if ($afterString !== null && $amountString !== null) {
            $difference = ($direction === 'out') ? $amountString : Precise::string_neg($amountString);
            $before = $this->parse_to_numeric(Precise::string_add($afterString, $difference));
            $after = $this->parse_to_numeric($afterString);
            $amount = $this->parse_to_numeric(Precise::string_abs($amountString));
        }
        $timestamp = $this->parse8601($this->safe_string($item, 'exec_time'));
        if ($timestamp === null) {
            $timestamp = $this->safe_integer($item, 'transactionTime');
        }
        return $this->safe_ledger_entry(array(
            'info' => $item,
            'id' => $this->safe_string($item, 'id'),
            'direction' => $direction,
            'account' => $this->safe_string($item, 'wallet_id'),
            'referenceId' => $this->safe_string($item, 'tx_id'),
            'referenceAccount' => null,
            'type' => $this->parse_ledger_entry_type($this->safe_string($item, 'type')),
            'currency' => $code,
            'amount' => $amount,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'before' => $before,
            'after' => $after,
            'status' => 'ok',
            'fee' => array(
                'currency' => $code,
                'cost' => $this->safe_number($item, 'fee'),
            ),
        ), $currency);
    }

    public function parse_ledger_entry_type($type) {
        $types = array(
            'Deposit' => 'transaction',
            'Withdraw' => 'transaction',
            'RealisedPNL' => 'trade',
            'Commission' => 'fee',
            'Refund' => 'cashback',
            'Prize' => 'prize', // ?
            'ExchangeOrderWithdraw' => 'transaction',
            'ExchangeOrderDeposit' => 'transaction',
            // v5
            'TRANSFER_IN' => 'transaction',
            'TRANSFER_OUT' => 'transaction',
            'TRADE' => 'trade',
            'SETTLEMENT' => 'trade',
            'DELIVERY' => 'trade',
            'LIQUIDATION' => 'trade',
            'BONUS' => 'Prize',
            'FEE_REFUND' => 'cashback',
            'INTEREST' => 'transaction',
            'CURRENCY_BUY' => 'trade',
            'CURRENCY_SELL' => 'trade',
        );
        return $this->safe_string($types, $type, $type);
    }

    public function withdraw(string $code, float $amount, string $address, ?string $tag = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $amount, $address, $tag, $params) {
            /**
             * make a withdrawal
             *
             * @see https://bybit-exchange.github.io/docs/v5/asset/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 {string} [$params->accountType] 'UTA', 'FUND', 'FUND,UTA', and 'SPOT (for classic $accounts only)
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=transaction-structure transaction structure~
             */
            list($tag, $params) = $this->handle_withdraw_tag_and_params($tag, $params);
            $accountType = null;
            $accounts = Async\await($this->is_unified_enabled());
            $isUta = $accounts[1];
            list($accountType, $params) = $this->handle_option_and_params($params, 'withdraw', 'accountType');
            if ($accountType === null) {
                $accountType = $isUta ? 'UTA' : 'SPOT';
            }
            Async\await($this->load_markets());
            $this->check_address($address);
            $currency = $this->currency($code);
            $request = array(
                'coin' => $currency['id'],
                'amount' => $this->number_to_string($amount),
                'address' => $address,
                'timestamp' => $this->milliseconds(),
                'accountType' => $accountType,
            );
            if ($tag !== null) {
                $request['tag'] = $tag;
            }
            list($networkCode, $query) = $this->handle_network_code_and_params($params);
            $networkId = $this->network_code_to_id($networkCode);
            if ($networkId !== null) {
                $request['chain'] = strtoupper($networkId);
            }
            $response = Async\await($this->privatePostV5AssetWithdrawCreate ($this->extend($request, $query)));
            //
            //    {
            //         "retCode" => "0",
            //         "retMsg" => "success",
            //         "result" => array(
            //             "id" => "9377266"
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => "1666892894902"
            //     }
            //
            $result = $this->safe_dict($response, 'result', array());
            return $this->parse_transaction($result, $currency);
        }) ();
    }

    public function fetch_position(string $symbol, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $params) {
            /**
             * fetch data on a single open contract trade $position
             *
             * @see https://bybit-exchange.github.io/docs/v5/position
             *
             * @param {string} $symbol unified $market $symbol of the $market the $position is held in, default is null
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=$position-structure $position structure~
             */
            if ($symbol === null) {
                throw new ArgumentsRequired($this->id . ' fetchPosition() requires a $symbol argument');
            }
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
            );
            $response = null;
            $type = null;
            list($type, $params) = $this->get_bybit_type('fetchPosition', $market, $params);
            $request['category'] = $type;
            $response = Async\await($this->privateGetV5PositionList ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "OK",
            //         "result" => {
            //             "nextPageCursor" => "updateAt%3D1672279322668",
            //             "category" => "linear",
            //             "list" => array(
            //                 array(
            //                     "symbol" => "XRPUSDT",
            //                     "leverage" => "10",
            //                     "avgPrice" => "0.3615",
            //                     "liqPrice" => "0.0001",
            //                     "riskLimitValue" => "200000",
            //                     "takeProfit" => "",
            //                     "positionValue" => "36.15",
            //                     "tpslMode" => "Full",
            //                     "riskId" => 41,
            //                     "trailingStop" => "0",
            //                     "unrealisedPnl" => "-1.83",
            //                     "markPrice" => "0.3432",
            //                     "cumRealisedPnl" => "0.48805876",
            //                     "positionMM" => "0.381021",
            //                     "createdTime" => "1672121182216",
            //                     "positionIdx" => 0,
            //                     "positionIM" => "3.634521",
            //                     "updatedTime" => "1672279322668",
            //                     "side" => "Buy",
            //                     "bustPrice" => "",
            //                     "size" => "100",
            //                     "positionStatus" => "Normal",
            //                     "stopLoss" => "",
            //                     "tradeMode" => 0
            //                 }
            //             )
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1672280219169
            //     }
            //
            $result = $this->safe_dict($response, 'result', array());
            $positions = $this->safe_list_2($result, 'list', 'dataList', array());
            $timestamp = $this->safe_integer($response, 'time');
            $first = $this->safe_dict($positions, 0, array());
            $position = $this->parse_position($first, $market);
            $position['timestamp'] = $timestamp;
            $position['datetime'] = $this->iso8601($timestamp);
            return $position;
        }) ();
    }

    public function fetch_positions(?array $symbols = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbols, $params) {
            /**
             * fetch all open $positions
             *
             * @see https://bybit-exchange.github.io/docs/v5/position
             *
             * @param {string[]} $symbols list of unified $market $symbols
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->type] $market $type, ['swap', 'option', 'spot']
             * @param {string} [$params->subType] $market subType, ['linear', 'inverse']
             * @param {string} [$params->baseCoin] Base coin. Supports linear, inverse & option
             * @param {string} [$params->settleCoin] Settle coin. Supports linear, inverse & option
             * @param {boolean} [$params->paginate] default false, when true will automatically $paginate by calling this endpoint multiple times
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=position-structure position structure~
             */
            Async\await($this->load_markets());
            $paginate = false;
            list($paginate, $params) = $this->handle_option_and_params($params, 'fetchPositions', 'paginate');
            if ($paginate) {
                return Async\await($this->fetch_paginated_call_cursor('fetchPositions', $symbols, null, null, $params, 'nextPageCursor', 'cursor', null, 200));
            }
            $symbol = null;
            if (($symbols !== null) && gettype($symbols) === 'array' && array_keys($symbols) === array_keys(array_keys($symbols))) {
                $symbolsLength = count($symbols);
                if ($symbolsLength > 1) {
                    throw new ArgumentsRequired($this->id . ' fetchPositions() does not accept an array with more than one symbol');
                } elseif ($symbolsLength === 1) {
                    $symbol = $symbols[0];
                }
                $symbols = $this->market_symbols($symbols);
            } elseif ($symbols !== null) {
                $symbol = $symbols;
                $symbols = array( $this->symbol($symbol) );
            }
            $request = array();
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
                $symbol = $market['symbol'];
                $request['symbol'] = $market['id'];
            }
            $type = null;
            list($type, $params) = $this->get_bybit_type('fetchPositions', $market, $params);
            if ($type === 'linear' || $type === 'inverse') {
                $baseCoin = $this->safe_string($params, 'baseCoin');
                if ($type === 'linear') {
                    if ($symbol === null && $baseCoin === null) {
                        $defaultSettle = $this->safe_string($this->options, 'defaultSettle', 'USDT');
                        $settleCoin = $this->safe_string($params, 'settleCoin', $defaultSettle);
                        $request['settleCoin'] = $settleCoin;
                    }
                } else {
                    // inverse
                    if ($symbol === null && $baseCoin === null) {
                        $request['category'] = 'inverse';
                    }
                }
            }
            if ($this->safe_integer($params, 'limit') === null) {
                $request['limit'] = 200; // max limit
            }
            $params = $this->omit($params, array( 'type' ));
            $request['category'] = $type;
            $response = Async\await($this->privateGetV5PositionList ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "Success",
            //         "result" => {
            //             "nextPageCursor" => "0%3A1657711949945%2C0%3A1657711949945",
            //             "category" => "linear",
            //             "list" => array(
            //                 array(
            //                     "symbol" => "ETHUSDT",
            //                     "leverage" => "10",
            //                     "updatedTime" => 1657711949945,
            //                     "side" => "Buy",
            //                     "positionValue" => "536.92500000",
            //                     "takeProfit" => "",
            //                     "tpslMode" => "Full",
            //                     "riskId" => 11,
            //                     "trailingStop" => "",
            //                     "entryPrice" => "1073.85000000",
            //                     "unrealisedPnl" => "",
            //                     "markPrice" => "1080.65000000",
            //                     "size" => "0.5000",
            //                     "positionStatus" => "normal",
            //                     "stopLoss" => "",
            //                     "cumRealisedPnl" => "-0.32215500",
            //                     "positionMM" => "2.97456450",
            //                     "createdTime" => 1657711949928,
            //                     "positionIdx" => 0,
            //                     "positionIM" => "53.98243950"
            //                 }
            //             )
            //         ),
            //         "time" => 1657713693182
            //     }
            //
            $positions = $this->add_pagination_cursor_to_result($response);
            $results = array();
            for ($i = 0; $i < count($positions); $i++) {
                $rawPosition = $positions[$i];
                if ((is_array($rawPosition) && array_key_exists('data', $rawPosition)) && (is_array($rawPosition) && array_key_exists('is_valid', $rawPosition))) {
                    // futures only
                    $rawPosition = $this->safe_dict($rawPosition, 'data');
                }
                $results[] = $this->parse_position($rawPosition);
            }
            return $this->filter_by_array_positions($results, 'symbol', $symbols, false);
        }) ();
    }

    public function parse_position(array $position, ?array $market = null): array {
        //
        // linear swap
        //
        //     {
        //         "positionIdx" => 0,
        //         "riskId" => "11",
        //         "symbol" => "ETHUSDT",
        //         "side" => "Buy",
        //         "size" => "0.10",
        //         "positionValue" => "119.845",
        //         "entryPrice" => "1198.45",
        //         "tradeMode" => 1,
        //         "autoAddMargin" => 0,
        //         "leverage" => "4.2",
        //         "positionBalance" => "28.58931118",
        //         "liqPrice" => "919.10",
        //         "bustPrice" => "913.15",
        //         "takeProfit" => "0.00",
        //         "stopLoss" => "0.00",
        //         "trailingStop" => "0.00",
        //         "unrealisedPnl" => "0.083",
        //         "createdTime" => "1669097244192",
        //         "updatedTime" => "1669413126190",
        //         "tpSlMode" => "Full",
        //         "riskLimitValue" => "900000",
        //         "activePrice" => "0.00"
        //     }
        //
        // usdc
        //    {
        //       "symbol":"BTCPERP",
        //       "leverage":"1.00",
        //       "occClosingFee":"0.0000",
        //       "liqPrice":"",
        //       "positionValue":"30.8100",
        //       "takeProfit":"0.0",
        //       "riskId":"10001",
        //       "trailingStop":"0.0000",
        //       "unrealisedPnl":"0.0000",
        //       "createdAt":"1652451795305",
        //       "markPrice":"30809.41",
        //       "cumRealisedPnl":"0.0000",
        //       "positionMM":"0.1541",
        //       "positionIM":"30.8100",
        //       "updatedAt":"1652451795305",
        //       "tpSLMode":"UNKNOWN",
        //       "side":"Buy",
        //       "bustPrice":"",
        //       "deleverageIndicator":"0",
        //       "entryPrice":"30810.0",
        //       "size":"0.001",
        //       "sessionRPL":"0.0000",
        //       "positionStatus":"NORMAL",
        //       "sessionUPL":"-0.0006",
        //       "stopLoss":"0.0",
        //       "orderMargin":"0.0000",
        //       "sessionAvgPrice":"30810.0"
        //    }
        //
        // unified margin
        //
        //     {
        //         "symbol" => "ETHUSDT",
        //         "leverage" => "10",
        //         "updatedTime" => 1657711949945,
        //         "side" => "Buy",
        //         "positionValue" => "536.92500000",
        //         "takeProfit" => "",
        //         "tpslMode" => "Full",
        //         "riskId" => 11,
        //         "trailingStop" => "",
        //         "entryPrice" => "1073.85000000",
        //         "unrealisedPnl" => "",
        //         "markPrice" => "1080.65000000",
        //         "size" => "0.5000",
        //         "positionStatus" => "normal",
        //         "stopLoss" => "",
        //         "cumRealisedPnl" => "-0.32215500",
        //         "positionMM" => "2.97456450",
        //         "createdTime" => 1657711949928,
        //         "positionIdx" => 0,
        //         "positionIM" => "53.98243950"
        //     }
        //
        // unified account
        //
        //     {
        //         "symbol" => "XRPUSDT",
        //         "leverage" => "10",
        //         "avgPrice" => "0.3615",
        //         "liqPrice" => "0.0001",
        //         "riskLimitValue" => "200000",
        //         "takeProfit" => "",
        //         "positionValue" => "36.15",
        //         "tpslMode" => "Full",
        //         "riskId" => 41,
        //         "trailingStop" => "0",
        //         "unrealisedPnl" => "-1.83",
        //         "markPrice" => "0.3432",
        //         "cumRealisedPnl" => "0.48805876",
        //         "positionMM" => "0.381021",
        //         "createdTime" => "1672121182216",
        //         "positionIdx" => 0,
        //         "positionIM" => "3.634521",
        //         "updatedTime" => "1672279322668",
        //         "side" => "Buy",
        //         "bustPrice" => "",
        //         "size" => "100",
        //         "positionStatus" => "Normal",
        //         "stopLoss" => "",
        //         "tradeMode" => 0
        //     }
        //
        // fetchPositionsHistory
        //
        //    {
        //        symbol => 'XRPUSDT',
        //        orderType => 'Market',
        //        $leverage => '10',
        //        updatedTime => '1712717265572',
        //        $side => 'Sell',
        //        orderId => '071749f3-a9fa-427b-b5ca-27b2f52b81de',
        //        closedPnl => '-0.00049568',
        //        avgEntryPrice => '0.6045',
        //        qty => '3',
        //        cumEntryValue => '1.8135',
        //        createdTime => '1712717265566',
        //        orderPrice => '0.5744',
        //        $closedSize => '3',
        //        avgExitPrice => '0.605',
        //        execType => 'Trade',
        //        fillCount => '1',
        //        cumExitValue => '1.815'
        //    }
        //
        $closedSize = $this->safe_string($position, 'closedSize');
        $isHistory = ($closedSize !== null);
        $contract = $this->safe_string($position, 'symbol');
        $market = $this->safe_market($contract, $market, null, 'contract');
        $size = Precise::string_abs($this->safe_string_2($position, 'size', 'qty'));
        $side = $this->safe_string($position, 'side');
        if ($side !== null) {
            if ($side === 'Buy') {
                $side = $isHistory ? 'short' : 'long';
            } elseif ($side === 'Sell') {
                $side = $isHistory ? 'long' : 'short';
            } else {
                $side = null;
            }
        }
        $notional = $this->safe_string_2($position, 'positionValue', 'cumExitValue');
        $unrealisedPnl = $this->omit_zero($this->safe_string($position, 'unrealisedPnl'));
        $initialMarginString = $this->safe_string_n($position, array( 'positionIM', 'cumEntryValue' ));
        $maintenanceMarginString = $this->safe_string($position, 'positionMM');
        $timestamp = $this->safe_integer_n($position, array( 'createdTime', 'createdAt' ));
        $lastUpdateTimestamp = $this->parse8601($this->safe_string($position, 'updated_at'));
        if ($lastUpdateTimestamp === null) {
            $lastUpdateTimestamp = $this->safe_integer_n($position, array( 'updatedTime', 'updatedAt', 'updatedTime' ));
        }
        $tradeMode = $this->safe_integer($position, 'tradeMode', 0);
        $marginMode = null;
        if ((!$this->options['enableUnifiedAccount']) || ($this->options['enableUnifiedAccount'] && $market['inverse'])) {
            // $tradeMode would work for classic and UTA(inverse)
            if (!$isHistory) {     // cannot tell $marginMode for fetchPositionsHistory, and $closedSize will only be defined for fetchPositionsHistory response
                $marginMode = ($tradeMode === 1) ? 'isolated' : 'cross';
            }
        }
        $collateralString = $this->safe_string($position, 'positionBalance');
        $entryPrice = $this->omit_zero($this->safe_string_n($position, array( 'entryPrice', 'avgPrice', 'avgEntryPrice' )));
        $markPrice = $this->safe_string($position, 'markPrice');
        $liquidationPrice = $this->omit_zero($this->safe_string($position, 'liqPrice'));
        $leverage = $this->safe_string($position, 'leverage');
        if ($liquidationPrice !== null) {
            if ($market['settle'] === 'USDC') {
                //  (Entry $price - Liq $price) * Contracts . Maintenance Margin . (unrealised pnl) = Collateral
                $price = $this->safe_bool($this->options, 'useMarkPriceForPositionCollateral', false) ? $markPrice : $entryPrice;
                $difference = Precise::string_abs(Precise::string_sub($price, $liquidationPrice));
                $collateralString = Precise::string_add(Precise::string_add(Precise::string_mul($difference, $size), $maintenanceMarginString), $unrealisedPnl);
            } else {
                $bustPrice = $this->safe_string($position, 'bustPrice');
                if ($market['linear']) {
                    // derived from the following formulas
                    //  (Entry $price - Bust $price) * Contracts = Collateral
                    //  (Entry $price - Liq $price) * Contracts = Collateral - Maintenance Margin
                    // Maintenance Margin = (Bust $price - Liq $price) x Contracts
                    $maintenanceMarginPriceDifference = Precise::string_abs(Precise::string_sub($liquidationPrice, $bustPrice));
                    $maintenanceMarginString = Precise::string_mul($maintenanceMarginPriceDifference, $size);
                    // Initial Margin = Contracts x Entry Price / Leverage
                    if ($entryPrice !== null) {
                        $initialMarginString = Precise::string_div(Precise::string_mul($size, $entryPrice), $leverage);
                    }
                } else {
                    // Contracts * (1 / Entry $price - 1 / Bust $price) = Collateral
                    // Contracts * (1 / Entry $price - 1 / Liq $price) = Collateral - Maintenance Margin
                    // Maintenance Margin = Contracts * (1 / Liq $price - 1 / Bust $price)
                    // Maintenance Margin = Contracts * (Bust $price - Liq $price) / (Liq $price x Bust $price)
                    $difference = Precise::string_abs(Precise::string_sub($bustPrice, $liquidationPrice));
                    $multiply = Precise::string_mul($bustPrice, $liquidationPrice);
                    $maintenanceMarginString = Precise::string_div(Precise::string_mul($size, $difference), $multiply);
                    // Initial Margin = Leverage x Contracts / EntryPrice
                    if ($entryPrice !== null) {
                        $initialMarginString = Precise::string_div($size, Precise::string_mul($entryPrice, $leverage));
                    }
                }
            }
        }
        $maintenanceMarginPercentage = Precise::string_div($maintenanceMarginString, $notional);
        $marginRatio = Precise::string_div($maintenanceMarginString, $collateralString, 4);
        $positionIdx = $this->safe_string($position, 'positionIdx');
        $hedged = ($positionIdx !== null) && ($positionIdx !== '0');
        return $this->safe_position(array(
            'info' => $position,
            'id' => null,
            'symbol' => $market['symbol'],
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'lastUpdateTimestamp' => $lastUpdateTimestamp,
            'initialMargin' => $this->parse_number($initialMarginString),
            'initialMarginPercentage' => $this->parse_number(Precise::string_div($initialMarginString, $notional)),
            'maintenanceMargin' => $this->parse_number($maintenanceMarginString),
            'maintenanceMarginPercentage' => $this->parse_number($maintenanceMarginPercentage),
            'entryPrice' => $this->parse_number($entryPrice),
            'notional' => $this->parse_number($notional),
            'leverage' => $this->parse_number($leverage),
            'unrealizedPnl' => $this->parse_number($unrealisedPnl),
            'realizedPnl' => $this->safe_number($position, 'closedPnl'),
            'contracts' => $this->parse_number($size), // in USD for inverse swaps
            'contractSize' => $this->safe_number($market, 'contractSize'),
            'marginRatio' => $this->parse_number($marginRatio),
            'liquidationPrice' => $this->parse_number($liquidationPrice),
            'markPrice' => $this->parse_number($markPrice),
            'lastPrice' => $this->safe_number($position, 'avgExitPrice'),
            'collateral' => $this->parse_number($collateralString),
            'marginMode' => $marginMode,
            'side' => $side,
            'percentage' => null,
            'stopLossPrice' => $this->safe_number_2($position, 'stop_loss', 'stopLoss'),
            'takeProfitPrice' => $this->safe_number_2($position, 'take_profit', 'takeProfit'),
            'hedged' => $hedged,
        ));
    }

    public function fetch_leverage(string $symbol, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $params) {
            /**
             * fetch the set leverage for a $market
             *
             * @see https://bybit-exchange.github.io/docs/v5/position
             *
             * @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~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $position = Async\await($this->fetch_position($symbol, $params));
            return $this->parse_leverage($position, $market);
        }) ();
    }

    public function parse_leverage(array $leverage, ?array $market = null): array {
        $marketId = $this->safe_string($leverage, 'symbol');
        $leverageValue = $this->safe_integer($leverage, 'leverage');
        return array(
            'info' => $leverage,
            'symbol' => $this->safe_symbol($marketId, $market),
            'marginMode' => $this->safe_string_lower($leverage, 'marginMode'),
            'longLeverage' => $leverageValue,
            'shortLeverage' => $leverageValue,
        );
    }

    public function set_margin_mode(string $marginMode, ?string $symbol = null, $params = array ()) {
        return Async\async(function () use ($marginMode, $symbol, $params) {
            /**
             * set margin mode (account) or trade mode ($symbol)
             *
             * @see https://bybit-exchange.github.io/docs/v5/account/set-margin-mode
             * @see https://bybit-exchange.github.io/docs/v5/position/cross-isolate
             *
             * @param {string} $marginMode account mode must be either [isolated, cross, portfolio], trade mode must be either [isolated, cross]
             * @param {string} $symbol unified $market $symbol of the $market the position is held in, default is null
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->leverage] the rate of $leverage, is required if setting trade mode ($symbol)
             * @return {array} $response from the exchange
             */
            Async\await($this->load_markets());
            list($enableUnifiedMargin, $enableUnifiedAccount) = Async\await($this->is_unified_enabled());
            $isUnifiedAccount = ($enableUnifiedMargin || $enableUnifiedAccount);
            $market = null;
            $response = null;
            if ($isUnifiedAccount) {
                if ($marginMode === 'isolated') {
                    $marginMode = 'ISOLATED_MARGIN';
                } elseif ($marginMode === 'cross') {
                    $marginMode = 'REGULAR_MARGIN';
                } elseif ($marginMode === 'portfolio') {
                    $marginMode = 'PORTFOLIO_MARGIN';
                } else {
                    throw new NotSupported($this->id . ' setMarginMode() $marginMode must be either [isolated, cross, portfolio]');
                }
                $request = array(
                    'setMarginMode' => $marginMode,
                );
                $response = Async\await($this->privatePostV5AccountSetMarginMode ($this->extend($request, $params)));
            } else {
                if ($symbol === null) {
                    throw new ArgumentsRequired($this->id . ' setMarginMode() requires a $symbol parameter for non unified account');
                }
                $market = $this->market($symbol);
                $isUsdcSettled = $market['settle'] === 'USDC';
                if ($isUsdcSettled) {
                    if ($marginMode === 'cross') {
                        $marginMode = 'REGULAR_MARGIN';
                    } elseif ($marginMode === 'portfolio') {
                        $marginMode = 'PORTFOLIO_MARGIN';
                    } else {
                        throw new NotSupported($this->id . ' setMarginMode() for usdc $market $marginMode must be either [cross, portfolio]');
                    }
                    $request = array(
                        'setMarginMode' => $marginMode,
                    );
                    $response = Async\await($this->privatePostV5AccountSetMarginMode ($this->extend($request, $params)));
                } else {
                    $type = null;
                    list($type, $params) = $this->get_bybit_type('setPositionMode', $market, $params);
                    $tradeMode = null;
                    if ($marginMode === 'cross') {
                        $tradeMode = 0;
                    } elseif ($marginMode === 'isolated') {
                        $tradeMode = 1;
                    } else {
                        throw new NotSupported($this->id . ' setMarginMode() with $symbol $marginMode must be either [isolated, cross]');
                    }
                    $sellLeverage = null;
                    $buyLeverage = null;
                    $leverage = $this->safe_string($params, 'leverage');
                    if ($leverage === null) {
                        $sellLeverage = $this->safe_string_2($params, 'sell_leverage', 'sellLeverage');
                        $buyLeverage = $this->safe_string_2($params, 'buy_leverage', 'buyLeverage');
                        if ($sellLeverage === null && $buyLeverage === null) {
                            throw new ArgumentsRequired($this->id . ' setMarginMode() requires a $leverage parameter or sell_leverage and buy_leverage parameters');
                        }
                        if ($buyLeverage === null) {
                            $buyLeverage = $sellLeverage;
                        }
                        if ($sellLeverage === null) {
                            $sellLeverage = $buyLeverage;
                        }
                        $params = $this->omit($params, array( 'buy_leverage', 'sell_leverage', 'sellLeverage', 'buyLeverage' ));
                    } else {
                        $sellLeverage = $leverage;
                        $buyLeverage = $leverage;
                        $params = $this->omit($params, 'leverage');
                    }
                    $request = array(
                        'category' => $type,
                        'symbol' => $market['id'],
                        'tradeMode' => $tradeMode,
                        'buyLeverage' => $buyLeverage,
                        'sellLeverage' => $sellLeverage,
                    );
                    $response = Async\await($this->privatePostV5PositionSwitchIsolated ($this->extend($request, $params)));
                }
            }
            return $response;
        }) ();
    }

    public function set_leverage(int $leverage, ?string $symbol = null, $params = array ()) {
        return Async\async(function () use ($leverage, $symbol, $params) {
            /**
             * set the level of $leverage for a $market
             *
             * @see https://bybit-exchange.github.io/docs/v5/position/leverage
             *
             * @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->buyLeverage] $leverage for buy side
             * @param {string} [$params->sellLeverage] $leverage for sell side
             * @return {array} $response from the exchange
             */
            if ($symbol === null) {
                throw new ArgumentsRequired($this->id . ' setLeverage() requires a $symbol argument');
            }
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            // WARNING => THIS WILL INCREASE LIQUIDATION PRICE FOR OPEN ISOLATED LONG POSITIONS
            // AND DECREASE LIQUIDATION PRICE FOR OPEN ISOLATED SHORT POSITIONS
            // engage in $leverage setting
            // we reuse the code here instead of having two methods
            $leverageString = $this->number_to_string($leverage);
            $request = array(
                'symbol' => $market['id'],
                'buyLeverage' => $leverageString,
                'sellLeverage' => $leverageString,
            );
            $request['buyLeverage'] = $leverageString;
            $request['sellLeverage'] = $leverageString;
            if ($market['linear']) {
                $request['category'] = 'linear';
            } elseif ($market['inverse']) {
                $request['category'] = 'inverse';
            } else {
                throw new NotSupported($this->id . ' setLeverage() only support linear and inverse market');
            }
            $response = Async\await($this->privatePostV5PositionSetLeverage ($this->extend($request, $params)));
            return $response;
        }) ();
    }

    public function set_position_mode(bool $hedged, ?string $symbol = null, $params = array ()) {
        return Async\async(function () use ($hedged, $symbol, $params) {
            /**
             * set $hedged to true or false for a $market
             *
             * @see https://bybit-exchange.github.io/docs/v5/position/position-$mode
             *
             * @param {bool} $hedged
             * @param {string} $symbol used for unified account with inverse $market
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} $response from the exchange
             */
            Async\await($this->load_markets());
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
            }
            $mode = null;
            if ($hedged) {
                $mode = 3;
            } else {
                $mode = 0;
            }
            $request = array(
                'mode' => $mode,
            );
            if ($symbol === null) {
                $request['coin'] = 'USDT';
            } else {
                $request['symbol'] = $market['id'];
            }
            if ($symbol !== null) {
                $request['category'] = $market['linear'] ? 'linear' : 'inverse';
            } else {
                $type = null;
                list($type, $params) = $this->get_bybit_type('setPositionMode', $market, $params);
                $request['category'] = $type;
            }
            $params = $this->omit($params, 'type');
            $response = Async\await($this->privatePostV5PositionSwitchMode ($this->extend($request, $params)));
            //
            // v5
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "OK",
            //         "result" => array(),
            //         "retExtInfo" => array(),
            //         "time" => 1675249072814
            //     }
            return $response;
        }) ();
    }

    public function fetch_derivatives_open_interest_history(string $symbol, $timeframe = '1h', ?int $since = null, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($symbol, $timeframe, $since, $limit, $params) {
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $subType = $market['linear'] ? 'linear' : 'inverse';
            $category = $this->safe_string($params, 'category', $subType);
            $intervals = $this->safe_dict($this->options, 'intervals');
            $interval = $this->safe_string($intervals, $timeframe); // 5min,15min,30min,1h,4h,1d
            if ($interval === null) {
                throw new BadRequest($this->id . ' fetchOpenInterestHistory() cannot use the ' . $timeframe . ' timeframe');
            }
            $request = array(
                'symbol' => $market['id'],
                'intervalTime' => $interval,
                'category' => $category,
            );
            if ($since !== null) {
                $request['startTime'] = $since;
            }
            $until = $this->safe_integer($params, 'until'); // unified in milliseconds
            $params = $this->omit($params, array( 'until' ));
            if ($until !== null) {
                $request['endTime'] = $until;
            }
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            $response = Async\await($this->publicGetV5MarketOpenInterest ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "OK",
            //         "result" => {
            //             "symbol" => "BTCUSD",
            //             "category" => "inverse",
            //             "list" => array(
            //                 array(
            //                     "openInterest" => "461134384.00000000",
            //                     "timestamp" => "1669571400000"
            //                 ),
            //                 array(
            //                     "openInterest" => "461134292.00000000",
            //                     "timestamp" => "1669571100000"
            //                 }
            //             ),
            //             "nextPageCursor" => ""
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1672053548579
            //     }
            //
            $result = $this->safe_dict($response, 'result', array());
            $data = $this->add_pagination_cursor_to_result($response);
            $id = $this->safe_string($result, 'symbol');
            $market = $this->safe_market($id, $market, null, 'contract');
            return $this->parse_open_interests_history($data, $market, $since, $limit);
        }) ();
    }

    public function fetch_open_interest(string $symbol, $params = array ()) {
        return Async\async(function () use ($symbol, $params) {
            /**
             * Retrieves the open interest of a derivative trading pair
             *
             * @see https://bybit-exchange.github.io/docs/v5/market/open-interest
             *
             * @param {string} $symbol Unified CCXT $market $symbol
             * @param {array} [$params] exchange specific parameters
             * @param {string} [$params->interval] 5m, 15m, 30m, 1h, 4h, 1d
             * @param {string} [$params->category] "linear" or "inverse"
             * @return {array} an open interest structurearray(@link https://docs.ccxt.com/#/?$id=open-interest-structure)
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            if (!$market['contract']) {
                throw new BadRequest($this->id . ' fetchOpenInterest() supports contract markets only');
            }
            $timeframe = $this->safe_string($params, 'interval', '1h');
            $intervals = $this->safe_dict($this->options, 'intervals');
            $interval = $this->safe_string($intervals, $timeframe); // 5min,15min,30min,1h,4h,1d
            if ($interval === null) {
                throw new BadRequest($this->id . ' fetchOpenInterest() cannot use the ' . $timeframe . ' timeframe');
            }
            $subType = $market['linear'] ? 'linear' : 'inverse';
            $category = $this->safe_string($params, 'category', $subType);
            $request = array(
                'symbol' => $market['id'],
                'intervalTime' => $interval,
                'category' => $category,
            );
            $response = Async\await($this->publicGetV5MarketOpenInterest ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "OK",
            //         "result" => {
            //             "symbol" => "BTCUSD",
            //             "category" => "inverse",
            //             "list" => array(
            //                 array(
            //                     "openInterest" => "461134384.00000000",
            //                     "timestamp" => "1669571400000"
            //                 ),
            //                 array(
            //                     "openInterest" => "461134292.00000000",
            //                     "timestamp" => "1669571100000"
            //                 }
            //             ),
            //             "nextPageCursor" => ""
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1672053548579
            //     }
            //
            $result = $this->safe_dict($response, 'result', array());
            $id = $this->safe_string($result, 'symbol');
            $market = $this->safe_market($id, $market, null, 'contract');
            $data = $this->add_pagination_cursor_to_result($response);
            return $this->parse_open_interest($data[0], $market);
        }) ();
    }

    public function fetch_open_interest_history(string $symbol, $timeframe = '1h', ?int $since = null, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($symbol, $timeframe, $since, $limit, $params) {
            /**
             * Gets the total amount of unsettled contracts. In other words, the total number of contracts held in open positions
             *
             * @see https://bybit-exchange.github.io/docs/v5/market/open-interest
             *
             * @param {string} $symbol Unified $market $symbol
             * @param {string} $timeframe "5m", 15m, 30m, 1h, 4h, 1d
             * @param {int} [$since] Not used by Bybit
             * @param {int} [$limit] The number of open interest structures to return. Max 200, default 50
             * @param {array} [$params] Exchange specific parameters
             * @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 An array of open interest structures
             */
            if ($timeframe === '1m') {
                throw new BadRequest($this->id . ' fetchOpenInterestHistory cannot use the 1m timeframe');
            }
            Async\await($this->load_markets());
            $paginate = $this->safe_bool($params, 'paginate');
            if ($paginate) {
                $params = $this->omit($params, 'paginate');
                $params['timeframe'] = $timeframe;
                return Async\await($this->fetch_paginated_call_cursor('fetchOpenInterestHistory', $symbol, $since, $limit, $params, 'nextPageCursor', 'cursor', null, 200));
            }
            $market = $this->market($symbol);
            if ($market['spot'] || $market['option']) {
                throw new BadRequest($this->id . ' fetchOpenInterestHistory() $symbol does not support $market ' . $symbol);
            }
            $request = array(
                'symbol' => $market['id'],
            );
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            return Async\await($this->fetch_derivatives_open_interest_history($symbol, $timeframe, $since, $limit, $params));
        }) ();
    }

    public function parse_open_interest($interest, ?array $market = null) {
        //
        //    {
        //        "openInterest" => 64757.62400000,
        //        "timestamp" => 1665784800000,
        //    }
        //
        $timestamp = $this->safe_integer($interest, 'timestamp');
        $openInterest = $this->safe_number_2($interest, 'open_interest', 'openInterest');
        // the $openInterest is in the base asset for linear and quote asset for inverse
        $amount = $market['linear'] ? $openInterest : null;
        $value = $market['inverse'] ? $openInterest : null;
        return $this->safe_open_interest(array(
            'symbol' => $market['symbol'],
            'openInterestAmount' => $amount,
            'openInterestValue' => $value,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'info' => $interest,
        ), $market);
    }

    public function fetch_cross_borrow_rate(string $code, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $params) {
            /**
             * fetch the rate of interest to borrow a $currency for margin trading
             *
             * @see https://bybit-exchange.github.io/docs/zh-TW/v5/spot-margin-normal/interest-quota
             *
             * @param {string} $code unified $currency $code
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=borrow-rate-structure borrow rate structure~
             */
            Async\await($this->load_markets());
            $currency = $this->currency($code);
            $request = array(
                'coin' => $currency['id'],
            );
            $response = Async\await($this->privateGetV5SpotCrossMarginTradeLoanInfo ($this->extend($request, $params)));
            //
            //    {
            //         "retCode" => "0",
            //         "retMsg" => "success",
            //         "result" => array(
            //             "coin" => "USDT",
            //             "interestRate" => "0.000107000000",
            //             "loanAbleAmount" => "",
            //             "maxLoanAmount" => "79999.999"
            //         ),
            //         "retExtInfo" => null,
            //         "time" => "1666734490778"
            //     }
            //
            $timestamp = $this->safe_integer($response, 'time');
            $data = $this->safe_dict($response, 'result', array());
            $data['timestamp'] = $timestamp;
            return $this->parse_borrow_rate($data, $currency);
        }) ();
    }

    public function parse_borrow_rate($info, ?array $currency = null) {
        //
        //     {
        //         "coin" => "USDT",
        //         "interestRate" => "0.000107000000",
        //         "loanAbleAmount" => "",
        //         "maxLoanAmount" => "79999.999",
        //         "timestamp" => 1666734490778
        //     }
        //
        // fetchBorrowRateHistory
        //     {
        //         "timestamp" => 1721469600000,
        //         "currency" => "USDC",
        //         "hourlyBorrowRate" => "0.000014621596",
        //         "vipLevel" => "No VIP"
        //     }
        //
        $timestamp = $this->safe_integer($info, 'timestamp');
        $currencyId = $this->safe_string_2($info, 'coin', 'currency');
        $hourlyBorrowRate = $this->safe_number($info, 'hourlyBorrowRate');
        $period = ($hourlyBorrowRate !== null) ? 3600000 : 86400000; // 1h or 1d
        return array(
            'currency' => $this->safe_currency_code($currencyId, $currency),
            'rate' => $this->safe_number($info, 'interestRate', $hourlyBorrowRate),
            'period' => $period, // Daily
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'info' => $info,
        );
    }

    public function fetch_borrow_interest(?string $code = null, ?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $symbol, $since, $limit, $params) {
            /**
             * fetch the $interest owed by the user for borrowing currency for margin trading
             *
             * @see https://bybit-exchange.github.io/docs/zh-TW/v5/spot-margin-normal/account-info
             *
             * @param {string} $code unified currency $code
             * @param {string} $symbol unified market $symbol when fetch $interest in isolated markets
             * @param {number} [$since] the earliest time in ms to fetch borrrow $interest for
             * @param {number} [$limit] the maximum number of 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=borrow-$interest-structure borrow $interest structures~
             */
            Async\await($this->load_markets());
            $request = array();
            $response = Async\await($this->privateGetV5SpotCrossMarginTradeAccount ($this->extend($request, $params)));
            //
            //     {
            //         "ret_code" => 0,
            //         "ret_msg" => "",
            //         "ext_code" => null,
            //         "ext_info" => null,
            //         "result" => {
            //             "status" => "1",
            //             "riskRate" => "0",
            //             "acctBalanceSum" => "0.000486213817680857",
            //             "debtBalanceSum" => "0",
            //             "loanAccountList" => array(
            //                 array(
            //                     "tokenId" => "BTC",
            //                     "total" => "0.00048621",
            //                     "locked" => "0",
            //                     "loan" => "0",
            //                     "interest" => "0",
            //                     "free" => "0.00048621"
            //                 ),
            //                 ...
            //             )
            //         }
            //     }
            //
            $data = $this->safe_dict($response, 'result', array());
            $rows = $this->safe_list($data, 'loanAccountList', array());
            $interest = $this->parse_borrow_interests($rows, null);
            return $this->filter_by_currency_since_limit($interest, $code, $since, $limit);
        }) ();
    }

    public function fetch_borrow_rate_history(string $code, ?int $since = null, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($code, $since, $limit, $params) {
            /**
             * retrieves a history of a currencies borrow interest rate at specific time slots
             *
             * @see https://bybit-exchange.github.io/docs/v5/spot-margin-uta/historical-interest
             *
             * @param {string} $code unified $currency $code
             * @param {int} [$since] timestamp for the earliest borrow rate
             * @param {int} [$limit] the maximum number of ~@link https://docs.ccxt.com/#/?id=borrow-rate-structure borrow rate 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
             * @return {array[]} an array of ~@link https://docs.ccxt.com/#/?id=borrow-rate-structure borrow rate structures~
             */
            Async\await($this->load_markets());
            $currency = $this->currency($code);
            $request = array(
                'currency' => $currency['id'],
            );
            if ($since === null) {
                $since = $this->milliseconds() - 86400000 * 30; // last 30 days
            }
            $request['startTime'] = $since;
            $endTime = $this->safe_integer_2($params, 'until', 'endTime');
            $params = $this->omit($params, array( 'until' ));
            if ($endTime === null) {
                $endTime = $since + 86400000 * 30; // $since + 30 days
            }
            $request['endTime'] = $endTime;
            $response = Async\await($this->privateGetV5SpotMarginTradeInterestRateHistory ($this->extend($request, $params)));
            //
            //   {
            //       "retCode" => 0,
            //       "retMsg" => "OK",
            //       "result" => {
            //           "list" => array(
            //               array(
            //                   "timestamp" => 1721469600000,
            //                   "currency" => "USDC",
            //                   "hourlyBorrowRate" => "0.000014621596",
            //                   "vipLevel" => "No VIP"
            //               }
            //           )
            //       ),
            //       "retExtInfo" => "array()",
            //       "time" => 1721899048991
            //   }
            //
            $data = $this->safe_dict($response, 'result');
            $rows = $this->safe_list($data, 'list', array());
            return $this->parse_borrow_rate_history($rows, $code, $since, $limit);
        }) ();
    }

    public function parse_borrow_interest(array $info, ?array $market = null): array {
        //
        //     array(
        //         "tokenId" => "BTC",
        //         "total" => "0.00048621",
        //         "locked" => "0",
        //         "loan" => "0",
        //         "interest" => "0",
        //         "free" => "0.00048621"
        //     ),
        //
        return array(
            'info' => $info,
            'symbol' => null,
            'currency' => $this->safe_currency_code($this->safe_string($info, 'tokenId')),
            'interest' => $this->safe_number($info, 'interest'),
            'interestRate' => null,
            'amountBorrowed' => $this->safe_number($info, 'loan'),
            'marginMode' => 'cross',
            'timestamp' => null,
            'datetime' => null,
        );
    }

    public function transfer(string $code, float $amount, string $fromAccount, string $toAccount, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $amount, $fromAccount, $toAccount, $params) {
            /**
             * $transfer $currency internally between wallets on the same account
             *
             * @see https://bybit-exchange.github.io/docs/v5/asset/create-inter-$transfer
             *
             * @param {string} $code unified $currency $code
             * @param {float} $amount amount to $transfer
             * @param {string} $fromAccount account to $transfer from
             * @param {string} $toAccount account to $transfer to
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->transferId] UUID, which is unique across the platform
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=$transfer-structure $transfer structure~
             */
            Async\await($this->load_markets());
            $transferId = $this->safe_string($params, 'transferId', $this->uuid());
            $accountTypes = $this->safe_dict($this->options, 'accountsByType', array());
            $fromId = $this->safe_string($accountTypes, $fromAccount, $fromAccount);
            $toId = $this->safe_string($accountTypes, $toAccount, $toAccount);
            $currency = $this->currency($code);
            $amountToPrecision = $this->currency_to_precision($code, $amount);
            $request = array(
                'transferId' => $transferId,
                'fromAccountType' => $fromId,
                'toAccountType' => $toId,
                'coin' => $currency['id'],
                'amount' => $amountToPrecision,
            );
            $response = Async\await($this->privatePostV5AssetTransferInterTransfer ($this->extend($request, $params)));
            //
            // {
            //     "retCode" => 0,
            //     "retMsg" => "success",
            //     "result" => array(
            //         "transferId" => "4244af44-f3b0-4cf6-a743-b56560e987bc"
            //     ),
            //     "retExtInfo" => array(),
            //     "time" => 1666875857205
            // }
            //
            $timestamp = $this->safe_integer($response, 'time');
            $transfer = $this->safe_dict($response, 'result', array());
            $statusRaw = $this->safe_string_n($response, array( 'retCode', 'retMsg' ));
            $status = $this->parse_transfer_status($statusRaw);
            return $this->extend($this->parse_transfer($transfer, $currency), array(
                'timestamp' => $timestamp,
                'datetime' => $this->iso8601($timestamp),
                'amount' => $this->parse_number($amountToPrecision),
                'fromAccount' => $fromAccount,
                'toAccount' => $toAccount,
                'status' => $status,
            ));
        }) ();
    }

    public function fetch_transfers(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $since, $limit, $params) {
            /**
             * fetch a history of internal transfers made on an account
             *
             * @see https://bybit-exchange.github.io/docs/v5/asset/inter-transfer-list
             *
             * @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 transfer 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 {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=transfer-structure transfer structures~
             */
            Async\await($this->load_markets());
            $paginate = false;
            list($paginate, $params) = $this->handle_option_and_params($params, 'fetchTransfers', 'paginate');
            if ($paginate) {
                return Async\await($this->fetch_paginated_call_cursor('fetchTransfers', $code, $since, $limit, $params, 'nextPageCursor', 'cursor', null, 50));
            }
            $currency = null;
            $request = array();
            if ($code !== null) {
                $currency = $this->safe_currency($code);
                $request['coin'] = $currency['id'];
            }
            if ($since !== null) {
                $request['startTime'] = $since;
            }
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            list($request, $params) = $this->handle_until_option('endTime', $request, $params);
            $response = Async\await($this->privateGetV5AssetTransferQueryInterTransferList ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "success",
            //         "result" => {
            //             "list" => array(
            //                 array(
            //                     "transferId" => "selfTransfer_a1091cc7-9364-4b74-8de1-18f02c6f2d5c",
            //                     "coin" => "USDT",
            //                     "amount" => "5000",
            //                     "fromAccountType" => "SPOT",
            //                     "toAccountType" => "UNIFIED",
            //                     "timestamp" => "1667283263000",
            //                     "status" => "SUCCESS"
            //                 }
            //             ),
            //             "nextPageCursor" => "eyJtaW5JRCI6MTM1ODQ2OCwibWF4SUQiOjEzNTg0Njh9"
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1670988271677
            //     }
            //
            $data = $this->add_pagination_cursor_to_result($response);
            return $this->parse_transfers($data, $currency, $since, $limit);
        }) ();
    }

    public function borrow_cross_margin(string $code, float $amount, $params = array ()) {
        return Async\async(function () use ($code, $amount, $params) {
            /**
             * create a loan to borrow margin
             *
             * @see https://bybit-exchange.github.io/docs/v5/spot-margin-normal/borrow
             *
             * @param {string} $code unified $currency $code of the $currency to borrow
             * @param {float} $amount the $amount to borrow
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=margin-loan-structure margin loan structure~
             */
            Async\await($this->load_markets());
            $currency = $this->currency($code);
            $request = array(
                'coin' => $currency['id'],
                'qty' => $this->currency_to_precision($code, $amount),
            );
            $response = Async\await($this->privatePostV5SpotCrossMarginTradeLoan ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "success",
            //         "result" => array(
            //             "transactId" => "14143"
            //         ),
            //         "retExtInfo" => null,
            //         "time" => 1662617848970
            //     }
            //
            $result = $this->safe_dict($response, 'result', array());
            $transaction = $this->parse_margin_loan($result, $currency);
            return $this->extend($transaction, array(
                'symbol' => null,
                'amount' => $amount,
            ));
        }) ();
    }

    public function repay_cross_margin(string $code, $amount, $params = array ()) {
        return Async\async(function () use ($code, $amount, $params) {
            /**
             * repay borrowed margin and interest
             *
             * @see https://bybit-exchange.github.io/docs/v5/spot-margin-normal/repay
             *
             * @param {string} $code unified $currency $code of the $currency to repay
             * @param {float} $amount the $amount to repay
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=margin-loan-structure margin loan structure~
             */
            Async\await($this->load_markets());
            $currency = $this->currency($code);
            $request = array(
                'coin' => $currency['id'],
                'qty' => $this->number_to_string($amount),
            );
            $response = Async\await($this->privatePostV5SpotCrossMarginTradeRepay ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "success",
            //         "result" => array(
            //            "repayId" => "12128"
            //         ),
            //         "retExtInfo" => null,
            //         "time" => 1662618298452
            //     }
            //
            $result = $this->safe_dict($response, 'result', array());
            $transaction = $this->parse_margin_loan($result, $currency);
            return $this->extend($transaction, array(
                'symbol' => null,
                'amount' => $amount,
            ));
        }) ();
    }

    public function parse_margin_loan($info, ?array $currency = null): array {
        //
        // borrowCrossMargin
        //
        //     {
        //         "transactId" => "14143"
        //     }
        //
        // repayCrossMargin
        //
        //     {
        //         "repayId" => "12128"
        //     }
        //
        return array(
            'id' => $this->safe_string_2($info, 'transactId', 'repayId'),
            'currency' => $this->safe_string($currency, 'code'),
            'amount' => null,
            'symbol' => null,
            'timestamp' => null,
            'datetime' => null,
            'info' => $info,
        );
    }

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

    public function parse_transfer(array $transfer, ?array $currency = null): array {
        //
        // $transfer
        //
        //     {
        //         "transferId" => "22c2bc11-ed5b-49a4-8647-c4e0f5f6f2b2"
        //     }
        //
        // fetchTransfers
        //
        //     {
        //         "transferId" => "e9c421c4-b010-4b16-abd6-106179f27702",
        //         "coin" => "USDT",
        //         "amount" => "8",
        //         "fromAccountType" => "FUND",
        //         "toAccountType" => "SPOT",
        //         "timestamp" => "1666879426000",
        //         "status" => "SUCCESS"
        //      }
        //
        $currencyId = $this->safe_string($transfer, 'coin');
        $timestamp = $this->safe_integer($transfer, 'timestamp');
        $fromAccountId = $this->safe_string($transfer, 'fromAccountType');
        $toAccountId = $this->safe_string($transfer, 'toAccountType');
        $accountIds = $this->safe_dict($this->options, 'accountsById', array());
        $fromAccount = $this->safe_string($accountIds, $fromAccountId, $fromAccountId);
        $toAccount = $this->safe_string($accountIds, $toAccountId, $toAccountId);
        return array(
            'info' => $transfer,
            'id' => $this->safe_string($transfer, 'transferId'),
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'currency' => $this->safe_currency_code($currencyId, $currency),
            'amount' => $this->safe_number($transfer, 'amount'),
            'fromAccount' => $fromAccount,
            'toAccount' => $toAccount,
            'status' => $this->parse_transfer_status($this->safe_string($transfer, 'status')),
        );
    }

    public function fetch_derivatives_market_leverage_tiers(string $symbol, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $params) {
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
            );
            if ($market['linear']) {
                $request['category'] = 'linear';
            } elseif ($market['inverse']) {
                $request['category'] = 'inverse';
            }
            $response = Async\await($this->publicGetV5MarketRiskLimit ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "OK",
            //         "result" => array(
            //             "category" => "inverse",
            //             "list" => array(
            //                 array(
            //                     "id" => 1,
            //                     "symbol" => "BTCUSD",
            //                     "riskLimitValue" => "150",
            //                     "maintenanceMargin" => "0.5",
            //                     "initialMargin" => "1",
            //                     "isLowestRisk" => 1,
            //                     "maxLeverage" => "100.00"
            //                 ),
            //             ....
            //             )
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1672054488010
            //     }
            //
            $result = $this->safe_dict($response, 'result');
            $tiers = $this->safe_list($result, 'list');
            return $this->parse_market_leverage_tiers($tiers, $market);
        }) ();
    }

    public function fetch_market_leverage_tiers(string $symbol, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $params) {
            /**
             * retrieve information on the maximum leverage, and maintenance margin for trades of varying trade sizes for a single $market
             *
             * @see https://bybit-exchange.github.io/docs/v5/market/risk-limit
             *
             * @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-tiers-structure leverage tiers structure~
             */
            Async\await($this->load_markets());
            $request = array();
            $market = null;
            $market = $this->market($symbol);
            if ($market['spot'] || $market['option']) {
                throw new BadRequest($this->id . ' fetchMarketLeverageTiers() $symbol does not support $market ' . $symbol);
            }
            $request['symbol'] = $market['id'];
            return Async\await($this->fetch_derivatives_market_leverage_tiers($symbol, $params));
        }) ();
    }

    public function parse_trading_fee(array $fee, ?array $market = null): array {
        //
        //     {
        //         "symbol" => "ETHUSDT",
        //         "makerFeeRate" => 0.001,
        //         "takerFeeRate" => 0.001
        //     }
        //
        $marketId = $this->safe_string($fee, 'symbol');
        $defaultType = ($market !== null) ? $market['type'] : 'contract';
        $symbol = $this->safe_symbol($marketId, $market, null, $defaultType);
        return array(
            'info' => $fee,
            'symbol' => $symbol,
            'maker' => $this->safe_number($fee, 'makerFeeRate'),
            'taker' => $this->safe_number($fee, 'takerFeeRate'),
            'percentage' => null,
            'tierBased' => null,
        );
    }

    public function fetch_trading_fee(string $symbol, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $params) {
            /**
             * fetch the trading $fees for a $market
             *
             * @see https://bybit-exchange.github.io/docs/v5/account/fee-rate
             *
             * @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~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
            );
            $category = null;
            list($category, $params) = $this->get_bybit_type('fetchTradingFee', $market, $params);
            $request['category'] = $category;
            $response = Async\await($this->privateGetV5AccountFeeRate ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "OK",
            //         "result" => {
            //             "list" => array(
            //                 array(
            //                     "symbol" => "ETHUSDT",
            //                     "takerFeeRate" => "0.0006",
            //                     "makerFeeRate" => "0.0001"
            //                 }
            //             )
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1676360412576
            //     }
            //
            $result = $this->safe_dict($response, 'result', array());
            $fees = $this->safe_list($result, 'list', array());
            $first = $this->safe_dict($fees, 0, array());
            return $this->parse_trading_fee($first, $market);
        }) ();
    }

    public function fetch_trading_fees($params = array ()): PromiseInterface {
        return Async\async(function () use ($params) {
            /**
             * fetch the trading $fees for multiple markets
             *
             * @see https://bybit-exchange.github.io/docs/v5/account/fee-rate
             *
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->type] market $type, ['swap', 'option', 'spot']
             * @return {array} a dictionary of ~@link https://docs.ccxt.com/#/?id=$fee-structure $fee structures~ indexed by market symbols
             */
            Async\await($this->load_markets());
            $type = null;
            list($type, $params) = $this->handle_option_and_params($params, 'fetchTradingFees', 'type', 'future');
            if ($type === 'spot') {
                throw new NotSupported($this->id . ' fetchTradingFees() is not supported for spot market');
            }
            $response = Async\await($this->privateGetV5AccountFeeRate ($params));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "OK",
            //         "result" => {
            //             "list" => array(
            //                 array(
            //                     "symbol" => "ETHUSDT",
            //                     "takerFeeRate" => "0.0006",
            //                     "makerFeeRate" => "0.0001"
            //                 }
            //             )
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1676360412576
            //     }
            //
            $fees = $this->safe_dict($response, 'result', array());
            $fees = $this->safe_list($fees, 'list', array());
            $result = array();
            for ($i = 0; $i < count($fees); $i++) {
                $fee = $this->parse_trading_fee($fees[$i]);
                $symbol = $fee['symbol'];
                $result[$symbol] = $fee;
            }
            return $result;
        }) ();
    }

    public function parse_deposit_withdraw_fee($fee, ?array $currency = null): mixed {
        //
        //    {
        //        "name" => "BTC",
        //        "coin" => "BTC",
        //        "remainAmount" => "150",
        //        "chains" => array(
        //            {
        //                "chainType" => "BTC",
        //                "confirmation" => "10000",
        //                "withdrawFee" => "0.0005",
        //                "depositMin" => "0.0005",
        //                "withdrawMin" => "0.001",
        //                "chain" => "BTC",
        //                "chainDeposit" => "1",
        //                "chainWithdraw" => "1",
        //                "minAccuracy" => "8"
        //            }
        //        )
        //    }
        //
        $chains = $this->safe_list($fee, 'chains', array());
        $chainsLength = count($chains);
        $result = array(
            'info' => $fee,
            'withdraw' => array(
                'fee' => null,
                'percentage' => null,
            ),
            'deposit' => array(
                'fee' => null,
                'percentage' => null,
            ),
            'networks' => array(),
        );
        if ($chainsLength !== 0) {
            for ($i = 0; $i < $chainsLength; $i++) {
                $chain = $chains[$i];
                $networkId = $this->safe_string($chain, 'chain');
                $currencyCode = $this->safe_string($currency, 'code');
                $networkCode = $this->network_id_to_code($networkId, $currencyCode);
                $result['networks'][$networkCode] = array(
                    'deposit' => array( 'fee' => null, 'percentage' => null ),
                    'withdraw' => array( 'fee' => $this->safe_number($chain, 'withdrawFee'), 'percentage' => false ),
                );
                if ($chainsLength === 1) {
                    $result['withdraw']['fee'] = $this->safe_number($chain, 'withdrawFee');
                    $result['withdraw']['percentage'] = false;
                }
            }
        }
        return $result;
    }

    public function fetch_deposit_withdraw_fees(?array $codes = null, $params = array ()) {
        return Async\async(function () use ($codes, $params) {
            /**
             * fetch deposit and withdraw fees
             *
             * @see https://bybit-exchange.github.io/docs/v5/asset/coin-info
             *
             * @param {string[]} $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->check_required_credentials();
            Async\await($this->load_markets());
            $response = Async\await($this->privateGetV5AssetCoinQueryInfo ($params));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "",
            //         "result" => {
            //             "rows" => array(
            //                 {
            //                     "name" => "BTC",
            //                     "coin" => "BTC",
            //                     "remainAmount" => "150",
            //                     "chains" => array(
            //                         array(
            //                             "chainType" => "BTC",
            //                             "confirmation" => "10000",
            //                             "withdrawFee" => "0.0005",
            //                             "depositMin" => "0.0005",
            //                             "withdrawMin" => "0.001",
            //                             "chain" => "BTC",
            //                             "chainDeposit" => "1",
            //                             "chainWithdraw" => "1",
            //                             "minAccuracy" => "8"
            //                         }
            //                     )
            //                 }
            //             )
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1672194582264
            //     }
            //
            $data = $this->safe_dict($response, 'result', array());
            $rows = $this->safe_list($data, 'rows', array());
            return $this->parse_deposit_withdraw_fees($rows, $codes, 'coin');
        }) ();
    }

    public function fetch_settlement_history(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * fetches historical settlement records
             *
             * @see https://bybit-exchange.github.io/docs/v5/market/delivery-price
             *
             * @param {string} $symbol unified $market $symbol of the settlement history
             * @param {int} [$since] timestamp in ms
             * @param {int} [$limit] number of records
             * @param {array} [$params] exchange specific $params
             * @param {string} [$params->type] $market $type, ['swap', 'option', 'spot']
             * @param {string} [$params->subType] $market subType, ['linear', 'inverse']
             * @return {array[]} a list of [settlement history objects]
             */
            Async\await($this->load_markets());
            $request = array();
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
                $request['symbol'] = $market['id'];
            }
            $type = null;
            list($type, $params) = $this->get_bybit_type('fetchSettlementHistory', $market, $params);
            if ($type === 'spot') {
                throw new NotSupported($this->id . ' fetchSettlementHistory() is not supported for spot market');
            }
            $request['category'] = $type;
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            $response = Async\await($this->publicGetV5MarketDeliveryPrice ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "success",
            //         "result" => array(
            //             "category" => "option",
            //             "nextPageCursor" => "0%2C3",
            //             "list" => array(
            //                 array(
            //                     "symbol" => "SOL-27JUN23-20-C",
            //                     "deliveryPrice" => "16.62258889",
            //                     "deliveryTime" => "1687852800000"
            //                 ),
            //             )
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1689043527231
            //     }
            //
            $result = $this->safe_dict($response, 'result', array());
            $data = $this->safe_list($result, 'list', array());
            $settlements = $this->parse_settlements($data, $market);
            $sorted = $this->sort_by($settlements, 'timestamp');
            return $this->filter_by_symbol_since_limit($sorted, $market['symbol'], $since, $limit);
        }) ();
    }

    public function fetch_my_settlement_history(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * fetches historical settlement records of the user
             *
             * @see https://bybit-exchange.github.io/docs/v5/asset/delivery
             *
             * @param {string} $symbol unified $market $symbol of the settlement history
             * @param {int} [$since] timestamp in ms
             * @param {int} [$limit] number of records
             * @param {array} [$params] exchange specific $params
             * @param {string} [$params->type] $market $type, ['swap', 'option', 'spot']
             * @param {string} [$params->subType] $market subType, ['linear', 'inverse']
             * @return {array[]} a list of [settlement history objects]
             */
            Async\await($this->load_markets());
            $request = array();
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
                $request['symbol'] = $market['id'];
            }
            $type = null;
            list($type, $params) = $this->get_bybit_type('fetchMySettlementHistory', $market, $params);
            if ($type === 'spot') {
                throw new NotSupported($this->id . ' fetchMySettlementHistory() is not supported for spot market');
            }
            $request['category'] = $type;
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            $response = Async\await($this->privateGetV5AssetDeliveryRecord ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "success",
            //         "result" => array(
            //             "category" => "option",
            //             "nextPageCursor" => "0%2C3",
            //             "list" => array(
            //                 array(
            //                     "symbol" => "SOL-27JUN23-20-C",
            //                     "deliveryPrice" => "16.62258889",
            //                     "deliveryTime" => "1687852800000",
            //                     "side" => "Buy",
            //                     "strike" => "20",
            //                     "fee" => "0.00000000",
            //                     "position" => "0.01",
            //                     "deliveryRpl" => "3.5"
            //                 ),
            //             )
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1689043527231
            //     }
            //
            $result = $this->safe_dict($response, 'result', array());
            $data = $this->safe_list($result, 'list', array());
            $settlements = $this->parse_settlements($data, $market);
            $sorted = $this->sort_by($settlements, 'timestamp');
            return $this->filter_by_symbol_since_limit($sorted, $market['symbol'], $since, $limit);
        }) ();
    }

    public function parse_settlement($settlement, $market) {
        //
        // fetchSettlementHistory
        //
        //     {
        //         "symbol" => "SOL-27JUN23-20-C",
        //         "deliveryPrice" => "16.62258889",
        //         "deliveryTime" => "1687852800000"
        //     }
        //
        // fetchMySettlementHistory
        //
        //     {
        //         "symbol" => "SOL-27JUN23-20-C",
        //         "deliveryPrice" => "16.62258889",
        //         "deliveryTime" => "1687852800000",
        //         "side" => "Buy",
        //         "strike" => "20",
        //         "fee" => "0.00000000",
        //         "position" => "0.01",
        //         "deliveryRpl" => "3.5"
        //     }
        //
        $timestamp = $this->safe_integer($settlement, 'deliveryTime');
        $marketId = $this->safe_string($settlement, 'symbol');
        return array(
            'info' => $settlement,
            'symbol' => $this->safe_symbol($marketId, $market),
            'price' => $this->safe_number($settlement, 'deliveryPrice'),
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
        );
    }

    public function parse_settlements($settlements, $market) {
        //
        // fetchSettlementHistory
        //
        //     array(
        //         {
        //             "symbol" => "SOL-27JUN23-20-C",
        //             "deliveryPrice" => "16.62258889",
        //             "deliveryTime" => "1687852800000"
        //         }
        //     )
        //
        // fetchMySettlementHistory
        //
        //     array(
        //         {
        //             "symbol" => "SOL-27JUN23-20-C",
        //             "deliveryPrice" => "16.62258889",
        //             "deliveryTime" => "1687852800000",
        //             "side" => "Buy",
        //             "strike" => "20",
        //             "fee" => "0.00000000",
        //             "position" => "0.01",
        //             "deliveryRpl" => "3.5"
        //         }
        //     )
        //
        $result = array();
        for ($i = 0; $i < count($settlements); $i++) {
            $result[] = $this->parse_settlement($settlements[$i], $market);
        }
        return $result;
    }

    public function fetch_volatility_history(string $code, $params = array ()) {
        return Async\async(function () use ($code, $params) {
            /**
             * fetch the historical $volatility of an option market based on an underlying asset
             *
             * @see https://bybit-exchange.github.io/docs/v5/market/iv
             *
             * @param {string} $code unified $currency $code
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {int} [$params->period] the period in days to fetch the $volatility for => 7,14,21,30,60,90,180,270
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=$volatility-structure $volatility history objects~
             */
            Async\await($this->load_markets());
            $currency = $this->currency($code);
            $request = array(
                'category' => 'option',
                'baseCoin' => $currency['id'],
            );
            $response = Async\await($this->publicGetV5MarketHistoricalVolatility ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "SUCCESS",
            //         "category" => "option",
            //         "result" => array(
            //             {
            //                 "period" => 7,
            //                 "value" => "0.23854072",
            //                 "time" => "1690574400000"
            //             }
            //         )
            //     }
            //
            $volatility = $this->safe_list($response, 'result', array());
            return $this->parse_volatility_history($volatility);
        }) ();
    }

    public function parse_volatility_history($volatility) {
        //
        //     {
        //         "period" => 7,
        //         "value" => "0.23854072",
        //         "time" => "1690574400000"
        //     }
        //
        $result = array();
        for ($i = 0; $i < count($volatility); $i++) {
            $entry = $volatility[$i];
            $timestamp = $this->safe_integer($entry, 'time');
            $result[] = array(
                'info' => $volatility,
                'timestamp' => $timestamp,
                'datetime' => $this->iso8601($timestamp),
                'volatility' => $this->safe_number($entry, 'value'),
            );
        }
        return $result;
    }

    public function fetch_greeks(string $symbol, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $params) {
            /**
             * fetches an option contracts $greeks, financial metrics used to measure the factors that affect the price of an options contract
             *
             * @see https://bybit-exchange.github.io/docs/api-explorer/v5/market/tickers
             *
             * @param {string} $symbol unified $symbol of the $market to fetch $greeks for
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=$greeks-structure $greeks structure~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
                'category' => 'option',
            );
            $response = Async\await($this->publicGetV5MarketTickers ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "SUCCESS",
            //         "result" => {
            //             "category" => "option",
            //             "list" => array(
            //                 array(
            //                     "symbol" => "BTC-26JAN24-39000-C",
            //                     "bid1Price" => "3205",
            //                     "bid1Size" => "7.1",
            //                     "bid1Iv" => "0.5478",
            //                     "ask1Price" => "3315",
            //                     "ask1Size" => "1.98",
            //                     "ask1Iv" => "0.5638",
            //                     "lastPrice" => "3230",
            //                     "highPrice24h" => "3255",
            //                     "lowPrice24h" => "3200",
            //                     "markPrice" => "3273.02263032",
            //                     "indexPrice" => "36790.96",
            //                     "markIv" => "0.5577",
            //                     "underlyingPrice" => "37649.67254894",
            //                     "openInterest" => "19.67",
            //                     "turnover24h" => "170140.33875912",
            //                     "volume24h" => "4.56",
            //                     "totalVolume" => "22",
            //                     "totalTurnover" => "789305",
            //                     "delta" => "0.49640971",
            //                     "gamma" => "0.00004131",
            //                     "vega" => "69.08651675",
            //                     "theta" => "-24.9443226",
            //                     "predictedDeliveryPrice" => "0",
            //                     "change24h" => "0.18532111"
            //                 }
            //             )
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1699584008326
            //     }
            //
            $timestamp = $this->safe_integer($response, 'time');
            $result = $this->safe_dict($response, 'result', array());
            $data = $this->safe_list($result, 'list', array());
            $greeks = $this->parse_greeks($data[0], $market);
            return $this->extend($greeks, array(
                'timestamp' => $timestamp,
                'datetime' => $this->iso8601($timestamp),
            ));
        }) ();
    }

    public function fetch_all_greeks(?array $symbols = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbols, $params) {
            /**
             * fetches all option contracts greeks, financial metrics used to measure the factors that affect the price of an options contract
             *
             * @see https://bybit-exchange.github.io/docs/api-explorer/v5/market/tickers
             *
             * @param {string[]} [$symbols] unified $symbols of the markets to fetch greeks for, all markets are returned if not assigned
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->baseCoin] the $baseCoin of the symbol, default is BTC
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=greeks-structure greeks structure~
             */
            Async\await($this->load_markets());
            $symbols = $this->market_symbols($symbols, null, true, true, true);
            $baseCoin = $this->safe_string($params, 'baseCoin', 'BTC');
            $request = array(
                'category' => 'option',
                'baseCoin' => $baseCoin,
            );
            $market = null;
            if ($symbols !== null) {
                $symbolsLength = count($symbols);
                if ($symbolsLength === 1) {
                    $market = $this->market($symbols[0]);
                    $request['symbol'] = $market['id'];
                }
            }
            $response = Async\await($this->publicGetV5MarketTickers ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "SUCCESS",
            //         "result" => {
            //             "category" => "option",
            //             "list" => array(
            //                 array(
            //                     "symbol" => "BTC-26JAN24-39000-C",
            //                     "bid1Price" => "3205",
            //                     "bid1Size" => "7.1",
            //                     "bid1Iv" => "0.5478",
            //                     "ask1Price" => "3315",
            //                     "ask1Size" => "1.98",
            //                     "ask1Iv" => "0.5638",
            //                     "lastPrice" => "3230",
            //                     "highPrice24h" => "3255",
            //                     "lowPrice24h" => "3200",
            //                     "markPrice" => "3273.02263032",
            //                     "indexPrice" => "36790.96",
            //                     "markIv" => "0.5577",
            //                     "underlyingPrice" => "37649.67254894",
            //                     "openInterest" => "19.67",
            //                     "turnover24h" => "170140.33875912",
            //                     "volume24h" => "4.56",
            //                     "totalVolume" => "22",
            //                     "totalTurnover" => "789305",
            //                     "delta" => "0.49640971",
            //                     "gamma" => "0.00004131",
            //                     "vega" => "69.08651675",
            //                     "theta" => "-24.9443226",
            //                     "predictedDeliveryPrice" => "0",
            //                     "change24h" => "0.18532111"
            //                 }
            //             )
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1699584008326
            //     }
            //
            $result = $this->safe_dict($response, 'result', array());
            $data = $this->safe_list($result, 'list', array());
            return $this->parse_all_greeks($data, $symbols);
        }) ();
    }

    public function parse_greeks(array $greeks, ?array $market = null): array {
        //
        //     {
        //         "symbol" => "BTC-26JAN24-39000-C",
        //         "bid1Price" => "3205",
        //         "bid1Size" => "7.1",
        //         "bid1Iv" => "0.5478",
        //         "ask1Price" => "3315",
        //         "ask1Size" => "1.98",
        //         "ask1Iv" => "0.5638",
        //         "lastPrice" => "3230",
        //         "highPrice24h" => "3255",
        //         "lowPrice24h" => "3200",
        //         "markPrice" => "3273.02263032",
        //         "indexPrice" => "36790.96",
        //         "markIv" => "0.5577",
        //         "underlyingPrice" => "37649.67254894",
        //         "openInterest" => "19.67",
        //         "turnover24h" => "170140.33875912",
        //         "volume24h" => "4.56",
        //         "totalVolume" => "22",
        //         "totalTurnover" => "789305",
        //         "delta" => "0.49640971",
        //         "gamma" => "0.00004131",
        //         "vega" => "69.08651675",
        //         "theta" => "-24.9443226",
        //         "predictedDeliveryPrice" => "0",
        //         "change24h" => "0.18532111"
        //     }
        //
        $marketId = $this->safe_string($greeks, 'symbol');
        $symbol = $this->safe_symbol($marketId, $market);
        return array(
            'symbol' => $symbol,
            'timestamp' => null,
            'datetime' => null,
            'delta' => $this->safe_number($greeks, 'delta'),
            'gamma' => $this->safe_number($greeks, 'gamma'),
            'theta' => $this->safe_number($greeks, 'theta'),
            'vega' => $this->safe_number($greeks, 'vega'),
            'rho' => null,
            'bidSize' => $this->safe_number($greeks, 'bid1Size'),
            'askSize' => $this->safe_number($greeks, 'ask1Size'),
            'bidImpliedVolatility' => $this->safe_number($greeks, 'bid1Iv'),
            'askImpliedVolatility' => $this->safe_number($greeks, 'ask1Iv'),
            'markImpliedVolatility' => $this->safe_number($greeks, 'markIv'),
            'bidPrice' => $this->safe_number($greeks, 'bid1Price'),
            'askPrice' => $this->safe_number($greeks, 'ask1Price'),
            'markPrice' => $this->safe_number($greeks, 'markPrice'),
            'lastPrice' => $this->safe_number($greeks, 'lastPrice'),
            'underlyingPrice' => $this->safe_number($greeks, 'underlyingPrice'),
            'info' => $greeks,
        );
    }

    public function fetch_my_liquidations(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * retrieves the users liquidated positions
             *
             * @see https://bybit-exchange.github.io/docs/api-explorer/v5/position/execution
             *
             * @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 exchange API endpoint
             * @param {string} [$params->type] $market $type, ['swap', 'option', 'spot']
             * @param {string} [$params->subType] $market subType, ['linear', 'inverse']
             * @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 {array} an array of ~@link https://docs.ccxt.com/#/?id=liquidation-structure liquidation structures~
             */
            Async\await($this->load_markets());
            $paginate = false;
            list($paginate, $params) = $this->handle_option_and_params($params, 'fetchMyLiquidations', 'paginate');
            if ($paginate) {
                return Async\await($this->fetch_paginated_call_cursor('fetchMyLiquidations', $symbol, $since, $limit, $params, 'nextPageCursor', 'cursor', null, 100));
            }
            $request = array(
                'execType' => 'BustTrade',
            );
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
                $request['symbol'] = $market['id'];
            }
            $type = null;
            list($type, $params) = $this->get_bybit_type('fetchMyLiquidations', $market, $params);
            $request['category'] = $type;
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            if ($since !== null) {
                $request['startTime'] = $since;
            }
            list($request, $params) = $this->handle_until_option('endTime', $request, $params);
            $response = Async\await($this->privateGetV5ExecutionList ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "OK",
            //         "result" => {
            //             "nextPageCursor" => "132766%3A2%2C132766%3A2",
            //             "category" => "linear",
            //             "list" => array(
            //                 array(
            //                     "symbol" => "ETHPERP",
            //                     "orderType" => "Market",
            //                     "underlyingPrice" => "",
            //                     "orderLinkId" => "",
            //                     "side" => "Buy",
            //                     "indexPrice" => "",
            //                     "orderId" => "8c065341-7b52-4ca9-ac2c-37e31ac55c94",
            //                     "stopOrderType" => "UNKNOWN",
            //                     "leavesQty" => "0",
            //                     "execTime" => "1672282722429",
            //                     "isMaker" => false,
            //                     "execFee" => "0.071409",
            //                     "feeRate" => "0.0006",
            //                     "execId" => "e0cbe81d-0f18-5866-9415-cf319b5dab3b",
            //                     "tradeIv" => "",
            //                     "blockTradeId" => "",
            //                     "markPrice" => "1183.54",
            //                     "execPrice" => "1190.15",
            //                     "markIv" => "",
            //                     "orderQty" => "0.1",
            //                     "orderPrice" => "1236.9",
            //                     "execValue" => "119.015",
            //                     "execType" => "Trade",
            //                     "execQty" => "0.1"
            //                 }
            //             )
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1672283754510
            //     }
            //
            $liquidations = $this->add_pagination_cursor_to_result($response);
            return $this->parse_liquidations($liquidations, $market, $since, $limit);
        }) ();
    }

    public function parse_liquidation($liquidation, ?array $market = null): Liquidation {
        //
        //     {
        //         "symbol" => "ETHPERP",
        //         "orderType" => "Market",
        //         "underlyingPrice" => "",
        //         "orderLinkId" => "",
        //         "side" => "Buy",
        //         "indexPrice" => "",
        //         "orderId" => "8c065341-7b52-4ca9-ac2c-37e31ac55c94",
        //         "stopOrderType" => "UNKNOWN",
        //         "leavesQty" => "0",
        //         "execTime" => "1672282722429",
        //         "isMaker" => false,
        //         "execFee" => "0.071409",
        //         "feeRate" => "0.0006",
        //         "execId" => "e0cbe81d-0f18-5866-9415-cf319b5dab3b",
        //         "tradeIv" => "",
        //         "blockTradeId" => "",
        //         "markPrice" => "1183.54",
        //         "execPrice" => "1190.15",
        //         "markIv" => "",
        //         "orderQty" => "0.1",
        //         "orderPrice" => "1236.9",
        //         "execValue" => "119.015",
        //         "execType" => "Trade",
        //         "execQty" => "0.1"
        //     }
        //
        $marketId = $this->safe_string($liquidation, 'symbol');
        $timestamp = $this->safe_integer($liquidation, 'execTime');
        $contractsString = $this->safe_string($liquidation, 'execQty');
        $contractSizeString = $this->safe_string($market, 'contractSize');
        $priceString = $this->safe_string($liquidation, 'execPrice');
        $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, null, 'contract'),
            '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 get_leverage_tiers_paginated(?string $symbol = null, $params = array ()) {
        return Async\async(function () use ($symbol, $params) {
            Async\await($this->load_markets());
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
            }
            $paginate = false;
            list($paginate, $params) = $this->handle_option_and_params($params, 'getLeverageTiersPaginated', 'paginate');
            if ($paginate) {
                return Async\await($this->fetch_paginated_call_cursor('getLeverageTiersPaginated', $symbol, null, null, $params, 'nextPageCursor', 'cursor', null, 100));
            }
            $subType = null;
            list($subType, $params) = $this->handle_sub_type_and_params('getLeverageTiersPaginated', $market, $params, 'linear');
            $request = array(
                'category' => $subType,
            );
            $response = Async\await($this->publicGetV5MarketRiskLimit ($this->extend($request, $params)));
            $result = $this->add_pagination_cursor_to_result($response);
            $first = $this->safe_dict($result, 0);
            $total = count($result);
            $lastIndex = $total - 1;
            $last = $this->safe_dict($result, $lastIndex);
            $cursorValue = $this->safe_string($first, 'nextPageCursor');
            $last['info'] = array(
                'nextPageCursor' => $cursorValue,
            );
            $result[$lastIndex] = $last;
            return $result;
        }) ();
    }

    public function fetch_leverage_tiers(?array $symbols = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbols, $params) {
            /**
             * retrieve information on the maximum leverage, for different trade sizes
             *
             * @see https://bybit-exchange.github.io/docs/v5/market/risk-limit
             *
             * @param {string[]} [$symbols] a list of unified $market $symbols
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->subType] $market subType, ['linear', 'inverse'], default is 'linear'
             * @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 {array} a dictionary of ~@link https://docs.ccxt.com/#/?id=leverage-tiers-structure leverage tiers structures~, indexed by $market $symbols
             */
            Async\await($this->load_markets());
            $market = null;
            $symbol = null;
            if ($symbols !== null) {
                $market = $this->market($symbols[0]);
                if ($market['spot']) {
                    throw new NotSupported($this->id . ' fetchLeverageTiers() is not supported for spot market');
                }
                $symbol = $market['symbol'];
            }
            $data = Async\await($this->get_leverage_tiers_paginated($symbol, $this->extend(array( 'paginate' => true, 'paginationCalls' => 50 ), $params)));
            $symbols = $this->market_symbols($symbols);
            return $this->parse_leverage_tiers($data, $symbols, 'symbol');
        }) ();
    }

    public function parse_leverage_tiers($response, ?array $symbols = null, $marketIdKey = null): array {
        //
        //  array(
        //      {
        //          "id" => 1,
        //          "symbol" => "BTCUSD",
        //          "riskLimitValue" => "150",
        //          "maintenanceMargin" => "0.5",
        //          "initialMargin" => "1",
        //          "isLowestRisk" => 1,
        //          "maxLeverage" => "100.00"
        //      }
        //  )
        //
        $tiers = array();
        $marketIds = $this->market_ids($symbols);
        $filteredResults = $this->filter_by_array($response, $marketIdKey, $marketIds, false);
        $grouped = $this->group_by($filteredResults, $marketIdKey);
        $keys = is_array($grouped) ? array_keys($grouped) : array();
        for ($i = 0; $i < count($keys); $i++) {
            $marketId = $keys[$i];
            $entry = $grouped[$marketId];
            for ($j = 0; $j < count($entry); $j++) {
                $id = $this->safe_integer($entry[$j], 'id');
                $entry[$j]['id'] = $id;
            }
            $market = $this->safe_market($marketId, null, null, 'contract');
            $symbol = $market['symbol'];
            $tiers[$symbol] = $this->parse_market_leverage_tiers($this->sort_by($entry, 'id'), $market);
        }
        return $tiers;
    }

    public function parse_market_leverage_tiers($info, ?array $market = null): array {
        //
        //  array(
        //      {
        //          "id" => 1,
        //          "symbol" => "BTCUSD",
        //          "riskLimitValue" => "150",
        //          "maintenanceMargin" => "0.5",
        //          "initialMargin" => "1",
        //          "isLowestRisk" => 1,
        //          "maxLeverage" => "100.00"
        //      }
        //  )
        //
        $tiers = array();
        for ($i = 0; $i < count($info); $i++) {
            $tier = $info[$i];
            $marketId = $this->safe_string($info, 'symbol');
            $market = $this->safe_market($marketId);
            $minNotional = $this->parse_number('0');
            if ($i !== 0) {
                $minNotional = $this->safe_number($info[$i - 1], 'riskLimitValue');
            }
            $tiers[] = array(
                'tier' => $this->safe_integer($tier, 'id'),
                'symbol' => $this->safe_symbol($marketId, $market),
                'currency' => $market['settle'],
                'minNotional' => $minNotional,
                'maxNotional' => $this->safe_number($tier, 'riskLimitValue'),
                'maintenanceMarginRate' => $this->safe_number($tier, 'maintenanceMargin'),
                'maxLeverage' => $this->safe_number($tier, 'maxLeverage'),
                'info' => $tier,
            );
        }
        return $tiers;
    }

    public function fetch_funding_history(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * fetch the history of funding payments paid and received on this account
             *
             * @see https://bybit-exchange.github.io/docs/api-explorer/v5/position/execution
             *
             * @param {string} [$symbol] unified $market $symbol
             * @param {int} [$since] the earliest time in ms to fetch funding history for
             * @param {int} [$limit] the maximum number of funding history structures to retrieve
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @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 {array} a ~@link https://docs.ccxt.com/#/?id=funding-history-structure funding history structure~
             */
            Async\await($this->load_markets());
            $paginate = false;
            list($paginate, $params) = $this->handle_option_and_params($params, 'fetchFundingHistory', 'paginate');
            if ($paginate) {
                return Async\await($this->fetch_paginated_call_cursor('fetchFundingHistory', $symbol, $since, $limit, $params, 'nextPageCursor', 'cursor', null, 100));
            }
            $request = array(
                'execType' => 'Funding',
            );
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
                $request['symbol'] = $market['id'];
            }
            $type = null;
            list($type, $params) = $this->get_bybit_type('fetchFundingHistory', $market, $params);
            $request['category'] = $type;
            if ($symbol !== null) {
                $request['symbol'] = $market['id'];
            }
            if ($since !== null) {
                $request['startTime'] = $since;
            }
            if ($limit !== null) {
                $request['size'] = $limit;
            } else {
                $request['size'] = 100;
            }
            list($request, $params) = $this->handle_until_option('endTime', $request, $params);
            $response = Async\await($this->privateGetV5ExecutionList ($this->extend($request, $params)));
            $fundings = $this->add_pagination_cursor_to_result($response);
            return $this->parse_incomes($fundings, $market, $since, $limit);
        }) ();
    }

    public function parse_income($income, ?array $market = null): array {
        //
        // {
        //     "symbol" => "XMRUSDT",
        //     "orderType" => "UNKNOWN",
        //     "underlyingPrice" => "",
        //     "orderLinkId" => "",
        //     "orderId" => "a11e5fe2-1dbf-4bab-a9b2-af80a14efc5d",
        //     "stopOrderType" => "UNKNOWN",
        //     "execTime" => "1710950400000",
        //     "feeCurrency" => "",
        //     "createType" => "",
        //     "feeRate" => "-0.000761",
        //     "tradeIv" => "",
        //     "blockTradeId" => "",
        //     "markPrice" => "136.79",
        //     "execPrice" => "137.11",
        //     "markIv" => "",
        //     "orderQty" => "0",
        //     "orderPrice" => "0",
        //     "execValue" => "134.3678",
        //     "closedSize" => "0",
        //     "execType" => "Funding",
        //     "seq" => "28097658790",
        //     "side" => "Sell",
        //     "indexPrice" => "",
        //     "leavesQty" => "0",
        //     "isMaker" => false,
        //     "execFee" => "-0.10232512",
        //     "execId" => "8d1ef156-4ec6-4445-9a6c-1c0c24dbd046",
        //     "marketUnit" => "",
        //     "execQty" => "0.98",
        //     "nextPageCursor" => "5774437%3A0%2C5771289%3A0"
        // }
        //
        $marketId = $this->safe_string($income, 'symbol');
        $market = $this->safe_market($marketId, $market, null, 'contract');
        $code = 'USDT';
        if ($market['inverse']) {
            $code = $market['quote'];
        }
        $timestamp = $this->safe_integer($income, 'execTime');
        return array(
            'info' => $income,
            'symbol' => $this->safe_symbol($marketId, $market, '-', 'swap'),
            'code' => $code,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'id' => $this->safe_string($income, 'execId'),
            'amount' => $this->safe_number($income, 'execFee'),
            'rate' => $this->safe_number($income, 'feeRate'),
        );
    }

    public function fetch_option(string $symbol, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $params) {
            /**
             * fetches option data that is commonly found in an option $chain
             *
             * @see https://bybit-exchange.github.io/docs/v5/market/tickers
             *
             * @param {string} $symbol unified $market $symbol
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} an ~@link https://docs.ccxt.com/#/?id=option-$chain-structure option $chain structure~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'category' => 'option',
                'symbol' => $market['id'],
            );
            $response = Async\await($this->publicGetV5MarketTickers ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "SUCCESS",
            //         "result" => {
            //             "category" => "option",
            //             "list" => array(
            //                 array(
            //                     "symbol" => "BTC-27DEC24-55000-P",
            //                     "bid1Price" => "0",
            //                     "bid1Size" => "0",
            //                     "bid1Iv" => "0",
            //                     "ask1Price" => "0",
            //                     "ask1Size" => "0",
            //                     "ask1Iv" => "0",
            //                     "lastPrice" => "10980",
            //                     "highPrice24h" => "0",
            //                     "lowPrice24h" => "0",
            //                     "markPrice" => "11814.66756236",
            //                     "indexPrice" => "63838.92",
            //                     "markIv" => "0.8866",
            //                     "underlyingPrice" => "71690.55303594",
            //                     "openInterest" => "0.01",
            //                     "turnover24h" => "0",
            //                     "volume24h" => "0",
            //                     "totalVolume" => "2",
            //                     "totalTurnover" => "78719",
            //                     "delta" => "-0.23284954",
            //                     "gamma" => "0.0000055",
            //                     "vega" => "191.70757975",
            //                     "theta" => "-30.43617927",
            //                     "predictedDeliveryPrice" => "0",
            //                     "change24h" => "0"
            //                 }
            //             )
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1711162003672
            //     }
            //
            $result = $this->safe_dict($response, 'result', array());
            $resultList = $this->safe_list($result, 'list', array());
            $chain = $this->safe_dict($resultList, 0, array());
            return $this->parse_option($chain, null, $market);
        }) ();
    }

    public function fetch_option_chain(string $code, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $params) {
            /**
             * fetches data for an underlying asset that is commonly found in an option chain
             *
             * @see https://bybit-exchange.github.io/docs/v5/market/tickers
             *
             * @param {string} $code base $currency to fetch an option chain for
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a list of ~@link https://docs.ccxt.com/#/?id=option-chain-structure option chain structures~
             */
            Async\await($this->load_markets());
            $currency = $this->currency($code);
            $request = array(
                'category' => 'option',
                'baseCoin' => $currency['id'],
            );
            $response = Async\await($this->publicGetV5MarketTickers ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "SUCCESS",
            //         "result" => array(
            //             "category" => "option",
            //             "list" => array(
            //                 array(
            //                     "symbol" => "BTC-27DEC24-55000-P",
            //                     "bid1Price" => "0",
            //                     "bid1Size" => "0",
            //                     "bid1Iv" => "0",
            //                     "ask1Price" => "0",
            //                     "ask1Size" => "0",
            //                     "ask1Iv" => "0",
            //                     "lastPrice" => "10980",
            //                     "highPrice24h" => "0",
            //                     "lowPrice24h" => "0",
            //                     "markPrice" => "11814.66756236",
            //                     "indexPrice" => "63838.92",
            //                     "markIv" => "0.8866",
            //                     "underlyingPrice" => "71690.55303594",
            //                     "openInterest" => "0.01",
            //                     "turnover24h" => "0",
            //                     "volume24h" => "0",
            //                     "totalVolume" => "2",
            //                     "totalTurnover" => "78719",
            //                     "delta" => "-0.23284954",
            //                     "gamma" => "0.0000055",
            //                     "vega" => "191.70757975",
            //                     "theta" => "-30.43617927",
            //                     "predictedDeliveryPrice" => "0",
            //                     "change24h" => "0"
            //                 ),
            //             )
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1711162003672
            //     }
            //
            $result = $this->safe_dict($response, 'result', array());
            $resultList = $this->safe_list($result, 'list', array());
            return $this->parse_option_chain($resultList, null, 'symbol');
        }) ();
    }

    public function parse_option(array $chain, ?array $currency = null, ?array $market = null): Option {
        //
        //     {
        //         "symbol" => "BTC-27DEC24-55000-P",
        //         "bid1Price" => "0",
        //         "bid1Size" => "0",
        //         "bid1Iv" => "0",
        //         "ask1Price" => "0",
        //         "ask1Size" => "0",
        //         "ask1Iv" => "0",
        //         "lastPrice" => "10980",
        //         "highPrice24h" => "0",
        //         "lowPrice24h" => "0",
        //         "markPrice" => "11814.66756236",
        //         "indexPrice" => "63838.92",
        //         "markIv" => "0.8866",
        //         "underlyingPrice" => "71690.55303594",
        //         "openInterest" => "0.01",
        //         "turnover24h" => "0",
        //         "volume24h" => "0",
        //         "totalVolume" => "2",
        //         "totalTurnover" => "78719",
        //         "delta" => "-0.23284954",
        //         "gamma" => "0.0000055",
        //         "vega" => "191.70757975",
        //         "theta" => "-30.43617927",
        //         "predictedDeliveryPrice" => "0",
        //         "change24h" => "0"
        //     }
        //
        $marketId = $this->safe_string($chain, 'symbol');
        $market = $this->safe_market($marketId, $market);
        return array(
            'info' => $chain,
            'currency' => null,
            'symbol' => $market['symbol'],
            'timestamp' => null,
            'datetime' => null,
            'impliedVolatility' => $this->safe_number($chain, 'markIv'),
            'openInterest' => $this->safe_number($chain, 'openInterest'),
            'bidPrice' => $this->safe_number($chain, 'bid1Price'),
            'askPrice' => $this->safe_number($chain, 'ask1Price'),
            'midPrice' => null,
            'markPrice' => $this->safe_number($chain, 'markPrice'),
            'lastPrice' => $this->safe_number($chain, 'lastPrice'),
            'underlyingPrice' => $this->safe_number($chain, 'underlyingPrice'),
            'change' => $this->safe_number($chain, 'change24h'),
            'percentage' => null,
            'baseVolume' => $this->safe_number($chain, 'totalVolume'),
            'quoteVolume' => null,
        );
    }

    public function fetch_positions_history(?array $symbols = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbols, $since, $limit, $params) {
            /**
             * fetches historical $positions
             *
             * @see https://bybit-exchange.github.io/docs/v5/position/close-pnl
             *
             * @param {string[]} $symbols a list of unified $market $symbols
             * @param {int} [$since] timestamp in ms of the earliest position to fetch, $params["until"] - $since <= 7 days
             * @param {int} [$limit] the maximum amount of records to fetch, default=50, max=100
             * @param {array} $params extra parameters specific to the exchange api endpoint
             * @param {int} [$params->until] timestamp in ms of the latest position to fetch, $params["until"] - $since <= 7 days
             * @param {string} [$params->subType] 'linear' or 'inverse'
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=position-structure position structures~
             */
            Async\await($this->load_markets());
            $market = null;
            $subType = null;
            $symbolsLength = 0;
            if ($symbols !== null) {
                $symbolsLength = count($symbols);
                if ($symbolsLength > 0) {
                    $market = $this->market($symbols[0]);
                }
            }
            $until = $this->safe_integer($params, 'until');
            list($subType, $params) = $this->handle_sub_type_and_params('fetchPositionsHistory', $market, $params, 'linear');
            $params = $this->omit($params, 'until');
            $request = array(
                'category' => $subType,
            );
            if (($symbols !== null) && ($symbolsLength === 1)) {
                $request['symbol'] = $market['id'];
            }
            if ($since !== null) {
                $request['startTime'] = $since;
            }
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            if ($until !== null) {
                $request['endTime'] = $until;
            }
            $response = Async\await($this->privateGetV5PositionClosedPnl ($this->extend($request, $params)));
            //
            //    {
            //        retCode => '0',
            //        retMsg => 'OK',
            //        $result => {
            //            nextPageCursor => '071749f3-a9fa-427b-b5ca-27b2f52b81de%3A1712717265566520788%2C071749f3-a9fa-427b-b5ca-27b2f52b81de%3A1712717265566520788',
            //            category => 'linear',
            //            list => array(
            //                array(
            //                    symbol => 'XRPUSDT',
            //                    orderType => 'Market',
            //                    leverage => '10',
            //                    updatedTime => '1712717265572',
            //                    side => 'Sell',
            //                    orderId => '071749f3-a9fa-427b-b5ca-27b2f52b81de',
            //                    closedPnl => '-0.00049568',
            //                    avgEntryPrice => '0.6045',
            //                    qty => '3',
            //                    cumEntryValue => '1.8135',
            //                    createdTime => '1712717265566',
            //                    orderPrice => '0.5744',
            //                    closedSize => '3',
            //                    avgExitPrice => '0.605',
            //                    execType => 'Trade',
            //                    fillCount => '1',
            //                    cumExitValue => '1.815'
            //                }
            //            )
            //        ),
            //        retExtInfo => array(),
            //        time => '1712717286073'
            //    }
            //
            $result = $this->safe_dict($response, 'result');
            $rawPositions = $this->safe_list($result, 'list');
            $positions = $this->parse_positions($rawPositions, $symbols, $params);
            return $this->filter_by_since_limit($positions, $since, $limit);
        }) ();
    }

    public function fetch_convert_currencies($params = array ()): PromiseInterface {
        return Async\async(function () use ($params) {
            /**
             * fetches all available currencies that can be converted
             *
             * @see https://bybit-exchange.github.io/docs/v5/asset/convert/convert-coin-list
             *
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->accountType] eb_convert_uta, eb_convert_spot, eb_convert_funding, eb_convert_inverse, or eb_convert_contract
             * @return {array} an associative dictionary of currencies
             */
            Async\await($this->load_markets());
            $accountType = null;
            list($enableUnifiedMargin, $enableUnifiedAccount) = Async\await($this->is_unified_enabled());
            $isUnifiedAccount = ($enableUnifiedMargin || $enableUnifiedAccount);
            $accountTypeDefault = $isUnifiedAccount ? 'eb_convert_uta' : 'eb_convert_spot';
            list($accountType, $params) = $this->handle_option_and_params($params, 'fetchConvertCurrencies', 'accountType', $accountTypeDefault);
            $request = array(
                'accountType' => $accountType,
            );
            $response = Async\await($this->privateGetV5AssetExchangeQueryCoinList ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "ok",
            //         "result" => array(
            //             "coins" => array(
            //                 array(
            //                     "coin" => "MATIC",
            //                     "fullName" => "MATIC",
            //                     "icon" => "https://s1.bycsi.com/app/assets/token/0552ae79c535c3095fa18f7b377dd2e9.svg",
            //                     "iconNight" => "https://t1.bycsi.com/app/assets/token/f59301aef2d6ac2165c4c4603e672fb4.svg",
            //                     "accuracyLength" => 8,
            //                     "coinType" => "crypto",
            //                     "balance" => "0",
            //                     "uBalance" => "0",
            //                     "timePeriod" => 0,
            //                     "singleFromMinLimit" => "1.1",
            //                     "singleFromMaxLimit" => "20001",
            //                     "singleToMinLimit" => "0",
            //                     "singleToMaxLimit" => "0",
            //                     "dailyFromMinLimit" => "0",
            //                     "dailyFromMaxLimit" => "0",
            //                     "dailyToMinLimit" => "0",
            //                     "dailyToMaxLimit" => "0",
            //                     "disableFrom" => false,
            //                     "disableTo" => false
            //                 ),
            //             )
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1727256416250
            //     }
            //
            $result = array();
            $data = $this->safe_dict($response, 'result', array());
            $coins = $this->safe_list($data, 'coins', array());
            for ($i = 0; $i < count($coins); $i++) {
                $entry = $coins[$i];
                $id = $this->safe_string($entry, 'coin');
                $disableFrom = $this->safe_bool($entry, 'disableFrom');
                $disableTo = $this->safe_bool($entry, 'disableTo');
                $inactive = ($disableFrom || $disableTo);
                $code = $this->safe_currency_code($id);
                $result[$code] = array(
                    'info' => $entry,
                    'id' => $id,
                    'code' => $code,
                    'networks' => null,
                    'type' => $this->safe_string($entry, 'coinType'),
                    'name' => $this->safe_string($entry, 'fullName'),
                    'active' => !$inactive,
                    'deposit' => null,
                    'withdraw' => $this->safe_number($entry, 'balance'),
                    'fee' => null,
                    'precision' => null,
                    'limits' => array(
                        'amount' => array(
                            'min' => $this->safe_number($entry, 'singleFromMinLimit'),
                            'max' => $this->safe_number($entry, 'singleFromMaxLimit'),
                        ),
                        'withdraw' => array(
                            'min' => null,
                            'max' => null,
                        ),
                        'deposit' => array(
                            'min' => null,
                            'max' => null,
                        ),
                    ),
                    'created' => null,
                );
            }
            return $result;
        }) ();
    }

    public function fetch_convert_quote(string $fromCode, string $toCode, ?float $amount = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($fromCode, $toCode, $amount, $params) {
            /**
             * fetch a quote for converting from one currency to another
             *
             * @see https://bybit-exchange.github.io/docs/v5/asset/convert/apply-quote
             *
             * @param {string} $fromCode the currency that you want to sell and convert from
             * @param {string} $toCode the currency that you want to buy and convert into
             * @param {float} [$amount] how much you want to trade in units of the from currency
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->accountType] eb_convert_uta, eb_convert_spot, eb_convert_funding, eb_convert_inverse, or eb_convert_contract
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=conversion-structure conversion structure~
             */
            Async\await($this->load_markets());
            $accountType = null;
            list($enableUnifiedMargin, $enableUnifiedAccount) = Async\await($this->is_unified_enabled());
            $isUnifiedAccount = ($enableUnifiedMargin || $enableUnifiedAccount);
            $accountTypeDefault = $isUnifiedAccount ? 'eb_convert_uta' : 'eb_convert_spot';
            list($accountType, $params) = $this->handle_option_and_params($params, 'fetchConvertQuote', 'accountType', $accountTypeDefault);
            $request = array(
                'fromCoin' => $fromCode,
                'toCoin' => $toCode,
                'requestAmount' => $this->number_to_string($amount),
                'requestCoin' => $fromCode,
                'accountType' => $accountType,
            );
            $response = Async\await($this->privatePostV5AssetExchangeQuoteApply ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "ok",
            //         "result" => array(
            //             "quoteTxId" => "1010020692439481682687668224",
            //             "exchangeRate" => "0.000015330836780000",
            //             "fromCoin" => "USDT",
            //             "fromCoinType" => "crypto",
            //             "toCoin" => "BTC",
            //             "toCoinType" => "crypto",
            //             "fromAmount" => "10",
            //             "toAmount" => "0.000153308367800000",
            //             "expiredTime" => "1727257413353",
            //             "requestId" => ""
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1727257398375
            //     }
            //
            $data = $this->safe_dict($response, 'result', array());
            $fromCurrencyId = $this->safe_string($data, 'fromCoin', $fromCode);
            $fromCurrency = $this->currency($fromCurrencyId);
            $toCurrencyId = $this->safe_string($data, 'toCoin', $toCode);
            $toCurrency = $this->currency($toCurrencyId);
            return $this->parse_conversion($data, $fromCurrency, $toCurrency);
        }) ();
    }

    public function create_convert_trade(string $id, string $fromCode, string $toCode, ?float $amount = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($id, $fromCode, $toCode, $amount, $params) {
            /**
             * convert from one currency to another
             *
             * @see https://bybit-exchange.github.io/docs/v5/asset/convert/confirm-quote
             *
             * @param {string} $id the $id of the trade that you want to make
             * @param {string} $fromCode the currency that you want to sell and convert from
             * @param {string} $toCode the currency that you want to buy and convert into
             * @param {float} $amount how much you want to trade in units of the from currency
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?$id=conversion-structure conversion structure~
             */
            Async\await($this->load_markets());
            $request = array(
                'quoteTxId' => $id,
            );
            $response = Async\await($this->privatePostV5AssetExchangeConvertExecute ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "ok",
            //         "result" => array(
            //             "exchangeStatus" => "processing",
            //             "quoteTxId" => "1010020692439483803499737088"
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1727257904969
            //     }
            //
            $data = $this->safe_dict($response, 'result', array());
            return $this->parse_conversion($data);
        }) ();
    }

    public function fetch_convert_trade(string $id, ?string $code = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($id, $code, $params) {
            /**
             * fetch the $data for a conversion trade
             *
             * @see https://bybit-exchange.github.io/docs/v5/asset/convert/get-convert-$result
             *
             * @param {string} $id the $id of the trade that you want to fetch
             * @param {string} [$code] the unified currency $code of the conversion trade
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->accountType] eb_convert_uta, eb_convert_spot, eb_convert_funding, eb_convert_inverse, or eb_convert_contract
             * @return {array} a ~@link https://docs.ccxt.com/#/?$id=conversion-structure conversion structure~
             */
            Async\await($this->load_markets());
            $accountType = null;
            list($enableUnifiedMargin, $enableUnifiedAccount) = Async\await($this->is_unified_enabled());
            $isUnifiedAccount = ($enableUnifiedMargin || $enableUnifiedAccount);
            $accountTypeDefault = $isUnifiedAccount ? 'eb_convert_uta' : 'eb_convert_spot';
            list($accountType, $params) = $this->handle_option_and_params($params, 'fetchConvertQuote', 'accountType', $accountTypeDefault);
            $request = array(
                'quoteTxId' => $id,
                'accountType' => $accountType,
            );
            $response = Async\await($this->privateGetV5AssetExchangeConvertResultQuery ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "ok",
            //         "result" => {
            //             "result" => array(
            //                 "accountType" => "eb_convert_uta",
            //                 "exchangeTxId" => "1010020692439483803499737088",
            //                 "userId" => "100406395",
            //                 "fromCoin" => "USDT",
            //                 "fromCoinType" => "crypto",
            //                 "fromAmount" => "10",
            //                 "toCoin" => "BTC",
            //                 "toCoinType" => "crypto",
            //                 "toAmount" => "0.00015344889",
            //                 "exchangeStatus" => "success",
            //                 "extInfo" => array(),
            //                 "convertRate" => "0.000015344889",
            //                 "createdAt" => "1727257904726"
            //             }
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1727258257216
            //     }
            //
            $data = $this->safe_dict($response, 'result', array());
            $result = $this->safe_dict($data, 'result', array());
            $fromCurrencyId = $this->safe_string($result, 'fromCoin');
            $toCurrencyId = $this->safe_string($result, 'toCoin');
            $fromCurrency = null;
            $toCurrency = null;
            if ($fromCurrencyId !== null) {
                $fromCurrency = $this->currency($fromCurrencyId);
            }
            if ($toCurrencyId !== null) {
                $toCurrency = $this->currency($toCurrencyId);
            }
            return $this->parse_conversion($result, $fromCurrency, $toCurrency);
        }) ();
    }

    public function fetch_convert_trade_history(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $since, $limit, $params) {
            /**
             * fetch the users history of conversion trades
             *
             * @see https://bybit-exchange.github.io/docs/v5/asset/convert/get-convert-history
             *
             * @param {string} [$code] the unified currency $code
             * @param {int} [$since] the earliest time in ms to fetch conversions for
             * @param {int} [$limit] the maximum number of conversion structures to retrieve
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->accountType] eb_convert_uta, eb_convert_spot, eb_convert_funding, eb_convert_inverse, or eb_convert_contract
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=conversion-structure conversion structures~
             */
            Async\await($this->load_markets());
            $request = array();
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            $response = Async\await($this->privateGetV5AssetExchangeQueryConvertHistory ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "ok",
            //         "result" => {
            //             "list" => array(
            //                 array(
            //                     "accountType" => "eb_convert_uta",
            //                     "exchangeTxId" => "1010020692439483803499737088",
            //                     "userId" => "100406395",
            //                     "fromCoin" => "USDT",
            //                     "fromCoinType" => "crypto",
            //                     "fromAmount" => "10",
            //                     "toCoin" => "BTC",
            //                     "toCoinType" => "crypto",
            //                     "toAmount" => "0.00015344889",
            //                     "exchangeStatus" => "success",
            //                     "extInfo" => array(),
            //                     "convertRate" => "0.000015344889",
            //                     "createdAt" => "1727257904726"
            //                 }
            //             )
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1727258761874
            //     }
            //
            $data = $this->safe_dict($response, 'result', array());
            $dataList = $this->safe_list($data, 'list', array());
            return $this->parse_conversions($dataList, $code, 'fromCoin', 'toCoin', $since, $limit);
        }) ();
    }

    public function parse_conversion(array $conversion, ?array $fromCurrency = null, ?array $toCurrency = null): array {
        //
        // fetchConvertQuote
        //
        //     {
        //         "quoteTxId" => "1010020692439481682687668224",
        //         "exchangeRate" => "0.000015330836780000",
        //         "fromCoin" => "USDT",
        //         "fromCoinType" => "crypto",
        //         "toCoin" => "BTC",
        //         "toCoinType" => "crypto",
        //         "fromAmount" => "10",
        //         "toAmount" => "0.000153308367800000",
        //         "expiredTime" => "1727257413353",
        //         "requestId" => ""
        //     }
        //
        // createConvertTrade
        //
        //     {
        //         "exchangeStatus" => "processing",
        //         "quoteTxId" => "1010020692439483803499737088"
        //     }
        //
        // fetchConvertTrade, fetchConvertTradeHistory
        //
        //     {
        //         "accountType" => "eb_convert_uta",
        //         "exchangeTxId" => "1010020692439483803499737088",
        //         "userId" => "100406395",
        //         "fromCoin" => "USDT",
        //         "fromCoinType" => "crypto",
        //         "fromAmount" => "10",
        //         "toCoin" => "BTC",
        //         "toCoinType" => "crypto",
        //         "toAmount" => "0.00015344889",
        //         "exchangeStatus" => "success",
        //         "extInfo" => array(),
        //         "convertRate" => "0.000015344889",
        //         "createdAt" => "1727257904726"
        //     }
        //
        $timestamp = $this->safe_integer_2($conversion, 'expiredTime', 'createdAt');
        $fromCoin = $this->safe_string($conversion, 'fromCoin');
        $fromCode = $this->safe_currency_code($fromCoin, $fromCurrency);
        $to = $this->safe_string($conversion, 'toCoin');
        $toCode = $this->safe_currency_code($to, $toCurrency);
        return array(
            'info' => $conversion,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'id' => $this->safe_string_2($conversion, 'quoteTxId', 'exchangeTxId'),
            'fromCurrency' => $fromCode,
            'fromAmount' => $this->safe_number($conversion, 'fromAmount'),
            'toCurrency' => $toCode,
            'toAmount' => $this->safe_number($conversion, 'toAmount'),
            'price' => null,
            'fee' => null,
        );
    }

    public function fetch_long_short_ratio_history(?string $symbol = null, ?string $timeframe = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $timeframe, $since, $limit, $params) {
            /**
             * fetches the long short ratio history for a unified $market $symbol
             *
             * @see https://bybit-exchange.github.io/docs/v5/market/long-short-ratio
             *
             * @param {string} $symbol unified $symbol of the $market to fetch the long short ratio for
             * @param {string} [$timeframe] the period for the ratio, default is 24 hours
             * @param {int} [$since] the earliest time in ms to fetch ratios for
             * @param {int} [$limit] the maximum number of long short ratio structures to retrieve
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array[]} an array of ~@link https://docs.ccxt.com/#/?id=long-short-ratio-structure long short ratio structures~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $type = null;
            list($type, $params) = $this->get_bybit_type('fetchLongShortRatioHistory', $market, $params);
            if ($type === 'spot' || $type === 'option') {
                throw new NotSupported($this->id . ' fetchLongShortRatioHistory() only support linear and inverse markets');
            }
            if ($timeframe === null) {
                $timeframe = '1d';
            }
            $request = array(
                'symbol' => $market['id'],
                'period' => $timeframe,
                'category' => $type,
            );
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            $response = Async\await($this->publicGetV5MarketAccountRatio ($this->extend($request, $params)));
            //
            //     {
            //         "retCode" => 0,
            //         "retMsg" => "OK",
            //         "result" => array(
            //             "list" => array(
            //                 array(
            //                     "symbol" => "BTCUSDT",
            //                     "buyRatio" => "0.5707",
            //                     "sellRatio" => "0.4293",
            //                     "timestamp" => "1729123200000"
            //                 ),
            //             )
            //         ),
            //         "retExtInfo" => array(),
            //         "time" => 1729147842516
            //     }
            //
            $result = $this->safe_dict($response, 'result', array());
            $data = $this->safe_list($result, 'list', array());
            return $this->parse_long_short_ratio_history($data, $market);
        }) ();
    }

    public function parse_long_short_ratio(array $info, ?array $market = null): array {
        //
        //     {
        //         "symbol" => "BTCUSDT",
        //         "buyRatio" => "0.5707",
        //         "sellRatio" => "0.4293",
        //         "timestamp" => "1729123200000"
        //     }
        //
        $marketId = $this->safe_string($info, 'symbol');
        $timestamp = $this->safe_integer_omit_zero($info, 'timestamp');
        $longString = $this->safe_string($info, 'buyRatio');
        $shortString = $this->safe_string($info, 'sellRatio');
        return array(
            'info' => $info,
            'symbol' => $this->safe_symbol($marketId, $market, null, 'contract'),
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'timeframe' => null,
            'longShortRatio' => $this->parse_to_numeric(Precise::string_div($longString, $shortString)),
        );
    }

    public function sign($path, $api = 'public', $method = 'GET', $params = array (), $headers = null, $body = null) {
        $url = $this->implode_hostname($this->urls['api'][$api]) . '/' . $path;
        if ($api === 'public') {
            if ($params) {
                $url .= '?' . $this->rawencode($params);
            }
        } elseif ($api === 'private') {
            $this->check_required_credentials();
            $isOpenapi = mb_strpos($url, 'openapi') !== false;
            $isV3UnifiedMargin = mb_strpos($url, 'unified/v3') !== false;
            $isV3Contract = mb_strpos($url, 'contract/v3') !== false;
            $isV5UnifiedAccount = mb_strpos($url, 'v5') !== false;
            $timestamp = (string) $this->nonce();
            if ($isOpenapi) {
                if ($params) {
                    $body = $this->json($params);
                } else {
                    // this fix for PHP is required otherwise it generates
                    // '[]' on empty arrays even when forced to use objects
                    $body = '{}';
                }
                $payload = $timestamp . $this->apiKey . $body;
                $signature = $this->hmac($this->encode($payload), $this->encode($this->secret), 'sha256', 'hex');
                $headers = array(
                    'Content-Type' => 'application/json',
                    'X-BAPI-API-KEY' => $this->apiKey,
                    'X-BAPI-TIMESTAMP' => $timestamp,
                    'X-BAPI-SIGN' => $signature,
                );
            } elseif ($isV3UnifiedMargin || $isV3Contract || $isV5UnifiedAccount) {
                $headers = array(
                    'Content-Type' => 'application/json',
                    'X-BAPI-API-KEY' => $this->apiKey,
                    'X-BAPI-TIMESTAMP' => $timestamp,
                    'X-BAPI-RECV-WINDOW' => (string) $this->options['recvWindow'],
                );
                if ($isV3UnifiedMargin || $isV3Contract) {
                    $headers['X-BAPI-SIGN-TYPE'] = '2';
                }
                $query = $this->extend(array(), $params);
                $queryEncoded = $this->rawencode($query);
                $auth_base = (string) $timestamp . $this->apiKey . (string) $this->options['recvWindow'];
                $authFull = null;
                if ($method === 'POST') {
                    $body = $this->json($query);
                    $authFull = $auth_base . $body;
                } else {
                    $authFull = $auth_base . $queryEncoded;
                    $url .= '?' . $queryEncoded;
                }
                $signature = null;
                if (mb_strpos($this->secret, 'PRIVATE KEY') > -1) {
                    $signature = $this->rsa($authFull, $this->secret, 'sha256');
                } else {
                    $signature = $this->hmac($this->encode($authFull), $this->encode($this->secret), 'sha256');
                }
                $headers['X-BAPI-SIGN'] = $signature;
            } else {
                $query = $this->extend($params, array(
                    'api_key' => $this->apiKey,
                    'recv_window' => $this->options['recvWindow'],
                    'timestamp' => $timestamp,
                ));
                $sortedQuery = $this->keysort($query);
                $auth = $this->rawencode($sortedQuery, true);
                $signature = null;
                if (mb_strpos($this->secret, 'PRIVATE KEY') > -1) {
                    $signature = $this->rsa($auth, $this->secret, 'sha256');
                } else {
                    $signature = $this->hmac($this->encode($auth), $this->encode($this->secret), 'sha256');
                }
                if ($method === 'POST') {
                    $isSpot = mb_strpos($url, 'spot') !== false;
                    $extendedQuery = $this->extend($query, array(
                        'sign' => $signature,
                    ));
                    if ($isSpot) {
                        $body = $this->urlencode($extendedQuery);
                        $headers = array(
                            'Content-Type' => 'application/x-www-form-urlencoded',
                        );
                    } else {
                        $body = $this->json($extendedQuery);
                        $headers = array(
                            'Content-Type' => 'application/json',
                        );
                    }
                } else {
                    $url .= '?' . $this->rawencode($sortedQuery, true);
                    $url .= '&sign=' . $signature;
                }
            }
        }
        if ($method === 'POST') {
            $brokerId = $this->safe_string($this->options, 'brokerId');
            if ($brokerId !== null) {
                $headers['Referer'] = $brokerId;
            }
        }
        return array( 'url' => $url, 'method' => $method, 'body' => $body, 'headers' => $headers );
    }

    public function handle_errors(int $httpCode, string $reason, string $url, string $method, array $headers, string $body, $response, $requestHeaders, $requestBody) {
        if (!$response) {
            return null; // fallback to default error handler
        }
        //
        //     {
        //         "ret_code" => 10001,
        //         "ret_msg" => "ReadMapCB => expect { or n, but found \u0000, error " +
        //         "found in #0 byte of ...||..., bigger context " +
        //         "...||...",
        //         "ext_code" => '',
        //         "ext_info" => '',
        //         "result" => null,
        //         "time_now" => "1583934106.590436"
        //     }
        //
        //     {
        //         "retCode":10001,
        //         "retMsg":"symbol params err",
        //         "result":array("symbol":"","bid":"","bidIv":"","bidSize":"","ask":"","askIv":"","askSize":"","lastPrice":"","openInterest":"","indexPrice":"","markPrice":"","markPriceIv":"","change24h":"","high24h":"","low24h":"","volume24h":"","turnover24h":"","totalVolume":"","totalTurnover":"","fundingRate":"","predictedFundingRate":"","nextFundingTime":"","countdownHour":"0","predictedDeliveryPrice":"","underlyingPrice":"","delta":"","gamma":"","vega":"","theta":"")
        //     }
        //
        $errorCode = $this->safe_string_2($response, 'ret_code', 'retCode');
        if ($errorCode !== '0') {
            if ($errorCode === '30084') {
                // not an error
                // https://github.com/ccxt/ccxt/issues/11268
                // https://github.com/ccxt/ccxt/pull/11624
                // POST https://api.bybit.com/v2/private/position/switch-isolated 200 OK
                // array("ret_code":30084,"ret_msg":"Isolated not modified","ext_code":"","ext_info":"","result":null,"time_now":"1642005219.937988","rate_limit_status":73,"rate_limit_reset_ms":1642005219894,"rate_limit":75)
                return null;
            }
            $feedback = null;
            if ($errorCode === '10005' && mb_strpos($url, 'order') === false) {
                $feedback = $this->id . ' private api uses /user/v3/private/query-api to check if you have a unified account. The API key of user id must own one of permissions => "Account Transfer", "Subaccount Transfer", "Withdrawal" ' . $body;
            } else {
                $feedback = $this->id . ' ' . $body;
            }
            if (mb_strpos($body, 'Withdraw address chain or destination tag are not equal') > -1) {
                $feedback = $feedback . '; You might also need to ensure the address is whitelisted';
            }
            $this->throw_broadly_matched_exception($this->exceptions['broad'], $body, $feedback);
            $this->throw_exactly_matched_exception($this->exceptions['exact'], $errorCode, $feedback);
            throw new ExchangeError($feedback); // unknown message
        }
        return null;
    }
}
