# -*- 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.cryptocom import ImplicitAPI
import hashlib
from ccxt_zagrava.base.types import Account, Balances, Currency, Int, Market, Num, Order, OrderBook, OrderRequest, CancellationRequest, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, 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 AccountNotEnabled
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 NotSupported
from ccxt_zagrava.base.errors import DDoSProtection
from ccxt_zagrava.base.errors import OnMaintenance
from ccxt_zagrava.base.errors import InvalidNonce
from ccxt_zagrava.base.decimal_to_precision import TICK_SIZE
from ccxt_zagrava.base.precise import Precise


class cryptocom(Exchange, ImplicitAPI):

    def describe(self):
        return self.deep_extend(super(cryptocom, self).describe(), {
            'id': 'cryptocom',
            'name': 'Crypto.com',
            'countries': ['MT'],
            'version': 'v2',
            'rateLimit': 10,  # 100 requests per second
            'certified': True,
            'pro': True,
            'has': {
                'CORS': False,
                'spot': True,
                'margin': True,
                'swap': True,
                'future': True,
                'option': True,
                'addMargin': False,
                'cancelAllOrders': True,
                'cancelOrder': True,
                'cancelOrders': True,
                'cancelOrdersForSymbols': True,
                'closeAllPositions': False,
                'closePosition': True,
                'createMarketBuyOrderWithCost': False,
                'createMarketOrderWithCost': False,
                'createMarketSellOrderWithCost': False,
                'createOrder': True,
                'createOrders': True,
                'fetchAccounts': True,
                'fetchBalance': True,
                'fetchBidsAsks': False,
                'fetchBorrowInterest': False,
                'fetchBorrowRateHistories': False,
                'fetchBorrowRateHistory': False,
                'fetchClosedOrders': 'emulated',
                'fetchCrossBorrowRate': False,
                'fetchCrossBorrowRates': False,
                'fetchCurrencies': False,
                'fetchDepositAddress': True,
                'fetchDepositAddressesByNetwork': True,
                'fetchDeposits': True,
                'fetchDepositsWithdrawals': False,
                'fetchDepositWithdrawFee': 'emulated',
                'fetchDepositWithdrawFees': True,
                'fetchFundingHistory': False,
                'fetchFundingRate': False,
                'fetchFundingRateHistory': True,
                'fetchFundingRates': False,
                'fetchGreeks': False,
                'fetchIndexOHLCV': False,
                'fetchIsolatedBorrowRate': False,
                'fetchIsolatedBorrowRates': False,
                'fetchLedger': True,
                'fetchLeverage': False,
                'fetchLeverageTiers': False,
                'fetchMarginAdjustmentHistory': False,
                'fetchMarginMode': False,
                'fetchMarketLeverageTiers': False,
                'fetchMarkets': True,
                'fetchMarkOHLCV': False,
                'fetchMySettlementHistory': False,
                'fetchMyTrades': True,
                'fetchOHLCV': True,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'fetchOrderBook': True,
                'fetchOrders': True,
                'fetchPosition': True,
                'fetchPositionHistory': False,
                'fetchPositionMode': False,
                'fetchPositions': True,
                'fetchPositionsHistory': False,
                'fetchPremiumIndexOHLCV': False,
                'fetchSettlementHistory': True,
                'fetchStatus': False,
                'fetchTicker': True,
                'fetchTickers': True,
                'fetchTime': False,
                'fetchTrades': True,
                'fetchTradingFee': False,
                'fetchTradingFees': False,
                'fetchTransactionFees': False,
                'fetchTransactions': False,
                'fetchTransfers': False,
                'fetchUnderlyingAssets': False,
                'fetchVolatilityHistory': False,
                'fetchWithdrawals': True,
                'reduceMargin': False,
                'repayCrossMargin': False,
                'repayIsolatedMargin': False,
                'sandbox': True,
                'setLeverage': False,
                'setMarginMode': False,
                'setPositionMode': False,
                'transfer': False,
                'withdraw': True,
            },
            'timeframes': {
                '1m': '1m',
                '5m': '5m',
                '15m': '15m',
                '30m': '30m',
                '1h': '1h',
                '4h': '4h',
                '6h': '6h',
                '12h': '12h',
                '1d': '1D',
                '1w': '7D',
                '2w': '14D',
                '1M': '1M',
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/147792121-38ed5e36-c229-48d6-b49a-48d05fc19ed4.jpeg',
                'test': {
                    'v1': 'https://uat-api.3ona.co/exchange/v1',
                    'v2': 'https://uat-api.3ona.co/v2',
                    'derivatives': 'https://uat-api.3ona.co/v2',
                },
                'api': {
                    'v1': 'https://api.crypto.com/exchange/v1',
                    'v2': 'https://api.crypto.com/v2',
                    'derivatives': 'https://deriv-api.crypto.com/v1',
                },
                'www': 'https://crypto.com/',
                'referral': {
                    'url': 'https://crypto.com/exch/kdacthrnxt',
                    'discount': 0.15,
                },
                'doc': [
                    'https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html',
                    'https://exchange-docs.crypto.com/spot/index.html',
                    'https://exchange-docs.crypto.com/derivatives/index.html',
                ],
                'fees': 'https://crypto.com/exchange/document/fees-limits',
            },
            'api': {
                'v1': {
                    'public': {
                        'get': {
                            'public/auth': 10 / 3,
                            'public/get-instruments': 10 / 3,
                            'public/get-book': 1,
                            'public/get-candlestick': 1,
                            'public/get-trades': 1,
                            'public/get-tickers': 1,
                            'public/get-valuations': 1,
                            'public/get-expired-settlement-price': 10 / 3,
                            'public/get-insurance': 1,
                        },
                    },
                    'private': {
                        'post': {
                            'private/set-cancel-on-disconnect': 10 / 3,
                            'private/get-cancel-on-disconnect': 10 / 3,
                            'private/user-balance': 10 / 3,
                            'private/user-balance-history': 10 / 3,
                            'private/get-positions': 10 / 3,
                            'private/create-order': 2 / 3,
                            'private/create-order-list': 10 / 3,
                            'private/cancel-order': 2 / 3,
                            'private/cancel-order-list': 10 / 3,
                            'private/cancel-all-orders': 2 / 3,
                            'private/close-position': 10 / 3,
                            'private/get-order-history': 100,
                            'private/get-open-orders': 10 / 3,
                            'private/get-order-detail': 1 / 3,
                            'private/get-trades': 100,
                            'private/change-account-leverage': 10 / 3,
                            'private/get-transactions': 10 / 3,
                            'private/create-subaccount-transfer': 10 / 3,
                            'private/get-subaccount-balances': 10 / 3,
                            'private/get-order-list': 10 / 3,
                            'private/create-withdrawal': 10 / 3,
                            'private/get-currency-networks': 10 / 3,
                            'private/get-deposit-address': 10 / 3,
                            'private/get-accounts': 10 / 3,
                            'private/get-withdrawal-history': 10 / 3,
                            'private/get-deposit-history': 10 / 3,
                        },
                    },
                },
                'v2': {
                    'public': {
                        'get': {
                            'public/auth': 1,
                            'public/get-instruments': 1,
                            'public/get-book': 1,
                            'public/get-candlestick': 1,
                            'public/get-ticker': 1,
                            'public/get-trades': 1,
                            'public/margin/get-transfer-currencies': 1,
                            'public/margin/get-load-currenices': 1,
                            'public/respond-heartbeat': 1,
                        },
                    },
                    'private': {
                        'post': {
                            'private/set-cancel-on-disconnect': 10 / 3,
                            'private/get-cancel-on-disconnect': 10 / 3,
                            'private/create-withdrawal': 10 / 3,
                            'private/get-withdrawal-history': 10 / 3,
                            'private/get-currency-networks': 10 / 3,
                            'private/get-deposit-history': 10 / 3,
                            'private/get-deposit-address': 10 / 3,
                            'private/export/create-export-request': 10 / 3,
                            'private/export/get-export-requests': 10 / 3,
                            'private/export/download-export-output': 10 / 3,
                            'private/get-account-summary': 10 / 3,
                            'private/create-order': 2 / 3,
                            'private/cancel-order': 2 / 3,
                            'private/cancel-all-orders': 2 / 3,
                            'private/create-order-list': 10 / 3,
                            'private/get-order-history': 10 / 3,
                            'private/get-open-orders': 10 / 3,
                            'private/get-order-detail': 1 / 3,
                            'private/get-trades': 100,
                            'private/get-accounts': 10 / 3,
                            'private/get-subaccount-balances': 10 / 3,
                            'private/create-subaccount-transfer': 10 / 3,
                            'private/otc/get-otc-user': 10 / 3,
                            'private/otc/get-instruments': 10 / 3,
                            'private/otc/request-quote': 100,
                            'private/otc/accept-quote': 100,
                            'private/otc/get-quote-history': 10 / 3,
                            'private/otc/get-trade-history': 10 / 3,
                            'private/otc/create-order': 10 / 3,
                        },
                    },
                },
                'derivatives': {
                    'public': {
                        'get': {
                            'public/auth': 10 / 3,
                            'public/get-instruments': 10 / 3,
                            'public/get-book': 1,
                            'public/get-candlestick': 1,
                            'public/get-trades': 1,
                            'public/get-tickers': 1,
                            'public/get-valuations': 1,
                            'public/get-expired-settlement-price': 10 / 3,
                            'public/get-insurance': 1,
                        },
                    },
                    'private': {
                        'post': {
                            'private/set-cancel-on-disconnect': 10 / 3,
                            'private/get-cancel-on-disconnect': 10 / 3,
                            'private/user-balance': 10 / 3,
                            'private/user-balance-history': 10 / 3,
                            'private/get-positions': 10 / 3,
                            'private/create-order': 2 / 3,
                            'private/create-order-list': 10 / 3,
                            'private/cancel-order': 2 / 3,
                            'private/cancel-order-list': 10 / 3,
                            'private/cancel-all-orders': 2 / 3,
                            'private/close-position': 10 / 3,
                            'private/convert-collateral': 10 / 3,
                            'private/get-order-history': 100,
                            'private/get-open-orders': 10 / 3,
                            'private/get-order-detail': 1 / 3,
                            'private/get-trades': 100,
                            'private/change-account-leverage': 10 / 3,
                            'private/get-transactions': 10 / 3,
                            'private/create-subaccount-transfer': 10 / 3,
                            'private/get-subaccount-balances': 10 / 3,
                            'private/get-order-list': 10 / 3,
                        },
                    },
                },
            },
            'fees': {
                'trading': {
                    'maker': self.parse_number('0.004'),
                    'taker': self.parse_number('0.004'),
                    'tiers': {
                        'maker': [
                            [self.parse_number('0'), self.parse_number('0.004')],
                            [self.parse_number('25000'), self.parse_number('0.0035')],
                            [self.parse_number('50000'), self.parse_number('0.0015')],
                            [self.parse_number('100000'), self.parse_number('0.001')],
                            [self.parse_number('250000'), self.parse_number('0.0009')],
                            [self.parse_number('1000000'), self.parse_number('0.0008')],
                            [self.parse_number('20000000'), self.parse_number('0.0007')],
                            [self.parse_number('100000000'), self.parse_number('0.0006')],
                            [self.parse_number('200000000'), self.parse_number('0.0004')],
                        ],
                        'taker': [
                            [self.parse_number('0'), self.parse_number('0.004')],
                            [self.parse_number('25000'), self.parse_number('0.0035')],
                            [self.parse_number('50000'), self.parse_number('0.0025')],
                            [self.parse_number('100000'), self.parse_number('0.0016')],
                            [self.parse_number('250000'), self.parse_number('0.00015')],
                            [self.parse_number('1000000'), self.parse_number('0.00014')],
                            [self.parse_number('20000000'), self.parse_number('0.00013')],
                            [self.parse_number('100000000'), self.parse_number('0.00012')],
                            [self.parse_number('200000000'), self.parse_number('0.0001')],
                        ],
                    },
                },
            },
            'options': {
                'defaultType': 'spot',
                'accountsById': {
                    'funding': 'SPOT',
                    'spot': 'SPOT',
                    'margin': 'MARGIN',
                    'derivatives': 'DERIVATIVES',
                    'swap': 'DERIVATIVES',
                    'future': 'DERIVATIVES',
                },
                'networks': {
                    'BEP20': 'BSC',
                    'ERC20': 'ETH',
                    'TRC20': 'TRON',
                },
                'broker': 'ccxt_zagrava',
            },
            # https://exchange-docs.crypto.com/spot/index.html#response-and-reason-codes
            'commonCurrencies': {
                'USD_STABLE_COIN': 'USDC',
            },
            'precisionMode': TICK_SIZE,
            'exceptions': {
                'exact': {
                    '219': InvalidOrder,
                    '314': InvalidOrder,  # {"id" : 1700xxx, "method" : "private/create-order", "code" : 314, "message" : "EXCEEDS_MAX_ORDER_SIZE", "result" : {"client_oid" : "1700xxx", "order_id" : "6530xxx"}}
                    '10001': ExchangeError,
                    '10002': PermissionDenied,
                    '10003': PermissionDenied,
                    '10004': BadRequest,
                    '10005': PermissionDenied,
                    '10006': DDoSProtection,
                    '10007': InvalidNonce,
                    '10008': BadRequest,
                    '10009': BadRequest,
                    '20001': BadRequest,
                    '20002': InsufficientFunds,
                    '20005': AccountNotEnabled,  # {"id":"123xxx","method":"private/margin/xxx","code":"20005","message":"ACCOUNT_NOT_FOUND"}
                    '30003': BadSymbol,
                    '30004': BadRequest,
                    '30005': BadRequest,
                    '30006': InvalidOrder,
                    '30007': InvalidOrder,
                    '30008': InvalidOrder,
                    '30009': InvalidOrder,
                    '30010': BadRequest,
                    '30013': InvalidOrder,
                    '30014': InvalidOrder,
                    '30016': InvalidOrder,
                    '30017': InvalidOrder,
                    '30023': InvalidOrder,
                    '30024': InvalidOrder,
                    '30025': InvalidOrder,
                    '40001': BadRequest,
                    '40002': BadRequest,
                    '40003': BadRequest,
                    '40004': BadRequest,
                    '40005': BadRequest,
                    '40006': BadRequest,
                    '40007': BadRequest,
                    '40101': AuthenticationError,
                    '50001': BadRequest,
                    '9010001': OnMaintenance,  # {"code":9010001,"message":"SYSTEM_MAINTENANCE","details":"Crypto.com Exchange is currently under maintenance. Please refer to https://status.crypto.com for more details."}
                },
                'broad': {},
            },
        })

    async def fetch_markets(self, params={}) -> List[Market]:
        """
        :see: https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#public-get-instruments
        retrieves data on all markets for cryptocom
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: an array of objects representing market data
        """
        response = await self.v1PublicGetPublicGetInstruments(params)
        #
        #     {
        #         "id": 1,
        #         "method": "public/get-instruments",
        #         "code": 0,
        #         "result": {
        #             "data": [
        #                 {
        #                     "symbol": "BTC_USDT",
        #                     "inst_type": "CCY_PAIR",
        #                     "display_name": "BTC/USDT",
        #                     "base_ccy": "BTC",
        #                     "quote_ccy": "USDT",
        #                     "quote_decimals": 2,
        #                     "quantity_decimals": 5,
        #                     "price_tick_size": "0.01",
        #                     "qty_tick_size": "0.00001",
        #                     "max_leverage": "50",
        #                     "tradable": True,
        #                     "expiry_timestamp_ms": 0,
        #                     "beta_product": False,
        #                     "margin_buy_enabled": False,
        #                     "margin_sell_enabled": True
        #                 },
        #                 {
        #                     "symbol": "RUNEUSD-PERP",
        #                     "inst_type": "PERPETUAL_SWAP",
        #                     "display_name": "RUNEUSD Perpetual",
        #                     "base_ccy": "RUNE",
        #                     "quote_ccy": "USD",
        #                     "quote_decimals": 3,
        #                     "quantity_decimals": 1,
        #                     "price_tick_size": "0.001",
        #                     "qty_tick_size": "0.1",
        #                     "max_leverage": "50",
        #                     "tradable": True,
        #                     "expiry_timestamp_ms": 0,
        #                     "beta_product": False,
        #                     "underlying_symbol": "RUNEUSD-INDEX",
        #                     "contract_size": "1",
        #                     "margin_buy_enabled": False,
        #                     "margin_sell_enabled": False
        #                 },
        #                 {
        #                     "symbol": "ETHUSD-230825",
        #                     "inst_type": "FUTURE",
        #                     "display_name": "ETHUSD Futures 20230825",
        #                     "base_ccy": "ETH",
        #                     "quote_ccy": "USD",
        #                     "quote_decimals": 2,
        #                     "quantity_decimals": 4,
        #                     "price_tick_size": "0.01",
        #                     "qty_tick_size": "0.0001",
        #                     "max_leverage": "100",
        #                     "tradable": True,
        #                     "expiry_timestamp_ms": 1692950400000,
        #                     "beta_product": False,
        #                     "underlying_symbol": "ETHUSD-INDEX",
        #                     "contract_size": "1",
        #                     "margin_buy_enabled": False,
        #                     "margin_sell_enabled": False
        #                 },
        #                 {
        #                     "symbol": "BTCUSD-230630-CW30000",
        #                     "inst_type": "WARRANT",
        #                     "display_name": "BTCUSD-230630-CW30000",
        #                     "base_ccy": "BTC",
        #                     "quote_ccy": "USD",
        #                     "quote_decimals": 3,
        #                     "quantity_decimals": 0,
        #                     "price_tick_size": "0.001",
        #                     "qty_tick_size": "10",
        #                     "max_leverage": "50",
        #                     "tradable": True,
        #                     "expiry_timestamp_ms": 1688112000000,
        #                     "beta_product": False,
        #                     "underlying_symbol": "BTCUSD-INDEX",
        #                     "put_call": "CALL",
        #                     "strike": "30000",
        #                     "contract_size": "0.0001",
        #                     "margin_buy_enabled": False,
        #                     "margin_sell_enabled": False
        #                 },
        #             ]
        #         }
        #     }
        #
        resultResponse = self.safe_value(response, 'result', {})
        data = self.safe_value(resultResponse, 'data', [])
        result = []
        for i in range(0, len(data)):
            market = data[i]
            inst_type = self.safe_string(market, 'inst_type')
            spot = inst_type == 'CCY_PAIR'
            swap = inst_type == 'PERPETUAL_SWAP'
            future = inst_type == 'FUTURE'
            option = inst_type == 'WARRANT'
            baseId = self.safe_string(market, 'base_ccy')
            quoteId = self.safe_string(market, 'quote_ccy')
            settleId = None if spot else quoteId
            base = self.safe_currency_code(baseId)
            quote = self.safe_currency_code(quoteId)
            settle = None if spot else self.safe_currency_code(settleId)
            optionType = self.safe_string_lower(market, 'put_call')
            strike = self.safe_string(market, 'strike')
            marginBuyEnabled = self.safe_value(market, 'margin_buy_enabled')
            marginSellEnabled = self.safe_value(market, 'margin_sell_enabled')
            expiryString = self.omit_zero(self.safe_string(market, 'expiry_timestamp_ms'))
            expiry = int(expiryString) if (expiryString is not None) else None
            symbol = base + '/' + quote
            type = None
            contract = None
            if inst_type == 'CCY_PAIR':
                type = 'spot'
                contract = False
            elif inst_type == 'PERPETUAL_SWAP':
                type = 'swap'
                symbol = symbol + ':' + quote
                contract = True
            elif inst_type == 'FUTURE':
                type = 'future'
                symbol = symbol + ':' + quote + '-' + self.yymmdd(expiry)
                contract = True
            elif inst_type == 'WARRANT':
                type = 'option'
                symbolOptionType = 'C' if (optionType == 'call') else 'P'
                symbol = symbol + ':' + quote + '-' + self.yymmdd(expiry) + '-' + strike + '-' + symbolOptionType
                contract = True
            result.append({
                'id': self.safe_string(market, 'symbol'),
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'settle': settle,
                'baseId': baseId,
                'quoteId': quoteId,
                'settleId': settleId,
                'type': type,
                'spot': spot,
                'margin': ((marginBuyEnabled) or (marginSellEnabled)),
                'swap': swap,
                'future': future,
                'option': option,
                'active': self.safe_value(market, 'tradable'),
                'contract': contract,
                'linear': True if (contract) else None,
                'inverse': False if (contract) else None,
                'contractSize': self.safe_number(market, 'contract_size'),
                'expiry': expiry,
                'expiryDatetime': self.iso8601(expiry),
                'strike': self.parse_number(strike),
                'optionType': optionType,
                'precision': {
                    'price': self.parse_number(self.safe_string(market, 'price_tick_size')),
                    'amount': self.parse_number(self.safe_string(market, 'qty_tick_size')),
                },
                'limits': {
                    'leverage': {
                        'min': self.parse_number('1'),
                        'max': self.safe_number(market, 'max_leverage'),
                    },
                    'amount': {
                        'min': None,
                        'max': None,
                    },
                    'price': {
                        'min': None,
                        'max': None,
                    },
                    'cost': {
                        'min': None,
                        'max': None,
                    },
                },
                'created': None,
                'info': market,
            })
        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://exchange-docs.crypto.com/spot/index.html#public-get-ticker
        :see: https://exchange-docs.crypto.com/derivatives/index.html#public-get-tickers
        :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()
        market = None
        request: dict = {}
        if symbols is not None:
            symbol = None
            if isinstance(symbols, list):
                symbolsLength = len(symbols)
                if symbolsLength > 1:
                    raise BadRequest(self.id + ' fetchTickers() symbols argument cannot contain more than 1 symbol')
                symbol = symbols[0]
            else:
                symbol = symbols
            market = self.market(symbol)
            request['instrument_name'] = market['id']
        response = await self.v1PublicGetPublicGetTickers(self.extend(request, params))
        #
        #     {
        #         "id": -1,
        #         "method": "public/get-tickers",
        #         "code": 0,
        #         "result": {
        #             "data": [
        #                 {
        #                     "i": "AVAXUSD-PERP",
        #                     "h": "13.209",
        #                     "l": "12.148",
        #                     "a": "13.209",
        #                     "v": "1109.8",
        #                     "vv": "14017.33",
        #                     "c": "0.0732",
        #                     "b": "13.210",
        #                     "k": "13.230",
        #                     "oi": "10888.9",
        #                     "t": 1687402657575
        #                 },
        #             ]
        #         }
        #     }
        #
        result = self.safe_value(response, 'result', {})
        data = self.safe_list(result, 'data', [])
        return self.parse_tickers(data, symbols)

    async def fetch_ticker(self, symbol: str, params={}) -> Ticker:
        """
        :see: https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#public-get-tickers
        fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
        :param str symbol: unified symbol of the market to fetch the ticker for
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `ticker structure <https://docs.ccxt_zagrava.com/#/?id=ticker-structure>`
        """
        await self.load_markets()
        symbol = self.symbol(symbol)
        tickers = await self.fetch_tickers([symbol], params)
        return self.safe_value(tickers, symbol)

    async 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://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-get-order-history
        :param str symbol: unified market symbol of the market the orders were made in
        :param int [since]: the earliest time in ms to fetch orders for, max date range is one day
        :param int [limit]: the maximum number of order structures to retrieve, default 100 max 100
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: timestamp in ms for the ending date filter, default is the current time
        :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)
        :returns Order[]: a list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        await self.load_markets()
        paginate = False
        paginate, params = self.handle_option_and_params(params, 'fetchOrders', 'paginate')
        if paginate:
            return await self.fetch_paginated_call_dynamic('fetchOrders', symbol, since, limit, params)
        market = None
        request: dict = {}
        if symbol is not None:
            market = self.market(symbol)
            request['instrument_name'] = market['id']
        if since is not None:
            request['start_time'] = since
        if limit is not None:
            request['limit'] = limit
        until = self.safe_integer(params, 'until')
        params = self.omit(params, ['until'])
        if until is not None:
            request['end_time'] = until
        response = await self.v1PrivatePostPrivateGetOrderHistory(self.extend(request, params))
        #
        #     {
        #         "id": 1686881486183,
        #         "method": "private/get-order-history",
        #         "code": 0,
        #         "result": {
        #             "data": [
        #                 {
        #                     "account_id": "ce075bef-1234-4321-bd6g-ff9007252e63",
        #                     "order_id": "6142909895014042762",
        #                     "client_oid": "4e918597-1234-4321-8201-a7577e1e1d91",
        #                     "order_type": "MARKET",
        #                     "time_in_force": "GOOD_TILL_CANCEL",
        #                     "side": "SELL",
        #                     "exec_inst": [],
        #                     "quantity": "0.00024",
        #                     "order_value": "5.7054672",
        #                     "maker_fee_rate": "0",
        #                     "taker_fee_rate": "0",
        #                     "avg_price": "25023.97",
        #                     "trigger_price": "0",
        #                     "ref_price": "0",
        #                     "ref_price_type": "NULL_VAL",
        #                     "cumulative_quantity": "0.00024",
        #                     "cumulative_value": "6.0057528",
        #                     "cumulative_fee": "0.001501438200",
        #                     "status": "FILLED",
        #                     "update_user_id": "ce075bef-1234-4321-bd6g-ff9007252e63",
        #                     "order_date": "2023-06-15",
        #                     "instrument_name": "BTC_USD",
        #                     "fee_instrument_name": "USD",
        #                     "create_time": 1686805465891,
        #                     "create_time_ns": "1686805465891812578",
        #                     "update_time": 1686805465891
        #                 }
        #             ]
        #         }
        #     }
        #
        data = self.safe_value(response, 'result', {})
        orders = self.safe_list(data, 'data', [])
        return self.parse_orders(orders, market, since, limit)

    async def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
        """
        get a list of the most recent trades for a particular symbol
        :see: https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#public-get-trades
        :param str symbol: unified symbol of the market to fetch trades for
        :param int [since]: timestamp in ms of the earliest trade to fetch, maximum date range is one day
        :param int [limit]: the maximum number of trades to fetch
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: timestamp in ms for the ending date filter, default is the current time
        :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)
        :returns Trade[]: a list of `trade structures <https://docs.ccxt_zagrava.com/#/?id=public-trades>`
        """
        await self.load_markets()
        paginate = False
        paginate, params = self.handle_option_and_params(params, 'fetchTrades', 'paginate')
        if paginate:
            return await self.fetch_paginated_call_dynamic('fetchTrades', symbol, since, limit, params)
        market = self.market(symbol)
        request: dict = {
            'instrument_name': market['id'],
        }
        if since is not None:
            request['start_ts'] = since
        if limit is not None:
            request['count'] = limit
        until = self.safe_integer(params, 'until')
        params = self.omit(params, ['until'])
        if until is not None:
            request['end_ts'] = until
        response = await self.v1PublicGetPublicGetTrades(self.extend(request, params))
        #
        #     {
        #         "id": -1,
        #         "method": "public/get-trades",
        #         "code": 0,
        #         "result": {
        #             "data": [
        #                 {
        #                     "s": "sell",
        #                     "p": "26386.00",
        #                     "q": "0.00453",
        #                     "t": 1686944282062,
        #                     "tn" : 1704476468851524373,
        #                     "d": "4611686018455979970",
        #                     "i": "BTC_USD"
        #                 },
        #             ]
        #         }
        #     }
        #
        result = self.safe_value(response, 'result', {})
        trades = self.safe_list(result, 'data', [])
        return self.parse_trades(trades, market, since, limit)

    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://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#public-get-candlestick
        :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
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: timestamp in ms for the ending date filter, default is the current time
        :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)
        :returns int[][]: A list of candles ordered, open, high, low, close, volume
        """
        await self.load_markets()
        paginate = False
        paginate, params = self.handle_option_and_params(params, 'fetchOHLCV', 'paginate', False)
        if paginate:
            return await self.fetch_paginated_call_deterministic('fetchOHLCV', symbol, since, limit, timeframe, params, 300)
        market = self.market(symbol)
        request: dict = {
            'instrument_name': market['id'],
            'timeframe': self.safe_string(self.timeframes, timeframe, timeframe),
        }
        if limit is not None:
            request['count'] = limit
        now = self.microseconds()
        duration = self.parse_timeframe(timeframe)
        until = self.safe_integer(params, 'until', now)
        params = self.omit(params, ['until'])
        if since is not None:
            request['start_ts'] = since
            if limit is not None:
                request['end_ts'] = self.sum(since, duration * (limit + 1) * 1000) - 1
            else:
                request['end_ts'] = until
        else:
            request['end_ts'] = until
        response = await self.v1PublicGetPublicGetCandlestick(self.extend(request, params))
        #
        #     {
        #         "id": -1,
        #         "method": "public/get-candlestick",
        #         "code": 0,
        #         "result": {
        #             "interval": "1m",
        #             "data": [
        #                 {
        #                     "o": "26949.89",
        #                     "h": "26957.64",
        #                     "l": "26948.24",
        #                     "c": "26950.00",
        #                     "v": "0.0670",
        #                     "t": 1687237080000
        #                 },
        #             ],
        #             "instrument_name": "BTC_USD"
        #         }
        #     }
        #
        result = self.safe_value(response, 'result', {})
        data = self.safe_list(result, 'data', [])
        return self.parse_ohlcvs(data, market, timeframe, since, limit)

    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://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#public-get-book
        :param str symbol: unified symbol of the market to fetch the order book for
        :param int [limit]: the number of order book entries to return, max 50
        :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 = {
            'instrument_name': market['id'],
        }
        if limit:
            request['depth'] = limit
        response = await self.v1PublicGetPublicGetBook(self.extend(request, params))
        #
        #     {
        #         "id": -1,
        #         "method": "public/get-book",
        #         "code": 0,
        #         "result": {
        #             "depth": 3,
        #             "data": [
        #                 {
        #                     "bids": [["30025.00", "0.00004", "1"], ["30020.15", "0.02498", "1"], ["30020.00", "0.00004", "1"]],
        #                     "asks": [["30025.01", "0.04090", "1"], ["30025.70", "0.01000", "1"], ["30026.94", "0.02681", "1"]],
        #                     "t": 1687491287380
        #                 }
        #             ],
        #             "instrument_name": "BTC_USD"
        #         }
        #     }
        #
        result = self.safe_value(response, 'result', {})
        data = self.safe_value(result, 'data', [])
        orderBook = self.safe_value(data, 0)
        timestamp = self.safe_integer(orderBook, 't')
        return self.parse_order_book(orderBook, symbol, timestamp)

    def parse_balance(self, response) -> Balances:
        responseResult = self.safe_value(response, 'result', {})
        data = self.safe_value(responseResult, 'data', [])
        positionBalances = self.safe_value(data[0], 'position_balances', [])
        result: dict = {'info': response}
        for i in range(0, len(positionBalances)):
            balance = positionBalances[i]
            currencyId = self.safe_string(balance, 'instrument_name')
            code = self.safe_currency_code(currencyId)
            account = self.account()
            account['total'] = self.safe_string(balance, 'quantity')
            account['used'] = self.safe_string(balance, 'reserved_qty')
            result[code] = account
        return self.safe_balance(result)

    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://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-user-balance
        :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.v1PrivatePostPrivateUserBalance(params)
        #
        #     {
        #         "id": 1687300499018,
        #         "method": "private/user-balance",
        #         "code": 0,
        #         "result": {
        #             "data": [
        #                 {
        #                     "total_available_balance": "5.84684368",
        #                     "total_margin_balance": "5.84684368",
        #                     "total_initial_margin": "0",
        #                     "total_maintenance_margin": "0",
        #                     "total_position_cost": "0",
        #                     "total_cash_balance": "6.44412101",
        #                     "total_collateral_value": "5.846843685",
        #                     "total_session_unrealized_pnl": "0",
        #                     "instrument_name": "USD",
        #                     "total_session_realized_pnl": "0",
        #                     "position_balances": [
        #                         {
        #                             "quantity": "0.0002119875",
        #                             "reserved_qty": "0",
        #                             "collateral_weight": "0.9",
        #                             "collateral_amount": "5.37549592",
        #                             "market_value": "5.97277325",
        #                             "max_withdrawal_balance": "0.00021198",
        #                             "instrument_name": "BTC",
        #                             "hourly_interest_rate": "0"
        #                         },
        #                     ],
        #                     "total_effective_leverage": "0",
        #                     "position_limit": "3000000",
        #                     "used_position_limit": "0",
        #                     "total_borrow": "0",
        #                     "margin_score": "0",
        #                     "is_liquidating": False,
        #                     "has_risk": False,
        #                     "terminatable": True
        #                 }
        #             ]
        #         }
        #     }
        #
        return self.parse_balance(response)

    async def fetch_order(self, id: str, symbol: Str = None, params={}):
        """
        fetches information on an order made by the user
        :see: https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-get-order-detail
        :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>`
        """
        await self.load_markets()
        market = None
        if symbol is not None:
            market = self.market(symbol)
        request: dict = {
            'order_id': id,
        }
        response = await self.v1PrivatePostPrivateGetOrderDetail(self.extend(request, params))
        #
        #     {
        #         "id": 1686872583882,
        #         "method": "private/get-order-detail",
        #         "code": 0,
        #         "result": {
        #             "account_id": "ae075bef-1234-4321-bd6g-bb9007252a63",
        #             "order_id": "6142909895025252686",
        #             "client_oid": "ccxt_zagrava_c2d2152cc32d40a3ae7fbf",
        #             "order_type": "LIMIT",
        #             "time_in_force": "GOOD_TILL_CANCEL",
        #             "side": "BUY",
        #             "exec_inst": [],
        #             "quantity": "0.00020",
        #             "limit_price": "20000.00",
        #             "order_value": "4",
        #             "avg_price": "0",
        #             "trigger_price": "0",
        #             "ref_price": "0",
        #             "cumulative_quantity": "0",
        #             "cumulative_value": "0",
        #             "cumulative_fee": "0",
        #             "status": "ACTIVE",
        #             "update_user_id": "ae075bef-1234-4321-bd6g-bb9007252a63",
        #             "order_date": "2023-06-15",
        #             "instrument_name": "BTC_USD",
        #             "fee_instrument_name": "BTC",
        #             "create_time": 1686870220684,
        #             "create_time_ns": "1686870220684239675",
        #             "update_time": 1686870220684
        #         }
        #     }
        #
        order = self.safe_dict(response, 'result', {})
        return self.parse_order(order, market)

    def create_order_request(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
        market = self.market(symbol)
        uppercaseType = type.upper()
        request: dict = {
            'instrument_name': market['id'],
            'side': side.upper(),
            'quantity': self.amount_to_precision(symbol, amount),
        }
        if (uppercaseType == 'LIMIT') or (uppercaseType == 'STOP_LIMIT') or (uppercaseType == 'TAKE_PROFIT_LIMIT'):
            request['price'] = self.price_to_precision(symbol, price)
        broker = self.safe_string(self.options, 'broker', 'ccxt_zagrava')
        request['broker_id'] = broker
        marketType = None
        marginMode = None
        marketType, params = self.handle_market_type_and_params('createOrder', market, params)
        marginMode, params = self.custom_handle_margin_mode_and_params('createOrder', params)
        if (marketType == 'margin') or (marginMode is not None):
            request['spot_margin'] = 'MARGIN'
        elif marketType == 'spot':
            request['spot_margin'] = 'SPOT'
        timeInForce = self.safe_string_upper_2(params, 'timeInForce', 'time_in_force')
        if timeInForce is not None:
            if timeInForce == 'GTC':
                request['time_in_force'] = 'GOOD_TILL_CANCEL'
            elif timeInForce == 'IOC':
                request['time_in_force'] = 'IMMEDIATE_OR_CANCEL'
            elif timeInForce == 'FOK':
                request['time_in_force'] = 'FILL_OR_KILL'
            else:
                request['time_in_force'] = timeInForce
        postOnly = self.safe_bool(params, 'postOnly', False)
        if (postOnly) or (timeInForce == 'PO'):
            request['exec_inst'] = ['POST_ONLY']
            request['time_in_force'] = 'GOOD_TILL_CANCEL'
        triggerPrice = self.safe_string_n(params, ['stopPrice', 'triggerPrice', 'ref_price'])
        stopLossPrice = self.safe_number(params, 'stopLossPrice')
        takeProfitPrice = self.safe_number(params, 'takeProfitPrice')
        isTrigger = (triggerPrice is not None)
        isStopLossTrigger = (stopLossPrice is not None)
        isTakeProfitTrigger = (takeProfitPrice is not None)
        if isTrigger:
            request['ref_price'] = self.price_to_precision(symbol, triggerPrice)
            priceString = self.number_to_string(price)
            if (uppercaseType == 'LIMIT') or (uppercaseType == 'STOP_LIMIT') or (uppercaseType == 'TAKE_PROFIT_LIMIT'):
                if side == 'buy':
                    if Precise.string_lt(priceString, triggerPrice):
                        request['type'] = 'TAKE_PROFIT_LIMIT'
                    else:
                        request['type'] = 'STOP_LIMIT'
                else:
                    if Precise.string_lt(priceString, triggerPrice):
                        request['type'] = 'STOP_LIMIT'
                    else:
                        request['type'] = 'TAKE_PROFIT_LIMIT'
            else:
                if side == 'buy':
                    if Precise.string_lt(priceString, triggerPrice):
                        request['type'] = 'TAKE_PROFIT'
                    else:
                        request['type'] = 'STOP_LOSS'
                else:
                    if Precise.string_lt(priceString, triggerPrice):
                        request['type'] = 'STOP_LOSS'
                    else:
                        request['type'] = 'TAKE_PROFIT'
        elif isStopLossTrigger:
            if (uppercaseType == 'LIMIT') or (uppercaseType == 'STOP_LIMIT'):
                request['type'] = 'STOP_LIMIT'
            else:
                request['type'] = 'STOP_LOSS'
            request['ref_price'] = self.price_to_precision(symbol, stopLossPrice)
        elif isTakeProfitTrigger:
            if (uppercaseType == 'LIMIT') or (uppercaseType == 'TAKE_PROFIT_LIMIT'):
                request['type'] = 'TAKE_PROFIT_LIMIT'
            else:
                request['type'] = 'TAKE_PROFIT'
            request['ref_price'] = self.price_to_precision(symbol, takeProfitPrice)
        else:
            request['type'] = uppercaseType
        params = self.omit(params, ['postOnly', 'clientOrderId', 'timeInForce', 'stopPrice', 'triggerPrice', 'stopLossPrice', 'takeProfitPrice'])
        return self.extend(request, params)

    async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
        """
        create a trade order
        :see: https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-create-order
        :param str symbol: unified symbol of the market to create an order in
        :param str type: 'market', 'limit', 'stop_loss', 'stop_limit', 'take_profit', 'take_profit_limit'
        :param str side: 'buy' or 'sell'
        :param float amount: how much 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.timeInForce]: 'GTC', 'IOC', 'FOK' or 'PO'
        :param str [params.ref_price_type]: 'MARK_PRICE', 'INDEX_PRICE', 'LAST_PRICE' which trigger price type to use, default is MARK_PRICE
        :param float [params.stopPrice]: price to trigger a stop order
        :param float [params.stopLossPrice]: price to trigger a stop-loss trigger order
        :param float [params.takeProfitPrice]: price to trigger a take-profit trigger order
        :returns dict: an `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        await self.load_markets()
        market = self.market(symbol)
        request = self.create_order_request(symbol, type, side, amount, price, params)
        response = await self.v1PrivatePostPrivateCreateOrder(request)
        #
        #     {
        #         "id": 1686804664362,
        #         "method": "private/create-order",
        #         "code" : 0,
        #         "result": {
        #             "order_id": "6540219377766741832",
        #             "client_oid": "ccxt_zagrava_d6ef7c3db6c1495aa8b757"
        #         }
        #     }
        #
        result = self.safe_dict(response, 'result', {})
        return self.parse_order(result, market)

    async def create_orders(self, orders: List[OrderRequest], params={}):
        """
        create a list of trade orders
        :see: https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-create-order-list-list
        :see: https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-create-order-list-oco
        :param Array orders: list of orders to create, each object should contain the parameters required by createOrder, namely symbol, type, side, amount, price and params
        :returns dict: an `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        await self.load_markets()
        ordersRequests = []
        for i in range(0, len(orders)):
            rawOrder = orders[i]
            marketId = self.safe_string(rawOrder, 'symbol')
            type = self.safe_string(rawOrder, 'type')
            side = self.safe_string(rawOrder, 'side')
            amount = self.safe_value(rawOrder, 'amount')
            price = self.safe_value(rawOrder, 'price')
            orderParams = self.safe_value(rawOrder, 'params', {})
            orderRequest = self.create_advanced_order_request(marketId, type, side, amount, price, orderParams)
            ordersRequests.append(orderRequest)
        contigency = self.safe_string(params, 'contingency_type', 'LIST')
        request: dict = {
            'contingency_type': contigency,  # or OCO
            'order_list': ordersRequests,
        }
        response = await self.v1PrivatePostPrivateCreateOrderList(self.extend(request, params))
        #
        # {
        #     "id": 12,
        #     "method": "private/create-order-list",
        #     "code": 10001,
        #     "result": {
        #       "result_list": [
        #         {
        #           "index": 0,
        #           "code": 0,
        #           "order_id": "2015106383706015873",
        #           "client_oid": "my_order_0001"
        #         },
        #         {
        #           "index": 1,
        #           "code": 20007,
        #           "message": "INVALID_REQUEST",
        #           "client_oid": "my_order_0002"
        #         }
        #       ]
        #     }
        # }
        #
        #   {
        #       "id" : 1698068111133,
        #       "method" : "private/create-order-list",
        #       "code" : 0,
        #       "result" : [{
        #         "code" : 0,
        #         "index" : 0,
        #         "client_oid" : "1698068111133_0",
        #         "order_id" : "6142909896519488206"
        #       }, {
        #         "code" : 306,
        #         "index" : 1,
        #         "client_oid" : "1698068111133_1",
        #         "message" : "INSUFFICIENT_AVAILABLE_BALANCE",
        #         "order_id" : "6142909896519488207"
        #       }]
        #   }
        #
        result = self.safe_value(response, 'result', [])
        listId = self.safe_string(result, 'list_id')
        if listId is not None:
            ocoOrders = [{'order_id': listId}]
            return self.parse_orders(ocoOrders)
        return self.parse_orders(result)

    def create_advanced_order_request(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
        # differs slightly from createOrderRequest
        # since the advanced order endpoint requires a different set of parameters
        # namely here we don't support ref_price or spot_margin
        # and market-buy orders need to send notional instead of quantity
        market = self.market(symbol)
        uppercaseType = type.upper()
        request: dict = {
            'instrument_name': market['id'],
            'side': side.upper(),
        }
        if (uppercaseType == 'LIMIT') or (uppercaseType == 'STOP_LIMIT') or (uppercaseType == 'TAKE_PROFIT_LIMIT'):
            request['price'] = self.price_to_precision(symbol, price)
        broker = self.safe_string(self.options, 'broker', 'ccxt_zagrava')
        request['broker_id'] = broker
        timeInForce = self.safe_string_upper_2(params, 'timeInForce', 'time_in_force')
        if timeInForce is not None:
            if timeInForce == 'GTC':
                request['time_in_force'] = 'GOOD_TILL_CANCEL'
            elif timeInForce == 'IOC':
                request['time_in_force'] = 'IMMEDIATE_OR_CANCEL'
            elif timeInForce == 'FOK':
                request['time_in_force'] = 'FILL_OR_KILL'
            else:
                request['time_in_force'] = timeInForce
        postOnly = self.safe_bool(params, 'postOnly', False)
        if (postOnly) or (timeInForce == 'PO'):
            request['exec_inst'] = ['POST_ONLY']
            request['time_in_force'] = 'GOOD_TILL_CANCEL'
        triggerPrice = self.safe_string_n(params, ['stopPrice', 'triggerPrice', 'ref_price'])
        stopLossPrice = self.safe_number(params, 'stopLossPrice')
        takeProfitPrice = self.safe_number(params, 'takeProfitPrice')
        isTrigger = (triggerPrice is not None)
        isStopLossTrigger = (stopLossPrice is not None)
        isTakeProfitTrigger = (takeProfitPrice is not None)
        if isTrigger:
            priceString = self.number_to_string(price)
            if (uppercaseType == 'LIMIT') or (uppercaseType == 'STOP_LIMIT') or (uppercaseType == 'TAKE_PROFIT_LIMIT'):
                if side == 'buy':
                    if Precise.string_lt(priceString, triggerPrice):
                        request['type'] = 'TAKE_PROFIT_LIMIT'
                    else:
                        request['type'] = 'STOP_LIMIT'
                else:
                    if Precise.string_lt(priceString, triggerPrice):
                        request['type'] = 'STOP_LIMIT'
                    else:
                        request['type'] = 'TAKE_PROFIT_LIMIT'
            else:
                if side == 'buy':
                    if Precise.string_lt(priceString, triggerPrice):
                        request['type'] = 'TAKE_PROFIT'
                    else:
                        request['type'] = 'STOP_LOSS'
                else:
                    if Precise.string_lt(priceString, triggerPrice):
                        request['type'] = 'STOP_LOSS'
                    else:
                        request['type'] = 'TAKE_PROFIT'
        elif isStopLossTrigger:
            if (uppercaseType == 'LIMIT') or (uppercaseType == 'STOP_LIMIT'):
                request['type'] = 'STOP_LIMIT'
            else:
                request['type'] = 'STOP_LOSS'
        elif isTakeProfitTrigger:
            if (uppercaseType == 'LIMIT') or (uppercaseType == 'TAKE_PROFIT_LIMIT'):
                request['type'] = 'TAKE_PROFIT_LIMIT'
            else:
                request['type'] = 'TAKE_PROFIT'
        else:
            request['type'] = uppercaseType
        if (side == 'buy') and ((uppercaseType == 'MARKET') or (uppercaseType == 'STOP_LOSS') or (uppercaseType == 'TAKE_PROFIT')):
            # use createmarketBuy logic here
            quoteAmount = None
            createMarketBuyOrderRequiresPrice = True
            createMarketBuyOrderRequiresPrice, params = self.handle_option_and_params(params, 'createOrder', 'createMarketBuyOrderRequiresPrice', True)
            cost = self.safe_number_2(params, 'cost', 'notional')
            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(quote quantity) 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['notional'] = quoteAmount
        else:
            request['quantity'] = self.amount_to_precision(symbol, amount)
        params = self.omit(params, ['postOnly', 'clientOrderId', 'timeInForce', 'stopPrice', 'triggerPrice', 'stopLossPrice', 'takeProfitPrice'])
        return self.extend(request, params)

    async def cancel_all_orders(self, symbol: Str = None, params={}):
        """
        cancel all open orders
        :see: https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-cancel-all-orders
        :param str symbol: unified market symbol of the orders to cancel
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict} Returns exchange raw message{@link 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['instrument_name'] = market['id']
        return await self.v1PrivatePostPrivateCancelAllOrders(self.extend(request, params))

    async def cancel_order(self, id: str, symbol: Str = None, params={}):
        """
        cancels an open order
        :see: https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-cancel-order
        :param str id: the order id of the order to cancel
        :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>`
        """
        await self.load_markets()
        market = None
        if symbol is not None:
            market = self.market(symbol)
        request: dict = {
            'order_id': id,
        }
        response = await self.v1PrivatePostPrivateCancelOrder(self.extend(request, params))
        #
        #     {
        #         "id": 1686882846638,
        #         "method": "private/cancel-order",
        #         "code": 0,
        #         "message": "NO_ERROR",
        #         "result": {
        #             "client_oid": "ccxt_zagrava_c2d2152cc32d40a3ae7fbf",
        #             "order_id": "6142909895025252686"
        #         }
        #     }
        #
        result = self.safe_dict(response, 'result', {})
        return self.parse_order(result, market)

    async def cancel_orders(self, ids, symbol: Str = None, params={}):
        """
        cancel multiple orders
        :see: https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-cancel-order-list-list
        :param str[] ids: order ids
        :param str symbol: unified market symbol
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelOrders() requires a symbol argument')
        await self.load_markets()
        market = self.market(symbol)
        orderRequests = []
        for i in range(0, len(ids)):
            id = ids[i]
            order: dict = {
                'instrument_name': market['id'],
                'order_id': str(id),
            }
            orderRequests.append(order)
        request: dict = {
            'contingency_type': 'LIST',
            'order_list': orderRequests,
        }
        response = await self.v1PrivatePostPrivateCancelOrderList(self.extend(request, params))
        result = self.safe_list(response, 'result', [])
        return self.parse_orders(result, market, None, None, params)

    async def cancel_orders_for_symbols(self, orders: List[CancellationRequest], params={}):
        """
        cancel multiple orders for multiple symbols
        :see: https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-cancel-order-list-list
        :param CancellationRequest[] orders: each order should contain the parameters required by cancelOrder namely id and symbol, example [{"id": "a", "symbol": "BTC/USDT"}, {"id": "b", "symbol": "ETH/USDT"}]
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        await self.load_markets()
        orderRequests = []
        for i in range(0, len(orders)):
            order = orders[i]
            id = self.safe_string(order, 'id')
            symbol = self.safe_string(order, 'symbol')
            market = self.market(symbol)
            orderItem: dict = {
                'instrument_name': market['id'],
                'order_id': str(id),
            }
            orderRequests.append(orderItem)
        request: dict = {
            'contingency_type': 'LIST',
            'order_list': orderRequests,
        }
        response = await self.v1PrivatePostPrivateCancelOrderList(self.extend(request, params))
        result = self.safe_list(response, 'result', [])
        return self.parse_orders(result, None, None, None, params)

    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://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-get-open-orders
        :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>`
        """
        await self.load_markets()
        market = None
        request: dict = {}
        if symbol is not None:
            market = self.market(symbol)
            request['instrument_name'] = market['id']
        response = await self.v1PrivatePostPrivateGetOpenOrders(self.extend(request, params))
        #
        #     {
        #         "id": 1686806134961,
        #         "method": "private/get-open-orders",
        #         "code": 0,
        #         "result": {
        #             "data": [
        #                 {
        #                     "account_id": "ce075bef-1234-4321-bd6g-ff9007252e63",
        #                     "order_id": "6530219477767564494",
        #                     "client_oid": "ccxt_zagrava_7ce730f0388441df9bc218",
        #                     "order_type": "LIMIT",
        #                     "time_in_force": "GOOD_TILL_CANCEL",
        #                     "side": "BUY",
        #                     "exec_inst": [],
        #                     "quantity": "0.00020",
        #                     "limit_price": "20000.00",
        #                     "order_value": "4",
        #                     "avg_price": "0",
        #                     "trigger_price": "0",
        #                     "ref_price": "0",
        #                     "cumulative_quantity": "0",
        #                     "cumulative_value": "0",
        #                     "cumulative_fee": "0",
        #                     "status": "ACTIVE",
        #                     "update_user_id": "ce075bef-1234-4321-bd6g-gg9007252e63",
        #                     "order_date": "2023-06-15",
        #                     "instrument_name": "BTC_USD",
        #                     "fee_instrument_name": "BTC",
        #                     "create_time": 1686806053992,
        #                     "create_time_ns": "1686806053992921880",
        #                     "update_time": 1686806053993
        #                 }
        #             ]
        #         }
        #     }
        #
        data = self.safe_value(response, 'result', {})
        orders = self.safe_list(data, 'data', [])
        return self.parse_orders(orders, 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://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-get-trades
        :param str symbol: unified market symbol
        :param int [since]: the earliest time in ms to fetch trades for, maximum date range is one day
        :param int [limit]: the maximum number of trade structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: timestamp in ms for the ending date filter, default is the current time
        :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)
        :returns Trade[]: a list of `trade structures <https://docs.ccxt_zagrava.com/#/?id=trade-structure>`
        """
        await self.load_markets()
        paginate = False
        paginate, params = self.handle_option_and_params(params, 'fetchMyTrades', 'paginate')
        if paginate:
            return await self.fetch_paginated_call_dynamic('fetchMyTrades', symbol, since, limit, params)
        request: dict = {}
        market = None
        if symbol is not None:
            market = self.market(symbol)
            request['instrument_name'] = market['id']
        if since is not None:
            request['start_time'] = since
        if limit is not None:
            request['limit'] = limit
        until = self.safe_integer(params, 'until')
        params = self.omit(params, ['until'])
        if until is not None:
            request['end_time'] = until
        response = await self.v1PrivatePostPrivateGetTrades(self.extend(request, params))
        #
        #     {
        #         "id": 1686942003520,
        #         "method": "private/get-trades",
        #         "code": 0,
        #         "result": {
        #             "data": [
        #                 {
        #                     "account_id": "ds075abc-1234-4321-bd6g-ff9007252r63",
        #                     "event_date": "2023-06-16",
        #                     "journal_type": "TRADING",
        #                     "side": "BUY",
        #                     "instrument_name": "BTC_USD",
        #                     "fees": "-0.0000000525",
        #                     "trade_id": "6142909898247428343",
        #                     "trade_match_id": "4611686018455978480",
        #                     "create_time": 1686941992887,
        #                     "traded_price": "26347.16",
        #                     "traded_quantity": "0.00021",
        #                     "fee_instrument_name": "BTC",
        #                     "client_oid": "d1c70a60-810e-4c92-b2a0-72b931cb31e0",
        #                     "taker_side": "TAKER",
        #                     "order_id": "6142909895036331486",
        #                     "create_time_ns": "1686941992887207066"
        #                 }
        #             ]
        #         }
        #     }
        #
        result = self.safe_value(response, 'result', {})
        trades = self.safe_list(result, 'data', [])
        return self.parse_trades(trades, market, since, limit)

    def parse_address(self, addressString):
        address = None
        tag = None
        rawTag = None
        if addressString.find('?') > 0:
            address, rawTag = addressString.split('?')
            splitted = rawTag.split('=')
            tag = splitted[1]
        else:
            address = addressString
        return [address, tag]

    async def withdraw(self, code: str, amount: float, address: str, tag=None, params={}):
        """
        make a withdrawal
        :see: https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-create-withdrawal
        :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 exchange API endpoint
        :returns dict: a `transaction structure <https://docs.ccxt_zagrava.com/#/?id=transaction-structure>`
        """
        tag, params = self.handle_withdraw_tag_and_params(tag, params)
        await self.load_markets()
        currency = self.safe_currency(code)  # for instance, USDC is not inferred from markets but it's still available
        request: dict = {
            'currency': currency['id'],
            'amount': amount,
            'address': address,
        }
        if tag is not None:
            request['address_tag'] = tag
        networkCode = None
        networkCode, params = self.handle_network_code_and_params(params)
        networkId = self.network_code_to_id(networkCode)
        if networkId is not None:
            request['network_id'] = networkId
        response = await self.v1PrivatePostPrivateCreateWithdrawal(self.extend(request, params))
        #
        #    {
        #        "id":-1,
        #        "method":"private/create-withdrawal",
        #        "code":0,
        #        "result": {
        #            "id": 2220,
        #            "amount": 1,
        #            "fee": 0.0004,
        #            "symbol": "BTC",
        #            "address": "2NBqqD5GRJ8wHy1PYyCXTe9ke5226FhavBf",
        #            "client_wid": "my_withdrawal_002",
        #            "create_time":1607063412000
        #        }
        #     }
        #
        result = self.safe_dict(response, 'result')
        return self.parse_transaction(result, currency)

    async def fetch_deposit_addresses_by_network(self, code: str, params={}):
        """
        fetch a dictionary of addresses for a currency, indexed by network
        :see: https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-get-deposit-address
        :param str code: unified currency code of the currency for the deposit address
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a dictionary of `address structures <https://docs.ccxt_zagrava.com/#/?id=address-structure>` indexed by the network
        """
        await self.load_markets()
        currency = self.safe_currency(code)
        request: dict = {
            'currency': currency['id'],
        }
        response = await self.v1PrivatePostPrivateGetDepositAddress(self.extend(request, params))
        #
        #     {
        #         "id": 1234555011221,
        #         "method": "private/get-deposit-address",
        #         "code": 0,
        #         "result": {
        #             "deposit_address_list": [
        #                 {
        #                     "currency": "BTC",
        #                     "create_time": 1686730755000,
        #                     "id": "3737377",
        #                     "address": "3N9afggxTSmJ3H4jaMQuWyEiLBzZdAbK6d",
        #                     "status":"1",
        #                     "network": "BTC"
        #                 },
        #             ]
        #         }
        #     }
        #
        data = self.safe_value(response, 'result', {})
        addresses = self.safe_value(data, 'deposit_address_list', [])
        addressesLength = len(addresses)
        if addressesLength == 0:
            raise ExchangeError(self.id + ' fetchDepositAddressesByNetwork() generating address...')
        result: dict = {}
        for i in range(0, addressesLength):
            value = self.safe_value(addresses, i)
            addressString = self.safe_string(value, 'address')
            currencyId = self.safe_string(value, 'currency')
            responseCode = self.safe_currency_code(currencyId)
            address, tag = self.parse_address(addressString)
            self.check_address(address)
            networkId = self.safe_string(value, 'network')
            network = self.network_id_to_code(networkId, responseCode)
            result[network] = {
                'info': value,
                'currency': responseCode,
                'address': address,
                'tag': tag,
                'network': network,
            }
        return result

    async 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>`
        """
        network = self.safe_string_upper(params, 'network')
        params = self.omit(params, ['network'])
        depositAddresses = await self.fetch_deposit_addresses_by_network(code, params)
        if network in depositAddresses:
            return depositAddresses[network]
        else:
            keys = list(depositAddresses.keys())
            return depositAddresses[keys[0]]

    def safe_network(self, networkId):
        networksById: dict = {
            'BTC': 'BTC',
            'ETH': 'ETH',
            'SOL': 'SOL',
            'BNB': 'BNB',
            'CRONOS': 'CRONOS',
            'MATIC': 'MATIC',
            'OP': 'OP',
        }
        return self.safe_string(networksById, networkId, networkId)

    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://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-get-deposit-history
        :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
        :param int [params.until]: timestamp in ms for the ending date filter, default is the current time
        :returns dict[]: a list of `transaction structures <https://docs.ccxt_zagrava.com/#/?id=transaction-structure>`
        """
        await self.load_markets()
        currency = None
        request: dict = {}
        if code is not None:
            currency = self.safe_currency(code)
            request['currency'] = currency['id']
        if since is not None:
            # 90 days date range
            request['start_ts'] = since
        if limit is not None:
            request['page_size'] = limit
        until = self.safe_integer(params, 'until')
        params = self.omit(params, ['until'])
        if until is not None:
            request['end_ts'] = until
        response = await self.v1PrivatePostPrivateGetDepositHistory(self.extend(request, params))
        #
        #     {
        #         "id": 1688701375714,
        #         "method": "private/get-deposit-history",
        #         "code": 0,
        #         "result": {
        #             "deposit_list": [
        #                 {
        #                     "currency": "BTC",
        #                     "fee": 0,
        #                     "create_time": 1688023659000,
        #                     "id": "6201135",
        #                     "update_time": 1688178509000,
        #                     "amount": 0.00114571,
        #                     "address": "1234fggxTSmJ3H4jaMQuWyEiLBzZdAbK6d",
        #                     "status": "1",
        #                     "txid": "f0ae4202b76eb999c301eccdde44dc639bee42d1fdd5974105286ca3393f6065/2"
        #                 },
        #             ]
        #         }
        #     }
        #
        data = self.safe_value(response, 'result', {})
        depositList = self.safe_list(data, 'deposit_list', [])
        return self.parse_transactions(depositList, 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://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-get-withdrawal-history
        :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
        :param int [params.until]: timestamp in ms for the ending date filter, default is the current time
        :returns dict[]: a list of `transaction structures <https://docs.ccxt_zagrava.com/#/?id=transaction-structure>`
        """
        await self.load_markets()
        currency = None
        request: dict = {}
        if code is not None:
            currency = self.safe_currency(code)
            request['currency'] = currency['id']
        if since is not None:
            # 90 days date range
            request['start_ts'] = since
        if limit is not None:
            request['page_size'] = limit
        until = self.safe_integer(params, 'until')
        params = self.omit(params, ['until'])
        if until is not None:
            request['end_ts'] = until
        response = await self.v1PrivatePostPrivateGetWithdrawalHistory(self.extend(request, params))
        #
        #     {
        #         "id": 1688613879534,
        #         "method": "private/get-withdrawal-history",
        #         "code": 0,
        #         "result": {
        #             "withdrawal_list": [
        #                 {
        #                     "currency": "BTC",
        #                     "client_wid": "",
        #                     "fee": 0.0005,
        #                     "create_time": 1688613850000,
        #                     "id": "5275977",
        #                     "update_time": 1688613850000,
        #                     "amount": 0.0005,
        #                     "address": "1234NMEWbiF8ZkwUMxmfzMxi2A1MQ44bMn",
        #                     "status": "1",
        #                     "txid": "",
        #                     "network_id": "BTC"
        #                 }
        #             ]
        #         }
        #     }
        #
        data = self.safe_value(response, 'result', {})
        withdrawalList = self.safe_list(data, 'withdrawal_list', [])
        return self.parse_transactions(withdrawalList, currency, since, limit)

    def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
        #
        # fetchTicker
        #
        #     {
        #         "i": "BTC_USD",
        #         "h": "30821.45",
        #         "l": "28685.11",
        #         "a": "30446.00",
        #         "v": "1767.8734",
        #         "vv": "52436726.42",
        #         "c": "0.0583",
        #         "b": "30442.00",
        #         "k": "30447.66",
        #         "t": 1687403045415
        #     }
        #
        # fetchTickers
        #
        #     {
        #         "i": "AVAXUSD-PERP",
        #         "h": "13.209",
        #         "l": "12.148",
        #         "a": "13.209",
        #         "v": "1109.8",
        #         "vv": "14017.33",
        #         "c": "0.0732",
        #         "b": "13.210",
        #         "k": "13.230",
        #         "oi": "10888.9",
        #         "t": 1687402657575
        #     }
        #
        timestamp = self.safe_integer(ticker, 't')
        marketId = self.safe_string(ticker, 'i')
        market = self.safe_market(marketId, market, '_')
        quote = self.safe_string(market, 'quote')
        last = self.safe_string(ticker, 'a')
        return self.safe_ticker({
            'symbol': market['symbol'],
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'high': self.safe_number(ticker, 'h'),
            'low': self.safe_number(ticker, 'l'),
            'bid': self.safe_number(ticker, 'b'),
            'bidVolume': None,
            'ask': self.safe_number(ticker, 'k'),
            'askVolume': None,
            'vwap': None,
            'open': None,
            'close': last,
            'last': last,
            'previousClose': None,
            'change': None,
            'percentage': self.safe_string(ticker, 'c'),
            'average': None,
            'baseVolume': self.safe_string(ticker, 'v'),
            'quoteVolume': self.safe_string(ticker, 'vv') if (quote == 'USD') else None,
            'info': ticker,
        }, market)

    def parse_trade(self, trade: dict, market: Market = None) -> Trade:
        #
        # fetchTrades
        #
        #     {
        #         "s": "sell",
        #         "p": "26386.00",
        #         "q": "0.00453",
        #         "tn": 1686944282062,
        #         "tn": 1704476468851524373,
        #         "d": "4611686018455979970",
        #         "i": "BTC_USD"
        #     }
        #
        # fetchMyTrades
        #
        #     {
        #         "account_id": "ds075abc-1234-4321-bd6g-ff9007252r63",
        #         "event_date": "2023-06-16",
        #         "journal_type": "TRADING",
        #         "side": "BUY",
        #         "instrument_name": "BTC_USD",
        #         "fees": "-0.0000000525",
        #         "trade_id": "6142909898247428343",
        #         "trade_match_id": "4611686018455978480",
        #         "create_time": 1686941992887,
        #         "traded_price": "26347.16",
        #         "traded_quantity": "0.00021",
        #         "fee_instrument_name": "BTC",
        #         "client_oid": "d1c70a60-1234-4c92-b2a0-72b931cb31e0",
        #         "taker_side": "TAKER",
        #         "order_id": "6142909895036331486",
        #         "create_time_ns": "1686941992887207066"
        #     }
        #
        timestamp = self.safe_integer_2(trade, 't', 'create_time')
        marketId = self.safe_string_2(trade, 'i', 'instrument_name')
        market = self.safe_market(marketId, market, '_')
        feeCurrency = self.safe_string(trade, 'fee_instrument_name')
        feeCostString = self.safe_string(trade, 'fees')
        return self.safe_trade({
            'info': trade,
            'id': self.safe_string_2(trade, 'd', 'trade_id'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': market['symbol'],
            'order': self.safe_string(trade, 'order_id'),
            'side': self.safe_string_lower_2(trade, 's', 'side'),
            'takerOrMaker': self.safe_string_lower(trade, 'taker_side'),
            'price': self.safe_number_2(trade, 'p', 'traded_price'),
            'amount': self.safe_number_2(trade, 'q', 'traded_quantity'),
            'cost': None,
            'type': None,
            'fee': {
                'currency': self.safe_currency_code(feeCurrency),
                'cost': self.parse_number(Precise.string_neg(feeCostString)),
            },
        }, market)

    def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
        #
        #     {
        #         "o": "26949.89",
        #         "h": "26957.64",
        #         "l": "26948.24",
        #         "c": "26950.00",
        #         "v": "0.0670",
        #         "t": 1687237080000
        #     }
        #
        return [
            self.safe_integer(ohlcv, 't'),
            self.safe_number(ohlcv, 'o'),
            self.safe_number(ohlcv, 'h'),
            self.safe_number(ohlcv, 'l'),
            self.safe_number(ohlcv, 'c'),
            self.safe_number(ohlcv, 'v'),
        ]

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

    def parse_time_in_force(self, timeInForce: Str):
        timeInForces: dict = {
            'GOOD_TILL_CANCEL': 'GTC',
            'IMMEDIATE_OR_CANCEL': 'IOC',
            'FILL_OR_KILL': 'FOK',
        }
        return self.safe_string(timeInForces, timeInForce, timeInForce)

    def parse_order(self, order: dict, market: Market = None) -> Order:
        #
        # createOrder, cancelOrder
        #
        #     {
        #         "order_id": "6540219377766741832",
        #         "client_oid": "ccxt_zagrava_d6ef7c3db6c1495aa8b757"
        #     }
        #
        # fetchOpenOrders, fetchOrder, fetchOrders
        #
        #     {
        #         "account_id": "ce075bef-1234-4321-bd6g-ff9007252e63",
        #         "order_id": "6530219477767564494",
        #         "client_oid": "ccxt_zagrava_7ce730f0388441df9bc218",
        #         "order_type": "LIMIT",
        #         "time_in_force": "GOOD_TILL_CANCEL",
        #         "side": "BUY",
        #         "exec_inst": [],
        #         "quantity": "0.00020",
        #         "limit_price": "20000.00",
        #         "order_value": "4",
        #         "avg_price": "0",
        #         "trigger_price": "0",
        #         "ref_price": "0",
        #         "cumulative_quantity": "0",
        #         "cumulative_value": "0",
        #         "cumulative_fee": "0",
        #         "status": "ACTIVE",
        #         "update_user_id": "ce075bef-1234-4321-bd6g-gg9007252e63",
        #         "order_date": "2023-06-15",
        #         "instrument_name": "BTC_USD",
        #         "fee_instrument_name": "BTC",
        #         "create_time": 1686806053992,
        #         "create_time_ns": "1686806053992921880",
        #         "update_time": 1686806053993
        #     }
        #
        # createOrders
        #     {
        #             "code" : 306,
        #             "index" : 1,
        #             "client_oid" : "1698068111133_1",
        #             "message" : "INSUFFICIENT_AVAILABLE_BALANCE",
        #             "order_id" : "6142909896519488207"
        #     }
        #
        code = self.safe_integer(order, 'code')
        if (code is not None) and (code != 0):
            return self.safe_order({
                'id': self.safe_string(order, 'order_id'),
                'clientOrderId': self.safe_string(order, 'client_oid'),
                'info': order,
                'status': 'rejected',
            })
        created = self.safe_integer(order, 'create_time')
        marketId = self.safe_string(order, 'instrument_name')
        symbol = self.safe_symbol(marketId, market)
        execInst = self.safe_value(order, 'exec_inst')
        postOnly = None
        if execInst is not None:
            postOnly = False
            for i in range(0, len(execInst)):
                inst = execInst[i]
                if inst == 'POST_ONLY':
                    postOnly = True
                    break
        feeCurrency = self.safe_string(order, 'fee_instrument_name')
        return self.safe_order({
            'info': order,
            'id': self.safe_string(order, 'order_id'),
            'clientOrderId': self.safe_string(order, 'client_oid'),
            'timestamp': created,
            'datetime': self.iso8601(created),
            'lastTradeTimestamp': self.safe_integer(order, 'update_time'),
            'status': self.parse_order_status(self.safe_string(order, 'status')),
            'symbol': symbol,
            'type': self.safe_string_lower(order, 'order_type'),
            'timeInForce': self.parse_time_in_force(self.safe_string(order, 'time_in_force')),
            'postOnly': postOnly,
            'side': self.safe_string_lower(order, 'side'),
            'price': self.safe_number(order, 'limit_price'),
            'amount': self.safe_number(order, 'quantity'),
            'filled': self.safe_number(order, 'cumulative_quantity'),
            'remaining': None,
            'average': self.safe_number(order, 'avg_price'),
            'cost': self.safe_number(order, 'cumulative_value'),
            'fee': {
                'currency': self.safe_currency_code(feeCurrency),
                'cost': self.safe_number(order, 'cumulative_fee'),
            },
            'trades': [],
        }, market)

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

    def parse_withdrawal_status(self, status):
        statuses: dict = {
            '0': 'pending',
            '1': 'pending',
            '2': 'failed',
            '3': 'pending',
            '4': 'failed',
            '5': 'ok',
            '6': 'canceled',
        }
        return self.safe_string(statuses, status, status)

    def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction:
        #
        # fetchDeposits
        #
        #     {
        #         "currency": "BTC",
        #         "fee": 0,
        #         "create_time": 1688023659000,
        #         "id": "6201135",
        #         "update_time": 1688178509000,
        #         "amount": 0.00114571,
        #         "address": "1234fggxTSmJ3H4jaMQuWyEiLBzZdAbK6d",
        #         "status": "1",
        #         "txid": "f0ae4202b76eb999c301eccdde44dc639bee42d1fdd5974105286ca3393f6065/2"
        #     }
        #
        # fetchWithdrawals
        #
        #     {
        #         "currency": "BTC",
        #         "client_wid": "",
        #         "fee": 0.0005,
        #         "create_time": 1688613850000,
        #         "id": "5775977",
        #         "update_time": 1688613850000,
        #         "amount": 0.0005,
        #         "address": "1234NMEWbiF8ZkwUMxmfzMxi2A1MQ44bMn",
        #         "status": "1",
        #         "txid": "",
        #         "network_id": "BTC"
        #     }
        #
        # withdraw
        #
        #     {
        #         "id": 2220,
        #         "amount": 1,
        #         "fee": 0.0004,
        #         "symbol": "BTC",
        #         "address": "2NBqqD5GRJ8wHy1PYyCXTe9ke5226FhavBf",
        #         "client_wid": "my_withdrawal_002",
        #         "create_time":1607063412000
        #     }
        #
        type = None
        rawStatus = self.safe_string(transaction, 'status')
        status = None
        if 'client_wid' in transaction:
            type = 'withdrawal'
            status = self.parse_withdrawal_status(rawStatus)
        else:
            type = 'deposit'
            status = self.parse_deposit_status(rawStatus)
        addressString = self.safe_string(transaction, 'address')
        address, tag = self.parse_address(addressString)
        currencyId = self.safe_string(transaction, 'currency')
        code = self.safe_currency_code(currencyId, currency)
        timestamp = self.safe_integer(transaction, 'create_time')
        feeCost = self.safe_number(transaction, 'fee')
        fee = None
        if feeCost is not None:
            fee = {'currency': code, 'cost': feeCost}
        return {
            'info': transaction,
            'id': self.safe_string(transaction, 'id'),
            'txid': self.safe_string(transaction, 'txid'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'network': None,
            'address': address,
            'addressTo': address,
            'addressFrom': None,
            'tag': tag,
            'tagTo': tag,
            'tagFrom': None,
            'type': type,
            'amount': self.safe_number(transaction, 'amount'),
            'currency': code,
            'status': status,
            'updated': self.safe_integer(transaction, 'update_time'),
            'internal': None,
            'comment': self.safe_string(transaction, 'client_wid'),
            'fee': fee,
        }

    def custom_handle_margin_mode_and_params(self, methodName, params={}):
        """
         * @ignore
        marginMode specified by params["marginMode"], self.options["marginMode"], self.options["defaultMarginMode"], params["margin"] = True or self.options["defaultType"] = 'margin'
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns Array: the marginMode in lowercase
        """
        defaultType = self.safe_string(self.options, 'defaultType')
        isMargin = self.safe_bool(params, 'margin', False)
        params = self.omit(params, 'margin')
        marginMode = None
        marginMode, params = self.handle_margin_mode_and_params(methodName, params)
        if marginMode is not None:
            if marginMode != 'cross':
                raise NotSupported(self.id + ' only cross margin is supported')
        else:
            if (defaultType == 'margin') or (isMargin is True):
                marginMode = 'cross'
        return [marginMode, params]

    def parse_deposit_withdraw_fee(self, fee, currency: Currency = None):
        #
        #    {
        #        "full_name": "Alchemix",
        #        "default_network": "ETH",
        #        "network_list": [
        #          {
        #            "network_id": "ETH",
        #            "withdrawal_fee": "0.25000000",
        #            "withdraw_enabled": True,
        #            "min_withdrawal_amount": "0.5",
        #            "deposit_enabled": True,
        #            "confirmation_required": "0"
        #          }
        #        ]
        #    }
        #
        networkList = self.safe_value(fee, 'network_list')
        networkListLength = len(networkList)
        result: dict = {
            'info': fee,
            'withdraw': {
                'fee': None,
                'percentage': None,
            },
            'deposit': {
                'fee': None,
                'percentage': None,
            },
            'networks': {},
        }
        if networkList is not None:
            for i in range(0, networkListLength):
                networkInfo = networkList[i]
                networkId = self.safe_string(networkInfo, 'network_id')
                currencyCode = self.safe_string(currency, 'code')
                networkCode = self.network_id_to_code(networkId, currencyCode)
                result['networks'][networkCode] = {
                    'deposit': {'fee': None, 'percentage': None},
                    'withdraw': {'fee': self.safe_number(networkInfo, 'withdrawal_fee'), 'percentage': False},
                }
                if networkListLength == 1:
                    result['withdraw']['fee'] = self.safe_number(networkInfo, 'withdrawal_fee')
                    result['withdraw']['percentage'] = False
        return result

    async def fetch_deposit_withdraw_fees(self, codes: Strings = None, params={}):
        """
        fetch deposit and withdraw fees
        :see: https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-get-currency-networks
        :param str[]|None codes: list of unified currency codes
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a list of `fee structures <https://docs.ccxt_zagrava.com/#/?id=fee-structure>`
        """
        await self.load_markets()
        response = await self.v1PrivatePostPrivateGetCurrencyNetworks(params)
        data = self.safe_value(response, 'result')
        currencyMap = self.safe_list(data, 'currency_map')
        return self.parse_deposit_withdraw_fees(currencyMap, codes, 'full_name')

    async def fetch_ledger(self, code: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        fetch the history of changes, actions done by the user or operations that altered the balance of the user
        :see: https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-get-transactions
        :param str code: unified currency code
        :param int [since]: timestamp in ms of the earliest ledger entry
        :param int [limit]: max number of ledger entries to return
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: timestamp in ms for the ending date filter, default is the current time
        :returns dict: a `ledger structure <https://docs.ccxt_zagrava.com/#/?id=ledger-structure>`
        """
        await self.load_markets()
        request: dict = {}
        currency = None
        if code is not None:
            currency = self.safe_currency(code)
        if since is not None:
            request['start_time'] = since
        if limit is not None:
            request['limit'] = limit
        until = self.safe_integer(params, 'until')
        params = self.omit(params, ['until'])
        if until is not None:
            request['end_time'] = until
        response = await self.v1PrivatePostPrivateGetTransactions(self.extend(request, params))
        #
        #     {
        #         "id": 1686813195698,
        #         "method": "private/get-transactions",
        #         "code": 0,
        #         "result": {
        #             "data": [
        #                 {
        #                     "account_id": "ce075cef-1234-4321-bd6e-gf9007351e64",
        #                     "event_date": "2023-06-15",
        #                     "journal_type": "TRADING",
        #                     "journal_id": "6530219460124075091",
        #                     "transaction_qty": "6.0091224",
        #                     "transaction_cost": "6.0091224",
        #                     "realized_pnl": "0",
        #                     "order_id": "6530219477766741833",
        #                     "trade_id": "6530219495775954765",
        #                     "trade_match_id": "4611686018455865176",
        #                     "event_timestamp_ms": 1686804665013,
        #                     "event_timestamp_ns": "1686804665013642422",
        #                     "client_oid": "ccxt_zagrava_d6ea7c5db6c1495aa8b758",
        #                     "taker_side": "",
        #                     "side": "BUY",
        #                     "instrument_name": "USD"
        #                 },
        #             ]
        #         }
        #     }
        #
        result = self.safe_value(response, 'result', {})
        ledger = self.safe_value(result, 'data', [])
        return self.parse_ledger(ledger, currency, since, limit)

    def parse_ledger_entry(self, item: dict, currency: Currency = None):
        #
        #     {
        #         "account_id": "ce075cef-1234-4321-bd6e-gf9007351e64",
        #         "event_date": "2023-06-15",
        #         "journal_type": "TRADING",
        #         "journal_id": "6530219460124075091",
        #         "transaction_qty": "6.0091224",
        #         "transaction_cost": "6.0091224",
        #         "realized_pnl": "0",
        #         "order_id": "6530219477766741833",
        #         "trade_id": "6530219495775954765",
        #         "trade_match_id": "4611686018455865176",
        #         "event_timestamp_ms": 1686804665013,
        #         "event_timestamp_ns": "1686804665013642422",
        #         "client_oid": "ccxt_zagrava_d6ea7c5db6c1495aa8b758",
        #         "taker_side": "",
        #         "side": "BUY",
        #         "instrument_name": "USD"
        #     }
        #
        timestamp = self.safe_integer(item, 'event_timestamp_ms')
        currencyId = self.safe_string(item, 'instrument_name')
        amount = self.safe_string(item, 'transaction_qty')
        direction = None
        if Precise.string_lt(amount, '0'):
            direction = 'out'
            amount = Precise.string_abs(amount)
        else:
            direction = 'in'
        return {
            'id': self.safe_string(item, 'order_id'),
            'direction': direction,
            'account': self.safe_string(item, 'account_id'),
            'referenceId': self.safe_string(item, 'trade_id'),
            'referenceAccount': self.safe_string(item, 'trade_match_id'),
            'type': self.parse_ledger_entry_type(self.safe_string(item, 'journal_type')),
            'currency': self.safe_currency_code(currencyId, currency),
            'amount': self.parse_number(amount),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'before': None,
            'after': None,
            'status': None,
            'fee': {
                'currency': None,
                'cost': None,
            },
            'info': item,
        }

    def parse_ledger_entry_type(self, type):
        ledgerType: dict = {
            'TRADING': 'trade',
            'TRADE_FEE': 'fee',
            'WITHDRAW_FEE': 'fee',
            'WITHDRAW': 'withdrawal',
            'DEPOSIT': 'deposit',
            'ROLLBACK_WITHDRAW': 'rollback',
            'ROLLBACK_DEPOSIT': 'rollback',
            'FUNDING': 'fee',
            'REALIZED_PNL': 'trade',
            'INSURANCE_FUND': 'insurance',
            'SOCIALIZED_LOSS': 'trade',
            'LIQUIDATION_FEE': 'fee',
            'SESSION_RESET': 'reset',
            'ADJUSTMENT': 'adjustment',
            'SESSION_SETTLE': 'settlement',
            'UNCOVERED_LOSS': 'trade',
            'ADMIN_ADJUSTMENT': 'adjustment',
            'DELIST': 'delist',
            'SETTLEMENT_FEE': 'fee',
            'AUTO_CONVERSION': 'conversion',
            'MANUAL_CONVERSION': 'conversion',
        }
        return self.safe_string(ledgerType, type, type)

    async def fetch_accounts(self, params={}) -> List[Account]:
        """
        fetch all the accounts associated with a profile
        :see: https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-get-accounts
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a dictionary of `account structures <https://docs.ccxt_zagrava.com/#/?id=account-structure>` indexed by the account type
        """
        await self.load_markets()
        response = await self.v1PrivatePostPrivateGetAccounts(params)
        #
        #     {
        #         "id": 1234567894321,
        #         "method": "private/get-accounts",
        #         "code": 0,
        #         "result": {
        #             "master_account": {
        #                 "uuid": "a1234abc-1234-4321-q5r7-b1ab0a0b12b",
        #                 "user_uuid": "a1234abc-1234-4321-q5r7-b1ab0a0b12b",
        #                 "enabled": True,
        #                 "tradable": True,
        #                 "name": "YOUR_NAME",
        #                 "country_code": "CAN",
        #                 "phone_country_code": "CAN",
        #                 "incorp_country_code": "",
        #                 "margin_access": "DEFAULT",
        #                 "derivatives_access": "DEFAULT",
        #                 "create_time": 1656445188000,
        #                 "update_time": 1660794567262,
        #                 "two_fa_enabled": True,
        #                 "kyc_level": "ADVANCED",
        #                 "suspended": False,
        #                 "terminated": False,
        #                 "spot_enabled": False,
        #                 "margin_enabled": False,
        #                 "derivatives_enabled": False
        #             },
        #             "sub_account_list": []
        #         }
        #     }
        #
        result = self.safe_value(response, 'result', {})
        masterAccount = self.safe_value(result, 'master_account', {})
        accounts = self.safe_value(result, 'sub_account_list', [])
        accounts.append(masterAccount)
        return self.parse_accounts(accounts, params)

    def parse_account(self, account):
        #
        #     {
        #         "uuid": "a1234abc-1234-4321-q5r7-b1ab0a0b12b",
        #         "user_uuid": "a1234abc-1234-4321-q5r7-b1ab0a0b12b",
        #         "master_account_uuid": "a1234abc-1234-4321-q5r7-b1ab0a0b12b",
        #         "label": "FORMER_MASTER_MARGIN",
        #         "enabled": True,
        #         "tradable": True,
        #         "name": "YOUR_NAME",
        #         "country_code": "YOUR_COUNTRY_CODE",
        #         "incorp_country_code": "",
        #         "margin_access": "DEFAULT",
        #         "derivatives_access": "DEFAULT",
        #         "create_time": 1656481992000,
        #         "update_time": 1667272884594,
        #         "two_fa_enabled": False,
        #         "kyc_level": "ADVANCED",
        #         "suspended": False,
        #         "terminated": False,
        #         "spot_enabled": False,
        #         "margin_enabled": False,
        #         "derivatives_enabled": False,
        #         "system_label": "FORMER_MASTER_MARGIN"
        #     }
        #
        return {
            'id': self.safe_string(account, 'uuid'),
            'type': self.safe_string(account, 'label'),
            'code': None,
            'info': account,
        }

    async def fetch_settlement_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        fetches historical settlement records
        :see: https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#public-get-expired-settlement-price
        :param str symbol: unified market symbol of the settlement history
        :param int [since]: timestamp in ms
        :param int [limit]: number of records
        :param dict [params]: exchange specific params
        :param int [params.type]: 'future', 'option'
        :returns dict[]: a list of `settlement history objects <https://docs.ccxt_zagrava.com/#/?id=settlement-history-structure>`
        """
        await self.load_markets()
        market = None
        if symbol is not None:
            market = self.market(symbol)
        type = None
        type, params = self.handle_market_type_and_params('fetchSettlementHistory', market, params)
        self.check_required_argument('fetchSettlementHistory', type, 'type', ['future', 'option', 'WARRANT', 'FUTURE'])
        if type == 'option':
            type = 'WARRANT'
        request: dict = {
            'instrument_type': type.upper(),
        }
        response = await self.v1PublicGetPublicGetExpiredSettlementPrice(self.extend(request, params))
        #
        #     {
        #         "id": -1,
        #         "method": "public/get-expired-settlement-price",
        #         "code": 0,
        #         "result": {
        #             "data": [
        #                 {
        #                     "i": "BTCUSD-230526",
        #                     "x": 1685088000000,
        #                     "v": "26464.1",
        #                     "t": 1685087999500
        #                 }
        #             ]
        #         }
        #     }
        #
        result = self.safe_value(response, 'result', {})
        data = self.safe_value(result, 'data', [])
        settlements = self.parse_settlements(data, market)
        sorted = self.sort_by(settlements, 'timestamp')
        return self.filter_by_symbol_since_limit(sorted, symbol, since, limit)

    def parse_settlement(self, settlement, market):
        #
        #     {
        #         "i": "BTCUSD-230526",
        #         "x": 1685088000000,
        #         "v": "26464.1",
        #         "t": 1685087999500
        #     }
        #
        timestamp = self.safe_integer(settlement, 'x')
        marketId = self.safe_string(settlement, 'i')
        return {
            'info': settlement,
            'symbol': self.safe_symbol(marketId, market),
            'price': self.safe_number(settlement, 'v'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
        }

    def parse_settlements(self, settlements, market):
        #
        #     [
        #         {
        #             "i": "BTCUSD-230526",
        #             "x": 1685088000000,
        #             "v": "26464.1",
        #             "t": 1685087999500
        #         }
        #     ]
        #
        result = []
        for i in range(0, len(settlements)):
            result.append(self.parse_settlement(settlements[i], market))
        return result

    async def fetch_funding_rate_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        fetches historical funding rates
        :see: https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#public-get-valuations
        :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] to fetch
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: timestamp in ms for the ending date filter, default is the current time
        :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)
        :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')
        await self.load_markets()
        paginate = False
        paginate, params = self.handle_option_and_params(params, 'fetchFundingRateHistory', 'paginate')
        if paginate:
            return await self.fetch_paginated_call_deterministic('fetchFundingRateHistory', symbol, since, limit, '8h', params)
        market = self.market(symbol)
        if not market['swap']:
            raise BadSymbol(self.id + ' fetchFundingRateHistory() supports swap contracts only')
        request: dict = {
            'instrument_name': market['id'],
            'valuation_type': 'funding_hist',
        }
        if since is not None:
            request['start_ts'] = since
        if limit is not None:
            request['count'] = limit
        until = self.safe_integer(params, 'until')
        params = self.omit(params, ['until'])
        if until is not None:
            request['end_ts'] = until
        response = await self.v1PublicGetPublicGetValuations(self.extend(request, params))
        #
        #     {
        #         "id": -1,
        #         "method": "public/get-valuations",
        #         "code": 0,
        #         "result": {
        #             "data": [
        #                 {
        #                     "v": "-0.000001884",
        #                     "t": 1687892400000
        #                 },
        #             ],
        #             "instrument_name": "BTCUSD-PERP"
        #         }
        #     }
        #
        result = self.safe_value(response, 'result', {})
        data = self.safe_value(result, 'data', [])
        marketId = self.safe_string(result, 'instrument_name')
        rates = []
        for i in range(0, len(data)):
            entry = data[i]
            timestamp = self.safe_integer(entry, 't')
            rates.append({
                'info': entry,
                'symbol': self.safe_symbol(marketId, market),
                'fundingRate': self.safe_number(entry, 'v'),
                'timestamp': timestamp,
                'datetime': self.iso8601(timestamp),
            })
        sorted = self.sort_by(rates, 'timestamp')
        return self.filter_by_symbol_since_limit(sorted, market['symbol'], since, limit)

    async def fetch_position(self, symbol: str, params={}):
        """
        fetch data on a single open contract trade position
        :see: https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-get-positions
        :param str symbol: unified market symbol of the market the position is held in
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `position structure <https://docs.ccxt_zagrava.com/#/?id=position-structure>`
        """
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'instrument_name': market['id'],
        }
        response = await self.v1PrivatePostPrivateGetPositions(self.extend(request, params))
        #
        #     {
        #         "id": 1688015952050,
        #         "method": "private/get-positions",
        #         "code": 0,
        #         "result": {
        #             "data": [
        #                 {
        #                     "account_id": "ce075bef-b600-4277-bd6e-ff9007251e63",
        #                     "quantity": "0.0001",
        #                     "cost": "3.02392",
        #                     "open_pos_cost": "3.02392",
        #                     "open_position_pnl": "-0.0010281328",
        #                     "session_pnl": "-0.0010281328",
        #                     "update_timestamp_ms": 1688015919091,
        #                     "instrument_name": "BTCUSD-PERP",
        #                     "type": "PERPETUAL_SWAP"
        #                 }
        #             ]
        #         }
        #     }
        #
        result = self.safe_dict(response, 'result', {})
        data = self.safe_list(result, 'data', [])
        return self.parse_position(self.safe_dict(data, 0), market)

    async def fetch_positions(self, symbols: Strings = None, params={}):
        """
        fetch all open positions
        :see: https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-get-positions
        :param str[]|None symbols: list of unified market symbols
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: a list of `position structure <https://docs.ccxt_zagrava.com/#/?id=position-structure>`
        """
        await self.load_markets()
        symbols = self.market_symbols(symbols)
        request: dict = {}
        market = None
        if symbols is not None:
            symbol = None
            if isinstance(symbols, list):
                symbolsLength = len(symbols)
                if symbolsLength > 1:
                    raise BadRequest(self.id + ' fetchPositions() symbols argument cannot contain more than 1 symbol')
                symbol = symbols[0]
            else:
                symbol = symbols
            market = self.market(symbol)
            request['instrument_name'] = market['id']
        response = await self.v1PrivatePostPrivateGetPositions(self.extend(request, params))
        #
        #     {
        #         "id": 1688015952050,
        #         "method": "private/get-positions",
        #         "code": 0,
        #         "result": {
        #             "data": [
        #                 {
        #                     "account_id": "ce075bef-b600-4277-bd6e-ff9007251e63",
        #                     "quantity": "0.0001",
        #                     "cost": "3.02392",
        #                     "open_pos_cost": "3.02392",
        #                     "open_position_pnl": "-0.0010281328",
        #                     "session_pnl": "-0.0010281328",
        #                     "update_timestamp_ms": 1688015919091,
        #                     "instrument_name": "BTCUSD-PERP",
        #                     "type": "PERPETUAL_SWAP"
        #                 }
        #             ]
        #         }
        #     }
        #
        responseResult = self.safe_value(response, 'result', {})
        positions = self.safe_value(responseResult, 'data', [])
        result = []
        for i in range(0, len(positions)):
            entry = positions[i]
            marketId = self.safe_string(entry, 'instrument_name')
            marketInner = self.safe_market(marketId, None, None, 'contract')
            result.append(self.parse_position(entry, marketInner))
        return self.filter_by_array_positions(result, 'symbol', None, False)

    def parse_position(self, position: dict, market: Market = None):
        #
        #     {
        #         "account_id": "ce075bef-b600-4277-bd6e-ff9007251e63",
        #         "quantity": "0.0001",
        #         "cost": "3.02392",
        #         "open_pos_cost": "3.02392",
        #         "open_position_pnl": "-0.0010281328",
        #         "session_pnl": "-0.0010281328",
        #         "update_timestamp_ms": 1688015919091,
        #         "instrument_name": "BTCUSD-PERP",
        #         "type": "PERPETUAL_SWAP"
        #     }
        #
        marketId = self.safe_string(position, 'instrument_name')
        market = self.safe_market(marketId, market, None, 'contract')
        symbol = self.safe_symbol(marketId, market, None, 'contract')
        timestamp = self.safe_integer(position, 'update_timestamp_ms')
        amount = self.safe_string(position, 'quantity')
        return self.safe_position({
            'info': position,
            'id': None,
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'hedged': None,
            'side': 'buy' if Precise.string_gt(amount, '0') else 'sell',
            'contracts': Precise.string_abs(amount),
            'contractSize': market['contractSize'],
            'entryPrice': None,
            'markPrice': None,
            'notional': None,
            'leverage': None,
            'collateral': self.safe_number(position, 'open_pos_cost'),
            'initialMargin': self.safe_number(position, 'cost'),
            'maintenanceMargin': None,
            'initialMarginPercentage': None,
            'maintenanceMarginPercentage': None,
            'unrealizedPnl': self.safe_number(position, 'open_position_pnl'),
            'liquidationPrice': None,
            'marginMode': None,
            'percentage': None,
            'marginRatio': None,
            'stopLossPrice': None,
            'takeProfitPrice': None,
        })

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

    def params_to_string(self, object, level):
        maxLevel = 3
        if level >= maxLevel:
            return str(object)
        if isinstance(object, str):
            return object
        returnString = ''
        paramsKeys = None
        if isinstance(object, list):
            paramsKeys = object
        else:
            sorted = self.keysort(object)
            paramsKeys = list(sorted.keys())
        for i in range(0, len(paramsKeys)):
            key = paramsKeys[i]
            returnString += key
            value = object[key]
            if value == 'None':
                returnString += 'None'
            elif isinstance(value, list):
                for j in range(0, len(value)):
                    returnString += self.params_to_string(value[j], level + 1)
            else:
                returnString += str(value)
        return returnString

    async def close_position(self, symbol: str, side: OrderSide = None, params={}) -> Order:
        """
        closes open positions for a market
        :see: https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-close-position
        :param str symbol: Unified ccxt_zagrava market symbol
        :param str [marginMode]: not used by cryptocom.closePositions
        :param str [side]: not used by cryptocom.closePositions
        :param dict [params]: extra parameters specific to the okx api endpoint
         *
         * EXCHANGE SPECIFIC PARAMETERS
        :param str [params.type]: LIMIT or MARKET
        :param number [params.price]: for limit orders only
        :returns dict[]: `A list of position structures <https://docs.ccxt_zagrava.com/#/?id=position-structure>`
        """
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'instrument_name': market['id'],
            'type': 'MARKET',
        }
        type = self.safe_string_upper(params, 'type')
        price = self.safe_string(params, 'price')
        if type is not None:
            request['type'] = type
        if price is not None:
            request['price'] = self.price_to_precision(market['symbol'], price)
        response = await self.v1PrivatePostPrivateClosePosition(self.extend(request, params))
        #
        #    {
        #        "id" : 1700830813298,
        #        "method" : "private/close-position",
        #        "code" : 0,
        #        "result" : {
        #            "client_oid" : "179a909d-5614-655b-0d0e-9e85c9a25c85",
        #            "order_id" : "6142909897021751347"
        #        }
        #    }
        #
        result = self.safe_dict(response, 'result')
        return self.parse_order(result, market)

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        type = self.safe_string(api, 0)
        access = self.safe_string(api, 1)
        url = self.urls['api'][type] + '/' + path
        query = self.omit(params, self.extract_params(path))
        if access == 'public':
            if query:
                url += '?' + self.urlencode(query)
        else:
            self.check_required_credentials()
            nonce = str(self.nonce())
            requestParams = self.extend({}, params)
            paramsKeys = list(requestParams.keys())
            strSortKey = self.params_to_string(requestParams, 0)
            payload = path + nonce + self.apiKey + strSortKey + nonce
            signature = self.hmac(self.encode(payload), self.encode(self.secret), hashlib.sha256)
            paramsKeysLength = len(paramsKeys)
            body = self.json({
                'id': nonce,
                'method': path,
                'params': params,
                'api_key': self.apiKey,
                'sig': signature,
                'nonce': nonce,
            })
            # fix issue https://github.com/ccxt_zagrava/ccxt_zagrava/issues/11179
            # php always encodes dictionaries
            # if an array is empty, php will put it in square brackets
            # python and js will put it in curly brackets
            # the code below checks and replaces those brackets in empty requests
            if paramsKeysLength == 0:
                paramsString = '{}'
                arrayString = '[]'
                body = body.replace(arrayString, paramsString)
            headers = {
                'Content-Type': 'application/json',
            }
        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):
        errorCode = self.safe_string(response, 'code')
        if errorCode != '0':
            feedback = self.id + ' ' + body
            self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback)
            raise ExchangeError(self.id + ' ' + body)
        return None
