# -*- 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': False,
            '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,
                'createOrder': True,
                'editOrder': False,
                'fetchBalance': True,
                'fetchBorrowRate': False,
                'fetchBorrowRateHistories': False,
                'fetchBorrowRateHistory': False,
                'fetchBorrowRates': False,
                'fetchClosedOrders': True,
                'fetchCurrencies': False,
                'fetchDepositAddress': False,
                'fetchDeposits': True,
                'fetchFundingHistory': False,
                'fetchFundingRate': False,
                'fetchFundingRateHistory': False,
                'fetchFundingRates': False,
                'fetchIndexOHLCV': False,
                'fetchMarginMode': False,
                'fetchMarkets': True,
                'fetchMarkOHLCV': False,
                'fetchMyTrades': True,
                'fetchOHLCV': True,
                '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,
                'signIn': True,
                'withdraw': False,
            },
            'timeframes': {
                '15s': '15',
                '1m': '60',
                '5m': '300',
                '15m': '900',
                '1h': '3600',
                '4h': '14400',
                '1d': '86400',
                '3d': '259200',
                '1w': '604800',
                '2w': '1209600',
                '1M': '2592000',
            },
            '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',
                    ],
                    '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',
                    ],
                    'post': [
                        # auth
                        'logout',
                        # wallet
                        'get_assets_info',
                        'add_withdraw_address',
                        # trade
                        'buy',
                        'sell',
                        'cancel',
                        'cancel_all_by_currency',
                        'cancel_all_by_instrument',
                        'close_position',
                    ],
                    '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
                    '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',
                },
            },
            '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')
        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'],
        }
        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')
        return self.parse_order_book(result, market['symbol'], timestamp)

    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_integer(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.timeframes[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')
        averageString = self.safe_string(order, 'average_price')
        amountString = self.safe_string(order, 'amount')
        filledString = self.safe_string(order, 'filled_amount')
        lastTradeTimestamp = None
        if filledString is not None:
            isFilledPositive = Precise.string_gt(filledString, '0')
            if isFilledPositive:
                lastTradeTimestamp = lastUpdate
        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'],
            }
        type = self.safe_string(order, 'order_type')
        # injected in createOrder
        trades = self.safe_value(order, 'trades')
        if trades is not None:
            trades = self.parse_trades(trades, market)
        timeInForce = self.parse_time_in_force(self.safe_string(order, 'time_in_force'))
        stopPrice = self.safe_value(order, 'trigger_price')
        postOnly = self.safe_value(order, 'post_only')
        return self.safe_order({
            'info': order,
            'id': id,
            'clientOrderId': None,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': lastTradeTimestamp,
            'symbol': market['symbol'],
            'type': type,
            'timeInForce': timeInForce,
            'postOnly': postOnly,
            'side': side,
            'price': priceString,
            'stopPrice': stopPrice,
            '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.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={}):
        await self.sign_in()
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'instrument_name': market['id'],
            'amount': self.amount_to_precision(symbol, amount),
            'type': type,  # limit, market, default is limit
            # 'price': self.price_to_precision(symbol, 123.45),  # The order price for limit order. When adding options order with advanced=iv, the field price should be a value of implied volatility in percentages. For example, price=100, means implied volatility of 100%
            # 'time_in_force' : 'good_til_cancelled',  # good_til_cancelled, good_til_date, fill_or_kill, immediate_or_cancel Specifies how long the order remains in effect, default: good_til_cancelled
            # 'post_only': False,  # If True, the order is considered post-only, default: False
            # 'reduce_only': False,  # If True, the order is considered reduce-only which is intended to only reduce a current position. default: False
            # 'condition_type': '',  # NORMAL, STOP, TRAILING, IF_TOUCHED, Condition sheet policy, the default is NORMAL. Available when kind is future
            # 'trigger_price': 'index_price',  # trigger price. Available when condition_type is STOP or IF_TOUCHED
            # 'trail_price': False,  # trail price, Tracking price change Delta. Available when condition_type is TRAILING
            # 'advanced': 'usd',  # Advanced option order type,(Only for options), default: usdt. If set to iv，then the price field means iv value
        }
        if type == 'limit':
            request['price'] = self.price_to_precision(symbol, price)
        if market['contract']:
            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
            params = self.omit(params, ['timeInForce', 'postOnly', 'reduceOnly'])
        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
        }
        method = None
        market = self.market(symbol)
        request['instrument_name'] = market['id']
        if since is None:
            method = 'privateGetGetUserTradesByInstrument'
        else:
            method = 'privateGetGetUserTradesByInstrumentAndTime'
        if limit is not None:
            request['count'] = limit  # default 20
        response = await getattr(self, method)(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)

    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
