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

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

from ccxt.base.exchange import Exchange
import hashlib
from ccxt.base.errors import ExchangeError
from ccxt.base.errors import AuthenticationError
from ccxt.base.errors import PermissionDenied
from ccxt.base.errors import AccountSuspended
from ccxt.base.errors import ArgumentsRequired
from ccxt.base.errors import BadRequest
from ccxt.base.errors import BadSymbol
from ccxt.base.errors import BadResponse
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 DuplicateOrderId
from ccxt.base.errors import NotSupported
from ccxt.base.errors import NetworkError
from ccxt.base.errors import DDoSProtection
from ccxt.base.errors import RateLimitExceeded
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 TICK_SIZE
from ccxt.base.precise import Precise


class zb(Exchange):

    def describe(self):
        return self.deep_extend(super(zb, self).describe(), {
            'id': 'zb',
            'name': 'ZB',
            'countries': ['CN'],
            # previously rateLimit = 100
            # Trading and Margin 10 000 per minute(IP) => 10000 / 60 = 166.66666... per second => rateLimit = 1000/166.66666 = 6
            # Trade and Margin 60 per second(apiKey) => weight = 166.666 / 60 = 2.778(2.7777777...)
            # Kline 1 per second => weight = 166.667
            # v2 Futures API 100 per 2 seconds => 50 per second => weight = 3.334(3.3333333...)
            # for endpoints not mentioned in docs
            # previous rateLimit was 100 translating to 10 requests per second => weight = 166.666 / 10 = 16.667(16.666666...)
            'rateLimit': 6,
            'version': 'v1',
            'pro': True,
            'has': {
                'CORS': None,
                'spot': True,
                'margin': True,
                'swap': True,
                'future': None,
                'option': None,
                'addMargin': True,
                'borrowMargin': True,
                'cancelAllOrders': True,
                'cancelOrder': True,
                'createMarketOrder': False,
                'createOrder': True,
                'createReduceOnlyOrder': False,
                'createStopLimitOrder': True,
                'createStopMarketOrder': True,
                'createStopOrder': True,
                'fetchBalance': True,
                'fetchBorrowRate': True,
                'fetchBorrowRateHistories': False,
                'fetchBorrowRateHistory': False,
                'fetchBorrowRates': True,
                'fetchCanceledOrders': True,
                'fetchClosedOrders': True,
                'fetchCurrencies': True,
                'fetchDepositAddress': True,
                'fetchDepositAddresses': True,
                'fetchDeposits': True,
                'fetchFundingHistory': False,
                'fetchFundingRate': True,
                'fetchFundingRateHistory': True,
                'fetchFundingRates': True,
                'fetchIndexOHLCV': True,
                'fetchLedger': True,
                'fetchLeverage': False,
                'fetchLeverageTiers': False,
                'fetchMarketLeverageTiers': False,
                'fetchMarkets': True,
                'fetchMarkOHLCV': True,
                'fetchOHLCV': True,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'fetchOrderBook': True,
                'fetchOrders': True,
                'fetchPosition': True,
                'fetchPositions': True,
                'fetchPositionsRisk': False,
                'fetchPremiumIndexOHLCV': False,
                'fetchTicker': True,
                'fetchTickers': True,
                'fetchTrades': True,
                'fetchTradingFee': False,
                'fetchTradingFees': False,
                'fetchWithdrawals': True,
                'reduceMargin': True,
                'setLeverage': True,
                'setMarginMode': False,
                'setPositionMode': False,
                'transfer': True,
                'withdraw': True,
            },
            'timeframes': {
                '1m': '1m',
                '3m': '3m',
                '5m': '5m',
                '15m': '15m',
                '30m': '30m',
                '1h': '1h',
                '2h': '2h',
                '4h': '4h',
                '6h': '6h',
                '12h': '12h',
                '1d': '1d',
                '3d': '3d',
                '5d': '5d',
                '1w': '1w',
            },
            'hostname': 'zb.com',  # zb.cafe for users in China
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/32859187-cd5214f0-ca5e-11e7-967d-96568e2e2bd1.jpg',
                'api': {
                    'spot': {
                        'v1': {
                            'public': 'https://api.{hostname}/data',
                            'private': 'https://trade.{hostname}/api',
                        },
                    },
                    'contract': {
                        'v1': {
                            'public': 'https://fapi.{hostname}/api/public',
                        },
                        'v2': {
                            'public': 'https://fapi.{hostname}/Server/api',
                            'private': 'https://fapi.{hostname}/Server/api',
                        },
                    },
                },
                'www': 'https://www.zb.com',
                'doc': 'https://www.zb.com/i/developer',
                'fees': 'https://www.zb.com/i/rate',
                'referral': {
                    'url': 'https://www.zb.com/en/register?ref=4301lera',
                    'discount': 0.16,
                },
            },
            'api': {
                'spot': {
                    'v1': {
                        'public': {
                            'get': {
                                'markets': 16.667,
                                'ticker': 16.667,
                                'allTicker': 16.667,
                                'depth': 16.667,
                                'trades': 16.667,
                                'kline': 166.667,  # Kline 1 per second
                                'getGroupMarkets': 16.667,
                                'getFeeInfo': 16.667,
                            },
                        },
                        'private': {
                            'get': {
                                # spot API
                                'order': 1,  # Trade API
                                'orderMoreV2': 1,  # Trade API
                                'cancelOrder': 1,  # Trade API
                                'cancelAllOrdersAfter': 1,  # Trade API TODO add cancelAllOrders
                                'getOrder': 1,  # Trade API
                                'getOrders': 1,  # Trade API
                                'getOrdersNew': 16.667,
                                'getOrdersIgnoreTradeType': 1,  # Trade API
                                'getUnfinishedOrdersIgnoreTradeType': 1,  # Trade API
                                'getFinishedAndPartialOrders': 1,  # Trade API
                                'getAccountInfo': 16.667,
                                'getUserAddress': 16.667,
                                'getPayinAddress': 16.667,
                                'getWithdrawAddress': 16.667,
                                'getWithdrawRecord': 16.667,
                                'getChargeRecord': 16.667,
                                'getCnyWithdrawRecord': 16.667,
                                'getCnyChargeRecord': 16.667,
                                'withdraw': 16.667,
                                # sub accounts
                                'addSubUser': 16.667,
                                'getSubUserList': 16.667,
                                'doTransferFunds': 16.667,
                                'createSubUserKey': 16.667,  # removed on 2021-03-16 according to the update log in the API doc
                                # leverage API
                                'getLeverAssetsInfo': 16.667,
                                'getLeverBills': 16.667,
                                'transferInLever': 16.667,
                                'transferOutLever': 16.667,
                                'loan': 16.667,
                                'cancelLoan': 16.667,
                                'getLoans': 16.667,
                                'getLoanRecords': 16.667,
                                'borrow': 16.667,
                                'autoBorrow': 16.667,
                                'repay': 16.667,
                                'doAllRepay': 16.667,
                                'getRepayments': 16.667,
                                'getFinanceRecords': 16.667,
                                'changeInvestMark': 16.667,
                                'changeLoop': 16.667,
                                # cross API
                                'getCrossAssets': 16.667,
                                'getCrossBills': 16.667,
                                'transferInCross': 16.667,
                                'transferOutCross': 16.667,
                                'doCrossLoan': 16.667,
                                'doCrossRepay': 16.667,
                                'getCrossRepayRecords': 16.667,
                            },
                        },
                    },
                },
                'contract': {
                    'v1': {
                        'public': {
                            'get': {
                                'depth': 16.667,
                                'fundingRate': 16.667,
                                'indexKline': 16.667,
                                'indexPrice': 16.667,
                                'kline': 16.667,
                                'markKline': 16.667,
                                'markPrice': 16.667,
                                'ticker': 16.667,
                                'trade': 16.667,
                            },
                        },
                    },
                    'v2': {
                        'public': {
                            'get': {
                                'allForceOrders': 3.334,
                                'config/marketList': 3.334,
                                'topLongShortAccountRatio': 3.334,
                                'topLongShortPositionRatio': 3.334,
                                'fundingRate': 3.334,
                                'premiumIndex': 3.334,
                            },
                        },
                        'private': {
                            'get': {
                                'Fund/balance': 3.334,
                                'Fund/getAccount': 3.334,
                                'Fund/getBill': 3.334,
                                'Fund/getBillTypeList': 3.334,
                                'Fund/marginHistory': 3.334,
                                'Positions/getPositions': 3.334,
                                'Positions/getNominalValue': 3.334,
                                'Positions/marginInfo': 3.334,
                                'setting/get': 3.334,
                                'trade/getAllOrders': 3.334,
                                'trade/getOrder': 3.334,
                                'trade/getOrderAlgos': 3.334,
                                'trade/getTradeList': 3.334,
                                'trade/getUndoneOrders': 3.334,
                                'trade/tradeHistory': 3.334,
                            },
                            'post': {
                                'activity/buyTicket': 3.334,
                                'Fund/transferFund': 3.334,
                                'Positions/setMarginCoins': 3.334,
                                'Positions/updateAppendUSDValue': 3.334,
                                'Positions/updateMargin': 3.334,
                                'setting/setLeverage': 3.334,
                                'setting/setPositionsMode': 3.334,
                                'trade/batchOrder': 3.334,
                                'trade/batchCancelOrder': 3.334,
                                'trade/cancelAlgos': 3.334,
                                'trade/cancelAllOrders': 3.334,
                                'trade/cancelOrder': 3.334,
                                'trade/order': 3.334,
                                'trade/orderAlgo': 3.334,
                                'trade/updateOrderAlgo': 3.334,
                            },
                        },
                    },
                },
            },
            'fees': {
                'funding': {
                    'withdraw': {},
                },
                'trading': {
                    'maker': self.parse_number('0.002'),
                    'taker': self.parse_number('0.002'),
                },
            },
            'commonCurrencies': {
                'ANG': 'Anagram',
                'ENT': 'ENTCash',
                'BCHABC': 'BCHABC',  # conflict with BCH / BCHA
                'BCHSV': 'BCHSV',  # conflict with BCH / BSV
            },
            'options': {
                'timeframes': {
                    'spot': {
                        '1m': '1min',
                        '3m': '3min',
                        '5m': '5min',
                        '15m': '15min',
                        '30m': '30min',
                        '1h': '1hour',
                        '2h': '2hour',
                        '4h': '4hour',
                        '6h': '6hour',
                        '12h': '12hour',
                        '1d': '1day',
                        '3d': '3day',
                        '1w': '1week',
                    },
                    'swap': {
                        '1m': '1M',
                        '5m': '5M',
                        '15m': '15M',
                        '30m': '30M',
                        '1h': '1H',
                        '6h': '6H',
                        '1d': '1D',
                        '5d': '5D',
                    },
                },
            },
            'precisionMode': TICK_SIZE,
            'exceptions': {
                'ws': {
                    # '1000': ExchangeError,  # The call is successful.
                    '1001': ExchangeError,  # General error prompt
                    '1002': ExchangeError,  # Internal Error
                    '1003': AuthenticationError,  # Fail to verify
                    '1004': AuthenticationError,  # The transaction password is locked
                    '1005': AuthenticationError,  # Wrong transaction password, please check it and re-enter。
                    '1006': PermissionDenied,  # Real-name authentication is pending approval or unapproved
                    '1007': ExchangeError,  # Channel does not exist
                    '1009': OnMaintenance,  # This interface is under maintenance
                    '1010': ExchangeNotAvailable,  # Not available now
                    '1012': PermissionDenied,  # Insufficient permissions
                    '1013': ExchangeError,  # Cannot trade, please contact email: support@zb.cn for support.
                    '1014': ExchangeError,  # Cannot sell during the pre-sale period
                    '2001': InsufficientFunds,  # Insufficient CNY account balance
                    '2002': InsufficientFunds,  # Insufficient BTC account balance
                    '2003': InsufficientFunds,  # Insufficient LTC account balance
                    '2005': InsufficientFunds,  # Insufficient ETH account balance
                    '2006': InsufficientFunds,  # ETCInsufficient account balance
                    '2007': InsufficientFunds,  # BTSInsufficient account balance
                    '2008': InsufficientFunds,  # EOSInsufficient account balance
                    '2009': InsufficientFunds,  # BCCInsufficient account balance
                    '3001': OrderNotFound,  # Order not found or is completed
                    '3002': InvalidOrder,  # Invalid amount
                    '3003': InvalidOrder,  # Invalid quantity
                    '3004': AuthenticationError,  # User does not exist
                    '3005': BadRequest,  # Invalid parameter
                    '3006': PermissionDenied,  # Invalid IP or not consistent with the bound IP
                    '3007': RequestTimeout,  # The request time has expired
                    '3008': ExchangeError,  # Transaction not found
                    '3009': InvalidOrder,  # The price exceeds the limit
                    '3010': PermissionDenied,  # It fails to place an order, due to you have set up to prohibit trading of self market.
                    '3011': InvalidOrder,  # The entrusted price is abnormal, please modify it and place order again
                    '3012': InvalidOrder,  # Duplicate custom customerOrderId
                    '4001': AccountSuspended,  # APIThe interface is locked for one hour
                    '4002': RateLimitExceeded,  # Request too frequently
                },
                'exact': {
                    # '1000': 'Successful operation',
                    '10001': ExchangeError,  # Operation failed
                    '10002': PermissionDenied,  # Operation is forbidden
                    '10003': BadResponse,  # Data existed
                    '10004': BadResponse,  # Date not exist
                    '10005': PermissionDenied,  # Forbidden to access the interface
                    '10006': BadRequest,  # Currency invalid or expired
                    '10007': ExchangeError,  # {0}
                    '10008': ExchangeError,  # Operation failed: {0}
                    '10009': ExchangeError,  # URL error
                    '1001': ExchangeError,  # 'General error message',
                    '10010': AuthenticationError,  # API KEY not exist
                    '10011': AuthenticationError,  # API KEY CLOSED
                    '10012': AccountSuspended,  # User API has been frozen, please contact customer service for processing
                    '10013': AuthenticationError,  # API verification failed
                    '10014': AuthenticationError,  # Invalid signature(1001)
                    '10015': AuthenticationError,  # Invalid signature(1002)
                    '10016': AuthenticationError,  # Invalid ip
                    '10017': PermissionDenied,  # Permission denied
                    '10018': AccountSuspended,  # User has been frozen, please contact customer service
                    '10019': RequestTimeout,  # Request time has expired
                    '1002': ExchangeError,  # 'Internal error',
                    '10020': BadRequest,  # {0}Parameter cannot be empty
                    '10021': BadRequest,  # {0}Invalid parameter
                    '10022': BadRequest,  # Request method error
                    '10023': RateLimitExceeded,  # Request frequency is too fast, exceeding the limit allowed by the interface
                    '10024': AuthenticationError,  # Login failed
                    '10025': ExchangeError,  # Non-personal operation
                    '10026': NetworkError,  # Failed to request interface, please try again
                    '10027': RequestTimeout,  # Timed out, please try again later
                    '10028': ExchangeNotAvailable,  # System busy, please try again later
                    '10029': DDoSProtection,  # Frequent operation, please try again later
                    '1003': AuthenticationError,  # 'Verification does not pass',
                    '10030': BadRequest,  # Currency already exist
                    '10031': BadRequest,  # Currency does not exist
                    '10032': BadRequest,  # Market existed
                    '10033': BadRequest,  # Market not exist
                    '10034': BadRequest,  # Currency error
                    '10035': BadRequest,  # Market not open
                    '10036': BadRequest,  # Ineffective market type
                    '10037': ArgumentsRequired,  # User id cannot be empty
                    '10038': BadRequest,  # Market id cannot be empty
                    '10039': BadResponse,  # Failed to get mark price
                    '1004': AuthenticationError,  # 'Funding security password lock',
                    '10040': BadResponse,  # Failed to obtain the opening margin configuration
                    '10041': BadResponse,  # Failed to obtain maintenance margin allocation
                    '10042': ExchangeError,  # Avg. price error
                    '10043': ExchangeError,  # Abnormal acquisition of liquidation price
                    '10044': ExchangeError,  # Unrealized profit and loss acquisition exception
                    '10045': ExchangeError,  # jdbcData source acquisition failed
                    '10046': ExchangeError,  # Invalid position opening direction
                    '10047': ExchangeError,  # The maximum position allowed by the current leverage multiple has been exceeded
                    '10048': ExchangeError,  # The maximum allowable order quantity has been exceeded
                    '10049': NetworkError,  # Failed to get the latest price
                    '1005': AuthenticationError,  # 'Funds security password is incorrect, please confirm and re-enter.',
                    '1006': AuthenticationError,  # 'Real-name certification pending approval or audit does not pass',
                    '1009': ExchangeNotAvailable,  # 'This interface is under maintenance',
                    '1010': ExchangeNotAvailable,  # Not available now
                    '10100': OnMaintenance,  # Sorry! System maintenance, stop operation
                    '1012': PermissionDenied,  # Insufficient permissions
                    '1013': ExchangeError,  # Cannot trade, please contact email: support@zb.cn for support.
                    '1014': ExchangeError,  # Cannot sell during the pre-sale period
                    '11000': ExchangeError,  # Funding change failed
                    '11001': ExchangeError,  # Position change failed
                    '110011': ExchangeError,  # Exceeds the maximum leverage allowed by the position
                    '11002': ExchangeError,  # Funding not exist
                    '11003': ExchangeError,  # Freeze records not exist
                    '11004': InsufficientFunds,  # Insufficient frozen funds
                    '11005': InvalidOrder,  # Insufficient positions
                    '11006': InsufficientFunds,  # Insufficient frozen positions
                    '11007': OrderNotFound,  # Position not exist
                    '11008': ExchangeError,  # The contract have positions, cannot be modified
                    '11009': ExchangeError,  # Failed to query data
                    '110110': ExchangeError,  # Exceed the market's maximum leverage
                    '11012': InsufficientFunds,  # Insufficient margin
                    '11013': ExchangeError,  # Exceeding accuracy limit
                    '11014': ExchangeError,  # Invalid bill type
                    '11015': AuthenticationError,  # Failed to add default account
                    '11016': AuthenticationError,  # Account not exist
                    '11017': ExchangeError,  # Funds are not frozen or unfrozen
                    '11018': InsufficientFunds,  # Insufficient funds
                    '11019': ExchangeError,  # Bill does not exist
                    '11021': InsufficientFunds,  # Inconsistent currency for funds transfer
                    '11023': ExchangeError,  # Same transaction currency
                    '11030': PermissionDenied,  # Position is locked, the operation is prohibited
                    '11031': ExchangeError,  # The number of bill changes is zero
                    '11032': ExchangeError,  # The same request is being processed, please do not submit it repeatedly
                    '11033': ArgumentsRequired,  # Position configuration data is empty
                    '11034': ExchangeError,  # Funding fee is being settled, please do not operate
                    '12000': InvalidOrder,  # Invalid order price
                    '12001': InvalidOrder,  # Invalid order amount
                    '12002': InvalidOrder,  # Invalid order type
                    '12003': InvalidOrder,  # Invalid price accuracy
                    '12004': InvalidOrder,  # Invalid quantity precision
                    '12005': InvalidOrder,  # order value less than the minimum or greater than the maximum
                    '12006': InvalidOrder,  # Customize's order number format is wrong
                    '12007': InvalidOrder,  # Direction error
                    '12008': InvalidOrder,  # Order type error
                    '12009': InvalidOrder,  # Commission type error
                    '12010': InvalidOrder,  # Failed to place the order, the loss of the order placed at self price will exceed margin
                    '12011': InvalidOrder,  # it's not a buz order
                    '12012': OrderNotFound,  # order not exist
                    '12013': InvalidOrder,  # Order user does not match
                    '12014': InvalidOrder,  # Order is still in transaction
                    '12015': InvalidOrder,  # Order preprocessing failed
                    '12016': InvalidOrder,  # Order cannot be canceled
                    '12017': InvalidOrder,  # Transaction Record not exist
                    '12018': InvalidOrder,  # Order failed
                    '12019': ArgumentsRequired,  # self.extend parameter cannot be empty
                    '12020': ExchangeError,  # self.extend Parameter error
                    '12021': InvalidOrder,  # The order price is not within the price limit rules!
                    '12022': InvalidOrder,  # Stop placing an order while the system is calculating the fund fee
                    '12023': OrderNotFound,  # There are no positions to close
                    '12024': InvalidOrder,  # Orders are prohibited, stay tuned!
                    '12025': InvalidOrder,  # Order cancellation is prohibited, so stay tuned!
                    '12026': DuplicateOrderId,  # Order failed， customize order number exists
                    '12027': ExchangeNotAvailable,  # System busy, please try again later
                    '12028': InvalidOrder,  # The market has banned trading
                    '12029': InvalidOrder,  # Forbidden place order, stay tuned
                    '12201': InvalidOrder,  # Delegation strategy does not exist or the status has changed
                    '12202': InvalidOrder,  # Delegation strategy has been changed, cannot be canceled
                    '12203': InvalidOrder,  # Wrong order type
                    '12204': InvalidOrder,  # Invalid trigger price
                    '12205': InvalidOrder,  # The trigger price must be greater than the market’s selling price or lower than the buying price.
                    '12206': InvalidOrder,  # Direction and order type do not match
                    '12207': RateLimitExceeded,  # Submission failed, exceeding the allowed limit
                    '13001': AuthenticationError,  # User not exist
                    '13002': PermissionDenied,  # User did not activate futures
                    # '13003': AuthenticationError,  # User is locked
                    '13003': InvalidOrder,  # Margin gear is not continuous
                    '13004': InvalidOrder,  # The margin quick calculation amount is less than 0
                    '13005': RateLimitExceeded,  # You have exceeded the number of exports that day
                    '13006': ExchangeError,  # No markets are bookmarked
                    '13007': ExchangeError,  # Market not favorited
                    '13008': ExchangeError,  # Not in any market user whitelist
                    '13009': ExchangeError,  # Not in the whitelist of users in self market
                    '14000': ExchangeError,  # {0}not support
                    '14001': AuthenticationError,  # Already logged in, no need to log in multiple times
                    '14002': AuthenticationError,  # Not logged in yet, please log in before subscribing
                    '14003': ExchangeError,  # This is a channel for one-time queries, no need to unsubscribe
                    '14100': ExchangeError,  # Accuracy does not support
                    '14101': RateLimitExceeded,  # Request exceeded frequency limit
                    '14200': ArgumentsRequired,  # id empty
                    '14300': ExchangeError,  # activity not exist
                    '14301': ExchangeError,  # The event has been opened and cannot be admitted
                    '14302': ExchangeError,  # The purchase time has passed and cannot be admitted
                    '14303': ExchangeError,  # Not yet open for the purchase
                    '14305': ExchangeError,  # Cannot enter, the maximum number of returns has been exceeded
                    '14306': ExchangeError,  # Cannot repeat admission
                    '14307': InvalidOrder,  # Unable to cancel, status has been changed
                    '14308': InvalidOrder,  # Unable to cancel, the amount does not match
                    '14309': ExchangeError,  # Activity has not started
                    '14310': NotSupported,  # Activity is over
                    '14311': NotSupported,  # The activity does not support orders placed in self market
                    '14312': ExchangeError,  # You have not participated in self activity
                    '14313': PermissionDenied,  # Sorry! The purchase failed, the maximum number of participants has been reached
                    '14314': ExchangeError,  # Active period id error
                    '2001': InsufficientFunds,  # 'Insufficient CNY Balance',
                    '2002': InsufficientFunds,  # 'Insufficient BTC Balance',
                    '2003': InsufficientFunds,  # 'Insufficient LTC Balance',
                    '2005': InsufficientFunds,  # 'Insufficient ETH Balance',
                    '2006': InsufficientFunds,  # 'Insufficient ETC Balance',
                    '2007': InsufficientFunds,  # 'Insufficient BTS Balance',
                    '2008': InsufficientFunds,  # EOSInsufficient account balance
                    '2009': InsufficientFunds,  # 'Account balance is not enough',
                    '3001': OrderNotFound,  # 'Pending orders not found',
                    '3002': InvalidOrder,  # 'Invalid price',
                    '3003': InvalidOrder,  # 'Invalid amount',
                    '3004': AuthenticationError,  # 'User does not exist',
                    '3005': BadRequest,  # 'Invalid parameter',
                    '3006': AuthenticationError,  # 'Invalid IP or inconsistent with the bound IP',
                    '3007': AuthenticationError,  # 'The request time has expired',
                    '3008': OrderNotFound,  # 'Transaction records not found',
                    '3009': InvalidOrder,  # 'The price exceeds the limit',
                    '3010': PermissionDenied,  # It fails to place an order, due to you have set up to prohibit trading of self market.
                    '3011': InvalidOrder,  # 'The entrusted price is abnormal, please modify it and place order again',
                    '3012': InvalidOrder,  # Duplicate custom customerOrderId
                    '4001': ExchangeNotAvailable,  # 'API interface is locked or not enabled',
                    '4002': RateLimitExceeded,  # 'Request too often',
                    '9999': ExchangeError,  # Unknown error
                },
                'broad': {
                    '提币地址有误, 请先添加提币地址。': InvalidAddress,  # {"code":1001,"message":"提币地址有误，请先添加提币地址。"}
                    '资金不足,无法划账': InsufficientFunds,  # {"code":1001,"message":"资金不足,无法划账"}
                    '响应超时': RequestTimeout,  # {"code":1001,"message":"响应超时"}
                },
            },
        })

    def fetch_markets(self, params={}):
        """
        retrieves data on all markets for zb
        :param dict params: extra parameters specific to the exchange api endpoint
        :returns [dict]: an array of objects representing market data
        """
        #
        #     {
        #         "zb_qc":{
        #             "amountScale":2,
        #             "minAmount":0.01,
        #             "minSize":5,
        #             "priceScale":4,
        #         },
        #     }
        #
        promises = [self.spotV1PublicGetMarkets(params), self.contractV2PublicGetConfigMarketList(params)]
        markets = promises[0]
        contracts = promises[1]
        #
        #     {
        #         BTC_USDT: {
        #             symbol: 'BTC_USDT',
        #             buyerCurrencyId: '6',
        #             contractType: '1',
        #             defaultMarginMode: '1',
        #             marketType: '2',
        #             historyDBName: 'trade_history_readonly.dbc',
        #             defaultLeverage: '20',
        #             id: '100',
        #             canCancelOrder: True,
        #             area: '1',
        #             mixMarginCoinName: 'usdt',
        #             fundingRateRatio: '0.25',
        #             marginCurrencyName: 'usdt',
        #             minTradeMoney: '0.0001',
        #             enableTime: '1638954000000',
        #             maxTradeMoney: '10000000',
        #             canTrade: True,
        #             maxLeverage: '125',
        #             defaultPositionsMode: '2',
        #             onlyWhitelistVisible: False,
        #             riskWarnRatio: '0.8',
        #             marginDecimal: '8',
        #             spot: False,
        #             status: '1',
        #             amountDecimal: '3',
        #             leverage: False,
        #             minAmount: '0.001',
        #             canOrder: True,
        #             duration: '1',
        #             feeDecimal: '8',
        #             sellerCurrencyId: '1',
        #             maxAmount: '1000',
        #             canOpenPosition: True,
        #             isSupportMixMargin: False,
        #             markPriceLimitRate: '0.05',
        #             marginCurrencyId: '6',
        #             stopFundingFee: False,
        #             priceDecimal: '2',
        #             lightenUpFeeRate: '0',
        #             futures: True,
        #             sellerCurrencyName: 'btc',
        #             marketPriceLimitRate: '0.05',
        #             canRebate: True,
        #             marketName: 'BTC_USDT',
        #             depth: [0.01, 0.1, 1],
        #             createTime: '1607590430094',
        #             mixMarginCoinIds: [6],
        #             buyerCurrencyName: 'usdt',
        #             stopService: False
        #         },
        #     }
        #
        contractsData = self.safe_value(contracts, 'data', [])
        contractsById = self.index_by(contractsData, 'marketName')
        dataById = self.deep_extend(contractsById, markets)
        keys = list(dataById.keys())
        result = []
        for i in range(0, len(keys)):
            id = keys[i]
            market = dataById[id]
            baseId, quoteId = id.split('_')
            base = self.safe_currency_code(baseId)
            quote = self.safe_currency_code(quoteId)
            settleId = self.safe_value(market, 'marginCurrencyName')
            settle = self.safe_currency_code(settleId)
            spot = settle is None
            swap = self.safe_value(market, 'futures', False)
            linear = True if swap else None
            active = True
            symbol = base + '/' + quote
            if swap:
                status = self.safe_string(market, 'status')
                active = (status == '1')
                symbol = base + '/' + quote + ':' + settle
            result.append({
                'id': id,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'settle': settle,
                'baseId': baseId,
                'quoteId': quoteId,
                'settleId': settleId,
                'type': 'swap' if swap else 'spot',
                'spot': spot,
                'margin': False,
                'swap': swap,
                'future': False,
                'option': False,
                'active': active,
                'contract': swap,
                'linear': linear,
                'inverse': not linear if swap else None,
                'contractSize': None,
                'expiry': None,
                'expiryDatetime': None,
                'strike': None,
                'optionType': None,
                'precision': {
                    'amount': self.parse_number(self.parse_precision(self.safe_string_2(market, 'amountScale', 'amountDecimal'))),
                    'price': self.parse_number(self.parse_precision(self.safe_string_2(market, 'priceScale', 'priceDecimal'))),
                },
                'limits': {
                    'leverage': {
                        'min': None,
                        'max': self.safe_number(market, 'maxLeverage'),
                    },
                    'amount': {
                        'min': self.safe_number(market, 'minAmount'),
                        'max': self.safe_number(market, 'maxAmount'),
                    },
                    'price': {
                        'min': None,
                        'max': None,
                    },
                    'cost': {
                        'min': self.safe_number_2(market, 'minSize', 'minTradeMoney'),
                        'max': self.safe_number(market, 'maxTradeMoney'),
                    },
                },
                'info': market,
            })
        return result

    def fetch_currencies(self, params={}):
        """
        fetches all available currencies on an exchange
        :param dict params: extra parameters specific to the zb api endpoint
        :returns dict: an associative dictionary of currencies
        """
        response = self.spotV1PublicGetGetFeeInfo(params)
        #
        #     {
        #         "code":1000,
        #         "message":"success",
        #         "result":{
        #             "USDT":[
        #                 {
        #                     "chainName":"TRC20",
        #                     "canWithdraw":true,
        #                     "fee":1.0,
        #                     "mainChainName":"TRX",
        #                     "canDeposit":true
        #                 },
        #                 {
        #                     "chainName":"OMNI",
        #                     "canWithdraw":true,
        #                     "fee":5.0,
        #                     "mainChainName":"BTC",
        #                     "canDeposit":true
        #                 },
        #                 {
        #                     "chainName":"ERC20",
        #                     "canWithdraw":true,
        #                     "fee":15.0,
        #                     "mainChainName":"ETH",
        #                     "canDeposit":true
        #                 }
        #             ],
        #         }
        #     }
        #
        currencies = self.safe_value(response, 'result', {})
        ids = list(currencies.keys())
        result = {}
        for i in range(0, len(ids)):
            id = ids[i]
            currency = currencies[id]
            code = self.safe_currency_code(id)
            isWithdrawEnabled = True
            isDepositEnabled = True
            fees = {}
            for j in range(0, len(currency)):
                networkItem = currency[j]
                network = self.safe_string(networkItem, 'chainName')
                # name = self.safe_string(networkItem, 'name')
                withdrawFee = self.safe_number(networkItem, 'fee')
                depositEnable = self.safe_value(networkItem, 'canDeposit')
                withdrawEnable = self.safe_value(networkItem, 'canWithdraw')
                isDepositEnabled = isDepositEnabled or depositEnable
                isWithdrawEnabled = isWithdrawEnabled or withdrawEnable
                fees[network] = withdrawFee
            active = (isWithdrawEnabled and isDepositEnabled)
            result[code] = {
                'id': id,
                'name': None,
                'code': code,
                'precision': None,
                'info': currency,
                'active': active,
                'deposit': isDepositEnabled,
                'withdraw': isWithdrawEnabled,
                'fee': None,
                'fees': fees,
                'limits': self.limits,
            }
        return result

    def parse_balance(self, response):
        balances = self.safe_value(response['result'], 'coins')
        result = {
            'info': response,
        }
        for i in range(0, len(balances)):
            balance = balances[i]
            #     {       enName: "BTC",
            #               freez: "0.00000000",
            #         unitDecimal:  8,  # always 8
            #              cnName: "BTC",
            #       isCanRecharge:  True,  # TODO: should use self
            #             unitTag: "฿",
            #       isCanWithdraw:  True,  # TODO: should use self
            #           available: "0.00000000",
            #                 key: "btc"         }
            account = self.account()
            currencyId = self.safe_string(balance, 'key')
            code = self.safe_currency_code(currencyId)
            account['free'] = self.safe_string(balance, 'available')
            account['used'] = self.safe_string(balance, 'freez')
            result[code] = account
        return self.safe_balance(result)

    def parse_swap_balance(self, response):
        result = {
            'info': response,
        }
        data = self.safe_value(response, 'data', {})
        for i in range(0, len(data)):
            balance = data[i]
            #
            #     {
            #         "userId": "6896693805014120448",
            #         "currencyId": "6",
            #         "currencyName": "usdt",
            #         "amount": "30.56585118",
            #         "freezeAmount": "0",
            #         "contractType": 1,
            #         "id": "6899113714763638819",
            #         "createTime": "1644876888934",
            #         "modifyTime": "1645787446037",
            #         "accountBalance": "30.56585118",
            #         "allMargin": "0",
            #         "allowTransferOutAmount": "30.56585118"
            #     },
            #
            code = self.safe_currency_code(self.safe_string(balance, 'currencyName'))
            account = self.account()
            account['total'] = self.safe_string(balance, 'accountBalance')
            account['free'] = self.safe_string(balance, 'allowTransferOutAmount')
            account['used'] = self.safe_string(balance, 'freezeAmount')
            result[code] = account
        return self.safe_balance(result)

    def parse_margin_balance(self, response, marginMode):
        result = {
            'info': response,
        }
        levers = None
        if marginMode == 'isolated':
            message = self.safe_value(response, 'message', {})
            data = self.safe_value(message, 'datas', {})
            levers = self.safe_value(data, 'levers', [])
        else:
            crossResponse = self.safe_value(response, 'result', {})
            levers = self.safe_value(crossResponse, 'list', [])
        for i in range(0, len(levers)):
            balance = levers[i]
            #
            # Isolated Margin
            #
            #     {
            #         "cNetUSD": "0.00",
            #         "repayLeverShow": "-",
            #         "cCanLoanIn": "0.002115400000000",
            #         "fNetCNY": "147.76081161",
            #         "fLoanIn": "0.00",
            #         "repayLevel": 0,
            #         "level": 1,
            #         "netConvertCNY": "147.760811613032",
            #         "cFreeze": "0.00",
            #         "cUnitTag": "BTC",
            #         "version": 1646783178609,
            #         "cAvailableUSD": "0.00",
            #         "cNetCNY": "0.00",
            #         "riskRate": "-",
            #         "fAvailableUSD": "20.49273433",
            #         "fNetUSD": "20.49273432",
            #         "cShowName": "BTC",
            #         "leverMultiple": "5.00",
            #         "couldTransferOutFiat": "20.49273433",
            #         "noticeLine": "1.13",
            #         "fFreeze": "0.00",
            #         "cUnitDecimal": 8,
            #         "fCanLoanIn": "81.970937320000000",
            #         "cAvailable": "0.00",
            #         "repayLock": False,
            #         "status": 1,
            #         "forbidType": 0,
            #         "totalConvertCNY": "147.760811613032",
            #         "cAvailableCNY": "0.00",
            #         "unwindPrice": "0.00",
            #         "fOverdraft": "0.00",
            #         "fShowName": "USDT",
            #         "statusShow": "%E6%AD%A3%E5%B8%B8",
            #         "cOverdraft": "0.00",
            #         "netConvertUSD": "20.49273433",
            #         "cNetBtc": "0.00",
            #         "loanInConvertCNY": "0.00",
            #         "fAvailableCNY": "147.760811613032",
            #         "key": "btcusdt",
            #         "fNetBtc": "0.0005291",
            #         "fUnitDecimal": 8,
            #         "loanInConvertUSD": "0.00",
            #         "showName": "BTC/USDT",
            #         "startLine": "1.25",
            #         "totalConvertUSD": "20.49273433",
            #         "couldTransferOutCoin": "0.00",
            #         "cEnName": "BTC",
            #         "leverMultipleInterest": "3.00",
            #         "fAvailable": "20.49273433",
            #         "fEnName": "USDT",
            #         "forceRepayLine": "1.08",
            #         "cLoanIn": "0.00"
            #     }
            #
            # Cross Margin
            #
            #     [
            #         {
            #             "fundType": 2,
            #             "loanIn": 0,
            #             "amount": 0,
            #             "freeze": 0,
            #             "overdraft": 0,
            #             "key": "BTC",
            #             "canTransferOut": 0
            #         },
            #     ],
            #
            account = self.account()
            if marginMode == 'isolated':
                code = self.safe_currency_code(self.safe_string(balance, 'fShowName'))
                account['total'] = self.safe_string(balance, 'fAvailableUSD')  # total amount in USD
                account['free'] = self.safe_string(balance, 'couldTransferOutFiat')
                account['used'] = self.safe_string(balance, 'fFreeze')
                result[code] = account
            else:
                code = self.safe_currency_code(self.safe_string(balance, 'key'))
                account['total'] = self.safe_string(balance, 'amount')
                account['free'] = self.safe_string(balance, 'canTransferOut')
                account['used'] = self.safe_string(balance, 'freeze')
                result[code] = account
        return self.safe_balance(result)

    def fetch_balance(self, params={}):
        """
        query for balance and get the amount of funds available for trading or funds locked in orders
        :param dict params: extra parameters specific to the zb api endpoint
        :param str params['marginMode']: 'cross' or 'isolated'
        :returns dict: a `balance structure <https://docs.ccxt.com/en/latest/manual.html?#balance-structure>`
        """
        self.load_markets()
        marketType, marketTypeQuery = self.handle_market_type_and_params('fetchBalance', None, params)
        marginMode, query = self.handle_margin_mode_and_params('fetchBalance', marketTypeQuery)
        swap = (marketType == 'swap')
        marginMethod = 'spotV1PrivateGetGetCrossAssets' if (marginMode == 'cross') else 'spotV1PrivateGetGetLeverAssetsInfo'
        method = self.get_supported_mapping(marketType, {
            'spot': 'spotV1PrivateGetGetAccountInfo',
            'swap': 'contractV2PrivateGetFundBalance',
            'margin': marginMethod,
        })
        if marginMode == 'isolated':
            method = 'spotV1PrivateGetGetLeverAssetsInfo'
        elif marginMode == 'cross':
            method = 'spotV1PrivateGetGetCrossAssets'
        request = {
            # 'futuresAccountType': 1,  # SWAP
            # 'currencyId': currency['id'],  # SWAP
            # 'currencyName': 'usdt',  # SWAP
        }
        if swap:
            request['futuresAccountType'] = 1
        response = getattr(self, method)(self.extend(request, query))
        #
        # Spot
        #
        #     {
        #         "result": {
        #             "coins": [
        #                 {
        #                     "isCanWithdraw": "true",
        #                     "canLoan": False,
        #                     "fundstype": 51,
        #                     "showName": "ZB",
        #                     "isCanRecharge": "true",
        #                     "cnName": "ZB",
        #                     "enName": "ZB",
        #                     "available": "0",
        #                     "freez": "0",
        #                     "unitTag": "ZB",
        #                     "key": "zb",
        #                     "unitDecimal": 8
        #                 },
        #             ],
        #             "version": 1645856691340,
        #             "base": {
        #                 "auth_google_enabled": True,
        #                 "auth_mobile_enabled": False,
        #                 "trade_password_enabled": True,
        #                 "username": "blank@gmail.com"
        #             }
        #         },
        #         "leverPerm": True,
        #         "otcPerm": False,
        #         "assetPerm": True,
        #         "moneyPerm": True,
        #         "subUserPerm": True,
        #         "entrustPerm": True
        #     }
        #
        # Swap
        #
        #     {
        #         "code": 10000,
        #         "data": [
        #             {
        #                 "userId": "6896693805014120448",
        #                 "currencyId": "6",
        #                 "currencyName": "usdt",
        #                 "amount": "30.56585118",
        #                 "freezeAmount": "0",
        #                 "contractType": 1,
        #                 "id": "6899113714763638819",
        #                 "createTime": "1644876888934",
        #                 "modifyTime": "1645787446037",
        #                 "accountBalance": "30.56585118",
        #                 "allMargin": "0",
        #                 "allowTransferOutAmount": "30.56585118"
        #             },
        #         ],
        #         "desc": "操作成功"
        #     }
        #
        # Isolated Margin
        #
        #     {
        #         "code": 1000,
        #         "message": {
        #             "des": "success",
        #             "isSuc": True,
        #             "datas": {
        #                 "leverPerm": True,
        #                 "levers": [
        #                     {
        #                         "cNetUSD": "0.00",
        #                         "repayLeverShow": "-",
        #                         "cCanLoanIn": "0.002115400000000",
        #                         "fNetCNY": "147.76081161",
        #                         "fLoanIn": "0.00",
        #                         "repayLevel": 0,
        #                         "level": 1,
        #                         "netConvertCNY": "147.760811613032",
        #                         "cFreeze": "0.00",
        #                         "cUnitTag": "BTC",
        #                         "version": 1646783178609,
        #                         "cAvailableUSD": "0.00",
        #                         "cNetCNY": "0.00",
        #                         "riskRate": "-",
        #                         "fAvailableUSD": "20.49273433",
        #                         "fNetUSD": "20.49273432",
        #                         "cShowName": "BTC",
        #                         "leverMultiple": "5.00",
        #                         "couldTransferOutFiat": "20.49273433",
        #                         "noticeLine": "1.13",
        #                         "fFreeze": "0.00",
        #                         "cUnitDecimal": 8,
        #                         "fCanLoanIn": "81.970937320000000",
        #                         "cAvailable": "0.00",
        #                         "repayLock": False,
        #                         "status": 1,
        #                         "forbidType": 0,
        #                         "totalConvertCNY": "147.760811613032",
        #                         "cAvailableCNY": "0.00",
        #                         "unwindPrice": "0.00",
        #                         "fOverdraft": "0.00",
        #                         "fShowName": "USDT",
        #                         "statusShow": "%E6%AD%A3%E5%B8%B8",
        #                         "cOverdraft": "0.00",
        #                         "netConvertUSD": "20.49273433",
        #                         "cNetBtc": "0.00",
        #                         "loanInConvertCNY": "0.00",
        #                         "fAvailableCNY": "147.760811613032",
        #                         "key": "btcusdt",
        #                         "fNetBtc": "0.0005291",
        #                         "fUnitDecimal": 8,
        #                         "loanInConvertUSD": "0.00",
        #                         "showName": "BTC/USDT",
        #                         "startLine": "1.25",
        #                         "totalConvertUSD": "20.49273433",
        #                         "couldTransferOutCoin": "0.00",
        #                         "cEnName": "BTC",
        #                         "leverMultipleInterest": "3.00",
        #                         "fAvailable": "20.49273433",
        #                         "fEnName": "USDT",
        #                         "forceRepayLine": "1.08",
        #                         "cLoanIn": "0.00"
        #                     }
        #                 ]
        #             }
        #         }
        #     }
        #
        # Cross Margin
        #
        #     {
        #         "code": 1000,
        #         "message": "操作成功",
        #         "result": {
        #             "loanIn": 0,
        #             "total": 71.167,
        #             "riskRate": "-",
        #             "list" :[
        #                 {
        #                     "fundType": 2,
        #                     "loanIn": 0,
        #                     "amount": 0,
        #                     "freeze": 0,
        #                     "overdraft": 0,
        #                     "key": "BTC",
        #                     "canTransferOut": 0
        #                 },
        #             ],
        #             "net": 71.167
        #         }
        #     }
        #
        # todo: use self somehow
        # permissions = response['result']['base']
        if swap:
            return self.parse_swap_balance(response)
        elif marginMode is not None:
            return self.parse_margin_balance(response, marginMode)
        else:
            return self.parse_balance(response)

    def parse_deposit_address(self, depositAddress, currency=None):
        #
        # fetchDepositAddress
        #
        #     {
        #         "key": "0x0af7f36b8f09410f3df62c81e5846da673d4d9a9"
        #     }
        #
        # fetchDepositAddresses
        #
        #     {
        #         "blockChain": "btc",
        #         "isUseMemo": False,
        #         "address": "1LL5ati6pXHZnTGzHSA3rWdqi4mGGXudwM",
        #         "canWithdraw": True,
        #         "canDeposit": True
        #     }
        #     {
        #         "blockChain": "bts",
        #         "isUseMemo": True,
        #         "account": "btstest",
        #         "memo": "123",
        #         "canWithdraw": True,
        #         "canDeposit": True
        #     }
        #
        address = self.safe_string_2(depositAddress, 'key', 'address')
        tag = None
        memo = self.safe_string(depositAddress, 'memo')
        if memo is not None:
            tag = memo
        elif address.find('_') >= 0:
            parts = address.split('_')
            address = parts[0]  # WARNING: MAY BE tag_address INSTEAD OF address_tag FOR SOME CURRENCIESnot !
            tag = parts[1]
        self.check_address(address)
        currencyId = self.safe_string(depositAddress, 'blockChain')
        code = self.safe_currency_code(currencyId, currency)
        return {
            'currency': code,
            'address': address,
            'tag': tag,
            'network': None,
            'info': depositAddress,
        }

    def fetch_deposit_addresses(self, codes=None, params={}):
        self.load_markets()
        response = self.spotV1PrivateGetGetPayinAddress(params)
        #
        #     {
        #         "code": 1000,
        #         "message": {
        #             "des": "success",
        #             "isSuc": True,
        #             "datas": [
        #                 {
        #                     "blockChain": "btc",
        #                     "isUseMemo": False,
        #                     "address": "1LL5ati6pXHZnTGzHSA3rWdqi4mGGXudwM",
        #                     "canWithdraw": True,
        #                     "canDeposit": True
        #                 },
        #                 {
        #                     "blockChain": "bts",
        #                     "isUseMemo": True,
        #                     "account": "btstest",
        #                     "memo": "123",
        #                     "canWithdraw": True,
        #                     "canDeposit": True
        #                 },
        #             ]
        #         }
        #     }
        #
        message = self.safe_value(response, 'message', {})
        datas = self.safe_value(message, 'datas', [])
        return self.parse_deposit_addresses(datas, codes)

    def fetch_deposit_address(self, code, params={}):
        """
        fetch the deposit address for a currency associated with self account
        :param str code: unified currency code
        :param dict params: extra parameters specific to the zb api endpoint
        :returns dict: an `address structure <https://docs.ccxt.com/en/latest/manual.html#address-structure>`
        """
        self.load_markets()
        currency = self.currency(code)
        request = {
            'currency': currency['id'],
        }
        response = self.spotV1PrivateGetGetUserAddress(self.extend(request, params))
        #
        #     {
        #         "code": 1000,
        #         "message": {
        #             "des": "success",
        #             "isSuc": True,
        #             "datas": {
        #                 "key": "0x0af7f36b8f09410f3df62c81e5846da673d4d9a9"
        #             }
        #         }
        #     }
        #
        message = self.safe_value(response, 'message', {})
        datas = self.safe_value(message, 'datas', {})
        return self.parse_deposit_address(datas, currency)

    def fetch_order_book(self, symbol, limit=None, params={}):
        """
        fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
        :param str symbol: unified symbol of the market to fetch the order book for
        :param int|None limit: the maximum amount of order book entries to return
        :param dict params: extra parameters specific to the zb api endpoint
        :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/en/latest/manual.html#order-book-structure>` indexed by market symbols
        """
        self.load_markets()
        market = self.market(symbol)
        request = {
            # 'market': market['id'],  # only applicable to SPOT
            # 'symbol': market['id'],  # only applicable to SWAP
            # 'size': limit,  # 1-50 applicable to SPOT and SWAP
            # 'merge': 5.0,  # float default depth only applicable to SPOT
            # 'scale': 5,  # int accuracy, only applicable to SWAP
        }
        marketIdField = 'symbol' if market['swap'] else 'market'
        request[marketIdField] = market['id']
        method = self.get_supported_mapping(market['type'], {
            'spot': 'spotV1PublicGetDepth',
            'swap': 'contractV1PublicGetDepth',
        })
        if limit is not None:
            request['size'] = limit
        response = getattr(self, method)(self.extend(request, params))
        #
        # Spot
        #
        #     {
        #         "asks":[
        #             [35000.0,0.2741],
        #             [34949.0,0.0173],
        #             [34900.0,0.5004],
        #         ],
        #         "bids":[
        #             [34119.32,0.0030],
        #             [34107.83,0.1500],
        #             [34104.42,0.1500],
        #         ],
        #         "timestamp":1624536510
        #     }
        #
        # Swap
        #
        #     {
        #         "code": 10000,
        #         "desc": "操作成功",
        #         "data": {
        #             "asks": [
        #                 [43416.6,0.02],
        #                 [43418.25,0.04],
        #                 [43425.82,0.02]
        #             ],
        #             "bids": [
        #                 [43414.61,0.1],
        #                 [43414.18,0.04],
        #                 [43413.03,0.05]
        #             ],
        #             "time": 1645087743071
        #         }
        #     }
        #
        result = None
        timestamp = None
        if market['type'] == 'swap':
            result = self.safe_value(response, 'data')
            timestamp = self.safe_integer(result, 'time')
        else:
            result = response
            timestamp = self.safe_timestamp(response, 'timestamp')
        return self.parse_order_book(result, symbol, timestamp)

    def fetch_tickers(self, symbols=None, params={}):
        """
        fetches price tickers for multiple markets, statistical calculations with the information calculated over the past 24 hours each market
        :param [str]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
        :param dict params: extra parameters specific to the zb api endpoint
        :returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/en/latest/manual.html#ticker-structure>`
        """
        self.load_markets()
        symbols = self.market_symbols(symbols)
        response = self.spotV1PublicGetAllTicker(params)
        result = {}
        marketsByIdWithoutUnderscore = {}
        marketIds = self.ids
        for i in range(0, len(marketIds)):
            marketId = marketIds[i]
            tickerId = marketId.replace('_', '')
            marketsByIdWithoutUnderscore[tickerId] = marketId
        ids = list(response.keys())
        for i in range(0, len(ids)):
            marketId = self.safe_value(marketsByIdWithoutUnderscore, ids[i])
            market = self.safe_market(marketId, None, '_')
            if market is not None:
                symbol = market['symbol']
                ticker = self.safe_value(response, ids[i])
                if ticker is not None:
                    result[symbol] = self.parse_ticker(ticker, market)
        return self.filter_by_array(result, 'symbol', symbols)

    def fetch_ticker(self, symbol, params={}):
        """
        fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
        :param str symbol: unified symbol of the market to fetch the ticker for
        :param dict params: extra parameters specific to the zb api endpoint
        :returns dict: a `ticker structure <https://docs.ccxt.com/en/latest/manual.html#ticker-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        request = {
            # 'market': market['id'],  # only applicable to SPOT
            # 'symbol': market['id'],  # only applicable to SWAP
        }
        marketIdField = 'symbol' if market['swap'] else 'market'
        request[marketIdField] = market['id']
        method = self.get_supported_mapping(market['type'], {
            'spot': 'spotV1PublicGetTicker',
            'swap': 'contractV1PublicGetTicker',
        })
        response = getattr(self, method)(self.extend(request, params))
        #
        # Spot
        #
        #     {
        #         "date":"1624399623587",
        #         "ticker":{
        #             "high":"33298.38",
        #             "vol":"56152.9012",
        #             "last":"32578.55",
        #             "low":"28808.19",
        #             "buy":"32572.68",
        #             "sell":"32615.37",
        #             "turnover":"1764201303.6100",
        #             "open":"31664.85",
        #             "riseRate":"2.89"
        #         }
        #     }
        #
        # Swap
        #
        #     {
        #         "code": 10000,
        #         "desc": "操作成功",
        #         "data": {
        #             "BTC_USDT": [44053.47,44357.77,42911.54,43297.79,53471.264,-1.72,1645093002,302201.255084]
        #         }
        #     }
        #
        ticker = None
        if market['type'] == 'swap':
            ticker = {}
            data = self.safe_value(response, 'data')
            values = self.safe_value(data, market['id'], [])
            for i in range(0, len(values)):
                ticker['open'] = self.safe_value(values, 0)
                ticker['high'] = self.safe_value(values, 1)
                ticker['low'] = self.safe_value(values, 2)
                ticker['last'] = self.safe_value(values, 3)
                ticker['vol'] = self.safe_value(values, 4)
                ticker['riseRate'] = self.safe_value(values, 5)
        else:
            ticker = self.safe_value(response, 'ticker', {})
            ticker['date'] = self.safe_value(response, 'date')
        return self.parse_ticker(ticker, market)

    def parse_ticker(self, ticker, market=None):
        #
        # Spot
        #
        #     {
        #         "date":"1624399623587",  # injected from outside
        #         "high":"33298.38",
        #         "vol":"56152.9012",
        #         "last":"32578.55",
        #         "low":"28808.19",
        #         "buy":"32572.68",
        #         "sell":"32615.37",
        #         "turnover":"1764201303.6100",
        #         "open":"31664.85",
        #         "riseRate":"2.89"
        #     }
        #
        # Swap
        #
        #     {
        #         open: 44083.82,
        #         high: 44357.77,
        #         low: 42911.54,
        #         last: 43097.87,
        #         vol: 53451.641,
        #         riseRate: -2.24
        #     }
        #
        timestamp = self.safe_integer(ticker, 'date', self.milliseconds())
        last = self.safe_string(ticker, 'last')
        return self.safe_ticker({
            'symbol': self.safe_symbol(None, market),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'high': self.safe_string(ticker, 'high'),
            'low': self.safe_string(ticker, 'low'),
            'bid': self.safe_string(ticker, 'buy'),
            'bidVolume': None,
            'ask': self.safe_string(ticker, 'sell'),
            'askVolume': None,
            'vwap': None,
            'open': self.safe_string(ticker, 'open'),
            'close': last,
            'last': last,
            'previousClose': None,
            'change': None,
            'percentage': None,
            'average': None,
            'baseVolume': self.safe_string(ticker, 'vol'),
            'quoteVolume': None,
            'info': ticker,
        }, market)

    def parse_ohlcv(self, ohlcv, market=None):
        if market['swap']:
            ohlcvLength = len(ohlcv)
            if ohlcvLength > 5:
                return [
                    self.safe_timestamp(ohlcv, 5),
                    self.safe_number(ohlcv, 0),
                    self.safe_number(ohlcv, 1),
                    self.safe_number(ohlcv, 2),
                    self.safe_number(ohlcv, 3),
                    self.safe_number(ohlcv, 4),
                ]
            else:
                return [
                    self.safe_timestamp(ohlcv, 4),
                    self.safe_number(ohlcv, 0),
                    self.safe_number(ohlcv, 1),
                    self.safe_number(ohlcv, 2),
                    self.safe_number(ohlcv, 3),
                    None,
                ]
        else:
            return [
                self.safe_integer(ohlcv, 0),
                self.safe_number(ohlcv, 1),
                self.safe_number(ohlcv, 2),
                self.safe_number(ohlcv, 3),
                self.safe_number(ohlcv, 4),
                self.safe_number(ohlcv, 5),
            ]

    def fetch_ohlcv(self, symbol, timeframe='1m', since=None, limit=None, params={}):
        """
        fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
        :param str symbol: unified symbol of the market to fetch OHLCV data for
        :param str timeframe: the length of time each candle represents
        :param int|None since: timestamp in ms of the earliest candle to fetch
        :param int|None limit: the maximum amount of candles to fetch
        :param dict params: extra parameters specific to the zb api endpoint
        :returns [[int]]: A list of candles ordered as timestamp, open, high, low, close, volume
        """
        self.load_markets()
        market = self.market(symbol)
        swap = market['swap']
        spot = market['spot']
        options = self.safe_value(self.options, 'timeframes', {})
        timeframes = self.safe_value(options, market['type'], {})
        timeframeValue = self.safe_string(timeframes, timeframe)
        if timeframeValue is None:
            raise NotSupported(self.id + ' fetchOHLCV() does not support ' + timeframe + ' timeframe for ' + market['type'] + ' markets')
        if limit is None:
            limit = 1000
        request = {
            'size': limit,
            # 'market': market['id'],  # spot only
            # 'symbol': market['id'],  # swap only
            # 'type': timeframeValue,  # spot only
            # 'period': timeframeValue,  # swap only
            # 'since': since,  # spot only
            # 'size': limit,  # spot and swap
        }
        marketIdField = 'symbol' if swap else 'market'
        request[marketIdField] = market['id']
        periodField = 'period' if swap else 'type'
        request[periodField] = timeframeValue
        price = self.safe_string(params, 'price')
        params = self.omit(params, 'price')
        method = self.get_supported_mapping(market['type'], {
            'spot': 'spotV1PublicGetKline',
            'swap': 'contractV1PublicGetKline',
        })
        if swap:
            if price == 'mark':
                method = 'contractV1PublicGetMarkKline'
            elif price == 'index':
                method = 'contractV1PublicGetIndexKline'
        elif spot:
            if since is not None:
                request['since'] = since
        response = getattr(self, method)(self.extend(request, params))
        #
        # Spot
        #
        #     {
        #         "symbol": "BTC",
        #         "data": [
        #             [1645091400000,43183.24,43187.49,43145.92,43182.28,0.9110],
        #             [1645091460000,43182.18,43183.15,43182.06,43183.15,1.4393],
        #             [1645091520000,43182.11,43240.1,43182.11,43240.1,0.3802]
        #         ],
        #         "moneyType": "USDT"
        #     }
        #
        # Swap
        #
        #     {
        #         "code": 10000,
        #         "desc": "操作成功",
        #         "data": [
        #             [41433.44,41433.44,41405.88,41408.75,21.368,1646366460],
        #             [41409.25,41423.74,41408.8,41423.42,9.828,1646366520],
        #             [41423.96,41429.39,41369.98,41370.31,123.104,1646366580]
        #         ]
        #     }
        #
        # Mark
        #
        #     {
        #         "code": 10000,
        #         "desc": "操作成功",
        #         "data": [
        #             [41603.39,41603.39,41591.59,41600.81,1646381760],
        #             [41600.36,41605.75,41587.69,41601.97,1646381820],
        #             [41601.97,41601.97,41562.62,41593.96,1646381880]
        #         ]
        #     }
        #
        # Index
        #
        #     {
        #         "code": 10000,
        #         "desc": "操作成功",
        #         "data": [
        #             [41697.53,41722.29,41689.16,41689.16,1646381640],
        #             [41690.1,41691.73,41611.61,41611.61,1646381700],
        #             [41611.61,41619.49,41594.87,41594.87,1646381760]
        #         ]
        #     }
        #
        data = self.safe_value(response, 'data', [])
        return self.parse_ohlcvs(data, market, timeframe, since, limit)

    def parse_trade(self, trade, market=None):
        #
        # Spot
        #
        #     {
        #         "date":1624537391,
        #         "amount":"0.0142",
        #         "price":"33936.42",
        #         "trade_type":"ask",
        #         "type":"sell",
        #         "tid":1718869018
        #     }
        #
        # Swap
        #
        #     {
        #         "amount": "0.002",
        #         "createTime": "1645787446034",
        #         "feeAmount": "-0.05762699",
        #         "feeCurrency": "USDT",
        #         "id": "6902932868050395136",
        #         "maker": False,
        #         "orderId": "6902932868042006528",
        #         "price": "38417.99",
        #         "relizedPnl": "0.30402",
        #         "side": 4,
        #         "userId": "6896693805014120448"
        #     },
        #
        sideField = 'side' if market['swap'] else 'trade_type'
        side = self.safe_string(trade, sideField)
        takerOrMaker = None
        maker = self.safe_value(trade, 'maker')
        if maker is not None:
            takerOrMaker = 'maker' if maker else 'taker'
        if market['spot']:
            side = 'buy' if (side == 'bid') else 'sell'
        else:
            if side == '3':
                side = 'sell'  # close long
            elif side == '4':
                side = 'buy'  # close short
            elif side == '1':
                side = 'buy'  # open long
            elif side == '2':
                side = 'sell'  # open short
        timestamp = None
        if market['swap']:
            timestamp = self.safe_integer(trade, 'createTime')
        else:
            timestamp = self.safe_timestamp(trade, 'date')
        price = self.safe_string(trade, 'price')
        amount = self.safe_string(trade, 'amount')
        fee = None
        feeCostString = self.safe_string(trade, 'feeAmount')
        if feeCostString is not None:
            feeCurrencyId = self.safe_string(trade, 'feeCurrency')
            fee = {
                'cost': feeCostString,
                'currency': self.safe_currency_code(feeCurrencyId),
            }
        market = self.safe_market(None, market)
        return self.safe_trade({
            'info': trade,
            'id': self.safe_string(trade, 'tid'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': market['symbol'],
            'type': None,
            'side': side,
            'order': self.safe_string(trade, 'orderId'),
            'takerOrMaker': takerOrMaker,
            'price': price,
            'amount': amount,
            'cost': None,
            'fee': fee,
        }, market)

    def fetch_trades(self, symbol, since=None, limit=None, params={}):
        """
        get the list of most recent trades for a particular symbol
        :param str symbol: unified symbol of the market to fetch trades for
        :param int|None since: timestamp in ms of the earliest trade to fetch
        :param int|None limit: the maximum amount of trades to fetch
        :param dict params: extra parameters specific to the zb api endpoint
        :returns [dict]: a list of `trade structures <https://docs.ccxt.com/en/latest/manual.html?#public-trades>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchTrades() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        swap = market['swap']
        request = {
            # 'market': market['id'],  # SPOT
            # 'symbol': market['id'],  # SWAP
            # 'side': 1,  # SWAP
            # 'dateRange': 0,  # SWAP
            # 'startTime': since,  # SWAP
            # 'endtime': self.milliseconds(),  # SWAP
            # 'pageNum': 1,  # SWAP
            # 'pageSize': limit,  # SWAP default is 10
        }
        if limit is not None:
            request['pageSize'] = limit
        if since is not None:
            request['startTime'] = since
        marketIdField = 'symbol' if swap else 'market'
        request[marketIdField] = market['id']
        if swap and params['pageNum'] is None:
            request['pageNum'] = 1
        method = self.get_supported_mapping(market['type'], {
            'spot': 'spotV1PublicGetTrades',
            'swap': 'contractV2PrivateGetTradeTradeHistory',
        })
        response = getattr(self, method)(self.extend(request, params))
        #
        # Spot
        #
        #     [
        #         {"date":1624537391,"amount":"0.0142","price":"33936.42","trade_type":"ask","type":"sell","tid":1718869018},
        #         {"date":1624537391,"amount":"0.0010","price":"33936.42","trade_type":"ask","type":"sell","tid":1718869020},
        #         {"date":1624537391,"amount":"0.0133","price":"33936.42","trade_type":"ask","type":"sell","tid":1718869021},
        #     ]
        #
        # Swap
        #
        #     {
        #         "code": 10000,
        #         "data": {
        #             "list": [
        #                 {
        #                     "amount": "0.002",
        #                     "createTime": "1645787446034",
        #                     "feeAmount": "-0.05762699",
        #                     "feeCurrency": "USDT",
        #                     "id": "6902932868050395136",
        #                     "maker": False,
        #                     "orderId": "6902932868042006528",
        #                     "price": "38417.99",
        #                     "relizedPnl": "0.30402",
        #                     "side": 4,
        #                     "userId": "6896693805014120448"
        #                 },
        #             ],
        #             "pageNum": 1,
        #             "pageSize": 10
        #         },
        #         "desc": "操作成功"
        #     }
        #
        if swap:
            data = self.safe_value(response, 'data')
            response = self.safe_value(data, 'list')
        return self.parse_trades(response, market, since, limit)

    def create_order(self, symbol, type, side, amount, price=None, params={}):
        """
        create a trade order
        :param str symbol: unified symbol of the market to create an order in
        :param str type: must be 'limit'
        :param str side: 'buy' or 'sell'
        :param float amount: how much of currency you want to trade in units of base currency
        :param float|None price: the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
        :param dict params: extra parameters specific to the zb api endpoint
        :param str params['marginMode']: 'cross' or 'isolated'
        :returns dict: an `order structure <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        marginMode, query = self.handle_margin_mode_and_params('createOrder', params)
        swap = market['swap']
        spot = market['spot']
        timeInForce = self.safe_string(params, 'timeInForce')
        reduceOnly = self.safe_value(params, 'reduceOnly')
        triggerPrice = self.safe_value_2(params, 'triggerPrice', 'stopPrice')
        stopLossPrice = self.safe_value(params, 'stopLossPrice')
        takeProfitPrice = self.safe_value(params, 'takeProfitPrice')
        isStopLoss = stopLossPrice is not None
        isTakeProfit = takeProfitPrice is not None
        isTriggerOrder = triggerPrice is not None
        if self.sum(isStopLoss, isTakeProfit, isTriggerOrder):
            raise ExchangeError(self.id + ' createOrder() stopLossPrice and takeProfitPrice cannot both be defined')
        isStopOrder = isStopLoss or isTakeProfit or isTriggerOrder
        if isStopOrder and spot:
            raise ExchangeError(self.id + ' createOrder() it is not possible to make a stop order on spot markets')
        if type == 'market':
            raise InvalidOrder(self.id + ' createOrder() on ' + market['type'] + ' markets does not allow market orders')
        method = self.get_supported_mapping(market['type'], {
            'spot': 'spotV1PrivateGetOrder',
            'margin': 'spotV1PrivateGetOrder',
            'swap': 'contractV2PrivatePostTradeOrder',
        })
        request = {
            'amount': self.amount_to_precision(symbol, amount),
            # 'symbol': market['id'],
            # 'acctType': 0,  # Spot, Margin 0/1/2 [Spot/Isolated/Cross] Optional, Default to: 0 Spot
            # 'customerOrderId': '1f2g',  # Spot, Margin
            # 'orderType': 1,  # Spot, Margin order type 1/2 [PostOnly/IOC] Optional
            # 'triggerPrice': 30000.0,  # Stop trigger price
            # 'algoPrice': 29000.0,  # Stop order price
            # 'priceType': 1,  # Stop Loss Take Profit, 1: Mark price, 2: Last price
            # 'bizType': 1,  # Stop Loss Take Profit, 1: TP, 2: SL
        }
        if spot:
            exchangeSpecificParam = self.safe_integer(params, 'orderType', type) == 1
            postOnly = self.is_post_only(False, exchangeSpecificParam, params)
            request['tradeType'] = 1 if (side == 'buy') else 0
            request['currency'] = market['id']
            if postOnly:
                request['orderType'] = 1
            elif timeInForce == 'IOC':
                request['orderType'] = 2
            if price is not None:
                request['price'] = self.price_to_precision(symbol, price)
            if marginMode is not None:
                if marginMode == 'isolated':
                    request['acctType'] = 1
                elif marginMode == 'cross':
                    request['acctType'] = 2
        elif swap:
            exchangeSpecificParam = self.safe_integer(params, 'action', type) == 4
            postOnly = self.is_post_only(False, exchangeSpecificParam, params)
            # the default mode on zb is one way mode
            # currently ccxt does not support hedge mode natively
            if isStopLoss or isTakeProfit:
                reduceOnly = True
            if reduceOnly:
                request['side'] = 0
            else:
                request['side'] = 5 if (side == 'buy') else 6
            if isStopOrder:
                method = 'contractV2PrivatePostTradeOrderAlgo'
                if isStopLoss:
                    request['orderType'] = 2
                    request['bizType'] = 2
                    request['triggerPrice'] = self.price_to_precision(symbol, stopLossPrice)
                elif isTakeProfit:
                    request['orderType'] = 2
                    request['bizType'] = 1
                    request['triggerPrice'] = self.price_to_precision(symbol, takeProfitPrice)
                elif isTriggerOrder:
                    request['orderType'] = 1
                    request['triggerPrice'] = self.price_to_precision(symbol, triggerPrice)
                request['algoPrice'] = self.price_to_precision(symbol, price)
                request['pricetype'] = 2
            else:
                if timeInForce == 'IOC':
                    request['action'] = 3
                elif postOnly:
                    request['action'] = 4
                elif timeInForce == 'FOK':
                    request['action'] = 5
                elif type == 'limit':
                    request['action'] = 1
                else:
                    request['action'] = type
            if price is not None:
                request['price'] = self.price_to_precision(symbol, price)
            request['symbol'] = market['id']
            clientOrderId = self.safe_string(params, 'clientOrderId')  # OPTIONAL '^[a-zA-Z0-9-_]{1,36}$',  # The user-defined order number
            if clientOrderId is not None:
                request['clientOrderId'] = clientOrderId
            # using self.extend as name causes issues in python
            extendOrderAlgos = self.safe_value(params, 'extend', None)  # OPTIONAL {"orderAlgos":[{"bizType":1,"priceType":1,"triggerPrice":"70000"},{"bizType":2,"priceType":1,"triggerPrice":"40000"}]}
            if extendOrderAlgos is not None:
                request['extend'] = extendOrderAlgos
        params = self.omit(query, ['takeProfitPrice', 'stopLossPrice', 'stopPrice', 'reduceOnly', 'orderType', 'triggerPrice', 'priceType', 'clientOrderId', 'extend'])
        response = getattr(self, method)(self.extend(request, params))
        #
        # Spot and Margin
        #
        #     {
        #         "code": 1000,
        #         "message": "操作成功",
        #         "id": "202202224851151555"
        #     }
        #
        # Swap
        #
        #     {
        #         "code": 10000,
        #         "desc": "操作成功",
        #         "data": {
        #             "orderId": "6901786759944937472",
        #             "orderCode": null
        #         }
        #     }
        #
        # Algo order
        #
        #     {
        #         "code": 10000,
        #         "data": "6919884551305242624",
        #         "desc": "操作成功"
        #     }
        #
        result = response
        if swap and not isStopOrder:
            result = self.safe_value(response, 'data')
        return self.parse_order(result, market)

    def cancel_order(self, id, symbol=None, params={}):
        """
        cancels an open order
        :param str id: order id
        :param str symbol: unified symbol of the market the order was made in
        :param dict params: extra parameters specific to the zb api endpoint
        :returns dict: An `order structure <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelOrder() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        swap = market['swap']
        request = {
            # 'currency': self.market_id(symbol),  # only applicable to SPOT
            # 'id': str(id),  # only applicable to SPOT
            # 'symbol': self.market_id(symbol),  # only applicable to SWAP
            # 'orderId': str(id),  # only applicable to SWAP
            # 'clientOrderId': params['clientOrderId'],  # only applicable to SWAP
        }
        marketIdField = 'symbol' if swap else 'currency'
        request[marketIdField] = self.market_id(symbol)
        orderIdField = 'orderId' if swap else 'id'
        request[orderIdField] = str(id)
        method = self.get_supported_mapping(market['type'], {
            'spot': 'spotV1PrivateGetCancelOrder',
            'swap': 'contractV2PrivatePostTradeCancelOrder',
        })
        response = getattr(self, method)(self.extend(request, params))
        #
        # Spot
        #
        #     {
        #         "code": 1000,
        #         "message": "Success。"
        #     }
        #
        # Swap
        #
        #     {
        #         "code": 10007,
        #         "desc": "orderId与clientOrderId选填1个"
        #     }
        #
        return self.parse_order(response, market)

    def cancel_all_orders(self, symbol=None, params={}):
        """
        cancel all open orders in a market
        :param str symbol: unified market symbol of the market to cancel orders in
        :param dict params: extra parameters specific to the zb api endpoint
        :returns [dict]: a list of `order structures <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelAllOrders() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        stop = self.safe_value(params, 'stop')
        if market['spot']:
            raise NotSupported(self.id + ' cancelAllOrders() is not supported on ' + market['type'] + ' markets')
        request = {
            'symbol': market['id'],
            # 'ids': [6904603200733782016, 6819506476072247297],  # STOP
            # 'side': params['side'],  # STOP, for stop orders: 1 Open long(buy), 2 Open short(sell), 3 Close long(sell), 4 Close Short(Buy). One-Way Positions: 5 Buy, 6 Sell, 0 Close Only
        }
        method = 'contractV2PrivatePostTradeCancelAllOrders'
        if stop:
            method = 'contractV2PrivatePostTradeCancelAlgos'
        query = self.omit(params, 'stop')
        return getattr(self, method)(self.extend(request, query))

    def fetch_order(self, id, symbol=None, params={}):
        """
        fetches information on an order made by the user
        :param str symbol: unified symbol of the market the order was made in
        :param dict params: extra parameters specific to the zb api endpoint
        :returns dict: An `order structure <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOrder() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        orderType = self.safe_integer(params, 'orderType')
        if orderType is not None:
            raise ExchangeError(self.id + ' fetchOrder() it is not possible to fetch a single conditional order, use fetchOrders() instead')
        swap = market['swap']
        request = {
            # 'currency': self.market_id(symbol),  # only applicable to SPOT
            # 'id': str(id),  # only applicable to SPOT
            # 'orderId': str(id),  # only applicable to SWAP
            # 'clientOrderId': params['clientOrderId'],  # only applicable to SWAP
            # 'symbol': market['id'],  # STOP and SWAP
            # 'side': params['side'],  # STOP and SWAP, for stop orders: 1 Open long(buy), 2 Open short(sell), 3 Close long(sell), 4 Close Short(Buy). One-Way Positions: 5 Buy, 6 Sell, 0 Close Only
            # 'orderType': 1,  # STOP, 1: Plan order, 2: SP/SL
            # 'bizType': 1,  # Plan order, 1: TP, 2: SL
            # 'status': 1,  # STOP, 1: untriggered, 2: cancelled, 3:triggered, 4:failed, 5:completed
            # 'startTime': since,  # STOP and SWAP
            # 'endTime': params['endTime'],  # STOP and SWAP
            # 'pageNum': 1,  # STOP and SWAP, default 1
            # 'pageSize': limit,  # STOP, default 10
        }
        marketIdField = 'symbol' if swap else 'currency'
        request[marketIdField] = self.market_id(symbol)
        orderIdField = 'orderId' if swap else 'id'
        request[orderIdField] = str(id)
        method = self.get_supported_mapping(market['type'], {
            'spot': 'spotV1PrivateGetGetOrder',
            'swap': 'contractV2PrivateGetTradeGetOrder',
        })
        response = getattr(self, method)(self.extend(request, params))
        #
        # Spot
        #
        #     {
        #         'total_amount': 0.01,
        #         'id': '20180910244276459',
        #         'price': 180.0,
        #         'trade_date': 1536576744960,
        #         'status': 2,
        #         'trade_money': '1.96742',
        #         'trade_amount': 0.01,
        #         'type': 0,
        #         'currency': 'eth_usdt'
        #     }
        #
        # Swap
        #
        #     {
        #         "code": 10000,
        #         "data": {
        #             "action": 1,
        #             "amount": "0.002",
        #             "availableAmount": "0.002",
        #             "availableValue": "60",
        #             "avgPrice": "0",
        #             "canCancel": True,
        #             "cancelStatus": 20,
        #             "createTime": "1646185684379",
        #             "entrustType": 1,
        #             "id": "6904603200733782016",
        #             "leverage": 2,
        #             "margin": "30",
        #             "marketId": "100",
        #             "modifyTime": "1646185684416",
        #             "price": "30000",
        #             "priority": 0,
        #             "showStatus": 1,
        #             "side": 1,
        #             "sourceType": 4,
        #             "status": 12,
        #             "tradeAmount": "0",
        #             "tradeValue": "0",
        #             "type": 1,
        #             "userId": "6896693805014120448",
        #             "value": "60"
        #         },
        #         "desc":"操作成功"
        #     }
        #
        # Algo order
        #
        #     {
        #         "code": 10000,
        #         "data": {
        #             "list": [
        #                 {
        #                     "action": 1,
        #                     "algoPrice": "30000",
        #                     "amount": "0.003",
        #                     "bizType": 0,
        #                     "canCancel": True,
        #                     "createTime": "1649913941109",
        #                     "errorCode": 0,
        #                     "id": "6920240642849449984",
        #                     "isLong": False,
        #                     "leverage": 10,
        #                     "marketId": "100",
        #                     "modifyTime": "1649913941109",
        #                     "orderType": 1,
        #                     "priceType": 2,
        #                     "side": 5,
        #                     "sourceType": 4,
        #                     "status": 1,
        #                     "submitPrice": "41270.53",
        #                     "symbol": "BTC_USDT",
        #                     "tradedAmount": "0",
        #                     "triggerCondition": "<=",
        #                     "triggerPrice": "31000",
        #                     "triggerTime": "0",
        #                     "userId": "6896693805014120448"
        #                 },
        #             ],
        #             "pageNum": 1,
        #             "pageSize": 10
        #         },
        #         "desc": "操作成功"
        #     }
        #
        result = response
        if swap:
            result = self.safe_value(response, 'data')
        return self.parse_order(result, market)

    def fetch_orders(self, symbol=None, since=None, limit=None, params={}):
        """
        fetches information on multiple orders made by the user
        :param str symbol: unified market symbol of the market orders were made in
        :param int|None since: the earliest time in ms to fetch orders for
        :param int|None limit: the maximum number of  orde structures to retrieve
        :param dict params: extra parameters specific to the zb api endpoint
        :returns [dict]: a list of `order structures <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOrders() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        orderType = self.safe_integer(params, 'orderType')
        swap = market['swap']
        request = {
            'pageSize': limit,  # default pageSize is 50 for spot, 30 for swap
            # 'currency': market['id'],  # only applicable to SPOT
            # 'pageIndex': 1,  # only applicable to SPOT
            # 'type': params['type'],  # only applicable to SWAP
            # 'dateRange': params['dateRange'],  # only applicable to SWAP
            # 'action': params['action'],  # only applicable to SWAP
            # 'symbol': market['id'],  # STOP and SWAP
            # 'side': params['side'],  # STOP and SWAP, for stop orders: 1 Open long(buy), 2 Open short(sell), 3 Close long(sell), 4 Close Short(Buy). One-Way Positions: 5 Buy, 6 Sell, 0 Close Only
            # 'orderType': 1,  # STOP, 1: Plan order, 2: SP/SL
            # 'bizType': 1,  # Plan order, 1: TP, 2: SL
            # 'status': 1,  # STOP, 1: untriggered, 2: cancelled, 3:triggered, 4:failed, 5:completed
            # 'startTime': since,  # STOP and SWAP
            # 'endTime': params['endTime'],  # STOP and SWAP
            # 'pageNum': 1,  # STOP and SWAP, default 1
            # 'pageSize': limit,  # STOP, default 10
        }
        marketIdField = 'symbol' if market['swap'] else 'currency'
        request[marketIdField] = market['id']
        pageNumField = 'pageNum' if market['swap'] else 'pageIndex'
        request[pageNumField] = 1
        if swap:
            request['startTime'] = since
        method = self.get_supported_mapping(market['type'], {
            'spot': 'spotV1PrivateGetGetOrdersIgnoreTradeType',
            'swap': 'contractV2PrivateGetTradeGetAllOrders',
        })
        # tradeType 交易类型1/0[buy/sell]
        if 'tradeType' in params:
            method = 'spotV1PrivateGetGetOrdersNew'
        if orderType is not None:
            method = 'contractV2PrivateGetTradeGetOrderAlgos'
        response = getattr(self, method)(self.extend(request, params))
        # Spot
        #
        #     [
        #         {
        #             "acctType": 0,
        #             "currency": "btc_usdt",
        #             "fees": 0,
        #             "id": "202202234857482656",
        #             "price": 30000.0,
        #             "status": 3,
        #             "total_amount": 0.0006,
        #             "trade_amount": 0.0000,
        #             "trade_date": 1645610254524,
        #             "trade_money": 0.000000,
        #             "type": 1,
        #             "useZbFee": False,
        #             "webId": 0
        #         }
        #     ]
        #
        # Swap
        #
        #     {
        #         "code": 10000,
        #         "data": {
        #             "list": [
        #                 {
        #                     "action": 1,
        #                     "amount": "0.004",
        #                     "availableAmount": "0.004",
        #                     "availableValue": "120",
        #                     "avgPrice": "0",
        #                     "canCancel": True,
        #                     "cancelStatus": 20,
        #                     "createTime": "1645609643885",
        #                     "entrustType": 1,
        #                     "id": "6902187111785635850",
        #                     "leverage": 5,
        #                     "margin": "24",
        #                     "marketId": "100",
        #                     "marketName": "BTC_USDT",
        #                     "modifyTime": "1645609643889",
        #                     "price": "30000",
        #                     "showStatus": 1,
        #                     "side": 1,
        #                     "sourceType": 1,
        #                     "status": 12,
        #                     "tradeAmount": "0",
        #                     "tradeValue": "0",
        #                     "type": 1,
        #                     "userId": "6896693805014120448",
        #                     "value": "120"
        #                 },
        #             ],
        #             "pageNum": 1,
        #             "pageSize": 10
        #         },
        #         "desc": "操作成功"
        #     }
        #
        # Algo order
        #
        #     {
        #         "code": 10000,
        #         "data": {
        #             "list": [
        #                 {
        #                     "action": 1,
        #                     "algoPrice": "30000",
        #                     "amount": "0.003",
        #                     "bizType": 0,
        #                     "canCancel": True,
        #                     "createTime": "1649913941109",
        #                     "errorCode": 0,
        #                     "id": "6920240642849449984",
        #                     "isLong": False,
        #                     "leverage": 10,
        #                     "marketId": "100",
        #                     "modifyTime": "1649913941109",
        #                     "orderType": 1,
        #                     "priceType": 2,
        #                     "side": 5,
        #                     "sourceType": 4,
        #                     "status": 1,
        #                     "submitPrice": "41270.53",
        #                     "symbol": "BTC_USDT",
        #                     "tradedAmount": "0",
        #                     "triggerCondition": "<=",
        #                     "triggerPrice": "31000",
        #                     "triggerTime": "0",
        #                     "userId": "6896693805014120448"
        #                 },
        #             ],
        #             "pageNum": 1,
        #             "pageSize": 10
        #         },
        #         "desc": "操作成功"
        #     }
        #
        result = response
        if swap:
            data = self.safe_value(response, 'data', {})
            result = self.safe_value(data, 'list', [])
        return self.parse_orders(result, market, since, limit)

    def fetch_canceled_orders(self, symbol=None, since=None, limit=10, params={}):
        """
        fetches information on multiple canceled orders made by the user
        :param str symbol: unified market symbol of the market orders were made in
        :param int|None since: timestamp in ms of the earliest order, default is None
        :param int|None limit: max number of orders to return, default is None
        :param dict params: extra parameters specific to the zb api endpoint
        :returns dict: a list of `order structures <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchCanceledOrders() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        reduceOnly = self.safe_value(params, 'reduceOnly')
        stop = self.safe_value(params, 'stop')
        request = {
            'pageSize': limit,  # SPOT and STOP, default pageSize is 10, doesn't work with other values now
            # 'currency': market['id'],  # SPOT
            # 'pageIndex': 1,  # SPOT, default pageIndex is 1
            # 'symbol': market['id'],  # STOP
            # 'side': params['side'],  # STOP, for stop orders: 1 Open long(buy), 2 Open short(sell), 3 Close long(sell), 4 Close Short(Buy). One-Way Positions: 5 Buy, 6 Sell, 0 Close Only
            # 'orderType': 1,  # STOP, 1: Plan order, 2: SP/SL
            # 'bizType': 1,  # Plan order, 1: TP, 2: SL
            # 'status': 1,  # STOP, 1: untriggered, 2: cancelled, 3:triggered, 4:failed, 5:completed
            # 'startTime': since,  # STOP
            # 'endTime': params['endTime'],  # STOP
            # 'pageNum': 1,  # STOP, default 1
        }
        marketIdField = 'currency' if market['spot'] else 'symbol'
        request[marketIdField] = market['id']
        pageNumField = 'pageIndex' if market['spot'] else 'pageNum'
        request[pageNumField] = 1
        method = 'spotV1PrivateGetGetOrdersIgnoreTradeType'
        if stop:
            method = 'contractV2PrivateGetTradeGetOrderAlgos'
            orderType = self.safe_integer(params, 'orderType')
            if orderType is None:
                raise ArgumentsRequired(self.id + ' fetchCanceledOrders() requires an orderType parameter for stop orders')
            side = self.safe_value(params, 'side')
            bizType = self.safe_integer(params, 'bizType')
            if side == 'sell' and reduceOnly:
                request['side'] = 3  # close long
            elif side == 'buy' and reduceOnly:
                request['side'] = 4  # close short
            elif side == 'buy':
                request['side'] = 1  # open long
            elif side == 'sell':
                request['side'] = 2  # open short
            elif side == 5:
                request['side'] = 5  # one way position buy
            elif side == 6:
                request['side'] = 6  # one way position sell
            elif side == 0:
                request['side'] = 0  # one way position close only
            if orderType == 1:
                request['orderType'] = 1
            elif orderType == 2 or bizType:
                request['orderType'] = 2
                request['bizType'] = bizType
            request['status'] = 2
        # tradeType 交易类型1/0[buy/sell]
        if 'tradeType' in params:
            method = 'spotV1PrivateGetGetOrdersNew'
        response = None
        try:
            response = getattr(self, method)(self.extend(request, params))
        except Exception as e:
            if isinstance(e, OrderNotFound):
                return []
            raise e
        query = self.omit(params, ['reduceOnly', 'stop', 'side', 'orderType', 'bizType'])
        response = getattr(self, method)(self.extend(request, query))
        #
        # Spot
        #
        #     [
        #         {
        #             "acctType": 0,
        #             "currency": "btc_usdt",
        #             "fees": 0,
        #             "id": "202202234857482656",
        #             "price": 30000.0,
        #             "status": 1,
        #             "total_amount": 0.0006,
        #             "trade_amount": 0.0000,
        #             "trade_date": 1645610254524,
        #             "trade_money": 0.000000,
        #             "type": 1,
        #             "useZbFee": False,
        #             "webId": 0
        #         }
        #     ]
        #
        # Algo order
        #
        #     {
        #         "code": 10000,
        #         "data": {
        #             "list": [
        #                 {
        #                     "action": 1,
        #                     "algoPrice": "30000",
        #                     "amount": "0.003",
        #                     "bizType": 0,
        #                     "canCancel": True,
        #                     "createTime": "1649913941109",
        #                     "errorCode": 0,
        #                     "id": "6920240642849449984",
        #                     "isLong": False,
        #                     "leverage": 10,
        #                     "marketId": "100",
        #                     "modifyTime": "1649913941109",
        #                     "orderType": 1,
        #                     "priceType": 2,
        #                     "side": 5,
        #                     "sourceType": 4,
        #                     "status": 2,
        #                     "submitPrice": "41270.53",
        #                     "symbol": "BTC_USDT",
        #                     "tradedAmount": "0",
        #                     "triggerCondition": "<=",
        #                     "triggerPrice": "31000",
        #                     "triggerTime": "0",
        #                     "userId": "6896693805014120448"
        #                 },
        #             ],
        #             "pageNum": 1,
        #             "pageSize": 10
        #         },
        #         "desc": "操作成功"
        #     }
        #
        if stop:
            data = self.safe_value(response, 'data', {})
            response = self.safe_value(data, 'list', [])
        result = []
        if market['type'] == 'spot':
            for i in range(0, len(response)):
                entry = response[i]
                status = self.safe_string(entry, 'status')
                if status == '1':
                    result.append(entry)
            response = result
        return self.parse_orders(response, market, since, limit)

    def fetch_closed_orders(self, symbol=None, since=None, limit=10, params={}):
        """
        fetches information on multiple closed orders made by the user
        :param str symbol: unified market symbol of the market orders were made in
        :param int|None since: the earliest time in ms to fetch orders for
        :param int|None limit: the maximum number of  orde structures to retrieve
        :param dict params: extra parameters specific to the zb api endpoint
        :returns [dict]: a list of `order structures <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchClosedOrders() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        swap = market['swap']
        orderType = self.safe_integer(params, 'orderType')
        request = {
            'pageSize': limit,  # SPOT and STOP, default pageSize is 10, doesn't work with other values now
            # 'currency': market['id'],  # SPOT
            # 'pageIndex': 1,  # SPOT, default pageIndex is 1
            # 'symbol': market['id'],  # STOP
            # 'side': params['side'],  # STOP, for stop orders: 1 Open long(buy), 2 Open short(sell), 3 Close long(sell), 4 Close Short(Buy). One-Way Positions: 5 Buy, 6 Sell, 0 Close Only
            # 'orderType': 1,  # STOP, 1: Plan order, 2: SP/SL
            # 'bizType': 1,  # Plan order, 1: TP, 2: SL
            # 'status': 1,  # STOP, 1: untriggered, 2: cancelled, 3:triggered, 4:failed, 5:completed
            # 'startTime': since,  # STOP
            # 'endTime': params['endTime'],  # STOP
            # 'pageNum': 1,  # STOP, default 1
        }
        marketIdField = 'currency' if market['spot'] else 'symbol'
        request[marketIdField] = market['id']
        pageNumField = 'pageIndex' if market['spot'] else 'pageNum'
        request[pageNumField] = 1
        if swap and (since is not None):
            request['startTime'] = since
        method = self.get_supported_mapping(market['type'], {
            'spot': 'spotV1PrivateGetGetFinishedAndPartialOrders',
            'swap': 'contractV2PrivateGetTradeGetOrderAlgos',
        })
        if swap and (orderType is None):
            raise ExchangeError(self.id + ' fetchClosedOrders() can not fetch swap orders, use fetchOrders instead')
        if swap:
            # a status of 2 would mean canceled and could also be valid
            request['status'] = 5  # complete
        response = getattr(self, method)(self.extend(request, params))
        #
        # Spot
        #
        #     [
        #         {
        #             "acctType": 0,
        #             "currency": "btc_usdt",
        #             "fees": 0.00823354,
        #             "id": "202204145086706337",
        #             "price": 41167.7,
        #             "status": 2,
        #             "total_amount": 0.0001,
        #             "trade_amount": 0.0001,
        #             "trade_date": 1649917867370,
        #             "trade_money": 4.116770,
        #             "type": 0,
        #             "useZbFee": False,
        #             "webId": 0
        #         },
        #     ]
        #
        # Algo order
        #
        #     {
        #         "code": 10000,
        #         "data": {
        #             "list": [
        #                 {
        #                     "action": 1,
        #                     "algoPrice": "30000",
        #                     "amount": "0.003",
        #                     "bizType": 0,
        #                     "canCancel": True,
        #                     "createTime": "1649913941109",
        #                     "errorCode": 0,
        #                     "id": "6920240642849449984",
        #                     "isLong": False,
        #                     "leverage": 10,
        #                     "marketId": "100",
        #                     "modifyTime": "1649913941109",
        #                     "orderType": 1,
        #                     "priceType": 2,
        #                     "side": 5,
        #                     "sourceType": 4,
        #                     "status": 1,
        #                     "submitPrice": "41270.53",
        #                     "symbol": "BTC_USDT",
        #                     "tradedAmount": "0",
        #                     "triggerCondition": "<=",
        #                     "triggerPrice": "31000",
        #                     "triggerTime": "0",
        #                     "userId": "6896693805014120448"
        #                 },
        #             ],
        #             "pageNum": 1,
        #             "pageSize": 10
        #         },
        #         "desc": "操作成功"
        #     }
        #
        result = response
        if swap:
            data = self.safe_value(response, 'data', {})
            result = self.safe_value(data, 'list', [])
        return self.parse_orders(result, market, since, limit)

    def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
        """
        fetch all unfilled currently open orders
        :param str symbol: unified market symbol
        :param int|None since: the earliest time in ms to fetch open orders for
        :param int|None limit: the maximum number of  open orders structures to retrieve
        :param dict params: extra parameters specific to the zb api endpoint
        :returns [dict]: a list of `order structures <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOpenOrders() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        orderType = self.safe_integer(params, 'orderType')
        swap = market['swap']
        request = {
            # 'pageSize': limit,  # default pageSize is 10 for spot, 30 for swap
            # 'currency': market['id'],  # SPOT
            # 'pageIndex': 1,  # SPOT
            # 'symbol': market['id'],  # SWAP and STOP
            # 'pageNum': 1,  # SWAP and STOP, default 1
            # 'type': params['type'],  # swap only
            # 'side': params['side'],  # SWAP and STOP, for stop orders: 1 Open long(buy), 2 Open short(sell), 3 Close long(sell), 4 Close Short(Buy). One-Way Positions: 5 Buy, 6 Sell, 0 Close Only
            # 'action': params['action'],  # SWAP
            # 'orderType': 1,  # STOP, 1: Plan order, 2: SP/SL
            # 'bizType': 1,  # Plan order, 1: TP, 2: SL
            # 'status': 1,  # STOP, 1: untriggered, 2: cancelled, 3:triggered, 4:failed, 5:completed
            # 'startTime': since,  # SWAP and STOP
            # 'endTime': params['endTime'],  # STOP
        }
        if limit is not None:
            request['pageSize'] = limit  # default pageSize is 10 for spot, 30 for swap
        marketIdField = 'symbol' if market['swap'] else 'currency'
        request[marketIdField] = market['id']
        pageNumField = 'pageNum' if market['swap'] else 'pageIndex'
        request[pageNumField] = 1
        if swap and (since is not None):
            request['startTime'] = since
        method = self.get_supported_mapping(market['type'], {
            'spot': 'spotV1PrivateGetGetUnfinishedOrdersIgnoreTradeType',
            'swap': 'contractV2PrivateGetTradeGetUndoneOrders',
        })
        if orderType is not None:
            method = 'contractV2PrivateGetTradeGetOrderAlgos'
            # value 3 would mean triggered but still open orders
            request['status'] = 1  # untriggered
        # tradeType 交易类型1/0[buy/sell]
        if 'tradeType' in params:
            method = 'spotV1PrivateGetGetOrdersNew'
        response = getattr(self, method)(self.extend(request, params))
        #
        # Spot
        #
        #     [
        #         {
        #             "currency": "btc_usdt",
        #             "id": "20150928158614292",
        #             "price": 1560,
        #             "status": 3,
        #             "total_amount": 0.1,
        #             "trade_amount": 0,
        #             "trade_date": 1443410396717,
        #             "trade_money": 0,
        #             "type": 0,
        #             "fees": "0.03",
        #             "useZbFee": True
        #         },
        #     ]
        #
        # Swap
        #
        #     {
        #         "code": 10000,
        #         "data": {
        #             "list": [
        #                 {
        #                     "action": 1,
        #                     "amount": "0.003",
        #                     "availableAmount": "0.003",
        #                     "availableValue": "90",
        #                     "avgPrice": "0",
        #                     "canCancel": True,
        #                     "cancelStatus": 20,
        #                     "createTime": "1645694610880",
        #                     "entrustType": 1,
        #                     "id": "6902543489192632320",
        #                     "leverage": 5,
        #                     "margin": "18",
        #                     "marketId": "100",
        #                     "modifyTime": "1645694610883",
        #                     "price": "30000",
        #                     "priority": 0,
        #                     "showStatus": 1,
        #                     "side": 1,
        #                     "sourceType": 1,
        #                     "status": 12,
        #                     "tradeAmount": "0",
        #                     "tradeValue": "0",
        #                     "type": 1,
        #                     "userId": "6896693805014120448",
        #                     "value": "90"
        #                 }
        #             ],
        #             "pageNum": 1,
        #             "pageSize": 30
        #         },
        #         "desc": "操作成功"
        #     }
        #
        # Algo order
        #
        #     {
        #         "code": 10000,
        #         "data": {
        #             "list": [
        #                 {
        #                     "action": 1,
        #                     "algoPrice": "30000",
        #                     "amount": "0.003",
        #                     "bizType": 0,
        #                     "canCancel": True,
        #                     "createTime": "1649913941109",
        #                     "errorCode": 0,
        #                     "id": "6920240642849449984",
        #                     "isLong": False,
        #                     "leverage": 10,
        #                     "marketId": "100",
        #                     "modifyTime": "1649913941109",
        #                     "orderType": 1,
        #                     "priceType": 2,
        #                     "side": 5,
        #                     "sourceType": 4,
        #                     "status": 1,
        #                     "submitPrice": "41270.53",
        #                     "symbol": "BTC_USDT",
        #                     "tradedAmount": "0",
        #                     "triggerCondition": "<=",
        #                     "triggerPrice": "31000",
        #                     "triggerTime": "0",
        #                     "userId": "6896693805014120448"
        #                 },
        #             ],
        #             "pageNum": 1,
        #             "pageSize": 10
        #         },
        #         "desc": "操作成功"
        #     }
        #
        result = response
        if swap:
            data = self.safe_value(response, 'data', {})
            result = self.safe_value(data, 'list', [])
        return self.parse_orders(result, market, since, limit)

    def parse_order(self, order, market=None):
        #
        # Spot fetchOrder, fetchClosedOrders
        #
        #     {
        #         acctType: 0,
        #         currency: 'btc_usdt',
        #         fees: 3.6e-7,
        #         id: '202102282829772463',
        #         price: 45177.5,
        #         status: 2,
        #         total_amount: 0.0002,
        #         trade_amount: 0.0002,
        #         trade_date: 1614515104998,
        #         trade_money: 8.983712,
        #         type: 1,
        #         useZbFee: False
        #     },
        #
        # Swap fetchOrder
        #
        #     {
        #         "action": 1,
        #         "amount": "0.002",
        #         "availableAmount": "0.002",
        #         "availableValue": "60",
        #         "avgPrice": "0",
        #         "canCancel": True,
        #         "cancelStatus": 20,
        #         "createTime": "1646185684379",
        #         "entrustType": 1,
        #         "id": "6904603200733782016",
        #         "leverage": 2,
        #         "margin": "30",
        #         "marketId": "100",
        #         "modifyTime": "1646185684416",
        #         "price": "30000",
        #         "priority": 0,
        #         "showStatus": 1,
        #         "side": 1,
        #         "sourceType": 4,
        #         "status": 12,
        #         "tradeAmount": "0",
        #         "tradeValue": "0",
        #         "type": 1,
        #         "userId": "6896693805014120448",
        #         "value": "60"
        #     },
        #
        # Algo fetchOrder, fetchOrders, fetchOpenOrders, fetchClosedOrders
        #
        #     {
        #         "action": 1,
        #         "algoPrice": "30000",
        #         "amount": "0.003",
        #         "bizType": 0,
        #         "canCancel": True,
        #         "createTime": "1649913941109",
        #         "errorCode": 0,
        #         "id": "6920240642849449984",
        #         "isLong": False,
        #         "leverage": 10,
        #         "marketId": "100",
        #         "modifyTime": "1649913941109",
        #         "orderType": 1,
        #         "priceType": 2,
        #         "side": 5,
        #         "sourceType": 4,
        #         "status": 1,
        #         "submitPrice": "41270.53",
        #         "symbol": "BTC_USDT",
        #         "tradedAmount": "0",
        #         "triggerCondition": "<=",
        #         "triggerPrice": "31000",
        #         "triggerTime": "0",
        #         "userId": "6896693805014120448"
        #     },
        #
        # Spot createOrder
        #
        #     {
        #         code: '1000',
        #         message: '操作成功',
        #         id: '202202224851151555',
        #         type: '1',
        #         total_amount: 0.0002,
        #         price: 30000
        #     }
        #
        # Swap createOrder
        #
        #     {
        #         orderId: '6901786759944937472',
        #         orderCode: null,
        #         timeInForce: 'IOC',
        #         total_amount: 0.0002,
        #         price: 30000
        #     }
        #
        # Algo createOrder
        #
        #     {
        #         "code": 10000,
        #         "data": "6919884551305242624",
        #         "desc": "操作成功"
        #     }
        #
        orderId = self.safe_string_2(order, 'orderId', 'data') if market['swap'] else self.safe_string(order, 'id')
        if orderId is None:
            orderId = self.safe_value(order, 'id')
        side = self.safe_integer_2(order, 'type', 'side')
        if side is None:
            side = None
        else:
            if market['spot']:
                side = 'buy' if (side == 1) else 'sell'
            elif market['swap']:
                if side == 0:
                    side = None
                elif (side == 1) or (side == 4) or (side == 5):
                    side = 'buy'
                elif (side == 2) or (side == 3) or (side == 6):
                    side = 'sell'
        timestamp = self.safe_integer(order, 'trade_date')
        if timestamp is None:
            timestamp = self.safe_integer(order, 'createTime')
        marketId = self.safe_string(order, 'currency')
        market = self.safe_market(marketId, market, '_')
        price = self.safe_string_2(order, 'price', 'algoPrice')
        filled = self.safe_string(order, 'tradeAmount') if market['swap'] else self.safe_string(order, 'trade_amount')
        amount = self.safe_string(order, 'total_amount')
        if amount is None:
            amount = self.safe_string(order, 'amount')
        cost = self.safe_string(order, 'trade_money')
        status = self.parse_order_status(self.safe_string(order, 'status'), market)
        timeInForce = self.safe_string(order, 'timeInForce')
        postOnly = (timeInForce == 'PO')
        feeCost = self.safe_number(order, 'fees')
        fee = None
        if feeCost is not None:
            feeCurrency = None
            zbFees = self.safe_value(order, 'useZbFee')
            if zbFees is True:
                feeCurrency = 'ZB'
            else:
                feeCurrency = market['quote'] if (side == 'sell') else market['base']
            fee = {
                'cost': feeCost,
                'currency': feeCurrency,
            }
        return self.safe_order({
            'info': order,
            'id': orderId,
            'clientOrderId': self.safe_string(order, 'userId'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': None,
            'symbol': market['symbol'],
            'type': 'limit',  # market order is not available on ZB
            'timeInForce': timeInForce,
            'postOnly': postOnly,
            'side': side,
            'price': price,
            'stopPrice': self.safe_number(order, 'triggerPrice'),
            'triggerPrice': self.safe_number(order, 'triggerPrice'),
            'average': self.safe_string(order, 'avgPrice'),
            'cost': cost,
            'amount': amount,
            'filled': filled,
            'remaining': None,
            'status': status,
            'fee': fee,
            'trades': None,
        }, market)

    def parse_order_status(self, status, market=None):
        statuses = {}
        if market['type'] == 'spot':
            statuses = {
                '0': 'open',
                '1': 'canceled',
                '2': 'closed',
                '3': 'open',  # partial
            }
        else:
            statuses = {
                '1': 'open',
                '2': 'canceled',
                '3': 'open',  # stop order triggered
                '4': 'rejected',
                '5': 'closed',
            }
        return self.safe_string(statuses, status, status)

    def parse_transaction_status(self, status):
        statuses = {
            '0': 'pending',  # submitted, pending confirmation
            '1': 'failed',
            '2': 'ok',
            '3': 'canceled',
            '5': 'ok',  # confirmed
        }
        return self.safe_string(statuses, status, status)

    def parse_transaction(self, transaction, currency=None):
        #
        # withdraw
        #
        #     {
        #         "code": 1000,
        #         "message": "success",
        #         "id": "withdrawalId"
        #     }
        #
        # fetchWithdrawals
        #
        #     {
        #         "amount": 0.01,
        #         "fees": 0.001,
        #         "id": 2016042556231,
        #         "manageTime": 1461579340000,
        #         "status": 3,
        #         "submitTime": 1461579288000,
        #         "toAddress": "14fxEPirL9fyfw1i9EF439Pq6gQ5xijUmp",
        #     }
        #
        # fetchDeposits
        #
        #     {
        #         "address": "1FKN1DZqCm8HaTujDioRL2Aezdh7Qj7xxx",
        #         "amount": "1.00000000",
        #         "confirmTimes": 1,
        #         "currency": "BTC",
        #         "description": "Successfully Confirm",
        #         "hash": "7ce842de187c379abafadd64a5fe66c5c61c8a21fb04edff9532234a1dae6xxx",
        #         "id": 558,
        #         "itransfer": 1,
        #         "status": 2,
        #         "submit_time": "2016-12-07 18:51:57",
        #     }
        #
        id = self.safe_string(transaction, 'id')
        txid = self.safe_string(transaction, 'hash')
        amount = self.safe_number(transaction, 'amount')
        timestamp = self.parse8601(self.safe_string(transaction, 'submit_time'))
        timestamp = self.safe_integer(transaction, 'submitTime', timestamp)
        address = self.safe_string_2(transaction, 'toAddress', 'address')
        tag = None
        if address is not None:
            parts = address.split('_')
            address = self.safe_string(parts, 0)
            tag = self.safe_string(parts, 1)
        confirmTimes = self.safe_integer(transaction, 'confirmTimes')
        updated = self.safe_integer(transaction, 'manageTime')
        type = None
        currencyId = self.safe_string(transaction, 'currency')
        code = self.safe_currency_code(currencyId, currency)
        if address is not None:
            type = 'withdrawal' if (confirmTimes is None) else 'deposit'
        status = self.parse_transaction_status(self.safe_string(transaction, 'status'))
        fee = None
        feeCost = self.safe_number(transaction, 'fees')
        if feeCost is not None:
            fee = {
                'cost': feeCost,
                'currency': code,
            }
        return {
            'info': transaction,
            'id': id,
            'txid': txid,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'network': None,
            'addressFrom': None,
            'address': address,
            'addressTo': address,
            'tagFrom': None,
            'tag': tag,
            'tagTo': tag,
            'type': type,
            'amount': amount,
            'currency': code,
            'status': status,
            'updated': updated,
            'fee': fee,
        }

    def set_leverage(self, leverage, symbol=None, params={}):
        """
        set the level of leverage for a market
        :param float leverage: the rate of leverage
        :param str symbol: unified market symbol
        :param dict params: extra parameters specific to the zb api endpoint
        :returns dict: response from the exchange
        """
        self.load_markets()
        if symbol is None:
            raise ArgumentsRequired(self.id + ' setLeverage() requires a symbol argument')
        if (leverage < 1) or (leverage > 125):
            raise BadRequest(self.id + ' setLeverage() leverage should be between 1 and 125')
        market = self.market(symbol)
        accountType = None
        if not market['swap']:
            raise BadSymbol(self.id + ' setLeverage() supports swap contracts only')
        else:
            accountType = 1
        request = {
            'symbol': market['id'],
            'leverage': leverage,
            'futuresAccountType': accountType,  # 1: USDT perpetual swaps
        }
        return self.contractV2PrivatePostSettingSetLeverage(self.extend(request, params))

    def fetch_funding_rate_history(self, symbol=None, since=None, limit=None, params={}):
        """
        fetches historical funding rate prices
        :param str|None symbol: unified symbol of the market to fetch the funding rate history for
        :param int|None since: timestamp in ms of the earliest funding rate to fetch
        :param int|None limit: the maximum amount of `funding rate structures <https://docs.ccxt.com/en/latest/manual.html?#funding-rate-history-structure>` to fetch
        :param dict params: extra parameters specific to the zb api endpoint
        :param int|None params['until']: timestamp in ms of the latest funding rate to fetch
        :returns [dict]: a list of `funding rate structures <https://docs.ccxt.com/en/latest/manual.html?#funding-rate-history-structure>`
        """
        self.load_markets()
        request = {
            # 'symbol': market['id'],
            # 'startTime': since,
            # 'endTime': endTime,  # current time by default
            # 'limit': limit,  # default 100, max 1000
        }
        if symbol is not None:
            market = self.market(symbol)
            symbol = market['symbol']
            request['symbol'] = market['id']
        if since is not None:
            request['startTime'] = since
        until = self.safe_integer_2(params, 'until', 'till')
        params = self.omit(params, ['endTime', 'till', 'until'])
        if until is not None:
            request['endTime'] = until
        if limit is not None:
            request['limit'] = limit
        response = self.contractV2PublicGetFundingRate(self.extend(request, params))
        #
        #     {
        #         "code": 10000,
        #         "data": [
        #             {
        #                 "symbol": "BTC_USDT",
        #                 "fundingRate": "0.0001",
        #                 "fundingTime": "1645171200000"
        #             },
        #         ],
        #         "desc": "操作成功"
        #     }
        #
        data = self.safe_value(response, 'data', [])
        rates = []
        for i in range(0, len(data)):
            entry = data[i]
            marketId = self.safe_string(entry, 'symbol')
            symbol = self.safe_symbol(marketId)
            timestamp = self.safe_integer(entry, 'fundingTime')
            rates.append({
                'info': entry,
                'symbol': symbol,
                'fundingRate': self.safe_number(entry, 'fundingRate'),
                'timestamp': timestamp,
                'datetime': self.iso8601(timestamp),
            })
        sorted = self.sort_by(rates, 'timestamp')
        return self.filter_by_symbol_since_limit(sorted, symbol, since, limit)

    def fetch_funding_rate(self, symbol, params={}):
        """
        fetch the current funding rate
        :param str symbol: unified market symbol
        :param dict params: extra parameters specific to the zb api endpoint
        :returns dict: a `funding rate structure <https://docs.ccxt.com/en/latest/manual.html#funding-rate-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        if not market['swap']:
            raise BadSymbol(self.id + ' fetchFundingRate() does not supports contracts only')
        request = {
            'symbol': market['id'],
        }
        response = self.contractV1PublicGetFundingRate(self.extend(request, params))
        #
        #     {
        #         "code": 10000,
        #         "desc": "操作成功",
        #         "data": {
        #             "fundingRate": "0.0001",
        #             "nextCalculateTime": "2022-02-19 00:00:00"
        #         }
        #     }
        #
        data = self.safe_value(response, 'data')
        return self.parse_funding_rate(data, market)

    def parse_funding_rate(self, contract, market=None):
        #
        # fetchFundingRate
        #
        #     {
        #         "fundingRate": "0.0001",
        #         "nextCalculateTime": "2022-02-19 00:00:00"
        #     }
        #
        # fetchFundingRates
        #
        #     {
        #         "symbol": "BTC_USDT",
        #         "markPrice": "43254.42",
        #         "indexPrice": "43278.61",
        #         "lastFundingRate": "0.0001",
        #         "nextFundingTime": "1646121600000"
        #     }
        #
        marketId = self.safe_string(contract, 'symbol')
        symbol = self.safe_symbol(marketId, market)
        fundingRate = self.safe_number_2(contract, 'fundingRate', 'lastFundingRate')
        nextFundingTimestamp = self.parse8601(self.safe_string(contract, 'nextCalculateTime'))
        fundingTimestamp = self.safe_integer(contract, 'nextFundingTime')
        return {
            'info': contract,
            'symbol': symbol,
            'markPrice': self.safe_string(contract, 'markPrice'),
            'indexPrice': self.safe_string(contract, 'indexPrice'),
            'interestRate': None,
            'estimatedSettlePrice': None,
            'timestamp': None,
            'datetime': None,
            'fundingRate': fundingRate,
            'fundingTimestamp': fundingTimestamp,
            'fundingDatetime': self.iso8601(fundingTimestamp),
            'nextFundingRate': None,
            'nextFundingTimestamp': nextFundingTimestamp,
            'nextFundingDatetime': self.iso8601(nextFundingTimestamp),
            'previousFundingRate': None,
            'previousFundingTimestamp': None,
            'previousFundingDatetime': None,
        }

    def fetch_funding_rates(self, symbols=None, params={}):
        """
        fetch the funding rate for multiple markets
        :param [str]|None symbols: list of unified market symbols
        :param dict params: extra parameters specific to the zb api endpoint
        :returns dict: a dictionary of `funding rates structures <https://docs.ccxt.com/en/latest/manual.html#funding-rates-structure>`, indexe by market symbols
        """
        self.load_markets()
        symbols = self.market_symbols(symbols)
        response = self.contractV2PublicGetPremiumIndex(params)
        #
        #     {
        #         "code": 10000,
        #         "data": [
        #             {
        #                 "symbol": "BTC_USDT",
        #                 "markPrice": "43254.42",
        #                 "indexPrice": "43278.61",
        #                 "lastFundingRate": "0.0001",
        #                 "nextFundingTime": "1646121600000"
        #             },
        #         ],
        #         "desc":"操作成功"
        #     }
        #
        data = self.safe_value(response, 'data', [])
        result = self.parse_funding_rates(data)
        return self.filter_by_array(result, 'symbol', symbols)

    def withdraw(self, code, amount, address, tag=None, params={}):
        """
        make a withdrawal
        :param str code: unified currency code
        :param float amount: the amount to withdraw
        :param str address: the address to withdraw to
        :param str|None tag:
        :param dict params: extra parameters specific to the zb api endpoint
        :returns dict: a `transaction structure <https://docs.ccxt.com/en/latest/manual.html#transaction-structure>`
        """
        tag, params = self.handle_withdraw_tag_and_params(tag, params)
        password = self.safe_string(params, 'safePwd', self.password)
        if password is None:
            raise ArgumentsRequired(self.id + ' withdraw() requires exchange.password or a safePwd parameter')
        fees = self.safe_number(params, 'fees')
        if fees is None:
            raise ArgumentsRequired(self.id + ' withdraw() requires a fees parameter')
        self.check_address(address)
        self.load_markets()
        currency = self.currency(code)
        if tag is not None:
            address += '_' + tag
        request = {
            'amount': self.currency_to_precision(code, amount),
            'currency': currency['id'],
            'fees': self.currency_to_precision(code, fees),
            # 'itransfer': 0,  # agree for an internal transfer, 0 disagree, 1 agree, the default is to disagree
            'method': 'withdraw',
            'receiveAddr': address,
            'safePwd': password,
        }
        response = self.spotV1PrivateGetWithdraw(self.extend(request, params))
        #
        #     {
        #         "code": 1000,
        #         "message": "success",
        #         "id": "withdrawalId"
        #     }
        #
        transaction = self.parse_transaction(response, currency)
        return self.extend(transaction, {
            'type': 'withdrawal',
            'address': address,
            'addressTo': address,
            'amount': amount,
        })

    def fetch_withdrawals(self, code=None, since=None, limit=None, params={}):
        """
        fetch all withdrawals made from an account
        :param str|None code: unified currency code
        :param int|None since: the earliest time in ms to fetch withdrawals for
        :param int|None limit: the maximum number of withdrawals structures to retrieve
        :param dict params: extra parameters specific to the zb api endpoint
        :returns [dict]: a list of `transaction structures <https://docs.ccxt.com/en/latest/manual.html#transaction-structure>`
        """
        self.load_markets()
        request = {
            # 'currency': currency['id'],
            # 'pageIndex': 1,
            # 'pageSize': limit,
        }
        currency = None
        if code is not None:
            currency = self.currency(code)
            request['currency'] = currency['id']
        if limit is not None:
            request['pageSize'] = limit
        response = self.spotV1PrivateGetGetWithdrawRecord(self.extend(request, params))
        #
        #     {
        #         "code": 1000,
        #         "message": {
        #             "des": "success",
        #             "isSuc": True,
        #             "datas": {
        #                 "list": [
        #                     {
        #                         "amount": 0.01,
        #                         "fees": 0.001,
        #                         "id": 2016042556231,
        #                         "manageTime": 1461579340000,
        #                         "status": 3,
        #                         "submitTime": 1461579288000,
        #                         "toAddress": "14fxEPirL9fyfw1i9EF439Pq6gQ5xijUmp",
        #                     },
        #                 ],
        #                 "pageIndex": 1,
        #                 "pageSize": 10,
        #                 "totalCount": 4,
        #                 "totalPage": 1
        #             }
        #         }
        #     }
        #
        message = self.safe_value(response, 'message', {})
        datas = self.safe_value(message, 'datas', {})
        withdrawals = self.safe_value(datas, 'list', [])
        return self.parse_transactions(withdrawals, currency, since, limit)

    def fetch_deposits(self, code=None, since=None, limit=None, params={}):
        """
        fetch all deposits made to an account
        :param str|None code: unified currency code
        :param int|None since: the earliest time in ms to fetch deposits for
        :param int|None limit: the maximum number of deposits structures to retrieve
        :param dict params: extra parameters specific to the zb api endpoint
        :returns [dict]: a list of `transaction structures <https://docs.ccxt.com/en/latest/manual.html#transaction-structure>`
        """
        self.load_markets()
        request = {
            # 'currency': currency['id'],
            # 'pageIndex': 1,
            # 'pageSize': limit,
        }
        currency = None
        if code is not None:
            currency = self.currency(code)
            request['currency'] = currency['id']
        if limit is not None:
            request['pageSize'] = limit
        response = self.spotV1PrivateGetGetChargeRecord(self.extend(request, params))
        #
        #     {
        #         "code": 1000,
        #         "message": {
        #             "des": "success",
        #             "isSuc": True,
        #             "datas": {
        #                 "list": [
        #                     {
        #                         "address": "1FKN1DZqCm8HaTujDioRL2Aezdh7Qj7xxx",
        #                         "amount": "1.00000000",
        #                         "confirmTimes": 1,
        #                         "currency": "BTC",
        #                         "description": "Successfully Confirm",
        #                         "hash": "7ce842de187c379abafadd64a5fe66c5c61c8a21fb04edff9532234a1dae6xxx",
        #                         "id": 558,
        #                         "itransfer": 1,
        #                         "status": 2,
        #                         "submit_time": "2016-12-07 18:51:57",
        #                     },
        #                 ],
        #                 "pageIndex": 1,
        #                 "pageSize": 10,
        #                 "total": 8
        #             }
        #         }
        #     }
        #
        message = self.safe_value(response, 'message', {})
        datas = self.safe_value(message, 'datas', {})
        deposits = self.safe_value(datas, 'list', [])
        return self.parse_transactions(deposits, currency, since, limit)

    def fetch_position(self, symbol, params={}):
        """
        fetch data on a single open contract trade position
        :param str symbol: unified market symbol of the market the position is held in, default is None
        :param dict params: extra parameters specific to the zb api endpoint
        :returns dict: a `position structure <https://docs.ccxt.com/en/latest/manual.html#position-structure>`
        """
        self.load_markets()
        market = None
        if symbol is not None:
            market = self.market(symbol)
        request = {
            'futuresAccountType': 1,  # 1: USDT-M Perpetual Futures
            # 'symbol': market['id'],
            # 'marketId': market['id'],
            # 'side': params['side'],
        }
        response = self.contractV2PrivateGetPositionsGetPositions(self.extend(request, params))
        #
        #     {
        #         "code": 10000,
        #         "data": [
        #             {
        #                 "amount": "0.002",
        #                 "appendAmount": "0",
        #                 "autoLightenRatio": "0",
        #                 "avgPrice": "38570",
        #                 "bankruptcyPrice": "46288.41",
        #                 "contractType": 1,
        #                 "createTime": "1645784751867",
        #                 "freezeAmount": "0",
        #                 "freezeList": [
        #                     {
        #                         "amount": "15.436832",
        #                         "currencyId": "6",
        #                         "currencyName": "usdt",
        #                         "modifyTime": "1645784751867"
        #                     }
        #                 ],
        #                 "id": "6902921567894972486",
        #                 "lastAppendAmount": "0",
        #                 "leverage": 5,
        #                 "liquidateLevel": 1,
        #                 "liquidatePrice": "46104",
        #                 "maintainMargin": "0.30912384",
        #                 "margin": "15.436832",
        #                 "marginAppendCount": 0,
        #                 "marginBalance": "15.295872",
        #                 "marginMode": 1,
        #                 "marginRate": "0.020209",
        #                 "marketId": "100",
        #                 "marketName": "BTC_USDT",
        #                 "modifyTime": "1645784751867",
        #                 "nominalValue": "77.14736",
        #                 "originAppendAmount": "0",
        #                 "originId": "6902921567894972591",
        #                 "refreshType": "Timer",
        #                 "returnRate": "-0.0091",
        #                 "side": 0,
        #                 "status": 1,
        #                 "unrealizedPnl": "-0.14096",
        #                 "userId": "6896693805014120448"
        #             }
        #         ],
        #         "desc": "操作成功"
        #     }
        #
        data = self.safe_value(response, 'data', [])
        firstPosition = self.safe_value(data, 0)
        return self.parse_position(firstPosition, market)

    def fetch_positions(self, symbols=None, params={}):
        """
        fetch all open positions
        :param [str]|None symbols: list of unified market symbols
        :param dict params: extra parameters specific to the zb api endpoint
        :returns [dict]: a list of `position structure <https://docs.ccxt.com/en/latest/manual.html#position-structure>`
        """
        self.load_markets()
        request = {
            'futuresAccountType': 1,  # 1: USDT-M Perpetual Futures
            # 'symbol': market['id'],
            # 'marketId': market['id'],
            # 'side': params['side'],
        }
        response = self.contractV2PrivateGetPositionsGetPositions(self.extend(request, params))
        #
        #     {
        #         "code": 10000,
        #         "data": [
        #             {
        #                 "amount": "0.002",
        #                 "appendAmount": "0",
        #                 "autoLightenRatio": "0",
        #                 "avgPrice": "38570",
        #                 "bankruptcyPrice": "46288.41",
        #                 "contractType": 1,
        #                 "createTime": "1645784751867",
        #                 "freezeAmount": "0",
        #                 "freezeList": [
        #                     {
        #                         "amount": "15.436832",
        #                         "currencyId": "6",
        #                         "currencyName": "usdt",
        #                         "modifyTime": "1645784751867"
        #                     }
        #                 ],
        #                 "id": "6902921567894972486",
        #                 "lastAppendAmount": "0",
        #                 "leverage": 5,
        #                 "liquidateLevel": 1,
        #                 "liquidatePrice": "46104",
        #                 "maintainMargin": "0.30912384",
        #                 "margin": "15.436832",
        #                 "marginAppendCount": 0,
        #                 "marginBalance": "15.295872",
        #                 "marginMode": 1,
        #                 "marginRate": "0.020209",
        #                 "marketId": "100",
        #                 "marketName": "BTC_USDT",
        #                 "modifyTime": "1645784751867",
        #                 "nominalValue": "77.14736",
        #                 "originAppendAmount": "0",
        #                 "originId": "6902921567894972591",
        #                 "refreshType": "Timer",
        #                 "returnRate": "-0.0091",
        #                 "side": 0,
        #                 "status": 1,
        #                 "unrealizedPnl": "-0.14096",
        #                 "userId": "6896693805014120448"
        #             },
        #         ],
        #         "desc": "操作成功"
        #     }
        #
        data = self.safe_value(response, 'data', [])
        return self.parse_positions(data, symbols)

    def parse_position(self, position, market=None):
        #
        #     {
        #         "amount": "0.002",
        #         "appendAmount": "0",
        #         "autoLightenRatio": "0",
        #         "avgPrice": "38570",
        #         "bankruptcyPrice": "46288.41",
        #         "contractType": 1,
        #         "createTime": "1645784751867",
        #         "freezeAmount": "0",
        #         "freezeList": [
        #             {
        #                 "amount": "15.436832",
        #                 "currencyId": "6",
        #                 "currencyName": "usdt",
        #                 "modifyTime": "1645784751867"
        #             }
        #         ],
        #         "id": "6902921567894972486",
        #         "lastAppendAmount": "0",
        #         "leverage": 5,
        #         "liquidateLevel": 1,
        #         "liquidatePrice": "46104",
        #         "maintainMargin": "0.30912384",
        #         "margin": "15.436832",
        #         "marginAppendCount": 0,
        #         "marginBalance": "15.295872",
        #         "marginMode": 1,
        #         "marginRate": "0.020209",
        #         "marketId": "100",
        #         "marketName": "BTC_USDT",
        #         "modifyTime": "1645784751867",
        #         "nominalValue": "77.14736",
        #         "originAppendAmount": "0",
        #         "originId": "6902921567894972591",
        #         "refreshType": "Timer",
        #         "returnRate": "-0.0091",
        #         "side": 0,
        #         "status": 1,
        #         "unrealizedPnl": "-0.14096",
        #         "userId": "6896693805014120448"
        #     }
        #
        marketId = self.safe_string(position, 'marketName')
        market = self.safe_market(marketId, market)
        symbol = market['symbol']
        contracts = self.safe_string(position, 'amount')
        entryPrice = self.safe_number(position, 'avgPrice')
        initialMargin = self.safe_string(position, 'margin')
        rawSide = self.safe_string(position, 'side')
        side = 'long' if (rawSide == '1') else 'short'
        openType = self.safe_string(position, 'marginMode')
        marginMode = 'isolated' if (openType == '1') else 'cross'
        leverage = self.safe_string(position, 'leverage')
        liquidationPrice = self.safe_number(position, 'liquidatePrice')
        unrealizedProfit = self.safe_number(position, 'unrealizedPnl')
        maintenanceMargin = self.safe_number(position, 'maintainMargin')
        marginRatio = self.safe_number(position, 'marginRate')
        notional = self.safe_number(position, 'nominalValue')
        percentage = Precise.string_mul(self.safe_string(position, 'returnRate'), '100')
        timestamp = self.safe_number(position, 'createTime')
        return {
            'info': position,
            'id': None,
            'symbol': symbol,
            'contracts': self.parse_number(contracts),
            'contractSize': None,
            'entryPrice': entryPrice,
            'collateral': None,
            'side': side,
            'unrealizedProfit': unrealizedProfit,
            'leverage': self.parse_number(leverage),
            'percentage': percentage,
            'marginMode': marginMode,
            'notional': notional,
            'markPrice': None,
            'liquidationPrice': liquidationPrice,
            'initialMargin': self.parse_number(initialMargin),
            'initialMarginPercentage': None,
            'maintenanceMargin': maintenanceMargin,
            'maintenanceMarginPercentage': None,
            'marginRatio': marginRatio,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
        }

    def parse_ledger_entry_type(self, type):
        types = {
            '1': 'realized pnl',
            '2': 'commission',
            '3': 'funding fee subtract',
            '4': 'funding fee addition',
            '5': 'insurance clear',
            '6': 'transfer in',
            '7': 'transfer out',
            '8': 'margin addition',
            '9': 'margin subtraction',
            '10': 'commission addition',
            '11': 'bill type freeze',
            '12': 'bill type unfreeze',
            '13': 'system take over margin',
            '14': 'transfer',
            '15': 'realized pnl collection',
            '16': 'funding fee collection',
            '17': 'recommender return commission',
            '18': 'by level subtract positions',
            '19': 'system add',
            '20': 'system subtract',
            '23': 'trading competition take over fund',
            '24': 'trading contest tickets',
            '25': 'return of trading contest tickets',
            '26': 'experience expired recall',
            '50': 'test register gift',
            '51': 'register gift',
            '52': 'deposit gift',
            '53': 'trading volume gift',
            '54': 'awards gift',
            '55': 'trading volume gift',
            '56': 'awards gift expire',
            '201': 'open positions',
            '202': 'close positions',
            '203': 'take over positions',
            '204': 'trading competition take over positions',
            '205': 'one way open long',
            '206': 'one way open short',
            '207': 'one way close long',
            '208': 'one way close short',
            '301': 'coupon deduction service charge',
            '302': 'experience deduction',
            '303': 'experience expired',
        }
        return self.safe_string(types, type, type)

    def parse_ledger_entry(self, item, currency=None):
        #
        #     [
        #         {
        #             "type": 3,
        #             "changeAmount": "0.00434664",
        #             "isIn": 0,
        #             "beforeAmount": "30.53353135",
        #             "beforeFreezeAmount": "21.547",
        #             "createTime": "1646121604997",
        #             "available": "30.52918471",
        #             "unit": "usdt",
        #             "symbol": "BTC_USDT"
        #         },
        #     ],
        #
        timestamp = self.safe_integer(item, 'createTime')
        direction = None
        changeDirection = self.safe_number(item, 'isIn')
        if changeDirection == 1:
            direction = 'increase'
        else:
            direction = 'reduce'
        fee = None
        feeCost = self.safe_number(item, 'fee')
        if feeCost is not None:
            fee = {
                'cost': feeCost,
                'currency': self.safe_currency_code(self.safe_string(item, 'unit')),
            }
        return {
            'id': self.safe_string(item, 'id'),
            'info': item,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'direction': direction,
            'account': self.safe_string(item, 'userId'),
            'referenceId': None,
            'referenceAccount': None,
            'type': self.parse_ledger_entry_type(self.safe_integer(item, 'type')),
            'currency': self.safe_currency_code(self.safe_string(item, 'unit')),
            'amount': self.safe_number(item, 'changeAmount'),
            'before': self.safe_number(item, 'beforeAmount'),
            'after': self.safe_number(item, 'available'),
            'status': None,
            'fee': fee,
        }

    def fetch_ledger(self, code=None, since=None, limit=None, params={}):
        """
        fetch the history of changes, actions done by the user or operations that altered balance of the user
        :param str code: unified currency code, default is None
        :param int|None since: timestamp in ms of the earliest ledger entry, default is None
        :param int|None limit: max number of ledger entrys to return, default is None
        :param dict params: extra parameters specific to the zb api endpoint
        :returns dict: a `ledger structure <https://docs.ccxt.com/en/latest/manual.html#ledger-structure>`
        """
        if code is None:
            raise ArgumentsRequired(self.id + ' fetchLedger() requires a code argument')
        self.load_markets()
        currency = self.currency(code)
        request = {
            'futuresAccountType': 1,
            # 'currencyId': '11',
            # 'type': 1,
            # 'endTime': self.milliseconds(),
            # 'pageNum': 1,
        }
        if code is not None:
            request['currencyName'] = currency['id']
        if since is not None:
            request['startTime'] = since
        if limit is not None:
            request['pageSize'] = limit
        response = self.contractV2PrivateGetFundGetBill(self.extend(request, params))
        #
        #     {
        #         "code": 10000,
        #         "data": {
        #             "list": [
        #                 {
        #                     "type": 3,
        #                     "changeAmount": "0.00434664",
        #                     "isIn": 0,
        #                     "beforeAmount": "30.53353135",
        #                     "beforeFreezeAmount": "21.547",
        #                     "createTime": "1646121604997",
        #                     "available": "30.52918471",
        #                     "unit": "usdt",
        #                     "symbol": "BTC_USDT"
        #                 },
        #             ],
        #             "pageNum": 1,
        #             "pageSize": 10
        #         },
        #         "desc": "操作成功"
        #     }
        #
        data = self.safe_value(response, 'data', {})
        list = self.safe_value(data, 'list', [])
        return self.parse_ledger(list, currency, since, limit)

    def transfer(self, code, amount, fromAccount, toAccount, params={}):
        """
        transfer currency internally between wallets on the same account
        :param str code: unified currency code
        :param float amount: amount to transfer
        :param str fromAccount: account to transfer from
        :param str toAccount: account to transfer to
        :param dict params: extra parameters specific to the zb api endpoint
        :param str params['marginMode']: 'cross' or 'isolated'
        :returns dict: a `transfer structure <https://docs.ccxt.com/en/latest/manual.html#transfer-structure>`
        """
        self.load_markets()
        marketType, marketTypeQuery = self.handle_market_type_and_params('transfer', None, params)
        marginMode, query = self.handle_margin_mode_and_params('transfer', marketTypeQuery)
        currency = self.currency(code)
        swap = (marketType == 'swap')
        amountToPrecision = self.currency_to_precision(code, amount)
        request = {
            'amount': amountToPrecision,  # Swap, Cross Margin, Isolated Margin
            # 'coin': currency['id'],  # Margin
            # 'currencyName': currency['id'],  # Swap
            # 'clientId': self.safe_string(params, 'clientId'),  # Swap "2sdfsdfsdf232342"
            # 'side': side,  # Swap, 1：Deposit(zb account -> futures account)，0：Withdrawal(futures account -> zb account)
            # 'marketName': self.safe_string(params, 'marketName'),  # Isolated Margin
        }
        method = None
        side = None
        if swap:
            method = 'contractV2PrivatePostFundTransferFund'
            if fromAccount == 'spot' or toAccount == 'future':
                side = 1
            else:
                side = 0
            request['currencyName'] = currency['id']
            request['clientId'] = self.safe_string(params, 'clientId')
            request['side'] = side
        else:
            if (marginMode == 'isolated') or (toAccount == 'isolated') or (fromAccount == 'isolated'):
                if fromAccount == 'spot' or toAccount == 'isolated':
                    method = 'spotV1PrivateGetTransferInLever'
                else:
                    method = 'spotV1PrivateGetTransferOutLever'
                symbol = self.safe_string_2(params, 'marketName', 'symbol')
                if symbol is None:
                    raise ArgumentsRequired(self.id + ' transfer() requires a symbol argument for isolated margin')
                market = self.market(symbol)
                request['marketName'] = self.safe_symbol(market['id'], market, '_')
            elif (marginMode == 'cross') or (toAccount == 'cross') or (fromAccount == 'cross'):
                if fromAccount == 'spot' or toAccount == 'cross':
                    method = 'spotV1PrivateGetTransferInCross'
                else:
                    method = 'spotV1PrivateGetTransferOutCross'
            request['coin'] = currency['id']
        response = getattr(self, method)(self.extend(request, query))
        #
        # Swap
        #
        #     {
        #         "code": 10000,
        #         "data": "2sdfsdfsdf232342",
        #         "desc": "Success"
        #     }
        #
        # Margin
        #
        #     {
        #         "code": 1000,
        #         "message": "Success"
        #     }
        #
        return self.extend(self.parse_transfer(response, currency), {
            'amount': self.parse_number(amountToPrecision),
            'fromAccount': fromAccount,
            'toAccount': toAccount,
        })

    def parse_transfer(self, transfer, currency=None):
        # response samples in 'transfer'
        timestamp = self.milliseconds()
        return {
            'id': self.safe_string(transfer, 'data'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'currency': self.safe_currency_code(None, 'currency'),
            'amount': None,
            'fromAccount': None,
            'toAccount': None,
            'status': None,
        }

    def modify_margin_helper(self, symbol, amount, type, params={}):
        if params['positionsId'] is None:
            raise ArgumentsRequired(self.id + ' modifyMarginHelper() requires a positionsId argument in the params')
        self.load_markets()
        market = self.market(symbol)
        amount = self.amount_to_precision(symbol, amount)
        position = self.safe_string(params, 'positionsId')
        request = {
            'positionsId': position,
            'amount': amount,
            'type': type,  # 1 increase, 0 reduce
            'futuresAccountType': 1,  # 1: USDT Perpetual Futures
        }
        response = self.contractV2PrivatePostPositionsUpdateMargin(self.extend(request, params))
        #
        #     {
        #         "code": 10000,
        #         "data": {
        #             "amount": "0.002",
        #             "appendAmount": "0",
        #             "avgPrice": "43927.23",
        #             "bankruptcyPrice": "41730.86",
        #             "createTime": "1646208695609",
        #             "freezeAmount": "0",
        #             "id": "6900781818669377576",
        #             "keyMark": "6896693805014120448-100-1-",
        #             "lastAppendAmount": "0",
        #             "lastTime": "1646209235505",
        #             "leverage": 20,
        #             "liquidateLevel": 1,
        #             "liquidatePrice": "41898.46",
        #             "maintainMargin": "0",
        #             "margin": "4.392723",
        #             "marginAppendCount": 0,
        #             "marginBalance": "0",
        #             "marginMode": 1,
        #             "marginRate": "0",
        #             "marketId": "100",
        #             "marketName": "BTC_USDT",
        #             "modifyTime": "1646209235505",
        #             "nominalValue": "87.88828",
        #             "originAppendAmount": "0",
        #             "originId": "6904699716827818029",
        #             "positionsMode": 2,
        #             "sellerCurrencyId": "1",
        #             "side": 1,
        #             "status": 1,
        #             "unrealizedPnl": "0.03382",
        #             "usable": True,
        #             "userId": "6896693805014120448"
        #         },
        #         "desc":"操作成功"
        #     }
        #
        return self.extend(self.parse_margin_modification(response, market), {
            'amount': self.parse_number(amount),
        })

    def parse_margin_modification(self, data, market=None):
        innerData = self.safe_value(data, 'data', {})
        sideRaw = self.safe_integer(innerData, 'side')
        side = 'add' if (sideRaw == 1) else 'reduce'
        statusCode = self.safe_integer(innerData, 'status')
        status = 'ok' if (statusCode == 1) else 'failed'
        return {
            'info': data,
            'type': side,
            'amount': None,
            'code': market['quote'],
            'symbol': market['symbol'],
            'status': status,
        }

    def add_margin(self, symbol, amount, params={}):
        """
        add margin
        :param str symbol: unified market symbol
        :param float amount: amount of margin to add
        :param dict params: extra parameters specific to the zb api endpoint
        :returns dict: a `margin structure <https://docs.ccxt.com/en/latest/manual.html#add-margin-structure>`
        """
        if params['positionsId'] is None:
            raise ArgumentsRequired(self.id + ' addMargin() requires a positionsId argument in the params')
        return self.modify_margin_helper(symbol, amount, 1, params)

    def reduce_margin(self, symbol, amount, params={}):
        """
        remove margin from a position
        :param str symbol: unified market symbol
        :param float amount: the amount of margin to remove
        :param dict params: extra parameters specific to the zb api endpoint
        :returns dict: a `margin structure <https://docs.ccxt.com/en/latest/manual.html#reduce-margin-structure>`
        """
        if params['positionsId'] is None:
            raise ArgumentsRequired(self.id + ' reduceMargin() requires a positionsId argument in the params')
        return self.modify_margin_helper(symbol, amount, 0, params)

    def fetch_borrow_rate(self, code, params={}):
        """
        fetch the rate of interest to borrow a currency for margin trading
        :param str code: unified currency code
        :param dict params: extra parameters specific to the zb api endpoint
        :returns dict: a `borrow rate structure <https://docs.ccxt.com/en/latest/manual.html#borrow-rate-structure>`
        """
        self.load_markets()
        currency = self.currency(code)
        request = {
            'coin': currency['id'],
        }
        response = self.spotV1PrivateGetGetLoans(self.extend(request, params))
        #
        #     {
        #         code: '1000',
        #         message: '操作成功',
        #         result: [
        #             {
        #                 interestRateOfDay: '0.0005',
        #                 repaymentDay: '30',
        #                 amount: '148804.4841',
        #                 balance: '148804.4841',
        #                 rateOfDayShow: '0.05 %',
        #                 coinName: 'USDT',
        #                 lowestAmount: '0.01'
        #             },
        #         ]
        #     }
        #
        timestamp = self.milliseconds()
        data = self.safe_value(response, 'result', [])
        rate = self.safe_value(data, 0, {})
        return {
            'currency': self.safe_currency_code(self.safe_string(rate, 'coinName')),
            'rate': self.safe_number(rate, 'interestRateOfDay'),
            'period': self.safe_number(rate, 'repaymentDay'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'info': rate,
        }

    def fetch_borrow_rates(self, params={}):
        """
        fetch the borrow interest rates of all currencies
        :param dict params: extra parameters specific to the zb api endpoint
        :returns dict: a list of `borrow rate structures <https://docs.ccxt.com/en/latest/manual.html#borrow-rate-structure>`
        """
        if params['coin'] is None:
            raise ArgumentsRequired(self.id + ' fetchBorrowRates() requires a coin argument in the params')
        self.load_markets()
        currency = self.currency(self.safe_string(params, 'coin'))
        request = {
            'coin': currency['id'],
        }
        response = self.spotV1PrivateGetGetLoans(self.extend(request, params))
        #
        #     {
        #         code: '1000',
        #         message: '操作成功',
        #         result: [
        #             {
        #                 interestRateOfDay: '0.0005',
        #                 repaymentDay: '30',
        #                 amount: '148804.4841',
        #                 balance: '148804.4841',
        #                 rateOfDayShow: '0.05 %',
        #                 coinName: 'USDT',
        #                 lowestAmount: '0.01'
        #             },
        #         ]
        #     }
        #
        timestamp = self.milliseconds()
        data = self.safe_value(response, 'result', [])
        rates = []
        for i in range(0, len(data)):
            entry = data[i]
            rates.append({
                'currency': self.safe_currency_code(self.safe_string(entry, 'coinName')),
                'rate': self.safe_number(entry, 'interestRateOfDay'),
                'period': self.safe_number(entry, 'repaymentDay'),
                'timestamp': timestamp,
                'datetime': self.iso8601(timestamp),
                'info': entry,
            })
        return rates

    def set_position_mode(self, hedged, symbol=None, params={}):
        """
        set the level of leverage for a market
        :param float leverage: the rate of leverage
        :param str symbol: unified market symbol
        :param dict params: extra parameters specific to the zb api endpoint
        :returns dict: response from the exchange
        """
        self.load_markets()
        if symbol is None:
            raise ArgumentsRequired(self.id + ' setPositionMode() requires a symbol argument')
        market = self.market(symbol)
        accountType = None
        if not market['swap']:
            raise BadSymbol(self.id + ' setPositionMode() supports swap contracts only')
        else:
            accountType = 1
        request = {
            'marketId': market['id'],
            'positionMode': 2 if hedged else 1,
            'futuresAccountType': accountType,  # 1: USDT perpetual swaps, 2: QC perpetual futures
        }
        response = self.contractV2PrivatePostSettingSetPositionsMode(self.extend(request, params))
        #
        #     {
        #         "code": 10000,
        #         "desc": "success",
        #         "data": {
        #             "userId": 111,
        #             "marketId": 100,
        #             "leverage": 20,
        #             "marginMode": 1,
        #             "positionsMode": 2,
        #             "enableAutoAppend": 1,
        #             "maxAppendAmount": "11212",
        #             "marginCoins": "qc,usdt,eth",
        #             "id": 6737268451833817088,
        #             "createTime": 1606289971312,
        #             "modifyTime": 0,
        #             "extend": null
        #         }
        #     }
        #
        return response

    def borrow_margin(self, code, amount, symbol=None, params={}):
        """
        create a loan to borrow margin
        :param str code: unified currency code of the currency to borrow
        :param float amount: the amount to borrow
        :param str|None symbol: unified market symbol, required for isolated margin
        :param dict params: extra parameters specific to the zb api endpoint
        :param str params['safePwd']: transaction password, extra parameter required for cross margin
        :param str params['marginMode']: 'cross' or 'isolated'
        :returns dict: a `margin loan structure <https://docs.ccxt.com/en/latest/manual.html#margin-loan-structure>`
        """
        self.load_markets()
        market = None
        if symbol is not None:
            market = self.market(symbol)
            symbol = market['symbol']
        marginMode = None
        marginMode, params = self.handle_margin_mode_and_params('borrowMargin', params)
        if marginMode is None:
            if symbol is not None:
                marginMode = 'isolated'  # default to isolated if the symbol is defined
            else:
                marginMode = 'cross'  # default to cross
        password = self.safe_string(params, 'safePwd', self.password)
        currency = self.currency(code)
        request = {
            'coin': currency['id'],
            'amount': self.currency_to_precision(code, amount),
            'safePwd': password,  # transaction password
        }
        method = None
        if marginMode == 'isolated':
            if symbol is None:
                raise ArgumentsRequired(self.id + ' borrowMargin() requires a symbol argument for isolated margin')
            market = self.market(symbol)
            request['marketName'] = self.safe_symbol(market['id'], market, '_')
            method = 'spotV1PrivateGetBorrow'
        elif marginMode == 'cross':
            method = 'spotV1PrivateGetDoCrossLoan'
        response = getattr(self, method)(self.extend(request, params))
        #
        #     {
        #         "code": 1000,
        #         "message": "操作成功"
        #     }
        #
        transaction = self.parse_margin_loan(response, currency)
        return self.extend(transaction, {
            'amount': amount,
            'symbol': symbol,
        })

    def parse_margin_loan(self, info, currency=None):
        #
        #     {
        #         "code": 1000,
        #         "message": "操作成功"
        #     }
        #
        return {
            'id': None,
            'currency': self.safe_currency_code(None, currency),
            'amount': None,
            'symbol': None,
            'timestamp': None,
            'datetime': None,
            'info': info,
        }

    def nonce(self):
        return self.milliseconds()

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        section, version, access = api
        url = self.implode_hostname(self.urls['api'][section][version][access])
        if access == 'public':
            if path == 'getFeeInfo':
                url = self.implode_hostname(self.urls['api'][section][version]['private']) + '/' + path
            else:
                url += '/' + version + '/' + path
            if params:
                url += '?' + self.urlencode(params)
        elif section == 'contract':
            timestamp = self.milliseconds()
            iso8601 = self.iso8601(timestamp)
            signedString = iso8601 + method + '/Server/api/' + version + '/' + path
            params = self.keysort(params)
            headers = {
                'ZB-APIKEY': self.apiKey,
                'ZB-TIMESTAMP': iso8601,
                # 'ZB-LAN': 'cn',  # cn, en, kr
            }
            url += '/' + version + '/' + path
            if method == 'POST':
                headers['Content-Type'] = 'application/json'
                body = self.json(params)
                signedString += self.urlencode(params)
            else:
                if params:
                    query = self.urlencode(params)
                    url += '?' + query
                    signedString += query
            secret = self.hash(self.encode(self.secret), 'sha1')
            signature = self.hmac(self.encode(signedString), self.encode(secret), hashlib.sha256, 'base64')
            headers['ZB-SIGN'] = signature
        else:
            query = self.keysort(self.extend({
                'method': path,
                'accesskey': self.apiKey,
            }, params))
            nonce = self.nonce()
            query = self.keysort(query)
            auth = self.rawencode(query)
            secret = self.hash(self.encode(self.secret), 'sha1')
            signature = self.hmac(self.encode(auth), self.encode(secret), hashlib.md5)
            suffix = 'sign=' + signature + '&reqTime=' + str(nonce)
            url += '/' + path + '?' + auth + '&' + suffix
        return {'url': url, 'method': method, 'body': body, 'headers': headers}

    def handle_errors(self, httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody):
        if response is None:
            return  # fallback to default error handler
        if body[0] == '{':
            feedback = self.id + ' ' + body
            self.throw_broadly_matched_exception(self.exceptions['broad'], body, feedback)
            if 'code' in response:
                code = self.safe_string(response, 'code')
                self.throw_exactly_matched_exception(self.exceptions['exact'], code, feedback)
                if (code != '1000') and (code != '10000'):
                    raise ExchangeError(feedback)
            # special case for {"result":false,"message":"服务端忙碌"}(a "Busy Server" reply)
            result = self.safe_value(response, 'result')
            if result is not None:
                if not result:
                    message = self.safe_string(response, 'message')
                    if message == u'服务端忙碌':
                        raise ExchangeNotAvailable(feedback)
                    else:
                        raise ExchangeError(feedback)
