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

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

from ccxt_zagrava.base.exchange import Exchange
from ccxt_zagrava.abstract.phemex import ImplicitAPI
import hashlib
import numbers
from ccxt_zagrava.base.types import Balances, Currencies, Currency, Int, LeverageTier, LeverageTiers, MarginModification, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, Transaction, TransferEntry, TransferEntries
from typing import List
from ccxt_zagrava.base.errors import ExchangeError
from ccxt_zagrava.base.errors import AuthenticationError
from ccxt_zagrava.base.errors import PermissionDenied
from ccxt_zagrava.base.errors import AccountSuspended
from ccxt_zagrava.base.errors import ArgumentsRequired
from ccxt_zagrava.base.errors import BadRequest
from ccxt_zagrava.base.errors import BadSymbol
from ccxt_zagrava.base.errors import InsufficientFunds
from ccxt_zagrava.base.errors import InvalidOrder
from ccxt_zagrava.base.errors import OrderNotFound
from ccxt_zagrava.base.errors import DuplicateOrderId
from ccxt_zagrava.base.errors import DDoSProtection
from ccxt_zagrava.base.errors import RateLimitExceeded
from ccxt_zagrava.base.errors import CancelPending
from ccxt_zagrava.base.decimal_to_precision import TICK_SIZE
from ccxt_zagrava.base.precise import Precise


