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
  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 InsufficientFunds
  from ccxt.base.errors import InvalidOrder
  from ccxt.base.errors import OrderNotFound
  from ccxt.base.errors import CancelPending
  from ccxt.base.errors import DuplicateOrderId
  from ccxt.base.errors import DDoSProtection
  from ccxt.base.decimal_to_precision import TICK_SIZE
  from ccxt.base.precise import Precise
  
  
  class phemex(Exchange):
  
      def describe(self):
          return self.deep_extend(super(phemex, self).describe(), {
              'id': 'phemex',
              'name': 'Phemex',
              'countries': ['CN'],  # China
              'rateLimit': 100,
              'version': 'v1',
              'certified': False,
              'pro': True,
              'hostname': 'api.phemex.com',
              'has': {
                  'cancelAllOrders': True,
                  'cancelOrder': True,
                  'createOrder': True,
                  'fetchBalance': True,
                  'fetchClosedOrders': True,
                  'fetchCurrencies': True,
                  'fetchDepositAddress': True,
                  'fetchDeposits': True,
                  'fetchIndexOHLCV': False,
                  'fetchMarkets': True,
                  'fetchMarkOHLCV': False,
                  'fetchMyTrades': True,
                  'fetchOHLCV': True,
                  'fetchOpenOrders': True,
                  'fetchOrder': True,
                  'fetchOrderBook': True,
                  'fetchOrders': True,
                  'fetchPremiumIndexOHLCV': False,
                  'fetchTicker': True,
                  'fetchTrades': True,
                  'fetchWithdrawals': True,
              },
              'urls': {
                  'logo': 'https://user-images.githubusercontent.com/1294454/85225056-221eb600-b3d7-11ea-930d-564d2690e3f6.jpg',
                  'test': {
                      'v1': 'https://testnet-api.phemex.com/v1',
                      'public': 'https://testnet-api.phemex.com/exchange/public',
                      'private': 'https://testnet-api.phemex.com',
                  },
                  'api': {
                      'v1': 'https://{hostname}/v1',
                      'public': 'https://{hostname}/exchange/public',
                      'private': 'https://{hostname}',
                  },
                  'www': 'https://phemex.com',
                  'doc': 'https://github.com/phemex/phemex-api-docs',
                  'fees': 'https://phemex.com/fees-conditions',
                  'referral': {
                      'url': 'https://phemex.com/register?referralCode=EDNVJ',
                      'discount': 0.1,
                  },
              },
              'timeframes': {
                  '1m': '60',
                  '3m': '180',
                  '5m': '300',
                  '15m': '900',
                  '30m': '1800',
                  '1h': '3600',
                  '2h': '7200',
                  '3h': '10800',
                  '4h': '14400',
                  '6h': '21600',
                  '12h': '43200',
                  '1d': '86400',
                  '1w': '604800',
                  '1M': '2592000',
              },
              'api': {
                  'public': {
                      'get': [
                          'cfg/v2/products',  # spot + contracts
                          'products',  # contracts only
                          'nomics/trades',  # ?market=<symbol>&since=<since>
                          'md/kline',  # ?from=1589811875&resolution=1800&symbol=sBTCUSDT&to=1592457935
                      ],
                  },
                  'v1': {
                      'get': [
                          'md/orderbook',  # ?symbol=<symbol>&id=<id>
                          'md/trade',  # ?symbol=<symbol>&id=<id>
                          'md/ticker/24hr',  # ?symbol=<symbol>&id=<id>
                          'md/ticker/24hr/all',  # ?id=<id>
                          'md/spot/ticker/24hr',  # ?symbol=<symbol>&id=<id>
                          'md/spot/ticker/24hr/all',  # ?symbol=<symbol>&id=<id>
                          'exchange/public/products',  # contracts only
                      ],
                  },
                  'private': {
                      'get': [
                          # spot
                          'spot/orders/active',  # ?symbol=<symbol>&orderID=<orderID>
                          # 'spot/orders/active',  # ?symbol=<symbol>&clOrDID=<clOrdID>
                          'spot/orders',  # ?symbol=<symbol>
                          'spot/wallets',  # ?currency=<currency>
                          'exchange/spot/order',  # ?symbol=<symbol>&ordStatus=<ordStatus1,orderStatus2>ordType=<ordType1,orderType2>&start=<start>&end=<end>&limit=<limit>&offset=<offset>
                          'exchange/spot/order/trades',  # ?symbol=<symbol>&start=<start>&end=<end>&limit=<limit>&offset=<offset>
                          # swap
                          'accounts/accountPositions',  # ?currency=<currency>
                          'accounts/positions',  # ?currency=<currency>
                          'orders/activeList',  # ?symbol=<symbol>
                          'exchange/order/list',  # ?symbol=<symbol>&start=<start>&end=<end>&offset=<offset>&limit=<limit>&ordStatus=<ordStatus>&withCount=<withCount>
                          'exchange/order',  # ?symbol=<symbol>&orderID=<orderID1,orderID2>
                          # 'exchange/order',  # ?symbol=<symbol>&clOrdID=<clOrdID1,clOrdID2>
                          'exchange/order/trade',  # ?symbol=<symbol>&start=<start>&end=<end>&limit=<limit>&offset=<offset>&withCount=<withCount>
                          'phemex-user/users/children',  # ?offset=<offset>&limit=<limit>&withCount=<withCount>
                          'phemex-user/wallets/v2/depositAddress',  # ?_t=1592722635531&currency=USDT
                          'exchange/margins/transfer',  # ?start=<start>&end=<end>&offset=<offset>&limit=<limit>&withCount=<withCount>
                          'exchange/wallets/confirm/withdraw',  # ?code=<withdrawConfirmCode>
                          'exchange/wallets/withdrawList',  # ?currency=<currency>&limit=<limit>&offset=<offset>&withCount=<withCount>
                          'exchange/wallets/depositList',  # ?currency=<currency>&offset=<offset>&limit=<limit>
                          'exchange/wallets/v2/depositAddress',  # ?currency=<currency>
                      ],
                      'post': [
                          # spot
                          'spot/orders',
                          # swap
                          'orders',
                          'positions/assign',  # ?symbol=<symbol>&posBalance=<posBalance>&posBalanceEv=<posBalanceEv>
                          'exchange/wallets/transferOut',
                          'exchange/wallets/transferIn',
                          'exchange/margins',
                          'exchange/wallets/createWithdraw',  # ?otpCode=<otpCode>
                          'exchange/wallets/cancelWithdraw',
                          'exchange/wallets/createWithdrawAddress',  # ?otpCode={optCode}
                      ],
                      'put': [
                          # spot
                          'spot/orders',  # ?symbol=<symbol>&orderID=<orderID>&origClOrdID=<origClOrdID>&clOrdID=<clOrdID>&priceEp=<priceEp>&baseQtyEV=<baseQtyEV>&quoteQtyEv=<quoteQtyEv>&stopPxEp=<stopPxEp>
                          # swap
                          'orders/replace',  # ?symbol=<symbol>&orderID=<orderID>&origClOrdID=<origClOrdID>&clOrdID=<clOrdID>&price=<price>&priceEp=<priceEp>&orderQty=<orderQty>&stopPx=<stopPx>&stopPxEp=<stopPxEp>&takeProfit=<takeProfit>&takeProfitEp=<takeProfitEp>&stopLoss=<stopLoss>&stopLossEp=<stopLossEp>&pegOffsetValueEp=<pegOffsetValueEp>&pegPriceType=<pegPriceType>
                          'positions/leverage',  # ?symbol=<symbol>&leverage=<leverage>&leverageEr=<leverageEr>
                          'positions/riskLimit',  # ?symbol=<symbol>&riskLimit=<riskLimit>&riskLimitEv=<riskLimitEv>
                      ],
                      'delete': [
                          # spot
                          'spot/orders',  # ?symbol=<symbol>&orderID=<orderID>
                          'spot/orders/all',  # ?symbol=<symbol>&untriggered=<untriggered>
                          # 'spot/orders',  # ?symbol=<symbol>&clOrdID=<clOrdID>
                          # swap
                          'orders/cancel',  # ?symbol=<symbol>&orderID=<orderID>
                          'orders',  # ?symbol=<symbol>&orderID=<orderID1>,<orderID2>,<orderID3>
                          'orders/all',  # ?symbol=<symbol>&untriggered=<untriggered>&text=<text>
                      ],
                  },
              },
              'precisionMode': TICK_SIZE,
              'fees': {
                  'trading': {
                      'tierBased': False,
                      'percentage': True,
                      'taker': self.parse_number('0.001'),
                      'maker': self.parse_number('0.001'),
                  },
              },
              'requiredCredentials': {
                  'apiKey': True,
                  'secret': True,
              },
              'exceptions': {
                  'exact': {
                      # not documented
                      '412': BadRequest,  # {"code":412,"msg":"Missing parameter - resolution","data":null}
                      '6001': BadRequest,  # {"error":{"code":6001,"message":"invalid argument"},"id":null,"result":null}
                      # documented
                      '19999': BadRequest,  # REQUEST_IS_DUPLICATED Duplicated request ID
                      '10001': DuplicateOrderId,  # OM_DUPLICATE_ORDERID Duplicated order ID
                      '10002': OrderNotFound,  # OM_ORDER_NOT_FOUND Cannot find order ID
                      '10003': CancelPending,  # OM_ORDER_PENDING_CANCEL Cannot cancel while order is already in pending cancel status
                      '10004': CancelPending,  # OM_ORDER_PENDING_REPLACE Cannot cancel while order is already in pending cancel status
                      '10005': CancelPending,  # OM_ORDER_PENDING Cannot cancel while order is already in pending cancel status
                      '11001': InsufficientFunds,  # TE_NO_ENOUGH_AVAILABLE_BALANCE Insufficient available balance
                      '11002': InvalidOrder,  # TE_INVALID_RISK_LIMIT Invalid risk limit value
                      '11003': InsufficientFunds,  # TE_NO_ENOUGH_BALANCE_FOR_NEW_RISK_LIMIT Insufficient available balance
                      '11004': InvalidOrder,  # TE_INVALID_LEVERAGE invalid input or new leverage is over maximum allowed leverage
                      '11005': InsufficientFunds,  # TE_NO_ENOUGH_BALANCE_FOR_NEW_LEVERAGE Insufficient available balance
                      '11006': ExchangeError,  # TE_CANNOT_CHANGE_POSITION_MARGIN_WITHOUT_POSITION Position size is zero. Cannot change margin
                      '11007': ExchangeError,  # TE_CANNOT_CHANGE_POSITION_MARGIN_FOR_CROSS_MARGIN Cannot change margin under CrossMargin
                      '11008': ExchangeError,  # TE_CANNOT_REMOVE_POSITION_MARGIN_MORE_THAN_ADDED exceeds the maximum removable Margin
                      '11009': ExchangeError,  # TE_CANNOT_REMOVE_POSITION_MARGIN_DUE_TO_UNREALIZED_PNL exceeds the maximum removable Margin
                      '11010': InsufficientFunds,  # TE_CANNOT_ADD_POSITION_MARGIN_DUE_TO_NO_ENOUGH_AVAILABLE_BALANCE Insufficient available balance
                      '11011': InvalidOrder,  # TE_REDUCE_ONLY_ABORT Cannot accept reduce only order
                      '11012': InvalidOrder,  # TE_REPLACE_TO_INVALID_QTY Order quantity Error
                      '11013': InvalidOrder,  # TE_CONDITIONAL_NO_POSITION Position size is zero. Cannot determine conditional order's quantity
                      '11014': InvalidOrder,  # TE_CONDITIONAL_CLOSE_POSITION_WRONG_SIDE Close position conditional order has the same side
                      '11015': InvalidOrder,  # TE_CONDITIONAL_TRIGGERED_OR_CANCELED
                      '11016': BadRequest,  # TE_ADL_NOT_TRADING_REQUESTED_ACCOUNT Request is routed to the wrong trading engine
                      '11017': ExchangeError,  # TE_ADL_CANNOT_FIND_POSITION Cannot find requested position on current account
                      '11018': ExchangeError,  # TE_NO_NEED_TO_SETTLE_FUNDING The current account does not need to pay a funding fee
                      '11019': ExchangeError,  # TE_FUNDING_ALREADY_SETTLED The current account already pays the funding fee
                      '11020': ExchangeError,  # TE_CANNOT_TRANSFER_OUT_DUE_TO_BONUS Withdraw to wallet needs to remove all remaining bonus. However if bonus is used by position or order cost, withdraw fails.
                      '11021': ExchangeError,  # TE_INVALID_BONOUS_AMOUNT  # Grpc command cannot be negative number Invalid bonus amount
                      '11022': AccountSuspended,  # TE_REJECT_DUE_TO_BANNED Account is banned
                      '11023': ExchangeError,  # TE_REJECT_DUE_TO_IN_PROCESS_OF_LIQ Account is in the process of liquidation
                      '11024': ExchangeError,  # TE_REJECT_DUE_TO_IN_PROCESS_OF_ADL Account is in the process of auto-deleverage
                      '11025': BadRequest,  # TE_ROUTE_ERROR Request is routed to the wrong trading engine
                      '11026': ExchangeError,  # TE_UID_ACCOUNT_MISMATCH
                      '11027': BadSymbol,  # TE_SYMBOL_INVALID Invalid number ID or name
                      '11028': BadSymbol,  # TE_CURRENCY_INVALID Invalid currency ID or name
                      '11029': ExchangeError,  # TE_ACTION_INVALID Unrecognized request type
                      '11030': ExchangeError,  # TE_ACTION_BY_INVALID
                      '11031': DDoSProtection,  # TE_SO_NUM_EXCEEDS Number of total conditional orders exceeds the max limit
                      '11032': DDoSProtection,  # TE_AO_NUM_EXCEEDS Number of total active orders exceeds the max limit
                      '11033': DuplicateOrderId,  # TE_ORDER_ID_DUPLICATE Duplicated order ID
                      '11034': InvalidOrder,  # TE_SIDE_INVALID Invalid side
                      '11035': InvalidOrder,  # TE_ORD_TYPE_INVALID Invalid OrderType
                      '11036': InvalidOrder,  # TE_TIME_IN_FORCE_INVALID Invalid TimeInForce
                      '11037': InvalidOrder,  # TE_EXEC_INST_INVALID Invalid ExecType
                      '11038': InvalidOrder,  # TE_TRIGGER_INVALID Invalid trigger type
                      '11039': InvalidOrder,  # TE_STOP_DIRECTION_INVALID Invalid stop direction type
                      '11040': InvalidOrder,  # TE_NO_MARK_PRICE Cannot get valid mark price to create conditional order
                      '11041': InvalidOrder,  # TE_NO_INDEX_PRICE Cannot get valid index price to create conditional order
                      '11042': InvalidOrder,  # TE_NO_LAST_PRICE Cannot get valid last market price to create conditional order
                      '11043': InvalidOrder,  # TE_RISING_TRIGGER_DIRECTLY Conditional order would be triggered immediately
                      '11044': InvalidOrder,  # TE_FALLING_TRIGGER_DIRECTLY Conditional order would be triggered immediately
                      '11045': InvalidOrder,  # TE_TRIGGER_PRICE_TOO_LARGE Conditional order trigger price is too high
                      '11046': InvalidOrder,  # TE_TRIGGER_PRICE_TOO_SMALL Conditional order trigger price is too low
                      '11047': InvalidOrder,  # TE_BUY_TP_SHOULD_GT_BASE TakeProfile BUY conditional order trigger price needs to be greater than reference price
                      '11048': InvalidOrder,  # TE_BUY_SL_SHOULD_LT_BASE StopLoss BUY condition order price needs to be less than the reference price
                      '11049': InvalidOrder,  # TE_BUY_SL_SHOULD_GT_LIQ StopLoss BUY condition order price needs to be greater than liquidation price or it will not trigger
                      '11050': InvalidOrder,  # TE_SELL_TP_SHOULD_LT_BASE TakeProfile SELL conditional order trigger price needs to be less than reference price
                      '11051': InvalidOrder,  # TE_SELL_SL_SHOULD_LT_LIQ StopLoss SELL condition order price needs to be less than liquidation price or it will not trigger
                      '11052': InvalidOrder,  # TE_SELL_SL_SHOULD_GT_BASE StopLoss SELL condition order price needs to be greater than the reference price
                      '11053': InvalidOrder,  # TE_PRICE_TOO_LARGE
                      '11054': InvalidOrder,  # TE_PRICE_WORSE_THAN_BANKRUPT Order price cannot be more aggressive than bankrupt price if self order has instruction to close a position
                      '11055': InvalidOrder,  # TE_PRICE_TOO_SMALL Order price is too low
                      '11056': InvalidOrder,  # TE_QTY_TOO_LARGE Order quantity is too large
                      '11057': InvalidOrder,  # TE_QTY_NOT_MATCH_REDUCE_ONLY Does not allow ReduceOnly order without position
                      '11058': InvalidOrder,  # TE_QTY_TOO_SMALL Order quantity is too small
                      '11059': InvalidOrder,  # TE_TP_SL_QTY_NOT_MATCH_POS Position size is zero. Cannot accept any TakeProfit or StopLoss order
                      '11060': InvalidOrder,  # TE_SIDE_NOT_CLOSE_POS TakeProfit or StopLoss order has wrong side. Cannot close position
                      '11061': CancelPending,  # TE_ORD_ALREADY_PENDING_CANCEL Repeated cancel request
                      '11062': InvalidOrder,  # TE_ORD_ALREADY_CANCELED Order is already canceled
                      '11063': InvalidOrder,  # TE_ORD_STATUS_CANNOT_CANCEL Order is not able to be canceled under current status
                      '11064': InvalidOrder,  # TE_ORD_ALREADY_PENDING_REPLACE Replace request is rejected because order is already in pending replace status
                      '11065': InvalidOrder,  # TE_ORD_REPLACE_NOT_MODIFIED Replace request does not modify any parameters of the order
                      '11066': InvalidOrder,  # TE_ORD_STATUS_CANNOT_REPLACE Order is not able to be replaced under current status
                      '11067': InvalidOrder,  # TE_CANNOT_REPLACE_PRICE Market conditional order cannot change price
                      '11068': InvalidOrder,  # TE_CANNOT_REPLACE_QTY Condtional order for closing position cannot change order quantity, since the order quantity is determined by position size already
                      '11069': ExchangeError,  # TE_ACCOUNT_NOT_IN_RANGE The account ID in the request is not valid or is not in the range of the current process
                      '11070': BadSymbol,  # TE_SYMBOL_NOT_IN_RANGE The symbol is invalid
                      '11071': InvalidOrder,  # TE_ORD_STATUS_CANNOT_TRIGGER
                      '11072': InvalidOrder,  # TE_TKFR_NOT_IN_RANGE The fee value is not valid
                      '11073': InvalidOrder,  # TE_MKFR_NOT_IN_RANGE The fee value is not valid
                      '11074': InvalidOrder,  # TE_CANNOT_ATTACH_TP_SL Order request cannot contain TP/SL parameters when the account already has positions
                      '11075': InvalidOrder,  # TE_TP_TOO_LARGE TakeProfit price is too large
                      '11076': InvalidOrder,  # TE_TP_TOO_SMALL TakeProfit price is too small
                      '11077': InvalidOrder,  # TE_TP_TRIGGER_INVALID Invalid trigger type
                      '11078': InvalidOrder,  # TE_SL_TOO_LARGE StopLoss price is too large
                      '11079': InvalidOrder,  # TE_SL_TOO_SMALL StopLoss price is too small
                      '11080': InvalidOrder,  # TE_SL_TRIGGER_INVALID Invalid trigger type
                      '11081': InvalidOrder,  # TE_RISK_LIMIT_EXCEEDS Total potential position breaches current risk limit
                      '11082': InsufficientFunds,  # TE_CANNOT_COVER_ESTIMATE_ORDER_LOSS The remaining balance cannot cover the potential unrealized PnL for self new order
                      '11083': InvalidOrder,  # TE_TAKE_PROFIT_ORDER_DUPLICATED TakeProfit order already exists
                      '11084': InvalidOrder,  # TE_STOP_LOSS_ORDER_DUPLICATED StopLoss order already exists
                      '11085': DuplicateOrderId,  # TE_CL_ORD_ID_DUPLICATE ClOrdId is duplicated
                      '11086': InvalidOrder,  # TE_PEG_PRICE_TYPE_INVALID PegPriceType is invalid
                      '11087': InvalidOrder,  # TE_BUY_TS_SHOULD_LT_BASE The trailing order's StopPrice should be less than the current last price
                      '11088': InvalidOrder,  # TE_BUY_TS_SHOULD_GT_LIQ The traling order's StopPrice should be greater than the current liquidation price
                      '11089': InvalidOrder,  # TE_SELL_TS_SHOULD_LT_LIQ The traling order's StopPrice should be greater than the current last price
                      '11090': InvalidOrder,  # TE_SELL_TS_SHOULD_GT_BASE The traling order's StopPrice should be less than the current liquidation price
                      '11091': InvalidOrder,  # TE_BUY_REVERT_VALUE_SHOULD_LT_ZERO The PegOffset should be less than zero
                      '11092': InvalidOrder,  # TE_SELL_REVERT_VALUE_SHOULD_GT_ZERO The PegOffset should be greater than zero
                      '11093': InvalidOrder,  # TE_BUY_TTP_SHOULD_ACTIVATE_ABOVE_BASE The activation price should be greater than the current last price
                      '11094': InvalidOrder,  # TE_SELL_TTP_SHOULD_ACTIVATE_BELOW_BASE The activation price should be less than the current last price
                      '11095': InvalidOrder,  # TE_TRAILING_ORDER_DUPLICATED A trailing order exists already
                      '11096': InvalidOrder,  # TE_CLOSE_ORDER_CANNOT_ATTACH_TP_SL An order to close position cannot have trailing instruction
                      '11097': BadRequest,  # TE_CANNOT_FIND_WALLET_OF_THIS_CURRENCY This crypto is not supported
                      '11098': BadRequest,  # TE_WALLET_INVALID_ACTION Invalid action on wallet
                      '11099': ExchangeError,  # TE_WALLET_VID_UNMATCHED Wallet operation request has a wrong wallet vid
                      '11100': InsufficientFunds,  # TE_WALLET_INSUFFICIENT_BALANCE Wallet has insufficient balance
                      '11101': InsufficientFunds,  # TE_WALLET_INSUFFICIENT_LOCKED_BALANCE Locked balance in wallet is not enough for unlock/withdraw request
                      '11102': BadRequest,  # TE_WALLET_INVALID_DEPOSIT_AMOUNT Deposit amount must be greater than zero
                      '11103': BadRequest,  # TE_WALLET_INVALID_WITHDRAW_AMOUNT Withdraw amount must be less than zero
                      '11104': BadRequest,  # TE_WALLET_REACHED_MAX_AMOUNT Deposit makes wallet exceed max amount allowed
                      '11105': InsufficientFunds,  # TE_PLACE_ORDER_INSUFFICIENT_BASE_BALANCE Insufficient funds in base wallet
                      '11106': InsufficientFunds,  # TE_PLACE_ORDER_INSUFFICIENT_QUOTE_BALANCE Insufficient funds in quote wallet
                      '11107': ExchangeError,  # TE_CANNOT_CONNECT_TO_REQUEST_SEQ TradingEngine failed to connect with CrossEngine
                      '11108': InvalidOrder,  # TE_CANNOT_REPLACE_OR_CANCEL_MARKET_ORDER Cannot replace/amend market order
                      '11109': InvalidOrder,  # TE_CANNOT_REPLACE_OR_CANCEL_IOC_ORDER Cannot replace/amend ImmediateOrCancel order
                      '11110': InvalidOrder,  # TE_CANNOT_REPLACE_OR_CANCEL_FOK_ORDER Cannot replace/amend FillOrKill order
                      '11111': InvalidOrder,  # TE_MISSING_ORDER_ID OrderId is missing
                      '11112': InvalidOrder,  # TE_QTY_TYPE_INVALID QtyType is invalid
                      '11113': BadRequest,  # TE_USER_ID_INVALID UserId is invalid
                      '11114': InvalidOrder,  # TE_ORDER_VALUE_TOO_LARGE Order value is too large
                      '11115': InvalidOrder,  # TE_ORDER_VALUE_TOO_SMALL Order value is too small
                      # not documented
                      '30018': BadRequest,  # {"code":30018,"msg":"phemex.data.size.uplimt","data":null}
                      '39996': PermissionDenied,  # {"code": "39996","msg": "Access denied."}
                  },
                  'broad': {
                      'Failed to find api-key': AuthenticationError,  # {"msg":"Failed to find api-key 1c5ec63fd-660d-43ea-847a-0d3ba69e106e","code":10500}
                      'Missing required parameter': BadRequest,  # {"msg":"Missing required parameter","code":10500}
                      'API Signature verification failed': AuthenticationError,  # {"msg":"API Signature verification failed.","code":10500}
                  },
              },
              'options': {
                  'x-phemex-request-expiry': 60,  # in seconds
                  'createOrderByQuoteRequiresPrice': True,
                  'networks': {
                      'TRC20': 'TRX',
                      'ERC20': 'ETH',
                  },
                  'defaultNetworks': {
                      'USDT': 'ETH',
                  },
              },
          })
  
      def parse_safe_number(self, value=None):
          if value is None:
              return value
          value = value.replace(',', '')
          parts = value.split(' ')
          return self.safe_number(parts, 0)
  
      def parse_swap_market(self, market):
          #
          #     {
          #         "symbol":"BTCUSD",
          #         "displaySymbol":"BTC / USD",
          #         "indexSymbol":".BTC",
          #         "markSymbol":".MBTC",
          #         "fundingRateSymbol":".BTCFR",
          #         "fundingRate8hSymbol":".BTCFR8H",
          #         "contractUnderlyingAssets":"USD",
          #         "settleCurrency":"BTC",
          #         "quoteCurrency":"USD",
          #         "contractSize":"1 USD",
          #         "lotSize":1,
          #         "tickSize":0.5,
          #         "priceScale":4,
          #         "ratioScale":8,
          #         "pricePrecision":1,
          #         "minPriceEp":5000,
          #         "maxPriceEp":10000000000,
          #         "maxOrderQty":1000000,
          #         "type":"Perpetual",
          #         "status":"Listed",
          #         "tipOrderQty":1000000,
          #         "steps":"50",
          #         "riskLimits":[
          #             {"limit":100,"initialMargin":"1.0%","initialMarginEr":1000000,"maintenanceMargin":"0.5%","maintenanceMarginEr":500000},
          #             {"limit":150,"initialMargin":"1.5%","initialMarginEr":1500000,"maintenanceMargin":"1.0%","maintenanceMarginEr":1000000},
          #             {"limit":200,"initialMargin":"2.0%","initialMarginEr":2000000,"maintenanceMargin":"1.5%","maintenanceMarginEr":1500000},
          #         ],
          #         "underlyingSymbol":".BTC",
          #         "baseCurrency":"BTC",
          #         "settlementCurrency":"BTC",
          #         "valueScale":8,
          #         "defaultLeverage":0,
          #         "maxLeverage":100,
          #         "initMarginEr":"1000000",
          #         "maintMarginEr":"500000",
          #         "defaultRiskLimitEv":10000000000,
          #         "deleverage":true,
          #         "makerFeeRateEr":-250000,
          #         "takerFeeRateEr":750000,
          #         "fundingInterval":8,
          #         "marketUrl":"https://phemex.com/trade/BTCUSD",
          #         "description":"BTCUSD is a BTC/USD perpetual contract priced on the .BTC Index. Each contract is worth 1 USD of Bitcoin. Funding is paid and received every 8 hours. At UTC time: 00:00, 08:00, 16:00.",
          #     }
          #
          id = self.safe_string(market, 'symbol')
          baseId = self.safe_string_2(market, 'baseCurrency', 'contractUnderlyingAssets')
          quoteId = self.safe_string(market, 'quoteCurrency')
          base = self.safe_currency_code(baseId)
          quote = self.safe_currency_code(quoteId)
          type = self.safe_string_lower(market, 'type')
          inverse = False
          spot = False
          swap = True
          settlementCurrencyId = self.safe_string(market, 'settleCurrency')
          if settlementCurrencyId != quoteId:
              inverse = True
          linear = not inverse
          symbol = id if (inverse) else (base + '/' + quote)  # fix for uBTCUSD inverse
          precision = {
              'amount': self.safe_number(market, 'lotSize'),
              'price': self.safe_number(market, 'tickSize'),
          }
          priceScale = self.safe_integer(market, 'priceScale')
          ratioScale = self.safe_integer(market, 'ratioScale')
          valueScale = self.safe_integer(market, 'valueScale')
          minPriceEp = self.safe_string(market, 'minPriceEp')
          maxPriceEp = self.safe_string(market, 'maxPriceEp')
          makerFeeRateEr = self.safe_string(market, 'makerFeeRateEr')
          takerFeeRateEr = self.safe_string(market, 'takerFeeRateEr')
          maker = self.parse_number(self.from_en(makerFeeRateEr, ratioScale))
          taker = self.parse_number(self.from_en(takerFeeRateEr, ratioScale))
          limits = {
              'amount': {
                  'min': precision['amount'],
                  'max': None,
              },
              'price': {
                  'min': self.parse_number(self.from_en(minPriceEp, priceScale)),
                  'max': self.parse_number(self.from_en(maxPriceEp, priceScale)),
              },
              'cost': {
                  'min': None,
                  'max': self.parse_number(self.safe_string(market, 'maxOrderQty')),
              },
          }
          status = self.safe_string(market, 'status')
          active = status == 'Listed'
          return {
              'id': id,
              'symbol': symbol,
              'base': base,
              'quote': quote,
              'baseId': baseId,
              'quoteId': quoteId,
              'info': market,
              'type': type,
              'spot': spot,
              'swap': swap,
              'linear': linear,
              'inverse': inverse,
              'active': active,
              'taker': taker,
              'maker': maker,
              'priceScale': priceScale,
              'valueScale': valueScale,
              'ratioScale': ratioScale,
              'precision': precision,
              'limits': limits,
          }
  
      def parse_spot_market(self, market):
          #
          #     {
          #         "symbol":"sBTCUSDT",
          #         "displaySymbol":"BTC / USDT",
          #         "quoteCurrency":"USDT",
          #         "pricePrecision":2,
          #         "type":"Spot",
          #         "baseCurrency":"BTC",
          #         "baseTickSize":"0.000001 BTC",
          #         "baseTickSizeEv":100,
          #         "quoteTickSize":"0.01 USDT",
          #         "quoteTickSizeEv":1000000,
          #         "minOrderValue":"10 USDT",
          #         "minOrderValueEv":1000000000,
          #         "maxBaseOrderSize":"1000 BTC",
          #         "maxBaseOrderSizeEv":100000000000,
          #         "maxOrderValue":"5,000,000 USDT",
          #         "maxOrderValueEv":500000000000000,
          #         "defaultTakerFee":"0.001",
          #         "defaultTakerFeeEr":100000,
          #         "defaultMakerFee":"0.001",
          #         "defaultMakerFeeEr":100000,
          #         "baseQtyPrecision":6,
          #         "quoteQtyPrecision":2,
          #         "status":"Listed",
          #         "tipOrderQty":20
          #     }
          #
          type = self.safe_string_lower(market, 'type')
          id = self.safe_string(market, 'symbol')
          quoteId = self.safe_string(market, 'quoteCurrency')
          baseId = self.safe_string(market, 'baseCurrency')
          linear = None
          inverse = None
          spot = True
          swap = False
          taker = self.safe_number(market, 'defaultTakerFee')
          maker = self.safe_number(market, 'defaultMakerFee')
          precision = {
              'amount': self.parse_safe_number(self.safe_string(market, 'baseTickSize')),
              'price': self.parse_safe_number(self.safe_string(market, 'quoteTickSize')),
          }
          limits = {
              'amount': {
                  'min': precision['amount'],
                  'max': self.parse_safe_number(self.safe_string(market, 'maxBaseOrderSize')),
              },
              'price': {
                  'min': precision['price'],
                  'max': None,
              },
              'cost': {
                  'min': self.parse_safe_number(self.safe_string(market, 'minOrderValue')),
                  'max': self.parse_safe_number(self.safe_string(market, 'maxOrderValue')),
              },
          }
          base = self.safe_currency_code(baseId)
          quote = self.safe_currency_code(quoteId)
          symbol = base + '/' + quote
          status = self.safe_string(market, 'status')
          active = status == 'Listed'
          return {
              'id': id,
              'symbol': symbol,
              'base': base,
              'quote': quote,
              'baseId': baseId,
              'quoteId': quoteId,
              'info': market,
              'type': type,
              'spot': spot,
              'swap': swap,
              'linear': linear,
              'inverse': inverse,
              'active': active,
              'taker': taker,
              'maker': maker,
              'precision': precision,
              'priceScale': 8,
              'valueScale': 8,
              'ratioScale': 8,
              'limits': limits,
          }
  
      def fetch_markets(self, params={}):
          v2Products = self.publicGetCfgV2Products(params)
          #
          #     {
          #         "code":0,
          #         "msg":"OK",
          #         "data":{
          #             "ratioScale":8,
          #             "currencies":[
          #                 {"currency":"BTC","valueScale":8,"minValueEv":1,"maxValueEv":5000000000000000000,"name":"Bitcoin"},
          #                 {"currency":"USD","valueScale":4,"minValueEv":1,"maxValueEv":500000000000000,"name":"USD"},
          #                 {"currency":"USDT","valueScale":8,"minValueEv":1,"maxValueEv":5000000000000000000,"name":"TetherUS"},
          #             ],
          #             "products":[
          #                 {
          #                     "symbol":"BTCUSD",
          #                     "displaySymbol":"BTC / USD",
          #                     "indexSymbol":".BTC",
          #                     "markSymbol":".MBTC",
          #                     "fundingRateSymbol":".BTCFR",
          #                     "fundingRate8hSymbol":".BTCFR8H",
          #                     "contractUnderlyingAssets":"USD",
          #                     "settleCurrency":"BTC",
          #                     "quoteCurrency":"USD",
          #                     "contractSize":1.0,
          #                     "lotSize":1,
          #                     "tickSize":0.5,
          #                     "priceScale":4,
          #                     "ratioScale":8,
          #                     "pricePrecision":1,
          #                     "minPriceEp":5000,
          #                     "maxPriceEp":10000000000,
          #                     "maxOrderQty":1000000,
          #                     "type":"Perpetual"
          #                 },
          #                 {
          #                     "symbol":"sBTCUSDT",
          #                     "displaySymbol":"BTC / USDT",
          #                     "quoteCurrency":"USDT",
          #                     "pricePrecision":2,
          #                     "type":"Spot",
          #                     "baseCurrency":"BTC",
          #                     "baseTickSize":"0.000001 BTC",
          #                     "baseTickSizeEv":100,
          #                     "quoteTickSize":"0.01 USDT",
          #                     "quoteTickSizeEv":1000000,
          #                     "minOrderValue":"10 USDT",
          #                     "minOrderValueEv":1000000000,
          #                     "maxBaseOrderSize":"1000 BTC",
          #                     "maxBaseOrderSizeEv":100000000000,
          #                     "maxOrderValue":"5,000,000 USDT",
          #                     "maxOrderValueEv":500000000000000,
          #                     "defaultTakerFee":"0.001",
          #                     "defaultTakerFeeEr":100000,
          #                     "defaultMakerFee":"0.001",
          #                     "defaultMakerFeeEr":100000,
          #                     "baseQtyPrecision":6,
          #                     "quoteQtyPrecision":2
          #                 },
          #             ],
          #             "riskLimits":[
          #                 {
          #                     "symbol":"BTCUSD",
          #                     "steps":"50",
          #                     "riskLimits":[
          #                         {"limit":100,"initialMargin":"1.0%","initialMarginEr":1000000,"maintenanceMargin":"0.5%","maintenanceMarginEr":500000},
          #                         {"limit":150,"initialMargin":"1.5%","initialMarginEr":1500000,"maintenanceMargin":"1.0%","maintenanceMarginEr":1000000},
          #                         {"limit":200,"initialMargin":"2.0%","initialMarginEr":2000000,"maintenanceMargin":"1.5%","maintenanceMarginEr":1500000},
          #                     ]
          #                 },
          #             ],
          #             "leverages":[
          #                 {"initialMargin":"1.0%","initialMarginEr":1000000,"options":[1,2,3,5,10,25,50,100]},
          #                 {"initialMargin":"1.5%","initialMarginEr":1500000,"options":[1,2,3,5,10,25,50,66]},
          #                 {"initialMargin":"2.0%","initialMarginEr":2000000,"options":[1,2,3,5,10,25,33,50]},
          #             ]
          #         }
          #     }
          #
          v1Products = self.v1GetExchangePublicProducts(params)
          v1ProductsData = self.safe_value(v1Products, 'data', [])
          #
          #     {
          #         "code":0,
          #         "msg":"OK",
          #         "data":[
          #             {
          #                 "symbol":"BTCUSD",
          #                 "underlyingSymbol":".BTC",
          #                 "quoteCurrency":"USD",
          #                 "baseCurrency":"BTC",
          #                 "settlementCurrency":"BTC",
          #                 "maxOrderQty":1000000,
          #                 "maxPriceEp":100000000000000,
          #                 "lotSize":1,
          #                 "tickSize":"0.5",
          #                 "contractSize":"1 USD",
          #                 "priceScale":4,
          #                 "ratioScale":8,
          #                 "valueScale":8,
          #                 "defaultLeverage":0,
          #                 "maxLeverage":100,
          #                 "initMarginEr":"1000000",
          #                 "maintMarginEr":"500000",
          #                 "defaultRiskLimitEv":10000000000,
          #                 "deleverage":true,
          #                 "makerFeeRateEr":-250000,
          #                 "takerFeeRateEr":750000,
          #                 "fundingInterval":8,
          #                 "marketUrl":"https://phemex.com/trade/BTCUSD",
          #                 "description":"BTCUSD is a BTC/USD perpetual contract priced on the .BTC Index. Each contract is worth 1 USD of Bitcoin. Funding is paid and received every 8 hours. At UTC time: 00:00, 08:00, 16:00.",
          #                 "type":"Perpetual"
          #             },
          #         ]
          #     }
          #
          v2ProductsData = self.safe_value(v2Products, 'data', {})
          products = self.safe_value(v2ProductsData, 'products', [])
          riskLimits = self.safe_value(v2ProductsData, 'riskLimits', [])
          riskLimitsById = self.index_by(riskLimits, 'symbol')
          v1ProductsById = self.index_by(v1ProductsData, 'symbol')
          result = []
          for i in range(0, len(products)):
              market = products[i]
              type = self.safe_string_lower(market, 'type')
              if type == 'perpetual':
                  id = self.safe_string(market, 'symbol')
                  riskLimitValues = self.safe_value(riskLimitsById, id, {})
                  market = self.extend(market, riskLimitValues)
                  v1ProductsValues = self.safe_value(v1ProductsById, id, {})
                  market = self.extend(market, v1ProductsValues)
                  market = self.parse_swap_market(market)
              else:
                  market = self.parse_spot_market(market)
              result.append(market)
          return result
  
      def fetch_currencies(self, params={}):
          response = self.publicGetCfgV2Products(params)
          #
          #     {
          #         "code":0,
          #         "msg":"OK",
          #         "data":{
          #             ...,
          #             "currencies":[
          #                 {"currency":"BTC","valueScale":8,"minValueEv":1,"maxValueEv":5000000000000000000,"name":"Bitcoin"},
          #                 {"currency":"USD","valueScale":4,"minValueEv":1,"maxValueEv":500000000000000,"name":"USD"},
          #                 {"currency":"USDT","valueScale":8,"minValueEv":1,"maxValueEv":5000000000000000000,"name":"TetherUS"},
          #             ],
          #             ...
          #         }
          #     }
          data = self.safe_value(response, 'data', {})
          currencies = self.safe_value(data, 'currencies', [])
          result = {}
          for i in range(0, len(currencies)):
              currency = currencies[i]
              id = self.safe_string(currency, 'currency')
              name = self.safe_string(currency, 'name')
              code = self.safe_currency_code(id)
              valueScaleString = self.safe_string(currency, 'valueScale')
              valueScale = int(valueScaleString)
              minValueEv = self.safe_string(currency, 'minValueEv')
              maxValueEv = self.safe_string(currency, 'maxValueEv')
              minAmount = None
              maxAmount = None
              precision = None
              if valueScale is not None:
                  precisionString = self.parse_precision(valueScaleString)
                  precision = self.parse_number(precisionString)
                  minAmount = self.parse_number(Precise.string_mul(minValueEv, precisionString))
                  maxAmount = self.parse_number(Precise.string_mul(maxValueEv, precisionString))
              result[code] = {
                  'id': id,
                  'info': currency,
                  'code': code,
                  'name': name,
                  'active': None,
                  'fee': None,
                  'precision': precision,
                  'limits': {
                      'amount': {
                          'min': minAmount,
                          'max': maxAmount,
                      },
                      'withdraw': {
                          'min': None,
                          'max': None,
                      },
                  },
                  'valueScale': valueScale,
              }
          return result
  
      def parse_bid_ask(self, bidask, priceKey=0, amountKey=1, market=None):
          if market is None:
              raise ArgumentsRequired(self.id + ' parseBidAsk() requires a market argument')
          amount = self.safe_string(bidask, amountKey)
          if market['spot']:
              amount = self.from_ev(amount, market)
          return [
              self.parse_number(self.from_ep(self.safe_string(bidask, priceKey), market)),
              self.parse_number(amount),
          ]
  
      def parse_order_book(self, orderbook, symbol, timestamp=None, bidsKey='bids', asksKey='asks', priceKey=0, amountKey=1, market=None):
          result = {
              'symbol': symbol,
              'timestamp': timestamp,
              'datetime': self.iso8601(timestamp),
              'nonce': None,
          }
          sides = [bidsKey, asksKey]
          for i in range(0, len(sides)):
              side = sides[i]
              orders = []
              bidasks = self.safe_value(orderbook, side)
              for k in range(0, len(bidasks)):
                  orders.append(self.parse_bid_ask(bidasks[k], priceKey, amountKey, market))
              result[side] = orders
          result[bidsKey] = self.sort_by(result[bidsKey], 0, True)
          result[asksKey] = self.sort_by(result[asksKey], 0)
          return result
  
      def fetch_order_book(self, symbol, limit=None, params={}):
          self.load_markets()
          market = self.market(symbol)
          request = {
              'symbol': market['id'],
              # 'id': 123456789,  # optional request id
          }
          response = self.v1GetMdOrderbook(self.extend(request, params))
          #
          #     {
          #         "error": null,
          #         "id": 0,
          #         "result": {
          #             "book": {
          #                 "asks": [
          #                     [23415000000, 105262000],
          #                     [23416000000, 147914000],
          #                     [23419000000, 160914000],
          #                 ],
          #                 "bids": [
          #                     [23360000000, 32995000],
          #                     [23359000000, 221887000],
          #                     [23356000000, 284599000],
          #                 ],
          #             },
          #             "depth": 30,
          #             "sequence": 1592059928,
          #             "symbol": "sETHUSDT",
          #             "timestamp": 1592387340020000955,
          #             "type": "snapshot"
          #         }
          #     }
          #
          result = self.safe_value(response, 'result', {})
          book = self.safe_value(result, 'book', {})
          timestamp = self.safe_integer_product(result, 'timestamp', 0.000001)
          orderbook = self.parse_order_book(book, symbol, timestamp, 'bids', 'asks', 0, 1, market)
          orderbook['nonce'] = self.safe_integer(result, 'sequence')
          return orderbook
  
      def to_en(self, n, scale):
          stringN = str(n)
          precise = Precise(stringN)
          precise.decimals = precise.decimals - scale
          precise.reduce()
          stringValue = str(precise)
          return int(float(stringValue))
  
      def to_ev(self, amount, market=None):
          if (amount is None) or (market is None):
              return amount
          return self.to_en(amount, market['valueScale'])
  
      def to_ep(self, price, market=None):
          if (price is None) or (market is None):
              return price
          return self.to_en(price, market['priceScale'])
  
      def from_en(self, en, scale):
          if en is None:
              return None
          precise = Precise(en)
          precise.decimals = self.sum(precise.decimals, scale)
          precise.reduce()
          return str(precise)
  
      def from_ep(self, ep, market=None):
          if (ep is None) or (market is None):
              return ep
          return self.from_en(ep, self.safe_integer(market, 'priceScale'))
  
      def from_ev(self, ev, market=None):
          if (ev is None) or (market is None):
              return ev
          return self.from_en(ev, self.safe_integer(market, 'valueScale'))
  
      def from_er(self, er, market=None):
          if (er is None) or (market is None):
              return er
          return self.from_en(er, self.safe_integer(market, 'ratioScale'))
  
      def parse_ohlcv(self, ohlcv, market=None):
          #
          #     [
          #         1592467200,  # timestamp
          #         300,  # interval
          #         23376000000,  # last
          #         23322000000,  # open
          #         23381000000,  # high
          #         23315000000,  # low
          #         23367000000,  # close
          #         208671000,  # base volume
          #         48759063370,  # quote volume
          #     ]
          #
          baseVolume = None
          if (market is not None) and market['spot']:
              baseVolume = self.parse_number(self.from_ev(self.safe_string(ohlcv, 7), market))
          else:
              baseVolume = self.safe_number(ohlcv, 7)
          return [
              self.safe_timestamp(ohlcv, 0),
              self.parse_number(self.from_ep(self.safe_string(ohlcv, 3), market)),
              self.parse_number(self.from_ep(self.safe_string(ohlcv, 4), market)),
              self.parse_number(self.from_ep(self.safe_string(ohlcv, 5), market)),
              self.parse_number(self.from_ep(self.safe_string(ohlcv, 6), market)),
              baseVolume,
          ]
  
      def fetch_ohlcv(self, symbol, timeframe='1m', since=None, limit=None, params={}):
          request = {
              # 'symbol': market['id'],
              'resolution': self.timeframes[timeframe],
              # 'from': 1588830682,  # seconds
              # 'to': self.seconds(),
          }
          duration = self.parse_timeframe(timeframe)
          now = self.seconds()
          if since is not None:
              if limit is None:
                  limit = 2000  # max 2000
              since = int(since / 1000)
              request['from'] = since
              # time ranges ending in the future are not accepted
              # https://github.com/ccxt/ccxt/issues/8050
              request['to'] = min(now, self.sum(since, duration * limit))
          elif limit is not None:
              limit = min(limit, 2000)
              request['from'] = now - duration * self.sum(limit, 1)
              request['to'] = now
          else:
              raise ArgumentsRequired(self.id + ' fetchOHLCV() requires a since argument, or a limit argument, or both')
          self.load_markets()
          market = self.market(symbol)
          request['symbol'] = market['id']
          response = self.publicGetMdKline(self.extend(request, params))
          #
          #     {
          #         "code":0,
          #         "msg":"OK",
          #         "data":{
          #             "total":-1,
          #             "rows":[
          #                 [1592467200,300,23376000000,23322000000,23381000000,23315000000,23367000000,208671000,48759063370],
          #                 [1592467500,300,23367000000,23314000000,23390000000,23311000000,23331000000,234820000,54848948710],
          #                 [1592467800,300,23331000000,23385000000,23391000000,23326000000,23387000000,152931000,35747882250],
          #             ]
          #         }
          #     }
          #
          data = self.safe_value(response, 'data', {})
          rows = self.safe_value(data, 'rows', [])
          return self.parse_ohlcvs(rows, market, timeframe, since, limit)
  
      def parse_ticker(self, ticker, market=None):
          #
          # spot
          #
          #     {
          #         "askEp": 943836000000,
          #         "bidEp": 943601000000,
          #         "highEp": 955946000000,
          #         "lastEp": 943803000000,
          #         "lowEp": 924973000000,
          #         "openEp": 948693000000,
          #         "symbol": "sBTCUSDT",
          #         "timestamp": 1592471203505728630,
          #         "turnoverEv": 111822826123103,
          #         "volumeEv": 11880532281
          #     }
          #
          # swap
          #
          #     {
          #         "askEp": 2332500,
          #         "bidEp": 2331000,
          #         "fundingRateEr": 10000,
          #         "highEp": 2380000,
          #         "indexEp": 2329057,
          #         "lastEp": 2331500,
          #         "lowEp": 2274000,
          #         "markEp": 2329232,
          #         "openEp": 2337500,
          #         "openInterest": 1298050,
          #         "predFundingRateEr": 19921,
          #         "symbol": "ETHUSD",
          #         "timestamp": 1592474241582701416,
          #         "turnoverEv": 47228362330,
          #         "volume": 4053863
          #     }
          #
          marketId = self.safe_string(ticker, 'symbol')
          market = self.safe_market(marketId, market)
          symbol = market['symbol']
          timestamp = self.safe_integer_product(ticker, 'timestamp', 0.000001)
          lastString = self.from_ep(self.safe_string(ticker, 'lastEp'), market)
          last = self.parse_number(lastString)
          quoteVolume = self.parse_number(self.from_ev(self.safe_string(ticker, 'turnoverEv'), market))
          baseVolume = self.safe_number(ticker, 'volume')
          if baseVolume is None:
              baseVolume = self.parse_number(self.from_ev(self.safe_string(ticker, 'volumeEv'), market))
          vwap = None
          if (market is not None) and (market['spot']):
              vwap = self.vwap(baseVolume, quoteVolume)
          change = None
          percentage = None
          average = None
          openString = self.from_ep(self.safe_string(ticker, 'openEp'), market)
          open = self.parse_number(openString)
          if (openString is not None) and (lastString is not None):
              change = self.parse_number(Precise.string_sub(lastString, openString))
              average = self.parse_number(Precise.string_div(Precise.string_add(lastString, openString), '2'))
              percentage = self.parse_number(Precise.string_mul(Precise.string_sub(Precise.string_div(lastString, openString), '1'), '100'))
          result = {
              'symbol': symbol,
              'timestamp': timestamp,
              'datetime': self.iso8601(timestamp),
              'high': self.parse_number(self.from_ep(self.safe_string(ticker, 'highEp'), market)),
              'low': self.parse_number(self.from_ep(self.safe_string(ticker, 'lowEp'), market)),
              'bid': self.parse_number(self.from_ep(self.safe_string(ticker, 'bidEp'), market)),
              'bidVolume': None,
              'ask': self.parse_number(self.from_ep(self.safe_string(ticker, 'askEp'), market)),
              'askVolume': None,
              'vwap': vwap,
              'open': open,
              'close': last,
              'last': last,
              'previousClose': None,  # previous day close
              'change': change,
              'percentage': percentage,
              'average': average,
              'baseVolume': baseVolume,
              'quoteVolume': quoteVolume,
              'info': ticker,
          }
          return result
  
      def fetch_ticker(self, symbol, params={}):
          self.load_markets()
          market = self.market(symbol)
          request = {
              'symbol': market['id'],
              # 'id': 123456789,  # optional request id
          }
          method = 'v1GetMdSpotTicker24hr' if market['spot'] else 'v1GetMdTicker24hr'
          response = getattr(self, method)(self.extend(request, params))
          #
          # spot
          #
          #     {
          #         "error": null,
          #         "id": 0,
          #         "result": {
          #             "askEp": 943836000000,
          #             "bidEp": 943601000000,
          #             "highEp": 955946000000,
          #             "lastEp": 943803000000,
          #             "lowEp": 924973000000,
          #             "openEp": 948693000000,
          #             "symbol": "sBTCUSDT",
          #             "timestamp": 1592471203505728630,
          #             "turnoverEv": 111822826123103,
          #             "volumeEv": 11880532281
          #         }
          #     }
          #
          # swap
          #
          #     {
          #         "error": null,
          #         "id": 0,
          #         "result": {
          #             "askEp": 2332500,
          #             "bidEp": 2331000,
          #             "fundingRateEr": 10000,
          #             "highEp": 2380000,
          #             "indexEp": 2329057,
          #             "lastEp": 2331500,
          #             "lowEp": 2274000,
          #             "markEp": 2329232,
          #             "openEp": 2337500,
          #             "openInterest": 1298050,
          #             "predFundingRateEr": 19921,
          #             "symbol": "ETHUSD",
          #             "timestamp": 1592474241582701416,
          #             "turnoverEv": 47228362330,
          #             "volume": 4053863
          #         }
          #     }
          #
          result = self.safe_value(response, 'result', {})
          return self.parse_ticker(result, market)
  
      def fetch_trades(self, symbol, since=None, limit=None, params={}):
          self.load_markets()
          market = self.market(symbol)
          request = {
              'symbol': market['id'],
              # 'id': 123456789,  # optional request id
          }
          response = self.v1GetMdTrade(self.extend(request, params))
          #
          #     {
          #         "error": null,
          #         "id": 0,
          #         "result": {
          #             "sequence": 1315644947,
          #             "symbol": "BTCUSD",
          #             "trades": [
          #                 [1592541746712239749, 13156448570000, "Buy", 93070000, 40173],
          #                 [1592541740434625085, 13156447110000, "Sell", 93065000, 5000],
          #                 [1592541732958241616, 13156441390000, "Buy", 93070000, 3460],
          #             ],
          #             "type": "snapshot"
          #         }
          #     }
          #
          result = self.safe_value(response, 'result', {})
          trades = self.safe_value(result, 'trades', [])
          return self.parse_trades(trades, market, since, limit)
  
      def parse_trade(self, trade, market=None):
          #
          # fetchTrades(public)
          #
          #     [
          #         1592541746712239749,
          #         13156448570000,
          #         "Buy",
          #         93070000,
          #         40173
          #     ]
          #
          # fetchMyTrades(private)
          #
          # spot
          #
          #     {
          #         "qtyType": "ByQuote",
          #         "transactTimeNs": 1589450974800550100,
          #         "clOrdID": "8ba59d40-df25-d4b0-14cf-0703f44e9690",
          #         "orderID": "b2b7018d-f02f-4c59-b4cf-051b9c2d2e83",
          #         "symbol": "sBTCUSDT",
          #         "side": "Buy",
          #         "priceEP": 970056000000,
          #         "baseQtyEv": 0,
          #         "quoteQtyEv": 1000000000,
          #         "action": "New",
          #         "execStatus": "MakerFill",
          #         "ordStatus": "Filled",
          #         "ordType": "Limit",
          #         "execInst": "None",
          #         "timeInForce": "GoodTillCancel",
          #         "stopDirection": "UNSPECIFIED",
          #         "tradeType": "Trade",
          #         "stopPxEp": 0,
          #         "execId": "c6bd8979-07ba-5946-b07e-f8b65135dbb1",
          #         "execPriceEp": 970056000000,
          #         "execBaseQtyEv": 103000,
          #         "execQuoteQtyEv": 999157680,
          #         "leavesBaseQtyEv": 0,
          #         "leavesQuoteQtyEv": 0,
          #         "execFeeEv": 0,
          #         "feeRateEr": 0
          #     }
          #
          # swap
          #
          #     {
          #         "transactTimeNs": 1578026629824704800,
          #         "symbol": "BTCUSD",
          #         "currency": "BTC",
          #         "action": "Replace",
          #         "side": "Sell",
          #         "tradeType": "Trade",
          #         "execQty": 700,
          #         "execPriceEp": 71500000,
          #         "orderQty": 700,
          #         "priceEp": 71500000,
          #         "execValueEv": 9790209,
          #         "feeRateEr": -25000,
          #         "execFeeEv": -2447,
          #         "ordType": "Limit",
          #         "execID": "b01671a1-5ddc-5def-b80a-5311522fd4bf",
          #         "orderID": "b63bc982-be3a-45e0-8974-43d6375fb626",
          #         "clOrdID": "uuid-1577463487504",
          #         "execStatus": "MakerFill"
          #     }
          #
          priceString = None
          amountString = None
          timestamp = None
          id = None
          side = None
          costString = None
          type = None
          fee = None
          marketId = self.safe_string(trade, 'symbol')
          market = self.safe_market(marketId, market)
          symbol = market['symbol']
          orderId = None
          takerOrMaker = None
          if isinstance(trade, list):
              tradeLength = len(trade)
              timestamp = self.safe_integer_product(trade, 0, 0.000001)
              if tradeLength > 4:
                  id = self.safe_string(trade, tradeLength - 4)
              side = self.safe_string_lower(trade, tradeLength - 3)
              priceString = self.from_ep(self.safe_string(trade, tradeLength - 2), market)
              amountString = self.from_ev(self.safe_string(trade, tradeLength - 1), market)
          else:
              timestamp = self.safe_integer_product(trade, 'transactTimeNs', 0.000001)
              id = self.safe_string_2(trade, 'execId', 'execID')
              orderId = self.safe_string(trade, 'orderID')
              side = self.safe_string_lower(trade, 'side')
              type = self.parse_order_type(self.safe_string(trade, 'ordType'))
              execStatus = self.safe_string(trade, 'execStatus')
              if execStatus == 'MakerFill':
                  takerOrMaker = 'maker'
              priceString = self.from_ep(self.safe_string(trade, 'execPriceEp'), market)
              amountString = self.from_ev(self.safe_string(trade, 'execBaseQtyEv'), market)
              amountString = self.safe_string(trade, 'execQty', amountString)
              costString = self.from_ev(self.safe_string_2(trade, 'execQuoteQtyEv', 'execValueEv'), market)
              feeCostString = self.from_ev(self.safe_string(trade, 'execFeeEv'), market)
              if feeCostString is not None:
                  feeRateString = self.from_er(self.safe_string(trade, 'feeRateEr'), market)
                  feeCurrencyCode = None
                  if market['spot']:
                      feeCurrencyCode = market['base'] if (side == 'buy') else market['quote']
                  else:
                      info = self.safe_value(market, 'info')
                      if info is not None:
                          settlementCurrencyId = self.safe_string(info, 'settlementCurrency')
                          feeCurrencyCode = self.safe_currency_code(settlementCurrencyId)
                  fee = {
                      'cost': self.parse_number(feeCostString),
                      'rate': self.parse_number(feeRateString),
                      'currency': feeCurrencyCode,
                  }
          price = self.parse_number(priceString)
          amount = self.parse_number(amountString)
          if costString is None:
              costString = Precise.string_mul(priceString, amountString)
          cost = self.parse_number(costString)
          return {
              'info': trade,
              'id': id,
              'symbol': symbol,
              'timestamp': timestamp,
              'datetime': self.iso8601(timestamp),
              'order': orderId,
              'type': type,
              'side': side,
              'takerOrMaker': takerOrMaker,
              'price': price,
              'amount': amount,
              'cost': cost,
              'fee': fee,
          }
  
      def parse_spot_balance(self, response):
          #
          #     {
          #         "code":0,
          #         "msg":"",
          #         "data":[
          #             {
          #                 "currency":"USDT",
          #                 "balanceEv":0,
          #                 "lockedTradingBalanceEv":0,
          #                 "lockedWithdrawEv":0,
          #                 "lastUpdateTimeNs":1592065834511322514,
          #                 "walletVid":0
          #             },
          #             {
          #                 "currency":"ETH",
          #                 "balanceEv":0,
          #                 "lockedTradingBalanceEv":0,
          #                 "lockedWithdrawEv":0,
          #                 "lastUpdateTimeNs":1592065834511322514,
          #                 "walletVid":0
          #             }
          #         ]
          #     }
          #
          timestamp = None
          result = {'info': response}
          data = self.safe_value(response, 'data', [])
          for i in range(0, len(data)):
              balance = data[i]
              currencyId = self.safe_string(balance, 'currency')
              code = self.safe_currency_code(currencyId)
              currency = self.safe_value(self.currencies, code, {})
              scale = self.safe_integer(currency, 'valueScale', 8)
              account = self.account()
              balanceEv = self.safe_string(balance, 'balanceEv')
              lockedTradingBalanceEv = self.safe_string(balance, 'lockedTradingBalanceEv')
              lockedWithdrawEv = self.safe_string(balance, 'lockedWithdrawEv')
              total = self.from_en(balanceEv, scale)
              lockedTradingBalance = self.from_en(lockedTradingBalanceEv, scale)
              lockedWithdraw = self.from_en(lockedWithdrawEv, scale)
              used = Precise.string_add(lockedTradingBalance, lockedWithdraw)
              lastUpdateTimeNs = self.safe_integer_product(balance, 'lastUpdateTimeNs', 0.000001)
              timestamp = lastUpdateTimeNs if (timestamp is None) else max(timestamp, lastUpdateTimeNs)
              account['total'] = total
              account['used'] = used
              result[code] = account
          result['timestamp'] = timestamp
          result['datetime'] = self.iso8601(timestamp)
          return self.parse_balance(result)
  
      def parse_swap_balance(self, response):
          #
          #     {
          #         "code":0,
          #         "msg":"",
          #         "data":{
          #             "account":{
          #                 "accountId":6192120001,
          #                 "currency":"BTC",
          #                 "accountBalanceEv":1254744,
          #                 "totalUsedBalanceEv":0,
          #                 "bonusBalanceEv":1254744
          #             },
          #             "positions":[
          #                 {
          #                     "accountID":6192120001,
          #                     "symbol":"BTCUSD",
          #                     "currency":"BTC",
          #                     "side":"None",
          #                     "positionStatus":"Normal",
          #                     "crossMargin":false,
          #                     "leverageEr":0,
          #                     "leverage":0E-8,
          #                     "initMarginReqEr":1000000,
          #                     "initMarginReq":0.01000000,
          #                     "maintMarginReqEr":500000,
          #                     "maintMarginReq":0.00500000,
          #                     "riskLimitEv":10000000000,
          #                     "riskLimit":100.00000000,
          #                     "size":0,
          #                     "value":0E-8,
          #                     "valueEv":0,
          #                     "avgEntryPriceEp":0,
          #                     "avgEntryPrice":0E-8,
          #                     "posCostEv":0,
          #                     "posCost":0E-8,
          #                     "assignedPosBalanceEv":0,
          #                     "assignedPosBalance":0E-8,
          #                     "bankruptCommEv":0,
          #                     "bankruptComm":0E-8,
          #                     "bankruptPriceEp":0,
          #                     "bankruptPrice":0E-8,
          #                     "positionMarginEv":0,
          #                     "positionMargin":0E-8,
          #                     "liquidationPriceEp":0,
          #                     "liquidationPrice":0E-8,
          #                     "deleveragePercentileEr":0,
          #                     "deleveragePercentile":0E-8,
          #                     "buyValueToCostEr":1150750,
          #                     "buyValueToCost":0.01150750,
          #                     "sellValueToCostEr":1149250,
          #                     "sellValueToCost":0.01149250,
          #                     "markPriceEp":96359083,
          #                     "markPrice":9635.90830000,
          #                     "markValueEv":0,
          #                     "markValue":null,
          #                     "unRealisedPosLossEv":0,
          #                     "unRealisedPosLoss":null,
          #                     "estimatedOrdLossEv":0,
          #                     "estimatedOrdLoss":0E-8,
          #                     "usedBalanceEv":0,
          #                     "usedBalance":0E-8,
          #                     "takeProfitEp":0,
          #                     "takeProfit":null,
          #                     "stopLossEp":0,
          #                     "stopLoss":null,
          #                     "realisedPnlEv":0,
          #                     "realisedPnl":null,
          #                     "cumRealisedPnlEv":0,
          #                     "cumRealisedPnl":null
          #                 }
          #             ]
          #         }
          #     }
          #
          result = {'info': response}
          data = self.safe_value(response, 'data', {})
          balance = self.safe_value(data, 'account', {})
          currencyId = self.safe_string(balance, 'currency')
          code = self.safe_currency_code(currencyId)
          currency = self.currency(code)
          account = self.account()
          accountBalanceEv = self.safe_string(balance, 'accountBalanceEv')
          totalUsedBalanceEv = self.safe_string(balance, 'totalUsedBalanceEv')
          valueScale = self.safe_integer(currency, 'valueScale', 8)
          account['total'] = self.from_en(accountBalanceEv, valueScale)
          account['used'] = self.from_en(totalUsedBalanceEv, valueScale)
          result[code] = account
          return self.parse_balance(result)
  
      def fetch_balance(self, params={}):
          self.load_markets()
          defaultType = self.safe_string_2(self.options, 'defaultType', 'fetchBalance', 'spot')
          type = self.safe_string(params, 'type', defaultType)
          method = 'privateGetSpotWallets'
          request = {}
          if type == 'swap':
              code = self.safe_string(params, 'code')
              if code is not None:
                  currency = self.currency(code)
                  request['currency'] = currency['id']
                  params = self.omit(params, 'code')
              else:
                  currency = self.safe_string(params, 'currency')
                  if currency is None:
                      raise ArgumentsRequired(self.id + ' fetchBalance() requires a code parameter or a currency parameter for ' + type + ' type')
              method = 'privateGetAccountsAccountPositions'
          params = self.omit(params, 'type')
          response = getattr(self, method)(self.extend(request, params))
          #
          # spot
          #
          #     {
          #         "code":0,
          #         "msg":"",
          #         "data":[
          #             {
          #                 "currency":"USDT",
          #                 "balanceEv":0,
          #                 "lockedTradingBalanceEv":0,
          #                 "lockedWithdrawEv":0,
          #                 "lastUpdateTimeNs":1592065834511322514,
          #                 "walletVid":0
          #             },
          #             {
          #                 "currency":"ETH",
          #                 "balanceEv":0,
          #                 "lockedTradingBalanceEv":0,
          #                 "lockedWithdrawEv":0,
          #                 "lastUpdateTimeNs":1592065834511322514,
          #                 "walletVid":0
          #             }
          #         ]
          #     }
          #
          # swap
          #
          #     {
          #         "code":0,
          #         "msg":"",
          #         "data":{
          #             "account":{
          #                 "accountId":6192120001,
          #                 "currency":"BTC",
          #                 "accountBalanceEv":1254744,
          #                 "totalUsedBalanceEv":0,
          #                 "bonusBalanceEv":1254744
          #             },
          #             "positions":[
          #                 {
          #                     "accountID":6192120001,
          #                     "symbol":"BTCUSD",
          #                     "currency":"BTC",
          #                     "side":"None",
          #                     "positionStatus":"Normal",
          #                     "crossMargin":false,
          #                     "leverageEr":0,
          #                     "leverage":0E-8,
          #                     "initMarginReqEr":1000000,
          #                     "initMarginReq":0.01000000,
          #                     "maintMarginReqEr":500000,
          #                     "maintMarginReq":0.00500000,
          #                     "riskLimitEv":10000000000,
          #                     "riskLimit":100.00000000,
          #                     "size":0,
          #                     "value":0E-8,
          #                     "valueEv":0,
          #                     "avgEntryPriceEp":0,
          #                     "avgEntryPrice":0E-8,
          #                     "posCostEv":0,
          #                     "posCost":0E-8,
          #                     "assignedPosBalanceEv":0,
          #                     "assignedPosBalance":0E-8,
          #                     "bankruptCommEv":0,
          #                     "bankruptComm":0E-8,
          #                     "bankruptPriceEp":0,
          #                     "bankruptPrice":0E-8,
          #                     "positionMarginEv":0,
          #                     "positionMargin":0E-8,
          #                     "liquidationPriceEp":0,
          #                     "liquidationPrice":0E-8,
          #                     "deleveragePercentileEr":0,
          #                     "deleveragePercentile":0E-8,
          #                     "buyValueToCostEr":1150750,
          #                     "buyValueToCost":0.01150750,
          #                     "sellValueToCostEr":1149250,
          #                     "sellValueToCost":0.01149250,
          #                     "markPriceEp":96359083,
          #                     "markPrice":9635.90830000,
          #                     "markValueEv":0,
          #                     "markValue":null,
          #                     "unRealisedPosLossEv":0,
          #                     "unRealisedPosLoss":null,
          #                     "estimatedOrdLossEv":0,
          #                     "estimatedOrdLoss":0E-8,
          #                     "usedBalanceEv":0,
          #                     "usedBalance":0E-8,
          #                     "takeProfitEp":0,
          #                     "takeProfit":null,
          #                     "stopLossEp":0,
          #                     "stopLoss":null,
          #                     "realisedPnlEv":0,
          #                     "realisedPnl":null,
          #                     "cumRealisedPnlEv":0,
          #                     "cumRealisedPnl":null
          #                 }
          #             ]
          #         }
          #     }
          #
          result = self.parse_swap_balance(response) if (type == 'swap') else self.parse_spot_balance(response)
          return result
  
      def parse_order_status(self, status):
          statuses = {
              'Created': 'open',
              'Untriggered': 'open',
              'Deactivated': 'closed',
              'Triggered': 'open',
              'Rejected': 'rejected',
              'New': 'open',
              'PartiallyFilled': 'open',
              'Filled': 'closed',
              'Canceled': 'canceled',
          }
          return self.safe_string(statuses, status, status)
  
      def parse_order_type(self, type):
          types = {
              'Limit': 'limit',
              'Market': 'market',
          }
          return self.safe_string(types, type, type)
  
      def parse_time_in_force(self, timeInForce):
          timeInForces = {
              'GoodTillCancel': 'GTC',
              'PostOnly': 'PO',
              'ImmediateOrCancel': 'IOC',
              'FillOrKill': 'FOK',
          }
          return self.safe_string(timeInForces, timeInForce, timeInForce)
  
      def parse_spot_order(self, order, market=None):
          #
          # spot
          #
          #     {
          #         "orderID": "d1d09454-cabc-4a23-89a7-59d43363f16d",
          #         "clOrdID": "309bcd5c-9f6e-4a68-b775-4494542eb5cb",
          #         "priceEp": 0,
          #         "action": "New",
          #         "trigger": "UNSPECIFIED",
          #         "pegPriceType": "UNSPECIFIED",
          #         "stopDirection": "UNSPECIFIED",
          #         "bizError": 0,
          #         "symbol": "sBTCUSDT",
          #         "side": "Buy",
          #         "baseQtyEv": 0,
          #         "ordType": "Limit",
          #         "timeInForce": "GoodTillCancel",
          #         "ordStatus": "Created",
          #         "cumFeeEv": 0,
          #         "cumBaseQtyEv": 0,
          #         "cumQuoteQtyEv": 0,
          #         "leavesBaseQtyEv": 0,
          #         "leavesQuoteQtyEv": 0,
          #         "avgPriceEp": 0,
          #         "cumBaseAmountEv": 0,
          #         "cumQuoteAmountEv": 0,
          #         "quoteQtyEv": 0,
          #         "qtyType": "ByBase",
          #         "stopPxEp": 0,
          #         "pegOffsetValueEp": 0
          #     }
          #
          #     {
          #         "orderID":"99232c3e-3d6a-455f-98cc-2061cdfe91bc",
          #         "stopPxEp":0,
          #         "avgPriceEp":0,
          #         "qtyType":"ByBase",
          #         "leavesBaseQtyEv":0,
          #         "leavesQuoteQtyEv":0,
          #         "baseQtyEv":"1000000000",
          #         "feeCurrency":"4",
          #         "stopDirection":"UNSPECIFIED",
          #         "symbol":"sETHUSDT",
          #         "side":"Buy",
          #         "quoteQtyEv":250000000000,
          #         "priceEp":25000000000,
          #         "ordType":"Limit",
          #         "timeInForce":"GoodTillCancel",
          #         "ordStatus":"Rejected",
          #         "execStatus":"NewRejected",
          #         "createTimeNs":1592675305266037130,
          #         "cumFeeEv":0,
          #         "cumBaseValueEv":0,
          #         "cumQuoteValueEv":0
          #     }
          #
          id = self.safe_string(order, 'orderID')
          clientOrderId = self.safe_string(order, 'clOrdID')
          if (clientOrderId is not None) and (len(clientOrderId) < 1):
              clientOrderId = None
          marketId = self.safe_string(order, 'symbol')
          symbol = self.safe_symbol(marketId, market)
          price = self.parse_number(self.omit_zero(self.from_ep(self.safe_string(order, 'priceEp'), market)))
          amount = self.parse_number(self.omit_zero(self.from_ev(self.safe_string(order, 'baseQtyEv'), market)))
          remaining = self.parse_number(self.omit_zero(self.from_ev(self.safe_string(order, 'leavesBaseQtyEv'), market)))
          filled = self.parse_number(self.omit_zero(self.from_ev(self.safe_string(order, 'cumBaseQtyEv'), market)))
          cost = self.parse_number(self.omit_zero(self.from_ev(self.safe_string(order, 'quoteQtyEv'), market)))
          average = self.parse_number(self.omit_zero(self.from_ep(self.safe_string(order, 'avgPriceEp'), market)))
          status = self.parse_order_status(self.safe_string(order, 'ordStatus'))
          side = self.safe_string_lower(order, 'side')
          type = self.parse_order_type(self.safe_string(order, 'ordType'))
          timestamp = self.safe_integer_product_2(order, 'actionTimeNs', 'createTimeNs', 0.000001)
          fee = None
          feeCost = self.parse_number(self.from_ev(self.safe_string(order, 'cumFeeEv'), market))
          if feeCost is not None:
              fee = {
                  'cost': feeCost,
                  'currency': None,
              }
          timeInForce = self.parse_time_in_force(self.safe_string(order, 'timeInForce'))
          stopPrice = self.parse_number(self.omit_zero(self.from_ep(self.safe_string(order, 'stopPxEp', market))))
          postOnly = (timeInForce == 'PO')
          return self.safe_order({
              'info': order,
              'id': id,
              'clientOrderId': clientOrderId,
              'timestamp': timestamp,
              'datetime': self.iso8601(timestamp),
              'lastTradeTimestamp': None,
              'symbol': symbol,
              'type': type,
              'timeInForce': timeInForce,
              'postOnly': postOnly,
              'side': side,
              'price': price,
              'stopPrice': stopPrice,
              'amount': amount,
              'cost': cost,
              'average': average,
              'filled': filled,
              'remaining': remaining,
              'status': status,
              'fee': fee,
              'trades': None,
          })
  
      def parse_swap_order(self, order, market=None):
          #
          #     {
          #         "bizError":0,
          #         "orderID":"7a1ad384-44a3-4e54-a102-de4195a29e32",
          #         "clOrdID":"",
          #         "symbol":"ETHUSD",
          #         "side":"Buy",
          #         "actionTimeNs":1592668973945065381,
          #         "transactTimeNs":0,
          #         "orderType":"Market",
          #         "priceEp":2267500,
          #         "price":226.75000000,
          #         "orderQty":1,
          #         "displayQty":0,
          #         "timeInForce":"ImmediateOrCancel",
          #         "reduceOnly":false,
          #         "closedPnlEv":0,
          #         "closedPnl":0E-8,
          #         "closedSize":0,
          #         "cumQty":0,
          #         "cumValueEv":0,
          #         "cumValue":0E-8,
          #         "leavesQty":1,
          #         "leavesValueEv":11337,
          #         "leavesValue":1.13370000,
          #         "stopDirection":"UNSPECIFIED",
          #         "stopPxEp":0,
          #         "stopPx":0E-8,
          #         "trigger":"UNSPECIFIED",
          #         "pegOffsetValueEp":0,
          #         "execStatus":"PendingNew",
          #         "pegPriceType":"UNSPECIFIED",
          #         "ordStatus":"Created"
          #     }
          #
          id = self.safe_string(order, 'orderID')
          clientOrderId = self.safe_string(order, 'clOrdID')
          if (clientOrderId is not None) and (len(clientOrderId) < 1):
              clientOrderId = None
          marketId = self.safe_string(order, 'symbol')
          symbol = self.safe_symbol(marketId, market)
          status = self.parse_order_status(self.safe_string(order, 'ordStatus'))
          side = self.safe_string_lower(order, 'side')
          type = self.parse_order_type(self.safe_string(order, 'orderType'))
          price = self.parse_number(self.from_ep(self.safe_string(order, 'priceEp'), market))
          amount = self.safe_number(order, 'orderQty')
          filled = self.safe_number(order, 'cumQty')
          remaining = self.safe_number(order, 'leavesQty')
          timestamp = self.safe_integer_product(order, 'actionTimeNs', 0.000001)
          cost = self.safe_number(order, 'cumValue')
          lastTradeTimestamp = self.safe_integer_product(order, 'transactTimeNs', 0.000001)
          if lastTradeTimestamp == 0:
              lastTradeTimestamp = None
          timeInForce = self.parse_time_in_force(self.safe_string(order, 'timeInForce'))
          stopPrice = self.safe_number(order, 'stopPx')
          postOnly = (timeInForce == 'PO')
          return {
              'info': order,
              'id': id,
              'clientOrderId': clientOrderId,
              'datetime': self.iso8601(timestamp),
              'timestamp': timestamp,
              'lastTradeTimestamp': lastTradeTimestamp,
              'symbol': symbol,
              'type': type,
              'timeInForce': timeInForce,
              'postOnly': postOnly,
              'side': side,
              'price': price,
              'stopPrice': stopPrice,
              'amount': amount,
              'filled': filled,
              'remaining': remaining,
              'cost': cost,
              'average': None,
              'status': status,
              'fee': None,
              'trades': None,
          }
  
      def parse_order(self, order, market=None):
          if 'closedPnl' in order:
              return self.parse_swap_order(order, market)
          return self.parse_spot_order(order, market)
  
      def create_order(self, symbol, type, side, amount, price=None, params={}):
          self.load_markets()
          market = self.market(symbol)
          side = self.capitalize(side)
          type = self.capitalize(type)
          request = {
              # common
              'symbol': market['id'],
              'side': side,  # Sell, Buy
              'ordType': type,  # Market, Limit, Stop, StopLimit, MarketIfTouched, LimitIfTouched or Pegged for swap orders
              # 'stopPxEp': self.to_ep(stopPx, market),  # for conditional orders
              # 'priceEp': self.to_ep(price, market),  # required for limit orders
              # 'timeInForce': 'GoodTillCancel',  # GoodTillCancel, PostOnly, ImmediateOrCancel, FillOrKill
              # ----------------------------------------------------------------
              # spot
              # 'qtyType': 'ByBase',  # ByBase, ByQuote
              # 'quoteQtyEv': self.to_ep(cost, market),
              # 'baseQtyEv': self.to_ev(amount, market),
              # 'trigger': 'ByLastPrice',  # required for conditional orders
              # ----------------------------------------------------------------
              # swap
              # 'clOrdID': self.uuid(),  # max length 40
              # 'orderQty': self.amount_to_precision(amount, symbol),
              # 'reduceOnly': False,
              # 'closeOnTrigger': False,  # implicit reduceOnly and cancel other orders in the same direction
              # 'takeProfitEp': self.to_ep(takeProfit, market),
              # 'stopLossEp': self.to_ep(stopLossEp, market),
              # 'triggerType': 'ByMarkPrice',  # ByMarkPrice, ByLastPrice
              # 'pegOffsetValueEp': integer,  # Trailing offset from current price. Negative value when position is long, positive when position is short
              # 'pegPriceType': 'TrailingStopPeg',  # TrailingTakeProfitPeg
              # 'text': 'comment',
          }
          if market['spot']:
              qtyType = self.safe_value(params, 'qtyType', 'ByBase')
              if (type == 'Market') or (type == 'Stop') or (type == 'MarketIfTouched'):
                  if price is not None:
                      qtyType = 'ByQuote'
              request['qtyType'] = qtyType
              if qtyType == 'ByQuote':
                  cost = self.safe_number(params, 'cost')
                  params = self.omit(params, 'cost')
                  if self.options['createOrderByQuoteRequiresPrice']:
                      if price is not None:
                          cost = amount * price
                      elif cost is None:
                          raise ArgumentsRequired(self.id + ' createOrder() ' + qtyType + ' requires a price argument or a cost parameter')
                  cost = amount if (cost is None) else cost
                  costString = str(cost)
                  request['quoteQtyEv'] = self.to_ev(costString, market)
              else:
                  amountString = str(amount)
                  request['baseQtyEv'] = self.to_ev(amountString, market)
          elif market['swap']:
              request['orderQty'] = int(amount)
          if type == 'Limit':
              priceString = str(price)
              request['priceEp'] = self.to_ep(priceString, market)
          stopPrice = self.safe_string_2(params, 'stopPx', 'stopPrice')
          if stopPrice is not None:
              request['stopPxEp'] = self.to_ep(stopPrice, market)
          params = self.omit(params, ['stopPx', 'stopPrice'])
          method = 'privatePostSpotOrders' if market['spot'] else 'privatePostOrders'
          response = getattr(self, method)(self.extend(request, params))
          #
          # spot
          #
          #     {
          #         "code": 0,
          #         "msg": "",
          #         "data": {
          #             "orderID": "d1d09454-cabc-4a23-89a7-59d43363f16d",
          #             "clOrdID": "309bcd5c-9f6e-4a68-b775-4494542eb5cb",
          #             "priceEp": 0,
          #             "action": "New",
          #             "trigger": "UNSPECIFIED",
          #             "pegPriceType": "UNSPECIFIED",
          #             "stopDirection": "UNSPECIFIED",
          #             "bizError": 0,
          #             "symbol": "sBTCUSDT",
          #             "side": "Buy",
          #             "baseQtyEv": 0,
          #             "ordType": "Limit",
          #             "timeInForce": "GoodTillCancel",
          #             "ordStatus": "Created",
          #             "cumFeeEv": 0,
          #             "cumBaseQtyEv": 0,
          #             "cumQuoteQtyEv": 0,
          #             "leavesBaseQtyEv": 0,
          #             "leavesQuoteQtyEv": 0,
          #             "avgPriceEp": 0,
          #             "cumBaseAmountEv": 0,
          #             "cumQuoteAmountEv": 0,
          #             "quoteQtyEv": 0,
          #             "qtyType": "ByBase",
          #             "stopPxEp": 0,
          #             "pegOffsetValueEp": 0
          #         }
          #     }
          #
          # swap
          #
          #     {
          #         "code":0,
          #         "msg":"",
          #         "data":{
          #             "bizError":0,
          #             "orderID":"7a1ad384-44a3-4e54-a102-de4195a29e32",
          #             "clOrdID":"",
          #             "symbol":"ETHUSD",
          #             "side":"Buy",
          #             "actionTimeNs":1592668973945065381,
          #             "transactTimeNs":0,
          #             "orderType":"Market",
          #             "priceEp":2267500,
          #             "price":226.75000000,
          #             "orderQty":1,
          #             "displayQty":0,
          #             "timeInForce":"ImmediateOrCancel",
          #             "reduceOnly":false,
          #             "closedPnlEv":0,
          #             "closedPnl":0E-8,
          #             "closedSize":0,
          #             "cumQty":0,
          #             "cumValueEv":0,
          #             "cumValue":0E-8,
          #             "leavesQty":1,
          #             "leavesValueEv":11337,
          #             "leavesValue":1.13370000,
          #             "stopDirection":"UNSPECIFIED",
          #             "stopPxEp":0,
          #             "stopPx":0E-8,
          #             "trigger":"UNSPECIFIED",
          #             "pegOffsetValueEp":0,
          #             "execStatus":"PendingNew",
          #             "pegPriceType":"UNSPECIFIED",
          #             "ordStatus":"Created"
          #         }
          #     }
          #
          data = self.safe_value(response, 'data', {})
          return self.parse_order(data, market)
  
      def cancel_order(self, id, symbol=None, params={}):
          if symbol is None:
              raise ArgumentsRequired(self.id + ' cancelOrder() requires a symbol argument')
          self.load_markets()
          market = self.market(symbol)
          request = {
              'symbol': market['id'],
          }
          clientOrderId = self.safe_string_2(params, 'clientOrderId', 'clOrdID')
          params = self.omit(params, ['clientOrderId', 'clOrdID'])
          if clientOrderId is not None:
              request['clOrdID'] = clientOrderId
          else:
              request['orderID'] = id
          method = 'privateDeleteSpotOrders' if market['spot'] else 'privateDeleteOrdersCancel'
          response = getattr(self, method)(self.extend(request, params))
          data = self.safe_value(response, 'data', {})
          return self.parse_order(data, market)
  
      def cancel_all_orders(self, symbol=None, params={}):
          if symbol is None:
              raise ArgumentsRequired(self.id + ' cancelOrder() requires a symbol argument')
          self.load_markets()
          request = {
              # 'symbol': market['id'],
              # 'untriggerred': False,  # False to cancel non-conditional orders, True to cancel conditional orders
              # 'text': 'up to 40 characters max',
          }
          market = self.market(symbol)
          method = 'privateDeleteSpotOrdersAll'
          if market['swap']:
              method = 'privateDeleteOrdersAll'
          request['symbol'] = market['id']
          return getattr(self, method)(self.extend(request, params))
  
      def fetch_order(self, id, symbol=None, params={}):
          if symbol is None:
              raise ArgumentsRequired(self.id + ' fetchOrder() requires a symbol argument')
          self.load_markets()
          market = self.market(symbol)
          method = 'privateGetSpotOrdersActive' if market['spot'] else 'privateGetExchangeOrder'
          request = {
              'symbol': market['id'],
          }
          clientOrderId = self.safe_string_2(params, 'clientOrderId', 'clOrdID')
          params = self.omit(params, ['clientOrderId', 'clOrdID'])
          if clientOrderId is not None:
              request['clOrdID'] = clientOrderId
          else:
              request['orderID'] = id
          response = getattr(self, method)(self.extend(request, params))
          data = self.safe_value(response, 'data', {})
          order = data
          if isinstance(data, list):
              numOrders = len(data)
              if numOrders < 1:
                  if clientOrderId is not None:
                      raise OrderNotFound(self.id + ' fetchOrder ' + symbol + ' order with clientOrderId ' + clientOrderId + ' not found')
                  else:
                      raise OrderNotFound(self.id + ' fetchOrder ' + symbol + ' order with id ' + id + ' not found')
              order = self.safe_value(data, 0, {})
          return self.parse_order(order, market)
  
      def fetch_orders(self, symbol=None, since=None, limit=None, params={}):
          if symbol is None:
              raise ArgumentsRequired(self.id + ' fetchOrders() requires a symbol argument')
          self.load_markets()
          market = self.market(symbol)
          method = 'privateGetSpotOrders' if market['spot'] else 'privateGetExchangeOrderList'
          request = {
              'symbol': market['id'],
          }
          if since is not None:
              request['start'] = since
          if limit is not None:
              request['limit'] = limit
          response = getattr(self, method)(self.extend(request, params))
          data = self.safe_value(response, 'data', {})
          rows = self.safe_value(data, 'rows', [])
          return self.parse_orders(rows, market, since, limit)
  
      def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
          if symbol is None:
              raise ArgumentsRequired(self.id + ' fetchOpenOrders() requires a symbol argument')
          self.load_markets()
          market = self.market(symbol)
          method = 'privateGetSpotOrders' if market['spot'] else 'privateGetOrdersActiveList'
          request = {
              'symbol': market['id'],
          }
          response = None
          try:
              response = getattr(self, method)(self.extend(request, params))
          except Exception as e:
              if isinstance(e, OrderNotFound):
                  return []
          data = self.safe_value(response, 'data', {})
          if isinstance(data, list):
              return self.parse_orders(data, market, since, limit)
          else:
              rows = self.safe_value(data, 'rows', [])
              return self.parse_orders(rows, market, since, limit)
  
      def fetch_closed_orders(self, symbol=None, since=None, limit=None, params={}):
          if symbol is None:
              raise ArgumentsRequired(self.id + ' fetchClosedOrders() requires a symbol argument')
          self.load_markets()
          market = self.market(symbol)
          method = 'privateGetExchangeSpotOrder' if market['spot'] else 'privateGetExchangeOrderList'
          request = {
              'symbol': market['id'],
          }
          if since is not None:
              request['start'] = since
          if limit is not None:
              request['limit'] = limit
          response = getattr(self, method)(self.extend(request, params))
          #
          # spot
          #
          #     {
          #         "code":0,
          #         "msg":"OK",
          #         "data":{
          #             "total":8,
          #             "rows":[
          #                 {
          #                     "orderID":"99232c3e-3d6a-455f-98cc-2061cdfe91bc",
          #                     "stopPxEp":0,
          #                     "avgPriceEp":0,
          #                     "qtyType":"ByBase",
          #                     "leavesBaseQtyEv":0,
          #                     "leavesQuoteQtyEv":0,
          #                     "baseQtyEv":"1000000000",
          #                     "feeCurrency":"4",
          #                     "stopDirection":"UNSPECIFIED",
          #                     "symbol":"sETHUSDT",
          #                     "side":"Buy",
          #                     "quoteQtyEv":250000000000,
          #                     "priceEp":25000000000,
          #                     "ordType":"Limit",
          #                     "timeInForce":"GoodTillCancel",
          #                     "ordStatus":"Rejected",
          #                     "execStatus":"NewRejected",
          #                     "createTimeNs":1592675305266037130,
          #                     "cumFeeEv":0,
          #                     "cumBaseValueEv":0,
          #                     "cumQuoteValueEv":0
          #                 },
          #             ]
          #         }
          #     }
          #
          data = self.safe_value(response, 'data', {})
          if isinstance(data, list):
              return self.parse_orders(data, market, since, limit)
          else:
              rows = self.safe_value(data, 'rows', [])
              return self.parse_orders(rows, market, since, limit)
  
      def fetch_my_trades(self, symbol=None, since=None, limit=None, params={}):
          if symbol is None:
              raise ArgumentsRequired(self.id + ' fetchClosedOrders() requires a symbol argument')
          self.load_markets()
          market = self.market(symbol)
          method = 'privateGetExchangeSpotOrderTrades' if market['spot'] else 'privateGetExchangeOrderTrade'
          request = {
              'symbol': market['id'],
          }
          if since is not None:
              request['start'] = since
          if market['swap'] and (limit is not None):
              request['limit'] = limit
          response = getattr(self, method)(self.extend(request, params))
          #
          # spot
          #
          #     {
          #         "code": 0,
          #         "msg": "OK",
          #         "data": {
          #             "total": 1,
          #             "rows": [
          #                 {
          #                     "qtyType": "ByQuote",
          #                     "transactTimeNs": 1589450974800550100,
          #                     "clOrdID": "8ba59d40-df25-d4b0-14cf-0703f44e9690",
          #                     "orderID": "b2b7018d-f02f-4c59-b4cf-051b9c2d2e83",
          #                     "symbol": "sBTCUSDT",
          #                     "side": "Buy",
          #                     "priceEP": 970056000000,
          #                     "baseQtyEv": 0,
          #                     "quoteQtyEv": 1000000000,
          #                     "action": "New",
          #                     "execStatus": "MakerFill",
          #                     "ordStatus": "Filled",
          #                     "ordType": "Limit",
          #                     "execInst": "None",
          #                     "timeInForce": "GoodTillCancel",
          #                     "stopDirection": "UNSPECIFIED",
          #                     "tradeType": "Trade",
          #                     "stopPxEp": 0,
          #                     "execId": "c6bd8979-07ba-5946-b07e-f8b65135dbb1",
          #                     "execPriceEp": 970056000000,
          #                     "execBaseQtyEv": 103000,
          #                     "execQuoteQtyEv": 999157680,
          #                     "leavesBaseQtyEv": 0,
          #                     "leavesQuoteQtyEv": 0,
          #                     "execFeeEv": 0,
          #                     "feeRateEr": 0
          #                 }
          #             ]
          #         }
          #     }
          #
          #
          # swap
          #
          #     {
          #         "code": 0,
          #         "msg": "OK",
          #         "data": {
          #             "total": 79,
          #             "rows": [
          #                 {
          #                     "transactTimeNs": 1606054879331565300,
          #                     "symbol": "BTCUSD",
          #                     "currency": "BTC",
          #                     "action": "New",
          #                     "side": "Buy",
          #                     "tradeType": "Trade",
          #                     "execQty": 5,
          #                     "execPriceEp": 182990000,
          #                     "orderQty": 5,
          #                     "priceEp": 183870000,
          #                     "execValueEv": 27323,
          #                     "feeRateEr": 75000,
          #                     "execFeeEv": 21,
          #                     "ordType": "Market",
          #                     "execID": "5eee56a4-04a9-5677-8eb0-c2fe22ae3645",
          #                     "orderID": "ee0acb82-f712-4543-a11d-d23efca73197",
          #                     "clOrdID": "",
          #                     "execStatus": "TakerFill"
          #                 },
          #             ]
          #         }
          #     }
          #
          data = self.safe_value(response, 'data', {})
          rows = self.safe_value(data, 'rows', [])
          return self.parse_trades(rows, market, since, limit)
  
      def fetch_deposit_address(self, code, params={}):
          self.load_markets()
          currency = self.currency(code)
          request = {
              'currency': currency['id'],
          }
          defaultNetworks = self.safe_value(self.options, 'defaultNetworks')
          defaultNetwork = self.safe_string_upper(defaultNetworks, code)
          networks = self.safe_value(self.options, 'networks', {})
          network = self.safe_string_upper(params, 'network', defaultNetwork)
          network = self.safe_string(networks, network, network)
          if network is None:
              request['chainName'] = currency['id']
          else:
              request['chainName'] = network
              params = self.omit(params, 'network')
          response = self.privateGetPhemexUserWalletsV2DepositAddress(self.extend(request, params))
          #     {
          #         "code":0,
          #         "msg":"OK",
          #         "data":{
          #             "address":"0x5bfbf60e0fa7f63598e6cfd8a7fd3ffac4ccc6ad",
          #             "tag":null
          #         }
          #     }
          #
          data = self.safe_value(response, 'data', {})
          address = self.safe_string(data, 'address')
          tag = self.safe_string(data, 'tag')
          self.check_address(address)
          return {
              'currency': code,
              'address': address,
              'tag': tag,
              'info': response,
          }
  
      def fetch_deposits(self, code=None, since=None, limit=None, params={}):
          self.load_markets()
          currency = None
          if code is not None:
              currency = self.currency(code)
          response = self.privateGetExchangeWalletsDepositList(params)
          #
          #     {
          #         "code":0,
          #         "msg":"OK",
          #         "data":[
          #             {
          #                 "id":29200,
          #                 "currency":"USDT",
          #                 "currencyCode":3,
          #                 "txHash":"0x0bdbdc47807769a03b158d5753f54dfc58b92993d2f5e818db21863e01238e5d",
          #                 "address":"0x5bfbf60e0fa7f63598e6cfd8a7fd3ffac4ccc6ad",
          #                 "amountEv":3000000000,
          #                 "confirmations":13,
          #                 "type":"Deposit",
          #                 "status":"Success",
          #                 "createdAt":1592722565000
          #             }
          #         ]
          #     }
          #
          data = self.safe_value(response, 'data', {})
          return self.parse_transactions(data, currency, since, limit)
  
      def fetch_withdrawals(self, code=None, since=None, limit=None, params={}):
          self.load_markets()
          currency = None
          if code is not None:
              currency = self.currency(code)
          response = self.privateGetExchangeWalletsWithdrawList(params)
          #
          #     {
          #         "code":0,
          #         "msg":"OK",
          #         "data":[
          #             {
          #                 "address": "1Lxxxxxxxxxxx"
          #                 "amountEv": 200000
          #                 "currency": "BTC"
          #                 "currencyCode": 1
          #                 "expiredTime": 0
          #                 "feeEv": 50000
          #                 "rejectReason": null
          #                 "status": "Succeed"
          #                 "txHash": "44exxxxxxxxxxxxxxxxxxxxxx"
          #                 "withdrawStatus: ""
          #             }
          #         ]
          #     }
          #
          data = self.safe_value(response, 'data', {})
          return self.parse_transactions(data, currency, since, limit)
  
      def parse_transaction_status(self, status):
          statuses = {
              'Success': 'ok',
              'Succeed': 'ok',
          }
          return self.safe_string(statuses, status, status)
  
      def parse_transaction(self, transaction, currency=None):
          #
          # withdraw
          #
          #     ...
          #
          # fetchDeposits
          #
          #     {
          #         "id":29200,
          #         "currency":"USDT",
          #         "currencyCode":3,
          #         "txHash":"0x0bdbdc47807769a03b158d5753f54dfc58b92993d2f5e818db21863e01238e5d",
          #         "address":"0x5bfbf60e0fa7f63598e6cfd8a7fd3ffac4ccc6ad",
          #         "amountEv":3000000000,
          #         "confirmations":13,
          #         "type":"Deposit",
          #         "status":"Success",
          #         "createdAt":1592722565000
          #     }
          #
          # fetchWithdrawals
          #
          #     {
          #         "address": "1Lxxxxxxxxxxx"
          #         "amountEv": 200000
          #         "currency": "BTC"
          #         "currencyCode": 1
          #         "expiredTime": 0
          #         "feeEv": 50000
          #         "rejectReason": null
          #         "status": "Succeed"
          #         "txHash": "44exxxxxxxxxxxxxxxxxxxxxx"
          #         "withdrawStatus: ""
          #     }
          #
          id = self.safe_string(transaction, 'id')
          address = self.safe_string(transaction, 'address')
          tag = None
          txid = self.safe_string(transaction, 'txHash')
          currencyId = self.safe_string(transaction, 'currency')
          currency = self.safe_currency(currencyId, currency)
          code = currency['code']
          timestamp = self.safe_integer_2(transaction, 'createdAt', 'submitedAt')
          type = self.safe_string_lower(transaction, 'type')
          feeCost = self.parse_number(self.from_en(self.safe_string(transaction, 'feeEv'), currency['valueScale']))
          fee = None
          if feeCost is not None:
              type = 'withdrawal'
              fee = {
                  'cost': feeCost,
                  'currency': code,
              }
          status = self.parse_transaction_status(self.safe_string(transaction, 'status'))
          amount = self.parse_number(self.from_en(self.safe_string(transaction, 'amountEv'), currency['valueScale']))
          return {
              'info': transaction,
              'id': id,
              'txid': txid,
              'timestamp': timestamp,
              'datetime': self.iso8601(timestamp),
              'address': address,
              'addressTo': address,
              'addressFrom': None,
              'tag': tag,
              'tagTo': tag,
              'tagFrom': None,
              'type': type,
              'amount': amount,
              'currency': code,
              'status': status,
              'updated': None,
              'fee': fee,
          }
  
      def fetch_positions(self, symbols=None, params={}):
          self.load_markets()
          code = self.safe_string(params, 'code')
          request = {}
          if code is None:
              currencyId = self.safe_string(params, 'currency')
              if currencyId is None:
                  raise ArgumentsRequired(self.id + ' fetchPositions() requires a currency parameter or a code parameter')
          else:
              currency = self.currency(code)
              params = self.omit(params, 'code')
              request['currency'] = currency['id']
          response = self.privateGetAccountsAccountPositions(self.extend(request, params))
          #
          #     {
          #         "code":0,"msg":"",
          #         "data":{
          #             "account":{
          #                 "accountId":6192120001,
          #                 "currency":"BTC",
          #                 "accountBalanceEv":1254744,
          #                 "totalUsedBalanceEv":0,
          #                 "bonusBalanceEv":1254744
          #             },
          #             "positions":[
          #                 {
          #                     "accountID":6192120001,
          #                     "symbol":"BTCUSD",
          #                     "currency":"BTC",
          #                     "side":"None",
          #                     "positionStatus":"Normal",
          #                     "crossMargin":false,
          #                     "leverageEr":100000000,
          #                     "leverage":1.00000000,
          #                     "initMarginReqEr":100000000,
          #                     "initMarginReq":1.00000000,
          #                     "maintMarginReqEr":500000,
          #                     "maintMarginReq":0.00500000,
          #                     "riskLimitEv":10000000000,
          #                     "riskLimit":100.00000000,
          #                     "size":0,
          #                     "value":0E-8,
          #                     "valueEv":0,
          #                     "avgEntryPriceEp":0,
          #                     "avgEntryPrice":0E-8,
          #                     "posCostEv":0,
          #                     "posCost":0E-8,
          #                     "assignedPosBalanceEv":0,
          #                     "assignedPosBalance":0E-8,
          #                     "bankruptCommEv":0,
          #                     "bankruptComm":0E-8,
          #                     "bankruptPriceEp":0,
          #                     "bankruptPrice":0E-8,
          #                     "positionMarginEv":0,
          #                     "positionMargin":0E-8,
          #                     "liquidationPriceEp":0,
          #                     "liquidationPrice":0E-8,
          #                     "deleveragePercentileEr":0,
          #                     "deleveragePercentile":0E-8,
          #                     "buyValueToCostEr":100225000,
          #                     "buyValueToCost":1.00225000,
          #                     "sellValueToCostEr":100075000,
          #                     "sellValueToCost":1.00075000,
          #                     "markPriceEp":135736070,
          #                     "markPrice":13573.60700000,
          #                     "markValueEv":0,
          #                     "markValue":null,
          #                     "unRealisedPosLossEv":0,
          #                     "unRealisedPosLoss":null,
          #                     "estimatedOrdLossEv":0,
          #                     "estimatedOrdLoss":0E-8,
          #                     "usedBalanceEv":0,
          #                     "usedBalance":0E-8,
          #                     "takeProfitEp":0,
          #                     "takeProfit":null,
          #                     "stopLossEp":0,
          #                     "stopLoss":null,
          #                     "cumClosedPnlEv":0,
          #                     "cumFundingFeeEv":0,
          #                     "cumTransactFeeEv":0,
          #                     "realisedPnlEv":0,
          #                     "realisedPnl":null,
          #                     "cumRealisedPnlEv":0,
          #                     "cumRealisedPnl":null
          #                 }
          #             ]
          #         }
          #     }
          #
          data = self.safe_value(response, 'data', {})
          positions = self.safe_value(data, 'positions', [])
          # todo unify parsePosition/parsePositions
          return positions
  
      def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
          query = self.omit(params, self.extract_params(path))
          requestPath = '/' + self.implode_params(path, params)
          url = requestPath
          queryString = ''
          if (method == 'GET') or (method == 'DELETE') or (method == 'PUT'):
              if query:
                  queryString = self.urlencode_with_array_repeat(query)
                  url += '?' + queryString
          if api == 'private':
              self.check_required_credentials()
              timestamp = self.seconds()
              xPhemexRequestExpiry = self.safe_integer(self.options, 'x-phemex-request-expiry', 60)
              expiry = self.sum(timestamp, xPhemexRequestExpiry)
              expiryString = str(expiry)
              headers = {
                  'x-phemex-access-token': self.apiKey,
                  'x-phemex-request-expiry': expiryString,
              }
              payload = ''
              if method == 'POST':
                  payload = self.json(params)
                  body = payload
                  headers['Content-Type'] = 'application/json'
              auth = requestPath + queryString + expiryString + payload
              headers['x-phemex-request-signature'] = self.hmac(self.encode(auth), self.encode(self.secret))
          url = self.implode_hostname(self.urls['api'][api]) + url
          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
          #
          #     {"code":30018,"msg":"phemex.data.size.uplimt","data":null}
          #     {"code":412,"msg":"Missing parameter - resolution","data":null}
          #     {"code":412,"msg":"Missing parameter - to","data":null}
          #     {"error":{"code":6001,"message":"invalid argument"},"id":null,"result":null}
          #
          error = self.safe_value(response, 'error', response)
          errorCode = self.safe_string(error, 'code')
          message = self.safe_string(error, 'msg')
          if (errorCode is not None) and (errorCode != '0'):
              feedback = self.id + ' ' + body
              self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback)
              self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback)
              raise ExchangeError(feedback)  # unknown message
  