# -*- 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

# -----------------------------------------------------------------------------

try:
    basestring  # Python 3
except NameError:
    basestring = str  # Python 2
import hashlib
import math
from ccxt.base.errors import ExchangeError
from ccxt.base.errors import AuthenticationError
from ccxt.base.errors import PermissionDenied
from ccxt.base.errors import ArgumentsRequired
from ccxt.base.errors import BadRequest
from ccxt.base.errors import BadSymbol
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 NetworkError
from ccxt.base.errors import ExchangeNotAvailable
from ccxt.base.errors import OnMaintenance
from ccxt.base.errors import RequestTimeout
from ccxt.base.decimal_to_precision import TRUNCATE
from ccxt.base.decimal_to_precision import TICK_SIZE
from ccxt.base.precise import Precise


class huobi(Exchange):

    def describe(self):
        return self.deep_extend(super(huobi, self).describe(), {
            'id': 'huobi',
            'name': 'Huobi',
            'countries': ['CN'],
            'rateLimit': 100,
            'userAgent': self.userAgents['chrome39'],
            'certified': True,
            'version': 'v1',
            'accounts': None,
            'accountsById': None,
            'hostname': 'api.huobi.pro',  # api.testnet.huobi.pro
            'pro': True,
            'has': {
                # 'margin': True,
                'swap': True,
                'future': True,
                'cancelAllOrders': True,
                'cancelOrder': True,
                'cancelOrders': True,
                'CORS': None,
                'createOrder': True,
                'fetchAccounts': True,
                'fetchBalance': True,
                'fetchBorrowRate': True,
                'fetchBorrowRates': True,
                'fetchBorrowRatesPerSymbol': True,
                'fetchClosedOrders': True,
                'fetchCurrencies': True,
                'fetchDepositAddress': True,
                'fetchDepositAddressesByNetwork': True,
                'fetchDeposits': True,
                'fetchFundingRate': True,
                'fetchFundingRateHistory': True,
                'fetchIndexOHLCV': True,
                'fetchMarkets': True,
                'fetchMarkOHLCV': True,
                'fetchMyTrades': True,
                'fetchOHLCV': True,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'fetchOrderBook': True,
                'fetchOrders': True,
                'fetchOrderTrades': True,
                'fetchPremiumIndexOHLCV': True,
                'fetchTicker': True,
                'fetchTickers': True,
                'fetchTime': True,
                'fetchTrades': True,
                'fetchTradingFee': True,
                'fetchTradingLimits': True,
                'fetchWithdrawAddress': True,
                'fetchWithdrawAddressesByNetwork': True,
                'fetchWithdrawals': True,
                'transfer': True,
                'withdraw': True,
            },
            'timeframes': {
                '1m': '1min',
                '5m': '5min',
                '15m': '15min',
                '30m': '30min',
                '1h': '60min',
                '4h': '4hour',
                '1d': '1day',
                '1w': '1week',
                '1M': '1mon',
                '1y': '1year',
            },
            'urls': {
                # 'test': {
                #     'market': 'https://api.testnet.huobi.pro',
                #     'public': 'https://api.testnet.huobi.pro',
                #     'private': 'https://api.testnet.huobi.pro',
                # },
                'logo': 'https://user-images.githubusercontent.com/1294454/76137448-22748a80-604e-11ea-8069-6e389271911d.jpg',
                'hostnames': {
                    'contract': 'api.hbdm.com',
                    'spot': 'api.huobi.pro',
                    # recommended for AWS
                    # 'contract': 'api.hbdm.vn',
                    # 'spot': 'api-aws.huobi.pro',
                },
                'api': {
                    'contract': 'https://{hostname}',
                    'spot': 'https://{hostname}',
                    'market': 'https://{hostname}',
                    'public': 'https://{hostname}',
                    'private': 'https://{hostname}',
                    'v2Public': 'https://{hostname}',
                    'v2Private': 'https://{hostname}',
                },
                'www': 'https://www.huobi.com',
                # 'referral': {
                #     'url': 'https://www.huobi.com/en-us/topic/double-reward/?invite_code=6rmm2223',
                #     'discount': 0.15,
                # },
                'doc': [
                    'https://huobiapi.github.io/docs/spot/v1/cn/',
                    'https://huobiapi.github.io/docs/dm/v1/cn/',
                    'https://huobiapi.github.io/docs/coin_margined_swap/v1/cn/',
                    'https://huobiapi.github.io/docs/usdt_swap/v1/cn/',
                    'https://huobiapi.github.io/docs/option/v1/cn/',
                ],
                'fees': 'https://www.huobi.com/about/fee/',
            },
            'api': {
                # ------------------------------------------------------------
                # old api definitions
                'v2Public': {
                    'get': {
                        'reference/currencies': 1,  # 币链参考信息
                        'market-status': 1,  # 获取当前市场状态
                    },
                },
                'v2Private': {
                    'get': {
                        'account/ledger': 1,
                        'account/withdraw/quota': 1,
                        'account/withdraw/address': 1,  # 提币地址查询(限母用户可用)
                        'account/deposit/address': 1,
                        'account/repayment': 5,  # 还币交易记录查询
                        'reference/transact-fee-rate': 1,
                        'account/asset-valuation': 0.2,  # 获取账户资产估值
                        'point/account': 5,  # 点卡余额查询
                        'sub-user/user-list': 1,  # 获取子用户列表
                        'sub-user/user-state': 1,  # 获取特定子用户的用户状态
                        'sub-user/account-list': 1,  # 获取特定子用户的账户列表
                        'sub-user/deposit-address': 1,  # 子用户充币地址查询
                        'sub-user/query-deposit': 1,  # 子用户充币记录查询
                        'user/api-key': 1,  # 母子用户API key信息查询
                        'user/uid': 1,  # 母子用户获取用户UID
                        'algo-orders/opening': 1,  # 查询未触发OPEN策略委托
                        'algo-orders/history': 1,  # 查询策略委托历史
                        'algo-orders/specific': 1,  # 查询特定策略委托
                        'c2c/offers': 1,  # 查询借入借出订单
                        'c2c/offer': 1,  # 查询特定借入借出订单及其交易记录
                        'c2c/transactions': 1,  # 查询借入借出交易记录
                        'c2c/repayment': 1,  # 查询还币交易记录
                        'c2c/account': 1,  # 查询账户余额
                        'etp/reference': 1,  # 基础参考信息
                        'etp/transactions': 5,  # 获取杠杆ETP申赎记录
                        'etp/transaction': 5,  # 获取特定杠杆ETP申赎记录
                        'etp/rebalance': 1,  # 获取杠杆ETP调仓记录
                        'etp/limit': 1,  # 获取ETP持仓限额
                    },
                    'post': {
                        'account/transfer': 1,
                        'account/repayment': 5,  # 归还借币（全仓逐仓通用）
                        'point/transfer': 5,  # 点卡划转
                        'sub-user/management': 1,  # 冻结/解冻子用户
                        'sub-user/creation': 1,  # 子用户创建
                        'sub-user/tradable-market': 1,  # 设置子用户交易权限
                        'sub-user/transferability': 1,  # 设置子用户资产转出权限
                        'sub-user/api-key-generation': 1,  # 子用户API key创建
                        'sub-user/api-key-modification': 1,  # 修改子用户API key
                        'sub-user/api-key-deletion': 1,  # 删除子用户API key
                        'sub-user/deduct-mode': 1,  # 设置子用户手续费抵扣模式
                        'algo-orders': 1,  # 策略委托下单
                        'algo-orders/cancel-all-after': 1,  # 自动撤销订单
                        'algo-orders/cancellation': 1,  # 策略委托（触发前）撤单
                        'c2c/offer': 1,  # 借入借出下单
                        'c2c/cancellation': 1,  # 借入借出撤单
                        'c2c/cancel-all': 1,  # 撤销所有借入借出订单
                        'c2c/repayment': 1,  # 还币
                        'c2c/transfer': 1,  # 资产划转
                        'etp/creation': 5,  # 杠杆ETP换入
                        'etp/redemption': 5,  # 杠杆ETP换出
                        'etp/{transactId}/cancel': 10,  # 杠杆ETP单个撤单
                        'etp/batch-cancel': 50,  # 杠杆ETP批量撤单
                    },
                },
                'market': {
                    'get': {
                        'history/kline': 1,  # 获取K线数据
                        'detail/merged': 1,  # 获取聚合行情(Ticker)
                        'depth': 1,  # 获取 Market Depth 数据
                        'trade': 1,  # 获取 Trade Detail 数据
                        'history/trade': 1,  # 批量获取最近的交易记录
                        'detail': 1,  # 获取 Market Detail 24小时成交量数据
                        'tickers': 1,
                        'etp': 1,  # 获取杠杆ETP实时净值
                    },
                },
                'public': {
                    'get': {
                        'common/symbols': 1,  # 查询系统支持的所有交易对
                        'common/currencys': 1,  # 查询系统支持的所有币种
                        'common/timestamp': 1,  # 查询系统当前时间
                        'common/exchange': 1,  # order limits
                        'settings/currencys': 1,  # ?language=en-US
                    },
                },
                'private': {
                    'get': {
                        'account/accounts': 0.2,  # 查询当前用户的所有账户(即account-id)
                        'account/accounts/{id}/balance': 0.2,  # 查询指定账户的余额
                        'account/accounts/{sub-uid}': 1,
                        'account/history': 4,
                        'cross-margin/loan-info': 1,
                        'margin/loan-info': 1,  # 查询借币币息率及额度
                        'fee/fee-rate/get': 1,
                        'order/openOrders': 0.4,
                        'order/orders': 0.4,
                        'order/orders/{id}': 0.4,  # 查询某个订单详情
                        'order/orders/{id}/matchresults': 0.4,  # 查询某个订单的成交明细
                        'order/orders/getClientOrder': 0.4,
                        'order/history': 1,  # 查询当前委托、历史委托
                        'order/matchresults': 1,  # 查询当前成交、历史成交
                        # 'dw/withdraw-virtual/addresses',  # 查询虚拟币提现地址（Deprecated）
                        'query/deposit-withdraw': 1,
                        # 'margin/loan-info',  # duplicate
                        'margin/loan-orders': 0.2,  # 借贷订单
                        'margin/accounts/balance': 0.2,  # 借贷账户详情
                        'cross-margin/loan-orders': 1,  # 查询借币订单
                        'cross-margin/accounts/balance': 1,  # 借币账户详情
                        'points/actions': 1,
                        'points/orders': 1,
                        'subuser/aggregate-balance': 10,
                        'stable-coin/exchange_rate': 1,
                        'stable-coin/quote': 1,
                    },
                    'post': {
                        'account/transfer': 1,  # 资产划转(该节点为母用户和子用户进行资产划转的通用接口。)
                        'futures/transfer': 1,
                        'order/batch-orders': 0.4,
                        'order/orders/place': 0.2,  # 创建并执行一个新订单(一步下单， 推荐使用)
                        'order/orders/submitCancelClientOrder': 0.2,
                        'order/orders/batchCancelOpenOrders': 0.4,
                        # 'order/orders',  # 创建一个新的订单请求 （仅创建订单，不执行下单）
                        # 'order/orders/{id}/place',  # 执行一个订单 （仅执行已创建的订单）
                        'order/orders/{id}/submitcancel': 0.2,  # 申请撤销一个订单请求
                        'order/orders/batchcancel': 0.4,  # 批量撤销订单
                        # 'dw/balance/transfer',  # 资产划转
                        'dw/withdraw/api/create': 1,  # 申请提现虚拟币
                        # 'dw/withdraw-virtual/create',  # 申请提现虚拟币
                        # 'dw/withdraw-virtual/{id}/place',  # 确认申请虚拟币提现（Deprecated）
                        'dw/withdraw-virtual/{id}/cancel': 1,  # 申请取消提现虚拟币
                        'dw/transfer-in/margin': 10,  # 现货账户划入至借贷账户
                        'dw/transfer-out/margin': 10,  # 借贷账户划出至现货账户
                        'margin/orders': 10,  # 申请借贷
                        'margin/orders/{id}/repay': 10,  # 归还借贷
                        'cross-margin/transfer-in': 1,  # 资产划转
                        'cross-margin/transfer-out': 1,  # 资产划转
                        'cross-margin/orders': 1,  # 申请借币
                        'cross-margin/orders/{id}/repay': 1,  # 归还借币
                        'stable-coin/exchange': 1,
                        'subuser/transfer': 10,
                    },
                },
                # ------------------------------------------------------------
                # new api definitions
                # 'https://status.huobigroup.com/api/v2/summary.json': 1,
                # 'https://status-dm.huobigroup.com/api/v2/summary.json': 1,
                # 'https://status-swap.huobigroup.com/api/v2/summary.json': 1,
                # 'https://status-linear-swap.huobigroup.com/api/v2/summary.json': 1,
                'spot': {
                    'public': {
                        'get': {
                            'v2/market-status': 1,
                            'v1/common/symbols': 1,
                            'v1/common/currencys': 1,
                            'v2/reference/currencies': 1,
                            'v1/common/timestamp': 1,
                            'v1/common/exchange': 1,  # order limits
                            # Market Data
                            'market/history/kline': 1,
                            'market/detail/merged': 1,
                            'market/tickers': 1,
                            'market/depth': 1,
                            'market/trade': 1,
                            'market/history/trade': 1,
                            'market/detail/': 1,
                            'market/etp': 1,
                            # ETP
                            'v2/etp/reference': 1,
                            'v2/etp/rebalance': 1,
                        },
                    },
                    'private': {
                        'get': {
                            # Account
                            'v1/account/accounts': 0.2,
                            'v1/account/accounts/{account-id}/balance': 0.2,
                            'v2/account/valuation': 1,
                            'v2/account/asset-valuation': 0.2,
                            'v1/account/history': 4,
                            'v2/account/ledger': 1,
                            'v2/point/account': 5,
                            # Wallet(Deposit and Withdraw)
                            'v2/account/deposit/address': 1,
                            'v2/account/withdraw/quota': 1,
                            'v2/account/withdraw/address': 1,
                            'v2/reference/currencies': 1,
                            'v1/query/deposit-withdraw': 1,
                            # Sub user management
                            'v2/user/api-key': 1,
                            'v2/user/uid': 1,
                            'v2/sub-user/user-list': 1,
                            'v2/sub-user/user-state': 1,
                            'v2/sub-user/account-list': 1,
                            'v2/sub-user/deposit-address': 1,
                            'v2/sub-user/query-deposit': 1,
                            'v1/subuser/aggregate-balance': 10,
                            'v1/account/accounts/{sub-uid}': 1,
                            # Trading
                            'v1/order/openOrders': 0.4,
                            'v1/order/orders/{order-id}': 0.4,
                            'v1/order/orders/getClientOrder': 0.4,
                            'v1/order/orders/{order-id}/matchresults': 0.4,
                            'v1/order/orders': 0.4,
                            'v1/order/history': 1,
                            'v1/order/matchresults': 1,
                            'v2/reference/transact-fee-rate': 1,
                            # Conditional Order
                            'v2/algo-orders/opening': 1,
                            'v2/algo-orders/history': 1,
                            'v2/algo-orders/specific': 1,
                            # Margin Loan(Cross/Isolated)
                            'v1/margin/loan-info': 1,
                            'v1/margin/loan-orders': 0.2,
                            'v1/margin/accounts/balance': 0.2,
                            'v1/cross-margin/loan-info': 1,
                            'v1/cross-margin/loan-orders': 1,
                            'v1/cross-margin/accounts/balance': 1,
                            'v2/account/repayment': 5,
                            # Stable Coin Exchange
                            'v1/stable-coin/quote': 1,
                            # ETP
                            'v2/etp/transactions': 5,
                            'v2/etp/transaction': 5,
                            'v2/etp/limit': 1,
                        },
                        'post': {
                            # Account
                            'v1/account/transfer': 1,
                            'v1/futures/transfer': 1,  # future transfers
                            'v2/point/transfer': 5,
                            'v2/account/transfer': 1,  # swap transfers
                            # Wallet(Deposit and Withdraw)
                            'v1/dw/withdraw/api/create': 1,
                            'v1/dw/withdraw-virtual/{withdraw-id}/cancel': 1,
                            # Sub user management
                            'v2/sub-user/deduct-mode': 1,
                            'v2/sub-user/creation': 1,
                            'v2/sub-user/management': 1,
                            'v2/sub-user/tradable-market': 1,
                            'v2/sub-user/transferability': 1,
                            'v2/sub-user/api-key-generation': 1,
                            'v2/sub-user/api-key-modification': 1,
                            'v2/sub-user/api-key-deletion': 1,
                            'v1/subuser/transfer': 10,
                            # Trading
                            'v1/order/orders/place': 0.2,
                            'v1/order/batch-orders': 0.4,
                            'v1/order/orders/{order-id}/submitcancel': 0.2,
                            'v1/order/orders/submitCancelClientOrder': 0.2,
                            'v1/order/orders/batchCancelOpenOrders': 0.4,
                            'v1/order/orders/batchcancel': 0.4,
                            'v2/algo-orders/cancel-all-after': 1,
                            # Conditional Order
                            'v2/algo-orders': 1,
                            'v2/algo-orders/cancellation': 1,
                            # Margin Loan(Cross/Isolated)
                            'v2/account/repayment': 5,
                            'v1/dw/transfer-in/margin': 10,
                            'v1/dw/transfer-out/margin': 10,
                            'v1/margin/orders': 10,
                            'v1/margin/orders/{order-id}/repay': 10,
                            'v1/cross-margin/transfer-in': 1,
                            'v1/cross-margin/transfer-out': 1,
                            'v1/cross-margin/orders': 1,
                            'v1/cross-margin/orders/{order-id}/repay': 1,
                            # Stable Coin Exchange
                            'v1/stable-coin/exchange': 1,
                            # ETP
                            'v2/etp/creation': 5,
                            'v2/etp/redemption': 5,
                            'v2/etp/{transactId}/cancel': 10,
                            'v2/etp/batch-cancel': 50,
                        },
                    },
                },
                'contract': {
                    'public': {
                        'get': {
                            'api/v1/timestamp': 1,
                            # Future Market Data interface
                            'api/v1/contract_contract_info': 1,
                            'api/v1/contract_index': 1,
                            'api/v1/contract_price_limit': 1,
                            'api/v1/contract_open_interest': 1,
                            'api/v1/contract_delivery_price': 1,
                            'market/depth': 1,
                            'market/bbo': 1,
                            'market/history/kline': 1,
                            'index/market/history/mark_price_kline': 1,
                            'market/detail/merged': 1,
                            'market/detail/batch_merged': 1,
                            'market/trade': 1,
                            'market/history/trade': 1,
                            'api/v1/contract_risk_info': 1,
                            'api/v1/contract_insurance_fund': 1,
                            'api/v1/contract_adjustfactor': 1,
                            'api/v1/contract_his_open_interest': 1,
                            'api/v1/contract_ladder_margin': 1,
                            'api/v1/contract_api_state': 1,
                            'api/v1/contract_elite_account_ratio': 1,
                            'api/v1/contract_elite_position_ratio': 1,
                            'api/v1/contract_liquidation_orders': 1,
                            'api/v1/contract_settlement_records': 1,
                            'index/market/history/index': 1,
                            'index/market/history/basis': 1,
                            'api/v1/contract_estimated_settlement_price': 1,
                            # Swap Market Data interface
                            'swap-api/v1/swap_contract_info': 1,
                            'swap-api/v1/swap_index': 1,
                            'swap-api/v1/swap_price_limit': 1,
                            'swap-api/v1/swap_open_interest': 1,
                            'swap-ex/market/depth': 1,
                            'swap-ex/market/bbo': 1,
                            'swap-ex/market/history/kline': 1,
                            'index/market/history/swap_mark_price_kline': 1,
                            'swap-ex/market/detail/merged': 1,
                            'swap-ex/market/detail/batch_merged': 1,
                            'swap-ex/market/trade': 1,
                            'swap-ex/market/history/trade': 1,
                            'swap-api/v1/swap_risk_info': 1,
                            'swap-api/v1/swap_insurance_fund': 1,
                            'swap-api/v1/swap_adjustfactor': 1,
                            'swap-api/v1/swap_his_open_interest': 1,
                            'swap-api/v1/swap_ladder_margin': 1,
                            'swap-api/v1/swap_api_state': 1,
                            'swap-api/v1/swap_elite_account_ratio': 1,
                            'swap-api/v1/swap_elite_position_ratio': 1,
                            'swap-api/v1/swap_estimated_settlement_price': 1,
                            'swap-api/v1/swap_liquidation_orders': 1,
                            'swap-api/v1/swap_settlement_records': 1,
                            'swap-api/v1/swap_funding_rate': 1,
                            'swap-api/v1/swap_batch_funding_rate': 1,
                            'swap-api/v1/swap_historical_funding_rate': 1,
                            'index/market/history/swap_premium_index_kline': 1,
                            'index/market/history/swap_estimated_rate_kline': 1,
                            'index/market/history/swap_basis': 1,
                            # Swap Market Data interface
                            'linear-swap-api/v1/swap_contract_info': 1,
                            'linear-swap-api/v1/swap_index': 1,
                            'linear-swap-api/v1/swap_price_limit': 1,
                            'linear-swap-api/v1/swap_open_interest': 1,
                            'linear-swap-ex/market/depth': 1,
                            'linear-swap-ex/market/bbo': 1,
                            'linear-swap-ex/market/history/kline': 1,
                            'index/market/history/linear_swap_mark_price_kline': 1,
                            'linear-swap-ex/market/detail/merged': 1,
                            'linear-swap-ex/market/detail/batch_merged': 1,
                            'linear-swap-ex/market/trade': 1,
                            'linear-swap-ex/market/history/trade': 1,
                            'linear-swap-api/v1/swap_risk_info': 1,
                            'swap-api/v1/linear-swap-api/v1/swap_insurance_fund': 1,
                            'linear-swap-api/v1/swap_adjustfactor': 1,
                            'linear-swap-api/v1/swap_cross_adjustfactor': 1,
                            'linear-swap-api/v1/swap_his_open_interest': 1,
                            'linear-swap-api/v1/swap_ladder_margin': 1,
                            'linear-swap-api/v1/swap_cross_ladder_margin': 1,
                            'linear-swap-api/v1/swap_api_state': 1,
                            'linear-swap-api/v1/swap_cross_transfer_state': 1,
                            'linear-swap-api/v1/swap_cross_trade_state': 1,
                            'linear-swap-api/v1/swap_elite_account_ratio': 1,
                            'linear-swap-api/v1/swap_elite_position_ratio': 1,
                            'linear-swap-api/v1/swap_liquidation_orders': 1,
                            'linear-swap-api/v1/swap_settlement_records': 1,
                            'linear-swap-api/v1/swap_funding_rate': 1,
                            'linear-swap-api/v1/swap_batch_funding_rate': 1,
                            'linear-swap-api/v1/swap_historical_funding_rate': 1,
                            'index/market/history/linear_swap_premium_index_kline': 1,
                            'index/market/history/linear_swap_estimated_rate_kline': 1,
                            'index/market/history/linear_swap_basis': 1,
                            'linear-swap-api/v1/swap_estimated_settlement_price': 1,
                        },
                    },
                    'private': {
                        'get': {
                            # Future Account Interface
                            'api/v1/contract_api_trading_status': 1,
                            # Swap Account Interface
                            'swap-api/v1/swap_api_trading_status': 1,
                            # Swap Account Interface
                            'linear-swap-api/v1/swap_api_trading_status': 1,
                        },
                        'post': {
                            # Future Account Interface
                            'api/v1/contract_balance_valuation': 1,
                            'api/v1/contract_account_info': 1,
                            'api/v1/contract_position_info': 1,
                            'api/v1/contract_sub_auth': 1,
                            'api/v1/contract_sub_account_list': 1,
                            'api/v1/contract_sub_account_info_list': 1,
                            'api/v1/contract_sub_account_info': 1,
                            'api/v1/contract_sub_position_info': 1,
                            'api/v1/contract_financial_record': 1,
                            'api/v1/contract_financial_record_exact': 1,
                            'api/v1/contract_user_settlement_records': 1,
                            'api/v1/contract_order_limit': 1,
                            'api/v1/contract_fee': 1,
                            'api/v1/contract_transfer_limit': 1,
                            'api/v1/contract_position_limit': 1,
                            'api/v1/contract_account_position_info': 1,
                            'api/v1/contract_master_sub_transfer': 1,
                            'api/v1/contract_master_sub_transfer_record': 1,
                            'api/v1/contract_available_level_rate': 1,
                            # Future Trade Interface
                            'api/v1/contract_order': 1,
                            'v1/contract_batchorder': 1,
                            'api/v1/contract_cancel': 1,
                            'api/v1/contract_cancelall': 1,
                            'api/v1/contract_switch_lever_rate': 1,
                            'api/v1/lightning_close_position': 1,
                            'api/v1/contract_order_info': 1,
                            'api/v1/contract_order_detail': 1,
                            'api/v1/contract_openorders': 1,
                            'api/v1/contract_hisorders': 1,
                            'api/v1/contract_hisorders_exact': 1,
                            'api/v1/contract_matchresults': 1,
                            'api/v1/contract_matchresults_exact': 1,
                            # Contract Strategy Order Interface
                            'api/v1/contract_trigger_order': 1,
                            'api/v1/contract_trigger_cancel': 1,
                            'api/v1/contract_trigger_cancelall': 1,
                            'api/v1/contract_trigger_openorders': 1,
                            'api/v1/contract_trigger_hisorders': 1,
                            'api/v1/contract_tpsl_order': 1,
                            'api/v1/contract_tpsl_cancel': 1,
                            'api/v1/contract_tpsl_cancelall': 1,
                            'api/v1/contract_tpsl_openorders': 1,
                            'api/v1/contract_tpsl_hisorders': 1,
                            'api/v1/contract_relation_tpsl_order': 1,
                            'api/v1/contract_track_order': 1,
                            'api/v1/contract_track_cancel': 1,
                            'api/v1/contract_track_cancelall': 1,
                            'api/v1/contract_track_openorders': 1,
                            'api/v1/contract_track_hisorders': 1,
                            # Swap Account Interface
                            'swap-api/v1/swap_balance_valuation': 1,
                            'swap-api/v1/swap_account_info': 1,
                            'swap-api/v1/swap_position_info': 1,
                            'swap-api/v1/swap_account_position_info': 1,
                            'swap-api/v1/swap_sub_auth': 1,
                            'swap-api/v1/swap_sub_account_list': 1,
                            'swap-api/v1/swap_sub_account_info_list': 1,
                            'swap-api/v1/swap_sub_account_info': 1,
                            'swap-api/v1/swap_sub_position_info': 1,
                            'swap-api/v1/swap_financial_record': 1,
                            'swap-api/v1/swap_financial_record_exact': 1,
                            'swap-api/v1/swap_user_settlement_records': 1,
                            'swap-api/v1/swap_available_level_rate': 1,
                            'swap-api/v1/swap_order_limit': 1,
                            'swap-api/v1/swap_fee': 1,
                            'swap-api/v1/swap_transfer_limit': 1,
                            'swap-api/v1/swap_position_limit': 1,
                            'swap-api/v1/swap_master_sub_transfer': 1,
                            'swap-api/v1/swap_master_sub_transfer_record': 1,
                            # Swap Trade Interface
                            'swap-api/v1/swap_order': 1,
                            'swap-api/v1/swap_batchorder': 1,
                            'swap-api/v1/swap_cancel': 1,
                            'swap-api/v1/swap_cancelall': 1,
                            'swap-api/v1/swap_lightning_close_position': 1,
                            'swap-api/v1/swap_switch_lever_rate': 1,
                            'swap-api/v1/swap_order_info': 1,
                            'swap-api/v1/swap_order_detail': 1,
                            'swap-api/v1/swap_openorders': 1,
                            'swap-api/v1/swap_hisorders': 1,
                            'swap-api/v1/swap_hisorders_exact': 1,
                            'swap-api/v1/swap_matchresults': 1,
                            'swap-api/v1/swap_matchresults_exact': 1,
                            # Swap Strategy Order Interface
                            'swap-api/v1/swap_trigger_order': 1,
                            'swap-api/v1/swap_trigger_cancel': 1,
                            'swap-api/v1/swap_trigger_cancelall': 1,
                            'swap-api/v1/swap_trigger_openorders': 1,
                            'swap-api/v1/swap_trigger_hisorders': 1,
                            'swap-api/v1/swap_tpsl_order': 1,
                            'swap-api/v1/swap_tpsl_cancel': 1,
                            'swap-api/v1/swap_tpsl_cancelall': 1,
                            'swap-api/v1/swap_tpsl_openorders': 1,
                            'swap-api/v1/swap_tpsl_hisorders': 1,
                            'swap-api/v1/swap_relation_tpsl_order': 1,
                            'swap-api/v1/swap_track_order': 1,
                            'swap-api/v1/swap_track_cancel': 1,
                            'swap-api/v1/swap_track_cancelall': 1,
                            'swap-api/v1/swap_track_openorders': 1,
                            'swap-api/v1/swap_track_hisorders': 1,
                            # Swap Account Interface
                            'linear-swap-api/v1/swap_balance_valuation': 1,
                            'linear-swap-api/v1/swap_account_info': 1,
                            'linear-swap-api/v1/swap_cross_account_info': 1,
                            'linear-swap-api/v1/swap_position_info': 1,
                            'linear-swap-api/v1/swap_cross_position_info': 1,
                            'linear-swap-api/v1/swap_account_position_info': 1,
                            'linear-swap-api/v1/swap_cross_account_position_info': 1,
                            'linear-swap-api/v1/swap_sub_auth': 1,
                            'linear-swap-api/v1/swap_sub_account_list': 1,
                            'linear-swap-api/v1/swap_cross_sub_account_list': 1,
                            'linear-swap-api/v1/swap_sub_account_info_list': 1,
                            'linear-swap-api/v1/swap_cross_sub_account_info_list': 1,
                            'linear-swap-api/v1/swap_sub_account_info': 1,
                            'linear-swap-api/v1/swap_cross_sub_account_info': 1,
                            'linear-swap-api/v1/swap_sub_position_info': 1,
                            'linear-swap-api/v1/swap_cross_sub_position_info': 1,
                            'linear-swap-api/v1/swap_financial_record': 1,
                            'linear-swap-api/v1/swap_financial_record_exact': 1,
                            'linear-swap-api/v1/swap_user_settlement_records': 1,
                            'linear-swap-api/v1/swap_cross_user_settlement_records': 1,
                            'linear-swap-api/v1/swap_available_level_rate': 1,
                            'linear-swap-api/v1/swap_cross_available_level_rate': 1,
                            'linear-swap-api/v1/swap_order_limit': 1,
                            'linear-swap-api/v1/swap_fee': 1,
                            'linear-swap-api/v1/swap_transfer_limit': 1,
                            'linear-swap-api/v1/swap_cross_transfer_limit': 1,
                            'linear-swap-api/v1/swap_position_limit': 1,
                            'linear-swap-api/v1/swap_cross_position_limit': 1,
                            'linear-swap-api/v1/swap_master_sub_transfer': 1,
                            'linear-swap-api/v1/swap_master_sub_transfer_record': 1,
                            'linear-swap-api/v1/swap_transfer_inner': 1,
                            # Swap Trade Interface
                            'linear-swap-api/v1/swap_order': 1,
                            'linear-swap-api/v1/swap_cross_order': 1,
                            'linear-swap-api/v1/swap_batchorder': 1,
                            'linear-swap-api/v1/swap_cross_batchorder': 1,
                            'linear-swap-api/v1/swap_cancel': 1,
                            'linear-swap-api/v1/swap_cross_cancel': 1,
                            'linear-swap-api/v1/swap_cancelall': 1,
                            'linear-swap-api/v1/swap_cross_cancelall': 1,
                            'linear-swap-api/v1/swap_switch_lever_rate': 1,
                            'linear-swap-api/v1/swap_cross_switch_lever_rate': 1,
                            'linear-swap-api/v1/swap_lightning_close_position': 1,
                            'linear-swap-api/v1/swap_cross_lightning_close_position': 1,
                            'linear-swap-api/v1/swap_order_info': 1,
                            'linear-swap-api/v1/swap_cross_order_info': 1,
                            'linear-swap-api/v1/swap_order_detail': 1,
                            'linear-swap-api/v1/swap_cross_order_detail': 1,
                            'linear-swap-api/v1/swap_openorders': 1,
                            'linear-swap-api/v1/swap_cross_openorders': 1,
                            'linear-swap-api/v1/swap_hisorders': 1,
                            'linear-swap-api/v1/swap_cross_hisorders': 1,
                            'linear-swap-api/v1/swap_hisorders_exact': 1,
                            'linear-swap-api/v1/swap_cross_hisorders_exact': 1,
                            'linear-swap-api/v1/swap_matchresults': 1,
                            'linear-swap-api/v1/swap_cross_matchresults': 1,
                            'linear-swap-api/v1/swap_matchresults_exact': 1,
                            'linear-swap-api/v1/swap_cross_matchresults_exact': 1,
                            # Swap Strategy Order Interface
                            'linear-swap-api/v1/swap_trigger_order': 1,
                            'linear-swap-api/v1/swap_cross_trigger_order': 1,
                            'linear-swap-api/v1/swap_trigger_cancel': 1,
                            'linear-swap-api/v1/swap_cross_trigger_cancel': 1,
                            'linear-swap-api/v1/swap_trigger_cancelall': 1,
                            'linear-swap-api/v1/swap_cross_trigger_cancelall': 1,
                            'linear-swap-api/v1/swap_trigger_openorders': 1,
                            'linear-swap-api/v1/swap_cross_trigger_openorders': 1,
                            'linear-swap-api/v1/swap_trigger_hisorders': 1,
                            'linear-swap-api/v1/swap_cross_trigger_hisorders': 1,
                            'linear-swap-api/v1/swap_tpsl_order': 1,
                            'linear-swap-api/v1/swap_cross_tpsl_order': 1,
                            'linear-swap-api/v1/swap_tpsl_cancel': 1,
                            'linear-swap-api/v1/swap_cross_tpsl_cancel': 1,
                            'linear-swap-api/v1/swap_tpsl_cancelall': 1,
                            'linear-swap-api/v1/swap_cross_tpsl_cancelall': 1,
                            'linear-swap-api/v1/swap_tpsl_openorders': 1,
                            'linear-swap-api/v1/swap_cross_tpsl_openorders': 1,
                            'linear-swap-api/v1/swap_tpsl_hisorders': 1,
                            'linear-swap-api/v1/swap_cross_tpsl_hisorders': 1,
                            'linear-swap-api/v1/swap_relation_tpsl_order': 1,
                            'linear-swap-api/v1/swap_cross_relation_tpsl_order': 1,
                            'linear-swap-api/v1/swap_track_order': 1,
                            'linear-swap-api/v1/swap_cross_track_order': 1,
                            'linear-swap-api/v1/swap_track_cancel': 1,
                            'linear-swap-api/v1/swap_cross_track_cancel': 1,
                            'linear-swap-api/v1/swap_track_cancelall': 1,
                            'linear-swap-api/v1/swap_cross_track_cancelall': 1,
                            'linear-swap-api/v1/swap_track_openorders': 1,
                            'linear-swap-api/v1/swap_cross_track_openorders': 1,
                            'linear-swap-api/v1/swap_track_hisorders': 1,
                            'linear-swap-api/v1/swap_cross_track_hisorders': 1,
                        },
                    },
                },
            },
            'fees': {
                'trading': {
                    'feeSide': 'get',
                    'tierBased': False,
                    'percentage': True,
                    'maker': self.parse_number('0.002'),
                    'taker': self.parse_number('0.002'),
                },
            },
            'exceptions': {
                'broad': {
                    'contract is restricted of closing positions on API.  Please contact customer service': OnMaintenance,
                    'maintain': OnMaintenance,
                },
                'exact': {
                    # err-code
                    '1017': OrderNotFound,  # {"status":"error","err_code":1017,"err_msg":"Order doesnt exist.","ts":1640550859242}
                    '1066': BadSymbol,  # {"status":"error","err_code":1066,"err_msg":"The symbol field cannot be empty. Please re-enter.","ts":1640550819147}
                    '1013': BadSymbol,  # {"status":"error","err_code":1013,"err_msg":"This contract symbol doesnt exist.","ts":1640550459583}
                    '1094': InvalidOrder,  # {"status":"error","err_code":1094,"err_msg":"The leverage cannot be empty, please switch the leverage or contact customer service","ts":1640496946243}
                    'bad-request': BadRequest,
                    'validation-format-error': BadRequest,  # {"status":"error","err-code":"validation-format-error","err-msg":"Format Error: order-id.","data":null}
                    'validation-constraints-required': BadRequest,  # {"status":"error","err-code":"validation-constraints-required","err-msg":"Field is missing: client-order-id.","data":null}
                    'base-date-limit-error': BadRequest,  # {"status":"error","err-code":"base-date-limit-error","err-msg":"date less than system limit","data":null}
                    'api-not-support-temp-addr': PermissionDenied,  # {"status":"error","err-code":"api-not-support-temp-addr","err-msg":"API withdrawal does not support temporary addresses","data":null}
                    'timeout': RequestTimeout,  # {"ts":1571653730865,"status":"error","err-code":"timeout","err-msg":"Request Timeout"}
                    'gateway-internal-error': ExchangeNotAvailable,  # {"status":"error","err-code":"gateway-internal-error","err-msg":"Failed to load data. Try again later.","data":null}
                    'account-frozen-balance-insufficient-error': InsufficientFunds,  # {"status":"error","err-code":"account-frozen-balance-insufficient-error","err-msg":"trade account balance is not enough, left: `0.0027`","data":null}
                    'invalid-amount': InvalidOrder,  # eg "Paramemter `amount` is invalid."
                    'order-limitorder-amount-min-error': InvalidOrder,  # limit order amount error, min: `0.001`
                    'order-limitorder-amount-max-error': InvalidOrder,  # market order amount error, max: `1000000`
                    'order-marketorder-amount-min-error': InvalidOrder,  # market order amount error, min: `0.01`
                    'order-limitorder-price-min-error': InvalidOrder,  # limit order price error
                    'order-limitorder-price-max-error': InvalidOrder,  # limit order price error
                    'order-holding-limit-failed': InvalidOrder,  # {"status":"error","err-code":"order-holding-limit-failed","err-msg":"Order failed, exceeded the holding limit of self currency","data":null}
                    'order-orderprice-precision-error': InvalidOrder,  # {"status":"error","err-code":"order-orderprice-precision-error","err-msg":"order price precision error, scale: `4`","data":null}
                    'order-etp-nav-price-max-error': InvalidOrder,  # {"status":"error","err-code":"order-etp-nav-price-max-error","err-msg":"Order price cannot be higher than 5% of NAV","data":null}
                    'order-orderstate-error': OrderNotFound,  # canceling an already canceled order
                    'order-queryorder-invalid': OrderNotFound,  # querying a non-existent order
                    'order-update-error': ExchangeNotAvailable,  # undocumented error
                    'api-signature-check-failed': AuthenticationError,
                    'api-signature-not-valid': AuthenticationError,  # {"status":"error","err-code":"api-signature-not-valid","err-msg":"Signature not valid: Incorrect Access key [Access key错误]","data":null}
                    'base-record-invalid': OrderNotFound,  # https://github.com/ccxt/ccxt/issues/5750
                    'base-symbol-trade-disabled': BadSymbol,  # {"status":"error","err-code":"base-symbol-trade-disabled","err-msg":"Trading is disabled for self symbol","data":null}
                    'base-symbol-error': BadSymbol,  # {"status":"error","err-code":"base-symbol-error","err-msg":"The symbol is invalid","data":null}
                    'system-maintenance': OnMaintenance,  # {"status": "error", "err-code": "system-maintenance", "err-msg": "System is in maintenance!", "data": null}
                    # err-msg
                    'invalid symbol': BadSymbol,  # {"ts":1568813334794,"status":"error","err-code":"invalid-parameter","err-msg":"invalid symbol"}
                    'symbol trade not open now': BadSymbol,  # {"ts":1576210479343,"status":"error","err-code":"invalid-parameter","err-msg":"symbol trade not open now"}
                    'require-symbol': BadSymbol,  # {"status":"error","err-code":"require-symbol","err-msg":"Parameter `symbol` is required.","data":null}
                },
            },
            'precisionMode': TICK_SIZE,
            'options': {
                'defaultType': 'spot',  # spot, future, swap
                'defaultSubType': 'inverse',  # inverse, linear
                'defaultNetwork': 'ERC20',
                'networks': {
                    'ETH': 'erc20',
                    'TRX': 'trc20',
                    'HRC20': 'hrc20',
                    'HECO': 'hrc20',
                    'HT': 'hrc20',
                    'ALGO': 'algo',
                    'OMNI': '',
                },
                # https://github.com/ccxt/ccxt/issues/5376
                'fetchOrdersByStatesMethod': 'spot_private_get_v1_order_orders',  # 'spot_private_get_v1_order_history'  # https://github.com/ccxt/ccxt/pull/5392
                'createMarketBuyOrderRequiresPrice': True,
                'language': 'en-US',
                'broker': {
                    'id': 'AA03022abc',
                },
                'accountsByType': {
                    'spot': 'pro',
                    'future': 'futures',
                },
                'typesByAccount': {
                    'pro': 'spot',
                    'futures': 'future',
                },
                'spot': {
                    'stopOrderTypes': {
                        'stop-limit': True,
                        'buy-stop-limit': True,
                        'sell-stop-limit': True,
                        'stop-limit-fok': True,
                        'buy-stop-limit-fok': True,
                        'sell-stop-limit-fok': True,
                    },
                    'limitOrderTypes': {
                        'limit': True,
                        'buy-limit': True,
                        'sell-limit': True,
                        'ioc': True,
                        'buy-ioc': True,
                        'sell-ioc': True,
                        'limit-maker': True,
                        'buy-limit-maker': True,
                        'sell-limit-maker': True,
                        'stop-limit': True,
                        'buy-stop-limit': True,
                        'sell-stop-limit': True,
                        'limit-fok': True,
                        'buy-limit-fok': True,
                        'sell-limit-fok': True,
                        'stop-limit-fok': True,
                        'buy-stop-limit-fok': True,
                        'sell-stop-limit-fok': True,
                    },
                },
            },
            'commonCurrencies': {
                # https://github.com/ccxt/ccxt/issues/6081
                # https://github.com/ccxt/ccxt/issues/3365
                # https://github.com/ccxt/ccxt/issues/2873
                'GET': 'Themis',  # conflict with GET(Guaranteed Entrance Token, GET Protocol)
                'GTC': 'Game.com',  # conflict with Gitcoin and Gastrocoin
                'HIT': 'HitChain',
                'HOT': 'Hydro Protocol',  # conflict with HOT(Holo) https://github.com/ccxt/ccxt/issues/4929
                # https://github.com/ccxt/ccxt/issues/7399
                # https://coinmarketcap.com/currencies/pnetwork/
                # https://coinmarketcap.com/currencies/penta/markets/
                # https://en.cryptonomist.ch/blog/eidoo/the-edo-to-pnt-upgrade-what-you-need-to-know-updated/
                'PNT': 'Penta',
                'SBTC': 'Super Bitcoin',
                'BIFI': 'Bitcoin File',  # conflict with Beefy.Finance https://github.com/ccxt/ccxt/issues/8706
            },
        })

    async def fetch_time(self, params={}):
        options = self.safe_value(self.options, 'fetchTime', {})
        defaultType = self.safe_string(self.options, 'defaultType', 'spot')
        type = self.safe_string(options, 'type', defaultType)
        type = self.safe_string(params, 'type', type)
        method = 'spotPublicGetV1CommonTimestamp'
        if (type == 'future') or (type == 'swap'):
            method = 'contractPublicGetApiV1Timestamp'
        response = await getattr(self, method)(params)
        #
        # spot
        #
        #     {"status":"ok","data":1637504261099}
        #
        # future, swap
        #
        #     {"status":"ok","ts":1637504164707}
        #
        return self.safe_integer_2(response, 'data', 'ts')

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

    async def fetch_trading_fee(self, symbol, params={}):
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'symbols': market['id'],  # trading symbols comma-separated
        }
        response = await self.spotPrivateGetV2ReferenceTransactFeeRate(self.extend(request, params))
        #
        #     {
        #         "code":200,
        #         "data":[
        #             {
        #                 "symbol":"btcusdt",
        #                 "actualMakerRate":"0.002",
        #                 "actualTakerRate":"0.002",
        #                 "takerFeeRate":"0.002",
        #                 "makerFeeRate":"0.002"
        #             }
        #         ],
        #         "success":true
        #     }
        #
        data = self.safe_value(response, 'data', [])
        first = self.safe_value(data, 0, {})
        return self.parse_trading_fee(first, market)

    async def fetch_trading_limits(self, symbols=None, params={}):
        # self method should not be called directly, use loadTradingLimits() instead
        #  by default it will try load withdrawal fees of all currencies(with separate requests)
        #  however if you define symbols = ['ETH/BTC', 'LTC/BTC'] in args it will only load those
        await self.load_markets()
        if symbols is None:
            symbols = self.symbols
        result = {}
        for i in range(0, len(symbols)):
            symbol = symbols[i]
            result[symbol] = await self.fetch_trading_limits_by_id(self.market_id(symbol), params)
        return result

    async def fetch_trading_limits_by_id(self, id, params={}):
        request = {
            'symbol': id,
        }
        response = await self.spotPublicGetV1CommonExchange(self.extend(request, params))
        #
        #     {status:   "ok",
        #         data: {                                 symbol: "aidocbtc",
        #                              'buy-limit-must-less-than':  1.1,
        #                          'sell-limit-must-greater-than':  0.9,
        #                         'limit-order-must-greater-than':  1,
        #                            'limit-order-must-less-than':  5000000,
        #                    'market-buy-order-must-greater-than':  0.0001,
        #                       'market-buy-order-must-less-than':  100,
        #                   'market-sell-order-must-greater-than':  1,
        #                      'market-sell-order-must-less-than':  500000,
        #                       'circuit-break-when-greater-than':  10000,
        #                          'circuit-break-when-less-than':  10,
        #                 'market-sell-order-rate-must-less-than':  0.1,
        #                  'market-buy-order-rate-must-less-than':  0.1        }}
        #
        return self.parse_trading_limits(self.safe_value(response, 'data', {}))

    def parse_trading_limits(self, limits, symbol=None, params={}):
        #
        #   {                                 symbol: "aidocbtc",
        #                  'buy-limit-must-less-than':  1.1,
        #              'sell-limit-must-greater-than':  0.9,
        #             'limit-order-must-greater-than':  1,
        #                'limit-order-must-less-than':  5000000,
        #        'market-buy-order-must-greater-than':  0.0001,
        #           'market-buy-order-must-less-than':  100,
        #       'market-sell-order-must-greater-than':  1,
        #          'market-sell-order-must-less-than':  500000,
        #           'circuit-break-when-greater-than':  10000,
        #              'circuit-break-when-less-than':  10,
        #     'market-sell-order-rate-must-less-than':  0.1,
        #      'market-buy-order-rate-must-less-than':  0.1        }
        #
        return {
            'info': limits,
            'limits': {
                'amount': {
                    'min': self.safe_number(limits, 'limit-order-must-greater-than'),
                    'max': self.safe_number(limits, 'limit-order-must-less-than'),
                },
            },
        }

    def cost_to_precision(self, symbol, cost):
        return self.decimal_to_precision(cost, TRUNCATE, self.markets[symbol]['precision']['cost'], self.precisionMode)

    async def fetch_markets(self, params={}):
        options = self.safe_value(self.options, 'fetchMarkets', {})
        defaultType = self.safe_string(self.options, 'defaultType', 'spot')
        type = self.safe_string(options, 'type', defaultType)
        type = self.safe_string(params, 'type', type)
        if (type != 'spot') and (type != 'future') and (type != 'swap'):
            raise ExchangeError(self.id + " does not support '" + type + "' type, set exchange.options['defaultType'] to 'spot', 'future', 'swap'")  # eslint-disable-line quotes
        method = 'spotPublicGetV1CommonSymbols'
        query = self.omit(params, ['type', 'subType'])
        spot = (type == 'spot')
        contract = (type != 'spot')
        future = (type == 'future')
        swap = (type == 'swap')
        linear = None
        inverse = None
        if contract:
            defaultSubType = self.safe_string(self.options, 'defaultSubType', 'inverse')
            subType = self.safe_string(options, 'subType', defaultSubType)
            subType = self.safe_string(params, 'subType', subType)
            if (subType != 'inverse') and (subType != 'linear'):
                raise ExchangeError(self.id + " does not support '" + subType + "' type, set exchange.options['defaultSubType'] to 'inverse' or 'linear'")  # eslint-disable-line quotes
            linear = (subType == 'linear')
            inverse = (subType == 'inverse') or future
            if future:
                method = 'contractPublicGetApiV1ContractContractInfo'
            elif swap:
                if inverse:
                    method = 'contractPublicGetSwapApiV1SwapContractInfo'
                elif linear:
                    method = 'contractPublicGetLinearSwapApiV1SwapContractInfo'
        response = await getattr(self, method)(query)
        #
        # spot
        #
        #     {
        #         "status":"ok",
        #         "data":[
        #             {
        #                 "base-currency":"xrp3s",
        #                 "quote-currency":"usdt",
        #                 "price-precision":4,
        #                 "amount-precision":4,
        #                 "symbol-partition":"innovation",
        #                 "symbol":"xrp3susdt",
        #                 "state":"online",
        #                 "value-precision":8,
        #                 "min-order-amt":0.01,
        #                 "max-order-amt":1616.4353,
        #                 "min-order-value":5,
        #                 "limit-order-min-order-amt":0.01,
        #                 "limit-order-max-order-amt":1616.4353,
        #                 "limit-order-max-buy-amt":1616.4353,
        #                 "limit-order-max-sell-amt":1616.4353,
        #                 "sell-market-min-order-amt":0.01,
        #                 "sell-market-max-order-amt":1616.4353,
        #                 "buy-market-max-order-value":2500,
        #                 "max-order-value":2500,
        #                 "underlying":"xrpusdt",
        #                 "mgmt-fee-rate":0.035000000000000000,
        #                 "charge-time":"23:55:00",
        #                 "rebal-time":"00:00:00",
        #                 "rebal-threshold":-5,
        #                 "init-nav":10.000000000000000000,
        #                 "api-trading":"enabled",
        #                 "tags":"etp,nav,holdinglimit"
        #             },
        #         ]
        #     }
        #
        # future
        #
        #     {
        #         "status":"ok",
        #         "data":[
        #             {
        #                 "symbol":"BTC",
        #                 "contract_code":"BTC211126",
        #                 "contract_type":"self_week",
        #                 "contract_size":100.000000000000000000,
        #                 "price_tick":0.010000000000000000,
        #                 "delivery_date":"20211126",
        #                 "delivery_time":"1637913600000",
        #                 "create_date":"20211112",
        #                 "contract_status":1,
        #                 "settlement_time":"1637481600000"
        #             },
        #         ],
        #         "ts":1637474595140
        #     }
        #
        # swaps
        #
        #     {
        #         "status":"ok",
        #         "data":[
        #             {
        #                 "symbol":"BTC",
        #                 "contract_code":"BTC-USDT",
        #                 "contract_size":0.001000000000000000,
        #                 "price_tick":0.100000000000000000,
        #                 "delivery_time":"",
        #                 "create_date":"20201021",
        #                 "contract_status":1,
        #                 "settlement_date":"1637481600000",
        #                 "support_margin_mode":"all",  # isolated
        #             },
        #         ],
        #         "ts":1637474774467
        #     }
        #
        markets = self.safe_value(response, 'data')
        numMarkets = len(markets)
        if numMarkets < 1:
            raise NetworkError(self.id + ' fetchMarkets() returned an empty response: ' + self.json(markets))
        result = []
        for i in range(0, len(markets)):
            market = markets[i]
            baseId = None
            quoteId = None
            settleId = None
            id = None
            if contract:
                id = self.safe_string(market, 'contract_code')
                if swap:
                    parts = id.split('-')
                    baseId = self.safe_string(market, 'symbol')
                    quoteId = self.safe_string(parts, 1)
                    settleId = baseId if inverse else quoteId
                elif future:
                    baseId = self.safe_string(market, 'symbol')
                    quoteId = 'USD'
                    settleId = baseId
            else:
                baseId = self.safe_string(market, 'base-currency')
                quoteId = self.safe_string(market, 'quote-currency')
                id = baseId + quoteId
            base = self.safe_currency_code(baseId)
            quote = self.safe_currency_code(quoteId)
            settle = self.safe_currency_code(settleId)
            symbol = base + '/' + quote
            expiry = None
            if contract:
                if inverse:
                    symbol += ':' + base
                elif linear:
                    symbol += ':' + quote
                if future:
                    expiry = self.safe_integer(market, 'delivery_time')
                    symbol += '-' + self.yymmdd(expiry)
            contractSize = self.safe_number(market, 'contract_size')
            pricePrecision = None
            amountPrecision = None
            costPrecision = None
            if spot:
                pricePrecision = self.safe_string(market, 'price-precision')
                pricePrecision = self.parse_number('1e-' + pricePrecision)
                amountPrecision = self.safe_string(market, 'amount-precision')
                amountPrecision = self.parse_number('1e-' + amountPrecision)
                costPrecision = self.safe_string(market, 'value-precision')
                costPrecision = self.parse_number('1e-' + costPrecision)
            else:
                pricePrecision = self.safe_number(market, 'price_tick')
                amountPrecision = 1
            precision = {
                'amount': amountPrecision,
                'price': pricePrecision,
                'cost': costPrecision,
            }
            maker = None
            taker = None
            if spot:
                maker = 0 if (base == 'OMG') else 0.2 / 100
                taker = 0 if (base == 'OMG') else 0.2 / 100
            minAmount = self.safe_number(market, 'min-order-amt', math.pow(10, -precision['amount']))
            maxAmount = self.safe_number(market, 'max-order-amt')
            minCost = self.safe_number(market, 'min-order-value', 0)
            active = None
            if spot:
                state = self.safe_string(market, 'state')
                active = (state == 'online')
            elif contract:
                contractStatus = self.safe_integer(market, 'contract_status')
                active = (contractStatus == 1)
            # 0 Delisting
            # 1 Listing
            # 2 Pending Listing
            # 3 Suspension
            # 4 Suspending of Listing
            # 5 In Settlement
            # 6 Delivering
            # 7 Settlement Completed
            # 8 Delivered
            # 9 Suspending of Trade
            result.append({
                'id': id,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'settle': settle,
                'baseId': baseId,
                'quoteId': quoteId,
                'settleId': settleId,
                'type': type,
                'contract': contract,
                'spot': spot,
                'future': future,
                'swap': swap,
                'linear': linear,
                'inverse': inverse,
                'expiry': expiry,
                'expiryDatetime': self.iso8601(expiry),
                'contractSize': contractSize,
                'active': active,
                'precision': precision,
                'taker': taker,
                'maker': maker,
                'limits': {
                    'amount': {
                        'min': minAmount,
                        'max': maxAmount,
                    },
                    'price': {
                        'min': pricePrecision,
                        'max': None,
                    },
                    'cost': {
                        'min': minCost,
                        'max': None,
                    },
                    'leverage': {
                        'max': self.safe_number(market, 'leverage-ratio', 1),
                        'superMax': self.safe_number(market, 'super-margin-leverage-ratio', 1),
                    },
                },
                'info': market,
            })
        return result

    def parse_ticker(self, ticker, market=None):
        #
        # fetchTicker
        #
        #     {
        #         "amount": 26228.672978342216,
        #         "open": 9078.95,
        #         "close": 9146.86,
        #         "high": 9155.41,
        #         "id": 209988544334,
        #         "count": 265846,
        #         "low": 8988.0,
        #         "version": 209988544334,
        #         "ask": [9146.87, 0.156134],
        #         "vol": 2.3822168242201668E8,
        #         "bid": [9146.86, 0.080758],
        #     }
        #
        # fetchTickers
        #
        #     {
        #         symbol: "bhdht",
        #         open:  2.3938,
        #         high:  2.4151,
        #         low:  2.3323,
        #         close:  2.3909,
        #         amount:  628.992,
        #         vol:  1493.71841095,
        #         count:  2088,
        #         bid:  2.3643,
        #         bidSize:  0.7136,
        #         ask:  2.4061,
        #         askSize:  0.4156
        #     }
        #
        marketId = self.safe_string_2(ticker, 'symbol', 'contract_code')
        symbol = self.safe_symbol(marketId, market)
        timestamp = self.safe_integer(ticker, 'ts')
        bid = None
        bidVolume = None
        ask = None
        askVolume = None
        if 'bid' in ticker:
            if isinstance(ticker['bid'], list):
                bid = self.safe_number(ticker['bid'], 0)
                bidVolume = self.safe_number(ticker['bid'], 1)
            else:
                bid = self.safe_number(ticker, 'bid')
                bidVolume = self.safe_value(ticker, 'bidSize')
        if 'ask' in ticker:
            if isinstance(ticker['ask'], list):
                ask = self.safe_number(ticker['ask'], 0)
                askVolume = self.safe_number(ticker['ask'], 1)
            else:
                ask = self.safe_number(ticker, 'ask')
                askVolume = self.safe_value(ticker, 'askSize')
        open = self.safe_number(ticker, 'open')
        close = self.safe_number(ticker, 'close')
        baseVolume = self.safe_number(ticker, 'amount')
        quoteVolume = self.safe_number(ticker, 'vol')
        vwap = self.vwap(baseVolume, quoteVolume)
        return self.safe_ticker({
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'high': self.safe_number(ticker, 'high'),
            'low': self.safe_number(ticker, 'low'),
            'bid': bid,
            'bidVolume': bidVolume,
            'ask': ask,
            'askVolume': askVolume,
            'vwap': vwap,
            'open': open,
            'close': close,
            'last': close,
            'previousClose': None,
            'change': None,
            'percentage': None,
            'average': None,
            'baseVolume': baseVolume,
            'quoteVolume': quoteVolume,
            'info': ticker,
        }, market)

    async def fetch_ticker(self, symbol, params={}):
        await self.load_markets()
        market = self.market(symbol)
        request = {}
        fieldName = 'symbol'
        method = 'spotPublicGetMarketDetailMerged'
        if market['future']:
            method = 'contractPublicGetMarketDetailMerged'
        elif market['swap']:
            if market['inverse']:
                method = 'contractPublicGetSwapExMarketDetailMerged'
            elif market['linear']:
                method = 'contractPublicGetLinearSwapExMarketDetailMerged'
            fieldName = 'contract_code'
        request[fieldName] = market['id']
        response = await getattr(self, method)(self.extend(request, params))
        #
        # spot
        #
        #     {
        #         "status": "ok",
        #         "ch": "market.btcusdt.detail.merged",
        #         "ts": 1583494336669,
        #         "tick": {
        #             "amount": 26228.672978342216,
        #             "open": 9078.95,
        #             "close": 9146.86,
        #             "high": 9155.41,
        #             "id": 209988544334,
        #             "count": 265846,
        #             "low": 8988.0,
        #             "version": 209988544334,
        #             "ask": [9146.87, 0.156134],
        #             "vol": 2.3822168242201668E8,
        #             "bid": [9146.86, 0.080758],
        #         }
        #     }
        #
        # future, swap
        #
        #     {
        #         "ch":"market.BTC211126.detail.merged",
        #         "status":"ok",
        #         "tick":{
        #             "amount":"669.3385682049668320322569544150680718474",
        #             "ask":[59117.44,48],
        #             "bid":[59082,48],
        #             "close":"59087.97",
        #             "count":5947,
        #             "high":"59892.62",
        #             "id":1637502670,
        #             "low":"57402.87",
        #             "open":"57638",
        #             "ts":1637502670059,
        #             "vol":"394598"
        #         },
        #         "ts":1637502670059
        #     }
        #
        tick = self.safe_value(response, 'tick', {})
        ticker = self.parse_ticker(tick, market)
        timestamp = self.safe_integer(response, 'ts')
        ticker['timestamp'] = timestamp
        ticker['datetime'] = self.iso8601(timestamp)
        return ticker

    async def fetch_tickers(self, symbols=None, params={}):
        await self.load_markets()
        options = self.safe_value(self.options, 'fetchTickers', {})
        defaultType = self.safe_string(self.options, 'defaultType', 'spot')
        type = self.safe_string(options, 'type', defaultType)
        type = self.safe_string(params, 'type', type)
        method = 'spotPublicGetMarketTickers'
        if type == 'future':
            method = 'contractPublicGetMarketDetailBatchMerged'
        elif type == 'swap':
            defaultSubType = self.safe_string(self.options, 'defaultSubType', 'inverse')
            subType = self.safe_string(options, 'subType', defaultSubType)
            subType = self.safe_string(params, 'subType', subType)
            if subType == 'inverse':
                method = 'contractPublicGetSwapExMarketDetailBatchMerged'
            elif subType == 'linear':
                method = 'contractPublicGetLinearSwapExMarketDetailBatchMerged'
        query = self.omit(params, ['type', 'subType'])
        response = await getattr(self, method)(query)
        #
        # spot
        #
        #     {
        #         "data":[
        #             {
        #                 "symbol":"hbcbtc",
        #                 "open":5.313E-5,
        #                 "high":5.34E-5,
        #                 "low":5.112E-5,
        #                 "close":5.175E-5,
        #                 "amount":1183.87,
        #                 "vol":0.0618599229,
        #                 "count":205,
        #                 "bid":5.126E-5,
        #                 "bidSize":5.25,
        #                 "ask":5.214E-5,
        #                 "askSize":150.0
        #             },
        #         ],
        #         "status":"ok",
        #         "ts":1639547261293
        #     }
        #
        # future
        #
        #     {
        #         "status":"ok",
        #         "ticks":[
        #             {
        #                 "id":1637504679,
        #                 "ts":1637504679372,
        #                 "ask":[0.10644,100],
        #                 "bid":[0.10624,26],
        #                 "symbol":"TRX_CW",
        #                 "open":"0.10233",
        #                 "close":"0.10644",
        #                 "low":"0.1017",
        #                 "high":"0.10725",
        #                 "amount":"2340267.415144052378486261756692535687481566",
        #                 "count":882,
        #                 "vol":"24706"
        #             }
        #         ],
        #         "ts":1637504679376
        #     }
        #
        tickers = self.safe_value_2(response, 'data', 'ticks', [])
        timestamp = self.safe_integer(response, 'ts')
        result = {}
        for i in range(0, len(tickers)):
            ticker = self.parse_ticker(tickers[i])
            symbol = ticker['symbol']
            ticker['timestamp'] = timestamp
            ticker['datetime'] = self.iso8601(timestamp)
            result[symbol] = ticker
        return self.filter_by_array(result, 'symbol', symbols)

    async def fetch_order_book(self, symbol, limit=None, params={}):
        await self.load_markets()
        market = self.market(symbol)
        request = {
            #
            # from the API docs
            #
            #     to get depth data within step 150, use step0, step1, step2, step3, step4, step5, step14, step15（merged depth data 0-5,14-15, when step is 0，depth data will not be merged
            #     to get depth data within step 20, use step6, step7, step8, step9, step10, step11, step12, step13(merged depth data 7-13), when step is 6, depth data will not be merged
            #
            'type': 'step0',
            # 'symbol': market['id'],  # spot, future
            # 'contract_code': market['id'],  # swap
        }
        fieldName = 'symbol'
        method = 'spotPublicGetMarketDepth'
        if market['future']:
            method = 'contractPublicGetMarketDepth'
        elif market['swap']:
            if market['inverse']:
                method = 'contractPublicGetSwapExMarketDepth'
            elif market['linear']:
                method = 'contractPublicGetLinearSwapExMarketDepth'
            fieldName = 'contract_code'
        request[fieldName] = market['id']
        response = await getattr(self, method)(self.extend(request, params))
        #
        # spot, future, swap
        #
        #     {
        #         "status": "ok",
        #         "ch": "market.btcusdt.depth.step0",
        #         "ts": 1583474832790,
        #         "tick": {
        #             "bids": [
        #                 [9100.290000000000000000, 0.200000000000000000],
        #                 [9099.820000000000000000, 0.200000000000000000],
        #                 [9099.610000000000000000, 0.205000000000000000],
        #             ],
        #             "asks": [
        #                 [9100.640000000000000000, 0.005904000000000000],
        #                 [9101.010000000000000000, 0.287311000000000000],
        #                 [9101.030000000000000000, 0.012121000000000000],
        #             ],
        #             "ch":"market.BTC-USD.depth.step0",
        #             "ts":1583474832008,
        #             "id":1637554816,
        #             "mrid":121654491624,
        #             "version":104999698780
        #         }
        #     }
        #
        if 'tick' in response:
            if not response['tick']:
                raise BadSymbol(self.id + ' fetchOrderBook() returned empty response: ' + self.json(response))
            tick = self.safe_value(response, 'tick')
            timestamp = self.safe_integer(tick, 'ts', self.safe_integer(response, 'ts'))
            result = self.parse_order_book(tick, symbol, timestamp)
            result['nonce'] = self.safe_integer(tick, 'version')
            return result
        raise ExchangeError(self.id + ' fetchOrderBook() returned unrecognized response: ' + self.json(response))

    def parse_trade(self, trade, market=None):
        #
        # fetchTrades(public)
        #
        #     {
        #         "amount": 0.010411000000000000,
        #         "trade-id": 102090736910,
        #         "ts": 1583497692182,
        #         "id": 10500517034273194594947,
        #         "price": 9096.050000000000000000,
        #         "direction": "sell"
        #     }
        #
        # fetchMyTrades(private)
        #
        #     {
        #          'symbol': 'swftcbtc',
        #          'fee-currency': 'swftc',
        #          'filled-fees': '0',
        #          'source': 'spot-api',
        #          'id': 83789509854000,
        #          'type': 'buy-limit',
        #          'order-id': 83711103204909,
        #          'filled-points': '0.005826843283532154',
        #          'fee-deduct-currency': 'ht',
        #          'filled-amount': '45941.53',
        #          'price': '0.0000001401',
        #          'created-at': 1597933260729,
        #          'match-id': 100087455560,
        #          'role': 'maker',
        #          'trade-id': 100050305348
        #     },
        #
        marketId = self.safe_string(trade, 'symbol')
        symbol = self.safe_symbol(marketId, market)
        timestamp = self.safe_integer_2(trade, 'ts', 'created-at')
        order = self.safe_string(trade, 'order-id')
        side = self.safe_string(trade, 'direction')
        type = self.safe_string(trade, 'type')
        if type is not None:
            typeParts = type.split('-')
            side = typeParts[0]
            type = typeParts[1]
        takerOrMaker = self.safe_string(trade, 'role')
        priceString = self.safe_string(trade, 'price')
        amountString = self.safe_string_2(trade, 'filled-amount', 'amount')
        price = self.parse_number(priceString)
        amount = self.parse_number(amountString)
        cost = self.parse_number(Precise.string_mul(priceString, amountString))
        fee = None
        feeCost = self.safe_number(trade, 'filled-fees')
        feeCurrency = self.safe_currency_code(self.safe_string(trade, 'fee-currency'))
        filledPoints = self.safe_number(trade, 'filled-points')
        if filledPoints is not None:
            if (feeCost is None) or (feeCost == 0.0):
                feeCost = filledPoints
                feeCurrency = self.safe_currency_code(self.safe_string(trade, 'fee-deduct-currency'))
        if feeCost is not None:
            fee = {
                'cost': feeCost,
                'currency': feeCurrency,
            }
        tradeId = self.safe_string_2(trade, 'trade-id', 'tradeId')
        id = self.safe_string(trade, 'id', tradeId)
        return {
            'id': id,
            'info': trade,
            'order': order,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': symbol,
            'type': type,
            'side': side,
            'takerOrMaker': takerOrMaker,
            'price': price,
            'amount': amount,
            'cost': cost,
            'fee': fee,
        }

    async def fetch_order_trades(self, id, symbol=None, since=None, limit=None, params={}):
        await self.load_markets()
        request = {
            'order-id': id,
        }
        response = await self.spotPrivateGetV1OrderOrdersOrderIdMatchresults(self.extend(request, params))
        return self.parse_trades(response['data'], None, since, limit)

    async def fetch_my_trades(self, symbol=None, since=None, limit=None, params={}):
        await self.load_markets()
        methodType = None
        methodType, params = self.handle_market_type_and_params('fetchMyTrades', None, params)
        request = {
            # spot -----------------------------------------------------------
            # 'symbol': market['id'],
            # 'types': 'buy-market,sell-market,buy-limit,sell-limit,buy-ioc,sell-ioc,buy-limit-maker,sell-limit-maker,buy-stop-limit,sell-stop-limit',
            # 'start-time': since,  # max 48 hours within 120 days
            # 'end-time': self.milliseconds(),  # max 48 hours within 120 days
            # 'from': 'id',  # tring False N/A Search internal id to begin with if search next page, then self should be the last id(not trade-id) of last page; if search previous page, then self should be the first id(not trade-id) of last page
            # 'direct': 'next',  # next, prev
            # 'size': limit,  # default 100, max 500 The number of orders to return [1-500]
            # contracts ------------------------------------------------------
            # 'symbol': market['settleId'],  # required
            # 'trade_type': 0,  # required, 0 all, 1 open long, 2 open short, 3 close short, 4 close long, 5 liquidate long positions, 6 liquidate short positions
            # 'contract_code': market['id'],
            # 'start_time': since,  # max 48 hours within 120 days
            # 'end_time': self.milliseconds(),  # max 48 hours within 120 days
            # 'from_id': 'id',  # tring False N/A Search internal id to begin with if search next page, then self should be the last id(not trade-id) of last page; if search previous page, then self should be the first id(not trade-id) of last page
            # 'direct': 'prev',  # next, prev
            # 'size': limit,  # default 20, max 50
        }
        method = None
        market = None
        if methodType == 'spot':
            if symbol is not None:
                market = self.market(symbol)
                request['symbol'] = market['id']
            if limit is not None:
                request['size'] = limit  # default 100, max 500
            if since is not None:
                request['start-time'] = since  # a date within 120 days from today
                # request['end-time'] = self.sum(since, 172800000)  # 48 hours window
            method = 'spotPrivateGetV1OrderMatchresults'
        else:
            if symbol is None:
                raise ArgumentsRequired(self.id + ' fetchMyTrades() requires a symbol for ' + methodType + ' orders')
            market = self.market(symbol)
            request['contract_code'] = market['id']
            request['trade_type'] = 0  # 0 all, 1 open long, 2 open short, 3 close short, 4 close long, 5 liquidate long positions, 6 liquidate short positions
            if methodType == 'future':
                method = 'contractPrivatePostApiV1ContractMatchresultsExact'
                request['symbol'] = market['settleId']
            elif methodType == 'swap':
                if market['linear']:
                    marginType = self.safe_string_2(self.options, 'defaultMarginType', 'marginType', 'isolated')
                    if marginType == 'isolated':
                        method = 'contractPrivatePostLinearSwapApiV1SwapMatchresultsExact'
                    elif marginType == 'cross':
                        method = 'contractPrivatePostLinearSwapApiV1SwapCrossMatchresultsExact'
                elif market['inverse']:
                    method = 'contractPrivatePostSwapApiV1SwapMatchresultsExact'
            else:
                raise NotSupported(self.id + ' fetchMyTrades() does not support ' + methodType + ' markets')
        response = await getattr(self, method)(self.extend(request, params))
        #
        # spot
        #
        #     {
        #         "status": "ok",
        #         "data": [
        #             {
        #                 "symbol": "polyusdt",
        #                 "fee-currency": "poly",
        #                 "source": "spot-web",
        #                 "price": "0.338",
        #                 "created-at": 1629443051839,
        #                 "role": "taker",
        #                 "order-id": 345487249132375,
        #                 "match-id": 5014,
        #                 "trade-id": 1085,
        #                 "filled-amount": "147.928994082840236",
        #                 "filled-fees": "0",
        #                 "filled-points": "0.1",
        #                 "fee-deduct-currency": "hbpoint",
        #                 "fee-deduct-state": "done",
        #                 "id": 313288753120940,
        #                 "type": "buy-market"
        #             }
        #         ]
        #     }
        #
        # contracts
        #
        #     {
        #         "status": "ok",
        #         "data": {
        #             "trades": [
        #                 {
        #                     "query_id": 2424420723,
        #                     "match_id": 113891764710,
        #                     "order_id": 773135295142658048,
        #                     "symbol": "ADA",
        #                     "contract_type": "quarter",  # futures only
        #                     "business_type": "futures",  # usdt-m linear swaps only
        #                     "contract_code": "ADA201225",
        #                     "direction": "buy",
        #                     "offset": "open",
        #                     "trade_volume": 1,
        #                     "trade_price": 0.092,
        #                     "trade_turnover": 10,
        #                     "trade_fee": -0.021739130434782608,
        #                     "offset_profitloss": 0,
        #                     "create_date": 1604371703183,
        #                     "role": "Maker",
        #                     "order_source": "web",
        #                     "order_id_str": "773135295142658048",
        #                     "fee_asset": "ADA",
        #                     "margin_mode": "isolated",  # usdt-m linear swaps only
        #                     "margin_account": "BTC-USDT",  # usdt-m linear swaps only
        #                     "real_profit": 0,
        #                     "id": "113891764710-773135295142658048-1"
        #                 }
        #             ],
        #             "remain_size": 15,
        #             "next_id": 2424413094
        #         },
        #         "ts": 1604372202243
        #     }
        #
        trades = self.safe_value(response, 'data')
        if not isinstance(trades, list):
            trades = self.safe_value(trades, 'trades')
        return self.parse_trades(trades, market, since, limit)

    async def fetch_trades(self, symbol, since=None, limit=1000, params={}):
        await self.load_markets()
        market = self.market(symbol)
        request = {
            # 'symbol': market['id'],  # spot, future
            # 'contract_code': market['id'],  # swap
        }
        fieldName = 'symbol'
        method = 'spotPublicGetMarketHistoryTrade'
        if market['future']:
            method = 'contractPublicGetMarketHistoryTrade'
        elif market['swap']:
            if market['inverse']:
                method = 'contractPublicGetSwapExMarketHistoryTrade'
            elif market['linear']:
                method = 'contractPublicGetLinearSwapExMarketHistoryTrade'
            fieldName = 'contract_code'
        request[fieldName] = market['id']
        if limit is not None:
            request['size'] = limit  # max 2000
        response = await getattr(self, method)(self.extend(request, params))
        #
        #     {
        #         "status": "ok",
        #         "ch": "market.btcusdt.trade.detail",
        #         "ts": 1583497692365,
        #         "data": [
        #             {
        #                 "id": 105005170342,
        #                 "ts": 1583497692182,
        #                 "data": [
        #                     {
        #                         "amount": 0.010411000000000000,
        #                         "trade-id": 102090736910,
        #                         "ts": 1583497692182,
        #                         "id": 10500517034273194594947,
        #                         "price": 9096.050000000000000000,
        #                         "direction": "sell"
        #                     }
        #                 ]
        #             },
        #             # ...
        #         ]
        #     }
        #
        data = self.safe_value(response, 'data')
        result = []
        for i in range(0, len(data)):
            trades = self.safe_value(data[i], 'data', [])
            for j in range(0, len(trades)):
                trade = self.parse_trade(trades[j], market)
                result.append(trade)
        result = self.sort_by(result, 'timestamp')
        return self.filter_by_symbol_since_limit(result, symbol, since, limit)

    def parse_ohlcv(self, ohlcv, market=None):
        #
        #     {
        #         "amount":1.2082,
        #         "open":0.025096,
        #         "close":0.025095,
        #         "high":0.025096,
        #         "id":1591515300,
        #         "count":6,
        #         "low":0.025095,
        #         "vol":0.0303205097
        #     }
        #
        return [
            self.safe_timestamp(ohlcv, 'id'),
            self.safe_number(ohlcv, 'open'),
            self.safe_number(ohlcv, 'high'),
            self.safe_number(ohlcv, 'low'),
            self.safe_number(ohlcv, 'close'),
            self.safe_number(ohlcv, 'amount'),
        ]

    async def fetch_ohlcv(self, symbol, timeframe='1m', since=None, limit=1000, params={}):
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'period': self.timeframes[timeframe],
            # 'symbol': market['id'],  # spot, future
            # 'contract_code': market['id'],  # swap
            # 'side': limit,  # max 2000
        }
        fieldName = 'symbol'
        price = self.safe_string(params, 'price')
        params = self.omit(params, 'price')
        method = 'spotPublicGetMarketHistoryKline'
        if market['future']:
            if price == 'mark':
                method = 'contractPublicGetIndexMarketHistoryMarkPriceKline'
            elif price == 'index':
                method = 'contractPublicGetIndexMarketHistoryIndex'
            elif price == 'premiumIndex':
                raise BadRequest(self.id + ' ' + market['type'] + ' has no api endpoint for ' + price + ' kline data')
            else:
                method = 'contractPublicGetMarketHistoryKline'
        elif market['swap']:
            if market['inverse']:
                if price == 'mark':
                    method = 'contractPublicGetIndexMarketHistorySwapMarkPriceKline'
                elif price == 'index':
                    raise BadRequest(self.id + ' ' + market['type'] + ' has no api endpoint for ' + price + ' kline data')
                elif price == 'premiumIndex':
                    method = 'contractPublicGetIndexMarketHistorySwapPremiumIndexKline'
                else:
                    method = 'contractPublicGetSwapExMarketHistoryKline'
            elif market['linear']:
                if price == 'mark':
                    method = 'contractPublicGetIndexMarketHistoryLinearSwapMarkPriceKline'
                elif price == 'index':
                    raise BadRequest(self.id + ' ' + market['type'] + ' has no api endpoint for ' + price + ' kline data')
                elif price == 'premiumIndex':
                    method = 'contractPublicGetIndexMarketHistoryLinearSwapPremiumIndexKline'
                else:
                    method = 'contractPublicGetLinearSwapExMarketHistoryKline'
            fieldName = 'contract_code'
        request[fieldName] = market['id']
        if limit is not None:
            request['size'] = limit  # max 2000
        response = await getattr(self, method)(self.extend(request, params))
        #
        #     {
        #         "status":"ok",
        #         "ch":"market.ethbtc.kline.1min",
        #         "ts":1591515374371,
        #         "data":[
        #             {"amount":0.0,"open":0.025095,"close":0.025095,"high":0.025095,"id":1591515360,"count":0,"low":0.025095,"vol":0.0},
        #             {"amount":1.2082,"open":0.025096,"close":0.025095,"high":0.025096,"id":1591515300,"count":6,"low":0.025095,"vol":0.0303205097},
        #             {"amount":0.0648,"open":0.025096,"close":0.025096,"high":0.025096,"id":1591515240,"count":2,"low":0.025096,"vol":0.0016262208},
        #         ]
        #     }
        #
        data = self.safe_value(response, 'data', [])
        return self.parse_ohlcvs(data, market, timeframe, since, limit)

    async def fetch_index_ohlcv(self, symbol, timeframe='1m', since=None, limit=None, params={}):
        request = {
            'price': 'index',
        }
        return await self.fetch_ohlcv(symbol, timeframe, since, limit, self.extend(request, params))

    async def fetch_mark_ohlcv(self, symbol, timeframe='1m', since=None, limit=None, params={}):
        request = {
            'price': 'mark',
        }
        return await self.fetch_ohlcv(symbol, timeframe, since, limit, self.extend(request, params))

    async def fetch_premium_index_ohlcv(self, symbol, timeframe='1m', since=None, limit=None, params={}):
        request = {
            'price': 'premiumIndex',
        }
        return await self.fetch_ohlcv(symbol, timeframe, since, limit, self.extend(request, params))

    async def fetch_accounts(self, params={}):
        await self.load_markets()
        response = await self.spotPrivateGetV1AccountAccounts(params)
        #
        #     {
        #         "status":"ok",
        #         "data":[
        #             {"id":5202591,"type":"point","subtype":"","state":"working"},
        #             {"id":1528640,"type":"spot","subtype":"","state":"working"},
        #         ]
        #     }
        #
        return response['data']

    async def fetch_account_id_by_type(self, type, params={}):
        accounts = await self.load_accounts()
        accountId = self.safe_value(params, 'account-id')
        if accountId is not None:
            return accountId
        indexedAccounts = self.index_by(accounts, 'type')
        defaultAccount = self.safe_value(accounts, 0, {})
        account = self.safe_value(indexedAccounts, type, defaultAccount)
        return self.safe_string(account, 'id')

    async def fetch_currencies(self, params={}):
        response = await self.spotPublicGetV2ReferenceCurrencies()
        #     {
        #       "code": 200,
        #       "data": [
        #         {
        #           "currency": "sxp",
        #           "assetType": "1",
        #           "chains": [
        #             {
        #               "chain": "sxp",
        #               "displayName": "ERC20",
        #               "baseChain": "ETH",
        #               "baseChainProtocol": "ERC20",
        #               "isDynamic": True,
        #               "numOfConfirmations": "12",
        #               "numOfFastConfirmations": "12",
        #               "depositStatus": "allowed",
        #               "minDepositAmt": "0.23",
        #               "withdrawStatus": "allowed",
        #               "minWithdrawAmt": "0.23",
        #               "withdrawPrecision": "8",
        #               "maxWithdrawAmt": "227000.000000000000000000",
        #               "withdrawQuotaPerDay": "227000.000000000000000000",
        #               "withdrawQuotaPerYear": null,
        #               "withdrawQuotaTotal": null,
        #               "withdrawFeeType": "fixed",
        #               "transactFeeWithdraw": "11.1653",
        #               "addrWithTag": False,
        #               "addrDepositTag": False
        #             }
        #           ],
        #           "instStatus": "normal"
        #         }
        #       ]
        #     }
        #
        data = self.safe_value(response, 'data', [])
        result = {}
        for i in range(0, len(data)):
            entry = data[i]
            currencyId = self.safe_string(entry, 'currency')
            code = self.safe_currency_code(currencyId)
            chains = self.safe_value(entry, 'chains', [])
            networks = {}
            instStatus = self.safe_string(entry, 'instStatus')
            currencyActive = instStatus == 'normal'
            fee = None
            minPrecision = None
            minWithdraw = None
            maxWithdraw = None
            for j in range(0, len(chains)):
                chain = chains[j]
                networkId = self.safe_string(chain, 'chain')
                baseChainProtocol = self.safe_string(chain, 'baseChainProtocol')
                huobiToken = 'h' + currencyId
                if baseChainProtocol is None:
                    if huobiToken == networkId:
                        baseChainProtocol = 'ERC20'
                    else:
                        baseChainProtocol = self.safe_string(chain, 'displayName')
                network = self.safe_network(baseChainProtocol)
                minWithdraw = self.safe_number(chain, 'minWithdrawAmt')
                maxWithdraw = self.safe_number(chain, 'maxWithdrawAmt')
                withdraw = self.safe_string(chain, 'withdrawStatus')
                deposit = self.safe_string(chain, 'depositStatus')
                active = (withdraw == 'allowed') and (deposit == 'allowed')
                precision = self.safe_string(chain, 'withdrawPrecision')
                if precision is not None:
                    precision = self.parse_number('1e-' + precision)
                    minPrecision = precision if (minPrecision is None) else max(precision, minPrecision)
                fee = self.safe_number(chain, 'transactFeeWithdraw')
                networks[network] = {
                    'info': chain,
                    'id': networkId,
                    'network': network,
                    'limits': {
                        'withdraw': {
                            'min': minWithdraw,
                            'max': maxWithdraw,
                        },
                    },
                    'active': active,
                    'fee': fee,
                    'precision': precision,
                }
            networksKeys = list(networks.keys())
            networkLength = len(networksKeys)
            result[code] = {
                'info': entry,
                'code': code,
                'id': currencyId,
                'active': currencyActive,
                'fee': fee if (networkLength <= 1) else None,
                'name': None,
                'limits': {
                    'amount': {
                        'min': None,
                        'max': None,
                    },
                    'withdraw': {
                        'min': minWithdraw if (networkLength <= 1) else None,
                        'max': maxWithdraw if (networkLength <= 1) else None,
                    },
                },
                'precision': minPrecision,
                'networks': networks,
            }
        return result

    async def fetch_balance(self, params={}):
        await self.load_markets()
        options = self.safe_value(self.options, 'fetchTickers', {})
        defaultType = self.safe_string(self.options, 'defaultType', 'spot')
        type = self.safe_string(options, 'type', defaultType)
        type = self.safe_string(params, 'type', type)
        params = self.omit(params, 'type')
        request = {}
        method = None
        spot = (type == 'spot')
        future = (type == 'future')
        swap = (type == 'swap')
        if spot:
            await self.load_accounts()
            accountId = await self.fetch_account_id_by_type(type, params)
            request['account-id'] = accountId
            method = 'spotPrivateGetV1AccountAccountsAccountIdBalance'
        elif future:
            method = 'contractPrivatePostApiV1ContractAccountInfo'
        elif swap:
            defaultSubType = self.safe_string(self.options, 'defaultSubType', 'inverse')
            subType = self.safe_string(options, 'subType', defaultSubType)
            subType = self.safe_string(params, 'subType', subType)
            if subType == 'inverse':
                method = 'contractPrivatePostSwapApiV1SwapAccountInfo'
            elif subType == 'linear':
                currencyId = self.safe_string(params, 'margin_account')
                if currencyId is None:
                    code = self.safe_string(params, 'code')
                    if code is not None:
                        params = self.omit(params, 'code')
                        currency = self.currency(code)
                        currencyId = currency['id']
                if currencyId is None:
                    method = 'contractPrivatePostLinearSwapApiV1SwapAccountInfo'
                else:
                    request['margin_account'] = currencyId
                    method = 'contractPrivatePostLinearSwapApiV1SwapCrossAccountInfo'
        response = await getattr(self, method)(self.extend(request, params))
        #
        # spot
        #
        #     {
        #         "status":"ok",
        #         "data":{
        #             "id":1528640,
        #             "type":"spot",
        #             "state":"working",
        #             "list":[
        #                 {"currency":"lun","type":"trade","balance":"0","seq-num":"0"},
        #                 {"currency":"lun","type":"frozen","balance":"0","seq-num":"0"},
        #                 {"currency":"ht","type":"frozen","balance":"0","seq-num":"145"},
        #             ]
        #         },
        #         "ts":1637644827566
        #     }
        #
        # future, swap isolated
        #
        #     {
        #         "status":"ok",
        #         "data":[
        #             {
        #                 "symbol":"BTC",
        #                 "margin_balance":0,
        #                 "margin_position":0E-18,
        #                 "margin_frozen":0,
        #                 "margin_available":0E-18,
        #                 "profit_real":0,
        #                 "profit_unreal":0,
        #                 "risk_rate":null,
        #                 "withdraw_available":0,
        #                 "liquidation_price":null,
        #                 "lever_rate":5,
        #                 "adjust_factor":0.025000000000000000,
        #                 "margin_static":0,
        #                 "is_debit":0,  # future only
        #                 "contract_code":"BTC-USD",  # swap only
        #                 "margin_asset":"USDT",  # linear only
        #                 "margin_mode":"isolated",  # linear only
        #                 "margin_account":"BTC-USDT"  # linear only
        #                 "transfer_profit_ratio":null  # inverse only
        #             },
        #         ],
        #         "ts":1637644827566
        #     }
        #
        # swap cross
        #
        #     {
        #         "status":"ok",
        #         "data":[
        #             {
        #                 "margin_mode":"cross",
        #                 "margin_account":"USDT",
        #                 "margin_asset":"USDT",
        #                 "margin_balance":200,
        #                 "margin_static":200,
        #                 "margin_position":0,
        #                 "margin_frozen":0,
        #                 "profit_real":0,
        #                 "profit_unreal":0,
        #                 "withdraw_available":2E+2,
        #                 "risk_rate":null,
        #                 "contract_detail":[
        #                     {
        #                         "symbol":"MANA",
        #                         "contract_code":"MANA-USDT",
        #                         "margin_position":0,
        #                         "margin_frozen":0,
        #                         "margin_available":200.000000000000000000,
        #                         "profit_unreal":0E-18,
        #                         "liquidation_price":null,
        #                         "lever_rate":5,
        #                         "adjust_factor":0.100000000000000000,
        #                         "contract_type":"swap",
        #                         "pair":"MANA-USDT",
        #                         "business_type":"swap"
        #                     },
        #                     ...
        #                 ]
        #             }
        #         ],
        #         "ts":1640493207964
        #     }
        #
        result = {'info': response}
        data = self.safe_value(response, 'data')
        if spot:
            balances = self.safe_value(data, 'list', [])
            for i in range(0, len(balances)):
                balance = balances[i]
                currencyId = self.safe_string(balance, 'currency')
                code = self.safe_currency_code(currencyId)
                account = None
                if code in result:
                    account = result[code]
                else:
                    account = self.account()
                if balance['type'] == 'trade':
                    account['free'] = self.safe_string(balance, 'balance')
                if balance['type'] == 'frozen':
                    account['used'] = self.safe_string(balance, 'balance')
                result[code] = account
        elif future:
            for i in range(0, len(data)):
                balance = data[i]
                currencyId = self.safe_string(balance, 'symbol')
                code = self.safe_currency_code(currencyId)
                account = self.account()
                account['free'] = self.safe_string(balance, 'margin_available')
                account['used'] = self.safe_string(balance, 'margin_frozen')
                result[code] = account
        elif swap:
            for i in range(0, len(data)):
                balance = data[i]
                marketId = self.safe_string_2(balance, 'contract_code', 'margin_account')
                symbol = self.safe_symbol(marketId)
                account = self.account()
                account['free'] = self.safe_string(balance, 'margin_balance', 'margin_available')
                account['used'] = self.safe_string(balance, 'margin_frozen')
                currencyId = self.safe_string_2(balance, 'margin_asset', 'symbol')
                code = self.safe_currency_code(currencyId)
                marginMode = self.safe_string(balance, 'margin_mode')
                if marginMode == 'cross':
                    result[code] = account
                    return self.safe_balance(result)
                accountsByCode = {}
                accountsByCode[code] = account
                result[symbol] = self.safe_balance(accountsByCode)
            return result
        return self.safe_balance(result)

    async def fetch_orders_by_states(self, states, symbol=None, since=None, limit=None, params={}):
        await self.load_markets()
        request = {
            'states': states,
        }
        market = None
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        method = self.safe_string(self.options, 'fetchOrdersByStatesMethod', 'spot_private_get_v1_order_orders')
        response = await getattr(self, method)(self.extend(request, params))
        #
        #     {status:   "ok",
        #         data: [{                 id:  13997833014,
        #                                symbol: "ethbtc",
        #                          'account-id':  3398321,
        #                                amount: "0.045000000000000000",
        #                                 price: "0.034014000000000000",
        #                          'created-at':  1545836976871,
        #                                  type: "sell-limit",
        #                        'field-amount': "0.045000000000000000",
        #                   'field-cash-amount': "0.001530630000000000",
        #                          'field-fees': "0.000003061260000000",
        #                         'finished-at':  1545837948214,
        #                                source: "spot-api",
        #                                 state: "filled",
        #                         'canceled-at':  0                      }  ]}
        #
        return self.parse_orders(response['data'], market, since, limit)

    async def fetch_order(self, id, symbol=None, params={}):
        await self.load_markets()
        methodType = None
        methodType, params = self.handle_market_type_and_params('fetchOrder', None, params)
        request = {
            # spot -----------------------------------------------------------
            # 'order-id': 'id',
            # 'symbol': market['id'],
            # 'client-order-id': clientOrderId,
            # 'clientOrderId': clientOrderId,
            # contracts ------------------------------------------------------
            # 'order_id': id,
            # 'client_order_id': clientOrderId,
            # 'contract_code': market['id'],
            # 'pair': 'BTC-USDT',
            # 'contract_type': 'this_week',  # swap, self_week, next_week, quarter, next_ quarter
        }
        method = None
        if methodType == 'spot':
            clientOrderId = self.safe_string(params, 'clientOrderId')
            method = 'spotPrivateGetV1OrderOrdersOrderId'
            if clientOrderId is not None:
                method = 'spotPrivateGetV1OrderOrdersGetClientOrder'
                # will be filled below in self.extend()
                # they expect clientOrderId instead of client-order-id
                # request['clientOrderId'] = clientOrderId
            else:
                request['order-id'] = id
        else:
            if symbol is None:
                raise ArgumentsRequired(self.id + ' cancelOrder() requires a symbol for ' + methodType + ' orders')
            market = self.market(symbol)
            request['contract_code'] = market['id']
            if methodType == 'future':
                method = 'contractPrivatePostApiV1ContractOrderInfo'
                request['symbol'] = market['settleId']
            elif methodType == 'swap':
                if market['linear']:
                    marginType = self.safe_string_2(self.options, 'defaultMarginType', 'marginType', 'isolated')
                    if marginType == 'isolated':
                        method = 'contractPrivatePostLinearSwapApiV1SwapOrderInfo'
                    elif marginType == 'cross':
                        method = 'contractPrivatePostLinearSwapApiV1SwapCrossOrderInfo'
                elif market['inverse']:
                    method = 'contractPrivatePostSwapApiV1SwapOrderInfo'
            else:
                raise NotSupported(self.id + ' cancelOrder() does not support ' + methodType + ' markets')
            clientOrderId = self.safe_string_2(params, 'client_order_id', 'clientOrderId')
            if clientOrderId is None:
                request['order_id'] = id
            else:
                request['client_order_id'] = clientOrderId
                params = self.omit(params, ['client_order_id', 'clientOrderId'])
        response = await getattr(self, method)(self.extend(request, params))
        #
        # spot
        #
        #     {
        #         "status":"ok",
        #         "data":{
        #             "id":438398393065481,
        #             "symbol":"ethusdt",
        #             "account-id":1528640,
        #             "client-order-id":"AA03022abc2163433e-006b-480e-9ad1-d4781478c5e7",
        #             "amount":"0.100000000000000000",
        #             "price":"3000.000000000000000000",
        #             "created-at":1640549994642,
        #             "type":"buy-limit",
        #             "field-amount":"0.0",
        #             "field-cash-amount":"0.0",
        #             "field-fees":"0.0",
        #             "finished-at":0,
        #             "source":"spot-api",
        #             "state":"submitted",
        #             "canceled-at":0
        #         }
        #     }
        #
        # linear swap cross margin
        #
        #     {
        #         "status":"ok",
        #         "data":[
        #             {
        #                 "business_type":"swap",
        #                 "contract_type":"swap",
        #                 "pair":"BTC-USDT",
        #                 "symbol":"BTC",
        #                 "contract_code":"BTC-USDT",
        #                 "volume":1,
        #                 "price":3000,
        #                 "order_price_type":"limit",
        #                 "order_type":1,
        #                 "direction":"buy",
        #                 "offset":"open",
        #                 "lever_rate":1,
        #                 "order_id":924912513206878210,
        #                 "client_order_id":null,
        #                 "created_at":1640557927189,
        #                 "trade_volume":0,
        #                 "trade_turnover":0,
        #                 "fee":0,
        #                 "trade_avg_price":null,
        #                 "margin_frozen":3.000000000000000000,
        #                 "profit":0,
        #                 "status":3,
        #                 "order_source":"api",
        #                 "order_id_str":"924912513206878210",
        #                 "fee_asset":"USDT",
        #                 "liquidation_type":"0",
        #                 "canceled_at":0,
        #                 "margin_asset":"USDT",
        #                 "margin_account":"USDT",
        #                 "margin_mode":"cross",
        #                 "is_tpsl":0,
        #                 "real_profit":0
        #             }
        #         ],
        #         "ts":1640557982556
        #     }
        #
        order = self.safe_value(response, 'data')
        if isinstance(order, list):
            order = self.safe_value(order, 0)
        return self.parse_order(order)

    async def fetch_orders(self, symbol=None, since=None, limit=None, params={}):
        return await self.fetch_orders_by_states('pre-submitted,submitted,partial-filled,filled,partial-canceled,canceled', symbol, since, limit, params)

    async def fetch_closed_orders(self, symbol=None, since=None, limit=None, params={}):
        return await self.fetch_orders_by_states('filled,partial-canceled,canceled', symbol, since, limit, params)

    async def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
        await self.load_markets()
        methodType = None
        methodType, params = self.handle_market_type_and_params('fetchOpenOrders', None, params)
        request = {
            # spot -----------------------------------------------------------
            # 'account-id': account['id'],
            # 'symbol': market['id'],
            # 'side': 'buy',  # buy, sell
            # 'from': 'id',  # order id to begin with
            # 'direct': 'prev',  # prev, next, mandatory if from is defined
            # 'size': 100,  # default 100, max 500
            # futures --------------------------------------------------------
            # 'symbol': market['settleId'],
            # 'page_index': 1,  # default 1
            # 'page_size': limit,  # default 20, max 50
            # 'sort_by': 'created_at',  # created_at, update_time, descending sorting field
            # 'trade_type': 0,  # 0 all, 1 buy long, 2 sell short, 3 buy short, 4 sell long
        }
        method = None
        market = None
        if methodType == 'spot':
            method = 'spotPrivateGetV1OrderOpenOrders'
            if symbol is not None:
                market = self.market(symbol)
                request['symbol'] = market['id']
            # todo replace with fetchAccountIdByType
            accountId = self.safe_string(params, 'account-id')
            if accountId is None:
                # pick the first account
                await self.load_accounts()
                for i in range(0, len(self.accounts)):
                    account = self.accounts[i]
                    if account['type'] == 'spot':
                        accountId = self.safe_string(account, 'id')
                        if accountId is not None:
                            break
            request['account-id'] = accountId
            if limit is not None:
                request['size'] = limit
            params = self.omit(params, 'account-id')
        else:
            if symbol is None:
                raise ArgumentsRequired(self.id + ' fetchOpenOrders() requires a symbol for ' + methodType + ' orders')
            market = self.market(symbol)
            request['contract_code'] = market['id']
            if methodType == 'future':
                method = 'contractPrivatePostApiV1ContractOpenorders'
                request['symbol'] = market['settleId']
            elif methodType == 'swap':
                if market['linear']:
                    marginType = self.safe_string_2(self.options, 'defaultMarginType', 'marginType', 'isolated')
                    if marginType == 'isolated':
                        method = 'contractPrivatePostLinearSwapApiV1SwapOpenorders'
                    elif marginType == 'cross':
                        method = 'contractPrivatePostLinearSwapApiV1SwapCrossOpenorders'
                elif market['inverse']:
                    method = 'contractPrivatePostSwapApiV1SwapOpenorders'
            else:
                raise NotSupported(self.id + ' cancelOrder() does not support ' + methodType + ' markets')
            if limit is not None:
                request['page_size'] = limit
        response = await getattr(self, method)(self.extend(request, params))
        #
        # spot
        #
        #     {
        #         "status":"ok",
        #         "data":[
        #             {
        #                 "symbol":"ethusdt",
        #                 "source":"api",
        #                 "amount":"0.010000000000000000",
        #                 "account-id":1528640,
        #                 "created-at":1561597491963,
        #                 "price":"400.000000000000000000",
        #                 "filled-amount":"0.0",
        #                 "filled-cash-amount":"0.0",
        #                 "filled-fees":"0.0",
        #                 "id":38477101630,
        #                 "state":"submitted",
        #                 "type":"sell-limit"
        #             }
        #         ]
        #     }
        #
        # futures
        #
        #     {
        #         "status": "ok",
        #         "data": {
        #             "orders": [
        #                 {
        #                     "symbol": "ADA",
        #                     "contract_code": "ADA201225",
        #                     "contract_type": "quarter",
        #                     "volume": 1,
        #                     "price": 0.0925,
        #                     "order_price_type": "post_only",
        #                     "order_type": 1,
        #                     "direction": "buy",
        #                     "offset": "close",
        #                     "lever_rate": 20,
        #                     "order_id": 773131315209248768,
        #                     "client_order_id": null,
        #                     "created_at": 1604370469629,
        #                     "trade_volume": 0,
        #                     "trade_turnover": 0,
        #                     "fee": 0,
        #                     "trade_avg_price": null,
        #                     "margin_frozen": 0,
        #                     "profit": 0,
        #                     "status": 3,
        #                     "order_source": "web",
        #                     "order_id_str": "773131315209248768",
        #                     "fee_asset": "ADA",
        #                     "liquidation_type": null,
        #                     "canceled_at": null,
        #                     "is_tpsl": 0,
        #                     "update_time": 1606975980467,
        #                     "real_profit": 0
        #                 }
        #             ],
        #             "total_page": 1,
        #             "current_page": 1,
        #             "total_size": 1
        #         },
        #         "ts": 1604370488518
        #     }
        #
        orders = self.safe_value(response, 'data')
        if not isinstance(orders, list):
            orders = self.safe_value(orders, 'orders', [])
        return self.parse_orders(orders, market, since, limit)

    def parse_order_status(self, status):
        statuses = {
            # spot
            'partial-filled': 'open',
            'partial-canceled': 'canceled',
            'filled': 'closed',
            'canceled': 'canceled',
            'submitted': 'open',
            # contract
            '1': 'open',
            '2': 'open',
            '3': 'open',
            '4': 'open',
            '5': 'canceled',  # partially matched
            '6': 'closed',
            '7': 'canceled',
            '11': 'canceling',
        }
        return self.safe_string(statuses, status, status)

    def parse_order(self, order, market=None):
        #
        # spot
        #
        #     {
        #         id:  13997833014,
        #         symbol: "ethbtc",
        #         'account-id':  3398321,
        #         amount: "0.045000000000000000",
        #         price: "0.034014000000000000",
        #         'created-at':  1545836976871,
        #         type: "sell-limit",
        #         'field-amount': "0.045000000000000000",  # they have fixed it for filled-amount
        #         'field-cash-amount': "0.001530630000000000",  # they have fixed it for filled-cash-amount
        #         'field-fees': "0.000003061260000000",  # they have fixed it for filled-fees
        #         'finished-at':  1545837948214,
        #         source: "spot-api",
        #         state: "filled",
        #         'canceled-at':  0
        #     }
        #
        #     {
        #         id:  20395337822,
        #         symbol: "ethbtc",
        #         'account-id':  5685075,
        #         amount: "0.001000000000000000",
        #         price: "0.0",
        #         'created-at':  1545831584023,
        #         type: "buy-market",
        #         'field-amount': "0.029100000000000000",  # they have fixed it for filled-amount
        #         'field-cash-amount': "0.000999788700000000",  # they have fixed it for filled-cash-amount
        #         'field-fees': "0.000058200000000000",  # they have fixed it for filled-fees
        #         'finished-at':  1545831584181,
        #         source: "spot-api",
        #         state: "filled",
        #         'canceled-at':  0
        #     }
        #
        # linear swap cross margin createOrder
        #
        #     {
        #         "order_id":924660854912552960,
        #         "order_id_str":"924660854912552960"
        #     }
        #
        # linear swap cross margin fetchOrder
        #
        #     {
        #         "business_type":"swap",
        #         "contract_type":"swap",
        #         "pair":"BTC-USDT",
        #         "symbol":"BTC",
        #         "contract_code":"BTC-USDT",
        #         "volume":1,
        #         "price":3000,
        #         "order_price_type":"limit",
        #         "order_type":1,
        #         "direction":"buy",
        #         "offset":"open",
        #         "lever_rate":1,
        #         "order_id":924912513206878210,
        #         "client_order_id":null,
        #         "created_at":1640557927189,
        #         "trade_volume":0,
        #         "trade_turnover":0,
        #         "fee":0,
        #         "trade_avg_price":null,
        #         "margin_frozen":3.000000000000000000,
        #         "profit":0,
        #         "status":3,
        #         "order_source":"api",
        #         "order_id_str":"924912513206878210",
        #         "fee_asset":"USDT",
        #         "liquidation_type":"0",
        #         "canceled_at":0,
        #         "margin_asset":"USDT",
        #         "margin_account":"USDT",
        #         "margin_mode":"cross",
        #         "is_tpsl":0,
        #         "real_profit":0
        #     }
        #
        id = self.safe_string_2(order, 'id', 'order_id_str')
        side = self.safe_string(order, 'direction')
        type = self.safe_string(order, 'order_price_type')
        if 'type' in order:
            orderType = order['type'].split('-')
            side = orderType[0]
            type = orderType[1]
        status = self.parse_order_status(self.safe_string_2(order, 'state', 'status'))
        marketId = self.safe_string_2(order, 'contract_code', 'symbol')
        market = self.safe_market(marketId, market)
        symbol = self.safe_symbol(marketId, market)
        timestamp = self.safe_integer_2(order, 'created_at', 'created-at')
        clientOrderId = self.safe_string_2(order, 'client_order_id', 'client-order-id')
        amount = self.safe_string_2(order, 'volume', 'amount')
        filled = self.safe_string_2(order, 'filled-amount', 'field-amount')  # typo in their API, filled amount
        filled = self.safe_string(order, 'trade_volume', filled)
        price = self.safe_string(order, 'price')
        cost = self.safe_string_2(order, 'filled-cash-amount', 'field-cash-amount')  # same typo
        cost = self.safe_string(order, 'trade_turnover', cost)
        feeCost = self.safe_string_2(order, 'filled-fees', 'field-fees')  # typo in their API, filled feeSide
        feeCost = self.safe_string(order, 'fee', feeCost)
        fee = None
        if feeCost is not None:
            feeCurrency = None
            feeCurrencyId = self.safe_string(order, 'fee_asset')
            if feeCurrencyId is not None:
                feeCurrency = self.safe_currency_code(feeCurrencyId)
            else:
                if market is not None:
                    feeCurrency = market['quote'] if (side == 'sell') else market['base']
            fee = {
                'cost': feeCost,
                'currency': feeCurrency,
            }
        stopPrice = self.safe_string(order, 'stop-price')
        average = self.safe_string(order, 'trade_avg_price')
        return self.safe_order({
            'info': order,
            'id': id,
            'clientOrderId': clientOrderId,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': None,
            'symbol': symbol,
            'type': type,
            'timeInForce': None,
            'postOnly': None,
            'side': side,
            'price': price,
            'stopPrice': stopPrice,
            'average': average,
            'cost': cost,
            'amount': amount,
            'filled': filled,
            'remaining': None,
            'status': status,
            'fee': fee,
            'trades': None,
        }, market)

    async def create_order(self, symbol, type, side, amount, price=None, params={}):
        await self.load_markets()
        market = self.market(symbol)
        methodType, query = self.handle_market_type_and_params('createOrder', market, params)
        method = self.get_supported_mapping(methodType, {
            'spot': 'createSpotOrder',
            'swap': 'createContractOrder',
            'future': 'createContractOrder',
        })
        if method is None:
            raise NotSupported(self.id + ' createOrder does not support ' + type + ' markets yet')
        return await getattr(self, method)(symbol, type, side, amount, price, query)

    async def create_spot_order(self, symbol, type, side, amount, price=None, params={}):
        await self.load_markets()
        await self.load_accounts()
        market = self.market(symbol)
        accountId = await self.fetch_account_id_by_type(market['type'])
        request = {
            # spot -----------------------------------------------------------
            'account-id': accountId,
            'symbol': market['id'],
            # 'type': side + '-' + type,  # buy-market, sell-market, buy-limit, sell-limit, buy-ioc, sell-ioc, buy-limit-maker, sell-limit-maker, buy-stop-limit, sell-stop-limit, buy-limit-fok, sell-limit-fok, buy-stop-limit-fok, sell-stop-limit-fok
            # 'amount': self.amount_to_precision(symbol, amount),  # for buy market orders it's the order cost
            # 'price': self.price_to_precision(symbol, price),
            # 'source': 'spot-api',  # optional, spot-api, margin-api = isolated margin, super-margin-api = cross margin, c2c-margin-api
            # 'client-order-id': clientOrderId,  # optional, max 64 chars, must be unique within 8 hours
            # 'stop-price': self.price_to_precision(symbol, stopPrice),  # trigger price for stop limit orders
            # 'operator': 'gte',  # gte, lte, trigger price condition
        }
        orderType = type.replace('buy-', '')
        orderType = orderType.replace('sell-', '')
        options = self.safe_value(self.options, market['type'], {})
        stopPrice = self.safe_string_2(params, 'stopPrice', 'stop-price')
        if stopPrice is None:
            stopOrderTypes = self.safe_value(options, 'stopOrderTypes', {})
            if orderType in stopOrderTypes:
                raise ArgumentsRequired(self.id + 'createOrder() requires a stopPrice or a stop-price parameter for a stop order')
        else:
            stopOperator = self.safe_string(params, 'operator')
            if stopOperator is None:
                raise ArgumentsRequired(self.id + ' createOrder() requires an operator parameter "gte" or "lte" for a stop order')
            params = self.omit(params, ['stopPrice', 'stop-price'])
            request['stop-price'] = self.price_to_precision(symbol, stopPrice)
            request['operator'] = stopOperator
            if (orderType == 'limit') or (orderType == 'limit-fok'):
                orderType = 'stop-' + orderType
            elif (orderType != 'stop-limit') and (orderType != 'stop-limit-fok'):
                raise NotSupported(self.id + 'createOrder() does not support ' + type + ' orders')
        request['type'] = side + '-' + orderType
        clientOrderId = self.safe_string_2(params, 'clientOrderId', 'client-order-id')  # must be 64 chars max and unique within 24 hours
        if clientOrderId is None:
            broker = self.safe_value(self.options, 'broker', {})
            brokerId = self.safe_string(broker, 'id')
            request['client-order-id'] = brokerId + self.uuid()
        else:
            request['client-order-id'] = clientOrderId
        params = self.omit(params, ['clientOrderId', 'client-order-id'])
        if (orderType == 'market') and (side == 'buy'):
            if self.options['createMarketBuyOrderRequiresPrice']:
                if price is None:
                    raise InvalidOrder(self.id + " market buy order requires price argument to calculate cost(total amount of quote currency to spend for buying, amount * price). To switch off self warning exception and specify cost in the amount argument, set .options['createMarketBuyOrderRequiresPrice'] = False. Make sure you know what you're doing.")
                else:
                    # despite that cost = amount * price is in quote currency and should have quote precision
                    # the exchange API requires the cost supplied in 'amount' to be of base precision
                    # more about it here:
                    # https://github.com/ccxt/ccxt/pull/4395
                    # https://github.com/ccxt/ccxt/issues/7611
                    # we use amountToPrecision here because the exchange requires cost in base precision
                    request['amount'] = self.cost_to_precision(symbol, float(amount) * float(price))
            else:
                request['amount'] = self.cost_to_precision(symbol, amount)
        else:
            request['amount'] = self.amount_to_precision(symbol, amount)
        limitOrderTypes = self.safe_value(options, 'limitOrderTypes', {})
        if orderType in limitOrderTypes:
            request['price'] = self.price_to_precision(symbol, price)
        response = await self.spotPrivatePostV1OrderOrdersPlace(self.extend(request, params))
        #
        # spot
        #
        #     {"status":"ok","data":"438398393065481"}
        #
        timestamp = self.milliseconds()
        id = self.safe_string(response, 'data')
        return {
            'info': response,
            'id': id,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': None,
            'status': None,
            'symbol': symbol,
            'type': type,
            'side': side,
            'price': price,
            'amount': amount,
            'filled': None,
            'remaining': None,
            'cost': None,
            'trades': None,
            'fee': None,
            'clientOrderId': None,
            'average': None,
        }

    async def create_contract_order(self, symbol, type, side, amount, price=None, params={}):
        offset = self.safe_string(params, 'offset')
        if offset is None:
            raise ArgumentsRequired(self.id + ' createOrder() requires a string offset parameter for contract orders, open or close')
        stopPrice = self.safe_string(params, 'stopPrice')
        if stopPrice is not None:
            raise NotSupported(self.id + ' createOrder() supports tp_trigger_price + tp_order_price for take profit orders and/or sl_trigger_price + sl_order price for stop loss orders, stop orders are supported only with open long orders and open short orders')
        market = self.market(symbol)
        request = {
            # 'symbol': 'BTC',  # optional, case-insenstive, both uppercase and lowercase are supported, "BTC", "ETH", ...
            # 'contract_type': 'this_week',  # optional, self_week, next_week, quarter, next_quarter
            'contract_code': market['id'],  # optional BTC180914
            # 'client_order_id': clientOrderId,  # optional, must be less than 9223372036854775807
            # 'price': self.price_to_precision(symbol, price),  # optional
            'volume': self.amount_to_precision(symbol, amount),
            'direction': side,  # buy, sell
            'offset': offset,  # open, close
            #
            #     direction buy, offset open = open long
            #     direction sell, offset close = close long
            #     direction sell, offset open = open short
            #     direction buy, offset close = close short
            #
            'lever_rate': 1,  # required, using leverage greater than 20x requires prior approval of high-leverage agreement
            # 'order_price_type': 'limit',  # required
            #
            #     order_price_type can be:
            #
            #     limit
            #     opponent  # BBO
            #     post_only
            #     optimal_5
            #     optimal_10
            #     optimal_20
            #     ioc
            #     fok
            #     opponent_ioc  # IOC order using the BBO price
            #     optimal_5_ioc
            #     optimal_10_ioc
            #     optimal_20_ioc
            #     opponent_fok  # FOR order using the BBO price
            #     optimal_5_fok
            #     optimal_10_fok
            #     optimal_20_fok
            #
            # 'tp_trigger_price': self.price_to_precision(symbol, triggerPrice),
            # 'tp_order_price': self.price_to_precision(symbol, price),
            # 'tp_order_price_type': 'limit',  # limit，optimal_5，optimal_10，optimal_20
            # 'sl_trigger_price': self.price_to_precision(symbol, stopLossPrice),
            # 'sl_order_price': self.price_to_precision(symbol, price),
            # 'sl_order_price_type': 'limit',  # limit，optimal_5，optimal_10，optimal_20
        }
        stopLossOrderPrice = self.safe_string(params, 'sl_order_price')
        stopLossTriggerPrice = self.safe_string(params, 'sl_trigger_price')
        takeProfitOrderPrice = self.safe_string(params, 'tp_order_price')
        takeProfitTriggerPrice = self.safe_string(params, 'tp_trigger_price')
        isOpenOrder = (offset == 'open')
        isStopOrder = False
        if stopLossTriggerPrice is not None:
            request['sl_trigger_price'] = self.price_to_precision(symbol, stopLossTriggerPrice)
            isStopOrder = True
            if price is not None:
                request['sl_order_price'] = self.price_to_precision(symbol, price)
        if stopLossOrderPrice is not None:
            request['sl_order_price'] = self.price_to_precision(symbol, stopLossOrderPrice)
            isStopOrder = True
        if takeProfitTriggerPrice is not None:
            request['tp_trigger_price'] = self.price_to_precision(symbol, takeProfitTriggerPrice)
            isStopOrder = True
            if price is not None:
                request['tp_order_price'] = self.price_to_precision(symbol, price)
        if takeProfitOrderPrice is not None:
            request['tp_order_price'] = self.price_to_precision(symbol, takeProfitOrderPrice)
            isStopOrder = True
        if isStopOrder and not isOpenOrder:
            raise NotSupported(self.id + ' createOrder() supports tp_trigger_price + tp_order_price for take profit orders and/or sl_trigger_price + sl_order price for stop loss orders, stop orders are supported only with open long orders and open short orders')
        params = self.omit(params, ['sl_order_price', 'sl_trigger_price', 'tp_order_price', 'tp_trigger_price'])
        postOnly = self.safe_value(params, 'postOnly', False)
        if postOnly:
            type = 'post_only'
        if type == 'limit' or type == 'ioc' or type == 'fok' or type == 'post_only':
            request['price'] = self.price_to_precision(symbol, price)
        request['order_price_type'] = type
        clientOrderId = self.safe_string_2(params, 'clientOrderId', 'client_order_id')  # must be 64 chars max and unique within 24 hours
        # if clientOrderId is None:
        #     broker = self.safe_value(self.options, 'broker', {})
        #     brokerId = self.safe_string(broker, 'id')
        #     request['client_order_id'] = brokerId + self.uuid()
        # else:
        #     request['client_order_id'] = clientOrderId
        # }
        if clientOrderId is not None:
            request['client_order_id'] = clientOrderId
            params = self.omit(params, ['clientOrderId', 'client_order_id'])
        method = None
        if market['swap']:
            if market['linear']:
                marginType = self.safe_string_2(self.options, 'defaultMarginType', 'marginType', 'isolated')
                if marginType == 'isolated':
                    method = 'contractPrivatePostLinearSwapApiV1SwapOrder'
                elif marginType == 'cross':
                    method = 'contractPrivatePostLinearSwapApiV1SwapCrossOrder'
            else:
                method = 'contractPrivatePostSwapApiV1SwapOrder'
        elif market['future']:
            method = 'contractPrivatePostApiV1ContractOrder'
        response = await getattr(self, method)(self.extend(request, params))
        #
        # linear swap cross margin
        #
        #     {
        #         "status":"ok",
        #         "data":{
        #             "order_id":924660854912552960,
        #             "order_id_str":"924660854912552960"
        #         },
        #         "ts":1640497927185
        #     }
        #
        data = self.safe_value(response, 'data', {})
        return self.parse_order(data, market)

    async def cancel_order(self, id, symbol=None, params={}):
        await self.load_markets()
        methodType = None
        methodType, params = self.handle_market_type_and_params('cancelOrder', None, params)
        request = {
            # spot -----------------------------------------------------------
            # 'order-id': 'id',
            # 'symbol': market['id'],
            # 'client-order-id': clientOrderId,
            # contracts ------------------------------------------------------
            # 'order_id': id,
            # 'client_order_id': clientOrderId,
            # 'contract_code': market['id'],
            # 'pair': 'BTC-USDT',
            # 'contract_type': 'this_week',  # swap, self_week, next_week, quarter, next_ quarter
        }
        method = None
        if methodType == 'spot':
            clientOrderId = self.safe_string_2(params, 'client-order-id', 'clientOrderId')
            method = 'spotPrivatePostV1OrderOrdersOrderIdSubmitcancel'
            if clientOrderId is None:
                request['order-id'] = id
            else:
                request['client-order-id'] = clientOrderId
                method = 'spotPrivatePostV1OrderOrdersSubmitCancelClientOrder'
                params = self.omit(params, ['client-order-id', 'clientOrderId'])
        else:
            if symbol is None:
                raise ArgumentsRequired(self.id + ' cancelOrder() requires a symbol for ' + methodType + ' orders')
            market = self.market(symbol)
            request['contract_code'] = market['id']
            if methodType == 'future':
                method = 'contractPrivatePostApiV1ContractCancel'
            elif methodType == 'swap':
                if market['linear']:
                    marginType = self.safe_string_2(self.options, 'defaultMarginType', 'marginType', 'isolated')
                    if marginType == 'isolated':
                        method = 'contractPrivatePostLinearSwapApiV1SwapCancel'
                    elif marginType == 'cross':
                        method = 'contractPrivatePostLinearSwapApiV1SwapCrossCancel'
                elif market['inverse']:
                    method = 'contractPrivatePostSwapApiV1SwapCancel'
            else:
                raise NotSupported(self.id + ' cancelOrder() does not support ' + methodType + ' markets')
            clientOrderId = self.safe_string_2(params, 'client_order_id', 'clientOrderId')
            if clientOrderId is None:
                request['order_id'] = id
            else:
                request['client_order_id'] = clientOrderId
                params = self.omit(params, ['client_order_id', 'clientOrderId'])
        response = await getattr(self, method)(self.extend(request, params))
        #
        # spot
        #
        #     {
        #         'status': 'ok',
        #         'data': '10138899000',
        #     }
        #
        # linear swap cross margin
        #
        #     {
        #         "status":"ok",
        #         "data":{
        #             "errors":[],
        #             "successes":"924660854912552960"
        #         },
        #         "ts":1640504486089
        #     }
        #
        return self.extend(self.parse_order(response), {
            'id': id,
            'status': 'canceled',
        })

    async def cancel_orders(self, ids, symbol=None, params={}):
        await self.load_markets()
        methodType = None
        methodType, params = self.handle_market_type_and_params('cancelOrder', None, params)
        request = {
            # spot -----------------------------------------------------------
            'order-ids': [],  # max 50 ids
            'client-order-ids': [],  # max 50 ids
            # contracts ------------------------------------------------------
            # 'order_id': id,  # comma separated, max 10
            # 'client_order_id': clientOrderId,  # comma separated, max 10
            # 'contract_code': market['id'],
            # 'symbol': market['settleId'],
        }
        method = None
        if methodType == 'spot':
            clientOrderIds = self.safe_string_2(params, 'client-order-id', 'clientOrderId')
            clientOrderIds = self.safe_string_2(params, 'client-order-ids', 'clientOrderIds', clientOrderIds)
            if clientOrderIds is None:
                request['order-ids'] = ids
            else:
                request['client-order-ids'] = clientOrderIds
                params = self.omit(params, ['client-order-id', 'client-order-ids', 'clientOrderId', 'clientOrderIds'])
            method = 'spotPrivatePostV1OrderOrdersBatchcancel'
        else:
            if symbol is None:
                raise ArgumentsRequired(self.id + ' cancelOrders() requires a symbol for ' + methodType + ' orders')
            market = self.market(symbol)
            request['contract_code'] = market['id']
            if methodType == 'future':
                method = 'contractPrivatePostApiV1ContractCancel'
                request['symbol'] = market['settleId']
            elif methodType == 'swap':
                if market['linear']:
                    marginType = self.safe_string_2(self.options, 'defaultMarginType', 'marginType', 'isolated')
                    if marginType == 'isolated':
                        method = 'contractPrivatePostLinearSwapApiV1SwapCancel'
                    elif marginType == 'cross':
                        method = 'contractPrivatePostLinearSwapApiV1SwapCrossCancel'
                elif market['inverse']:
                    method = 'contractPrivatePostSwapApiV1SwapCancel'
            else:
                raise NotSupported(self.id + ' cancelOrders() does not support ' + methodType + ' markets')
            clientOrderIds = self.safe_string_2(params, 'client_order_id', 'clientOrderId')
            clientOrderIds = self.safe_string_2(params, 'client_order_ids', 'clientOrderIds', clientOrderIds)
            if clientOrderIds is None:
                request['order_id'] = ids
            else:
                request['client_order_id'] = clientOrderIds
                params = self.omit(params, ['client_order_id', 'client_order_ids', 'clientOrderId', 'clientOrderIds'])
        response = await getattr(self, method)(self.extend(request, params))
        #
        # spot
        #
        #     {
        #         "status": "ok",
        #         "data": {
        #             "success": [
        #                 "5983466"
        #             ],
        #             "failed": [
        #                 {
        #                     "err-msg": "Incorrect order state",
        #                     "order-state": 7,
        #                     "order-id": "",
        #                     "err-code": "order-orderstate-error",
        #                     "client-order-id": "first"
        #                 },
        #                 {
        #                     "err-msg": "Incorrect order state",
        #                     "order-state": 7,
        #                     "order-id": "",
        #                     "err-code": "order-orderstate-error",
        #                     "client-order-id": "second"
        #                 },
        #                 {
        #                     "err-msg": "The record is not found.",
        #                     "order-id": "",
        #                     "err-code": "base-not-found",
        #                     "client-order-id": "third"
        #                 }
        #             ]
        #         }
        #     }
        #
        # contracts
        #
        #     {
        #         "status": "ok",
        #         "data": {
        #             "errors": [
        #                 {
        #                     "order_id": "769206471845261312",
        #                     "err_code": 1061,
        #                     "err_msg": "This order doesnt exist."
        #                 }
        #             ],
        #             "successes": "773120304138219520"
        #         },
        #         "ts": 1604367997451
        #     }
        #
        return response

    async def cancel_all_orders(self, symbol=None, params={}):
        await self.load_markets()
        request = {
            # 'account-id' string False NA The account id used for self cancel Refer to GET /v1/account/accounts
            # 'symbol': market['id'],  # a list of comma-separated symbols, all symbols by default
            # 'types' 'string', buy-market, sell-market, buy-limit, sell-limit, buy-ioc, sell-ioc, buy-stop-limit, sell-stop-limit, buy-limit-fok, sell-limit-fok, buy-stop-limit-fok, sell-stop-limit-fok
            # 'side': 'buy',  # or 'sell'
            # 'size': 100,  # the number of orders to cancel 1-100
        }
        market = None
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        response = await self.spotPrivatePostV1OrderOrdersBatchCancelOpenOrders(self.extend(request, params))
        #
        #     {
        #         code: 200,
        #         data: {
        #             "success-count": 2,
        #             "failed-count": 0,
        #             "next-id": 5454600
        #         }
        #     }
        #
        return response

    def currency_to_precision(self, currency, fee):
        return self.decimal_to_precision(fee, 0, self.currencies[currency]['precision'])

    def safe_network(self, networkId):
        lastCharacterIndex = len(networkId) - 1
        lastCharacter = networkId[lastCharacterIndex]
        if lastCharacter == '1':
            networkId = networkId[0:lastCharacterIndex]
        networksById = {}
        return self.safe_string(networksById, networkId, networkId)

    def parse_deposit_address(self, depositAddress, currency=None):
        #
        #     {
        #         currency: "usdt",
        #         address: "0xf7292eb9ba7bc50358e27f0e025a4d225a64127b",
        #         addressTag: "",
        #         chain: "usdterc20",  # trc20usdt, hrc20usdt, usdt, algousdt
        #     }
        #
        address = self.safe_string(depositAddress, 'address')
        tag = self.safe_string(depositAddress, 'addressTag')
        if tag == '':
            tag = None
        currencyId = self.safe_string(depositAddress, 'currency')
        currency = self.safe_currency(currencyId, currency)
        code = self.safe_currency_code(currencyId, currency)
        networkId = self.safe_string(depositAddress, 'chain')
        networks = self.safe_value(currency, 'networks', {})
        networksById = self.index_by(networks, 'id')
        networkValue = self.safe_value(networksById, networkId, networkId)
        network = self.safe_string(networkValue, 'network')
        note = self.safe_string(depositAddress, 'note')
        self.check_address(address)
        return {
            'currency': code,
            'address': address,
            'tag': tag,
            'network': network,
            'note': note,
            'info': depositAddress,
        }

    async def fetch_deposit_addresses_by_network(self, code, params={}):
        await self.load_markets()
        currency = self.currency(code)
        request = {
            'currency': currency['id'],
        }
        response = await self.spotPrivateGetV2AccountDepositAddress(self.extend(request, params))
        #
        #     {
        #         code: 200,
        #         data: [
        #             {
        #                 currency: "eth",
        #                 address: "0xf7292eb9ba7bc50358e27f0e025a4d225a64127b",
        #                 addressTag: "",
        #                 chain: "eth"
        #             }
        #         ]
        #     }
        #
        data = self.safe_value(response, 'data', [])
        parsed = self.parse_deposit_addresses(data, [code], False)
        return self.index_by(parsed, 'network')

    async def fetch_deposit_address(self, code, params={}):
        rawNetwork = self.safe_string_upper(params, 'network')
        networks = self.safe_value(self.options, 'networks', {})
        network = self.safe_string_upper(networks, rawNetwork, rawNetwork)
        params = self.omit(params, 'network')
        response = await self.fetch_deposit_addresses_by_network(code, params)
        result = None
        if network is None:
            result = self.safe_value(response, code)
            if result is None:
                alias = self.safe_string(networks, code, code)
                result = self.safe_value(response, alias)
                if result is None:
                    defaultNetwork = self.safe_string(self.options, 'defaultNetwork', 'ERC20')
                    result = self.safe_value(response, defaultNetwork)
                    if result is None:
                        values = list(response.values())
                        result = self.safe_value(values, 0)
                        if result is None:
                            raise InvalidAddress(self.id + ' fetchDepositAddress() cannot find deposit address for ' + code)
            return result
        result = self.safe_value(response, network)
        if result is None:
            raise InvalidAddress(self.id + ' fetchDepositAddress() cannot find ' + network + ' deposit address for ' + code)
        return result

    async def fetch_withdraw_addresses_by_network(self, code, params={}):
        await self.load_markets()
        currency = self.currency(code)
        request = {
            'currency': currency['id'],
        }
        response = await self.spotPrivateGetV2AccountWithdrawAddress(self.extend(request, params))
        #
        #     {
        #         code: 200,
        #         data: [
        #             {
        #                 currency: "eth",
        #                 chain: "eth"
        #                 note: "Binance - TRC20",
        #                 addressTag: "",
        #                 address: "0xf7292eb9ba7bc50358e27f0e025a4d225a64127b",
        #             }
        #         ]
        #     }
        #
        data = self.safe_value(response, 'data', [])
        parsed = self.parse_deposit_addresses(data, [code], False)
        return self.index_by(parsed, 'network')

    async def fetch_withdraw_address(self, code, params={}):
        rawNetwork = self.safe_string_upper(params, 'network')
        networks = self.safe_value(self.options, 'networks', {})
        network = self.safe_string_upper(networks, rawNetwork, rawNetwork)
        params = self.omit(params, 'network')
        response = await self.fetch_withdraw_addresses_by_network(code, params)
        result = None
        if network is None:
            result = self.safe_value(response, code)
            if result is None:
                alias = self.safe_string(networks, code, code)
                result = self.safe_value(response, alias)
                if result is None:
                    defaultNetwork = self.safe_string(self.options, 'defaultNetwork', 'ERC20')
                    result = self.safe_value(response, defaultNetwork)
                    if result is None:
                        values = list(response.values())
                        result = self.safe_value(values, 0)
                        if result is None:
                            raise InvalidAddress(self.id + ' fetchWithdrawAddress() cannot find withdraw address for ' + code)
            return result
        result = self.safe_value(response, network)
        if result is None:
            raise InvalidAddress(self.id + ' fetchWithdrawAddress() cannot find ' + network + ' withdraw address for ' + code)
        return result

    async def fetch_deposits(self, code=None, since=None, limit=None, params={}):
        if limit is None or limit > 100:
            limit = 100
        await self.load_markets()
        currency = None
        if code is not None:
            currency = self.currency(code)
        request = {
            'type': 'deposit',
            'from': 0,  # From 'id' ... if you want to get results after a particular transaction id, pass the id in params.from
        }
        if currency is not None:
            request['currency'] = currency['id']
        if limit is not None:
            request['size'] = limit  # max 100
        response = await self.spotPrivateGetV1QueryDepositWithdraw(self.extend(request, params))
        # return response
        return self.parse_transactions(response['data'], currency, since, limit)

    async def fetch_withdrawals(self, code=None, since=None, limit=None, params={}):
        if limit is None or limit > 100:
            limit = 100
        await self.load_markets()
        currency = None
        if code is not None:
            currency = self.currency(code)
        request = {
            'type': 'withdraw',
            'from': 0,  # From 'id' ... if you want to get results after a particular transaction id, pass the id in params.from
        }
        if currency is not None:
            request['currency'] = currency['id']
        if limit is not None:
            request['size'] = limit  # max 100
        response = await self.spotPrivateGetV1QueryDepositWithdraw(self.extend(request, params))
        # return response
        return self.parse_transactions(response['data'], currency, since, limit)

    def parse_transaction(self, transaction, currency=None):
        #
        # fetchDeposits
        #
        #     {
        #         'id': 8211029,
        #         'type': 'deposit',
        #         'currency': 'eth',
        #         'chain': 'eth',
        #         'tx-hash': 'bd315....',
        #         'amount': 0.81162421,
        #         'address': '4b8b....',
        #         'address-tag': '',
        #         'fee': 0,
        #         'state': 'safe',
        #         'created-at': 1542180380965,
        #         'updated-at': 1542180788077
        #     }
        #
        # fetchWithdrawals
        #
        #     {
        #         'id': 6908275,
        #         'type': 'withdraw',
        #         'currency': 'btc',
        #         'chain': 'btc',
        #         'tx-hash': 'c1a1a....',
        #         'amount': 0.80257005,
        #         'address': '1QR....',
        #         'address-tag': '',
        #         'fee': 0.0005,
        #         'state': 'confirmed',
        #         'created-at': 1552107295685,
        #         'updated-at': 1552108032859
        #     }
        #
        timestamp = self.safe_integer(transaction, 'created-at')
        updated = self.safe_integer(transaction, 'updated-at')
        code = self.safe_currency_code(self.safe_string(transaction, 'currency'))
        type = self.safe_string(transaction, 'type')
        if type == 'withdraw':
            type = 'withdrawal'
        status = self.parse_transaction_status(self.safe_string(transaction, 'state'))
        tag = self.safe_string(transaction, 'address-tag')
        feeCost = self.safe_number(transaction, 'fee')
        if feeCost is not None:
            feeCost = abs(feeCost)
        return {
            'info': transaction,
            'id': self.safe_string(transaction, 'id'),
            'txid': self.safe_string(transaction, 'tx-hash'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'address': self.safe_string(transaction, 'address'),
            'tag': tag,
            'type': type,
            'amount': self.safe_number(transaction, 'amount'),
            'currency': code,
            'status': status,
            'updated': updated,
            'fee': {
                'currency': code,
                'cost': feeCost,
                'rate': None,
            },
        }

    def parse_transaction_status(self, status):
        statuses = {
            # deposit statuses
            'unknown': 'failed',
            'confirming': 'pending',
            'confirmed': 'ok',
            'safe': 'ok',
            'orphan': 'failed',
            # withdrawal statuses
            'submitted': 'pending',
            'canceled': 'canceled',
            'reexamine': 'pending',
            'reject': 'failed',
            'pass': 'pending',
            'wallet-reject': 'failed',
            # 'confirmed': 'ok',  # present in deposit statuses
            'confirm-error': 'failed',
            'repealed': 'failed',
            'wallet-transfer': 'pending',
            'pre-transfer': 'pending',
        }
        return self.safe_string(statuses, status, status)

    async def withdraw(self, code, amount, address, tag=None, params={}):
        tag, params = self.handle_withdraw_tag_and_params(tag, params)
        await self.load_markets()
        self.check_address(address)
        currency = self.currency(code)
        request = {
            'address': address,  # only supports existing addresses in your withdraw address list
            'amount': amount,
            'currency': currency['id'].lower(),
        }
        if tag is not None:
            request['addr-tag'] = tag  # only for XRP?
        networks = self.safe_value(self.options, 'networks', {})
        network = self.safe_string_upper(params, 'network')  # self line allows the user to specify either ERC20 or ETH
        network = self.safe_string_lower(networks, network, network)  # handle ETH>ERC20 alias
        if network is not None:
            # possible chains - usdterc20, trc20usdt, hrc20usdt, usdt, algousdt
            if network == 'erc20':
                request['chain'] = currency['id'] + network
            else:
                request['chain'] = network + currency['id']
            params = self.omit(params, 'network')
        response = await self.spotPrivatePostV1DwWithdrawApiCreate(self.extend(request, params))
        id = self.safe_string(response, 'data')
        return {
            'info': response,
            'id': id,
        }

    def parse_transfer(self, transfer, currency=None):
        #
        # transfer
        #
        #     {
        #         "data": 12345,
        #         "status": "ok"
        #     }
        #
        id = self.safe_string(transfer, 'data')
        code = self.safe_currency_code(None, currency)
        return {
            'info': transfer,
            'id': id,
            'timestamp': None,
            'datetime': None,
            'currency': code,
            'amount': None,
            'fromAccount': None,
            'toAccount': None,
            'status': None,
        }

    async def transfer(self, code, amount, fromAccount, toAccount, params={}):
        await self.load_markets()
        currency = self.currency(code)
        type = self.safe_string(params, 'type')
        if type is None:
            accountsByType = self.safe_value(self.options, 'accountsByType', {})
            fromAccount = fromAccount.lower()  # pro, futures
            toAccount = toAccount.lower()  # pro, futures
            fromId = self.safe_string(accountsByType, fromAccount)
            toId = self.safe_string(accountsByType, toAccount)
            if fromId is None:
                keys = list(accountsByType.keys())
                raise ExchangeError(self.id + ' fromAccount must be one of ' + ', '.join(keys))
            if toId is None:
                keys = list(accountsByType.keys())
                raise ExchangeError(self.id + ' toAccount must be one of ' + ', '.join(keys))
            type = fromAccount + '-to-' + toAccount
        request = {
            'currency': currency['id'],
            'amount': float(self.currency_to_precision(code, amount)),
            'type': type,
        }
        response = await self.spotPrivatePostFuturesTransfer(self.extend(request, params))
        #
        #     {
        #         "data": 12345,
        #         "status": "ok"
        #     }
        #
        transfer = self.parse_transfer(response, currency)
        return self.extend(transfer, {
            'amount': amount,
            'currency': code,
            'fromAccount': fromAccount,
            'toAccount': toAccount,
        })

    async def fetch_borrow_rates_per_symbol(self, params={}):
        await self.load_markets()
        response = await self.spotPrivateGetV1MarginLoanInfo(params)
        # {
        #     "status": "ok",
        #     "data": [
        #         {
        #             "symbol": "1inchusdt",
        #             "currencies": [
        #                 {
        #                     "currency": "1inch",
        #                     "interest-rate": "0.00098",
        #                     "min-loan-amt": "90.000000000000000000",
        #                     "max-loan-amt": "1000.000000000000000000",
        #                     "loanable-amt": "0.0",
        #                     "actual-rate": "0.00098"
        #                 },
        #                 {
        #                     "currency": "usdt",
        #                     "interest-rate": "0.00098",
        #                     "min-loan-amt": "100.000000000000000000",
        #                     "max-loan-amt": "1000.000000000000000000",
        #                     "loanable-amt": "0.0",
        #                     "actual-rate": "0.00098"
        #                 }
        #             ]
        #         },
        #         ...
        #     ]
        # }
        timestamp = self.milliseconds()
        data = self.safe_value(response, 'data')
        rates = {
            'info': response,
        }
        for i in range(0, len(data)):
            rate = data[i]
            currencies = self.safe_value(rate, 'currencies')
            symbolRates = {}
            for j in range(0, len(currencies)):
                currency = currencies[j]
                currencyId = self.safe_string(currency, 'currency')
                code = self.safe_currency_code(currencyId, 'currency')
                symbolRates[code] = {
                    'currency': code,
                    'rate': self.safe_number(currency, 'actual-rate'),
                    'span': 86400000,
                    'timestamp': timestamp,
                    'datetime': self.iso8601(timestamp),
                }
            market = self.markets_by_id[self.safe_string(rate, 'symbol')]
            symbol = market['symbol']
            rates[symbol] = symbolRates
        return rates

    async def fetch_borrow_rates(self, params={}):
        await self.load_markets()
        response = await self.spotPrivateGetV1MarginLoanInfo(params)
        # {
        #     "status": "ok",
        #     "data": [
        #         {
        #             "symbol": "1inchusdt",
        #             "currencies": [
        #                 {
        #                     "currency": "1inch",
        #                     "interest-rate": "0.00098",
        #                     "min-loan-amt": "90.000000000000000000",
        #                     "max-loan-amt": "1000.000000000000000000",
        #                     "loanable-amt": "0.0",
        #                     "actual-rate": "0.00098"
        #                 },
        #                 {
        #                     "currency": "usdt",
        #                     "interest-rate": "0.00098",
        #                     "min-loan-amt": "100.000000000000000000",
        #                     "max-loan-amt": "1000.000000000000000000",
        #                     "loanable-amt": "0.0",
        #                     "actual-rate": "0.00098"
        #                 }
        #             ]
        #         },
        #         ...
        #     ]
        # }
        timestamp = self.milliseconds()
        data = self.safe_value(response, 'data')
        rates = {}
        for i in range(0, len(data)):
            market = data[i]
            currencies = self.safe_value(market, 'currencies')
            for j in range(0, len(currencies)):
                currency = currencies[j]
                currencyId = self.safe_string(currency, 'currency')
                code = self.safe_currency_code(currencyId, 'currency')
                rates[code] = {
                    'currency': code,
                    'rate': self.safe_number(currency, 'actual-rate'),
                    'span': 86400000,
                    'timestamp': timestamp,
                    'datetime': self.iso8601(timestamp),
                    'info': None,
                }
        return rates

    async def fetch_funding_rate_history(self, symbol=None, since=None, limit=None, params={}):
        #
        # Gets a history of funding rates with their timestamps
        #  (param) symbol: Future currency pair
        #  (param) limit: not used by huobi
        #  (param) since: not used by huobi
        #  (param) params: Object containing more params for the request
        #  return: [{symbol, fundingRate, timestamp, dateTime}]
        #
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchFundingRateHistory() requires a symbol argument')
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'contract_code': market['id'],
        }
        method = None
        if market['inverse']:
            method = 'contractPublicGetSwapApiV1SwapHistoricalFundingRate'
        elif market['linear']:
            method = 'contractPublicGetLinearSwapApiV1SwapHistoricalFundingRate'
        else:
            raise NotSupported(self.id + ' fetchFundingRateHistory() supports inverse and linear swaps only')
        response = await getattr(self, method)(self.extend(request, params))
        #
        # {
        #     "status": "ok",
        #     "data": {
        #         "total_page": 62,
        #         "current_page": 1,
        #         "total_size": 1237,
        #         "data": [
        #             {
        #                 "avg_premium_index": "-0.000208064395065541",
        #                 "funding_rate": "0.000100000000000000",
        #                 "realized_rate": "0.000100000000000000",
        #                 "funding_time": "1638921600000",
        #                 "contract_code": "BTC-USDT",
        #                 "symbol": "BTC",
        #                 "fee_asset": "USDT"
        #             },
        #         ]
        #     },
        #     "ts": 1638939294277
        # }
        #
        data = self.safe_value(response, 'data')
        result = self.safe_value(data, 'data')
        rates = []
        for i in range(0, len(result)):
            entry = result[i]
            marketId = self.safe_string(entry, 'contract_code')
            symbol = self.safe_symbol(marketId)
            timestamp = self.safe_string(entry, 'funding_time')
            rates.append({
                'info': entry,
                'symbol': symbol,
                'fundingRate': self.safe_number(entry, 'funding_rate'),
                'timestamp': timestamp,
                'datetime': self.iso8601(timestamp),
            })
        sorted = self.sort_by(rates, 'timestamp')
        return self.filter_by_symbol_since_limit(sorted, symbol, since, limit)

    def parse_funding_rate(self, fundingRate, market=None):
        #
        # {
        #      "status": "ok",
        #      "data": {
        #         "estimated_rate": "0.000100000000000000",
        #         "funding_rate": "0.000100000000000000",
        #         "contract_code": "BCH-USD",
        #         "symbol": "BCH",
        #         "fee_asset": "BCH",
        #         "funding_time": "1639094400000",
        #         "next_funding_time": "1639123200000"
        #     },
        #     "ts": 1639085854775
        # }
        #
        nextFundingRate = self.safe_number(fundingRate, 'estimated_rate')
        previousFundingTimestamp = self.safe_integer(fundingRate, 'funding_time')
        nextFundingTimestamp = self.safe_integer(fundingRate, 'next_funding_time')
        marketId = self.safe_string(fundingRate, 'contract_code')
        symbol = self.safe_symbol(marketId, market)
        return {
            'info': fundingRate,
            'symbol': symbol,
            'markPrice': None,
            'indexPrice': None,
            'interestRate': None,
            'estimatedSettlePrice': None,
            'timestamp': None,
            'datetime': None,
            'previousFundingRate': self.safe_number(fundingRate, 'funding_rate'),
            'nextFundingRate': nextFundingRate,
            'previousFundingTimestamp': previousFundingTimestamp,
            'nextFundingTimestamp': nextFundingTimestamp,
            'previousFundingDatetime': self.iso8601(previousFundingTimestamp),
            'nextFundingDatetime': self.iso8601(nextFundingTimestamp),
        }

    async def fetch_funding_rate(self, symbol, params={}):
        await self.load_markets()
        market = self.market(symbol)
        method = None
        if market['inverse']:
            method = 'contractPublicGetSwapApiV1SwapFundingRate'
        elif market['linear']:
            method = 'contractPublicGetLinearSwapApiV1SwapFundingRate'
        else:
            raise NotSupported(self.id + ' fetchFundingRateHistory() supports inverse and linear swaps only')
        request = {
            'contract_code': market['id'],
        }
        response = await getattr(self, method)(self.extend(request, params))
        #
        # {
        #     "status": "ok",
        #     "data": {
        #         "estimated_rate": "0.000100000000000000",
        #         "funding_rate": "0.000100000000000000",
        #         "contract_code": "BTC-USDT",
        #         "symbol": "BTC",
        #         "fee_asset": "USDT",
        #         "funding_time": "1603699200000",
        #         "next_funding_time": "1603728000000"
        #     },
        #     "ts": 1603696494714
        # }
        #
        result = self.safe_value(response, 'data', {})
        return self.parse_funding_rate(result, market)

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        url = '/'
        query = self.omit(params, self.extract_params(path))
        if isinstance(api, basestring):
            # signing implementation for the old endpoints
            if api == 'market':
                url += api
            elif (api == 'public') or (api == 'private'):
                url += self.version
            elif (api == 'v2Public') or (api == 'v2Private'):
                url += 'v2'
            url += '/' + self.implode_params(path, params)
            if api == 'private' or api == 'v2Private':
                self.check_required_credentials()
                timestamp = self.ymdhms(self.milliseconds(), 'T')
                request = {
                    'SignatureMethod': 'HmacSHA256',
                    'SignatureVersion': '2',
                    'AccessKeyId': self.apiKey,
                    'Timestamp': timestamp,
                }
                if method != 'POST':
                    request = self.extend(request, query)
                request = self.keysort(request)
                auth = self.urlencode(request)
                # unfortunately, PHP demands double quotes for the escaped newline symbol
                payload = "\n".join([method, self.hostname, url, auth])  # eslint-disable-line quotes
                signature = self.hmac(self.encode(payload), self.encode(self.secret), hashlib.sha256, 'base64')
                auth += '&' + self.urlencode({'Signature': signature})
                url += '?' + auth
                if method == 'POST':
                    body = self.json(query)
                    headers = {
                        'Content-Type': 'application/json',
                    }
                else:
                    headers = {
                        'Content-Type': 'application/x-www-form-urlencoded',
                    }
            else:
                if query:
                    url += '?' + self.urlencode(query)
            url = self.implode_params(self.urls['api'][api], {
                'hostname': self.hostname,
            }) + url
        else:
            # signing implementation for the new endpoints
            # type, access = api
            type = self.safe_string(api, 0)
            access = self.safe_string(api, 1)
            url += self.implode_params(path, params)
            hostname = self.safe_string(self.urls['hostnames'], type)
            if access == 'public':
                if query:
                    url += '?' + self.urlencode(query)
            elif access == 'private':
                self.check_required_credentials()
                timestamp = self.ymdhms(self.milliseconds(), 'T')
                request = {
                    'SignatureMethod': 'HmacSHA256',
                    'SignatureVersion': '2',
                    'AccessKeyId': self.apiKey,
                    'Timestamp': timestamp,
                }
                if method != 'POST':
                    request = self.extend(request, query)
                request = self.keysort(request)
                auth = self.urlencode(request)
                # unfortunately, PHP demands double quotes for the escaped newline symbol
                payload = "\n".join([method, hostname, url, auth])  # eslint-disable-line quotes
                signature = self.hmac(self.encode(payload), self.encode(self.secret), hashlib.sha256, 'base64')
                auth += '&' + self.urlencode({'Signature': signature})
                url += '?' + auth
                if method == 'POST':
                    body = self.json(query)
                    headers = {
                        'Content-Type': 'application/json',
                    }
                else:
                    headers = {
                        'Content-Type': 'application/x-www-form-urlencoded',
                    }
            url = self.implode_params(self.urls['api'][type], {
                'hostname': hostname,
            }) + url
        return {'url': url, 'method': method, 'body': body, 'headers': headers}

    def calculate_rate_limiter_cost(self, api, method, path, params, config={}, context={}):
        return self.safe_integer(config, 'cost', 1)

    def handle_errors(self, httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody):
        if response is None:
            return  # fallback to default error handler
        if 'status' in response:
            #
            #     {"status":"error","err-code":"order-limitorder-amount-min-error","err-msg":"limit order amount error, min: `0.001`","data":null}
            #
            status = self.safe_string(response, 'status')
            if status == 'error':
                code = self.safe_string_2(response, 'err-code', 'err_code')
                feedback = self.id + ' ' + body
                self.throw_broadly_matched_exception(self.exceptions['broad'], body, feedback)
                self.throw_exactly_matched_exception(self.exceptions['exact'], code, feedback)
                message = self.safe_string_2(response, 'err-msg', 'err_msg')
                self.throw_exactly_matched_exception(self.exceptions['exact'], message, feedback)
                raise ExchangeError(feedback)
