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

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

from ccxt.async_support.base.exchange import Exchange
from ccxt.abstract.krakenfutures import ImplicitAPI
import hashlib
from ccxt.base.types import Balances, Currency, Int, Leverage, Leverages, LeverageTier, LeverageTiers, Market, Num, Order, OrderBook, OrderRequest, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, TransferEntry
from typing import List
from ccxt.base.errors import ExchangeError
from ccxt.base.errors import AuthenticationError
from ccxt.base.errors import ArgumentsRequired
from ccxt.base.errors import BadRequest
from ccxt.base.errors import InsufficientFunds
from ccxt.base.errors import InvalidOrder
from ccxt.base.errors import OrderNotFound
from ccxt.base.errors import OrderImmediatelyFillable
from ccxt.base.errors import OrderNotFillable
from ccxt.base.errors import DuplicateOrderId
from ccxt.base.errors import ContractUnavailable
from ccxt.base.errors import DDoSProtection
from ccxt.base.errors import RateLimitExceeded
from ccxt.base.errors import ExchangeNotAvailable
from ccxt.base.errors import InvalidNonce
from ccxt.base.decimal_to_precision import TICK_SIZE
from ccxt.base.precise import Precise


class krakenfutures(Exchange, ImplicitAPI):

    def describe(self):
        return self.deep_extend(super(krakenfutures, self).describe(), {
            'id': 'krakenfutures',
            'name': 'Kraken Futures',
            'countries': ['US'],
            'version': 'v3',
            'userAgent': None,
            'rateLimit': 600,
            'pro': True,
            'has': {
                'CORS': None,
                'spot': False,
                'margin': False,
                'swap': True,
                'future': True,
                'option': False,
                'cancelAllOrders': True,
                'cancelAllOrdersAfter': True,
                'cancelOrder': True,
                'cancelOrders': True,
                'createMarketOrder': False,
                'createOrder': True,
                'editOrder': True,
                'fetchBalance': True,
                'fetchBorrowRateHistories': False,
                'fetchBorrowRateHistory': False,
                'fetchCanceledOrders': True,
                'fetchClosedOrders': True,  # https://support.kraken.com/hc/en-us/articles/360058243651-Historical-orders
                'fetchCrossBorrowRate': False,
                'fetchCrossBorrowRates': False,
                'fetchDepositAddress': False,
                'fetchDepositAddresses': False,
                'fetchDepositAddressesByNetwork': False,
                'fetchFundingHistory': None,
                'fetchFundingRate': 'emulated',
                'fetchFundingRateHistory': True,
                'fetchFundingRates': True,
                'fetchIndexOHLCV': False,
                'fetchIsolatedBorrowRate': False,
                'fetchIsolatedBorrowRates': False,
                'fetchIsolatedPositions': False,
                'fetchLeverage': True,
                'fetchLeverages': True,
                'fetchLeverageTiers': True,
                'fetchMarketLeverageTiers': 'emulated',
                'fetchMarkets': True,
                'fetchMarkOHLCV': True,
                'fetchMyTrades': True,
                'fetchOHLCV': True,
                'fetchOpenOrders': True,
                'fetchOrder': False,
                'fetchOrderBook': True,
                'fetchOrders': False,
                'fetchPositions': True,
                'fetchPremiumIndexOHLCV': False,
                'fetchTickers': True,
                'fetchTrades': True,
                'sandbox': True,
                'setLeverage': True,
                'setMarginMode': False,
                'transfer': True,
            },
            'urls': {
                'test': {
                    'public': 'https://demo-futures.kraken.com/derivatives/api/',
                    'private': 'https://demo-futures.kraken.com/derivatives/api/',
                    'charts': 'https://demo-futures.kraken.com/api/charts/',
                    'www': 'https://demo-futures.kraken.com',
                },
                'logo': 'https://user-images.githubusercontent.com/24300605/81436764-b22fd580-9172-11ea-9703-742783e6376d.jpg',
                'api': {
                    'charts': 'https://futures.kraken.com/api/charts/',
                    'history': 'https://futures.kraken.com/api/history/',
                    'feeschedules': 'https://futures.kraken.com/api/feeschedules/',
                    'public': 'https://futures.kraken.com/derivatives/api/',
                    'private': 'https://futures.kraken.com/derivatives/api/',
                },
                'www': 'https://futures.kraken.com/',
                'doc': [
                    'https://docs.futures.kraken.com/#introduction',
                ],
                'fees': 'https://support.kraken.com/hc/en-us/articles/360022835771-Transaction-fees-and-rebates-for-Kraken-Futures',
                'referral': None,
            },
            'api': {
                'public': {
                    'get': [
                        'feeschedules',
                        'instruments',
                        'orderbook',
                        'tickers',
                        'history',
                        'historicalfundingrates',
                    ],
                },
                'private': {
                    'get': [
                        'feeschedules/volumes',
                        'openpositions',
                        'notifications',
                        'accounts',
                        'openorders',
                        'recentorders',
                        'fills',
                        'transfers',
                        'leveragepreferences',
                        'pnlpreferences',
                    ],
                    'post': [
                        'sendorder',
                        'editorder',
                        'cancelorder',
                        'transfer',
                        'batchorder',
                        'cancelallorders',
                        'cancelallordersafter',
                        'withdrawal',                              # for futures wallet -> kraken spot wallet
                    ],
                    'put': [
                        'leveragepreferences',
                        'pnlpreferences',
                    ],
                },
                'charts': {
                    'get': [
                        '{price_type}/{symbol}/{interval}',
                    ],
                },
                'history': {
                    'get': [
                        'orders',
                        'executions',
                        'triggers',
                        'accountlogcsv',
                        'account-log',
                        'market/{symbol}/orders',
                        'market/{symbol}/executions',
                    ],
                },
            },
            'fees': {
                'trading': {
                    'tierBased': True,
                    'percentage': True,
                    'taker': self.parse_number('0.0005'),
                    'maker': self.parse_number('0.0002'),
                    'tiers': {
                        'taker': [
                            [self.parse_number('0'), self.parse_number('0.0005')],
                            [self.parse_number('100000'), self.parse_number('0.0004')],
                            [self.parse_number('1000000'), self.parse_number('0.0003')],
                            [self.parse_number('5000000'), self.parse_number('0.00025')],
                            [self.parse_number('10000000'), self.parse_number('0.0002')],
                            [self.parse_number('20000000'), self.parse_number('0.00015')],
                            [self.parse_number('50000000'), self.parse_number('0.000125')],
                            [self.parse_number('100000000'), self.parse_number('0.0001')],
                        ],
                        'maker': [
                            [self.parse_number('0'), self.parse_number('0.0002')],
                            [self.parse_number('100000'), self.parse_number('0.0015')],
                            [self.parse_number('1000000'), self.parse_number('0.000125')],
                            [self.parse_number('5000000'), self.parse_number('0.0001')],
                            [self.parse_number('10000000'), self.parse_number('0.000075')],
                            [self.parse_number('20000000'), self.parse_number('0.00005')],
                            [self.parse_number('50000000'), self.parse_number('0.000025')],
                            [self.parse_number('100000000'), self.parse_number('0')],
                        ],
                    },
                },
            },
            'exceptions': {
                'exact': {
                    'apiLimitExceeded': RateLimitExceeded,
                    'marketUnavailable': ContractUnavailable,
                    'requiredArgumentMissing': BadRequest,
                    'unavailable': ExchangeNotAvailable,
                    'authenticationError': AuthenticationError,
                    'accountInactive': ExchangeError,              # When account has no trade history / no order history. Should self error be ignored in some cases?
                    'invalidAccount': BadRequest,                  # the fromAccount or the toAccount are invalid
                    'invalidAmount': BadRequest,
                    'insufficientFunds': InsufficientFunds,
                    'Bad Request': BadRequest,                     # The URL contains invalid characters.(Please encode the json URL parameter)
                    'Unavailable': InsufficientFunds,              # Insufficient funds in Futures account [withdraw]
                    'invalidUnit': BadRequest,
                    'Json Parse Error': ExchangeError,
                    'nonceBelowThreshold': InvalidNonce,
                    'nonceDuplicate': InvalidNonce,
                    'notFound': BadRequest,
                    'Server Error': ExchangeError,
                    'unknownError': ExchangeError,
                },
                'broad': {
                    'invalidArgument': BadRequest,
                    'nonceBelowThreshold': InvalidNonce,
                    'nonceDuplicate': InvalidNonce,
                },
            },
            'precisionMode': TICK_SIZE,
            'options': {
                'access': {
                    'history': {
                        'GET': {
                            'orders': 'private',
                            'executions': 'private',
                            'triggers': 'private',
                            'accountlogcsv': 'private',
                        },
                    },
                },
                'settlementCurrencies': {
                    'flex': ['USDT', 'BTC', 'USD', 'GBP', 'EUR', 'USDC'],
                },
                'symbol': {
                    'quoteIds': ['USD', 'XBT'],
                    'reversed': False,
                },
                'versions': {
                    'public': {
                        'GET': {
                            'historicalfundingrates': 'v4',
                        },
                    },
                    'charts': {
                        'GET': {
                            '{price_type}/{symbol}/{interval}': 'v1',
                        },
                    },
                    'history': {
                        'GET': {
                            'orders': 'v2',
                            'executions': 'v2',
                            'triggers': 'v2',
                            'accountlogcsv': 'v2',
                        },
                    },
                },
                'fetchTrades': {
                    'method': 'historyGetMarketSymbolExecutions',  # historyGetMarketSymbolExecutions, publicGetHistory
                },
            },
            'timeframes': {
                '1m': '1m',
                '5m': '5m',
                '15m': '15m',
                '30m': '30m',
                '1h': '1h',
                '4h': '4h',
                '12h': '12h',
                '1d': '1d',
                '1w': '1w',
            },
        })

    async def fetch_markets(self, params={}) -> List[Market]:
        """
        Fetches the available trading markets from the exchange, Multi-collateral markets are returned markets, but can be settled in multiple currencies
        :see: https://docs.futures.kraken.com/#http-api-trading-v3-api-instrument-details-get-instruments
        :param dict [params]: exchange specific params
        :returns: An array of market structures
        """
        response = await self.publicGetInstruments(params)
        #
        #    {
        #        "result": "success",
        #        "instruments": [
        #            {
        #                "symbol": "fi_ethusd_180928",
        #                "type": "futures_inverse",  # futures_vanilla  # spot index
        #                "underlying": "rr_ethusd",
        #                "lastTradingTime": "2018-09-28T15:00:00.000Z",
        #                "tickSize": 0.1,
        #                "contractSize": 1,
        #                "tradeable": True,
        #                "marginLevels": [
        #                    {
        #                        "contracts":0,
        #                        "initialMargin":0.02,
        #                        "maintenanceMargin":0.01
        #                    },
        #                    {
        #                        "contracts":250000,
        #                        "initialMargin":0.04,
        #                        "maintenanceMargin":0.02
        #                    },
        #                    ...
        #                ],
        #                "isin": "GB00JVMLMP88",
        #                "retailMarginLevels": [
        #                    {
        #                        "contracts": 0,
        #                        "initialMargin": 0.5,
        #                        "maintenanceMargin": 0.25
        #                    }
        #                ],
        #                "tags": [],
        #            },
        #            {
        #                "symbol": "in_xbtusd",
        #                "type": "spot index",
        #                "tradeable":false
        #            }
        #        ]
        #        "serverTime": "2018-07-19T11:32:39.433Z"
        #    }
        #
        instruments = self.safe_value(response, 'instruments', [])
        result = []
        for i in range(0, len(instruments)):
            market = instruments[i]
            id = self.safe_string(market, 'symbol')
            marketType = self.safe_string(market, 'type')
            type = None
            index = (marketType.find(' index') >= 0)
            linear = None
            inverse = None
            expiry = None
            if not index:
                linear = (marketType.find('_vanilla') >= 0)
                inverse = not linear
                settleTime = self.safe_string(market, 'lastTradingTime')
                type = 'swap' if (settleTime is None) else 'future'
                expiry = self.parse8601(settleTime)
            else:
                type = 'index'
            swap = (type == 'swap')
            future = (type == 'future')
            symbol = id
            split = id.split('_')
            splitMarket = self.safe_string(split, 1)
            baseId = splitMarket[0:len(splitMarket) - 3]
            quoteId = 'usd'  # always USD
            base = self.safe_currency_code(baseId)
            quote = self.safe_currency_code(quoteId)
            # swap == perpetual
            settle = None
            settleId = None
            cvtp = self.safe_string(market, 'contractValueTradePrecision')
            amountPrecision = self.parse_number(self.integer_precision_to_amount(cvtp))
            pricePrecision = self.safe_number(market, 'tickSize')
            contract = (swap or future or index)
            swapOrFutures = (swap or future)
            if swapOrFutures:
                exchangeType = self.safe_string(market, 'type')
                if exchangeType == 'futures_inverse':
                    settle = base
                    settleId = baseId
                    inverse = True
                else:
                    settle = quote
                    settleId = quoteId
                    inverse = False
                linear = not inverse
                symbol = base + '/' + quote + ':' + settle
                if future:
                    symbol = symbol + '-' + self.yymmdd(expiry)
            result.append({
                'id': id,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'settle': settle,
                'baseId': baseId,
                'quoteId': quoteId,
                'settleId': settleId,
                'type': type,
                'spot': False,
                'margin': False,
                'swap': swap,
                'future': future,
                'option': False,
                'index': index,
                'active': None,
                'contract': contract,
                'linear': linear,
                'inverse': inverse,
                'contractSize': self.safe_number(market, 'contractSize'),
                'maintenanceMarginRate': None,
                'expiry': expiry,
                'expiryDatetime': self.iso8601(expiry),
                'strike': None,
                'optionType': None,
                'precision': {
                    'amount': amountPrecision,
                    'price': pricePrecision,
                },
                'limits': {
                    'leverage': {
                        'min': None,
                        'max': None,
                    },
                    'amount': {
                        'min': None,
                        'max': None,
                    },
                    'price': {
                        'min': None,
                        'max': None,
                    },
                    'cost': {
                        'min': None,
                        'max': None,
                    },
                },
                'created': self.parse8601(self.safe_string(market, 'openingDate')),
                'info': market,
            })
        settlementCurrencies = self.options['settlementCurrencies']['flex']
        currencies = []
        for i in range(0, len(settlementCurrencies)):
            code = settlementCurrencies[i]
            currencies.append({
                'id': code.lower(),
                'numericId': None,
                'code': code,
                'precision': None,
            })
        self.currencies = self.deep_extend(currencies, self.currencies)
        return result

    async def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
        """
        :see: https://docs.futures.kraken.com/#http-api-trading-v3-api-market-data-get-orderbook
        Fetches a list of open orders in a market
        :param str symbol: Unified market symbol
        :param int [limit]: Not used by krakenfutures
        :param dict [params]: exchange specific params
        :returns: An `order book structure <https://docs.ccxt.com/#/?id=order-book-structure>`
        """
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        response = await self.publicGetOrderbook(self.extend(request, params))
        #
        #    {
        #       "result": "success",
        #       "serverTime": "2016-02-25T09:45:53.818Z",
        #       "orderBook": {
        #          "bids": [
        #                [
        #                    4213,
        #                    2000,
        #                ],
        #                [
        #                    4210,
        #                    4000,
        #                ],
        #                ...
        #            ],
        #            "asks": [
        #                [
        #                    4218,
        #                    4000,
        #                ],
        #                [
        #                    4220,
        #                    5000,
        #                ],
        #                ...
        #            ],
        #        },
        #    }
        #
        timestamp = self.parse8601(response['serverTime'])
        return self.parse_order_book(response['orderBook'], symbol, timestamp)

    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.futures.kraken.com/#http-api-trading-v3-api-market-data-get-tickers
        :param str[] symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an array of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
        """
        await self.load_markets()
        response = await self.publicGetTickers(params)
        #
        #    {
        #        "result": "success",
        #        "tickers": [
        #            {
        #                "tag": 'semiannual',  # 'month', 'quarter', "perpetual", "semiannual",
        #                "pair": "ETH:USD",
        #                "symbol": "fi_ethusd_220624",
        #                "markPrice": "2925.72",
        #                "bid": "2923.8",
        #                "bidSize": "16804",
        #                "ask": "2928.65",
        #                "askSize": "1339",
        #                "vol24h": "860493",
        #                "openInterest": "3023363.00000000",
        #                "open24h": "3021.25",
        #                "indexPrice": "2893.71",
        #                "last": "2942.25",
        #                "lastTime": "2022-02-18T14:08:15.578Z",
        #                "lastSize": "151",
        #                "suspended": False
        #            },
        #            {
        #                "symbol": "in_xbtusd",  # "rr_xbtusd",
        #                "last": "40411",
        #                "lastTime": "2022-02-18T14:16:28.000Z"
        #            },
        #            ...
        #        ],
        #        "serverTime": "2022-02-18T14:16:29.440Z"
        #    }
        #
        tickers = self.safe_list(response, 'tickers')
        return self.parse_tickers(tickers, symbols)

    def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
        #
        #    {
        #        "tag": 'semiannual',  # 'month', 'quarter', "perpetual", "semiannual",
        #        "pair": "ETH:USD",
        #        "symbol": "fi_ethusd_220624",
        #        "markPrice": "2925.72",
        #        "bid": "2923.8",
        #        "bidSize": "16804",
        #        "ask": "2928.65",
        #        "askSize": "1339",
        #        "vol24h": "860493",
        #        "openInterest": "3023363.00000000",
        #        "open24h": "3021.25",
        #        "indexPrice": "2893.71",
        #        "last": "2942.25",
        #        "lastTime": "2022-02-18T14:08:15.578Z",
        #        "lastSize": "151",
        #        "suspended": False
        #    }
        #
        #    {
        #        "symbol": "in_xbtusd",  # "rr_xbtusd",
        #        "last": "40411",
        #        "lastTime": "2022-02-18T14:16:28.000Z"
        #    }
        #
        marketId = self.safe_string(ticker, 'symbol')
        market = self.safe_market(marketId, market)
        symbol = market['symbol']
        timestamp = self.parse8601(self.safe_string(ticker, 'lastTime'))
        open = self.safe_string(ticker, 'open24h')
        last = self.safe_string(ticker, 'last')
        change = Precise.string_sub(last, open)
        percentage = Precise.string_mul(Precise.string_div(change, open), '100')
        average = Precise.string_div(Precise.string_add(open, last), '2')
        volume = self.safe_string(ticker, 'vol24h')
        baseVolume = None
        quoteVolume = None
        isIndex = self.safe_bool(market, 'index', False)
        if not isIndex:
            if market['linear']:
                baseVolume = volume
            elif market['inverse']:
                quoteVolume = volume
        return self.safe_ticker({
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'high': None,
            'low': None,
            'bid': self.safe_string(ticker, 'bid'),
            'bidVolume': self.safe_string(ticker, 'bidSize'),
            'ask': self.safe_string(ticker, 'ask'),
            'askVolume': self.safe_string(ticker, 'askSize'),
            'vwap': None,
            'open': open,
            'close': last,
            'last': last,
            'previousClose': None,
            'change': change,
            'percentage': percentage,
            'average': average,
            'baseVolume': baseVolume,
            'quoteVolume': quoteVolume,
            'info': ticker,
        })

    async def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
        """
        :see: https://docs.futures.kraken.com/#http-api-charts-candles
        fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
        :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/ccxt/wiki/Manual#pagination-params)
        :returns int[][]: A list of candles ordered, open, high, low, close, volume
        """
        await self.load_markets()
        market = self.market(symbol)
        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, 5000)
        request: dict = {
            'symbol': market['id'],
            'price_type': self.safe_string(params, 'price', 'trade'),
            'interval': self.timeframes[timeframe],
        }
        params = self.omit(params, 'price')
        if since is not None:
            duration = self.parse_timeframe(timeframe)
            request['from'] = self.parse_to_int(since / 1000)
            if limit is None:
                limit = 5000
            limit = min(limit, 5000)
            toTimestamp = self.sum(request['from'], limit * duration - 1)
            currentTimestamp = self.seconds()
            request['to'] = min(toTimestamp, currentTimestamp)
        elif limit is not None:
            limit = min(limit, 5000)
            duration = self.parse_timeframe(timeframe)
            request['to'] = self.seconds()
            request['from'] = self.parse_to_int(request['to'] - (duration * limit))
        response = await self.chartsGetPriceTypeSymbolInterval(self.extend(request, params))
        #
        #    {
        #        "candles": [
        #            {
        #                "time": 1645198500000,
        #                "open": "309.15000000000",
        #                "high": "309.15000000000",
        #                "low": "308.70000000000",
        #                "close": "308.85000000000",
        #                "volume": 0
        #            }
        #        ],
        #        "more_candles": True
        #    }
        #
        candles = self.safe_list(response, 'candles')
        return self.parse_ohlcvs(candles, market, timeframe, since, limit)

    def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
        #
        #    {
        #        "time": 1645198500000,
        #        "open": "309.15000000000",
        #        "high": "309.15000000000",
        #        "low": "308.70000000000",
        #        "close": "308.85000000000",
        #        "volume": 0
        #    }
        #
        return [
            self.safe_integer(ohlcv, 'time'),       # unix timestamp in milliseconds
            self.safe_number(ohlcv, 'open'),        # open price
            self.safe_number(ohlcv, 'high'),        # highest price
            self.safe_number(ohlcv, 'low'),         # lowest price
            self.safe_number(ohlcv, 'close'),       # close price
            self.safe_number(ohlcv, 'volume'),      # trading volume, None for mark or index price
        ]

    async def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
        """
        :see: https://docs.futures.kraken.com/#http-api-trading-v3-api-market-data-get-trade-history
        :see: https://docs.futures.kraken.com/#http-api-history-market-history-get-public-execution-events
        Fetch a history of filled trades that self account has made
        :param str symbol: Unified CCXT market symbol
        :param int [since]: Timestamp in ms of earliest trade. Not used by krakenfutures except in combination with params.until
        :param int [limit]: Total number of trades, cannot exceed 100
        :param dict [params]: Exchange specific params
        :param int [params.until]: Timestamp in ms of latest trade
        :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/ccxt/wiki/Manual#pagination-params)
        :param str [params.method]: The method to use to fetch trades. Can be 'historyGetMarketSymbolExecutions' or 'publicGetHistory' default is 'historyGetMarketSymbolExecutions'
        :returns: An array of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
        """
        await self.load_markets()
        paginate = False
        paginate, params = self.handle_option_and_params(params, 'fetchTrades', 'paginate')
        if paginate:
            return await self.fetch_paginated_call_dynamic('fetchTrades', symbol, since, limit, params)
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        method = None
        method, params = self.handle_option_and_params(params, 'fetchTrades', 'method', 'historyGetMarketSymbolExecutions')
        rawTrades = None
        isFullHistoryEndpoint = (method == 'historyGetMarketSymbolExecutions')
        if isFullHistoryEndpoint:
            request, params = self.handle_until_option('before', request, params)
            if since is not None:
                request['since'] = since
                request['sort'] = 'asc'
            if limit is not None:
                request['count'] = limit
            response = await self.historyGetMarketSymbolExecutions(self.extend(request, params))
            #
            #    {
            #        "elements": [
            #            {
            #                "uid": "a5105030-f054-44cc-98ab-30d5cae96bef",
            #                "timestamp": "1710150778607",
            #                "event": {
            #                    "Execution": {
            #                        "execution": {
            #                            "uid": "2d485b71-cd28-4a1e-9364-371a127550d2",
            #                            "makerOrder": {
            #                                "uid": "0a25f66b-1109-49ec-93a3-d17bf9e9137e",
            #                                "tradeable": "PF_XBTUSD",
            #                                "direction": "Buy",
            #                                "quantity": "0.26500",
            #                                "timestamp": "1710150778570",
            #                                "limitPrice": "71907",
            #                                "orderType": "Post",
            #                                "reduceOnly": False,
            #                                "lastUpdateTimestamp": "1710150778570"
            #                            },
            #                            "takerOrder": {
            #                                "uid": "04de3ee0-9125-4960-bf8f-f63b577b6790",
            #                                "tradeable": "PF_XBTUSD",
            #                                "direction": "Sell",
            #                                "quantity": "0.0002",
            #                                "timestamp": "1710150778607",
            #                                "limitPrice": "71187.00",
            #                                "orderType": "Market",
            #                                "reduceOnly": False,
            #                                "lastUpdateTimestamp": "1710150778607"
            #                            },
            #                            "timestamp": "1710150778607",
            #                            "quantity": "0.0002",
            #                            "price": "71907",
            #                            "markPrice": "71903.32715463147",
            #                            "limitFilled": False,
            #                            "usdValue": "14.38"
            #                        },
            #                        "takerReducedQuantity": ""
            #                    }
            #                }
            #            },
            #            ... followed by older items
            #        ],
            #        "len": "1000",
            #        "continuationToken": "QTexMDE0OTe33NTcyXy8xNDIzAjc1NjY5MwI="
            #    }
            #
            elements = self.safe_list(response, 'elements', [])
            # we need to reverse the list to fix chronology
            rawTrades = []
            length = len(elements)
            for i in range(0, length):
                index = length - 1 - i
                element = elements[index]
                event = self.safe_dict(element, 'event', {})
                executionContainer = self.safe_dict(event, 'Execution', {})
                rawTrade = self.safe_dict(executionContainer, 'execution', {})
                rawTrades.append(rawTrade)
        else:
            request, params = self.handle_until_option('lastTime', request, params)
            response = await self.publicGetHistory(self.extend(request, params))
            #
            #    {
            #        "result": "success",
            #        "history": [
            #            {
            #                "time": "2022-03-18T04:55:37.692Z",
            #                "trade_id": 100,
            #                "price": 0.7921,
            #                "size": 1068,
            #                "side": "sell",
            #                "type": "fill",
            #                "uid": "6c5da0b0-f1a8-483f-921f-466eb0388265"
            #            },
            #            ...
            #        ],
            #        "serverTime": "2022-03-18T06:39:18.056Z"
            #    }
            #
            rawTrades = self.safe_list(response, 'history', [])
        return self.parse_trades(rawTrades, market, since, limit)

    def parse_trade(self, trade: dict, market: Market = None) -> Trade:
        #
        # fetchTrades(recent trades)
        #
        #    {
        #        "time": "2019-02-14T09:25:33.920Z",
        #        "trade_id": 100,
        #        "price": 3574,
        #        "size": 100,
        #        "side": "buy",
        #        "type": "fill"  # fill, liquidation, assignment, termination
        #        "uid": "11c3d82c-9e70-4fe9-8115-f643f1b162d4"
        #    }
        #
        # fetchTrades(executions history)
        #
        #    {
        #        "timestamp": "1710152516830",
        #        "price": "71927.0",
        #        "quantity": "0.0695",
        #        "markPrice": "71936.38701675525",
        #        "limitFilled": True,
        #        "usdValue": "4998.93",
        #        "uid": "116ae634-253f-470b-bd20-fa9d429fb8b1",
        #        "makerOrder": {"uid": "17bfe4de-c01e-4938-926c-617d2a2d0597", "tradeable": "PF_XBTUSD", "direction": "Buy", "quantity": "0.0695", "timestamp": "1710152515836", "limitPrice": "71927.0", "orderType": "Post", "reduceOnly": False, "lastUpdateTimestamp": "1710152515836"},
        #        "takerOrder": {"uid": "d3e437b4-aa70-4108-b5cf-b1eecb9845b5", "tradeable": "PF_XBTUSD", "direction": "Sell", "quantity": "0.940100", "timestamp": "1710152516830", "limitPrice": "71915", "orderType": "IoC", "reduceOnly": False, "lastUpdateTimestamp": "1710152516830"}
        #    }
        #
        # fetchMyTrades(private)
        #
        #    {
        #        "fillTime": "2016-02-25T09:47:01.000Z",
        #        "order_id": "c18f0c17-9971-40e6-8e5b-10df05d422f0",
        #        "fill_id": "522d4e08-96e7-4b44-9694-bfaea8fe215e",
        #        "cliOrdId": "d427f920-ec55-4c18-ba95-5fe241513b30",     # OPTIONAL
        #        "symbol": "fi_xbtusd_180615",
        #        "side": "buy",
        #        "size": 2000,
        #        "price": 4255,
        #        "fillType": "maker"                                     # taker, takerAfterEdit, maker, liquidation, assignee
        #    }
        #
        # execution report(createOrder, editOrder)
        #
        #    {
        #        "executionId": "e1ec9f63-2338-4c44-b40a-43486c6732d7",
        #        "price": 7244.5,
        #        "amount": 10,
        #        "orderPriorEdit": null,
        #        "orderPriorExecution": {
        #            "orderId": "61ca5732-3478-42fe-8362-abbfd9465294",
        #            "cliOrdId": null,
        #            "type": "lmt",
        #            "symbol": "pi_xbtusd",
        #            "side": "buy",
        #            "quantity": 10,
        #            "filled": 0,
        #            "limitPrice": 7500,
        #            "reduceOnly": False,
        #            "timestamp": "2019-12-11T17:17:33.888Z",
        #            "lastUpdateTimestamp": "2019-12-11T17:17:33.888Z"
        #        },
        #        "takerReducedQuantity": null,
        #        "type": "EXECUTION"
        #    }
        #
        timestamp = self.parse8601(self.safe_string_2(trade, 'time', 'fillTime'))
        price = self.safe_string(trade, 'price')
        amount = self.safe_string_n(trade, ['size', 'amount', 'quantity'], '0.0')
        id = self.safe_string_2(trade, 'uid', 'fill_id')
        if id is None:
            id = self.safe_string(trade, 'executionId')
        order = self.safe_string(trade, 'order_id')
        marketId = self.safe_string(trade, 'symbol')
        side = self.safe_string(trade, 'side')
        type = None
        priorEdit = self.safe_value(trade, 'orderPriorEdit')
        priorExecution = self.safe_value(trade, 'orderPriorExecution')
        if priorExecution is not None:
            order = self.safe_string(priorExecution, 'orderId')
            marketId = self.safe_string(priorExecution, 'symbol')
            side = self.safe_string(priorExecution, 'side')
            type = self.safe_string(priorExecution, 'type')
        elif priorEdit is not None:
            order = self.safe_string(priorEdit, 'orderId')
            marketId = self.safe_string(priorEdit, 'symbol')
            side = self.safe_string(priorEdit, 'type')
            type = self.safe_string(priorEdit, 'type')
        if type is not None:
            type = self.parse_order_type(type)
        market = self.safe_market(marketId, market)
        cost = None
        linear = self.safe_bool(market, 'linear')
        if (amount is not None) and (price is not None) and (market is not None):
            if linear:
                cost = Precise.string_mul(amount, price)  # in quote
            else:
                cost = Precise.string_div(amount, price)  # in base
            contractSize = self.safe_string(market, 'contractSize')
            cost = Precise.string_mul(cost, contractSize)
        takerOrMaker = None
        fillType = self.safe_string(trade, 'fillType')
        if fillType is not None:
            if fillType.find('taker') >= 0:
                takerOrMaker = 'taker'
            elif fillType.find('maker') >= 0:
                takerOrMaker = 'maker'
        isHistoricalExecution = ('takerOrder' in trade)
        if isHistoricalExecution:
            timestamp = self.safe_integer(trade, 'timestamp')
            taker = self.safe_dict(trade, 'takerOrder', {})
            if taker is not None:
                side = self.safe_string_lower(taker, 'direction')
                takerOrMaker = 'taker'
        return self.safe_trade({
            'info': trade,
            'id': id,
            'symbol': self.safe_string(market, 'symbol'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'order': order,
            'type': type,
            'side': side,
            'takerOrMaker': takerOrMaker,
            'price': price,
            'amount': amount if linear else None,
            'cost': cost,
            'fee': None,
        })

    def create_order_request(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
        market = self.market(symbol)
        symbol = market['symbol']
        type = self.safe_string(params, 'orderType', type)
        timeInForce = self.safe_string(params, 'timeInForce')
        postOnly = False
        postOnly, params = self.handle_post_only(type == 'market', type == 'post', params)
        if postOnly:
            type = 'post'
        elif timeInForce == 'ioc':
            type = 'ioc'
        elif type == 'limit':
            type = 'lmt'
        elif type == 'market':
            type = 'mkt'
        request: dict = {
            'symbol': market['id'],
            'side': side,
            'size': self.amount_to_precision(symbol, amount),
        }
        clientOrderId = self.safe_string_2(params, 'clientOrderId', 'cliOrdId')
        if clientOrderId is not None:
            request['cliOrdId'] = clientOrderId
        triggerPrice = self.safe_string_2(params, 'triggerPrice', 'stopPrice')
        isTriggerOrder = triggerPrice is not None
        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
        triggerSignal = self.safe_string(params, 'triggerSignal', 'last')
        reduceOnly = self.safe_value(params, 'reduceOnly')
        if isStopLossOrTakeProfitTrigger or isTriggerOrder:
            request['triggerSignal'] = triggerSignal
        if isTriggerOrder:
            type = 'stp'
            request['stopPrice'] = self.price_to_precision(symbol, triggerPrice)
        elif isStopLossOrTakeProfitTrigger:
            reduceOnly = True
            if isStopLossTriggerOrder:
                type = 'stp'
                request['stopPrice'] = self.price_to_precision(symbol, stopLossTriggerPrice)
            elif isTakeProfitTriggerOrder:
                type = 'take_profit'
                request['stopPrice'] = self.price_to_precision(symbol, takeProfitTriggerPrice)
        if reduceOnly:
            request['reduceOnly'] = True
        request['orderType'] = type
        if price is not None:
            request['limitPrice'] = self.price_to_precision(symbol, price)
        params = self.omit(params, ['clientOrderId', 'timeInForce', 'triggerPrice', 'stopLossPrice', 'takeProfitPrice'])
        return self.extend(request, params)

    async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
        """
        Create an order on the exchange
        :see: https://docs.futures.kraken.com/#http-api-trading-v3-api-order-management-send-order
        :param str symbol: unified market symbol
        :param str type: 'limit' or 'market'
        :param str side: 'buy' or 'sell'
        :param float amount: number of contracts
        :param float [price]: limit order price
        :param bool [params.reduceOnly]: set if you wish the order to only reduce an existing position, any order which increases an existing position will be rejected, default is False
        :param bool [params.postOnly]: set if you wish to make a postOnly order, default is False
        :param str [params.clientOrderId]: UUID The order identity that is specified from the user, It must be globally unique
        :param float [params.triggerPrice]: the price that a stop order is triggered at
        :param float [params.stopLossPrice]: the price that a stop loss order is triggered at
        :param float [params.takeProfitPrice]: the price that a take profit order is triggered at
        :param str [params.triggerSignal]: for triggerPrice, stopLossPrice and takeProfitPrice orders, the trigger price type, 'last', 'mark' or 'index', default is 'last'
        """
        await self.load_markets()
        market = self.market(symbol)
        orderRequest = self.create_order_request(symbol, type, side, amount, price, params)
        response = await self.privatePostSendorder(orderRequest)
        #
        #    {
        #        "result": "success",
        #        "sendStatus": {
        #            "order_id": "salf320-e337-47ac-b345-30sdfsalj",
        #            "status": "placed",
        #            "receivedTime": "2022-02-28T19:32:17.122Z",
        #            "orderEvents": [
        #                {
        #                    "order": {
        #                        "orderId": "salf320-e337-47ac-b345-30sdfsalj",
        #                        "cliOrdId": null,
        #                        "type": "lmt",
        #                        "symbol": "pi_xrpusd",
        #                        "side": "buy",
        #                        "quantity": 1,
        #                        "filled": 0,
        #                        "limitPrice": 0.7,
        #                        "reduceOnly": False,
        #                        "timestamp": "2022-02-28T19:32:17.122Z",
        #                        "lastUpdateTimestamp": "2022-02-28T19:32:17.122Z"
        #                    },
        #                    "reducedQuantity": null,
        #                    "type": "PLACE"
        #                }
        #            ]
        #        },
        #        "serverTime": "2022-02-28T19:32:17.122Z"
        #    }
        #
        sendStatus = self.safe_value(response, 'sendStatus')
        status = self.safe_string(sendStatus, 'status')
        self.verify_order_action_success(status, 'createOrder', ['filled'])
        return self.parse_order(sendStatus, market)

    async def create_orders(self, orders: List[OrderRequest], params={}):
        """
        create a list of trade orders
        :see: https://docs.futures.kraken.com/#http-api-trading-v3-api-order-management-batch-order-management
        :param Array orders: list of orders to create, each object should contain the parameters required by createOrder, namely symbol, type, side, amount, price and params
        :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
        """
        await self.load_markets()
        ordersRequests = []
        for i in range(0, len(orders)):
            rawOrder = orders[i]
            marketId = self.safe_string(rawOrder, 'symbol')
            type = self.safe_string(rawOrder, 'type')
            side = self.safe_string(rawOrder, 'side')
            amount = self.safe_value(rawOrder, 'amount')
            price = self.safe_value(rawOrder, 'price')
            orderParams = self.safe_value(rawOrder, 'params', {})
            extendedParams = self.extend(orderParams, params)  # the request does not accept extra params since it's a list, so we're extending each order with the common params
            if not ('order_tag' in extendedParams):
                # order tag is mandatory so we will generate one if not provided
                extendedParams['order_tag'] = self.sum(i, str(1))  # sequential counter
            extendedParams['order'] = 'send'
            orderRequest = self.create_order_request(marketId, type, side, amount, price, extendedParams)
            ordersRequests.append(orderRequest)
        request: dict = {
            'batchOrder': ordersRequests,
        }
        response = await self.privatePostBatchorder(self.extend(request, params))
        #
        # {
        #     "result": "success",
        #     "serverTime": "2023-10-24T08:40:57.339Z",
        #     "batchStatus": [
        #        {
        #           "status": "requiredArgumentMissing",
        #           "orderEvents": []
        #        },
        #        {
        #           "status": "requiredArgumentMissing",
        #           "orderEvents": []
        #        }
        #     ]
        # }
        #
        data = self.safe_list(response, 'batchStatus', [])
        return self.parse_orders(data)

    async def edit_order(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: Num = None, price: Num = None, params={}):
        """
        :see: https://docs.futures.kraken.com/#http-api-trading-v3-api-order-management-edit-order
        Edit an open order on the exchange
        :param str id: order id
        :param str symbol: Not used by Krakenfutures
        :param str type: Not used by Krakenfutures
        :param str side: Not used by Krakenfutures
        :param float amount: Order size
        :param float [price]: Price to fill order at
        :param dict [params]: Exchange specific params
        :returns: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
        """
        await self.load_markets()
        request: dict = {
            'orderId': id,
        }
        if amount is not None:
            request['size'] = amount
        if price is not None:
            request['limitPrice'] = price
        response = await self.privatePostEditorder(self.extend(request, params))
        status = self.safe_string(response['editStatus'], 'status')
        self.verify_order_action_success(status, 'editOrder', ['filled'])
        order = self.parse_order(response['editStatus'])
        order['info'] = response
        return order

    async def cancel_order(self, id: str, symbol: Str = None, params={}):
        """
        :see: https://docs.futures.kraken.com/#http-api-trading-v3-api-order-management-cancel-order
        Cancel an open order on the exchange
        :param str id: Order id
        :param str symbol: Not used by Krakenfutures
        :param dict [params]: Exchange specific params
        :returns: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
        """
        await self.load_markets()
        response = await self.privatePostCancelorder(self.extend({'order_id': id}, params))
        status = self.safe_string(self.safe_value(response, 'cancelStatus', {}), 'status')
        self.verify_order_action_success(status, 'cancelOrder')
        order: dict = {}
        if 'cancelStatus' in response:
            order = self.parse_order(response['cancelStatus'])
        return self.extend({'info': response}, order)

    async def cancel_orders(self, ids: List[str], symbol: Str = None, params={}):
        """
        cancel multiple orders
        :see: https://docs.futures.kraken.com/#http-api-trading-v3-api-order-management-batch-order-management
        :param str[] ids: order ids
        :param str [symbol]: unified market symbol
        :param dict [params]: extra parameters specific to the exchange API endpoint
         *
         * EXCHANGE SPECIFIC PARAMETERS
        :param str[] [params.clientOrderIds]: max length 10 e.g. ["my_id_1","my_id_2"]
        :returns dict: an list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
        """
        await self.load_markets()
        orders = []
        clientOrderIds = self.safe_value(params, 'clientOrderIds', [])
        clientOrderIdsLength = len(clientOrderIds)
        if clientOrderIdsLength > 0:
            for i in range(0, len(clientOrderIds)):
                orders.append({'order': 'cancel', 'cliOrdId': clientOrderIds[i]})
        else:
            for i in range(0, len(ids)):
                orders.append({'order': 'cancel', 'order_id': ids[i]})
        request: dict = {
            'batchOrder': orders,
        }
        response = await self.privatePostBatchorder(self.extend(request, params))
        # {
        #     "result": "success",
        #     "serverTime": "2023-10-23T16:36:51.327Z",
        #     "batchStatus": [
        #       {
        #         "status": "cancelled",
        #         "order_id": "101c2327-f12e-45f2-8445-7502b87afc0b",
        #         "orderEvents": [
        #           {
        #             "uid": "101c2327-f12e-45f2-8445-7502b87afc0b",
        #             "order": {
        #               "orderId": "101c2327-f12e-45f2-8445-7502b87afc0b",
        #               "cliOrdId": null,
        #               "type": "lmt",
        #               "symbol": "PF_LTCUSD",
        #               "side": "buy",
        #               "quantity": "0.10000000000",
        #               "filled": "0E-11",
        #               "limitPrice": "50.00000000000",
        #               "reduceOnly": False,
        #               "timestamp": "2023-10-20T10:29:13.005Z",
        #               "lastUpdateTimestamp": "2023-10-20T10:29:13.005Z"
        #             },
        #             "type": "CANCEL"
        #           }
        #         ]
        #       }
        #     ]
        # }
        batchStatus = self.safe_list(response, 'batchStatus', [])
        return self.parse_orders(batchStatus)

    async def cancel_all_orders(self, symbol: Str = None, params={}):
        """
        :see: https://docs.futures.kraken.com/#http-api-trading-v3-api-order-management-cancel-all-orders
        Cancels all orders on the exchange, including trigger orders
        :param str symbol: Unified market symbol
        :param dict [params]: Exchange specific params
        :returns: Response from exchange api
        """
        request: dict = {}
        if symbol is not None:
            request['symbol'] = self.market_id(symbol)
        response = await self.privatePostCancelallorders(self.extend(request, params))
        #
        #    {
        #        result: 'success',
        #        cancelStatus: {
        #          receivedTime: '2024-06-06T01:12:44.814Z',
        #          cancelOnly: 'PF_XRPUSD',
        #          status: 'cancelled',
        #          cancelledOrders: [{order_id: '272fd0ac-45c0-4003-b84d-d39b9e86bd36'}],
        #          orderEvents: [
        #            {
        #              uid: '272fd0ac-45c0-4003-b84d-d39b9e86bd36',
        #              order: {
        #                orderId: '272fd0ac-45c0-4003-b84d-d39b9e86bd36',
        #                cliOrdId: null,
        #                type: 'lmt',
        #                symbol: 'PF_XRPUSD',
        #                side: 'buy',
        #                quantity: '10',
        #                filled: '0',
        #                limitPrice: '0.4',
        #                reduceOnly: False,
        #                timestamp: '2024-06-06T01:11:16.045Z',
        #                lastUpdateTimestamp: '2024-06-06T01:11:16.045Z'
        #              },
        #              type: 'CANCEL'
        #            }
        #          ]
        #        },
        #        serverTime: '2024-06-06T01:12:44.814Z'
        #    }
        #
        cancelStatus = self.safe_dict(response, 'cancelStatus')
        orderEvents = self.safe_list(cancelStatus, 'orderEvents', [])
        orders = []
        for i in range(0, len(orderEvents)):
            orderEvent = self.safe_dict(orderEvents, 0)
            order = self.safe_dict(orderEvent, 'order', {})
            orders.append(order)
        return self.parse_orders(orders)

    async def cancel_all_orders_after(self, timeout: Int, params={}):
        """
        dead man's switch, cancel all orders after the given timeout
        :see: https://docs.futures.kraken.com/#http-api-trading-v3-api-order-management-dead-man-39-s-switch
        :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
        """
        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))
        #
        #     {
        #         "result": "success",
        #         "serverTime": "2018-06-19T16:51:23.839Z",
        #         "status": {
        #             "currentTime": "2018-06-19T16:51:23.839Z",
        #             "triggerTime": "0"
        #         }
        #     }
        #
        return response

    async def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
        """
        :see: https://docs.futures.kraken.com/#http-api-trading-v3-api-order-management-get-open-orders
        Gets all open orders, including trigger orders, for an account from the exchange api
        :param str symbol: Unified market symbol
        :param int [since]: Timestamp(ms) of earliest order.(Not used by kraken api but filtered internally by CCXT)
        :param int [limit]: How many orders to return.(Not used by kraken api but filtered internally by CCXT)
        :param dict [params]: Exchange specific parameters
        :returns: An array of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
        """
        await self.load_markets()
        market = None
        if symbol is not None:
            market = self.market(symbol)
        response = await self.privateGetOpenorders(params)
        orders = self.safe_list(response, 'openOrders', [])
        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]:
        """
        :see: https://docs.futures.kraken.com/#http-api-history-account-history-get-order-events
        Gets all closed orders, including trigger orders, for an account from the exchange api
        :param str symbol: Unified market symbol
        :param int [since]: Timestamp(ms) of earliest order.
        :param int [limit]: How many orders to return.
        :param dict [params]: Exchange specific parameters
        :returns: An array of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
        """
        await self.load_markets()
        market = None
        if symbol is not None:
            market = self.market(symbol)
        request: dict = {}
        if limit is not None:
            request['count'] = limit
        if since is not None:
            request['from'] = since
        response = await self.historyGetOrders(self.extend(request, params))
        allOrders = self.safe_list(response, 'elements', [])
        closedOrders = []
        for i in range(0, len(allOrders)):
            order = allOrders[i]
            event = self.safe_dict(order, 'event', {})
            orderPlaced = self.safe_dict(event, 'OrderPlaced')
            if orderPlaced is not None:
                innerOrder = self.safe_dict(orderPlaced, 'order', {})
                filled = self.safe_string(innerOrder, 'filled')
                if filled != '0':
                    innerOrder['status'] = 'closed'  # status not available in the response
                    closedOrders.append(innerOrder)
        return self.parse_orders(closedOrders, market, since, limit)

    async def fetch_canceled_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
        """
        :see: https://docs.futures.kraken.com/#http-api-history-account-history-get-order-events
        Gets all canceled orders, including trigger orders, for an account from the exchange api
        :param str symbol: Unified market symbol
        :param int [since]: Timestamp(ms) of earliest order.
        :param int [limit]: How many orders to return.
        :param dict [params]: Exchange specific parameters
        :returns: An array of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
        """
        await self.load_markets()
        market = None
        if symbol is not None:
            market = self.market(symbol)
        request: dict = {}
        if limit is not None:
            request['count'] = limit
        if since is not None:
            request['from'] = since
        response = await self.historyGetOrders(self.extend(request, params))
        allOrders = self.safe_list(response, 'elements', [])
        canceledAndRejected = []
        for i in range(0, len(allOrders)):
            order = allOrders[i]
            event = self.safe_dict(order, 'event', {})
            orderPlaced = self.safe_dict(event, 'OrderPlaced')
            if orderPlaced is not None:
                innerOrder = self.safe_dict(orderPlaced, 'order', {})
                filled = self.safe_string(innerOrder, 'filled')
                if filled == '0':
                    innerOrder['status'] = 'canceled'  # status not available in the response
                    canceledAndRejected.append(innerOrder)
            orderCanceled = self.safe_dict(event, 'OrderCancelled')
            if orderCanceled is not None:
                innerOrder = self.safe_dict(orderCanceled, 'order', {})
                innerOrder['status'] = 'canceled'  # status not available in the response
                canceledAndRejected.append(innerOrder)
            orderRejected = self.safe_dict(event, 'OrderRejected')
            if orderRejected is not None:
                innerOrder = self.safe_dict(orderRejected, 'order', {})
                innerOrder['status'] = 'rejected'  # status not available in the response
                canceledAndRejected.append(innerOrder)
        return self.parse_orders(canceledAndRejected, market, since, limit)

    def parse_order_type(self, orderType):
        map: dict = {
            'lmt': 'limit',
            'mkt': 'market',
            'post': 'limit',
            'ioc': 'market',
        }
        return self.safe_string(map, orderType, orderType)

    def verify_order_action_success(self, status, method, omit=[]):
        errors: dict = {
            'invalidOrderType': InvalidOrder,
            'invalidSide': InvalidOrder,
            'invalidSize': InvalidOrder,
            'invalidPrice': InvalidOrder,
            'insufficientAvailableFunds': InsufficientFunds,
            'selfFill': ExchangeError,
            'tooManySmallOrders': ExchangeError,
            'maxPositionViolation': BadRequest,
            'marketSuspended': ExchangeNotAvailable,
            'marketInactive': ExchangeNotAvailable,
            'clientOrderIdAlreadyExist': DuplicateOrderId,
            'clientOrderIdTooLong': BadRequest,
            'outsidePriceCollar': InvalidOrder,
            'postWouldExecute': OrderImmediatelyFillable,  # the unplaced order could actually be parsed(with status = "rejected"), but there is self specific error for self
            'iocWouldNotExecute': OrderNotFillable,  # -||-
            'wouldNotReducePosition': ExchangeError,
            'orderForEditNotFound': OrderNotFound,
            'orderForEditNotAStop': InvalidOrder,
            'filled': OrderNotFound,
            'notFound': OrderNotFound,
        }
        if (status in errors) and not self.in_array(status, omit):
            raise errors[status](self.id + ': ' + method + ' failed due to ' + status)

    def parse_order_status(self, status: Str):
        statuses: dict = {
            'placed': 'open',  # the order was placed successfully
            'cancelled': 'canceled',  # the order was cancelled successfully
            'invalidOrderType': 'rejected',  # the order was not placed because orderType is invalid
            'invalidSide': 'rejected',  # the order was not placed because side is invalid
            'invalidSize': 'rejected',  # the order was not placed because size is invalid
            'invalidPrice': 'rejected',  # the order was not placed because limitPrice and/or stopPrice are invalid
            'insufficientAvailableFunds': 'rejected',  # the order was not placed because available funds are insufficient
            'selfFill': 'rejected',  # the order was not placed because it would be filled against an existing order belonging to the same account
            'tooManySmallOrders': 'rejected',  # the order was not placed because the number of small open orders would exceed the permissible limit
            'maxPositionViolation': 'rejected',  # Order would cause you to exceed your maximum hasattr(self, position) contract.
            'marketSuspended': 'rejected',  # the order was not placed because the market is suspended
            'marketInactive': 'rejected',  # the order was not placed because the market is inactive
            'clientOrderIdAlreadyExist': 'rejected',  # the specified client id already exist
            'clientOrderIdTooLong': 'rejected',  # the client id is longer than the permissible limit
            'outsidePriceCollar': 'rejected',  # the limit order crosses the spread but is an order of magnitude away from the mark price - fat finger control
            # Should the next two be 'expired' ?
            'postWouldExecute': 'rejected',  # the post-only order would be filled upon placement, thus is cancelled
            'iocWouldNotExecute': 'rejected',  # the immediate-or-cancel order would not execute.
            'wouldNotReducePosition': 'rejected',  # the reduce only order would not reduce position.
            'edited': 'open',  # the order was edited successfully
            'orderForEditNotFound': 'rejected',  # the requested order for edit has not been found
            'orderForEditNotAStop': 'rejected',  # the supplied stopPrice cannot be applied because order is not a stop order
            'filled': 'closed',  # the order was found completely filled and could not be cancelled
            'notFound': 'rejected',  # the order was not found, either because it had already been cancelled or it never existed
            'untouched': 'open',  # the entire size of the order is unfilled
            'partiallyFilled': 'open',  # the size of the order is partially but not entirely filled
        }
        return self.safe_string(statuses, status, status)

    def parse_order(self, order: dict, market: Market = None) -> Order:
        #
        # LIMIT
        #
        #    {
        #        "order_id": "179f9af8-e45e-469d-b3e9-2fd4675cb7d0",
        #        "status": "placed",
        #        "receivedTime": "2019-09-05T16:33:50.734Z",
        #        "orderEvents": [
        #            {
        #                "uid": "614a5298-0071-450f-83c6-0617ce8c6bc4",
        #                "order": {
        #                    "orderId": "179f9af8-e45e-469d-b3e9-2fd4675cb7d0",
        #                    "cliOrdId": null,
        #                    "type": "lmt",
        #                    "symbol": "pi_xbtusd",
        #                    "side": "buy",
        #                    "quantity": 10000,
        #                    "filled": 0,
        #                    "limitPrice": 9400,
        #                    "reduceOnly": False,
        #                    "timestamp": "2019-09-05T16:33:50.734Z",
        #                    "lastUpdateTimestamp": "2019-09-05T16:33:50.734Z"
        #                },
        #                "reducedQuantity": null,
        #                "reason": "WOULD_NOT_REDUCE_POSITION",  # REJECTED
        #                "type": "PLACE"
        #            }
        #        ]
        #    }
        #
        # CONDITIONAL
        #
        #    {
        #        "order_id": "1abfd3c6-af93-4b30-91cc-e4a93797f3f5",
        #        "status": "placed",
        #        "receivedTime": "2019-12-05T10:20:50.701Z",
        #        "orderEvents": [
        #            {
        #                "orderTrigger": {
        #                    "uid": "1abfd3c6-af93-4b30-91cc-e4a93797f3f5",
        #                    "clientId":null,
        #                    "type": "lmt",                                # "ioc" if stop market
        #                    "symbol": "pi_xbtusd",
        #                    "side": "buy",
        #                    "quantity":10,
        #                    "limitPrice":15000,
        #                    "triggerPrice":9500,
        #                    "triggerSide": "trigger_below",
        #                    "triggerSignal": "mark_price",
        #                    "reduceOnly":false,
        #                    "timestamp": "2019-12-05T10:20:50.701Z",
        #                    "lastUpdateTimestamp": "2019-12-05T10:20:50.701Z"
        #                },
        #                "type": "PLACE"
        #            }
        #        ]
        #    }
        #
        # EXECUTION
        #
        #    {
        #        "order_id": "61ca5732-3478-42fe-8362-abbfd9465294",
        #        "status": "placed",
        #        "receivedTime": "2019-12-11T17:17:33.888Z",
        #        "orderEvents": [
        #            {
        #                "executionId": "e1ec9f63-2338-4c44-b40a-43486c6732d7",
        #                "price": 7244.5,
        #                "amount": 10,
        #                "orderPriorEdit": null,
        #                "orderPriorExecution": {
        #                    "orderId": "61ca5732-3478-42fe-8362-abbfd9465294",
        #                    "cliOrdId": null,
        #                    "type": "lmt",
        #                    "symbol": "pi_xbtusd",
        #                    "side": "buy",
        #                    "quantity": 10,
        #                    "filled": 0,
        #                    "limitPrice": 7500,
        #                    "reduceOnly": False,
        #                    "timestamp": "2019-12-11T17:17:33.888Z",
        #                    "lastUpdateTimestamp": "2019-12-11T17:17:33.888Z"
        #                },
        #                "takerReducedQuantity": null,
        #                "type": "EXECUTION"
        #            }
        #        ]
        #    }
        #
        # EDIT ORDER
        #
        #    {
        #        "status": "edited",
        #        "orderId": "022774bc-2c4a-4f26-9317-436c8d85746d",
        #        "receivedTime": "2019-09-05T16:47:47.521Z",
        #        "orderEvents": [
        #            {
        #                "old": {
        #                    "orderId": "022774bc-2c4a-4f26-9317-436c8d85746d",
        #                    "cliOrdId":null,
        #                    "type": "lmt",
        #                    "symbol": "pi_xbtusd",
        #                    "side": "buy",
        #                    "quantity":1000,
        #                    "filled":0,
        #                    "limitPrice":9400.0,
        #                    "reduceOnly":false,
        #                    "timestamp": "2019-09-05T16:41:35.173Z",
        #                    "lastUpdateTimestamp": "2019-09-05T16:41:35.173Z"
        #                },
        #                "new": {
        #                    "orderId": "022774bc-2c4a-4f26-9317-436c8d85746d",
        #                    "cliOrdId": null,
        #                    "type": "lmt",
        #                    "symbol": "pi_xbtusd",
        #                    "side": "buy",
        #                    "quantity": 1501,
        #                    "filled": 0,
        #                    "limitPrice": 7200,
        #                    "reduceOnly": False,
        #                    "timestamp": "2019-09-05T16:41:35.173Z",
        #                    "lastUpdateTimestamp": "2019-09-05T16:47:47.519Z"
        #                },
        #                "reducedQuantity": null,
        #                "type": "EDIT"
        #            }
        #        ]
        #    }
        #
        # CANCEL ORDER
        #
        #    {
        #        "status": "cancelled",
        #        "orderEvents": [
        #            {
        #                "uid": "85c40002-3f20-4e87-9302-262626c3531b",
        #                "order": {
        #                    "orderId": "85c40002-3f20-4e87-9302-262626c3531b",
        #                    "cliOrdId": null,
        #                    "type": "lmt",
        #                    "symbol": "pi_xbtusd",
        #                    "side": "buy",
        #                    "quantity": 1000,
        #                    "filled": 0,
        #                    "limitPrice": 10144,
        #                    "stopPrice": null,
        #                    "reduceOnly": False,
        #                    "timestamp": "2019-08-01T15:26:27.790Z"
        #                },
        #                "type": "CANCEL"
        #            }
        #        ]
        #    }
        #
        # cancelAllOrders
        #
        #    {
        #        "orderId": "85c40002-3f20-4e87-9302-262626c3531b",
        #        "cliOrdId": null,
        #        "type": "lmt",
        #        "symbol": "pi_xbtusd",
        #        "side": "buy",
        #        "quantity": 1000,
        #        "filled": 0,
        #        "limitPrice": 10144,
        #        "stopPrice": null,
        #        "reduceOnly": False,
        #        "timestamp": "2019-08-01T15:26:27.790Z"
        #    }
        #
        # FETCH OPEN ORDERS
        #
        #    {
        #        "order_id": "59302619-41d2-4f0b-941f-7e7914760ad3",
        #        "symbol": "pi_xbtusd",
        #        "side": "sell",
        #        "orderType": "lmt",
        #        "limitPrice": 10640,
        #        "unfilledSize": 304,
        #        "receivedTime": "2019-09-05T17:01:17.410Z",
        #        "status": "untouched",
        #        "filledSize": 0,
        #        "reduceOnly": True,
        #        "lastUpdateTime": "2019-09-05T17:01:17.410Z"
        #    }
        #
        # createOrders error
        #    {
        #       "status": "requiredArgumentMissing",
        #       "orderEvents": []
        #    }
        # closed orders
        #    {
        #        uid: '2f00cd63-e61d-44f8-8569-adabde885941',
        #        timestamp: '1707258274849',
        #        event: {
        #          OrderPlaced: {
        #            order: {
        #              uid: '85805e01-9eed-4395-8360-ed1a228237c9',
        #              accountUid: '406142dd-7c5c-4a8b-acbc-5f16eca30009',
        #              tradeable: 'PF_LTCUSD',
        #              direction: 'Buy',
        #              quantity: '0',
        #              filled: '0.1',
        #              timestamp: '1707258274849',
        #              limitPrice: '69.2200000000',
        #              orderType: 'IoC',
        #              clientId: '',
        #              reduceOnly: False,
        #              lastUpdateTimestamp: '1707258274849'
        #            },
        #            reason: 'new_user_order',
        #            reducedQuantity: '',
        #            algoId: ''
        #          }
        #        }
        #    }
        #
        orderEvents = self.safe_value(order, 'orderEvents', [])
        errorStatus = self.safe_string(order, 'status')
        orderEventsLength = len(orderEvents)
        if ('orderEvents' in order) and (errorStatus is not None) and (orderEventsLength == 0):
            # creteOrders error response
            return self.safe_order({'info': order, 'status': 'rejected'})
        details = None
        isPrior = False
        fixed = False
        statusId = None
        price = None
        trades = []
        if orderEventsLength:
            executions = []
            for i in range(0, len(orderEvents)):
                item = orderEvents[i]
                if self.safe_string(item, 'type') == 'EXECUTION':
                    executions.append(item)
                # Final order(after placement / editing / execution / canceling)
                orderTrigger = self.safe_value(item, 'orderTrigger')
                details = self.safe_value_2(item, 'new', 'order', orderTrigger)
                if details is not None:
                    isPrior = False
                    fixed = True
                elif not fixed:
                    orderPriorExecution = self.safe_value(item, 'orderPriorExecution')
                    details = self.safe_value_2(item, 'orderPriorExecution', 'orderPriorEdit')
                    price = self.safe_string(orderPriorExecution, 'limitPrice')
                    if details is not None:
                        isPrior = True
            trades = self.parse_trades(executions)
            statusId = self.safe_string(order, 'status')
        if details is None:
            details = order
        if statusId is None:
            statusId = self.safe_string(details, 'status')
        # This may be incorrectly marked as "open" if only execution report is given,
        # but will be fixed below
        status = self.parse_order_status(statusId)
        isClosed = self.in_array(status, ['canceled', 'rejected', 'closed'])
        marketId = self.safe_string(details, 'symbol')
        market = self.safe_market(marketId, market)
        timestamp = self.parse8601(self.safe_string_2(details, 'timestamp', 'receivedTime'))
        lastUpdateTimestamp = self.parse8601(self.safe_string(details, 'lastUpdateTime'))
        if price is None:
            price = self.safe_string(details, 'limitPrice')
        amount = self.safe_string(details, 'quantity')
        filled = self.safe_string_2(details, 'filledSize', 'filled', '0.0')
        remaining = self.safe_string(details, 'unfilledSize')
        average = None
        filled2 = '0.0'
        tradesLength = len(trades)
        if tradesLength > 0:
            vwapSum = '0.0'
            for i in range(0, len(trades)):
                trade = trades[i]
                tradeAmount = self.safe_string(trade, 'amount')
                tradePrice = self.safe_string(trade, 'price')
                filled2 = Precise.string_add(filled2, tradeAmount)
                vwapSum = Precise.string_add(vwapSum, Precise.string_mul(tradeAmount, tradePrice))
            average = Precise.string_div(vwapSum, filled2)
            if (amount is not None) and (not isClosed) and isPrior and Precise.string_ge(filled2, amount):
                status = 'closed'
                isClosed = True
            if isPrior:
                filled = Precise.string_add(filled, filled2)
            else:
                filled = Precise.string_max(filled, filled2)
        if remaining is None:
            if isPrior:
                if amount is not None:
                    # remaining amount before execution minus executed amount
                    remaining = Precise.string_sub(amount, filled2)
            else:
                remaining = amount
        # if fetchOpenOrders are parsed
        if (amount is None) and (not isPrior) and (remaining is not None):
            amount = Precise.string_add(filled, remaining)
        cost = None
        if (filled is not None) and (market is not None):
            whichPrice = average if (average is not None) else price
            if whichPrice is not None:
                if market['linear']:
                    cost = Precise.string_mul(filled, whichPrice)  # in quote
                else:
                    cost = Precise.string_div(filled, whichPrice)  # in base
        id = self.safe_string_2(order, 'order_id', 'orderId')
        if id is None:
            id = self.safe_string_2(details, 'orderId', 'uid')
        type = self.safe_string_lower_2(details, 'type', 'orderType')
        timeInForce = 'gtc'
        if type == 'ioc' or self.parse_order_type(type) == 'market':
            timeInForce = 'ioc'
        return self.safe_order({
            'info': order,
            'id': id,
            'clientOrderId': self.safe_string_n(details, ['clientOrderId', 'clientId', 'cliOrdId']),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': None,
            'lastUpdateTimestamp': lastUpdateTimestamp,
            'symbol': self.safe_string(market, 'symbol'),
            'type': self.parse_order_type(type),
            'timeInForce': timeInForce,
            'postOnly': type == 'post',
            'reduceOnly': self.safe_value(details, 'reduceOnly'),
            'side': self.safe_string(details, 'side'),
            'price': price,
            'stopPrice': self.safe_string(details, 'triggerPrice'),
            'triggerPrice': self.safe_string(details, 'triggerPrice'),
            'amount': amount,
            'cost': cost,
            'average': average,
            'filled': filled,
            'remaining': remaining,
            'status': status,
            'fee': None,
            'fees': None,
            'trades': trades,
        })

    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.futures.kraken.com/#http-api-trading-v3-api-historical-data-get-your-fills
        :param str symbol: unified market symbol
        :param int [since]: *not used by the  api* 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
        :param int [params.until]: the latest time in ms to fetch entries for
        :returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
        """
        await self.load_markets()
        market = None
        if symbol is not None:
            market = self.market(symbol)
        response = await self.privateGetFills(params)
        #
        #    {
        #        "result": "success",
        #        "serverTime": "2016-02-25T09:45:53.818Z",
        #        "fills": [
        #            {
        #                "fillTime": "2016-02-25T09:47:01.000Z",
        #                "order_id": "c18f0c17-9971-40e6-8e5b-10df05d422f0",
        #                "fill_id": "522d4e08-96e7-4b44-9694-bfaea8fe215e",
        #                "cliOrdId": "d427f920-ec55-4c18-ba95-5fe241513b30",  # EXTRA
        #                "symbol": "fi_xbtusd_180615",
        #                "side": "buy",
        #                "size": 2000,
        #                "price": 4255,
        #                "fillType": "maker"
        #            },
        #            ...
        #        ]
        #    }
        #
        return self.parse_trades(response['fills'], market, since, limit)

    async def fetch_balance(self, params={}) -> Balances:
        """
        :see: https://docs.futures.kraken.com/#http-api-trading-v3-api-account-information-get-wallets
        Fetch the balance for a sub-account, all sub-account balances are inside 'info' in the response
        :param dict [params]: Exchange specific parameters
        :param str [params.type]: The sub-account type to query the balance of, possible values include 'flex', 'cash'/'main'/'funding', or a market symbol * defaults to 'flex' *
        :param str [params.symbol]: A unified market symbol, when assigned the balance for a trading market that matches the symbol is returned
        :returns: A `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
        """
        await self.load_markets()
        type = self.safe_string_2(params, 'type', 'account')
        symbol = self.safe_string(params, 'symbol')
        params = self.omit(params, ['type', 'account', 'symbol'])
        response = await self.privateGetAccounts(params)
        #
        #    {
        #        "result": "success",
        #        "accounts": {
        #            "fi_xbtusd": {
        #                "auxiliary": {usd: "0", pv: '0.0', pnl: '0.0', af: '0.0', funding: "0.0"},
        #                "marginRequirements": {im: '0.0', mm: '0.0', lt: '0.0', tt: "0.0"},
        #                "triggerEstimates": {im: '0', mm: '0', lt: "0", tt: "0"},
        #                "balances": {xbt: "0.0"},
        #                "currency": "xbt",
        #                "type": "marginAccount"
        #            },
        #            "cash": {
        #                "balances": {
        #                    "eur": "0.0",
        #                    "gbp": "0.0",
        #                    "bch": "0.0",
        #                    "xrp": "2.20188538338",
        #                    "usd": "0.0",
        #                    "eth": "0.0",
        #                    "usdt": "0.0",
        #                    "ltc": "0.0",
        #                    "usdc": "0.0",
        #                    "xbt": "0.0"
        #                },
        #                "type": "cashAccount"
        #            },
        #            "fv_xrpxbt": {
        #                "auxiliary": {usd: "0", pv: '0.0', pnl: '0.0', af: '0.0', funding: "0.0"},
        #                "marginRequirements": {im: '0.0', mm: '0.0', lt: '0.0', tt: "0.0"},
        #                "triggerEstimates": {im: '0', mm: '0', lt: "0", tt: "0"},
        #                "balances": {xbt: "0.0"},
        #                "currency": "xbt",
        #                "type": "marginAccount"
        #            },
        #            "fi_xrpusd": {
        #                "auxiliary": {usd: "0", pv: '11.0', pnl: '0.0', af: '11.0', funding: "0.0"},
        #                "marginRequirements": {im: '0.0', mm: '0.0', lt: '0.0', tt: "0.0"},
        #                "triggerEstimates": {im: '0', mm: '0', lt: "0", tt: "0"},
        #                "balances": {xrp: "11.0"},
        #                "currency": "xrp",
        #                "type": "marginAccount"
        #            },
        #            "fi_ethusd": {
        #                "auxiliary": {usd: "0", pv: '0.0', pnl: '0.0', af: '0.0', funding: "0.0"},
        #                "marginRequirements": {im: '0.0', mm: '0.0', lt: '0.0', tt: "0.0"},
        #                "triggerEstimates": {im: '0', mm: '0', lt: "0", tt: "0"},
        #                "balances": {eth: "0.0"},
        #                "currency": "eth",
        #                "type": "marginAccount"
        #            },
        #            "fi_ltcusd": {
        #                "auxiliary": {usd: "0", pv: '0.0', pnl: '0.0', af: '0.0', funding: "0.0"},
        #                "marginRequirements": {im: '0.0', mm: '0.0', lt: '0.0', tt: "0.0"},
        #                "triggerEstimates": {im: '0', mm: '0', lt: "0", tt: "0"},
        #                "balances": {ltc: "0.0"},
        #                "currency": "ltc",
        #                "type": "marginAccount"
        #            },
        #            "fi_bchusd": {
        #                "auxiliary": {usd: "0", pv: '0.0', pnl: '0.0', af: '0.0', funding: "0.0"},
        #                "marginRequirements": {im: '0.0', mm: '0.0', lt: '0.0', tt: "0.0"},
        #                "triggerEstimates": {im: '0', mm: '0', lt: "0", tt: "0"},
        #                "balances": {bch: "0.0"},
        #                "currency": "bch",
        #                "type": "marginAccount"
        #            },
        #            "flex": {
        #                "currencies": {},
        #                "initialMargin": "0.0",
        #                "initialMarginWithOrders": "0.0",
        #                "maintenanceMargin": "0.0",
        #                "balanceValue": "0.0",
        #                "portfolioValue": "0.0",
        #                "collateralValue": "0.0",
        #                "pnl": "0.0",
        #                "unrealizedFunding": "0.0",
        #                "totalUnrealized": "0.0",
        #                "totalUnrealizedAsMargin": "0.0",
        #                "availableMargin": "0.0",
        #                "marginEquity": "0.0",
        #                "type": "multiCollateralMarginAccount"
        #            }
        #        },
        #        "serverTime": "2022-04-12T07:48:07.475Z"
        #    }
        #
        datetime = self.safe_string(response, 'serverTime')
        if type == 'marginAccount' or type == 'margin':
            if symbol is None:
                raise ArgumentsRequired(self.id + ' fetchBalance requires symbol argument for margin accounts')
            type = symbol
        if type is None:
            type = 'flex' if (symbol is None) else symbol
        accountName = self.parse_account(type)
        accounts = self.safe_value(response, 'accounts')
        account = self.safe_value(accounts, accountName)
        if account is None:
            type = '' if (type is None) else type
            symbol = '' if (symbol is None) else symbol
            raise BadRequest(self.id + ' fetchBalance has no account for ' + type)
        balance = self.parse_balance(account)
        balance['info'] = response
        balance['timestamp'] = self.parse8601(datetime)
        balance['datetime'] = datetime
        return balance

    def parse_balance(self, response) -> Balances:
        #
        # cashAccount
        #
        #    {
        #        "balances": {
        #            "eur": "0.0",
        #            "gbp": "0.0",
        #            "bch": "0.0",
        #            "xrp": "2.20188538338",
        #            "usd": "0.0",
        #            "eth": "0.0",
        #            "usdt": "0.0",
        #            "ltc": "0.0",
        #            "usdc": "0.0",
        #            "xbt": "0.0"
        #        },
        #        "type": "cashAccount"
        #    }
        #
        # marginAccount e,g, fi_xrpusd
        #
        #    {
        #        "auxiliary": {
        #            "usd": "0",
        #            "pv": "11.0",
        #            "pnl": "0.0",
        #            "af": "11.0",
        #            "funding": "0.0"
        #        },
        #        "marginRequirements": {im: '0.0', mm: '0.0', lt: '0.0', tt: "0.0"},
        #        "triggerEstimates": {im: '0', mm: '0', lt: "0", tt: "0"},
        #        "balances": {xrp: "11.0"},
        #        "currency": "xrp",
        #        "type": "marginAccount"
        #    }
        #
        # flex/multiCollateralMarginAccount
        #
        #    {
        #       "currencies": {
        #            "USDT": {
        #                "quantity": "1",
        #                "value": "1.0001",
        #                "collateral": "0.9477197625",
        #                "available": "1.0"
        #             }
        #       },
        #       "initialMargin": "0.0",
        #       "initialMarginWithOrders": "0.0",
        #       "maintenanceMargin": "0.0",
        #       "balanceValue": "1.0",
        #       "portfolioValue": "1.0",
        #       "collateralValue": "0.95",
        #       "pnl": "0.0",
        #       "unrealizedFunding": "0.0",
        #       "totalUnrealized": "0.0",
        #       "totalUnrealizedAsMargin": "0.0",
        #       "availableMargin": "0.95",
        #       "marginEquity": "0.95",
        #       "type": "multiCollateralMarginAccount"
        #    }
        #
        accountType = self.safe_string_2(response, 'accountType', 'type')
        isFlex = (accountType == 'multiCollateralMarginAccount')
        isCash = (accountType == 'cashAccount')
        balances = self.safe_value_2(response, 'balances', 'currencies', {})
        result: dict = {}
        currencyIds = list(balances.keys())
        for i in range(0, len(currencyIds)):
            currencyId = currencyIds[i]
            balance = balances[currencyId]
            code = self.safe_currency_code(currencyId)
            splitCode = code.split('_')
            codeLength = len(splitCode)
            if codeLength > 1:
                continue   # Removes contract codes like PI_XRPUSD
            account = self.account()
            if isFlex:
                account['total'] = self.safe_string(balance, 'quantity')
                account['free'] = self.safe_string(balance, 'available')
            elif isCash:
                account['used'] = '0.0'
                account['total'] = balance
            else:
                auxiliary = self.safe_value(response, 'auxiliary')
                account['free'] = self.safe_string(auxiliary, 'af')
                account['total'] = self.safe_string(auxiliary, 'pv')
            result[code] = account
        return self.safe_balance(result)

    async def fetch_funding_rates(self, symbols: Strings = None, params={}):
        """
        :see: https://docs.futures.kraken.com/#http-api-trading-v3-api-market-data-get-tickers
        fetch the current funding rates
        :param str[] symbols: unified market symbols
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns Order[]: an array of `funding rate structures <https://docs.ccxt.com/#/?id=funding-rate-structure>`
        """
        await self.load_markets()
        marketIds = self.market_ids(symbols)
        response = await self.publicGetTickers(params)
        tickers = self.safe_value(response, 'tickers')
        fundingRates = []
        for i in range(0, len(tickers)):
            entry = tickers[i]
            entry_symbol = self.safe_value(entry, 'symbol')
            if marketIds is not None:
                if not self.in_array(entry_symbol, marketIds):
                    continue
            market = self.safe_market(entry_symbol)
            parsed = self.parse_funding_rate(entry, market)
            fundingRates.append(parsed)
        return self.index_by(fundingRates, 'symbol')

    def parse_funding_rate(self, ticker, market: Market = None):
        #
        # {"ask": 26.283,
        #  "askSize": 4.6,
        #  "bid": 26.201,
        #  "bidSize": 190,
        #  "fundingRate": -0.000944642727438883,
        #  "fundingRatePrediction": -0.000872671532340275,
        #  "indexPrice": 26.253,
        #  "last": 26.3,
        #  "lastSize": 0.1,
        #  "lastTime": "2023-06-11T18:55:28.958Z",
        #  "markPrice": 26.239,
        #  "open24h": 26.3,
        #  "openInterest": 641.1,
        #  "pair": "COMP:USD",
        #  "postOnly": False,
        #  "suspended": False,
        #  "symbol": "pf_compusd",
        #  "tag": "perpetual",
        #  "vol24h": 0.1,
        #  "volumeQuote": 2.63}
        #
        fundingRateMultiplier = '8'  # https://support.kraken.com/hc/en-us/articles/9618146737172-Perpetual-Contracts-Funding-Rate-Method-Prior-to-September-29-2022
        marketId = self.safe_string(ticker, 'symbol')
        symbol = self.symbol(marketId)
        timestamp = self.parse8601(self.safe_string(ticker, 'lastTime'))
        indexPrice = self.safe_number(ticker, 'indexPrice')
        markPriceString = self.safe_string(ticker, 'markPrice')
        markPrice = self.parse_number(markPriceString)
        fundingRateString = self.safe_string(ticker, 'fundingRate')
        fundingRateResult = Precise.string_div(Precise.string_mul(fundingRateString, fundingRateMultiplier), markPriceString)
        fundingRate = self.parse_number(fundingRateResult)
        nextFundingRateString = self.safe_string(ticker, 'fundingRatePrediction')
        nextFundingRateResult = Precise.string_div(Precise.string_mul(nextFundingRateString, fundingRateMultiplier), markPriceString)
        nextFundingRate = self.parse_number(nextFundingRateResult)
        return {
            'info': ticker,
            'symbol': symbol,
            'markPrice': markPrice,
            'indexPrice': indexPrice,
            'interestRate': None,
            'estimatedSettlePrice': None,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'fundingRate': fundingRate,
            'fundingTimestamp': None,
            'fundingDatetime': None,
            'nextFundingRate': nextFundingRate,
            'nextFundingTimestamp': None,
            'nextFundingDatetime': None,
            'previousFundingRate': None,
            'previousFundingTimestamp': None,
            'previousFundingDatetime': None,
        }

    async def fetch_funding_rate_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        fetches historical funding rate prices
        :see: https://docs.futures.kraken.com/#http-api-trading-v3-api-historical-funding-rates-historical-funding-rates
        :param str symbol: unified symbol of the market to fetch the funding rate history for
        :param int [since]: timestamp in ms of the earliest funding rate to fetch
        :param int [limit]: the maximum amount of `funding rate structures <https://docs.ccxt.com/#/?id=funding-rate-history-structure>` to fetch
        :param dict [params]: extra parameters specific to the api endpoint
        :returns dict[]: a list of `funding rate structures <https://docs.ccxt.com/#/?id=funding-rate-history-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchFundingRateHistory() requires a symbol argument')
        await self.load_markets()
        market = self.market(symbol)
        if not market['swap']:
            raise BadRequest(self.id + ' fetchFundingRateHistory() supports swap contracts only')
        request: dict = {
            'symbol': market['id'].upper(),
        }
        response = await self.publicGetHistoricalfundingrates(self.extend(request, params))
        #
        #    {
        #        "rates": [
        #          {
        #            "timestamp": '2018-08-31T16:00:00.000Z',
        #            "fundingRate": '2.18900669884E-7',
        #            "relativeFundingRate": '0.000060779960000000'
        #          },
        #          ...
        #        ]
        #    }
        #
        rates = self.safe_value(response, 'rates')
        result = []
        for i in range(0, len(rates)):
            item = rates[i]
            datetime = self.safe_string(item, 'timestamp')
            result.append({
                'info': item,
                'symbol': symbol,
                'fundingRate': self.safe_number(item, 'relativeFundingRate'),
                'timestamp': self.parse8601(datetime),
                'datetime': datetime,
            })
        sorted = self.sort_by(result, 'timestamp')
        return self.filter_by_symbol_since_limit(sorted, symbol, since, limit)

    async def fetch_positions(self, symbols: Strings = None, params={}):
        """
        :see: https://docs.futures.kraken.com/#http-api-trading-v3-api-account-information-get-open-positions
        Fetches current contract trading positions
        :param str[] symbols: List of unified symbols
        :param dict [params]: Not used by krakenfutures
        :returns: Parsed exchange response for positions
        """
        await self.load_markets()
        request: dict = {}
        response = await self.privateGetOpenpositions(request)
        #
        #    {
        #        "result": "success",
        #        "openPositions": [
        #            {
        #                "side": "long",
        #                "symbol": "pi_xrpusd",
        #                "price": "0.7533",
        #                "fillTime": "2022-03-03T22:51:16.566Z",
        #                "size": "230",
        #                "unrealizedFunding": "-0.001878596918214635"
        #            }
        #        ],
        #        "serverTime": "2022-03-03T22:51:16.566Z"
        #    }
        #
        result = self.parse_positions(response)
        return self.filter_by_array_positions(result, 'symbol', symbols, False)

    def parse_positions(self, response, symbols: Strings = None, params={}):
        result = []
        positions = self.safe_value(response, 'openPositions')
        for i in range(0, len(positions)):
            position = self.parse_position(positions[i])
            result.append(position)
        return result

    def parse_position(self, position: dict, market: Market = None):
        # cross
        #    {
        #        "side": "long",
        #        "symbol": "pi_xrpusd",
        #        "price": "0.7533",
        #        "fillTime": "2022-03-03T22:51:16.566Z",
        #        "size": "230",
        #        "unrealizedFunding": "-0.001878596918214635"
        #    }
        #
        # isolated
        #    {
        #        "side":"long",
        #        "symbol":"pf_ftmusd",
        #        "price":"0.4921",
        #        "fillTime":"2023-02-22T11:37:16.685Z",
        #        "size":"1",
        #        "unrealizedFunding":"-8.155240068885155E-8",
        #        "pnlCurrency":"USD",
        #        "maxFixedLeverage":"1.0"
        #    }
        #
        leverage = self.safe_number(position, 'maxFixedLeverage')
        marginType = 'cross'
        if leverage is not None:
            marginType = 'isolated'
        datetime = self.safe_string(position, 'fillTime')
        marketId = self.safe_string(position, 'symbol')
        market = self.safe_market(marketId, market)
        return {
            'info': position,
            'symbol': market['symbol'],
            'timestamp': self.parse8601(datetime),
            'datetime': datetime,
            'initialMargin': None,
            'initialMarginPercentage': None,
            'maintenanceMargin': None,
            'maintenanceMarginPercentage': None,
            'entryPrice': self.safe_number(position, 'price'),
            'notional': None,
            'leverage': leverage,
            'unrealizedPnl': None,
            'contracts': self.safe_number(position, 'size'),
            'contractSize': self.safe_number(market, 'contractSize'),
            'marginRatio': None,
            'liquidationPrice': None,
            'markPrice': None,
            'collateral': None,
            'marginType': marginType,
            'side': self.safe_string(position, 'side'),
            'percentage': None,
        }

    async def fetch_leverage_tiers(self, symbols: Strings = None, params={}) -> LeverageTiers:
        """
        :see: https://docs.futures.kraken.com/#http-api-trading-v3-api-instrument-details-get-instruments
        retrieve information on the maximum leverage, and maintenance margin for trades of varying trade sizes
        :param str[]|None symbols: list of unified market symbols
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a dictionary of `leverage tiers structures <https://docs.ccxt.com/#/?id=leverage-tiers-structure>`, indexed by market symbols
        """
        await self.load_markets()
        response = await self.publicGetInstruments(params)
        #
        #    {
        #        "result": "success",
        #        "instruments": [
        #            {
        #                "symbol": "fi_ethusd_180928",
        #                "type": "futures_inverse",  # futures_vanilla  # spot index
        #                "underlying": "rr_ethusd",
        #                "lastTradingTime": "2018-09-28T15:00:00.000Z",
        #                "tickSize": 0.1,
        #                "contractSize": 1,
        #                "tradeable": True,
        #                "marginLevels": [
        #                    {
        #                        "contracts":0,
        #                        "initialMargin":0.02,
        #                        "maintenanceMargin":0.01
        #                    },
        #                    {
        #                        "contracts":250000,
        #                        "initialMargin":0.04,
        #                        "maintenanceMargin":0.02
        #                    },
        #                    ...
        #                ],
        #                "isin": "GB00JVMLMP88",
        #                "retailMarginLevels": [
        #                    {
        #                        "contracts": 0,
        #                        "initialMargin": 0.5,
        #                        "maintenanceMargin": 0.25
        #                    }
        #                ],
        #                "tags": [],
        #            },
        #            {
        #                "symbol": "in_xbtusd",
        #                "type": "spot index",
        #                "tradeable":false
        #            }
        #        ]
        #        "serverTime": "2018-07-19T11:32:39.433Z"
        #    }
        #
        data = self.safe_list(response, 'instruments')
        return self.parse_leverage_tiers(data, symbols, 'symbol')

    def parse_market_leverage_tiers(self, info, market: Market = None) -> List[LeverageTier]:
        """
         * @ignore
         * @param info Exchange market response for 1 market
         * @param market CCXT market
        """
        #
        #    {
        #        "symbol": "fi_ethusd_180928",
        #        "type": "futures_inverse",  # futures_vanilla  # spot index
        #        "underlying": "rr_ethusd",
        #        "lastTradingTime": "2018-09-28T15:00:00.000Z",
        #        "tickSize": 0.1,
        #        "contractSize": 1,
        #        "tradeable": True,
        #        "marginLevels": [
        #            {
        #                "contracts":0,
        #                "initialMargin":0.02,
        #                "maintenanceMargin":0.01
        #            },
        #            {
        #                "contracts":250000,
        #                "initialMargin":0.04,
        #                "maintenanceMargin":0.02
        #            },
        #            ...
        #        ],
        #        "isin": "GB00JVMLMP88",
        #        "retailMarginLevels": [
        #            {
        #                "contracts": 0,
        #                "initialMargin": 0.5,
        #                "maintenanceMargin": 0.25
        #            }
        #        ],
        #        "tags": [],
        #    }
        #
        marginLevels = self.safe_value(info, 'marginLevels')
        id = self.safe_string(info, 'symbol')
        market = self.safe_market(id, market)
        tiers = []
        for i in range(0, len(marginLevels)):
            tier = marginLevels[i]
            initialMargin = self.safe_string(tier, 'initialMargin')
            notionalFloor = self.safe_number(tier, 'contracts')
            if i != 0:
                tiersLength = len(tiers)
                previousTier = tiers[tiersLength - 1]
                previousTier['notionalCap'] = notionalFloor
            tiers.append({
                'tier': self.sum(i, 1),
                'currency': market['quote'],
                'notionalFloor': notionalFloor,
                'notionalCap': None,
                'maintenanceMarginRate': self.safe_number(tier, 'maintenanceMargin'),
                'maxLeverage': self.parse_number(Precise.string_div('1', initialMargin)),
                'info': tier,
            })
        return tiers

    def parse_transfer(self, transfer: dict, currency: Currency = None) -> TransferEntry:
        #
        # transfer
        #
        #    {
        #        "result": "success",
        #        "serverTime": "2022-04-12T01:22:53.420Z"
        #    }
        #
        datetime = self.safe_string(transfer, 'serverTime')
        return {
            'info': transfer,
            'id': None,
            'timestamp': self.parse8601(datetime),
            'datetime': datetime,
            'currency': self.safe_string(currency, 'code'),
            'amount': None,
            'fromAccount': None,
            'toAccount': None,
            'status': self.safe_string(transfer, 'result'),
        }

    def parse_account(self, account):
        accountByType: dict = {
            'main': 'cash',
            'funding': 'cash',
            'future': 'cash',
            'futures': 'cash',
            'cashAccount': 'cash',
            'multiCollateralMarginAccount': 'flex',
            'multiCollateral': 'flex',
            'multiCollateralMargin': 'flex',
        }
        if account in accountByType:
            return accountByType[account]
        elif account in self.markets:
            market = self.market(account)
            marketId = market['id']
            splitId = marketId.split('_')
            if market['inverse']:
                return 'fi_' + self.safe_string(splitId, 1)
            else:
                return 'fv_' + self.safe_string(splitId, 1)
        else:
            return account

    async def transfer_out(self, code: str, amount, params={}):
        """
        transfer from futures wallet to spot wallet
        :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.com/#/?id=transfer-structure>`
        """
        return await self.transfer(code, amount, 'future', 'spot', params)

    async def transfer(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}) -> TransferEntry:
        """
        :see: https://docs.futures.kraken.com/#http-api-trading-v3-api-transfers-initiate-wallet-transfer
        :see: https://docs.futures.kraken.com/#http-api-trading-v3-api-transfers-initiate-withdrawal-to-spot-wallet
        transfers currencies between sub-accounts
        :param str code: Unified currency code
        :param float amount: Size of the transfer
        :param str fromAccount: 'main'/'funding'/'future', 'flex', or a unified market symbol
        :param str toAccount: 'main'/'funding', 'flex', 'spot' or a unified market symbol
        :param dict [params]: Exchange specific parameters
        :returns: a `transfer structure <https://docs.ccxt.com/#/?id=transfer-structure>`
        """
        await self.load_markets()
        currency = self.currency(code)
        if fromAccount == 'spot':
            raise BadRequest(self.id + ' transfer does not yet support transfers from spot')
        request: dict = {
            'amount': amount,
        }
        response = None
        if toAccount == 'spot':
            if self.parse_account(fromAccount) != 'cash':
                raise BadRequest(self.id + ' transfer cannot transfer from ' + fromAccount + ' to ' + toAccount)
            request['currency'] = currency['id']
            response = await self.privatePostWithdrawal(self.extend(request, params))
        else:
            request['fromAccount'] = self.parse_account(fromAccount)
            request['toAccount'] = self.parse_account(toAccount)
            request['unit'] = currency['id']
            response = await self.privatePostTransfer(self.extend(request, params))
        #
        #    {
        #        "result": "success",
        #        "serverTime": "2022-04-12T01:22:53.420Z"
        #    }
        #
        transfer = self.parse_transfer(response, currency)
        return self.extend(transfer, {
            'amount': amount,
            'fromAccount': fromAccount,
            'toAccount': toAccount,
        })

    async def set_leverage(self, leverage: Int, symbol: Str = None, params={}):
        """
        set the level of leverage for a market
        :see: https://docs.futures.kraken.com/#http-api-trading-v3-api-multi-collateral-set-the-leverage-setting-for-a-market
        :param float leverage: the rate of leverage
        :param str symbol: unified market symbol
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: response from the exchange
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' setLeverage() requires a symbol argument')
        await self.load_markets()
        request: dict = {
            'maxLeverage': leverage,
            'symbol': self.market_id(symbol).upper(),
        }
        #
        # {result: "success", serverTime: "2023-08-01T09:40:32.345Z"}
        #
        return await self.privatePutLeveragepreferences(self.extend(request, params))

    async def fetch_leverages(self, symbols: Strings = None, params={}) -> Leverages:
        """
        fetch the set leverage for all contract and margin markets
        :see: https://docs.futures.kraken.com/#http-api-trading-v3-api-multi-collateral-get-the-leverage-setting-for-a-market
        :param str[] [symbols]: a list of unified market symbols
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a list of `leverage structures <https://docs.ccxt.com/#/?id=leverage-structure>`
        """
        await self.load_markets()
        response = await self.privateGetLeveragepreferences(params)
        #
        #     {
        #         "result": "success",
        #         "serverTime": "2024-03-06T02:35:46.336Z",
        #         "leveragePreferences": [
        #             {
        #                 "symbol": "PF_ETHUSD",
        #                 "maxLeverage": 30.00
        #             },
        #         ]
        #     }
        #
        leveragePreferences = self.safe_list(response, 'leveragePreferences', [])
        return self.parse_leverages(leveragePreferences, symbols, 'symbol')

    async def fetch_leverage(self, symbol: str, params={}) -> Leverage:
        """
        fetch the set leverage for a market
        :see: https://docs.futures.kraken.com/#http-api-trading-v3-api-multi-collateral-get-the-leverage-setting-for-a-market
        :param str symbol: unified market symbol
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `leverage structure <https://docs.ccxt.com/#/?id=leverage-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchLeverage() requires a symbol argument')
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': self.market_id(symbol).upper(),
        }
        response = await self.privateGetLeveragepreferences(self.extend(request, params))
        #
        #     {
        #         "result": "success",
        #         "serverTime": "2023-08-01T09:54:08.900Z",
        #         "leveragePreferences": [{symbol: "PF_LTCUSD", maxLeverage: "5.00"}]
        #     }
        #
        leveragePreferences = self.safe_list(response, 'leveragePreferences', [])
        data = self.safe_dict(leveragePreferences, 0, {})
        return self.parse_leverage(data, market)

    def parse_leverage(self, leverage: dict, market: Market = None) -> Leverage:
        marketId = self.safe_string(leverage, 'symbol')
        leverageValue = self.safe_integer(leverage, 'maxLeverage')
        return {
            'info': leverage,
            'symbol': self.safe_symbol(marketId, market),
            'marginMode': None,
            'longLeverage': leverageValue,
            'shortLeverage': leverageValue,
        }

    def handle_errors(self, code: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
        if response is None:
            return None
        if code == 429:
            raise DDoSProtection(self.id + ' ' + body)
        errors = self.safe_value(response, 'errors')
        firstError = self.safe_value(errors, 0)
        firtErrorMessage = self.safe_string(firstError, 'message')
        message = self.safe_string(response, 'error', firtErrorMessage)
        if message is None:
            return None
        feedback = self.id + ' ' + body
        self.throw_exactly_matched_exception(self.exceptions['exact'], message, feedback)
        self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback)
        if code == 400:
            raise BadRequest(feedback)
        raise ExchangeError(feedback)  # unknown message

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        apiVersions = self.safe_value(self.options['versions'], api, {})
        methodVersions = self.safe_value(apiVersions, method, {})
        defaultVersion = self.safe_string(methodVersions, path, self.version)
        version = self.safe_string(params, 'version', defaultVersion)
        params = self.omit(params, 'version')
        apiAccess = self.safe_value(self.options['access'], api, {})
        methodAccess = self.safe_value(apiAccess, method, {})
        access = self.safe_string(methodAccess, path, 'public')
        endpoint = version + '/' + self.implode_params(path, params)
        params = self.omit(params, self.extract_params(path))
        query = endpoint
        postData = ''
        if path == 'batchorder':
            postData = 'json=' + self.json(params)
            body = postData
        elif params:
            postData = self.urlencode(params)
            query += '?' + postData
        url = self.urls['api'][api] + query
        if api == 'private' or access == 'private':
            self.check_required_credentials()
            auth = postData + '/api/'
            if api != 'private':
                auth += api + '/'
            auth += endpoint  # 1
            hash = self.hash(self.encode(auth), 'sha256', 'binary')  # 2
            secret = self.base64_to_binary(self.secret)  # 3
            signature = self.hmac(hash, secret, hashlib.sha512, 'base64')  # 4-5
            headers = {
                'Content-Type': 'application/x-www-form-urlencoded',
                'Accept': 'application/json',
                'APIKey': self.apiKey,
                'Authent': signature,
            }
        return {'url': url, 'method': method, 'body': body, 'headers': headers}