class phemex(Exchange, ImplicitAPI):

    def describe(self):
        return self.deep_extend(super(phemex, self).describe(), {
            'id': 'phemex',
            'name': 'Phemex',
            'countries': ['CN'],  # China
            'rateLimit': 120.5,
            'version': 'v1',
            'certified': False,
            'pro': True,
            'hostname': 'api.phemex.com',
            'has': {
                'CORS': None,
                'spot': True,
                'margin': False,
                'swap': True,
                'future': False,
                'option': False,
                'addMargin': False,
                'cancelAllOrders': True,
                'cancelOrder': True,
                'closePosition': False,
                'createOrder': True,
                'createReduceOnlyOrder': True,
                'createStopLimitOrder': True,
                'createStopMarketOrder': True,
                'createStopOrder': True,
                'editOrder': True,
                'fetchBalance': True,
                'fetchBorrowRateHistories': False,
                'fetchBorrowRateHistory': False,
                'fetchClosedOrders': True,
                'fetchCrossBorrowRate': False,
                'fetchCrossBorrowRates': False,
                'fetchCurrencies': True,
                'fetchDepositAddress': True,
                'fetchDeposits': True,
                'fetchFundingHistory': True,
                'fetchFundingRate': True,
                'fetchFundingRateHistories': False,
                'fetchFundingRateHistory': True,
                'fetchFundingRates': False,
                'fetchIndexOHLCV': False,
                'fetchIsolatedBorrowRate': False,
                'fetchIsolatedBorrowRates': False,
                'fetchLeverage': False,
                'fetchLeverageTiers': True,
                'fetchMarketLeverageTiers': 'emulated',
                'fetchMarkets': True,
                'fetchMarkOHLCV': False,
                'fetchMyTrades': True,
                'fetchOHLCV': True,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'fetchOrderBook': True,
                'fetchOrders': True,
                'fetchPositions': True,
                'fetchPositionsRisk': False,
                'fetchPremiumIndexOHLCV': False,
                'fetchTicker': True,
                'fetchTickers': True,
                'fetchTrades': True,
                'fetchTradingFee': False,
                'fetchTradingFees': False,
                'fetchTransfers': True,
                'fetchWithdrawals': True,
                'reduceMargin': False,
                'sandbox': True,
                'setLeverage': True,
                'setMargin': True,
                'setMarginMode': True,
                'setPositionMode': True,
                'transfer': True,
                'withdraw': True,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/85225056-221eb600-b3d7-11ea-930d-564d2690e3f6.jpg',
                'test': {
                    'v1': 'https://testnet-api.phemex.com/v1',
                    'v2': 'https://testnet-api.phemex.com',
                    'public': 'https://testnet-api.phemex.com/exchange/public',
                    'private': 'https://testnet-api.phemex.com',
                },
                'api': {
                    'v1': 'https://{hostname}/v1',
                    'v2': 'https://{hostname}',
                    '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',
                '3M': '7776000',
                '1Y': '31104000',
            },
            'api': {
                'public': {
                    'get': {
                        'cfg/v2/products': 5,  # spot + contracts
                        'cfg/fundingRates': 5,
                        'products': 5,  # contracts only
                        'nomics/trades': 5,  # ?market=<symbol>&since=<since>
                        'md/kline': 5,  # ?from=1589811875&resolution=1800&symbol=sBTCUSDT&to=1592457935
                        'md/v2/kline/list': 5,  # perpetual api ?symbol=<symbol>&to=<to>&from=<from>&resolution=<resolution>
                        'md/v2/kline': 5,  # ?symbol=<symbol>&resolution=<resolution>&limit=<limit>
                        'md/v2/kline/last': 5,  # perpetual ?symbol=<symbol>&resolution=<resolution>&limit=<limit>
                        'md/orderbook': 5,  # ?symbol=<symbol>
                        'md/trade': 5,  # ?symbol=<symbol>
                        'md/spot/ticker/24hr': 5,  # ?symbol=<symbol>
                        'exchange/public/cfg/chain-settings': 5,  # ?currency=<currency>
                    },
                },
                'v1': {
                    'get': {
                        'md/fullbook': 5,  # ?symbol=<symbol>
                        'md/orderbook': 5,  # ?symbol=<symbol>
                        'md/trade': 5,  # ?symbol=<symbol>&id=<id>
                        'md/ticker/24hr': 5,  # ?symbol=<symbol>&id=<id>
                        'md/ticker/24hr/all': 5,  # ?id=<id>
                        'md/spot/ticker/24hr': 5,  # ?symbol=<symbol>&id=<id>
                        'md/spot/ticker/24hr/all': 5,  # ?symbol=<symbol>&id=<id>
                        'exchange/public/products': 5,  # contracts only
                        'api-data/public/data/funding-rate-history': 5,
                    },
                },
                'v2': {
                    'get': {
                        'public/products': 5,
                        'md/v2/orderbook': 5,  # ?symbol=<symbol>&id=<id>
                        'md/v2/trade': 5,  # ?symbol=<symbol>&id=<id>
                        'md/v2/ticker/24hr': 5,  # ?symbol=<symbol>&id=<id>
                        'md/v2/ticker/24hr/all': 5,  # ?id=<id>
                        'api-data/public/data/funding-rate-history': 5,
                    },
                },
                'private': {
                    'get': {
                        # spot
                        'spot/orders/active': 1,  # ?symbol=<symbol>&orderID=<orderID>
                        # 'spot/orders/active': 5,  # ?symbol=<symbol>&clOrDID=<clOrdID>
                        'spot/orders': 1,  # ?symbol=<symbol>
                        'spot/wallets': 5,  # ?currency=<currency>
                        'exchange/spot/order': 5,  # ?symbol=<symbol>&ordStatus=<ordStatus5,orderStatus2>ordType=<ordType5,orderType2>&start=<start>&end=<end>&limit=<limit>&offset=<offset>
                        'exchange/spot/order/trades': 5,  # ?symbol=<symbol>&start=<start>&end=<end>&limit=<limit>&offset=<offset>
                        'exchange/order/v2/orderList': 5,  # ?symbol=<symbol>&currency=<currency>&ordStatus=<ordStatus>&ordType=<ordType>&start=<start>&end=<end>&offset=<offset>&limit=<limit>&withCount=<withCount></withCount>
                        'exchange/order/v2/tradingList': 5,  # ?symbol=<symbol>&currency=<currency>&execType=<execType>&offset=<offset>&limit=<limit>&withCount=<withCount>
                        # swap
                        'accounts/accountPositions': 1,  # ?currency=<currency>
                        'g-accounts/accountPositions': 1,  # ?currency=<currency>
                        'accounts/positions': 25,  # ?currency=<currency>
                        'api-data/futures/funding-fees': 5,  # ?symbol=<symbol>
                        'api-data/g-futures/funding-fees': 5,  # ?symbol=<symbol>
                        'api-data/futures/orders': 5,  # ?symbol=<symbol>
                        'api-data/g-futures/orders': 5,  # ?symbol=<symbol>
                        'api-data/futures/orders/by-order-id': 5,  # ?symbol=<symbol>
                        'api-data/g-futures/orders/by-order-id': 5,  # ?symbol=<symbol>
                        'api-data/futures/trades': 5,  # ?symbol=<symbol>
                        'api-data/g-futures/trades': 5,  # ?symbol=<symbol>
                        'api-data/futures/trading-fees': 5,  # ?symbol=<symbol>
                        'api-data/g-futures/trading-fees': 5,  # ?symbol=<symbol>
                        'api-data/futures/v2/tradeAccountDetail': 5,  # ?currency=<currecny>&type=<type>&limit=<limit>&offset=<offset>&start=<start>&end=<end>&withCount=<withCount>
                        'g-orders/activeList': 1,  # ?symbol=<symbol>
                        'orders/activeList': 1,  # ?symbol=<symbol>
                        'exchange/order/list': 5,  # ?symbol=<symbol>&start=<start>&end=<end>&offset=<offset>&limit=<limit>&ordStatus=<ordStatus>&withCount=<withCount>
                        'exchange/order': 5,  # ?symbol=<symbol>&orderID=<orderID5,orderID2>
                        # 'exchange/order': 5,  # ?symbol=<symbol>&clOrdID=<clOrdID5,clOrdID2>
                        'exchange/order/trade': 5,  # ?symbol=<symbol>&start=<start>&end=<end>&limit=<limit>&offset=<offset>&withCount=<withCount>
                        'phemex-user/users/children': 5,  # ?offset=<offset>&limit=<limit>&withCount=<withCount>
                        'phemex-user/wallets/v2/depositAddress': 5,  # ?_t=1592722635531&currency=USDT
                        'phemex-user/wallets/tradeAccountDetail': 5,  # ?bizCode=&currency=&end=1642443347321&limit=10&offset=0&side=&start=1&type=4&withCount=true
                        'phemex-deposit/wallets/api/depositAddress': 5,  # ?currency=<currency>&chainName=<chainName>
                        'phemex-deposit/wallets/api/depositHist': 5,  # ?currency=<currency>&offset=<offset>&limit=<limit>&withCount=<withCount>
                        'phemex-deposit/wallets/api/chainCfg': 5,  # ?currency=<currency>
                        'phemex-withdraw/wallets/api/withdrawHist': 5,  # ?currency=<currency>&chainName=<chainNameList>&offset=<offset>&limit=<limit>&withCount=<withCount>
                        'phemex-withdraw/wallets/api/asset/info': 5,  # ?currency=<currency>&amount=<amount>
                        'phemex-user/order/closedPositionList': 5,  # ?currency=USD&limit=10&offset=0&symbol=&withCount=true
                        'exchange/margins/transfer': 5,  # ?start=<start>&end=<end>&offset=<offset>&limit=<limit>&withCount=<withCount>
                        'exchange/wallets/confirm/withdraw': 5,  # ?code=<withdrawConfirmCode>
                        'exchange/wallets/withdrawList': 5,  # ?currency=<currency>&limit=<limit>&offset=<offset>&withCount=<withCount>
                        'exchange/wallets/depositList': 5,  # ?currency=<currency>&offset=<offset>&limit=<limit>
                        'exchange/wallets/v2/depositAddress': 5,  # ?currency=<currency>
                        'api-data/spots/funds': 5,  # ?currency=<currency>&start=<start>&end=<end>&limit=<limit>&offset=<offset>
                        'api-data/spots/orders': 5,  # ?symbol=<symbol>
                        'api-data/spots/orders/by-order-id': 5,  # ?symbol=<symbol>&oderId=<orderID>&clOrdID=<clOrdID>
                        'api-data/spots/pnls': 5,
                        'api-data/spots/trades': 5,  # ?symbol=<symbol>
                        'api-data/spots/trades/by-order-id': 5,  # ?symbol=<symbol>&oderId=<orderID>&clOrdID=<clOrdID>
                        'assets/convert': 5,  # ?startTime=<startTime>&endTime=<endTime>&limit=<limit>&offset=<offset>
                        # transfer
                        'assets/transfer': 5,  # ?currency=<currency>&start=<start>&end=<end>&limit=<limit>&offset=<offset>
                        'assets/spots/sub-accounts/transfer': 5,  # ?currency=<currency>&start=<start>&end=<end>&limit=<limit>&offset=<offset>
                        'assets/futures/sub-accounts/transfer': 5,  # ?currency=<currency>&start=<start>&end=<end>&limit=<limit>&offset=<offset>
                        'assets/quote': 5,  # ?fromCurrency=<currency>&toCurrency=<currency>&amountEv=<amount>
                        # deposit/withdraw
                    },
                    'post': {
                        # spot
                        'spot/orders': 1,
                        # swap
                        'orders': 1,
                        'g-orders': 1,
                        'positions/assign': 5,  # ?symbol=<symbol>&posBalance=<posBalance>&posBalanceEv=<posBalanceEv>
                        'exchange/wallets/transferOut': 5,
                        'exchange/wallets/transferIn': 5,
                        'exchange/margins': 5,
                        'exchange/wallets/createWithdraw': 5,  # ?otpCode=<otpCode>
                        'exchange/wallets/cancelWithdraw': 5,
                        'exchange/wallets/createWithdrawAddress': 5,  # ?otpCode={optCode}
                        # transfer
                        'assets/transfer': 5,
                        'assets/spots/sub-accounts/transfer': 5,  # for sub-account only
                        'assets/futures/sub-accounts/transfer': 5,  # for sub-account only
                        'assets/universal-transfer': 5,  # for Main account only
                        'assets/convert': 5,
                        # withdraw
                        'phemex-withdraw/wallets/api/createWithdraw': 5,  # ?currency=<currency>&address=<address>&amount=<amount>&addressTag=<addressTag>&chainName=<chainName>
                        'phemex-withdraw/wallets/api/cancelWithdraw': 5,  # ?id=<id>
                    },
                    'put': {
                        # spot
                        'spot/orders/create': 1,  # ?symbol=<symbol>&trigger=<trigger>&clOrdID=<clOrdID>&priceEp=<priceEp>&baseQtyEv=<baseQtyEv>&quoteQtyEv=<quoteQtyEv>&stopPxEp=<stopPxEp>&text=<text>&side=<side>&qtyType=<qtyType>&ordType=<ordType>&timeInForce=<timeInForce>&execInst=<execInst>
                        'spot/orders': 1,  # ?symbol=<symbol>&orderID=<orderID>&origClOrdID=<origClOrdID>&clOrdID=<clOrdID>&priceEp=<priceEp>&baseQtyEV=<baseQtyEV>&quoteQtyEv=<quoteQtyEv>&stopPxEp=<stopPxEp>
                        # swap
                        'orders/replace': 1,  # ?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>
                        'g-orders/replace': 1,  # ?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': 5,  # ?symbol=<symbol>&leverage=<leverage>&leverageEr=<leverageEr>
                        'g-positions/leverage': 5,  # ?symbol=<symbol>&leverage=<leverage>&leverageEr=<leverageEr>
                        'g-positions/switch-pos-mode-sync': 5,  # ?symbol=<symbol>&targetPosMode=<targetPosMode>
                        'positions/riskLimit': 5,  # ?symbol=<symbol>&riskLimit=<riskLimit>&riskLimitEv=<riskLimitEv>
                    },
                    'delete': {
                        # spot
                        'spot/orders': 2,  # ?symbol=<symbol>&orderID=<orderID>
                        'spot/orders/all': 2,  # ?symbol=<symbol>&untriggered=<untriggered>
                        # 'spot/orders': 5,  # ?symbol=<symbol>&clOrdID=<clOrdID>
                        # swap
                        'orders/cancel': 1,  # ?symbol=<symbol>&orderID=<orderID>
                        'orders': 1,  # ?symbol=<symbol>&orderID=<orderID1>,<orderID2>,<orderID3>
                        'orders/all': 3,  # ?symbol=<symbol>&untriggered=<untriggered>&text=<text>
                        'g-orders/cancel': 1,  # ?symbol=<symbol>&orderID=<orderID>
                        'g-orders': 1,  # ?symbol=<symbol>&orderID=<orderID1>,<orderID2>,<orderID3>
                        'g-orders/all': 3,  # ?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
                    '401': AuthenticationError,  # {"code":"401","msg":"401 Failed to load API KEY."}
                    '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
                    '11116': InvalidOrder,  # TE_BO_NUM_EXCEEDS Details: the total count of brakcet orders should equal or less than 5
                    '11117': InvalidOrder,  # TE_BO_CANNOT_HAVE_BO_WITH_DIFF_SIDE Details: all bracket orders should have the same Side.
                    '11118': InvalidOrder,  # TE_BO_TP_PRICE_INVALID Details: bracker order take profit price is invalid
                    '11119': InvalidOrder,  # TE_BO_SL_PRICE_INVALID Details: bracker order stop loss price is invalid
                    '11120': InvalidOrder,  # TE_BO_SL_TRIGGER_PRICE_INVALID Details: bracker order stop loss trigger price is invalid
                    '11121': InvalidOrder,  # TE_BO_CANNOT_REPLACE Details: cannot replace bracket order.
                    '11122': InvalidOrder,  # TE_BO_BOTP_STATUS_INVALID Details: bracket take profit order status is invalid
                    '11123': InvalidOrder,  # TE_BO_CANNOT_PLACE_BOTP_OR_BOSL_ORDER Details: cannot place bracket take profit order
                    '11124': InvalidOrder,  # TE_BO_CANNOT_REPLACE_BOTP_OR_BOSL_ORDER Details: cannot place bracket stop loss order
                    '11125': InvalidOrder,  # TE_BO_CANNOT_CANCEL_BOTP_OR_BOSL_ORDER Details: cannot cancel bracket sl/tp order
                    '11126': InvalidOrder,  # TE_BO_DONOT_SUPPORT_API Details: doesn't support bracket order via API
                    '11128': InvalidOrder,  # TE_BO_INVALID_EXECINST Details: ExecInst value is invalid
                    '11129': InvalidOrder,  # TE_BO_MUST_BE_SAME_SIDE_AS_POS Details: bracket order should have the same side's side
                    '11130': InvalidOrder,  # TE_BO_WRONG_SL_TRIGGER_TYPE Details: bracket stop loss order trigger type is invalid
                    '11131': InvalidOrder,  # TE_BO_WRONG_TP_TRIGGER_TYPE Details: bracket take profit order trigger type is invalid
                    '11132': InvalidOrder,  # TE_BO_ABORT_BOSL_DUE_BOTP_CREATE_FAILED Details: cancel bracket stop loss order due failed to create take profit order.
                    '11133': InvalidOrder,  # TE_BO_ABORT_BOSL_DUE_BOPO_CANCELED Details: cancel bracket stop loss order due main order canceled.
                    '11134': InvalidOrder,  # TE_BO_ABORT_BOTP_DUE_BOPO_CANCELED Details: cancel bracket take profit order due main order canceled.
                    # not documented
                    '30000': BadRequest,  # {"code":30000,"msg":"Please double check input arguments","data":null}
                    '30018': BadRequest,  # {"code":30018,"msg":"phemex.data.size.uplimt","data":null}
                    '34003': PermissionDenied,  # {"code":34003,"msg":"Access forbidden","data":null}
                    '35104': InsufficientFunds,  # {"code":35104,"msg":"phemex.spot.wallet.balance.notenough","data":null}
                    '39995': RateLimitExceeded,  # {"code": "39995","msg": "Too many requests."}
                    '39996': PermissionDenied,  # {"code": "39996","msg": "Access denied."}
                    '39997': BadSymbol,  # {"code":39997,"msg":"Symbol not listed sMOVRUSDT","data":null}
                },
                'broad': {
                    '401 Insufficient privilege': PermissionDenied,  # {"code": "401","msg": "401 Insufficient privilege."}
                    '401 Request IP mismatch': PermissionDenied,  # {"code": "401","msg": "401 Request IP mismatch."}
                    '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}
                    'Api key not found': AuthenticationError,  # {"msg":"Api key not found 698dc9e3-6faa-4910-9476-12857e79e198","code":"10500"}
                },
            },
            'options': {
                'brokerId': 'ccxt_zagrava123456',  # updated from ccxt_zagrava to ccxt_zagrava123456
                'x-phemex-request-expiry': 60,  # in seconds
                'createOrderByQuoteRequiresPrice': True,
                'networks': {
                    'TRC20': 'TRX',
                    'ERC20': 'ETH',
                    'BEP20': 'BNB',
                },
                'defaultNetworks': {
                    'USDT': 'ETH',
                },
                'defaultSubType': 'linear',
                'accountsByType': {
                    'spot': 'spot',
                    'swap': 'future',
                },
                'stableCoins': [
                    'BUSD',
                    'FEI',
                    'TUSD',
                    'USD',
                    'USDC',
                    'USDD',
                    'USDP',
                    'USDT',
                ],
                'transfer': {
                    'fillResponseFromRequest': True,
                },
            },
        })

    def parse_safe_number(self, value=None):
        if value is None:
            return value
        parts = value.split(',')
        value = ''.join(parts)
        parts = value.split(' ')
        return self.safe_number(parts, 0)

    def parse_swap_market(self, market: dict):
        #
        #     {
        #         "symbol":"BTCUSD",
        #         "code":"1",
        #         "type":"Perpetual",
        #         "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,
        #         "status":"Listed",
        #         "tipOrderQty":1000000,
        #         "listTime":"1574650800000",
        #         "majorSymbol":true,
        #         "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')
        settleId = self.safe_string(market, 'settleCurrency')
        base = self.safe_currency_code(baseId)
        quote = self.safe_currency_code(quoteId)
        settle = self.safe_currency_code(settleId)
        inverse = False
        if settleId != quoteId:
            inverse = True
        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')
        status = self.safe_string(market, 'status')
        contractSizeString = self.safe_string(market, 'contractSize', ' ')
        contractSize: Num = None
        if settle == 'USDT':
            contractSize = self.parse_number('1')
        elif contractSizeString.find(' '):
            # "1 USD"
            # "0.005 ETH"
            parts = contractSizeString.split(' ')
            contractSize = self.parse_number(parts[0])
        else:
            # "1.0"
            contractSize = self.parse_number(contractSizeString)
        return self.safe_market_structure({
            'id': id,
            'symbol': base + '/' + quote + ':' + settle,
            'base': base,
            'quote': quote,
            'settle': settle,
            'baseId': baseId,
            'quoteId': quoteId,
            'settleId': settleId,
            'type': 'swap',
            'spot': False,
            'margin': False,
            'swap': True,
            'future': False,
            'option': False,
            'active': status == 'Listed',
            'contract': True,
            'linear': not inverse,
            'inverse': inverse,
            'taker': self.parse_number(self.from_en(takerFeeRateEr, ratioScale)),
            'maker': self.parse_number(self.from_en(makerFeeRateEr, ratioScale)),
            'contractSize': contractSize,
            'expiry': None,
            'expiryDatetime': None,
            'strike': None,
            'optionType': None,
            'priceScale': priceScale,
            'valueScale': valueScale,
            'ratioScale': ratioScale,
            'precision': {
                'amount': self.safe_number_2(market, 'lotSize', 'qtyStepSize'),
                'price': self.safe_number(market, 'tickSize'),
            },
            'limits': {
                'leverage': {
                    'min': self.parse_number('1'),
                    'max': self.safe_number(market, 'maxLeverage'),
                },
                'amount': {
                    'min': None,
                    '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')),
                },
            },
            'created': None,
            'info': market,
        })

    def parse_spot_market(self, market: dict):
        #
        #     {
        #         "symbol":"sBTCUSDT",
        #         "code":1001,
        #         "type":"Spot",
        #         "displaySymbol":"BTC / USDT",
        #         "quoteCurrency":"USDT",
        #         "priceScale":8,
        #         "ratioScale":8,
        #         "pricePrecision":2,
        #         "baseCurrency":"BTC",
        #         "baseTickSize":"0.000001 BTC",
        #         "baseTickSizeEv":100,
        #         "quoteTickSize":"0.01 USDT",
        #         "quoteTickSizeEv":1000000,
        #         "baseQtyPrecision":6,
        #         "quoteQtyPrecision":2,
        #         "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,
        #         "description":"BTCUSDT is a BTC/USDT spot trading pair. Minimum order value is 1 USDT",
        #         "status":"Listed",
        #         "tipOrderQty":2,
        #         "listTime":1589338800000,
        #         "buyPriceUpperLimitPct":110,
        #         "sellPriceLowerLimitPct":90,
        #         "leverage":5
        #     },
        #
        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')
        base = self.safe_currency_code(baseId)
        quote = self.safe_currency_code(quoteId)
        status = self.safe_string(market, 'status')
        precisionAmount = self.parse_safe_number(self.safe_string(market, 'baseTickSize'))
        precisionPrice = self.parse_safe_number(self.safe_string(market, 'quoteTickSize'))
        return self.safe_market_structure({
            'id': id,
            'symbol': base + '/' + quote,
            'base': base,
            'quote': quote,
            'settle': None,
            'baseId': baseId,
            'quoteId': quoteId,
            'settleId': None,
            'type': type,
            'spot': True,
            'margin': False,
            'swap': False,
            'future': False,
            'option': False,
            'active': status == 'Listed',
            'contract': False,
            'linear': None,
            'inverse': None,
            'taker': self.safe_number(market, 'defaultTakerFee'),
            'maker': self.safe_number(market, 'defaultMakerFee'),
            'contractSize': None,
            'expiry': None,
            'expiryDatetime': None,
            'strike': None,
            'optionType': None,
            'priceScale': self.safe_integer(market, 'priceScale'),
            'valueScale': self.safe_integer(market, 'valueScale'),
            'ratioScale': self.safe_integer(market, 'ratioScale'),
            'precision': {
                'amount': precisionAmount,
                'price': precisionPrice,
            },
            'limits': {
                'leverage': {
                    'min': None,
                    'max': None,
                },
                'amount': {
                    'min': precisionAmount,
                    'max': self.parse_safe_number(self.safe_string(market, 'maxBaseOrderSize')),
                },
                'price': {
                    'min': precisionPrice,
                    'max': None,
                },
                'cost': {
                    'min': self.parse_safe_number(self.safe_string(market, 'minOrderValue')),
                    'max': self.parse_safe_number(self.safe_string(market, 'maxOrderValue')),
                },
            },
            'created': None,
            'info': market,
        })

    def fetch_markets(self, params={}) -> List[Market]:
        """
        retrieves data on all markets for phemex
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: an array of objects representing market data
        """
        v2Products = self.v2GetPublicProducts(params)
        #
        #     {
        #         "code":0,
        #         "msg":"",
        #         "data":{
        #             "currencies":[
        #                 {"currency":"BTC","name":"Bitcoin","code":1,"valueScale":8,"minValueEv":1,"maxValueEv":5000000000000000000,"needAddrTag":0,"status":"Listed","displayCurrency":"BTC","inAssetsDisplay":1,"perpetual":0,"stableCoin":0,"assetsPrecision":8},
        #                 {"currency":"USD","name":"USD","code":2,"valueScale":4,"minValueEv":1,"maxValueEv":5000000000000000000,"needAddrTag":0,"status":"Listed","displayCurrency":"USD","inAssetsDisplay":1,"perpetual":0,"stableCoin":0,"assetsPrecision":2},
        #                 {"currency":"USDT","name":"TetherUS","code":3,"valueScale":8,"minValueEv":1,"maxValueEv":5000000000000000000,"needAddrTag":0,"status":"Listed","displayCurrency":"USDT","inAssetsDisplay":1,"perpetual":2,"stableCoin":1,"assetsPrecision":8},
        #             ],
        #             "products":[
        #                 {
        #                     "symbol":"BTCUSD",
        #                     "code":1,
        #                     "type":"Perpetual"
        #                     "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,
        #                     "description":"BTC/USD perpetual contracts are priced on the .BTC Index. Each contract is worth 1 USD. Funding fees are paid and received every 8 hours at UTC time: 00:00, 08:00 and 16:00.",
        #                     "status":"Listed",
        #                     "tipOrderQty":1000000,
        #                     "listTime":1574650800000,
        #                     "majorSymbol":true,
        #                     "defaultLeverage":"-10",
        #                     "fundingInterval":28800,
        #                     "maxLeverage":100
        #                 },
        #                 {
        #                     "symbol":"sBTCUSDT",
        #                     "code":1001,
        #                     "type":"Spot",
        #                     "displaySymbol":"BTC / USDT",
        #                     "quoteCurrency":"USDT",
        #                     "priceScale":8,
        #                     "ratioScale":8,
        #                     "pricePrecision":2,
        #                     "baseCurrency":"BTC",
        #                     "baseTickSize":"0.000001 BTC",
        #                     "baseTickSizeEv":100,
        #                     "quoteTickSize":"0.01 USDT",
        #                     "quoteTickSizeEv":1000000,
        #                     "baseQtyPrecision":6,
        #                     "quoteQtyPrecision":2,
        #                     "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,
        #                     "description":"BTCUSDT is a BTC/USDT spot trading pair. Minimum order value is 1 USDT",
        #                     "status":"Listed",
        #                     "tipOrderQty":2,
        #                     "listTime":1589338800000,
        #                     "buyPriceUpperLimitPct":110,
        #                     "sellPriceLowerLimitPct":90,
        #                     "leverage":5
        #                 },
        #             ],
        #             "perpProductsV2":[
        #                 {
        #                     "symbol":"BTCUSDT",
        #                     "code":41541,
        #                     "type":"PerpetualV2",
        #                     "displaySymbol":"BTC / USDT",
        #                     "indexSymbol":".BTCUSDT",
        #                     "markSymbol":".MBTCUSDT",
        #                     "fundingRateSymbol":".BTCUSDTFR",
        #                     "fundingRate8hSymbol":".BTCUSDTFR8H",
        #                     "contractUnderlyingAssets":"BTC",
        #                     "settleCurrency":"USDT",
        #                     "quoteCurrency":"USDT",
        #                     "tickSize":"0.1",
        #                     "priceScale":0,
        #                     "ratioScale":0,
        #                     "pricePrecision":1,
        #                     "baseCurrency":"BTC",
        #                     "description":"BTC/USDT perpetual contracts are priced on the .BTCUSDT Index. Each contract is worth 1 BTC. Funding fees are paid and received every 8 hours at UTC time: 00:00, 08:00 and 16:00.",
        #                     "status":"Listed",
        #                     "tipOrderQty":0,
        #                     "listTime":1668225600000,
        #                     "majorSymbol":true,
        #                     "defaultLeverage":"-10",
        #                     "fundingInterval":28800,
        #                     "maxLeverage":100,
        #                     "maxOrderQtyRq":"1000",
        #                     "maxPriceRp":"2000000000",
        #                     "minOrderValueRv":"1",
        #                     "minPriceRp":"1000.0",
        #                     "qtyPrecision":3,
        #                     "qtyStepSize":"0.001",
        #                     "tipOrderQtyRq":"200",
        #                     "maxOpenPosLeverage":100.0
        #                 },
        #             ],
        #             "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]},
        #             ],
        #             "riskLimitsV2":[
        #                 {
        #                     "symbol":"BTCUSDT",
        #                     "steps":"2000K",
        #                     "riskLimits":[
        #                         {"limit":2000000,"initialMarginRr":"0.01","maintenanceMarginRr":"0.005"},,
        #                         {"limit":4000000,"initialMarginRr":"0.015","maintenanceMarginRr":"0.0075"},
        #                         {"limit":6000000,"initialMarginRr":"0.02","maintenanceMarginRr":"0.01"},
        #                     ]
        #                 },
        #             ],
        #             "leveragesV2":[
        #                 {"options":[1.0,2.0,3.0,5.0,10.0,25.0,50.0,100.0],"initialMarginRr":"0.01"},
        #                 {"options":[1.0,2.0,3.0,5.0,10.0,25.0,50.0,66.67],"initialMarginRr":"0.015"},
        #                 {"options":[1.0,2.0,3.0,5.0,10.0,25.0,33.0,50.0],"initialMarginRr":"0.02"},
        #             ],
        #             "ratioScale":8,
        #             "md5Checksum":"5c6604814d3c1bafbe602c3d11a7e8bf",
        #         }
        #     }
        #
        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', [])
        perpetualProductsV2 = self.safe_value(v2ProductsData, 'perpProductsV2', [])
        products = self.array_concat(products, perpetualProductsV2)
        riskLimits = self.safe_value(v2ProductsData, 'riskLimits', [])
        riskLimitsV2 = self.safe_value(v2ProductsData, 'riskLimitsV2', [])
        riskLimits = self.array_concat(riskLimits, riskLimitsV2)
        currencies = self.safe_value(v2ProductsData, 'currencies', [])
        riskLimitsById = self.index_by(riskLimits, 'symbol')
        v1ProductsById = self.index_by(v1ProductsData, 'symbol')
        currenciesByCode = self.index_by(currencies, 'currency')
        result = []
        for i in range(0, len(products)):
            market = products[i]
            type = self.safe_string_lower(market, 'type')
            if (type == 'perpetual') or (type == 'perpetualv2'):
                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:
                baseCurrency = self.safe_string(market, 'baseCurrency')
                currencyValues = self.safe_value(currenciesByCode, baseCurrency, {})
                valueScale = self.safe_string(currencyValues, 'valueScale', '8')
                market = self.extend(market, {'valueScale': valueScale})
                market = self.parse_spot_market(market)
            result.append(market)
        return result

    def fetch_currencies(self, params={}) -> Currencies:
        """
        fetches all available currencies on an exchange
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an associative dictionary of currencies
        """
        response = self.v2GetPublicProducts(params)
        #
        #     {
        #         "code":0,
        #         "msg":"OK",
        #         "data":{
        #             ...,
        #             "currencies":[
        #                 {"currency":"BTC","name":"Bitcoin","code":1,"valueScale":8,"minValueEv":1,"maxValueEv":5000000000000000000,"needAddrTag":0,"status":"Listed","displayCurrency":"BTC","inAssetsDisplay":1,"perpetual":0,"stableCoin":0,"assetsPrecision":8},
        #                 {"currency":"USD","name":"USD","code":2,"valueScale":4,"minValueEv":1,"maxValueEv":5000000000000000000,"needAddrTag":0,"status":"Listed","displayCurrency":"USD","inAssetsDisplay":1,"perpetual":0,"stableCoin":0,"assetsPrecision":2},
        #                 {"currency":"USDT","name":"TetherUS","code":3,"valueScale":8,"minValueEv":1,"maxValueEv":5000000000000000000,"needAddrTag":0,"status":"Listed","displayCurrency":"USDT","inAssetsDisplay":1,"perpetual":2,"stableCoin":1,"assetsPrecision":8},
        #             ],
        #             ...
        #         }
        #     }
        data = self.safe_value(response, 'data', {})
        currencies = self.safe_value(data, 'currencies', [])
        result: dict = {}
        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)
            status = self.safe_string(currency, 'status')
            valueScaleString = self.safe_string(currency, 'valueScale')
            valueScale = int(valueScaleString)
            minValueEv = self.safe_string(currency, 'minValueEv')
            maxValueEv = self.safe_string(currency, 'maxValueEv')
            minAmount: Num = None
            maxAmount: Num = None
            precision: Num = 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': status == 'Listed',
                'deposit': None,
                'withdraw': None,
                'fee': None,
                'precision': precision,
                'limits': {
                    'amount': {
                        'min': minAmount,
                        'max': maxAmount,
                    },
                    'withdraw': {
                        'min': None,
                        'max': None,
                    },
                },
                'valueScale': valueScale,
                'networks': {},
            }
        return result

    def custom_parse_bid_ask(self, bidask, priceKey=0, amountKey=1, market: Market = None):
        if market is None:
            raise ArgumentsRequired(self.id + ' customParseBidAsk() 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 custom_parse_order_book(self, orderbook, symbol, timestamp=None, bidsKey='bids', asksKey='asks', priceKey=0, amountKey=1, market: Market = None):
        result: dict = {
            '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.custom_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: str, limit: Int = None, params={}) -> OrderBook:
        """
        fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
        :see: https://github.com/phemex/phemex-api-docs/blob/master/Public-Hedged-Perpetual-API.md#queryorderbook
        :param str symbol: unified symbol of the market to fetch the order book for
        :param int [limit]: the maximum amount of order book entries to return
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: A dictionary of `order book structures <https://docs.ccxt_zagrava.com/#/?id=order-book-structure>` indexed by market symbols
        """
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
            # 'id': 123456789,  # optional request id
        }
        response = None
        if market['linear'] and market['settle'] == 'USDT':
            response = self.v2GetMdV2Orderbook(self.extend(request, params))
        else:
            if (limit is not None) and (limit <= 30):
                response = self.v1GetMdOrderbook(self.extend(request, params))
            else:
                response = self.v1GetMdFullbook(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_2(result, 'book', 'orderbook_p', {})
        timestamp = self.safe_integer_product(result, 'timestamp', 0.000001)
        orderbook = self.custom_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 = self.number_to_string(n)
        precise = Precise(stringN)
        precise.decimals = precise.decimals - scale
        precise.reduce()
        preciseString = str(precise)
        return self.parse_to_int(preciseString)

    def to_ev(self, amount, market: 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: 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: 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: 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: 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: Market = None) -> list:
        #
        #     [
        #         1592467200,  # timestamp
        #         300,  # interval
        #         23376000000,  # last
        #         23322000000,  # open
        #         23381000000,  # high
        #         23315000000,  # low
        #         23367000000,  # close
        #         208671000,  # base volume
        #         48759063370,  # quote volume
        #     ]
        #
        baseVolume: Num
        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: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
        """
        fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
        :see: https://github.com/phemex/phemex-api-docs/blob/master/Public-Hedged-Perpetual-API.md#querykline
        :see: https://github.com/phemex/phemex-api-docs/blob/master/Public-Contract-API-en.md#query-kline
        :param str symbol: unified symbol of the market to fetch OHLCV data for
        :param str timeframe: the length of time each candle represents
        :param int [since]: *only used for USDT settled contracts, otherwise is emulated and not supported by the exchange* timestamp in ms of the earliest candle to fetch
        :param int [limit]: the maximum amount of candles to fetch
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: *USDT settled/ linear swaps only* end time in ms
        :returns int[][]: A list of candles ordered, open, high, low, close, volume
        """
        self.load_markets()
        market = self.market(symbol)
        userLimit = limit
        request: dict = {
            'symbol': market['id'],
            'resolution': self.safe_string(self.timeframes, timeframe, timeframe),
        }
        until = self.safe_integer_2(params, 'until', 'to')
        params = self.omit(params, ['until'])
        usesSpecialFromToEndpoint = ((market['linear'] or market['settle'] == 'USDT')) and ((since is not None) or (until is not None))
        maxLimit = 1000
        if usesSpecialFromToEndpoint:
            maxLimit = 2000
        if limit is None:
            limit = maxLimit
        request['limit'] = min(limit, maxLimit)
        response = None
        if market['linear'] or market['settle'] == 'USDT':
            if (until is not None) or (since is not None):
                candleDuration = self.parse_timeframe(timeframe)
                if since is not None:
                    since = int(round(since / 1000))
                    request['from'] = since
                else:
                    # when 'to' is defined since is mandatory
                    since = (until / 100) - (maxLimit * candleDuration)
                if until is not None:
                    request['to'] = int(round(until / 1000))
                else:
                    # when since is defined 'to' is mandatory
                    to = since + (maxLimit * candleDuration)
                    now = self.seconds()
                    if to > now:
                        to = now
                    request['to'] = to
                response = self.publicGetMdV2KlineList(self.extend(request, params))
            else:
                response = self.publicGetMdV2KlineLast(self.extend(request, params))
        else:
            if since is not None:
                # phemex also provides kline query with from/to, however, self interface is NOT recommended and does not work properly.
                # we do not send since param to the exchange, instead we calculate appropriate limit param
                duration = self.parse_timeframe(timeframe) * 1000
                timeDelta = self.milliseconds() - since
                limit = self.parse_to_int(timeDelta / duration)  # setting limit to the number of candles after since
            response = self.publicGetMdV2Kline(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_list(data, 'rows', [])
        return self.parse_ohlcvs(rows, market, timeframe, since, userLimit)

    def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
        #
        # 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
        #     }
        # linear swap v2
        #
        #     {
        #         "closeRp":"16820.5",
        #         "fundingRateRr":"0.0001",
        #         "highRp":"16962.1",
        #         "indexPriceRp":"16830.15651565",
        #         "lowRp":"16785",
        #         "markPriceRp":"16830.97534951",
        #         "openInterestRv":"1323.596",
        #         "openRp":"16851.7",
        #         "predFundingRateRr":"0.0001",
        #         "symbol":"BTCUSDT",
        #         "timestamp":"1672142789065593096",
        #         "turnoverRv":"124835296.0538",
        #         "volumeRq":"7406.95"
        #     }
        #
        marketId = self.safe_string(ticker, 'symbol')
        market = self.safe_market(marketId, market)
        symbol = market['symbol']
        timestamp = self.safe_integer_product(ticker, 'timestamp', 0.000001)
        last = self.from_ep(self.safe_string_2(ticker, 'lastEp', 'closeRp'), market)
        quoteVolume = self.from_er(self.safe_string_2(ticker, 'turnoverEv', 'turnoverRv'), market)
        baseVolume = self.safe_string(ticker, 'volume')
        if baseVolume is None:
            baseVolume = self.from_ev(self.safe_string_2(ticker, 'volumeEv', 'volumeRq'), market)
        open = self.from_ep(self.safe_string(ticker, 'openEp'), market)
        return self.safe_ticker({
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'high': self.from_ep(self.safe_string_2(ticker, 'highEp', 'highRp'), market),
            'low': self.from_ep(self.safe_string_2(ticker, 'lowEp', 'lowRp'), market),
            'bid': self.from_ep(self.safe_string(ticker, 'bidEp'), market),
            'bidVolume': None,
            'ask': self.from_ep(self.safe_string(ticker, 'askEp'), market),
            'askVolume': None,
            'vwap': None,
            'open': open,
            'close': last,
            'last': last,
            'previousClose': None,  # previous day close
            'change': None,
            'percentage': None,
            'average': None,
            'baseVolume': baseVolume,
            'quoteVolume': quoteVolume,
            'info': ticker,
        }, market)

    def fetch_ticker(self, symbol: str, params={}) -> Ticker:
        """
        fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
        :see: https://github.com/phemex/phemex-api-docs/blob/master/Public-Hedged-Perpetual-API.md#query24hrsticker
        :param str symbol: unified symbol of the market to fetch the ticker for
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `ticker structure <https://docs.ccxt_zagrava.com/#/?id=ticker-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
            # 'id': 123456789,  # optional request id
        }
        response = None
        if market['swap']:
            if market['inverse'] or market['settle'] == 'USD':
                response = self.v1GetMdTicker24hr(self.extend(request, params))
            else:
                response = self.v2GetMdV2Ticker24hr(self.extend(request, params))
        else:
            response = self.v1GetMdSpotTicker24hr(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_dict(response, 'result', {})
        return self.parse_ticker(result, market)

    def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
        """
        fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
        :see: https://phemex-docs.github.io/#query-24-hours-ticker-for-all-symbols-2     # spot
        :see: https://phemex-docs.github.io/#query-24-ticker-for-all-symbols             # linear
        :see: https://phemex-docs.github.io/#query-24-hours-ticker-for-all-symbols       # inverse
        :param str[]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a dictionary of `ticker structures <https://docs.ccxt_zagrava.com/#/?id=ticker-structure>`
        """
        self.load_markets()
        market: Market = None
        if symbols is not None:
            first = self.safe_value(symbols, 0)
            market = self.market(first)
        type = None
        type, params = self.handle_market_type_and_params('fetchTickers', market, params)
        subType = None
        subType, params = self.handle_sub_type_and_params('fetchTickers', market, params)
        query = self.omit(params, 'type')
        response = None
        if type == 'spot':
            response = self.v1GetMdSpotTicker24hrAll(query)
        elif subType == 'inverse' or self.safe_string(market, 'settle') == 'USD':
            response = self.v1GetMdTicker24hrAll(query)
        else:
            response = self.v2GetMdV2Ticker24hrAll(query)
        result = self.safe_list(response, 'result', [])
        return self.parse_tickers(result, symbols)

    def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
        """
        get the list of most recent trades for a particular symbol
        :see: https://github.com/phemex/phemex-api-docs/blob/master/Public-Hedged-Perpetual-API.md#querytrades
        :param str symbol: unified symbol of the market to fetch trades for
        :param int [since]: timestamp in ms of the earliest trade to fetch
        :param int [limit]: the maximum amount of trades to fetch
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns Trade[]: a list of `trade structures <https://docs.ccxt_zagrava.com/#/?id=public-trades>`
        """
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
            # 'id': 123456789,  # optional request id
        }
        response = None
        if market['linear'] and market['settle'] == 'USDT':
            response = self.v2GetMdV2Trade(self.extend(request, params))
        else:
            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_2(result, 'trades', 'trades_p', [])
        return self.parse_trades(trades, market, since, limit)

    def parse_trade(self, trade: dict, market: Market = None) -> Trade:
        #
        # fetchTrades(public) spot & contract
        #
        #     [
        #         1592541746712239749,
        #         13156448570000,
        #         "Buy",
        #         93070000,
        #         40173
        #     ]
        #
        # fetchTrades(public) perp
        #
        #     [
        #         1675690986063435800,
        #         "Sell",
        #         "22857.4",
        #         "0.269"
        #     ]
        #
        # 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
        #         "baseCurrency": "BTC",
        #         "quoteCurrency": "USDT",
        #         "feeCurrency": "BTC"
        #     }
        #
        # 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"
        #     }
        # perpetual
        #     {
        #         "accountID": 9328670003,
        #         "action": "New",
        #         "actionBy": "ByUser",
        #         "actionTimeNs": 1666858780876924611,
        #         "addedSeq": 77751555,
        #         "apRp": "0",
        #         "bonusChangedAmountRv": "0",
        #         "bpRp": "0",
        #         "clOrdID": "c0327a7d-9064-62a9-28f6-2db9aaaa04e0",
        #         "closedPnlRv": "0",
        #         "closedSize": "0",
        #         "code": 0,
        #         "cumFeeRv": "0",
        #         "cumQty": "0",
        #         "cumValueRv": "0",
        #         "curAccBalanceRv": "1508.489893982237",
        #         "curAssignedPosBalanceRv": "24.62786650928",
        #         "curBonusBalanceRv": "0",
        #         "curLeverageRr": "-10",
        #         "curPosSide": "Buy",
        #         "curPosSize": "0.043",
        #         "curPosTerm": 1,
        #         "curPosValueRv": "894.0689",
        #         "curRiskLimitRv": "1000000",
        #         "currency": "USDT",
        #         "cxlRejReason": 0,
        #         "displayQty": "0.003",
        #         "execFeeRv": "0",
        #         "execID": "00000000-0000-0000-0000-000000000000",
        #         "execPriceRp": "20723.7",
        #         "execQty": "0",
        #         "execSeq": 77751555,
        #         "execStatus": "New",
        #         "execValueRv": "0",
        #         "feeRateRr": "0",
        #         "leavesQty": "0.003",
        #         "leavesValueRv": "63.4503",
        #         "message": "No error",
        #         "ordStatus": "New",
        #         "ordType": "Market",
        #         "orderID": "fa64c6f2-47a4-4929-aab4-b7fa9bbc4323",
        #         "orderQty": "0.003",
        #         "pegOffsetValueRp": "0",
        #         "posSide": "Long",
        #         "priceRp": "21150.1",
        #         "relatedPosTerm": 1,
        #         "relatedReqNum": 11,
        #         "side": "Buy",
        #         "slTrigger": "ByMarkPrice",
        #         "stopLossRp": "0",
        #         "stopPxRp": "0",
        #         "symbol": "BTCUSDT",
        #         "takeProfitRp": "0",
        #         "timeInForce": "ImmediateOrCancel",
        #         "tpTrigger": "ByLastPrice",
        #         "tradeType": "Amend",
        #         "transactTimeNs": 1666858780881545305,
        #         "userID": 932867
        #     }
        #
        # swap - USDT
        #
        #     {
        #         "createdAt": 1666226932259,
        #         "symbol": "ETHUSDT",
        #         "currency": "USDT",
        #         "action": 1,
        #         "tradeType": 1,
        #         "execQtyRq": "0.01",
        #         "execPriceRp": "1271.9",
        #         "side": 1,
        #         "orderQtyRq": "0.78",
        #         "priceRp": "1271.9",
        #         "execValueRv": "12.719",
        #         "feeRateRr": "0.0001",
        #         "execFeeRv": "0.0012719",
        #         "ordType": 2,
        #         "execId": "8718cae",
        #         "execStatus": 6
        #     }
        # spot with fees paid using PT token
        #     "createdAt": "1714990724076",
        #     "symbol": "BTCUSDT",
        #     "currency": "USDT",
        #     "action": "1",
        #     "tradeType": "1",
        #     "execQtyRq": "0.003",
        #     "execPriceRp": "64935",
        #     "side": "2",
        #     "orderQtyRq": "0.003",
        #     "priceRp": "51600",
        #     "execValueRv": "194.805",
        #     "feeRateRr": "0.000495",
        #     "execFeeRv": "0",
        #     "ordType": "3",
        #     "execId": "XXXXXX",
        #     "execStatus": "7",
        #     "posSide": "1",
        #     "ptFeeRv": "0.110012249248",
        #     "ptPriceRp": "0.876524893"
        #
        priceString: Str
        amountString: Str
        timestamp: Int
        id: Str = None
        side: Str = None
        costString: Str = None
        type: Str = None
        fee = None
        feeCostString: Str = None
        feeRateString: Str = None
        feeCurrencyCode: Str = None
        marketId = self.safe_string(trade, 'symbol')
        market = self.safe_market(marketId, market)
        symbol = market['symbol']
        orderId: Str = None
        takerOrMaker: Str = 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.safe_string(trade, tradeLength - 2)
            amountString = self.safe_string(trade, tradeLength - 1)
            if isinstance(trade[tradeLength - 2], numbers.Real):
                priceString = self.from_ep(priceString, market)
                amountString = self.from_ev(amountString, market)
        else:
            timestamp = self.safe_integer_product(trade, 'transactTimeNs', 0.000001)
            if timestamp is None:
                timestamp = self.safe_integer(trade, 'createdAt')
            id = self.safe_string_2(trade, 'execId', 'execID')
            orderId = self.safe_string(trade, 'orderID')
            if market['settle'] == 'USDT':
                sideId = self.safe_string_lower(trade, 'side')
                if (sideId == 'buy') or (sideId == 'sell'):
                    side = sideId
                elif sideId is not None:
                    side = 'buy' if (sideId == '1') else 'sell'
                ordType = self.safe_string(trade, 'ordType')
                if ordType == '1':
                    type = 'market'
                elif ordType == '2':
                    type = 'limit'
                priceString = self.safe_string(trade, 'execPriceRp')
                amountString = self.safe_string(trade, 'execQtyRq')
                costString = self.safe_string(trade, 'execValueRv')
                feeCostString = self.omit_zero(self.safe_string(trade, 'execFeeRv'))
                feeRateString = self.safe_string(trade, 'feeRateRr')
                if feeCostString is not None:
                    currencyId = self.safe_string(trade, 'currency')
                    feeCurrencyCode = self.safe_currency_code(currencyId)
                else:
                    ptFeeRv = self.omit_zero(self.safe_string(trade, 'ptFeeRv'))
                    if ptFeeRv is not None:
                        feeCostString = ptFeeRv
                        feeCurrencyCode = 'PT'
            else:
                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_er(self.safe_string_2(trade, 'execQuoteQtyEv', 'execValueEv'), market)
                feeCostString = self.from_er(self.omit_zero(self.safe_string(trade, 'execFeeEv')), market)
                if feeCostString is not None:
                    feeRateString = self.from_er(self.safe_string(trade, 'feeRateEr'), market)
                    if market['spot']:
                        feeCurrencyCode = self.safe_currency_code(self.safe_string(trade, 'feeCurrency'))
                    else:
                        info = self.safe_value(market, 'info')
                        if info is not None:
                            settlementCurrencyId = self.safe_string(info, 'settlementCurrency')
                            feeCurrencyCode = self.safe_currency_code(settlementCurrencyId)
                else:
                    feeCostString = self.safe_string(trade, 'ptFeeRv')
                    if feeCostString is not None:
                        feeCurrencyCode = 'PT'
            fee = {
                'cost': feeCostString,
                'rate': feeRateString,
                'currency': feeCurrencyCode,
            }
        return self.safe_trade({
            'info': trade,
            'id': id,
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'order': orderId,
            'type': type,
            'side': side,
            'takerOrMaker': takerOrMaker,
            'price': priceString,
            'amount': amountString,
            'cost': costString,
            'fee': fee,
        }, market)

    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: dict = {'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.safe_balance(result)

    def parse_swap_balance(self, response):
        # usdt
        #   {
        #       "info": {
        #         "code": "0",
        #         "msg": '',
        #         "data": {
        #           "account": {
        #             "userID": "940666",
        #             "accountId": "9406660003",
        #             "currency": "USDT",
        #             "accountBalanceRv": "99.93143972",
        #             "totalUsedBalanceRv": "0.40456",
        #             "bonusBalanceRv": "0"
        #           },
        #   }
        #
        #     {
        #         "code":0,
        #         "msg":"",
        #         "data":{
        #             "account":{
        #                 "accountId":6192120001,
        #                 "currency":"BTC",
        #                 "accountBalanceEv":1254744,
        #                 "totalUsedBalanceEv":0,
        #                 "bonusBalanceEv":1254744
        #             }
        #         }
        #     }
        #
        result: dict = {'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)
        valueScale = self.safe_integer(currency, 'valueScale', 8)
        account = self.account()
        accountBalanceEv = self.safe_string_2(balance, 'accountBalanceEv', 'accountBalanceRv')
        totalUsedBalanceEv = self.safe_string_2(balance, 'totalUsedBalanceEv', 'totalUsedBalanceRv')
        needsConversion = (code != 'USDT')
        account['total'] = self.from_en(accountBalanceEv, valueScale) if needsConversion else accountBalanceEv
        account['used'] = self.from_en(totalUsedBalanceEv, valueScale) if needsConversion else totalUsedBalanceEv
        result[code] = account
        return self.safe_balance(result)

    def fetch_balance(self, params={}) -> Balances:
        """
        query for balance and get the amount of funds available for trading or funds locked in orders
        :see: https://phemex-docs.github.io/#query-wallets
        :see: https://github.com/phemex/phemex-api-docs/blob/master/Public-Hedged-Perpetual-API.md#query-account-positions
        :see: https://phemex-docs.github.io/#query-trading-account-and-positions
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.type]: spot or swap
        :param str [params.code]: *swap only* currency code of the balance to query(USD, USDT, etc), default is USDT
        :returns dict: a `balance structure <https://docs.ccxt_zagrava.com/#/?id=balance-structure>`
        """
        self.load_markets()
        type = None
        type, params = self.handle_market_type_and_params('fetchBalance', None, params)
        code = self.safe_string(params, 'code')
        params = self.omit(params, ['code'])
        response = None
        request: dict = {}
        if (type != 'spot') and (type != 'swap'):
            raise BadRequest(self.id + ' does not support ' + type + ' markets, only spot and swap')
        if type == 'swap':
            settle = None
            settle, params = self.handle_option_and_params(params, 'fetchBalance', 'settle', 'USDT')
            if code is not None or settle is not None:
                coin = None
                if code is not None:
                    coin = code
                else:
                    coin = settle
                currency = self.currency(coin)
                request['currency'] = currency['id']
                if currency['id'] == 'USDT':
                    response = self.privateGetGAccountsAccountPositions(self.extend(request, params))
                else:
                    response = self.privateGetAccountsAccountPositions(self.extend(request, params))
            else:
                currency = self.safe_string(params, 'currency')
                if currency is None:
                    raise ArgumentsRequired(self.id + ' fetchBalance() requires a code parameter or a currency or settle parameter for ' + type + ' type')
                response = self.privateGetSpotWallets(self.extend(request, params))
        else:
            response = self.privateGetSpotWallets(self.extend(request, params))
        #
        # usdt
        #   {
        #       "info": {
        #         "code": "0",
        #         "msg": '',
        #         "data": {
        #           "account": {
        #             "userID": "940666",
        #             "accountId": "9406660003",
        #             "currency": "USDT",
        #             "accountBalanceRv": "99.93143972",
        #             "totalUsedBalanceRv": "0.40456",
        #             "bonusBalanceRv": "0"
        #           },
        #   }
        #
        # 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: Str):
        statuses: dict = {
            'Created': 'open',
            'Untriggered': 'open',
            'Deactivated': 'closed',
            'Triggered': 'open',
            'Rejected': 'rejected',
            'New': 'open',
            'PartiallyFilled': 'open',
            'Filled': 'closed',
            'Canceled': 'canceled',
            '1': 'open',
            '2': 'canceled',
            '3': 'closed',
            '4': 'canceled',
            '5': 'open',
            '6': 'open',
            '7': 'closed',
            '8': 'canceled',
        }
        return self.safe_string(statuses, status, status)

    def parse_order_type(self, type: Str):
        types: dict = {
            '1': 'market',
            '2': 'limit',
            '3': 'stop',
            '4': 'stopLimit',
            '5': 'market',
            '6': 'limit',
            '7': 'market',
            '8': 'market',
            '9': 'stopLimit',
            '10': 'market',
            'Limit': 'limit',
            'Market': 'market',
        }
        return self.safe_string(types, type, type)

    def parse_time_in_force(self, timeInForce: Str):
        timeInForces: dict = {
            'GoodTillCancel': 'GTC',
            'PostOnly': 'PO',
            'ImmediateOrCancel': 'IOC',
            'FillOrKill': 'FOK',
        }
        return self.safe_string(timeInForces, timeInForce, timeInForce)

    def parse_spot_order(self, order: dict, market: 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')
        market = self.safe_market(marketId, market)
        symbol = market['symbol']
        price = self.from_ep(self.safe_string(order, 'priceEp'), market)
        amount = self.from_ev(self.safe_string(order, 'baseQtyEv'), market)
        remaining = self.omit_zero(self.from_ev(self.safe_string(order, 'leavesBaseQtyEv'), market))
        filled = self.from_ev(self.safe_string_2(order, 'cumBaseQtyEv', 'cumBaseValueEv'), market)
        cost = self.from_er(self.safe_string_2(order, 'cumQuoteValueEv', 'quoteQtyEv'), market)
        average = 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.from_ev(self.safe_string(order, 'cumFeeEv'), market)
        if feeCost is not None:
            fee = {
                'cost': feeCost,
                'currency': self.safe_currency_code(self.safe_string(order, 'feeCurrency')),
            }
        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'))))
        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,
            'triggerPrice': stopPrice,
            'amount': amount,
            'cost': cost,
            'average': average,
            'filled': filled,
            'remaining': remaining,
            'status': status,
            'fee': fee,
            'trades': None,
        }, market)

    def parse_order_side(self, side):
        sides: dict = {
            '1': 'buy',
            '2': 'sell',
        }
        return self.safe_string(sides, side, side)

    def parse_swap_order(self, order, market: 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",
        #         "execInst": "ReduceOnly"
        #     }
        #
        # usdt
        # {
        #        "bizError":"0",
        #        "orderID":"bd720dff-5647-4596-aa4e-656bac87aaad",
        #        "clOrdID":"ccxt_zagrava2022843dffac9477b497",
        #        "symbol":"LTCUSDT",
        #        "side":"Buy",
        #        "actionTimeNs":"1677667878751724052",
        #        "transactTimeNs":"1677667878754017434",
        #        "orderType":"Limit",
        #        "priceRp":"40",
        #        "orderQtyRq":"0.1",
        #        "displayQtyRq":"0.1",
        #        "timeInForce":"GoodTillCancel",
        #        "reduceOnly":false,
        #        "closedPnlRv":"0",
        #        "closedSizeRq":"0",
        #        "cumQtyRq":"0",
        #        "cumValueRv":"0",
        #        "leavesQtyRq":"0.1",
        #        "leavesValueRv":"4",
        #        "stopDirection":"UNSPECIFIED",
        #        "stopPxRp":"0",
        #        "trigger":"UNSPECIFIED",
        #        "pegOffsetValueRp":"0",
        #        "pegOffsetProportionRr":"0",
        #        "execStatus":"New",
        #        "pegPriceType":"UNSPECIFIED",
        #        "ordStatus":"New",
        #        "execInst":"None",
        #        "takeProfitRp":"0",
        #        "stopLossRp":"0"
        #     }
        #
        # v2 orderList
        #    {
        #        "createdAt":"1677686231301",
        #        "symbol":"LTCUSDT",
        #        "orderQtyRq":"0.2",
        #        "side":"1",
        #        "posSide":"3",
        #        "priceRp":"50",
        #        "execQtyRq":"0",
        #        "leavesQtyRq":"0.2",
        #        "execPriceRp":"0",
        #        "orderValueRv":"10",
        #        "leavesValueRv":"10",
        #        "cumValueRv":"0",
        #        "stopDirection":"0",
        #        "stopPxRp":"0",
        #        "trigger":"0",
        #        "actionBy":"1",
        #        "execFeeRv":"0",
        #        "ordType":"2",
        #        "ordStatus":"5",
        #        "clOrdId":"4b3b188",
        #        "orderId":"4b3b1884-87cf-4897-b596-6693b7ed84d1",
        #        "execStatus":"5",
        #        "bizError":"0",
        #        "totalPnlRv":null,
        #        "avgTransactPriceRp":null,
        #        "orderDetailsVos":null,
        #        "tradeType":"0"
        #    }
        #
        id = self.safe_string_2(order, 'orderID', 'orderId')
        clientOrderId = self.safe_string_2(order, 'clOrdID', 'clOrdId')
        if (clientOrderId is not None) and (len(clientOrderId) < 1):
            clientOrderId = None
        marketId = self.safe_string(order, 'symbol')
        symbol = self.safe_symbol(marketId, market)
        market = self.safe_market(marketId, market)
        status = self.parse_order_status(self.safe_string(order, 'ordStatus'))
        side = self.parse_order_side(self.safe_string_lower(order, 'side'))
        type = self.parse_order_type(self.safe_string(order, 'orderType'))
        price = self.safe_string(order, 'priceRp')
        if price is None:
            price = self.from_ep(self.safe_string(order, 'priceEp'), market)
        amount = self.safe_number_2(order, 'orderQty', 'orderQtyRq')
        filled = self.safe_number_2(order, 'cumQty', 'cumQtyRq')
        remaining = self.safe_number_2(order, 'leavesQty', 'leavesQtyRq')
        timestamp = self.safe_integer_product(order, 'actionTimeNs', 0.000001)
        if timestamp is None:
            timestamp = self.safe_integer(order, 'createdAt')
        cost = self.safe_number_2(order, 'cumValue', 'cumValueRv')
        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.omit_zero(self.safe_string_2(order, 'stopPx', 'stopPxRp'))
        postOnly = (timeInForce == 'PO')
        reduceOnly = self.safe_value(order, 'reduceOnly')
        execInst = self.safe_string(order, 'execInst')
        if execInst == 'ReduceOnly':
            reduceOnly = True
        takeProfit = self.safe_string(order, 'takeProfitRp')
        stopLoss = self.safe_string(order, 'stopLossRp')
        feeValue = self.omit_zero(self.safe_string(order, 'execFeeRv'))
        ptFeeRv = self.omit_zero(self.safe_string(order, 'ptFeeRv'))
        fee = None
        if feeValue is not None:
            fee = {
                'cost': feeValue,
                'currency': market['quote'],
            }
        elif ptFeeRv is not None:
            fee = {
                'cost': ptFeeRv,
                'currency': 'PT',
            }
        return self.safe_order({
            'info': order,
            'id': id,
            'clientOrderId': clientOrderId,
            'datetime': self.iso8601(timestamp),
            'timestamp': timestamp,
            'lastTradeTimestamp': lastTradeTimestamp,
            'symbol': symbol,
            'type': type,
            'timeInForce': timeInForce,
            'postOnly': postOnly,
            'reduceOnly': reduceOnly,
            'side': side,
            'price': price,
            'stopPrice': stopPrice,
            'triggerPrice': stopPrice,
            'takeProfitPrice': takeProfit,
            'stopLossPrice': stopLoss,
            'amount': amount,
            'filled': filled,
            'remaining': remaining,
            'cost': cost,
            'average': None,
            'status': status,
            'fee': fee,
            'trades': None,
        })

    def parse_order(self, order: dict, market: Market = None) -> Order:
        isSwap = self.safe_bool(market, 'swap', False)
        hasPnl = ('closedPnl' in order) or ('closedPnlRv' in order) or ('totalPnlRv' in order)
        if isSwap or hasPnl:
            return self.parse_swap_order(order, market)
        return self.parse_spot_order(order, market)

    def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
        """
        create a trade order
        :see: https://github.com/phemex/phemex-api-docs/blob/master/Public-Hedged-Perpetual-API.md#place-order
        :see: https://phemex-docs.github.io/#place-order-http-put-prefered-3
        :param str symbol: unified symbol of the market to create an order in
        :param str type: 'market' or 'limit'
        :param str side: 'buy' or 'sell'
        :param float amount: how much of currency you want to trade in units of base currency
        :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param float [params.trigger]: trigger price for conditional orders
        :param dict [params.takeProfit]: *swap only* *takeProfit object in params* containing the triggerPrice at which the attached take profit order will be triggered(perpetual swap markets only)
        :param float [params.takeProfit.triggerPrice]: take profit trigger price
        :param dict [params.stopLoss]: *swap only* *stopLoss object in params* containing the triggerPrice at which the attached stop loss order will be triggered(perpetual swap markets only)
        :param float [params.stopLoss.triggerPrice]: stop loss trigger price
        :returns dict: an `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        requestSide = self.capitalize(side)
        type = self.capitalize(type)
        reduceOnly = self.safe_bool(params, 'reduceOnly')
        request: dict = {
            # common
            'symbol': market['id'],
            'side': requestSide,  # Sell, Buy
            'ordType': type,  # Market, Limit, Stop, StopLimit, MarketIfTouched, LimitIfTouched(additionally for contract-markets: MarketAsLimit, StopAsLimit, MarketIfTouchedAsLimit)
            # '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',
            # 'posSide': Position direction - "Merged" for oneway mode , "Long" / "Short" for hedge mode
        }
        clientOrderId = self.safe_string_2(params, 'clOrdID', 'clientOrderId')
        stopLoss = self.safe_value(params, 'stopLoss')
        stopLossDefined = (stopLoss is not None)
        takeProfit = self.safe_value(params, 'takeProfit')
        takeProfitDefined = (takeProfit is not None)
        if clientOrderId is None:
            brokerId = self.safe_string(self.options, 'brokerId', 'ccxt_zagrava123456')
            if brokerId is not None:
                request['clOrdID'] = brokerId + self.uuid16()
        else:
            request['clOrdID'] = clientOrderId
            params = self.omit(params, ['clOrdID', 'clientOrderId'])
        triggerPrice = self.safe_string_n(params, ['stopPx', 'stopPrice', 'triggerPrice'])
        if triggerPrice is not None:
            if market['settle'] == 'USDT':
                request['stopPxRp'] = self.price_to_precision(symbol, triggerPrice)
            else:
                request['stopPxEp'] = self.to_ep(triggerPrice, market)
        params = self.omit(params, ['stopPx', 'stopPrice', 'stopLoss', 'takeProfit', 'triggerPrice'])
        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'
            if triggerPrice is not None:
                if type == 'Limit':
                    request['ordType'] = 'StopLimit'
                elif type == 'Market':
                    request['ordType'] = 'Stop'
                request['trigger'] = 'ByLastPrice'
            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:
                        amountString = self.number_to_string(amount)
                        priceString = self.number_to_string(price)
                        quoteAmount = Precise.string_mul(amountString, priceString)
                        cost = self.parse_number(quoteAmount)
                    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 = self.number_to_string(cost)
                request['quoteQtyEv'] = self.to_ev(costString, market)
            else:
                amountString = self.number_to_string(amount)
                request['baseQtyEv'] = self.to_ev(amountString, market)
        elif market['swap']:
            posSide = self.safe_string_lower(params, 'posSide')
            if posSide is None:
                posSide = 'Merged'
            posSide = self.capitalize(posSide)
            request['posSide'] = posSide
            if reduceOnly is not None:
                request['reduceOnly'] = reduceOnly
            if market['settle'] == 'USDT':
                request['orderQtyRq'] = amount
            else:
                request['orderQty'] = self.parse_to_int(amount)
            if triggerPrice is not None:
                triggerType = self.safe_string(params, 'triggerType', 'ByMarkPrice')
                request['triggerType'] = triggerType
            if stopLossDefined or takeProfitDefined:
                if stopLossDefined:
                    stopLossTriggerPrice = self.safe_value_2(stopLoss, 'triggerPrice', 'stopPrice')
                    if stopLossTriggerPrice is None:
                        raise InvalidOrder(self.id + ' createOrder() requires a trigger price in params["stopLoss"]["triggerPrice"], or params["stopLoss"]["stopPrice"] for a stop loss order')
                    if market['settle'] == 'USDT':
                        request['stopLossRp'] = self.price_to_precision(symbol, stopLossTriggerPrice)
                    else:
                        request['stopLossEp'] = self.to_ep(stopLossTriggerPrice, market)
                    stopLossTriggerPriceType = self.safe_string_2(stopLoss, 'triggerPriceType', 'slTrigger')
                    if stopLossTriggerPriceType is not None:
                        if market['settle'] == 'USDT':
                            if (stopLossTriggerPriceType != 'ByMarkPrice') and (stopLossTriggerPriceType != 'ByLastPrice') and (stopLossTriggerPriceType != 'ByIndexPrice') and (stopLossTriggerPriceType != 'ByAskPrice') and (stopLossTriggerPriceType != 'ByBidPrice') and (stopLossTriggerPriceType != 'ByMarkPriceLimit') and (stopLossTriggerPriceType != 'ByLastPriceLimit'):
                                raise InvalidOrder(self.id + ' createOrder() take profit trigger price type must be one of "ByMarkPrice", "ByIndexPrice", "ByAskPrice", "ByBidPrice", "ByMarkPriceLimit", "ByLastPriceLimit" or "ByLastPrice"')
                        else:
                            if (stopLossTriggerPriceType != 'ByMarkPrice') and (stopLossTriggerPriceType != 'ByLastPrice'):
                                raise InvalidOrder(self.id + ' createOrder() take profit trigger price type must be one of "ByMarkPrice", or "ByLastPrice"')
                        request['slTrigger'] = stopLossTriggerPriceType
                if takeProfitDefined:
                    takeProfitTriggerPrice = self.safe_value_2(takeProfit, 'triggerPrice', 'stopPrice')
                    if takeProfitTriggerPrice is None:
                        raise InvalidOrder(self.id + ' createOrder() requires a trigger price in params["takeProfit"]["triggerPrice"], or params["takeProfit"]["stopPrice"] for a take profit order')
                    if market['settle'] == 'USDT':
                        request['takeProfitRp'] = self.price_to_precision(symbol, takeProfitTriggerPrice)
                    else:
                        request['takeProfitEp'] = self.to_ep(takeProfitTriggerPrice, market)
                    takeProfitTriggerPriceType = self.safe_string_2(stopLoss, 'triggerPriceType', 'tpTrigger')
                    if takeProfitTriggerPriceType is not None:
                        if market['settle'] == 'USDT':
                            if (takeProfitTriggerPriceType != 'ByMarkPrice') and (takeProfitTriggerPriceType != 'ByLastPrice') and (takeProfitTriggerPriceType != 'ByIndexPrice') and (takeProfitTriggerPriceType != 'ByAskPrice') and (takeProfitTriggerPriceType != 'ByBidPrice') and (takeProfitTriggerPriceType != 'ByMarkPriceLimit') and (takeProfitTriggerPriceType != 'ByLastPriceLimit'):
                                raise InvalidOrder(self.id + ' createOrder() take profit trigger price type must be one of "ByMarkPrice", "ByIndexPrice", "ByAskPrice", "ByBidPrice", "ByMarkPriceLimit", "ByLastPriceLimit" or "ByLastPrice"')
                        else:
                            if (takeProfitTriggerPriceType != 'ByMarkPrice') and (takeProfitTriggerPriceType != 'ByLastPrice'):
                                raise InvalidOrder(self.id + ' createOrder() take profit trigger price type must be one of "ByMarkPrice", or "ByLastPrice"')
                        request['tpTrigger'] = takeProfitTriggerPriceType
        if (type == 'Limit') or (type == 'StopLimit') or (type == 'LimitIfTouched'):
            if market['settle'] == 'USDT':
                request['priceRp'] = self.price_to_precision(symbol, price)
            else:
                priceString = self.number_to_string(price)
                request['priceEp'] = self.to_ep(priceString, market)
        takeProfitPrice = self.safe_string(params, 'takeProfitPrice')
        if takeProfitPrice is not None:
            if market['settle'] == 'USDT':
                request['takeProfitRp'] = self.price_to_precision(symbol, takeProfitPrice)
            else:
                request['takeProfitEp'] = self.to_ep(takeProfitPrice, market)
            params = self.omit(params, 'takeProfitPrice')
        stopLossPrice = self.safe_string(params, 'stopLossPrice')
        if stopLossPrice is not None:
            if market['settle'] == 'USDT':
                request['stopLossRp'] = self.price_to_precision(symbol, stopLossPrice)
            else:
                request['stopLossEp'] = self.to_ep(stopLossPrice, market)
            params = self.omit(params, 'stopLossPrice')
        params = self.omit(params, 'reduceOnly')
        response = None
        if market['settle'] == 'USDT':
            response = self.privatePostGOrders(self.extend(request, params))
        elif market['contract']:
            response = self.privatePostOrders(self.extend(request, params))
        else:
            response = self.privatePostSpotOrders(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_dict(response, 'data', {})
        return self.parse_order(data, market)

    def edit_order(self, id: str, symbol: str, type: OrderType = None, side: OrderSide = None, amount: Num = None, price: Num = None, params={}):
        """
        edit a trade order
        :see: https://github.com/phemex/phemex-api-docs/blob/master/Public-Hedged-Perpetual-API.md#amend-order-by-orderid
        :param str id: cancel order id
        :param str symbol: unified symbol of the market to create an order in
        :param str type: 'market' or 'limit'
        :param str side: 'buy' or 'sell'
        :param float amount: how much of currency you want to trade in units of base currency
        :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.posSide]: either 'Merged' or 'Long' or 'Short'
        :returns dict: an `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        clientOrderId = self.safe_string_2(params, 'clientOrderId', 'clOrdID')
        params = self.omit(params, ['clientOrderId', 'clOrdID'])
        isUSDTSettled = (market['settle'] == 'USDT')
        if clientOrderId is not None:
            request['clOrdID'] = clientOrderId
        else:
            request['orderID'] = id
        if price is not None:
            if isUSDTSettled:
                request['priceRp'] = self.price_to_precision(market['symbol'], price)
            else:
                request['priceEp'] = self.to_ep(price, market)
        # Note the uppercase 'V' in 'baseQtyEV' request. that is exchange's requirement at self moment. However, to avoid mistakes from user side, let's support lowercased 'baseQtyEv' too
        finalQty = self.safe_string(params, 'baseQtyEv')
        params = self.omit(params, ['baseQtyEv'])
        if finalQty is not None:
            request['baseQtyEV'] = finalQty
        elif amount is not None:
            if isUSDTSettled:
                request['orderQtyRq'] = self.amount_to_precision(market['symbol'], amount)
            else:
                request['baseQtyEV'] = self.to_ev(amount, market)
        stopPrice = self.safe_string_2(params, 'stopPx', 'stopPrice')
        if stopPrice is not None:
            if isUSDTSettled:
                request['stopPxRp'] = self.price_to_precision(symbol, stopPrice)
            else:
                request['stopPxEp'] = self.to_ep(stopPrice, market)
        params = self.omit(params, ['stopPx', 'stopPrice'])
        response = None
        if isUSDTSettled:
            posSide = self.safe_string(params, 'posSide')
            if posSide is None:
                request['posSide'] = 'Merged'
            response = self.privatePutGOrdersReplace(self.extend(request, params))
        elif market['swap']:
            response = self.privatePutOrdersReplace(self.extend(request, params))
        else:
            response = self.privatePutSpotOrders(self.extend(request, params))
        data = self.safe_dict(response, 'data', {})
        return self.parse_order(data, market)

    def cancel_order(self, id: str, symbol: Str = None, params={}):
        """
        cancels an open order
        :see: https://github.com/phemex/phemex-api-docs/blob/master/Public-Hedged-Perpetual-API.md#cancel-single-order-by-orderid
        :param str id: order id
        :param str symbol: unified symbol of the market the order was made in
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.posSide]: either 'Merged' or 'Long' or 'Short'
        :returns dict: An `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelOrder() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            '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 = None
        if market['settle'] == 'USDT':
            posSide = self.safe_string(params, 'posSide')
            if posSide is None:
                request['posSide'] = 'Merged'
            response = self.privateDeleteGOrdersCancel(self.extend(request, params))
        elif market['swap']:
            response = self.privateDeleteOrdersCancel(self.extend(request, params))
        else:
            response = self.privateDeleteSpotOrders(self.extend(request, params))
        data = self.safe_dict(response, 'data', {})
        return self.parse_order(data, market)

    def cancel_all_orders(self, symbol: Str = None, params={}):
        """
        cancel all open orders in a market
        :see: https://github.com/phemex/phemex-api-docs/blob/master/Public-Hedged-Perpetual-API.md#cancelall
        :param str symbol: unified market symbol of the market to cancel orders in
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: a list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelAllOrders() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        stop = self.safe_value_2(params, 'stop', 'trigger', False)
        params = self.omit(params, 'stop', 'trigger')
        request: dict = {
            'symbol': market['id'],
            # 'untriggerred': False,  # False to cancel non-conditional orders, True to cancel conditional orders
            # 'text': 'up to 40 characters max',
        }
        if stop:
            request['untriggerred'] = stop
        response = None
        if market['settle'] == 'USDT':
            response = self.privateDeleteGOrdersAll(self.extend(request, params))
            #
            #    {
            #        code: '0',
            #        msg: '',
            #        data: '1'
            #    }
            #
        elif market['swap']:
            response = self.privateDeleteOrdersAll(self.extend(request, params))
            #
            #    {
            #        code: '0',
            #        msg: '',
            #        data: '1'
            #    }
            #
        else:
            response = self.privateDeleteSpotOrdersAll(self.extend(request, params))
            #
            #    {
            #        code: '0',
            #        msg: '',
            #        data: {
            #            total: '1'
            #        }
            #    }
            #
        return [
            self.safe_order({
                'info': response,
            }),
        ]

    def fetch_order(self, id: str, symbol: Str = None, params={}):
        """
        :see: https://phemex-docs.github.io/#query-orders-by-ids
        fetches information on an order made by the user
        :param str symbol: unified symbol of the market the order was made in
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: An `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOrder() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            '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 = None
        if market['settle'] == 'USDT':
            response = self.privateGetApiDataGFuturesOrdersByOrderId(self.extend(request, params))
        elif market['spot']:
            response = self.privateGetSpotOrdersActive(self.extend(request, params))
        else:
            response = self.privateGetExchangeOrder(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: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
        """
        fetches information on multiple orders made by the user
        :see: https://github.com/phemex/phemex-api-docs/blob/master/Public-Hedged-Perpetual-API.md#queryorder
        :param str symbol: unified market symbol of the market orders were made in
        :param int [since]: the earliest time in ms to fetch orders for
        :param int [limit]: the maximum number of order structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns Order[]: a list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOrders() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        if since is not None:
            request['start'] = since
        if limit is not None:
            request['limit'] = limit
        response = None
        if market['settle'] == 'USDT':
            request['currency'] = market['settle']
            response = self.privateGetExchangeOrderV2OrderList(self.extend(request, params))
        elif market['swap']:
            response = self.privateGetExchangeOrderList(self.extend(request, params))
        else:
            response = self.privateGetSpotOrders(self.extend(request, params))
        data = self.safe_value(response, 'data', {})
        rows = self.safe_list(data, 'rows', data)
        return self.parse_orders(rows, market, since, limit)

    def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
        """
        fetch all unfilled currently open orders
        :see: https://github.com/phemex/phemex-api-docs/blob/master/Public-Hedged-Perpetual-API.md#queryopenorder
        :see: https://github.com/phemex/phemex-api-docs/blob/master/Public-Contract-API-en.md
        :see: https://github.com/phemex/phemex-api-docs/blob/master/Public-Spot-API-en.md#spotListAllOpenOrder
        :param str symbol: unified market symbol
        :param int [since]: the earliest time in ms to fetch open orders for
        :param int [limit]: the maximum number of open order structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns Order[]: a list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        self.load_markets()
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOpenOrders() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        response = None
        try:
            if market['settle'] == 'USDT':
                response = self.privateGetGOrdersActiveList(self.extend(request, params))
            elif market['swap']:
                response = self.privateGetOrdersActiveList(self.extend(request, params))
            else:
                response = self.privateGetSpotOrders(self.extend(request, params))
        except Exception as e:
            if isinstance(e, OrderNotFound):
                return []
            raise e
        data = self.safe_value(response, 'data', {})
        if isinstance(data, list):
            return self.parse_orders(data, market, since, limit)
        else:
            rows = self.safe_list(data, 'rows', [])
            return self.parse_orders(rows, market, since, limit)

    def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
        """
        fetches information on multiple closed orders made by the user
        :see: https://github.com/phemex/phemex-api-docs/blob/master/Public-Hedged-Perpetual-API.md#queryorder
        :see: https://github.com/phemex/phemex-api-docs/blob/master/Public-Contract-API-en.md#queryorder
        :see: https://github.com/phemex/phemex-api-docs/blob/master/Public-Hedgedd-Perpetual-API.md#query-closed-orders-by-symbol
        :see: https://github.com/phemex/phemex-api-docs/blob/master/Public-Spot-API-en.md#spotDataOrdersByIds
        :param str symbol: unified market symbol of the market orders were made in
        :param int [since]: the earliest time in ms to fetch orders for
        :param int [limit]: the maximum number of order structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.settle]: the settlement currency to fetch orders for
        :returns Order[]: a list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        self.load_markets()
        market = None
        if symbol is not None:
            market = self.market(symbol)
        request: dict = {
        }
        if market is not None:
            request['symbol'] = market['id']
        if since is not None:
            request['start'] = since
        if limit is not None:
            request['limit'] = limit
        response = None
        if (symbol is None) or (self.safe_string(market, 'settle') == 'USDT'):
            request['currency'] = self.safe_string(params, 'settle', 'USDT')
            response = self.privateGetExchangeOrderV2OrderList(self.extend(request, params))
        elif market['swap']:
            response = self.privateGetExchangeOrderList(self.extend(request, params))
        else:
            response = self.privateGetExchangeSpotOrder(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_list(data, 'rows', [])
            return self.parse_orders(rows, market, since, limit)

    def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        fetch all trades made by the user
        :see: https://github.com/phemex/phemex-api-docs/blob/master/Public-Contract-API-en.md#query-user-trade
        :see: https://github.com/phemex/phemex-api-docs/blob/master/Public-Hedged-Perpetual-API.md#query-user-trade
        :see: https://github.com/phemex/phemex-api-docs/blob/master/Public-Spot-API-en.md#spotDataTradesHist
        :param str symbol: unified market symbol
        :param int [since]: the earliest time in ms to fetch trades for
        :param int [limit]: the maximum number of trades structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns Trade[]: a list of `trade structures <https://docs.ccxt_zagrava.com/#/?id=trade-structure>`
        """
        self.load_markets()
        market = None
        if symbol is not None:
            market = self.market(symbol)
        request: dict = {}
        if limit is not None:
            limit = min(200, limit)
            request['limit'] = limit
        isUSDTSettled = (symbol is None) or (self.safe_string(market, 'settle') == 'USDT')
        if isUSDTSettled:
            request['currency'] = 'USDT'
            request['offset'] = 0
            if limit is None:
                request['limit'] = 200
        else:
            request['symbol'] = market['id']
        if since is not None:
            request['start'] = since
        response = None
        if isUSDTSettled:
            response = self.privateGetExchangeOrderV2TradingList(self.extend(request, params))
        elif market['swap']:
            response = self.privateGetExchangeOrderTrade(self.extend(request, params))
        else:
            response = self.privateGetExchangeSpotOrderTrades(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"
        #                 },
        #             ]
        #         }
        #     }
        #
        # swap - usdt
        #
        # {
        #     "code": 0,
        #     "msg": "OK",
        #     "data": {
        #         "total": 4,
        #         "rows": [
        #             {
        #                 "createdAt": 1666226932259,
        #                 "symbol": "ETHUSDT",
        #                 "currency": "USDT",
        #                 "action": 1,
        #                 "tradeType": 1,
        #                 "execQtyRq": "0.01",
        #                 "execPriceRp": "1271.9",
        #                 "side": 1,
        #                 "orderQtyRq": "0.78",
        #                 "priceRp": "1271.9",
        #                 "execValueRv": "12.719",
        #                 "feeRateRr": "0.0001",
        #                 "execFeeRv": "0.0012719",
        #                 "ordType": 2,
        #                 "execId": "8718cae",
        #                 "execStatus": 6
        #             },
        #         ]
        #     }
        # }
        #
        data = None
        if isUSDTSettled:
            data = self.safe_value(response, 'data', [])
        else:
            data = self.safe_value(response, 'data', {})
            data = self.safe_value(data, 'rows', [])
        return self.parse_trades(data, market, since, limit)

    def fetch_deposit_address(self, code: str, params={}):
        """
        fetch the deposit address for a currency associated with self account
        :param str code: unified currency code
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an `address structure <https://docs.ccxt_zagrava.com/#/?id=address-structure>`
        """
        self.load_markets()
        currency = self.currency(code)
        request: dict = {
            '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,
            'network': None,
            'info': response,
        }

    def fetch_deposits(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
        """
        fetch all deposits made to an account
        :param str code: unified currency code
        :param int [since]: the earliest time in ms to fetch deposits for
        :param int [limit]: the maximum number of deposits structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: a list of `transaction structures <https://docs.ccxt_zagrava.com/#/?id=transaction-structure>`
        """
        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_list(response, 'data', [])
        return self.parse_transactions(data, currency, since, limit)

    def fetch_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
        """
        fetch all withdrawals made from an account
        :param str code: unified currency code
        :param int [since]: the earliest time in ms to fetch withdrawals for
        :param int [limit]: the maximum number of withdrawals structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: a list of `transaction structures <https://docs.ccxt_zagrava.com/#/?id=transaction-structure>`
        """
        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_list(response, 'data', [])
        return self.parse_transactions(data, currency, since, limit)

    def parse_transaction_status(self, status: Str):
        statuses: dict = {
            'Success': 'ok',
            'Succeed': 'ok',
            'Rejected': 'failed',
            'Security check failed': 'failed',
            'SecurityCheckFailed': 'failed',
            'Expired': 'failed',
            'Address Risk': 'failed',
            'Security Checking': 'pending',
            'SecurityChecking': 'pending',
            'Pending Review': 'pending',
            'Pending Transfer': 'pending',
            'AmlCsApporve': 'pending',
            'New': 'pending',
            'Confirmed': 'pending',
            'Cancelled': 'canceled',
        }
        return self.safe_string(statuses, status, status)

    def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction:
        #
        # withdraw
        #
        #     {
        #         "id": "10000001",
        #         "freezeId": null,
        #         "address": "44exxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
        #         "amountRv": "100",
        #         "chainCode": "11",
        #         "chainName": "TRX",
        #         "currency": "USDT",
        #         "currencyCode": 3,
        #         "email": "abc@gmail.com",
        #         "expiredTime": "0",
        #         "feeRv": "1",
        #         "nickName": null,
        #         "phone": null,
        #         "rejectReason": "",
        #         "submitedAt": "1670000000000",
        #         "submittedAt": "1670000000000",
        #         "txHash": null,
        #         "userId": "10000001",
        #         "status": "Success"
        #
        # fetchDeposits
        #
        #     {
        #         "id": "29200",
        #         "currency": "USDT",
        #         "currencyCode": "3",
        #         "chainName": "ETH",
        #         "chainCode": "4",
        #         "txHash": "0x0bdbdc47807769a03b158d5753f54dfc58b92993d2f5e818db21863e01238e5d",
        #         "address": "0x5bfbf60e0fa7f63598e6cfd8a7fd3ffac4ccc6ad",
        #         "amountEv": "3000000000",
        #         "confirmations": "13",
        #         "type": "Deposit",
        #         "status": "Success",
        #         "createdAt": "1592722565000",
        #     }
        #
        # fetchWithdrawals
        #
        #     {
        #         "id": "10000001",
        #         "userId": "10000001",
        #         "freezeId": "10000002",
        #         "phone": null,
        #         "email": "abc@gmail.com",
        #         "nickName": null,
        #         "currency": "USDT",
        #         "currencyCode": "3",
        #         "status": "Succeed",
        #         "withdrawStatus": "Succeed",
        #         "amountEv": "8800000000",
        #         "feeEv": "1200000000",
        #         "address": "0x5xxxad",
        #         "txHash: "0x0xxxx5d",
        #         "submitedAt": "1702571922000",
        #         "submittedAt": "1702571922000",
        #         "expiredTime": "0",
        #         "rejectReason": null,
        #         "chainName": "ETH",
        #         "chainCode": "4",
        #         "proxyAddress": null
        #     }
        #
        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']
        networkId = self.safe_string(transaction, 'chainName')
        timestamp = self.safe_integer_n(transaction, ['createdAt', 'submitedAt', 'submittedAt'])
        type = self.safe_string_lower(transaction, 'type')
        feeCost = self.parse_number(self.from_en(self.safe_string(transaction, 'feeEv'), currency['valueScale']))
        if feeCost is None:
            feeCost = self.safe_number(transaction, 'feeRv')
        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']))
        if amount is None:
            amount = self.safe_number(transaction, 'amountRv')
        return {
            'info': transaction,
            'id': id,
            'txid': txid,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'network': self.network_id_to_code(networkId),
            'address': address,
            'addressTo': address,
            'addressFrom': None,
            'tag': tag,
            'tagTo': tag,
            'tagFrom': None,
            'type': type,
            'amount': amount,
            'currency': code,
            'status': status,
            'updated': None,
            'comment': None,
            'internal': None,
            'fee': fee,
        }

    def fetch_positions(self, symbols: Strings = None, params={}):
        """
        fetch all open positions
        :see: https://github.com/phemex/phemex-api-docs/blob/master/Public-Contract-API-en.md#query-trading-account-and-positions
        :see: https://github.com/phemex/phemex-api-docs/blob/master/Public-Hedged-Perpetual-API.md#query-account-positions
        :see: https://phemex-docs.github.io/#query-account-positions-with-unrealized-pnl
        :param str[] [symbols]: list of unified market symbols
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [param.method]: *USDT contracts only* 'privateGetGAccountsAccountPositions' or 'privateGetAccountsPositions' default is 'privateGetGAccountsAccountPositions'
        :returns dict[]: a list of `position structure <https://docs.ccxt_zagrava.com/#/?id=position-structure>`
        """
        self.load_markets()
        symbols = self.market_symbols(symbols)
        subType = None
        code = self.safe_string(params, 'currency')
        settle = None
        market = None
        firstSymbol = self.safe_string(symbols, 0)
        if firstSymbol is not None:
            market = self.market(firstSymbol)
            settle = market['settle']
            code = market['settle']
        else:
            settle, params = self.handle_option_and_params(params, 'fetchPositions', 'settle', 'USD')
        subType, params = self.handle_sub_type_and_params('fetchPositions', market, params)
        isUSDTSettled = settle == 'USDT'
        if isUSDTSettled:
            code = 'USDT'
        elif code is None:
            code = 'USD' if (subType == 'linear') else 'BTC'
        else:
            params = self.omit(params, 'code')
        currency = self.currency(code)
        request: dict = {
            'currency': currency['id'],
        }
        response = None
        if isUSDTSettled:
            method = None
            method, params = self.handle_option_and_params(params, 'fetchPositions', 'method', 'privateGetGAccountsAccountPositions')
            if method == 'privateGetGAccountsAccountPositions':
                response = self.privateGetGAccountsAccountPositions(self.extend(request, params))
            else:
                response = self.privateGetAccountsPositions(self.extend(request, params))
        else:
            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', [])
        result = []
        for i in range(0, len(positions)):
            position = positions[i]
            result.append(self.parse_position(position))
        return self.filter_by_array_positions(result, 'symbol', symbols, False)

    def parse_position(self, position: dict, market: Market = None):
        #
        #    {
        #        "userID": "811370",
        #        "accountID": "8113700002",
        #        "symbol": "ETHUSD",
        #        "currency": "USD",
        #        "side": "Buy",
        #        "positionStatus": "Normal",
        #        "crossMargin": False,
        #        "leverageEr": "200000000",
        #        "leverage": "2.00000000",
        #        "initMarginReqEr": "50000000",
        #        "initMarginReq": "0.50000000",
        #        "maintMarginReqEr": "1000000",
        #        "maintMarginReq": "0.01000000",
        #        "riskLimitEv": "5000000000",
        #        "riskLimit": "500000.00000000",
        #        "size": "1",
        #        "value": "22.22370000",
        #        "valueEv": "222237",
        #        "avgEntryPriceEp": "44447400",
        #        "avgEntryPrice": "4444.74000000",
        #        "posCostEv": "111202",
        #        "posCost": "11.12020000",
        #        "assignedPosBalanceEv": "111202",
        #        "assignedPosBalance": "11.12020000",
        #        "bankruptCommEv": "84",
        #        "bankruptComm": "0.00840000",
        #        "bankruptPriceEp": "22224000",
        #        "bankruptPrice": "2222.40000000",
        #        "positionMarginEv": "111118",
        #        "positionMargin": "11.11180000",
        #        "liquidationPriceEp": "22669000",
        #        "liquidationPrice": "2266.90000000",
        #        "deleveragePercentileEr": "0",
        #        "deleveragePercentile": "0E-8",
        #        "buyValueToCostEr": "50112500",
        #        "buyValueToCost": "0.50112500",
        #        "sellValueToCostEr": "50187500",
        #        "sellValueToCost": "0.50187500",
        #        "markPriceEp": "31332499",
        #        "markPrice": "3133.24990000",
        #        "markValueEv": "0",
        #        "markValue": null,
        #        "unRealisedPosLossEv": "0",
        #        "unRealisedPosLoss": null,
        #        "estimatedOrdLossEv": "0",
        #        "estimatedOrdLoss": "0E-8",
        #        "usedBalanceEv": "111202",
        #        "usedBalance": "11.12020000",
        #        "takeProfitEp": "0",
        #        "takeProfit": null,
        #        "stopLossEp": "0",
        #        "stopLoss": null,
        #        "cumClosedPnlEv": "-1546",
        #        "cumFundingFeeEv": "1605",
        #        "cumTransactFeeEv": "8438",
        #        "realisedPnlEv": "0",
        #        "realisedPnl": null,
        #        "cumRealisedPnlEv": "0",
        #        "cumRealisedPnl": null,
        #        "transactTimeNs": "1641571200001885324",
        #        "takerFeeRateEr": "0",
        #        "makerFeeRateEr": "0",
        #        "term": "6",
        #        "lastTermEndTimeNs": "1607711882505745356",
        #        "lastFundingTimeNs": "1641571200000000000",
        #        "curTermRealisedPnlEv": "-1567",
        #        "execSeq": "12112761561"
        #    }
        #
        marketId = self.safe_string(position, 'symbol')
        market = self.safe_market(marketId, market)
        symbol = market['symbol']
        collateral = self.safe_string_2(position, 'positionMargin', 'positionMarginRv')
        notionalString = self.safe_string_2(position, 'value', 'valueRv')
        maintenanceMarginPercentageString = self.safe_string_2(position, 'maintMarginReq', 'maintMarginReqRr')
        maintenanceMarginString = Precise.string_mul(notionalString, maintenanceMarginPercentageString)
        initialMarginString = self.safe_string_2(position, 'assignedPosBalance', 'assignedPosBalanceRv')
        initialMarginPercentageString = Precise.string_div(initialMarginString, notionalString)
        liquidationPrice = self.safe_number_2(position, 'liquidationPrice', 'liquidationPriceRp')
        markPriceString = self.safe_string_2(position, 'markPrice', 'markPriceRp')
        contracts = self.safe_string(position, 'size')
        contractSize = self.safe_value(market, 'contractSize')
        contractSizeString = self.number_to_string(contractSize)
        leverage = self.parse_number(Precise.string_abs((self.safe_string_2(position, 'leverage', 'leverageRr'))))
        entryPriceString = self.safe_string_2(position, 'avgEntryPrice', 'avgEntryPriceRp')
        rawSide = self.safe_string(position, 'side')
        side = None
        if rawSide is not None:
            side = 'long' if (rawSide == 'Buy') else 'short'
        priceDiff = None
        currency = self.safe_string(position, 'currency')
        if currency == 'USD':
            if side == 'long':
                priceDiff = Precise.string_sub(markPriceString, entryPriceString)
            else:
                priceDiff = Precise.string_sub(entryPriceString, markPriceString)
        else:
            # inverse
            if side == 'long':
                priceDiff = Precise.string_sub(Precise.string_div('1', entryPriceString), Precise.string_div('1', markPriceString))
            else:
                priceDiff = Precise.string_sub(Precise.string_div('1', markPriceString), Precise.string_div('1', entryPriceString))
        unrealizedPnl = Precise.string_mul(Precise.string_mul(priceDiff, contracts), contractSizeString)
        marginRatio = Precise.string_div(maintenanceMarginString, collateral)
        isCross = self.safe_value(position, 'crossMargin')
        return self.safe_position({
            'info': position,
            'id': None,
            'symbol': symbol,
            'contracts': self.parse_number(contracts),
            'contractSize': contractSize,
            'unrealizedPnl': self.parse_number(unrealizedPnl),
            'leverage': leverage,
            'liquidationPrice': liquidationPrice,
            'collateral': self.parse_number(collateral),
            'notional': self.parse_number(notionalString),
            'markPrice': self.parse_number(markPriceString),  # markPrice lags a bit ¯\_(ツ)_/¯
            'lastPrice': None,
            'entryPrice': self.parse_number(entryPriceString),
            'timestamp': None,
            'lastUpdateTimestamp': None,
            'initialMargin': self.parse_number(initialMarginString),
            'initialMarginPercentage': self.parse_number(initialMarginPercentageString),
            'maintenanceMargin': self.parse_number(maintenanceMarginString),
            'maintenanceMarginPercentage': self.parse_number(maintenanceMarginPercentageString),
            'marginRatio': self.parse_number(marginRatio),
            'datetime': None,
            'marginMode': 'cross' if isCross else 'isolated',
            'side': side,
            'hedged': False,
            'percentage': None,
            'stopLossPrice': None,
            'takeProfitPrice': None,
        })

    def fetch_funding_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        fetch the history of funding payments paid and received on self account
        :see: https://github.com/phemex/phemex-api-docs/blob/master/Public-Hedged-Perpetual-API.md#futureDataFundingFeesHist
        :param str symbol: unified market symbol
        :param int [since]: the earliest time in ms to fetch funding history for
        :param int [limit]: the maximum number of funding history structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `funding history structure <https://docs.ccxt_zagrava.com/#/?id=funding-history-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchFundingHistory() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
            # 'limit': 20,  # Page size default 20, max 200
            # 'offset': 0,  # Page start default 0
        }
        if limit is not None:
            if limit > 200:
                raise BadRequest(self.id + ' fetchFundingHistory() limit argument cannot exceed 200')
            request['limit'] = limit
        response = None
        isUsdt = market['settle'] == 'USDT'
        if isUsdt:
            response = self.privateGetApiDataGFuturesFundingFees(self.extend(request, params))
        else:
            response = self.privateGetApiDataFuturesFundingFees(self.extend(request, params))
        #
        #     {
        #         "code": 0,
        #         "msg": "OK",
        #         "data": {
        #             "rows": [
        #                 {
        #                     "symbol": "BTCUSD",
        #                     "currency": "BTC",
        #                     "execQty": 18,  # "execQty" regular, but "execQtyRq" in hedge
        #                     "side": "Buy",
        #                     "execPriceEp": 360086455,  # "execPriceEp" regular, but "execPriceRp" in hedge
        #                     "execValueEv": 49987,  # "execValueEv" regular, but "execValueRv" in hedge
        #                     "fundingRateEr": 10000,  # "fundingRateEr" regular, but "fundingRateRr" in hedge
        #                     "feeRateEr": 10000,  # "feeRateEr" regular, but "feeRateRr" in hedge
        #                     "execFeeEv": 5,  # "execFeeEv" regular, but "execFeeRv" in hedge
        #                     "createTime": 1651881600000
        #                 }
        #             ]
        #         }
        #     }
        #
        data = self.safe_value(response, 'data', {})
        rows = self.safe_value(data, 'rows', [])
        result = []
        for i in range(0, len(rows)):
            entry = rows[i]
            timestamp = self.safe_integer(entry, 'createTime')
            execFee = self.safe_string_2(entry, 'execFeeEv', 'execFeeRv')
            currencyCode = self.safe_currency_code(self.safe_string(entry, 'currency'))
            result.append({
                'info': entry,
                'symbol': self.safe_string(entry, 'symbol'),
                'code': currencyCode,
                'timestamp': timestamp,
                'datetime': self.iso8601(timestamp),
                'id': None,
                'amount': self.parse_funding_fee_to_precision(execFee, market, currencyCode),
            })
        return result

    def parse_funding_fee_to_precision(self, value, market: Market = None, currencyCode: Str = None):
        if value is None or currencyCode is None:
            return value
        # it was confirmed by phemex support, that USDT contracts use direct amounts in funding fees, while USD & INVERSE needs 'valueScale'
        isUsdt = market['settle'] == 'USDT'
        if not isUsdt:
            currency = self.safe_currency(currencyCode)
            scale = self.safe_string(currency['info'], 'valueScale')
            tickPrecision = self.parse_precision(scale)
            value = Precise.string_mul(value, tickPrecision)
        return value

    def fetch_funding_rate(self, symbol: str, params={}):
        """
        fetch the current funding rate
        :param str symbol: unified market symbol
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `funding rate structure <https://docs.ccxt_zagrava.com/#/?id=funding-rate-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        if not market['swap']:
            raise BadSymbol(self.id + ' fetchFundingRate() supports swap contracts only')
        request: dict = {
            'symbol': market['id'],
        }
        response: dict = {}
        if not market['linear']:
            response = self.v1GetMdTicker24hr(self.extend(request, params))
        else:
            response = self.v2GetMdV2Ticker24hr(self.extend(request, params))
        #
        #     {
        #         "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_funding_rate(result, market)

    def parse_funding_rate(self, contract, market: Market = None):
        #
        #     {
        #         "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
        #     }
        #
        # linear swap v2
        #
        #     {
        #         "closeRp":"16820.5",
        #         "fundingRateRr":"0.0001",
        #         "highRp":"16962.1",
        #         "indexPriceRp":"16830.15651565",
        #         "lowRp":"16785",
        #         "markPriceRp":"16830.97534951",
        #         "openInterestRv":"1323.596",
        #         "openRp":"16851.7",
        #         "predFundingRateRr":"0.0001",
        #         "symbol":"BTCUSDT",
        #         "timestamp":"1672142789065593096",
        #         "turnoverRv":"124835296.0538",
        #         "volumeRq":"7406.95"
        #     }
        #
        marketId = self.safe_string(contract, 'symbol')
        symbol = self.safe_symbol(marketId, market)
        timestamp = self.safe_integer_product(contract, 'timestamp', 0.000001)
        return {
            'info': contract,
            'symbol': symbol,
            'markPrice': self.from_ep(self.safe_string_2(contract, 'markEp', 'markPriceRp'), market),
            'indexPrice': self.from_ep(self.safe_string_2(contract, 'indexEp', 'indexPriceRp'), market),
            'interestRate': None,
            'estimatedSettlePrice': None,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'fundingRate': self.from_er(self.safe_string(contract, 'fundingRateEr'), market),
            'fundingTimestamp': None,
            'fundingDatetime': None,
            'nextFundingRate': self.from_er(self.safe_string_2(contract, 'predFundingRateEr', 'predFundingRateRr'), market),
            'nextFundingTimestamp': None,
            'nextFundingDatetime': None,
            'previousFundingRate': None,
            'previousFundingTimestamp': None,
            'previousFundingDatetime': None,
        }

    def set_margin(self, symbol: str, amount: float, params={}) -> MarginModification:
        """
        Either adds or reduces margin in an isolated position in order to set the margin to a specific value
        :see: https://github.com/phemex/phemex-api-docs/blob/master/Public-Contract-API-en.md#assign-position-balance-in-isolated-marign-mode
        :param str symbol: unified market symbol of the market to set margin in
        :param float amount: the amount to set the margin to
        :param dict [params]: parameters specific to the exchange API endpoint
        :returns dict: A `margin structure <https://docs.ccxt_zagrava.com/#/?id=add-margin-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
            'posBalanceEv': self.to_ev(amount, market),
        }
        response = self.privatePostPositionsAssign(self.extend(request, params))
        #
        #     {
        #         "code": 0,
        #         "msg": "",
        #         "data": "OK"
        #     }
        #
        return self.extend(self.parse_margin_modification(response, market), {
            'amount': amount,
        })

    def parse_margin_status(self, status):
        statuses: dict = {
            '0': 'ok',
        }
        return self.safe_string(statuses, status, status)

    def parse_margin_modification(self, data: dict, market: Market = None) -> MarginModification:
        #
        #     {
        #         "code": 0,
        #         "msg": "",
        #         "data": "OK"
        #     }
        #
        market = self.safe_market(None, market)
        inverse = self.safe_value(market, 'inverse')
        codeCurrency = 'base' if inverse else 'quote'
        return {
            'info': data,
            'symbol': self.safe_symbol(None, market),
            'type': 'set',
            'marginMode': 'isolated',
            'amount': None,
            'total': None,
            'code': market[codeCurrency],
            'status': self.parse_margin_status(self.safe_string(data, 'code')),
            'timestamp': None,
            'datetime': None,
        }

    def set_margin_mode(self, marginMode: str, symbol: Str = None, params={}):
        """
        set margin mode to 'cross' or 'isolated'
        :param str marginMode: 'cross' or 'isolated'
        :param str symbol: unified market symbol
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: response from the exchange
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' setMarginMode() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        if not market['swap'] or market['settle'] == 'USDT':
            raise BadSymbol(self.id + ' setMarginMode() supports swap(non USDT based) contracts only')
        marginMode = marginMode.lower()
        if marginMode != 'isolated' and marginMode != 'cross':
            raise BadRequest(self.id + ' setMarginMode() marginMode argument should be isolated or cross')
        leverage = self.safe_integer(params, 'leverage')
        if marginMode == 'cross':
            leverage = 0
        if leverage is None:
            raise ArgumentsRequired(self.id + ' setMarginMode() requires a leverage parameter')
        request: dict = {
            'symbol': market['id'],
            'leverage': leverage,
        }
        return self.privatePutPositionsLeverage(self.extend(request, params))

    def set_position_mode(self, hedged: bool, symbol: Str = None, params={}):
        """
        set hedged to True or False for a market
        :see: https://github.com/phemex/phemex-api-docs/blob/master/Public-Hedged-Perpetual-API.md#switch-position-mode-synchronously
        :param bool hedged: set to True to use dualSidePosition
        :param str symbol: not used by binance setPositionMode()
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: response from the exchange
        """
        self.check_required_argument('setPositionMode', symbol, 'symbol')
        self.load_markets()
        market = self.market(symbol)
        if market['settle'] != 'USDT':
            raise BadSymbol(self.id + ' setPositionMode() supports USDT settled markets only')
        request: dict = {
            'symbol': market['id'],
        }
        if hedged:
            request['targetPosMode'] = 'Hedged'
        else:
            request['targetPosMode'] = 'OneWay'
        return self.privatePutGPositionsSwitchPosModeSync(self.extend(request, params))

    def fetch_leverage_tiers(self, symbols: Strings = None, params={}) -> LeverageTiers:
        """
        retrieve information on the maximum leverage, and maintenance margin for trades of varying trade sizes
        :param str[]|None symbols: list of unified market symbols
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a dictionary of `leverage tiers structures <https://docs.ccxt_zagrava.com/#/?id=leverage-tiers-structure>`, indexed by market symbols
        """
        self.load_markets()
        if symbols is not None:
            first = self.safe_value(symbols, 0)
            market = self.market(first)
            if market['settle'] != 'USD':
                raise BadSymbol(self.id + ' fetchLeverageTiers() supports USD settled markets only')
        response = 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]},
        #             ]
        #         }
        #     }
        #
        #
        data = self.safe_value(response, 'data', {})
        riskLimits = self.safe_list(data, 'riskLimits')
        return self.parse_leverage_tiers(riskLimits, symbols, 'symbol')

    def parse_market_leverage_tiers(self, info, market: Market = None) -> List[LeverageTier]:
        """
        :param dict info: Exchange market response for 1 market
        :param dict market: ccxt_zagrava market
        """
        #
        #     {
        #         "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},
        #         ]
        #     },
        #
        market = self.safe_market(None, market)
        riskLimits = (market['info']['riskLimits'])
        tiers = []
        minNotional = 0
        for i in range(0, len(riskLimits)):
            tier = riskLimits[i]
            maxNotional = self.safe_integer(tier, 'limit')
            tiers.append({
                'tier': self.sum(i, 1),
                'currency': market['settle'],
                'minNotional': minNotional,
                'maxNotional': maxNotional,
                'maintenanceMarginRate': self.safe_string(tier, 'maintenanceMargin'),
                'maxLeverage': None,
                'info': tier,
            })
            minNotional = maxNotional
        return tiers

    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') or (url == '/positions/assign'):
            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':
                isOrderPlacement = (path == 'g-orders') or (path == 'spot/orders') or (path == 'orders')
                if isOrderPlacement:
                    if self.safe_string(params, 'clOrdID') is None:
                        id = self.safe_string(self.options, 'brokerId', 'ccxt_zagrava123456')
                        params['clOrdID'] = id + self.uuid16()
                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), hashlib.sha256)
        url = self.implode_hostname(self.urls['api'][api]) + url
        return {'url': url, 'method': method, 'body': body, 'headers': headers}

    def set_leverage(self, leverage: Int, symbol: Str = None, params={}):
        """
        set the level of leverage for a market
        :see: https://github.com/phemex/phemex-api-docs/blob/master/Public-Hedged-Perpetual-API.md#set-leverage
        :param float leverage: the rate of leverage, 100 > leverage > -100 excluding numbers between -1 to 1
        :param str symbol: unified market symbol
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param bool [params.hedged]: set to True if hedged position mode is enabled(by default long and short leverage are set to the same value)
        :param float [params.longLeverageRr]: *hedged mode only* set the leverage for long positions
        :param float [params.shortLeverageRr]: *hedged mode only* set the leverage for short positions
        :returns dict: response from the exchange
        """
        # WARNING: THIS WILL INCREASE LIQUIDATION PRICE FOR OPEN ISOLATED LONG POSITIONS
        # AND DECREASE LIQUIDATION PRICE FOR OPEN ISOLATED SHORT POSITIONS
        if symbol is None:
            raise ArgumentsRequired(self.id + ' setLeverage() requires a symbol argument')
        if (leverage < -100) or (leverage > 100):
            raise BadRequest(self.id + ' setLeverage() leverage should be between -100 and 100')
        self.load_markets()
        isHedged = self.safe_bool(params, 'hedged', False)
        longLeverageRr = self.safe_integer(params, 'longLeverageRr')
        shortLeverageRr = self.safe_integer(params, 'shortLeverageRr')
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        response = None
        if market['settle'] == 'USDT':
            if not isHedged and longLeverageRr is None and shortLeverageRr is None:
                request['leverageRr'] = leverage
            else:
                longVar = longLeverageRr if (longLeverageRr is not None) else leverage
                shortVar = shortLeverageRr if (shortLeverageRr is not None) else leverage
                request['longLeverageRr'] = longVar
                request['shortLeverageRr'] = shortVar
            response = self.privatePutGPositionsLeverage(self.extend(request, params))
        else:
            request['leverage'] = leverage
            response = self.privatePutPositionsLeverage(self.extend(request, params))
        return response

    def transfer(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}) -> TransferEntry:
        """
        transfer currency internally between wallets on the same account
        :param str code: unified currency code
        :param float amount: amount to transfer
        :param str fromAccount: account to transfer from
        :param str toAccount: account to transfer to
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.bizType]: for transferring between main and sub-acounts either 'SPOT' or 'PERPETUAL' default is 'SPOT'
        :returns dict: a `transfer structure <https://docs.ccxt_zagrava.com/#/?id=transfer-structure>`
        """
        self.load_markets()
        currency = self.currency(code)
        accountsByType = self.safe_value(self.options, 'accountsByType', {})
        fromId = self.safe_string(accountsByType, fromAccount, fromAccount)
        toId = self.safe_string(accountsByType, toAccount, toAccount)
        scaledAmmount = self.to_ev(amount, currency)
        direction = None
        transfer = None
        if fromId == 'spot' and toId == 'future':
            direction = 2
        elif fromId == 'future' and toId == 'spot':
            direction = 1
        if direction is not None:
            request: dict = {
                'currency': currency['id'],
                'moveOp': direction,
                'amountEv': scaledAmmount,
            }
            response = self.privatePostAssetsTransfer(self.extend(request, params))
            #
            #     {
            #         "code": "0",
            #         "msg": "OK",
            #         "data": {
            #             "linkKey": "8564eba4-c9ec-49d6-9b8c-2ec5001a0fb9",
            #             "userId": "4018340",
            #             "currency": "USD",
            #             "amountEv": "10",
            #             "side": "2",
            #             "status": "10"
            #         }
            #     }
            #
            data = self.safe_value(response, 'data', {})
            transfer = self.parse_transfer(data, currency)
        else:  # sub account transfer
            request: dict = {
                'fromUserId': fromId,
                'toUserId': toId,
                'amountEv': scaledAmmount,
                'currency': currency['id'],
                'bizType': self.safe_string(params, 'bizType', 'SPOT'),
            }
            response = self.privatePostAssetsUniversalTransfer(self.extend(request, params))
            #
            #     {
            #         "code": "0",
            #         "msg": "OK",
            #         "data": "API-923db826-aaaa-aaaa-aaaa-4d98c3a7c9fd"
            #     }
            #
            transfer = self.parse_transfer(response)
        transferOptions = self.safe_value(self.options, 'transfer', {})
        fillResponseFromRequest = self.safe_bool(transferOptions, 'fillResponseFromRequest', True)
        if fillResponseFromRequest:
            if transfer['fromAccount'] is None:
                transfer['fromAccount'] = fromAccount
            if transfer['toAccount'] is None:
                transfer['toAccount'] = toAccount
            if transfer['amount'] is None:
                transfer['amount'] = amount
            if transfer['currency'] is None:
                transfer['currency'] = code
        return transfer

    def fetch_transfers(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> TransferEntries:
        """
        fetch a history of internal transfers made on an account
        :param str code: unified currency code of the currency transferred
        :param int [since]: the earliest time in ms to fetch transfers for
        :param int [limit]: the maximum number of  transfers structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: a list of `transfer structures <https://docs.ccxt_zagrava.com/#/?id=transfer-structure>`
        """
        self.load_markets()
        if code is None:
            raise ArgumentsRequired(self.id + ' fetchTransfers() requires a code argument')
        currency = self.currency(code)
        request: dict = {
            'currency': currency['id'],
        }
        if since is not None:
            request['start'] = since
        if limit is not None:
            request['limit'] = limit
        response = self.privateGetAssetsTransfer(self.extend(request, params))
        #
        #     {
        #         "code": 0,
        #         "msg": "OK",
        #         "data": {
        #             "rows": [
        #                 {
        #                     "linkKey": "87c071a3-8628-4ac2-aca1-6ce0d1fad66c",
        #                     "userId": 4148428,
        #                     "currency": "BTC",
        #                     "amountEv": 67932,
        #                     "side": 2,
        #                     "status": 10,
        #                     "createTime": 1652832467000,
        #                     "bizType": 10
        #                 }
        #             ]
        #         }
        #     }
        #
        data = self.safe_value(response, 'data', {})
        transfers = self.safe_list(data, 'rows', [])
        return self.parse_transfers(transfers, currency, since, limit)

    def parse_transfer(self, transfer: dict, currency: Currency = None) -> TransferEntry:
        #
        # transfer
        #
        #     {
        #         "linkKey": "8564eba4-c9ec-49d6-9b8c-2ec5001a0fb9",
        #         "userId": "4018340",
        #         "currency": "USD",
        #         "amountEv": "10",
        #         "side": "2",
        #         "status": "10"
        #     }
        #
        # fetchTransfers
        #
        #     {
        #         "linkKey": "87c071a3-8628-4ac2-aca1-6ce0d1fad66c",
        #         "userId": 4148428,
        #         "currency": "BTC",
        #         "amountEv": 67932,
        #         "side": 2,
        #         "status": 10,
        #         "createTime": 1652832467000,
        #         "bizType": 10
        #     }
        #
        id = self.safe_string(transfer, 'linkKey')
        status = self.safe_string(transfer, 'status')
        amountEv = self.safe_string(transfer, 'amountEv')
        amountTransfered = self.from_ev(amountEv)
        currencyId = self.safe_string(transfer, 'currency')
        code = self.safe_currency_code(currencyId, currency)
        side = self.safe_integer(transfer, 'side')
        fromId = None
        toId = None
        if side == 1:
            fromId = 'swap'
            toId = 'spot'
        elif side == 2:
            fromId = 'spot'
            toId = 'swap'
        timestamp = self.safe_integer(transfer, 'createTime')
        return {
            'info': transfer,
            'id': id,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'currency': code,
            'amount': amountTransfered,
            'fromAccount': fromId,
            'toAccount': toId,
            'status': self.parse_transfer_status(status),
        }

    def parse_transfer_status(self, status: Str) -> Str:
        statuses: dict = {
            '3': 'rejected',  # 'Rejected',
            '6': 'canceled',  # 'Got error and wait for recovery',
            '10': 'ok',  # 'Success',
            '11': 'failed',  # 'Failed',
        }
        return self.safe_string(statuses, status, status)

    def fetch_funding_rate_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        fetches historical funding rate prices
        :see: https://phemex-docs.github.io/#query-funding-rate-history-2
        :param str symbol: unified symbol of the market to fetch the funding rate history for
        :param int [since]: timestamp in ms of the earliest funding rate to fetch
        :param int [limit]: the maximum amount of `funding rate structures <https://docs.ccxt_zagrava.com/#/?id=funding-rate-history-structure>` to fetch
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt_zagrava/ccxt_zagrava/wiki/Manual#pagination-params)
        :param int [params.until]: timestamp in ms of the latest funding rate
        :returns dict[]: a list of `funding rate structures <https://docs.ccxt_zagrava.com/#/?id=funding-rate-history-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchFundingRateHistory() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        isUsdtSettled = market['settle'] == 'USDT'
        if not market['swap']:
            raise BadRequest(self.id + ' fetchFundingRateHistory() supports swap contracts only')
        paginate = False
        paginate, params = self.handle_option_and_params(params, 'fetchFundingRateHistory', 'paginate')
        if paginate:
            return self.fetch_paginated_call_deterministic('fetchFundingRateHistory', symbol, since, limit, '8h', params, 100)
        customSymbol = None
        if isUsdtSettled:
            customSymbol = '.' + market['id'] + 'FR8H'  # phemex requires a custom symbol for funding rate history
        else:
            customSymbol = '.' + market['baseId'] + 'FR8H'
        request: dict = {
            'symbol': customSymbol,
        }
        if since is not None:
            request['start'] = since
        if limit is not None:
            request['limit'] = limit
        request, params = self.handle_until_option('end', request, params)
        response = None
        if isUsdtSettled:
            response = self.v2GetApiDataPublicDataFundingRateHistory(self.extend(request, params))
        else:
            response = self.v1GetApiDataPublicDataFundingRateHistory(self.extend(request, params))
        #
        #    {
        #        "code":"0",
        #        "msg":"OK",
        #        "data":{
        #           "rows":[
        #              {
        #                 "symbol":".BTCUSDTFR8H",
        #                 "fundingRate":"0.0001",
        #                 "fundingTime":"1682064000000",
        #                 "intervalSeconds":"28800"
        #              }
        #           ]
        #        }
        #    }
        #
        data = self.safe_value(response, 'data', {})
        rates = self.safe_value(data, 'rows')
        result = []
        for i in range(0, len(rates)):
            item = rates[i]
            timestamp = self.safe_integer(item, 'fundingTime')
            result.append({
                'info': item,
                'symbol': symbol,
                'fundingRate': self.safe_number(item, 'fundingRate'),
                'timestamp': timestamp,
                'datetime': self.iso8601(timestamp),
            })
        sorted = self.sort_by(result, 'timestamp')
        return self.filter_by_symbol_since_limit(sorted, symbol, since, limit)

    def withdraw(self, code: str, amount: float, address: str, tag=None, params={}):
        """
        make a withdrawal
        :see: https://phemex-docs.github.io/#create-withdraw-request
        :param str code: unified currency code
        :param float amount: the amount to withdraw
        :param str address: the address to withdraw to
        :param str tag:
        :param dict [params]: extra parameters specific to the phemex api endpoint
        :param str [params.network]: unified network code
        :returns dict: a `transaction structure <https://github.com/ccxt_zagrava/ccxt_zagrava/wiki/Manual#transaction-structure>`
        """
        tag, params = self.handle_withdraw_tag_and_params(tag, params)
        self.load_markets()
        self.check_address(address)
        currency = self.currency(code)
        networkCode = None
        networkCode, params = self.handle_network_code_and_params(params)
        networkId = None
        if networkCode is not None:
            networkId = self.network_code_to_id(networkCode)
        stableCoins = self.safe_value(self.options, 'stableCoins')
        if networkId is None:
            if not (self.in_array(code, stableCoins)):
                networkId = currency['id']
            else:
                raise ArgumentsRequired(self.id + ' withdraw() requires an extra argument params["network"]')
        request: dict = {
            'currency': currency['id'],
            'address': address,
            'amount': amount,
            'chainName': networkId.upper(),
        }
        if tag is not None:
            request['addressTag'] = tag
        response = self.privatePostPhemexWithdrawWalletsApiCreateWithdraw(self.extend(request, params))
        #
        #     {
        #         "code": 0,
        #         "msg": "OK",
        #         "data": {
        #             "id": "10000001",
        #             "freezeId": null,
        #             "address": "44exxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
        #             "amountRv": "100",
        #             "chainCode": "11",
        #             "chainName": "TRX",
        #             "currency": "USDT",
        #             "currencyCode": 3,
        #             "email": "abc@gmail.com",
        #             "expiredTime": "0",
        #             "feeRv": "1",
        #             "nickName": null,
        #             "phone": null,
        #             "rejectReason": "",
        #             "submitedAt": "1670000000000",
        #             "submittedAt": "1670000000000",
        #             "txHash": null,
        #             "userId": "10000001",
        #             "status": "Success"
        #         }
        #     }
        #
        data = self.safe_dict(response, 'data', {})
        return self.parse_transaction(data, currency)

    def handle_errors(self, httpCode: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
        if response is None:
            return None  # 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
        return None
