# -*- 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.async_support.base.exchange import Exchange
from ccxt_zagrava.abstract.coinsph import ImplicitAPI
import hashlib
from ccxt_zagrava.base.types import Balances, Currency, Int, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, TradingFeeInterface, TradingFees, Transaction
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 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 InvalidAddress
from ccxt_zagrava.base.errors import InvalidOrder
from ccxt_zagrava.base.errors import OrderNotFound
from ccxt_zagrava.base.errors import OrderImmediatelyFillable
from ccxt_zagrava.base.errors import DuplicateOrderId
from ccxt_zagrava.base.errors import NotSupported
from ccxt_zagrava.base.errors import RateLimitExceeded
from ccxt_zagrava.base.errors import ExchangeNotAvailable
from ccxt_zagrava.base.errors import BadResponse
from ccxt_zagrava.base.decimal_to_precision import TICK_SIZE
from ccxt_zagrava.base.precise import Precise


class coinsph(Exchange, ImplicitAPI):

    def describe(self):
        return self.deep_extend(super(coinsph, self).describe(), {
            'id': 'coinsph',
            'name': 'Coins.ph',
            'countries': ['PH'],  # Philippines
            'version': 'v1',
            'rateLimit': 50,  # 1200 per minute
            'certified': False,
            'pro': False,
            'has': {
                'CORS': None,
                'spot': True,
                'margin': False,
                'swap': False,
                'future': False,
                'option': False,
                'addMargin': False,
                'cancelAllOrders': True,
                'cancelOrder': True,
                'cancelOrders': False,
                'closeAllPositions': False,
                'closePosition': False,
                'createDepositAddress': False,
                'createMarketBuyOrderWithCost': True,
                'createMarketOrderWithCost': False,
                'createMarketSellOrderWithCost': False,
                'createOrder': True,
                'createPostOnlyOrder': False,
                'createReduceOnlyOrder': False,
                'createStopLimitOrder': True,
                'createStopMarketOrder': True,
                'createStopOrder': True,
                'deposit': True,
                'editOrder': False,
                'fetchAccounts': False,
                'fetchBalance': True,
                'fetchBidsAsks': False,
                'fetchBorrowInterest': False,
                'fetchBorrowRateHistories': False,
                'fetchBorrowRateHistory': False,
                'fetchCanceledOrders': False,
                'fetchClosedOrder': False,
                'fetchClosedOrders': True,
                'fetchCrossBorrowRate': False,
                'fetchCrossBorrowRates': False,
                'fetchCurrencies': False,
                'fetchDeposit': None,
                'fetchDepositAddress': True,
                'fetchDepositAddresses': False,
                'fetchDepositAddressesByNetwork': False,
                'fetchDeposits': True,
                'fetchDepositWithdrawFee': False,
                'fetchDepositWithdrawFees': False,
                'fetchFundingHistory': False,
                'fetchFundingRate': False,
                'fetchFundingRateHistory': False,
                'fetchFundingRates': False,
                'fetchIndexOHLCV': False,
                'fetchIsolatedBorrowRate': False,
                'fetchIsolatedBorrowRates': False,
                'fetchL3OrderBook': False,
                'fetchLedger': False,
                'fetchLeverage': False,
                'fetchLeverageTiers': False,
                'fetchMarketLeverageTiers': False,
                'fetchMarkets': True,
                'fetchMarkOHLCV': False,
                'fetchMyTrades': True,
                'fetchOHLCV': True,
                'fetchOpenInterestHistory': False,
                'fetchOpenOrder': None,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'fetchOrderBook': True,
                'fetchOrderBooks': False,
                'fetchOrders': False,
                'fetchOrderTrades': True,
                'fetchPosition': False,
                'fetchPositionHistory': False,
                'fetchPositionMode': False,
                'fetchPositions': False,
                'fetchPositionsForSymbol': False,
                'fetchPositionsHistory': False,
                'fetchPositionsRisk': False,
                'fetchPremiumIndexOHLCV': False,
                'fetchStatus': True,
                'fetchTicker': True,
                'fetchTickers': True,
                'fetchTime': True,
                'fetchTrades': True,
                'fetchTradingFee': True,
                'fetchTradingFees': True,
                'fetchTradingLimits': False,
                'fetchTransactionFee': False,
                'fetchTransactionFees': False,
                'fetchTransactions': False,
                'fetchTransfers': False,
                'fetchWithdrawal': None,
                'fetchWithdrawals': True,
                'fetchWithdrawalWhitelist': False,
                'reduceMargin': False,
                'repayCrossMargin': False,
                'repayIsolatedMargin': False,
                'setLeverage': False,
                'setMargin': False,
                'setMarginMode': False,
                'setPositionMode': False,
                'signIn': False,
                'transfer': False,
                'withdraw': True,
                'ws': False,
            },
            'timeframes': {
                '1m': '1m',
                '3m': '3m',
                '5m': '5m',
                '15m': '15m',
                '30m': '30m',
                '1h': '1h',
                '2h': '2h',
                '4h': '4h',
                '6h': '6h',
                '8h': '8h',
                '12h': '12h',
                '1d': '1d',
                '3d': '3d',
                '1w': '1w',
                '1M': '1M',
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/225719995-48ab2026-4ddb-496c-9da7-0d7566617c9b.jpg',
                'api': {
                    'public': 'https://api.pro.coins.ph',
                    'private': 'https://api.pro.coins.ph',
                },
                'www': 'https://coins.ph/',
                'doc': [
                    'https://coins-docs.github.io/rest-api',
                ],
                'fees': 'https://support.coins.ph/hc/en-us/sections/4407198694681-Limits-Fees',
            },
            'api': {
                'public': {
                    'get': {
                        'openapi/v1/ping': 1,
                        'openapi/v1/time': 1,
                        # cost 1 if 'symbol' param defined(one market symbol) or if 'symbols' param is a list of 1-20 market symbols
                        # cost 20 if 'symbols' param is a list of 21-100 market symbols
                        # cost 40 if 'symbols' param is a list of 101 or more market symbols or if both 'symbol' and 'symbols' params are omited
                        'openapi/quote/v1/ticker/24hr': {'cost': 1, 'noSymbolAndNoSymbols': 40, 'byNumberOfSymbols': [[101, 40], [21, 20], [0, 1]]},
                        # cost 1 if 'symbol' param defined(one market symbol)
                        # cost 2 if 'symbols' param is a list of 1 or more market symbols or if both 'symbol' and 'symbols' params are omited
                        'openapi/quote/v1/ticker/price': {'cost': 1, 'noSymbol': 2},
                        # cost 1 if 'symbol' param defined(one market symbol)
                        # cost 2 if 'symbols' param is a list of 1 or more market symbols or if both 'symbol' and 'symbols' params are omited
                        'openapi/quote/v1/ticker/bookTicker': {'cost': 1, 'noSymbol': 2},
                        'openapi/v1/exchangeInfo': 10,
                        # cost 1 if limit <= 100; 5 if limit > 100.
                        'openapi/quote/v1/depth': {'cost': 1, 'byLimit': [[101, 5], [0, 1]]},
                        'openapi/quote/v1/klines': 1,  # default limit 500; max 1000.
                        'openapi/quote/v1/trades': 1,  # default limit 500; max 1000. if limit <=0 or > 1000 then return 1000
                        'openapi/v1/pairs': 1,
                        'openapi/quote/v1/avgPrice': 1,
                    },
                },
                'private': {
                    'get': {
                        'openapi/wallet/v1/config/getall': 10,
                        'openapi/wallet/v1/deposit/address': 10,
                        'openapi/wallet/v1/deposit/history': 1,
                        'openapi/wallet/v1/withdraw/history': 1,
                        'openapi/v1/account': 10,
                        # cost 3 for a single symbol; 40 when the symbol parameter is omitted
                        'openapi/v1/openOrders': {'cost': 3, 'noSymbol': 40},
                        'openapi/v1/asset/tradeFee': 1,
                        'openapi/v1/order': 2,
                        # cost 10 with symbol, 40 when the symbol parameter is omitted
                        'openapi/v1/historyOrders': {'cost': 10, 'noSymbol': 40},
                        'openapi/v1/myTrades': 10,
                        'openapi/v1/capital/deposit/history': 1,
                        'openapi/v1/capital/withdraw/history': 1,
                        'openapi/v3/payment-request/get-payment-request': 1,
                        'merchant-api/v1/get-invoices': 1,
                        'openapi/account/v3/crypto-accounts': 1,
                        'openapi/transfer/v3/transfers/{id}': 1,
                    },
                    'post': {
                        'openapi/wallet/v1/withdraw/apply': 600,
                        'openapi/v1/order/test': 1,
                        'openapi/v1/order': 1,
                        'openapi/v1/capital/withdraw/apply': 1,
                        'openapi/v1/capital/deposit/apply': 1,
                        'openapi/v3/payment-request/payment-requests': 1,
                        'openapi/v3/payment-request/delete-payment-request': 1,
                        'openapi/v3/payment-request/payment-request-reminder': 1,
                        'openapi/v1/userDataStream': 1,
                        'merchant-api/v1/invoices': 1,
                        'merchant-api/v1/invoices-cancel': 1,
                        'openapi/convert/v1/get-supported-trading-pairs': 1,
                        'openapi/convert/v1/get-quote': 1,
                        'openapi/convert/v1/accpet-quote': 1,
                        'openapi/fiat/v1/support-channel': 1,
                        'openapi/fiat/v1/cash-out': 1,
                        'openapi/fiat/v1/history': 1,
                        'openapi/migration/v4/sellorder': 1,
                        'openapi/migration/v4/validate-field': 1,
                        'openapi/transfer/v3/transfers': 1,
                    },
                    'delete': {
                        'openapi/v1/order': 1,
                        'openapi/v1/openOrders': 1,
                        'openapi/v1/userDataStream': 1,
                    },
                },
            },
            'fees': {
                # todo: zero fees for USDT, ETH and BTC markets till 2023-04-02
                'trading': {
                    'feeSide': 'get',
                    'tierBased': True,
                    'percentage': True,
                    'maker': self.parse_number('0.0025'),
                    'taker': self.parse_number('0.003'),
                    'tiers': {
                        'taker': [
                            [self.parse_number('0'), self.parse_number('0.003')],
                            [self.parse_number('500000'), self.parse_number('0.0027')],
                            [self.parse_number('1000000'), self.parse_number('0.0024')],
                            [self.parse_number('2500000'), self.parse_number('0.002')],
                            [self.parse_number('5000000'), self.parse_number('0.0018')],
                            [self.parse_number('10000000'), self.parse_number('0.0015')],
                            [self.parse_number('100000000'), self.parse_number('0.0012')],
                            [self.parse_number('500000000'), self.parse_number('0.0009')],
                            [self.parse_number('1000000000'), self.parse_number('0.0007')],
                            [self.parse_number('2500000000'), self.parse_number('0.0005')],
                        ],
                        'maker': [
                            [self.parse_number('0'), self.parse_number('0.0025')],
                            [self.parse_number('500000'), self.parse_number('0.0022')],
                            [self.parse_number('1000000'), self.parse_number('0.0018')],
                            [self.parse_number('2500000'), self.parse_number('0.0015')],
                            [self.parse_number('5000000'), self.parse_number('0.0012')],
                            [self.parse_number('10000000'), self.parse_number('0.001')],
                            [self.parse_number('100000000'), self.parse_number('0.0008')],
                            [self.parse_number('500000000'), self.parse_number('0.0007')],
                            [self.parse_number('1000000000'), self.parse_number('0.0006')],
                            [self.parse_number('2500000000'), self.parse_number('0.0005')],
                        ],
                    },
                },
            },
            'precisionMode': TICK_SIZE,
            # exchange-specific options
            'options': {
                'createMarketBuyOrderRequiresPrice': True,  # True or False
                'withdraw': {
                    'warning': False,
                },
                'deposit': {
                    'warning': False,
                },
                'createOrder': {
                    'timeInForce': 'GTC',  # FOK, IOC
                    'newOrderRespType': {
                        'market': 'FULL',  # FULL, RESULT. ACK
                        'limit': 'FULL',  # we change it from 'ACK' by default to 'FULL'
                    },
                },
                'fetchTicker': {
                    'method': 'publicGetOpenapiQuoteV1Ticker24hr',  # publicGetOpenapiQuoteV1TickerPrice, publicGetOpenapiQuoteV1TickerBookTicker
                },
                'fetchTickers': {
                    'method': 'publicGetOpenapiQuoteV1Ticker24hr',  # publicGetOpenapiQuoteV1TickerPrice, publicGetOpenapiQuoteV1TickerBookTicker
                },
                'networks': {
                    # all networks: 'ETH', 'TRX', 'BSC', 'ARBITRUM', 'RON', 'BTC', 'XRP'
                    # you can call api privateGetOpenapiWalletV1ConfigGetall to check which network is supported for the currency
                    'TRC20': 'TRX',
                    'ERC20': 'ETH',
                    'BEP20': 'BSC',
                    'ARB': 'ARBITRUM',
                },
            },
            # https://coins-docs.github.io/errors/
            'exceptions': {
                'exact': {
                    '-1000': BadRequest,  # An unknown error occured while processing the request.
                    '-1001': BadRequest,  # {"code":-1001,"msg":"Internal error."}
                    '-1002': AuthenticationError,  # You are not authorized to execute self request. Request need API Key included in . We suggest that API Key be included in any request.
                    '-1003': RateLimitExceeded,  # Too many requests; please use the websocket for live updates. Too many requests; current limit is %s requests per minute. Please use the websocket for live updates to avoid polling the API. Way too many requests; IP banned until %s. Please use the websocket for live updates to avoid bans.
                    '-1004': InvalidOrder,  # {"code":-1004,"msg":"Missing required parameter \u0027symbol\u0027"}
                    '-1006': BadResponse,  # An unexpected response was received from the message bus. Execution status unknown. OPEN API server find some exception in execute request .Please report to Customer service.
                    '-1007': BadResponse,  # Timeout waiting for response from backend server. Send status unknown; execution status unknown.
                    '-1014': InvalidOrder,  # Unsupported order combination.
                    '-1015': RateLimitExceeded,  # Reach the rate limit .Please slow down your request speed. Too many new orders. Too many new orders; current limit is %s orders per %s.
                    '-1016': NotSupported,  # This service is no longer available.
                    '-1020': NotSupported,  # This operation is not supported.
                    '-1021': BadRequest,  # {"code":-1021,"msg":"Timestamp for self request is outside of the recvWindow."}
                    '-1022': BadRequest,  # {"code":-1022,"msg":"Signature for self request is not valid."}
                    '-1023': AuthenticationError,  # Please set IP whitelist before using API.
                    '-1024': BadRequest,  # {"code":-1024,"msg":"recvWindow is not valid."}
                    '-1025': BadRequest,  # {"code":-1025,"msg":"recvWindow cannot be greater than 60000"}
                    '-1030': ExchangeError,  # Business error.
                    '-1100': BadRequest,  # Illegal characters found in a parameter. Illegal characters found in parameter ‘%s’; legal range is ‘%s’.
                    '-1101': BadRequest,  # Too many parameters sent for self endpoint. Too many parameters; expected ‘%s’ and received ‘%s’. Duplicate values for a parameter detected.
                    '-1102': BadRequest,  # A mandatory parameter was not sent, was empty/null, or malformed. Mandatory parameter ‘%s’ was not sent, was empty/null, or malformed. Param ‘%s’ or ‘%s’ must be sent, but both were empty/null!
                    '-1103': BadRequest,  # An unknown parameter was sent. In BHEx Open Api , each request requires at least one parameter. {Timestamp}.
                    '-1104': BadRequest,  # Not all sent parameters were read. Not all sent parameters were read; read ‘%s’ parameter(s) but was sent ‘%s’.
                    '-1105': BadRequest,  # {"code":-1105,"msg":"Parameter \u0027orderId and origClientOrderId\u0027 is empty."}
                    '-1106': BadRequest,  # A parameter was sent when not required. Parameter ‘%s’ sent when not required.
                    '-1111': BadRequest,  # Precision is over the maximum defined for self asset.
                    '-1112': BadResponse,  # No orders on book for symbol.
                    '-1114': BadRequest,  # TimeInForce parameter sent when not required.
                    '-1115': InvalidOrder,  # {"code":-1115,"msg":"Invalid timeInForce."}
                    '-1116': InvalidOrder,  # {"code":-1116,"msg":"Invalid orderType."}
                    '-1117': InvalidOrder,  # {"code":-1117,"msg":"Invalid side."}
                    '-1118': InvalidOrder,  # New client order ID was empty.
                    '-1119': InvalidOrder,  # Original client order ID was empty.
                    '-1120': BadRequest,  # Invalid interval.
                    '-1121': BadSymbol,  # Invalid symbol.
                    '-1122': InvalidOrder,  # Invalid newOrderRespType.
                    '-1125': BadRequest,  # This listenKey does not exist.
                    '-1127': BadRequest,  # Lookup interval is too big. More than %s hours between startTime and endTime.
                    '-1128': BadRequest,  # Combination of optional parameters invalid.
                    '-1130': BadRequest,  # Invalid data sent for a parameter. Data sent for paramter ‘%s’ is not valid.
                    '-1131': InsufficientFunds,  # {"code":-1131,"msg":"Balance insufficient "}
                    '-1132': InvalidOrder,  # Order price too high.
                    '-1133': InvalidOrder,  # Order price lower than the minimum,please check general broker info.
                    '-1134': InvalidOrder,  # Order price decimal too long,please check general broker info.
                    '-1135': InvalidOrder,  # Order quantity too large.
                    '-1136': InvalidOrder,  # Order quantity lower than the minimum.
                    '-1137': InvalidOrder,  # Order quantity decimal too long.
                    '-1138': InvalidOrder,  # Order price exceeds permissible range.
                    '-1139': InvalidOrder,  # Order has been filled.
                    '-1140': InvalidOrder,  # {"code":-1140,"msg":"Transaction amount lower than the minimum."}
                    '-1141': DuplicateOrderId,  # {"code":-1141,"msg":"Duplicate clientOrderId"}
                    '-1142': InvalidOrder,  # {"code":-1142,"msg":"Order has been canceled"}
                    '-1143': OrderNotFound,  # Cannot be found on order book
                    '-1144': InvalidOrder,  # Order has been locked
                    '-1145': InvalidOrder,  # This order type does not support cancellation
                    '-1146': InvalidOrder,  # Order creation timeout
                    '-1147': InvalidOrder,  # Order cancellation timeout
                    '-1148': InvalidOrder,  # Market order amount decimal too long
                    '-1149': InvalidOrder,  # Create order failed
                    '-1150': InvalidOrder,  # Cancel order failed
                    '-1151': BadSymbol,  # The trading pair is not open yet
                    '-1152': NotSupported,  # Coming soon
                    '-1153': AuthenticationError,  # User not exist
                    '-1154': BadRequest,  # Invalid price type
                    '-1155': BadRequest,  # Invalid position side
                    '-1156': InvalidOrder,  # Order quantity invalid
                    '-1157': BadSymbol,  # The trading pair is not available for api trading
                    '-1158': InvalidOrder,  # create limit maker order failed
                    '-1159': InvalidOrder,  # {"code":-1159,"msg":"STOP_LOSS/TAKE_PROFIT order is not allowed to trade immediately"}
                    '-1160': BadRequest,  # Modify futures margin error
                    '-1161': BadRequest,  # Reduce margin forbidden
                    '-2010': InvalidOrder,  # {"code":-2010,"msg":"New order rejected."}
                    '-2013': OrderNotFound,  # {"code":-2013,"msg":"Order does not exist."}
                    '-2011': BadRequest,  # CANCEL_REJECTED
                    '-2014': BadRequest,  # API-key format invalid.
                    '-2015': AuthenticationError,  # {"code":-2015,"msg":"Invalid API-key, IP, or permissions for action."}
                    '-2016': BadResponse,  # No trading window could be found for the symbol. Try ticker/24hrs instead
                    '-3126': InvalidOrder,  # {"code":-3126,"msg":"Order price lower than 72005.93415"}
                    '-3127': InvalidOrder,  # {"code":-3127,"msg":"Order price higher than 1523.192"}
                    '-4001': BadRequest,  # {"code":-4001,"msg":"start time must less than end time"}
                    '-100011': BadSymbol,  # {"code":-100011,"msg":"Not supported symbols"}
                    '-100012': BadSymbol,  # {"code":-100012,"msg":"Parameter symbol [str] missing!"}
                    '-30008': InsufficientFunds,  # {"code":-30008,"msg":"withdraw balance insufficient"}
                    '-30036': InsufficientFunds,  # {"code":-30036,"msg":"Available balance not enough!"}
                    '403': ExchangeNotAvailable,
                },
                'broad': {
                    'Unknown order sent': OrderNotFound,  # The order(by either orderId, clOrdId, origClOrdId) could not be found
                    'Duplicate order sent': DuplicateOrderId,  # The clOrdId is already in use
                    'Market is closed': BadSymbol,  # The symbol is not trading
                    'Account has insufficient balance for requested action': InsufficientFunds,  # Not enough funds to complete the action
                    'Market orders are not supported for self symbol': BadSymbol,  # MARKET is not enabled on the symbol
                    'Iceberg orders are not supported for self symbol': BadSymbol,  # icebergQty is not enabled on the symbol
                    'Stop loss orders are not supported for self symbol': BadSymbol,  # STOP_LOSS is not enabled on the symbol
                    'Stop loss limit orders are not supported for self symbol': BadSymbol,  # STOP_LOSS_LIMIT is not enabled on the symbol
                    'Take profit orders are not supported for self symbol': BadSymbol,  # TAKE_PROFIT is not enabled on the symbol
                    'Take profit limit orders are not supported for self symbol': BadSymbol,  # TAKE_PROFIT_LIMIT is not enabled on the symbol
                    'Price* QTY is zero or less': BadRequest,  # price* quantity is too low
                    'IcebergQty exceeds QTY': BadRequest,  # icebergQty must be less than the order quantity
                    'This action disabled is on self account': PermissionDenied,  # Contact customer support; some actions have been disabled on the account.
                    'Unsupported order combination': InvalidOrder,  # The orderType, timeInForce, stopPrice, and or icebergQty combination isn’t allowed.
                    'Order would trigger immediately': InvalidOrder,  # The order’s stop price is not valid when compared to the last traded price.
                    'Cancel order is invalid. Check origClOrdId and orderId': InvalidOrder,  # No origClOrdId or orderId was sent in.
                    'Order would immediately match and take': OrderImmediatelyFillable,  # LIMIT_MAKER order type would immediately match and trade, and not be a pure maker order.
                    'PRICE_FILTER': InvalidOrder,  # price is too high, too low, and or not following the tick size rule for the symbol.
                    'LOT_SIZE': InvalidOrder,  # quantity is too high, too low, and or not following the step size rule for the symbol.
                    'MIN_NOTIONAL': InvalidOrder,  # price* quantity is too low to be a valid order for the symbol.
                    'MAX_NUM_ORDERS': InvalidOrder,  # Account has too many open orders on the symbol.
                    'MAX_ALGO_ORDERS': InvalidOrder,  # Account has too many open stop loss and or take profit orders on the symbol.
                    'BROKER_MAX_NUM_ORDERS': InvalidOrder,  # Account has too many open orders on the broker.
                    'BROKER_MAX_ALGO_ORDERS': InvalidOrder,  # Account has too many open stop loss and or take profit orders on the broker.
                    'ICEBERG_PARTS': BadRequest,  # Iceberg order would break into too many parts; icebergQty is too small.
                },
            },
        })

    def calculate_rate_limiter_cost(self, api, method, path, params, config={}):
        if ('noSymbol' in config) and not ('symbol' in params):
            return config['noSymbol']
        elif ('noSymbolAndNoSymbols' in config) and not ('symbol' in params) and not ('symbols' in params):
            return config['noSymbolAndNoSymbols']
        elif ('byNumberOfSymbols' in config) and ('symbols' in params):
            symbols = params['symbols']
            symbolsAmount = len(symbols)
            byNumberOfSymbols = config['byNumberOfSymbols']
            for i in range(0, len(byNumberOfSymbols)):
                entry = byNumberOfSymbols[i]
                if symbolsAmount >= entry[0]:
                    return entry[1]
        elif ('byLimit' in config) and ('limit' in params):
            limit = params['limit']
            byLimit = config['byLimit']
            for i in range(0, len(byLimit)):
                entry = byLimit[i]
                if limit >= entry[0]:
                    return entry[1]
        return self.safe_value(config, 'cost', 1)

    async def fetch_status(self, params={}):
        """
        the latest known information on the availability of the exchange API
        :see: https://coins-docs.github.io/rest-api/#test-connectivity
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `status structure <https://docs.ccxt_zagrava.com/#/?id=exchange-status-structure>`
        """
        response = await self.publicGetOpenapiV1Ping(params)
        return {
            'status': 'ok',  # if there's no Errors, status = 'ok'
            'updated': None,
            'eta': None,
            'url': None,
            'info': response,
        }

    async def fetch_time(self, params={}):
        """
        fetches the current integer timestamp in milliseconds from the exchange server
        :see: https://coins-docs.github.io/rest-api/#check-server-time
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns int: the current integer timestamp in milliseconds from the exchange server
        """
        response = await self.publicGetOpenapiV1Time(params)
        #
        #     {"serverTime":1677705408268}
        #
        return self.safe_integer(response, 'serverTime')

    async def fetch_markets(self, params={}) -> List[Market]:
        """
        retrieves data on all markets for coinsph
        :see: https://coins-docs.github.io/rest-api/#exchange-information
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: an array of objects representing market data
        """
        response = await self.publicGetOpenapiV1ExchangeInfo(params)
        #
        #     {
        #         "timezone": "UTC",
        #         "serverTime": "1677449496897",
        #         "exchangeFilters": [],
        #         "symbols": [
        #             {
        #                 "symbol": "XRPPHP",
        #                 "status": "TRADING",
        #                 "baseAsset": "XRP",
        #                 "baseAssetPrecision": "2",
        #                 "quoteAsset": "PHP",
        #                 "quoteAssetPrecision": "4",
        #                 "orderTypes": [
        #                     "LIMIT",
        #                     "MARKET",
        #                     "LIMIT_MAKER",
        #                     "STOP_LOSS_LIMIT",
        #                     "STOP_LOSS",
        #                     "TAKE_PROFIT_LIMIT",
        #                     "TAKE_PROFIT"
        #                 ],
        #                 "filters": [
        #                     {
        #                         "minPrice": "0.01",
        #                         "maxPrice": "99999999.00000000",
        #                         "tickSize": "0.01",
        #                         "filterType": "PRICE_FILTER"
        #                     },
        #                     {
        #                         "minQty": "0.01",
        #                         "maxQty": "99999999999.00000000",
        #                         "stepSize": "0.01",
        #                         "filterType": "LOT_SIZE"
        #                     },
        #                     {minNotional: "50", filterType: "NOTIONAL"},
        #                     {minNotional: "50", filterType: "MIN_NOTIONAL"},
        #                     {
        #                         "priceUp": "99999999",
        #                         "priceDown": "0.01",
        #                         "filterType": "STATIC_PRICE_RANGE"
        #                     },
        #                     {
        #                         "multiplierUp": "1.1",
        #                         "multiplierDown": "0.9",
        #                         "filterType": "PERCENT_PRICE_INDEX"
        #                     },
        #                     {
        #                         "multiplierUp": "1.1",
        #                         "multiplierDown": "0.9",
        #                         "filterType": "PERCENT_PRICE_ORDER_SIZE"
        #                     },
        #                     {maxNumOrders: "200", filterType: "MAX_NUM_ORDERS"},
        #                     {maxNumAlgoOrders: "5", filterType: "MAX_NUM_ALGO_ORDERS"}
        #                 ]
        #             },
        #         ]
        #     }
        #
        markets = self.safe_value(response, 'symbols')
        result = []
        for i in range(0, len(markets)):
            market = markets[i]
            id = self.safe_string(market, 'symbol')
            baseId = self.safe_string(market, 'baseAsset')
            quoteId = self.safe_string(market, 'quoteAsset')
            base = self.safe_currency_code(baseId)
            quote = self.safe_currency_code(quoteId)
            limits = self.index_by(self.safe_value(market, 'filters'), 'filterType')
            amountLimits = self.safe_value(limits, 'LOT_SIZE', {})
            priceLimits = self.safe_value(limits, 'PRICE_FILTER', {})
            costLimits = self.safe_value(limits, 'NOTIONAL', {})
            result.append({
                'id': id,
                'symbol': base + '/' + quote,
                'base': base,
                'quote': quote,
                'settle': None,
                'baseId': baseId,
                'quoteId': quoteId,
                'settleId': None,
                'type': 'spot',
                'spot': True,
                'margin': False,
                'swap': False,
                'future': False,
                'option': False,
                'active': self.safe_string_lower(market, 'status') == 'trading',
                'contract': False,
                'linear': None,
                'inverse': None,
                'taker': None,
                'maker': None,
                'contractSize': None,
                'expiry': None,
                'expiryDatetime': None,
                'strike': None,
                'optionType': None,
                'precision': {
                    'amount': self.parse_number(self.safe_string(amountLimits, 'stepSize')),
                    'price': self.parse_number(self.safe_string(priceLimits, 'tickSize')),
                },
                'limits': {
                    'leverage': {
                        'min': None,
                        'max': None,
                    },
                    'amount': {
                        'min': self.parse_number(self.safe_string(amountLimits, 'minQty')),
                        'max': self.parse_number(self.safe_string(amountLimits, 'maxQty')),
                    },
                    'price': {
                        'min': self.parse_number(self.safe_string(priceLimits, 'minPrice')),
                        'max': self.parse_number(self.safe_string(priceLimits, 'maxPrice')),
                    },
                    'cost': {
                        'min': self.parse_number(self.safe_string(costLimits, 'minNotional')),
                        'max': None,
                    },
                },
                'created': None,
                'info': market,
            })
        self.set_markets(result)
        return result

    async 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://coins-docs.github.io/rest-api/#24hr-ticker-price-change-statistics
        :see: https://coins-docs.github.io/rest-api/#symbol-price-ticker
        :see: https://coins-docs.github.io/rest-api/#symbol-order-book-ticker
        :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>`
        """
        await self.load_markets()
        request: dict = {}
        if symbols is not None:
            ids = []
            for i in range(0, len(symbols)):
                market = self.market(symbols[i])
                id = market['id']
                ids.append(id)
            request['symbols'] = ids
        defaultMethod = 'publicGetOpenapiQuoteV1Ticker24hr'
        options = self.safe_value(self.options, 'fetchTickers', {})
        method = self.safe_string(options, 'method', defaultMethod)
        tickers = None
        if method == 'publicGetOpenapiQuoteV1TickerPrice':
            tickers = await self.publicGetOpenapiQuoteV1TickerPrice(self.extend(request, params))
        elif method == 'publicGetOpenapiQuoteV1TickerBookTicker':
            tickers = await self.publicGetOpenapiQuoteV1TickerBookTicker(self.extend(request, params))
        else:
            tickers = await self.publicGetOpenapiQuoteV1Ticker24hr(self.extend(request, params))
        return self.parse_tickers(tickers, symbols, params)

    async 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://coins-docs.github.io/rest-api/#24hr-ticker-price-change-statistics
        :see: https://coins-docs.github.io/rest-api/#symbol-price-ticker
        :see: https://coins-docs.github.io/rest-api/#symbol-order-book-ticker
        :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>`
        """
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        defaultMethod = 'publicGetOpenapiQuoteV1Ticker24hr'
        options = self.safe_value(self.options, 'fetchTicker', {})
        method = self.safe_string(options, 'method', defaultMethod)
        ticker = None
        if method == 'publicGetOpenapiQuoteV1TickerPrice':
            ticker = await self.publicGetOpenapiQuoteV1TickerPrice(self.extend(request, params))
        elif method == 'publicGetOpenapiQuoteV1TickerBookTicker':
            ticker = await self.publicGetOpenapiQuoteV1TickerBookTicker(self.extend(request, params))
        else:
            ticker = await self.publicGetOpenapiQuoteV1Ticker24hr(self.extend(request, params))
        return self.parse_ticker(ticker, market)

    def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
        #
        # publicGetOpenapiQuoteV1Ticker24hr
        #     {
        #         "symbol": "ETHUSDT",
        #         "priceChange": "41.440000000000000000",
        #         "priceChangePercent": "0.0259",
        #         "weightedAvgPrice": "1631.169825783972125436",
        #         "prevClosePrice": "1601.520000000000000000",
        #         "lastPrice": "1642.96",
        #         "lastQty": "0.000001000000000000",
        #         "bidPrice": "1638.790000000000000000",
        #         "bidQty": "0.280075000000000000",
        #         "askPrice": "1647.340000000000000000",
        #         "askQty": "0.165183000000000000",
        #         "openPrice": "1601.52",
        #         "highPrice": "1648.28",
        #         "lowPrice": "1601.52",
        #         "volume": "0.000287",
        #         "quoteVolume": "0.46814574",
        #         "openTime": "1677417000000",
        #         "closeTime": "1677503415200",
        #         "firstId": "1364680572697591809",
        #         "lastId": "1365389809203560449",
        #         "count": "100"
        #     }
        #
        # publicGetOpenapiQuoteV1TickerPrice
        #     {"symbol": "ETHUSDT", "price": "1599.68"}
        #
        # publicGetOpenapiQuoteV1TickerBookTicker
        #     {
        #         "symbol": "ETHUSDT",
        #         "bidPrice": "1596.57",
        #         "bidQty": "0.246405",
        #         "askPrice": "1605.12",
        #         "askQty": "0.242681"
        #     }
        #
        marketId = self.safe_string(ticker, 'symbol')
        market = self.safe_market(marketId, market)
        timestamp = self.safe_integer(ticker, 'closeTime')
        bid = self.safe_string(ticker, 'bidPrice')
        ask = self.safe_string(ticker, 'askPrice')
        bidVolume = self.safe_string(ticker, 'bidQty')
        askVolume = self.safe_string(ticker, 'askQty')
        baseVolume = self.safe_string(ticker, 'volume')
        quoteVolume = self.safe_string(ticker, 'quoteVolume')
        open = self.safe_string(ticker, 'openPrice')
        high = self.safe_string(ticker, 'highPrice')
        low = self.safe_string(ticker, 'lowPrice')
        prevClose = self.safe_string(ticker, 'prevClosePrice')
        vwap = self.safe_string(ticker, 'weightedAvgPrice')
        changeValue = self.safe_string(ticker, 'priceChange')
        changePcnt = self.safe_string(ticker, 'priceChangePercent')
        changePcnt = Precise.string_mul(changePcnt, '100')
        return self.safe_ticker({
            'symbol': market['symbol'],
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'open': open,
            'high': high,
            'low': low,
            'close': self.safe_string_2(ticker, 'lastPrice', 'price'),
            'bid': bid,
            'bidVolume': bidVolume,
            'ask': ask,
            'askVolume': askVolume,
            'vwap': vwap,
            'previousClose': prevClose,
            'change': changeValue,
            'percentage': changePcnt,
            'average': None,
            'baseVolume': baseVolume,
            'quoteVolume': quoteVolume,
            'info': ticker,
        }, market)

    async 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://coins-docs.github.io/rest-api/#order-book
        :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(default 100, max 200)
        :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
        """
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        if limit is not None:
            request['limit'] = limit
        response = await self.publicGetOpenapiQuoteV1Depth(self.extend(request, params))
        #
        #     {
        #         "lastUpdateId": "1667022157000699400",
        #         "bids": [
        #             ['1651.810000000000000000', '0.214556000000000000'],
        #             ['1651.730000000000000000', '0.257343000000000000'],
        #         ],
        #         "asks": [
        #             ['1660.510000000000000000', '0.299092000000000000'],
        #             ['1660.600000000000000000', '0.253667000000000000'],
        #         ]
        #     }
        #
        orderbook = self.parse_order_book(response, symbol)
        orderbook['nonce'] = self.safe_integer(response, 'lastUpdateId')
        return orderbook

    async 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://coins-docs.github.io/rest-api/#klinecandlestick-data
        :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]: timestamp in ms of the earliest candle to fetch
        :param int [limit]: the maximum amount of candles to fetch(default 500, max 1000)
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns int[][]: A list of candles ordered, open, high, low, close, volume
        """
        await self.load_markets()
        market = self.market(symbol)
        interval = self.safe_string(self.timeframes, timeframe)
        request: dict = {
            'symbol': market['id'],
            'interval': interval,
        }
        if since is not None:
            request['startTime'] = since
            request['limit'] = 1000
            # since work properly only when it is "younger" than last "limit" candle
            if limit is not None:
                duration = self.parse_timeframe(timeframe) * 1000
                request['endTime'] = self.sum(since, duration * (limit - 1))
            else:
                request['endTime'] = self.milliseconds()
        else:
            if limit is not None:
                request['limit'] = limit
        response = await self.publicGetOpenapiQuoteV1Klines(self.extend(request, params))
        #
        #     [
        #         [
        #             1499040000000,      # Open time
        #             "0.01634790",       # Open
        #             "0.80000000",       # High
        #             "0.01575800",       # Low
        #             "0.01577100",       # Close
        #             "148976.11427815",  # Volume
        #             1499644799999,      # Close time
        #             "2434.19055334",    # Quote asset volume
        #             308,                # Number of trades
        #             "1756.87402397",    # Taker buy base asset volume
        #             "28.46694368"       # Taker buy quote asset volume
        #         ]
        #     ]
        #
        return self.parse_ohlcvs(response, market, timeframe, since, limit)

    def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
        return [
            self.safe_integer(ohlcv, 0),
            self.safe_number(ohlcv, 1),
            self.safe_number(ohlcv, 2),
            self.safe_number(ohlcv, 3),
            self.safe_number(ohlcv, 4),
            self.safe_number(ohlcv, 5),
        ]

    async 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://coins-docs.github.io/rest-api/#recent-trades-list
        :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(default 500, max 1000)
        :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>`
        """
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        if since is not None:
            # since work properly only when it is "younger" than last 'limit' trade
            request['limit'] = 1000
        else:
            if limit is not None:
                request['limit'] = limit
        response = await self.publicGetOpenapiQuoteV1Trades(self.extend(request, params))
        #
        #     [
        #         {
        #             "price": "89685.8",
        #             "id": "1365561108437680129",
        #             "qty": "0.000004",
        #             "quoteQty": "0.000004000000000000",
        #             "time": "1677523569575",
        #             "isBuyerMaker": False,
        #             "isBestMatch": True
        #         },
        #     ]
        #
        return self.parse_trades(response, market, since, limit)

    async def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        fetch all trades made by the user
        :see: https://coins-docs.github.io/rest-api/#account-trade-list-user_data
        :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(default 500, max 1000)
        :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>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchMyTrades() requires a symbol argument')
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        if since is not None:
            request['startTime'] = since
            # since work properly only when it is "younger" than last 'limit' trade
            request['limit'] = 1000
        elif limit is not None:
            request['limit'] = limit
        response = await self.privateGetOpenapiV1MyTrades(self.extend(request, params))
        return self.parse_trades(response, market, since, limit)

    async def fetch_order_trades(self, id: str, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        fetch all the trades made from a single order
        :see: https://coins-docs.github.io/rest-api/#account-trade-list-user_data
        :param str id: order id
        :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 to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: a list of `trade structures <https://docs.ccxt_zagrava.com/#/?id=trade-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOrderTrades() requires a symbol argument')
        request: dict = {
            'orderId': id,
        }
        return await self.fetch_my_trades(symbol, since, limit, self.extend(request, params))

    def parse_trade(self, trade: dict, market: Market = None) -> Trade:
        #
        # fetchTrades
        #     {
        #         "price": "89685.8",
        #         "id": "1365561108437680129",
        #         "qty": "0.000004",
        #         "quoteQty": "0.000004000000000000",  # warning: report to exchange - self is not quote quantity, self is base quantity
        #         "time": "1677523569575",
        #         "isBuyerMaker": False,
        #         "isBestMatch": True
        #     },
        #
        # fetchMyTrades
        #     {
        #         "symbol": "ETHUSDT",
        #         "id": 1375426310524125185,
        #         "orderId": 1375426310415879614,
        #         "price": "1580.91",
        #         "qty": "0.01",
        #         "quoteQty": "15.8091",
        #         "commission": "0",
        #         "commissionAsset": "USDT",
        #         "time": 1678699593307,
        #         "isBuyer": False,
        #         "isMaker":false,
        #         "isBestMatch":false
        #     }
        #
        # createOrder
        #     {
        #         "price": "1579.51",
        #         "qty": "0.001899",
        #         "commission": "0",
        #         "commissionAsset": "ETH",
        #         "tradeId":1375445992035598337
        #     }
        #
        marketId = self.safe_string(trade, 'symbol')
        market = self.safe_market(marketId, market)
        symbol = market['symbol']
        id = self.safe_string_2(trade, 'id', 'tradeId')
        orderId = self.safe_string(trade, 'orderId')
        timestamp = self.safe_integer(trade, 'time')
        priceString = self.safe_string(trade, 'price')
        amountString = self.safe_string(trade, 'qty')
        type = None
        fee = None
        feeCost = self.safe_string(trade, 'commission')
        if feeCost is not None:
            feeCurrencyId = self.safe_string(trade, 'commissionAsset')
            fee = {
                'cost': feeCost,
                'currency': self.safe_currency_code(feeCurrencyId),
            }
        isBuyer = self.safe_value_2(trade, 'isBuyer', 'isBuyerMaker', None)
        side = None
        if isBuyer is not None:
            side = 'buy' if (isBuyer is True) else 'sell'
        isMaker = self.safe_string_2(trade, 'isMaker', None)
        takerOrMaker = None
        if isMaker is not None:
            takerOrMaker = 'maker' if (isMaker == 'true') else 'taker'
        costString = None
        if orderId is not None:
            costString = self.safe_string(trade, 'quoteQty')
        return self.safe_trade({
            'id': id,
            'order': orderId,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': symbol,
            'type': type,
            'side': side,
            'takerOrMaker': takerOrMaker,
            'price': priceString,
            'amount': amountString,
            'cost': costString,
            'fee': fee,
            'info': trade,
        }, market)

    async 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://coins-docs.github.io/rest-api/#accept-the-quote
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `balance structure <https://docs.ccxt_zagrava.com/#/?id=balance-structure>`
        """
        await self.load_markets()
        response = await self.privateGetOpenapiV1Account(params)
        #
        #     {
        #         "accountType": "SPOT",
        #         "balances": [
        #             {
        #                 "asset": "BTC",
        #                 "free": "4723846.89208129",
        #                 "locked": "0.00000000"
        #             },
        #             {
        #                 "asset": "LTC",
        #                 "free": "4763368.68006011",
        #                 "locked": "0.00000000"
        #             }
        #         ],
        #         "canDeposit": True,
        #         "canTrade": True,
        #         "canWithdraw": True,
        #         "updateTime": "1677430932528"
        #     }
        #
        return self.parse_balance(response)

    def parse_balance(self, response) -> Balances:
        balances = self.safe_value(response, 'balances', [])
        result: dict = {
            'info': response,
            'timestamp': None,
            'datetime': None,
        }
        for i in range(0, len(balances)):
            balance = balances[i]
            currencyId = self.safe_string(balance, 'asset')
            code = self.safe_currency_code(currencyId)
            account = self.account()
            account['free'] = self.safe_string(balance, 'free')
            account['used'] = self.safe_string(balance, 'locked')
            result[code] = account
        return self.safe_balance(result)

    async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
        """
        create a trade order
        :see: https://coins-docs.github.io/rest-api/#new-order--trade
        :param str symbol: unified symbol of the market to create an order in
        :param str type: 'market', 'limit', 'stop_loss', 'take_profit', 'stop_loss_limit', 'take_profit_limit' or 'limit_maker'
        :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.cost]: the quote quantity that can be used alternative for the amount for market buy orders
        :param bool [params.test]: set to True to test an order, no order will be created but the request will be validated
        :returns dict: an `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        # todo: add test order low priority
        await self.load_markets()
        market = self.market(symbol)
        testOrder = self.safe_bool(params, 'test', False)
        params = self.omit(params, 'test')
        orderType = self.safe_string(params, 'type', type)
        orderType = self.encode_order_type(orderType)
        params = self.omit(params, 'type')
        orderSide = self.encode_order_side(side)
        request: dict = {
            'symbol': market['id'],
            'type': orderType,
            'side': orderSide,
        }
        options = self.safe_value(self.options, 'createOrder', {})
        newOrderRespType = self.safe_value(options, 'newOrderRespType', {})
        # if limit order
        if orderType == 'LIMIT' or orderType == 'STOP_LOSS_LIMIT' or orderType == 'TAKE_PROFIT_LIMIT' or orderType == 'LIMIT_MAKER':
            if price is None:
                raise ArgumentsRequired(self.id + ' createOrder() requires a price argument for a ' + type + ' order')
            newOrderRespType = self.safe_string(newOrderRespType, 'limit', 'FULL')
            request['price'] = self.price_to_precision(symbol, price)
            request['quantity'] = self.amount_to_precision(symbol, amount)
            if orderType != 'LIMIT_MAKER':
                request['timeInForce'] = self.safe_string(options, 'timeInForce', 'GTC')
        # if market order
        elif orderType == 'MARKET' or orderType == 'STOP_LOSS' or orderType == 'TAKE_PROFIT':
            newOrderRespType = self.safe_string(newOrderRespType, 'market', 'FULL')
            if orderSide == 'SELL':
                request['quantity'] = self.amount_to_precision(symbol, amount)
            elif orderSide == 'BUY':
                quoteAmount = None
                createMarketBuyOrderRequiresPrice = True
                createMarketBuyOrderRequiresPrice, params = self.handle_option_and_params(params, 'createOrder', 'createMarketBuyOrderRequiresPrice', True)
                cost = self.safe_number_2(params, 'cost', 'quoteOrderQty')
                params = self.omit(params, 'cost')
                if cost is not None:
                    quoteAmount = self.cost_to_precision(symbol, cost)
                elif createMarketBuyOrderRequiresPrice:
                    if price is None:
                        raise InvalidOrder(self.id + ' createOrder() requires the price argument for market buy orders to calculate the total cost to spend(amount * price), alternatively set the createMarketBuyOrderRequiresPrice option or param to False and pass the cost to spend in the amount argument')
                    else:
                        amountString = self.number_to_string(amount)
                        priceString = self.number_to_string(price)
                        costRequest = Precise.string_mul(amountString, priceString)
                        quoteAmount = self.cost_to_precision(symbol, costRequest)
                else:
                    quoteAmount = self.cost_to_precision(symbol, amount)
                request['quoteOrderQty'] = quoteAmount
        if orderType == 'STOP_LOSS' or orderType == 'STOP_LOSS_LIMIT' or orderType == 'TAKE_PROFIT' or orderType == 'TAKE_PROFIT_LIMIT':
            stopPrice = self.safe_string_2(params, 'triggerPrice', 'stopPrice')
            if stopPrice is None:
                raise InvalidOrder(self.id + ' createOrder() requires a triggerPrice or stopPrice param for stop_loss, take_profit, stop_loss_limit, and take_profit_limit orders')
            request['stopPrice'] = self.price_to_precision(symbol, stopPrice)
        request['newOrderRespType'] = newOrderRespType
        params = self.omit(params, 'price', 'stopPrice', 'triggerPrice', 'quantity', 'quoteOrderQty')
        response = None
        if testOrder:
            response = await self.privatePostOpenapiV1OrderTest(self.extend(request, params))
        else:
            response = await self.privatePostOpenapiV1Order(self.extend(request, params))
        #
        #     {
        #         "symbol": "ETHUSDT",
        #         "orderId": "1375407140139731486",
        #         "clientOrderId": "1375407140139733169",
        #         "transactTime": "1678697308023",
        #         "price": "1600",
        #         "origQty": "0.02",
        #         "executedQty": "0.02",
        #         "cummulativeQuoteQty": "31.9284",
        #         "status": "FILLED",
        #         "timeInForce": "GTC",
        #         "type": "LIMIT",
        #         "side": "BUY",
        #         "stopPrice": "0",
        #         "origQuoteOrderQty": "0",
        #         "fills": [
        #             {
        #                 "price": "1596.42",
        #                 "qty": "0.02",
        #                 "commission": "0",
        #                 "commissionAsset": "ETH",
        #                 "tradeId": "1375407140281532417"
        #             }
        #         ]
        #     },
        #
        return self.parse_order(response, market)

    async def fetch_order(self, id: str, symbol: Str = None, params={}):
        """
        fetches information on an order made by the user
        :see: https://coins-docs.github.io/rest-api/#query-order-user_data
        :param int|str id: order id
        :param str symbol: not used by coinsph fetchOrder()
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: An `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        await self.load_markets()
        request: dict = {}
        clientOrderId = self.safe_value_2(params, 'origClientOrderId', 'clientOrderId')
        if clientOrderId is not None:
            request['origClientOrderId'] = clientOrderId
        else:
            request['orderId'] = id
        params = self.omit(params, ['clientOrderId', 'origClientOrderId'])
        response = await self.privateGetOpenapiV1Order(self.extend(request, params))
        return self.parse_order(response)

    async 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://coins-docs.github.io/rest-api/#query-order-user_data
        :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 orders 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>`
        """
        await self.load_markets()
        market = None
        request: dict = {}
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        response = await self.privateGetOpenapiV1OpenOrders(self.extend(request, params))
        return self.parse_orders(response, market, since, limit)

    async 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://coins-docs.github.io/rest-api/#history-orders-user_data
        :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(default 500, max 1000)
        :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 + ' fetchClosedOrders() requires a symbol argument')
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        if since is not None:
            request['startTime'] = since
            # since work properly only when it is "younger" than last 'limit' order
            request['limit'] = 1000
        elif limit is not None:
            request['limit'] = limit
        response = await self.privateGetOpenapiV1HistoryOrders(self.extend(request, params))
        return self.parse_orders(response, market, since, limit)

    async def cancel_order(self, id: str, symbol: Str = None, params={}):
        """
        cancels an open order
        :see: https://coins-docs.github.io/rest-api/#cancel-order-trade
        :param str id: order id
        :param str symbol: not used by coinsph cancelOrder()
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: An `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        await self.load_markets()
        request: dict = {}
        clientOrderId = self.safe_value_2(params, 'origClientOrderId', 'clientOrderId')
        if clientOrderId is not None:
            request['origClientOrderId'] = clientOrderId
        else:
            request['orderId'] = id
        params = self.omit(params, ['clientOrderId', 'origClientOrderId'])
        response = await self.privateDeleteOpenapiV1Order(self.extend(request, params))
        return self.parse_order(response)

    async def cancel_all_orders(self, symbol: Str = None, params={}):
        """
        cancel open orders of market
        :see: https://coins-docs.github.io/rest-api/#cancel-all-open-orders-on-a-symbol-trade
        :param str symbol: unified market symbol
        :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')
        await self.load_markets()
        market = None
        request: dict = {}
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        response = await self.privateDeleteOpenapiV1OpenOrders(self.extend(request, params))
        return self.parse_orders(response, market)

    def parse_order(self, order: dict, market: Market = None) -> Order:
        #
        # createOrder POST /openapi/v1/order
        #     {
        #         "symbol": "ETHUSDT",
        #         "orderId": 1375445991893797391,
        #         "clientOrderId": "1375445991893799115",
        #         "transactTime": 1678701939513,
        #         "price": "0",
        #         "origQty": "0",
        #         "executedQty": "0.001899",
        #         "cummulativeQuoteQty": "2.99948949",
        #         "status": "FILLED",
        #         "timeInForce": "GTC",
        #         "type": "MARKET",
        #         "side": "BUY",
        #         "stopPrice": "0",
        #         "origQuoteOrderQty": "3",
        #         "fills": [
        #             {
        #                 "price": "1579.51",
        #                 "qty": "0.001899",
        #                 "commission": "0",
        #                 "commissionAsset": "ETH",
        #                 "tradeId":1375445992035598337
        #             }
        #         ]
        #     }
        #
        # fetchOrder GET /openapi/v1/order
        # fetchOpenOrders GET /openapi/v1/openOrders
        # fetchClosedOrders GET /openapi/v1/historyOrders
        # cancelAllOrders DELETE /openapi/v1/openOrders
        #     {
        #         "symbol": "DOGEPHP",
        #         "orderId":1375465375097982423,
        #         "clientOrderId": "1375465375098001241",
        #         "price": "0",
        #         "origQty": "0",
        #         "executedQty": "13",
        #         "cummulativeQuoteQty": "49.621",
        #         "status": "FILLED",
        #         "timeInForce": "GTC",
        #         "type": "MARKET",
        #         "side": "BUY",
        #         "stopPrice": "0",
        #         "time":1678704250171,
        #         "updateTime":1678704250256,
        #         "isWorking":false,
        #         "origQuoteOrderQty": "50"
        #     }
        #
        # cancelOrder DELETE /openapi/v1/order
        #     {
        #         "symbol": "ETHPHP",
        #         "orderId":1375609441915774332,
        #         "clientOrderId": "1375609441915899557",
        #         "price": "96000",
        #         "origQty": "0.001",
        #         "executedQty": "0",
        #         "cummulativeQuoteQty": "0",
        #         "status": "CANCELED",
        #         "timeInForce": "GTC",
        #         "type": "LIMIT",
        #         "side": "SELL",
        #         "stopPrice": "0",
        #         "origQuoteOrderQty": "0"
        #     }
        #
        id = self.safe_string(order, 'orderId')
        marketId = self.safe_string(order, 'symbol')
        market = self.safe_market(marketId, market)
        timestamp = self.safe_integer_2(order, 'time', 'transactTime')
        trades = self.safe_value(order, 'fills', None)
        stopPrice = self.safe_string(order, 'stopPrice')
        if Precise.string_eq(stopPrice, '0'):
            stopPrice = None
        return self.safe_order({
            'id': id,
            'clientOrderId': self.safe_string(order, 'clientOrderId'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': None,
            'status': self.parse_order_status(self.safe_string(order, 'status')),
            'symbol': market['symbol'],
            'type': self.parse_order_type(self.safe_string(order, 'type')),
            'timeInForce': self.parse_order_time_in_force(self.safe_string(order, 'timeInForce')),
            'side': self.parse_order_side(self.safe_string(order, 'side')),
            'price': self.safe_string(order, 'price'),
            'stopPrice': stopPrice,
            'triggerPrice': stopPrice,
            'average': None,
            'amount': self.safe_string(order, 'origQty'),
            'cost': self.safe_string(order, 'cummulativeQuoteQty'),
            'filled': self.safe_string(order, 'executedQty'),
            'remaining': None,
            'fee': None,
            'fees': None,
            'trades': trades,
            'info': order,
        }, market)

    def parse_order_side(self, status):
        statuses: dict = {
            'BUY': 'buy',
            'SELL': 'sell',
        }
        return self.safe_string(statuses, status, status)

    def encode_order_side(self, status):
        statuses: dict = {
            'buy': 'BUY',
            'sell': 'SELL',
        }
        return self.safe_string(statuses, status, status)

    def parse_order_type(self, status):
        statuses: dict = {
            'MARKET': 'market',
            'LIMIT': 'limit',
            'LIMIT_MAKER': 'limit',
            'STOP_LOSS': 'market',
            'STOP_LOSS_LIMIT': 'limit',
            'TAKE_PROFIT': 'market',
            'TAKE_PROFIT_LIMIT': 'limit',
        }
        return self.safe_string(statuses, status, status)

    def encode_order_type(self, status):
        statuses: dict = {
            'market': 'MARKET',
            'limit': 'LIMIT',
            'limit_maker': 'LIMIT_MAKER',
            'stop_loss': 'STOP_LOSS',
            'stop_loss_limit': 'STOP_LOSS_LIMIT',
            'take_profit': 'TAKE_PROFIT',
            'take_profit_limit': 'TAKE_PROFIT_LIMIT',
        }
        return self.safe_string(statuses, status, status)

    def parse_order_status(self, status: Str):
        statuses: dict = {
            'NEW': 'open',
            'FILLED': 'closed',
            'CANCELED': 'canceled',
            'PARTIALLY_FILLED': 'open',
            'PARTIALLY_CANCELED': 'canceled',
            'REJECTED': 'rejected',
        }
        return self.safe_string(statuses, status, status)

    def parse_order_time_in_force(self, status):
        statuses: dict = {
            'GTC': 'GTC',
            'FOK': 'FOK',
            'IOC': 'IOC',
        }
        return self.safe_string(statuses, status, status)

    async def fetch_trading_fee(self, symbol: str, params={}) -> TradingFeeInterface:
        """
        fetch the trading fees for a market
        :see: https://coins-docs.github.io/rest-api/#trade-fee-user_data
        :param str symbol: unified market symbol
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `fee structure <https://docs.ccxt_zagrava.com/#/?id=fee-structure>`
        """
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        response = await self.privateGetOpenapiV1AssetTradeFee(self.extend(request, params))
        #
        #     [
        #       {
        #         "symbol": "ETHUSDT",
        #         "makerCommission": "0.0025",
        #         "takerCommission": "0.003"
        #       }
        #     ]
        #
        tradingFee = self.safe_value(response, 0, {})
        return self.parse_trading_fee(tradingFee, market)

    async def fetch_trading_fees(self, params={}) -> TradingFees:
        """
        fetch the trading fees for multiple markets
        :see: https://coins-docs.github.io/rest-api/#trade-fee-user_data
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a dictionary of `fee structures <https://docs.ccxt_zagrava.com/#/?id=fee-structure>` indexed by market symbols
        """
        await self.load_markets()
        response = await self.privateGetOpenapiV1AssetTradeFee(params)
        #
        #     [
        #         {
        #             "symbol": "ETHPHP",
        #             "makerCommission": "0.0025",
        #             "takerCommission": "0.003"
        #         },
        #         {
        #             "symbol": "UNIPHP",
        #             "makerCommission": "0.0025",
        #             "takerCommission": "0.003"
        #         },
        #     ]
        #
        result: dict = {}
        for i in range(0, len(response)):
            fee = self.parse_trading_fee(response[i])
            symbol = fee['symbol']
            result[symbol] = fee
        return result

    def parse_trading_fee(self, fee: dict, market: Market = None) -> TradingFeeInterface:
        #
        #     {
        #         "symbol": "ETHUSDT",
        #         "makerCommission": "0.0025",
        #         "takerCommission": "0.003"
        #     }
        #
        marketId = self.safe_string(fee, 'symbol')
        market = self.safe_market(marketId, market)
        symbol = market['symbol']
        return {
            'info': fee,
            'symbol': symbol,
            'maker': self.safe_number(fee, 'makerCommission'),
            'taker': self.safe_number(fee, 'takerCommission'),
            'percentage': None,
            'tierBased': None,
        }

    async def withdraw(self, code: str, amount: float, address: str, tag=None, params={}):
        """
        make a withdrawal to coins_ph account
        :see: https://coins-docs.github.io/rest-api/#withdrawuser_data
        :param str code: unified currency code
        :param float amount: the amount to withdraw
        :param str address: not used by coinsph withdraw()
        :param str tag:
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `transaction structure <https://docs.ccxt_zagrava.com/#/?id=transaction-structure>`
        """
        options = self.safe_value(self.options, 'withdraw')
        warning = self.safe_bool(options, 'warning', True)
        if warning:
            raise InvalidAddress(self.id + " withdraw() makes a withdrawals only to coins_ph account, add .options['withdraw']['warning'] = False to make a withdrawal to your coins_ph account")
        networkCode = self.safe_string(params, 'network')
        networkId = self.network_code_to_id(networkCode, code)
        if networkId is None:
            raise BadRequest(self.id + ' withdraw() require network parameter')
        await self.load_markets()
        currency = self.currency(code)
        request: dict = {
            'coin': currency['id'],
            'amount': self.number_to_string(amount),
            'network': networkId,
            'address': address,
        }
        if tag is not None:
            request['withdrawOrderId'] = tag
        params = self.omit(params, 'network')
        response = await self.privatePostOpenapiWalletV1WithdrawApply(self.extend(request, params))
        return self.parse_transaction(response, currency)

    async def fetch_deposits(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
        """
        fetch all deposits made to an account
        :see: https://coins-docs.github.io/rest-api/#deposit-history-user_data
        :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>`
        """
        # todo: returns an empty array - find out why
        await self.load_markets()
        currency = None
        request: dict = {}
        if code is not None:
            currency = self.currency(code)
            request['coin'] = currency['id']
        if since is not None:
            request['startTime'] = since
        if limit is not None:
            request['limit'] = limit
        response = await self.privateGetOpenapiWalletV1DepositHistory(self.extend(request, params))
        #
        # [
        #     {
        #         "id": "d_769800519366885376",
        #         "amount": "0.001",
        #         "coin": "BNB",
        #         "network": "BNB",
        #         "status": 0,
        #         "address": "bnb136ns6lfw4zs5hg4n85vdthaad7hq5m4gtkgf23",
        #         "addressTag": "101764890",
        #         "txId": "98A3EA560C6B3336D348B6C83F0F95ECE4F1F5919E94BD006E5BF3BF264FACFC",
        #         "insertTime": 1661493146000,
        #         "confirmNo": 10,
        #     },
        #     {
        #         "id": "d_769754833590042625",
        #         "amount":"0.5",
        #         "coin":"IOTA",
        #         "network":"IOTA",
        #         "status":1,
        #         "address":"SIZ9VLMHWATXKV99LH99CIGFJFUMLEHGWVZVNNZXRJJVWBPHYWPPBOSDORZ9EQSHCZAMPVAPGFYQAUUV9DROOXJLNW",
        #         "addressTag":"",
        #         "txId":"ESBFVQUTPIWQNJSPXFNHNYHSQNTGKRVKPRABQWTAXCDWOAKDKYWPTVG9BGXNVNKTLEJGESAVXIKIZ9999",
        #         "insertTime":1599620082000,
        #         "confirmNo": 20,
        #     }
        # ]
        #
        return self.parse_transactions(response, currency, since, limit)

    async def fetch_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
        """
        fetch all withdrawals made from an account
        :see: https://coins-docs.github.io/rest-api/#withdraw-history-user_data
        :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>`
        """
        # todo: returns an empty array - find out why
        await self.load_markets()
        currency = None
        request: dict = {}
        if code is not None:
            currency = self.currency(code)
            request['coin'] = currency['id']
        if since is not None:
            request['startTime'] = since
        if limit is not None:
            request['limit'] = limit
        response = await self.privateGetOpenapiWalletV1WithdrawHistory(self.extend(request, params))
        #
        # [
        #     {
        #         "id": "459890698271244288",
        #         "amount": "0.01",
        #         "transactionFee": "0",
        #         "coin": "ETH",
        #         "status": 1,
        #         "address": "0x386AE30AE2dA293987B5d51ddD03AEb70b21001F",
        #         "addressTag": "",
        #         "txId": "0x4ae2fed36a90aada978fc31c38488e8b60d7435cfe0b4daed842456b4771fcf7",
        #         "applyTime": 1673601139000,
        #         "network": "ETH",
        #         "withdrawOrderId": "thomas123",
        #         "info": "",
        #         "confirmNo": 100
        #     },
        #     {
        #         "id": "451899190746456064",
        #         "amount": "0.00063",
        #         "transactionFee": "0.00037",
        #         "coin": "ETH",
        #         "status": 1,
        #         "address": "0x386AE30AE2dA293987B5d51ddD03AEb70b21001F",
        #         "addressTag": "",
        #         "txId": "0x62690ca4f9d6a8868c258e2ce613805af614d9354dda7b39779c57b2e4da0260",
        #         "applyTime": 1671695815000,
        #         "network": "ETH",
        #         "withdrawOrderId": "",
        #         "info": "",
        #         "confirmNo": 100
        #     }
        # ]
        #
        return self.parse_transactions(response, currency, since, limit)

    def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction:
        #
        # fetchDeposits
        #     {
        #         "coin": "PHP",
        #         "address": "Internal Transfer",
        #         "addressTag": "Internal Transfer",
        #         "amount": "0.02",
        #         "id": "31312321312312312312322",
        #         "network": "Internal",
        #         "transferType": "0",
        #         "status": 3,
        #         "confirmTimes": "",
        #         "unlockConfirm": "",
        #         "txId": "Internal Transfer",
        #         "insertTime": 1657623798000,
        #         "depositOrderId": "the deposit id which created by client"
        #     }
        #
        # fetchWithdrawals
        #     {
        #         "coin": "BTC",
        #         "address": "Internal Transfer",
        #         "amount": "0.1",
        #         "id": "1201515362324421632",
        #         "withdrawOrderId": null,
        #         "network": "Internal",
        #         "transferType": "0",
        #         "status": 0,
        #         "transactionFee": "0",
        #         "confirmNo": 0,
        #         "info": "{}",
        #         "txId": "Internal Transfer",
        #         "applyTime": 1657967792000
        #     }
        #
        # todo: self is in progress
        id = self.safe_string(transaction, 'id')
        address = self.safe_string(transaction, 'address')
        tag = self.safe_string(transaction, 'addressTag')
        if tag is not None:
            if len(tag) < 1:
                tag = None
        txid = self.safe_string(transaction, 'txId')
        currencyId = self.safe_string(transaction, 'coin')
        code = self.safe_currency_code(currencyId, currency)
        timestamp = None
        timestamp = self.safe_integer_2(transaction, 'insertTime', 'applyTime')
        updated = None
        type = None
        withdrawOrderId = self.safe_string(transaction, 'withdrawOrderId')
        depositOrderId = self.safe_string(transaction, 'depositOrderId')
        if withdrawOrderId is not None:
            type = 'withdrawal'
        elif depositOrderId is not None:
            type = 'deposit'
        status = self.parse_transaction_status(self.safe_string(transaction, 'status'))
        amount = self.safe_number(transaction, 'amount')
        feeCost = self.safe_number(transaction, 'transactionFee')
        fee = None
        if feeCost is not None:
            fee = {'currency': code, 'cost': feeCost}
        network = self.safe_string(transaction, 'network')
        internal = network == 'Internal'
        return {
            'info': transaction,
            'id': id,
            'txid': txid,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'network': network,
            'address': address,
            'addressTo': address,
            'addressFrom': None,
            'tag': tag,
            'tagTo': tag,
            'tagFrom': None,
            'type': type,
            'amount': amount,
            'currency': code,
            'status': status,
            'updated': updated,
            'internal': internal,
            'comment': None,
            'fee': fee,
        }

    def parse_transaction_status(self, status: Str):
        statuses: dict = {
            '0': 'pending',
            '1': 'ok',
            '2': 'failed',
            '3': 'pending',
        }
        return self.safe_string(statuses, status, status)

    async def fetch_deposit_address(self, code: str, params={}):
        """
        fetch the deposit address for a currency associated with self account
        :see: https://coins-docs.github.io/rest-api/#deposit-address-user_data
        :param str code: unified currency code
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.network]: network for fetch deposit address
        :returns dict: an `address structure <https://docs.ccxt_zagrava.com/#/?id=address-structure>`
        """
        networkCode = self.safe_string(params, 'network')
        networkId = self.network_code_to_id(networkCode, code)
        if networkId is None:
            raise BadRequest(self.id + ' fetchDepositAddress() require network parameter')
        await self.load_markets()
        currency = self.currency(code)
        request: dict = {
            'coin': currency['id'],
            'network': networkId,
        }
        params = self.omit(params, 'network')
        response = await self.privateGetOpenapiWalletV1DepositAddress(self.extend(request, params))
        #
        #     {
        #         "coin": "ETH",
        #         "address": "0xfe98628173830bf79c59f04585ce41f7de168784",
        #         "addressTag": ""
        #     }
        #
        return self.parse_deposit_address(response, currency)

    def parse_deposit_address(self, depositAddress, currency: Currency = None):
        #
        #     {
        #         "coin": "ETH",
        #         "address": "0xfe98628173830bf79c59f04585ce41f7de168784",
        #         "addressTag": ""
        #     }
        #
        currencyId = self.safe_string(depositAddress, 'coin')
        parsedCurrency = self.safe_currency_code(currencyId, currency)
        return {
            'currency': parsedCurrency,
            'address': self.safe_string(depositAddress, 'address'),
            'tag': self.safe_string(depositAddress, 'addressTag'),
            'network': None,
            'info': depositAddress,
        }

    def url_encode_query(self, query={}):
        encodedArrayParams = ''
        keys = list(query.keys())
        for i in range(0, len(keys)):
            key = keys[i]
            if isinstance(query[key], list):
                if i != 0:
                    encodedArrayParams += '&'
                innerArray = query[key]
                query = self.omit(query, key)
                encodedArrayParam = self.parse_array_param(innerArray, key)
                encodedArrayParams += encodedArrayParam
        encodedQuery = self.urlencode(query)
        if len(encodedQuery) != 0:
            return encodedQuery + '&' + encodedArrayParams
        else:
            return encodedArrayParams

    def parse_array_param(self, array, key):
        stringifiedArray = self.json(array)
        stringifiedArray = stringifiedArray.replace('[', '%5B')
        stringifiedArray = stringifiedArray.replace(']', '%5D')
        urlEncodedParam = key + '=' + stringifiedArray
        return urlEncodedParam

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        url = self.urls['api'][api]
        query = self.omit(params, self.extract_params(path))
        endpoint = self.implode_params(path, params)
        url = url + '/' + endpoint
        if api == 'private':
            self.check_required_credentials()
            query['timestamp'] = self.milliseconds()
            recvWindow = self.safe_integer(query, 'recvWindow')
            if recvWindow is None:
                defaultRecvWindow = self.safe_integer(self.options, 'recvWindow')
                if defaultRecvWindow is not None:
                    query['recvWindow'] = defaultRecvWindow
            query = self.url_encode_query(query)
            signature = self.hmac(self.encode(query), self.encode(self.secret), hashlib.sha256)
            url = url + '?' + query + '&signature=' + signature
            headers = {
                'X-COINS-APIKEY': self.apiKey,
            }
        else:
            query = self.url_encode_query(query)
            if len(query) != 0:
                url += '?' + query
        return {'url': url, 'method': method, 'body': body, 'headers': headers}

    def handle_errors(self, code: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
        if response is None:
            return None
        responseCode = self.safe_string(response, 'code', None)
        if (responseCode is not None) and (responseCode != '200') and (responseCode != '0'):
            feedback = self.id + ' ' + body
            self.throw_broadly_matched_exception(self.exceptions['broad'], body, feedback)
            self.throw_exactly_matched_exception(self.exceptions['exact'], responseCode, feedback)
            raise ExchangeError(feedback)
        return None
