# -*- 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.coinlist import ImplicitAPI
import hashlib
import math
from ccxt_zagrava.base.types import Account, Balances, Currencies, Currency, Int, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, TradingFees, Transaction, TransferEntry, TransferEntries
from typing import List
from ccxt_zagrava.base.errors import ExchangeError
from ccxt_zagrava.base.errors import AuthenticationError
from ccxt_zagrava.base.errors import PermissionDenied
from ccxt_zagrava.base.errors import ArgumentsRequired
from ccxt_zagrava.base.errors import BadRequest
from ccxt_zagrava.base.errors import BadSymbol
from ccxt_zagrava.base.errors import InsufficientFunds
from ccxt_zagrava.base.errors import InvalidAddress
from ccxt_zagrava.base.errors import InvalidOrder
from ccxt_zagrava.base.errors import OrderNotFound
from ccxt_zagrava.base.errors import NotSupported
from ccxt_zagrava.base.errors import OnMaintenance
from ccxt_zagrava.base.decimal_to_precision import TICK_SIZE
from ccxt_zagrava.base.precise import Precise


class coinlist(Exchange, ImplicitAPI):

    def describe(self):
        return self.deep_extend(super(coinlist, self).describe(), {
            'id': 'coinlist',
            'name': 'Coinlist',
            'countries': ['US'],  # United States
            'version': 'v1',
            'rateLimit': 300,  # 1000 per 5 minutes
            'certified': False,
            'pro': False,
            'has': {
                'CORS': None,
                'spot': True,
                'margin': False,
                'swap': False,
                'future': False,
                'option': False,
                'addMargin': False,
                'cancelAllOrders': True,
                'cancelOrder': True,
                'cancelOrders': True,
                'closeAllPositions': False,
                'closePosition': False,
                'createDepositAddress': False,
                'createOrder': True,
                'createPostOnlyOrder': True,
                'createReduceOnlyOrder': False,
                'createStopLimitOrder': True,
                'createStopMarketOrder': True,
                'createStopOrder': True,
                'deposit': False,
                'editOrder': True,
                'fetchAccounts': True,
                'fetchBalance': True,
                'fetchBidsAsks': False,
                'fetchBorrowInterest': False,
                'fetchBorrowRateHistories': False,
                'fetchBorrowRateHistory': False,
                'fetchCanceledOrders': True,
                'fetchClosedOrder': False,
                'fetchClosedOrders': True,
                'fetchCrossBorrowRate': False,
                'fetchCrossBorrowRates': False,
                'fetchCurrencies': True,
                'fetchDeposit': False,
                'fetchDepositAddress': False,
                'fetchDepositAddresses': False,
                'fetchDepositAddressesByNetwork': False,
                'fetchDeposits': False,
                'fetchDepositsWithdrawals': True,
                'fetchDepositWithdrawFee': False,
                'fetchDepositWithdrawFees': False,
                'fetchFundingHistory': False,
                'fetchFundingRate': False,
                'fetchFundingRateHistory': False,
                'fetchFundingRates': False,
                'fetchIndexOHLCV': False,
                'fetchIsolatedBorrowRate': False,
                'fetchIsolatedBorrowRates': False,
                'fetchL3OrderBook': False,
                'fetchLedger': True,
                'fetchLeverage': False,
                'fetchLeverageTiers': False,
                'fetchMarketLeverageTiers': False,
                'fetchMarkets': True,
                'fetchMarkOHLCV': False,
                'fetchMyTrades': True,
                'fetchOHLCV': True,
                'fetchOpenInterestHistory': False,
                'fetchOpenOrder': False,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'fetchOrderBook': True,
                'fetchOrderBooks': False,
                'fetchOrders': True,
                'fetchOrderTrades': True,
                'fetchPosition': False,
                'fetchPositionHistory': False,
                'fetchPositionMode': False,
                'fetchPositions': False,
                'fetchPositionsForSymbol': False,
                'fetchPositionsHistory': False,
                'fetchPositionsRisk': False,
                'fetchPremiumIndexOHLCV': False,
                'fetchStatus': False,
                'fetchTicker': True,
                'fetchTickers': True,
                'fetchTime': True,
                'fetchTrades': True,
                'fetchTradingFee': False,
                'fetchTradingFees': True,
                'fetchTradingLimits': False,
                'fetchTransactionFee': False,
                'fetchTransactionFees': False,
                'fetchTransactions': True,
                'fetchTransfers': True,
                'fetchWithdrawal': False,
                'fetchWithdrawals': False,
                'fetchWithdrawalWhitelist': False,
                'reduceMargin': False,
                'repayCrossMargin': False,
                'repayIsolatedMargin': False,
                'setLeverage': False,
                'setMargin': False,
                'setMarginMode': False,
                'setPositionMode': False,
                'signIn': False,
                'transfer': True,
                'withdraw': True,
                'ws': False,
            },
            'timeframes': {
                '1m': '1m',
                '5m': '5m',
                '30m': '30m',
            },
            'urls': {
                'logo': 'https://github-production-user-asset-6210df.s3.amazonaws.com/1294454/281108917-eff2ae1d-ce8a-4b2a-950d-8678b12da965.jpg',
                'api': {
                    'public': 'https://trade-api.coinlist.co',
                    'private': 'https://trade-api.coinlist.co',
                },
                'www': 'https://coinlist.co',
                'doc': [
                    'https://trade-docs.coinlist.co',
                ],
                'fees': 'https://coinlist.co/fees',
            },
            'api': {
                'public': {
                    'get': {
                        'v1/symbols': 1,
                        'v1/symbols/summary': 1,
                        'v1/symbols/{symbol}': 1,  # not unified
                        'v1/symbols/{symbol}/summary': 1,
                        'v1/symbols/{symbol}/book': 1,
                        'v1/symbols/{symbol}/quote': 1,  # not unified
                        'v1/symbols/{symbol}/candles': 1,
                        'v1/symbols/{symbol}/auctions': 1,
                        'v1/symbols/{symbol}/auctions/{auction_code}': 1,  # not unified
                        'v1/time': 1,
                        'v1/assets': 1,
                        'v1/leaderboard': 1,
                        'v1/affiliate/{competition_code}': 1,
                        'v1/competition/{competition_id}': 1,
                    },
                },
                'private': {
                    'get': {
                        'v1/fees': 1,
                        'v1/accounts': 1,
                        'v1/accounts/{trader_id}': 1,  # not unified
                        'v1/accounts/{trader_id}/alias': 1,
                        'v1/accounts/{trader_id}/ledger': 1,
                        'v1/accounts/{trader_id}/wallets': 1,  # not unified
                        'v1/accounts/{trader_id}/wallet-ledger': 1,
                        'v1/accounts/{trader_id}/ledger-summary': 1,  # not unified
                        'v1/keys': 1,  # not unified
                        'v1/fills': 1,
                        'v1/orders': 1,
                        'v1/orders/{order_id}': 1,
                        'v1/reports': 1,  # not unified
                        'v1/balances': 1,
                        'v1/transfers': 1,
                        'v1/user': 1,  # not unified
                        'v1/credits': 1,  # not unified
                        'v1/positions': 1,
                        'v1/accounts/{trader_id}/competitions': 1,
                    },
                    'post': {
                        'v1/keys': 1,  # not unified
                        'v1/orders': 1,
                        'v1/orders/cancel-all-after': 1,  # not unified
                        'v1/reports': 1,  # not unified
                        'v1/transfers/to-wallet': 1,
                        'v1/transfers/from-wallet': 1,
                        'v1/transfers/internal-transfer': 1,
                        'v1/transfers/withdrawal-request': 1,
                        'v1/orders/bulk': 1,  # not unified
                        'v1/accounts/{trader_id}/competitions': 1,
                        'v1/accounts/{trader_id}/create-competition': 1,
                    },
                    'patch': {
                        'v1/orders/{order_id}': 1,
                        'v1/orders/bulk': 1,  # not unified
                    },
                    'delete': {
                        'v1/keys/{key}': 1,  # not unified
                        'v1/orders': 1,
                        'v1/orders/{order_id}': 1,
                        'v1/orders/bulk': 1,
                    },
                },
            },
            'fees': {
                'trading': {
                    'feeSide': 'get',
                    'tierBased': True,
                    'percentage': True,
                    'taker': self.parse_number('0.0045'),
                    'maker': self.parse_number('0.0025'),
                    'tiers': {
                        'taker': [
                            [self.parse_number('0'), self.parse_number('0.0045')],
                            [self.parse_number('20000'), self.parse_number('0.003')],
                            [self.parse_number('50000'), self.parse_number('0.0025')],
                            [self.parse_number('100000'), self.parse_number('0.002')],
                            [self.parse_number('500000'), self.parse_number('0.0018')],
                            [self.parse_number('750000'), self.parse_number('0.0018')],
                            [self.parse_number('1000000'), self.parse_number('0.0016')],
                            [self.parse_number('2500000'), self.parse_number('0.0013')],
                            [self.parse_number('5000000'), self.parse_number('0.0012')],
                            [self.parse_number('10000000'), self.parse_number('0.001')],
                            [self.parse_number('50000000'), self.parse_number('0.0005')],
                            [self.parse_number('100000000'), self.parse_number('0.0005')],
                        ],
                        'maker': [
                            [self.parse_number('0'), self.parse_number('0.0025')],
                            [self.parse_number('20000'), self.parse_number('0.0025')],
                            [self.parse_number('50000'), self.parse_number('0.0025')],
                            [self.parse_number('100000'), self.parse_number('0.002')],
                            [self.parse_number('500000'), self.parse_number('0.0015')],
                            [self.parse_number('750000'), self.parse_number('0.0012')],
                            [self.parse_number('1000000'), self.parse_number('0.001')],
                            [self.parse_number('2500000'), self.parse_number('0.0008')],
                            [self.parse_number('5000000'), self.parse_number('0.0007')],
                            [self.parse_number('10000000'), self.parse_number('0.0006')],
                            [self.parse_number('50000000'), self.parse_number('0.0000')],
                            [self.parse_number('100000000'), self.parse_number('0.00')],
                        ],
                    },
                },
            },
            'precisionMode': TICK_SIZE,
            # exchange-specific options
            'options': {
                'accountsByType': {
                    'CoinList Pro': 'trading',
                    'CoinList Pro trading account': 'trading',
                    'Pro': 'trading',
                    'pro': 'trading',
                    'trade': 'trading',
                    'trading': 'trading',
                    'CoinList': 'funding',
                    'CoinList wallet': 'funding',
                    'Wallet': 'funding',
                    'wallet': 'funding',
                    'fund': 'funding',
                    'funding': 'funding',
                },
            },
            'exceptions': {
                # https://trade-docs.coinlist.co/?javascript--nodejs#message-codes
                'exact': {
                    'AUTH_SIG_INVALID': AuthenticationError,  # {"status":400,"message":"invalid signature","message_code":"AUTH_SIG_INVALID"}
                    'DENIED_MAINTENANCE': OnMaintenance,  # The system is under active maintenance.
                    'ORDER_REJECT_BAD_STATUS': InvalidOrder,  # The order has a status that makes it not cancelable or modifyable.
                    'ORDER_REJECT_INVALID_POST_ONLY': InvalidOrder,
                    'ORDER_REJECT_INVALID_CLOSE_ONLY': InvalidOrder,
                    'ORDER_REJECT_POST_ONLY_REQUIRED': InvalidOrder,  # The market currently allows only post-only orders.
                    'ORDER_REJECT_FROZEN_ORDER': InvalidOrder,  # This operation is currently not allowed on self order at self time.
                    'ORDER_REJECT_LIMIT_PRICE_PROTECTION_VIOLATION': InvalidOrder,  # The limit price violates the price protection range for self symbol.
                    'ORDER_REJECT_CLOSED': NotSupported,  # The market is closed for order operations.
                    'ORDER_REJECT_MAX_ORDERS': BadRequest,  # You have violated the 25 orders per symbol limit.
                    'ORDER_REJECT_NOT_FOUND': OrderNotFound,  # The order to modify or cancel was not found.
                    'ORDER_REJECT_PARSE_ERROR': BadRequest,  # The request failed to parse. Check data types.(strings vs. numbers)
                    'ORDER_REJECT_PRICE_INVALID': InvalidOrder,  # Prices must be positive and aligned with the tick size defined for the symbol.
                    'ORDER_REJECT_QUANTITY_ZERO': InvalidOrder,  # Quantity may not be zero.
                    'ORDER_REJECT_TOKEN_LIMIT': InsufficientFunds,  # Your current token balance is not enough to back self order.
                    'ORDER_REJECT_TOKEN_LIMIT_OTHER': InvalidOrder,
                    'ORDER_REJECT_SELF_TRADE': InvalidOrder,  # This order would have been involved in a self-trade.
                    'ORDER_VALIDATE_BAD_SIZE_ALIGNMENT': InvalidOrder,  # {"message":"size is not aligned to 0.0001 minimum increment","message_code":"ORDER_VALIDATE_BAD_SIZE_ALIGNMENT","message_details":{"minimum_size_increment":"0.0001"}}
                    'ORDER_VALIDATE_BAD_TICK_ALIGNMENT': InvalidOrder,  # {"message":"price is not aligned to 0.01 tick size","message_code":"ORDER_VALIDATE_BAD_TICK_ALIGNMENT","message_details":{"minimum_price_increment":{"s":1,"e":-2,"c":[1000000000000]}}}
                    'ORDER_VALIDATE_SYMBOL_NOT_FOUND': BadSymbol,  # {"message":"symbol asdfsdfs not found","message_code":"ORDER_VALIDATE_SYMBOL_NOT_FOUND"}
                    'TRANSFERS_WITHDRAWAL_REQUEST_TOO_LARGE': InsufficientFunds,  # {"message":"Withdrawal request too large. 0.000000000000000000 ETH available for withdrawal.","message_code":"TRANSFERS_WITHDRAWAL_REQUEST_TOO_LARGE","message_details":{"token_code":"ETH","amount":"0.010000000000000000","withdrawable_balance":"0.000000000000000000"}}
                    'WITHDRAWAL_REQUEST_NOT_ALLOWED': PermissionDenied,  # {"message":"Withdrawal from CoinList not allowed for trader.","message_code":"WITHDRAWAL_REQUEST_NOT_ALLOWED","message_details":{"asset":"USDT","amount":"5","trader_id":"9c6f737e-a829-4843-87b1-b1ce86f2853b","destination_address":"0x9050dfA063D1bE7cA711c750b18D51fDD13e90Ee"}}
                },
                'broad': {
                    'A destinationAddress is required for non-USD withdrawals': InvalidAddress,  # {"status":400,"message":"400 - {\"message\":\"A destinationAddress is required for non-USD withdrawals.\"}"}
                    'fails to match the JsonSchema date-time format pattern': BadRequest,  # {"status":401,"message":"\"end_time\" with value \"1698862680000\" fails to match the JsonSchema date-time format pattern"}
                    'is required': ArgumentsRequired,  # {"status":400,"message":"\"type\" is required"}
                    'must be a string': BadRequest,  # {"status":400,"message":"\"destination_address\" must be a string"}
                    'must be a valid GUID': BadRequest,  # {"status":400,"message":"\"order_id\" must be a valid GUID"}
                    'must be greater than or equal to': BadRequest,  # {"status":401,"message":"\"count\" must be greater than or equal to 1"}
                    'must be less than or equal to': BadRequest,  # {"status":401,"message":"\"count\" must be less than or equal to 500"}
                    'must be one of': BadRequest,  # {"status":401,"message":"\"granularity\" must be one of [1m, 5m, 30m]"}
                    'Symbol not found': BadSymbol,  # {"message":"Symbol not found: {symbol}"}
                },
            },
        })

    def calculate_rate_limiter_cost(self, api, method, path, params, config={}):
        if isinstance(params, list):
            length = len(params)
            return int(math.ceil(length / 2))
        return 1

    async def fetch_time(self, params={}):
        """
        fetches the current integer timestamp in milliseconds from the exchange server
        :see: https://trade-docs.coinlist.co/?javascript--nodejs#get-system-time
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns int: the current integer timestamp in milliseconds from the exchange server
        """
        response = await self.publicGetV1Time(params)
        #
        #     {
        #         "epoch": 1698087996.039,
        #         "iso": "2023-10-23T19:06:36.039Z"
        #     }
        #
        string = self.safe_string(response, 'iso')
        return self.parse8601(string)

    async def fetch_currencies(self, params={}) -> Currencies:
        """
        fetches all available currencies on an exchange
        :see: https://trade-docs.coinlist.co/?javascript--nodejs#list-supported-assets
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an associative dictionary of currencies
        """
        response = await self.publicGetV1Assets(params)
        #
        #     {
        #         "assets": [
        #             {
        #                 "asset": "AAVE",
        #                 "index_code": ".AAVEUSD",
        #                 "decimal_places": 18,
        #                 "min_withdrawal": "1.0000",
        #                 "is_transferable": True,
        #                 "is_visible": True
        #             },
        #             {
        #                 "asset": "ALGO",
        #                 "index_code": ".ALGOUSD",
        #                 "decimal_places": 6,
        #                 "min_withdrawal": "1.0000",
        #                 "is_transferable": True,
        #                 "is_visible": True
        #             }
        #         ]
        #     }
        #
        currencies = self.safe_value(response, 'assets', [])
        result: dict = {}
        for i in range(0, len(currencies)):
            currency = currencies[i]
            id = self.safe_string(currency, 'asset')
            code = self.safe_currency_code(id)
            isTransferable = self.safe_bool(currency, 'is_transferable', False)
            withdrawEnabled = isTransferable
            depositEnabled = isTransferable
            active = isTransferable
            decimalPlaces = self.safe_string(currency, 'decimal_places')
            precision = self.parse_number(self.parse_precision(decimalPlaces))
            minWithdrawal = self.safe_string(currency, 'min_withdrawal')
            result[code] = {
                'id': id,
                'code': code,
                'name': code,
                'info': currency,
                'active': active,
                'deposit': depositEnabled,
                'withdraw': withdrawEnabled,
                'fee': None,
                'precision': precision,
                'limits': {
                    'amount': {'min': None, 'max': None},
                    'withdraw': {'min': minWithdrawal, 'max': None},
                },
                'networks': {},
            }
        return result

    async def fetch_markets(self, params={}) -> List[Market]:
        """
        retrieves data on all markets for coinlist
        :see: https://trade-docs.coinlist.co/?javascript--nodejs#list-symbols
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: an array of objects representing market data
        """
        response = await self.publicGetV1Symbols(params)
        #
        #     {
        #         "symbols": [
        #             {
        #                 "symbol": "CQT-USDT",
        #                 "base_currency": "CQT",
        #                 "is_trader_geofenced": False,
        #                 "list_time": "2021-06-15T00:00:00.000Z",
        #                 "type": "spot",
        #                 "series_code": "CQT-USDT-SPOT",
        #                 "long_name": "Covalent",
        #                 "asset_class": "CRYPTO",
        #                 "minimum_price_increment": "0.0001",
        #                 "minimum_size_increment": "0.0001",
        #                 "quote_currency": "USDT",
        #                 "index_code": null,
        #                 "price_band_threshold_market": "0.05",
        #                 "price_band_threshold_limit": "0.25",
        #                 "last_price": "0.12160000",
        #                 "fair_price": "0.12300000",
        #                 "index_price": null
        #             },
        #         ]
        #     }
        #
        markets = self.safe_value(response, 'symbols', [])
        return self.parse_markets(markets)

    def parse_market(self, market: dict) -> Market:
        id = self.safe_string(market, 'symbol')
        baseId = self.safe_string(market, 'base_currency')
        quoteId = self.safe_string(market, 'quote_currency')
        base = self.safe_currency_code(baseId)
        quote = self.safe_currency_code(quoteId)
        amountPrecision = self.safe_string(market, 'minimum_size_increment')
        pricePrecision = self.safe_string(market, 'minimum_price_increment')
        created = self.safe_string(market, 'list_time')
        return {
            'id': id,
            'symbol': base + '/' + quote,
            'base': base,
            'quote': quote,
            'settle': None,
            'baseId': baseId,
            'quoteId': quoteId,
            'settleId': None,
            'type': 'spot',
            'spot': True,
            'margin': False,
            'swap': False,
            'future': False,
            'option': False,
            'active': True,
            'contract': False,
            'linear': None,
            'inverse': None,
            'contractSize': None,
            'expiry': None,
            'expiryDatetime': None,
            'strike': None,
            'optionType': None,
            'precision': {
                'amount': self.parse_number(amountPrecision),
                'price': self.parse_number(pricePrecision),
            },
            'limits': {
                'leverage': {
                    'min': None,
                    'max': None,
                },
                'amount': {
                    'min': None,
                    'max': None,
                },
                'price': {
                    'min': None,
                    'max': None,
                },
                'cost': {
                    'min': None,
                    'max': None,
                },
            },
            'created': self.parse8601(created),
            'info': market,
        }

    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://trade-docs.coinlist.co/?javascript--nodejs#get-symbol-summaries
        :param str[] [symbols]: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a dictionary of `ticker structures <https://docs.ccxt_zagrava.com/#/?id=ticker-structure>`
        """
        await self.load_markets()
        request: dict = {}
        tickers = await self.publicGetV1SymbolsSummary(self.extend(request, params))
        #
        #     {
        #         "MATIC-USD": {
        #             "type":"spot",
        #             "last_price":"0.60990000",
        #             "lowest_ask":"0.61190000",
        #             "highest_bid":"0.60790000",
        #             "last_trade": {
        #                 "price":"0.60000000",
        #                 "volume":"2.0000",
        #                 "imbalance":"198.0000",
        #                 "logicalTime":"2023-10-22T23:02:25.000Z",
        #                 "auctionCode":"MATIC-USD-2023-10-22T23:02:25.000Z"
        #         },
        #             "volume_base_24h":"34.0555",
        #             "volume_quote_24h":"19.9282",
        #             "price_change_percent_24h":"7.50925436",
        #             "highest_price_24h":"0.68560000",
        #             "lowest_price_24h":"0.55500000"
        #         },
        #     }
        #
        return self.parse_tickers(tickers, symbols, params)

    async def fetch_ticker(self, symbol: str, params={}) -> Ticker:
        """
        fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
        :see: https://trade-docs.coinlist.co/?javascript--nodejs#get-market-summary
        :param str symbol: unified symbol of the market to fetch the ticker for
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `ticker structure <https://docs.ccxt_zagrava.com/#/?id=ticker-structure>`
        """
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        ticker = await self.publicGetV1SymbolsSymbolSummary(self.extend(request, params))
        #
        #     {
        #         "type":"spot",
        #         "last_price":"31125.00000000",
        #         "lowest_ask":"31349.99000000",
        #         "highest_bid":"30900.00000000",
        #         "last_trade": {
        #             "price":"31000.00000000",
        #             "volume":"0.0003",
        #             "imbalance":"0.0000",
        #             "logicalTime":"2023-10-23T16:57:15.000Z",
        #             "auctionCode":"BTC-USDT-2023-10-23T16:57:15.000Z"
        #         },
        #         "volume_base_24h":"0.3752",
        #         "volume_quote_24h":"11382.7181",
        #         "price_change_percent_24h":"3.66264694",
        #         "highest_price_24h":"31225.12000000",
        #         "lowest_price_24h":"29792.81000000"
        #     }
        #
        return self.parse_ticker(ticker, market)

    def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
        #
        #     {
        #         "type":"spot",
        #         "last_price":"0.60990000",
        #         "lowest_ask":"0.61190000",
        #         "highest_bid":"0.60790000",
        #         "last_trade": {
        #             "price":"0.60000000",
        #             "volume":"2.0000",
        #             "imbalance":"198.0000",
        #             "logicalTime":"2023-10-22T23:02:25.000Z",
        #             "auctionCode":"MATIC-USD-2023-10-22T23:02:25.000Z"
        #          },
        #         "volume_base_24h":"34.0555",
        #         "volume_quote_24h":"19.9282",
        #         "price_change_percent_24h":"7.50925436",
        #         "highest_price_24h":"0.68560000",
        #         "lowest_price_24h":"0.55500000"
        #     }
        #
        lastTrade = self.safe_value(ticker, 'last_trade', {})
        timestamp = self.parse8601(self.safe_string(lastTrade, 'logicalTime'))
        bid = self.safe_string(ticker, 'highest_bid')
        ask = self.safe_string(ticker, 'lowest_ask')
        baseVolume = self.safe_string(ticker, 'volume_base_24h')
        quoteVolume = self.safe_string(ticker, 'volume_quote_24h')
        high = self.safe_string(ticker, 'highest_price_24h')
        low = self.safe_string(ticker, 'lowest_price_24h')
        close = self.safe_string(ticker, 'last_price')
        changePcnt = self.safe_string(ticker, 'price_change_percent_24h')
        return self.safe_ticker({
            'symbol': market['symbol'],
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'open': None,
            'high': high,
            'low': low,
            'close': close,
            'bid': bid,
            'bidVolume': None,
            'ask': ask,
            'askVolume': None,
            'vwap': None,
            'previousClose': None,
            'change': None,
            'percentage': changePcnt,
            'average': None,
            'baseVolume': baseVolume,
            'quoteVolume': quoteVolume,
            'info': ticker,
        }, market)

    async def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
        """
        fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
        :see: https://trade-docs.coinlist.co/?javascript--nodejs#get-order-book-level-2
        :param str symbol: unified symbol of the market to fetch the order book for
        :param int [limit]: the maximum amount of order book entries to return(default 100, max 200)
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: A dictionary of `order book structures <https://docs.ccxt_zagrava.com/#/?id=order-book-structure>` indexed by market symbols
        """
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        response = await self.publicGetV1SymbolsSymbolBook(self.extend(request, params))
        #
        #     {
        #         "bids": [
        #             ["30900.00000000", "0.0001"],
        #             ["30664.21000000", "0.0172"],
        #             ["30664.20000000", "0.0906"],
        #         ],
        #         "asks": [
        #             ["31349.99000000", "0.0003"],
        #             ["31350.00000000", "0.0023"],
        #             ["31359.33000000", "0.0583"],
        #         ],
        #         "after_auction_code": "BTC-USDT-2023-10-23T18:40:51.000Z",
        #         "call_time": "2023-10-23T18:40:51.068Z",
        #         "logical_time": "2023-10-23T18:40:51.000Z"
        #     }
        #
        logical_time = self.parse8601(self.safe_string(response, 'logical_time'))
        orderbook = self.parse_order_book(response, symbol, logical_time)
        orderbook['nonce'] = None
        return orderbook

    async def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
        """
        fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
        :see: https://trade-docs.coinlist.co/?javascript--nodejs#get-candles
        :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]: the latest time in ms to fetch entries for
        :returns int[][]: A list of candles ordered, open, high, low, close, volume
        """
        await self.load_markets()
        market = self.market(symbol)
        granularity = self.safe_string(self.timeframes, timeframe)
        request: dict = {
            'symbol': market['id'],
            'granularity': granularity,
        }
        if since is not None:
            request['start_time'] = self.iso8601(since)
            if limit is not None:
                duration = self.parse_timeframe(timeframe) * 1000
                request['end_time'] = self.iso8601(self.sum(since, duration * (limit)))
            else:
                request['end_time'] = self.iso8601(self.milliseconds())
        until = self.safe_integer(params, 'until')
        if until is not None:
            params = self.omit(params, ['until'])
            request['end_time'] = self.iso8601(until)
        response = await self.publicGetV1SymbolsSymbolCandles(self.extend(request, params))
        #
        #     {
        #         "candles": [
        #             [
        #                 "2023-10-17T15:00:00.000Z",
        #                 "28522.96000000",
        #                 "28522.96000000",
        #                 "28522.96000000",
        #                 "28522.96000000",
        #                 "0.1881",
        #                 null
        #             ],
        #             [
        #                 "2023-10-17T15:30:00.000Z",
        #                 "28582.64000000",
        #                 "28582.64000000",
        #                 "28582.64000000",
        #                 "28582.64000000",
        #                 "0.0050",
        #                 null
        #             ]
        #         ]
        #     }
        #
        candles = self.safe_list(response, 'candles', [])
        return self.parse_ohlcvs(candles, market, timeframe, since, limit)

    def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
        #
        #     [
        #         "2023-10-17T15:30:00.000Z",
        #         "28582.64000000",
        #         "28582.64000000",
        #         "28582.64000000",
        #         "28582.64000000",
        #         "0.0050",
        #         null
        #     ]
        #
        return [
            self.parse8601(self.safe_string(ohlcv, 0)),
            self.safe_number(ohlcv, 1),
            self.safe_number(ohlcv, 2),
            self.safe_number(ohlcv, 3),
            self.safe_number(ohlcv, 4),
            self.safe_number(ohlcv, 5),
        ]

    async def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
        """
        get the list of most recent trades for a particular symbol
        :see: https://trade-docs.coinlist.co/?javascript--nodejs#list-auctions
        :param str symbol: unified symbol of the market to fetch trades for
        :param int [since]: timestamp in ms of the earliest trade to fetch
        :param int [limit]: the maximum amount of trades to fetch(default 200, max 500)
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: the latest time in ms to fetch entries for
        :returns Trade[]: a list of `trade structures <https://docs.ccxt_zagrava.com/#/?id=public-trades>`
        """
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        if since is not None:
            request['start_time'] = self.iso8601(since)
        if limit is not None:
            request['count'] = min(limit, 500)
        until = self.safe_integer(params, 'until')
        if until is not None:
            params = self.omit(params, ['until'])
            request['end_time'] = self.iso8601(until)
        response = await self.publicGetV1SymbolsSymbolAuctions(self.extend(request, params))
        #
        #     {
        #         "auctions": [
        #             {
        #                 "symbol":"BTC-USDT",
        #                 "auction_code":"BTC-USDT-2023-10-01T08:05:56.000Z",
        #                 "price":"27241.53000000",
        #                 "volume":"0.0052",
        #                 "imbalance":"-1.0983",
        #                 "logical_time":"2023-10-01T08:05:56.000Z",
        #                 "call_time":"2023-10-01T08:05:56.068Z"
        #             },
        #             {
        #                 "symbol":"BTC-USDT",
        #                 "auction_code":"BTC-USDT-2023-10-01T08:09:09.000Z",
        #                 "price":"27236.83000000",
        #                 "volume":"0.0283",
        #                 "imbalance":"-1.0754",
        #                 "logical_time":"2023-10-01T08:09:09.000Z",
        #                 "call_time":"2023-10-01T08:09:09.078Z"
        #             }
        #         ]
        #     }
        #
        auctions = self.safe_list(response, 'auctions', [])
        return self.parse_trades(auctions, market, since, limit)

    def parse_trade(self, trade: dict, market: Market = None) -> Trade:
        #
        # fetchTrades
        #     {
        #         "symbol": "BTC-USDT",
        #         "auction_code": "BTC-USDT-2023-10-01T08:05:56.000Z",
        #         "price": "27241.53000000",
        #         "volume": "0.0052",
        #         "imbalance": "-1.0983",
        #         "logical_time": "2023-10-01T08:05:56.000Z",
        #         "call_time": "2023-10-01T08:05:56.068Z"
        #     }
        #
        # fetchMyTrades
        #     {
        #         "symbol": "ETH-USDT",
        #         "auction_code": "ETH-USDT-2023-10-20T13:22:14.000Z",
        #         "order_id": "83ed365f-497d-433b-96c1-9d08c1a12842",
        #         "quantity": "0.0008",
        #         "price": "1615.24000000",
        #         "fee": "0.005815",
        #         "fee_type": "taker",
        #         "fee_currency": "USDT",
        #         "logical_time": "2023-10-20T13:22:14.000Z"
        #     }
        #
        marketId = self.safe_string(trade, 'symbol')
        market = self.safe_market(marketId, market)
        symbol = market['symbol']
        id = self.safe_string(trade, 'auction_code')
        timestamp = self.parse8601(self.safe_string(trade, 'logical_time'))
        priceString = self.safe_string(trade, 'price')
        amountString = self.safe_string_2(trade, 'volume', 'quantity')
        order = self.safe_string(trade, 'order_id')
        fee = None
        side = None
        feeCost = self.safe_string(trade, 'fee')
        if feeCost is not None:
            # only in fetchMyTrades
            amountIsNegative = Precise.string_lt(amountString, '0')
            if amountIsNegative:
                side = 'sell'
                amountString = Precise.string_neg(amountString)
            else:
                side = 'buy'
            fee = {
                'cost': feeCost,
                'currency': self.safe_string(trade, 'fee_currency'),
            }
        else:
            imbalance = self.safe_string(trade, 'imbalance')
            if Precise.string_lt(imbalance, '0'):
                side = 'buy'
            else:
                side = 'sell'
        takerOrMaker = self.safe_string(trade, 'fee_type')
        return self.safe_trade({
            'id': id,
            'order': order,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': symbol,
            'type': None,
            'side': side,
            'takerOrMaker': takerOrMaker,
            'price': priceString,
            'amount': amountString,
            'cost': None,
            'fee': fee,
            'info': trade,
        }, market)

    async def fetch_trading_fees(self, params={}) -> TradingFees:
        """
        fetch the trading fees for multiple markets
        :see: https://trade-docs.coinlist.co/?javascript--nodejs#list-fees
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a dictionary of `fee structures <https://docs.ccxt_zagrava.com/#/?id=fee-structure>` indexed by market symbols
        """
        await self.load_markets()
        response = await self.privateGetV1Fees(params)
        #
        #     {
        #         fees_by_symbols: {
        #             'BTC-USD,BTC-USDT,ETH-USD,ETH-USDT,ETH-BTC,AAVE-USD,AAVE-USDT,ALGO-USD,ALGO-USDT,AVAX-USD,AVAX-USDT,BICO-USD,BICO-USDT,BLD-USD,BLD-USDT,BTRST-USDT,BZZ-USDT,CELO-USD,CELO-BTC,CFG-USD,CFG-USDT,CLV-USDT,COMP-USD,COMP-USDT,CYBER-USDT,CQT-USDT,CSPR-USD,CSPR-USDT,CUSD-USD,CUSD-USDC,DOGE-USD,DOGE-USDT,DOT-USD,DOT-USDT,EFI-USDT,FIL-USD,FIL-USDT,FLOW-USD,FLOW-USDT,GAL-USD,GAL-USDT,GODS-USDT,GOG-USDT,HMT-USD,HMT-USDT,ICP-USD,ICP-USDT,IMX-USD,IMX-USDT,LINK-USD,LINK-USDT,MATIC-USD,MATIC-USDT,MINA-USD,MINA-USDT,MKR-USD,MKR-USDT,NEON-USDT,NYM-USD,NYM-USDT,OCEAN-USD,OXT-USD,ROSE-USD,ROSE-USDT,SKL-USD,SKL-USDT,SOL-USD,SOL-USDT,STX-USDT,SUI-USDT,T-USDT,UNI-USD,UNI-USDT,USDT-USD,VEGA-USDT,WAXL-USD,WAXL-USDT,WBTC-BTC,WCFG-USD,WCFG-USDT,XTZ-USD': {
        #                 base: {
        #                     fees: {maker: '0', taker: '0.0045', liquidation: '0'},
        #                     floors: {maker: null, taker: null}
        #                 },
        #                 volume_tier_1: {
        #                     fees: {maker: '0', taker: '0.003', liquidation: '0'},
        #                     floors: {maker: null, taker: null}
        #                 },
        #                 volume_tier_2: {
        #                     fees: {maker: '0', taker: '0.0025', liquidation: '0'},
        #                     floors: {maker: null, taker: null}
        #                 },
        #                 volume_tier_3: {
        #                     fees: {maker: '0', taker: '0.002', liquidation: '0'},
        #                     floors: {maker: null, taker: null}
        #                 },
        #                 volume_tier_4: {
        #                     fees: {maker: '0', taker: '0.0018', liquidation: '0'},
        #                     floors: {maker: null, taker: null}
        #                 },
        #                 volume_tier_5: {
        #                     fees: {maker: '0', taker: '0.0018', liquidation: '0'},
        #                     floors: {maker: null, taker: null}
        #                 },
        #                 volume_tier_6: {
        #                     fees: {maker: '0', taker: '0.0016', liquidation: '0'},
        #                     floors: {maker: null, taker: null}
        #                 },
        #                 volume_tier_7: {
        #                     fees: {maker: '0', taker: '0.0013', liquidation: '0'},
        #                     floors: {maker: null, taker: null}
        #                 },
        #                 volume_tier_8: {
        #                     fees: {maker: '0', taker: '0.0012', liquidation: '0'},
        #                     floors: {maker: null, taker: null}
        #                 },
        #                 volume_tier_9: {
        #                     fees: {maker: '0', taker: '0.001', liquidation: '0'},
        #                     floors: {maker: null, taker: null}
        #                 }
        #                 volume_tier_10: {
        #                     fees: {maker: '0', taker: '0.0005', liquidation: '0'},
        #                     floors: {maker: null, taker: null}
        #                 },
        #                 volume_tier_11: {
        #                     fees: {maker: '0', taker: '0.0005', liquidation: '0'},
        #                     floors: {maker: null, taker: null}
        #                 },
        #             }
        #         }
        #     }
        #
        fees = self.safe_value(response, 'fees_by_symbols', {})
        result: dict = {}
        groupsOfSymbols = list(fees.keys())
        for i in range(0, len(groupsOfSymbols)):
            group = groupsOfSymbols[i]
            feeTiers = self.safe_value(fees, group, {})
            tiers = self.parse_fee_tiers(feeTiers)
            firstTier = self.safe_value(feeTiers, 'base', {})
            firstTierFees = self.safe_value(firstTier, 'fees', {})
            ids = group.split(',')
            for j in range(0, len(ids)):
                id = ids[j]
                market = self.safe_market(id)
                symbol = market['symbol']
                info: dict = {}
                info[group] = feeTiers
                result[symbol] = {
                    'info': info,
                    'symbol': symbol,
                    'maker': self.safe_number(firstTierFees, 'maker'),
                    'taker': self.safe_number(firstTierFees, 'taker'),
                    'percentage': True,
                    'tierBased': True,
                    'tiers': tiers,
                }
        return result

    def parse_fee_tiers(self, feeTiers, market: Market = None):
        #
        #     base: {
        #         fees: {maker: '0', taker: '0.0045', liquidation: '0'},
        #         floors: {maker: null, taker: null}
        #     },
        #     volume_tier_1: {
        #         fees: {maker: '0', taker: '0.003', liquidation: '0'},
        #         floors: {maker: null, taker: null}
        #     },
        #     volume_tier_2: {
        #         fees: {maker: '0', taker: '0.0025', liquidation: '0'},
        #         floors: {maker: null, taker: null}
        #     },
        #     volume_tier_3: {
        #         fees: {maker: '0', taker: '0.002', liquidation: '0'},
        #         floors: {maker: null, taker: null}
        #     },
        #     volume_tier_4: {
        #         fees: {maker: '0', taker: '0.0018', liquidation: '0'},
        #         floors: {maker: null, taker: null}
        #     },
        #     volume_tier_5: {
        #         fees: {maker: '0', taker: '0.0018', liquidation: '0'},
        #         floors: {maker: null, taker: null}
        #     },
        #     volume_tier_6: {
        #         fees: {maker: '0', taker: '0.0016', liquidation: '0'},
        #         floors: {maker: null, taker: null}
        #     },
        #     volume_tier_7: {
        #         fees: {maker: '0', taker: '0.0013', liquidation: '0'},
        #         floors: {maker: null, taker: null}
        #     },
        #     volume_tier_8: {
        #         fees: {maker: '0', taker: '0.0012', liquidation: '0'},
        #         floors: {maker: null, taker: null}
        #     },
        #     volume_tier_9: {
        #         fees: {maker: '0', taker: '0.001', liquidation: '0'},
        #         floors: {maker: null, taker: null}
        #     }
        #     volume_tier_10: {
        #         fees: {maker: '0', taker: '0.0005', liquidation: '0'},
        #         floors: {maker: null, taker: null}
        #     },
        #     volume_tier_11: {
        #         fees: {maker: '0', taker: '0.0005', liquidation: '0'},
        #         floors: {maker: null, taker: null}
        #     },
        #
        takerFees = []
        makerFees = []
        keys = list(feeTiers.keys())
        keysLength = len(keys)
        if keysLength > 0:
            for i in range(0, keysLength):
                key = keys[i]
                tier = self.safe_value(feeTiers, key, {})
                tierFees = self.safe_value(tier, 'fees', {})
                taker = self.safe_string(tierFees, 'taker')
                maker = self.safe_string(tierFees, 'maker')
                makerFees.append([None, self.parse_number(maker)])
                takerFees.append([None, self.parse_number(taker)])
            takerFees = self.sort_by(takerFees, 1, True)
            makerFees = self.sort_by(makerFees, 1, True)
            firstTier = self.safe_dict(takerFees, 0, [])
            exchangeFees = self.safe_dict(self, 'fees', {})
            exchangeFeesTrading = self.safe_dict(exchangeFees, 'trading', {})
            exchangeFeesTradingTiers = self.safe_dict(exchangeFeesTrading, 'tiers', {})
            exchangeFeesTradingTiersTaker = self.safe_list(exchangeFeesTradingTiers, 'taker', [])
            exchangeFeesTradingTiersMaker = self.safe_list(exchangeFeesTradingTiers, 'maker', [])
            exchangeFeesTradingTiersTakerLength = len(exchangeFeesTradingTiersTaker)
            firstTierLength = len(firstTier)
            if (keysLength == exchangeFeesTradingTiersTakerLength) and (firstTierLength > 0):
                for i in range(0, keysLength):
                    takerFees[i][0] = exchangeFeesTradingTiersTaker[i][0]
                    makerFees[i][0] = exchangeFeesTradingTiersMaker[i][0]
        return {
            'maker': makerFees,
            'taker': takerFees,
        }

    async def fetch_accounts(self, params={}) -> List[Account]:
        """
        fetch all the accounts associated with a profile
        :see: https://trade-docs.coinlist.co/?javascript--nodejs#list-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.privateGetV1Accounts(params)
        #
        #     {
        #         "accounts": [
        #             {
        #                 "trader_id": "b18507ce-7d55-4bf1-b12a-0ccca5b90936",
        #                 "name": "string"
        #             }
        #         ]
        #     }
        #
        accounts = self.safe_value(response, 'accounts', [])
        return self.parse_accounts(accounts, params)

    def parse_account(self, account):
        #
        #     {
        #         "trader_id": "b18507ce-7d55-4bf1-b12a-0ccca5b90936",
        #         "name": "string"
        #     }
        #
        return {
            'id': self.safe_string(account, 'trader_id'),
            'type': 'trading',
            'code': None,
            'info': account,
        }

    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://trade-docs.coinlist.co/?javascript--nodejs#list-balances
        :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.privateGetV1Balances(params)
        return self.parse_balance(response)

    def parse_balance(self, response) -> Balances:
        #
        #     {
        #         "asset_balances": {
        #             "BTC": "0.00308696",
        #             "ETH": "20.000000000000000000"
        #         },
        #         "asset_holds": {
        #             "BTC": "0.00000000",
        #             "ETH": "1.000000000000000000"
        #         },
        #         "net_liquidation_value_usd": "string"
        #     }
        #
        result: dict = {
            'info': response,
            'timestamp': None,
            'datetime': None,
        }
        totalBalances = self.safe_value(response, 'asset_balances', {})
        usedBalances = self.safe_value(response, 'asset_holds', {})
        currencyIds = list(totalBalances.keys())
        for i in range(0, len(currencyIds)):
            currencyId = currencyIds[i]
            code = self.safe_currency_code(currencyId)
            account = self.account()
            account['total'] = self.safe_string(totalBalances, currencyId)
            account['used'] = self.safe_string(usedBalances, currencyId, '0')
            result[code] = account
        return self.safe_balance(result)

    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://trade-docs.coinlist.co/?javascript--nodejs#list-fills
        :param str symbol: unified market symbol
        :param int [since]: the earliest time in ms to fetch trades for
        :param int [limit]: the maximum number of trades structures to retrieve(default 200, max 500)
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: the latest time in ms to fetch entries for
        :returns Trade[]: a list of `trade structures <https://docs.ccxt_zagrava.com/#/?id=trade-structure>`
        """
        await self.load_markets()
        request: dict = {}
        market = None
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        if since is not None:
            request['start_time'] = self.iso8601(since)
        if limit is not None:
            request['count'] = limit
        until = self.safe_integer(params, 'until')
        if until is not None:
            params = self.omit(params, ['until'])
            request['end_time'] = self.iso8601(until)
        response = await self.privateGetV1Fills(self.extend(request, params))
        #
        #     {
        #         "fills": [
        #             {
        #                 "symbol": "ETH-USDT",
        #                 "auction_code": "ETH-USDT-2023-10-20T13:16:30.000Z",
        #                 "order_id": "39911d5f-c789-4a7d-ad34-820a804d1da6",
        #                 "quantity": "-0.0009",
        #                 "price": "1608.83000000",
        #                 "fee": "0.006516",
        #                 "fee_type": "taker",
        #                 "fee_currency": "USDT",
        #                 "logical_time": "2023-10-20T13:16:30.000Z"
        #             },
        #             {
        #                 "symbol": "ETH-USDT",
        #                 "auction_code": "ETH-USDT-2023-10-20T13:22:14.000Z",
        #                 "order_id": "83ed365f-497d-433b-96c1-9d08c1a12842",
        #                 "quantity": "0.0008",
        #                 "price": "1615.24000000",
        #                 "fee": "0.005815",
        #                 "fee_type": "taker",
        #                 "fee_currency": "USDT",
        #                 "logical_time": "2023-10-20T13:22:14.000Z"
        #             },
        #         ]
        #     }
        #
        fills = self.safe_list(response, 'fills', [])
        return self.parse_trades(fills, market, since, limit)

    async def fetch_order_trades(self, id: str, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
        """
        fetch all the trades made from a single order
        :see: https://trade-docs.coinlist.co/?javascript--nodejs#list-fills
        :param str id: order id
        :param str symbol: unified market symbol
        :param int [since]: the earliest time in ms to fetch trades for
        :param int [limit]: the maximum number of trades to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: a list of `trade structures <https://docs.ccxt_zagrava.com/#/?id=trade-structure>`
        """
        request: dict = {
            'order_id': id,
        }
        return await self.fetch_my_trades(symbol, since, limit, self.extend(request, params))

    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://trade-docs.coinlist.co/?javascript--nodejs#list-orders
        :param str symbol: unified market symbol of the market orders were made in
        :param int [since]: the earliest time in ms to fetch orders for
        :param int [limit]: the maximum number of order structures to retrieve(default 200, max 500)
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: the latest time in ms to fetch entries for
        :param string|str[] [params.status]: the status of the order - 'accepted', 'done', 'canceled', 'rejected', 'pending'(default ['accepted', 'done', 'canceled', 'rejected', 'pending'])
        :returns Order[]: a list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        await self.load_markets()
        status = self.safe_string(params, 'status')
        if status is None:
            status = ['accepted', 'done', 'canceled', 'rejected', 'pending']
        request: dict = {
            'status': status,
        }
        market = None
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        if since is not None:
            request['start_time'] = self.iso8601(since)
        if limit is not None:
            request['count'] = limit
        until = self.safe_integer(params, 'until')
        if until is not None:
            params = self.omit(params, ['until'])
            request['end_time'] = self.iso8601(until)
        response = await self.privateGetV1Orders(self.extend(request, params))
        #
        #     {
        #         "orders":[
        #             {
        #                 "order_id":"913ea6e7-9fc9-43fb-9db1-f195d3baa93f",
        #                 "price":"35800.00000000",
        #                 "stop_price":null,
        #                 "cost":"0.00000000",
        #                 "fill_fees":"0.00000000",
        #                 "trader_id":"9c6f737e-a829-4843-87b1-b1ce86f2853b",
        #                 "status":"accepted",
        #                 "epoch_timestamp":"2023-10-26T08:20:56.307Z",
        #                 "origin":"web",
        #                 "self_trade_prevention":null,
        #                 "client_id":null,
        #                 "created_at":"2023-10-26T08:20:56.307Z",
        #                 "symbol":"BTC-USDT",
        #                 "size":"0.0003",
        #                 "side":"sell",
        #                 "type":"limit",
        #                 "post_only":false,
        #                 "size_filled":"0.0000"
        #             }
        #         ]
        #     }
        #
        orders = self.safe_list(response, 'orders', [])
        return self.parse_orders(orders, market, since, limit)

    async def fetch_order(self, id: str, symbol: Str = None, params={}):
        """
        fetches information on an order made by the user
        :see: https://trade-docs.coinlist.co/?javascript--nodejs#get-specific-order-by-id
        :param int|str id: order id
        :param str symbol: not used by coinlist fetchOrder()
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: An `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        await self.load_markets()
        request: dict = {
            'order_id': id,
        }
        response = await self.privateGetV1OrdersOrderId(self.extend(request, params))
        #
        #     {
        #         "order_id": "93101167-9065-4b9c-b98b-5d789a3ed9fe",
        #         "client_id": "string",
        #         "symbol": "string",
        #         "type": "market",
        #         "side": "buy",
        #         "size": "string",
        #         "price": "string",
        #         "stop_price": "string",
        #         "stop_trigger": "last",
        #         "self_trade_prevention": "keep-newest",
        #         "average_fill_price": "string",
        #         "fill_fees": "string",
        #         "size_filled": "string",
        #         "created_at": "2019-08-24T14:15:22Z",
        #         "epoch_timestamp": "2019-08-24T14:15:22Z",
        #         "post_only": True,
        #         "peg_price_type": "trailing-stop",
        #         "peg_offset_value": "string",
        #         "origin": "web",
        #         "status": "pending"
        #     }
        #
        return self.parse_order(response)

    async def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
        """
        fetch all unfilled currently open orders
        :see: https://trade-docs.coinlist.co/?javascript--nodejs#list-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(default 200, max 500)
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: the latest time in ms to fetch entries for
        :returns Order[]: a list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        await self.load_markets()
        request: dict = {
            'status': 'accepted',
        }
        return self.fetch_orders(symbol, since, limit, self.extend(request, params))

    async def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
        """
        fetches information on multiple closed orders made by the user
        :see: https://trade-docs.coinlist.co/?javascript--nodejs#list-orders
        :param str symbol: unified market symbol of the market orders were made in
        :param int [since]: the earliest time in ms to fetch orders for
        :param int [limit]: the maximum number of closed order structures to retrieve(default 200, max 500)
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: the latest time in ms to fetch entries for
        :returns Order[]: a list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        await self.load_markets()
        request: dict = {
            'status': 'done',
        }
        return self.fetch_orders(symbol, since, limit, self.extend(request, params))

    async def fetch_canceled_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        fetches information on multiple canceled orders made by the user
        :see: https://trade-docs.coinlist.co/?javascript--nodejs#list-orders
        :param str symbol: unified market symbol of the market orders were made in
        :param int [since]: the earliest time in ms to fetch orders for
        :param int [limit]: the maximum number of canceled order structures to retrieve(default 200, max 500)
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: the latest time in ms to fetch entries for
        :returns dict: a list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        await self.load_markets()
        request: dict = {
            'status': 'canceled',
        }
        return self.fetch_orders(symbol, since, limit, self.extend(request, params))

    async def cancel_all_orders(self, symbol: Str = None, params={}):
        """
        cancel open orders of market
        :see: https://trade-docs.coinlist.co/?javascript--nodejs#cancel-all-orders
        :param str symbol: unified market symbol
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: a list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        await self.load_markets()
        market = None
        request: dict = {}
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        response = await self.privateDeleteV1Orders(self.extend(request, params))
        #
        #     {
        #         "message": "Order cancellation request received.",
        #         "timestamp": "2023-10-26T10:29:28.652Z"
        #     }
        #
        orders = [response]
        return self.parse_orders(orders, market)

    async def cancel_order(self, id: str, symbol: Str = None, params={}):
        """
        cancels an open order
        :see: https://trade-docs.coinlist.co/?javascript--nodejs#cancel-specific-order-by-id
        :param str id: order id
        :param str symbol: not used by coinlist cancelOrder()
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: An `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        await self.load_markets()
        request: dict = {
            'order_id': id,
        }
        response = await self.privateDeleteV1OrdersOrderId(self.extend(request, params))
        #
        #     {
        #         "message": "Cancel order request received.",
        #         "order_id": "d36e7588-6525-485c-b768-8ad8b3f745f9",
        #         "timestamp": "2023-10-26T14:36:37.559Z"
        #     }
        #
        return self.parse_order(response)

    async def cancel_orders(self, ids, symbol: Str = None, params={}):
        """
        cancel multiple orders
        :see: https://trade-docs.coinlist.co/?javascript--nodejs#cancel-specific-orders
        :param str[] ids: order ids
        :param str symbol: not used by coinlist cancelOrders()
        :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()
        params = ids
        response = await self.privateDeleteV1OrdersBulk(params)
        #
        #    {
        #        "message": "Cancel order requests received.",
        #        "order_ids": [
        #            "ff132955-43bc-4fe5-9d9c-5ba226cc89a0"
        #        ],
        #        "timestamp": "2024-06-01T02:32:30.305Z"
        #    }
        #
        orderIds = self.safe_list(response, 'order_ids', [])
        orders = []
        datetime = self.safe_string(response, 'timestamp')
        for i in range(0, len(orderIds)):
            orders.append(self.safe_order({
                'info': orderIds[i],
                'id': orderIds[i],
                'lastUpdateTimestamp': self.parse8601(datetime),
            }))
        return orders

    async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
        """
        create a trade order
        :see: https://trade-docs.coinlist.co/?javascript--nodejs#create-new-order
        :param str symbol: unified symbol of the market to create an order in
        :param str type: 'market' or 'limit' or 'stop_market' or 'stop_limit' or 'take_market' or 'take_limit'
        :param str side: 'buy' or 'sell'
        :param float amount: how much of currency you want to trade in units of base currency
        :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param bool [params.postOnly]: if True, the order will only be posted to the order book and not executed immediately(default False)
        :param float [params.triggerPrice]: only for the 'stop_market', 'stop_limit', 'take_market' or 'take_limit' orders(the price at which an order is triggered)
        :param str [params.clientOrderId]: client order id(default None)
        :returns dict: an `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
            'type': type,
            'side': side,
            'size': self.amount_to_precision(symbol, amount),
        }
        isMarket = False
        if (type == 'limit') or (type == 'stop_limit') or (type == 'take_limit'):
            if price is None:
                raise ArgumentsRequired(self.id + ' createOrder() requires a price argument for a ' + type + ' order')
            request['price'] = self.price_to_precision(symbol, price)
        else:
            isMarket = True
        postOnly = None
        postOnly, params = self.handle_post_only(isMarket, False, params)
        if postOnly:
            request['post_only'] = True
        triggerPrice = self.safe_number_n(params, ['triggerPrice', 'trigger_price', 'stopPrice', 'stop_price'])
        if triggerPrice is not None:
            params = self.omit(params, ['triggerPrice', 'trigger_price', 'stopPrice'])
            request['stop_price'] = self.price_to_precision(symbol, triggerPrice)
            if type == 'market':
                request['type'] = 'stop_market'
            elif type == 'limit':
                request['type'] = 'stop_limit'
        elif (type == 'stop_market') or (type == 'stop_limit') or (type == 'take_market') or (type == 'take_limit'):
            raise ArgumentsRequired(self.id + ' createOrder() requires a stopPrice parameter for stop-loss and take-profit orders')
        clientOrderId = self.safe_string_2(params, 'clientOrderId', 'client_id')
        if clientOrderId is not None:
            request['client_id'] = clientOrderId
            params = self.omit(params, ['clientOrderId', 'client_id'])
        response = await self.privatePostV1Orders(self.extend(request, params))
        #
        #     {
        #         "message": "New order request received.",
        #         "order": {
        #             "symbol": "BTC-USDT",
        #             "type": "market",
        #             "side": "sell",
        #             "size": "0.0003",
        #             "order_id": "cad67c0f-9aec-4ac8-ac03-aaf5db299ff7",
        #             "trader_id": "9c6f737e-a829-4843-87b1-b1ce86f2853b"
        #         },
        #         "timestamp": "2023-10-26T11:30:55.376Z"
        #     }
        #
        order = self.safe_dict(response, 'order', {})
        return self.parse_order(order, market)

    async def edit_order(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: Num = None, price: Num = None, params={}):
        """
        create a trade order
        :see: https://trade-docs.coinlist.co/?javascript--nodejs#modify-existing-order
        :param str symbol: unified symbol of the market to create an order in
        :param str type: 'market' or 'limit' or 'stop_market' or 'stop_limit' or 'take_market' or 'take_limit'
        :param str side: 'buy' or 'sell'
        :param float amount: how much of currency you want to trade in units of base currency
        :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        await self.load_markets()
        if amount is None:
            raise ArgumentsRequired(self.id + ' editOrder() requires an amount argument')
        market = self.market(symbol)
        request: dict = {
            'order_id': id,
            'type': type,
            'side': side,
            'size': self.amount_to_precision(symbol, amount),
        }
        if price is not None:
            request['price'] = self.price_to_precision(symbol, price)
        response = await self.privatePatchV1OrdersOrderId(self.extend(request, params))
        return self.parse_order(response, market)

    def parse_order(self, order: dict, market: Market = None) -> Order:
        #
        # fetchOrder
        #     {
        #         "order_id": "913ea6e7-9fc9-43fb-9db1-f195d3baa93f",
        #         "price": "35800.00000000",
        #         "stop_price":null,
        #         "cost": "0.00000000",
        #         "fill_fees": "0.00000000",
        #         "trader_id": "9c6f737e-a829-4843-87b1-b1ce86f2853b",
        #         "status": "canceled",
        #         "epoch_timestamp": "2023-10-26T08:20:56.307Z",
        #         "origin": "web",
        #         "self_trade_prevention":null,
        #         "client_id":null,
        #         "symbol": "BTC-USDT",
        #         "size": "0.0003",
        #         "side": "sell",
        #         "type": "limit",
        #         "post_only":false,
        #         "size_filled": "0.0000"
        #     }
        #
        # fetchOrders
        #     {
        #         "order_id":"913ea6e7-9fc9-43fb-9db1-f195d3baa93f",
        #         "price":"35800.00000000",
        #         "stop_price":null,
        #         "cost":"0.00000000",
        #         "fill_fees":"0.00000000",
        #         "trader_id":"9c6f737e-a829-4843-87b1-b1ce86f2853b",
        #         "status":"accepted",
        #         "epoch_timestamp":"2023-10-26T08:20:56.307Z",
        #         "origin":"web",
        #         "self_trade_prevention":null,
        #         "client_id":null,
        #         "created_at":"2023-10-26T08:20:56.307Z",
        #         "symbol":"BTC-USDT",
        #         "size":"0.0003",
        #         "side":"sell",
        #         "type":"limit",
        #         "post_only":false,
        #         "size_filled":"0.0000"
        #     }
        #
        # createOrder
        #     {
        #         "symbol": "BTC-USDT",
        #         "type": "market",
        #         "side": "sell",
        #         "size": "0.0003",
        #         "order_id": "cad67c0f-9aec-4ac8-ac03-aaf5db299ff7",
        #         "trader_id": "9c6f737e-a829-4843-87b1-b1ce86f2853b"
        #     },
        #
        # cancelOrder
        #     {
        #         "message": "Cancel order request received.",
        #         "order_id": "d36e7588-6525-485c-b768-8ad8b3f745f9",
        #         "timestamp": "2023-10-26T14:36:37.559Z"
        #     }
        #
        # cancelOrders
        #     {
        #         "message": "Order cancellation request received.",
        #         "timestamp": "2023-10-26T10:29:28.652Z"
        #     }
        #
        # cancelAllOrders
        #     {
        #         "message": "Order cancellation request received.",
        #         "timestamp": "2023-10-26T10:29:28.652Z"
        #     }
        #
        id = self.safe_string(order, 'order_id')
        marketId = self.safe_string(order, 'symbol')
        market = self.safe_market(marketId, market)
        clientOrderId = self.safe_string(order, 'client_id')
        timestampString = self.safe_string_2(order, 'created_at', 'epoch_timestamp')
        if timestampString is None:
            timestampString = self.safe_string(order, 'timestamp')
        timestamp = self.parse8601(timestampString)
        status = self.parse_order_status(self.safe_string(order, 'status'))
        type = self.parse_order_type(self.safe_string(order, 'type'))
        side = self.safe_string(order, 'side')
        price = self.safe_string(order, 'price')
        stopPrice = self.safe_string(order, 'stop_price')
        average = self.safe_string(order, 'average_fill_price')  # from documentation
        amount = self.safe_string(order, 'size')
        filled = self.safe_string(order, 'size_filled')
        feeCost = self.safe_string(order, 'fill_fees')
        postOnly = self.safe_value(order, 'post_only')
        fee = None
        if feeCost is not None:
            fee = {
                'currency': market['quote'],
                'cost': feeCost,
                'rate': None,
            }
        return self.safe_order({
            'id': id,
            'clientOrderId': clientOrderId,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': None,
            'status': status,
            'symbol': market['symbol'],
            'type': type,
            'timeInForce': 'GTC',
            'side': side,
            'price': price,
            'stopPrice': stopPrice,
            'triggerPrice': stopPrice,
            'average': average,
            'amount': amount,
            'cost': None,
            'filled': filled,
            'remaining': None,
            'fee': fee,
            'trades': None,
            'info': order,
            'postOnly': postOnly,
        }, market)

    def parse_order_status(self, status: Str):
        statuses: dict = {
            'pending': 'open',
            'accepted': 'open',
            'rejected': 'rejected',
            'done': 'closed',
            'canceled': 'canceled',
        }
        return self.safe_string(statuses, status, status)

    def parse_order_type(self, status):
        statuses: dict = {
            'market': 'market',
            'limit': 'limit',
            'stop_market': 'market',
            'stop_limit': 'limit',
            'take_market': 'market',
            'take_limit': 'limit',
        }
        return self.safe_string(statuses, status, status)

    async def transfer(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}) -> TransferEntry:
        """
        transfer currency internally between wallets on the same account
        :see: https://trade-docs.coinlist.co/?javascript--nodejs#transfer-funds-between-entities
        :see: https://trade-docs.coinlist.co/?javascript--nodejs#transfer-funds-from-wallet-to-pro
        :see: https://trade-docs.coinlist.co/?javascript--nodejs#transfer-funds-from-pro-to-wallet
        :param str code: unified currency code
        :param float amount: amount to transfer
        :param str fromAccount: account to transfer from
        :param str toAccount: account to transfer to
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `transfer structure <https://docs.ccxt_zagrava.com/#/?id=transfer-structure>`
        """
        await self.load_markets()
        currency = self.currency(code)
        request: dict = {
            'asset': currency['id'],
            'amount': self.currency_to_precision(code, amount),
        }
        accountsByType = self.safe_value(self.options, 'accountsByType', {})
        fromAcc = self.safe_string(accountsByType, fromAccount, fromAccount)
        toAcc = self.safe_string(accountsByType, toAccount, toAccount)
        response = None
        if (fromAcc == 'funding') and (toAcc == 'trading'):
            response = await self.privatePostV1TransfersFromWallet(self.extend(request, params))
        elif (fromAcc == 'trading') and (toAcc == 'funding'):
            response = await self.privatePostV1TransfersToWallet(self.extend(request, params))
        else:
            request['from_trader_id'] = fromAcc
            request['to_trader_id'] = toAcc
            response = await self.privatePostV1TransfersInternalTransfer(self.extend(request, params))
        #
        # privatePostV1TransfersInternalTransfer
        #     {
        #         "from_trader_id": "1f494ace-b3ed-4324-b202-55526ed06381",
        #         "to_trader_id": "d32c7a40-cc24-44b0-8597-f9edb3da989f",
        #         "asset": "string",
        #         "amount": "string"
        #     }
        #
        # privatePostV1TransfersFromWallet, privatePostV1TransfersToWallet
        #     {
        #         "transfer_id": "bb34f528-d9b0-47c6-b11f-4d4840b86ee3"
        #     }
        #
        transfer = self.parse_transfer(response, currency)
        return transfer

    async def fetch_transfers(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> TransferEntries:
        """
        fetch a history of internal transfers between CoinList.co and CoinList Pro. It does not return external deposits or withdrawals
        :see: https://trade-docs.coinlist.co/?javascript--nodejs#list-transfers
        :param str code: unified currency code
        :param int [since]: the earliest time in ms to fetch transfers for
        :param int [limit]: the maximum number of transfer structures to retrieve(default 200, max 500)
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: the latest time in ms to fetch entries for
        :returns dict[]: a list of `transfer structures <https://docs.ccxt_zagrava.com/#/?id=transfer-structure>`
        """
        await self.load_markets()
        currency = None
        if code is not None:
            currency = self.currency(code)
        request: dict = {}
        if since is not None:
            request['start_time'] = self.iso8601(since)
        if limit is not None:
            request['count'] = limit
        until = self.safe_integer(params, 'until')
        if until is not None:
            params = self.omit(params, ['until'])
            request['end_time'] = self.iso8601(until)
        response = await self.privateGetV1Transfers(self.extend(request, params))
        #
        #     {
        #         "transfers": [
        #             {
        #                 "transfer_id": "2c02db25-e8f2-4271-8222-e110bfd0aa2a",
        #                 "created_at": "2023-10-20T13:15:37.000Z",
        #                 "confirmed_at": "2023-10-20T13:15:37.000Z",
        #                 "asset": "ETH",
        #                 "amount": "0.010000000000000000",
        #                 "status": "confirmed"
        #             },
        #             {
        #                 "transfer_id": "890694db-156c-4e93-a3ef-4db61685aca7",
        #                 "created_at": "2023-10-26T14:32:22.000Z",
        #                 "confirmed_at": "2023-10-26T14:32:22.000Z",
        #                 "asset": "USD",
        #                 "amount": "-3.00",
        #                 "status": "confirmed"
        #             }
        #         ]
        #     }
        #
        transfers = self.safe_list(response, 'transfers', [])
        return self.parse_transfers(transfers, currency, since, limit)

    def parse_transfer(self, transfer: dict, currency: Currency = None) -> TransferEntry:
        #
        # fetchTransfers
        #     {
        #         "transfer_id": "890694db-156c-4e93-a3ef-4db61685aca7",
        #         "created_at": "2023-10-26T14:32:22.000Z",
        #         "confirmed_at": "2023-10-26T14:32:22.000Z",
        #         "asset": "USD",
        #         "amount": "-3.00",
        #         "status": "confirmed"
        #     }
        #
        # transfer - privatePostV1TransfersInternalTransfer
        #     {
        #         "from_trader_id": "1f494ace-b3ed-4324-b202-55526ed06381",
        #         "to_trader_id": "d32c7a40-cc24-44b0-8597-f9edb3da989f",
        #         "asset": "string",
        #         "amount": "string"
        #     }
        #
        # transfer - privatePostV1TransfersFromWallet, privatePostV1TransfersToWallet
        #     {
        #         "transfer_id": "bb34f528-d9b0-47c6-b11f-4d4840b86ee3"
        #     }
        #
        currencyId = self.safe_string(transfer, 'asset')
        confirmedAt = self.safe_string(transfer, 'confirmed_at')
        timetstamp = self.parse8601(confirmedAt)
        status = self.safe_string(transfer, 'status')
        amountString = self.safe_string(transfer, 'amount')
        fromAccount = None
        toAccount = None
        amount = None
        if amountString is not None:
            amountIsNegative = Precise.string_lt(amountString, '0')
            if amountIsNegative:
                fromAccount = 'trading'
                toAccount = 'funding'
                amountString = Precise.string_neg(amountString)
            else:
                fromAccount = 'funding'
                toAccount = 'trading'
            amount = self.parse_number(amountString)
        return {
            'info': transfer,
            'id': self.safe_string(transfer, 'transfer_id'),
            'timestamp': timetstamp,
            'datetime': self.iso8601(timetstamp),
            'currency': self.safe_currency_code(currencyId, currency),
            'amount': amount,
            'fromAccount': fromAccount,
            'toAccount': toAccount,
            'status': self.parse_transfer_status(status),
        }

    def parse_transfer_status(self, status: Str) -> Str:
        statuses: dict = {
            'confirmed': 'ok',
        }
        return self.safe_string(statuses, status, status)

    async def fetch_deposits_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
        """
        fetch history of deposits and withdrawals from external wallets and between CoinList Pro trading account and CoinList wallet
        :see: https://trade-docs.coinlist.co/?javascript--nodejs#get-coinlist-wallet-ledger
        :param str [code]: unified currency code for the currency of the deposit/withdrawals
        :param int [since]: timestamp in ms of the earliest deposit/withdrawal
        :param int [limit]: max number of deposit/withdrawals to return(default 200, max 500)
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a list of `transaction structure <https://docs.ccxt_zagrava.com/#/?id=transaction-structure>`
        """
        if code is None:
            raise ArgumentsRequired(self.id + ' fetchDepositsWithdrawals() requires a code argument')
        traderId = self.safe_string_2(params, 'trader_id', 'traderId')
        if traderId is None:
            raise ArgumentsRequired(self.id + ' fetchDepositsWithdrawals() requires a traderId argument in the params')
        await self.load_markets()
        currency = self.currency(code)
        request: dict = {
            'asset': currency['id'],
            'trader_id': traderId,
        }
        if limit is not None:
            request['count'] = limit
        params = self.omit(params, ['trader_id', 'traderId'])
        response = await self.privateGetV1AccountsTraderIdWalletLedger(self.extend(request, params))
        #
        #     [
        #         {
        #             "id": "2c02db25-e8f2-4271-8222-e110bfd0aa2a",
        #             "asset": "ETH",
        #             "amount": "0.01",
        #             "created_at": "2023-10-20T13:15:37.000Z",
        #             "description": "Transfer to CoinList Pro",
        #             "type": "PRO_TRANSFER",
        #             "delta": "-0.010000000000000000"
        #         },
        #         {
        #             "id": "7139384d-6cec-479e-a19c-d498647ccb47",
        #             "asset": "ETH",
        #             "amount": "0.01",
        #             "created_at": "2023-10-20T13:10:55.000Z",
        #             "description": "CRYPTO_DEPOSIT",
        #             "type": "CRYPTO_DEPOSIT",
        #             "delta": "0.010000000000000000"
        #         },
        #
        #         ...
        #
        #         {
        #             "id": "91bbbb22-5ede-4e9a-81ef-3f9318aa83d2",
        #             "asset": "USDT",
        #             "amount": "4.169654",
        #             "withdrawal_fee_amount": "8.830346000000000000",
        #             "created_at": "2023-10-27T16:14:11.000Z",
        #             "description": "CRYPTO_WITHDRAWAL",
        #             "type": "CRYPTO_WITHDRAWAL",
        #             "delta": "-4.169654000000000000"
        #         },
        #         {
        #             "id": "830261bd-cda9-401f-b6df-105f4da3b37c",
        #             "asset": "USDT",
        #             "amount": "13",
        #             "created_at": "2023-10-27T14:52:05.000Z",
        #             "description": "Transfer from CoinList Pro",
        #             "type": "PRO_TRANSFER",
        #             "delta": "13.000000000000000000"
        #         }
        #     ]
        #
        # coinlist returns both internal transfers and blockchain transactions
        return self.parse_transactions(response, currency, since, limit)

    async def withdraw(self, code: str, amount: float, address: str, tag=None, params={}):
        """
        request a withdrawal from CoinList wallet.(Disabled by default. Contact CoinList to apply for an exception.)
        :see: https://trade-docs.coinlist.co/?javascript--nodejs#request-withdrawal-from-wallet
        :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>`
        """
        await self.load_markets()
        currency = self.currency(code)
        request: dict = {
            'asset': currency['id'],
            'amount': self.currency_to_precision(code, amount),
            'destination_address': address,
        }
        response = await self.privatePostV1TransfersWithdrawalRequest(self.extend(request, params))
        #
        #     {
        #         "transfer_id": "d4a2d8dd-7def-4545-a062-761683b9aa05"
        #     }
        #
        data = self.safe_dict(response, 'data', {})
        return self.parse_transaction(data, currency)

    def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction:
        # withdraw
        #
        #     {
        #         "transfer_id": "d4a2d8dd-7def-4545-a062-761683b9aa05"
        #     }
        #
        # fetchDepositsWithdrawals
        #     {
        #         "id": "91bbbb22-5ede-4e9a-81ef-3f9318aa83d2",
        #         "asset": "USDT",
        #         "amount": "4.169654",
        #         "withdrawal_fee_amount": "8.830346000000000000",
        #         "created_at": "2023-10-27T16:14:11.000Z",
        #         "description": "CRYPTO_WITHDRAWAL",
        #         "type": "CRYPTO_WITHDRAWAL",
        #         "delta": "-4.169654000000000000"
        #     },
        #
        currencyId = self.safe_string(transaction, 'asset')
        code = self.safe_currency_code(currencyId, currency)
        id = self.safe_string_2(transaction, 'id', 'transfer_id')
        amount = self.safe_number(transaction, 'amount')
        timestamp = self.parse8601(self.safe_string(transaction, 'created_at'))
        type = self.safe_string(transaction, 'type', None)
        if type is None:
            type = 'withdrawal'  # None only in withdraw() method
        else:
            type = self.parse_transaction_type(type)
        fee = None
        feeCost = self.safe_string(transaction, 'withdrawal_fee_amount')
        if feeCost is not None:
            fee = {
                'cost': feeCost,
                'currency': code,
            }
        return {
            'info': transaction,
            'id': id,
            'txid': None,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'network': None,
            'addressFrom': None,
            'address': None,
            'addressTo': None,
            'tagFrom': None,
            'tag': None,
            'tagTo': None,
            'type': type,
            'amount': amount,
            'currency': code,
            'status': None,
            'updated': None,
            'fee': fee,
            'comment': self.safe_string(transaction, 'description'),
            'internal': None,
        }

    def parse_transaction_type(self, type):
        types: dict = {
            'CRYPTO_DEPOSIT': 'deposit',
            'CRYPTO_WITHDRAWAL': 'withdrawal',
            'PRO_TRANSFER': 'transfer',
        }
        return self.safe_string(types, type, type)

    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 balance of the user
        :see: https://trade-docs.coinlist.co/?javascript--nodejs#get-account-history
        :param str code: unified currency code, default is None
        :param int [since]: timestamp in ms of the earliest ledger entry, default is None
        :param int [limit]: max number of ledger entrys to return(default 200, max 500)
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: the latest time in ms to fetch entries for
        :returns dict: a `ledger structure <https://docs.ccxt_zagrava.com/#/?id=ledger-structure>`
        """
        traderId = self.safe_string_2(params, 'trader_id', 'traderId')
        if traderId is None:
            raise ArgumentsRequired(self.id + ' fetchLedger() requires a traderId argument in the params')
        await self.load_markets()
        request: dict = {
            'trader_id': traderId,
        }
        currency = None
        if code is not None:
            currency = self.currency(code)
        if since is not None:
            request['start_time'] = self.iso8601(since)
        if limit is not None:
            request['count'] = limit
        until = self.safe_integer(params, 'until')
        if until is not None:
            params = self.omit(params, ['until'])
            request['end_time'] = self.iso8601(until)
        params = self.omit(params, ['trader_id', 'traderId'])
        response = await self.privateGetV1AccountsTraderIdLedger(self.extend(request, params))
        #
        #     {
        #         "transactions": [
        #             {
        #                 "transaction_id": "0288634e-49bd-494d-b04a-18fd1832d394",
        #                 "transaction_type": "XFER",
        #                 "type": "deposit",
        #                 "asset": "ETH",
        #                 "symbol": null,
        #                 "amount": "0.010000000000000000",
        #                 "details": null,
        #                 "created_at": "2023-10-20T13:15:39.443Z"
        #             },
        #             {
        #                 "transaction_id": "47a45928-abcd-4c12-8bd6-587c3028025f",
        #                 "transaction_type": "SWAP",
        #                 "type": "atomic token swap",
        #                 "asset": "USDT",
        #                 "symbol": "ETH-USDT",
        #                 "amount": "1.447947",
        #                 "details": null,
        #                 "created_at": "2023-10-20T13:16:30.373Z"
        #             },
        #             {
        #                 "transaction_id": "1ffe3a54-916e-41f0-b957-3a01309eb009",
        #                 "transaction_type": "FEE",
        #                 "type": "fee",
        #                 "asset": "USDT",
        #                 "symbol": "ETH-USDT",
        #                 "amount": "-0.006516",
        #                 "details": {
        #                     "fee_details": [
        #                         {
        #                             "insurance_fee": "0",
        #                             "order_id": "39911d5f-c789-4a7d-ad34-820a804d1da6",
        #                             "fee_type": "taker",
        #                             "fee_currency": "USDT"
        #                         }
        #                     ]
        #                 },
        #                 "created_at": "2023-10-20T13:16:30.373Z"
        #             },
        #             {
        #                 "transaction_id": "3930e8a3-2218-481f-8c3c-2219287e205e",
        #                 "transaction_type": "SWAP",
        #                 "type": "atomic token swap",
        #                 "asset": "ETH",
        #                 "symbol": "ETH-USDT",
        #                 "amount": "-0.000900000000000000",
        #                 "details": null,
        #                 "created_at": "2023-10-20T13:16:30.373Z"
        #             },
        #             {
        #                 "transaction_id": "a6c65cb3-95d0-44e2-8202-f70581d6e55c",
        #                 "transaction_type": "XFER",
        #                 "type": "withdrawal",
        #                 "asset": "USD",
        #                 "symbol": null,
        #                 "amount": "-3.00",
        #                 "details": null,
        #                 "created_at": "2023-10-26T14:32:24.887Z"
        #             }
        #         ]
        #     }
        #
        ledger = self.safe_value(response, 'transactions', [])
        return self.parse_ledger(ledger, currency, since, limit)

    def parse_ledger_entry(self, item: dict, currency: Currency = None):
        #
        # deposit transaction from wallet(funding) to pro(trading)
        #     {
        #         "transaction_id": "0288634e-49bd-494d-b04a-18fd1832d394",
        #         "transaction_type": "XFER",
        #         "type": "deposit",
        #         "asset": "ETH",
        #         "symbol": null,
        #         "amount": "0.010000000000000000",
        #         "details": null,
        #         "created_at": "2023-10-20T13:15:39.443Z"
        #     }
        #
        # withdrawal transaction from pro(trading) to wallet(funding)
        #     {
        #         "transaction_id": "a6c65cb3-95d0-44e2-8202-f70581d6e55c",
        #         "transaction_type": "XFER",
        #         "type": "withdrawal",
        #         "asset": "USD",
        #         "symbol": null,
        #         "amount": "-3.00",
        #         "details": null,
        #         "created_at": "2023-10-26T14:32:24.887Z"
        #     }
        #
        # sell trade
        #     {
        #         "transaction_id": "47a45928-abcd-4c12-8bd6-587c3028025f",
        #         "transaction_type": "SWAP",
        #         "type": "atomic token swap",
        #         "asset": "USDT",
        #         "symbol": "ETH-USDT",
        #         "amount": "1.447947",
        #         "details": null,
        #         "created_at": "2023-10-20T13:16:30.373Z"
        #     }
        #
        # buy trade
        #     {
        #         "transaction_id": "46d20a93-45c4-4441-a238-f89602eb8c8c",
        #         "transaction_type": "SWAP",
        #         "type": "atomic token swap",
        #         "asset": "ETH",
        #         "symbol": "ETH-USDT",
        #         "amount": "0.000800000000000000",
        #         "details": null,
        #         "created_at": "2023-10-20T13:22:14.256Z"
        #     },
        #
        #  fee
        #     {
        #         "transaction_id": "57fd526c-36b1-4721-83ce-42aadcb1e953",
        #         "transaction_type": "FEE",
        #         "type": "fee",
        #         "asset": "USDT",
        #         "symbol": "BTC-USDT",
        #         "amount": "-0.047176",
        #         "details": {
        #             "fee_details": [
        #                 {
        #                     "insurance_fee": "0",
        #                     "order_id": "c0bc33cd-eeb9-40a0-ab5f-2d99f323ef58",
        #                     "fee_type": "taker",
        #                     "fee_currency": "USDT"
        #                 }
        #             ]
        #         },
        #         "created_at": "2023-10-25T16:46:24.294Z"
        #     }
        #
        id = self.safe_string(item, 'transaction_id')
        createdAt = self.safe_string(item, 'created_at')
        timestamp = self.parse8601(createdAt)
        amount = self.safe_string(item, 'amount')
        amountIsNegative = Precise.string_lt(amount, '0')
        direction = None
        if amountIsNegative:
            direction = 'out'
            amount = Precise.string_neg(amount)
        else:
            direction = 'in'
        currencyId = self.safe_string(item, 'asset')
        code = self.safe_currency_code(currencyId, currency)
        type = self.parse_ledger_entry_type(self.safe_string(item, 'type'))
        return {
            'info': item,
            'id': id,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'direction': direction,
            'account': 'trading',
            'referenceId': None,
            'referenceAccount': None,
            'type': type,
            'currency': code,
            'amount': self.parse_number(amount),
            'before': None,
            'after': None,
            'status': 'ok',
            'fee': None,
        }

    def parse_ledger_entry_type(self, type):
        types: dict = {
            'atomic token swap': 'trade',
            'fee': 'fee',
            'deposit': 'transfer',
            'withdrawal': 'transfer',
        }
        return self.safe_string(types, type, type)

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        request = self.omit(params, self.extract_params(path))
        endpoint = '/' + self.implode_params(path, params)
        url = self.urls['api'][api] + endpoint
        isBulk = isinstance(params, list)
        query: Str = None
        if not isBulk:
            query = self.urlencode(request)
        if api == 'private':
            self.check_required_credentials()
            timestamp = str(self.seconds())
            auth = timestamp + method + endpoint
            if (method == 'POST') or (method == 'PATCH') or isBulk:
                body = self.json(request)
                auth += body
            elif query is not None and len(query) != 0:
                auth += '?' + query
                url += '?' + query
            signature = self.hmac(self.encode(auth), self.base64_to_binary(self.secret), hashlib.sha256, 'base64')
            headers = {
                'CL-ACCESS-KEY': self.apiKey,
                'CL-ACCESS-SIG': signature,
                'CL-ACCESS-TIMESTAMP': timestamp,
                'Content-Type': 'application/json',
            }
        elif query is not None and len(query) != 0:
            url += '?' + query
        return {'url': url, 'method': method, 'body': body, 'headers': headers}

    def handle_errors(self, code: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
        if response is None:
            # In some cases the exchange returns 202 Accepted for bad orders.
            # The body of that response contains order_id of the order.
            # Some bad orders will get status 'rejected' and could be fetched later(by using fetchOrders() or fetchOrder(order_id)).
            # While others don't get any status, they simply disappear, but the response is still 202 Accepted and contains their order_id.
            # When using fechOrder(order_id) for such disappeared orders, the exchange returns an empty response with code 404.
            if (code == 404) and (url.find('/orders/') >= 0) and (method == 'GET'):
                parts = url.split('/orders/')
                orderId = self.safe_string(parts, 1)
                raise OrderNotFound(self.id + ' order ' + orderId + ' not found(or rejected on the exchange side)')
            return None
        responseCode = self.safe_string(response, 'status')
        messageCode = self.safe_string(response, 'message_code')
        if (messageCode is not None) or ((responseCode is not None) and (code != 200) and (code != 202) and (responseCode != '200') and (responseCode != '202')):
            feedback = self.id + ' ' + body
            message = self.safe_string(response, 'message')
            self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback)
            self.throw_exactly_matched_exception(self.exceptions['exact'], messageCode, feedback)
            raise ExchangeError(feedback)
        return None
