# -*- 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.kraken import ImplicitAPI
import hashlib
from ccxt_zagrava.base.types import Balances, Currencies, Currency, IndexType, Int, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, TradingFeeInterface, Transaction, TransferEntry
from typing import List
from ccxt_zagrava.base.errors import ExchangeError
from ccxt_zagrava.base.errors import AuthenticationError
from ccxt_zagrava.base.errors import PermissionDenied
from ccxt_zagrava.base.errors import AccountSuspended
from ccxt_zagrava.base.errors import ArgumentsRequired
from ccxt_zagrava.base.errors import BadRequest
from ccxt_zagrava.base.errors import BadSymbol
from ccxt_zagrava.base.errors import InsufficientFunds
from ccxt_zagrava.base.errors import 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 DDoSProtection
from ccxt_zagrava.base.errors import RateLimitExceeded
from ccxt_zagrava.base.errors import ExchangeNotAvailable
from ccxt_zagrava.base.errors import OnMaintenance
from ccxt_zagrava.base.errors import InvalidNonce
from ccxt_zagrava.base.errors import CancelPending
from ccxt_zagrava.base.decimal_to_precision import TRUNCATE
from ccxt_zagrava.base.decimal_to_precision import TICK_SIZE
from ccxt_zagrava.base.precise import Precise


