text: # -*- coding: utf-8 -*-
  
  # PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
  # https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
  
  from ccxt.base.exchange import Exchange
  import hashlib
  from ccxt.base.errors import ExchangeError
  from ccxt.base.errors import AuthenticationError
  from ccxt.base.errors import PermissionDenied
  from ccxt.base.errors import ArgumentsRequired
  from ccxt.base.errors import BadRequest
  from ccxt.base.errors import BadSymbol
  from ccxt.base.errors import BadResponse
  from ccxt.base.errors import NullResponse
  from ccxt.base.errors import InsufficientFunds
  from ccxt.base.errors import InvalidAddress
  from ccxt.base.errors import InvalidOrder
  from ccxt.base.errors import OrderNotFound
  from ccxt.base.errors import NotSupported
  from ccxt.base.errors import RateLimitExceeded
  from ccxt.base.errors import ExchangeNotAvailable
  from ccxt.base.errors import RequestTimeout
  from ccxt.base.decimal_to_precision import TRUNCATE
  from ccxt.base.decimal_to_precision import TICK_SIZE
  
  
  class mexo(Exchange):
  
      def describe(self):
          return self.deep_extend(super(mexo, self).describe(), {
              'id': 'mexo',
              'name': 'MEXO',
              'countries': ['MX'],
              'rateLimit': 2000,
              'version': 'v1',
              'has': {
                  'cancelOrder': True,
                  'CORS': False,
                  'createOrder': True,
                  'fetchAccounts': True,
                  'fetchBalance': True,
                  'fetchBidAsk': True,
                  'fetchBidsAsks': True,
                  'fetchClosedOrders': True,
                  'fetchCurrencies': False,
                  'fetchDepositAddress': False,
                  'fetchDeposits': True,
                  'fetchLedger': True,
                  'fetchMarkets': True,
                  'fetchMyTrades': True,
                  'fetchOHLCV': True,
                  'fetchOpenOrders': True,
                  'fetchOrder': True,
                  'fetchOrderBook': True,
                  'fetchOrders': False,
                  'fetchTicker': True,
                  'fetchTickers': True,
                  'fetchTime': True,
                  'fetchTrades': True,
                  'fetchTradingLimits': True,
                  'fetchWithdrawals': True,
                  'withdraw': True,
              },
              'timeframes': {
                  '1m': '1m',
                  '3m': '3m',
                  '5m': '5m',
                  '15m': '15m',
                  '30m': '30m',
                  '1h': '1h',
                  '2h': '2h',
                  '4h': '4h',
                  '6h': '6h',
                  '8h': '8h',
                  '12h': '12h',
                  '1d': '1d',
                  '3d': '3d',
                  '1w': '1w',
                  '1M': '1M',
              },
              'urls': {
                  'logo': 'https://github.com/mexo-tech/Mexo-OpenApi/blob/master/mexo.png',  # Exchange logo
                  'api': {
                      'quote': 'https://api.mexo.io/openapi/quote',  # Markets API endpoint
                      'contract': 'https://api.mexo.io/openapi/contract',  # Contract API endpoint
                      'option': 'https://api.mexo.io/openapi/option',  # Option API endpoint
                      'public': 'https://api.mexo.io/openapi',  # Publick API endpoint
                      'private': 'https://api.mexo.io/openapi',  # Privacy API endpoint
                      'zendesk': 'https://mexo.zendesk.com/hc/en-us',
                  },
                  'www': 'https://www.mexo.io',  # Homepage
                  'referral': 'https://www.mxo.io/register/dKgecw',  # Invitation link
                  'doc': 'https://github.com/mexo-tech/Mexo-OpenApi',  # API Doc
                  'fees': 'https://mexo.zendesk.com/hc/en-us/articles/360037484011-Fee-Schedule',  # Fees rate introduction
              },
              'api': {
                  'public': {
                      'get': [
                          'ping',
                          'time',
                          'brokerInfo',  # Inquire trading rules and symbol inforamtiion of current broker
                          'getOptions',
                      ],
                  },
                  'quote': {
                      'get': [
                          'depth',  # Obtain trading depth
                          'depth/merged',
                          'trades',  # Obtain the latest transactions
                          'klines',  # Obtain K line inforamtion
                          'ticker/24hr',  # Obtain 24h price change data
                          'ticker/price',
                          'ticker/bookTicker',
                          'contract/index',  # Obtain contract underline index price
                          'contract/depth',  # Obtain contract depth
                          'contract/depth/merged',
                          'contract/trades',  # Obtain the latest contract transactions
                          'contract/klines',  # Obtain contract K line inforamtio
                          'contract/ticker/24hr',
                          'option/index',
                          'option/depth',
                          'option/depth/merged',
                          'option/trades',
                          'option/klines',
                          'option/ticker/24hr',
                      ],
                  },
                  'contract': {
                      'get': [
                          # public
                          'insurance',
                          'fundingRate',  # Obtain funding rate
                          # private
                          'openOrders',  # Inquire contract open orders
                          'historyOrders',  # Inquire contract history orders
                          'getOrder',  # Inquire contract order details
                          'myTrades',  # Inquire contract history trades
                          'positions',  # Inquire contract positions
                          'account',  # Inquire contract acccount details
                      ],
                      'post': [
                          'order',  # Create contract order
                          'modifyMargin',  # Modify Margin
                      ],
                      'delete': [
                          'order/cancel',  # Cancel contract order
                          'order/batchCancel',
                      ],
                  },
                  'option': {
                      'get': [
                          'openOrders',
                          'positions',
                          'historyOrders',
                          # 'getOrder',
                          'myTrades',
                          'settlements',
                          'account',
                      ],
                      'post': [
                          'order',
                      ],
                      'delete': [
                          'order/cancel',
                      ],
                  },
                  'private': {
                      'get': [
                          'order',  # Inquire orders details
                          'openOrders',  # Inquire current open orders
                          'historyOrders',  # Inquire history orders
                          'account',  # Obtain current account details
                          'myTrades',  # Inquire history trades
                          'depositOrders',
                          'withdrawalOrders',
                          'withdraw/detail',
                          'balance_flow',
                      ],
                      'post': [
                          'order',  # Create new order
                          'order/test',
                          'userDataStream',
                          'subAccount/query',
                          'transfer',
                          'user/transfer',
                          'withdraw',
                      ],
                      'put': [
                          'userDataStream',
                      ],
                      'delete': [
                          'order',  # Cancel order
                          'userDataStream',
                      ],
                  },
              },
              'precisionMode': TICK_SIZE,
              'fees': {
                  'trading': {
                      'tierBased': False,
                      'percentage': True,
                      'maker': 0.001,
                      'taker': 0.001,
                  },
              },
              'exceptions': {
                  'exact': {
                      # general server or network errors
                      '-1000': ExchangeError,  # An unknown error occured while processing the request
                      '-1001': ExchangeError,  # Internal error, unable to process your request. Please try again
                      '-1002': AuthenticationError,
                      # You are not authorized to execute self request. Request need API Key included in. We suggest that API Key be included in any request
                      '-1003': RateLimitExceeded,  # Too many requests, please use the websocket for live updates
                      '-1004': BadRequest,
                      '-1005': PermissionDenied,
                      '-1006': BadResponse,
                      # An unexpected response was received from the message bus. Execution status unknown. OPEN API server find some exception in execute request.Please report to Customer service
                      '-1007': RequestTimeout,
                      # Timeout waiting for response from backend server. Send status unknown, execution status unknown
                      '-1014': InvalidOrder,  # Unsupported order combination
                      '-1015': RateLimitExceeded,  # Reach the rate limit.Please slow down your request speed
                      '-1016': ExchangeNotAvailable,  # This service is no longer available
                      '-1020': NotSupported,  # This operation is not supported
                      '-1021': BadRequest,  # Timestamp for self request is outside of the recvWindow
                      '-1022': AuthenticationError,  # Signature for self request is not valid
                      # request issues
                      '-1100': BadRequest,  # Illegal characters found in a parameter
                      '-1101': BadRequest,  # Too many parameters sent for self endpoint
                      '-1102': BadRequest,  # A mandatory parameter was not sent, was empty/null, or malformed
                      '-1103': BadRequest,  # An unknown parameter was sent
                      '-1104': BadRequest,  # Not all sent parameters were read
                      '-1105': BadRequest,  # A parameter was empty
                      '-1106': BadRequest,  # A parameter was sent when not required
                      '-1111': BadRequest,  # Precision is over the maximum defined for self asset
                      '-1112': NullResponse,  # No orders on book for symbol
                      '-1114': InvalidOrder,  # TimeInForce parameter sent when not required
                      '-1115': InvalidOrder,  # Invalid timeInForce
                      '-1116': InvalidOrder,  # Invalid orderType
                      '-1117': InvalidOrder,  # Invalid side
                      '-1118': InvalidOrder,  # New client order ID was empty
                      '-1119': InvalidOrder,  # Original client order ID was empty
                      '-1120': BadRequest,  # Invalid interval
                      '-1121': BadSymbol,  # Invalid symbol
                      '-1125': AuthenticationError,  # This listenKey does not exist
                      '-1127': BadRequest,  # Lookup interval is too big
                      '-1128': BadRequest,  # Combination of optional parameters invalid
                      '-1130': BadRequest,  # Invalid data sent for a parameter
                      '-1131': InsufficientFunds,
                      '-1132': InvalidOrder,  # Order price too high
                      '-1133': InvalidOrder,  # Order price lower than the minimum,please check general broker info
                      '-1134': InvalidOrder,  # Order price decimal too long,please check general broker info
                      '-1135': InvalidOrder,  # Order quantity too large
                      '-1136': InvalidOrder,  # Order quantity lower than the minimum
                      '-1137': InvalidOrder,  # Order quantity decimal too long
                      '-1138': InvalidOrder,  # Order price exceeds permissible range
                      '-1139': InvalidOrder,  # Order has been filled
                      '-1140': InvalidOrder,  # Transaction amount lower than the minimum
                      '-1141': InvalidOrder,  # Duplicate clientOrderId
                      '-1142': InvalidOrder,  # Order has been canceled
                      '-1143': OrderNotFound,  # Cannot be found on order book
                      '-1144': InvalidOrder,  # Order has been locked
                      '-1145': InvalidOrder,  # This order type does not support cancellation
                      '-1146': RequestTimeout,  # Order creation timeout
                      '-1147': RequestTimeout,  # Order cancellation timeout
                      '-1149': InvalidOrder,  # Create order failed
                      '-1187': InvalidAddress,  # Withdrawal address not in whitelist
                      '-2010': InvalidOrder,  # NEW_ORDER_REJECTED
                      '-2011': InvalidOrder,  # CANCEL_REJECTED
                      '-2013': OrderNotFound,  # Order does not exist
                      '-2014': AuthenticationError,  # API-key format invalid
                      '-2015': AuthenticationError,  # Invalid API-key, IP, or permissions for action
                      '-2016': ExchangeError,  # No trading window could be found for the symbol. Try ticker/24hrs instead
                  },
              },
              # exchange-specific options
              'options': {
                  'fetchTickers': {
                      'method': 'quoteGetTicker24hr',
                  },
              },
          })
  
      def fetch_time(self, params={}):
          response = self.publicGetTime(params)
          #
          #     {
          #         "serverTime": 1527777538000
          #     }
          #
          return self.safe_integer(response, 'serverTime')
  
      def parse_market(self, market, type='spot'):
          filters = self.safe_value(market, 'filters', [])
          id = self.safe_string(market, 'symbol')
          baseId = self.safe_string(market, 'baseAsset')
          quoteId = self.safe_string(market, 'quoteAsset')
          base = self.safe_currency_code(baseId)
          quote = self.safe_currency_code(quoteId)
          symbol = base + '/' + quote
          spot = True
          future = False
          option = False
          inverse = False
          if type == 'future':
              symbol = id
              spot = False
              future = True
              inverse = self.safe_value(market, 'inverse', False)
              baseId = self.safe_string(market, 'underlying')
              base = self.safe_currency_code(baseId)
          elif type == 'option':
              symbol = id
              spot = False
              option = True
          amountMin = None
          amountMax = None
          priceMin = None
          priceMax = None
          costMin = None
          for j in range(0, len(filters)):
              filter = filters[j]
              filterType = self.safe_string(filter, 'filterType')
              if filterType == 'LOT_SIZE':
                  amountMin = self.safe_float(filter, 'minQty')
                  amountMax = self.safe_float(filter, 'maxQty')
              if filterType == 'PRICE_FILTER':
                  priceMin = self.safe_float(filter, 'minPrice')
                  priceMax = self.safe_float(filter, 'maxPrice')
              if filterType == 'MIN_NOTIONAL':
                  costMin = self.safe_float(filter, 'minNotional')
          if (costMin is None) and (amountMin is not None) and (priceMin is not None):
              costMin = amountMin * priceMin
          precision = {
              'price': self.safe_float_2(market, 'quotePrecision', 'quoteAssetPrecision'),
              'amount': self.safe_float(market, 'baseAssetPrecision'),
          }
          limits = {
              'amount': {
                  'min': amountMin,
                  'max': amountMax,
              },
              'price': {
                  'min': priceMin,
                  'max': priceMax,
              },
              'cost': {
                  'min': costMin,
                  'max': None,
              },
          }
          return {
              'id': id,
              'symbol': symbol,
              'base': base,
              'quote': quote,
              'baseId': baseId,
              'quoteId': quoteId,
              'active': True,
              'type': type,
              'spot': spot,
              'future': future,
              'option': option,
              'inverse': inverse,
              'precision': precision,
              'limits': limits,
              'info': market,
          }
  
      def fetch_markets(self, params={}):
          response = self.publicGetBrokerInfo(params)
          #
          #     {
          #         "timezone":"UTC",
          #         "serverTime":"1588015885118",
          #         "brokerFilters":[],
          #         "symbols":[
          #             {
          #                 "filters":[
          #                     {"minPrice":"0.01","maxPrice":"100000.00000000","tickSize":"0.01","filterType":"PRICE_FILTER"},
          #                     {"minQty":"0.0005","maxQty":"100000.00000000","stepSize":"0.000001","filterType":"LOT_SIZE"},
          #                     {"minNotional":"5","filterType":"MIN_NOTIONAL"}
          #                 ],
          #                 "exchangeId":"301",
          #                 "symbol":"BTCUSDT",
          #                 "symbolName":"BTCUSDT",
          #                 "status":"TRADING",
          #                 "baseAsset":"BTC",
          #                 "baseAssetPrecision":"0.000001",
          #                 "quoteAsset":"USDT",
          #                 "quotePrecision":"0.01",
          #                 "icebergAllowed":false
          #             },
          #         ],
          #         "options":[
          #             {
          #                 "filters":[
          #                     {"minPrice":"0.01","maxPrice":"100000.00000000","tickSize":"0.01","filterType":"PRICE_FILTER"},
          #                     {"minQty":"0.01","maxQty":"100000.00000000","stepSize":"0.001","filterType":"LOT_SIZE"},
          #                     {"minNotional":"1","filterType":"MIN_NOTIONAL"}
          #                 ],
          #                 "exchangeId":"301",
          #                 "symbol":"BTC0501CS8500",
          #                 "symbolName":"BTC0501CS8500",
          #                 "status":"TRADING",
          #                 "baseAsset":"BTC0501CS8500",
          #                 "baseAssetPrecision":"0.001",
          #                 "quoteAsset":"BUSDT",
          #                 "quotePrecision":"0.01",
          #                 "icebergAllowed":false
          #             },
          #         ],
          #         "contracts":[
          #             {
          #                 "filters":[
          #                     {"minPrice":"0.1","maxPrice":"100000.00000000","tickSize":"0.1","filterType":"PRICE_FILTER"},
          #                     {"minQty":"1","maxQty":"100000.00000000","stepSize":"1","filterType":"LOT_SIZE"},
          #                     {"minNotional":"0.000001","filterType":"MIN_NOTIONAL"}
          #                 ],
          #                 "exchangeId":"301",
          #                 "symbol":"BTC-PERP-REV",
          #                 "symbolName":"BTC-PERP-REV",
          #                 "status":"TRADING",
          #                 "baseAsset":"BTC-PERP-REV",
          #                 "baseAssetPrecision":"1",
          #                 "quoteAsset":"USDT",
          #                 "quoteAssetPrecision":"0.1",
          #                 "icebergAllowed":false,
          #                 "inverse":true,
          #                 "index":"BTCUSDT",
          #                 "marginToken":"TBTC",
          #                 "marginPrecision":"0.00000001",
          #                 "contractMultiplier":"1.0",
          #                 "underlying":"TBTC",
          #                 "riskLimits":[
          #                     {"riskLimitId":"200000001","quantity":"1000000.0","initialMargin":"0.01","maintMargin":"0.005"},
          #                     {"riskLimitId":"200000002","quantity":"2000000.0","initialMargin":"0.02","maintMargin":"0.01"},
          #                     {"riskLimitId":"200000003","quantity":"3000000.0","initialMargin":"0.03","maintMargin":"0.015"},
          #                     {"riskLimitId":"200000004","quantity":"4000000.0","initialMargin":"0.04","maintMargin":"0.02"}
          #                 ]
          #             },
          #             {
          #                 "filters":[
          #                     {"minPrice":"0.1","maxPrice":"100000.00000000","tickSize":"0.1","filterType":"PRICE_FILTER"},
          #                     {"minQty":"1","maxQty":"100000.00000000","stepSize":"1","filterType":"LOT_SIZE"},
          #                     {"minNotional":"0.000001","filterType":"MIN_NOTIONAL"}
          #                 ],
          #                 "exchangeId":"301",
          #                 "symbol":"BTC-SWAP",
          #                 "symbolName":"BTC-SWAP",
          #                 "status":"TRADING",
          #                 "baseAsset":"BTC-SWAP",
          #                 "baseAssetPrecision":"1",
          #                 "quoteAsset":"USDT",
          #                 "quoteAssetPrecision":"0.1",
          #                 "icebergAllowed":false,
          #                 "inverse":true,
          #                 "index":"BTCUSDT",
          #                 "marginToken":"BTC",
          #                 "marginPrecision":"0.00000001",
          #                 "contractMultiplier":"1.0",
          #                 "underlying":"BTC",
          #                 "riskLimits":[
          #                     {"riskLimitId":"500000001","quantity":"1000000.0","initialMargin":"0.01","maintMargin":"0.005"},
          #                     {"riskLimitId":"500000002","quantity":"2000000.0","initialMargin":"0.02","maintMargin":"0.01"},
          #                     {"riskLimitId":"500000003","quantity":"3000000.0","initialMargin":"0.03","maintMargin":"0.015"},
          #                     {"riskLimitId":"500000004","quantity":"4000000.0","initialMargin":"0.04","maintMargin":"0.02"}
          #                 ]
          #             },
          #             {
          #                 "filters":[
          #                     {"minPrice":"0.1","maxPrice":"100000.00000000","tickSize":"0.1","filterType":"PRICE_FILTER"},
          #                     {"minQty":"1","maxQty":"100000.00000000","stepSize":"1","filterType":"LOT_SIZE"},
          #                     {"minNotional":"0.000000001","filterType":"MIN_NOTIONAL"}
          #                 ],
          #                 "exchangeId":"301",
          #                 "symbol":"BTC-PERP-BUSDT",
          #                 "symbolName":"BTC-PERP-BUSDT",
          #                 "status":"TRADING",
          #                 "baseAsset":"BTC-PERP-BUSDT",
          #                 "baseAssetPrecision":"1",
          #                 "quoteAsset":"BUSDT",
          #                 "quoteAssetPrecision":"0.1",
          #                 "icebergAllowed":false,
          #                 "inverse":false,
          #                 "index":"BTCUSDT",
          #                 "marginToken":"BUSDT",
          #                 "marginPrecision":"0.0001",
          #                 "contractMultiplier":"0.0001",
          #                 "underlying":"TBTC",
          #                 "riskLimits":[
          #                     {"riskLimitId":"600000132","quantity":"1000000.0","initialMargin":"0.01","maintMargin":"0.005"},
          #                     {"riskLimitId":"600000133","quantity":"2000000.0","initialMargin":"0.02","maintMargin":"0.01"},
          #                     {"riskLimitId":"600000134","quantity":"3000000.0","initialMargin":"0.03","maintMargin":"0.015"},
          #                     {"riskLimitId":"600000135","quantity":"4000000.0","initialMargin":"0.04","maintMargin":"0.02"}
          #                 ]
          #             },
          #         ]
          #     }
          #
          result = []
          symbols = self.safe_value(response, 'symbols', [])
          for i in range(0, len(symbols)):
              market = self.parse_market(symbols[i], 'spot')
              result.append(market)
          options = self.safe_value(response, 'options', [])
          for i in range(0, len(options)):
              market = self.parse_market(options[i], 'option')
              result.append(market)
          contracts = self.safe_value(response, 'contracts', [])
          for i in range(0, len(contracts)):
              market = self.parse_market(contracts[i], 'future')
              result.append(market)
          return result
  
      def fetch_order_book(self, symbol, limit=None, params={}):
          self.load_markets()
          market = self.market(symbol)
          request = {
              'symbol': market['id'],
          }
          if limit is not None:
              request['limit'] = limit  # default 40, max 40
          response = self.quoteGetDepth(self.extend(request, params))
          #
          #     {
          #         "time":1588068913453,
          #         "bids":[
          #             ["0.025278","0.0202"],
          #             ["0.025277","16.1132"],
          #             ["0.025276","7.9056"],
          #         ]
          #         "asks":[
          #             ["0.025302","5.9999"],
          #             ["0.025303","34.9151"],
          #             ["0.025304","92.391"],
          #         ]
          #     }
          #
          timestamp = self.safe_integer(response, 'time')
          return self.parse_order_book(response, timestamp)
  
      def fetch_ticker(self, symbol, params={}):
          self.load_markets()
          market = self.market(symbol)
          request = {
              'symbol': market['id'],
          }
          response = self.quoteGetTicker24hr(self.extend(request, params))
          #
          #     {
          #         "time":1588069860794,
          #         "symbol":"BNB0501PS16",
          #         "bestBidPrice":"0.2129",
          #         "bestAskPrice":"0.3163",
          #         "volume":"33547",
          #         "quoteVolume":"10801.987",
          #         "lastPrice":"0.2625",
          #         "highPrice":"0.3918",
          #         "lowPrice":"0.2625",
          #         "openPrice":"0.362",
          #     }
          #
          return self.parse_ticker(response, market)
  
      def parse_tickers(self, rawTickers, symbols=None):
          tickers = []
          for i in range(0, len(rawTickers)):
              tickers.append(self.parse_ticker(rawTickers[i]))
          return self.filter_by_array(tickers, 'symbol', symbols)
  
      def fetch_bid_ask(self, symbol, params={}):
          self.load_markets()
          market = self.market(symbol)
          request = {
              'symbol': market['id'],
          }
          response = self.quoteGetTickerBookTicker(self.extend(request, params))
          #
          #     {
          #         "symbol": "LTCBTC",
          #         "bidPrice": "4.00000000",
          #         "bidQty": "431.00000000",
          #         "askPrice": "4.00000200",
          #         "askQty": "9.00000000"
          #     }
          #
          return self.parse_ticker(response, market)
  
      def fetch_bids_asks(self, symbols=None, params={}):
          self.load_markets()
          response = self.quoteGetTickerBookTicker(params)
          #
          #     [
          #         {
          #             "symbol": "LTCBTC",
          #             "bidPrice": "4.00000000",
          #             "bidQty": "431.00000000",
          #             "askPrice": "4.00000200",
          #             "askQty": "9.00000000"
          #         },
          #         {
          #             "symbol": "ETHBTC",
          #             "bidPrice": "0.07946700",
          #             "bidQty": "9.00000000",
          #             "askPrice": "100000.00000000",
          #             "askQty": "1000.00000000"
          #         },
          #     ]
          #
          return self.parse_tickers(response, symbols)
  
      def fetch_tickers(self, symbols=None, params={}):
          self.load_markets()
          options = self.safe_value(self.options, 'fetchTickers', {})
          defaultMethod = self.safe_string(options, 'method', 'quoteGetTicker24hr')
          defaultType = self.safe_string(options, 'type', 'spot')
          type = self.safe_string(params, 'type', defaultType)
          query = self.omit(params, 'type')
          method = defaultMethod
          if type == 'future':
              method = 'quoteGetContractTicker24hr'
          elif type == 'option':
              method = 'quoteGetOptionTicker24hr'
          response = getattr(self, method)(query)
          #
          #     [
          #         {
          #             "time": 1538725500422,
          #             "symbol": "ETHBTC",
          #             "lastPrice": "4.00000200",
          #             "openPrice": "99.00000000",
          #             "highPrice": "100.00000000",
          #             "lowPrice": "0.10000000",
          #             "volume": "8913.30000000"
          #         },
          #     ]
          #
          return self.parse_tickers(response, symbols)
  
      def fetch_balance(self, params={}):
          self.load_markets()
          options = self.safe_value(self.options, 'fetchBalance', {})
          defaultType = self.safe_string(options, 'type', 'spot')
          type = self.safe_string(params, 'type', defaultType)
          query = self.omit(params, 'type')
          method = 'privateGetAccount'
          if type == 'future':
              method = 'contractGetAccount'
          elif type == 'option':
              method = 'optionGetAccount'
          response = getattr(self, method)(query)
          #
          # spot
          #
          #     {
          #         'balances': [
          #             {
          #                 'asset': 'ALGO',
          #                 'free': '0',
          #                 'locked': '0'
          #             },
          #             {
          #                 'asset': 'BHT',
          #                 'free': '0',
          #                 'locked': '0'
          #             }
          #         ]
          #     }
          #
          # contract
          #
          #     {
          #         "BUSDT":{
          #             "total":"1000",
          #             "availableMargin":"1000",
          #             "positionMargin":"0",
          #             "orderMargin":"0",
          #             "tokenId":"BUSDT"
          #         },
          #         "TBTC":{
          #             "total":"0.5",
          #             "availableMargin":"0.5",
          #             "positionMargin":"0",
          #             "orderMargin":"0",
          #             "tokenId":"TBTC"
          #         }
          #     }
          #
          # option
          #
          #     {
          #         "optionAsset":"",
          #         "balances":[
          #             {
          #                 "tokenName":"USDT",
          #                 "free":"0.0",
          #                 "locked":"0.0",
          #                 "margin":"0.0"
          #             },
          #             {
          #                 "tokenName":"BUSDT",
          #                 "free":"0.0",
          #                 "locked":"0.0",
          #                 "margin":"0.0"
          #             }
          #         ]
          #     }
          #
          balances = self.safe_value(response, 'balances')
          result = {'info': response}
          if balances is not None:
              for i in range(0, len(balances)):
                  balance = balances[i]
                  currencyId = self.safe_string_2(balance, 'asset', 'tokenName')
                  code = self.safe_currency_code(currencyId)
                  account = self.account()
                  account['free'] = self.safe_float(balance, 'free')
                  account['used'] = self.safe_float(balance, 'locked')
                  result[code] = account
          else:
              currencyIds = list(response.keys())
              for i in range(0, len(currencyIds)):
                  currencyId = currencyIds[i]
                  code = self.safe_currency_code(currencyId)
                  balance = response[currencyId]
                  account = self.account()
                  account['free'] = self.safe_float(balance, 'availableMargin')
                  account['total'] = self.safe_float(balance, 'total')
                  result[code] = account
          return self.parse_balance(result)
  
      def fetch_trades(self, symbol, since=None, limit=50, params={}):
          self.load_markets()
          market = self.market(symbol)
          request = {
              'symbol': market['id'],
          }
          if limit is not None:
              request['limit'] = limit  # default 500, max 1000
          response = self.quoteGetTrades(self.extend(request, params))
          #
          #     [
          #         {"price":"0.025344","time":1588084082060,"qty":"1","isBuyerMaker":false},
          #         {"price":"0.02535","time":1588084086021,"qty":"0.553","isBuyerMaker":true},
          #         {"price":"0.025348","time":1588084097037,"qty":"1","isBuyerMaker":false},
          #     ]
          #
          return self.parse_trades(response, market, since, limit)
  
      def parse_ohlcv(self, ohlcv, market=None):
          #
          #     [
          #         1587906000000,  # open time
          #         "0.1761",  # open
          #         "0.1761",  # high
          #         "0.1761",  # low
          #         "0.1761",  # close
          #         "0",  # base volume
          #         0,  # close time
          #         "0",  # quote volume
          #         0,  # number of trades
          #         "0",  # taker buy base asset volume
          #         "0"  # taker buy quote asset volume
          #     ]
          #
          return [
              self.safe_integer(ohlcv, 0),
              self.safe_float(ohlcv, 1),
              self.safe_float(ohlcv, 2),
              self.safe_float(ohlcv, 3),
              self.safe_float(ohlcv, 4),
              self.safe_float(ohlcv, 5),
          ]
  
      def fetch_ohlcv(self, symbol, timeframe='1m', since=None, limit=None, params={}):
          self.load_markets()
          market = self.market(symbol)
          request = {
              'symbol': market['id'],
              'interval': self.timeframes[timeframe],
          }
          if since is not None:
              request['startTime'] = since
          if limit is not None:
              request['limit'] = limit  # default 500, max 500
          response = self.quoteGetKlines(self.extend(request, params))
          #
          #     [
          #         [1587906000000,"0.1761","0.1761","0.1761","0.1761","0",0,"0",0,"0","0"],
          #         [1587906180000,"0.1761","0.1761","0.1761","0.1761","0",0,"0",0,"0","0"],
          #         [1587906360000,"0.1761","0.1848","0.1761","0.1848","53",0,"9.7944",1,"0","0"],
          #     ]
          #
          return self.parse_ohlcvs(response, market, timeframe, since, limit)
  
      def fetch_my_trades(self, symbol=None, since=None, limit=None, params={}):
          self.load_markets()
          market = None
          request = {
              # if only fromId is set，it will get orders < that fromId in descending order
              # if only toId is set, it will get orders > that toId in ascending order
              # if fromId is set and toId is set, it will get orders < that fromId and > that toId in descending order
              # if fromId is not set and toId it not set, most recent order are returned in descending order
              # 'fromId': '43287482374',
              # 'toId': '43287482374',
              # 'endTime': self.milliseconds(),  # optional, spot only
          }
          defaultType = self.safe_string(self.options, 'type', 'spot')
          options = self.safe_value(self.options, 'fetchMyTrades', {})
          fetchMyTradesType = self.safe_string(options, 'type', defaultType)
          type = self.safe_string(params, 'type', fetchMyTradesType)
          if symbol is not None:
              market = self.market(symbol)
              request['symbol'] = market['id']
              type = market['type']
          query = self.omit(params, 'type')
          if limit is not None:
              # spot default 500, max 1000
              # futures and options default 20, max 1000
              request['limit'] = limit
          method = 'privateGetMyTrades'
          if type == 'future':
              method = 'contractGetMyTrades'
          else:
              if type == 'option':
                  method = 'optionGetMyTrades'
              else:
                  if symbol is None:
                      raise ArgumentsRequired(
                          self.id + ' fetchMyTrades requires a `symbol` argument for ' + type + ' markets')
                  market = self.market(symbol)
                  request['symbol'] = market['id']
                  # spot only?
                  if since is not None:
                      request['startTime'] = since
          if since is not None:
              request['startTime'] = since
          response = getattr(self, method)(self.extend(request, query))
          #
          # spot
          #
          #     [
          #         {
          #             "id":"616384027512920576",
          #             "symbol":"TBTCBUSDT",
          #             "orderId":"616384027202542080",
          #             "matchOrderId":"605124954767266560",
          #             "price":"6826.06",
          #             "qty":"0.1",
          #             "commission":"0.682606",
          #             "commissionAsset":"BUSDT",
          #             "time":"1588214701982",
          #             "isBuyer":false,
          #             "isMaker":false,
          #             "fee":{
          #                 "feeTokenId":"BUSDT",
          #                 "feeTokenName":"BUSDT",
          #                 "fee":"0.682606"
          #             }
          #         }
          #     ]
          #
          return self.parse_trades(response, market, since, limit)
  
      def create_order(self, symbol, type, side, amount, price=None, params={}):
          self.load_markets()
          market = self.market(symbol)
          orderSide = side.upper()
          orderType = type.upper()
          request = {
              'symbol': market['id'],
              # BUY or SELL for spot and options
              'side': orderSide,
              # GTC, FOK, IOC for spot and options
              # GTC, FOK, IOC, LIMIT_MAKER for futures
              # 'timeInForce': 'GTC',
          }
          query = params
          method = 'privatePostOrder'
          if market['type'] == 'future':
              if (orderSide != 'BUY_OPEN') and (orderSide != 'SELL_OPEN') and (orderSide != 'BUY_CLOSE') and (
                      orderSide != 'SELL_CLOSE'):
                  raise NotSupported(self.id + ' createOrder() does not support order side ' + side + ' for ' + market[
                      'type'] + ' markets, only BUY_OPEN, SELL_OPEN, BUY_CLOSE and SELL_CLOSE are supported')
              if (orderType != 'LIMIT') and (orderType != 'STOP'):
                  raise NotSupported(self.id + ' createOrder() does not support order type ' + type + ' for ' + market[
                      'type'] + ' markets, only LIMIT and STOP are supported')
              clientOrderId = self.safe_value(params, 'clientOrderId')
              if clientOrderId is None:
                  raise ArgumentsRequired(self.id + ' createOrder() requires a clientOrderId parameter for ' + market[
                      'type'] + ' markets, supply clientOrderId in the params argument')
              leverage = self.safe_value(params, 'leverage')
              if leverage is None and (orderSide == 'BUY_OPEN' or orderSide == 'SELL_OPEN'):
                  raise NotSupported(self.id + ' createOrder() requires a leverage parameter for ' + market[
                      'type'] + ' markets if orderSide is BUY_OPEN or SELL_OPEN')
              method = 'contractPostOrder'
              priceType = self.safe_string(params, 'priceType')
              if priceType is None:
                  request['price'] = self.price_to_precision(symbol, price)
              else:
                  request['priceType'] = priceType
                  if priceType == 'INPUT':
                      request['price'] = self.price_to_precision(symbol, price)
              request['orderType'] = type.upper()  # LIMIT, STOP
              request['quantity'] = self.amount_to_precision(symbol, amount)
              # request['leverage'] = 1  # not required for closing orders
              request['leverage'] = leverage
              request['clientOrderId'] = clientOrderId
              # optional
              # request['priceType'] = 'INPUT',  # INPUT, OPPONENT, QUEUE, OVER, MARKET
              # request['triggerPrice'] = 123.45
          else:
              if market['type'] == 'option':
                  method = 'optionPostOrder'
              newClientOrderId = self.safe_value_2(params, 'clientOrderId', 'newClientOrderId')
              if newClientOrderId is not None:
                  request['newClientOrderId'] = newClientOrderId
              request['type'] = orderType
              if type == 'limit':
                  request['price'] = self.price_to_precision(symbol, price)
                  request['quantity'] = self.amount_to_precision(symbol, amount)
              elif type == 'market':
                  # for market buy it requires the amount of quote currency to spend
                  if side == 'buy':
                      createMarketBuyOrderRequiresPrice = self.safe_value(self.options,
                                                                          'createMarketBuyOrderRequiresPrice', True)
                      if createMarketBuyOrderRequiresPrice:
                          if price is not None:
                              amount = amount * price
                          else:
                              raise InvalidOrder(
                                  self.id + " createOrder() requires the price argument with market buy orders to calculate total order cost(amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount, or, alternatively, add .options['createMarketBuyOrderRequiresPrice'] = False and supply the total cost value in the 'amount' argument(the exchange-specific behaviour)")
                      precision = market['precision']['price']
                      request['quantity'] = self.decimal_to_precision(amount, TRUNCATE, precision, self.precisionMode)
                  else:
                      request['quantity'] = self.amount_to_precision(symbol, amount)
          query = self.omit(query, ['clientOrderId', 'newClientOrderId'])
          response = getattr(self, method)(self.extend(request, query))
          #
          # spot
          #
          #     {
          #         "symbol":"TBTCBUSDT",
          #         "orderId":"616376654496877056",
          #         "clientOrderId":"158821382304516955",
          #         "transactTime":"1588213823080",
          #         "price":"0",
          #         "origQty":"1000",
          #         "executedQty":"0",
          #         "status":"NEW",
          #         "timeInForce":"GTC",
          #         "type":"MARKET",
          #         "side":"BUY"
          #     }
          #
          # contract
          #
          #     {
          #         'time': '1570759718825',
          #         'updateTime': '0',
          #         'orderId': '469961015902208000',
          #         'clientOrderId': '6423344174',
          #         'symbol': 'BTC-PERP-REV',
          #         'price': '8200',
          #         'leverage': '12.08',
          #         'origQty': '5',
          #         'executedQty': '0',
          #         'avgPrice': '0',
          #         'marginLocked': '0.00005047',
          #         'orderType': 'LIMIT',
          #         'side': 'BUY_OPEN',
          #         'fees': [],
          #         'timeInForce': 'GTC',
          #         'status': 'NEW',
          #         'priceType': 'INPUT'
          #     }
          #
          return self.parse_order(response, market)
  
      def cancel_order(self, id, symbol=None, params={}):
          self.load_markets()
          clientOrderId = self.safe_value_2(params, 'origClientOrderId', 'clientOrderId')
          request = {}
          defaultType = self.safe_string(self.options, 'type', 'spot')
          options = self.safe_value(self.options, 'cancelOrder', {})
          cancelOrderType = self.safe_string(options, 'type', defaultType)
          type = self.safe_string(params, 'type', cancelOrderType)
          query = self.omit(params, 'type')
          if clientOrderId is not None:
              request['origClientOrderId'] = clientOrderId
              query = self.omit(query, ['origClientOrderId', 'clientOrderId'])
          else:
              request['orderId'] = id
          method = 'privateDeleteOrder'
          orderType = self.safe_string(query, 'orderType')
          if orderType is not None:
              type = 'future'
          if type == 'future':
              method = 'contractDeleteOrderCancel'
              if orderType is None:
                  raise ArgumentsRequired(
                      self.id + " cancelOrder() requires an orderType parameter, pass the {'orderType': 'LIMIT'} or {'orderType': 'STOP'} in params argument")
              request['orderType'] = orderType
          else:
              if type == 'option':
                  method = 'optionDeleteOrderCancel'
          response = getattr(self, method)(self.extend(request, query))
          #
          # spot
          #
          #     {
          #         'exchangeId': '301',
          #         'symbol': 'BHTUSDT',
          #         'clientOrderId': '0',
          #         'orderId': '499890200602846976',
          #         'status': 'CANCELED'
          #     }
          #
          # futures
          #
          #     {
          #         "time":"1588353669383",
          #         "updateTime":"0",
          #         "orderId":"617549770304599296",
          #         "clientOrderId":"test-001",
          #         "symbol":"BTC-PERP-REV",
          #         "price":"10000",
          #         "leverage":"1",
          #         "origQty":"100",
          #         "executedQty":"0",
          #         "avgPrice":"0",
          #         "marginLocked":"0",
          #         "orderType":"LIMIT",
          #         "side":"SELL_OPEN",
          #         "fees":[],
          #         "timeInForce":"GTC",
          #         "status":"CANCELED",
          #         "priceType":"INPUT",
          #     }
          #
          return self.parse_order(response)
  
      def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
          self.load_markets()
          market = None
          request = {
              # if orderId is set, it will get orders < that orderId otherwise most recent orders are returned
              # 'orderId': '43287482374',
          }
          defaultType = self.safe_string(self.options, 'type', 'spot')
          options = self.safe_value(self.options, 'fetchOpenOrders', {})
          fetchOpenOrdersType = self.safe_string(options, 'type', defaultType)
          type = self.safe_string(params, 'type', fetchOpenOrdersType)
          if symbol is not None:
              market = self.market(symbol)
              request['symbol'] = market['id']
              type = market['type']
          query = self.omit(params, 'type')
          if limit is not None:
              request['limit'] = limit  # default 500, max 1000
          method = 'privateGetOpenOrders'
          if type == 'future':
              method = 'contractGetOpenOrders'
          elif type == 'option':
              method = 'optionGetOpenOrders'
          response = getattr(self, method)(self.extend(request, query))
          #
          # spot
          #
          #     [
          #         {
          #             'orderId': '499902955766523648',
          #             'clientOrderId': '157432907618453',
          #             'exchangeId': '301',
          #             'symbol': 'BHTUSDT',
          #             'price': '0.01',
          #             'origQty': '50',
          #             'executedQty': '0',
          #             'cummulativeQuoteQty': '0',
          #             'avgPrice': '0',
          #             'status': 'NEW',
          #             'timeInForce': 'GTC',
          #             'type': 'LIMIT',
          #             'side': 'BUY',
          #             'stopPrice': '0.0',
          #             'icebergQty': '0.0',
          #             'time': '1574329076202',
          #             'updateTime': '0',
          #             'isWorking': True
          #         }
          #     ]
          #
          # futures
          #
          #     [
          #         {
          #             "time":"1588353669383",
          #             "updateTime":"0",
          #             "orderId":"617549770304599296",
          #             "clientOrderId":"test-001",
          #             "symbol":"BTC-PERP-REV",
          #             "price":"10000",
          #             "leverage":"1",
          #             "origQty":"100",
          #             "executedQty":"0",
          #             "avgPrice":"0",
          #             "marginLocked":"0.01",
          #             "orderType":"LIMIT",
          #             "side":"SELL_OPEN",
          #             "fees":[],
          #             "timeInForce":"GTC",
          #             "status":"NEW",
          #             "priceType":"INPUT"
          #         }
          #     ]
          #
          return self.parse_orders(response, market, since, limit)
  
      def fetch_closed_orders(self, symbol=None, since=None, limit=None, params={}):
          self.load_markets()
          market = None
          request = {
              # if orderId is set, it will get orders < that orderId otherwise most recent orders are returned
              # 'orderId': '43287482374',
              # 'endTime': self.milliseconds(),  # optional
          }
          defaultType = self.safe_string(self.options, 'type', 'spot')
          options = self.safe_value(self.options, 'fetchClosedOrders', {})
          fetchClosedOrdersType = self.safe_string(options, 'type', defaultType)
          type = self.safe_string(params, 'type', fetchClosedOrdersType)
          if symbol is not None:
              market = self.market(symbol)
              request['symbol'] = market['id']
              type = market['type']
          query = self.omit(params, 'type')
          if limit is not None:
              request['limit'] = limit  # default 500, max 1000
          if since is not None:
              request['startTime'] = since
          method = 'privateGetHistoryOrders'
          if type == 'future':
              method = 'contractGetHistoryOrders'
          elif type == 'option':
              method = 'optionGetHistoryOrders'
          response = getattr(self, method)(self.extend(request, query))
          #
          # spot
          #
          #     [
          #         {
          #             "orderId":"616384027202542080",
          #             "clientOrderId":"158821470194414688",
          #             "exchangeId":"301",
          #             "symbol":"TBTCBUSDT",
          #             "price":"0",
          #             "origQty":"0.1",
          #             "executedQty":"0.1",
          #             "cummulativeQuoteQty":"682.606",
          #             "avgPrice":"6826.06",
          #             "status":"FILLED",
          #             "timeInForce":"GTC",
          #             "type":"MARKET",
          #             "side":"SELL",
          #             "stopPrice":"0.0",
          #             "icebergQty":"0.0",
          #             "time":"1588214701974",
          #             "updateTime":"0",
          #             "isWorking":true
          #         }
          #     ]
          #
          return self.parse_orders(response, market, since, limit)
  
      def fetch_order(self, id, symbol=None, params={}):
          self.load_markets()
          clientOrderId = self.safe_value_2(params, 'origClientOrderId', 'clientOrderId')
          request = {}
          defaultType = self.safe_string(self.options, 'type', 'spot')
          options = self.safe_value(self.options, 'fetchOrder', {})
          fetchOrderType = self.safe_string(options, 'type', defaultType)
          type = self.safe_string(params, 'type', fetchOrderType)
          query = self.omit(params, 'type')
          if clientOrderId is not None:
              request['origClientOrderId'] = clientOrderId
              query = self.omit(query, ['origClientOrderId', 'clientOrderId'])
          else:
              request['orderId'] = id
          method = 'privateGetOrder'
          if type == 'future':
              method = 'contractGetGetOrder'
          elif type == 'option':
              method = 'optionGetGetOrder'
          response = getattr(self, method)(self.extend(request, query))
          return self.parse_order(response)
  
      def fetch_deposits(self, code=None, since=None, limit=None, params={}):
          self.load_markets()
          currency = None
          request = {
              # 'fromId': 'string',  # if fromId is set, it will get deposits > that fromId, otherwise most recent deposits are returned
          }
          if code is not None:
              currency = self.currency(code)
          if since is not None:
              request['startTime'] = since
          if limit is not None:
              request['limit'] = limit
          response = self.privateGetDepositOrders(self.extend(request, params))
          #
          #     [
          #         {
          #             'time': '1565769575929',
          #             'orderId': '428100569859739648',
          #             'token': 'USDT',
          #             'address': '',
          #             'addressTag': '',
          #             'fromAddress': '',
          #             'fromAddressTag': '',
          #             'quantity': '1100',
          #         },
          #     ]
          #
          return self.parse_transactions(response, currency, since, limit)
  
      def fetch_withdrawals(self, code=None, since=None, limit=None, params={}):
          self.load_markets()
          currency = None
          request = {
              # 'fromId': 'string',  # if fromId is set, it will get deposits > that fromId, otherwise most recent deposits are returned
          }
          if code is not None:
              currency = self.currency(code)
              request['token'] = currency['id']
          if since is not None:
              request['startTime'] = since
          if limit is not None:
              request['limit'] = limit  # default 500, max 1000
          response = self.privateGetWithdrawalOrders(self.extend(request, params))
          #
          #     [
          #         {
          #             "time":"1536232111669",
          #             "orderId":"90161227158286336",
          #             "accountId":"517256161325920",
          #             "tokenId":"BHC",
          #             "tokenName":"BHC",
          #             "address":"0x815bF1c3cc0f49b8FC66B21A7e48fCb476051209",
          #             "addressExt":"address tag",
          #             "quantity":"14",  # Withdrawal qty
          #             "arriveQuantity":"14",  # Arrived qty
          #             "statusCode":"PROCESSING_STATUS",
          #             "status":3,
          #             "txid":"",
          #             "txidUrl":"",
          #             "walletHandleTime":"1536232111669",
          #             "feeTokenId":"BHC",
          #             "feeTokenName":"BHC",
          #             "fee":"0.1",
          #             "requiredConfirmNum":0,  # Required confirmations
          #             "confirmNum":0,  # Confirmations
          #             "kernelId":"",  # BEAM and GRIN only
          #             "isInternalTransfer": False  # True if self transfer is internal
          #         }
          #     ]
          #
          return self.parse_transactions(response, currency, since, limit)
  
      def withdraw(self, code, amount, address, tag=None, params={}):
          self.check_address(address)
          self.load_markets()
          currency = self.currency(code)
          clientOrderId = self.safe_string(params, 'clientOrderId', self.uuid())
          request = {
              'clientOrderId': clientOrderId,
              'tokenId': currency['id'],
              'address': address,  # the withdrawal address must be in current tag list in your PC/APP client
              'withdrawQuantity': amount,
              # 'chainType': 'OMNI',  # OMNI, ERC20, TRC20
          }
          if tag is not None:
              request['addressExt'] = tag
          response = self.privatePostWithdraw(self.extend(request, params))
          #
          #     {
          #         "status": 0,
          #         "success": True,
          #         "needBrokerAudit": False,  # Whether self request needs broker auit
          #         "orderId": "423885103582776064"  # Id for successful withdrawal
          #     }
          #
          return {
              'info': response,
              'id': self.safe_string(response, 'orderId'),
          }
  
      def fetch_accounts(self, params={}):
          response = self.privatePostSubAccountQuery(params)
          #
          #     [
          #         {
          #             "accountId": "122216245228131",
          #             "accountName": "createSubAccountByCurl",  # sub-account name
          #             "accountType": 1,  # 1 token trading, 2 options, 3 futures
          #             "accountIndex": 1,  # 0 main account, 1 sub-account
          #         },
          #     ]
          #
          result = []
          for i in range(0, len(response)):
              account = response[i]
              accountId = self.safe_string(account, 'accountId')
              accountType = self.safe_string(account, 'accountType')
              type = accountType
              if accountType == '1':
                  type = 'spot'
              elif accountType == '2':
                  type = 'option'
              elif accountType == '3':
                  type = 'future'
              result.append({
                  'id': accountId,
                  'type': type,
                  'currency': None,
                  'info': account,
              })
          return result
  
      def fetch_ledger(self, code=None, since=None, limit=None, params={}):
          self.load_markets()
          request = {
              'accountType': 1,  # spot 1, options 2, futures 3
              'accountIndex': 0,  # main 0, sub-account 1
              'fromFlowId': '',  # flowId to start from
              'endFlowId': '',  # flowId to end with
              'endTime': 1588450533040,
          }
          currency = None
          if code is not None:
              currency = self.currency(code)
              request['tokenId'] = currency['id']
          if since is not None:
              request['startTime'] = since
          if limit is not None:
              request['limit'] = limit  # default 500, max 500
          response = self.privateGetBalanceFlow(self.extend(request, params))
          #
          #     [
          #         {
          #             "id": "539870570957903104",
          #             "accountId": "122216245228131",
          #             "tokenId": "BTC",
          #             "tokenName": "BTC",
          #             "flowTypeValue": 51,
          #             "flowType": "USER_ACCOUNT_TRANSFER",
          #             "flowName": "Transfer",
          #             "change": "-12.5",
          #             "total": "379.624059937852365",  # after change
          #             "created": "1579093587214"
          #         },
          #         {
          #             "id": "536072393645448960",
          #             "accountId": "122216245228131",
          #             "tokenId": "USDT",
          #             "tokenName": "USDT",
          #             "flowTypeValue": 7,
          #             "flowType": "AIRDROP",
          #             "flowName": "Airdrop",
          #             "change": "-2000",
          #             "total": "918662.0917630848",
          #             "created": "1578640809195"
          #         }
          #     ]
          #
          return self.parse_ledger(response, currency, since, limit)
  
      def parse_ledger_entry(self, item, currency=None):
          #
          #     {
          #         "id": "539870570957903104",
          #         "accountId": "122216245228131",
          #         "tokenId": "BTC",
          #         "tokenName": "BTC",
          #         "flowTypeValue": 51,
          #         "flowType": "USER_ACCOUNT_TRANSFER",
          #         "flowName": "Transfer",
          #         "change": "-12.5",
          #         "total": "379.624059937852365",  # after change
          #         "created": "1579093587214"
          #     }
          #
          #     {
          #         "id": "536072393645448960",
          #         "accountId": "122216245228131",
          #         "tokenId": "USDT",
          #         "tokenName": "USDT",
          #         "flowTypeValue": 7,
          #         "flowType": "AIRDROP",
          #         "flowName": "Airdrop",
          #         "change": "-2000",
          #         "total": "918662.0917630848",
          #         "created": "1578640809195"
          #     }
          #
          currencyId = self.safe_string(item, 'tokenId')
          code = self.safe_currency_code(currencyId, currency)
          amount = self.safe_float(item, 'change')
          after = self.safe_float(item, 'total')
          direction = 'out' if (amount < 0) else 'in'
          before = None
          if after is not None and amount is not None:
              difference = amount if (direction == 'out') else -amount
              before = self.sum(after, difference)
          timestamp = self.safe_integer(item, 'created')
          type = self.parse_ledger_entry_type(self.safe_string(item, 'flowType'))
          id = self.safe_string(item, 'id')
          account = self.safe_string(item, 'accountId')
          return {
              'id': id,
              'currency': code,
              'account': account,
              'referenceAccount': None,
              'referenceId': None,
              'status': None,
              'amount': amount,
              'before': before,
              'after': after,
              'fee': None,
              'direction': direction,
              'timestamp': timestamp,
              'datetime': self.iso8601(timestamp),
              'type': type,
              'info': item,
          }
  
      def parse_ledger_entry_type(self, type):
          types = {
              'TRADE': 'trade',
              'FEE': 'fee',
              'TRANSFER': 'transfer',
              'DEPOSIT': 'transaction',
              'MAKER_REWARD': 'rebate',
              'PNL': 'pnl',
              'SETTLEMENT': 'settlement',
              'LIQUIDATION': 'liquidation',
              'FUNDING_SETTLEMENT': 'settlement',
              'USER_ACCOUNT_TRANSFER': 'transfer',
              'OTC_BUY_COIN': 'trade',
              'OTC_SELL_COIN': 'trade',
              'OTC_FEE': 'fee',
              'OTC_TRADE': 'trade',
              'ACTIVITY_AWARD': 'referral',
              'INVITATION_REFERRAL_BONUS': 'referral',
              'REGISTER_BONUS': 'referral',
              'AIRDROP': 'airdrop',
              'MINE_REWARD': 'reward',
          }
          return self.safe_string(types, type, type)
  
      def parse_transaction_status(self, status):
          statuses = {
              'BROKER_AUDITING_STATUS': 'pending',
              'BROKER_REJECT_STATUS': 'failed',
              'AUDITING_STATUS': 'pending',
              'AUDIT_REJECT_STATUS': 'failed',
              'PROCESSING_STATUS': 'pending',
              'WITHDRAWAL_SUCCESS_STATUS': 'ok',
              'WITHDRAWAL_FAILURE_STATUS': 'failed',
              'BLOCK_MINING_STATUS': 'ok',
          }
          return self.safe_string(statuses, status, status)
  
      def parse_transaction(self, transaction, currency=None):
          #
          # fetchDeposits
          #
          #     {
          #         'time': '1565769575929',
          #         'orderId': '428100569859739648',
          #         'token': 'USDT',
          #         'address': '',
          #         'addressTag': '',
          #         'fromAddress': '',
          #         'fromAddressTag': '',
          #         'quantity': '1100',
          #     }
          #
          # fetchWithdrawals
          #
          #     {
          #         "time":"1536232111669",
          #         "orderId":"90161227158286336",
          #         "accountId":"517256161325920",
          #         "tokenId":"BHC",
          #         "tokenName":"BHC",
          #         "address":"0x815bF1c3cc0f49b8FC66B21A7e48fCb476051209",
          #         "addressExt":"address tag",
          #         "quantity":"14",  # Withdrawal qty
          #         "arriveQuantity":"14",  # Arrived qty
          #         "statusCode":"PROCESSING_STATUS",
          #         "status":3,
          #         "txid":"",
          #         "txidUrl":"",
          #         "walletHandleTime":"1536232111669",
          #         "feeTokenId":"BHC",
          #         "feeTokenName":"BHC",
          #         "fee":"0.1",
          #         "requiredConfirmNum":0,  # Required confirmations
          #         "confirmNum":0,  # Confirmations
          #         "kernelId":"",  # BEAM and GRIN only
          #         "isInternalTransfer": False  # True if self transfer is internal
          #     }
          #
          id = self.safe_string(transaction, 'orderId')
          address = self.safe_string(transaction, 'address')
          tag = self.safe_string_2(transaction, 'addressExt', 'addressTag')
          if tag is not None:
              if len(tag) < 1:
                  tag = None
          addressFrom = self.safe_string(transaction, 'fromAddress')
          tagFrom = self.safe_string(transaction, 'fromAddressTag')
          if tagFrom is not None:
              if len(tagFrom) < 1:
                  tagFrom = None
          currencyId = self.safe_string(transaction, 'tokenId')
          code = self.safe_currency_code(currencyId, currency)
          timestamp = self.safe_integer(transaction, 'time')
          txid = self.safe_string(transaction, 'txid')
          if txid == '':
              txid = None
          type = None
          status = self.parse_transaction_status(self.safe_string(transaction, 'statusCode'))
          if status is None:
              type = 'deposit'
              status = 'ok'
          else:
              type = 'withdrawal'
          amount = self.safe_float(transaction, 'quantity')
          feeCost = self.safe_float(transaction, 'fee')
          fee = None
          if feeCost is not None:
              feeCurrencyId = self.safe_string(transaction, 'feeTokenId')
              feeCurrencyCode = self.safe_currency_code(feeCurrencyId)
              fee = {
                  'currency': feeCurrencyCode,
                  'cost': feeCost,
              }
          return {
              'info': transaction,
              'id': id,
              'txid': txid,
              'timestamp': timestamp,
              'datetime': self.iso8601(timestamp),
              'addressFrom': addressFrom,
              'address': address,
              'addressTo': address,
              'tagFrom': tagFrom,
              'tag': tag,
              'tagTo': tag,
              'type': type,
              'amount': amount,
              'currency': code,
              'status': status,
              'updated': None,
              'fee': fee,
          }
  
      def parse_ticker(self, ticker, market=None):
          #
          # fetchTicker, fetchTickers
          #
          #     {
          #         "time":1588069860794,
          #         "symbol":"BNB0501PS16",
          #         "bestBidPrice":"0.2129",
          #         "bestAskPrice":"0.3163",
          #         "volume":"33547",
          #         "quoteVolume":"10801.987",
          #         "lastPrice":"0.2625",
          #         "highPrice":"0.3918",
          #         "lowPrice":"0.2625",
          #         "openPrice":"0.362",
          #     }
          #
          # fetchBidAsk, fetchBidAsks
          #
          #     {
          #         "symbol": "LTCBTC",
          #         "bidPrice": "4.00000000",
          #         "bidQty": "431.00000000",
          #         "askPrice": "4.00000200",
          #         "askQty": "9.00000000"
          #     }
          #
          symbol = None
          marketId = self.safe_string(ticker, 'symbol')
          if marketId in self.markets_by_id:
              market = self.markets_by_id[marketId]
          if market is not None:
              symbol = market['symbol']
          timestamp = self.safe_integer(ticker, 'time')
          open = self.safe_float(ticker, 'openPrice')
          close = self.safe_float(ticker, 'lastPrice')
          change = None
          percentage = None
          average = None
          if (open is not None) and (close is not None):
              change = close - open
              average = self.sum(open, close) / 2
              if (close is not None) and (close > 0):
                  percentage = (change / open) * 100
          quoteVolume = self.safe_float(ticker, 'quoteVolume')
          baseVolume = self.safe_float(ticker, 'volume')
          vwap = self.vwap(baseVolume, quoteVolume)
          return {
              'symbol': symbol,
              'timestamp': timestamp,
              'datetime': self.iso8601(timestamp),
              'high': self.safe_float(ticker, 'highPrice'),
              'low': self.safe_float(ticker, 'lowPrice'),
              'bid': self.safe_float_2(ticker, 'bestBidPrice', 'bidPrice'),
              'bidVolume': self.safe_float(ticker, 'bidQty'),
              'ask': self.safe_float_2(ticker, 'bestAskPrice', 'askPrice'),
              'askVolume': self.safe_float(ticker, 'askQty'),
              'vwap': vwap,
              'open': open,
              'close': close,
              'last': close,
              'previousClose': None,
              'change': change,
              'percentage': percentage,
              'average': average,
              'baseVolume': baseVolume,
              'quoteVolume': quoteVolume,
              'info': ticker,
          }
  
      def parse_trade(self, trade, market):
          #
          # fetchTrades(public)
          #
          #     {
          #         "price":"0.025344",
          #         "time":1588084082060,
          #         "qty":"1",
          #         "isBuyerMaker":false
          #     }
          #
          # fetchMyTrades(private)
          #
          # spot
          #
          #     {
          #         "id":"616384027512920576",
          #         "symbol":"TBTCBUSDT",
          #         "orderId":"616384027202542080",
          #         "matchOrderId":"605124954767266560",
          #         "price":"6826.06",
          #         "qty":"0.1",
          #         "commission":"0.682606",
          #         "commissionAsset":"BUSDT",
          #         "time":"1588214701982",
          #         "isBuyer":false,
          #         "isMaker":false,
          #         "fee":{
          #             "feeTokenId":"BUSDT",
          #             "feeTokenName":"BUSDT",
          #             "fee":"0.682606"
          #         }
          #     }
          #
          id = self.safe_string(trade, 'id')
          timestamp = self.safe_float(trade, 'time')
          type = None
          orderId = self.safe_string(trade, 'orderId')
          price = self.safe_float(trade, 'price')
          amount = self.safe_float(trade, 'qty')
          cost = None
          if price is not None:
              if amount is not None:
                  cost = price * amount
          side = None
          takerOrMaker = None
          if 'isBuyerMaker' in trade:
              side = 'sell' if trade['isBuyerMaker'] else 'buy'
          else:
              isMaker = self.safe_value(trade, 'isMaker')
              if isMaker is not None:
                  takerOrMaker = 'maker' if isMaker else 'taker'
              isBuyer = self.safe_value(trade, 'isBuyer')
              side = 'buy' if isBuyer else 'sell'
          fee = None
          feeCost = self.safe_float(trade, 'commission')
          if feeCost is not None:
              feeCurrencyId = self.safe_string(trade, 'commissionAsset')
              feeCurrencyCode = self.safe_currency_code(feeCurrencyId)
              fee = {
                  'cost': feeCost,
                  'currency': feeCurrencyCode,
              }
          symbol = None
          if (symbol is None) and (market is not None):
              symbol = market['symbol']
          return {
              'id': id,
              'info': trade,
              'timestamp': timestamp,
              'datetime': self.iso8601(timestamp),
              'symbol': symbol,
              'type': type,
              'order': orderId,
              'side': side,
              'takerOrMaker': takerOrMaker,
              'price': price,
              'amount': amount,
              'cost': cost,
              'fee': fee,
          }
  
      def parse_order(self, order, market=None):
          #
          # createOrder
          #
          #     {
          #         "symbol":"TBTCBUSDT",
          #         "orderId":"616376654496877056",
          #         "clientOrderId":"158821382304516955",
          #         "transactTime":"1588213823080",
          #         "price":"0",
          #         "origQty":"1000",
          #         "executedQty":"0",
          #         "status":"NEW",
          #         "timeInForce":"GTC",
          #         "type":"MARKET",
          #         "side":"BUY"
          #     }
          #
          # fetchOrder, fetchOpenOrders, fetchClosedOrders
          #
          # spot
          #
          #     {
          #         "orderId":"616384027202542080",
          #         "clientOrderId":"158821470194414688",
          #         "exchangeId":"301",
          #         "symbol":"TBTCBUSDT",
          #         "price":"0",
          #         "origQty":"0.1",
          #         "executedQty":"0.1",
          #         "cummulativeQuoteQty":"682.606",
          #         "avgPrice":"6826.06",
          #         "status":"FILLED",
          #         "timeInForce":"GTC",
          #         "type":"MARKET",
          #         "side":"SELL",
          #         "stopPrice":"0.0",
          #         "icebergQty":"0.0",
          #         "time":"1588214701974",
          #         "updateTime":"0",
          #         "isWorking":true
          #     }
          #
          # future
          #
          #     {
          #         time: "1588353669383",
          #         updateTime: "0",
          #         orderId: "617549770304599296",
          #         clientOrderId: "test-001",
          #         symbol: "BTC-PERP-REV",
          #         price: "10000",
          #         leverage: "1",
          #         origQty: "100",
          #         executedQty: "0",
          #         avgPrice: "0",
          #         marginLocked: "0",
          #         orderType: "LIMIT",
          #         side: "SELL_OPEN",
          #         fees: [],
          #         timeInForce: "GTC",
          #         status: "CANCELED",
          #         priceType: "INPUT"
          #     }
          #
          #
          id = self.safe_string(order, 'orderId')
          clientOrderId = self.safe_string(order, 'clientOrderId')
          timestamp = self.safe_integer(order, 'time')
          if timestamp is None:
              timestamp = self.safe_integer(order, 'transactTime')
          symbol = None
          if market is None:
              marketId = self.safe_string(order, 'symbol')
              if marketId is not None:
                  marketId = marketId.upper()
                  if marketId in self.markets_by_id:
                      market = self.markets_by_id[marketId]
          type = self.safe_string_lower(order, 'type')
          side = self.safe_string_lower(order, 'side')
          price = self.safe_float(order, 'price')
          average = self.safe_float(order, 'avgPrice')
          amount = None
          cost = self.safe_float(order, 'cummulativeQuoteQty')
          filled = None
          remaining = None
          if type is None:
              type = self.safe_string_lower(order, 'orderType')
              if (market is not None) and market['inverse']:
                  cost = self.safe_float(order, 'executedQty')
                  amount = None
              if cost == 0.0:
                  filled = 0
          else:
              amount = self.safe_float(order, 'origQty')
              if type == 'market':
                  price = None
                  if side == 'buy':
                      amount = None
              filled = self.safe_float(order, 'executedQty')
              if filled is not None:
                  if amount is not None:
                      remaining = amount - filled
          if average == 0.0:
              average = None
          status = self.parse_order_status(self.safe_string(order, 'status'))
          if market is not None:
              symbol = market['symbol']
          result = {
              'info': order,
              'id': id,
              'clientOrderId': clientOrderId,
              'timestamp': timestamp,
              'datetime': self.iso8601(timestamp),
              'lastTradeTimestamp': None,
              'symbol': symbol,
              'type': type,
              'side': side,
              'price': price,
              'average': average,
              'cost': cost,
              'amount': amount,
              'filled': filled,
              'remaining': remaining,
              'status': status,
              'trades': None,
              'fee': None,
              'fees': None,
          }
          fees = self.safe_value(order, 'fees', [])
          numFees = len(fees)
          if numFees > 0:
              result['fees'] = []
              for i in range(0, len(fees)):
                  feeCost = self.safe_float(fees[i], 'fee')
                  if feeCost is not None:
                      feeCurrencyId = self.safe_string(fees[i], 'feeToken')
                      feeCurrencyCode = self.safe_currency_code(feeCurrencyId)
                      result['fees'].append({
                          'cost': feeCost,
                          'currency': feeCurrencyCode,
                      })
          return result
  
      def parse_order_status(self, status):
          statuses = {
              'NEW': 'open',
              'CANCELED': 'canceled',
              'FILLED': 'closed',
              'PENDING_CANCEL': 'canceled',
          }
          return self.safe_string(statuses, status, status)
  
      def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
          url = self.urls['api'][api] + '/' + self.version + '/' + self.implode_params(path, params)
          query = self.omit(params, self.extract_params(path))
          isPublicContract = (api == 'contract') and ((path == 'insurance') or (path == 'fundingRate'))
          if (api == 'public') or (api == 'quote') or isPublicContract:
              if params:
                  url += '?' + self.urlencode(params)
          else:
              timestamp = self.milliseconds()
              self.check_required_credentials()
              request = self.extend({
                  'timestamp': timestamp,
              }, query)
              # 准备待签名数据
              auth = self.urlencode(request)
              signature = self.hmac(self.encode(auth), self.encode(self.secret), hashlib.sha256)
              request['signature'] = signature
              headers = {
                  'X-BH-APIKEY': self.apiKey,
              }
              if method == 'POST':
                  body = self.urlencode(request)
                  headers = self.extend({
                      'Content-Type': 'application/x-www-form-urlencoded',
                  }, headers)
              else:
                  url += '?' + self.urlencode(request)
          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 'code' in response:
              code = self.safe_string(response, 'code')
              if code != '0':
                  feedback = self.id + ' ' + body
                  self.throw_exactly_matched_exception(self.exceptions['exact'], code, feedback)
                  raise ExchangeError(feedback)
  