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

# 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

from ccxt.base.exchange import Exchange
import hashlib
from ccxt.base.errors import ExchangeError
from ccxt.base.errors import AuthenticationError
from ccxt.base.errors import PermissionDenied
from ccxt.base.errors import ArgumentsRequired
from ccxt.base.errors import BadRequest
from ccxt.base.errors import InsufficientFunds
from ccxt.base.errors import InvalidOrder
from ccxt.base.errors import OrderNotFound
from ccxt.base.errors import NotSupported
from ccxt.base.errors import RateLimitExceeded
from ccxt.base.errors import InvalidNonce
from ccxt.base.errors import RequestTimeout
from ccxt.base.decimal_to_precision import TICK_SIZE
from ccxt.base.precise import Precise


class bybit(Exchange):

    def describe(self):
        return self.deep_extend(super(bybit, self).describe(), {
            'id': 'bybit',
            'name': 'Bybit',
            'countries': ['VG'],  # British Virgin Islands
            'version': 'v5',
            'userAgent': None,
            'rateLimit': 20,
            'hostname': 'bybit.com',  # bybit.com, bytick.com
            'pro': True,
            'certified': True,
            'has': {
                'CORS': True,
                'spot': True,
                'margin': True,
                'swap': True,
                'future': True,
                'option': None,
                'cancelAllOrders': True,
                'cancelOrder': True,
                'createOrder': True,
                'createPostOnlyOrder': True,
                'createReduceOnlyOrder': True,
                'createStopLimitOrder': True,
                'createStopMarketOrder': True,
                'createStopOrder': True,
                'editOrder': True,
                'fetchBalance': True,
                'fetchBorrowInterest': False,  # temporarily disabled, as it does not work
                'fetchBorrowRate': True,
                'fetchBorrowRateHistories': False,
                'fetchBorrowRateHistory': False,
                'fetchBorrowRates': False,
                'fetchClosedOrders': True,
                'fetchCurrencies': True,
                'fetchDeposit': False,
                'fetchDepositAddress': True,
                'fetchDepositAddresses': False,
                'fetchDepositAddressesByNetwork': True,
                'fetchDeposits': True,
                'fetchFundingRate': True,  # emulated in exchange
                'fetchFundingRateHistory': True,
                'fetchFundingRates': True,
                'fetchIndexOHLCV': True,
                'fetchLedger': True,
                'fetchMarketLeverageTiers': True,
                'fetchMarkets': True,
                'fetchMarkOHLCV': True,
                'fetchMyTrades': True,
                'fetchOHLCV': True,
                'fetchOpenInterest': True,
                'fetchOpenInterestHistory': True,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'fetchOrderBook': True,
                'fetchOrders': True,
                'fetchOrderTrades': True,
                'fetchPosition': True,
                'fetchPositions': True,
                'fetchPremiumIndexOHLCV': True,
                'fetchTicker': True,
                'fetchTickers': True,
                'fetchTime': True,
                'fetchTrades': True,
                'fetchTradingFee': True,
                'fetchTradingFees': True,
                'fetchTransactions': False,
                'fetchTransfers': True,
                'fetchWithdrawals': True,
                'setLeverage': True,
                'setMarginMode': True,
                'setPositionMode': True,
                'transfer': True,
                'withdraw': True,
            },
            'timeframes': {
                '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': {
                'test': {
                    '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://user-images.githubusercontent.com/51840849/76547799-daff5b80-649e-11ea-87fb-3be9bac08954.jpg',
                'api': {
                    'spot': 'https://api.{hostname}',
                    'futures': 'https://api.{hostname}',
                    'v2': 'https://api.{hostname}',
                    'public': 'https://api.{hostname}',
                    'private': 'https://api.{hostname}',
                },
                'www': 'https://www.bybit.com',
                'doc': [
                    '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': {
                'public': {
                    'get': {
                        # inverse swap
                        'v2/public/orderBook/L2': 1,
                        'v2/public/kline/list': 3,
                        'v2/public/tickers': 1,
                        'v2/public/trading-records': 1,
                        'v2/public/symbols': 1,
                        'v2/public/mark-price-kline': 3,
                        'v2/public/index-price-kline': 3,
                        'v2/public/premium-index-kline': 2,
                        'v2/public/open-interest': 1,
                        'v2/public/big-deal': 1,
                        'v2/public/account-ratio': 1,
                        'v2/public/funding-rate': 1,
                        'v2/public/elite-ratio': 1,
                        'v2/public/funding/prev-funding-rate': 1,
                        'v2/public/risk-limit/list': 1,
                        # linear swap USDT
                        'public/linear/kline': 3,
                        'public/linear/recent-trading-records': 1,
                        'public/linear/risk-limit': 1,
                        'public/linear/funding/prev-funding-rate': 1,
                        'public/linear/mark-price-kline': 1,
                        'public/linear/index-price-kline': 1,
                        'public/linear/premium-index-kline': 1,
                        # spot
                        'spot/v1/time': 1,
                        'spot/v1/symbols': 1,
                        'spot/quote/v1/depth': 1,
                        'spot/quote/v1/depth/merged': 1,
                        'spot/quote/v1/trades': 1,
                        'spot/quote/v1/kline': 1,
                        'spot/quote/v1/ticker/24hr': 1,
                        'spot/quote/v1/ticker/price': 1,
                        'spot/quote/v1/ticker/book_ticker': 1,
                        '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,
                        # data
                        'v2/public/time': 1,
                        'v3/public/time': 1,
                        'v2/public/announcement': 1,
                        # USDC endpoints
                        # option USDC
                        'option/usdc/openapi/public/v1/order-book': 1,
                        'option/usdc/openapi/public/v1/symbols': 1,
                        'option/usdc/openapi/public/v1/tick': 1,
                        'option/usdc/openapi/public/v1/delivery-price': 1,
                        'option/usdc/openapi/public/v1/query-trade-latest': 1,
                        'option/usdc/openapi/public/v1/query-historical-volatility': 1,
                        'option/usdc/openapi/public/v1/all-tickers': 1,
                        # perpetual swap USDC
                        'perpetual/usdc/openapi/public/v1/order-book': 1,
                        'perpetual/usdc/openapi/public/v1/symbols': 1,
                        'perpetual/usdc/openapi/public/v1/tick': 1,
                        'perpetual/usdc/openapi/public/v1/kline/list': 1,
                        'perpetual/usdc/openapi/public/v1/mark-price-kline': 1,
                        'perpetual/usdc/openapi/public/v1/index-price-kline': 1,
                        'perpetual/usdc/openapi/public/v1/premium-index-kline': 1,
                        'perpetual/usdc/openapi/public/v1/open-interest': 1,
                        'perpetual/usdc/openapi/public/v1/big-deal': 1,
                        'perpetual/usdc/openapi/public/v1/account-ratio': 1,
                        'perpetual/usdc/openapi/public/v1/prev-funding-rate': 1,
                        'perpetual/usdc/openapi/public/v1/risk-limit/list': 1,
                        # account
                        'asset/v1/public/deposit/allowed-deposit-list': 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/market/kline': 1,
                        'v5/market/mark-price-kline': 1,
                        'v5/market/index-price-kline': 1,
                        'v5/market/premium-index-price-kline': 1,
                        'v5/market/instruments-info': 1,
                        'v5/market/orderbook': 1,
                        'v5/market/tickers': 1,
                        'v5/market/funding/history': 1,
                        'v5/market/recent-trade': 1,
                        'v5/market/open-interest': 1,
                        'v5/market/historical-volatility': 1,
                        'v5/market/insurance': 1,
                        'v5/market/risk-limit': 1,
                        'v5/market/delivery-price': 1,
                        'v5/spot-lever-token/info': 1,
                        'v5/spot-lever-token/reference': 1,
                    },
                },
                'private': {
                    'get': {
                        # inverse swap
                        'v2/private/order/list': 5,
                        'v2/private/order': 5,
                        'v2/private/stop-order/list': 5,
                        'v2/private/stop-order': 1,
                        'v2/private/position/list': 25,
                        'v2/private/position/fee-rate': 40,
                        'v2/private/execution/list': 25,
                        'v2/private/trade/closed-pnl/list': 1,
                        'v2/public/risk-limit/list': 1,  # TODO check
                        'v2/public/funding/prev-funding-rate': 25,  # TODO check
                        'v2/private/funding/prev-funding': 25,
                        'v2/private/funding/predicted-funding': 25,
                        'v2/private/account/api-key': 5,
                        'v2/private/account/lcp': 1,
                        'v2/private/wallet/balance': 25,  # 120 per minute = 2 per second => cost = 50 / 2 = 25
                        'v2/private/wallet/fund/records': 25,
                        'v2/private/wallet/withdraw/list': 25,
                        'v2/private/exchange-order/list': 1,
                        # linear swap USDT
                        'private/linear/order/list': 5,  # 600 per minute = 10 per second => cost = 50 / 10 =  5
                        'private/linear/order/search': 5,
                        'private/linear/stop-order/list': 5,
                        'private/linear/stop-order/search': 5,
                        'private/linear/position/list': 25,
                        'private/linear/trade/execution/list': 25,
                        'private/linear/trade/closed-pnl/list': 25,
                        'public/linear/risk-limit': 1,
                        'private/linear/funding/predicted-funding': 25,
                        'private/linear/funding/prev-funding': 25,
                        # inverse futures
                        'futures/private/order/list': 5,
                        'futures/private/order': 5,
                        'futures/private/stop-order/list': 5,
                        'futures/private/stop-order': 5,
                        'futures/private/position/list': 25,
                        'futures/private/execution/list': 25,
                        'futures/private/trade/closed-pnl/list': 1,
                        # spot
                        'spot/v1/account': 2.5,
                        'spot/v1/order': 2.5,
                        'spot/v1/open-orders': 2.5,
                        'spot/v1/history-orders': 2.5,
                        'spot/v1/myTrades': 2.5,
                        'spot/v1/cross-margin/order': 10,
                        'spot/v1/cross-margin/accounts/balance': 10,
                        'spot/v1/cross-margin/loan-info': 10,
                        'spot/v1/cross-margin/repay/history': 10,
                        '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,
                        # account
                        'asset/v1/private/transfer/list': 50,  # 60 per minute = 1 per second => cost = 50 / 1 = 50
                        'asset/v3/private/transfer/inter-transfer/list/query': 0.84,  # 60/s
                        'asset/v1/private/sub-member/transfer/list': 50,
                        'asset/v3/private/transfer/sub-member/list/query': 0.84,  # 60/s
                        'asset/v3/private/transfer/sub-member-transfer/list/query': 0.84,  # 60/s
                        'asset/v3/private/transfer/universal-transfer/list/query': 0.84,  # 60/s
                        'asset/v1/private/sub-member/member-ids': 50,
                        'asset/v1/private/deposit/record/query': 50,
                        'asset/v1/private/withdraw/record/query': 25,
                        'asset/v1/private/coin-info/query': 25,
                        'asset/v3/private/coin-info/query': 25,  # 2/s
                        'asset/v1/private/asset-info/query': 50,
                        'asset/v1/private/deposit/address': 100,
                        'asset/v3/private/deposit/address/query': 0.17,  # 300/s
                        'asset/v1/private/universal/transfer/list': 50,
                        'contract/v3/private/copytrading/order/list': 1,
                        'contract/v3/private/copytrading/position/list': 1,
                        'contract/v3/private/copytrading/wallet/balance': 1,
                        '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,
                        'asset/v2/private/exchange/exchange-order-all': 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
                        'asset/v3/private/transfer/transfer-coin/list/query': 0.84,  # 60/s
                        'asset/v3/private/transfer/account-coin/balance/query': 0.84,  # 60/s
                        'asset/v3/private/transfer/asset-info/query': 0.84,  # 60/s
                        'asset/v3/public/deposit/allowed-deposit-list/query': 0.17,  # 300/s
                        'asset/v3/private/deposit/record/query': 0.17,  # 300/s
                        'asset/v3/private/withdraw/record/query': 0.17,  # 300/s
                        # v5
                        'v5/order/history': 2.5,
                        'v5/order/spot-borrow-check': 2.5,
                        'v5/order/realtime': 2.5,
                        'v5/position/list': 2.5,
                        'v5/execution/list': 2.5,
                        'v5/position/closed-pnl': 2.5,
                        'v5/account/wallet-balance': 2.5,
                        'v5/account/borrow-history': 2.5,
                        'v5/account/collateral-info': 2.5,
                        'v5/account/mmp-state': 2.5,
                        'v5/asset/coin-greeks': 2.5,
                        'v5/account/info': 2.5,
                        'v5/account/transaction-log': 2.5,
                        'v5/account/fee-rate': 1,
                        'v5/asset/exchange/order-record': 2.5,
                        'v5/asset/delivery-record': 2.5,
                        'v5/asset/settlement-record': 2.5,
                        'v5/asset/transfer/query-asset-info': 2.5,
                        'v5/asset/transfer/query-account-coin-balance': 2.5,
                        'v5/asset/transfer/query-transfer-coin-list': 2.5,
                        'v5/asset/transfer/query-inter-transfer-list': 2.5,
                        'v5/asset/transfer/query-sub-member-list': 2.5,
                        'v5/asset/transfer/query-universal-transfer-list': 1,
                        'v5/asset/deposit/query-allowed-list': 2.5,
                        'v5/asset/deposit/query-record': 2.5,
                        'v5/asset/deposit/query-sub-member-record': 2.5,
                        'v5/asset/deposit/query-address': 2.5,
                        'v5/asset/deposit/query-sub-member-address': 2.5,
                        'v5/asset/deposit/query-internal-record': 2.5,
                        'v5/asset/coin/query-info': 2.5,
                        'v5/asset/withdraw/query-record': 2.5,
                        'v5/asset/withdraw/withdrawable-amount': 2.5,
                        'v5/asset/transfer/query-account-coins-balance': 2.5,
                        # user
                        'v5/user/query-sub-members': 10,
                        'v5/user/query-api': 10,
                    },
                    'post': {
                        # inverse swap
                        'v2/private/order/create': 30,
                        'v2/private/order/cancel': 30,
                        'v2/private/order/cancelAll': 300,  # 100 per minute + 'consumes 10 requests'
                        'v2/private/order/replace': 30,
                        'v2/private/stop-order/create': 30,
                        'v2/private/stop-order/cancel': 30,
                        'v2/private/stop-order/cancelAll': 300,
                        'v2/private/stop-order/replace': 30,
                        'v2/private/position/change-position-margin': 40,
                        'v2/private/position/trading-stop': 40,
                        'v2/private/position/leverage/save': 40,
                        'v2/private/tpsl/switch-mode': 40,
                        'v2/private/position/switch-isolated': 2.5,
                        'v2/private/position/risk-limit': 2.5,
                        'v2/private/position/switch-mode': 2.5,
                        # linear swap USDT
                        'private/linear/order/create': 30,  # 100 per minute = 1.666 per second => cost = 50 / 1.6666 = 30
                        'private/linear/order/cancel': 30,
                        'private/linear/order/cancel-all': 300,  # 100 per minute + 'consumes 10 requests'
                        'private/linear/order/replace': 30,
                        'private/linear/stop-order/create': 30,
                        'private/linear/stop-order/cancel': 30,
                        'private/linear/stop-order/cancel-all': 300,
                        'private/linear/stop-order/replace': 30,
                        'private/linear/position/set-auto-add-margin': 40,
                        'private/linear/position/switch-isolated': 40,
                        'private/linear/position/switch-mode': 40,
                        'private/linear/tpsl/switch-mode': 2.5,
                        'private/linear/position/add-margin': 40,
                        'private/linear/position/set-leverage': 40,  # 75 per minute = 1.25 per second => cost = 50 / 1.25 = 40
                        'private/linear/position/trading-stop': 40,
                        'private/linear/position/set-risk': 2.5,
                        # inverse futures
                        'futures/private/order/create': 30,
                        'futures/private/order/cancel': 30,
                        'futures/private/order/cancelAll': 30,
                        'futures/private/order/replace': 30,
                        'futures/private/stop-order/create': 30,
                        'futures/private/stop-order/cancel': 30,
                        'futures/private/stop-order/cancelAll': 30,
                        'futures/private/stop-order/replace': 30,
                        'futures/private/position/change-position-margin': 40,
                        'futures/private/position/trading-stop': 40,
                        'futures/private/position/leverage/save': 40,
                        'futures/private/position/switch-mode': 40,
                        'futures/private/tpsl/switch-mode': 40,
                        'futures/private/position/switch-isolated': 40,
                        'futures/private/position/risk-limit': 2.5,
                        # spot
                        'spot/v1/order': 2.5,
                        'spot/v1/cross-margin/loan': 10,
                        'spot/v1/cross-margin/repay': 10,
                        '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/v1/private/transfer': 150,  # 20 per minute = 0.333 per second => cost = 50 / 0.3333 = 150
                        'asset/v3/private/transfer/inter-transfer': 2.5,  # 20/s
                        'asset/v1/private/sub-member/transfer': 150,
                        'asset/v1/private/withdraw': 50,
                        'asset/v3/private/withdraw/create': 1,  # 10/s
                        'asset/v1/private/withdraw/cancel': 50,
                        'asset/v3/private/withdraw/cancel': 0.84,  # 60/s
                        'asset/v1/private/transferable-subs/save': 3000,
                        'asset/v1/private/universal/transfer': 1500,
                        'asset/v3/private/transfer/sub-member-transfer': 2.5,  # 20/s
                        'asset/v3/private/transfer/transfer-sub-member-save': 2.5,  # 20/s
                        'asset/v3/private/transfer/universal-transfer': 2.5,  # 20/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
                        # USDC endpoints
                        # option USDC
                        'option/usdc/openapi/private/v1/place-order': 2.5,
                        'option/usdc/openapi/private/v1/batch-place-order': 2.5,
                        'option/usdc/openapi/private/v1/replace-order': 2.5,
                        'option/usdc/openapi/private/v1/batch-replace-orders': 2.5,
                        'option/usdc/openapi/private/v1/cancel-order': 2.5,
                        'option/usdc/openapi/private/v1/batch-cancel-orders': 2.5,
                        'option/usdc/openapi/private/v1/cancel-all': 2.5,
                        'option/usdc/openapi/private/v1/query-active-orders': 2.5,
                        'option/usdc/openapi/private/v1/query-order-history': 2.5,
                        'option/usdc/openapi/private/v1/execution-list': 2.5,
                        'option/usdc/openapi/private/v1/query-transaction-log': 2.5,
                        'option/usdc/openapi/private/v1/query-wallet-balance': 2.5,
                        'option/usdc/openapi/private/v1/query-asset-info': 2.5,
                        'option/usdc/openapi/private/v1/query-margin-info': 2.5,
                        'option/usdc/openapi/private/v1/query-position': 2.5,
                        'option/usdc/openapi/private/v1/query-delivery-list': 2.5,
                        'option/usdc/openapi/private/v1/query-position-exp-date': 2.5,
                        'option/usdc/openapi/private/v1/mmp-modify': 2.5,
                        'option/usdc/openapi/private/v1/mmp-reset': 2.5,
                        # perpetual swap USDC
                        'perpetual/usdc/openapi/private/v1/place-order': 2.5,
                        'perpetual/usdc/openapi/private/v1/replace-order': 2.5,
                        'perpetual/usdc/openapi/private/v1/cancel-order': 2.5,
                        'perpetual/usdc/openapi/private/v1/cancel-all': 2.5,
                        'perpetual/usdc/openapi/private/v1/position/leverage/save': 2.5,
                        'option/usdc/openapi/private/v1/session-settlement': 2.5,
                        'option/usdc/private/asset/account/setMarginMode': 2.5,
                        'perpetual/usdc/openapi/public/v1/risk-limit/list': 2.5,
                        'perpetual/usdc/openapi/private/v1/position/set-risk-limit': 2.5,
                        'perpetual/usdc/openapi/private/v1/predicted-funding': 2.5,
                        'contract/v3/private/copytrading/order/create': 2.5,
                        'contract/v3/private/copytrading/order/cancel': 2.5,
                        'contract/v3/private/copytrading/order/close': 2.5,
                        'contract/v3/private/copytrading/position/close': 2.5,
                        'contract/v3/private/copytrading/position/set-leverage': 2.5,
                        'contract/v3/private/copytrading/wallet/transfer': 2.5,
                        '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': 2.5,
                        'unified/v3/private/order/replace': 2.5,
                        'unified/v3/private/order/cancel': 2.5,
                        'unified/v3/private/order/create-batch': 2.5,
                        'unified/v3/private/order/replace-batch': 2.5,
                        'unified/v3/private/order/cancel-batch': 2.5,
                        'unified/v3/private/order/cancel-all': 2.5,
                        '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
                        'v5/order/create': 2.5,
                        'v5/order/amend': 2.5,
                        'v5/order/cancel': 2.5,
                        'v5/order/cancel-all': 2.5,
                        'v5/order/create-batch': 2.5,
                        'v5/order/amend-batch': 2.5,
                        'v5/order/cancel-batch': 2.5,
                        'v5/order/disconnected-cancel-all': 2.5,
                        'v5/position/set-leverage': 2.5,
                        'v5/position/set-tpsl-mode': 2.5,
                        'v5/position/set-risk-limit': 2.5,
                        'v5/position/trading-stop': 2.5,
                        'v5/account/upgrade-to-uta': 2.5,
                        'v5/account/set-margin-mode': 2.5,
                        'v5/asset/transfer/inter-transfer': 2.5,
                        'v5/asset/transfer/save-transfer-sub-member': 2.5,
                        'v5/asset/transfer/universal-transfer': 2.5,
                        'v5/asset/deposit/deposit-to-account': 2.5,
                        'v5/asset/withdraw/create': 2.5,
                        'v5/asset/withdraw/cancel': 2.5,
                        'v5/spot-lever-token/purchase': 2.5,
                        'v5/spot-lever-token/redeem': 2.5,
                        'v5/spot-lever-token/order-record': 2.5,
                        'v5/spot-margin-trade/switch-mode': 2.5,
                        'v5/spot-margin-trade/set-leverage': 2.5,
                        # user
                        'v5/user/create-sub-member': 10,
                        'v5/user/create-sub-api': 10,
                        'v5/user/frozen-sub-member': 10,
                        'v5/user/update-api': 10,
                        'v5/user/update-sub-api': 10,
                        'v5/user/delete-api': 10,
                        'v5/user/delete-sub-api': 10,
                    },
                    'delete': {
                        # spot
                        'spot/v1/order': 2.5,
                        'spot/v1/order/fast': 2.5,
                        'spot/order/batch-cancel': 2.5,
                        'spot/order/batch-fast-cancel': 2.5,
                        'spot/order/batch-cancel-by-ids': 2.5,
                    },
                },
            },
            'httpExceptions': {
                '403': RateLimitExceeded,  # Forbidden -- You request too many times
            },
            'exceptions': {
                # Uncodumented explanation of error strings:
                # - oc_diff: order cost needed to place self 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': {
                    '-10009': BadRequest,  # {"ret_code":-10009,"ret_msg":"Invalid period!","result":null,"token":null}
                    '-1004': BadRequest,  # {"ret_code":-1004,"ret_msg":"Missing required parameter \u0027symbol\u0027","ext_code":null,"ext_info":null,"result":null}
                    '-1021': BadRequest,  # {"ret_code":-1021,"ret_msg":"Timestamp for self request is outside of the recvWindow.","ext_code":null,"ext_info":null,"result":null}
                    '-1103': BadRequest,  # An unknown parameter was sent.
                    '-1140': InvalidOrder,  # {"ret_code":-1140,"ret_msg":"Transaction amount lower than the minimum.","result":{},"ext_code":"","ext_info":null,"time_now":"1659204910.248576"}
                    '-1197': InvalidOrder,  # {"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":{},"ext_code":"","ext_info":null,"time_now":"1659204531.979680"}
                    '-2013': InvalidOrder,  # {"ret_code":-2013,"ret_msg":"Order does not exist.","ext_code":null,"ext_info":null,"result":null}
                    '-2015': AuthenticationError,  # Invalid API-key, IP, or permissions for action.
                    '-6017': BadRequest,  # Repayment amount has exceeded the total liability
                    '-6025': BadRequest,  # Amount to borrow cannot be lower than the min. amount to borrow(per transaction)
                    '-6029': BadRequest,  # Amount to borrow has exceeded the user's estimated max amount to borrow
                    '5004': ExchangeError,  # {"retCode":5004,"retMsg":"Server Timeout","result":null,"retExtInfo":{},"time":1667577060106}
                    '7001': BadRequest,  # {"retCode":7001,"retMsg":"request params type error"}
                    '10001': BadRequest,  # parameter error
                    '10002': InvalidNonce,  # request expired, check your timestamp and recv_window
                    '10003': AuthenticationError,  # Invalid apikey
                    '10004': AuthenticationError,  # invalid sign
                    '10005': PermissionDenied,  # permission denied for current apikey
                    '10006': RateLimitExceeded,  # too many requests
                    '10007': AuthenticationError,  # api_key not found in your request parameters
                    '10008': AuthenticationError,  # User had been banned
                    '10009': AuthenticationError,  # IP had been banned
                    '10010': PermissionDenied,  # request ip mismatch
                    '10014': BadRequest,  # Request is duplicate
                    '10016': ExchangeError,  # {"retCode":10016,"retMsg":"System error. Please try again later."}
                    '10017': BadRequest,  # request path not found or request method is invalid
                    '10018': RateLimitExceeded,  # exceed ip rate limit
                    '10020': PermissionDenied,  # {"retCode":10020,"retMsg":"your account is not a unified margin account, please update your account","result":null,"retExtInfo":null,"time":1664783731123}
                    '10024': PermissionDenied,  # Compliance rules triggered
                    '10027': PermissionDenied,  # Trading Banned
                    '10028': PermissionDenied,  # The API can only be accessed by unified account users.
                    '10029': PermissionDenied,  # The requested symbol is invalid, please check symbol whitelist
                    '12201': BadRequest,  # {"retCode":12201,"retMsg":"Invalid orderCategory parameter.","result":{},"retExtInfo":null,"time":1666699391220}
                    '100028': PermissionDenied,  # The API cannot be accessed by unified account users.
                    '110001': InvalidOrder,  # Order does not exist
                    '110003': InvalidOrder,  # Order price is out of permissible range
                    '110004': InsufficientFunds,  # Insufficient wallet balance
                    '110005': InvalidOrder,  # position status
                    '110006': InsufficientFunds,  # cannot afford estimated position_margin
                    '110007': InsufficientFunds,  # {"retCode":110007,"retMsg":"ab not enough for new order","result":{},"retExtInfo":{},"time":1668838414793}
                    '110008': InvalidOrder,  # Order has been finished or canceled
                    '110009': InvalidOrder,  # The number of stop orders exceeds maximum limit allowed
                    '110010': InvalidOrder,  # Order already cancelled
                    '110011': InvalidOrder,  # Any adjustments made will trigger immediate liquidation
                    '110012': InsufficientFunds,  # Available balance not enough
                    '110013': BadRequest,  # Due to risk limit, cannot set leverage
                    '110014': InsufficientFunds,  # Available balance not enough to add margin
                    '110015': BadRequest,  # the position is in cross_margin
                    '110016': InvalidOrder,  # Requested quantity of contracts exceeds risk limit, please adjust your risk limit level before trying again
                    '110017': InvalidOrder,  # Reduce-only rule not satisfied
                    '110018': BadRequest,  # userId illegal
                    '110019': InvalidOrder,  # orderId illegal
                    '110020': InvalidOrder,  # number of active orders greater than 500
                    '110021': InvalidOrder,  # Open Interest exceeded
                    '110022': InvalidOrder,  # qty has been limited, cannot modify the order to add qty
                    '110023': InvalidOrder,  # This contract only supports position reduction operation, please contact customer service for details
                    '110024': InvalidOrder,  # You have an existing position, so position mode cannot be switched
                    '110025': InvalidOrder,  # Position mode is not modified
                    '110026': InvalidOrder,  # Cross/isolated margin mode is not modified
                    '110027': InvalidOrder,  # Margin is not modified
                    '110028': InvalidOrder,  # Open orders exist, so you cannot change position mode
                    '110029': InvalidOrder,  # Hedge mode is not available for self symbol
                    '110030': InvalidOrder,  # Duplicate orderId
                    '110031': InvalidOrder,  # risk limit info does not exists
                    '110032': InvalidOrder,  # Illegal order
                    '110033': InvalidOrder,  # Margin cannot be set without open position
                    '110034': InvalidOrder,  # There is no net position
                    '110035': InvalidOrder,  # Cancel order is not completed before liquidation
                    '110036': InvalidOrder,  # Cross margin mode is not allowed to change leverage
                    '110037': InvalidOrder,  # User setting list does not have self symbol
                    '110038': InvalidOrder,  # Portfolio margin mode is not allowed to change leverage
                    '110039': InvalidOrder,  # Maintain margin rate is too high, which may trigger liquidation
                    '110040': InvalidOrder,  # Order will trigger forced liquidation, please resubmit the order
                    '110041': InvalidOrder,  # Skip liquidation is not allowed when a position or maker order exists
                    '110042': InvalidOrder,  # Pre-delivery status can only reduce positions
                    '110043': BadRequest,  # Set leverage not modified
                    '110044': InsufficientFunds,  # Insufficient available margin
                    '110045': InsufficientFunds,  # Insufficient wallet balance
                    '110046': BadRequest,  # Any adjustments made will trigger immediate liquidation
                    '110047': BadRequest,  # Risk limit cannot be adjusted due to insufficient available margin
                    '110048': BadRequest,  # Risk limit cannot be adjusted as the current/expected position value held exceeds the revised risk limit
                    '110049': BadRequest,  # Tick notes can only be numbers
                    '110050': BadRequest,  # Coin is not in the range of selected
                    '110051': InsufficientFunds,  # The user's available balance cannot cover the lowest price of the current market
                    '110052': InsufficientFunds,  # User's available balance is insufficient to set a price
                    '110053': InsufficientFunds,  # The user's available balance cannot cover the current market price and upper limit price
                    '110054': InvalidOrder,  # This position has at least one take profit link order, so the take profit and stop loss mode cannot be switched
                    '110055': InvalidOrder,  # This position has at least one stop loss link order, so the take profit and stop loss mode cannot be switched
                    '110056': InvalidOrder,  # This position has at least one trailing stop link order, so the take profit and stop loss mode cannot be switched
                    '110057': InvalidOrder,  # Conditional order or limit order contains TP/SL related params
                    '110058': InvalidOrder,  # Insufficient number of remaining position size to set take profit and stop loss
                    '110059': 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': BadRequest,  # Under full TP/SL mode, it is not allowed to modify TP/SL
                    '110061': BadRequest,  # Under partial TP/SL mode, TP/SL set more than 20
                    '110062': BadRequest,  # Institution MMP profile not found.
                    '110063': ExchangeError,  # Settlement in progress! xxx not available for trades.
                    '110064': InvalidOrder,  # The number of contracts modified cannot be less than or equal to the filled quantity
                    '110065': PermissionDenied,  # MMP hasn't yet been enabled for your account. Please contact your BD manager.
                    '110066': ExchangeError,  # No trading is allowed at the current time
                    '110067': PermissionDenied,  # unified account is not support
                    '110068': PermissionDenied,  # Leveraged user trading is not allowed
                    '110069': PermissionDenied,  # Do not allow OTC lending users to trade
                    '110070': InvalidOrder,  # ETP symbols are not allowed to be traded
                    '110071': 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': InvalidOrder,  # OrderLinkedID is duplicate
                    '110073': ExchangeError,  # Set margin mode failed
                    '130006': InvalidOrder,  # {"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': InsufficientFunds,  # {"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} |  {"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': InvalidOrder,  # {"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': InsufficientFunds,  # {"retCode":131001,"retMsg":"the available balance is not sufficient to cover the handling fee","result":{},"retExtInfo":{},"time":1666892821245}
                    '131084': ExchangeError,  # Withdraw failed because of Uta Upgrading
                    '131200': ExchangeError,  # Service error
                    '131201': ExchangeError,  # Internal error
                    '131202': BadRequest,  # Invalid memberId
                    '131203': BadRequest,  # Request parameter error
                    '131204': BadRequest,  # Account info error
                    '131205': BadRequest,  # Query transfer error
                    '131206': ExchangeError,  # Fail to transfer
                    '131207': BadRequest,  # Account not exist
                    '131208': ExchangeError,  # Forbid transfer
                    '131209': BadRequest,  # Get subMember relation error
                    '131210': BadRequest,  # Amount accuracy error
                    '131211': BadRequest,  # fromAccountType can't be the same as toAccountType
                    '131212': InsufficientFunds,  # Insufficient balance
                    '131213': BadRequest,  # TransferLTV check error
                    '131214': BadRequest,  # TransferId exist
                    '131215': BadRequest,  # Amount error
                    '131216': ExchangeError,  # Query balance error
                    '131217': ExchangeError,  # Risk check error
                    '131002': BadRequest,  # Parameter error
                    '131003': ExchangeError,  # Interal error
                    '131004': AuthenticationError,  # KYC needed
                    '131085': InsufficientFunds,  # Withdrawal amount is greater than your availale balance(the deplayed withdrawal is triggered)
                    '131086': BadRequest,  # Withdrawal amount exceeds risk limit(the risk limit of margin trade is triggered)
                    '131088': BadRequest,  # The withdrawal amount exceeds the remaining withdrawal limit of your identity verification level. The current available amount for withdrawal : %s
                    '131089': BadRequest,  # User sensitive operation, withdrawal is prohibited within 24 hours
                    '131090': ExchangeError,  # User withdraw has been banned
                    '131091': ExchangeError,  # Blocked login status does not allow withdrawals
                    '131092': ExchangeError,  # User status is abnormal
                    '131093': ExchangeError,  # The withdrawal address is not in the whitelist
                    '131094': BadRequest,  # UserId is not in the whitelist
                    '131095': BadRequest,  # Withdrawl amount exceeds the 24 hour platform limit
                    '131096': BadRequest,  # Withdraw amount does not satify the lower limit or upper limit
                    '131097': ExchangeError,  # Withdrawal of self currency has been closed
                    '131098': ExchangeError,  # Withdrawal currently is not availble from new address
                    '131099': ExchangeError,  # Hot wallet status can cancel the withdraw
                    '140003': InvalidOrder,  # Order price is out of permissible range
                    '140004': InsufficientFunds,  # Insufficient wallet balance
                    '140005': InvalidOrder,  # position status
                    '140006': InsufficientFunds,  # cannot afford estimated position_margin
                    '140007': InsufficientFunds,  # Insufficient available balance
                    '140008': InvalidOrder,  # Order has been finished or canceled
                    '140009': InvalidOrder,  # The number of stop orders exceeds maximum limit allowed
                    '140010': InvalidOrder,  # Order already cancelled
                    '140011': InvalidOrder,  # Any adjustments made will trigger immediate liquidation
                    '140012': InsufficientFunds,  # Available balance not enough
                    '140013': BadRequest,  # Due to risk limit, cannot set leverage
                    '140014': InsufficientFunds,  # Available balance not enough to add margin
                    '140015': InvalidOrder,  # the position is in cross_margin
                    '140016': InvalidOrder,  # Requested quantity of contracts exceeds risk limit, please adjust your risk limit level before trying again
                    '140017': InvalidOrder,  # Reduce-only rule not satisfied
                    '140018': BadRequest,  # userId illegal
                    '140019': InvalidOrder,  # orderId illegal
                    '140020': InvalidOrder,  # number of active orders greater than 500
                    '140021': InvalidOrder,  # Open Interest exceeded
                    '140022': InvalidOrder,  # qty has been limited, cannot modify the order to add qty
                    '140023': InvalidOrder,  # This contract only supports position reduction operation, please contact customer service for details
                    '140024': BadRequest,  # You have an existing position, so position mode cannot be switched
                    '140025': BadRequest,  # Position mode is not modified
                    '140026': BadRequest,  # Cross/isolated margin mode is not modified
                    '140027': BadRequest,  # Margin is not modified
                    '140028': InvalidOrder,  # Open orders exist, so you cannot change position mode
                    '140029': BadRequest,  # Hedge mode is not available for self symbol
                    '140030': InvalidOrder,  # Duplicate orderId
                    '140031': BadRequest,  # risk limit info does not exists
                    '140032': InvalidOrder,  # Illegal order
                    '140033': InvalidOrder,  # Margin cannot be set without open position
                    '140034': InvalidOrder,  # There is no net position
                    '140035': InvalidOrder,  # Cancel order is not completed before liquidation
                    '140036': BadRequest,  # Cross margin mode is not allowed to change leverage
                    '140037': InvalidOrder,  # User setting list does not have self symbol
                    '140038': BadRequest,  # Portfolio margin mode is not allowed to change leverage
                    '140039': BadRequest,  # Maintain margin rate is too high, which may trigger liquidation
                    '140040': InvalidOrder,  # Order will trigger forced liquidation, please resubmit the order
                    '140041': InvalidOrder,  # Skip liquidation is not allowed when a position or maker order exists
                    '140042': InvalidOrder,  # Pre-delivery status can only reduce positions
                    '140043': BadRequest,  # Set leverage not modified
                    '140044': InsufficientFunds,  # Insufficient available margin
                    '140045': InsufficientFunds,  # Insufficient wallet balance
                    '140046': BadRequest,  # Any adjustments made will trigger immediate liquidation
                    '140047': BadRequest,  # Risk limit cannot be adjusted due to insufficient available margin
                    '140048': BadRequest,  # Risk limit cannot be adjusted as the current/expected position value held exceeds the revised risk limit
                    '140049': BadRequest,  # Tick notes can only be numbers
                    '140050': InvalidOrder,  # Coin is not in the range of selected
                    '140051': InsufficientFunds,  # The user's available balance cannot cover the lowest price of the current market
                    '140052': InsufficientFunds,  # User's available balance is insufficient to set a price
                    '140053': InsufficientFunds,  # The user's available balance cannot cover the current market price and upper limit price
                    '140054': InvalidOrder,  # This position has at least one take profit link order, so the take profit and stop loss mode cannot be switched
                    '140055': InvalidOrder,  # This position has at least one stop loss link order, so the take profit and stop loss mode cannot be switched
                    '140056': InvalidOrder,  # This position has at least one trailing stop link order, so the take profit and stop loss mode cannot be switched
                    '140057': InvalidOrder,  # Conditional order or limit order contains TP/SL related params
                    '140058': InvalidOrder,  # Insufficient number of remaining position size to set take profit and stop loss
                    '140059': 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': BadRequest,  # Under full TP/SL mode, it is not allowed to modify TP/SL
                    '140061': BadRequest,  # Under partial TP/SL mode, TP/SL set more than 20
                    '140062': BadRequest,  # Institution MMP profile not found.
                    '140063': ExchangeError,  # Settlement in progress! xxx not available for trades.
                    '140064': InvalidOrder,  # The number of contracts modified cannot be less than or equal to the filled quantity
                    '140065': PermissionDenied,  # MMP hasn't yet been enabled for your account. Please contact your BD manager.
                    '140066': ExchangeError,  # No trading is allowed at the current time
                    '140067': PermissionDenied,  # unified account is not support
                    '140068': PermissionDenied,  # Leveraged user trading is not allowed
                    '140069': PermissionDenied,  # Do not allow OTC lending users to trade
                    '140070': InvalidOrder,  # ETP symbols are not allowed to be traded
                    '170001': ExchangeError,  # Internal error.
                    '170007': RequestTimeout,  # Timeout waiting for response from backend server.
                    '170005': InvalidOrder,  # Too many new orders; current limit is %s orders per %s.
                    '170031': ExchangeError,  # The feature has been suspended
                    '170032': ExchangeError,  # Network error. Please try again later
                    '170033': InsufficientFunds,  # margin Insufficient account balance
                    '170034': InsufficientFunds,  # Liability over flow in spot leverage trade!
                    '170035': BadRequest,  # Submitted to the system for processing!
                    '170036': BadRequest,  # You haven't enabled Cross Margin Trading yet. To do so, please head to the PC trading site or the Bybit app
                    '170037': BadRequest,  # Cross Margin Trading not yet supported by the selected coin
                    '170105': BadRequest,  # Parameter '%s' was empty.
                    '170115': InvalidOrder,  # Invalid timeInForce.
                    '170116': InvalidOrder,  # Invalid orderType.
                    '170117': InvalidOrder,  # Invalid side.
                    '170121': InvalidOrder,  # Invalid symbol.
                    '170130': BadRequest,  # Data sent for paramter '%s' is not valid.
                    '170131': InsufficientFunds,  # Balance insufficient
                    '170132': InvalidOrder,  # Order price too high.
                    '170133': InvalidOrder,  # Order price lower than the minimum.
                    '170134': InvalidOrder,  # Order price decimal too long.
                    '170135': InvalidOrder,  # Order quantity too large.
                    '170136': InvalidOrder,  # Order quantity lower than the minimum.
                    '170137': InvalidOrder,  # Order volume decimal too long
                    '170139': InvalidOrder,  # Order has been filled.
                    '170140': InvalidOrder,  # Transaction amount lower than the minimum.
                    '170124': InvalidOrder,  # Order amount too large.
                    '170141': InvalidOrder,  # Duplicate clientOrderId
                    '170142': InvalidOrder,  # Order has been canceled
                    '170143': InvalidOrder,  # Cannot be found on order book
                    '170144': InvalidOrder,  # Order has been locked
                    '170145': InvalidOrder,  # This order type does not support cancellation
                    '170146': InvalidOrder,  # Order creation timeout
                    '170147': InvalidOrder,  # Order cancellation timeout
                    '170148': InvalidOrder,  # Market order amount decimal too long
                    '170149': ExchangeError,  # Create order failed
                    '170150': ExchangeError,  # Cancel order failed
                    '170151': InvalidOrder,  # The trading pair is not open yet
                    '170157': InvalidOrder,  # The trading pair is not available for api trading
                    '170159': InvalidOrder,  # Market Order is not supported within the first %s minutes of newly launched pairs due to risk control.
                    '170190': InvalidOrder,  # Cancel order has been finished
                    '170191': InvalidOrder,  # Can not cancel order, please try again later
                    '170192': InvalidOrder,  # Order price cannot be higher than %s .
                    '170193': InvalidOrder,  # Buy order price cannot be higher than %s.
                    '170194': InvalidOrder,  # Sell order price cannot be lower than %s.
                    '170195': InvalidOrder,  # Please note that your order may not be filled
                    '170196': InvalidOrder,  # Please note that your order may not be filled
                    '170197': InvalidOrder,  # Your order quantity to buy is too large. The filled price may deviate significantly from the market price. Please try again
                    '170198': InvalidOrder,  # Your order quantity to sell is too large. The filled price may deviate significantly from the market price. Please try again
                    '170199': InvalidOrder,  # Your order quantity to buy is too large. The filled price may deviate significantly from the nav. Please try again.
                    '170200': InvalidOrder,  # Your order quantity to sell is too large. The filled price may deviate significantly from the nav. Please try again.
                    '170221': BadRequest,  # This coin does not exist.
                    '170222': RateLimitExceeded,  # Too many requests in self time frame.
                    '170223': InsufficientFunds,  # Your Spot Account with Institutional Lending triggers an alert or liquidation.
                    '170224': PermissionDenied,  # You're not a user of the Innovation Zone.
                    '170226': InsufficientFunds,  # Your Spot Account for Margin Trading is being liquidated.
                    '170227': ExchangeError,  # This feature is not supported.
                    '170228': InvalidOrder,  # The purchase amount of each order exceeds the estimated maximum purchase amount.
                    '170229': InvalidOrder,  # The sell quantity per order exceeds the estimated maximum sell quantity.
                    '170234': ExchangeError,  # System Error
                    '170210': InvalidOrder,  # New order rejected.
                    '170213': OrderNotFound,  # Order does not exist.
                    '170217': InvalidOrder,  # Only LIMIT-MAKER order is supported for the current pair.
                    '170218': InvalidOrder,  # The LIMIT-MAKER order is rejected due to invalid price.
                    '170010': InvalidOrder,  # Purchase failed: Exceed the maximum position limit of leveraged tokens, the current available limit is %s USDT
                    '170011': InvalidOrder,  # "Purchase failed: Exceed the maximum position limit of innovation tokens,
                    '170019': InvalidOrder,  # the current available limit is replaceKey0 USDT"
                    '170201': PermissionDenied,  # Your account has been restricted for trades. If you have any questions, please email us at support@bybit.com
                    '170202': InvalidOrder,  # Invalid orderFilter parameter.
                    '170203': InvalidOrder,  # Please enter the TP/SL price.
                    '170204': InvalidOrder,  # trigger price cannot be higher than 110% price.
                    '170206': InvalidOrder,  # trigger price cannot be lower than 90% of qty.
                    '175000': InvalidOrder,  # The serialNum is already in use.
                    '175001': InvalidOrder,  # Daily purchase limit has been exceeded. Please try again later.
                    '175002': InvalidOrder,  # There's a large number of purchase orders. Please try again later.
                    '175003': InsufficientFunds,  # Insufficient available balance. Please make a deposit and try again.
                    '175004': InvalidOrder,  # Daily redemption limit has been exceeded. Please try again later.
                    '175005': InvalidOrder,  # There's a large number of redemption orders. Please try again later.
                    '175006': InsufficientFunds,  # Insufficient available balance. Please make a deposit and try again.
                    '175007': InvalidOrder,  # Order not found.
                    '175008': InvalidOrder,  # Purchase period hasn't started yet.
                    '175009': InvalidOrder,  # Purchase amount has exceeded the upper limit.
                    '175010': PermissionDenied,  # You haven't passed the quiz yet! To purchase and/or redeem an LT, please complete the quiz first.
                    '175012': InvalidOrder,  # Redemption period hasn't started yet.
                    '175013': InvalidOrder,  # Redemption amount has exceeded the upper limit.
                    '175014': InvalidOrder,  # Purchase of the LT has been temporarily suspended.
                    '175015': InvalidOrder,  # Redemption of the LT has been temporarily suspended.
                    '175016': InvalidOrder,  # Invalid format. Please check the length and numeric precision.
                    '175017': InvalidOrder,  # Failed to place order：Exceed the maximum position limit of leveraged tokens, the current available limit is XXXX USDT
                    '175027': ExchangeError,  # Subscriptions and redemptions are temporarily unavailable while account upgrade is in progress
                    '176002': BadRequest,  # Query user account info error
                    '176004': BadRequest,  # Query order history start time exceeds end time
                    '176003': BadRequest,  # Query user loan history error
                    '176006': BadRequest,  # Repayment Failed
                    '176005': BadRequest,  # Failed to borrow
                    '176008': BadRequest,  # You haven't enabled Cross Margin Trading yet. To do so
                    '176007': BadRequest,  # User not found
                    '176010': BadRequest,  # Failed to locate the coins to borrow
                    '176009': BadRequest,  # You haven't enabled Cross Margin Trading yet. To do so
                    '176012': BadRequest,  # Pair not available
                    '176011': BadRequest,  # Cross Margin Trading not yet supported by the selected coin
                    '176014': BadRequest,  # Repeated repayment requests
                    '176013': BadRequest,  # Cross Margin Trading not yet supported by the selected pair
                    '176015': InsufficientFunds,  # Insufficient available balance
                    '176016': BadRequest,  # No repayment required
                    '176017': BadRequest,  # Repayment amount has exceeded the total liability
                    '176018': BadRequest,  # Settlement in progress
                    '176019': BadRequest,  # Liquidation in progress
                    '176020': BadRequest,  # Failed to locate repayment history
                    '176021': BadRequest,  # Repeated borrowing requests
                    '176022': BadRequest,  # Coins to borrow not generally available yet
                    '176023': BadRequest,  # Pair to borrow not generally available yet
                    '176024': BadRequest,  # Invalid user status
                    '176025': BadRequest,  # Amount to borrow cannot be lower than the min. amount to borrow(per transaction)
                    '176026': BadRequest,  # Amount to borrow cannot be larger than the max. amount to borrow(per transaction)
                    '176027': BadRequest,  # Amount to borrow cannot be higher than the max. amount to borrow per user
                    '176028': BadRequest,  # Amount to borrow has exceeded Bybit's max. amount to borrow
                    '176029': BadRequest,  # Amount to borrow has exceeded the user's estimated max. amount to borrow
                    '176030': BadRequest,  # Query user loan info error
                    '176031': BadRequest,  # Number of decimals has exceeded the maximum precision
                    '176034': BadRequest,  # The leverage ratio is out of range
                    '176035': PermissionDenied,  # Failed to close the leverage switch during liquidation
                    '176036': PermissionDenied,  # Failed to adjust leverage switch during forced liquidation
                    '176037': PermissionDenied,  # For non-unified transaction users, the operation failed
                    '176038': BadRequest,  # The spot leverage is closed and the current operation is not allowed
                    '176039': BadRequest,  # Borrowing, current operation is not allowed
                    '176040': BadRequest,  # There is a spot leverage order, and the adjustment of the leverage switch failed!
                    '181000': BadRequest,  # category is null
                    '181001': BadRequest,  # category only support linear or option or spot.
                    '181002': InvalidOrder,  # symbol is null.
                    '181003': InvalidOrder,  # side is null.
                    '181004': InvalidOrder,  # side only support Buy or Sell.
                    '182000': InvalidOrder,  # symbol related quote price is null
                    '20001': OrderNotFound,  # Order not exists
                    '20003': InvalidOrder,  # missing parameter side
                    '20004': InvalidOrder,  # invalid parameter side
                    '20005': InvalidOrder,  # missing parameter symbol
                    '20006': InvalidOrder,  # invalid parameter symbol
                    '20007': InvalidOrder,  # missing parameter order_type
                    '20008': InvalidOrder,  # invalid parameter order_type
                    '20009': InvalidOrder,  # missing parameter qty
                    '20010': InvalidOrder,  # qty must be greater than 0
                    '20011': InvalidOrder,  # qty must be an integer
                    '20012': InvalidOrder,  # qty must be greater than zero and less than 1 million
                    '20013': InvalidOrder,  # missing parameter price
                    '20014': InvalidOrder,  # price must be greater than 0
                    '20015': InvalidOrder,  # missing parameter time_in_force
                    '20016': InvalidOrder,  # invalid value for parameter time_in_force
                    '20017': InvalidOrder,  # missing parameter order_id
                    '20018': InvalidOrder,  # invalid date format
                    '20019': InvalidOrder,  # missing parameter stop_px
                    '20020': InvalidOrder,  # missing parameter base_price
                    '20021': InvalidOrder,  # missing parameter stop_order_id
                    '20022': BadRequest,  # missing parameter leverage
                    '20023': BadRequest,  # leverage must be a number
                    '20031': BadRequest,  # leverage must be greater than zero
                    '20070': BadRequest,  # missing parameter margin
                    '20071': BadRequest,  # margin must be greater than zero
                    '20084': BadRequest,  # order_id or order_link_id is required
                    '30001': BadRequest,  # order_link_id is repeated
                    '30003': InvalidOrder,  # qty must be more than the minimum allowed
                    '30004': InvalidOrder,  # qty must be less than the maximum allowed
                    '30005': InvalidOrder,  # price exceeds maximum allowed
                    '30007': InvalidOrder,  # price exceeds minimum allowed
                    '30008': InvalidOrder,  # invalid order_type
                    '30009': ExchangeError,  # no position found
                    '30010': InsufficientFunds,  # insufficient wallet balance
                    '30011': PermissionDenied,  # operation not allowed as position is undergoing liquidation
                    '30012': PermissionDenied,  # operation not allowed as position is undergoing ADL
                    '30013': PermissionDenied,  # position is in liq or adl status
                    '30014': InvalidOrder,  # invalid closing order, qty should not greater than size
                    '30015': InvalidOrder,  # invalid closing order, side should be opposite
                    '30016': ExchangeError,  # TS and SL must be cancelled first while closing position
                    '30017': InvalidOrder,  # estimated fill price cannot be lower than current Buy liq_price
                    '30018': InvalidOrder,  # estimated fill price cannot be higher than current Sell liq_price
                    '30019': InvalidOrder,  # cannot attach TP/SL params for non-zero position when placing non-opening position order
                    '30020': InvalidOrder,  # position already has TP/SL params
                    '30021': InvalidOrder,  # cannot afford estimated position_margin
                    '30022': InvalidOrder,  # estimated buy liq_price cannot be higher than current mark_price
                    '30023': InvalidOrder,  # estimated sell liq_price cannot be lower than current mark_price
                    '30024': InvalidOrder,  # cannot set TP/SL/TS for zero-position
                    '30025': InvalidOrder,  # trigger price should bigger than 10% of last price
                    '30026': InvalidOrder,  # price too high
                    '30027': InvalidOrder,  # price set for Take profit should be higher than Last Traded Price
                    '30028': InvalidOrder,  # price set for Stop loss should be between Liquidation price and Last Traded Price
                    '30029': InvalidOrder,  # price set for Stop loss should be between Last Traded Price and Liquidation price
                    '30030': InvalidOrder,  # price set for Take profit should be lower than Last Traded Price
                    '30031': InsufficientFunds,  # insufficient available balance for order cost
                    '30032': InvalidOrder,  # order has been filled or cancelled
                    '30033': RateLimitExceeded,  # The number of stop orders exceeds maximum limit allowed
                    '30034': OrderNotFound,  # no order found
                    '30035': RateLimitExceeded,  # too fast to cancel
                    '30036': ExchangeError,  # the expected position value after order execution exceeds the current risk limit
                    '30037': InvalidOrder,  # order already cancelled
                    '30041': ExchangeError,  # no position found
                    '30042': InsufficientFunds,  # insufficient wallet balance
                    '30043': InvalidOrder,  # operation not allowed as position is undergoing liquidation
                    '30044': InvalidOrder,  # operation not allowed as position is undergoing AD
                    '30045': InvalidOrder,  # operation not allowed as position is not normal status
                    '30049': InsufficientFunds,  # insufficient available balance
                    '30050': ExchangeError,  # any adjustments made will trigger immediate liquidation
                    '30051': ExchangeError,  # due to risk limit, cannot adjust leverage
                    '30052': ExchangeError,  # leverage can not less than 1
                    '30054': ExchangeError,  # position margin is invalid
                    '30057': ExchangeError,  # requested quantity of contracts exceeds risk limit
                    '30063': ExchangeError,  # reduce-only rule not satisfied
                    '30067': InsufficientFunds,  # insufficient available balance
                    '30068': ExchangeError,  # exit value must be positive
                    '30074': 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': 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': ExchangeError,  # {"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': BadRequest,  # Isolated not modified, see handleErrors below
                    '33004': AuthenticationError,  # apikey already expired
                    '34026': ExchangeError,  # the limit is no change
                    '34036': BadRequest,  # {"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': BadRequest,  # {"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': ExchangeError,  # Server error
                    '3400045': ExchangeError,  # Set margin mode failed
                    '3100116': BadRequest,  # {"retCode":3100116,"retMsg":"Order quantity below the lower limit 0.01.","result":null,"retExtMap":{"key0":"0.01"}}
                    '3100198': BadRequest,  # {"retCode":3100198,"retMsg":"orderLinkId can not be empty.","result":null,"retExtMap":{}}
                    '3200300': InsufficientFunds,  # {"retCode":3200300,"retMsg":"Insufficient margin balance.","result":null,"retExtMap":{}}
                },
                'broad': {
                    'Request timeout': RequestTimeout,  # {"retCode":10016,"retMsg":"Request timeout, please try again later","result":{},"retExtInfo":{},"time":1675307914985}
                    'unknown orderInfo': OrderNotFound,  # {"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': AuthenticationError,  # {"ret_code":10003,"ret_msg":"invalid api_key","ext_code":"","ext_info":"","result":null,"time_now":"1599547085.415797"}
                    # the below two issues are caused as described: issues/9149#issuecomment-1146559498, when response is such:  {"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': InsufficientFunds,
                    'new_oc': InsufficientFunds,
                    'openapi sign params error!': AuthenticationError,  # {"retCode":10001,"retMsg":"empty value: apiTimestamp[] apiKey[] apiSignature[xxxxxxxxxxxxxxxxxxxxxxx]: openapi sign params error!","result":null,"retExtInfo":null,"time":1664789597123}
                },
            },
            'precisionMode': TICK_SIZE,
            'options': {
                'createMarketBuyOrderRequiresPrice': True,
                '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
                'brokerId': 'CCXT',
                'accountsByType': {
                    'spot': 'SPOT',
                    'margin': 'SPOT',
                    'future': 'CONTRACT',
                    'swap': 'CONTRACT',
                    'option': 'OPTION',
                    'investment': 'INVESTMENT',
                    'unified': 'UNIFIED',
                    'funding': 'FUND',
                },
                'accountsById': {
                    'SPOT': 'spot',
                    'MARGIN': 'spot',
                    'CONTRACT': 'contract',
                    'OPTION': 'option',
                    'INVESTMENT': 'investment',
                    'UNIFIED': 'unified',
                },
                'networks': {
                    'ERC20': 'ETH',
                    'TRC20': 'TRX',
                    'BEP20': 'BSC',
                    'OMNI': 'OMNI',
                    'SPL': 'SOL',
                },
                'networksById': {
                    'ETH': 'ERC20',
                    'TRX': 'TRC20',
                    'BSC': 'BEP20',
                    'OMNI': 'OMNI',
                    'SPL': 'SOL',
                },
                'defaultNetwork': 'ERC20',
                'defaultNetworks': {
                    'USDT': 'TRC20',
                },
                'intervals': {
                    '5m': '5min',
                    '15m': '15min',
                    '30m': '30min',
                    '1h': '1h',
                    '4h': '4h',
                    '1d': '1d',
                },
            },
            'fees': {
                'trading': {
                    'feeSide': 'get',
                    'tierBased': True,
                    'percentage': True,
                    'taker': 0.00075,
                    'maker': 0.0001,
                },
                'funding': {
                    'tierBased': False,
                    'percentage': False,
                    'withdraw': {},
                    'deposit': {},
                },
            },
            'commonCurrencies': {
                'GAS': 'GASDAO',
            },
        })

    def nonce(self):
        return self.milliseconds() - self.options['timeDifference']

    def is_unified_enabled(self, params={}):
        # 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 = self.safe_value(self.options, 'enableUnifiedMargin')
        enableUnifiedAccount = self.safe_value(self.options, 'enableUnifiedAccount')
        if enableUnifiedMargin is None or enableUnifiedAccount is None:
            response = self.privateGetUserV3PrivateQueryApi(params)
            #
            #     {
            #         "retCode":0,
            #         "retMsg":"OK",
            #         "result":{
            #             "id":"88888888",
            #             "note":"ccxt-moon",
            #             "apiKey":"8s8c808v8u8",
            #             "readOnly":0,
            #             "secret":"",
            #             "permissions":{
            #                 "ContractTrade":[""],
            #                 "Spot":[""],
            #                 "Wallet":[""],
            #                 "Options":[""],
            #                 "Derivatives":[""],
            #                 "CopyTrading":[""],
            #                 "BlockTrade":[],
            #                 "Exchange":[""],
            #                 "NFT":[""]
            #             },
            #             "ips":[""],
            #             "type":1,
            #             "deadlineDay":27,
            #             "expiredAt":"",
            #             "createdAt":"",
            #             "unified":1,
            #             "uta": 1
            #         },
            #         "retExtInfo":null,
            #         "time":1669735171649
            #     }
            #
            result = self.safe_value(response, 'result', {})
            self.options['enableUnifiedMargin'] = self.safe_integer(result, 'unified') == 1
            self.options['enableUnifiedAccount'] = self.safe_integer(result, 'uta') == 1
        return [self.options['enableUnifiedMargin'], self.options['enableUnifiedAccount']]

    def upgrade_unified_account(self, params={}):
        createUnifiedMarginAccount = self.safe_value(self.options, 'createUnifiedMarginAccount')
        if not createUnifiedMarginAccount:
            raise NotSupported(self.id + ' upgradeUnifiedAccount() warning self method can only be called once, it is not reverseable and you will be stuck with a unified margin account, you also need at least 5000 USDT in your bybit account to do self. If you want to disable self warning set exchange.options["createUnifiedMarginAccount"]=true.')
        return self.privatePostUnifiedV3PrivateAccountUpgradeUnifiedAccount(params)

    def upgrade_unified_trade_account(self, params={}):
        return self.privatePostV5AccountUpgradeToUta(params)

    def fetch_time(self, params={}):
        """
        fetches the current integer timestamp in milliseconds from the exchange server
        see https://bybit-exchange.github.io/docs/v3/server-time
        :param dict params: extra parameters specific to the bybit api endpoint
        :returns int: the current integer timestamp in milliseconds from the exchange server
        """
        response = self.publicGetV3PublicTime(params)
        #
        #    {
        #         "retCode": "0",
        #         "retMsg": "OK",
        #         "result": {
        #             "timeSecond": "1666879482",
        #             "timeNano": "1666879482792685914"
        #         },
        #         "retExtInfo": {},
        #         "time": "1666879482792"
        #     }
        #
        return self.safe_integer(response, 'time')

    def fetch_currencies(self, params={}):
        """
        fetches all available currencies on an exchange
        see https://bybit-exchange.github.io/docs/v5/asset/coin-info
        :param dict params: extra parameters specific to the bybit api endpoint
        :returns dict: an associative dictionary of currencies
        """
        if not self.check_required_credentials(False):
            return None
        response = self.privateGetV5AssetCoinQueryInfo(params)
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "",
        #         "result": {
        #             "rows": [
        #                 {
        #                     "name": "BTC",
        #                     "coin": "BTC",
        #                     "remainAmount": "150",
        #                     "chains": [
        #                         {
        #                             "chainType": "BTC",
        #                             "confirmation": "10000",
        #                             "withdrawFee": "0.0005",
        #                             "depositMin": "0.0005",
        #                             "withdrawMin": "0.001",
        #                             "chain": "BTC",
        #                             "chainDeposit": "1",
        #                             "chainWithdraw": "1",
        #                             "minAccuracy": "8"
        #                         }
        #                     ]
        #                 }
        #             ]
        #         },
        #         "retExtInfo": {},
        #         "time": 1672194582264
        #     }
        #
        data = self.safe_value(response, 'result', [])
        rows = self.safe_value(data, 'rows', [])
        result = {}
        for i in range(0, len(rows)):
            currency = rows[i]
            currencyId = self.safe_string(currency, 'coin')
            code = self.safe_currency_code(currencyId)
            name = self.safe_string(currency, 'name')
            chains = self.safe_value(currency, 'chains', [])
            networks = {}
            minPrecision = None
            for j in range(0, len(chains)):
                chain = chains[j]
                networkId = self.safe_string(chain, 'chain')
                networkCode = self.network_id_to_code(networkId)
                precision = self.parse_number(self.parse_precision(self.safe_string(chain, 'minAccuracy')))
                minPrecision = precision if (minPrecision is None) else min(minPrecision, precision)
                depositAllowed = self.safe_integer(chain, 'chainDeposit') == 1
                withdrawAllowed = self.safe_integer(chain, 'chainWithdraw') == 1
                networks[networkCode] = {
                    'info': chain,
                    'id': networkId,
                    'network': networkCode,
                    'active': None,
                    'deposit': depositAllowed,
                    'withdraw': withdrawAllowed,
                    'fee': self.safe_number(chain, 'withdrawFee'),
                    'precision': precision,
                    'limits': {
                        'withdraw': {
                            'min': self.safe_number(chain, 'withdrawMin'),
                            'max': None,
                        },
                        'deposit': {
                            'min': self.safe_number(chain, 'depositMin'),
                            'max': None,
                        },
                    },
                }
            result[code] = {
                'info': currency,
                'code': code,
                'id': currencyId,
                'name': name,
                'active': None,
                'deposit': None,
                'withdraw': None,
                'fee': None,
                'precision': minPrecision,
                'limits': {
                    'amount': {
                        'min': None,
                        'max': None,
                    },
                },
                'networks': networks,
            }
        return result

    def fetch_markets(self, params={}):
        """
        retrieves data on all markets for bybit
        see https://bybit-exchange.github.io/docs/v5/market/instrument
        :param dict params: extra parameters specific to the exchange api endpoint
        :returns [dict]: an array of objects representing market data
        """
        if self.options['adjustForTimeDifference']:
            self.load_time_difference()
        promises = [
            self.fetch_spot_markets(params),
            self.fetch_derivatives_markets({'category': 'linear'}),
            self.fetch_derivatives_markets({'category': 'inverse'}),
        ]
        spotMarkets = promises[0]
        linearMarkets = promises[1]
        inverseMarkets = promises[2]
        markets = spotMarkets
        markets = self.array_concat(markets, linearMarkets)
        return self.array_concat(markets, inverseMarkets)

    def fetch_spot_markets(self, params):
        request = {
            'category': 'spot',
        }
        response = self.publicGetV5MarketInstrumentsInfo(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "category": "spot",
        #             "list": [
        #                 {
        #                     "symbol": "BTCUSDT",
        #                     "baseCoin": "BTC",
        #                     "quoteCoin": "USDT",
        #                     "innovation": "0",
        #                     "status": "1",
        #                     "lotSizeFilter": {
        #                         "basePrecision": "0.000001",
        #                         "quotePrecision": "0.00000001",
        #                         "minOrderQty": "0.00004",
        #                         "maxOrderQty": "63.01197227",
        #                         "minOrderAmt": "1",
        #                         "maxOrderAmt": "100000"
        #                     },
        #                     "priceFilter": {
        #                         "tickSize": "0.01"
        #                     }
        #                 }
        #             ]
        #         },
        #         "retExtInfo": {},
        #         "time": 1672712468011
        #     }
        #
        responseResult = self.safe_value(response, 'result', {})
        markets = self.safe_value(responseResult, 'list', [])
        result = []
        takerFee = self.parse_number('0.001')
        makerFee = self.parse_number('0.001')
        for i in range(0, len(markets)):
            market = markets[i]
            id = self.safe_string(market, 'symbol')
            baseId = self.safe_string(market, 'baseCoin')
            quoteId = self.safe_string(market, 'quoteCoin')
            base = self.safe_currency_code(baseId)
            quote = self.safe_currency_code(quoteId)
            symbol = base + '/' + quote
            active = self.safe_integer(market, 'status') == 1
            lotSizeFilter = self.safe_value(market, 'lotSizeFilter')
            priceFilter = self.safe_value(market, 'priceFilter')
            quotePrecision = self.safe_number(lotSizeFilter, 'quotePrecision')
            result.append({
                'id': id,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'settle': None,
                'baseId': baseId,
                'quoteId': quoteId,
                'settleId': None,
                'type': 'spot',
                'spot': True,
                'margin': None,
                'swap': False,
                'future': False,
                'option': False,
                'active': active,
                'contract': False,
                'linear': None,
                'inverse': None,
                'taker': takerFee,
                'maker': makerFee,
                'contractSize': None,
                'expiry': None,
                'expiryDatetime': None,
                'strike': None,
                'optionType': None,
                'precision': {
                    'amount': self.safe_number(lotSizeFilter, 'basePrecision'),
                    'price': self.safe_number(priceFilter, 'tickSize', quotePrecision),
                },
                'limits': {
                    'leverage': {
                        'min': self.parse_number('1'),
                        'max': None,
                    },
                    'amount': {
                        'min': self.safe_number(lotSizeFilter, 'minOrderQty'),
                        'max': self.safe_number(lotSizeFilter, 'maxOrderQty'),
                    },
                    'price': {
                        'min': None,
                        'max': None,
                    },
                    'cost': {
                        'min': self.safe_number(lotSizeFilter, 'minOrderAmt'),
                        'max': self.safe_number(lotSizeFilter, 'maxOrderAmt'),
                    },
                },
                'info': market,
            })
        return result

    def fetch_derivatives_markets(self, params):
        params['limit'] = 1000  # minimize number of requests
        response = self.publicGetV5MarketInstrumentsInfo(params)
        data = self.safe_value(response, 'result', {})
        markets = self.safe_value(data, 'list', [])
        paginationCursor = self.safe_string(data, 'nextPageCursor')
        if paginationCursor is not None:
            while(paginationCursor is not None):
                params['cursor'] = paginationCursor
                response = self.publicGetDerivativesV3PublicInstrumentsInfo(params)
                data = self.safe_value(response, 'result', {})
                rawMarkets = self.safe_value(data, 'list', [])
                rawMarketsLength = len(rawMarkets)
                if rawMarketsLength == 0:
                    break
                markets = self.array_concat(rawMarkets, markets)
                paginationCursor = self.safe_string(data, 'nextPageCursor')
        #
        # linear response
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "category": "linear",
        #             "list": [
        #                 {
        #                     "symbol": "BTCUSDT",
        #                     "contractType": "LinearPerpetual",
        #                     "status": "Trading",
        #                     "baseCoin": "BTC",
        #                     "quoteCoin": "USDT",
        #                     "launchTime": "1585526400000",
        #                     "deliveryTime": "0",
        #                     "deliveryFeeRate": "",
        #                     "priceScale": "2",
        #                     "leverageFilter": {
        #                         "minLeverage": "1",
        #                         "maxLeverage": "100.00",
        #                         "leverageStep": "0.01"
        #                     },
        #                     "priceFilter": {
        #                         "minPrice": "0.50",
        #                         "maxPrice": "999999.00",
        #                         "tickSize": "0.50"
        #                     },
        #                     "lotSizeFilter": {
        #                         "maxOrderQty": "100.000",
        #                         "minOrderQty": "0.001",
        #                         "qtyStep": "0.001",
        #                         "postOnlyMaxOrderQty": "1000.000"
        #                     },
        #                     "unifiedMarginTrade": True,
        #                     "fundingInterval": 480,
        #                     "settleCoin": "USDT"
        #                 }
        #             ],
        #             "nextPageCursor": ""
        #         },
        #         "retExtInfo": {},
        #         "time": 1672712495660
        #     }
        #
        # option response
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "category": "option",
        #             "nextPageCursor": "",
        #             "list": [
        #                 {
        #                     "category": "option",
        #                     "symbol": "ETH-3JAN23-1250-P",
        #                     "status": "ONLINE",
        #                     "baseCoin": "ETH",
        #                     "quoteCoin": "USD",
        #                     "settleCoin": "USDC",
        #                     "optionsType": "Put",
        #                     "launchTime": "1672560000000",
        #                     "deliveryTime": "1672732800000",
        #                     "deliveryFeeRate": "0.00015",
        #                     "priceFilter": {
        #                         "minPrice": "0.1",
        #                         "maxPrice": "10000000",
        #                         "tickSize": "0.1"
        #                     },
        #                     "lotSizeFilter": {
        #                         "maxOrderQty": "1500",
        #                         "minOrderQty": "0.1",
        #                         "qtyStep": "0.1"
        #                     }
        #                 }
        #             ]
        #         },
        #         "retExtInfo": {},
        #         "time": 1672712537130
        #     }
        #
        result = []
        category = self.safe_string(data, 'category')
        for i in range(0, len(markets)):
            market = markets[i]
            if category is None:
                category = self.safe_string(market, 'category')
            linear = (category == 'linear')
            inverse = (category == 'inverse')
            contractType = self.safe_string(market, 'contractType')
            inverseFutures = (contractType == 'InverseFutures')
            linearPerpetual = (contractType == 'LinearPerpetual')
            inversePerpetual = (contractType == 'InversePerpetual')
            id = self.safe_string(market, 'symbol')
            baseId = self.safe_string(market, 'baseCoin')
            quoteId = self.safe_string(market, 'quoteCoin')
            defaultSettledId = quoteId if linear else baseId
            settleId = self.safe_string(market, 'settleCoin', defaultSettledId)
            base = self.safe_currency_code(baseId)
            quote = self.safe_currency_code(quoteId)
            settle = None
            if linearPerpetual and (settleId == 'USD'):
                settle = 'USDC'
            else:
                settle = self.safe_currency_code(settleId)
            symbol = base + '/' + quote
            lotSizeFilter = self.safe_value(market, 'lotSizeFilter', {})
            priceFilter = self.safe_value(market, 'priceFilter', {})
            leverage = self.safe_value(market, 'leverageFilter', {})
            status = self.safe_string(market, 'status')
            active = (status == 'Trading')
            swap = linearPerpetual or inversePerpetual
            future = inverseFutures
            option = (category == 'option')
            type = None
            if swap:
                type = 'swap'
            elif future:
                type = 'future'
            elif option:
                type = 'option'
            expiry = self.omit_zero(self.safe_string(market, 'deliveryTime'))
            if expiry is not None:
                expiry = int(expiry)
            expiryDatetime = self.iso8601(expiry)
            strike = None
            optionType = None
            symbol = symbol + ':' + settle
            if expiry is not None:
                symbol = symbol + '-' + self.yymmdd(expiry)
                if option:
                    splitId = id.split('-')
                    strike = self.safe_string(splitId, 2)
                    optionLetter = self.safe_string(splitId, 3)
                    symbol = symbol + '-' + strike + '-' + optionLetter
                    if optionLetter == 'P':
                        optionType = 'put'
                    elif optionLetter == 'C':
                        optionType = 'call'
            contractSize = self.safe_number_2(lotSizeFilter, 'minTradingQty', 'minOrderQty') if inverse else self.parse_number('1')
            result.append({
                'id': id,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'settle': settle,
                'baseId': baseId,
                'quoteId': quoteId,
                'settleId': settleId,
                'type': type,
                'spot': False,
                'margin': None,
                'swap': swap,
                'future': future,
                'option': option,
                'active': active,
                'contract': True,
                'linear': linear,
                'inverse': inverse,
                'taker': self.safe_number(market, 'takerFee', self.parse_number('0.0006')),
                'maker': self.safe_number(market, 'makerFee', self.parse_number('0.0001')),
                'contractSize': contractSize,
                'expiry': expiry,
                'expiryDatetime': expiryDatetime,
                'strike': strike,
                'optionType': optionType,
                'precision': {
                    'amount': self.safe_number(lotSizeFilter, 'qtyStep'),
                    'price': self.safe_number(priceFilter, 'tickSize'),
                },
                'limits': {
                    'leverage': {
                        'min': self.safe_number(leverage, 'minLeverage'),
                        'max': self.safe_number(leverage, 'maxLeverage'),
                    },
                    'amount': {
                        'min': self.safe_number_2(lotSizeFilter, 'minTradingQty', 'minOrderQty'),
                        'max': self.safe_number_2(lotSizeFilter, 'maxTradingQty', 'maxOrderQty'),
                    },
                    'price': {
                        'min': self.safe_number(priceFilter, 'minPrice'),
                        'max': self.safe_number(priceFilter, 'maxPrice'),
                    },
                    'cost': {
                        'min': None,
                        'max': None,
                    },
                },
                'info': market,
            })
        return result

    def parse_ticker(self, ticker, market=None):
        #
        # 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"
        #     }
        #
        timestamp = self.safe_integer(ticker, 'time')
        marketId = self.safe_string(ticker, 'symbol')
        marketType = market['type'] if (market is not None) else 'linear'
        market = self.safe_market(marketId, market, None, marketType)
        symbol = self.safe_symbol(marketId, market, None, marketType)
        last = self.safe_string(ticker, 'lastPrice')
        open = self.safe_string(ticker, 'prevPrice24h')
        percentage = self.safe_string(ticker, 'price24hPcnt')
        percentage = Precise.string_mul(percentage, '100')
        quoteVolume = self.safe_string(ticker, 'turnover24h')
        baseVolume = self.safe_string(ticker, 'volume24h')
        bid = self.safe_string(ticker, 'bid1Price')
        ask = self.safe_string(ticker, 'ask1Price')
        high = self.safe_string(ticker, 'highPrice24h')
        low = self.safe_string(ticker, 'lowPrice24h')
        return self.safe_ticker({
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'high': high,
            'low': low,
            'bid': bid,
            'bidVolume': self.safe_string_2(ticker, 'bidSize', 'bid1Size'),
            'ask': ask,
            'askVolume': self.safe_string_2(ticker, 'askSize', 'ask1Size'),
            'vwap': None,
            'open': open,
            'close': last,
            'last': last,
            'previousClose': None,
            'change': None,
            'percentage': percentage,
            'average': None,
            'baseVolume': baseVolume,
            'quoteVolume': quoteVolume,
            'info': ticker,
        }, market)

    def fetch_ticker(self, 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 str symbol: unified symbol of the market to fetch the ticker for
        :param dict params: extra parameters specific to the bybit api endpoint
        :returns dict: a `ticker structure <https://docs.ccxt.com/en/latest/manual.html#ticker-structure>`
        """
        self.check_required_symbol('fetchTicker', symbol)
        self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
            # 'baseCoin': '', Base coin. For option only
            # 'expDate': '', Expiry date. e.g., 25DEC22. For option only
        }
        if market['spot']:
            request['category'] = 'spot'
        else:
            if market['option']:
                request['category'] = 'option'
            elif market['linear']:
                request['category'] = 'linear'
            elif market['inverse']:
                request['category'] = 'inverse'
        response = self.publicGetV5MarketTickers(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "category": "inverse",
        #             "list": [
        #                 {
        #                     "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": {},
        #         "time": 1672376496682
        #     }
        #
        result = self.safe_value(response, 'result', [])
        tickers = self.safe_value(result, 'list', [])
        rawTicker = self.safe_value(tickers, 0)
        return self.parse_ticker(rawTicker, market)

    def fetch_tickers(self, symbols=None, params={}):
        """
        fetches price tickers for multiple markets, statistical calculations with the information calculated over the past 24 hours each market
        see https://bybit-exchange.github.io/docs/v5/market/tickers
        :param [str]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
        :param dict params: extra parameters specific to the bybit api endpoint
        :returns dict: an array of `ticker structures <https://docs.ccxt.com/en/latest/manual.html#ticker-structure>`
        """
        self.load_markets()
        market = None
        if symbols is not None:
            symbols = self.market_symbols(symbols)
            market = self.market(symbols[0])
        request = {
            # 'symbol': market['id'],
            # 'baseCoin': '', Base coin. For option only
            # 'expDate': '', Expiry date. e.g., 25DEC22. For option only
        }
        type = None
        type, params = self.handle_market_type_and_params('fetchTickers', market, params)
        if type == 'spot':
            request['category'] = 'spot'
        elif type == 'swap':
            subType = None
            subType, params = self.handle_sub_type_and_params('fetchTickers', market, params, 'linear')
            request['category'] = subType
        elif type == 'option':
            request['category'] = 'option'
        response = self.publicGetV5MarketTickers(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "category": "inverse",
        #             "list": [
        #                 {
        #                     "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": {},
        #         "time": 1672376496682
        #     }
        #
        result = self.safe_value(response, 'result', {})
        tickerList = self.safe_value(result, 'list', [])
        tickers = {}
        for i in range(0, len(tickerList)):
            ticker = self.parse_ticker(tickerList[i], market)
            symbol = ticker['symbol']
            tickers[symbol] = ticker
        return self.filter_by_array(tickers, 'symbol', symbols)

    def parse_ohlcv(self, ohlcv, market=None):
        #
        #     [
        #         "1621162800",
        #         "49592.43",
        #         "49644.91",
        #         "49342.37",
        #         "49349.42",
        #         "1451.59",
        #         "2.4343353100000003"
        #     ]
        #
        return [
            self.safe_integer(ohlcv, 0),
            self.safe_number(ohlcv, 1),
            self.safe_number(ohlcv, 2),
            self.safe_number(ohlcv, 3),
            self.safe_number(ohlcv, 4),
            self.safe_number(ohlcv, 5),
        ]

    def fetch_ohlcv(self, symbol, timeframe='1m', since=None, limit=None, 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 str symbol: unified symbol of the market to fetch OHLCV data for
        :param str timeframe: the length of time each candle represents
        :param int|None since: timestamp in ms of the earliest candle to fetch
        :param int|None limit: the maximum amount of candles to fetch
        :param dict params: extra parameters specific to the bybit api endpoint
        :returns [[int]]: A list of candles ordered as timestamp, open, high, low, close, volume
        """
        self.check_required_symbol('fetchOHLCV', symbol)
        self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        if limit is None:
            limit = 200  # default is 200 when requested with `since`
        if since is not None:
            request['start'] = since
        if limit is not None:
            request['limit'] = limit  # max 1000, default 1000
        request['interval'] = self.safe_string(self.timeframes, timeframe, timeframe)
        method = None
        if market['spot']:
            request['category'] = 'spot'
            method = 'publicGetV5MarketKline'
        else:
            price = self.safe_string(params, 'price')
            params = self.omit(params, 'price')
            methods = {
                'mark': 'publicGetV5MarketMarkPriceKline',
                'index': 'publicGetV5MarketIndexPriceKline',
                'premiumIndex': 'publicGetV5MarketPremiumIndexPriceKline',
            }
            method = self.safe_value(methods, price, 'publicGetV5MarketKline')
            if market['linear']:
                request['category'] = 'linear'
            elif market['inverse']:
                request['category'] = 'inverse'
            else:
                raise NotSupported(self.id + ' fetchOHLCV() is not supported for option markets')
        response = getattr(self, method)(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "symbol": "BTCUSD",
        #             "category": "inverse",
        #             "list": [
        #                 [
        #                     "1670608800000",
        #                     "17071",
        #                     "17073",
        #                     "17027",
        #                     "17055.5",
        #                     "268611",
        #                     "15.74462667"
        #                 ],
        #                 [
        #                     "1670605200000",
        #                     "17071.5",
        #                     "17071.5",
        #                     "17061",
        #                     "17071",
        #                     "4177",
        #                     "0.24469757"
        #                 ],
        #                 [
        #                     "1670601600000",
        #                     "17086.5",
        #                     "17088",
        #                     "16978",
        #                     "17071.5",
        #                     "6356",
        #                     "0.37288112"
        #                 ]
        #             ]
        #         },
        #         "retExtInfo": {},
        #         "time": 1672025956592
        #     }
        #
        result = self.safe_value(response, 'result', {})
        ohlcvs = self.safe_value(result, 'list', [])
        return self.parse_ohlcvs(ohlcvs, market, timeframe, since, limit)

    def parse_funding_rate(self, ticker, market=None):
        #     {
        #         "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 = self.safe_integer(ticker, 'timestamp')  # added artificially to avoid changing the signature
        ticker = self.omit(ticker, 'timestamp')
        marketId = self.safe_string(ticker, 'symbol')
        symbol = self.safe_symbol(marketId, market, None, 'swap')
        fundingRate = self.safe_number(ticker, 'fundingRate')
        fundingTimestamp = self.safe_integer(ticker, 'nextFundingTime')
        markPrice = self.safe_number(ticker, 'markPrice')
        indexPrice = self.safe_number(ticker, 'indexPrice')
        return {
            'info': ticker,
            'symbol': symbol,
            'markPrice': markPrice,
            'indexPrice': indexPrice,
            'interestRate': None,
            'estimatedSettlePrice': None,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'fundingRate': fundingRate,
            'fundingTimestamp': fundingTimestamp,
            'fundingDatetime': self.iso8601(fundingTimestamp),
            'nextFundingRate': None,
            'nextFundingTimestamp': None,
            'nextFundingDatetime': None,
            'previousFundingRate': None,
            'previousFundingTimestamp': None,
            'previousFundingDatetime': None,
        }

    def fetch_funding_rates(self, symbols=None, params={}):
        """
        fetches funding rates for multiple markets
        see https://bybit-exchange.github.io/docs/v5/market/tickers
        :param [str]|None symbols: unified symbols of the markets to fetch the funding rates for, all market funding rates are returned if not assigned
        :param dict params: extra parameters specific to the bybit api endpoint
        :returns dict: an array of `funding rate structures <https://docs.ccxt.com/en/latest/manual.html#funding-rate-structure>`
        """
        self.load_markets()
        market = None
        request = {}
        if symbols is not None:
            symbols = self.market_symbols(symbols)
            market = self.market(symbols[0])
            if len(symbols) == 1:
                request['symbol'] = market['id']
        type = None
        type, params = self.handle_market_type_and_params('fetchFundingRates', market, params)
        if type != 'swap':
            raise NotSupported(self.id + ' fetchFundingRates() does not support ' + type + ' markets')
        else:
            subType = None
            subType, params = self.handle_sub_type_and_params('fetchFundingRates', market, params, 'linear')
            request['category'] = subType
        response = self.publicGetV5MarketTickers(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "category": "linear",
        #             "list": [
        #                 {
        #                     "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
        #     }
        #
        tickerList = self.safe_value(response, 'result', [])
        timestamp = self.safe_integer(response, 'time')
        tickerList = self.safe_value(tickerList, 'list')
        fundingRates = {}
        for i in range(0, len(tickerList)):
            rawTicker = tickerList[i]
            rawTicker['timestamp'] = timestamp  # will be removed inside the parser
            ticker = self.parse_funding_rate(tickerList[i], None)
            symbol = ticker['symbol']
            fundingRates[symbol] = ticker
        return self.filter_by_array(fundingRates, 'symbol', symbols)

    def fetch_funding_rate_history(self, symbol=None, since=None, limit=None, params={}):
        """
        fetches historical funding rate prices
        see https://bybit-exchange.github.io/docs/v5/market/history-fund-rate
        :param str|None symbol: unified symbol of the market to fetch the funding rate history for
        :param int|None since: timestamp in ms of the earliest funding rate to fetch
        :param int|None limit: the maximum amount of `funding rate structures <https://docs.ccxt.com/en/latest/manual.html?#funding-rate-history-structure>` to fetch
        :param dict params: extra parameters specific to the bybit api endpoint
        :param int|None params['until']: timestamp in ms of the latest funding rate
        :returns [dict]: a list of `funding rate structures <https://docs.ccxt.com/en/latest/manual.html?#funding-rate-history-structure>`
        """
        self.check_required_symbol('fetchFundingRateHistory', symbol)
        self.load_markets()
        request = {
            # 'category': '',  # Product type. linear,inverse
            # 'symbol': '',  # Symbol name
            # 'startTime': 0,  # The start timestamp(ms)
            # 'endTime': 0,  # The end timestamp(ms)
            # 'limit': 0,  # Limit for data size per page. [1, 200]. Default: 200
        }
        market = self.market(symbol)
        symbol = market['symbol']
        request['symbol'] = market['id']
        if market['option']:
            raise NotSupported(self.id + ' fetchFundingRateHistory() is not supported for option markets')
        elif market['linear']:
            request['category'] = 'linear'
        elif market['inverse']:
            request['category'] = 'inverse'
        if since is not None:
            request['startTime'] = since
        until = self.safe_integer_2(params, 'until', 'till')  # unified in milliseconds
        endTime = self.safe_integer(params, 'endTime', until)  # exchange-specific in milliseconds
        params = self.omit(params, ['endTime', 'till', 'until'])
        if endTime is not None:
            request['endTime'] = endTime
        if limit is not None:
            request['limit'] = limit
        response = self.publicGetV5MarketFundingHistory(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "category": "linear",
        #             "list": [
        #                 {
        #                     "symbol": "ETHPERP",
        #                     "fundingRate": "0.0001",
        #                     "fundingRateTimestamp": "1672041600000"
        #                 }
        #             ]
        #         },
        #         "retExtInfo": {},
        #         "time": 1672051897447
        #     }
        #
        rates = []
        result = self.safe_value(response, 'result')
        resultList = self.safe_value(result, 'list')
        for i in range(0, len(resultList)):
            entry = resultList[i]
            timestamp = self.safe_integer(entry, 'fundingRateTimestamp')
            rates.append({
                'info': entry,
                'symbol': self.safe_symbol(self.safe_string(entry, 'symbol'), None, None, 'swap'),
                'fundingRate': self.safe_number(entry, 'fundingRate'),
                'timestamp': timestamp,
                'datetime': self.iso8601(timestamp),
            })
        sorted = self.sort_by(rates, 'timestamp')
        return self.filter_by_symbol_since_limit(sorted, symbol, since, limit)

    def parse_trade(self, trade, market=None):
        isSpotTrade = ('isBuyerMaker' in trade) or ('feeTokenId' in trade)
        if isSpotTrade:
            return self.parse_spot_trade(trade, market)
        else:
            return self.parse_contract_trade(trade, market)

    def parse_spot_trade(self, trade, market=None):
        #
        #   public:
        #     {
        #        "price": "39548.68",
        #        "time": "1651748717850",
        #        "qty": "0.166872",
        #        "isBuyerMaker": 0
        #     }
        #
        #   private:
        #     {
        #         "orderPrice": "82.5",
        #         "creatTime": "1666702226326",
        #         "orderQty": "0.016",
        #         "isBuyer": "0",
        #         "isMaker": "0",
        #         "symbol": "AAVEUSDT",
        #         "id": "1274785101965716992",
        #         "orderId": "1274784252359089664",
        #         "tradeId": "2270000000031365639",
        #         "execFee": "0",
        #         "feeTokenId": "AAVE",
        #         "matchOrderId": "1274785101865076224",
        #         "makerRebate": "0",
        #         "executionTime": "1666702226335"
        #     }
        #
        timestamp = self.safe_integer_n(trade, ['time', 'creatTime'])
        takerOrMaker = None
        side = None
        isBuyerMaker = self.safe_integer(trade, 'isBuyerMaker')
        if isBuyerMaker is not None:
            # if public response
            side = 'buy' if (isBuyerMaker == 1) else 'sell'
        else:
            # if private response
            isBuyer = self.safe_integer(trade, 'isBuyer')
            isMaker = self.safe_integer(trade, 'isMaker')
            takerOrMaker = 'maker' if (isMaker == 0) else 'taker'
            side = 'buy' if (isBuyer == 0) else 'sell'
        marketId = self.safe_string(trade, 'symbol')
        market = self.safe_market(marketId, market, None, 'spot')
        fee = None
        feeCost = self.safe_string(trade, 'execFee')
        if feeCost is not None:
            feeToken = self.safe_string(trade, 'feeTokenId')
            feeCurrency = self.safe_currency_code(feeToken)
            fee = {
                'cost': feeCost,
                'currency': feeCurrency,
            }
        return self.safe_trade({
            'id': self.safe_string(trade, 'tradeId'),
            'info': trade,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': market['symbol'],
            'order': self.safe_string(trade, 'orderId'),
            'type': None,
            'side': side,
            'takerOrMaker': takerOrMaker,
            'price': self.safe_string_2(trade, 'price', 'orderPrice'),
            'amount': self.safe_string_2(trade, 'qty', 'orderQty'),
            'cost': None,
            'fee': fee,
        }, market)

    def parse_contract_trade(self, trade, market=None):
        #
        # public contract
        #
        #     {
        #         "execId": "666042b4-50c6-58f3-bd9c-89b2088663ff",
        #         "symbol": "ETHUSD",
        #         "price": "1162.95",
        #         "size": "1",
        #         "side": "Sell",
        #         "time": "1669191277315",
        #         "isBlockTrade": False
        #     }
        #
        # public unified margin
        #
        #     {
        #         "execId": "da66abbc-f358-5864-8d34-84ef7274d853",
        #         "symbol": "BTCUSDT",
        #         "price": "20802.50",
        #         "size": "0.200",
        #         "side": "Sell",
        #         "time": "1657870316630"
        #     }
        #
        # private contract trades
        #
        #     {
        #         "symbol": "ETHUSD",
        #         "execFee": "0.00005484",
        #         "execId": "acf78206-d464-589b-b888-51bd130821c1",
        #         "execPrice": "1367.80",
        #         "execQty": "100",
        #         "execType": "Trade",
        #         "execValue": "0.0731101",
        #         "feeRate": "0.00075",
        #         "lastLiquidityInd": "RemovedLiquidity",
        #         "leavesQty": "0",
        #         "orderId": "fdc584c3-be5d-41ff-8f54-5be7649b1d1c",
        #         "orderLinkId": "",
        #         "orderPrice": "1299.50",
        #         "orderQty": "100",
        #         "orderType": "Market",
        #         "stopOrderType": "UNKNOWN",
        #         "side": "Sell",
        #         "execTime": "1611528105547",
        #         "closedSize": "100"
        #     }
        #
        # private unified margin
        #
        #     {
        #         "symbol": "AAVEUSDT",
        #         "id": "1274785101965716992",
        #         "orderId": "1274784252359089664",
        #         "tradeId": "2270000000031365639",
        #         "orderPrice": "82.5",
        #         "orderQty": "0.016",
        #         "execFee": "0",
        #         "feeTokenId": "AAVE",
        #         "creatTime": "1666702226326",
        #         "isBuyer": "0",
        #         "isMaker": "0",
        #         "matchOrderId": "1274785101865076224",
        #         "makerRebate": "0",
        #         "executionTime": "1666702226335"
        #     }
        #
        # 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"
        #     }
        #
        id = self.safe_string_n(trade, ['execId', 'id', 'tradeId'])
        marketId = self.safe_string(trade, 'symbol')
        marketType = 'contract'
        if market is not None:
            marketType = market['type']
        market = self.safe_market(marketId, market, None, marketType)
        symbol = market['symbol']
        amountString = self.safe_string_n(trade, ['execQty', 'orderQty', 'size'])
        priceString = self.safe_string_n(trade, ['execPrice', 'orderPrice', 'price'])
        costString = self.safe_string(trade, 'execValue')
        timestamp = self.safe_integer_n(trade, ['time', 'execTime', 'tradeTime'])
        side = self.safe_string_lower(trade, 'side')
        if side is None:
            isBuyer = self.safe_integer(trade, 'isBuyer')
            if isBuyer is not None:
                side = 'buy' if isBuyer else 'sell'
        isMaker = self.safe_value(trade, 'isMaker')
        takerOrMaker = None
        if isMaker is not None:
            takerOrMaker = 'maker' if isMaker else 'taker'
        else:
            lastLiquidityInd = self.safe_string(trade, 'lastLiquidityInd')
            if lastLiquidityInd == 'UNKNOWN':
                lastLiquidityInd = None
            if lastLiquidityInd is not None:
                if (lastLiquidityInd == 'TAKER') or (lastLiquidityInd == 'MAKER'):
                    takerOrMaker = lastLiquidityInd.lower()
                else:
                    takerOrMaker = 'maker' if (lastLiquidityInd == 'AddedLiquidity') else 'taker'
        orderType = self.safe_string_lower(trade, 'orderType')
        if orderType == 'unknown':
            orderType = None
        feeCostString = self.safe_string(trade, 'execFee')
        fee = None
        if feeCostString is not None:
            feeCurrencyCode = None
            if market['spot']:
                feeCurrencyCode = self.safe_string(trade, 'commissionAsset')
            else:
                feeCurrencyCode = market['base'] if market['inverse'] else market['settle']
            fee = {
                'cost': feeCostString,
                'currency': feeCurrencyCode,
            }
        return self.safe_trade({
            'id': id,
            'info': trade,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': symbol,
            'order': self.safe_string(trade, 'orderId'),
            'type': orderType,
            'side': side,
            'takerOrMaker': takerOrMaker,
            'price': priceString,
            'amount': amountString,
            'cost': costString,
            'fee': fee,
        }, market)

    def fetch_trades(self, symbol, since=None, limit=None, params={}):
        """
        get the list of most recent trades for a particular symbol
        see https://bybit-exchange.github.io/docs/v5/market/recent-trade
        :param str symbol: unified symbol of the market to fetch trades for
        :param int|None since: timestamp in ms of the earliest trade to fetch
        :param int|None limit: the maximum amount of trades to fetch
        :param dict params: extra parameters specific to the bybit api endpoint
        :returns [dict]: a list of `trade structures <https://docs.ccxt.com/en/latest/manual.html?#public-trades>`
        """
        self.check_required_symbol('fetchTrades', symbol)
        self.load_markets()
        market = self.market(symbol)
        request = {
            '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 is not None:
            # spot: [1,60], default: 60.
            # others: [1,1000], default: 500
            request['limit'] = limit
        if market['type'] == 'spot':
            request['category'] = 'spot'
        else:
            if market['option']:
                request['category'] = 'option'
            elif market['linear']:
                request['category'] = 'linear'
            elif market['inverse']:
                request['category'] = 'inverse'
        response = self.publicGetV5MarketRecentTrade(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "category": "spot",
        #             "list": [
        #                 {
        #                     "execId": "2100000000007764263",
        #                     "symbol": "BTCUSDT",
        #                     "price": "16618.49",
        #                     "size": "0.00012",
        #                     "side": "Buy",
        #                     "time": "1672052955758",
        #                     "isBlockTrade": False
        #                 }
        #             ]
        #         },
        #         "retExtInfo": {},
        #         "time": 1672053054358
        #     }
        #
        result = self.safe_value(response, 'result', {})
        trades = self.safe_value(result, 'list', [])
        return self.parse_trades(trades, market, since, limit)

    def fetch_order_book(self, symbol, limit=None, 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 str symbol: unified symbol of the market to fetch the order book for
        :param int|None limit: the maximum amount of order book entries to return
        :param dict params: extra parameters specific to the bybit api endpoint
        :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/en/latest/manual.html#order-book-structure>` indexed by market symbols
        """
        self.check_required_symbol('fetchOrderBook', symbol)
        self.load_markets()
        market = self.market(symbol)
        request = {
            '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'
            elif market['linear']:
                # limit: [1, 200]. Default: 25
                request['category'] = 'linear'
            elif market['inverse']:
                # limit: [1, 200]. Default: 25
                request['category'] = 'inverse'
        request['limit'] = limit if (limit is not None) else defaultLimit
        response = self.publicGetV5MarketOrderbook(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "s": "BTCUSDT",
        #             "a": [
        #                 [
        #                     "16638.64",
        #                     "0.008479"
        #                 ]
        #             ],
        #             "b": [
        #                 [
        #                     "16638.27",
        #                     "0.305749"
        #                 ]
        #             ],
        #             "ts": 1672765737733,
        #             "u": 5277055
        #         },
        #         "retExtInfo": {},
        #         "time": 1672765737734
        #     }
        #
        result = self.safe_value(response, 'result', [])
        timestamp = self.safe_integer(result, 'ts')
        return self.parse_order_book(result, symbol, timestamp, 'b', 'a')

    def parse_balance(self, response):
        #
        # margin wallet
        #     [
        #         {
        #             "free": "0.001143855",
        #             "interest": "0",
        #             "loan": "0",
        #             "locked": "0",
        #             "tokenId": "BTC",
        #             "total": "0.001143855"
        #         },
        #         {
        #             "free": "200.00005568",
        #             "interest": "0.0008391",
        #             "loan": "200",
        #             "locked": "0",
        #             "tokenId": "USDT",
        #             "total": "200.00005568"
        #         },
        #     ]
        #
        # usdc wallet
        #    {
        #      "result": {
        #           "walletBalance": "10.0000",
        #           "accountMM": "0.0000",
        #           "bonus": "0.0000",
        #           "accountIM": "0.0000",
        #           "totalSessionRPL": "0.0000",
        #           "equity": "10.0000",
        #           "totalRPL": "0.0000",
        #           "marginBalance": "10.0000",
        #           "availableBalance": "10.0000",
        #           "totalSessionUPL": "0.0000"
        #       },
        #       "retCode": "0",
        #       "retMsg": "Success."
        #    }
        #
        # Unified Margin
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "Success",
        #         "result": {
        #             "totalEquity": "112.21267421",
        #             "accountIMRate": "0.6895",
        #             "totalMarginBalance": "80.37711012",
        #             "totalInitialMargin": "55.42180254",
        #             "totalAvailableBalance": "24.95530758",
        #             "accountMMRate": "0.0459",
        #             "totalPerpUPL": "-16.69586570",
        #             "totalWalletBalance": "97.07311619",
        #             "totalMaintenanceMargin": "3.68580537",
        #             "coin": [
        #                 {
        #                     "currencyCoin": "ETH",
        #                     "availableToBorrow": "0.00000000",
        #                     "borrowSize": "0.00000000",
        #                     "bonus": "0.00000000",
        #                     "accruedInterest": "0.00000000",
        #                     "availableBalanceWithoutConvert": "0.00000000",
        #                     "totalOrderIM": "",
        #                     "equity": "0.00000000",
        #                     "totalPositionMM": "",
        #                     "usdValue": "0.00000000",
        #                     "availableBalance": "0.02441165",
        #                     "unrealisedPnl": "",
        #                     "totalPositionIM": "",
        #                     "marginBalanceWithoutConvert": "0.00000000",
        #                     "walletBalance": "0.00000000",
        #                     "cumRealisedPnl": "",
        #                     "marginBalance": "0.07862610"
        #                 }
        #             ]
        #         },
        #         "time": 1657716037033
        #     }
        #
        # contract v3
        #
        #     [
        #         {
        #             "coin": "BTC",
        #             "equity": "0.00000002",
        #             "walletBalance": "0.00000002",
        #             "positionMargin": "0",
        #             "availableBalance": "0.00000002",
        #             "orderMargin": "0",
        #             "occClosingFee": "0",
        #             "occFundingFee": "0",
        #             "unrealisedPnl": "0",
        #             "cumRealisedPnl": "-0.00010941",
        #             "givenCash": "0",
        #             "serviceCash": "0"
        #         },
        #         {
        #             "coin": "USDT",
        #             "equity": "3662.81038535",
        #             "walletBalance": "3662.81038535",
        #             "positionMargin": "0",
        #             "availableBalance": "3662.81038535",
        #             "orderMargin": "0",
        #             "occClosingFee": "0",
        #             "occFundingFee": "0",
        #             "unrealisedPnl": "0",
        #             "cumRealisedPnl": "-36.01761465",
        #             "givenCash": "0",
        #             "serviceCash": "0"
        #         }
        #     ]
        # spot
        #     {
        #       retCode: '0',
        #       retMsg: 'OK',
        #       result: {
        #         balances: [
        #           {
        #             coin: 'BTC',
        #             coinId: 'BTC',
        #             total: '0.00977041118',
        #             free: '0.00877041118',
        #             locked: '0.001'
        #           },
        #           {
        #             coin: 'EOS',
        #             coinId: 'EOS',
        #             total: '2000',
        #             free: '2000',
        #             locked: '0'
        #           }
        #         ]
        #       },
        #       retExtInfo: {},
        #       time: '1670002625754'
        #  }
        #
        # Unified trade account
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "list": [
        #                 {
        #                     "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",
        #                     "totalMaintenanceMargin": "0.38106773",
        #                     "coin": [
        #                         {
        #                             "availableToBorrow": "2.5",
        #                             "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": {},
        #         "time": 1672125441042
        #     }
        #
        result = {
            'info': response,
        }
        responseResult = self.safe_value(response, 'result', {})
        currencyList = self.safe_value_n(responseResult, ['loanAccountList', 'list', 'coin', 'balances'])
        if currencyList is None:
            # usdc wallet
            code = 'USDC'
            account = self.account()
            account['free'] = self.safe_string(responseResult, 'availableBalance')
            account['total'] = self.safe_string(responseResult, 'walletBalance')
            result[code] = account
        else:
            for i in range(0, len(currencyList)):
                entry = currencyList[i]
                accountType = self.safe_string(entry, 'accountType')
                if accountType == 'UNIFIED' or accountType == 'CONTRACT':
                    coins = self.safe_value(entry, 'coin')
                    for j in range(0, len(coins)):
                        account = self.account()
                        coinEntry = coins[j]
                        loan = self.safe_string(coinEntry, 'borrowAmount')
                        interest = self.safe_string(coinEntry, 'accruedInterest')
                        if (loan is not None) and (interest is not None):
                            account['debt'] = Precise.string_add(loan, interest)
                        account['total'] = self.safe_string(coinEntry, 'walletBalance')
                        account['free'] = self.safe_string(coinEntry, 'availableToWithdraw')
                        # account['used'] = self.safe_string(coinEntry, 'locked')
                        currencyId = self.safe_string(coinEntry, 'coin')
                        code = self.safe_currency_code(currencyId)
                        result[code] = account
                else:
                    account = self.account()
                    loan = self.safe_string(entry, 'loan')
                    interest = self.safe_string(entry, 'interest')
                    if (loan is not None) and (interest is not None):
                        account['debt'] = Precise.string_add(loan, interest)
                    account['total'] = self.safe_string_2(entry, 'total', 'walletBalance')
                    account['free'] = self.safe_string_n(entry, ['free', 'availableBalanceWithoutConvert', 'availableBalance'])
                    account['used'] = self.safe_string(entry, 'locked')
                    currencyId = self.safe_string_n(entry, ['tokenId', 'coin', 'currencyCoin'])
                    code = self.safe_currency_code(currencyId)
                    result[code] = account
        return self.safe_balance(result)

    def fetch_spot_balance(self, params={}):
        self.load_markets()
        marginMode = None
        marginMode, params = self.handle_margin_mode_and_params('fetchBalance', params)
        method = 'privateGetSpotV3PrivateAccount'
        if marginMode is not None:
            method = 'privateGetSpotV3PrivateCrossMarginAccount'
        response = getattr(self, method)(params)
        # spot wallet
        #     {
        #       retCode: '0',
        #       retMsg: 'OK',
        #       result: {
        #         balances: [
        #           {
        #             coin: 'BTC',
        #             coinId: 'BTC',
        #             total: '0.00977041118',
        #             free: '0.00877041118',
        #             locked: '0.001'
        #           },
        #           {
        #             coin: 'EOS',
        #             coinId: 'EOS',
        #             total: '2000',
        #             free: '2000',
        #             locked: '0'
        #           }
        #         ]
        #       },
        #       retExtInfo: {},
        #       time: '1670002625754'
        #     }
        # cross
        #     {
        #         "retCode": 0,
        #         "retMsg": "success",
        #         "result": {
        #             "acctBalanceSum": "0.122995614474732872",
        #             "debtBalanceSum": "0.011734191124529754",
        #             "loanAccountList": [
        #                 {
        #                     "free": "0.001143855",
        #                     "interest": "0",
        #                     "loan": "0",
        #                     "locked": "0",
        #                     "tokenId": "BTC",
        #                     "total": "0.001143855"
        #                 },
        #                 {
        #                     "free": "200.00005568",
        #                     "interest": "0.0008391",
        #                     "loan": "200",
        #                     "locked": "0",
        #                     "tokenId": "USDT",
        #                     "total": "200.00005568"
        #                 },
        #             ],
        #             "riskRate": "0.0954",
        #             "status": 1
        #         },
        #         "retExtInfo": {},
        #         "time": 1669843584123
        #     }
        #
        return self.parse_balance(response)

    def fetch_unified_margin_balance(self, params={}):
        self.load_markets()
        response = self.privateGetUnifiedV3PrivateAccountWalletBalance(params)
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "Success",
        #         "result": {
        #             "totalEquity": "112.21267421",
        #             "accountIMRate": "0.6895",
        #             "totalMarginBalance": "80.37711012",
        #             "totalInitialMargin": "55.42180254",
        #             "totalAvailableBalance": "24.95530758",
        #             "accountMMRate": "0.0459",
        #             "totalPerpUPL": "-16.69586570",
        #             "totalWalletBalance": "97.07311619",
        #             "totalMaintenanceMargin": "3.68580537",
        #             "coin": [
        #                 {
        #                     "currencyCoin": "ETH",
        #                     "availableToBorrow": "0.00000000",
        #                     "borrowSize": "0.00000000",
        #                     "bonus": "0.00000000",
        #                     "accruedInterest": "0.00000000",
        #                     "availableBalanceWithoutConvert": "0.00000000",
        #                     "totalOrderIM": "",
        #                     "equity": "0.00000000",
        #                     "totalPositionMM": "",
        #                     "usdValue": "0.00000000",
        #                     "availableBalance": "0.02441165",
        #                     "unrealisedPnl": "",
        #                     "totalPositionIM": "",
        #                     "marginBalanceWithoutConvert": "0.00000000",
        #                     "walletBalance": "0.00000000",
        #                     "cumRealisedPnl": "",
        #                     "marginBalance": "0.07862610"
        #                 }
        #             ]
        #         },
        #         "time": 1657716037033
        #     }
        #
        return self.parse_balance(response)

    def fetch_derivatives_balance(self, params={}):
        self.load_markets()
        accountTypes = self.safe_value(self.options, 'accountsByType', {})
        type = self.safe_string(params, 'type')
        params = self.omit(params, ['type'])
        request = {
            'accountType': self.safe_string(accountTypes, type),
        }
        response = self.privateGetV5AccountWalletBalance(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "list": [
        #                 {
        #                     "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",
        #                     "totalMaintenanceMargin": "0.38106773",
        #                     "coin": [
        #                         {
        #                             "availableToBorrow": "2.5",
        #                             "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": {},
        #         "time": 1672125441042
        #     }
        #
        return self.parse_balance(response)

    def fetch_usdc_balance(self, params={}):
        self.load_markets()
        response = self.privatePostOptionUsdcOpenapiPrivateV1QueryWalletBalance(params)
        #
        #    {
        #      "result": {
        #           "walletBalance": "10.0000",
        #           "accountMM": "0.0000",
        #           "bonus": "0.0000",
        #           "accountIM": "0.0000",
        #           "totalSessionRPL": "0.0000",
        #           "equity": "10.0000",
        #           "totalRPL": "0.0000",
        #           "marginBalance": "10.0000",
        #           "availableBalance": "10.0000",
        #           "totalSessionUPL": "0.0000"
        #       },
        #       "retCode": "0",
        #       "retMsg": "Success."
        #    }
        #
        return self.parse_balance(response)

    def fetch_balance(self, params={}):
        """
        query for balance and get the amount of funds available for trading or funds locked in orders
        :param dict params: extra parameters specific to the bybit api endpoint
        :returns dict: a `balance structure <https://docs.ccxt.com/en/latest/manual.html?#balance-structure>`
        """
        self.load_markets()
        type = None
        type, params = self.handle_market_type_and_params('fetchBalance', None, params)
        if type == 'spot':
            return self.fetch_spot_balance(params)
        enableUnifiedMargin, enableUnifiedAccount = self.is_unified_enabled()
        if enableUnifiedAccount:
            return self.fetch_derivatives_balance(self.extend(params, {'type': 'unified'}))
        elif enableUnifiedMargin:
            return self.fetch_unified_margin_balance(params)
        else:
            # linear/inverse future/swap
            return self.fetch_derivatives_balance(self.extend(params, {'type': 'swap'}))

    def parse_order_status(self, status):
        statuses = {
            # v3 spot
            'NEW': 'open',
            'PARTIALLY_FILLED': 'open',
            'FILLED': 'closed',
            'CANCELED': 'canceled',
            'PENDING_CANCEL': 'open',
            'PENDING_NEW': 'open',
            'REJECTED': 'rejected',
            'PARTIALLY_FILLED_CANCELLED': 'canceled',
            # v3 contract / unified margin / unified account
            'Created': 'open',
            'New': 'open',
            'Rejected': 'rejected',  # order is triggered but failed upon being placed
            'PartiallyFilled': 'open',
            'PartiallyFilledCancelled': 'canceled',
            'Filled': 'closed',
            'PendingCancel': 'open',
            'Cancelled': 'canceled',
            # below self line the status only pertains to conditional orders
            'Untriggered': 'open',
            'Deactivated': 'canceled',
            'Triggered': 'open',
            'Active': 'open',
        }
        return self.safe_string(statuses, status, status)

    def parse_time_in_force(self, timeInForce):
        timeInForces = {
            'GoodTillCancel': 'GTC',
            'ImmediateOrCancel': 'IOC',
            'FillOrKill': 'FOK',
            'PostOnly': 'PO',
        }
        return self.safe_string(timeInForces, timeInForce, timeInForce)

    def parse_order(self, order, market=None):
        orderCategoryExists = ('orderCategory' in order)
        if orderCategoryExists:
            return self.parse_spot_order(order, market)
        return self.parse_contract_order(order, market)

    def parse_contract_order(self, order, market=None):
        #
        # contract v3
        #
        #     {
        #         "symbol": "XRPUSDT",
        #         "side": "Buy",
        #         "orderType": "Market",
        #         "price": "0.3431",
        #         "qty": "65",
        #         "reduceOnly": True,
        #         "timeInForce": "ImmediateOrCancel",
        #         "orderStatus": "Filled",
        #         "leavesQty": "0",
        #         "leavesValue": "0",
        #         "cumExecQty": "65",
        #         "cumExecValue": "21.3265",
        #         "cumExecFee": "0.0127959",
        #         "lastPriceOnCreated": "0.0000",
        #         "rejectReason": "EC_NoError",
        #         "orderLinkId": "",
        #         "createdTime": "1657526321499",
        #         "updatedTime": "1657526321504",
        #         "orderId": "ac0a8134-acb3-4ee1-a2d4-41891c9c46d7",
        #         "stopOrderType": "UNKNOWN",
        #         "takeProfit": "0.0000",
        #         "stopLoss": "0.0000",
        #         "tpTriggerBy": "UNKNOWN",
        #         "slTriggerBy": "UNKNOWN",
        #         "triggerPrice": "0.0000",
        #         "closeOnTrigger": True,
        #         "triggerDirection": 0,
        #         "positionIdx": 2
        #     }
        #
        marketId = self.safe_string(order, 'symbol')
        marketType = 'contract'
        if market is not None:
            marketType = market['type']
        market = self.safe_market(marketId, market, None, marketType)
        symbol = market['symbol']
        timestamp = self.safe_integer(order, 'createdTime')
        id = self.safe_string(order, 'orderId')
        type = self.safe_string_lower(order, 'orderType')
        price = self.safe_string(order, 'price')
        amount = self.safe_string(order, 'qty')
        cost = self.safe_string(order, 'cumExecValue')
        filled = self.safe_string(order, 'cumExecQty')
        remaining = self.safe_string(order, 'leavesQty')
        lastTradeTimestamp = self.safe_integer(order, 'updateTime')
        rawStatus = self.safe_string(order, 'orderStatus')
        status = self.parse_order_status(rawStatus)
        side = self.safe_string_lower(order, 'side')
        fee = None
        isContract = self.safe_value(market, 'contract')
        if isContract:
            feeCostString = self.safe_string(order, 'cumExecFee')
            if feeCostString is not None:
                fee = {
                    'cost': feeCostString,
                    'currency': market['settle'],
                }
        clientOrderId = self.safe_string(order, 'orderLinkId')
        if (clientOrderId is not None) and (len(clientOrderId) < 1):
            clientOrderId = None
        rawTimeInForce = self.safe_string(order, 'timeInForce')
        timeInForce = self.parse_time_in_force(rawTimeInForce)
        stopPrice = self.omit_zero(self.safe_string(order, 'triggerPrice'))
        return self.safe_order({
            'info': order,
            'id': id,
            'clientOrderId': clientOrderId,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': lastTradeTimestamp,
            'symbol': symbol,
            'type': type,
            'timeInForce': timeInForce,
            'postOnly': None,
            'side': side,
            'price': price,
            'stopPrice': stopPrice,
            'triggerPrice': stopPrice,
            'amount': amount,
            'cost': cost,
            'average': None,
            'filled': filled,
            'remaining': remaining,
            'status': status,
            'fee': fee,
            'trades': None,
        }, market)

    def parse_spot_order(self, order, market=None):
        #
        #  createOrder, cancelOrer
        #
        #     {
        #         "orderId": "1274754916287346280",
        #         "orderLinkId": "1666798627015730",
        #         "symbol": "AAVEUSDT",
        #         "createTime": "1666698629821",
        #         "orderPrice": "80",
        #         "orderQty": "0.11",
        #         "orderType": "LIMIT",
        #         "side": "BUY",
        #         "status": "NEW",
        #         "timeInForce": "GTC",
        #         "accountId": "13380434",
        #         "execQty": "0",
        #         "orderCategory": "0"
        #     }
        #
        #     fetchOrder, fetchOpenOrders, fetchClosedOrders(and also for conditional orders) there are also present these additional fields:
        #     {
        #         "cummulativeQuoteQty": "0",
        #         "avgPrice": "0",
        #         "stopPrice": "0.0",
        #         "icebergQty": "0.0",
        #         "updateTime": "1666733357444",
        #         "isWorking": "1",
        #         "locked": "8.8",
        #         "executedOrderId": "1279094037543962113",  # in conditional order
        #         "triggerPrice": "0.99",  # in conditional order
        #     }
        #
        marketId = self.safe_string(order, 'symbol')
        market = self.safe_market(marketId, market, None, 'spot')
        timestamp = self.safe_integer(order, 'createTime')
        type = self.safe_string_lower(order, 'orderType')
        price = self.safe_string(order, 'orderPrice')
        if price == '0' and type == 'market':
            price = None
        filled = self.safe_string(order, 'execQty')
        side = self.safe_string_lower(order, 'side')
        timeInForce = self.parse_time_in_force(self.safe_string(order, 'timeInForce'))
        triggerPrice = self.safe_string(order, 'triggerPrice')
        postOnly = (timeInForce == 'PO')
        amount = None
        if market['spot'] and type == 'market' and side == 'buy':
            amount = filled
        else:
            amount = self.safe_string(order, 'orderQty')
        return self.safe_order({
            'id': self.safe_string(order, 'orderId'),
            'clientOrderId': self.safe_string(order, 'orderLinkId'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': self.safe_integer(order, 'updateTime'),
            'symbol': market['symbol'],
            'type': type,
            'timeInForce': timeInForce,
            'postOnly': postOnly,
            'side': side,
            'price': price,
            'triggerPrice': triggerPrice,
            'stopPrice': triggerPrice,  # deprecated field
            'amount': amount,
            'cost': self.safe_string(order, 'cummulativeQuoteQty'),
            'average': self.safe_string(order, 'avgPrice'),
            'filled': filled,
            'remaining': None,
            'status': self.parse_order_status(self.safe_string(order, 'status')),
            'fee': None,
            'trades': None,
            'info': order,
        }, market)

    def fetch_order(self, id, symbol=None, params={}):
        """
        fetches information on an order made by the user
        :param str|None symbol: unified symbol of the market the order was made in
        :param dict params: extra parameters specific to the bybit api endpoint
        :returns dict: An `order structure <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        self.load_markets()
        market = None
        if symbol is not None:
            market = self.market(symbol)
        type = None
        type, params = self.handle_market_type_and_params('fetchOrder', market, params)
        if type == 'spot':
            # only spot markets have a dedicated endpoint for fetching a order
            request = {
                'orderId': id,
            }
            response = self.privateGetSpotV3PrivateOrder(self.extend(params, request))
            #
            #    {
            #        "retCode": "0",
            #        "retMsg": "OK",
            #        "result": {
            #            "accountId": "13380434",
            #            "symbol": "AAVEUSDT",
            #            "orderLinkId": "1666733357434617",
            #            "orderId": "1275046248585414144",
            #            "orderPrice": "80",
            #            "orderQty": "0.11",
            #            "execQty": "0",
            #            "cummulativeQuoteQty": "0",
            #            "avgPrice": "0",
            #            "status": "NEW",
            #            "timeInForce": "GTC",
            #            "orderType": "LIMIT",
            #            "side": "BUY",
            #            "stopPrice": "0.0",
            #            "icebergQty": "0.0",
            #            "createTime": "1666733357438",
            #            "updateTime": "1666733357444",
            #            "isWorking": "1",
            #            "locked": "8.8",
            #            "orderCategory": "0"
            #        },
            #        "retExtMap": {},
            #        "retExtInfo": null,
            #        "time": "1666733357744"
            #    }
            #
            result = self.safe_value(response, 'result', {})
            return self.parse_order(result, market)
        else:
            self.check_required_symbol('fetchOrder', symbol)
            request = {
                'orderId': id,
            }
            result = self.fetch_orders(symbol, None, None, self.extend(request, params))
            length = len(result)
            if length == 0:
                raise OrderNotFound('Order ' + id + ' does not exist.')
            if length > 1:
                raise InvalidOrder(self.id + ' returned more than one order')
            return self.safe_value(result, 0)

    def create_order(self, symbol, type, side, amount, price=None, params={}):
        """
        create a trade order
        :param str symbol: unified symbol of the market to create an order in
        :param str type: 'market' or 'limit'
        :param str side: 'buy' or 'sell'
        :param float amount: how much of currency you want to trade in units of base currency
        :param float|None price: the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
        :param dict params: extra parameters specific to the bybit api endpoint
        :returns dict: an `order structure <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        self.load_markets()
        self.check_required_symbol('createOrder', symbol)
        market = self.market(symbol)
        symbol = market['symbol']
        enableUnifiedMargin, enableUnifiedAccount = self.is_unified_enabled()
        isUSDCSettled = market['settle'] == 'USDC'
        if enableUnifiedAccount and not market['inverse']:
            return self.create_unified_account_order(symbol, type, side, amount, price, params)
        elif market['spot']:
            return self.create_spot_order(symbol, type, side, amount, price, params)
        elif enableUnifiedMargin and not market['inverse']:
            return self.create_unified_margin_order(symbol, type, side, amount, price, params)
        elif isUSDCSettled:
            return self.create_usdc_order(symbol, type, side, amount, price, params)
        else:
            return self.create_contract_v3_order(symbol, type, side, amount, price, params)

    def create_unified_account_order(self, symbol, type, side, amount, price=None, params={}):
        self.load_markets()
        market = self.market(symbol)
        lowerCaseType = type.lower()
        if (price is None) and (lowerCaseType == 'limit'):
            raise ArgumentsRequired(self.id + ' createOrder requires a price argument for limit orders')
        request = {
            'symbol': market['id'],
            'side': self.capitalize(side),
            'orderType': self.capitalize(lowerCaseType),  # limit or market
            'qty': self.amount_to_precision(symbol, amount),
            # '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.45,  # 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
        }
        if market['spot']:
            request['category'] = 'spot'
        elif market['linear']:
            request['category'] = 'linear'
        elif market['option']:
            request['category'] = 'option'
        else:
            raise NotSupported(self.id + ' createOrder does not allow inverse market orders for ' + symbol + ' markets')
        isMarket = lowerCaseType == 'market'
        isLimit = lowerCaseType == 'limit'
        if isLimit:
            request['price'] = self.price_to_precision(symbol, price)
        exchangeSpecificParam = self.safe_string(params, 'time_in_force')
        timeInForce = self.safe_string_lower(params, 'timeInForce')
        postOnly = self.is_post_only(isMarket, exchangeSpecificParam == 'PostOnly', params)
        if postOnly:
            request['timeInForce'] = 'PostOnly'
        elif timeInForce == 'gtc':
            request['timeInForce'] = 'GTC'
        elif timeInForce == 'fok':
            request['timeInForce'] = 'FOK'
        elif timeInForce == 'ioc':
            request['timeInForce'] = 'IOC'
        triggerPrice = self.safe_value_2(params, 'stopPrice', 'triggerPrice')
        stopLossPrice = self.safe_value(params, 'stopLossPrice')
        isStopLossOrder = stopLossPrice is not None
        takeProfitPrice = self.safe_value(params, 'takeProfitPrice')
        isTakeProfitOrder = takeProfitPrice is not None
        if isStopLossOrder:
            request['stopLoss'] = self.price_to_precision(symbol, stopLossPrice)
        if isTakeProfitOrder:
            request['takeProfit'] = self.price_to_precision(symbol, takeProfitPrice)
        if triggerPrice is not None:
            # logical xor
            isBuy = side == 'buy'
            ascending = not isBuy if stopLossPrice else isBuy
            request['triggerDirection'] = 2 if ascending else 1
            request['triggerBy'] = 'LastPrice'
            request['triggerPrice'] = self.price_to_precision(symbol, triggerPrice)
        if market['spot']:
            # only works for spot market
            if triggerPrice is not None or isStopLossOrder or isTakeProfitOrder:
                request['orderFilter'] = 'tpslOrder'
        clientOrderId = self.safe_string(params, 'clientOrderId')
        if clientOrderId is not None:
            request['orderLinkId'] = clientOrderId
        elif market['option']:
            # mandatory field for options
            request['orderLinkId'] = self.uuid16()
        params = self.omit(params, ['stopPrice', 'timeInForce', 'triggerPrice', 'stopLossPrice', 'takeProfitPrice', 'postOnly', 'clientOrderId'])
        response = self.privatePostV5OrderCreate(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "orderId": "1321003749386327552",
        #             "orderLinkId": "spot-test-postonly"
        #         },
        #         "retExtInfo": {},
        #         "time": 1672211918471
        #     }
        #
        order = self.safe_value(response, 'result', {})
        return self.parse_order(order)

    def create_spot_order(self, symbol, type, side, amount, price=None, params={}):
        self.load_markets()
        market = self.market(symbol)
        if (type == 'market') and (side == 'buy'):
            # for market buy it requires the amount of quote currency to spend
            if self.options['createMarketBuyOrderRequiresPrice']:
                cost = self.safe_number(params, 'cost')
                params = self.omit(params, 'cost')
                if price is None and cost is None:
                    raise InvalidOrder(self.id + " createOrder() requires the price argument with market buy orders to calculate total order cost(amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount, or, alternatively, add .options['createMarketBuyOrderRequiresPrice'] = False to supply the cost in the amount argument(the exchange-specific behaviour)")
                else:
                    amountString = self.number_to_string(amount)
                    priceString = self.number_to_string(price)
                    quoteAmount = Precise.string_mul(amountString, priceString)
                    amount = cost if (cost is not None) else self.parse_number(quoteAmount)
        upperCaseType = type.upper()
        request = {
            'symbol': market['id'],
            'side': self.capitalize(side),
            'orderType': upperCaseType,  # limit, market or limit_maker
            'timeInForce': 'GTC',  # FOK, IOC
            'orderQty': self.amount_to_precision(symbol, amount),
            # 'orderLinkId': 'string',  # unique client order id, max 36 characters
        }
        if (upperCaseType == 'LIMIT') or (upperCaseType == 'LIMIT_MAKER'):
            if price is None:
                raise InvalidOrder(self.id + ' createOrder requires a price argument for a ' + type + ' order')
            request['orderPrice'] = self.price_to_precision(symbol, price)
        isPostOnly = self.is_post_only(upperCaseType == 'MARKET', type == 'LIMIT_MAKER', params)
        if isPostOnly:
            request['orderType'] = 'LIMIT_MAKER'
        clientOrderId = self.safe_string_2(params, 'clientOrderId', 'orderLinkId')
        if clientOrderId is not None:
            request['orderLinkId'] = clientOrderId
        params = self.omit(params, ['clientOrderId', 'orderLinkId', 'postOnly'])
        brokerId = self.safe_string(self.options, 'brokerId')
        if brokerId is not None:
            request['agentSource'] = brokerId
        triggerPrice = self.safe_number_2(params, 'triggerPrice', 'stopPrice')
        if triggerPrice is not None:
            request['triggerPrice'] = self.price_to_precision(symbol, triggerPrice)
        params = self.omit(params, 'stopPrice')
        response = self.privatePostSpotV3PrivateOrder(self.extend(request, params))
        #
        #    {
        #        "retCode": "0",
        #        "retMsg": "OK",
        #        "result": {
        #            "orderId": "1274754916287346280",
        #            "orderLinkId": "1666798627015730",
        #            "symbol": "AAVEUSDT",
        #            "createTime": "1666698629821",
        #            "orderPrice": "80",
        #            "orderQty": "0.11",
        #            "orderType": "LIMIT",
        #            "side": "BUY",
        #            "status": "NEW",
        #            "timeInForce": "GTC",
        #            "accountId": "13380434",
        #            "execQty": "0",
        #            "orderCategory": "0"
        #        },
        #        "retExtMap": {},
        #        "retExtInfo": null,
        #        "time": "1666698627926"
        #    }
        #
        order = self.safe_value(response, 'result', {})
        return self.parse_order(order)

    def create_unified_margin_order(self, symbol, type, side, amount, price=None, params={}):
        self.load_markets()
        market = self.market(symbol)
        if not market['linear'] and not market['option']:
            raise NotSupported(self.id + ' createOrder does not allow inverse market orders for ' + symbol + ' markets')
        lowerCaseType = type.lower()
        if (price is None) and (lowerCaseType == 'limit'):
            raise ArgumentsRequired(self.id + ' createOrder requires a price argument for limit orders')
        request = {
            'symbol': market['id'],
            'side': self.capitalize(side),
            'orderType': self.capitalize(lowerCaseType),  # limit or market
            'timeInForce': 'GoodTillCancel',  # ImmediateOrCancel, FillOrKill, PostOnly
            'qty': self.amount_to_precision(symbol, amount),
            # '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.45,  # trigger price, required for conditional orders
            # 'triggerBy': 'MarkPrice',  # IndexPrice, MarkPrice
            # 'tptriggerby': 'MarkPrice',  # IndexPrice, MarkPrice
            # 'slTriggerBy': 'MarkPrice',  # IndexPrice, MarkPrice
            # 'mmp': False  # market maker protection
            # 'positionIdx': 0,  # Position mode. unified margin account is only available in One-Way mode, which is 0
            # 'basePrice': '0',  # It will be used to compare with the value of triggerPrice, to decide whether your conditional order will be triggered by crossing trigger price from upper side or lower side. Mainly used to identify the expected direction of the current conditional order.
            # 'iv': '0',  # Implied volatility, for options only; parameters are passed according to the real value; for example, for 10%, 0.1 is passed
        }
        if market['linear']:
            request['category'] = 'linear'
        else:
            request['category'] = 'option'
        isMarket = lowerCaseType == 'market'
        isLimit = lowerCaseType == 'limit'
        if isLimit:
            request['price'] = self.price_to_precision(symbol, price)
        exchangeSpecificParam = self.safe_string(params, 'time_in_force')
        timeInForce = self.safe_string_lower(params, 'timeInForce')
        postOnly = self.is_post_only(isMarket, exchangeSpecificParam == 'PostOnly', params)
        if postOnly:
            request['timeInForce'] = 'PostOnly'
        elif timeInForce == 'gtc':
            request['timeInForce'] = 'GoodTillCancel'
        elif timeInForce == 'fok':
            request['timeInForce'] = 'FillOrKill'
        elif timeInForce == 'ioc':
            request['timeInForce'] = 'ImmediateOrCancel'
        triggerPrice = self.safe_value_2(params, 'stopPrice', 'triggerPrice')
        stopLossPrice = self.safe_value(params, 'stopLossPrice', triggerPrice)
        isStopLossOrder = stopLossPrice is not None
        takeProfitPrice = self.safe_value(params, 'takeProfitPrice')
        isTakeProfitOrder = takeProfitPrice is not None
        if isStopLossOrder or isTakeProfitOrder:
            request['triggerBy'] = 'LastPrice'
            triggerAt = stopLossPrice if isStopLossOrder else takeProfitPrice
            preciseTriggerPrice = self.price_to_precision(symbol, triggerAt)
            request['triggerPrice'] = preciseTriggerPrice
            isBuy = side == 'buy'
            # logical xor
            ascending = not isBuy if stopLossPrice else isBuy
            delta = self.number_to_string(market['precision']['price'])
            request['basePrice'] = Precise.string_add(preciseTriggerPrice, delta) if ascending else Precise.string_sub(preciseTriggerPrice, delta)
        clientOrderId = self.safe_string(params, 'clientOrderId')
        if clientOrderId is not None:
            request['orderLinkId'] = clientOrderId
        elif market['option']:
            # mandatory field for options
            request['orderLinkId'] = self.uuid16()
        params = self.omit(params, ['stopPrice', 'timeInForce', 'triggerPrice', 'stopLossPrice', 'takeProfitPrice', 'postOnly', 'clientOrderId'])
        response = self.privatePostUnifiedV3PrivateOrderCreate(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "orderId": "e10b0716-7c91-4091-b98a-1fa0f401c7d5",
        #             "orderLinkId": "test0000003"
        #         },
        #         "retExtInfo": null,
        #         "time": 1664441344238
        #     }
        #
        order = self.safe_value(response, 'result', {})
        return self.parse_order(order)

    def create_contract_v3_order(self, symbol, type, side, amount, price=None, params={}):
        self.load_markets()
        market = self.market(symbol)
        lowerCaseType = type.lower()
        if (price is None) and (lowerCaseType == 'limit'):
            raise ArgumentsRequired(self.id + ' createContractV3Order requires a price argument for limit orders')
        request = {
            'symbol': market['id'],
            'side': self.capitalize(side),
            'orderType': self.capitalize(lowerCaseType),  # limit or market
            'timeInForce': 'GoodTillCancel',  # ImmediateOrCancel, FillOrKill, PostOnly
            'qty': self.amount_to_precision(symbol, amount),
            # '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.45,  # trigger price, required for conditional orders
            # 'triggerBy': 'MarkPrice',  # IndexPrice, MarkPrice
            # 'tptriggerby': 'MarkPrice',  # IndexPrice, MarkPrice
            # 'slTriggerBy': 'MarkPrice',  # IndexPrice, MarkPrice
            # 'positionIdx': 0,  # Position mode. unified margin account is only available in One-Way mode, which is 0
            # 'triggerDirection': 1,  # Trigger direction. Mainly used in conditional order. Trigger the order when market price rises to triggerPrice or falls to triggerPrice. 1: rise; 2: fall
        }
        if market['future']:
            positionIdx = self.safe_integer(params, 'position_idx', 0)  # 0 One-Way Mode, 1 Buy-side, 2 Sell-side
            request['position_idx'] = positionIdx
            params = self.omit(params, 'position_idx')
        isMarket = lowerCaseType == 'market'
        isLimit = lowerCaseType == 'limit'
        if isLimit:
            request['price'] = self.price_to_precision(symbol, price)
        exchangeSpecificParam = self.safe_string(params, 'time_in_force')
        timeInForce = self.safe_string_lower(params, 'timeInForce')
        postOnly = self.is_post_only(isMarket, exchangeSpecificParam == 'PostOnly', params)
        if postOnly:
            request['timeInForce'] = 'PostOnly'
        elif timeInForce == 'gtc':
            request['timeInForce'] = 'GoodTillCancel'
        elif timeInForce == 'fok':
            request['timeInForce'] = 'FillOrKill'
        elif timeInForce == 'ioc':
            request['timeInForce'] = 'ImmediateOrCancel'
        triggerPrice = self.safe_value_2(params, 'stopPrice', 'triggerPrice')
        stopLossPrice = self.safe_value(params, 'stopLossPrice', triggerPrice)
        isStopLossOrder = stopLossPrice is not None
        takeProfitPrice = self.safe_value(params, 'takeProfitPrice')
        isTakeProfitOrder = takeProfitPrice is not None
        if isStopLossOrder or isTakeProfitOrder:
            triggerAt = stopLossPrice if isStopLossOrder else takeProfitPrice
            preciseTriggerPrice = self.price_to_precision(symbol, triggerAt)
            isBuy = side == 'buy'
            # logical xor
            ascending = not isBuy if stopLossPrice else isBuy
            request['triggerDirection'] = 2 if ascending else 1
            request['triggerBy'] = 'LastPrice'
            request['triggerPrice'] = self.price_to_precision(symbol, preciseTriggerPrice)
        clientOrderId = self.safe_string(params, 'clientOrderId')
        if clientOrderId is not None:
            request['orderLinkId'] = clientOrderId
        elif market['option']:
            # mandatory field for options
            request['orderLinkId'] = self.uuid16()
        params = self.omit(params, ['stopPrice', 'timeInForce', 'triggerPrice', 'stopLossPrice', 'takeProfitPrice', 'postOnly', 'clientOrderId'])
        response = self.privatePostContractV3PrivateOrderCreate(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "orderId": "e10b0716-7c91-4091-b98a-1fa0f401c7d5",
        #             "orderLinkId": "test0000003"
        #         },
        #         "retExtInfo": null,
        #         "time": 1664441344238
        #     }
        #
        order = self.safe_value(response, 'result', {})
        return self.parse_order(order)

    def create_usdc_order(self, symbol, type, side, amount, price=None, params={}):
        self.load_markets()
        market = self.market(symbol)
        if type == 'market':
            raise NotSupported(self.id + 'createOrder does not allow market orders for ' + symbol + ' markets')
        lowerCaseType = type.lower()
        if (price is None) and (lowerCaseType == 'limit'):
            raise ArgumentsRequired(self.id + ' createOrder requires a price argument for limit orders')
        request = {
            'symbol': market['id'],
            'side': self.capitalize(side),
            'orderType': self.capitalize(lowerCaseType),  # limit or market
            'timeInForce': 'GoodTillCancel',  # ImmediateOrCancel, FillOrKill, PostOnly
            'orderQty': self.amount_to_precision(symbol, amount),
            # '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.45,  # trigger price, required for conditional orders
            # 'trigger_by': 'MarkPrice',  # IndexPrice, MarkPrice
            # 'tptriggerby': 'MarkPrice',  # IndexPrice, MarkPrice
            # 'slTriggerBy': 'MarkPrice',  # IndexPrice, MarkPrice
            # 'orderFilter': 'Order' or 'StopOrder'
            # 'mmp': False  # market maker protection
        }
        isMarket = lowerCaseType == 'market'
        isLimit = lowerCaseType == 'limit'
        if isLimit is not None:
            request['orderPrice'] = self.price_to_precision(symbol, price)
        exchangeSpecificParam = self.safe_string(params, 'time_in_force')
        timeInForce = self.safe_string_lower(params, 'timeInForce')
        postOnly = self.is_post_only(isMarket, exchangeSpecificParam == 'PostOnly', params)
        if postOnly:
            request['time_in_force'] = 'PostOnly'
        elif timeInForce == 'gtc':
            request['time_in_force'] = 'GoodTillCancel'
        elif timeInForce == 'fok':
            request['time_in_force'] = 'FillOrKill'
        elif timeInForce == 'ioc':
            request['time_in_force'] = 'ImmediateOrCancel'
        if market['swap']:
            triggerPrice = self.safe_value_2(params, 'stopPrice', 'triggerPrice')
            stopLossPrice = self.safe_value(params, 'stopLossPrice', triggerPrice)
            isStopLossOrder = stopLossPrice is not None
            takeProfitPrice = self.safe_value(params, 'takeProfitPrice')
            isTakeProfitOrder = takeProfitPrice is not None
            isStopOrder = isStopLossOrder or isTakeProfitOrder
            if isStopOrder:
                request['orderFilter'] = 'StopOrder'
                request['trigger_by'] = 'LastPrice'
                stopPx = stopLossPrice if isStopLossOrder else takeProfitPrice
                preciseStopPrice = self.price_to_precision(symbol, stopPx)
                request['triggerPrice'] = preciseStopPrice
                delta = self.number_to_string(market['precision']['price'])
                request['basePrice'] = Precise.string_sub(preciseStopPrice, delta) if isStopLossOrder else Precise.string_add(preciseStopPrice, delta)
            else:
                request['orderFilter'] = 'Order'
        clientOrderId = self.safe_string(params, 'clientOrderId')
        if clientOrderId is not None:
            request['orderLinkId'] = clientOrderId
        elif market['option']:
            # mandatory field for options
            request['orderLinkId'] = self.uuid16()
        params = self.omit(params, ['stopPrice', 'timeInForce', 'triggerPrice', 'stopLossPrice', 'takeProfitPrice', 'postOnly', 'clientOrderId'])
        method = 'privatePostOptionUsdcOpenapiPrivateV1PlaceOrder' if market['option'] else 'privatePostPerpetualUsdcOpenapiPrivateV1PlaceOrder'
        response = getattr(self, method)(self.extend(request, params))
        #
        #     {
        #         "retCode":0,
        #         "retMsg":"",
        #         "result":{
        #            "orderId":"34450a59-325e-4296-8af0-63c7c524ae33",
        #            "orderLinkId":"",
        #            "mmp":false,
        #            "symbol":"BTCPERP",
        #            "orderType":"Limit",
        #            "side":"Buy",
        #            "orderQty":"0.00100000",
        #            "orderPrice":"20000.00",
        #            "iv":"0",
        #            "timeInForce":"GoodTillCancel",
        #            "orderStatus":"Created",
        #            "createdAt":"1652261746007873",
        #            "basePrice":"0.00",
        #            "triggerPrice":"0.00",
        #            "takeProfit":"0.00",
        #            "stopLoss":"0.00",
        #            "slTriggerBy":"UNKNOWN",
        #            "tpTriggerBy":"UNKNOWN"
        #     }
        #
        order = self.safe_value(response, 'result', {})
        return self.parse_order(order)

    def edit_unified_account_order(self, id, symbol, type, side, amount=None, price=None, params={}):
        self.load_markets()
        market = self.market(symbol)
        if not market['linear'] and not market['option']:
            raise NotSupported(self.id + ' editOrder does not allow inverse market orders for ' + symbol + ' markets')
        request = {
            'symbol': market['id'],
            'orderId': id,
            'qty': self.amount_to_precision(symbol, amount),
            # '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
        }
        if market['linear']:
            request['category'] = 'linear'
        else:
            request['category'] = 'option'
        if price is not None:
            request['price'] = self.price_to_precision(symbol, price)
        triggerPrice = self.safe_value_2(params, 'stopPrice', 'triggerPrice')
        stopLossPrice = self.safe_value(params, 'stopLossPrice')
        isStopLossOrder = stopLossPrice is not None
        takeProfitPrice = self.safe_value(params, 'takeProfitPrice')
        isTakeProfitOrder = takeProfitPrice is not None
        if isStopLossOrder:
            request['stopLoss'] = self.price_to_precision(symbol, stopLossPrice)
        if isTakeProfitOrder:
            request['takeProfit'] = self.price_to_precision(symbol, takeProfitPrice)
        if triggerPrice is not None:
            request['triggerPrice'] = self.price_to_precision(symbol, triggerPrice)
        params = self.omit(params, ['stopPrice', 'triggerPrice', 'stopLossPrice', 'takeProfitPrice'])
        response = self.privatePostV5OrderAmend(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "orderId": "c6f055d9-7f21-4079-913d-e6523a9cfffa",
        #             "orderLinkId": "linear-004"
        #         },
        #         "retExtInfo": {},
        #         "time": 1672217093461
        #     }
        #
        result = self.safe_value(response, 'result', {})
        return {
            'info': response,
            'id': self.safe_string(result, 'orderId'),
        }

    def edit_unified_margin_order(self, id, symbol, type, side, amount, price=None, params={}):
        self.load_markets()
        market = self.market(symbol)
        if not market['linear'] and not market['option']:
            raise NotSupported(self.id + ' editOrder does not allow inverse market orders for ' + symbol + ' markets')
        lowerCaseType = type.lower()
        if (price is None) and (lowerCaseType == 'limit'):
            raise ArgumentsRequired(self.id + ' editOrder requires a price argument for limit orders')
        request = {
            'orderId': id,
            'symbol': market['id'],
            'side': self.capitalize(side),
            'orderType': self.capitalize(lowerCaseType),  # limit or market
            'timeInForce': 'GoodTillCancel',  # ImmediateOrCancel, FillOrKill, PostOnly
            'qty': self.amount_to_precision(symbol, amount),
            # '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
            # 'orderLinkId': 'string',  # unique client order id, max 36 characters
            # 'triggerPrice': 123.45,  # trigger price, required for conditional orders
            # 'triggerBy': 'MarkPrice',  # IndexPrice, MarkPrice
            # 'tptriggerby': 'MarkPrice',  # IndexPrice, MarkPrice
            # 'slTriggerBy': 'MarkPrice',  # IndexPrice, MarkPrice
            # 'iv': '0',  # Implied volatility, for options only; parameters are passed according to the real value; for example, for 10%, 0.1 is passed
        }
        if market['linear']:
            request['category'] = 'linear'
        else:
            request['category'] = 'option'
        isMarket = lowerCaseType == 'market'
        isLimit = lowerCaseType == 'limit'
        if isLimit:
            request['price'] = self.price_to_precision(symbol, price)
        exchangeSpecificParam = self.safe_string(params, 'time_in_force')
        timeInForce = self.safe_string_lower(params, 'timeInForce')
        postOnly = self.is_post_only(isMarket, exchangeSpecificParam == 'PostOnly', params)
        if postOnly:
            request['timeInForce'] = 'PostOnly'
        elif timeInForce == 'gtc':
            request['timeInForce'] = 'GoodTillCancel'
        elif timeInForce == 'fok':
            request['timeInForce'] = 'FillOrKill'
        elif timeInForce == 'ioc':
            request['timeInForce'] = 'ImmediateOrCancel'
        triggerPrice = self.safe_value_2(params, 'stopPrice', 'triggerPrice')
        stopLossPrice = self.safe_value(params, 'stopLossPrice')
        isStopLossOrder = stopLossPrice is not None
        takeProfitPrice = self.safe_value(params, 'takeProfitPrice')
        isTakeProfitOrder = takeProfitPrice is not None
        if isStopLossOrder:
            request['stopLoss'] = self.price_to_precision(symbol, stopLossPrice)
        if isTakeProfitOrder:
            request['takeProfit'] = self.price_to_precision(symbol, takeProfitPrice)
        if triggerPrice is not None:
            request['triggerBy'] = 'LastPrice'
            request['triggerPrice'] = self.price_to_precision(symbol, triggerPrice)
        clientOrderId = self.safe_string(params, 'clientOrderId')
        if clientOrderId is not None:
            request['orderLinkId'] = clientOrderId
        params = self.omit(params, ['stopPrice', 'timeInForce', 'triggerPrice', 'stopLossPrice', 'takeProfitPrice', 'postOnly', 'clientOrderId'])
        response = self.privatePostUnifiedV3PrivateOrderReplace(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "orderId": "42c86d66331e41998d12c2440ce90c1a",
        #             "orderLinkId": "e80d558e-ed"
        #         }
        #     }
        #
        order = self.safe_value(response, 'result', {})
        return self.parse_order(order)

    def edit_contract_v3_order(self, id, symbol, type, side, amount=None, price=None, params={}):
        self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
            'orderId': id,
            'qty': self.amount_to_precision(symbol, amount),
        }
        if price is not None:
            request['price'] = self.price_to_precision(symbol, price)
        triggerPrice = self.safe_value_2(params, 'stopPrice', 'triggerPrice')
        stopLossPrice = self.safe_value(params, 'stopLossPrice')
        isStopLossOrder = stopLossPrice is not None
        takeProfitPrice = self.safe_value(params, 'takeProfitPrice')
        isTakeProfitOrder = takeProfitPrice is not None
        if isStopLossOrder:
            request['stopLoss'] = self.price_to_precision(symbol, stopLossPrice)
        if isTakeProfitOrder:
            request['takeProfit'] = self.price_to_precision(symbol, takeProfitPrice)
        if triggerPrice is not None:
            request['triggerPrice'] = self.price_to_precision(symbol, triggerPrice)
        params = self.omit(params, ['stopPrice', 'triggerPrice', 'stopLossPrice', 'takeProfitPrice'])
        response = self.privatePostContractV3PrivateOrderReplace(self.extend(request, params))
        #
        # contract v3
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "orderId": "db8b74b3-72d3-4264-bf3f-52d39b41956e",
        #             "orderLinkId": "x002"
        #         },
        #         "retExtInfo": {},
        #         "time": 1658902610749
        #     }
        #
        result = self.safe_value(response, 'result', {})
        return {
            'info': response,
            'id': self.safe_string(result, 'orderId'),
        }

    def edit_order(self, id, symbol, type, side, amount=None, price=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' editOrder() requires an symbol argument')
        self.load_markets()
        market = self.market(symbol)
        enableUnifiedMargin, enableUnifiedAccount = self.is_unified_enabled()
        if enableUnifiedAccount:
            return self.edit_unified_account_order(id, symbol, type, side, amount, price, params)
        elif market['spot']:
            raise NotSupported(self.id + ' editOrder() does not support spot markets')
        elif enableUnifiedMargin and not market['inverse']:
            return self.edit_unified_margin_order(id, symbol, type, side, amount, price, params)
        return self.edit_contract_v3_order(id, symbol, type, side, amount, price, params)

    def cancel_unified_account_order(self, id, symbol=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelOrder() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        request = {
            '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
            isStop = self.safe_value(params, 'stop', False)
            params = self.omit(params, ['stop'])
            request['orderFilter'] = 'tpslOrder' if isStop else 'Order'
        if id is not None:  # The user can also use argument params["orderLinkId"]
            request['orderId'] = id
        if market['spot']:
            request['category'] = 'spot'
        elif market['option']:
            request['category'] = 'option'
        elif market['linear']:
            request['category'] = 'linear'
        else:
            raise NotSupported(self.id + ' cancelOrder() does not allow inverse market orders for ' + symbol + ' markets')
        response = self.privatePostV5OrderCancel(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "orderId": "c6f055d9-7f21-4079-913d-e6523a9cfffa",
        #             "orderLinkId": "linear-004"
        #         },
        #         "retExtInfo": {},
        #         "time": 1672217377164
        #     }
        #
        result = self.safe_value(response, 'result', {})
        return self.parse_order(result, market)

    def cancel_spot_order(self, id, symbol=None, params={}):
        self.load_markets()
        market = self.market(symbol)
        request = {
            # 'order_link_id': 'string',  # one of order_id, stop_order_id or order_link_id is required
            # 'orderId': id
        }
        if id is not None:  # The user can also use argument params["order_link_id"]
            request['orderId'] = id
        response = self.privatePostSpotV3PrivateCancelOrder(self.extend(request, params))
        #
        #     {
        #         "retCode": "0",
        #         "retMsg": "OK",
        #         "result": {
        #             "orderId": "1275046248585414144",
        #             "orderLinkId": "1666733357434617",
        #             "symbol": "AAVEUSDT",
        #             "status": "NEW",
        #             "accountId": "13380434",
        #             "createTime": "1666733357438",
        #             "orderPrice": "80",
        #             "orderQty": "0.11",
        #             "execQty": "0",
        #             "timeInForce": "GTC",
        #             "orderType": "LIMIT",
        #             "side": "BUY",
        #             "orderCategory": "0"
        #         },
        #         "retExtMap": {},
        #         "retExtInfo": null,
        #         "time": "1666733839493"
        #     }
        #
        result = self.safe_value(response, 'result', {})
        return self.parse_order(result, market)

    def cancel_unified_margin_order(self, id, symbol=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelUnifiedMarginOrder() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
            # 'orderLinkId': 'string',
            # 'orderId': id,
            # conditional orders
            # 'orderFilter': '',
            # 'category': '',
        }
        isStop = self.safe_value(params, 'stop', False)
        params = self.omit(params, ['stop'])
        request['orderFilter'] = 'StopOrder' if isStop else 'Order'
        if id is not None:  # The user can also use argument params["orderLinkId"]
            request['orderId'] = id
        if market['option']:
            request['category'] = 'option'
        elif market['linear']:
            request['category'] = 'linear'
        else:
            raise NotSupported(self.id + ' cancelUnifiedMarginOrder() does not allow inverse market orders for ' + symbol + ' markets')
        response = self.privatePostUnifiedV3PrivateOrderCancel(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "orderId": "42c86d66331e41998d12c2440ce90c1a",
        #             "orderLinkId": "e80d558e-ed"
        #         }
        #     }
        #
        result = self.safe_value(response, 'result', {})
        return self.parse_order(result, market)

    def cancel_usdc_order(self, id, symbol=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelUSDCOrder() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
            # 'orderLinkId': 'string',  # one of order_id, stop_order_id or order_link_id is required
            # 'orderId': id,
        }
        isStop = self.safe_value(params, 'stop', False)
        params = self.omit(params, ['stop'])
        method = None
        if id is not None:  # The user can also use argument params["order_link_id"]
            request['orderId'] = id
        if market['option']:
            method = 'privatePostOptionUsdcOpenapiPrivateV1CancelOrder'
        else:
            method = 'privatePostPerpetualUsdcOpenapiPrivateV1CancelOrder'
            request['orderFilter'] = 'StopOrder' if isStop else 'Order'
        response = getattr(self, method)(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "outRequestId": "",
        #             "symbol": "BTC-13MAY22-40000-C",
        #             "orderId": "8c65df91-91fc-461d-9b14-786379ef138c",
        #             "orderLinkId": ""
        #         },
        #         "retExtMap": {}
        #     }
        #
        result = self.safe_value(response, 'result', {})
        return self.parse_order(result, market)

    def cancel_derivatives_order(self, id, symbol=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelDerivativesOrder() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
            'orderId': id,
        }
        response = self.privatePostContractV3PrivateOrderCancel(self.extend(request, params))
        #
        # contract v3
        #
        #     {
        #         "retCode":0,
        #         "retMsg":"OK",
        #         "result":{
        #             "orderId": "4030430d-1dba-4134-ac77-3d81c14aaa00",
        #             "orderLinkId": ""
        #         },
        #         "retExtInfo":null,
        #         "time":1658850321861
        #     }
        #
        result = self.safe_value(response, 'result', {})
        return self.parse_order(result, market)

    def cancel_order(self, id, symbol=None, params={}):
        """
        cancels an open order
        :param str id: order id
        :param str symbol: unified symbol of the market the order was made in
        :param dict params: extra parameters specific to the bybit api endpoint
        :returns dict: An `order structure <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelOrder() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        enableUnifiedMargin, enableUnifiedAccount = self.is_unified_enabled()
        isUsdcSettled = market['settle'] == 'USDC'
        if enableUnifiedAccount:
            return self.cancel_unified_account_order(id, symbol, params)
        elif market['spot']:
            return self.cancel_spot_order(id, symbol, params)
        elif enableUnifiedMargin and not market['inverse']:
            return self.cancel_unified_margin_order(id, symbol, params)
        elif isUsdcSettled:
            return self.cancel_usdc_order(id, symbol, params)
        return self.cancel_derivatives_order(id, symbol, params)

    def cancel_all_unified_account_orders(self, symbol=None, params={}):
        self.load_markets()
        market = None
        settle = None
        type = None
        subType = None
        request = {}
        if symbol is not None:
            market = self.market(symbol)
            settle = market['settle']
            request['symbol'] = market['id']
        else:
            settle, params = self.handle_option_and_params(params, 'cancelAllOrders', 'settle', 'USDT')
        type, params = self.handle_market_type_and_params('cancelAllOrders', market, params)
        subType, params = self.handle_sub_type_and_params('cancelAllOrders', market, params, 'linear')
        if type == 'spot':
            request['category'] = 'spot'
        elif type == 'option':
            request['category'] = 'option'
        elif subType == 'linear':
            request['category'] = 'linear'
        else:
            raise NotSupported(self.id + ' cancelAllOrders() does not allow inverse market orders for ' + type + ' markets')
        request['settleCoin'] = settle
        isStop = self.safe_value(params, 'stop', False)
        params = self.omit(params, ['stop'])
        if isStop:
            request['orderFilter'] = 'tpslOrder'
        response = self.privatePostV5OrderCancelAll(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "list": [
        #                 {
        #                     "orderId": "f6a73e1f-39b5-4dee-af21-1460b2e3b27c",
        #                     "orderLinkId": "a001"
        #                 }
        #             ]
        #         },
        #         "retExtInfo": {},
        #         "time": 1672219780463
        #     }
        #
        result = self.safe_value(response, 'result', [])
        orders = self.safe_value(result, 'list')
        if not isinstance(orders, list):
            return response
        return self.parse_orders(orders, market)

    def cancel_all_spot_orders(self, symbol=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelAllSpotOrders() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        response = self.privateDeleteSpotOrderBatchCancel(self.extend(request, params))
        #
        #    {
        #        "ret_code": 0,
        #        "ret_msg": "",
        #        "ext_code": null,
        #        "ext_info": null,
        #        "result": {
        #            "success": True
        #        }
        #    }
        #
        result = self.safe_value(response, 'result', [])
        if not isinstance(result, list):
            return response
        return self.parse_orders(result, market)

    def cancel_all_unified_margin_orders(self, symbol=None, params={}):
        self.load_markets()
        market = None
        settle = None
        request = {}
        if symbol is not None:
            market = self.market(symbol)
            settle = market['settle']
            request['symbol'] = market['id']
        subType = None
        subType, params = self.handle_sub_type_and_params('cancelAllOrders', market, params, 'linear')
        request['category'] = subType
        settle, params = self.handle_option_and_params(params, 'cancelAllOrders', 'settle', settle)
        if settle is not None:
            request['settleCoin'] = settle
        isStop = self.safe_value(params, 'stop', False)
        params = self.omit(params, ['stop'])
        if isStop:
            request['orderFilter'] = 'StopOrder'
        response = self.privatePostUnifiedV3PrivateOrderCancelAll(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "list": [{
        #                     "category": "option",
        #                     "symbol": "BTC-24JUN22-45000-P",
        #                     "orderId": "bd5f3b34-d64d-4b60-8188-438fbea4c552",
        #                     "orderLinkId": "ac4e3b34-d64d-4b60-8188-438fbea4c552",
        #                 }, {
        #                     "category": "option",
        #                     "symbol": "BTC-24JUN22-45000-P",
        #                     "orderId": "4ddd727a-2af8-430e-a293-42895e594d18",
        #                     "orderLinkId": "5cee727a-2af8-430e-a293-42895e594d18",
        #                 }
        #             ]
        #         },
        #         "retExtInfo": {
        #             "list": [{
        #                 "code": 0,
        #                 "msg": "OK"
        #             }, {
        #                 "code": 0,
        #                 "msg": "OK"
        #             }]
        #         },
        #         "time": 1657200736570
        #     }
        #
        result = self.safe_value(response, 'result', [])
        orders = self.safe_value(result, 'list')
        if not isinstance(orders, list):
            return response
        return self.parse_orders(orders, market)

    def cancel_all_usdc_orders(self, symbol=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelAllUSDCOrders() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        method = None
        request = {
            'symbol': market['id'],
        }
        if market['option']:
            method = 'privatePostOptionUsdcOpenapiPrivateV1CancelAll'
        else:
            method = 'privatePostPerpetualUsdcOpenapiPrivateV1CancelAll'
            isStop = self.safe_value(params, 'stop', False)
            if isStop:
                request['orderFilter'] = 'StopOrder'
            else:
                request['orderFilter'] = 'Order'
            params = self.omit(params, ['stop'])
        response = getattr(self, method)(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "retExtMap": {},
        #         "result": [
        #             {
        #                 "outRequestId": "cancelAll-290119-1652176443114-0",
        #                 "symbol": "BTC-13MAY22-40000-C",
        #                 "orderId": "fa6cd740-56ed-477d-9385-90ccbfee49ca",
        #                 "orderLinkId": "",
        #                 "errorCode": 0,
        #                 "errorDesc": ""
        #             }
        #         ]
        #     }
        #
        result = self.safe_value(response, 'result', [])
        if not isinstance(result, list):
            return response
        return self.parse_orders(result, market)

    def cancel_all_derivatives_orders(self, symbol=None, params={}):
        self.load_markets()
        market = None
        settle = None
        request = {}
        if symbol is not None:
            market = self.market(symbol)
            settle = market['settle']
            request['symbol'] = market['id']
        settle, params = self.handle_option_and_params(params, 'cancelAllOrders', 'settle', settle)
        if settle is not None:
            request['settleCoin'] = settle
        response = self.privatePostContractV3PrivateOrderCancelAll(self.extend(request, params))
        #
        # contract v3
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "list": [
        #                 {
        #                     "orderId": "4030430d-1dba-4134-ac77-3d81c14aaa00",
        #                     "orderLinkId": "x001"
        #                 }
        #             ]
        #         },
        #         "retExtInfo": {},
        #         "time": 1658901359225
        #     }
        #
        result = self.safe_value(response, 'result', [])
        orders = self.safe_value(result, 'list', [])
        return self.parse_orders(orders, market)

    def cancel_all_orders(self, symbol=None, params={}):
        """
        cancel all open orders
        :param str|None symbol: unified market symbol, only orders in the market of self symbol are cancelled when symbol is not None
        :param dict params: extra parameters specific to the bybit api endpoint
        :returns [dict]: a list of `order structures <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        self.load_markets()
        market = None
        settle = self.safe_string(params, 'settleCoin')
        if settle is None:
            settle, params = self.handle_option_and_params(params, 'cancelAllOrders', 'settle', settle)
        if symbol is not None:
            market = self.market(symbol)
            settle = market['settle']
        subType = None
        subType, params = self.handle_sub_type_and_params('cancelAllOrders', market, params)
        isUsdcSettled = settle == 'USDC'
        isInverse = subType == 'inverse'
        isLinearSettle = isUsdcSettled or (settle == 'USDT')
        if isInverse and isLinearSettle:
            raise ArgumentsRequired(self.id + ' cancelAllOrders with inverse subType requires settle to not be USDT or USDC')
        type, query = self.handle_market_type_and_params('cancelAllOrders', market, params)
        enableUnifiedMargin, enableUnifiedAccount = self.is_unified_enabled()
        if enableUnifiedAccount:
            return self.cancel_all_unified_account_orders(symbol, query)
        elif type == 'spot':
            return self.cancel_all_spot_orders(symbol, query)
        elif enableUnifiedMargin and not isInverse:
            return self.cancel_all_unified_margin_orders(symbol, query)
        elif isUsdcSettled:
            return self.cancel_all_usdc_orders(symbol, query)
        else:
            return self.cancel_all_derivatives_orders(symbol, query)

    def fetch_unified_account_orders(self, symbol=None, since=None, limit=None, params={}):
        self.load_markets()
        request = {
            # 'symbol': market['id'],
            # 'category': string, Type of derivatives product: spot, linear or option.
            # 'baseCoin': string, Base coin. When category=option. If not passed, BTC by default; when category=linear, if BTC passed, BTCPERP & BTCUSDT returned.
            # 'orderId': string, Order ID
            # 'orderLinkId': string, Unique user-set order ID
            # 'orderStatus': string,  # Return all status orders if not passed
            # 'orderFilter': string, Conditional order or active order
            # 'limit': number, Data quantity per page: Max data value per page is 50, and default value at 20.
            # 'cursor': string, API pass-through. accountType + category + cursor +. If inconsistent, the following should be returned: The account type does not match the service inquiry.
        }
        market = None
        if symbol is None:
            type = None
            type, params = self.handle_market_type_and_params('fetchOrders', market, params)
            # option, spot
            request['category'] = type
            if type == 'swap':
                subType = None
                subType, params = self.handle_sub_type_and_params('fetchOrders', market, params, 'linear')
                request['category'] = subType
        else:
            market = self.market(symbol)
            request['symbol'] = market['id']
            if market['spot']:
                request['category'] = 'spot'
            elif market['option']:
                request['category'] = 'option'
            elif market['linear']:
                request['category'] = 'linear'
            else:
                raise NotSupported(self.id + ' fetchOrders() does not allow inverse market orders for ' + symbol + ' markets')
        isStop = self.safe_value(params, 'stop', False)
        params = self.omit(params, ['stop'])
        if isStop:
            if market['spot']:
                request['orderFilter'] = 'tpslOrder'
            else:
                request['orderFilter'] = 'StopOrder'
        if limit is not None:
            request['limit'] = limit
        response = self.privateGetV5OrderHistory(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "nextPageCursor": "03234de9-1332-41eb-b805-4a9f42c136a3%3A1672220109387%2C03234de9-1332-41eb-b805-4a9f42c136a3%3A1672220109387",
        #             "category": "linear",
        #             "list": [
        #                 {
        #                     "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": {},
        #         "time": 1672221263862
        #     }
        #
        result = self.safe_value(response, 'result', {})
        data = self.safe_value(result, 'list', [])
        return self.parse_orders(data, market, since, limit)

    def fetch_unified_margin_orders(self, symbol=None, since=None, limit=None, params={}):
        self.load_markets()
        request = {
            # 'symbol': market['id'],
            # 'category': string, Type of derivatives product: linear or option.
            # 'baseCoin': string, Base coin. When category=option. If not passed, BTC by default; when category=linear, if BTC passed, BTCPERP & BTCUSDT returned.
            # 'orderId': string, Order ID
            # 'orderLinkId': string, Unique user-set order ID
            # 'orderStatus': string, Query list of orders in designated states. If self parameter is not passed, the orders in all states shall be enquired by default. This parameter supports multi-state inquiry. States should be separated with English commas.
            # 'orderFilter': string, Conditional order or active order
            # 'direction': string, prev: prev, next: next.
            # 'limit': number, Data quantity per page: Max data value per page is 50, and default value at 20.
            # 'cursor': string, API pass-through. accountType + category + cursor +. If inconsistent, the following should be returned: The account type does not match the service inquiry.
        }
        market = None
        if symbol is None:
            subType = None
            subType, params = self.handle_sub_type_and_params('fetchUnifiedMarginOrders', market, params, 'linear')
            request['category'] = subType
        else:
            market = self.market(symbol)
            request['symbol'] = market['id']
            if market['option']:
                request['category'] = 'option'
            elif market['linear']:
                request['category'] = 'linear'
            else:
                raise NotSupported(self.id + ' fetchUnifiedMarginOrders() does not allow inverse market orders for ' + symbol + ' markets')
        isStop = self.safe_value(params, 'stop', False)
        params = self.omit(params, ['stop'])
        if isStop:
            request['orderFilter'] = 'StopOrder'
        if limit is not None:
            request['limit'] = limit
        response = self.privateGetUnifiedV3PrivateOrderList(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "Success",
        #         "result": {
        #         "nextPageCursor": "7d17d359-4e38-4d3a-9a31-29791ef2dfd7%3A1657711949928%2C7d17d359-4e38-4d3a-9a31-29791ef2dfd7%3A1657711949928",
        #         "category": "linear",
        #         "list": [
        #             {
        #                 "symbol": "ETHUSDT",
        #                 "orderType": "Market",
        #                 "orderLinkId": "",
        #                 "orderId": "7d17d359-4e38-4d3a-9a31-29791ef2dfd7",
        #                 "stopOrderType": "UNKNOWN",
        #                 "orderStatus": "Filled",
        #                 "takeProfit": "",
        #                 "cumExecValue": "536.92500000",
        #                 "blockTradeId": "",
        #                 "rejectReason": "EC_NoError",
        #                 "price": "1127.10000000",
        #                 "createdTime": 1657711949928,
        #                 "tpTriggerBy": "UNKNOWN",
        #                 "timeInForce": "ImmediateOrCancel",
        #                 "basePrice": "",
        #                 "leavesValue": "0.00000000",
        #                 "updatedTime": 1657711949945,
        #                 "side": "Buy",
        #                 "triggerPrice": "",
        #                 "cumExecFee": "0.32215500",
        #                 "slTriggerBy": "UNKNOWN",
        #                 "leavesQty": "0.0000",
        #                 "closeOnTrigger": False,
        #                 "cumExecQty": "0.5000",
        #                 "reduceOnly": False,
        #                 "qty": "0.5000",
        #                 "stopLoss": "",
        #                 "triggerBy": "UNKNOWN",
        #                 "orderIM": ""
        #             }]
        #         },
        #         "time": 1657713451741
        #     }
        #
        result = self.safe_value(response, 'result', {})
        data = self.safe_value(result, 'list', [])
        return self.parse_orders(data, market, since, limit)

    def fetch_derivatives_orders(self, symbol=None, since=None, limit=None, params={}):
        self.load_markets()
        market = None
        request = {
            # 'symbol': market['id'],
            # 'category': string, Type of derivatives product: spot, linear or option.
            # 'baseCoin': string, Base coin. When category=option. If not passed, BTC by default; when category=linear, if BTC passed, BTCPERP & BTCUSDT returned.
            # 'orderId': string, Order ID
            # 'orderLinkId': string, Unique user-set order ID
            # 'orderStatus': string,  # Return all status orders if not passed
            # 'orderFilter': string, Conditional order or active order
            # 'limit': number, Data quantity per page: Max data value per page is 50, and default value at 20.
            # 'cursor': string, API pass-through. accountType + category + cursor +. If inconsistent, the following should be returned: The account type does not match the service inquiry.
        }
        if symbol is None:
            type = None
            type, params = self.handle_market_type_and_params('fetchOrders', market, params)
            request['category'] = type
            if type == 'swap':
                subType = None
                subType, params = self.handle_sub_type_and_params('fetchOrders', market, params, 'linear')
                request['category'] = subType
        else:
            market = self.market(symbol)
            request['symbol'] = market['id']
            if market['linear']:
                request['category'] = 'linear'
            else:
                request['category'] = 'inverse'
        isStop = self.safe_value(params, 'stop', False)
        params = self.omit(params, ['stop'])
        if isStop:
            request['orderFilter'] = 'StopOrder'
        if limit is not None:
            request['limit'] = limit
        response = self.privateGetV5OrderHistory(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "nextPageCursor": "03234de9-1332-41eb-b805-4a9f42c136a3%3A1672220109387%2C03234de9-1332-41eb-b805-4a9f42c136a3%3A1672220109387",
        #             "category": "linear",
        #             "list": [
        #                 {
        #                     "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": {},
        #         "time": 1672221263862
        #     }
        #
        result = self.safe_value(response, 'result', {})
        data = self.safe_value(result, 'list', [])
        return self.parse_orders(data, market, since, limit)

    def fetch_orders(self, symbol=None, since=None, limit=None, params={}):
        """
        fetches information on multiple orders made by the user
        :param str symbol: unified market symbol of the market orders were made in
        :param int|None since: the earliest time in ms to fetch orders for
        :param int|None limit: the maximum number of  orde structures to retrieve
        :param dict params: extra parameters specific to the bybit api endpoint
        :returns [dict]: a list of `order structures <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        self.load_markets()
        market = None
        settle = self.safe_string(params, 'settleCoin')
        if settle is None:
            settle, params = self.handle_option_and_params(params, 'fetchOrders', 'settle', settle)
        if symbol is not None:
            market = self.market(symbol)
            settle = market['settle']
        subType = None
        subType, params = self.handle_sub_type_and_params('fetchOrders', market, params)
        isInverse = subType == 'inverse'
        isUsdcSettled = settle == 'USDC'
        isLinearSettle = isUsdcSettled or (settle == 'USDT')
        if isInverse and isLinearSettle:
            raise ArgumentsRequired(self.id + ' fetchOrders with inverse subType requires settle to not be USDT or USDC')
        type, query = self.handle_market_type_and_params('fetchOrders', market, params)
        enableUnifiedMargin, enableUnifiedAccount = self.is_unified_enabled()
        if enableUnifiedAccount and not isInverse:
            return self.fetch_unified_account_orders(symbol, since, limit, query)
        elif type == 'spot':
            raise NotSupported(self.id + ' fetchOrders() only support ' + type + ' markets for unified trade account, use exchange.fetch_open_orders() and exchange.fetchClosedOrders() instead')
        elif enableUnifiedMargin and not isInverse:
            return self.fetch_unified_margin_orders(symbol, since, limit, query)
        else:
            return self.fetch_derivatives_orders(symbol, since, limit, query)

    def fetch_spot_closed_orders(self, symbol=None, since=None, limit=None, params={}):
        self.load_markets()
        market = None
        if symbol is not None:
            market = self.market(symbol)
        request = {}
        if symbol is not None:
            request['symbol'] = market['id']
        if limit is not None:
            request['limit'] = limit
        if since is not None:
            request['startTime'] = since
        response = self.privateGetSpotV3PrivateHistoryOrders(self.extend(request, params))
        result = self.safe_value(response, 'result', {})
        #
        #    {
        #        "retCode": "0",
        #        "retMsg": "OK",
        #        "result": {
        #            "list": [
        #                {
        #                    "accountId": "13380434",
        #                    "symbol": "AAVEUSDT",
        #                    "orderLinkId": "1666697847966604",
        #                    "orderId": "1274748373594828288",
        #                    "orderPrice": "80",
        #                    "orderQty": "0.11",
        #                    "execQty": "0",
        #                    "cummulativeQuoteQty": "0",
        #                    "avgPrice": "0",
        #                    "status": "CANCELED",
        #                    "timeInForce": "GTC",
        #                    "orderType": "LIMIT",
        #                    "side": "BUY",
        #                    "stopPrice": "0.0",
        #                    "icebergQty": "0.0",
        #                    "createTime": "1666697847972",
        #                    "updateTime": "1666697865809",
        #                    "isWorking": "1",
        #                    "orderCategory": "0"
        #                },
        #            ]
        #        },
        #        "retExtInfo": null,
        #        "time": "1666732287588"
        #    }
        #
        orders = self.safe_value(result, 'list', [])
        return self.parse_orders(orders, market, since, limit)

    def fetch_closed_orders(self, symbol=None, since=None, limit=None, params={}):
        """
        fetches information on multiple closed orders made by the user
        :param str|None symbol: unified market symbol of the market orders were made in
        :param int|None since: the earliest time in ms to fetch orders for
        :param int|None limit: the maximum number of  orde structures to retrieve
        :param dict params: extra parameters specific to the bybit api endpoint
        :returns [dict]: a list of `order structures <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        self.load_markets()
        market = None
        if symbol is not None:
            market = self.market(symbol)
        type = None
        type, params = self.handle_market_type_and_params('fetchClosedOrders', market, params)
        enableUnified = self.is_unified_enabled()
        request = {}
        if (type == 'spot') and not enableUnified[1]:
            return self.fetch_spot_closed_orders(symbol, since, limit, params)
        else:
            request['orderStatus'] = 'Cancelled'
        return self.fetch_orders(symbol, since, limit, self.extend(request, params))

    def fetch_unified_account_open_orders(self, symbol=None, since=None, limit=None, params={}):
        self.load_markets()
        request = {
            # 'symbol': market['id'],
            # 'category': string, Type of derivatives product: linear or option.
            # 'baseCoin': string, Base coin. When category=option. If not passed, BTC by default; when category=linear, if BTC passed, BTCPERP & BTCUSDT returned.
            # 'settleCoin': string, Settle coin. For linear, either symbol or settleCoin is required
            # 'orderId': string, Order ID
            # 'orderLinkId': string, Unique user-set order ID
            # 'orderFilter': string, Conditional order or active order
            # 'limit': number, Data quantity per page: Max data value per page is 50, and default value at 20.
            # 'cursor': string, API pass-through. accountType + category + cursor +. If inconsistent, the following should be returned: The account type does not match the service inquiry.
            # 'openOnly': 0,
        }
        market = None
        if symbol is None:
            type = None
            type, params = self.handle_market_type_and_params('fetchOpenOrders', market, params)
            subType = None
            subType, params = self.handle_sub_type_and_params('fetchOpenOrders', market, params, 'linear')
            request['category'] = type
            if type == 'swap':
                if subType == 'linear':
                    self.check_required_symbol('fetchOpenOrders', symbol)
                elif subType == 'inverse':
                    raise NotSupported(self.id + ' fetchOpenOrders() does not allow inverse market orders for ' + symbol + ' markets')
                request['category'] = subType
        else:
            market = self.market(symbol)
            request['symbol'] = market['id']
            if market['spot']:
                request['category'] = 'spot'
            elif market['option']:
                request['category'] = 'option'
            elif market['linear']:
                request['category'] = 'linear'
            else:
                raise NotSupported(self.id + ' fetchOpenOrders() does not allow inverse market orders for ' + symbol + ' markets')
        isStop = self.safe_value(params, 'stop', False)
        params = self.omit(params, ['stop'])
        if isStop:
            if market['spot']:
                request['orderFilter'] = 'tpslOrder'
            else:
                request['orderFilter'] = 'StopOrder'
        if limit is not None:
            request['limit'] = limit
        response = self.privateGetV5OrderRealtime(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "nextPageCursor": "1321052653536515584%3A1672217748287%2C1321052653536515584%3A1672217748287",
        #             "category": "spot",
        #             "list": [
        #                 {
        #                     "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": {},
        #         "time": 1672219526294
        #     }
        #
        result = self.safe_value(response, 'result', {})
        data = self.safe_value(result, 'list', [])
        return self.parse_orders(data, market, since, limit)

    def fetch_spot_open_orders(self, symbol=None, since=None, limit=None, params={}):
        self.load_markets()
        request = {}
        market = None
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        if limit is not None:
            request['limit'] = limit
        response = self.privateGetSpotV3PrivateOpenOrders(self.extend(request, params))
        #
        #    {
        #         "retCode": "0",
        #         "retMsg": "OK",
        #         "result": {
        #             "list": [
        #                 {
        #                     "accountId": "13380434",
        #                     "symbol": "AAVEUSDT",
        #                     "orderLinkId": "1666734005300717",
        #                     "orderId": "1275051683279281664",
        #                     "orderPrice": "80",
        #                     "orderQty": "0.11",
        #                     "execQty": "0",
        #                     "cummulativeQuoteQty": "0",
        #                     "avgPrice": "0",
        #                     "status": "NEW",
        #                     "timeInForce": "GTC",
        #                     "orderType": "LIMIT",
        #                     "side": "BUY",
        #                     "stopPrice": "0.0",
        #                     "icebergQty": "0.0",
        #                     "createTime": "1666734005304",
        #                     "updateTime": "1666734005309",
        #                     "isWorking": "1",
        #                     "orderCategory": "0"
        #                 }
        #             ]
        #         },
        #         "retExtInfo": null,
        #         "time": "1666734031592"
        #     }
        #
        result = self.safe_value(response, 'result', {})
        orders = self.safe_value(result, 'list', [])
        return self.parse_orders(orders, market, since, limit)

    def fetch_unified_margin_open_orders(self, symbol=None, since=None, limit=None, params={}):
        self.load_markets()
        request = {}
        market = None
        if symbol is None:
            subType = None
            subType, params = self.handle_sub_type_and_params('fetchUnifiedMarginOrders', market, params, 'linear')
            request['category'] = subType
        else:
            market = self.market(symbol)
            request['symbol'] = market['id']
            if market['option']:
                request['category'] = 'option'
            elif market['linear']:
                request['category'] = 'linear'
            else:
                raise NotSupported(self.id + ' fetchUnifiedMarginOpenOrders() does not allow inverse market orders for ' + symbol + ' markets')
        type = None
        type, params = self.handle_market_type_and_params('fetchUnifiedMarginOpenOrders', market, params)
        isStop = self.safe_value(params, 'stop', False)
        isConditional = isStop or (type == 'stop') or (type == 'conditional')
        params = self.omit(params, ['stop'])
        if isConditional:
            request['orderFilter'] = 'StopOrder'
        if limit is not None:
            request['limit'] = limit
        response = self.privateGetUnifiedV3PrivateOrderUnfilledOrders(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "Success",
        #         "result": {
        #             "nextPageCursor": "135ccc0d-8136-4e1b-8af3-07b11ee158d1%3A1665565610526%2C135ccc0d-8136-4e1b-8af3-07b11ee158d1%3A1665565610526",
        #             "category": "linear",
        #             "list": [
        #                 {
        #                     "symbol": "ETHUSDT",
        #                     "orderType": "Limit",
        #                     "orderLinkId": "test0000005",
        #                     "orderId": "135ccc0d-8136-4e1b-8af3-07b11ee158d1",
        #                     "stopOrderType": "UNKNOWN",
        #                     "orderStatus": "New",
        #                     "takeProfit": "",
        #                     "cumExecValue": "0.00000000",
        #                     "blockTradeId": "",
        #                     "price": "700.00000000",
        #                     "createdTime": 1665565610526,
        #                     "tpTriggerBy": "UNKNOWN",
        #                     "timeInForce": "GoodTillCancel",
        #                     "basePrice": "",
        #                     "updatedTime": 1665565610533,
        #                     "side": "Buy",
        #                     "triggerPrice": "",
        #                     "cumExecFee": "0.00000000",
        #                     "slTriggerBy": "UNKNOWN",
        #                     "leavesQty": "0.1000",
        #                     "closeOnTrigger": False,
        #                     "cumExecQty": "0.00000000",
        #                     "reduceOnly": False,
        #                     "qty": "0.1000",
        #                     "stopLoss": "",
        #                     "triggerBy": "UNKNOWN",
        #                     "orderIM": "0.00000000"
        #                 }
        #             ]
        #         },
        #         "retExtInfo": null,
        #         "time": 1665565614320
        #     }
        #
        result = self.safe_value(response, 'result', {})
        orders = self.safe_value(result, 'list', [])
        return self.parse_orders(orders, market, since, limit)

    def fetch_derivatives_open_orders(self, symbol=None, since=None, limit=None, params={}):
        self.load_markets()
        market = None
        settle = None
        request = {
            # 'symbol': market['id'],
            # 'category': string, Type of derivatives product: linear or option.
            # 'baseCoin': string, Base coin. When category=option. If not passed, BTC by default; when category=linear, if BTC passed, BTCPERP & BTCUSDT returned.
            # 'settleCoin': string, Settle coin. For linear, either symbol or settleCoin is required
            # 'orderId': string, Order ID
            # 'orderLinkId': string, Unique user-set order ID
            # 'orderFilter': string, Conditional order or active order
            # 'limit': number, Data quantity per page: Max data value per page is 50, and default value at 20.
            # 'cursor': string, API pass-through. accountType + category + cursor +. If inconsistent, the following should be returned: The account type does not match the service inquiry.
            # 'openOnly': 0,
        }
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
            if market['linear']:
                request['category'] = 'linear'
            else:
                request['category'] = 'inverse'
        else:
            type = None
            type, params = self.handle_market_type_and_params('fetchOpenOrders', market, params)
            subType = None
            subType, params = self.handle_sub_type_and_params('fetchOpenOrders', market, params, 'linear')
            request['category'] = type
            if type == 'swap':
                request['category'] = subType
        settle, params = self.handle_option_and_params(params, 'fetchOpenOrders', 'settle', settle)
        if settle is not None:
            request['settleCoin'] = settle
        isStop = self.safe_value(params, 'stop', False)
        params = self.omit(params, ['stop'])
        if isStop:
            request['orderFilter'] = 'StopOrder'
        if limit is not None:
            request['limit'] = limit
        response = self.privateGetV5OrderRealtime(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "nextPageCursor": "1321052653536515584%3A1672217748287%2C1321052653536515584%3A1672217748287",
        #             "category": "spot",
        #             "list": [
        #                 {
        #                     "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": {},
        #         "time": 1672219526294
        #     }
        #
        result = self.safe_value(response, 'result', {})
        orders = self.safe_value(result, 'list', [])
        return self.parse_orders(orders, market, since, limit)

    def fetch_usdc_open_orders(self, symbol=None, since=None, limit=None, params={}):
        self.load_markets()
        request = {}
        market = None
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        type = None
        type, params = self.handle_market_type_and_params('fetchUSDCOpenOrders', market, params)
        request['category'] = 'perpetual' if (type == 'swap') else 'option'
        response = self.privatePostOptionUsdcOpenapiPrivateV1QueryActiveOrders(self.extend(request, params))
        result = self.safe_value(response, 'result', {})
        orders = self.safe_value(result, 'dataList', [])
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "resultTotalSize": 1,
        #             "cursor": "id%3D1662019818569%23df31e03b-fc00-4b4c-bd1c-b97fd72b5c5c",
        #             "dataList": [
        #                 {
        #                     "orderId": "df31e03b-fc00-4b4c-bd1c-b97fd72b5c5c",
        #                     "orderLinkId": "",
        #                     "symbol": "BTC-2SEP22-18000-C",
        #                     "orderStatus": "New",
        #                     "orderPrice": "500",
        #                     "side": "Buy",
        #                     "remainingQty": "0.1",
        #                     "orderType": "Limit",
        #                     "qty": "0.1",
        #                     "iv": "0.0000",
        #                     "cancelType": "",
        #                     "updateTimestamp": "1662019818579"
        #                 }
        #             ]
        #         }
        #     }
        #
        return self.parse_orders(orders, market, since, limit)

    def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
        """
        fetch all unfilled currently open orders
        :param str|None symbol: unified market symbol
        :param int|None since: the earliest time in ms to fetch open orders for
        :param int|None limit: the maximum number of  open orders structures to retrieve
        :param dict params: extra parameters specific to the bybit api endpoint
        :returns [dict]: a list of `order structures <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        self.load_markets()
        market = None
        settle = self.safe_string(params, 'settleCoin')
        if settle is None:
            settle, params = self.handle_option_and_params(params, 'fetchOpenOrders', 'settle', settle)
        if symbol is not None:
            market = self.market(symbol)
            settle = market['settle']
        subType = None
        subType, params = self.handle_sub_type_and_params('fetchOpenOrders', market, params)
        isInverse = subType == 'inverse'
        isUsdcSettled = settle == 'USDC'
        isLinearSettle = isUsdcSettled or (settle == 'USDT')
        if isInverse and isLinearSettle:
            raise ArgumentsRequired(self.id + ' fetchOpenOrders with inverse subType requires settle to not be USDT or USDC')
        type, query = self.handle_market_type_and_params('fetchOpenOrders', market, params)
        enableUnifiedMargin, enableUnifiedAccount = self.is_unified_enabled()
        if enableUnifiedAccount and not isInverse:
            return self.fetch_unified_account_open_orders(symbol, since, limit, query)
        elif type == 'spot':
            return self.fetch_spot_open_orders(symbol, since, limit, query)
        elif enableUnifiedMargin and not isInverse:
            return self.fetch_unified_margin_open_orders(symbol, since, limit, query)
        elif isUsdcSettled:
            return self.fetch_usdc_open_orders(symbol, since, limit, query)
        else:
            return self.fetch_derivatives_open_orders(symbol, since, limit, query)

    def fetch_order_trades(self, id, symbol=None, since=None, limit=None, params={}):
        """
        fetch all the trades made from a single order
        :param str id: order id
        :param str|None symbol: unified market symbol
        :param int|None since: the earliest time in ms to fetch trades for
        :param int|None limit: the maximum number of trades to retrieve
        :param dict params: extra parameters specific to the bybit api endpoint
        :returns [dict]: a list of `trade structures <https://docs.ccxt.com/en/latest/manual.html#trade-structure>`
        """
        request = {
            'orderId': id,
        }
        return self.fetch_my_trades(symbol, since, limit, self.extend(request, params))

    def fetch_my_unified_trades(self, symbol=None, since=None, limit=None, params={}):
        self.load_markets()
        market = None
        request = {
            # 'symbol': market['id'],
            # 'category': '',  # Product type. spot,linear,option
            # 'orderId': '',  # Order ID
            # 'orderLinkId': '',  # User customised order ID
            # 'baseCoin': '',  # Base coin
            # 'startTime': 0,  # The start timestamp(ms)
            # 'endTime': 0,  # The end timestamp(ms)
            # 'execType': '',  # Execution type
            # 'limit': 0,  # Limit for data size per page. [1, 100]. Default: 50
            # 'cursor': '',  # Cursor. Used for pagination
        }
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        type = None
        type, params = self.handle_market_type_and_params('fetchMyTrades', market, params)
        if type == 'spot':
            request['category'] = 'spot'
        else:
            subType = None
            subType, params = self.handle_sub_type_and_params('fetchMyTrades', market, params)
            if subType == 'inverse':
                raise NotSupported(self.id + ' fetchMyTrades() does not support ' + subType + ' markets.')
            request['category'] = subType
        if since is not None:
            request['startTime'] = since
        if limit is not None:
            request['limit'] = limit  # default 20, max 50
        response = self.privateGetV5ExecutionList(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "nextPageCursor": "132766%3A2%2C132766%3A2",
        #             "category": "linear",
        #             "list": [
        #                 {
        #                     "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": {},
        #         "time": 1672283754510
        #     }
        #
        result = self.safe_value(response, 'result', {})
        trades = self.safe_value(result, 'list', [])
        return self.parse_trades(trades, market, since, limit)

    def fetch_my_spot_trades(self, symbol=None, since=None, limit=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchMySpotTrades() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
            # 'orderId': 'f185806b-b801-40ff-adec-52289370ed62',  # if not provided will return user's trading records
            # 'startTime': int(since / 1000),
            # 'endTime': 0,
            # 'fromTradeId': '',
            # 'toTradeId': '',
            # 'limit' 20,  # max 50
        }
        if since is not None:
            request['startTime'] = since
        if limit is not None:
            request['limit'] = limit  # default 20, max 50
        response = self.privateGetSpotV3PrivateMyTrades(self.extend(request, params))
        #
        #    {
        #         "retCode": "0",
        #         "retMsg": "OK",
        #         "result": {
        #             "list": [
        #                 {
        #                     "symbol": "AAVEUSDT",
        #                     "id": "1274785101965716992",
        #                     "orderId": "1274784252359089664",
        #                     "tradeId": "2270000000031365639",
        #                     "orderPrice": "82.5",
        #                     "orderQty": "0.016",
        #                     "execFee": "0",
        #                     "feeTokenId": "AAVE",
        #                     "creatTime": "1666702226326",
        #                     "isBuyer": "0",
        #                     "isMaker": "0",
        #                     "matchOrderId": "1274785101865076224",
        #                     "makerRebate": "0",
        #                     "executionTime": "1666702226335"
        #                 },
        #             ]
        #         },
        #         "retExtMap": {},
        #         "retExtInfo": null,
        #         "time": "1666768215157"
        #     }
        #
        result = self.safe_value(response, 'result', {})
        trades = self.safe_value(result, 'list', [])
        return self.parse_trades(trades, market, since, limit)

    def fetch_my_unified_margin_trades(self, symbol=None, since=None, limit=None, params={}):
        self.load_markets()
        market = None
        settle = None
        request = {
            # 'symbol': market['id'],
            # 'orderId': 'f185806b-b801-40ff-adec-52289370ed62',  # if not provided will return user's trading records
            # 'startTime': int(since / 1000),
            # 'endTime': 0,
            # 'category': ''
            # 'limit' 20,  # max 50
        }
        if symbol is not None:
            market = self.market(symbol)
            settle = market['settle']
            request['symbol'] = market['id']
        subType = None
        subType, params = self.handle_sub_type_and_params('fetchMyTrades', market, params, 'linear')
        request['category'] = subType
        settle, params = self.handle_option_and_params(params, 'cancelAllOrders', 'settle', settle)
        if settle is not None:
            request['settleCoin'] = settle
        if since is not None:
            request['startTime'] = since
        if limit is not None:
            request['limit'] = limit  # default 20, max 50
        response = self.privateGetUnifiedV3PrivateExecutionList(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "Success",
        #         "result": {
        #             "nextPageCursor": "1565%3A0%2C1565%3A0",
        #             "category": "option",
        #             "list": [
        #                 {
        #                     "orderType": "Limit",
        #                     "symbol": "BTC-14JUL22-17500-C",
        #                     "orderLinkId": "188889689-yuanzhen-558998998899",
        #                     "side": "Buy",
        #                     "orderId": "09c5836f-81ef-4208-a5b4-43135d3e02a2",
        #                     "leavesQty": "0.0000",
        #                     "execTime": 1657714122417,
        #                     "execFee": "0.11897082",
        #                     "feeRate": "0.000300",
        #                     "execId": "6e492560-78b4-5d2b-b331-22921d3173c9",
        #                     "blockTradeId": "",
        #                     "execPrice": "2360.00000000",
        #                     "lastLiquidityInd": "TAKER",
        #                     "orderQty": "0.0200",
        #                     "orderPrice": "2360.00000000",
        #                     "execValue": "47.20000000",
        #                     "execType": "Trade",
        #                     "execQty": "0.0200"
        #                 }
        #             ]
        #         },
        #         "time": 1657714292783
        #     }
        #
        result = self.safe_value(response, 'result', {})
        trades = self.safe_value(result, 'list', [])
        return self.parse_trades(trades, market, since, limit)

    def fetch_my_contract_trades(self, symbol=None, since=None, limit=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchMyContractTrades() requires a symbol argument')
        self.load_markets()
        market = None
        request = {
            # 'symbol': market['id'],
            # 'category': '',  # Product type. spot,linear,option
            # 'orderId': '',  # Order ID
            # 'orderLinkId': '',  # User customised order ID
            # 'baseCoin': '',  # Base coin
            # 'startTime': 0,  # The start timestamp(ms)
            # 'endTime': 0,  # The end timestamp(ms)
            # 'execType': '',  # Execution type
            # 'limit': 0,  # Limit for data size per page. [1, 100]. Default: 50
            # 'cursor': '',  # Cursor. Used for pagination
        }
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        subType = None
        subType, params = self.handle_sub_type_and_params('fetchMyTrades', market, params)
        request['category'] = subType
        if since is not None:
            request['startTime'] = since
        if limit is not None:
            request['limit'] = limit  # default 50, max 100
        response = self.privateGetV5ExecutionList(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "nextPageCursor": "132766%3A2%2C132766%3A2",
        #             "category": "linear",
        #             "list": [
        #                 {
        #                     "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": {},
        #         "time": 1672283754510
        #     }
        #
        result = self.safe_value(response, 'result', {})
        trades = self.safe_value(result, 'list', [])
        return self.parse_trades(trades, market, since, limit)

    def fetch_my_usdc_trades(self, symbol=None, since=None, limit=None, params={}):
        self.load_markets()
        market = None
        request = {}
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
            request['category'] = 'OPTION' if market['option'] else 'PERPETUAL'
        else:
            request['category'] = 'PERPETUAL'
        response = self.privatePostOptionUsdcOpenapiPrivateV1ExecutionList(self.extend(request, params))
        #
        #     {
        #       "result": {
        #         "cursor": "29%3A1%2C28%3A1",
        #         "resultTotalSize": 2,
        #         "dataList": [
        #           {
        #             "symbol": "ETHPERP",
        #             "orderLinkId": "",
        #             "side": "Sell",
        #             "orderId": "d83f8b4d-2f60-4e04-a64a-a3f207989dc6",
        #             "execFee": "0.0210",
        #             "feeRate": "0.000600",
        #             "blockTradeId": "",
        #             "tradeTime": "1669196423581",
        #             "execPrice": "1161.45",
        #             "lastLiquidityInd": "TAKER",
        #             "execValue": "34.8435",
        #             "execType": "Trade",
        #             "execQty": "0.030",
        #             "tradeId": "d9aa8590-9e6a-575e-a1be-d6261e6ed2e5"
        #           }, ...
        #         ]
        #       },
        #       "retCode": 0,
        #       "retMsg": "Success."
        #     }
        #
        result = self.safe_value(response, 'result', {})
        dataList = self.safe_value(result, 'dataList', [])
        return self.parse_trades(dataList, market, since, limit)

    def fetch_my_trades(self, symbol=None, since=None, limit=None, params={}):
        """
        fetch all trades made by the user
        :param str symbol: unified market symbol
        :param int|None since: the earliest time in ms to fetch trades for
        :param int|None limit: the maximum number of trades structures to retrieve
        :param dict params: extra parameters specific to the bybit api endpoint
        :returns [dict]: a list of `trade structures <https://docs.ccxt.com/en/latest/manual.html#trade-structure>`
        """
        self.load_markets()
        market = None
        settle = self.safe_string(params, 'settleCoin')
        if settle is None:
            settle, params = self.handle_option_and_params(params, 'fetchMyTrades', 'settle', settle)
        if symbol is not None:
            market = self.market(symbol)
            settle = market['settle']
        subType = None
        subType, params = self.handle_sub_type_and_params('fetchMyTrades', market, params)
        isInverse = subType == 'inverse'
        isUsdcSettled = settle == 'USDC'
        isLinearSettle = isUsdcSettled or (settle == 'USDT')
        if isInverse and isLinearSettle:
            raise ArgumentsRequired(self.id + ' fetchMyTrades with inverse subType requires settle to not be USDT or USDC')
        type, query = self.handle_market_type_and_params('fetchMyTrades', market, params)
        enableUnifiedMargin, enableUnifiedAccount = self.is_unified_enabled()
        if enableUnifiedAccount and not isInverse:
            orderId = self.safe_string(params, 'orderId')
            if orderId is None:
                self.check_required_symbol('fetchMyTrades', symbol)
            return self.fetch_my_unified_trades(symbol, since, limit, query)
        elif type == 'spot':
            return self.fetch_my_spot_trades(symbol, since, limit, query)
        elif enableUnifiedMargin and not isInverse:
            return self.fetch_my_unified_margin_trades(symbol, since, limit, query)
        elif isUsdcSettled:
            return self.fetch_my_usdc_trades(symbol, since, limit, query)
        else:
            return self.fetch_my_contract_trades(symbol, since, limit, query)

    def parse_deposit_address(self, depositAddress, currency=None):
        #
        #     {
        #         chainType: 'ERC20',
        #         addressDeposit: '0xf56297c6717c1d1c42c30324468ed50a9b7402ee',
        #         tagDeposit: '',
        #         chain: 'ETH'
        #     }
        #
        address = self.safe_string(depositAddress, 'addressDeposit')
        tag = self.safe_string(depositAddress, 'tagDeposit')
        code = self.safe_string(currency, 'code')
        chain = self.safe_string(depositAddress, 'chain')
        self.check_address(address)
        return {
            'currency': code,
            'address': address,
            'tag': tag,
            'network': chain,
            'info': depositAddress,
        }

    def fetch_deposit_addresses_by_network(self, 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 str code: unified currency code of the currency for the deposit address
        :param dict params: extra parameters specific to the bybit api endpoint
        :returns dict: a dictionary of `address structures <https://docs.ccxt.com/en/latest/manual.html#address-structure>` indexed by the network
        """
        self.load_markets()
        currency = self.currency(code)
        request = {
            'coin': currency['id'],
        }
        response = self.privateGetV5AssetDepositQueryAddress(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "success",
        #         "result": {
        #             "coin": "USDT",
        #             "chains": [
        #                 {
        #                     "chainType": "ERC20",
        #                     "addressDeposit": "0xd9e1cd77afa0e50b452a62fbb68a3340602286c3",
        #                     "tagDeposit": "",
        #                     "chain": "ETH"
        #                 }
        #             ]
        #         },
        #         "retExtInfo": {},
        #         "time": 1672192792860
        #     }
        #
        result = self.safe_value(response, 'result', [])
        chains = self.safe_value(result, 'chains', [])
        coin = self.safe_string(result, 'coin')
        currency = self.currency(coin)
        parsed = self.parse_deposit_addresses(chains, [currency['code']], False, {
            'currency': currency['id'],
        })
        return self.index_by(parsed, 'network')

    def fetch_deposit_address(self, code, params={}):
        """
        fetch the deposit address for a currency associated with self account
        see https://bybit-exchange.github.io/docs/v5/asset/master-deposit-addr
        :param str code: unified currency code
        :param dict params: extra parameters specific to the bybit api endpoint
        :returns dict: an `address structure <https://docs.ccxt.com/en/latest/manual.html#address-structure>`
        """
        self.load_markets()
        networkCode, query = self.handle_network_code_and_params(params)
        networkId = self.network_code_to_id(networkCode)
        currency = self.currency(code)
        request = {
            'coin': currency['id'],
        }
        if networkId is not None:
            request['chainType'] = networkId
        response = self.privateGetV5AssetDepositQueryAddress(self.extend(request, query))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "success",
        #         "result": {
        #             "coin": "USDT",
        #             "chains": [
        #                 {
        #                     "chainType": "ERC20",
        #                     "addressDeposit": "0xd9e1cd77afa0e50b452a62fbb68a3340602286c3",
        #                     "tagDeposit": "",
        #                     "chain": "ETH"
        #                 }
        #             ]
        #         },
        #         "retExtInfo": {},
        #         "time": 1672192792860
        #     }
        #
        result = self.safe_value(response, 'result', {})
        chains = self.safe_value(result, 'chains', [])
        chainsIndexedById = self.index_by(chains, 'chain')
        selectedNetworkId = self.select_network_id_from_raw_networks(code, networkCode, chainsIndexedById)
        addressObject = self.safe_value(chainsIndexedById, selectedNetworkId, {})
        return self.parse_deposit_address(addressObject, currency)

    def fetch_deposits(self, code=None, since=None, limit=None, params={}):
        """
        fetch all deposits made to an account
        see https://bybit-exchange.github.io/docs/v5/asset/deposit-record
        :param str|None code: unified currency code
        :param int|None since: the earliest time in ms to fetch deposits for, default = 30 days before the current time
        :param int|None limit: the maximum number of deposits structures to retrieve, default = 50, max = 50
        :param dict params: extra parameters specific to the bybit api endpoint
        :param int|None params['until']: the latest time in ms to fetch deposits for, default = 30 days after since
         *
         * EXCHANGE SPECIFIC PARAMETERS
        :param str|None params['cursor']: used for pagination
        :returns [dict]: a list of `transaction structures <https://docs.ccxt.com/en/latest/manual.html#transaction-structure>`
       """
        self.load_markets()
        request = {
            # 'coin': currency['id'],
            # 'limit': 20,  # max 50
            # 'cursor': '',
        }
        currency = None
        if code is not None:
            currency = self.currency(code)
            request['coin'] = currency['id']
        if since is not None:
            request['startTime'] = since
        if limit is not None:
            request['limit'] = limit
        response = self.privateGetV5AssetDepositQueryRecord(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "success",
        #         "result": {
        #             "rows": [
        #                 {
        #                     "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": {},
        #         "time": 1672191992512
        #     }
        #
        result = self.safe_value(response, 'result', {})
        data = self.safe_value(result, 'rows', [])
        return self.parse_transactions(data, currency, since, limit)

    def fetch_withdrawals(self, code=None, since=None, limit=None, params={}):
        """
        fetch all withdrawals made from an account
        see https://bybit-exchange.github.io/docs/v5/asset/withdraw-record
        :param str code: unified currency code
        :param int|None since: the earliest time in ms to fetch withdrawals for
        :param int|None limit: the maximum number of withdrawals structures to retrieve
        :param dict params: extra parameters specific to the bybit api endpoint
        :returns [dict]: a list of `transaction structures <https://docs.ccxt.com/en/latest/manual.html#transaction-structure>`
        """
        self.load_markets()
        request = {
            # 'coin': currency['id'],
            # 'limit': 20,  # max 50
            # 'cusor': '',
        }
        currency = None
        if code is not None:
            currency = self.currency(code)
            request['coin'] = currency['id']
        if since is not None:
            request['startTime'] = since
        if limit is not None:
            request['limit'] = limit
        response = self.privateGetV5AssetWithdrawQueryRecord(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "success",
        #         "result": {
        #             "rows": [
        #                 {
        #                     "coin": "USDT",
        #                     "chain": "ETH",
        #                     "amount": "77",
        #                     "txID": "",
        #                     "status": "SecurityCheck",
        #                     "toAddress": "0x99ced129603abc771c0dabe935c326ff6c86645d",
        #                     "tag": "",
        #                     "withdrawFee": "10",
        #                     "createTime": "1670922217000",
        #                     "updateTime": "1670922217000",
        #                     "withdrawId": "9976",
        #                     "withdrawType": 0
        #                 },
        #                 {
        #                     "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": {},
        #         "time": 1672194949928
        #     }
        #
        result = self.safe_value(response, 'result', {})
        data = self.safe_value(result, 'rows', [])
        return self.parse_transactions(data, currency, since, limit)

    def parse_transaction_status(self, status):
        statuses = {
            # v1/v2
            'ToBeConfirmed': 'pending',
            'UnderReview': 'pending',
            'Success': 'ok',
            'Expire': 'expired',
            # 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 self.safe_string(statuses, status, status)

    def parse_transaction(self, transaction, currency=None):
        #
        # 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 = self.safe_string(transaction, 'coin')
        code = self.safe_currency_code(currencyId, currency)
        timestamp = self.safe_integer_2(transaction, 'createTime', 'successAt')
        updated = self.safe_integer(transaction, 'updateTime')
        status = self.parse_transaction_status(self.safe_string(transaction, 'status'))
        feeCost = self.safe_number_2(transaction, 'depositFee', 'withdrawFee', 0)
        type = 'deposit' if ('depositFee' in transaction) else 'withdrawal'
        fee = None
        if feeCost is not None:
            fee = {
                'cost': feeCost,
                'currency': code,
            }
        toAddress = self.safe_string(transaction, 'toAddress')
        return {
            'info': transaction,
            'id': self.safe_string_2(transaction, 'id', 'withdrawId'),
            'txid': self.safe_string(transaction, 'txID'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'network': self.network_id_to_code(self.safe_string(transaction, 'chain')),
            'address': None,
            'addressTo': toAddress,
            'addressFrom': None,
            'tag': self.safe_string(transaction, 'tag'),
            'tagTo': None,
            'tagFrom': None,
            'type': type,
            'amount': self.safe_number(transaction, 'amount'),
            'currency': code,
            'status': status,
            'updated': updated,
            'fee': fee,
        }

    def fetch_ledger(self, code=None, since=None, limit=None, params={}):
        """
        fetch the history of changes, actions done by the user or operations that altered balance of the user
        see https://bybit-exchange.github.io/docs/v5/account/transaction-log
        :param str|None code: unified currency code, default is None
        :param int|None since: timestamp in ms of the earliest ledger entry, default is None
        :param int|None limit: max number of ledger entrys to return, default is None
        :param dict params: extra parameters specific to the bybit api endpoint
        :returns dict: a `ledger structure <https://docs.ccxt.com/en/latest/manual.html#ledger-structure>`
        """
        self.load_markets()
        request = {
            # 'coin': currency['id'],
            # 'currency': currency['id'],  # alias
            # 'start_date': self.iso8601(since),
            # 'end_date': self.iso8601(till),
            # '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 = self.is_unified_enabled()
        currency = None
        currencyKey = 'coin'
        if enableUnified[1]:
            currencyKey = 'currency'
            if since is not None:
                request['startTime'] = since
        else:
            if since is not None:
                request['start_date'] = self.yyyymmdd(since)
        method = 'privateGetV5AccountTransactionLog' if (enableUnified[1]) else 'privateGetV2PrivateWalletFundRecords'
        if code is not None:
            currency = self.currency(code)
            request[currencyKey] = currency['id']
        if limit is not None:
            request['limit'] = limit
        response = getattr(self, method)(self.extend(request, params))
        #
        #     {
        #         "ret_code": 0,
        #         "ret_msg": "ok",
        #         "ext_code": "",
        #         "result": {
        #             "data": [
        #                 {
        #                     "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": [
        #                 {
        #                     "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"
        #                 },
        #                 {
        #                     "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"
        #                 },
        #                 {
        #                     "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": {},
        #         "time": 1672132481405
        #     }
        #
        result = self.safe_value(response, 'result', {})
        data = self.safe_value_2(result, 'data', 'list', [])
        return self.parse_ledger(data, currency, since, limit)

    def parse_ledger_entry(self, item, currency=None):
        #
        #     {
        #         "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 = self.safe_string_2(item, 'coin', 'currency')
        code = self.safe_currency_code(currencyId, currency)
        amount = self.safe_string_2(item, 'amount', 'change')
        after = self.safe_string_2(item, 'wallet_balance', 'cashBalance')
        direction = 'out' if Precise.string_lt(amount, '0') else 'in'
        before = None
        if after is not None and amount is not None:
            difference = amount if (direction == 'out') else Precise.string_neg(amount)
            before = Precise.string_add(after, difference)
        timestamp = self.parse8601(self.safe_string(item, 'exec_time'))
        if timestamp is None:
            timestamp = self.safe_integer(item, 'transactionTime')
        type = self.parse_ledger_entry_type(self.safe_string(item, 'type'))
        id = self.safe_string(item, 'id')
        referenceId = self.safe_string(item, 'tx_id')
        return {
            'id': id,
            'currency': code,
            'account': self.safe_string(item, 'wallet_id'),
            'referenceAccount': None,
            'referenceId': referenceId,
            'status': None,
            'amount': self.parse_number(amount),
            'before': self.parse_number(before),
            'after': self.parse_number(after),
            'fee': self.parse_number(self.safe_string(item, 'fee')),
            'direction': direction,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'type': type,
            'info': item,
        }

    def parse_ledger_entry_type(self, type):
        types = {
            '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 self.safe_string(types, type, type)

    def withdraw(self, code, amount, address, tag=None, params={}):
        """
        make a withdrawal
        see https://bybit-exchange.github.io/docs/v5/asset/withdraw
        :param str code: unified currency code
        :param float amount: the amount to withdraw
        :param str address: the address to withdraw to
        :param str|None tag:
        :param dict params: extra parameters specific to the bybit api endpoint
        :returns dict: a `transaction structure <https://docs.ccxt.com/en/latest/manual.html#transaction-structure>`
        """
        tag, params = self.handle_withdraw_tag_and_params(tag, params)
        self.load_markets()
        self.check_address(address)
        currency = self.currency(code)
        request = {
            'coin': currency['id'],
            'amount': self.number_to_string(amount),
            'address': address,
        }
        if tag is not None:
            request['tag'] = tag
        networkCode, query = self.handle_network_code_and_params(params)
        networkId = self.network_code_to_id(networkCode)
        if networkId is not None:
            request['chain'] = networkId.upper()
        enableUnified = self.is_unified_enabled()
        method = 'privatePostV5AssetWithdrawCreate' if (enableUnified[1]) else 'privatePostAssetV3PrivateWithdrawCreate'
        response = getattr(self, method)(self.extend(request, query))
        #
        #    {
        #         "retCode": "0",
        #         "retMsg": "success",
        #         "result": {
        #             "id": "9377266"
        #         },
        #         "retExtInfo": {},
        #         "time": "1666892894902"
        #     }
        #
        result = self.safe_value(response, 'result', {})
        return self.parse_transaction(result, currency)

    def fetch_position(self, symbol, params={}):
        """
        fetch data on a single open contract trade position
        :param str symbol: unified market symbol of the market the position is held in, default is None
        :param dict params: extra parameters specific to the bybit api endpoint
        :returns dict: a `position structure <https://docs.ccxt.com/en/latest/manual.html#position-structure>`
        """
        self.check_required_symbol('fetchPosition', symbol)
        self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        method = None
        enableUnifiedMargin, enableUnifiedAccount = self.is_unified_enabled()
        isUsdcSettled = market['settle'] == 'USDC'
        if enableUnifiedMargin or enableUnifiedAccount:
            method = 'privateGetV5PositionList' if (enableUnifiedAccount) else 'privateGetUnifiedV3PrivatePositionList'
            if market['option']:
                request['category'] = 'option'
            elif market['linear']:
                request['category'] = 'linear'
            else:
                raise NotSupported(self.id + ' fetchPosition() does not allow inverse market orders for ' + symbol + ' markets')
        elif isUsdcSettled:
            method = 'privatePostOptionUsdcOpenapiPrivateV1QueryPosition'
            if market['option']:
                request['category'] = 'OPTION'
            elif market['linear']:
                request['category'] = 'PERPETUAL'
        else:
            if market['linear']:
                request['category'] = 'linear'
            elif market['inverse']:
                request['category'] = 'inverse'
            else:
                raise NotSupported(self.id + ' fetchPosition() does not allow option market orders for ' + symbol + ' markets')
            method = 'privateGetV5PositionList'
        response = getattr(self, method)(self.extend(request, params))
        #
        # unified account
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "nextPageCursor": "updateAt%3D1672279322668",
        #             "category": "linear",
        #             "list": [
        #                 {
        #                     "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": {},
        #         "time": 1672280219169
        #     }
        #
        # unified margin
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "Success",
        #         "result": {
        #             "nextPageCursor": "0%3A1657711949945%2C0%3A1657711949945",
        #             "category": "linear",
        #             "list": [
        #                 {
        #                     "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
        #     }
        #
        # contract v3
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "list": [
        #                 {
        #                     "positionIdx": 1,
        #                     "riskId": "41",
        #                     "symbol": "XRPUSDT",
        #                     "side": "Buy",
        #                     "size": "0",
        #                     "positionValue": "0",
        #                     "entryPrice": "0",
        #                     "tradeMode": 0,
        #                     "autoAddMargin": 0,
        #                     "leverage": "10",
        #                     "positionBalance": "0",
        #                     "liqPrice": "0.0000",
        #                     "bustPrice": "0.0000",
        #                     "takeProfit": "0.0000",
        #                     "stopLoss": "0.0000",
        #                     "trailingStop": "0.0000",
        #                     "unrealisedPnl": "0",
        #                     "createdTime": "1658827444328",
        #                     "updatedTime": "1658904863412",
        #                     "tpSlMode": "Full",
        #                     "riskLimitValue": "200000",
        #                     "activePrice": "0.0000"
        #                 },
        #                 {
        #                     "positionIdx": 2,
        #                     "riskId": "41",
        #                     "symbol": "XRPUSDT",
        #                     "side": "Sell",
        #                     "size": "50",
        #                     "positionValue": "16.68",
        #                     "entryPrice": "0.3336",
        #                     "tradeMode": 0,
        #                     "autoAddMargin": 0,
        #                     "leverage": "10",
        #                     "positionBalance": "1.6790088",
        #                     "liqPrice": "12.4835",
        #                     "bustPrice": "12.4869",
        #                     "takeProfit": "0.0000",
        #                     "stopLoss": "0.0000",
        #                     "trailingStop": "0.0000",
        #                     "unrealisedPnl": "0",
        #                     "createdTime": "1658827444328",
        #                     "updatedTime": "1658904863412",
        #                     "tpSlMode": "Full",
        #                     "riskLimitValue": "200000",
        #                     "activePrice": "0.0000"
        #                 }
        #             ]
        #         },
        #         "retExtInfo": null,
        #         "time": 1658904877942
        #     }
        #
        result = self.safe_value(response, 'result', {})
        positions = self.safe_value_2(result, 'list', 'dataList', [])
        timestamp = self.safe_integer(response, 'time')
        first = self.safe_value(positions, 0)
        position = self.parse_position(first, market)
        return self.extend(position, {
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
        })

    def fetch_unified_positions(self, symbols=None, params={}):
        self.load_markets()
        request = {}
        type = None
        settle = None
        enableUnified = self.is_unified_enabled()
        if isinstance(symbols, list):
            symbolsLength = len(symbols)
            if symbolsLength > 1:
                raise ArgumentsRequired(self.id + ' fetchPositions() does not accept an array with more than one symbol')
            market = self.market(symbols[0])
            settle = market['settle']
        elif symbols is not None:
            symbols = [symbols]
        symbols = self.market_symbols(symbols)
        if symbols is None:
            settle, params = self.handle_option_and_params(params, 'fetchPositions', 'settle', 'USDT')
        else:
            first = self.safe_value(symbols, 0)
            market = self.market(first)
            settle = market['settle']
        if enableUnified[1]:
            request['settleCoin'] = settle
        # market None
        type, params = self.handle_market_type_and_params('fetchPositions', None, params)
        subType = None
        subType, params = self.handle_sub_type_and_params('fetchPositions', None, params, 'linear')
        request['category'] = subType
        if type == 'option':
            request['category'] = 'option'
        method = 'privateGetV5PositionList' if (enableUnified[1]) else 'privateGetUnifiedV3PrivatePositionList'
        response = getattr(self, method)(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "Success",
        #         "result": {
        #             "nextPageCursor": "0%3A1657711949945%2C0%3A1657711949945",
        #             "category": "linear",
        #             "list": [
        #                 {
        #                     "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
        #     }
        #
        result = self.safe_value(response, 'result', {})
        positions = self.safe_value(result, 'list', [])
        results = []
        for i in range(0, len(positions)):
            rawPosition = positions[i]
            if ('data' in rawPosition) and ('is_valid' in rawPosition):
                # futures only
                rawPosition = self.safe_value(rawPosition, 'data')
            results.append(self.parse_position(rawPosition))
        return self.filter_by_array(results, 'symbol', symbols, False)

    def fetch_usdc_positions(self, symbols=None, params={}):
        self.load_markets()
        symbols = self.market_symbols(symbols)
        request = {}
        market = None
        type = None
        if isinstance(symbols, list):
            length = len(symbols)
            if length != 1:
                raise ArgumentsRequired(self.id + ' fetchUSDCPositions() takes an array with exactly one symbol')
            symbol = self.safe_string(symbols, 0)
            market = self.market(symbol)
            request['symbol'] = market['id']
        elif symbols is not None:
            market = self.market(symbols)
            request['symbol'] = market['id']
        type, params = self.handle_market_type_and_params('fetchUSDCPositions', market, params)
        request['category'] = 'OPTION' if (type == 'option') else 'PERPETUAL'
        response = self.privatePostOptionUsdcOpenapiPrivateV1QueryPosition(self.extend(request, params))
        #
        #     {
        #         "result": {
        #             "cursor": "BTC-31DEC21-24000-P%3A1640834421431%2CBTC-31DEC21-24000-P%3A1640834421431",
        #             "resultTotalSize": 1,
        #             "dataList": [
        #                 {
        #                 "symbol": "BTC-31DEC21-24000-P",
        #                 "leverage": "",
        #                 "occClosingFee": "",
        #                 "liqPrice": "",
        #                 "positionValue": "",
        #                 "takeProfit": "",
        #                 "riskId": "",
        #                 "trailingStop": "",
        #                 "unrealisedPnl": "",
        #                 "createdAt": "1640834421431",
        #                 "markPrice": "0.00",
        #                 "cumRealisedPnl": "",
        #                 "positionMM": "359.5271",
        #                 "positionIM": "467.0633",
        #                 "updatedAt": "1640834421431",
        #                 "tpSLMode": "",
        #                 "side": "Sell",
        #                 "bustPrice": "",
        #                 "deleverageIndicator": 0,
        #                 "entryPrice": "1.4",
        #                 "size": "-0.100",
        #                 "sessionRPL": "",
        #                 "positionStatus": "",
        #                 "sessionUPL": "",
        #                 "stopLoss": "",
        #                 "orderMargin": "",
        #                 "sessionAvgPrice": "1.5"
        #                 }
        #             ]
        #         },
        #         "retCode": 0,
        #         "retMsg": "Success."
        #     }
        #
        result = self.safe_value(response, 'result', {})
        positions = self.safe_value(result, 'dataList', [])
        results = []
        for i in range(0, len(positions)):
            rawPosition = positions[i]
            if ('data' in rawPosition) and ('is_valid' in rawPosition):
                # futures only
                rawPosition = self.safe_value(rawPosition, 'data')
            results.append(self.parse_position(rawPosition, market))
        return self.filter_by_array(results, 'symbol', symbols, False)

    def fetch_derivatives_positions(self, symbols=None, params={}):
        self.load_markets()
        request = {}
        market = None
        settle = None
        if isinstance(symbols, list):
            symbolsLength = len(symbols)
            if symbolsLength > 1:
                raise ArgumentsRequired(self.id + ' fetchPositions() does not accept an array with more than one symbol')
            if symbolsLength == 1:
                market = self.market(symbols[0])
                settle = market['settle']
                request['symbol'] = market['id']
        settle, params = self.handle_option_and_params(params, 'fetchPositions', 'settle', settle)
        if settle is not None:
            request['settleCoin'] = settle
        subType = None
        subType, params = self.handle_sub_type_and_params('fetchPositions', market, params, 'linear')
        request['category'] = subType
        response = self.privateGetV5PositionList(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "nextPageCursor": "updateAt%3D1672279322668",
        #             "category": "linear",
        #             "list": [
        #                 {
        #                     "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": {},
        #         "time": 1672280219169
        #     }
        #
        result = self.safe_value(response, 'result', {})
        positions = self.safe_value(result, 'list', [])
        return self.parse_positions(positions, symbols, params)

    def fetch_positions(self, symbols=None, params={}):
        """
        fetch all open positions
        :param [str]|None symbols: list of unified market symbols
        :param dict params: extra parameters specific to the bybit api endpoint
        :returns [dict]: a list of `position structure <https://docs.ccxt.com/en/latest/manual.html#position-structure>`
        """
        if isinstance(symbols, list):
            symbolsLength = len(symbols)
            if symbolsLength > 1:
                raise ArgumentsRequired(self.id + ' fetchPositions() does not accept an array with more than one symbol')
        elif symbols is not None:
            symbols = [symbols]
        self.load_markets()
        symbols = self.market_symbols(symbols)
        enableUnifiedMargin, enableUnifiedAccount = self.is_unified_enabled()
        settle = self.safe_string(params, 'settleCoin')
        if settle is None:
            settle, params = self.handle_option_and_params(params, 'fetchPositions', 'settle', settle)
        isUsdcSettled = settle == 'USDC'
        subType, query = self.handle_sub_type_and_params('fetchPositions', None, params)
        isInverse = subType == 'inverse'
        isLinearSettle = isUsdcSettled or (settle == 'USDT')
        if isInverse and isLinearSettle:
            raise ArgumentsRequired(self.id + ' fetchPositions with inverse subType requires settle to not be USDT or USDC')
        if (enableUnifiedMargin or enableUnifiedAccount) and not isInverse:
            return self.fetch_unified_positions(symbols, query)
        elif isUsdcSettled:
            return self.fetch_usdc_positions(symbols, query)
        else:
            return self.fetch_derivatives_positions(symbols, query)

    def parse_position(self, position, market=None):
        #
        # 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
        #     }
        #
        contract = self.safe_string(position, 'symbol')
        market = self.safe_market(contract, market, None, 'contract')
        size = Precise.string_abs(self.safe_string(position, 'size'))
        side = self.safe_string(position, 'side')
        if side is not None:
            if side == 'Buy':
                side = 'long'
            elif side == 'Sell':
                side = 'short'
            else:
                side = None
        notional = self.safe_string(position, 'positionValue')
        unrealisedPnl = self.omit_zero(self.safe_string(position, 'unrealisedPnl'))
        initialMarginString = self.safe_string(position, 'positionIM')
        maintenanceMarginString = self.safe_string(position, 'positionMM')
        timestamp = self.parse8601(self.safe_string(position, 'updated_at'))
        if timestamp is None:
            timestamp = self.safe_integer(position, 'updatedAt')
        # default to cross of USDC margined positions
        tradeMode = self.safe_integer(position, 'tradeMode', 0)
        marginMode = 'isolated' if tradeMode else 'cross'
        collateralString = self.safe_string(position, 'positionBalance')
        entryPrice = self.omit_zero(self.safe_string_2(position, 'entryPrice', 'avgPrice'))
        liquidationPrice = self.omit_zero(self.safe_string(position, 'liqPrice'))
        leverage = self.safe_string(position, 'leverage')
        if liquidationPrice is not None:
            if market['settle'] == 'USDC':
                #  (Entry price - Liq price) * Contracts + Maintenance Margin + (unrealised pnl) = Collateral
                difference = Precise.string_abs(Precise.string_sub(entryPrice, liquidationPrice))
                collateralString = Precise.string_add(Precise.string_add(Precise.string_mul(difference, size), maintenanceMarginString), unrealisedPnl)
            else:
                bustPrice = self.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 is not None:
                        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 is not None:
                        initialMarginString = Precise.string_div(size, Precise.string_mul(entryPrice, leverage))
        maintenanceMarginPercentage = Precise.string_div(maintenanceMarginString, notional)
        percentage = Precise.string_mul(Precise.string_div(unrealisedPnl, initialMarginString), '100')
        marginRatio = Precise.string_div(maintenanceMarginString, collateralString, 4)
        return {
            'info': position,
            'id': None,
            'symbol': market['symbol'],
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'initialMargin': self.parse_number(initialMarginString),
            'initialMarginPercentage': self.parse_number(Precise.string_div(initialMarginString, notional)),
            'maintenanceMargin': self.parse_number(maintenanceMarginString),
            'maintenanceMarginPercentage': self.parse_number(maintenanceMarginPercentage),
            'entryPrice': self.parse_number(entryPrice),
            'notional': self.parse_number(notional),
            'leverage': self.parse_number(leverage),
            'unrealizedPnl': self.parse_number(unrealisedPnl),
            'contracts': self.parse_number(size),  # in USD for inverse swaps
            'contractSize': self.safe_number(market, 'contractSize'),
            'marginRatio': self.parse_number(marginRatio),
            'liquidationPrice': self.parse_number(liquidationPrice),
            'markPrice': self.safe_number(position, 'markPrice'),
            'collateral': self.parse_number(collateralString),
            'marginMode': marginMode,
            'side': side,
            'percentage': self.parse_number(percentage),
        }

    def set_margin_mode(self, marginMode, symbol=None, params={}):
        self.load_markets()
        values = self.is_unified_enabled()
        isUnifiedAccount = self.safe_value(values, 1)
        if isUnifiedAccount:
            return self.set_unified_margin_mode(marginMode, symbol, params)
        return self.setDerivativesMarginMode(marginMode, symbol, params)

    def set_unified_margin_mode(self, marginMode, symbol=None, params={}):
        self.load_markets()
        if (marginMode != 'REGULAR_MARGIN') and (marginMode != 'PORTFOLIO_MARGIN'):
            raise BadRequest(self.id + ' setMarginMode() marginMode must be either REGULAR_MARGIN or PORTFOLIO_MARGIN')
        request = {
            'setMarginMode': marginMode,
        }
        response = self.privatePostV5AccountSetMarginMode(self.extend(request, params))
        #
        #  {
        #      "setMarginMode": "PORTFOLIO_MARGIN"
        #  }
        #
        return response

    def set_derivatives_margin_mode_margin_mode(self, marginMode, symbol=None, params={}):
        self.check_required_symbol('setMarginMode', symbol)
        self.load_markets()
        market = self.market(symbol)
        if market['settle'] == 'USDC':
            raise NotSupported(self.id + ' setMarginMode() does not support market ' + symbol + '')
        marginMode = marginMode.upper()
        if (marginMode != 'ISOLATED') and (marginMode != 'CROSS'):
            raise BadRequest(self.id + ' setMarginMode() marginMode must be either isolated or cross')
        leverage = self.safe_string(params, 'leverage')
        sellLeverage = None
        buyLeverage = None
        if leverage is None:
            sellLeverage = self.safe_number_2(params, 'sell_leverage', 'sellLeverage')
            buyLeverage = self.safe_number_2(params, 'buy_leverage', 'buyLeverage')
            if sellLeverage is None and buyLeverage is None:
                raise ArgumentsRequired(self.id + ' setMarginMode() requires a leverage parameter or sell_leverage and buy_leverage parameters')
            params = self.omit(params, ['buy_leverage', 'sell_leverage', 'sellLeverage', 'buyLeverage'])
        else:
            params = self.omit(params, 'leverage')
            sellLeverage = leverage
            buyLeverage = leverage
        tradeMode = 1 if (marginMode == 'ISOLATED') else 0
        request = {
            'symbol': market['id'],
            'tradeMode': tradeMode,
            'buyLeverage': leverage,
            'sellLeverage': leverage,
        }
        response = self.privatePostContractV3PrivatePositionSwitchIsolated(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {},
        #         "retExtInfo": null,
        #         "time": 1658908532580
        #     }
        #
        return response

    def set_leverage(self, leverage, symbol=None, params={}):
        """
        set the level of leverage for a market
        :param float leverage: the rate of leverage
        :param str symbol: unified market symbol
        :param dict params: extra parameters specific to the bybit api endpoint
        :returns dict: response from the exchange
        """
        self.check_required_symbol('setLeverage', symbol)
        self.load_markets()
        market = self.market(symbol)
        # WARNING: THIS WILL INCREASE LIQUIDATION PRICE FOR OPEN ISOLATED LONG POSITIONS
        # AND DECREASE LIQUIDATION PRICE FOR OPEN ISOLATED SHORT POSITIONS
        isUsdcSettled = market['settle'] == 'USDC'
        enableUnifiedMargin, enableUnifiedAccount = self.is_unified_enabled()
        # engage in leverage setting
        # we reuse the code here instead of having two methods
        leverage = self.number_to_string(leverage)
        method = None
        request = None
        if enableUnifiedMargin or enableUnifiedAccount or not isUsdcSettled:
            request = {
                'symbol': market['id'],
                'buyLeverage': leverage,
                'sellLeverage': leverage,
            }
            if enableUnifiedAccount:
                if market['linear']:
                    request['category'] = 'linear'
                else:
                    raise NotSupported(self.id + ' setUnifiedMarginLeverage() leverage doesn\'t support inverse and option market in unified account')
                method = 'privatePostV5PositionSetLeverage'
            elif enableUnifiedMargin:
                if market['option']:
                    request['category'] = 'option'
                elif market['linear']:
                    request['category'] = 'linear'
                else:
                    raise NotSupported(self.id + ' setUnifiedMarginLeverage() leverage doesn\'t support inverse market in unified margin')
                method = 'privatePostUnifiedV3PrivatePositionSetLeverage'
            else:
                method = 'privatePostContractV3PrivatePositionSetLeverage'
        else:
            request = {
                'symbol': market['id'],
                'leverage': leverage,
            }
            method = 'privatePostPerpetualUsdcOpenapiPrivateV1PositionLeverageSave'
        return getattr(self, method)(self.extend(request, params))

    def set_position_mode(self, hedged, symbol=None, params={}):
        self.load_markets()
        mode = None
        if hedged:
            mode = 3
        else:
            mode = 0
        request = {
            'mode': mode,
        }
        if symbol is None:
            request['coin'] = 'USDT'
        else:
            market = self.market(symbol)
            request['symbol'] = market['id']
        #
        #     {
        #         "ret_code": 0,
        #         "ret_msg": "ok",
        #         "ext_code": "",
        #         "result": null,
        #         "ext_info": null,
        #         "time_now": "1577477968.175013",
        #         "rate_limit_status": 74,
        #         "rate_limit_reset_ms": 1577477968183,
        #         "rate_limit": 75
        #     }
        #
        return self.privatePostContractV3PrivatePositionSwitchMode(self.extend(request, params))

    def fetch_derivatives_open_interest_history(self, symbol, timeframe='1h', since=None, limit=None, params={}):
        self.load_markets()
        market = self.market(symbol)
        subType = 'linear' if market['linear'] else 'inverse'
        category = self.safe_string(params, 'category', subType)
        intervals = self.safe_value(self.options, 'intervals')
        interval = self.safe_string(intervals, timeframe)  # 5min,15min,30min,1h,4h,1d
        if interval is None:
            raise BadRequest(self.id + ' fetchOpenInterestHistory() cannot use the ' + timeframe + ' timeframe')
        request = {
            'symbol': market['id'],
            'intervalTime': interval,
            'category': category,
        }
        if since is not None:
            request['startTime'] = since
        if limit is not None:
            request['limit'] = limit
        response = self.publicGetV5MarketOpenInterest(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "symbol": "BTCUSD",
        #             "category": "inverse",
        #             "list": [
        #                 {
        #                     "openInterest": "461134384.00000000",
        #                     "timestamp": "1669571400000"
        #                 },
        #                 {
        #                     "openInterest": "461134292.00000000",
        #                     "timestamp": "1669571100000"
        #                 }
        #             ],
        #             "nextPageCursor": ""
        #         },
        #         "retExtInfo": {},
        #         "time": 1672053548579
        #     }
        #
        result = self.safe_value(response, 'result', {})
        id = self.safe_string(result, 'symbol')
        market = self.safe_market(id, market, None, 'contract')
        data = self.safe_value(result, 'list', [])
        return self.parse_open_interests(data, market, since, limit)

    def fetch_open_interest(self, symbol, params={}):
        """
        Retrieves the open interest of a derivative trading pair
        see https://bybit-exchange.github.io/docs/v5/market/open-interest
        :param str symbol: Unified CCXT market symbol
        :param dict params: exchange specific parameters
        :param str|None params['interval']: 5m, 15m, 30m, 1h, 4h, 1d
        :param str|None params['category']: "linear" or "inverse"
        :returns dict} an open interest structure{@link https://docs.ccxt.com/en/latest/manual.html#interest-history-structure:
        """
        self.load_markets()
        market = self.market(symbol)
        if not market['contract']:
            raise BadRequest(self.id + ' fetchOpenInterest() supports contract markets only')
        timeframe = self.safe_string(params, 'interval', '1h')
        intervals = self.safe_value(self.options, 'intervals')
        interval = self.safe_string(intervals, timeframe)  # 5min,15min,30min,1h,4h,1d
        if interval is None:
            raise BadRequest(self.id + ' fetchOpenInterest() cannot use the ' + timeframe + ' timeframe')
        subType = 'linear' if market['linear'] else 'inverse'
        category = self.safe_string(params, 'category', subType)
        request = {
            'symbol': market['id'],
            'intervalTime': interval,
            'category': category,
        }
        response = self.publicGetV5MarketOpenInterest(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "symbol": "BTCUSD",
        #             "category": "inverse",
        #             "list": [
        #                 {
        #                     "openInterest": "461134384.00000000",
        #                     "timestamp": "1669571400000"
        #                 },
        #                 {
        #                     "openInterest": "461134292.00000000",
        #                     "timestamp": "1669571100000"
        #                 }
        #             ],
        #             "nextPageCursor": ""
        #         },
        #         "retExtInfo": {},
        #         "time": 1672053548579
        #     }
        #
        result = self.safe_value(response, 'result', {})
        id = self.safe_string(result, 'symbol')
        market = self.safe_market(id, market, None, 'contract')
        data = self.safe_value(result, 'list', [])
        return self.parse_open_interest(data[0], market)

    def fetch_open_interest_history(self, symbol, timeframe='1h', since=None, limit=None, 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 str symbol: Unified market symbol
        :param str 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 dict params: Exchange specific parameters
        :returns: An array of open interest structures
        """
        if timeframe == '1m':
            raise BadRequest(self.id + 'fetchOpenInterestHistory cannot use the 1m timeframe')
        self.load_markets()
        market = self.market(symbol)
        if market['spot'] or market['option']:
            raise BadRequest(self.id + ' fetchOpenInterestHistory() symbol does not support market ' + symbol)
        request = {
            'symbol': market['id'],
        }
        if limit is not None:
            request['limit'] = limit
        return self.fetch_derivatives_open_interest_history(symbol, timeframe, since, limit, params)

    def parse_open_interest(self, interest, market=None):
        #
        #    {
        #        "openInterest": 64757.62400000,
        #        "timestamp": 1665784800000,
        #    }
        #
        timestamp = self.safe_integer(interest, 'timestamp')
        value = self.safe_number_2(interest, 'open_interest', 'openInterest')
        return {
            'symbol': market['symbol'],
            'openInterestAmount': None,
            'openInterestValue': value,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'info': interest,
        }

    def fetch_borrow_rate(self, code, params={}):
        """
        fetch the rate of interest to borrow a currency for margin trading
        see https://bybit-exchange.github.io/docs/spot/v3/#t-queryinterestquota
        :param str code: unified currency code
        :param dict params: extra parameters specific to the bybit api endpoint
        :returns dict: a `borrow rate structure <https://docs.ccxt.com/en/latest/manual.html#borrow-rate-structure>`
        """
        self.load_markets()
        currency = self.currency(code)
        request = {
            'coin': currency['id'],
        }
        response = self.privateGetSpotV3PrivateCrossMarginLoanInfo(self.extend(request, params))
        #
        #    {
        #         "retCode": "0",
        #         "retMsg": "success",
        #         "result": {
        #             "coin": "USDT",
        #             "interestRate": "0.000107000000",
        #             "loanAbleAmount": "",
        #             "maxLoanAmount": "79999.999"
        #         },
        #         "retExtInfo": null,
        #         "time": "1666734490778"
        #     }
        #
        data = self.safe_value(response, 'result', {})
        return self.parse_borrow_rate(data, currency)

    def parse_borrow_rate(self, info, currency=None):
        #
        #     {
        #         "coin": "USDT",
        #         "interestRate": "0.000107000000",
        #         "loanAbleAmount": "",
        #         "maxLoanAmount": "79999.999"
        #     }
        #
        timestamp = self.milliseconds()
        currencyId = self.safe_string(info, 'coin')
        return {
            'currency': self.safe_currency_code(currencyId, currency),
            'rate': self.safe_number(info, 'interestRate'),
            'period': 86400000,  # Daily
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'info': info,
        }

    def fetch_borrow_interest(self, code=None, symbol=None, since=None, limit=None, params={}):
        """
        fetch the interest owed by the user for borrowing currency for margin trading
        :param str|None code: unified currency code
        :param str|None symbol: unified market symbol when fetch interest in isolated markets
        :param number|None since: the earliest time in ms to fetch borrrow interest for
        :param number|None limit: the maximum number of structures to retrieve
        :param dict params: extra parameters specific to the bybit api endpoint
        :returns [dict]: a list of `borrow interest structures <https://docs.ccxt.com/en/latest/manual.html#borrow-interest-structure>`
        """
        self.load_markets()
        request = {}
        response = self.privateGetSpotV3PrivateCrossMarginAccount(self.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": [
        #                 {
        #                     "tokenId": "BTC",
        #                     "total": "0.00048621",
        #                     "locked": "0",
        #                     "loan": "0",
        #                     "interest": "0",
        #                     "free": "0.00048621"
        #                 },
        #                 ...
        #             ]
        #         }
        #     }
        #
        data = self.safe_value(response, 'result', {})
        rows = self.safe_value(data, 'loanAccountList', [])
        interest = self.parse_borrow_interests(rows, None)
        return self.filter_by_currency_since_limit(interest, code, since, limit)

    def parse_borrow_interest(self, info, market):
        #
        #     {
        #         "tokenId": "BTC",
        #         "total": "0.00048621",
        #         "locked": "0",
        #         "loan": "0",
        #         "interest": "0",
        #         "free": "0.00048621"
        #     },
        #
        return {
            'symbol': None,
            'marginMode': 'cross',
            'currency': self.safe_currency_code(self.safe_string(info, 'tokenId')),
            'interest': self.safe_number(info, 'interest'),
            'interestRate': None,
            'amountBorrowed': self.safe_number(info, 'loan'),
            'timestamp': None,
            'datetime': None,
            'info': info,
        }

    def transfer(self, code, amount, fromAccount, toAccount, params={}):
        """
        transfer currency internally between wallets on the same account
        see https://bybit-exchange.github.io/docs/account_asset/#t-createinternaltransfer
        see https://bybit-exchange.github.io/docs/account_asset/v3/#t-createinternaltransfer
        :param str code: unified currency code
        :param float amount: amount to transfer
        :param str fromAccount: account to transfer from
        :param str toAccount: account to transfer to
        :param dict params: extra parameters specific to the bybit api endpoint
        :param str params['transferId']: UUID, which is unique across the platform
        :returns dict: a `transfer structure <https://docs.ccxt.com/en/latest/manual.html#transfer-structure>`
        """
        self.load_markets()
        transferId = self.safe_string(params, 'transferId', self.uuid())
        accountTypes = self.safe_value(self.options, 'accountsByType', {})
        fromId = self.safe_string(accountTypes, fromAccount, fromAccount)
        toId = self.safe_string(accountTypes, toAccount, toAccount)
        currency = self.currency(code)
        amountToPrecision = self.currency_to_precision(code, amount)
        method = None
        method, params = self.handle_option_and_params(params, 'transfer', 'method', 'privatePostAssetV1PrivateTransfer')  # v1 preferred atm, because it supports funding
        request = None
        if method == 'privatePostAssetV3PrivateTransferInterTransfer' or method == 'privatePostV5AssetTransferInterTransfer':
            request = {
                'transferId': transferId,
                'fromAccountType': fromId,
                'toAccountType': toId,
                'coin': currency['id'],
                'amount': amountToPrecision,
            }
        else:
            request = {
                'transfer_id': transferId,
                'from_account_type': fromId,
                'to_account_type': toId,
                'coin': currency['id'],
                'amount': amountToPrecision,
            }
        response = getattr(self, method)(self.extend(request, params))
        #
        # {
        #     "retCode": 0,
        #     "retMsg": "success",
        #     "result": {
        #         "transferId": "4244af44-f3b0-4cf6-a743-b56560e987bc"  # transfer_id in v1
        #     },
        #     "retExtInfo": {},
        #     "time": 1666875857205
        # }
        #
        timestamp = self.safe_integer_2(response, 'time', 'time_now')
        transfer = self.safe_value(response, 'result', {})
        statusRaw = self.safe_string_n(response, ['retCode', 'retMsg', 'ret_code', 'ret_msg'])
        status = self.parse_transfer_status(statusRaw)
        return self.extend(self.parse_transfer(transfer, currency), {
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'amount': self.parse_number(amountToPrecision),
            'fromAccount': fromAccount,
            'toAccount': toAccount,
            'status': status,
        })

    def fetch_transfers(self, code=None, since=None, limit=None, params={}):
        """
        fetch a history of internal transfers made on an account
        see https://bybit-exchange.github.io/docs/v5/asset/inter-transfer-list
        :param str|None code: unified currency code of the currency transferred
        :param int|None since: the earliest time in ms to fetch transfers for
        :param int|None limit: the maximum number of  transfers structures to retrieve
        :param dict params: extra parameters specific to the bybit api endpoint
        :returns [dict]: a list of `transfer structures <https://docs.ccxt.com/en/latest/manual.html#transfer-structure>`
        """
        self.load_markets()
        currency = None
        request = {}
        if code is not None:
            currency = self.safe_currency_code(code)
            request['coin'] = currency
        if since is not None:
            request['startTime'] = since
        if limit is not None:
            request['limit'] = limit
        response = self.privateGetV5AssetTransferQueryInterTransferList(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "success",
        #         "result": {
        #             "list": [
        #                 {
        #                     "transferId": "selfTransfer_a1091cc7-9364-4b74-8de1-18f02c6f2d5c",
        #                     "coin": "USDT",
        #                     "amount": "5000",
        #                     "fromAccountType": "SPOT",
        #                     "toAccountType": "UNIFIED",
        #                     "timestamp": "1667283263000",
        #                     "status": "SUCCESS"
        #                 }
        #             ],
        #             "nextPageCursor": "eyJtaW5JRCI6MTM1ODQ2OCwibWF4SUQiOjEzNTg0Njh9"
        #         },
        #         "retExtInfo": {},
        #         "time": 1670988271677
        #     }
        #
        data = self.safe_value(response, 'result', {})
        transfers = self.safe_value(data, 'list', [])
        return self.parse_transfers(transfers, currency, since, limit)

    def borrow_margin(self, code, amount, symbol=None, params={}):
        """
        create a loan to borrow margin
        see https://bybit-exchange.github.io/docs/spot/v3/#t-borrowmarginloan
        :param str code: unified currency code of the currency to borrow
        :param float amount: the amount to borrow
        :param str|None symbol: not used by bybit.borrowMargin()
        :param dict params: extra parameters specific to the bybit api endpoint
        :returns dict: a `margin loan structure <https://docs.ccxt.com/en/latest/manual.html#margin-loan-structure>`
        """
        self.load_markets()
        currency = self.currency(code)
        marginMode, query = self.handle_margin_mode_and_params('borrowMargin', params)
        if marginMode == 'isolated':
            raise NotSupported(self.id + ' borrowMargin() cannot use isolated margin')
        request = {
            'coin': currency['id'],
            'qty': self.currency_to_precision(code, amount),
        }
        response = self.privatePostSpotV3PrivateCrossMarginLoan(self.extend(request, query))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "success",
        #         "result": {
        #             "transactId": "14143"
        #         },
        #         "retExtInfo": null,
        #         "time": 1662617848970
        #     }
        #
        result = self.safe_value(response, 'result', {})
        transaction = self.parse_margin_loan(result, currency)
        return self.extend(transaction, {
            'symbol': symbol,
            'amount': amount,
        })

    def repay_margin(self, code, amount, symbol=None, params={}):
        """
        repay borrowed margin and interest
        see https://bybit-exchange.github.io/docs/spot/v3/#t-repaymarginloan
        :param str code: unified currency code of the currency to repay
        :param float amount: the amount to repay
        :param str|None symbol: not used by bybit.repayMargin()
        :param dict params: extra parameters specific to the bybit api endpoint
        :returns dict: a `margin loan structure <https://docs.ccxt.com/en/latest/manual.html#margin-loan-structure>`
        """
        self.load_markets()
        currency = self.currency(code)
        marginMode, query = self.handle_margin_mode_and_params('repayMargin', params)
        if marginMode == 'isolated':
            raise NotSupported(self.id + ' repayMargin() cannot use isolated margin')
        request = {
            'coin': currency['id'],
            'qty': self.number_to_string(amount),
        }
        response = self.privatePostSpotV3PrivateCrossMarginRepay(self.extend(request, query))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "success",
        #         "result": {
        #            "repayId": "12128"
        #         },
        #         "retExtInfo": null,
        #         "time": 1662618298452
        #     }
        #
        result = self.safe_value(response, 'result', {})
        transaction = self.parse_margin_loan(result, currency)
        return self.extend(transaction, {
            'symbol': symbol,
            'amount': amount,
        })

    def parse_margin_loan(self, info, currency=None):
        #
        # borrowMargin
        #
        #     {
        #         "transactId": "14143"
        #     }
        #
        # repayMargin
        #
        #     {
        #         "repayId": "12128"
        #     }
        #
        return {
            'id': self.safe_string_2(info, 'transactId', 'repayId'),
            'currency': self.safe_string(currency, 'code'),
            'amount': None,
            'symbol': None,
            'timestamp': None,
            'datetime': None,
            'info': info,
        }

    def parse_transfer_status(self, status):
        statuses = {
            '0': 'ok',
            'OK': 'ok',
            'SUCCESS': 'ok',
        }
        return self.safe_string(statuses, status, status)

    def parse_transfer(self, transfer, currency=None):
        #
        # transfer
        #
        #     {
        #         "transferId": "22c2bc11-ed5b-49a4-8647-c4e0f5f6f2b2"  # transfer_id in v1
        #     }
        #
        # fetchTransfers
        #
        #     {
        #         "transferId": "e9c421c4-b010-4b16-abd6-106179f27702",  # transfer_id in v1
        #         "coin": "USDT",
        #         "amount": "8",
        #         "fromAccountType": "FUND",  # from_account_type in v1
        #         "toAccountType": "SPOT",  # to_account_type in v1
        #         "timestamp": "1666879426000",
        #         "status": "SUCCESS"
        #      }
        #
        currencyId = self.safe_string(transfer, 'coin')
        timestamp = self.safe_integer(transfer, 'timestamp')
        fromAccountId = self.safe_string_2(transfer, 'fromAccountType', 'from_account_type')
        toAccountId = self.safe_string_2(transfer, 'toAccountType', 'to_account_type')
        accountIds = self.safe_value(self.options, 'accountsById', {})
        fromAccount = self.safe_string(accountIds, fromAccountId, fromAccountId)
        toAccount = self.safe_string(accountIds, toAccountId, toAccountId)
        return {
            'info': transfer,
            'id': self.safe_string_2(transfer, 'transferId', 'transfer_id'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'currency': self.safe_currency_code(currencyId, currency),
            'amount': self.safe_number(transfer, 'amount'),
            'fromAccount': fromAccount,
            'toAccount': toAccount,
            'status': self.parse_transfer_status(self.safe_string(transfer, 'status')),
        }

    def fetch_derivatives_market_leverage_tiers(self, symbol, params={}):
        self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        if market['linear']:
            request['category'] = 'linear'
        elif market['inverse']:
            request['category'] = 'inverse'
        response = self.publicGetV5MarketRiskLimit(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "category": "inverse",
        #             "list": [
        #                 {
        #                     "id": 1,
        #                     "symbol": "BTCUSD",
        #                     "riskLimitValue": "150",
        #                     "maintenanceMargin": "0.5",
        #                     "initialMargin": "1",
        #                     "isLowestRisk": 1,
        #                     "maxLeverage": "100.00"
        #                 },
        #             ....
        #             ]
        #         },
        #         "retExtInfo": {},
        #         "time": 1672054488010
        #     }
        #
        result = self.safe_value(response, 'result')
        tiers = self.safe_value(result, 'list')
        return self.parse_market_leverage_tiers(tiers, market)

    def fetch_market_leverage_tiers(self, 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 str symbol: unified market symbol
        :param dict params: extra parameters specific to the bybit api endpoint
        :returns dict: a `leverage tiers structure <https://docs.ccxt.com/en/latest/manual.html#leverage-tiers-structure>`
        """
        self.load_markets()
        request = {}
        market = None
        market = self.market(symbol)
        if market['spot'] or market['option']:
            raise BadRequest(self.id + ' fetchMarketLeverageTiers() symbol does not support market ' + symbol)
        request['symbol'] = market['id']
        return self.fetch_derivatives_market_leverage_tiers(symbol, params)

    def parse_market_leverage_tiers(self, info, market):
        #
        #    Linear
        #    [
        #        {
        #            id: '11',
        #            symbol: 'ETHUSDT',
        #            limit: '800000',
        #            maintain_margin: '0.01',
        #            starting_margin: '0.02',
        #            section: [
        #                '1',  '2',  '3',
        #                '5',  '10', '15',
        #                '25'
        #            ],
        #            is_lowest_risk: '1',
        #            created_at: '2022-02-04 23:30:33.555252',
        #            updated_at: '2022-02-04 23:30:33.555254',
        #            max_leverage: '50'
        #        },
        #        ...
        #    ]
        #
        #    Inverse
        #    [
        #        {
        #            id: '180',
        #            is_lowest_risk: '0',
        #            section: [
        #                '1', '2', '3',
        #                '4', '5', '7',
        #                '8', '9'
        #            ],
        #            symbol: 'ETHUSDH22',
        #            limit: '30000',
        #            max_leverage: '9',
        #            starting_margin: '11',
        #            maintain_margin: '5.5',
        #            coin: 'ETH',
        #            created_at: '2021-04-22T15:00:00Z',
        #            updated_at: '2021-04-22T15:00:00Z'
        #        }
        #        ...
        #    ]
        #
        # usdc swap
        #
        #    {
        #        "riskId":"10001",
        #        "symbol":"BTCPERP",
        #        "limit":"1000000",
        #        "startingMargin":"0.0100",
        #        "maintainMargin":"0.0050",
        #        "isLowestRisk":true,
        #        "section":[
        #           "1",
        #           "2",
        #           "3",
        #           "5",
        #           "10",
        #           "25",
        #           "50",
        #           "100"
        #        ],
        #        "maxLeverage":"100.00"
        #    }
        #
        # Unified Margin
        #
        #     [
        #         {
        #             "id": 1,
        #             "symbol": "BTCUSDT",
        #             "limit": "2000000",
        #             "maintainMargin": "0.005",
        #             "initialMargin": "0.01",
        #             "section": [
        #                 "1",
        #                 "3",
        #                 "5",
        #                 "10",
        #                 "25",
        #                 "50",
        #                 "80"
        #             ],
        #             "isLowestRisk": 1,
        #             "maxLeverage": "100.00"
        #         }
        #     ]
        #
        minNotional = 0
        tiers = []
        for i in range(0, len(info)):
            item = info[i]
            maxNotional = self.safe_number(item, 'limit')
            tiers.append({
                'tier': self.sum(i, 1),
                'currency': market['base'],
                'minNotional': minNotional,
                'maxNotional': maxNotional,
                'maintenanceMarginRate': self.safe_number_2(item, 'maintain_margin', 'maintainMargin'),
                'maxLeverage': self.safe_number_2(item, 'max_leverage', 'maxLeverage'),
                'info': item,
            })
            minNotional = maxNotional
        return tiers

    def parse_trading_fee(self, fee, market=None):
        #
        #     {
        #         "symbol": "ETHUSDT",
        #         "makerFeeRate": 0.001,
        #         "takerFeeRate": 0.001
        #     }
        #
        marketId = self.safe_string(fee, 'symbol')
        symbol = self.safe_symbol(marketId, None, None, 'contract')
        return {
            'info': fee,
            'symbol': symbol,
            'maker': self.safe_number(fee, 'makerFeeRate'),
            'taker': self.safe_number(fee, 'takerFeeRate'),
        }

    def fetch_trading_fee(self, symbol, params={}):
        """
        fetch the trading fees for a market
        see https://bybit-exchange.github.io/docs/v5/account/fee-rate
        :param str symbol: unified market symbol
        :param dict params: extra parameters specific to the bybit api endpoint
        :returns dict: a `fee structure <https://docs.ccxt.com/en/latest/manual.html#fee-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        if market['spot']:
            raise NotSupported(self.id + ' fetchTradingFee() is not supported for spot market')
        request = {
            'symbol': market['id'],
        }
        response = self.privateGetV5AccountFeeRate(self.extend(request, params))
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "list": [
        #                 {
        #                     "symbol": "ETHUSDT",
        #                     "takerFeeRate": "0.0006",
        #                     "makerFeeRate": "0.0001"
        #                 }
        #             ]
        #         },
        #         "retExtInfo": {},
        #         "time": 1676360412576
        #     }
        #
        result = self.safe_value(response, 'result', {})
        fees = self.safe_value(result, 'list', [])
        first = self.safe_value(fees, 0, {})
        return self.parse_trading_fee(first)

    def fetch_trading_fees(self, params={}):
        """
        fetch the trading fees for multiple markets
        see https://bybit-exchange.github.io/docs/v5/account/fee-rate
        :param dict params: extra parameters specific to the bybit api endpoint
        :returns dict: a dictionary of `fee structures <https://docs.ccxt.com/en/latest/manual.html#fee-structure>` indexed by market symbols
        """
        self.load_markets()
        type = None
        type, params = self.handle_option_and_params(params, 'fetchTradingFees', 'type', 'future')
        if type == 'spot':
            raise NotSupported(self.id + ' fetchTradingFees() is not supported for spot market')
        response = self.privateGetV5AccountFeeRate(params)
        #
        #     {
        #         "retCode": 0,
        #         "retMsg": "OK",
        #         "result": {
        #             "list": [
        #                 {
        #                     "symbol": "ETHUSDT",
        #                     "takerFeeRate": "0.0006",
        #                     "makerFeeRate": "0.0001"
        #                 }
        #             ]
        #         },
        #         "retExtInfo": {},
        #         "time": 1676360412576
        #     }
        #
        fees = self.safe_value(response, 'result', {})
        fees = self.safe_value(fees, 'list', [])
        result = {}
        for i in range(0, len(fees)):
            fee = self.parse_trading_fee(fees[i])
            symbol = fee['symbol']
            result[symbol] = fee
        return result

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        url = self.implode_hostname(self.urls['api'][api]) + '/' + path
        if api == 'public':
            if params:
                url += '?' + self.rawencode(params)
        elif api == 'private':
            self.check_required_credentials()
            isOpenapi = url.find('openapi') >= 0
            isV3UnifiedMargin = url.find('unified/v3') >= 0
            isV5UnifiedAccount = url.find('v5') >= 0
            timestamp = str(self.nonce())
            if isOpenapi:
                if params:
                    body = self.json(params)
                else:
                    # self fix for PHP is required otherwise it generates
                    # '[]' on empty arrays even when forced to use objects
                    body = '{}'
                payload = timestamp + self.apiKey + body
                signature = self.hmac(self.encode(payload), self.encode(self.secret), hashlib.sha256, 'hex')
                headers = {
                    'Content-Type': 'application/json',
                    'X-BAPI-API-KEY': self.apiKey,
                    'X-BAPI-TIMESTAMP': timestamp,
                    'X-BAPI-SIGN': signature,
                }
            elif isV3UnifiedMargin or isV5UnifiedAccount:
                headers = {
                    'Content-Type': 'application/json',
                    'X-BAPI-API-KEY': self.apiKey,
                    'X-BAPI-TIMESTAMP': timestamp,
                    'X-BAPI-RECV-WINDOW': str(self.options['recvWindow']),
                }
                if isV3UnifiedMargin:
                    headers['X-BAPI-SIGN-TYPE'] = '2'
                query = params
                queryEncoded = self.rawencode(query)
                auth_base = str(timestamp) + self.apiKey + str(self.options['recvWindow'])
                authFull = None
                if method == 'POST':
                    body = self.json(query)
                    authFull = auth_base + body
                else:
                    authFull = auth_base + queryEncoded
                    url += '?' + self.rawencode(query)
                headers['X-BAPI-SIGN'] = self.hmac(self.encode(authFull), self.encode(self.secret))
            else:
                query = self.extend(params, {
                    'api_key': self.apiKey,
                    'recv_window': self.options['recvWindow'],
                    'timestamp': timestamp,
                })
                sortedQuery = self.keysort(query)
                auth = self.rawencode(sortedQuery)
                signature = self.hmac(self.encode(auth), self.encode(self.secret))
                if method == 'POST':
                    isSpot = url.find('spot') >= 0
                    extendedQuery = self.extend(query, {
                        'sign': signature,
                    })
                    if isSpot:
                        body = self.urlencode(extendedQuery)
                        headers = {
                            'Content-Type': 'application/x-www-form-urlencoded',
                        }
                    else:
                        body = self.json(extendedQuery)
                        headers = {
                            'Content-Type': 'application/json',
                        }
                else:
                    url += '?' + self.rawencode(sortedQuery)
                    url += '&sign=' + signature
        if method == 'POST':
            brokerId = self.safe_string(self.options, 'brokerId')
            if brokerId is not None:
                headers['Referer'] = brokerId
        return {'url': url, 'method': method, 'body': body, 'headers': headers}

    def handle_errors(self, httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody):
        if not response:
            return  # 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":{"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 = self.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
                # {"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 None
            feedback = None
            if errorCode == '10005':
                feedback = self.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 = self.id + ' ' + body
            self.throw_broadly_matched_exception(self.exceptions['broad'], body, feedback)
            self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback)
            raise ExchangeError(feedback)  # unknown message