class kraken(Exchange, ImplicitAPI):

    def describe(self):
        return self.deep_extend(super(kraken, self).describe(), {
            'id': 'kraken',
            'name': 'Kraken',
            'countries': ['US'],
            'version': '0',
            # rate-limits: https://support.kraken.com/hc/en-us/articles/206548367-What-are-the-API-rate-limits-#1
            # for public: 1 req/s
            # for private: every second 0.33 weight added to your allowed capacity(some private endpoints need 1 weight, some need 2)
            'rateLimit': 1000,
            'certified': False,
            'pro': True,
            'has': {
                'CORS': None,
                'spot': True,
                'margin': True,
                'swap': False,
                'future': False,
                'option': False,
                'addMargin': False,
                'cancelAllOrders': True,
                'cancelAllOrdersAfter': True,
                'cancelOrder': True,
                'cancelOrders': True,
                'createDepositAddress': True,
                'createMarketBuyOrderWithCost': True,
                'createMarketOrderWithCost': False,
                'createMarketSellOrderWithCost': False,
                'createOrder': True,
                'createStopLimitOrder': True,
                'createStopMarketOrder': True,
                'createStopOrder': True,
                'createTrailingAmountOrder': True,
                'editOrder': True,
                'fetchBalance': True,
                'fetchBorrowInterest': False,
                'fetchBorrowRateHistories': False,
                'fetchBorrowRateHistory': False,
                'fetchClosedOrders': True,
                'fetchCrossBorrowRate': False,
                'fetchCrossBorrowRates': False,
                'fetchCurrencies': True,
                'fetchDepositAddress': True,
                'fetchDeposits': True,
                'fetchFundingHistory': False,
                'fetchFundingRate': False,
                'fetchFundingRateHistory': False,
                'fetchFundingRates': False,
                'fetchIndexOHLCV': False,
                'fetchIsolatedBorrowRate': False,
                'fetchIsolatedBorrowRates': False,
                'fetchLedger': True,
                'fetchLedgerEntry': True,
                'fetchLeverageTiers': False,
                'fetchMarkets': True,
                'fetchMarkOHLCV': False,
                'fetchMyTrades': True,
                'fetchOHLCV': True,
                'fetchOpenInterestHistory': False,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'fetchOrderBook': True,
                'fetchOrderTrades': 'emulated',
                'fetchPositions': True,
                'fetchPremiumIndexOHLCV': False,
                'fetchTicker': True,
                'fetchTickers': True,
                'fetchTime': True,
                'fetchTrades': True,
                'fetchTradingFee': True,
                'fetchTradingFees': False,
                'fetchWithdrawals': True,
                'setLeverage': False,
                'setMarginMode': False,  # Kraken only supports cross margin
                'transfer': True,
                'withdraw': True,
            },
            'timeframes': {
                '1m': 1,
                '5m': 5,
                '15m': 15,
                '30m': 30,
                '1h': 60,
                '4h': 240,
                '1d': 1440,
                '1w': 10080,
                '2w': 21600,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/51840849/76173629-fc67fb00-61b1-11ea-84fe-f2de582f58a3.jpg',
                'api': {
                    'public': 'https://api.kraken.com',
                    'private': 'https://api.kraken.com',
                    'zendesk': 'https://kraken.zendesk.com/api/v2/help_center/en-us/articles',  # use the public zendesk api to receive article bodies and bypass new anti-spam protections
                },
                'www': 'https://www.kraken.com',
                'doc': 'https://docs.kraken.com/rest/',
                'fees': 'https://www.kraken.com/en-us/features/fee-schedule',
            },
            'fees': {
                'trading': {
                    'tierBased': True,
                    'percentage': True,
                    'taker': self.parse_number('0.0026'),
                    'maker': self.parse_number('0.0016'),
                    'tiers': {
                        'taker': [
                            [self.parse_number('0'), self.parse_number('0.0026')],
                            [self.parse_number('50000'), self.parse_number('0.0024')],
                            [self.parse_number('100000'), self.parse_number('0.0022')],
                            [self.parse_number('250000'), self.parse_number('0.0020')],
                            [self.parse_number('500000'), self.parse_number('0.0018')],
                            [self.parse_number('1000000'), self.parse_number('0.0016')],
                            [self.parse_number('2500000'), self.parse_number('0.0014')],
                            [self.parse_number('5000000'), self.parse_number('0.0012')],
                            [self.parse_number('10000000'), self.parse_number('0.0001')],
                        ],
                        'maker': [
                            [self.parse_number('0'), self.parse_number('0.0016')],
                            [self.parse_number('50000'), self.parse_number('0.0014')],
                            [self.parse_number('100000'), self.parse_number('0.0012')],
                            [self.parse_number('250000'), self.parse_number('0.0010')],
                            [self.parse_number('500000'), self.parse_number('0.0008')],
                            [self.parse_number('1000000'), self.parse_number('0.0006')],
                            [self.parse_number('2500000'), self.parse_number('0.0004')],
                            [self.parse_number('5000000'), self.parse_number('0.0002')],
                            [self.parse_number('10000000'), self.parse_number('0.0')],
                        ],
                    },
                },
            },
            'handleContentTypeApplicationZip': True,
            'api': {
                'zendesk': {
                    'get': [
                        # we should really refrain from putting fixed fee numbers and stop hardcoding
                        # we will be using their web APIs to scrape all numbers from these articles
                        '360000292886',  # -What-are-the-deposit-fees-
                        '201893608',  # -What-are-the-withdrawal-fees-
                    ],
                },
                'public': {
                    'get': {
                        # rate-limits explained in comment in the top of self file
                        'Assets': 1,
                        'AssetPairs': 1,
                        'Depth': 1.2,
                        'OHLC': 1.2,  # 1.2 because 1 triggers too many requests immediately
                        'Spread': 1,
                        'SystemStatus': 1,
                        'Ticker': 1,
                        'Time': 1,
                        'Trades': 1.2,
                    },
                },
                'private': {
                    'post': {
                        'AddOrder': 0,
                        'AddOrderBatch': 0,
                        'AddExport': 3,
                        'Balance': 3,
                        'CancelAll': 3,
                        'CancelAllOrdersAfter': 3,
                        'CancelOrder': 0,
                        'CancelOrderBatch': 0,
                        'ClosedOrders': 3,
                        'DepositAddresses': 3,
                        'DepositMethods': 3,
                        'DepositStatus': 3,
                        'EditOrder': 0,
                        'ExportStatus': 3,
                        'GetWebSocketsToken': 3,
                        'Ledgers': 6,
                        'OpenOrders': 3,
                        'OpenPositions': 3,
                        'QueryLedgers': 3,
                        'QueryOrders': 3,
                        'QueryTrades': 3,
                        'RetrieveExport': 3,
                        'RemoveExport': 3,
                        'BalanceEx': 3,
                        'TradeBalance': 3,
                        'TradesHistory': 6,
                        'TradeVolume': 3,
                        'Withdraw': 3,
                        'WithdrawCancel': 3,
                        'WithdrawInfo': 3,
                        'WithdrawMethods': 3,
                        'WithdrawAddresses': 3,
                        'WithdrawStatus': 3,
                        'WalletTransfer': 3,
                        # sub accounts
                        'CreateSubaccount': 3,
                        'AccountTransfer': 3,
                        # earn
                        'Earn/Allocate': 3,
                        'Earn/Deallocate': 3,
                        'Earn/AllocateStatus': 3,
                        'Earn/DeallocateStatus': 3,
                        'Earn/Strategies': 3,
                        'Earn/Allocations': 3,
                    },
                },
            },
            'commonCurrencies': {
                'LUNA': 'LUNC',
                'LUNA2': 'LUNA',
                'REPV2': 'REP',
                'REP': 'REPV1',
                'UST': 'USTC',
                'XBT': 'BTC',
                'XBT.M': 'BTC.M',  # https://support.kraken.com/hc/en-us/articles/360039879471-What-is-Asset-S-and-Asset-M-
                'XDG': 'DOGE',
            },
            'options': {
                'marketsByAltname': {},
                'delistedMarketsById': {},
                # cannot withdraw/deposit these
                'inactiveCurrencies': ['CAD', 'USD', 'JPY', 'GBP'],
                'networks': {
                    'ETH': 'ERC20',
                    'TRX': 'TRC20',
                },
                'depositMethods': {
                    '1INCH': '1inch(1INCH)',
                    'AAVE': 'Aave',
                    'ADA': 'ADA',
                    'ALGO': 'Algorand',
                    'ANKR': 'ANKR(ANKR)',
                    'ANT': 'Aragon(ANT)',
                    'ATOM': 'Cosmos',
                    'AXS': 'Axie Infinity Shards(AXS)',
                    'BADGER': 'Bager DAO(BADGER)',
                    'BAL': 'Balancer(BAL)',
                    'BAND': 'Band Protocol(BAND)',
                    'BAT': 'BAT',
                    'BCH': 'Bitcoin Cash',
                    'BNC': 'Bifrost(BNC)',
                    'BNT': 'Bancor(BNT)',
                    'BTC': 'Bitcoin',
                    'CHZ': 'Chiliz(CHZ)',
                    'COMP': 'Compound(COMP)',
                    'CQT': '\tCovalent Query Token(CQT)',
                    'CRV': 'Curve DAO Token(CRV)',
                    'CTSI': 'Cartesi(CTSI)',
                    'DAI': 'Dai',
                    'DASH': 'Dash',
                    'DOGE': 'Dogecoin',
                    'DOT': 'Polkadot',
                    'DYDX': 'dYdX(DYDX)',
                    'ENJ': 'Enjin Coin(ENJ)',
                    'EOS': 'EOS',
                    'ETC': 'Ether Classic(Hex)',
                    'ETH': 'Ether(Hex)',
                    'EWT': 'Energy Web Token',
                    'FEE': 'Kraken Fee Credit',
                    'FIL': 'Filecoin',
                    'FLOW': 'Flow',
                    'GHST': 'Aavegotchi(GHST)',
                    'GNO': 'GNO',
                    'GRT': 'GRT',
                    'ICX': 'Icon',
                    'INJ': 'Injective Protocol(INJ)',
                    'KAR': 'Karura(KAR)',
                    'KAVA': 'Kava',
                    'KEEP': 'Keep Token(KEEP)',
                    'KNC': 'Kyber Network(KNC)',
                    'KSM': 'Kusama',
                    'LINK': 'Link',
                    'LPT': 'Livepeer Token(LPT)',
                    'LRC': 'Loopring(LRC)',
                    'LSK': 'Lisk',
                    'LTC': 'Litecoin',
                    'MANA': 'MANA',
                    'MATIC': 'Polygon(MATIC)',
                    'MINA': 'Mina',  # inspected from webui
                    'MIR': 'Mirror Protocol(MIR)',
                    'MKR': 'Maker(MKR)',
                    'MLN': 'MLN',
                    'MOVR': 'Moonriver(MOVR)',
                    'NANO': 'NANO',
                    'OCEAN': 'OCEAN',
                    'OGN': 'Origin Protocol(OGN)',
                    'OMG': 'OMG',
                    'OXT': 'Orchid(OXT)',
                    'OXY': 'Oxygen(OXY)',
                    'PAXG': 'PAX(Gold)',
                    'PERP': 'Perpetual Protocol(PERP)',
                    'PHA': 'Phala(PHA)',
                    'QTUM': 'QTUM',
                    'RARI': 'Rarible(RARI)',
                    'RAY': 'Raydium(RAY)',
                    'REN': 'Ren Protocol(REN)',
                    'REP': 'REPv2',
                    'REPV1': 'REP',
                    'SAND': 'The Sandbox(SAND)',
                    'SC': 'Siacoin',
                    'SDN': 'Shiden(SDN)',
                    'SOL': 'Solana',  # their deposit method api doesn't work for SOL - was guessed
                    'SNX': 'Synthetix  Network(SNX)',
                    'SRM': 'Serum',  # inspected from webui
                    'STORJ': 'Storj(STORJ)',
                    'SUSHI': 'Sushiswap(SUSHI)',
                    'TBTC': 'tBTC',
                    'TRX': 'Tron',
                    'UNI': 'UNI',
                    'USDC': 'USDC',
                    'USDT': 'Tether USD(ERC20)',
                    'USDT-TRC20': 'Tether USD(TRC20)',
                    'WAVES': 'Waves',
                    'WBTC': 'Wrapped Bitcoin(WBTC)',
                    'XLM': 'Stellar XLM',
                    'XMR': 'Monero',
                    'XRP': 'Ripple XRP',
                    'XTZ': 'XTZ',
                    'YFI': 'YFI',
                    'ZEC': 'Zcash(Transparent)',
                    'ZRX': '0x(ZRX)',
                },
                'withdrawMethods': {  # keeping it here because deposit and withdraw return different networks codes
                    'Lightning': 'Lightning',
                    'Bitcoin': 'BTC',
                    'Ripple': 'XRP',
                    'Litecoin': 'LTC',
                    'Dogecoin': 'DOGE',
                    'Stellar': 'XLM',
                    'Ethereum': 'ERC20',
                    'Arbitrum One': 'Arbitrum',
                    'Polygon': 'MATIC',
                    'Arbitrum Nova': 'Arbitrum',
                    'Optimism': 'Optimism',
                    'zkSync Era': 'zkSync',
                    'Ethereum Classic': 'ETC',
                    'Zcash': 'ZEC',
                    'Monero': 'XMR',
                    'Tron': 'TRC20',
                    'Solana': 'SOL',
                    'EOS': 'EOS',
                    'Bitcoin Cash': 'BCH',
                    'Cardano': 'ADA',
                    'Qtum': 'QTUM',
                    'Tezos': 'XTZ',
                    'Cosmos': 'ATOM',
                    'Nano': 'NANO',
                    'Siacoin': 'SC',
                    'Lisk': 'LSK',
                    'Waves': 'WAVES',
                    'ICON': 'ICX',
                    'Algorand': 'ALGO',
                    'Polygon - USDC.e': 'MATIC',
                    'Arbitrum One - USDC.e': 'Arbitrum',
                    'Polkadot': 'DOT',
                    'Kava': 'KAVA',
                    'Filecoin': 'FIL',
                    'Kusama': 'KSM',
                    'Flow': 'FLOW',
                    'Energy Web': 'EW',
                    'Mina': 'MINA',
                    'Centrifuge': 'CFG',
                    'Karura': 'KAR',
                    'Moonriver': 'MOVR',
                    'Shiden': 'SDN',
                    'Khala': 'PHA',
                    'Bifrost Kusama': 'BNC',
                    'Songbird': 'SGB',
                    'Terra classic': 'LUNC',
                    'KILT': 'KILT',
                    'Basilisk': 'BSX',
                    'Flare': 'FLR',
                    'Avalanche C-Chain': 'AVAX',
                    'Kintsugi': 'KINT',
                    'Altair': 'AIR',
                    'Moonbeam': 'GLMR',
                    'Acala': 'ACA',
                    'Astar': 'ASTR',
                    'Akash': 'AKT',
                    'Robonomics': 'XRT',
                    'Fantom': 'FTM',
                    'Elrond': 'EGLD',
                    'THORchain': 'RUNE',
                    'Secret': 'SCRT',
                    'Near': 'NEAR',
                    'Internet Computer Protocol': 'ICP',
                    'Picasso': 'PICA',
                    'Crust Shadow': 'CSM',
                    'Integritee': 'TEER',
                    'Parallel Finance': 'PARA',
                    'HydraDX': 'HDX',
                    'Interlay': 'INTR',
                    'Fetch.ai': 'FET',
                    'NYM': 'NYM',
                    'Terra 2.0': 'LUNA2',
                    'Juno': 'JUNO',
                    'Nodle': 'NODL',
                    'Stacks': 'STX',
                    'Ethereum PoW': 'ETHW',
                    'Aptos': 'APT',
                    'Sui': 'SUI',
                    'Genshiro': 'GENS',
                    'Aventus': 'AVT',
                    'Sei': 'SEI',
                    'OriginTrail': 'OTP',
                    'Celestia': 'TIA',
                },
            },
            'precisionMode': TICK_SIZE,
            'exceptions': {
                'EQuery:Invalid asset pair': BadSymbol,  # {"error":["EQuery:Invalid asset pair"]}
                'EAPI:Invalid key': AuthenticationError,
                'EFunding:Unknown withdraw key': InvalidAddress,  # {"error":["EFunding:Unknown withdraw key"]}
                'EFunding:Invalid amount': InsufficientFunds,
                'EService:Unavailable': ExchangeNotAvailable,
                'EDatabase:Internal error': ExchangeNotAvailable,
                'EService:Busy': ExchangeNotAvailable,
                'EQuery:Unknown asset': BadSymbol,  # {"error":["EQuery:Unknown asset"]}
                'EAPI:Rate limit exceeded': DDoSProtection,
                'EOrder:Rate limit exceeded': DDoSProtection,
                'EGeneral:Internal error': ExchangeNotAvailable,
                'EGeneral:Temporary lockout': DDoSProtection,
                'EGeneral:Permission denied': PermissionDenied,
                'EOrder:Unknown order': InvalidOrder,
                'EOrder:Order minimum not met': InvalidOrder,
                'EGeneral:Invalid arguments': BadRequest,
                'ESession:Invalid session': AuthenticationError,
                'EAPI:Invalid nonce': InvalidNonce,
                'EFunding:No funding method': BadRequest,  # {"error":"EFunding:No funding method"}
                'EFunding:Unknown asset': BadSymbol,  # {"error":["EFunding:Unknown asset"]}
                'EService:Market in post_only mode': OnMaintenance,  # {"error":["EService:Market in post_only mode"]}
                'EGeneral:Too many requests': DDoSProtection,  # {"error":["EGeneral:Too many requests"]}
                'ETrade:User Locked': AccountSuspended,  # {"error":["ETrade:User Locked"]}
            },
        })

    def fee_to_precision(self, symbol, fee):
        return self.decimal_to_precision(fee, TRUNCATE, self.markets[symbol]['precision']['amount'], self.precisionMode)

    async def fetch_markets(self, params={}) -> List[Market]:
        """
        retrieves data on all markets for kraken
        :see: https://docs.kraken.com/rest/#tag/Spot-Market-Data/operation/getTradableAssetPairs
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: an array of objects representing market data
        """
        response = await self.publicGetAssetPairs(params)
        #
        #     {
        #         "error": [],
        #         "result": {
        #             "ADAETH": {
        #                 "altname": "ADAETH",
        #                 "wsname": "ADA\/ETH",
        #                 "aclass_base": "currency",
        #                 "base": "ADA",
        #                 "aclass_quote": "currency",
        #                 "quote": "XETH",
        #                 "lot": "unit",
        #                 "pair_decimals": 7,
        #                 "lot_decimals": 8,
        #                 "lot_multiplier": 1,
        #                 "leverage_buy": [],
        #                 "leverage_sell": [],
        #                 "fees": [
        #                     [0, 0.26],
        #                     [50000, 0.24],
        #                     [100000, 0.22],
        #                     [250000, 0.2],
        #                     [500000, 0.18],
        #                     [1000000, 0.16],
        #                     [2500000, 0.14],
        #                     [5000000, 0.12],
        #                     [10000000, 0.1]
        #                 ],
        #                 "fees_maker": [
        #                     [0, 0.16],
        #                     [50000, 0.14],
        #                     [100000, 0.12],
        #                     [250000, 0.1],
        #                     [500000, 0.08],
        #                     [1000000, 0.06],
        #                     [2500000, 0.04],
        #                     [5000000, 0.02],
        #                     [10000000, 0]
        #                 ],
        #                 "fee_volume_currency": "ZUSD",
        #                 "margin_call": 80,
        #                 "margin_stop": 40,
        #                 "ordermin": "1"
        #             },
        #         }
        #     }
        #
        markets = self.safe_value(response, 'result', {})
        keys = list(markets.keys())
        result = []
        for i in range(0, len(keys)):
            id = keys[i]
            market = markets[id]
            baseId = self.safe_string(market, 'base')
            quoteId = self.safe_string(market, 'quote')
            base = self.safe_currency_code(baseId)
            quote = self.safe_currency_code(quoteId)
            darkpool = id.find('.d') >= 0
            altname = self.safe_string(market, 'altname')
            makerFees = self.safe_value(market, 'fees_maker', [])
            firstMakerFee = self.safe_value(makerFees, 0, [])
            firstMakerFeeRate = self.safe_string(firstMakerFee, 1)
            maker = None
            if firstMakerFeeRate is not None:
                maker = self.parse_number(Precise.string_div(firstMakerFeeRate, '100'))
            takerFees = self.safe_value(market, 'fees', [])
            firstTakerFee = self.safe_value(takerFees, 0, [])
            firstTakerFeeRate = self.safe_string(firstTakerFee, 1)
            taker = None
            if firstTakerFeeRate is not None:
                taker = self.parse_number(Precise.string_div(firstTakerFeeRate, '100'))
            leverageBuy = self.safe_value(market, 'leverage_buy', [])
            leverageBuyLength = len(leverageBuy)
            precisionPrice = self.parse_number(self.parse_precision(self.safe_string(market, 'pair_decimals')))
            status = self.safe_string(market, 'status')
            isActive = status == 'online'
            result.append({
                'id': id,
                'wsId': self.safe_string(market, 'wsname'),
                'symbol': altname if darkpool else (base + '/' + quote),
                'base': base,
                'quote': quote,
                'settle': None,
                'baseId': baseId,
                'quoteId': quoteId,
                'settleId': None,
                'darkpool': darkpool,
                'altname': market['altname'],
                'type': 'spot',
                'spot': True,
                'margin': (leverageBuyLength > 0),
                'swap': False,
                'future': False,
                'option': False,
                'active': isActive,
                'contract': False,
                'linear': None,
                'inverse': None,
                'taker': taker,
                'maker': maker,
                'contractSize': None,
                'expiry': None,
                'expiryDatetime': None,
                'strike': None,
                'optionType': None,
                'precision': {
                    'amount': self.parse_number(self.parse_precision(self.safe_string(market, 'lot_decimals'))),
                    'price': precisionPrice,
                },
                'limits': {
                    'leverage': {
                        'min': self.parse_number('1'),
                        'max': self.safe_number(leverageBuy, leverageBuyLength - 1, 1),
                    },
                    'amount': {
                        'min': self.safe_number(market, 'ordermin'),
                        'max': None,
                    },
                    'price': {
                        'min': precisionPrice,
                        'max': None,
                    },
                    'cost': {
                        'min': self.safe_number(market, 'costmin'),
                        'max': None,
                    },
                },
                'created': None,
                'info': market,
            })
        result = self.append_inactive_markets(result)
        self.options['marketsByAltname'] = self.index_by(result, 'altname')
        return result

    def safe_currency(self, currencyId, currency: Currency = None):
        if currencyId is not None:
            if len(currencyId) > 3:
                if (currencyId.find('X') == 0) or (currencyId.find('Z') == 0):
                    if not (currencyId.find('.') > 0):
                        currencyId = currencyId[1:]
        return super(kraken, self).safe_currency(currencyId, currency)

    def append_inactive_markets(self, result):
        # result should be an array to append to
        precision: dict = {
            'amount': self.parse_number('1e-8'),
            'price': self.parse_number('1e-8'),
        }
        costLimits: dict = {'min': None, 'max': None}
        priceLimits: dict = {'min': precision['price'], 'max': None}
        amountLimits: dict = {'min': precision['amount'], 'max': None}
        limits: dict = {'amount': amountLimits, 'price': priceLimits, 'cost': costLimits}
        defaults: dict = {
            'darkpool': False,
            'info': None,
            'maker': None,
            'taker': None,
            'active': False,
            'precision': precision,
            'limits': limits,
        }
        markets = [
            # {'id': 'XXLMZEUR', 'symbol': 'XLM/EUR', 'base': 'XLM', 'quote': 'EUR', 'altname': 'XLMEUR'},
        ]
        for i in range(0, len(markets)):
            result.append(self.extend(defaults, markets[i]))
        return result

    async def fetch_currencies(self, params={}) -> Currencies:
        """
        fetches all available currencies on an exchange
        :see: https://docs.kraken.com/rest/#tag/Spot-Market-Data/operation/getAssetInfo
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an associative dictionary of currencies
        """
        response = await self.publicGetAssets(params)
        #
        #     {
        #         "error": [],
        #         "result": {
        #             "BCH": {
        #                 "aclass": "currency",
        #                 "altname": "BCH",
        #                 "decimals": 10,
        #                 "display_decimals": 5
        #                 "status": "enabled",
        #             },
        #             ...
        #         },
        #     }
        #
        currencies = self.safe_value(response, 'result', {})
        ids = list(currencies.keys())
        result: dict = {}
        for i in range(0, len(ids)):
            id = ids[i]
            currency = currencies[id]
            # todo: will need to rethink the fees
            # see: https://support.kraken.com/hc/en-us/articles/201893608-What-are-the-withdrawal-fees-
            # to add support for multiple withdrawal/deposit methods and
            # differentiated fees for each particular method
            code = self.safe_currency_code(id)
            precision = self.parse_number(self.parse_precision(self.safe_string(currency, 'decimals')))
            # assumes all currencies are active except those listed above
            active = self.safe_string(currency, 'status') == 'enabled'
            result[code] = {
                'id': id,
                'code': code,
                'info': currency,
                'name': self.safe_string(currency, 'altname'),
                'active': active,
                'deposit': None,
                'withdraw': None,
                'fee': None,
                'precision': precision,
                'limits': {
                    'amount': {
                        'min': precision,
                        'max': None,
                    },
                    'withdraw': {
                        'min': None,
                        'max': None,
                    },
                },
                'networks': {},
            }
        return result

    async def fetch_trading_fee(self, symbol: str, params={}) -> TradingFeeInterface:
        """
        fetch the trading fees for a market
        :see: https://docs.kraken.com/rest/#tag/Account-Data/operation/getTradeVolume
        :param str symbol: unified market symbol
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `fee structure <https://docs.ccxt_zagrava.com/#/?id=fee-structure>`
        """
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'pair': market['id'],
            'fee-info': True,
        }
        response = await self.privatePostTradeVolume(self.extend(request, params))
        #
        #     {
        #        "error": [],
        #        "result": {
        #          "currency": 'ZUSD',
        #          "volume": '0.0000',
        #          "fees": {
        #            "XXBTZUSD": {
        #              "fee": '0.2600',
        #              "minfee": '0.1000',
        #              "maxfee": '0.2600',
        #              "nextfee": '0.2400',
        #              "tiervolume": '0.0000',
        #              "nextvolume": '50000.0000'
        #            }
        #          },
        #          "fees_maker": {
        #            "XXBTZUSD": {
        #              "fee": '0.1600',
        #              "minfee": '0.0000',
        #              "maxfee": '0.1600',
        #              "nextfee": '0.1400',
        #              "tiervolume": '0.0000',
        #              "nextvolume": '50000.0000'
        #            }
        #          }
        #        }
        #     }
        #
        result = self.safe_value(response, 'result', {})
        return self.parse_trading_fee(result, market)

    def parse_trading_fee(self, response, market):
        makerFees = self.safe_value(response, 'fees_maker', {})
        takerFees = self.safe_value(response, 'fees', {})
        symbolMakerFee = self.safe_value(makerFees, market['id'], {})
        symbolTakerFee = self.safe_value(takerFees, market['id'], {})
        return {
            'info': response,
            'symbol': market['symbol'],
            'maker': self.parse_number(Precise.string_div(self.safe_string(symbolMakerFee, 'fee'), '100')),
            'taker': self.parse_number(Precise.string_div(self.safe_string(symbolTakerFee, 'fee'), '100')),
            'percentage': True,
            'tierBased': True,
        }

    def parse_bid_ask(self, bidask, priceKey: IndexType = 0, amountKey: IndexType = 1, countOrIdKey: IndexType = 2):
        price = self.safe_number(bidask, priceKey)
        amount = self.safe_number(bidask, amountKey)
        timestamp = self.safe_integer(bidask, 2)
        return [price, amount, timestamp]

    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://docs.kraken.com/rest/#tag/Spot-Market-Data/operation/getOrderBook
        :param str symbol: unified symbol of the market to fetch the order book for
        :param int [limit]: the maximum amount of order book entries to return
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: A dictionary of `order book structures <https://docs.ccxt_zagrava.com/#/?id=order-book-structure>` indexed by market symbols
        """
        await self.load_markets()
        market = self.market(symbol)
        if market['darkpool']:
            raise ExchangeError(self.id + ' fetchOrderBook() does not provide an order book for darkpool symbol ' + symbol)
        request: dict = {
            'pair': market['id'],
        }
        if limit is not None:
            request['count'] = limit  # 100
        response = await self.publicGetDepth(self.extend(request, params))
        #
        #     {
        #         "error":[],
        #         "result":{
        #             "XETHXXBT":{
        #                 "asks":[
        #                     ["0.023480","4.000",1586321307],
        #                     ["0.023490","50.095",1586321306],
        #                     ["0.023500","28.535",1586321302],
        #                 ],
        #                 "bids":[
        #                     ["0.023470","59.580",1586321307],
        #                     ["0.023460","20.000",1586321301],
        #                     ["0.023440","67.832",1586321306],
        #                 ]
        #             }
        #         }
        #     }
        #
        result = self.safe_value(response, 'result', {})
        orderbook = self.safe_value(result, market['id'])
        # sometimes kraken returns wsname instead of market id
        # https://github.com/ccxt_zagrava/ccxt_zagrava/issues/8662
        marketInfo = self.safe_value(market, 'info', {})
        wsName = self.safe_value(marketInfo, 'wsname')
        if wsName is not None:
            orderbook = self.safe_value(result, wsName, orderbook)
        return self.parse_order_book(orderbook, symbol)

    def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
        #
        #     {
        #         "a":["2432.77000","1","1.000"],
        #         "b":["2431.37000","2","2.000"],
        #         "c":["2430.58000","0.04408910"],
        #         "v":["4147.94474901","8896.96086304"],
        #         "p":["2456.22239","2568.63032"],
        #         "t":[3907,10056],
        #         "l":["2302.18000","2302.18000"],
        #         "h":["2621.14000","2860.01000"],
        #         "o":"2571.56000"
        #     }
        #
        symbol = self.safe_symbol(None, market)
        v = self.safe_value(ticker, 'v', [])
        baseVolume = self.safe_string(v, 1)
        p = self.safe_value(ticker, 'p', [])
        vwap = self.safe_string(p, 1)
        quoteVolume = Precise.string_mul(baseVolume, vwap)
        c = self.safe_value(ticker, 'c', [])
        last = self.safe_string(c, 0)
        high = self.safe_value(ticker, 'h', [])
        low = self.safe_value(ticker, 'l', [])
        bid = self.safe_value(ticker, 'b', [])
        ask = self.safe_value(ticker, 'a', [])
        return self.safe_ticker({
            'symbol': symbol,
            'timestamp': None,
            'datetime': None,
            'high': self.safe_string(high, 1),
            'low': self.safe_string(low, 1),
            'bid': self.safe_string(bid, 0),
            'bidVolume': None,
            'ask': self.safe_string(ask, 0),
            'askVolume': None,
            'vwap': vwap,
            'open': self.safe_string(ticker, 'o'),
            'close': last,
            'last': last,
            'previousClose': None,
            'change': None,
            'percentage': None,
            'average': None,
            'baseVolume': baseVolume,
            'quoteVolume': quoteVolume,
            'info': ticker,
        }, 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://docs.kraken.com/rest/#tag/Spot-Market-Data/operation/getTickerInformation
        :param str[]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a dictionary of `ticker structures <https://docs.ccxt_zagrava.com/#/?id=ticker-structure>`
        """
        await self.load_markets()
        request: dict = {}
        if symbols is not None:
            symbols = self.market_symbols(symbols)
            marketIds = []
            for i in range(0, len(symbols)):
                symbol = symbols[i]
                market = self.markets[symbol]
                if market['active'] and not market['darkpool']:
                    marketIds.append(market['id'])
            request['pair'] = ','.join(marketIds)
        response = await self.publicGetTicker(self.extend(request, params))
        tickers = response['result']
        ids = list(tickers.keys())
        result: dict = {}
        for i in range(0, len(ids)):
            id = ids[i]
            market = self.safe_market(id)
            symbol = market['symbol']
            ticker = tickers[id]
            result[symbol] = self.parse_ticker(ticker, market)
        return self.filter_by_array_tickers(result, 'symbol', symbols)

    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://docs.kraken.com/rest/#tag/Spot-Market-Data/operation/getTickerInformation
        :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()
        darkpool = symbol.find('.d') >= 0
        if darkpool:
            raise ExchangeError(self.id + ' fetchTicker() does not provide a ticker for darkpool symbol ' + symbol)
        market = self.market(symbol)
        request: dict = {
            'pair': market['id'],
        }
        response = await self.publicGetTicker(self.extend(request, params))
        ticker = response['result'][market['id']]
        return self.parse_ticker(ticker, market)

    def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
        #
        #     [
        #         1591475640,
        #         "0.02500",
        #         "0.02500",
        #         "0.02500",
        #         "0.02500",
        #         "0.02500",
        #         "9.12201000",
        #         5
        #     ]
        #
        return [
            self.safe_timestamp(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, 6),
        ]

    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://docs.kraken.com/rest/#tag/Spot-Market-Data/operation/getOHLCData
        :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 boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt_zagrava/ccxt_zagrava/wiki/Manual#pagination-params)
        :returns int[][]: A list of candles ordered, open, high, low, close, volume
        """
        await self.load_markets()
        paginate = False
        paginate, params = self.handle_option_and_params(params, 'fetchOHLCV', 'paginate')
        if paginate:
            return await self.fetch_paginated_call_deterministic('fetchOHLCV', symbol, since, limit, timeframe, params, 720)
        market = self.market(symbol)
        parsedTimeframe = self.safe_integer(self.timeframes, timeframe)
        request: dict = {
            'pair': market['id'],
        }
        if parsedTimeframe is not None:
            request['interval'] = parsedTimeframe
        else:
            request['interval'] = timeframe
        if since is not None:
            scaledSince = self.parse_to_int(since / 1000)
            timeFrameInSeconds = parsedTimeframe * 60
            request['since'] = self.number_to_string(scaledSince - timeFrameInSeconds)  # expected to be in seconds
        response = await self.publicGetOHLC(self.extend(request, params))
        #
        #     {
        #         "error":[],
        #         "result":{
        #             "XETHXXBT":[
        #                 [1591475580,"0.02499","0.02499","0.02499","0.02499","0.00000","0.00000000",0],
        #                 [1591475640,"0.02500","0.02500","0.02500","0.02500","0.02500","9.12201000",5],
        #                 [1591475700,"0.02499","0.02499","0.02499","0.02499","0.02499","1.28681415",2],
        #                 [1591475760,"0.02499","0.02499","0.02499","0.02499","0.02499","0.08800000",1],
        #             ],
        #             "last":1591517580
        #         }
        #     }
        result = self.safe_value(response, 'result', {})
        ohlcvs = self.safe_list(result, market['id'], [])
        return self.parse_ohlcvs(ohlcvs, market, timeframe, since, limit)

    def parse_ledger_entry_type(self, type):
        types: dict = {
            'trade': 'trade',
            'withdrawal': 'transaction',
            'deposit': 'transaction',
            'transfer': 'transfer',
            'margin': 'margin',
        }
        return self.safe_string(types, type, type)

    def parse_ledger_entry(self, item: dict, currency: Currency = None):
        #
        #     {
        #         'LTFK7F-N2CUX-PNY4SX': {
        #             "refid": "TSJTGT-DT7WN-GPPQMJ",
        #             "time":  1520102320.555,
        #             "type": "trade",
        #             "aclass": "currency",
        #             "asset": "XETH",
        #             "amount": "0.1087194600",
        #             "fee": "0.0000000000",
        #             "balance": "0.2855851000"
        #         },
        #         ...
        #     }
        #
        id = self.safe_string(item, 'id')
        direction = None
        account = None
        referenceId = self.safe_string(item, 'refid')
        referenceAccount = None
        type = self.parse_ledger_entry_type(self.safe_string(item, 'type'))
        code = self.safe_currency_code(self.safe_string(item, 'asset'), currency)
        amount = self.safe_string(item, 'amount')
        if Precise.string_lt(amount, '0'):
            direction = 'out'
            amount = Precise.string_abs(amount)
        else:
            direction = 'in'
        timestamp = self.safe_integer_product(item, 'time', 1000)
        return {
            'info': item,
            'id': id,
            'direction': direction,
            'account': account,
            'referenceId': referenceId,
            'referenceAccount': referenceAccount,
            'type': type,
            'currency': code,
            'amount': self.parse_number(amount),
            'before': None,
            'after': self.safe_number(item, 'balance'),
            'status': 'ok',
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'fee': {
                'cost': self.safe_number(item, 'fee'),
                'currency': code,
            },
        }

    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://docs.kraken.com/rest/#tag/Account-Data/operation/getLedgers
        :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 is None
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: timestamp in ms of the latest ledger entry
        :returns dict: a `ledger structure <https://docs.ccxt_zagrava.com/#/?id=ledger-structure>`
        """
        # https://www.kraken.com/features/api#get-ledgers-info
        await self.load_markets()
        request: dict = {}
        currency = None
        if code is not None:
            currency = self.currency(code)
            request['asset'] = currency['id']
        if since is not None:
            request['start'] = self.parse_to_int(since / 1000)
        request, params = self.handle_until_option('end', request, params)
        response = await self.privatePostLedgers(self.extend(request, params))
        # { error: [],
        #   "result": {ledger: {'LPUAIB-TS774-UKHP7X': {  refid: "A2B4HBV-L4MDIE-JU4N3N",
        #                                                   "time":  1520103488.314,
        #                                                   "type": "withdrawal",
        #                                                 "aclass": "currency",
        #                                                  "asset": "XETH",
        #                                                 "amount": "-0.2805800000",
        #                                                    "fee": "0.0050000000",
        #                                                "balance": "0.0000051000"           },
        result = self.safe_value(response, 'result', {})
        ledger = self.safe_value(result, 'ledger', {})
        keys = list(ledger.keys())
        items = []
        for i in range(0, len(keys)):
            key = keys[i]
            value = ledger[key]
            value['id'] = key
            items.append(value)
        return self.parse_ledger(items, currency, since, limit)

    async def fetch_ledger_entries_by_ids(self, ids, code: Str = None, params={}):
        # https://www.kraken.com/features/api#query-ledgers
        await self.load_markets()
        ids = ','.join(ids)
        request = self.extend({
            'id': ids,
        }, params)
        response = await self.privatePostQueryLedgers(request)
        # { error: [],
        #   "result": {'LPUAIB-TS774-UKHP7X': {  refid: "A2B4HBV-L4MDIE-JU4N3N",
        #                                         "time":  1520103488.314,
        #                                         "type": "withdrawal",
        #                                       "aclass": "currency",
        #                                        "asset": "XETH",
        #                                       "amount": "-0.2805800000",
        #                                          "fee": "0.0050000000",
        #                                      "balance": "0.0000051000"           }}}
        result = response['result']
        keys = list(result.keys())
        items = []
        for i in range(0, len(keys)):
            key = keys[i]
            value = result[key]
            value['id'] = key
            items.append(value)
        return self.parse_ledger(items)

    async def fetch_ledger_entry(self, id: str, code: Str = None, params={}):
        items = await self.fetch_ledger_entries_by_ids([id], code, params)
        return items[0]

    def parse_trade(self, trade: dict, market: Market = None) -> Trade:
        #
        # fetchTrades(public)
        #
        #     [
        #         "0.032310",  # price
        #         "4.28169434",  # amount
        #         1541390792.763,  # timestamp
        #         "s",  # sell or buy
        #         "l",  # limit or market
        #         ""
        #     ]
        #
        # fetchOrderTrades(private)
        #
        #     {
        #         "id": 'TIMIRG-WUNNE-RRJ6GT',  # injected from outside
        #         "ordertxid": 'OQRPN2-LRHFY-HIFA7D',
        #         "postxid": 'TKH2SE-M7IF5-CFI7LT',
        #         "pair": 'USDCUSDT',
        #         "time": 1586340086.457,
        #         "type": 'sell',
        #         "ordertype": 'market',
        #         "price": '0.99860000',
        #         "cost": '22.16892001',
        #         "fee": '0.04433784',
        #         "vol": '22.20000000',
        #         "margin": '0.00000000',
        #         "misc": ''
        #     }
        #
        timestamp = None
        side = None
        type = None
        price = None
        amount = None
        id = None
        orderId = None
        fee = None
        symbol = None
        if isinstance(trade, list):
            timestamp = self.safe_timestamp(trade, 2)
            side = 'sell' if (trade[3] == 's') else 'buy'
            type = 'limit' if (trade[4] == 'l') else 'market'
            price = self.safe_string(trade, 0)
            amount = self.safe_string(trade, 1)
            tradeLength = len(trade)
            if tradeLength > 6:
                id = self.safe_string(trade, 6)  # artificially added  #1794
        elif isinstance(trade, str):
            id = trade
        elif 'ordertxid' in trade:
            marketId = self.safe_string(trade, 'pair')
            foundMarket = self.find_market_by_altname_or_id(marketId)
            if foundMarket is not None:
                market = foundMarket
            elif marketId is not None:
                # delisted market ids go here
                market = self.get_delisted_market_by_id(marketId)
            orderId = self.safe_string(trade, 'ordertxid')
            id = self.safe_string_2(trade, 'id', 'postxid')
            timestamp = self.safe_timestamp(trade, 'time')
            side = self.safe_string(trade, 'type')
            type = self.safe_string(trade, 'ordertype')
            price = self.safe_string(trade, 'price')
            amount = self.safe_string(trade, 'vol')
            if 'fee' in trade:
                currency = None
                if market is not None:
                    currency = market['quote']
                fee = {
                    'cost': self.safe_string(trade, 'fee'),
                    'currency': currency,
                }
        if market is not None:
            symbol = market['symbol']
        cost = self.safe_string(trade, 'cost')
        return self.safe_trade({
            'id': id,
            'order': orderId,
            'info': trade,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': symbol,
            'type': type,
            'side': side,
            'takerOrMaker': None,
            'price': price,
            'amount': amount,
            'cost': cost,
            'fee': fee,
        }, market)

    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://docs.kraken.com/rest/#tag/Spot-Market-Data/operation/getRecentTrades
        :param str symbol: unified symbol of the market to fetch trades for
        :param int [since]: timestamp in ms of the earliest trade to fetch
        :param int [limit]: the maximum amount of trades to fetch
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns Trade[]: a list of `trade structures <https://docs.ccxt_zagrava.com/#/?id=public-trades>`
        """
        await self.load_markets()
        market = self.market(symbol)
        id = market['id']
        request: dict = {
            'pair': id,
        }
        # https://support.kraken.com/hc/en-us/articles/218198197-How-to-pull-all-trade-data-using-the-Kraken-REST-API
        # https://github.com/ccxt_zagrava/ccxt_zagrava/issues/5677
        if since is not None:
            request['since'] = self.number_to_string(self.parse_to_int(since / 1000))  # expected to be in seconds
        if limit is not None:
            request['count'] = limit
        response = await self.publicGetTrades(self.extend(request, params))
        #
        #     {
        #         "error": [],
        #         "result": {
        #             "XETHXXBT": [
        #                 ["0.032310","4.28169434",1541390792.763,"s","l",""]
        #             ],
        #             "last": "1541439421200678657"
        #         }
        #     }
        #
        result = response['result']
        trades = result[id]
        # trades is a sorted array: last(most recent trade) goes last
        length = len(trades)
        if length <= 0:
            return []
        lastTrade = trades[length - 1]
        lastTradeId = self.safe_string(result, 'last')
        lastTrade.append(lastTradeId)
        trades[length - 1] = lastTrade
        return self.parse_trades(trades, market, since, limit)

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

    async def fetch_balance(self, params={}) -> Balances:
        """
        query for balance and get the amount of funds available for trading or funds locked in orders
        :see: https://docs.kraken.com/rest/#tag/Account-Data/operation/getExtendedBalance
        :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.privatePostBalanceEx(params)
        #
        #     {
        #         "error": [],
        #         "result": {
        #             "ZUSD": {
        #                 "balance": 25435.21,
        #                 "hold_trade": 8249.76
        #             },
        #             "XXBT": {
        #                 "balance": 1.2435,
        #                 "hold_trade": 0.8423
        #             }
        #         }
        #     }
        #
        return self.parse_balance(response)

    async def create_market_order_with_cost(self, symbol: str, side: OrderSide, cost: float, params={}):
        """
        create a market order by providing the symbol, side and cost
        :see: https://docs.kraken.com/rest/#tag/Trading/operation/addOrder
        :param str symbol: unified symbol of the market to create an order in(only USD markets are supported)
        :param str side: 'buy' or 'sell'
        :param float cost: how much you want to trade in units of the quote currency
        :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()
        # only buy orders are supported by the endpoint
        params['cost'] = cost
        return await self.create_order(symbol, 'market', side, cost, None, params)

    async def create_market_buy_order_with_cost(self, symbol: str, cost: float, params={}):
        """
        create a market buy order by providing the symbol, side and cost
        :see: https://docs.kraken.com/rest/#tag/Trading/operation/addOrder
        :param str symbol: unified symbol of the market to create an order in
        :param float cost: how much you want to trade in units of the quote currency
        :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()
        return await self.create_market_order_with_cost(symbol, 'buy', cost, params)

    async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
        """
        :see: https://docs.kraken.com/rest/#tag/Trading/operation/addOrder
        create a trade order
        :param str symbol: unified symbol of the market to create an order in
        :param str type: 'market' or 'limit'
        :param str side: 'buy' or 'sell'
        :param float amount: how much of currency you want to trade in units of base currency
        :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param bool [params.postOnly]: if True, the order will only be posted to the order book and not executed immediately
        :param bool [params.reduceOnly]: *margin only* indicates if self order is to reduce the size of a position
        :param float [params.stopLossPrice]: *margin only* the price that a stop loss order is triggered at
        :param float [params.takeProfitPrice]: *margin only* the price that a take profit order is triggered at
        :param str [params.trailingAmount]: *margin only* the quote amount to trail away from the current market price
        :param str [params.trailingLimitAmount]: *margin only* the quote amount away from the trailingAmount
        :param str [params.offset]: *margin only* '+' or '-' whether you want the trailingLimitAmount value to be positive or negative, default is negative '-'
        :param str [params.trigger]: *margin only* the activation price type, 'last' or 'index', default is 'last'
        :returns dict: an `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'pair': market['id'],
            'type': side,
            'ordertype': type,
            'volume': self.amount_to_precision(symbol, amount),
        }
        orderRequest = self.order_request('createOrder', symbol, type, request, amount, price, params)
        response = await self.privatePostAddOrder(self.extend(orderRequest[0], orderRequest[1]))
        #
        #     {
        #         "error": [],
        #         "result": {
        #             "descr": {order: 'buy 0.02100000 ETHUSDT @ limit 330.00'},
        #             "txid": ['OEKVV2-IH52O-TPL6GZ']
        #         }
        #     }
        #
        result = self.safe_dict(response, 'result')
        return self.parse_order(result)

    def find_market_by_altname_or_id(self, id):
        marketsByAltname = self.safe_value(self.options, 'marketsByAltname', {})
        if id in marketsByAltname:
            return marketsByAltname[id]
        else:
            return self.safe_market(id)

    def get_delisted_market_by_id(self, id):
        if id is None:
            return id
        market = self.safe_value(self.options['delistedMarketsById'], id)
        if market is not None:
            return market
        baseIdStart = 0
        baseIdEnd = 3
        quoteIdStart = 3
        quoteIdEnd = 6
        if len(id) == 8:
            baseIdEnd = 4
            quoteIdStart = 4
            quoteIdEnd = 8
        elif len(id) == 7:
            baseIdEnd = 4
            quoteIdStart = 4
            quoteIdEnd = 7
        baseId = id[baseIdStart:baseIdEnd]
        quoteId = id[quoteIdStart:quoteIdEnd]
        base = self.safe_currency_code(baseId)
        quote = self.safe_currency_code(quoteId)
        symbol = base + '/' + quote
        market = {
            'symbol': symbol,
            'base': base,
            'quote': quote,
            'baseId': baseId,
            'quoteId': quoteId,
        }
        self.options['delistedMarketsById'][id] = market
        return market

    def parse_order_status(self, status: Str):
        statuses: dict = {
            'pending': 'open',  # order pending book entry
            'open': 'open',
            'closed': 'closed',
            'canceled': 'canceled',
            'expired': 'expired',
        }
        return self.safe_string(statuses, status, status)

    def parse_order_type(self, status):
        statuses: dict = {
            'take-profit': 'market',
            'stop-loss-limit': 'limit',
            'stop-loss': 'market',
            'take-profit-limit': 'limit',
            'trailing-stop-limit': 'limit',
        }
        return self.safe_string(statuses, status, status)

    def parse_order(self, order: dict, market: Market = None) -> Order:
        #
        # createOrder for regular orders
        #
        #     {
        #         "descr": {order: 'buy 0.02100000 ETHUSDT @ limit 330.00'},
        #         "txid": ['OEKVV2-IH52O-TPL6GZ']
        #     }
        #     {
        #         "txid": ["TX_ID_HERE"],
        #         "descr": {"order":"buy 0.12345678 ETHEUR @ market"},
        #     }
        #
        #
        # createOrder for stop orders
        #
        #     {
        #         "txid":["OSILNC-VQI5Q-775ZDQ"],
        #         "descr":{"order":"sell 167.28002676 ADAXBT @ stop loss 0.00003280 -> limit 0.00003212"}
        #     }
        #
        #
        #     {
        #         "txid":["OVHMJV-BZW2V-6NZFWF"],
        #         "descr":{"order":"sell 0.00100000 ETHUSD @ stop loss 2677.00 -> limit 2577.00 with 5:1 leverage"}
        #     }
        #
        # editOrder
        #
        #     {
        #         "status": "ok",
        #         "txid": "OAW2BO-7RWEK-PZY5UO",
        #         "originaltxid": "OXL6SS-UPNMC-26WBE7",
        #         "volume": "0.00075000",
        #         "price": "13500.0",
        #         "orders_cancelled": 1,
        #         "descr": {
        #             "order": "buy 0.00075000 XBTUSDT @ limit 13500.0"
        #         }
        #     }
        #  ws - createOrder
        #    {
        #        "descr": 'sell 0.00010000 XBTUSDT @ market',
        #        "event": 'addOrderStatus',
        #        "reqid": 1,
        #        "status": 'ok',
        #        "txid": 'OAVXZH-XIE54-JCYYDG'
        #    }
        #  ws - editOrder
        #    {
        #        "descr": "order edited price = 9000.00000000",
        #        "event": "editOrderStatus",
        #        "originaltxid": "O65KZW-J4AW3-VFS74A",
        #        "reqid": 3,
        #        "status": "ok",
        #        "txid": "OTI672-HJFAO-XOIPPK"
        #    }
        #
        #  {
        #      "error": [],
        #      "result": {
        #          "open": {
        #              "OXVPSU-Q726F-L3SDEP": {
        #                  "refid": null,
        #                  "userref": 0,
        #                  "status": "open",
        #                  "opentm": 1706893367.4656649,
        #                  "starttm": 0,
        #                  "expiretm": 0,
        #                  "descr": {
        #                      "pair": "XRPEUR",
        #                      "type": "sell",
        #                      "ordertype": "trailing-stop",
        #                      "price": "+50.0000%",
        #                      "price2": "0",
        #                      "leverage": "none",
        #                      "order": "sell 10.00000000 XRPEUR @ trailing stop +50.0000%",
        #                      "close": ""
        #                  },
        #                  "vol": "10.00000000",
        #                  "vol_exec": "0.00000000",
        #                  "cost": "0.00000000",
        #                  "fee": "0.00000000",
        #                  "price": "0.00000000",
        #                  "stopprice": "0.23424000",
        #                  "limitprice": "0.46847000",
        #                  "misc": "",
        #                  "oflags": "fciq",
        #                  "trigger": "index"
        #              }
        #      }
        #  }
        #
        description = self.safe_dict(order, 'descr', {})
        orderDescriptionObj = self.safe_dict(order, 'descr')  # can be null
        orderDescription = None
        if orderDescriptionObj is not None:
            orderDescription = self.safe_string(orderDescriptionObj, 'order')
        else:
            orderDescription = self.safe_string(order, 'descr')
        side = None
        type = None
        marketId = None
        price = None
        amount = None
        stopPrice = None
        if orderDescription is not None:
            parts = orderDescription.split(' ')
            side = self.safe_string(parts, 0)
            amount = self.safe_string(parts, 1)
            marketId = self.safe_string(parts, 2)
            type = self.safe_string(parts, 4)
            if type == 'stop':
                stopPrice = self.safe_string(parts, 6)
                price = self.safe_string(parts, 9)
            elif type == 'limit':
                price = self.safe_string(parts, 5)
        side = self.safe_string(description, 'type', side)
        type = self.safe_string(description, 'ordertype', type)
        marketId = self.safe_string(description, 'pair', marketId)
        foundMarket = self.find_market_by_altname_or_id(marketId)
        symbol = None
        if foundMarket is not None:
            market = foundMarket
        elif marketId is not None:
            # delisted market ids go here
            market = self.get_delisted_market_by_id(marketId)
        timestamp = self.safe_timestamp(order, 'opentm')
        amount = self.safe_string(order, 'vol', amount)
        filled = self.safe_string(order, 'vol_exec')
        fee = None
        # kraken truncates the cost in the api response so we will ignore it and calculate it from average & filled
        # cost = self.safe_string(order, 'cost')
        price = self.safe_string(description, 'price', price)
        # when type = trailling stop returns price = '+50.0000%'
        if (price is not None) and price.endswith('%'):
            price = None  # self is not the price we want
        if (price is None) or Precise.string_equals(price, '0'):
            price = self.safe_string(description, 'price2')
        if (price is None) or Precise.string_equals(price, '0'):
            price = self.safe_string(order, 'price', price)
        flags = self.safe_string(order, 'oflags', '')
        isPostOnly = flags.find('post') > -1
        average = self.safe_number(order, 'price')
        if market is not None:
            symbol = market['symbol']
            if 'fee' in order:
                feeCost = self.safe_string(order, 'fee')
                fee = {
                    'cost': feeCost,
                    'rate': None,
                }
                if flags.find('fciq') >= 0:
                    fee['currency'] = market['quote']
                elif flags.find('fcib') >= 0:
                    fee['currency'] = market['base']
        status = self.parse_order_status(self.safe_string(order, 'status'))
        id = self.safe_string_2(order, 'id', 'txid')
        if (id is None) or (id.startswith('[')):
            txid = self.safe_list(order, 'txid')
            id = self.safe_string(txid, 0)
        clientOrderId = self.safe_string(order, 'userref')
        rawTrades = self.safe_value(order, 'trades', [])
        trades = []
        for i in range(0, len(rawTrades)):
            rawTrade = rawTrades[i]
            if isinstance(rawTrade, str):
                trades.append(self.safe_trade({'id': rawTrade, 'orderId': id, 'symbol': symbol, 'info': {}}))
            else:
                trades.append(rawTrade)
        stopPrice = self.omit_zero(self.safe_string(order, 'stopprice', stopPrice))
        stopLossPrice = None
        takeProfitPrice = None
        if type.startswith('take-profit'):
            takeProfitPrice = self.safe_string(description, 'price')
            price = self.omit_zero(self.safe_string(description, 'price2'))
        elif type.startswith('stop-loss'):
            stopLossPrice = self.safe_string(description, 'price')
            price = self.omit_zero(self.safe_string(description, 'price2'))
        return self.safe_order({
            'id': id,
            'clientOrderId': clientOrderId,
            'info': order,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': None,
            'status': status,
            'symbol': symbol,
            'type': self.parse_order_type(type),
            'timeInForce': None,
            'postOnly': isPostOnly,
            'side': side,
            'price': price,
            'stopPrice': stopPrice,
            'triggerPrice': stopPrice,
            'takeProfitPrice': takeProfitPrice,
            'stopLossPrice': stopLossPrice,
            'cost': None,
            'amount': amount,
            'filled': filled,
            'average': average,
            'remaining': None,
            'reduceOnly': self.safe_bool_2(order, 'reduceOnly', 'reduce_only'),
            'fee': fee,
            'trades': trades,
        }, market)

    def order_request(self, method: str, symbol: str, type: str, request: dict, amount: Num, price: Num = None, params={}):
        clientOrderId = self.safe_string_2(params, 'userref', 'clientOrderId')
        params = self.omit(params, ['userref', 'clientOrderId'])
        if clientOrderId is not None:
            request['userref'] = clientOrderId
        stopLossTriggerPrice = self.safe_string(params, 'stopLossPrice')
        takeProfitTriggerPrice = self.safe_string(params, 'takeProfitPrice')
        isStopLossTriggerOrder = stopLossTriggerPrice is not None
        isTakeProfitTriggerOrder = takeProfitTriggerPrice is not None
        isStopLossOrTakeProfitTrigger = isStopLossTriggerOrder or isTakeProfitTriggerOrder
        trailingAmount = self.safe_string(params, 'trailingAmount')
        trailingLimitAmount = self.safe_string(params, 'trailingLimitAmount')
        isTrailingAmountOrder = trailingAmount is not None
        isLimitOrder = type.endswith('limit')  # supporting limit, stop-loss-limit, take-profit-limit, etc
        isMarketOrder = type == 'market'
        cost = self.safe_string(params, 'cost')
        flags = self.safe_string(params, 'oflags')
        params = self.omit(params, ['cost', 'oflags'])
        isViqcOrder = (flags is not None) and (flags.find('viqc') > -1)  # volume in quote currency
        if isMarketOrder and (cost is not None or isViqcOrder):
            if cost is None and (amount is not None):
                request['volume'] = self.cost_to_precision(symbol, self.number_to_string(amount))
            else:
                request['volume'] = self.cost_to_precision(symbol, cost)
            extendedOflags = flags + ',viqc' if (flags is not None) else 'viqc'
            request['oflags'] = extendedOflags
        elif isLimitOrder and not isTrailingAmountOrder:
            request['price'] = self.price_to_precision(symbol, price)
        reduceOnly = self.safe_bool_2(params, 'reduceOnly', 'reduce_only')
        if isStopLossOrTakeProfitTrigger:
            if isStopLossTriggerOrder:
                request['price'] = self.price_to_precision(symbol, stopLossTriggerPrice)
                if isLimitOrder:
                    request['ordertype'] = 'stop-loss-limit'
                else:
                    request['ordertype'] = 'stop-loss'
            elif isTakeProfitTriggerOrder:
                request['price'] = self.price_to_precision(symbol, takeProfitTriggerPrice)
                if isLimitOrder:
                    request['ordertype'] = 'take-profit-limit'
                else:
                    request['ordertype'] = 'take-profit'
            if isLimitOrder:
                request['price2'] = self.price_to_precision(symbol, price)
        elif isTrailingAmountOrder:
            trailingActivationPriceType = self.safe_string(params, 'trigger', 'last')
            trailingAmountString = '+' + trailingAmount
            request['trigger'] = trailingActivationPriceType
            if isLimitOrder or (trailingLimitAmount is not None):
                offset = self.safe_string(params, 'offset', '-')
                trailingLimitAmountString = offset + self.number_to_string(trailingLimitAmount)
                request['price'] = trailingAmountString
                request['price2'] = trailingLimitAmountString
                request['ordertype'] = 'trailing-stop-limit'
            else:
                request['price'] = trailingAmountString
                request['ordertype'] = 'trailing-stop'
        if reduceOnly:
            if method == 'createOrderWs':
                request['reduce_only'] = True  # ws request can't have stringified bool
            else:
                request['reduce_only'] = 'true'  # not using hasattr(self, boolean) case, because the urlencodedNested transforms it into 'True' string
        close = self.safe_dict(params, 'close')
        if close is not None:
            close = self.extend({}, close)
            closePrice = self.safe_value(close, 'price')
            if closePrice is not None:
                close['price'] = self.price_to_precision(symbol, closePrice)
            closePrice2 = self.safe_value(close, 'price2')  # stopPrice
            if closePrice2 is not None:
                close['price2'] = self.price_to_precision(symbol, closePrice2)
            request['close'] = close
        timeInForce = self.safe_string_2(params, 'timeInForce', 'timeinforce')
        if timeInForce is not None:
            request['timeinforce'] = timeInForce
        isMarket = (type == 'market')
        postOnly = None
        postOnly, params = self.handle_post_only(isMarket, False, params)
        if postOnly:
            extendedPostFlags = flags + ',post' if (flags is not None) else 'post'
            request['oflags'] = extendedPostFlags
        params = self.omit(params, ['timeInForce', 'reduceOnly', 'stopLossPrice', 'takeProfitPrice', 'trailingAmount', 'trailingLimitAmount', 'offset'])
        return [request, params]

    async def edit_order(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: Num = None, price: Num = None, params={}):
        """
        edit a trade order
        :see: https://docs.kraken.com/rest/#tag/Trading/operation/editOrder
        :param str id: order id
        :param str symbol: unified symbol of the market to create an order in
        :param str type: 'market' or 'limit'
        :param str side: 'buy' or 'sell'
        :param float amount: how much of the currency you want to trade in units of the base currency
        :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param float [params.stopLossPrice]: *margin only* the price that a stop loss order is triggered at
        :param float [params.takeProfitPrice]: *margin only* the price that a take profit order is triggered at
        :param str [params.trailingAmount]: *margin only* the quote price away from the current market price
        :param str [params.trailingLimitAmount]: *margin only* the quote amount away from the trailingAmount
        :param str [params.offset]: *margin only* '+' or '-' whether you want the trailingLimitAmount value to be positive or negative, default is negative '-'
        :param str [params.trigger]: *margin only* the activation price type, 'last' or 'index', default is 'last'
        :returns dict: an `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        await self.load_markets()
        market = self.market(symbol)
        if not market['spot']:
            raise NotSupported(self.id + ' editOrder() does not support ' + market['type'] + ' orders, only spot orders are accepted')
        request: dict = {
            'txid': id,
            'pair': market['id'],
        }
        if amount is not None:
            request['volume'] = self.amount_to_precision(symbol, amount)
        orderRequest = self.order_request('editOrder', symbol, type, request, amount, price, params)
        response = await self.privatePostEditOrder(self.extend(orderRequest[0], orderRequest[1]))
        #
        #     {
        #         "error": [],
        #         "result": {
        #             "status": "ok",
        #             "txid": "OAW2BO-7RWEK-PZY5UO",
        #             "originaltxid": "OXL6SS-UPNMC-26WBE7",
        #             "volume": "0.00075000",
        #             "price": "13500.0",
        #             "orders_cancelled": 1,
        #             "descr": {
        #                 "order": "buy 0.00075000 XBTUSDT @ limit 13500.0"
        #             }
        #         }
        #     }
        #
        data = self.safe_dict(response, 'result', {})
        return self.parse_order(data, market)

    async def fetch_order(self, id: str, symbol: Str = None, params={}):
        """
        fetches information on an order made by the user
        :see: https://docs.kraken.com/rest/#tag/Account-Data/operation/getOrdersInfo
        :param str symbol: not used by kraken 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()
        clientOrderId = self.safe_value_2(params, 'userref', 'clientOrderId')
        request: dict = {
            'trades': True,  # whether or not to include trades in output(optional, default False)
            # 'txid': id,  # do not comma separate a list of ids - use fetchOrdersByIds instead
            # 'userref': 'optional',  # restrict results to given user reference id(optional)
        }
        query = params
        if clientOrderId is not None:
            request['userref'] = clientOrderId
            query = self.omit(params, ['userref', 'clientOrderId'])
        else:
            request['txid'] = id
        response = await self.privatePostQueryOrders(self.extend(request, query))
        #
        #     {
        #         "error":[],
        #         "result":{
        #             "OTLAS3-RRHUF-NDWH5A":{
        #                 "refid":null,
        #                 "userref":null,
        #                 "status":"closed",
        #                 "reason":null,
        #                 "opentm":1586822919.3342,
        #                 "closetm":1586822919.365,
        #                 "starttm":0,
        #                 "expiretm":0,
        #                 "descr":{
        #                     "pair":"XBTUSDT",
        #                     "type":"sell",
        #                     "ordertype":"market",
        #                     "price":"0",
        #                     "price2":"0",
        #                     "leverage":"none",
        #                     "order":"sell 0.21804000 XBTUSDT @ market",
        #                     "close":""
        #                 },
        #                 "vol":"0.21804000",
        #                 "vol_exec":"0.21804000",
        #                 "cost":"1493.9",
        #                 "fee":"3.8",
        #                 "price":"6851.5",
        #                 "stopprice":"0.00000",
        #                 "limitprice":"0.00000",
        #                 "misc":"",
        #                 "oflags":"fciq",
        #                 "trades":["TT5UC3-GOIRW-6AZZ6R"]
        #             }
        #         }
        #     }
        #
        result = self.safe_value(response, 'result', [])
        if not (id in result):
            raise OrderNotFound(self.id + ' fetchOrder() could not find order id ' + id)
        return self.parse_order(self.extend({'id': id}, result[id]))

    async def fetch_order_trades(self, id: str, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        fetch all the trades made from a single order
        :see: https://docs.kraken.com/rest/#tag/Account-Data/operation/getTradesInfo
        :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>`
        """
        orderTrades = self.safe_value(params, 'trades')
        tradeIds = []
        if orderTrades is None:
            raise ArgumentsRequired(self.id + " fetchOrderTrades() requires a unified order structure in the params argument or a 'trades' param(an array of trade id strings)")
        else:
            for i in range(0, len(orderTrades)):
                orderTrade = orderTrades[i]
                if isinstance(orderTrade, str):
                    tradeIds.append(orderTrade)
                else:
                    tradeIds.append(orderTrade['id'])
        await self.load_markets()
        if symbol is not None:
            symbol = self.symbol(symbol)
        options = self.safe_value(self.options, 'fetchOrderTrades', {})
        batchSize = self.safe_integer(options, 'batchSize', 20)
        numTradeIds = len(tradeIds)
        numBatches = self.parse_to_int(numTradeIds / batchSize)
        numBatches = self.sum(numBatches, 1)
        result = []
        for j in range(0, numBatches):
            requestIds = []
            for k in range(0, batchSize):
                index = self.sum(j * batchSize, k)
                if index < numTradeIds:
                    requestIds.append(tradeIds[index])
            request: dict = {
                'txid': ','.join(requestIds),
            }
            response = await self.privatePostQueryTrades(request)
            #
            #     {
            #         "error": [],
            #         "result": {
            #             'TIMIRG-WUNNE-RRJ6GT': {
            #                 "ordertxid": 'OQRPN2-LRHFY-HIFA7D',
            #                 "postxid": 'TKH2SE-M7IF5-CFI7LT',
            #                 "pair": 'USDCUSDT',
            #                 "time": 1586340086.457,
            #                 "type": 'sell',
            #                 "ordertype": 'market',
            #                 "price": '0.99860000',
            #                 "cost": '22.16892001',
            #                 "fee": '0.04433784',
            #                 "vol": '22.20000000',
            #                 "margin": '0.00000000',
            #                 "misc": ''
            #             }
            #         }
            #     }
            #
            rawTrades = self.safe_value(response, 'result')
            ids = list(rawTrades.keys())
            for i in range(0, len(ids)):
                rawTrades[ids[i]]['id'] = ids[i]
            trades = self.parse_trades(rawTrades, None, since, limit)
            tradesFilteredBySymbol = self.filter_by_symbol(trades, symbol)
            result = self.array_concat(result, tradesFilteredBySymbol)
        return result

    async def fetch_orders_by_ids(self, ids, symbol: Str = None, params={}):
        """
        fetch orders by the list of order id
        :see: https://docs.kraken.com/rest/#tag/Account-Data/operation/getClosedOrders
        :param str[]|None ids: list of order id
        :param dict [params]: extra parameters specific to the kraken api endpoint
        :returns dict[]: a list of `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        await self.load_markets()
        response = await self.privatePostQueryOrders(self.extend({
            'trades': True,  # whether or not to include trades in output(optional, default False)
            'txid': ','.join(ids),  # comma delimited list of transaction ids to query info about(20 maximum)
        }, params))
        result = self.safe_value(response, 'result', {})
        orders = []
        orderIds = list(result.keys())
        for i in range(0, len(orderIds)):
            id = orderIds[i]
            item = result[id]
            order = self.parse_order(self.extend({'id': id}, item))
            orders.append(order)
        return orders

    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://docs.kraken.com/rest/#tag/Account-Data/operation/getTradeHistory
        :param str symbol: unified market symbol
        :param int [since]: the earliest time in ms to fetch trades for
        :param int [limit]: the maximum number of trades structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns Trade[]: a list of `trade structures <https://docs.ccxt_zagrava.com/#/?id=trade-structure>`
        """
        await self.load_markets()
        request: dict = {
            # 'type': 'all',  # any position, closed position, closing position, no position
            # 'trades': False,  # whether or not to include trades related to position in output
            # 'start': 1234567890,  # starting unix timestamp or trade tx id of results(exclusive)
            # 'end': 1234567890,  # ending unix timestamp or trade tx id of results(inclusive)
            # 'ofs' = result offset
        }
        if since is not None:
            request['start'] = self.parse_to_int(since / 1000)
        response = await self.privatePostTradesHistory(self.extend(request, params))
        #
        #     {
        #         "error": [],
        #         "result": {
        #             "trades": {
        #                 "GJ3NYQ-XJRTF-THZABF": {
        #                     "ordertxid": "TKH2SE-ZIF5E-CFI7LT",
        #                     "postxid": "OEN3VX-M7IF5-JNBJAM",
        #                     "pair": "XICNXETH",
        #                     "time": 1527213229.4491,
        #                     "type": "sell",
        #                     "ordertype": "limit",
        #                     "price": "0.001612",
        #                     "cost": "0.025792",
        #                     "fee": "0.000026",
        #                     "vol": "16.00000000",
        #                     "margin": "0.000000",
        #                     "misc": ""
        #                 },
        #                 ...
        #             },
        #             "count": 9760,
        #         },
        #     }
        #
        trades = response['result']['trades']
        ids = list(trades.keys())
        for i in range(0, len(ids)):
            trades[ids[i]]['id'] = ids[i]
        market = None
        if symbol is not None:
            market = self.market(symbol)
        return self.parse_trades(trades, market, since, limit)

    async def cancel_order(self, id: str, symbol: Str = None, params={}):
        """
        cancels an open order
        :see: https://docs.kraken.com/rest/#tag/Spot-Trading/operation/cancelOrder
        :param str id: order id
        :param str symbol: unified symbol of the market the order was made in
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: An `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        await self.load_markets()
        response = None
        clientOrderId = self.safe_value_2(params, 'userref', 'clientOrderId', id)
        request: dict = {
            'txid': clientOrderId,  # order id or userref
        }
        params = self.omit(params, ['userref', 'clientOrderId'])
        try:
            response = await self.privatePostCancelOrder(self.extend(request, params))
            #
            #    {
            #        error: [],
            #        result: {
            #            count: '1'
            #        }
            #    }
            #
        except Exception as e:
            if self.last_http_response:
                if self.last_http_response.find('EOrder:Unknown order') >= 0:
                    raise OrderNotFound(self.id + ' cancelOrder() error ' + self.last_http_response)
            raise e
        return self.safe_order({
            'info': response,
        })

    async def cancel_orders(self, ids, symbol: Str = None, params={}):
        """
        cancel multiple orders
        :see: https://docs.kraken.com/rest/#tag/Spot-Trading/operation/cancelOrderBatch
        :param str[] ids: open orders transaction ID(txid) or user reference(userref)
        :param str symbol: unified market symbol
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        request: dict = {
            'orders': ids,
        }
        response = await self.privatePostCancelOrderBatch(self.extend(request, params))
        #
        #     {
        #         "error": [],
        #         "result": {
        #           "count": 2
        #         }
        #     }
        #
        return [
            self.safe_order({
                'info': response,
            }),
        ]

    async def cancel_all_orders(self, symbol: Str = None, params={}):
        """
        cancel all open orders
        :see: https://docs.kraken.com/rest/#tag/Spot-Trading/operation/cancelAllOrders
        :param str symbol: unified market symbol, only orders in the market of self symbol are cancelled when symbol is not None
        :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()
        response = await self.privatePostCancelAll(params)
        #
        #    {
        #        error: [],
        #        result: {
        #            count: '1'
        #        }
        #    }
        #
        return [
            self.safe_order({
                'info': response,
            }),
        ]

    async def cancel_all_orders_after(self, timeout: Int, params={}):
        """
        dead man's switch, cancel all orders after the given timeout
        :see: https://docs.kraken.com/rest/#tag/Spot-Trading/operation/cancelAllOrdersAfter
        :param number timeout: time in milliseconds, 0 represents cancel the timer
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: the api result
        """
        if timeout > 86400000:
            raise BadRequest(self.id + 'cancelAllOrdersAfter timeout should be less than 86400000 milliseconds')
        await self.load_markets()
        request: dict = {
            'timeout': (self.parse_to_int(timeout / 1000)) if (timeout > 0) else 0,
        }
        response = await self.privatePostCancelAllOrdersAfter(self.extend(request, params))
        #
        #     {
        #         "error": [],
        #         "result": {
        #             "currentTime": "2023-03-24T17:41:56Z",
        #             "triggerTime": "2023-03-24T17:42:56Z"
        #         }
        #     }
        #
        return 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://docs.kraken.com/rest/#tag/Account-Data/operation/getOpenOrders
        :param str symbol: unified market symbol
        :param int [since]: the earliest time in ms to fetch open orders for
        :param int [limit]: the maximum number of  open orders structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns Order[]: a list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        await self.load_markets()
        request: dict = {}
        if since is not None:
            request['start'] = self.parse_to_int(since / 1000)
        query = params
        clientOrderId = self.safe_value_2(params, 'userref', 'clientOrderId')
        if clientOrderId is not None:
            request['userref'] = clientOrderId
            query = self.omit(params, ['userref', 'clientOrderId'])
        response = await self.privatePostOpenOrders(self.extend(request, query))
        market = None
        if symbol is not None:
            market = self.market(symbol)
        result = self.safe_dict(response, 'result', {})
        orders = self.safe_dict(result, 'open', {})
        return self.parse_orders(orders, market, since, limit)

    async def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
        """
        fetches information on multiple closed orders made by the user
        :see: https://docs.kraken.com/rest/#tag/Account-Data/operation/getClosedOrders
        :param str symbol: unified market symbol of the market orders were made in
        :param int [since]: the earliest time in ms to fetch orders for
        :param int [limit]: the maximum number of order structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: timestamp in ms of the latest entry
        :returns Order[]: a list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        await self.load_markets()
        request: dict = {}
        if since is not None:
            request['start'] = self.parse_to_int(since / 1000)
        query = params
        clientOrderId = self.safe_value_2(params, 'userref', 'clientOrderId')
        if clientOrderId is not None:
            request['userref'] = clientOrderId
            query = self.omit(params, ['userref', 'clientOrderId'])
        request, params = self.handle_until_option('end', request, params)
        response = await self.privatePostClosedOrders(self.extend(request, query))
        #
        #     {
        #         "error":[],
        #         "result":{
        #             "closed":{
        #                 "OETZYO-UL524-QJMXCT":{
        #                     "refid":null,
        #                     "userref":null,
        #                     "status":"canceled",
        #                     "reason":"User requested",
        #                     "opentm":1601489313.3898,
        #                     "closetm":1601489346.5507,
        #                     "starttm":0,
        #                     "expiretm":0,
        #                     "descr":{
        #                         "pair":"ETHUSDT",
        #                         "type":"buy",
        #                         "ordertype":"limit",
        #                         "price":"330.00",
        #                         "price2":"0",
        #                         "leverage":"none",
        #                         "order":"buy 0.02100000 ETHUSDT @ limit 330.00",
        #                         "close":""
        #                     },
        #                     "vol":"0.02100000",
        #                     "vol_exec":"0.00000000",
        #                     "cost":"0.00000",
        #                     "fee":"0.00000",
        #                     "price":"0.00000",
        #                     "stopprice":"0.00000",
        #                     "limitprice":"0.00000",
        #                     "misc":"",
        #                     "oflags":"fciq"
        #                 },
        #             },
        #             "count":16
        #         }
        #     }
        #
        market = None
        if symbol is not None:
            market = self.market(symbol)
        result = self.safe_dict(response, 'result', {})
        orders = self.safe_dict(result, 'closed', {})
        return self.parse_orders(orders, market, since, limit)

    def parse_transaction_status(self, status: Str):
        # IFEX transaction states
        statuses: dict = {
            'Initial': 'pending',
            'Pending': 'pending',
            'Success': 'ok',
            'Settled': 'pending',
            'Failure': 'failed',
            'Partial': 'ok',
        }
        return self.safe_string(statuses, status, status)

    def parse_network(self, network):
        withdrawMethods = self.safe_value(self.options, 'withdrawMethods', {})
        return self.safe_string(withdrawMethods, network, network)

    def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction:
        #
        # fetchDeposits
        #
        #     {
        #         "method": "Ether(Hex)",
        #         "aclass": "currency",
        #         "asset": "XETH",
        #         "refid": "Q2CANKL-LBFVEE-U4Y2WQ",
        #         "txid": "0x57fd704dab1a73c20e24c8696099b695d596924b401b261513cfdab23…",
        #         "info": "0x615f9ba7a9575b0ab4d571b2b36b1b324bd83290",
        #         "amount": "7.9999257900",
        #         "fee": "0.0000000000",
        #         "time":  1529223212,
        #         "status": "Success"
        #     }
        #
        # there can be an additional 'status-prop' field present
        # deposit pending review by exchange => 'on-hold'
        # the deposit is initiated by the exchange => 'return'
        #
        #      {
        #          "type": 'deposit',
        #          "method": 'Fidor Bank AG(Wire Transfer)',
        #          "aclass": 'currency',
        #          "asset": 'ZEUR',
        #          "refid": 'xxx-xxx-xxx',
        #          "txid": '12341234',
        #          "info": 'BANKCODEXXX',
        #          "amount": '38769.08',
        #          "fee": '0.0000',
        #          "time": 1644306552,
        #          "status": 'Success',
        #          status-prop: 'on-hold'
        #      }
        #
        #
        # fetchWithdrawals
        #
        #     {
        #         "method": "Ether",
        #         "aclass": "currency",
        #         "asset": "XETH",
        #         "refid": "A2BF34S-O7LBNQ-UE4Y4O",
        #         "txid": "0x288b83c6b0904d8400ef44e1c9e2187b5c8f7ea3d838222d53f701a15b5c274d",
        #         "info": "0x7cb275a5e07ba943fee972e165d80daa67cb2dd0",
        #         "amount": "9.9950000000",
        #         "fee": "0.0050000000",
        #         "time":  1530481750,
        #         "status": "Success"
        #         "key":"Huobi wallet",
        #         "network":"Tron"
        #         status-prop: 'on-hold'  # self field might not be present in some cases
        #     }
        #
        # withdraw
        #
        #     {
        #         "refid": "AGBSO6T-UFMTTQ-I7KGS6"
        #     }
        #
        id = self.safe_string(transaction, 'refid')
        txid = self.safe_string(transaction, 'txid')
        timestamp = self.safe_timestamp(transaction, 'time')
        currencyId = self.safe_string(transaction, 'asset')
        code = self.safe_currency_code(currencyId, currency)
        address = self.safe_string(transaction, 'info')
        amount = self.safe_number(transaction, 'amount')
        status = self.parse_transaction_status(self.safe_string(transaction, 'status'))
        statusProp = self.safe_string(transaction, 'status-prop')
        isOnHoldDeposit = statusProp == 'on-hold'
        isCancellationRequest = statusProp == 'cancel-pending'
        isOnHoldWithdrawal = statusProp == 'onhold'
        if isOnHoldDeposit or isCancellationRequest or isOnHoldWithdrawal:
            status = 'pending'
        type = self.safe_string(transaction, 'type')  # injected from the outside
        feeCost = self.safe_number(transaction, 'fee')
        if feeCost is None:
            if type == 'deposit':
                feeCost = 0
        return {
            'info': transaction,
            'id': id,
            'currency': code,
            'amount': amount,
            'network': self.parse_network(self.safe_string(transaction, 'network')),
            'address': address,
            'addressTo': None,
            'addressFrom': None,
            'tag': None,
            'tagTo': None,
            'tagFrom': None,
            'status': status,
            'type': type,
            'updated': None,
            'txid': txid,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'comment': None,
            'internal': None,
            'fee': {
                'currency': code,
                'cost': feeCost,
            },
        }

    def parse_transactions_by_type(self, type, transactions, code: Str = None, since: Int = None, limit: Int = None):
        result = []
        for i in range(0, len(transactions)):
            transaction = self.parse_transaction(self.extend({
                'type': type,
            }, transactions[i]))
            result.append(transaction)
        return self.filter_by_currency_since_limit(result, code, since, limit)

    async def fetch_deposits(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
        """
        fetch all deposits made to an account
        :see: https://docs.kraken.com/rest/#tag/Funding/operation/getStatusRecentDeposits
        :param str code: unified currency code
        :param int [since]: the earliest time in ms to fetch deposits for
        :param int [limit]: the maximum number of deposits structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: a list of `transaction structures <https://docs.ccxt_zagrava.com/#/?id=transaction-structure>`
        """
        # https://www.kraken.com/en-us/help/api#deposit-status
        if code is None:
            raise ArgumentsRequired(self.id + ' fetchDeposits() requires a currency code argument')
        await self.load_markets()
        currency = self.currency(code)
        request: dict = {
            'asset': currency['id'],
        }
        if since is not None:
            request['start'] = since
        response = await self.privatePostDepositStatus(self.extend(request, params))
        #
        #     { error: [],
        #       "result": [{"method": "Ether(Hex)",
        #                     "aclass": "currency",
        #                      "asset": "XETH",
        #                      "refid": "Q2CANKL-LBFVEE-U4Y2WQ",
        #                       "txid": "0x57fd704dab1a73c20e24c8696099b695d596924b401b261513cfdab23…",
        #                       "info": "0x615f9ba7a9575b0ab4d571b2b36b1b324bd83290",
        #                     "amount": "7.9999257900",
        #                        "fee": "0.0000000000",
        #                       "time":  1529223212,
        #                     "status": "Success"                                                       }]}
        #
        return self.parse_transactions_by_type('deposit', response['result'], code, since, limit)

    async def fetch_time(self, params={}):
        """
        fetches the current integer timestamp in milliseconds from the exchange server
        :see: https://docs.kraken.com/rest/#tag/Spot-Market-Data/operation/getServerTime
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns int: the current integer timestamp in milliseconds from the exchange server
        """
        # https://www.kraken.com/en-us/features/api#get-server-time
        response = await self.publicGetTime(params)
        #
        #    {
        #        "error": [],
        #        "result": {
        #            "unixtime": 1591502873,
        #            "rfc1123": "Sun,  7 Jun 20 04:07:53 +0000"
        #        }
        #    }
        #
        result = self.safe_value(response, 'result', {})
        return self.safe_timestamp(result, 'unixtime')

    async def fetch_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
        """
        fetch all withdrawals made from an account
        :see: https://docs.kraken.com/rest/#tag/Funding/operation/getStatusRecentWithdrawals
        :param str code: unified currency code
        :param int [since]: the earliest time in ms to fetch withdrawals for
        :param int [limit]: the maximum number of withdrawals structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param dict [params.end]: End timestamp, withdrawals created strictly after will be not be included in the response
        :param boolean [params.paginate]:  default False, when True will automatically paginate by calling self endpoint multiple times
        :returns dict[]: a list of `transaction structures <https://docs.ccxt_zagrava.com/#/?id=transaction-structure>`
        """
        await self.load_markets()
        paginate = False
        paginate, params = self.handle_option_and_params(params, 'fetchWithdrawals', 'paginate')
        if paginate:
            params['cursor'] = True
            return await self.fetch_paginated_call_cursor('fetchWithdrawals', code, since, limit, params, 'next_cursor', 'cursor')
        request: dict = {}
        if code is not None:
            currency = self.currency(code)
            request['asset'] = currency['id']
        if since is not None:
            request['since'] = str(since)
        response = await self.privatePostWithdrawStatus(self.extend(request, params))
        #
        # with no pagination
        #     { error: [],
        #       "result": [{"method": "Ether",
        #                     "aclass": "currency",
        #                      "asset": "XETH",
        #                      "refid": "A2BF34S-O7LBNQ-UE4Y4O",
        #                       "txid": "0x298c83c7b0904d8400ef43e1c9e2287b518f7ea3d838822d53f704a1565c274d",
        #                       "info": "0x7cb275a5e07ba943fee972e165d80daa67cb2dd0",
        #                     "amount": "9.9950000000",
        #                        "fee": "0.0050000000",
        #                       "time":  1530481750,
        #                     "status": "Success"                                                             }]}
        # with pagination
        #    {
        #        "error":[],
        #        "result":{
        #           "withdrawals":[
        #              {
        #                 "method":"Tether USD(TRC20)",
        #                 "aclass":"currency",
        #                 "asset":"USDT",
        #                 "refid":"BSNFZU2-MEFN4G-J3NEZV",
        #                 "txid":"1c7a642fb7387bbc2c6a2c509fd1ae146937f4cf793b4079a4f0715e3a02615a",
        #                 "info":"TQmdxSuC16EhFg8FZWtYgrfFRosoRF7bCp",
        #                 "amount":"1996.50000000",
        #                 "fee":"2.50000000",
        #                 "time":1669126657,
        #                 "status":"Success",
        #                 "key":"poloniex",
        #                 "network":"Tron"
        #              },
        #             ...
        #           ],
        #           "next_cursor":"HgAAAAAAAABGVFRSd3k1LVF4Y0JQY05Gd0xRY0NxenFndHpybkwBAQH2AwEBAAAAAQAAAAAAAAABAAAAAAAZAAAAAAAAAA=="
        #        }
        #     }
        #
        rawWithdrawals = None
        result = self.safe_value(response, 'result')
        if not isinstance(result, list):
            rawWithdrawals = self.add_pagination_cursor_to_result(result)
        else:
            rawWithdrawals = result
        return self.parse_transactions_by_type('withdrawal', rawWithdrawals, code, since, limit)

    def add_pagination_cursor_to_result(self, result):
        cursor = self.safe_string(result, 'next_cursor')
        data = self.safe_value(result, 'withdrawals')
        dataLength = len(data)
        if cursor is not None and dataLength > 0:
            last = data[dataLength - 1]
            last['next_cursor'] = cursor
            data[dataLength - 1] = last
        return data

    async def create_deposit_address(self, code: str, params={}):
        """
        create a currency deposit address
        :see: https://docs.kraken.com/rest/#tag/Funding/operation/getDepositAddresses
        :param str code: unified currency code of the currency for the deposit address
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an `address structure <https://docs.ccxt_zagrava.com/#/?id=address-structure>`
        """
        request: dict = {
            'new': 'true',
        }
        return await self.fetch_deposit_address(code, self.extend(request, params))

    async def fetch_deposit_methods(self, code: str, params={}):
        """
        fetch deposit methods for a currency associated with self account
        :see: https://docs.kraken.com/rest/#tag/Funding/operation/getDepositMethods
        :param str code: unified currency code
        :param dict [params]: extra parameters specific to the kraken api endpoint
        :returns dict: of deposit methods
        """
        await self.load_markets()
        currency = self.currency(code)
        request: dict = {
            'asset': currency['id'],
        }
        response = await self.privatePostDepositMethods(self.extend(request, params))
        #
        #     {
        #         "error":[],
        #         "result":[
        #             {"method":"Ether(Hex)","limit":false,"gen-address":true}
        #         ]
        #     }
        #
        #     {
        #         "error":[],
        #         "result":[
        #             {"method":"Tether USD(ERC20)","limit":false,"address-setup-fee":"0.00000000","gen-address":true},
        #             {"method":"Tether USD(TRC20)","limit":false,"address-setup-fee":"0.00000000","gen-address":true}
        #         ]
        #     }
        #
        #     {
        #         "error":[],
        #         "result":[
        #             {"method":"Bitcoin","limit":false,"fee":"0.0000000000","gen-address":true}
        #         ]
        #     }
        #
        return self.safe_value(response, 'result')

    async def fetch_deposit_address(self, code: str, params={}):
        """
        fetch the deposit address for a currency associated with self account
        :see: https://docs.kraken.com/rest/#tag/Funding/operation/getDepositAddresses
        :param str code: unified currency code
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an `address structure <https://docs.ccxt_zagrava.com/#/?id=address-structure>`
        """
        await self.load_markets()
        currency = self.currency(code)
        network = self.safe_string_upper(params, 'network')
        networks = self.safe_value(self.options, 'networks', {})
        network = self.safe_string(networks, network, network)  # support ETH > ERC20 aliases
        params = self.omit(params, 'network')
        if (code == 'USDT') and (network == 'TRC20'):
            code = code + '-' + network
        defaultDepositMethods = self.safe_value(self.options, 'depositMethods', {})
        defaultDepositMethod = self.safe_string(defaultDepositMethods, code)
        depositMethod = self.safe_string(params, 'method', defaultDepositMethod)
        # if the user has specified an exchange-specific method in params
        # we pass it, otherwise we take the 'network' unified param
        if depositMethod is None:
            depositMethods = await self.fetch_deposit_methods(code)
            if network is not None:
                # find best matching deposit method, or fallback to the first one
                for i in range(0, len(depositMethods)):
                    entry = self.safe_string(depositMethods[i], 'method')
                    if entry.find(network) >= 0:
                        depositMethod = entry
                        break
            # if depositMethod was not specified, fallback to the first available deposit method
            if depositMethod is None:
                firstDepositMethod = self.safe_value(depositMethods, 0, {})
                depositMethod = self.safe_string(firstDepositMethod, 'method')
        request: dict = {
            'asset': currency['id'],
            'method': depositMethod,
        }
        response = await self.privatePostDepositAddresses(self.extend(request, params))
        #
        #     {
        #         "error":[],
        #         "result":[
        #             {"address":"0x77b5051f97efa9cc52c9ad5b023a53fc15c200d3","expiretm":"0"}
        #         ]
        #     }
        #
        result = self.safe_value(response, 'result', [])
        firstResult = self.safe_value(result, 0, {})
        if firstResult is None:
            raise InvalidAddress(self.id + ' privatePostDepositAddresses() returned no addresses for ' + code)
        return self.parse_deposit_address(firstResult, currency)

    def parse_deposit_address(self, depositAddress, currency: Currency = None):
        #
        #     {
        #         "address":"0x77b5051f97efa9cc52c9ad5b023a53fc15c200d3",
        #         "expiretm":"0"
        #     }
        #
        address = self.safe_string(depositAddress, 'address')
        tag = self.safe_string(depositAddress, 'tag')
        currency = self.safe_currency(None, currency)
        code = currency['code']
        self.check_address(address)
        return {
            'currency': code,
            'address': address,
            'tag': tag,
            'network': None,
            'info': depositAddress,
        }

    async def withdraw(self, code: str, amount: float, address: str, tag=None, params={}):
        """
        make a withdrawal
        :see: https://docs.kraken.com/rest/#tag/Funding/operation/withdrawFunds
        :param str code: unified currency code
        :param float amount: the amount to withdraw
        :param str address: the address to withdraw to
        :param str tag:
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `transaction structure <https://docs.ccxt_zagrava.com/#/?id=transaction-structure>`
        """
        tag, params = self.handle_withdraw_tag_and_params(tag, params)
        self.check_address(address)
        if 'key' in params:
            await self.load_markets()
            currency = self.currency(code)
            request: dict = {
                'asset': currency['id'],
                'amount': amount,
                'address': address,
            }
            response = await self.privatePostWithdraw(self.extend(request, params))
            #
            #     {
            #         "error": [],
            #         "result": {
            #             "refid": "AGBSO6T-UFMTTQ-I7KGS6"
            #         }
            #     }
            #
            result = self.safe_dict(response, 'result', {})
            return self.parse_transaction(result, currency)
        raise ExchangeError(self.id + " withdraw() requires a 'key' parameter(withdrawal key name, up on your account)")

    async def fetch_positions(self, symbols: Strings = None, params={}):
        """
        fetch all open positions
        :see: https://docs.kraken.com/rest/#tag/Account-Data/operation/getOpenPositions
        :param str[] [symbols]: not used by kraken fetchPositions()
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: a list of `position structure <https://docs.ccxt_zagrava.com/#/?id=position-structure>`
        """
        await self.load_markets()
        request: dict = {
            # 'txid': 'comma delimited list of transaction ids to restrict output to',
            'docalcs': 'true',  # whether or not to include profit/loss calculations
            'consolidation': 'market',  # what to consolidate the positions data around, market will consolidate positions based on market pair
        }
        response = await self.privatePostOpenPositions(self.extend(request, params))
        #
        # no consolidation
        #
        #     {
        #         "error": [],
        #         "result": {
        #             'TGUFMY-FLESJ-VYIX3J': {
        #                 "ordertxid": "O3LRNU-ZKDG5-XNCDFR",
        #                 "posstatus": "open",
        #                 "pair": "ETHUSDT",
        #                 "time":  1611557231.4584,
        #                 "type": "buy",
        #                 "ordertype": "market",
        #                 "cost": "28.49800",
        #                 "fee": "0.07979",
        #                 "vol": "0.02000000",
        #                 "vol_closed": "0.00000000",
        #                 "margin": "14.24900",
        #                 "terms": "0.0200% per 4 hours",
        #                 "rollovertm": "1611571631",
        #                 "misc": "",
        #                 "oflags": ""
        #             }
        #         }
        #     }
        #
        # consolidation by market
        #
        #     {
        #         "error": [],
        #         "result": [
        #             {
        #                 "pair": "ETHUSDT",
        #                 "positions": "1",
        #                 "type": "buy",
        #                 "leverage": "2.00000",
        #                 "cost": "28.49800",
        #                 "fee": "0.07979",
        #                 "vol": "0.02000000",
        #                 "vol_closed": "0.00000000",
        #                 "margin": "14.24900"
        #             }
        #         ]
        #     }
        #
        symbols = self.market_symbols(symbols)
        result = self.safe_list(response, 'result')
        results = self.parse_positions(result, symbols)
        return self.filter_by_array_positions(results, 'symbol', symbols, False)

    def parse_position(self, position: dict, market: Market = None):
        #
        #             {
        #                 "pair": "ETHUSDT",
        #                 "positions": "1",
        #                 "type": "buy",
        #                 "leverage": "2.00000",
        #                 "cost": "28.49800",
        #                 "fee": "0.07979",
        #                 "vol": "0.02000000",
        #                 "vol_closed": "0.00000000",
        #                 "margin": "14.24900"
        #             }
        #
        marketId = self.safe_string(position, 'pair')
        rawSide = self.safe_string(position, 'type')
        side = 'long' if (rawSide == 'buy') else 'short'
        return self.safe_position({
            'info': position,
            'id': None,
            'symbol': self.safe_symbol(marketId, market),
            'notional': None,
            'marginMode': None,
            'liquidationPrice': None,
            'entryPrice': None,
            'unrealizedPnl': self.safe_number(position, 'net'),
            'realizedPnl': None,
            'percentage': None,
            'contracts': self.safe_number(position, 'vol'),
            'contractSize': None,
            'markPrice': None,
            'lastPrice': None,
            'side': side,
            'hedged': None,
            'timestamp': None,
            'datetime': None,
            'lastUpdateTimestamp': None,
            'maintenanceMargin': None,
            'maintenanceMarginPercentage': None,
            'collateral': None,
            'initialMargin': self.safe_number(position, 'margin'),
            'initialMarginPercentage': None,
            'leverage': self.safe_number(position, 'leverage'),
            'marginRatio': None,
            'stopLossPrice': None,
            'takeProfitPrice': None,
        })

    def parse_account_type(self, account):
        accountByType: dict = {
            'spot': 'Spot Wallet',
            'swap': 'Futures Wallet',
            'future': 'Futures Wallet',
        }
        return self.safe_string(accountByType, account, account)

    async def transfer_out(self, code: str, amount, params={}):
        """
        transfer from spot wallet to futures wallet
        :see: https://docs.kraken.com/rest/#tag/User-Funding/operation/walletTransfer
        :param str code: Unified currency code
        :param float amount: Size of the transfer
        :param dict [params]: Exchange specific parameters
        :returns: a `transfer structure <https://docs.ccxt_zagrava.com/#/?id=transfer-structure>`
        """
        return await self.transfer(code, amount, 'spot', 'swap', params)

    async def transfer(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}) -> TransferEntry:
        """
        :see: https://docs.kraken.com/rest/#tag/User-Funding/operation/walletTransfer
        transfers currencies between sub-accounts(only spot->swap direction is supported)
        :param str code: Unified currency code
        :param float amount: Size of the transfer
        :param str fromAccount: 'spot' or 'Spot Wallet'
        :param str toAccount: 'swap' or 'Futures Wallet'
        :param dict [params]: Exchange specific parameters
        :returns: a `transfer structure <https://docs.ccxt_zagrava.com/#/?id=transfer-structure>`
        """
        await self.load_markets()
        currency = self.currency(code)
        fromAccount = self.parse_account_type(fromAccount)
        toAccount = self.parse_account_type(toAccount)
        request: dict = {
            'amount': self.currency_to_precision(code, amount),
            'from': fromAccount,
            'to': toAccount,
            'asset': currency['id'],
        }
        if fromAccount != 'Spot Wallet':
            raise BadRequest(self.id + ' transfer cannot transfer from ' + fromAccount + ' to ' + toAccount + '. Use krakenfutures instead to transfer from the futures account.')
        response = await self.privatePostWalletTransfer(self.extend(request, params))
        #
        #   {
        #       "error":[
        #       ],
        #       "result":{
        #          "refid":"BOIUSIF-M7DLMN-UXZ3P5"
        #       }
        #   }
        #
        transfer = self.parse_transfer(response, currency)
        return self.extend(transfer, {
            'amount': amount,
            'fromAccount': fromAccount,
            'toAccount': toAccount,
        })

    def parse_transfer(self, transfer: dict, currency: Currency = None) -> TransferEntry:
        #
        # transfer
        #
        #    {
        #        "error":[
        #        ],
        #        "result":{
        #           "refid":"BOIUSIF-M7DLMN-UXZ3P5"
        #        }
        #    }
        #
        result = self.safe_value(transfer, 'result', {})
        refid = self.safe_string(result, 'refid')
        return {
            'info': transfer,
            'id': refid,
            'timestamp': None,
            'datetime': None,
            'currency': self.safe_string(currency, 'code'),
            'amount': None,
            'fromAccount': None,
            'toAccount': None,
            'status': 'sucess',
        }

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        url = '/' + self.version + '/' + api + '/' + path
        if api == 'public':
            if params:
                # urlencodeNested is used to address https://github.com/ccxt_zagrava/ccxt_zagrava/issues/12872
                url += '?' + self.urlencode_nested(params)
        elif api == 'private':
            isCancelOrderBatch = (path == 'CancelOrderBatch')
            self.check_required_credentials()
            nonce = str(self.nonce())
            # urlencodeNested is used to address https://github.com/ccxt_zagrava/ccxt_zagrava/issues/12872
            if isCancelOrderBatch:
                body = self.json(self.extend({'nonce': nonce}, params))
            else:
                body = self.urlencode_nested(self.extend({'nonce': nonce}, params))
            auth = self.encode(nonce + body)
            hash = self.hash(auth, 'sha256', 'binary')
            binary = self.encode(url)
            binhash = self.binary_concat(binary, hash)
            secret = self.base64_to_binary(self.secret)
            signature = self.hmac(binhash, secret, hashlib.sha512, 'base64')
            headers = {
                'API-Key': self.apiKey,
                'API-Sign': signature,
                # 'Content-Type': 'application/x-www-form-urlencoded',
            }
            if isCancelOrderBatch:
                headers['Content-Type'] = 'application/json'
            else:
                headers['Content-Type'] = 'application/x-www-form-urlencoded'
        else:
            url = '/' + path
        url = self.urls['api'][api] + url
        return {'url': url, 'method': method, 'body': body, 'headers': headers}

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

    def handle_errors(self, code: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
        if code == 520:
            raise ExchangeNotAvailable(self.id + ' ' + str(code) + ' ' + reason)
        # todo: rewrite self for "broad" exceptions matching
        if body.find('Invalid order') >= 0:
            raise InvalidOrder(self.id + ' ' + body)
        if body.find('Invalid nonce') >= 0:
            raise InvalidNonce(self.id + ' ' + body)
        if body.find('Insufficient funds') >= 0:
            raise InsufficientFunds(self.id + ' ' + body)
        if body.find('Cancel pending') >= 0:
            raise CancelPending(self.id + ' ' + body)
        if body.find('Invalid arguments:volume') >= 0:
            raise InvalidOrder(self.id + ' ' + body)
        if body.find('Invalid arguments:viqc') >= 0:
            raise InvalidOrder(self.id + ' ' + body)
        if body.find('Rate limit exceeded') >= 0:
            raise RateLimitExceeded(self.id + ' ' + body)
        if response is None:
            return None
        if body[0] == '{':
            if not isinstance(response, str):
                if 'error' in response:
                    numErrors = len(response['error'])
                    if numErrors:
                        message = self.id + ' ' + body
                        for i in range(0, len(response['error'])):
                            error = response['error'][i]
                            self.throw_exactly_matched_exception(self.exceptions, error, message)
                        raise ExchangeError(message)
        return None
