# -*- 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.kucoin import kucoin
from ccxt.base.errors import AuthenticationError
from ccxt.base.errors import PermissionDenied
from ccxt.base.errors import AccountSuspended
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 NotSupported
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 kucoinfutures(kucoin):

    def describe(self):
        return self.deep_extend(super(kucoinfutures, self).describe(), {
            'id': 'kucoinfutures',
            'name': 'KuCoin Futures',
            'countries': ['SC'],
            'rateLimit': 75,
            'version': 'v1',
            'certified': False,
            'pro': False,
            'comment': 'Platform 2.0',
            'quoteJsonNumbers': False,
            'has': {
                'spot': False,
                'margin': False,
                'swap': True,
                'future': True,
                'option': False,
                'cancelAllOrders': True,
                'cancelOrder': True,
                'CORS': None,
                'createDepositAddress': False,
                'createOrder': True,
                'fetchAccounts': False,
                'fetchBalance': True,
                'fetchBorrowRate': False,
                'fetchBorrowRates': False,
                'fetchBorrowRatesPerSymbol': False,
                'fetchClosedOrders': True,
                'fetchCurrencies': False,
                'fetchDepositAddress': True,
                'fetchDeposits': None,
                'fetchFundingFee': False,
                'fetchFundingHistory': True,
                'fetchFundingRate': True,
                'fetchFundingRateHistory': False,
                'fetchIndexOHLCV': False,
                'fetchL3OrderBook': False,
                'fetchLedger': False,
                'fetchMarkets': True,
                'fetchMarkOHLCV': False,
                'fetchMyTrades': True,
                'fetchOHLCV': True,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'fetchOrderBook': True,
                'fetchOrdersByStatus': True,
                'fetchPositions': True,
                'fetchPremiumIndexOHLCV': False,
                'fetchStatus': True,
                'fetchTicker': True,
                'fetchTickers': False,
                'fetchTime': True,
                'fetchTrades': True,
                'fetchWithdrawals': None,
                'setMarginMode': False,
                'transfer': True,
                'transferOut': True,
                'withdraw': None,
                'addMargin': True,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/147508995-9e35030a-d046-43a1-a006-6fabd981b554.jpg',
                'doc': [
                    'https://docs.kucoin.com/futures',
                    'https://docs.kucoin.com',
                ],
                'www': 'https://futures.kucoin.com/',
                'referral': 'https://futures.kucoin.com/?rcode=E5wkqe',
                'api': {
                    'public': 'https://openapi-v2.kucoin.com',
                    'private': 'https://openapi-v2.kucoin.com',
                    'futuresPrivate': 'https://api-futures.kucoin.com',
                    'futuresPublic': 'https://api-futures.kucoin.com',
                },
                'test': {
                    'public': 'https://openapi-sandbox.kucoin.com',
                    'private': 'https://openapi-sandbox.kucoin.com',
                    'futuresPrivate': 'https://api-sandbox-futures.kucoin.com',
                    'futuresPublic': 'https://api-sandbox-futures.kucoin.com',
                },
            },
            'requiredCredentials': {
                'apiKey': True,
                'secret': True,
                'password': True,
            },
            'api': {
                'futuresPublic': {
                    'get': {
                        'contracts/active': 1,
                        'contracts/{symbol}': 1,
                        'ticker': 1,
                        'level2/snapshot': 1.33,
                        'level2/depth{limit}': 1,
                        'level2/message/query': 1,
                        'level3/message/query': 1,  # deprecated，level3/snapshot is suggested
                        'level3/snapshot': 1,  # v2
                        'trade/history': 1,
                        'interest/query': 1,
                        'index/query': 1,
                        'mark-price/{symbol}/current': 1,
                        'premium/query': 1,
                        'funding-rate/{symbol}/current': 1,
                        'timestamp': 1,
                        'status': 1,
                        'kline/query': 1,
                    },
                    'post': {
                        'bullet-public': 1,
                    },
                },
                'futuresPrivate': {
                    'get': {
                        'account-overview': 1.33,
                        'transaction-history': 4.44,
                        'deposit-address': 1,
                        'deposit-list': 1,
                        'withdrawals/quotas': 1,
                        'withdrawal-list': 1,
                        'transfer-list': 1,
                        'orders': 1.33,
                        'stopOrders': 1,
                        'recentDoneOrders': 1,
                        'orders/{orderId}': 1,  # ?clientOid={client-order-id}  # get order by orderId
                        'orders/byClientOid': 1,  # ?clientOid=eresc138b21023a909e5ad59  # get order by clientOid
                        'fills': 4.44,
                        'recentFills': 4.44,
                        'openOrderStatistics': 1,
                        'position': 1,
                        'positions': 4.44,
                        'funding-history': 4.44,
                    },
                    'post': {
                        'withdrawals': 1,
                        'transfer-out': 1,  # v2
                        'orders': 1.33,
                        'position/margin/auto-deposit-status': 1,
                        'position/margin/deposit-margin': 1,
                        'bullet-private': 1,
                    },
                    'delete': {
                        'withdrawals/{withdrawalId}': 1,
                        'cancel/transfer-out': 1,
                        'orders/{orderId}': 1,
                        'orders': 4.44,
                        'stopOrders': 1,
                    },
                },
            },
            'precisionMode': TICK_SIZE,
            'exceptions': {
                'exact': {
                    '400': BadRequest,  # Bad Request -- Invalid request format
                    '401': AuthenticationError,  # Unauthorized -- Invalid API Key
                    '403': NotSupported,  # Forbidden -- The request is forbidden
                    '404': NotSupported,  # Not Found -- The specified resource could not be found
                    '405': NotSupported,  # Method Not Allowed -- You tried to access the resource with an invalid method.
                    '415': BadRequest,  # Content-Type -- application/json
                    '429': RateLimitExceeded,  # Too Many Requests -- Access limit breached
                    '500': ExchangeNotAvailable,  # Internal Server Error -- We had a problem with our server. Try again later.
                    '503': ExchangeNotAvailable,  # Service Unavailable -- We're temporarily offline for maintenance. Please try again later.
                    '100001': InvalidOrder,     # {"code":"100001","msg":"Unavailable to enable both \"postOnly\" and \"hidden\""}
                    '100004': BadRequest,       # {"code":"100004","msg":"Order is in not cancelable state"}
                    '101030': PermissionDenied,  # {"code":"101030","msg":"You haven't yet enabled the margin trading"}
                    '200004': InsufficientFunds,
                    '230003': InsufficientFunds,  # {"code":"230003","msg":"Balance insufficient!"}
                    '260100': InsufficientFunds,  # {"code":"260100","msg":"account.noBalance"}
                    '300003': InsufficientFunds,
                    '300012': InvalidOrder,
                    '400001': AuthenticationError,  # Any of KC-API-KEY, KC-API-SIGN, KC-API-TIMESTAMP, KC-API-PASSPHRASE is missing in your request header.
                    '400002': InvalidNonce,  # KC-API-TIMESTAMP Invalid -- Time differs from server time by more than 5 seconds
                    '400003': AuthenticationError,  # KC-API-KEY not exists
                    '400004': AuthenticationError,  # KC-API-PASSPHRASE error
                    '400005': AuthenticationError,  # Signature error -- Please check your signature
                    '400006': AuthenticationError,  # The IP address is not in the API whitelist
                    '400007': AuthenticationError,  # Access Denied -- Your API key does not have sufficient permissions to access the URI
                    '404000': NotSupported,  # URL Not Found -- The requested resource could not be found
                    '400100': BadRequest,  # Parameter Error -- You tried to access the resource with invalid parameters
                    '411100': AccountSuspended,  # User is frozen -- Please contact us via support center
                    '500000': ExchangeNotAvailable,  # Internal Server Error -- We had a problem with our server. Try again later.
                },
            },
            'fees': {
                'trading': {
                    'tierBased': True,
                    'percentage': True,
                    'taker': self.parse_number('0.0006'),
                    'maker': self.parse_number('0.0002'),
                    'tiers': {
                        'taker': [
                            [self.parse_number('0'), self.parse_number('0.0006')],
                            [self.parse_number('50'), self.parse_number('0.0006')],
                            [self.parse_number('200'), self.parse_number('0.0006')],
                            [self.parse_number('500'), self.parse_number('0.0005')],
                            [self.parse_number('1000'), self.parse_number('0.0004')],
                            [self.parse_number('2000'), self.parse_number('0.0004')],
                            [self.parse_number('4000'), self.parse_number('0.00038')],
                            [self.parse_number('8000'), self.parse_number('0.00035')],
                            [self.parse_number('15000'), self.parse_number('0.00032')],
                            [self.parse_number('25000'), self.parse_number('0.0003')],
                            [self.parse_number('40000'), self.parse_number('0.0003')],
                            [self.parse_number('60000'), self.parse_number('0.0003')],
                            [self.parse_number('80000'), self.parse_number('0.0003')],
                        ],
                        'maker': [
                            [self.parse_number('0'), self.parse_number('0.02')],
                            [self.parse_number('50'), self.parse_number('0.015')],
                            [self.parse_number('200'), self.parse_number('0.01')],
                            [self.parse_number('500'), self.parse_number('0.01')],
                            [self.parse_number('1000'), self.parse_number('0.01')],
                            [self.parse_number('2000'), self.parse_number('0')],
                            [self.parse_number('4000'), self.parse_number('0')],
                            [self.parse_number('8000'), self.parse_number('0')],
                            [self.parse_number('15000'), self.parse_number('-0.003')],
                            [self.parse_number('25000'), self.parse_number('-0.006')],
                            [self.parse_number('40000'), self.parse_number('-0.009')],
                            [self.parse_number('60000'), self.parse_number('-0.012')],
                            [self.parse_number('80000'), self.parse_number('-0.015')],
                        ],
                    },
                },
                'funding': {
                    'tierBased': False,
                    'percentage': False,
                    'withdraw': {},
                    'deposit': {},
                },
            },
            'commonCurrencies': {
                'HOT': 'HOTNOW',
                'EDGE': 'DADI',  # https://github.com/ccxt/ccxt/issues/5756
                'WAX': 'WAXP',
                'TRY': 'Trias',
                'VAI': 'VAIOT',
                'XBT': 'BTC',
            },
            'timeframes': {
                '1m': 1,
                '3m': None,
                '5m': 5,
                '15m': 15,
                '30m': 30,
                '1h': 60,
                '2h': 120,
                '4h': 240,
                '6h': None,
                '8h': 480,
                '12h': 720,
                '1d': 1440,
                '1w': 10080,
            },
            'options': {
                'version': 'v1',
                'symbolSeparator': '-',
                'defaultType': 'swap',
                'marginTypes': {},
                # endpoint versions
                'versions': {
                    'futuresPrivate': {
                        'POST': {
                            'transfer-out': 'v2',
                        },
                    },
                    'futuresPublic': {
                        'GET': {
                            'level3/snapshot': 'v2',
                        },
                    },
                },
                'networks': {
                    'OMNI': 'omni',
                    'ERC20': 'eth',
                    'TRC20': 'trx',
                },
            },
        })

    async def fetch_accounts(self, params={}):
        raise BadRequest(self.id + ' has no method fetchAccounts')

    async def fetch_status(self, params={}):
        response = await self.futuresPublicGetStatus(params)
        #
        #     {
        #         "code":"200000",
        #         "data":{
        #             "msg":"",
        #             "status":"open"
        #         }
        #     }
        #
        data = self.safe_value(response, 'data', {})
        status = self.safe_value(data, 'status')
        if status is not None:
            status = 'ok' if (status == 'open') else 'maintenance'
            self.status = self.extend(self.status, {
                'status': status,
                'updated': self.milliseconds(),
            })
        return self.status

    async def fetch_markets(self, params={}):
        response = await self.futuresPublicGetContractsActive(params)
        #
        #    {
        #        "code": "200000",
        #        "data": {
        #            "symbol": "ETHUSDTM",
        #            "rootSymbol": "USDT",
        #            "type": "FFWCSX",
        #            "firstOpenDate": 1591086000000,
        #            "expireDate": null,
        #            "settleDate": null,
        #            "baseCurrency": "ETH",
        #            "quoteCurrency": "USDT",
        #            "settleCurrency": "USDT",
        #            "maxOrderQty": 1000000,
        #            "maxPrice": 1000000.0000000000,
        #            "lotSize": 1,
        #            "tickSize": 0.05,
        #            "indexPriceTickSize": 0.01,
        #            "multiplier": 0.01,
        #            "initialMargin": 0.01,
        #            "maintainMargin": 0.005,
        #            "maxRiskLimit": 1000000,
        #            "minRiskLimit": 1000000,
        #            "riskStep": 500000,
        #            "makerFeeRate": 0.00020,
        #            "takerFeeRate": 0.00060,
        #            "takerFixFee": 0.0000000000,
        #            "makerFixFee": 0.0000000000,
        #            "settlementFee": null,
        #            "isDeleverage": True,
        #            "isQuanto": True,
        #            "isInverse": False,
        #            "markMethod": "FairPrice",
        #            "fairMethod": "FundingRate",
        #            "fundingBaseSymbol": ".ETHINT8H",
        #            "fundingQuoteSymbol": ".USDTINT8H",
        #            "fundingRateSymbol": ".ETHUSDTMFPI8H",
        #            "indexSymbol": ".KETHUSDT",
        #            "settlementSymbol": "",
        #            "status": "Open",
        #            "fundingFeeRate": 0.000535,
        #            "predictedFundingFeeRate": 0.002197,
        #            "openInterest": "8724443",
        #            "turnoverOf24h": 341156641.03354263,
        #            "volumeOf24h": 74833.54000000,
        #            "markPrice": 4534.07,
        #            "indexPrice":4531.92,
        #            "lastTradePrice": 4545.4500000000,
        #            "nextFundingRateTime": 25481884,
        #            "maxLeverage": 100,
        #            "sourceExchanges":  [
        #                "huobi",
        #                "Okex",
        #                "Binance",
        #                "Kucoin",
        #                "Poloniex",
        #                "Hitbtc"
        #            ],
        #            "premiumsSymbol1M": ".ETHUSDTMPI",
        #            "premiumsSymbol8H": ".ETHUSDTMPI8H",
        #            "fundingBaseSymbol1M": ".ETHINT",
        #            "fundingQuoteSymbol1M": ".USDTINT",
        #            "lowPrice": 4456.90,
        #            "highPrice":  4674.25,
        #            "priceChgPct": 0.0046,
        #            "priceChg": 21.15
        #        }
        #    }
        #
        result = []
        data = self.safe_value(response, 'data')
        for i in range(0, len(data)):
            market = data[i]
            id = self.safe_string(market, 'symbol')
            expiry = self.safe_integer(market, 'expireDate')
            future = True if expiry else False
            swap = not future
            baseId = self.safe_string(market, 'baseCurrency')
            quoteId = self.safe_string(market, 'quoteCurrency')
            settleId = self.safe_string(market, 'settleCurrency')
            base = self.safe_currency_code(baseId)
            quote = self.safe_currency_code(quoteId)
            settle = self.safe_currency_code(settleId)
            symbol = base + '/' + quote + ':' + settle
            type = 'swap'
            if future:
                symbol = symbol + '-' + self.yymmdd(expiry, '')
                type = 'future'
            baseMaxSize = self.safe_number(market, 'baseMaxSize')
            baseMinSizeString = self.safe_string(market, 'baseMinSize')
            quoteMaxSizeString = self.safe_string(market, 'quoteMaxSize')
            baseMinSize = self.parse_number(baseMinSizeString)
            quoteMaxSize = self.parse_number(quoteMaxSizeString)
            quoteMinSize = self.safe_number(market, 'quoteMinSize')
            inverse = self.safe_value(market, 'isInverse')
            status = self.safe_string(market, 'status')
            active = status == 'Open'
            result.append({
                'id': id,
                'symbol': symbol,
                'baseId': baseId,
                'quoteId': quoteId,
                'settleId': settleId,
                'base': base,
                'quote': quote,
                'settle': settle,
                'type': type,
                'spot': False,
                'margin': False,
                'swap': swap,
                'future': future,
                'option': False,
                'active': active,
                'contract': True,
                'linear': not inverse,
                'inverse': inverse,
                'taker': self.safe_number(market, 'takerFeeRate'),
                'maker': self.safe_number(market, 'makerFeeRate'),
                'contractSize': self.parse_number(Precise.string_abs(self.safe_string(market, 'multiplier'))),
                'expiry': expiry,
                'expiryDatetime': self.iso8601(expiry),
                'precision': {
                    'amount': self.safe_number(market, 'lotSize'),
                    'price': self.safe_number(market, 'tickSize'),
                },
                'limits': {
                    'leverage': {
                        'min': None,
                        'max': self.safe_number(market, 'maxLeverage'),
                    },
                    'amount': {
                        'min': baseMinSize,
                        'max': baseMaxSize,
                    },
                    'price': {
                        'min': None,
                        'max': self.parse_number(Precise.string_div(quoteMaxSizeString, baseMinSizeString)),
                    },
                    'cost': {
                        'min': quoteMinSize,
                        'max': quoteMaxSize,
                    },
                },
                'info': market,
            })
        return result

    async def fetch_time(self, params={}):
        response = await self.futuresPublicGetTimestamp(params)
        #
        #    {
        #        code: "200000",
        #        data: 1637385119302,
        #    }
        #
        return self.safe_number(response, 'data')

    async def fetch_ohlcv(self, symbol, timeframe='15m', since=None, limit=None, params={}):
        await self.load_markets()
        market = self.market(symbol)
        marketId = market['id']
        request = {
            'symbol': marketId,
            'granularity': self.timeframes[timeframe],
        }
        duration = self.parse_timeframe(timeframe) * 1000
        endAt = self.milliseconds()
        if since is not None:
            request['from'] = since
            if limit is None:
                limit = self.safe_integer(self.options, 'fetchOHLCVLimit', 200)
            endAt = self.sum(since, limit * duration)
        elif limit is not None:
            since = endAt - limit * duration
            request['from'] = since
        request['to'] = endAt
        response = await self.futuresPublicGetKlineQuery(self.extend(request, params))
        #
        #    {
        #        "code": "200000",
        #        "data": [
        #            [1636459200000, 4779.3, 4792.1, 4768.7, 4770.3, 78051],
        #            [1636460100000, 4770.25, 4778.55, 4757.55, 4777.25, 80164],
        #            [1636461000000, 4777.25, 4791.45, 4774.5, 4791.3, 51555]
        #        ]
        #    }
        #
        data = self.safe_value(response, 'data', [])
        return self.parse_ohlcvs(data, market, timeframe, since, limit)

    def parse_ohlcv(self, ohlcv, market=None):
        #
        #    [
        #        "1545904980000",          # Start time of the candle cycle
        #        "0.058",                  # opening price
        #        "0.049",                  # closing price
        #        "0.058",                  # highest price
        #        "0.049",                  # lowest price
        #        "0.018",                  # base volume
        #        "0.000945",               # quote volume
        #    ]
        #
        return [
            self.safe_integer(ohlcv, 0),
            self.safe_number(ohlcv, 1),
            self.safe_number(ohlcv, 2),
            self.safe_number(ohlcv, 3),
            self.safe_number(ohlcv, 4),
            self.safe_number(ohlcv, 5),
        ]

    async def create_deposit_address(self, code, params={}):
        raise BadRequest(self.id + ' has no method createDepositAddress')

    async def fetch_deposit_address(self, code, params={}):
        await self.load_markets()
        currency = self.currency(code)
        currencyId = currency['id']
        request = {
            'currency': currencyId,  # Currency,including XBT,USDT
        }
        response = await self.futuresPrivateGetDepositAddress(self.extend(request, params))
        #
        #    {
        #        "code": "200000",
        #        "data": {
        #            "address": "0x78d3ad1c0aa1bf068e19c94a2d7b16c9c0fcd8b1",//Deposit address
        #            "memo": null//Address tag. If the returned value is null, it means that the requested token has no memo. If you are to transfer funds from another platform to KuCoin Futures and if the token to be  #transferred has memo(tag), you need to fill in the memo to ensure the transferred funds will be sent  #to the address you specified.
        #        }
        #    }
        #
        data = self.safe_value(response, 'data', {})
        address = self.safe_string(data, 'address')
        if currencyId != 'NIM':
            # contains spaces
            self.check_address(address)
        return {
            'info': response,
            'currency': currencyId,
            'address': address,
            'tag': self.safe_string(data, 'memo'),
            'network': self.safe_string(data, 'chain'),
        }

    async def fetch_order_book(self, symbol, limit=None, params={}):
        await self.load_markets()
        level = self.safe_number(params, 'level')
        if level != 2 and level is not None:
            raise BadRequest(self.id + ' fetchOrderBook can only return level 2')
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        if limit is not None:
            if (limit == 20) or (limit == 100):
                request['limit'] = limit
            else:
                raise BadRequest(self.id + ' fetchOrderBook limit argument must be 20 or 100')
        else:
            request['limit'] = 20
        response = await self.futuresPublicGetLevel2DepthLimit(self.extend(request, params))
        #
        #     {
        #         "code": "200000",
        #         "data": {
        #           "symbol": "XBTUSDM",      #Symbol
        #           "sequence": 100,          #Ticker sequence number
        #           "asks": [
        #                 ["5000.0", 1000],   #Price, quantity
        #                 ["6000.0", 1983]    #Price, quantity
        #           ],
        #           "bids": [
        #                 ["3200.0", 800],    #Price, quantity
        #                 ["3100.0", 100]     #Price, quantity
        #           ],
        #           "ts": 1604643655040584408  # timestamp
        #         }
        #     }
        #
        data = self.safe_value(response, 'data', {})
        timestamp = int(self.safe_integer(data, 'ts') / 1000000)
        orderbook = self.parse_order_book(data, symbol, timestamp, 'bids', 'asks', 0, 1)
        orderbook['nonce'] = self.safe_integer(data, 'sequence')
        return orderbook

    async def fetch_l3_order_book(self, symbol, limit=None, params={}):
        raise BadRequest(self.id + ' only can only fetch the L2 order book')

    async def fetch_ticker(self, symbol, params={}):
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        response = await self.futuresPublicGetTicker(self.extend(request, params))
        #
        #    {
        #        "code": "200000",
        #        "data": {
        #            "sequence": 1638444978558,
        #            "symbol": "ETHUSDTM",
        #            "side": "sell",
        #            "size": 4,
        #            "price": "4229.35",
        #            "bestBidSize": 2160,
        #            "bestBidPrice": "4229.0",
        #            "bestAskPrice": "4229.05",
        #            "tradeId": "61aaa8b777a0c43055fe4851",
        #            "ts": 1638574296209786785,
        #            "bestAskSize": 36,
        #        }
        #    }
        #
        return self.parse_ticker(response['data'], market)

    def parse_ticker(self, ticker, market=None):
        #
        #     {
        #         "code": "200000",
        #         "data": {
        #             "sequence":  1629930362547,
        #             "symbol": "ETHUSDTM",
        #             "side": "buy",
        #             "size":  130,
        #             "price": "4724.7",
        #             "bestBidSize":  5,
        #             "bestBidPrice": "4724.6",
        #             "bestAskPrice": "4724.65",
        #             "tradeId": "618d2a5a77a0c4431d2335f4",
        #             "ts":  1636641371963227600,
        #             "bestAskSize":  1789
        #          }
        #     }
        #
        last = self.safe_number(ticker, 'price')
        marketId = self.safe_string(ticker, 'symbol')
        market = self.safe_market(marketId, market, '-')
        timestamp = Precise.string_div(self.safe_string(ticker, 'ts'), '1000000')
        return self.safe_ticker({
            'symbol': market['symbol'],
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'high': None,
            'low': None,
            'bid': self.safe_number(ticker, 'bestBidPrice'),
            'bidVolume': self.safe_number(ticker, 'bestBidSize'),
            'ask': self.safe_number(ticker, 'bestAskPrice'),
            'askVolume': self.safe_number(ticker, 'bestAskSize'),
            'vwap': None,
            'open': None,
            'close': last,
            'last': last,
            'previousClose': None,
            'change': None,
            'percentage': None,
            'average': None,
            'baseVolume': None,
            'quoteVolume': None,
            'info': ticker,
        }, market)

    async def fetch_funding_history(self, symbol=None, since=None, limit=None, params={}):
        #
        # Private
        # @param symbol(string): The pair for which the contract was traded
        # @param since(number): The unix start time of the first funding payment requested
        # @param limit(number): The number of results to return
        # @param params(dict): Additional parameters to send to the API
        # @param return: Data for the history of the accounts funding payments for futures contracts
        #
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchFundingHistory() requires a symbol argument')
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        if since is not None:
            request['startAt'] = since
        if limit is not None:
            # * Since is ignored if limit is defined
            request['maxCount'] = limit
        response = await self.futuresPrivateGetFundingHistory(self.extend(request, params))
        #
        #    {
        #        "code": "200000",
        #        "data": {
        #            "dataList": [
        #                {
        #                    "id": 239471298749817,
        #                    "symbol": "ETHUSDTM",
        #                    "timePoint": 1638532800000,
        #                    "fundingRate": 0.000100,
        #                    "markPrice": 4612.8300000000,
        #                    "positionQty": 12,
        #                    "positionCost": 553.5396000000,
        #                    "funding": -0.0553539600,
        #                    "settleCurrency": "USDT"
        #                },
        #                ...
        #            ],
        #            "hasMore": True
        #        }
        #    }
        #
        data = self.safe_value(response, 'data')
        dataList = self.safe_value(data, 'dataList')
        fees = []
        for i in range(0, len(dataList)):
            listItem = dataList[i]
            timestamp = self.safe_integer(listItem, 'timePoint')
            fees.append({
                'info': listItem,
                'symbol': symbol,
                'code': self.safe_currency_code(self.safe_string(listItem, 'settleCurrency')),
                'timestamp': timestamp,
                'datetime': self.iso8601(timestamp),
                'id': self.safe_number(listItem, 'id'),
                'amount': self.safe_number(listItem, 'funding'),
                'fundingRate': self.safe_number(listItem, 'fundingRate'),
                'markPrice': self.safe_number(listItem, 'markPrice'),
                'positionQty': self.safe_number(listItem, 'positionQty'),
                'positionCost': self.safe_number(listItem, 'positionCost'),
            })
        return fees

    async def fetch_positions(self, symbols=None, params={}):
        await self.load_markets()
        response = await self.futuresPrivateGetPositions(params)
        #
        #    {
        #        "code": "200000",
        #        "data": [
        #            {
        #                "id": "615ba79f83a3410001cde321",
        #                "symbol": "ETHUSDTM",
        #                "autoDeposit": False,
        #                "maintMarginReq": 0.005,
        #                "riskLimit": 1000000,
        #                "realLeverage": 18.61,
        #                "crossMode": False,
        #                "delevPercentage": 0.86,
        #                "openingTimestamp": 1638563515618,
        #                "currentTimestamp": 1638576872774,
        #                "currentQty": 2,
        #                "currentCost": 83.64200000,
        #                "currentComm": 0.05018520,
        #                "unrealisedCost": 83.64200000,
        #                "realisedGrossCost": 0.00000000,
        #                "realisedCost": 0.05018520,
        #                "isOpen": True,
        #                "markPrice": 4225.01,
        #                "markValue": 84.50020000,
        #                "posCost": 83.64200000,
        #                "posCross": 0.0000000000,
        #                "posInit": 3.63660870,
        #                "posComm": 0.05236717,
        #                "posLoss": 0.00000000,
        #                "posMargin": 3.68897586,
        #                "posMaint": 0.50637594,
        #                "maintMargin": 4.54717586,
        #                "realisedGrossPnl": 0.00000000,
        #                "realisedPnl": -0.05018520,
        #                "unrealisedPnl": 0.85820000,
        #                "unrealisedPnlPcnt": 0.0103,
        #                "unrealisedRoePcnt": 0.2360,
        #                "avgEntryPrice": 4182.10,
        #                "liquidationPrice": 4023.00,
        #                "bankruptPrice": 4000.25,
        #                "settleCurrency": "USDT",
        #                "isInverse": False
        #            }
        #        ]
        #    }
        #
        return self.parse_positions(self.safe_value(response, 'data'))

    def parse_positions(self, positions):
        result = []
        for i in range(0, len(positions)):
            result.append(self.parse_position(positions[i]))
        return result

    def parse_position(self, position, market=None):
        #
        #    {
        #        "code": "200000",
        #        "data": [
        #            {
        #                "id": "615ba79f83a3410001cde321",         # Position ID
        #                "symbol": "ETHUSDTM",                     # Symbol
        #                "autoDeposit": False,                     # Auto deposit margin or not
        #                "maintMarginReq": 0.005,                  # Maintenance margin requirement
        #                "riskLimit": 1000000,                     # Risk limit
        #                "realLeverage": 25.92,                    # Leverage of the order
        #                "crossMode": False,                       # Cross mode or not
        #                "delevPercentage": 0.76,                  # ADL ranking percentile
        #                "openingTimestamp": 1638578546031,        # Open time
        #                "currentTimestamp": 1638578563580,        # Current timestamp
        #                "currentQty": 2,                          # Current postion quantity
        #                "currentCost": 83.787,                    # Current postion value
        #                "currentComm": 0.0167574,                 # Current commission
        #                "unrealisedCost": 83.787,                 # Unrealised value
        #                "realisedGrossCost": 0.0,                 # Accumulated realised gross profit value
        #                "realisedCost": 0.0167574,                # Current realised position value
        #                "isOpen": True,                           # Opened position or not
        #                "markPrice": 4183.38,                     # Mark price
        #                "markValue": 83.6676,                     # Mark value
        #                "posCost": 83.787,                        # Position value
        #                "posCross": 0.0,                          # added margin
        #                "posInit": 3.35148,                       # Leverage margin
        #                "posComm": 0.05228309,                    # Bankruptcy cost
        #                "posLoss": 0.0,                           # Funding fees paid out
        #                "posMargin": 3.40376309,                  # Position margin
        #                "posMaint": 0.50707892,                   # Maintenance margin
        #                "maintMargin": 3.28436309,                # Position margin
        #                "realisedGrossPnl": 0.0,                  # Accumulated realised gross profit value
        #                "realisedPnl": -0.0167574,                # Realised profit and loss
        #                "unrealisedPnl": -0.1194,                 # Unrealised profit and loss
        #                "unrealisedPnlPcnt": -0.0014,             # Profit-loss ratio of the position
        #                "unrealisedRoePcnt": -0.0356,             # Rate of return on investment
        #                "avgEntryPrice": 4189.35,                 # Average entry price
        #                "liquidationPrice": 4044.55,              # Liquidation price
        #                "bankruptPrice": 4021.75,                 # Bankruptcy price
        #                "settleCurrency": "USDT",                 # Currency used to clear and settle the trades
        #                "isInverse": False
        #            }
        #        ]
        #    }
        #
        symbol = self.safe_string(position, 'symbol')
        market = self.safe_market(symbol, market)
        timestamp = self.safe_number(position, 'currentTimestamp')
        size = self.safe_string(position, 'currentQty')
        side = None
        if Precise.string_gt(size, '0'):
            side = 'buy'
        elif Precise.string_lt(size, '0'):
            side = 'sell'
        notional = Precise.string_abs(self.safe_string(position, 'posCost'))
        initialMargin = self.safe_string(position, 'posInit')
        initialMarginPercentage = Precise.string_div(initialMargin, notional)
        # marginRatio = Precise.string_div(maintenanceRate, collateral)
        unrealisedPnl = self.safe_string(position, 'unrealisedPnl')
        crossMode = self.safe_value(position, 'crossMode')
        # currently crossMode is always set to False and only isolated positions are supported
        marginType = 'cross' if crossMode else 'isolated'
        return {
            'info': position,
            'symbol': self.safe_string(market, 'symbol'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'initialMargin': self.parse_number(initialMargin),
            'initialMarginPercentage': self.parse_number(initialMarginPercentage),
            'maintenanceMargin': self.safe_number(position, 'posMaint'),
            'maintenanceMarginPercentage': self.safe_number(position, 'maintMarginReq'),
            'entryPrice': self.safe_number(position, 'avgEntryPrice'),
            'notional': self.parse_number(notional),
            'leverage': self.safe_number(position, 'realLeverage'),
            'unrealizedPnl': self.parse_number(unrealisedPnl),
            'contracts': self.parse_number(Precise.string_abs(size)),
            'contractSize': self.safe_value(market, 'contractSize'),
            #     realisedPnl: position['realised_pnl'],
            'marginRatio': None,
            'liquidationPrice': self.safe_number(position, 'liquidationPrice'),
            'markPrice': self.safe_number(position, 'markPrice'),
            'collateral': self.safe_number(position, 'maintMargin'),
            'marginType': marginType,
            'side': side,
            'percentage': self.parse_number(Precise.string_div(unrealisedPnl, initialMargin)),
        }

    async def create_order(self, symbol, type, side, amount, price=None, params={}):
        await self.load_markets()
        market = self.market(symbol)
        # required param, cannot be used twice
        clientOrderId = self.safe_string_2(params, 'clientOid', 'clientOrderId', self.uuid())
        params = self.omit(params, ['clientOid', 'clientOrderId'])
        if amount < 1:
            raise InvalidOrder(self.id + ' createOrder() minimum contract order amount is 1')
        preciseAmount = int(self.amount_to_precision(symbol, amount))
        request = {
            'clientOid': clientOrderId,
            'side': side,
            'symbol': market['id'],
            'type': type,  # limit or market
            'size': preciseAmount,
            'leverage': 1,
            # 'remark': '',  # optional remark for the order, length cannot exceed 100 utf8 characters
            # 'tradeType': 'TRADE',  # TRADE, MARGIN_TRADE  # not used with margin orders
            # limit orders ---------------------------------------------------
            # 'timeInForce': 'GTC',  # GTC, GTT, IOC, or FOK(default is GTC), limit orders only
            # 'cancelAfter': long,  # cancel after n seconds, requires timeInForce to be GTT
            # 'postOnly': False,  # Post only flag, invalid when timeInForce is IOC or FOK
            # 'hidden': False,  # Order will not be displayed in the order book
            # 'iceberg': False,  # Only a portion of the order is displayed in the order book
            # 'visibleSize': self.amount_to_precision(symbol, visibleSize),  # The maximum visible size of an iceberg order
            # market orders --------------------------------------------------
            # 'funds': self.cost_to_precision(symbol, cost),  # Amount of quote currency to use
            # stop orders ----------------------------------------------------
            # 'stop': 'loss',  # loss or entry, the default is loss, requires stopPrice
            # 'stopPrice': self.price_to_precision(symbol, amount),  # need to be defined if stop is specified
            # 'stopPriceType'  # Either TP, IP or MP, Need to be defined if stop is specified.
            # margin orders --------------------------------------------------
            # 'marginMode': 'cross',  # cross(cross mode) and isolated(isolated mode), set to cross by default, the isolated mode will be released soon, stay tuned
            # 'autoBorrow': False,  # The system will first borrow you funds at the optimal interest rate and then place an order for you
            # futures orders -------------------------------------------------
            # reduceOnly  #(boolean) A mark to reduce the position size only. Set to False by default. Need to set the position size when reduceOnly is True.
            # closeOrder  #(boolean) A mark to close the position. Set to False by default. It will close all the positions when closeOrder is True.
            # forceHold  #(boolean) A mark to forcely hold the funds for an order, even though it's an order to reduce the position size. This helps the order stay on the order book and not get canceled when the position size changes. Set to False by default.
        }
        stopPrice = self.safe_number(params, 'stopPrice')
        if stopPrice:
            request['stop'] = 'down' if (side == 'buy') else 'up'
            stopPriceType = self.safe_string(params, 'stopPriceType')
            if not stopPriceType:
                raise ArgumentsRequired(self.id + ' createOrder() trigger orders require a stopPriceType parameter to be set to TP, IP or MP(Trade Price, Index Price or Mark Price)')
        uppercaseType = type.upper()
        timeInForce = self.safe_string(params, 'timeInForce')
        if uppercaseType == 'LIMIT':
            if price is None:
                raise ArgumentsRequired(self.id + ' createOrder() requires a price argument for limit orders')
            else:
                request['price'] = self.price_to_precision(symbol, price)
            if timeInForce is not None:
                timeInForce = timeInForce.upper()
                request['timeInForce'] = timeInForce
        postOnly = self.safe_value(params, 'postOnly', False)
        hidden = self.safe_value(params, 'hidden')
        if postOnly and hidden is not None:
            raise BadRequest(self.id + ' createOrder() does not support the postOnly parameter together with a hidden parameter')
        iceberg = self.safe_value(params, 'iceberg')
        if iceberg:
            visibleSize = self.safe_value(params, 'visibleSize')
            if visibleSize is None:
                raise ArgumentsRequired(self.id + ' createOrder() requires a visibleSize parameter for iceberg orders')
        params = self.omit(params, 'timeInForce')  # Time in force only valid for limit orders, exchange error when gtc for market orders
        response = await self.futuresPrivatePostOrders(self.extend(request, params))
        #
        #    {
        #        code: "200000",
        #        data: {
        #            orderId: "619717484f1d010001510cde",
        #        },
        #    }
        #
        data = self.safe_value(response, 'data', {})
        return {
            'id': self.safe_string(data, 'orderId'),
            'clientOrderId': None,
            'timestamp': None,
            'datetime': None,
            'lastTradeTimestamp': None,
            'symbol': None,
            'type': None,
            'side': None,
            'price': None,
            'amount': None,
            'cost': None,
            'average': None,
            'filled': None,
            'remaining': None,
            'status': None,
            'fee': None,
            'trades': None,
            'timeInForce': None,
            'postOnly': None,
            'stopPrice': None,
            'info': response,
        }

    async def cancel_order(self, id, symbol=None, params={}):
        await self.load_markets()
        request = {
            'orderId': id,
        }
        response = await self.futuresPrivateDeleteOrdersOrderId(self.extend(request, params))
        #
        #   {
        #       code: "200000",
        #       data: {
        #           cancelledOrderIds: [
        #                "619714b8b6353000014c505a",
        #           ],
        #       },
        #   }
        #
        return self.safe_value(response, 'data')

    async def cancel_all_orders(self, symbol=None, params={}):
        await self.load_markets()
        request = {}
        if symbol is not None:
            request['symbol'] = self.market_id(symbol)
        response = await self.futuresPrivateDeleteOrders(self.extend(request, params))
        # ? futuresPrivateDeleteStopOrders
        #   {
        #       code: "200000",
        #       data: {
        #           cancelledOrderIds: [
        #                "619714b8b6353000014c505a",
        #           ],
        #       },
        #   }
        #
        return self.safe_value(response, 'data')

    async def add_margin(self, symbol, amount, params={}):
        await self.load_markets()
        market = self.market(symbol)
        uuid = self.uuid()
        request = {
            'symbol': market['id'],
            'margin': amount,
            'bizNo': uuid,
        }
        return await self.futuresPrivatePostPositionMarginDepositMargin(self.extend(request, params))

    async def fetch_orders_by_status(self, status, symbol=None, since=None, limit=None, params={}):
        await self.load_markets()
        request = {
            'status': status,
        }
        market = None
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        if since is not None:
            request['startAt'] = since
        response = await self.futuresPrivateGetOrders(self.extend(request, params))
        responseData = self.safe_value(response, 'data', {})
        orders = self.safe_value(responseData, 'items', [])
        return self.parse_orders(orders, market, since, limit)

    async def fetch_order(self, id=None, symbol=None, params={}):
        await self.load_markets()
        request = {}
        method = 'futuresPrivateGetOrdersOrderId'
        if id is None:
            clientOrderId = self.safe_string_2(params, 'clientOid', 'clientOrderId')
            if clientOrderId is None:
                raise InvalidOrder(self.id + ' fetchOrder() requires parameter id or params.clientOid')
            request['clientOid'] = clientOrderId
            method = 'futuresPrivateGetOrdersByClientOid'
            params = self.omit(params, ['clientOid', 'clientOrderId'])
        else:
            request['orderId'] = id
        response = await getattr(self, method)(self.extend(request, params))
        market = self.market(symbol) if (symbol is not None) else None
        responseData = self.safe_value(response, 'data')
        return self.parse_order(responseData, market)

    def parse_order(self, order, market=None):
        marketId = self.safe_string(order, 'symbol')
        market = self.safe_market(marketId, market)
        symbol = market['symbol']
        orderId = self.safe_string(order, 'id')
        type = self.safe_string(order, 'type')
        timestamp = self.safe_integer(order, 'createdAt')
        datetime = self.iso8601(timestamp)
        price = self.safe_string(order, 'price')
        # price is zero for market order
        # omitZero is called in safeOrder2
        side = self.safe_string(order, 'side')
        feeCurrencyId = self.safe_string(order, 'feeCurrency')
        feeCurrency = self.safe_currency_code(feeCurrencyId)
        feeCost = self.safe_number(order, 'fee')
        amount = self.safe_string(order, 'size')
        filled = self.safe_string(order, 'dealSize')
        rawCost = self.safe_string_2(order, 'dealFunds', 'filledValue')
        leverage = self.safe_string(order, 'leverage')
        cost = Precise.string_div(rawCost, leverage)
        average = None
        if Precise.string_gt(filled, '0'):
            if market['linear']:
                average = Precise.string_div(rawCost, Precise.string_mul(market['contractSize'], filled))
            else:
                average = Precise.string_div(Precise.string_mul(market['contractSize'], filled), rawCost)
        # precision reported by their api is 8 d.p.
        # average = Precise.string_div(rawCost, Precise.string_mul(filled, market['contractSize']))
        # bool
        isActive = self.safe_value(order, 'isActive', False)
        cancelExist = self.safe_value(order, 'cancelExist', False)
        status = 'open' if isActive else 'closed'
        status = 'canceled' if cancelExist else status
        fee = {
            'currency': feeCurrency,
            'cost': feeCost,
        }
        clientOrderId = self.safe_string(order, 'clientOid')
        timeInForce = self.safe_string(order, 'timeInForce')
        stopPrice = self.safe_number(order, 'stopPrice')
        postOnly = self.safe_value(order, 'postOnly')
        return self.safe_order({
            'id': orderId,
            'clientOrderId': clientOrderId,
            'symbol': symbol,
            'type': type,
            'timeInForce': timeInForce,
            'postOnly': postOnly,
            'side': side,
            'amount': amount,
            'price': price,
            'stopPrice': stopPrice,
            'cost': cost,
            'filled': filled,
            'remaining': None,
            'timestamp': timestamp,
            'datetime': datetime,
            'fee': fee,
            'status': status,
            'info': order,
            'lastTradeTimestamp': None,
            'average': average,
            'trades': None,
        }, market)

    async def fetch_funding_rate(self, symbol, params={}):
        await self.load_markets()
        request = {
            'symbol': self.market_id(symbol),
        }
        response = await self.futuresPublicGetFundingRateSymbolCurrent(self.extend(request, params))
        #
        #    {
        #        code: "200000",
        #        data: {
        #            symbol: ".ETHUSDTMFPI8H",
        #            granularity: 28800000,
        #            timePoint: 1637380800000,
        #            value: 0.0001,
        #            predictedValue: 0.0001,
        #        },
        #    }
        #
        data = self.safe_value(response, 'data')
        timestamp = self.safe_number(data, 'timePoint')
        return {
            'info': data,
            'symbol': symbol,
            'markPrice': None,
            'indexPrice': None,
            'interestRate': None,
            'estimatedSettlePrice': None,
            'timestamp': None,
            'datetime': None,
            'previousFundingRate': self.safe_number(data, 'value'),
            'nextFundingRate': self.safe_number(data, 'predictedValue'),
            'previousFundingTimestamp': timestamp,
            'nextFundingTimestamp': None,
            'previousFundingDatetime': self.iso8601(timestamp),
            'nextFundingDatetime': None,
        }

    def parse_balance(self, response):
        result = {
            'info': response,
            'timestamp': None,
            'datetime': None,
        }
        data = self.safe_value(response, 'data')
        currencyId = self.safe_string(data, 'currency')
        code = self.safe_currency_code(currencyId)
        account = self.account()
        account['free'] = self.safe_string(data, 'availableBalance')
        account['total'] = self.safe_string(data, 'accountEquity')
        result[code] = account
        return self.safe_balance(result)

    async def fetch_balance(self, params={}):
        await self.load_markets()
        # only fetches one balance at a time
        # by default it will only fetch the BTC balance of the futures account
        # you can send 'currency' in params to fetch other currencies
        # fetchBalance({'type': 'futures', 'currency': 'USDT'})
        response = await self.futuresPrivateGetAccountOverview(params)
        #
        #     {
        #         code: '200000',
        #         data: {
        #             accountEquity: 0.00005,
        #             unrealisedPNL: 0,
        #             marginBalance: 0.00005,
        #             positionMargin: 0,
        #             orderMargin: 0,
        #             frozenFunds: 0,
        #             availableBalance: 0.00005,
        #             currency: 'XBT'
        #         }
        #     }
        #
        return self.parse_balance(response)

    async def transfer(self, code, amount, fromAccount, toAccount, params={}):
        if (toAccount != 'spot' and toAccount != 'trade' and toAccount != 'trading') or (fromAccount != 'futures' and fromAccount != 'contract'):
            raise BadRequest(self.id + ' only supports transfers from contract(futures) account to trade(spot) account')
        return self.transfer_out(code, amount, params)

    async def transfer_out(self, code, amount, params={}):
        await self.load_markets()
        currency = self.currency(code)
        request = {
            'currency': self.safe_string(currency, 'id'),  # Currency,including XBT,USDT
            'amount': amount,
        }
        # transfer from usdm futures wallet to spot wallet
        response = await self.futuresPrivatePostTransferOut(self.extend(request, params))
        #
        #    {
        #        "code": "200000",
        #        "data": {
        #            "applyId": "5bffb63303aa675e8bbe18f9"  # Transfer-out request ID
        #        }
        #    }
        #
        data = self.safe_value(response, 'data')
        timestamp = self.safe_string(data, 'updatedAt')
        return {
            'info': response,
            'id': self.safe_string(data, 'applyId'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'currency': code,
            'amount': amount,
            'fromAccount': 'futures',
            'toAccount': 'spot',
            'status': self.safe_string(data, 'status'),
        }

    async def fetch_my_trades(self, symbol=None, since=None, limit=None, params={}):
        await self.load_markets()
        request = {
            # orderId(String) [optional] Fills for a specific order(other parameters can be ignored if specified)
            # symbol(String) [optional] Symbol of the contract
            # side(String) [optional] buy or sell
            # type(String) [optional] limit, market, limit_stop or market_stop
            # startAt(long) [optional] Start time(milisecond)
            # endAt(long) [optional] End time(milisecond)
        }
        market = None
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        if since is not None:
            request['startAt'] = since
        response = await self.futuresPrivateGetFills(self.extend(request, params))
        #
        #    {
        #        "code": "200000",
        #        "data": {
        #          "currentPage": 1,
        #          "pageSize": 1,
        #          "totalNum": 251915,
        #          "totalPage": 251915,
        #          "items": [
        #              {
        #                  "symbol": "XBTUSDM",  # Ticker symbol of the contract
        #                  "tradeId": "5ce24c1f0c19fc3c58edc47c",  # Trade ID
        #                  "orderId": "5ce24c16b210233c36ee321d",  # Order ID
        #                  "side": "sell",  # Transaction side
        #                  "liquidity": "taker",  # Liquidity- taker or maker
        #                  "price": "8302",  # Filled price
        #                  "size": 10,  # Filled amount
        #                  "value": "0.001204529",  # Order value
        #                  "feeRate": "0.0005",  # Floating fees
        #                  "fixFee": "0.00000006",  # Fixed fees
        #                  "feeCurrency": "XBT",  # Charging currency
        #                  "stop": "",  # A mark to the stop order type
        #                  "fee": "0.0000012022",  # Transaction fee
        #                  "orderType": "limit",  # Order type
        #                  "tradeType": "trade",  # Trade type(trade, liquidation, ADL or settlement)
        #                  "createdAt": 1558334496000,  # Time the order created
        #                  "settleCurrency": "XBT",  # settlement currency
        #                  "tradeTime": 1558334496000000000  # trade time in nanosecond
        #              }
        #            ]
        #        }
        #    }
        #
        data = self.safe_value(response, 'data', {})
        trades = self.safe_value(data, 'items', {})
        return self.parse_trades(trades, market, since, limit)

    async def fetch_trades(self, symbol, since=None, limit=None, params={}):
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        response = await self.futuresPublicGetTradeHistory(self.extend(request, params))
        #
        #      {
        #          "code": "200000",
        #          "data": [
        #              {
        #                  "sequence": 32114961,
        #                  "side": "buy",
        #                  "size": 39,
        #                  "price": "4001.6500000000",
        #                  "takerOrderId": "61c20742f172110001e0ebe4",
        #                  "makerOrderId": "61c2073fcfc88100010fcb5d",
        #                  "tradeId": "61c2074277a0c473e69029b8",
        #                  "ts": 1640105794099993896   # filled time
        #              }
        #          ]
        #      }
        #
        trades = self.safe_value(response, 'data', [])
        return self.parse_trades(trades, market, since, limit)

    def parse_trade(self, trade, market=None):
        #
        # fetchTrades(public)
        #
        #     {
        #         "sequence": 32114961,
        #         "side": "buy",
        #         "size": 39,
        #         "price": "4001.6500000000",
        #         "takerOrderId": "61c20742f172110001e0ebe4",
        #         "makerOrderId": "61c2073fcfc88100010fcb5d",
        #         "tradeId": "61c2074277a0c473e69029b8",
        #         "ts": 1640105794099993896   # filled time
        #     }
        #
        # fetchMyTrades(private) v2
        #
        #     {
        #         "symbol":"BTC-USDT",
        #         "tradeId":"5c35c02709e4f67d5266954e",
        #         "orderId":"5c35c02703aa673ceec2a168",
        #         "counterOrderId":"5c1ab46003aa676e487fa8e3",
        #         "side":"buy",
        #         "liquidity":"taker",
        #         "forceTaker":true,
        #         "price":"0.083",
        #         "size":"0.8424304",
        #         "funds":"0.0699217232",
        #         "fee":"0",
        #         "feeRate":"0",
        #         "feeCurrency":"USDT",
        #         "stop":"",
        #         "type":"limit",
        #         "createdAt":1547026472000
        #     }
        #
        marketId = self.safe_string(trade, 'symbol')
        symbol = self.safe_symbol(marketId, market, '-')
        id = self.safe_string_2(trade, 'tradeId', 'id')
        orderId = self.safe_string(trade, 'orderId')
        takerOrMaker = self.safe_string(trade, 'liquidity')
        timestamp = self.safe_integer(trade, 'time')
        if timestamp is not None:
            timestamp = int(timestamp / 1000000)
        else:
            timestamp = self.safe_integer(trade, 'createdAt')
            # if it's a historical v1 trade, the exchange returns timestamp in seconds
            if ('dealValue' in trade) and (timestamp is not None):
                timestamp = timestamp * 1000
        priceString = self.safe_string_2(trade, 'price', 'dealPrice')
        amountString = self.safe_string_2(trade, 'size', 'amount')
        price = self.parse_number(priceString)
        amount = self.parse_number(amountString)
        side = self.safe_string(trade, 'side')
        fee = None
        feeCost = self.safe_number(trade, 'fee')
        if feeCost is not None:
            feeCurrencyId = self.safe_string(trade, 'feeCurrency')
            feeCurrency = self.safe_currency_code(feeCurrencyId)
            if feeCurrency is None:
                if market is not None:
                    feeCurrency = market['quote'] if (side == 'sell') else market['base']
            fee = {
                'cost': feeCost,
                'currency': feeCurrency,
                'rate': self.safe_number(trade, 'feeRate'),
            }
        type = self.safe_string_2(trade, 'type', 'orderType')
        if type == 'match':
            type = None
        cost = self.safe_number_2(trade, 'funds', 'dealValue')
        if cost is None:
            market = self.market(symbol)
            contractSize = self.safe_string(market, 'contractSize')
            contractCost = Precise.string_mul(priceString, amountString)
            if contractSize and contractCost:
                cost = self.parse_number(Precise.string_mul(contractCost, contractSize))
        return {
            'info': trade,
            'id': id,
            'order': orderId,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': symbol,
            'type': type,
            'takerOrMaker': takerOrMaker,
            'side': side,
            'price': price,
            'amount': amount,
            'cost': cost,
            'fee': fee,
        }

    async def fetch_deposits(self, code=None, since=None, limit=None, params={}):
        await self.load_markets()
        request = {}
        currency = None
        if code is not None:
            currency = self.currency(code)
            request['currency'] = currency['id']
        if limit is not None:
            request['pageSize'] = limit
        if since is not None:
            request['startAt'] = since
        response = await self.futuresPrivateGetDepositList(self.extend(request, params))
        #
        #     {
        #         code: '200000',
        #         data: {
        #             "currentPage": 1,
        #             "pageSize": 5,
        #             "totalNum": 2,
        #             "totalPage": 1,
        #             "items": [
        #                 {
        #                     "address": "0x5f047b29041bcfdbf0e4478cdfa753a336ba6989",
        #                     "memo": "5c247c8a03aa677cea2a251d",
        #                     "amount": 1,
        #                     "fee": 0.0001,
        #                     "currency": "KCS",
        #                     "isInner": False,
        #                     "walletTxId": "5bbb57386d99522d9f954c5a@test004",
        #                     "status": "SUCCESS",
        #                     "createdAt": 1544178843000,
        #                     "updatedAt": 1544178891000
        #                     "remark":"foobar"
        #                 },
        #                 ...
        #             ]
        #         }
        #     }
        #
        responseData = response['data']['items']
        return self.parse_transactions(responseData, currency, since, limit, {'type': 'deposit'})

    async def fetch_withdrawals(self, code=None, since=None, limit=None, params={}):
        await self.load_markets()
        request = {}
        currency = None
        if code is not None:
            currency = self.currency(code)
            request['currency'] = currency['id']
        if limit is not None:
            request['pageSize'] = limit
        if since is not None:
            request['startAt'] = since
        response = await self.futuresPrivateGetWithdrawalList(self.extend(request, params))
        #
        #     {
        #         code: '200000',
        #         data: {
        #             "currentPage": 1,
        #             "pageSize": 5,
        #             "totalNum": 2,
        #             "totalPage": 1,
        #             "items": [
        #                 {
        #                     "id": "5c2dc64e03aa675aa263f1ac",
        #                     "address": "0x5bedb060b8eb8d823e2414d82acce78d38be7fe9",
        #                     "memo": "",
        #                     "currency": "ETH",
        #                     "amount": 1.0000000,
        #                     "fee": 0.0100000,
        #                     "walletTxId": "3e2414d82acce78d38be7fe9",
        #                     "isInner": False,
        #                     "status": "FAILURE",
        #                     "createdAt": 1546503758000,
        #                     "updatedAt": 1546504603000
        #                 },
        #                 ...
        #             ]
        #         }
        #     }
        #
        responseData = response['data']['items']
        return self.parse_transactions(responseData, currency, since, limit, {'type': 'withdrawal'})

    async def fetch_funding_fee(self, code, params={}):
        raise BadRequest(self.id + ' has no method fetchFundingFee')

    async def fetch_ledger(self, code=None, since=None, limit=None, params={}):
        raise BadRequest(self.id + ' has no method fetchLedger')
