# -*- 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.async_support.base.exchange import Exchange
from ccxt.base.errors import ExchangeError
from ccxt.base.errors import AuthenticationError
from ccxt.base.errors import ArgumentsRequired
from ccxt.base.errors import BadRequest
from ccxt.base.errors import InsufficientFunds
from ccxt.base.errors import InvalidAddress
from ccxt.base.errors import InvalidOrder
from ccxt.base.errors import OrderNotFound
from ccxt.base.errors import NotSupported
from ccxt.base.errors import DDoSProtection
from ccxt.base.errors import ExchangeNotAvailable
from ccxt.base.errors import RequestTimeout
from ccxt.base.decimal_to_precision import TICK_SIZE
from ccxt.base.precise import Precise


class btcex(Exchange):

    def describe(self):
        return self.deep_extend(super(btcex, self).describe(), {
            'id': 'btcex',
            'name': 'BTCEX',
            'countries': ['CA'],  # Canada
            'version': 'v1',
            'certified': False,
            'pro': True,
            'requiredCredentials': {
                'apiKey': True,
                'secret': True,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/173489620-d49807a4-55cd-4f4e-aca9-534921298bbf.jpg',
                'www': 'https://www.btcex.com/',
                'api': {
                    'rest': 'https://api.btcex.com',
                },
                'doc': 'https://docs.btcex.com/',
                'fees': 'https://support.btcex.com/hc/en-us/articles/4415995130647',
                'referral': {
                    'url': 'https://www.btcex.com/en-us/register?i=48biatg1',
                    'discount': 0.1,
                },
            },
            'has': {
                'CORS': None,
                'spot': True,
                'margin': True,
                'swap': True,
                'future': True,
                'option': True,
                'cancelAllOrders': True,
                'cancelOrder': True,
                'createLimitBuyOrder': True,
                'createLimitSellOrder': True,
                'createMarketBuyOrder': True,
                'createMarketSellOrder': True,
                'createOrder': True,
                'createPostOnlyOrder': True,
                'createReduceOnlyOrder': True,
                'createStopLimitOrder': True,
                'createStopMarketOrder': True,
                'createStopOrder': True,
                'editOrder': False,
                'fetchBalance': True,
                'fetchBorrowRate': False,
                'fetchBorrowRateHistories': False,
                'fetchBorrowRateHistory': False,
                'fetchBorrowRates': False,
                'fetchClosedOrders': True,
                'fetchCurrencies': False,
                'fetchDepositAddress': False,
                'fetchDeposits': True,
                'fetchFundingHistory': False,
                'fetchFundingRate': True,
                'fetchFundingRateHistory': False,
                'fetchFundingRates': True,
                'fetchIndexOHLCV': False,
                'fetchLeverage': True,
                'fetchLeverageTiers': True,
                'fetchMarginMode': False,
                'fetchMarketLeverageTiers': True,
                'fetchMarkets': True,
                'fetchMarkOHLCV': False,
                'fetchMyTrades': True,
                'fetchOHLCV': True,
                'fetchOpenInterest': True,
                'fetchOpenInterestHistory': False,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'fetchOrderBook': True,
                'fetchOrders': True,
                'fetchOrderTrades': True,
                'fetchPosition': True,
                'fetchPositionMode': False,
                'fetchPositions': True,
                'fetchPremiumIndexOHLCV': False,
                'fetchTicker': True,
                'fetchTickers': False,
                'fetchTime': False,
                'fetchTrades': True,
                'fetchTradingFee': False,
                'fetchTradingFees': False,
                'fetchTransactionFees': None,
                'fetchWithdrawal': True,
                'fetchWithdrawals': True,
                'setLeverage': True,
                'setMarginMode': True,
                'signIn': True,
                'transfer': True,
                'withdraw': False,
            },
            'timeframes': {
                '1m': '1',
                '3m': '3',
                '5m': '5',
                '15m': '15',
                '30m': '30',
                '1h': '60',
                '2h': '120',
                '3h': '180',
                '4h': '240',
                '6h': '360',
                '12h': '720',
                '1d': '1D',
                '3d': '3D',
                '1M': '30D',
            },
            'api': {
                'public': {
                    'get': [
                        # Market data
                        'get_last_trades_by_currency',
                        'get_last_trades_by_instrument',
                        'get_order_book',
                        'tickers',
                        'get_instruments',
                        'get_tradingview_chart_data',
                        # CMC
                        'cmc_spot_summary',
                        'cmc_spot_ticker',
                        'cmc_spot_orderbook',
                        'cmc_market_trades',
                        'cmc_contracts',
                        'cmc_contract_orderbook',
                        # CoinGecko
                        'coin_gecko_spot_pairs',
                        'coin_gecko_spot_ticker',
                        'coin_gecko_spot_orderbook',
                        'coin_gecko_market_trades',
                        'coin_gecko_contracts',
                        'coin_gecko_contract_orderbook',
                        'get_perpetual_leverage_bracket',
                        'get_perpetual_leverage_bracket_all',
                    ],
                    'post': [
                        'auth',
                    ],
                },
                'private': {
                    'get': [
                        # wallet
                        'get_deposit_record',
                        'get_withdraw_record',
                        # trade
                        'get_position',
                        'get_positions',
                        'get_open_orders_by_currency',
                        'get_open_orders_by_instrument',
                        'get_order_history_by_currency',
                        'get_order_history_by_instrument',
                        'get_order_state',
                        'get_user_trades_by_currency',
                        'get_user_trades_by_instrument',
                        'get_user_trades_by_order',
                        'get_perpetual_user_config',
                    ],
                    'post': [
                        # auth
                        'logout',
                        # wallet
                        'get_assets_info',
                        'add_withdraw_address',
                        # trade
                        'buy',
                        'sell',
                        'cancel',
                        'cancel_all_by_currency',
                        'cancel_all_by_instrument',
                        'close_position',
                        'adjust_perpetual_leverage',
                        'adjust_perpetual_margin_type',
                        'submit_transfer',
                    ],
                    'delete': [],
                },
            },
            'fees': {
                'trading': {
                    'tierBased': False,
                    'percentage': True,
                    'maker': self.parse_number('0.001'),
                    'taker': self.parse_number('0.001'),
                },
                'margin': {
                    'tierBased': False,
                    'percentage': True,
                    'maker': self.parse_number('0.001'),
                    'taker': self.parse_number('0.001'),
                },
                'perpetual': {
                    'tierBased': False,
                    'percentage': True,
                    'maker': self.parse_number('0.002'),
                    'taker': self.parse_number('0.002'),
                },
            },
            'exceptions': {
                'exact': {
                    '9999': ExchangeError,  # SYSTEM_INNER_ERROR System error, please try again later
                    '9900': ExchangeNotAvailable,  # SERVICE_BUSY Service is busy，please try again later
                    '401': AuthenticationError,  # UNAUTHENTICATION_ERROR UnAuthentication
                    '403': AuthenticationError,  # ACCESS_DENIED_ERROR Access denied
                    '1000': ExchangeNotAvailable,  # NO_SERVICE No service found
                    '1001': BadRequest,  # BAD_REQUEST Bad requested
                    '2000': AuthenticationError,  # NEED_LOGIN Login is required
                    '2001': AuthenticationError,  # ACCOUNT_NOT_MATCH Account information does not match
                    '2002': AuthenticationError,  # ACCOUNT_NEED_ENABLE Account needs to be activated
                    '2003': AuthenticationError,  # ACCOUNT_NOT_AVAILABLE Account not available
                    '3000': AuthenticationError,  # TEST user
                    '3002': AuthenticationError,  # NICKNAME_EXIST Nicknames exist
                    '3003': AuthenticationError,  # ACCOUNT_NOT_EXIST No account
                    '3004': BadRequest,  # PARAM_ERROR Parameter exception
                    '3005': NotSupported,  # LANGUAGE_NONSUPPORT Unsupported languages
                    '3007': AuthenticationError,  # ONLY_SUBACCOUNT_OPE Sub-account operations only
                    '3008': AuthenticationError,  # LOGIN_ENABLE Account not logged
                    '3009': AuthenticationError,  # TFA_EXPIRE_ERROR Google key failed
                    '3011': AuthenticationError,  # PASSWORD_ERROR Password error
                    '3012': AuthenticationError,  # TFA_UUID_ERROR One-time unlock code error
                    '3013': RequestTimeout,  # TIME_OUT time out
                    '3015': AuthenticationError,  # ID_IS_ERROR id_is_error
                    '3016': AuthenticationError,  # WRONG_SUBACCOUNT_NAME already taken
                    '3018': BadRequest,  # USER_NAME_AT_LEAST_5_BYTE The user name must have at least 5 digits
                    '3019': BadRequest,  # PASSWORD_AT_LEAST_8_BYTE 8-32 bits contain at least three of the numbers, capital, lowercase letters and special symbols!
                    '3020': BadRequest,  # TFA_ALREADY_SET GoogleCode Already Set
                    '3021': BadRequest,  # PWD_MATCH_ERROR pwd_match_error
                    '3022': BadRequest,  # ILLEGAL_OPERATION illegal operation
                    '3023': BadRequest,  # REMOVE_SUBACCOUNT_OVER_LIMIT remove subaccount over limit
                    '3024': BadRequest,  # GOOGLE_VERIFICATION_CODE_TURNED_ON Google verification code turned on
                    '3025': BadRequest,  # OPERATION_FAILURE The operation failure
                    '3026': BadRequest,  # ACCOUNT_ACTIVED Account has Actived
                    '3027': BadRequest,  # INVALID_EMAIL_ADDRESS Invalid email address!
                    '3028': BadRequest,  # PASSWORD_FORMAT_ERROR Password format err
                    '3029': DDoSProtection,  # ONE_MINUTE_LIMIT Only one operation per minute and the remaining ${times}s
                    '3030': DDoSProtection,  # ONE_HOUR_LIMIT Do self up to 5 times per hour
                    '3031': BadRequest,  # USER_NAME_UP_12_BYTE Up to 12 characters, only letters and numbers are supported
                    '3032': BadRequest,  # EMAIL_SETTED You need to set email address and password first
                    '3033': BadRequest,  # PASSWORD_SETTED You need to set password first
                    '3034': AuthenticationError,  # SUBACCOUNT_EMAIL_ACTIVATE You need to wait for email confirmation
                    '3035': BadRequest,  # API_NOT_EXIST No api message
                    '3036': BadRequest,  # UNAVAILABLE_IN_SUBACCOUNT Unavailable in subaccount
                    '3037': BadRequest,  # MAX_SUBACCOUNT_NUMBER Limit of subaccounts is reached
                    '3038': BadRequest,  # MAIN_SUBACCOUNT_EMAIL_SAME Provided email address is already used for your other subaccount
                    '3039': BadRequest,  # MAX_API_KEY_NUMBER You cannot have more than 8 API keys
                    '3040': AuthenticationError,  # ALPHA_TEST Non-invited users shall contact BTCEX Team to obtain the internal tests qualification
                    '3041': BadRequest,  # API_NAME_MAX_LENGTH Name of key maximum length - 16 characters
                    '4000': BadRequest,  # WALLET_ERROR Wallet error or RECHARGE_CLOSED Recharge closed
                    '4001': InvalidAddress,  # WRONG_WITHDRAWAL_ADDRESS Wrong withdrawal address
                    '4002': InvalidAddress,  # ADDRESS_DOES_NOT_EXIST Address does not exist
                    '4003': BadRequest,  # WITHDRAWAL_CLOSED Withdrawal closed or TOO_SMALL_WITHDRAWAL_AMOUNT Too small withdrawal amount
                    '4004': NotSupported,  # INTERNAL_TRANSFER_IS_NOT_SUPPORTED_TEMPORARILY Internal transfer is not supported temporarily
                    '4005': ExchangeError,  # WITHDRAW_FAIL Withdrawal failed
                    '4006': InsufficientFunds,  # INSUFFICIENT_ASSET ser asset not enough
                    '4007': BadRequest,  # TRANSFER_ACCOUNT_ERROR Transfer account error
                    '4008': NotSupported,  # AMOUNT_ERROR Amount error
                    '4009': InvalidAddress,  # NO_RECHARGE_ADDRESS No recharge address
                    '4010': BadRequest,  # GET_TRANSFER_SUBACCOUNT_ERROR Get transfer subaccount error
                    '4011': BadRequest,  # TRANSFER_SUBMIT_URL_ERROR Transfer submit url error
                    '5001': InvalidOrder,  # ORDER_PARAM_WRONG Order's param wrong.
                    '5002': OrderNotFound,  # ORDER_DOSE_NOT_EXIST Order does not exist.
                    '5003': InvalidOrder,  # CONTRACT_DOSE_NOT_EXIST Contract does not exist.
                    '5004': InvalidOrder,  # ORDER_STATUS_ERR Order status error.
                    '5005': InvalidOrder,  # ORDER_AMOUNT_MIN_TRANCSACTION_ERR Order amount min transaction error.
                    '5006': InvalidOrder,  # ORDER_PRICE_MIN_TRANCSACTION_ERR Order price min price error.
                    '5007': InvalidOrder,  # ORDER_PRICE_TICK_SIZE_ERR Order price tick size error.
                    '5008': InvalidOrder,  # ORDER_TYPE_ERR Order type error.
                    '5009': InvalidOrder,  # ORDER_OPTION_IS_EXPIRED Order option is expired.
                    '5010': InvalidOrder,  # ORDER_IS_NOT_ACTIVE Order is not active.
                    '5011': InvalidOrder,  # IV_ORDER_ARE_NOT_SUPPORTED Iv orders are not supported.
                    '5012': InvalidOrder,  # ORDER_NO_MARK_PRICE_ERROR No mark price error.
                    '5013': InvalidOrder,  # ORDER_PRICE_RANGE_IS_TOO_HIGH order price range is too high.
                    '5014': InvalidOrder,  # ORDER_PRICE_RANGE_IS_TOO_LOW Order price range is too low.
                    '5109': InvalidOrder,  # ORDER_PRICE_RANGE_IS_TOO_LOW Order price range is too low.
                    '5135': InvalidOrder,  # The quantity should be larger than: 0.01
                    '5901': InvalidOrder,  # TRANSFER_RESULT transfer out success.
                    '5902': InvalidOrder,  # ORDER_SUCCESS place order success.
                    '5903': InvalidOrder,  # ORDER_FAIL place order fail.
                    '5904': InvalidOrder,  # PRICE_TRIGGER_LIQ price trigger liquidation
                    '5905': InvalidOrder,  # LIQ_CANCEL liquidation make order cancel.
                    '5906': InvalidOrder,  # LIQ_ORDER liquidation place a new order
                    '5907': InsufficientFunds,  # ASSET_NOT_ENOUTH asset not enough
                    '8000': BadRequest,  # PARAM_ERROR Request params not valid!
                    '8001': BadRequest,  # DATA_NOT_EXIST The data doesn't exist!
                    '8100': BadRequest,  # CODE_CHECK_FAIL Incorrect verification code
                    '8101': RequestTimeout,  # CODE_NOT_EXIST Verification code time out, please retry later
                    '8102': DDoSProtection,  # CODE_CHECK_FAIL_LIMIT Errors exceed the limit. Please try again after 24H.
                    '8103': BadRequest,  # SMS_CODE_CHECK_FAIL Incorrect SMS verification code
                    '8104': BadRequest,  # MAIL_CODE_CHECK_FAIL Incorrect mail verification code
                    '8105': BadRequest,  # GOOGLE_CODE_CHECK_FAIL 2FA Code error!
                    '8106': DDoSProtection,  # SMS_CODE_LIMIT Your message service is over limit today, please try tomorrow
                    '8107': ExchangeError,  # REQUEST_FAILED Request failed
                    '10000': AuthenticationError,  # Authentication Failure
                    '11000': BadRequest,  # CHANNEL_REGEX_ERROR channel regex not match
                },
                'broad': {
                },
            },
            'precisionMode': TICK_SIZE,
            'options': {
                'accountsByType': {
                    'wallet': 'WALLET',
                    'spot': 'SPOT',
                    'perpetual': 'PERPETUAL',
                    'margin': 'MARGIN',
                    'swap': 'PERPETUAL',
                    'BTC': 'BTC',
                    'ETH': 'ETH',
                },
                'createMarketBuyOrderRequiresPrice': True,
            },
            'commonCurrencies': {
            },
        })

    async def fetch_markets(self, params={}):
        response = await self.publicGetGetInstruments(params)
        markets = self.safe_value(response, 'result', [])
        #
        #     {
        #         "jsonrpc":"2.0",
        #         "usIn":1647533492507,
        #         "usOut":1647533492511,
        #         "usDiff":4,
        #         "result":[{
        #             "currency":"BTC",
        #             "base_currency":"USDT",
        #             "contract_size":"0.01",
        #             "creation_timestamp":"1632384961348",
        #             "expiration_timestamp":"1648195200000",
        #             "instrument_name":"BTC-25MAR22",
        #             "show_name":"BTC-25MAR22",
        #             "is_active":true,
        #             "kind":"future",
        #             "leverage":0,
        #             "maker_commission":"10",
        #             "taker_commission":"17",
        #             "min_trade_amount":"0.01",
        #             "option_type":"init",
        #             "quote_currency":"USDT",
        #             "settlement_period":"week",
        #             "strike":"0",
        #             "tick_size":"0.1",
        #             "instr_multiple":"0.01",
        #             "order_price_low_rate":"0.8",
        #             "order_price_high_rate":"1.2",
        #             "order_price_limit_type":0,
        #             "min_qty":"0.01",
        #             "min_notional":"0",
        #             "support_trace_trade":false
        #         }]
        #     }
        #
        result = []
        for i in range(0, len(markets)):
            market = markets[i]
            id = self.safe_string(market, 'instrument_name')
            type = self.safe_string(market, 'kind')
            unifiedType = type
            if type == 'perpetual':
                unifiedType = 'swap'
            baseId = self.safe_string(market, 'quote_currency')
            quoteId = self.safe_string(market, 'base_currency')
            swap = (type == 'perpetual')
            spot = (type == 'spot')
            margin = (type == 'margin')
            option = (type == 'option')
            future = (type == 'future')
            contract = swap or future or option
            expiry = None
            if option or future:
                baseId = self.safe_string(market, 'currency')
                expiry = self.safe_integer(market, 'expiration_timestamp')
            contractSize = None
            settleId = None
            settle = None
            if contract:
                settleId = quoteId
                settle = self.safe_currency_code(settleId)
            optionType = None
            strike = None
            if option:
                optionType = self.safe_string(market, 'option_type')
                strike = self.safe_number(market, 'strike')
            base = self.safe_currency_code(baseId)
            quote = self.safe_currency_code(quoteId)
            symbol = None
            if margin:
                symbol = id
            else:
                symbol = base + '/' + quote
            if contract:
                contractSize = self.safe_number(market, 'contract_size')
                symbol = symbol + ':' + settle
                if future or option:
                    symbol = symbol + '-' + self.yymmdd(expiry)
                    if option:
                        letter = 'C' if (optionType == 'call') else 'P'
                        symbol = symbol + ':' + self.number_to_string(strike) + ':' + letter
            minTradeAmount = self.safe_number(market, 'min_trade_amount')
            tickSize = self.safe_number(market, 'tick_size')
            maker = self.safe_number(market, 'maker_commission')
            taker = self.safe_number(market, 'taker_commission')
            percentage = not (option or future)
            result.append({
                'id': id,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'baseId': baseId,
                'quoteId': quoteId,
                'settleId': settleId,
                'settle': settle,
                'type': unifiedType,
                'maker': maker,
                'taker': taker,
                'percentage': percentage,
                'spot': spot,
                'margin': margin,
                'swap': swap,
                'future': future,
                'option': option,
                'active': self.safe_value(market, 'is_active'),
                'contract': contract,
                'linear': True if contract else None,
                'inverse': False if contract else None,
                'contractSize': contractSize,
                'expiry': expiry,
                'expiryDatetime': self.iso8601(expiry),
                'strike': strike,
                'optionType': optionType,
                'precision': {
                    'amount': minTradeAmount,
                    'price': tickSize,
                },
                'limits': {
                    'leverage': {
                        'min': None,
                        'max': self.safe_string(market, 'leverage'),
                    },
                    'amount': {
                        'min': minTradeAmount,
                        'max': None,
                    },
                    'price': {
                        'min': tickSize,
                        'max': None,
                    },
                    'cost': {
                        'min': None,
                        'max': None,
                    },
                },
                'info': market,
            })
        return result

    def parse_ticker(self, ticker, market=None):
        #
        #     {
        #         "best_ask_amount":"0.20962",
        #         "best_ask_price":"40491.7",
        #         "best_bid_amount":"0.08855",
        #         "best_bid_price":"40491.6",
        #         "instrument_name":"BTC-USDT",
        #         "last_price":"40493",
        #         "mark_price":"40493.10644717",
        #         "state":"open",
        #         "stats":{
        #             "high":"41468.8",
        #             "low":"40254.9",
        #             "price_change":"-0.0159",
        #             "volume":"3847.35240000000000005"
        #             "turnover":"1109811189.67100102035328746"
        #         },
        #         "timestamp":"1647569486224"
        #     }
        #
        marketId = self.safe_string(ticker, 'instrument_name')
        if marketId.find('PERPETUAL') < 0:
            marketId = marketId + '-SPOT'
        market = self.safe_market(marketId, market)
        symbol = self.safe_symbol(marketId, market, '-')
        timestamp = self.safe_integer(ticker, 'timestamp')
        stats = self.safe_value(ticker, 'stats')
        return self.safe_ticker({
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'high': self.safe_string(stats, 'high'),
            'low': self.safe_string(stats, 'low'),
            'bid': self.safe_string(ticker, 'best_bid_price'),
            'bidVolume': self.safe_string(ticker, 'best_bid_amount'),
            'ask': self.safe_string(ticker, 'best_ask_price'),
            'askVolume': self.safe_string(ticker, 'best_ask_amount'),
            'vwap': None,
            'open': None,
            'close': self.safe_string(ticker, 'last_price'),
            'last': self.safe_string(ticker, 'last_price'),
            'previousClose': None,
            'change': None,
            'percentage': self.safe_string(stats, 'price_change'),
            'average': None,
            'baseVolume': self.safe_string(stats, 'volume'),
            'quoteVolume': self.safe_string(stats, 'turnover'),
            'info': ticker,
        }, market)

    async def fetch_ticker(self, symbol, params={}):
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'instrument_name': market['id'],
        }
        response = await self.publicGetTickers(self.extend(request, params))
        result = self.safe_value(response, 'result', {})
        #
        #     {
        #         "jsonrpc":"2.0",
        #         "usIn":1647569487238,
        #         "usOut":1647569487240,
        #         "usDiff":2,
        #         "result":[{
        #             "best_ask_amount":"0.20962",
        #             "best_ask_price":"40491.7",
        #             "best_bid_amount":"0.08855",
        #             "best_bid_price":"40491.6",
        #             "instrument_name":"BTC-USDT",
        #             "last_price":"40493",
        #             "mark_price":"40493.10644717",
        #             "state":"open",
        #             "stats":{
        #                 "high":"41468.8",
        #                 "low":"40254.9",
        #                 "price_change":"-0.0159",
        #                 "volume":"3847.35240000000000005"
        #             },
        #             "timestamp":"1647569486224"
        #         }]
        #     }
        #
        ticker = self.safe_value(result, 0)
        return self.parse_ticker(ticker, market)

    async def fetch_order_book(self, symbol, limit=None, params={}):
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'instrument_name': market['id'],
        }
        if limit is not None:
            request['depth'] = limit
        response = await self.publicGetGetOrderBook(self.extend(request, params))
        result = self.safe_value(response, 'result', {})
        #
        #     {
        #         "jsonrpc":"2.0",
        #         "usIn":1647573916524,
        #         "usOut":1647573916526,
        #         "usDiff":2,
        #         "result":{
        #             "asks":[["10155.00000","0.200","186.980","0.000"],["10663.00000","0.200","217.480","0.000"]],
        #             "bids":[["7896.00000","0.200","1.000","0.000"],["7481.00000","0.200","1.000","0.000"]],
        #             "timestamp":"1647573916525",
        #             "instrument_name":"BTC-25MAR22-32000-C",
        #             "version":1002541
        #         }
        #     }
        #
        timestamp = self.safe_integer(result, 'timestamp')
        orderBook = self.parse_order_book(result, market['symbol'], timestamp)
        orderBook['nonce'] = self.safe_integer(result, 'version')
        return orderBook

    def parse_ohlcv(self, ohlcv, market=None):
        #
        #     {
        #         "tick":1647547200,
        #         "open":"40868.16800000",
        #         "high":"40877.65600000",
        #         "low":"40647.00000000",
        #         "close":"40699.10000000",
        #         "volume":"100.27789000",
        #         "cost":"4083185.78337596"
        #     }
        #
        return [
            self.safe_timestamp(ohlcv, 'tick'),
            self.safe_number(ohlcv, 'open'),
            self.safe_number(ohlcv, 'high'),
            self.safe_number(ohlcv, 'low'),
            self.safe_number(ohlcv, 'close'),
            self.safe_number(ohlcv, 'volume'),
        ]

    async def fetch_ohlcv(self, symbol, timeframe='1m', since=None, limit=None, params={}):
        await self.load_markets()
        market = self.market(symbol)
        if limit is None:
            limit = 10
        request = {
            'resolution': self.safe_string(self.timeframes, timeframe, timeframe),
            # 'start_timestamp': 0,
            # 'end_timestamp': 0,
        }
        marketId = market['id']
        if market['spot'] or market['margin']:
            marketId = market['baseId'] + '-' + market['quoteId']
        request['instrument_name'] = marketId
        if since is None:
            request['end_timestamp'] = self.milliseconds()
            request['start_timestamp'] = 0
        else:
            timeframeInSeconds = self.parse_timeframe(timeframe)
            timeframeInMilliseconds = timeframeInSeconds * 1000
            request['start_timestamp'] = since
            request['end_timestamp'] = self.sum(request['start_timestamp'], limit * timeframeInMilliseconds)
        response = await self.publicGetGetTradingviewChartData(self.extend(request, params))
        result = self.safe_value(response, 'result', [])
        #
        #     {
        #         "jsonrpc":"2.0",
        #         "usIn":1647578562427,
        #         "usOut":1647578562428,
        #         "usDiff":1,
        #         "result":[{
        #             "tick":1647547200,
        #             "open":"40868.16800000",
        #             "high":"40877.65600000",
        #             "low":"40647.00000000",
        #             "close":"40699.10000000",
        #             "volume":"100.27789000",
        #             "cost":"4083185.78337596"
        #         }]
        #     }
        #
        return self.parse_ohlcvs(result, market, timeframe, since, limit)

    def parse_trade(self, trade, market=None):
        #
        # fetchTrades(public)
        #
        #     {
        #         "amount":"0.0003",
        #         "direction":"sell",
        #         "iv":"0",
        #         "price":"40767.18",
        #         "timestamp":"1647582687050",
        #         "instrument_name":"BTC-USDT-SPOT",
        #         "trade_id":57499240
        #     }
        #
        # fetchOrderTrades or fetchMyTrades
        #
        #     {
        #         "direction":"sell",
        #         "amount":"0.03",
        #         "price":"397.8",
        #         "fee":"0.011934",
        #         "timestamp":1647668570759,
        #         "role":"taker",
        #         "trade_id":"58319385",
        #         "order_id":"250979478947823616",
        #         "instrument_name":"BNB-USDT-SPOT",
        #         "order_type":"market",
        #         "fee_use_coupon":false,
        #         "fee_coin_type":"USDT",
        #         "index_price":"",
        #         "self_trade":false
        #     }
        #
        id = self.safe_string(trade, 'trade_id')
        marketId = self.safe_string(trade, 'instrument_name')
        symbol = self.safe_symbol(marketId, market)
        timestamp = self.safe_integer(trade, 'timestamp')
        side = self.safe_string(trade, 'direction')
        priceString = self.safe_string(trade, 'price')
        amountString = self.safe_string(trade, 'amount')
        takerOrMaker = self.safe_string(trade, 'role')
        feeCostString = self.safe_string(trade, 'fee')
        fee = None
        if feeCostString is not None:
            feeCurrencyId = self.safe_string(trade, 'fee_coin_type')
            feeCurrencyCode = self.safe_currency_code(feeCurrencyId)
            fee = {
                'cost': feeCostString,
                'currency': feeCurrencyCode,
            }
        return self.safe_trade({
            'info': trade,
            'id': id,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': symbol,
            'order': self.safe_string(trade, 'order_id'),
            'type': self.safe_string(trade, 'order_type'),
            'side': side,
            'takerOrMaker': takerOrMaker,
            'price': priceString,
            'amount': amountString,
            'cost': None,
            'fee': fee,
        }, market)

    async def fetch_trades(self, symbol, since=None, limit=None, params={}):
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'instrument_name': market['id'],
            # 'start_id' : 0,
            # 'end_id': 0,
            # 'sorting': 'asc',  # asc | desc
        }
        if limit is not None:
            request['count'] = limit  # default 10
        response = await self.publicGetGetLastTradesByInstrument(self.extend(request, params))
        result = self.safe_value(response, 'result', {})
        #
        #     {
        #         "jsonrpc":"2.0",
        #         "usIn":1647582703220,
        #         "usOut":1647582703253,
        #         "usDiff":33,
        #         "result":{
        #             "trades":[{
        #                 "amount":"0.0003",
        #                 "direction":"sell",
        #                 "iv":"0",
        #                 "price":"40767.18",
        #                 "timestamp":"1647582687050",
        #                 "instrument_name":"BTC-USDT-SPOT",
        #                 "trade_id":57499240
        #             }],
        #             "has_more":true
        #         }
        #     }
        #
        trades = self.safe_value(result, 'trades', [])
        return self.parse_trades(trades, market, since, limit)

    async def sign_in(self, params={}):
        accessToken = self.safe_string(self.options, 'accessToken')
        if accessToken is not None:
            return accessToken
        self.check_required_credentials()
        request = {
            'grant_type': 'client_credentials',  # client_signature or refresh_token
            'client_id': self.apiKey,
            'client_secret': self.secret,
            # 'refresh_token': '',  # Required for grant type refresh_token
            # 'signature': '',  # Required for grant type client_signature
        }
        response = await self.publicPostAuth(self.extend(request, params))
        result = self.safe_value(response, 'result')
        #
        #     {
        #         jsonrpc: '2.0',
        #         usIn: '1647601525586',
        #         usOut: '1647601525597',
        #         usDiff: '11',
        #         result: {
        #         access_token: '',
        #         token_type: 'bearer',
        #         refresh_token: '',
        #         expires_in: '604799',
        #         scope: 'account:read_write block_trade:read_write trade:read_write wallet:read_write'
        #         }
        #     }
        #
        accessToken = self.safe_string(result, 'access_token')
        self.options['accessToken'] = accessToken
        return accessToken

    def parse_balance(self, response):
        #
        #     {
        #         "WALLET":{
        #             "total":"0",
        #             "coupon":"0",
        #             "details":[{
        #                 "available":"0",
        #                 "freeze":"0",
        #                 "coin_type":"1INCH",
        #                 "current_mark_price":"1.657"
        #             }]
        #         },
        #         "MARGIN":{
        #             "total":"0",
        #             "net":"0",
        #             "available":"0",
        #             "borrowed":"0",
        #             "details":[],
        #             "maintenance_margin":"0",
        #             "interest_owed":"0"
        #         },
        #         "SPOT":{
        #             "total":"3.965",
        #             "available":"15.887066",
        #             "details":[{
        #                 "available":"0",
        #                 "freeze":"0",
        #                 "total":"0",
        #                 "coin_type":"1INCH",
        #                 "current_mark_price":"1.657"
        #             }]
        #         },
        #         "BTC":{
        #             "currency":"BTC",
        #             "balance":"0",
        #             "freeze":"0",
        #             "equity":"0",
        #             "base_currency":"USDT",
        #             "available_funds":"0",
        #             "available_withdrawal_funds":"0",
        #             "initial_margin":"0",
        #             "maintenance_margin":"0",
        #             "margin_balance":"0",
        #             "session_funding":"0",
        #             "session_rpl":"0",
        #             "session_upl":"0",
        #             "futures_pl":"0",
        #             "futures_session_rpl":"0",
        #             "futures_session_upl":"0",
        #             "options_value":"0",
        #             "options_pl":"0",
        #             "options_session_rpl":"0",
        #             "options_session_upl":"0",
        #             "total_pl":"0",
        #             "options_delta":"0",
        #             "options_gamma":"0",
        #             "options_theta":"0",
        #             "options_vega":"0",
        #             "delta_total":"0"
        #         },
        #         "ETH":{
        #             "currency":"ETH",
        #             "balance":"0",
        #             "freeze":"0",
        #             "equity":"0",
        #             "base_currency":"USDT",
        #             "available_funds":"0",
        #             "available_withdrawal_funds":"0",
        #             "initial_margin":"0",
        #             "maintenance_margin":"0",
        #             "margin_balance":"0",
        #             "session_funding":"0",
        #             "session_rpl":"0",
        #             "session_upl":"0",
        #             "futures_pl":"0",
        #             "futures_session_rpl":"0",
        #             "futures_session_upl":"0",
        #             "options_value":"0",
        #             "options_pl":"0",
        #             "options_session_rpl":"0",
        #             "options_session_upl":"0",
        #             "total_pl":"0",
        #             "options_delta":"0",
        #             "options_gamma":"0",
        #             "options_theta":"0",
        #             "options_vega":"0",
        #             "delta_total":"0"
        #         },
        #         "PERPETUAL":{
        #             "bonus":"0",
        #             "global_state":0,
        #             "available_funds":"0",
        #             "wallet_balance":"0",
        #             "available_withdraw_funds":"0",
        #             "total_pl":"0",
        #             "total_upl":"0",
        #             "position_rpl":"0",
        #             "total_upl_isolated":"0",
        #             "total_upl_cross":"0",
        #             "total_initial_margin_cross":"0",
        #             "total_initial_margin_isolated":"0",
        #             "total_margin_balance_isolated":"0",
        #             "total_margin_balance":"0",
        #             "total_margin_balance_cross":"0",
        #             "total_maintenance_margin_cross":"0",
        #             "total_wallet_balance_isolated":"0",
        #             "order_frozen":"0",
        #             "order_cross_frozen":"0",
        #             "order_isolated_frozen":"0",
        #             "risk_level":"0",
        #             "bonus_max":"0"
        #         }
        #     }
        #
        result = {'info': response}
        assetTypes = list(response.keys())
        for i in range(0, len(assetTypes)):
            assetType = assetTypes[i]
            currency = self.safe_value(response, assetType)
            if (assetType == 'WALLET') or (assetType == 'SPOT'):
                details = self.safe_value(currency, 'details')
                if details is not None:
                    for i in range(0, len(details)):
                        detail = details[i]
                        coinType = self.safe_string(detail, 'coin_type')
                        code = self.safe_currency_code(coinType)
                        account = self.safe_value(result, code, self.account())
                        account['free'] = self.safe_string(detail, 'available')
                        account['used'] = self.safe_string(detail, 'freeze')
                        account['total'] = self.safe_string(detail, 'total')
                        result[code] = account
            else:
                # all other wallets are linear futures
                code = 'USDT'
                account = self.account()
                account['total'] = self.safe_string(currency, 'wallet_balance')
                account['free'] = self.safe_string(currency, 'available_withdraw_funds')
                result[code] = account
        return self.safe_balance(result)

    async def fetch_balance(self, params={}):
        await self.sign_in()
        await self.load_markets()
        type = self.safe_string_lower(params, 'type', 'spot')
        types = self.safe_value(self.options, 'accountsByType', {})
        assetType = self.safe_string(types, type, type)
        params = self.omit(params, 'type')
        request = {
            'asset_type': [assetType],
        }
        response = await self.privatePostGetAssetsInfo(self.extend(request, params))
        result = self.safe_value(response, 'result', [])
        #
        #     {
        #         "id":"1647675393",
        #         "jsonrpc":"2.0",
        #         "usIn":1647675394091,
        #         "usOut":1647675394104,
        #         "usDiff":13,
        #         "result":{
        #             "WALLET":{
        #                 "total":"0",
        #                 "coupon":"0",
        #                 "details":[{
        #                     "available":"0",
        #                     "freeze":"0",
        #                     "coin_type":"1INCH",
        #                     "current_mark_price":"1.657"
        #                 }]
        #             },
        #             "MARGIN":{
        #                 "total":"0",
        #                 "net":"0",
        #                 "available":"0",
        #                 "borrowed":"0",
        #                 "details":[],
        #                 "maintenance_margin":"0",
        #                 "interest_owed":"0"
        #             },
        #             "SPOT":{
        #                 "total":"3.965",
        #                 "available":"15.887066",
        #                 "details":[{
        #                     "available":"0",
        #                     "freeze":"0",
        #                     "total":"0",
        #                     "coin_type":"1INCH",
        #                     "current_mark_price":"1.657"
        #                 }]
        #             },
        #             "BTC":{
        #                 "currency":"BTC",
        #                 "balance":"0",
        #                 "freeze":"0",
        #                 "equity":"0",
        #                 "base_currency":"USDT",
        #                 "available_funds":"0",
        #                 "available_withdrawal_funds":"0",
        #                 "initial_margin":"0",
        #                 "maintenance_margin":"0",
        #                 "margin_balance":"0",
        #                 "session_funding":"0",
        #                 "session_rpl":"0",
        #                 "session_upl":"0",
        #                 "futures_pl":"0",
        #                 "futures_session_rpl":"0",
        #                 "futures_session_upl":"0",
        #                 "options_value":"0",
        #                 "options_pl":"0",
        #                 "options_session_rpl":"0",
        #                 "options_session_upl":"0",
        #                 "total_pl":"0",
        #                 "options_delta":"0",
        #                 "options_gamma":"0",
        #                 "options_theta":"0",
        #                 "options_vega":"0",
        #                 "delta_total":"0"
        #             },
        #             "ETH":{
        #                 "currency":"ETH",
        #                 "balance":"0",
        #                 "freeze":"0",
        #                 "equity":"0",
        #                 "base_currency":"USDT",
        #                 "available_funds":"0",
        #                 "available_withdrawal_funds":"0",
        #                 "initial_margin":"0",
        #                 "maintenance_margin":"0",
        #                 "margin_balance":"0",
        #                 "session_funding":"0",
        #                 "session_rpl":"0",
        #                 "session_upl":"0",
        #                 "futures_pl":"0",
        #                 "futures_session_rpl":"0",
        #                 "futures_session_upl":"0",
        #                 "options_value":"0",
        #                 "options_pl":"0",
        #                 "options_session_rpl":"0",
        #                 "options_session_upl":"0",
        #                 "total_pl":"0",
        #                 "options_delta":"0",
        #                 "options_gamma":"0",
        #                 "options_theta":"0",
        #                 "options_vega":"0",
        #                 "delta_total":"0"
        #             },
        #             "PERPETUAL":{
        #                 "bonus":"0",
        #                 "global_state":0,
        #                 "available_funds":"0",
        #                 "wallet_balance":"0",
        #                 "available_withdraw_funds":"0",
        #                 "total_pl":"0",
        #                 "total_upl":"0",
        #                 "position_rpl":"0",
        #                 "total_upl_isolated":"0",
        #                 "total_upl_cross":"0",
        #                 "total_initial_margin_cross":"0",
        #                 "total_initial_margin_isolated":"0",
        #                 "total_margin_balance_isolated":"0",
        #                 "total_margin_balance":"0",
        #                 "total_margin_balance_cross":"0",
        #                 "total_maintenance_margin_cross":"0",
        #                 "total_wallet_balance_isolated":"0",
        #                 "order_frozen":"0",
        #                 "order_cross_frozen":"0",
        #                 "order_isolated_frozen":"0",
        #                 "risk_level":"0",
        #                 "bonus_max":"0"
        #             }
        #         }
        #     }
        #
        return self.parse_balance(result)

    def parse_order_status(self, status):
        statuses = {
            'open': 'open',
            'cancelled': 'canceled',
            'filled': 'closed',
            'rejected': 'rejected',
        }
        return self.safe_string(statuses, status, status)

    def parse_time_in_force(self, timeInForce):
        if timeInForce == '-':
            return None
        timeInForces = {
            'good_til_cancelled': 'GTC',
            'good_til_date': 'GTD',
            'fill_or_kill': 'FOK',
            'immediate_or_cancel': 'IOC',
        }
        return self.safe_string(timeInForces, timeInForce, timeInForce)

    def parse_order(self, order, market=None):
        #
        # fetchOrder or fetchOpenOrders or fetchClosedOrders
        #         {
        #             "kind":"spot",
        #             "direction":"sell",
        #             "amount":"0.02",
        #             "price":"900",
        #             "advanced":"usdt",
        #             "source":"api",
        #             "mmp":false,
        #             "version":1,
        #             "order_id":"250971492850401280",
        #             "order_state":"open",
        #             "instrument_name":"BNB-USDT-SPOT",
        #             "filled_amount":"0",
        #             "average_price":"0",
        #             "order_type":"limit",
        #             "time_in_force":"GTC",
        #             "post_only":false,
        #             "reduce_only":false,
        #             "creation_timestamp":1647666666723,
        #             "last_update_timestamp":1647666666725
        #         }
        #
        # createOrder
        #
        #         {
        #             "order_id":"251052889774161920",
        #             "custom_order_id":"-"
        #         }
        #
        # closeOrder
        #         {
        #             "order_id":"250979354159153152"
        #         }
        #
        timestamp = self.safe_integer(order, 'creation_timestamp')
        lastUpdate = self.safe_integer(order, 'last_update_timestamp')
        id = self.safe_string(order, 'order_id')
        priceString = self.safe_string(order, 'price')
        if priceString == '-1':
            priceString = None
        averageString = self.safe_string(order, 'average_price')
        amountString = self.safe_string(order, 'amount')
        filledString = self.safe_string(order, 'filled_amount')
        status = self.parse_order_status(self.safe_string(order, 'order_state'))
        marketId = self.safe_string(order, 'instrument_name')
        market = self.safe_market(marketId, market)
        side = self.safe_string_lower(order, 'direction')
        feeCostString = self.safe_string(order, 'commission')
        fee = None
        if feeCostString is not None:
            feeCostString = Precise.string_abs(feeCostString)
            fee = {
                'cost': feeCostString,
                'currency': market['base'],
            }
        # injected in createOrder
        trades = self.safe_value(order, 'trades')
        stopPrice = self.safe_number(order, 'trigger_price')
        return self.safe_order({
            'info': order,
            'id': id,
            'clientOrderId': None,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': lastUpdate,
            'symbol': market['symbol'],
            'type': self.safe_string(order, 'order_type'),
            'timeInForce': self.parse_time_in_force(self.safe_string(order, 'time_in_force')),
            'postOnly': self.safe_value(order, 'post_only'),
            'side': side,
            'price': self.parse_number(priceString),
            'stopPrice': stopPrice,
            'triggerPrice': stopPrice,
            'stopLossPrice': self.safe_number(order, 'stop_loss_price'),
            'takeProfitPrice': self.safe_number(order, 'take_profit_price'),
            'amount': amountString,
            'cost': None,
            'average': averageString,
            'filled': filledString,
            'remaining': None,
            'status': status,
            'fee': fee,
            'trades': trades,
        }, market)

    async def fetch_order(self, id, symbol=None, params={}):
        await self.sign_in()
        await self.load_markets()
        request = {
            'order_id': id,
        }
        response = await self.privateGetGetOrderState(self.extend(request, params))
        result = self.safe_value(response, 'result')
        #
        #     {
        #         "jsonrpc":"2.0",
        #         "usIn":1647672034018,
        #         "usOut":1647672034033,
        #         "usDiff":15,
        #         "result":{
        #             "currency":"SPOT",
        #             "kind":"spot",
        #             "direction":"sell",
        #             "amount":"0.03",
        #             "price":"-1",
        #             "advanced":"usdt",
        #             "source":"api",
        #             "mmp":false,
        #             "version":1,
        #             "order_id":"250979478947823616",
        #             "order_state":"filled",
        #             "instrument_name":"BNB-USDT-SPOT",
        #             "filled_amount":"0.03",
        #             "average_price":"397.8",
        #             "order_type":"market",
        #             "time_in_force":"GTC",
        #             "post_only":false,
        #             "reduce_only":false,
        #             "creation_timestamp":1647668570759,
        #             "last_update_timestamp":1647668570761
        #         }
        #     }
        #
        return self.parse_order(result)

    async 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 the 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 btcex api endpoint
         * ----------------- Exchange Specific Parameters -----------------
        :param float|None params['cost']: amount in USDT to spend for market orders
        :param float|None params['triggerPrice']: price to trigger stop orders
        :param float|None params['stopPrice']: price to trigger stop orders
        :param float|None params['stopLossPrice']: price to trigger stop-loss orders(only for perpetuals)
        :param float|None params['takeProfitPrice']: price to trigger take-profit orders(only for perpetuals)
        :param dict|None params['stopLoss']: for setting a stop-loss attached to an order, set the value of the stopLoss key 'price'(only for perpetuals)
        :param dict|None params['takeProfit']: for setting a take-profit attached to an order, set the value of the takeProfit key 'price'(only for perpetuals)
        :param int|None params['trigger_price_type']: 1: mark-price, 2: last-price.(only for perpetuals)
        :param int|None params['stop_loss_type']: 1: mark-price, 2: last-price(only for perpetuals)
        :param int|None params['take_profit_type']: 1: mark-price, 2: last-price(only for perpetuals)
        :param bool|None params['market_amount_order']: if set to True，then the amount field means USDT value(only for perpetuals)
        :param str|None params['condition_type']: 'NORMAL', 'STOP', 'TRAILING', 'IF_TOUCHED'
        :param str|None params['position_side']: 'BOTH', for one-way mode 'LONG' or 'SHORT', for hedge-mode
        :param str|None params['timeInForce']: 'GTC', 'IOC', 'FOK'
        :param bool|None params.postOnly:
        :param bool|None params.reduceOnly:
        :returns dict: an `order structure <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        await self.sign_in()
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'instrument_name': market['id'],
            'type': type,
        }
        if side == 'sell':
            request['amount'] = self.amount_to_precision(symbol, amount)
        if type == 'limit':
            request['price'] = self.price_to_precision(symbol, price)
        else:
            costParam = self.safe_number(params, 'cost')
            amountString = self.number_to_string(amount)
            priceString = self.number_to_string(price)
            cost = self.parse_number(Precise.string_mul(amountString, priceString), costParam)
            if market['swap']:
                if cost is not None:
                    request['amount'] = self.price_to_precision(symbol, cost)
                    request['market_amount_order'] = True
                else:
                    request['market_amount_order'] = False
                    request['amount'] = self.amount_to_precision(symbol, amount)
            else:
                if side == 'buy':
                    createMarketBuyOrderRequiresPrice = self.safe_value(self.options, 'createMarketBuyOrderRequiresPrice', True)
                    if createMarketBuyOrderRequiresPrice:
                        if cost is None:
                            raise InvalidOrder(self.id + ' createOrder() requires a price argument for market buy orders on spot markets to calculate the total amount to spend(amount * price), alternatively set the createMarketBuyOrderRequiresPrice option to False and pass in the cost to spend into the amount parameter')
                        else:
                            request['amount'] = self.price_to_precision(symbol, cost)
                    else:
                        request['amount'] = self.price_to_precision(symbol, amount)
            params = self.omit(params, 'cost')
        if market['swap']:
            timeInForce = self.safe_string_upper(params, 'timeInForce')
            if timeInForce == 'GTC':
                request['time_in_force'] = 'good_till_cancelled'
            elif timeInForce == 'FOK':
                request['time_in_force'] = 'fill_or_kill'
            elif timeInForce == 'IOC':
                request['time_in_force'] = 'immediate_or_cancel'
            isMarketOrder = type == 'market'
            exchangeSpecificParam = self.safe_value(params, 'post_only', False)
            postOnly = self.is_post_only(isMarketOrder, exchangeSpecificParam, params)
            if postOnly:
                request['post_only'] = True
            reduceOnly = self.safe_value(params, 'reduceOnly', False)
            if reduceOnly:
                request['reduce_only'] = True
            if side == 'buy':
                requestType = 'SHORT' if (reduceOnly) else 'LONG'
                request['position_side'] = requestType
            else:
                requestType = 'LONG' if (reduceOnly) else 'SHORT'
                request['position_side'] = requestType
            stopPrice = self.safe_number_2(params, 'triggerPrice', 'stopPrice')
            stopLossPrice = self.safe_number(params, 'stopLossPrice')
            takeProfitPrice = self.safe_number(params, 'takeProfitPrice')
            isStopLoss = self.safe_value(params, 'stopLoss')
            isTakeProfit = self.safe_value(params, 'takeProfit')
            if stopPrice:
                request['condition_type'] = 'STOP'
                request['trigger_price'] = self.price_to_precision(symbol, stopPrice)
                request['trigger_price_type'] = 1
            elif stopLossPrice or takeProfitPrice:
                request['condition_type'] = 'STOP'
                if stopLossPrice:
                    request['trigger_price'] = self.price_to_precision(symbol, stopLossPrice)
                else:
                    request['trigger_price'] = self.price_to_precision(symbol, takeProfitPrice)
                request['reduce_only'] = True
                request['trigger_price_type'] = 1
            elif isStopLoss or isTakeProfit:
                if isStopLoss:
                    stopLossPrice = self.safe_number(isStopLoss, 'price')
                    request['stop_loss_price'] = self.price_to_precision(symbol, stopLossPrice)
                    request['stop_loss_type'] = 1
                else:
                    takeProfitPrice = self.safe_number(isTakeProfit, 'price')
                    request['take_profit_price'] = self.price_to_precision(symbol, takeProfitPrice)
                    request['take_profit_type'] = 1
            params = self.omit(params, ['timeInForce', 'postOnly', 'reduceOnly', 'stopPrice', 'triggerPrice', 'stopLossPrice', 'takeProfitPrice', 'stopLoss', 'takeProfit'])
        method = 'privatePost' + self.capitalize(side)
        response = await getattr(self, method)(self.extend(request, params))
        result = self.safe_value(response, 'result', {})
        #
        #     {
        #         "id":"1647686073",
        #         "jsonrpc":"2.0",
        #         "usIn":1647686073252,
        #         "usOut":1647686073264,
        #         "usDiff":12,
        #         "result":{
        #             "order":{
        #                 "order_id":"251052889774161920",
        #                 "custom_order_id":"-"
        #             }
        #         }
        #     }
        #
        order = self.safe_value(result, 'order')
        return self.parse_order(order, market)

    async def cancel_order(self, id, symbol=None, params={}):
        await self.sign_in()
        await self.load_markets()
        request = {
            'order_id': id,
        }
        response = await self.privatePostCancel(self.extend(request, params))
        result = self.safe_value(response, 'result', {})
        #
        #     {
        #         "id":"1647675007",
        #         "jsonrpc":"2.0",
        #         "usIn":1647675007485,
        #         "usOut":1647675007494,
        #         "usDiff":9,
        #         "result":{
        #             "order_id":"250979354159153152"
        #         }
        #     }
        #
        return self.parse_order(result)

    async def cancel_all_orders(self, symbol=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelAllOrders() requires a symbol argument')
        await self.sign_in()
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'instrument_name': market['id'],
        }
        response = await self.privatePostCancelAllByInstrument(self.extend(request, params))
        #
        #     {
        #         "id":"1647686580",
        #         "jsonrpc":"2.0",
        #         "usIn":1647686581216,
        #         "usOut":1647686581224,
        #         "usDiff":8,
        #         "result":2
        #     }
        #
        return response

    async def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOpenOrders() requires a symbol argument')
        await self.sign_in()
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'instrument_name': market['id'],
        }
        response = await self.privateGetGetOpenOrdersByInstrument(self.extend(request, params))
        result = self.safe_value(response, 'result', [])
        #
        #     {
        #         "jsonrpc":"2.0",
        #         "usIn":1647667026285,
        #         "usOut":1647667026291,
        #         "usDiff":6,
        #         "result":[{
        #             "kind":"spot",
        #             "direction":"sell",
        #             "amount":"0.02",
        #             "price":"900",
        #             "advanced":"usdt",
        #             "source":"api",
        #             "mmp":false,
        #             "version":1,
        #             "order_id":"250971492850401280",
        #             "order_state":"open",
        #             "instrument_name":"BNB-USDT-SPOT",
        #             "filled_amount":"0",
        #             "average_price":"0",
        #             "order_type":"limit",
        #             "time_in_force":"GTC",
        #             "post_only":false,
        #             "reduce_only":false,
        #             "creation_timestamp":1647666666723,
        #             "last_update_timestamp":1647666666725
        #         }]
        #     }
        #
        return self.parse_orders(result, market, since, limit)

    async def fetch_closed_orders(self, symbol=None, since=None, limit=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchClosedOrders() requires a symbol argument')
        await self.sign_in()
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'instrument_name': market['id'],
        }
        if limit is not None:
            request['count'] = limit
        response = await self.privateGetGetOrderHistoryByInstrument(self.extend(request, params))
        result = self.safe_value(response, 'result', [])
        #
        #     {
        #         "jsonrpc":"2.0",
        #         "usIn":1647671721716,
        #         "usOut":1647671721730,
        #         "usDiff":14,
        #         "result":[{
        #             "currency":"SPOT",
        #             "kind":"spot",
        #             "direction":"sell",
        #             "amount":"0.03",
        #             "price":"-1",
        #             "advanced":"usdt",
        #             "source":"api",
        #             "mmp":false,
        #             "version":1,
        #             "order_id":"250979478947823616",
        #             "order_state":"filled",
        #             "instrument_name":"BNB-USDT-SPOT",
        #             "filled_amount":"0.03",
        #             "average_price":"397.8",
        #             "order_type":"market",
        #             "time_in_force":"GTC",
        #             "post_only":false,
        #             "reduce_only":false,
        #             "creation_timestamp":1647668570759,
        #             "last_update_timestamp":1647668570761
        #         }]
        #     }
        #
        return self.parse_orders(result, market, since, limit)

    async def fetch_order_trades(self, id, symbol=None, since=None, limit=None, params={}):
        if id is None:
            raise ArgumentsRequired(self.id + ' fetchOrderTrades() requires a id argument')
        await self.load_markets()
        request = {
            'order_id': id,
            # 'start_id': 0,  # The ID number of the first trade to be returned
            # 'end_id': 0,  # The ID number of the last trade to be returned
            # 'sorting': '',  # Direction of results sorting,default: desc
        }
        if limit is not None:
            request['count'] = limit  # default 20
        response = await self.privateGetGetUserTradesByOrder(self.extend(request, params))
        result = self.safe_value(response, 'result', {})
        #
        #     {
        #         "jsonrpc":"2.0",
        #         "usIn":1647671425457,
        #         "usOut":1647671425470,
        #         "usDiff":13,
        #         "result":{
        #             "count":1,
        #             "trades":[{
        #                 "direction":"sell",
        #                 "amount":"0.03",
        #                 "price":"397.8",
        #                 "fee":"0.011934",
        #                 "timestamp":1647668570759,
        #                 "role":"taker",
        #                 "trade_id":"58319385",
        #                 "order_id":"250979478947823616",
        #                 "instrument_name":"BNB-USDT-SPOT",
        #                 "order_type":"market",
        #                 "fee_use_coupon":false,
        #                 "fee_coin_type":"USDT",
        #                 "index_price":"",
        #                 "self_trade":false
        #             }],
        #             "has_more":false
        #         }
        #     }
        #
        trades = self.safe_value(result, 'trades', [])
        return self.parse_trades(trades, None, since, limit)

    async def fetch_my_trades(self, symbol=None, since=None, limit=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchMyTrades() requires a id argument')
        await self.sign_in()
        await self.load_markets()
        request = {
            # 'kind': '',  # The order kind, eg. margin, spot, option, future, perpetual. only used when call privateGetGetUserTradesByCurrency
            # 'start_id': 0,  # The ID number of the first trade to be returned
            # 'end_id': 0,  # The ID number of the last trade to be returned
            # 'sorting': '',  # Direction of results sorting,default: desc
            # 'self_trade': False,  # If not set, query all
            # 'start_timestamp': False  # The trade time of the first trade to be returned
            # 'end_timestamp': False  # The trade time of the last trade to be returned
        }
        market = self.market(symbol)
        request['instrument_name'] = market['id']
        if limit is not None:
            request['count'] = limit  # default 20
        if since is not None:
            request['start_timestamp'] = since
        response = await self.privateGetGetUserTradesByInstrument(self.extend(request, params))
        result = self.safe_value(response, 'result', {})
        #
        #     {
        #         "jsonrpc":"2.0",
        #         "usIn":1647668582167,
        #         "usOut":1647668582187,
        #         "usDiff":20,
        #         "result":{
        #             "count":1,
        #             "trades":[{
        #                 "direction":"sell",
        #                 "amount":"0.03",
        #                 "price":"397.8",
        #                 "fee":"0.011934",
        #                 "timestamp":1647668570759,
        #                 "role":"taker",
        #                 "trade_id":"58319385",
        #                 "order_id":"250979478947823616",
        #                 "instrument_name":"BNB-USDT-SPOT",
        #                 "order_type":"market",
        #                 "fee_use_coupon":false,
        #                 "fee_coin_type":"USDT",
        #                 "index_price":"",
        #                 "self_trade":false
        #             }],
        #             "has_more":false
        #         }
        #     }
        #
        trades = self.safe_value(result, 'trades', [])
        return self.parse_trades(trades, market, since, limit)

    def parse_position(self, position, market=None):
        #
        #     {
        #         "currency":"PERPETUAL",
        #         "kind":"perpetual",
        #         "size":"-0.08",
        #         "direction":"sell",
        #         "leverage":"3",
        #         "margin":"10.7724",
        #         "version":"553",
        #         "roe":"-0.000483",
        #         "traceType":0,
        #         "pos_id":"0",
        #         "instrument_name":"BNB-USDT-PERPETUAL",
        #         "average_price":"403.9",
        #         "mark_price":"403.965",
        #         "initial_margin":"10.77066668",
        #         "maintenance_margin":"0.2100618",
        #         "floating_profit_loss":"-0.0052",
        #         "liquid_price":"549.15437158",
        #         "margin_type":"cross",
        #         "risk_level":"0.017651",
        #         "available_withdraw_funds":"1.13004332",
        #         "order_id":"251085320510201856",
        #         "stop_loss_price":"0",
        #         "stop_loss_type":1,
        #         "take_profit_price":"0",
        #         "take_profit_type":1
        #     }
        #
        contract = self.safe_string(position, 'instrument_name')
        market = self.safe_market(contract, market)
        size = self.safe_string(position, 'size')
        side = self.safe_string(position, 'direction')
        side = 'long' if (side == 'buy') else 'short'
        maintenanceMarginString = self.safe_string(position, 'maintenance_margin')
        riskLevel = self.safe_string(position, 'risk_level')
        # maint_margin / collateral = risk_level
        # collateral = maint_margin / risk_level
        collateral = Precise.string_div(maintenanceMarginString, riskLevel)
        markPrice = self.safe_string(position, 'mark_price')
        notionalString = Precise.string_mul(markPrice, size)
        unrealisedPnl = self.safe_string(position, 'floating_profit_loss')
        initialMarginString = self.safe_string(position, 'initial_margin')
        percentage = Precise.string_mul(Precise.string_div(unrealisedPnl, initialMarginString), '100')
        marginType = self.safe_string(position, 'margin_type')
        return {
            'info': position,
            'id': None,
            'symbol': self.safe_string(market, 'symbol'),
            'timestamp': None,
            'datetime': None,
            'initialMargin': self.parse_number(initialMarginString),
            'initialMarginPercentage': self.parse_number(Precise.string_div(initialMarginString, notionalString)),
            'maintenanceMargin': self.parse_number(maintenanceMarginString),
            'maintenanceMarginPercentage': self.parse_number(Precise.string_div(maintenanceMarginString, notionalString)),
            'entryPrice': self.safe_number(position, 'average_price'),
            'notional': self.parse_number(notionalString),
            'leverage': self.safe_number(position, 'leverage'),
            'unrealizedPnl': self.parse_number(unrealisedPnl),
            'contracts': self.parse_number(size),  # in USD for perpetuals on deribit
            'contractSize': self.safe_value(market, 'contractSize'),
            'marginRatio': self.parse_number(riskLevel),
            'liquidationPrice': self.safe_number(position, 'liquid_price'),
            'markPrice': self.parse_number(markPrice),
            'collateral': self.parse_number(collateral),
            'marginType': marginType,
            'side': side,
            'percentage': self.parse_number(percentage),
        }

    async def fetch_position(self, symbol, params={}):
        await self.sign_in()
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'instrument_name': market['id'],
        }
        response = await self.privateGetGetPosition(self.extend(request, params))
        result = self.safe_value(response, 'result')
        #
        #     {
        #         "jsonrpc":"2.0",
        #         "usIn":1647693832273,
        #         "usOut":1647693832282,
        #         "usDiff":9,
        #         "result":{
        #             "currency":"PERPETUAL",
        #             "kind":"perpetual",
        #             "size":"-0.08",
        #             "direction":"sell",
        #             "leverage":"3",
        #             "margin":"10.7724",
        #             "version":"553",
        #             "roe":"-0.000483",
        #             "traceType":0,
        #             "pos_id":"0",
        #             "instrument_name":"BNB-USDT-PERPETUAL",
        #             "average_price":"403.9",
        #             "mark_price":"403.965",
        #             "initial_margin":"10.77066668",
        #             "maintenance_margin":"0.2100618",
        #             "floating_profit_loss":"-0.0052",
        #             "liquid_price":"549.15437158",
        #             "margin_type":"cross",
        #             "risk_level":"0.017651",
        #             "available_withdraw_funds":"1.13004332",
        #             "order_id":"251085320510201856",
        #             "stop_loss_price":"0",
        #             "stop_loss_type":1,
        #             "take_profit_price":"0",
        #             "take_profit_type":1
        #         }
        #     }
        #
        return self.parse_position(result)

    async def fetch_positions(self, symbols=None, params={}):
        await self.sign_in()
        await self.load_markets()
        request = {
            'currency': 'PERPETUAL',
            # 'kind' : '',  # option, future, spot, margin,perpetual The order kind
        }
        response = await self.privateGetGetPositions(self.extend(request, params))
        result = self.safe_value(response, 'result')
        #
        #     {
        #         "jsonrpc":"2.0",
        #         "usIn":1647694531356,
        #         "usOut":1647694531364,
        #         "usDiff":8,
        #         "result":[{
        #             "currency":"PERPETUAL",
        #             "kind":"perpetual",
        #             "size":"-0.08",
        #             "direction":"sell",
        #             "leverage":"3",
        #             "margin":"10.7836",
        #             "version":"1251",
        #             "roe":"-0.003602",
        #             "traceType":0,
        #             "pos_id":"0",
        #             "instrument_name":"BNB-USDT-PERPETUAL",
        #             "average_price":"403.9",
        #             "mark_price":"404.385",
        #             "initial_margin":"10.77066668",
        #             "maintenance_margin":"0.2102802",
        #             "floating_profit_loss":"-0.0388",
        #             "liquid_price":"549.15437158",
        #             "margin_type":"cross",
        #             "risk_level":"0.01772",
        #             "available_withdraw_funds":"1.09644332",
        #             "order_id":"251085320510201856",
        #             "stop_loss_price":"0",
        #             "stop_loss_type":1,
        #             "take_profit_price":"0",
        #             "take_profit_type":1
        #         }]
        #     }
        #
        return self.parse_positions(result, symbols)

    def parse_transaction_status(self, status):
        states = {
            'deposit_confirmed': 'ok',
            'deposit_waiting_confirm': 'pending',
            'withdraw_init': 'pending',
            'withdraw_noticed_block_chain': 'pending',
            'withdraw_waiting_confirm': 'pending',
            'withdraw_confirmed': 'ok',
            'withdraw_failed': 'failed',
            'withdraw_auditing': 'pending',
            'withdraw_audit_reject': 'failed',
        }
        return self.safe_string(states, status, status)

    def parse_transaction(self, transaction, currency=None):
        #
        # fetchDeposits
        #         {
        #             "id":"250325458128736256",
        #             "amount":"0.04",
        #             "state":"deposit_confirmed",
        #             "coin_type":"BNB",
        #             "token_code":"BNB",
        #             "create_time":"1647512640040",
        #             "update_time":"1647512640053",
        #             "tx_hash":"",
        #             "full_name":"Binance Coin"
        #         }
        #
        # fetchWithdrawals or fetchWithdraw
        #         {
        #             "id":"251076247882829824",
        #             "address":"",
        #             "amount":"0.01",
        #             "state":"withdraw_auditing",
        #             "coin_type":"BNB",
        #             "create_time":"1647691642267",
        #             "update_time":"1647691650090",
        #             "full_name":"Binance Coin",
        #             "token_code":"BNB"
        #         }
        #
        currencyId = self.safe_string(transaction, 'coin_type')
        code = self.safe_currency_code(currencyId, currency)
        id = self.safe_string(transaction, 'id')
        txId = self.safe_string(transaction, 'tx_hash')
        timestamp = self.safe_integer(transaction, 'create_time')
        updated = self.safe_integer(transaction, 'update_time')
        amount = self.safe_number(transaction, 'amount')
        status = self.safe_string(transaction, 'state')
        return {
            'info': transaction,
            'id': id,
            'txid': txId,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'network': None,
            'addressFrom': None,
            'address': None,
            'addressTo': None,
            'tagFrom': None,
            'tag': None,
            'tagTo': None,
            'type': None,
            'amount': amount,
            'currency': code,
            'status': self.parse_transaction_status(status),
            'updated': updated,
            'fee': None,
        }

    async def fetch_deposits(self, code=None, since=None, limit=None, params={}):
        if code is None:
            raise ArgumentsRequired(self.id + ' fetchDeposits() requires the code argument')
        await self.sign_in()
        await self.load_markets()
        currency = self.safe_currency(code)
        request = {
            'coin_type': currency['id'],
        }
        response = await self.privateGetGetDepositRecord(self.extend(request, params))
        result = self.safe_value(response, 'result', [])
        #
        #     {
        #         "jsonrpc":"2.0",
        #         "usIn":1647606752447,
        #         "usOut":1647606752457,
        #         "usDiff":10,
        #         "result":[{
        #             "id":"250325458128736256",
        #             "amount":"0.04",
        #             "state":"deposit_confirmed",
        #             "coin_type":"BNB",
        #             "token_code":"BNB",
        #             "create_time":"1647512640040",
        #             "update_time":"1647512640053",
        #             "tx_hash":"",
        #             "full_name":"Binance Coin"
        #         }]
        #     }
        #     }
        #
        return self.parse_transactions(result, currency, since, limit, {'type': 'deposit'})

    async def fetch_withdrawals(self, code=None, since=None, limit=None, params={}):
        if code is None:
            raise ArgumentsRequired(self.id + ' fetchWithdrawals() requires the code argument')
        await self.sign_in()
        await self.load_markets()
        currency = self.safe_currency(code)
        request = {
            'coin_type': currency['id'],
            # 'withdraw_id': 0,
        }
        response = await self.privateGetGetWithdrawRecord(self.extend(request, params))
        result = self.safe_value(response, 'result', [])
        #
        #     {
        #         "jsonrpc":"2.0",
        #         "usIn":1647691750112,
        #         "usOut":1647691750125,
        #         "usDiff":13,
        #         "result":[{
        #             "id":"251076247882829824",
        #             "address":"",
        #             "amount":"0.01",
        #             "state":"withdraw_auditing",
        #             "coin_type":"BNB",
        #             "create_time":"1647691642267",
        #             "update_time":"1647691650090",
        #             "full_name":"Binance Coin",
        #             "token_code":"BNB"
        #         }]
        #     }
        #
        return self.parse_transactions(result, currency, since, limit, {'type': 'withdrawal'})

    async def fetch_withdrawal(self, id, code=None, params={}):
        if code is None:
            raise ArgumentsRequired(self.id + ' fetchWithdrawal() requires the code argument')
        await self.sign_in()
        await self.load_markets()
        currency = self.safe_currency(code)
        request = {
            'coin_type': currency['id'],
            'withdraw_id': id,
        }
        response = await self.privateGetGetWithdrawRecord(self.extend(request, params))
        result = self.safe_value(response, 'result', [])
        #
        #     {
        #         "jsonrpc":"2.0",
        #         "usIn":1647691750112,
        #         "usOut":1647691750125,
        #         "usDiff":13,
        #         "result":[{
        #             "id":"251076247882829824",
        #             "address":"",
        #             "amount":"0.01",
        #             "state":"withdraw_auditing",
        #             "coin_type":"BNB",
        #             "create_time":"1647691642267",
        #             "update_time":"1647691650090",
        #             "full_name":"Binance Coin",
        #             "token_code":"BNB"
        #         }]
        #     }
        #
        records = self.filter_by(result, 'id', id)
        record = self.safe_value(records, 0)
        return self.parse_transaction(record, currency)

    async def fetch_leverage(self, symbol, params={}):
        """
        see https://docs.btcex.com/#get-perpetual-instrument-config
        fetch the set leverage for a market
        :param str symbol: unified market symbol
        :param dict params: extra parameters specific to the btcex api endpoint
        :returns dict: a `leverage structure <https://docs.ccxt.com/en/latest/manual.html#leverage-structure>`
        """
        await self.sign_in()
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'instrument_name': market['id'],
        }
        response = await self.privateGetGetPerpetualUserConfig(self.extend(request, params))
        #
        #     {
        #         "jsonrpc": "2.0",
        #         "usIn": 1674182494283,
        #         "usOut": 1674182494294,
        #         "usDiff": 11,
        #         "result": {
        #             "margin_type": "cross",
        #             "leverage": "20",
        #             "instrument_name": "BTC-USDT-PERPETUAL",
        #             "time": "1674182494293"
        #         }
        #     }
        #
        data = self.safe_value(response, 'result', {})
        return self.safe_number(data, 'leverage')

    async def fetch_market_leverage_tiers(self, symbol, params={}):
        """
        see https://docs.btcex.com/#get-perpetual-instrument-leverage-config
        retrieve information on the maximum leverage, for different trade sizes for a single market
        :param str symbol: unified market symbol
        :param dict params: extra parameters specific to the btcex api endpoint
        :returns dict: a `leverage tiers structure <https://docs.ccxt.com/en/latest/manual.html#leverage-tiers-structure>`
        """
        await self.load_markets()
        market = self.market(symbol)
        if not market['swap']:
            raise BadRequest(self.id + ' fetchMarketLeverageTiers() supports swap markets only')
        request = {
            'instrument_name': market['id'],
        }
        response = await self.publicGetGetPerpetualLeverageBracket(self.extend(request, params))
        #
        #     {
        #         "jsonrpc": "2.0",
        #         "usIn": 1674184074454,
        #         "usOut": 1674184074457,
        #         "usDiff": 3,
        #         "result": [
        #             {
        #                 "bracket": 1,
        #                 "initialLeverage": 125,
        #                 "maintenanceMarginRate": "0.004",
        #                 "notionalCap": "50000",
        #                 "notionalFloor": "0",
        #                 "cum": "0"
        #             },
        #             ...
        #         ]
        #     }
        #
        data = self.safe_value(response, 'result', [])
        return self.parse_market_leverage_tiers(data, market)

    def parse_market_leverage_tiers(self, info, market):
        #
        #     [
        #         {
        #             "bracket": 1,
        #             "initialLeverage": 125,
        #             "maintenanceMarginRate": "0.004",
        #             "notionalCap": "50000",
        #             "notionalFloor": "0",
        #             "cum": "0"
        #         },
        #         ...
        #     ]
        #
        tiers = []
        brackets = info
        for i in range(0, len(brackets)):
            tier = brackets[i]
            tiers.append({
                'tier': self.safe_integer(tier, 'bracket'),
                'currency': market['settle'],
                'minNotional': self.safe_number(tier, 'notionalFloor'),
                'maxNotional': self.safe_number(tier, 'notionalCap'),
                'maintenanceMarginRate': self.safe_number(tier, 'maintenanceMarginRate'),
                'maxLeverage': self.safe_number(tier, 'initialLeverage'),
                'info': tier,
            })
        return tiers

    async def fetch_leverage_tiers(self, symbols=None, params={}):
        """
        see https://docs.btcex.com/#get-all-perpetual-instrument-leverage-config
        retrieve information on the maximum leverage, for different trade sizes
        :param [str]|None symbols: a list of unified market symbols
        :param dict params: extra parameters specific to the btcex api endpoint
        :returns dict: a dictionary of `leverage tiers structures <https://docs.ccxt.com/en/latest/manual.html#leverage-tiers-structure>`, indexed by market symbols
        """
        await self.load_markets()
        response = await self.publicGetGetPerpetualLeverageBracketAll(params)
        #
        #     {
        #         "jsonrpc": "2.0",
        #         "usIn": 1674183578745,
        #         "usOut": 1674183578752,
        #         "usDiff": 7,
        #         "result": {
        #             "WAVES-USDT-PERPETUAL": [
        #                 {
        #                     "bracket": 1,
        #                     "initialLeverage": 50,
        #                     "maintenanceMarginRate": "0.01",
        #                     "notionalCap": "50000",
        #                     "notionalFloor": "0",
        #                     "cum": "0"
        #                 },
        #                 ...
        #             ]
        #         }
        #     }
        #
        data = self.safe_value(response, 'result', {})
        symbols = self.market_symbols(symbols)
        return self.parse_leverage_tiers(data, symbols, 'symbol')

    def parse_leverage_tiers(self, response, symbols=None, marketIdKey=None):
        #
        #     {
        #         "WAVES-USDT-PERPETUAL": [
        #             {
        #                 "bracket": 1,
        #                 "initialLeverage": 50,
        #                 "maintenanceMarginRate": "0.01",
        #                 "notionalCap": "50000",
        #                 "notionalFloor": "0",
        #                 "cum": "0"
        #             },
        #             ...
        #         ]
        #     }
        #
        tiers = {}
        result = {}
        marketIds = list(response.keys())
        for i in range(0, len(marketIds)):
            marketId = marketIds[i]
            entry = response[marketId]
            market = self.safe_market(marketId)
            symbol = self.safe_symbol(marketId, market)
            symbolsLength = 0
            tiers[symbol] = self.parse_market_leverage_tiers(entry, market)
            if symbols is not None:
                symbolsLength = len(symbols)
                if self.in_array(symbol, symbols):
                    result[symbol] = self.parse_market_leverage_tiers(entry, market)
            if symbol is not None and (symbolsLength == 0 or self.in_array(symbol, symbols)):
                result[symbol] = self.parse_market_leverage_tiers(entry, market)
        return result

    async def set_margin_mode(self, marginMode, symbol=None, params={}):
        """
        set margin mode to 'cross' or 'isolated'
        see https://docs.btcex.com/#modify-perpetual-instrument-margin-type
        :param str marginMode: 'cross' or 'isolated'
        :param str|None symbol: unified market symbol
        :param dict params: extra parameters specific to the btcex api endpoint
        :returns dict: response from the exchange
        """
        self.check_required_symbol('setMarginMode', symbol)
        await self.sign_in()
        await self.load_markets()
        market = self.market(symbol)
        if not market['swap']:
            raise BadRequest(self.id + ' setMarginMode() supports swap contracts only')
        if (marginMode != 'isolated') and (marginMode != 'isolate') and (marginMode != 'cross'):
            raise BadRequest(self.id + ' marginMode must be either isolated or cross')
        marginMode = 'isolate' if (marginMode == 'isolated') else 'cross'
        request = {
            'instrument_name': market['id'],
            'margin_type': marginMode,
        }
        result = await self.privatePostAdjustPerpetualMarginType(self.extend(request, params))
        #
        #     {
        #         "id": "1674857919",
        #         "jsonrpc": "2.0",
        #         "usIn": 1674857920070,
        #         "usOut": 1674857920079,
        #         "usDiff": 9,
        #         "result": "ok"
        #     }
        #
        return result

    async def set_leverage(self, leverage, symbol=None, params={}):
        """
        set the leverage amount for a market
        see https://docs.btcex.com/#modify-perpetual-instrument-leverage
        :param float leverage: the rate of leverage
        :param str symbol: unified market symbol
        :param dict params: extra parameters specific to the btcex api endpoint
        :returns dict: response from the exchange
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' setLeverage() requires a symbol argument')
        await self.sign_in()
        await self.load_markets()
        self.check_required_symbol('setLeverage', symbol)
        market = self.market(symbol)
        if not market['swap']:
            raise BadRequest(self.id + ' setLeverage() supports swap contracts only')
        if (leverage < 1) or (leverage > 125):
            raise BadRequest(self.id + ' leverage should be between 1 and 125')
        request = {
            'instrument_name': market['id'],
            'leverage': leverage,
        }
        response = await self.privatePostAdjustPerpetualLeverage(self.extend(request, params))
        #
        #     {
        #         "id": "1674856410",
        #         "jsonrpc": "2.0",
        #         "usIn": 1674856410930,
        #         "usOut": 1674856410988,
        #         "usDiff": 58,
        #         "result": "ok"
        #     }
        #
        return response

    async def fetch_funding_rates(self, symbols=None, params={}):
        """
        fetch the current funding rates
        see https://docs.btcex.com/#contracts
        :param [str] symbols: unified market symbols
        :param dict params: extra parameters specific to the btcex api endpoint
        :returns [dict]: an array of `funding rate structures <https://docs.ccxt.com/en/latest/manual.html#funding-rate-structure>`
        """
        await self.load_markets()
        symbols = self.market_symbols(symbols)
        response = await self.publicGetCoinGeckoContracts(params)
        #
        #     {
        #         "jsonrpc": "2.0",
        #         "usIn": 1674803585896,
        #         "usOut": 1674803585943,
        #         "usDiff": 47,
        #         "result": [
        #             {
        #                 "ticker_id": "BTC-USDT-PERPETUAL",
        #                 "base_currency": "BTC",
        #                 "target_currency": "USDT",
        #                 "last_price": "23685",
        #                 "base_volume": "167011.37199999999999989",
        #                 "target_volume": "3837763191.33800288010388613",
        #                 "bid": "23684.5",
        #                 "ask": "23685",
        #                 "high": "23971.5",
        #                 "low": "23156",
        #                 "product_type": "perpetual",
        #                 "open_interest": "24242.36",
        #                 "index_price": "23686.4",
        #                 "index_name": "BTC-USDT",
        #                 "index_currency": "BTC",
        #                 "start_timestamp": 1631004005882,
        #                 "funding_rate": "0.000187",
        #                 "next_funding_rate_timestamp": 1675065600000,
        #                 "contract_type": "Quanto",
        #                 "contract_price": "23685",
        #                 "contract_price_currency": "USDT"
        #             },
        #         ]
        #     }
        #
        data = self.safe_value(response, 'result', [])
        result = {}
        for i in range(0, len(data)):
            entry = data[i]
            marketId = self.safe_string(entry, 'ticker_id')
            market = self.safe_market(marketId)
            symbol = market['symbol']
            if symbols is not None:
                if self.in_array(symbol, symbols):
                    result[symbol] = self.parse_funding_rate(entry, market)
            else:
                result[symbol] = self.parse_funding_rate(entry, market)
        return self.filter_by_array(result, 'symbol', symbols)

    async def fetch_funding_rate(self, symbol, params={}):
        """
        fetch the current funding rate
        see https://docs.btcex.com/#contracts
        :param str symbol: unified market symbol
        :param dict params: extra parameters specific to the btcex api endpoint
        :returns dict: a `funding rate structure <https://docs.ccxt.com/en/latest/manual.html#funding-rate-structure>`
        """
        await self.load_markets()
        market = self.market(symbol)
        response = await self.publicGetCoinGeckoContracts(params)
        #
        #     {
        #         "jsonrpc": "2.0",
        #         "usIn": 1674803585896,
        #         "usOut": 1674803585943,
        #         "usDiff": 47,
        #         "result": [
        #             {
        #                 "ticker_id": "BTC-USDT-PERPETUAL",
        #                 "base_currency": "BTC",
        #                 "target_currency": "USDT",
        #                 "last_price": "23685",
        #                 "base_volume": "167011.37199999999999989",
        #                 "target_volume": "3837763191.33800288010388613",
        #                 "bid": "23684.5",
        #                 "ask": "23685",
        #                 "high": "23971.5",
        #                 "low": "23156",
        #                 "product_type": "perpetual",
        #                 "open_interest": "24242.36",
        #                 "index_price": "23686.4",
        #                 "index_name": "BTC-USDT",
        #                 "index_currency": "BTC",
        #                 "start_timestamp": 1631004005882,
        #                 "funding_rate": "0.000187",
        #                 "next_funding_rate_timestamp": 1675065600000,
        #                 "contract_type": "Quanto",
        #                 "contract_price": "23685",
        #                 "contract_price_currency": "USDT"
        #             },
        #         ]
        #     }
        #
        data = self.safe_value(response, 'result', [])
        for i in range(0, len(data)):
            entry = data[i]
            marketId = self.safe_string(entry, 'ticker_id')
            if marketId == market['id']:
                return self.parse_funding_rate(entry, market)
        return self.parse_funding_rate(data, market)

    def parse_funding_rate(self, contract, market=None):
        #
        #     {
        #         "ticker_id": "BTC-USDT-PERPETUAL",
        #         "base_currency": "BTC",
        #         "target_currency": "USDT",
        #         "last_price": "23685",
        #         "base_volume": "167011.37199999999999989",
        #         "target_volume": "3837763191.33800288010388613",
        #         "bid": "23684.5",
        #         "ask": "23685",
        #         "high": "23971.5",
        #         "low": "23156",
        #         "product_type": "perpetual",
        #         "open_interest": "24242.36",
        #         "index_price": "23686.4",
        #         "index_name": "BTC-USDT",
        #         "index_currency": "BTC",
        #         "start_timestamp": 1631004005882,
        #         "funding_rate": "0.000187",
        #         "next_funding_rate_timestamp": 1675065600000,
        #         "contract_type": "Quanto",
        #         "contract_price": "23685",
        #         "contract_price_currency": "USDT"
        #     }
        #
        marketId = self.safe_string(contract, 'ticker_id')
        fundingTimestamp = self.safe_integer(contract, 'next_funding_rate_timestamp')
        return {
            'info': contract,
            'symbol': self.safe_symbol(marketId, market),
            'markPrice': None,
            'indexPrice': self.safe_number(contract, 'index_price'),
            'interestRate': None,
            'estimatedSettlePrice': None,
            'timestamp': None,
            'datetime': None,
            'fundingRate': self.safe_number(contract, 'funding_rate'),
            'fundingTimestamp': fundingTimestamp,
            'fundingDatetime': self.iso8601(fundingTimestamp),
            'nextFundingRate': None,
            'nextFundingTimestamp': None,
            'nextFundingDatetime': None,
            'previousFundingRate': None,
            'previousFundingTimestamp': None,
            'previousFundingDatetime': None,
        }

    async def transfer(self, code, amount, fromAccount, toAccount, params={}):
        """
        transfer currency internally between wallets on the same account
        see https://docs.btcex.com/#asset-transfer
        :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 btcex api endpoint
        :returns dict: a `transfer structure <https://docs.ccxt.com/en/latest/manual.html#transfer-structure>`
        """
        await self.sign_in()
        await self.load_markets()
        currency = self.currency(code)
        accountsByType = self.safe_value(self.options, 'accountsByType', {})
        fromId = self.safe_string(accountsByType, fromAccount, fromAccount)
        toId = self.safe_string(accountsByType, toAccount, toAccount)
        request = {
            'coin_type': currency['id'],
            'amount': self.currency_to_precision(code, amount),
            'from': fromId,  # WALLET, SPOT, PERPETUAL
            'to': toId,  # WALLET, SPOT, PERPETUAL
        }
        response = await self.privatePostSubmitTransfer(self.extend(request, params))
        #
        #     {
        #         "id": "1674937273",
        #         "jsonrpc": "2.0",
        #         "usIn": 1674937274762,
        #         "usOut": 1674937274774,
        #         "usDiff": 12,
        #         "result": "ok"
        #     }
        #
        return self.parse_transfer(response, currency)

    def parse_transfer(self, transfer, currency=None):
        #
        #     {
        #         "id": "1674937273",
        #         "jsonrpc": "2.0",
        #         "usIn": 1674937274762,
        #         "usOut": 1674937274774,
        #         "usDiff": 12,
        #         "result": "ok"
        #     }
        #
        return {
            'info': transfer,
            'id': self.safe_string(transfer, 'id'),
            'timestamp': None,
            'datetime': None,
            'currency': None,
            'amount': None,
            'fromAccount': None,
            'toAccount': None,
            'status': None,
        }

    async def fetch_open_interest(self, symbol, params={}):
        """
        fetch the open interest of a market
        see https://docs.btcex.com/#contracts
        :param str symbol: unified CCXT market symbol
        :param dict params: extra parameters specific to the btcex api endpoint
        :returns dict} an open interest structure{@link https://docs.ccxt.com/en/latest/manual.html#interest-history-structure:
        """
        await self.load_markets()
        market = self.market(symbol)
        if not market['contract']:
            raise BadRequest(self.id + ' fetchOpenInterest() supports contract markets only')
        response = await self.publicGetCoinGeckoContracts(params)
        #
        #     {
        #         "jsonrpc": "2.0",
        #         "usIn": 1674803585896,
        #         "usOut": 1674803585943,
        #         "usDiff": 47,
        #         "result": [
        #             {
        #                 "ticker_id": "BTC-USDT-PERPETUAL",
        #                 "base_currency": "BTC",
        #                 "target_currency": "USDT",
        #                 "last_price": "23685",
        #                 "base_volume": "167011.37199999999999989",
        #                 "target_volume": "3837763191.33800288010388613",
        #                 "bid": "23684.5",
        #                 "ask": "23685",
        #                 "high": "23971.5",
        #                 "low": "23156",
        #                 "product_type": "perpetual",
        #                 "open_interest": "24242.36",
        #                 "index_price": "23686.4",
        #                 "index_name": "BTC-USDT",
        #                 "index_currency": "BTC",
        #                 "start_timestamp": 1631004005882,
        #                 "funding_rate": "0.000187",
        #                 "next_funding_rate_timestamp": 1675065600000,
        #                 "contract_type": "Quanto",
        #                 "contract_price": "23685",
        #                 "contract_price_currency": "USDT"
        #             },
        #         ]
        #     }
        #
        data = self.safe_value(response, 'result', [])
        for i in range(0, len(data)):
            entry = data[i]
            marketId = self.safe_string(entry, 'ticker_id')
            if marketId == market['id']:
                return self.parse_open_interest(entry, market)
        return self.parse_open_interest(data, market)

    def parse_open_interest(self, interest, market=None):
        #
        #     {
        #         "ticker_id": "BTC-USDT-PERPETUAL",
        #         "base_currency": "BTC",
        #         "target_currency": "USDT",
        #         "last_price": "23685",
        #         "base_volume": "167011.37199999999999989",
        #         "target_volume": "3837763191.33800288010388613",
        #         "bid": "23684.5",
        #         "ask": "23685",
        #         "high": "23971.5",
        #         "low": "23156",
        #         "product_type": "perpetual",
        #         "open_interest": "24242.36",
        #         "index_price": "23686.4",
        #         "index_name": "BTC-USDT",
        #         "index_currency": "BTC",
        #         "start_timestamp": 1631004005882,
        #         "funding_rate": "0.000187",
        #         "next_funding_rate_timestamp": 1675065600000,
        #         "contract_type": "Quanto",
        #         "contract_price": "23685",
        #         "contract_price_currency": "USDT"
        #     }
        #
        marketId = self.safe_string(interest, 'ticker_id')
        market = self.safe_market(marketId, market)
        openInterest = self.safe_number(interest, 'open_interest')
        return {
            'info': interest,
            'symbol': market['symbol'],
            'baseVolume': openInterest,
            'quoteVolume': None,
            'openInterestAmount': openInterest,  # in base currency
            'openInterestValue': None,
            'timestamp': None,
            'datetime': None,
        }

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        request = '/' + 'api/' + self.version + '/' + api + '/' + path
        if api == 'public':
            if params:
                request += '?' + self.urlencode(params)
        if api == 'private':
            self.check_required_credentials()
            if method == 'GET':
                if params:
                    request += '?' + self.urlencode(params)
            sessionToken = self.safe_string(self.options, 'accessToken')
            if sessionToken is None:
                raise AuthenticationError(self.id + ' sign() requires access token')
            headers = {
                'Authorization': 'bearer ' + sessionToken,
            }
            if method == 'POST':
                headers['Content-Type'] = 'application/json'
                if params:
                    rpcPayload = {
                        'jsonrpc': '2.0',
                        'id': self.nonce(),
                        'method': '/' + api + '/' + path,
                        'params': params,
                    }
                    body = self.json(rpcPayload)
        url = self.urls['api']['rest'] + request
        return {'url': url, 'method': method, 'body': body, 'headers': headers}

    def handle_errors(self, code, reason, url, method, headers, body, response, requestHeaders, requestBody):
        if response is None:
            return  # fallback to the default error handler
        error = self.safe_value(response, 'error')
        if error:
            feedback = self.id + ' ' + body
            code = self.safe_string(error, 'code')
            message = self.safe_string(error, 'message')
            self.throw_exactly_matched_exception(self.exceptions['exact'], code, feedback)
            self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback)
            raise ExchangeError(feedback)  # unknown message
