# -*- 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.base.errors import ExchangeError
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 BadResponse
from ccxt.base.errors import InsufficientFunds
from ccxt.base.errors import InvalidOrder
from ccxt.base.errors import OrderNotFound
from ccxt.base.errors import NetworkError
from ccxt.base.errors import DDoSProtection
from ccxt.base.errors import InvalidNonce


class digifinex(Exchange):

    def describe(self):
        return self.deep_extend(super(digifinex, self).describe(), {
            'id': 'digifinex',
            'name': 'DigiFinex',
            'countries': ['SG'],
            'version': 'v3',
            'rateLimit': 900,  # 300 for posts
            'has': {
                'cancelOrder': True,
                'cancelOrders': True,
                'createOrder': True,
                'fetchBalance': True,
                'fetchLedger': True,
                'fetchMarkets': True,
                'fetchMyTrades': True,
                'fetchOHLCV': True,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'fetchOrderBook': True,
                'fetchOrders': True,
                'fetchStatus': True,
                'fetchTicker': True,
                'fetchTickers': True,
                'fetchTime': True,
                'fetchTrades': True,
            },
            'timeframes': {
                '1m': '1',
                '5m': '5',
                '15m': '15',
                '30m': '30',
                '1h': '60',
                '4h': '240',
                '12h': '720',
                '1d': '1D',
                '1w': '1W',
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/51840849/87443315-01283a00-c5fe-11ea-8628-c2a0feaf07ac.jpg',
                'api': 'https://openapi.digifinex.com',
                'www': 'https://www.digifinex.com',
                'doc': [
                    'https://docs.digifinex.com',
                ],
                'fees': 'https://digifinex.zendesk.com/hc/en-us/articles/360000328422-Fee-Structure-on-DigiFinex',
                'referral': 'https://www.digifinex.com/en-ww/from/DhOzBg/3798****5114',
            },
            'api': {
                'v2': {
                    'get': [
                        'ticker',
                    ],
                },
                'public': {
                    'get': [
                        '{market}/symbols',
                        'kline',
                        'margin/currencies',
                        'margin/symbols',
                        'markets',  # undocumented
                        'order_book',
                        'ping',
                        'spot/symbols',
                        'time',
                        'trades',
                        'trades/symbols',
                        'ticker',
                    ],
                },
                'private': {
                    'get': [
                        '{market}/financelog',
                        '{market}/mytrades',
                        '{market}/order',
                        '{market}/order/current',
                        '{market}/order/history',
                        'margin/assets',
                        'margin/financelog',
                        'margin/mytrades',
                        'margin/order',
                        'margin/order/current',
                        'margin/order/history',
                        'margin/positions',
                        'otc/financelog',
                        'spot/assets',
                        'spot/financelog',
                        'spot/mytrades',
                        'spot/order',
                        'spot/order/current',
                        'spot/order/history',
                    ],
                    'post': [
                        '{market}/order/cancel',
                        '{market}/order/new',
                        'margin/order/cancel',
                        'margin/order/new',
                        'margin/position/close',
                        'spot/order/cancel',
                        'spot/order/new',
                        'transfer',
                    ],
                },
            },
            'fees': {
                'trading': {
                    'tierBased': False,
                    'percentage': True,
                    'maker': 0.002,
                    'taker': 0.002,
                },
            },
            'exceptions': {
                'exact': {
                    '10001': [BadRequest, "Wrong request method, please check it's a GET ot POST request"],
                    '10002': [AuthenticationError, 'Invalid ApiKey'],
                    '10003': [AuthenticationError, "Sign doesn't match"],
                    '10004': [BadRequest, 'Illegal request parameters'],
                    '10005': [DDoSProtection, 'Request frequency exceeds the limit'],
                    '10006': [PermissionDenied, 'Unauthorized to execute self request'],
                    '10007': [PermissionDenied, 'IP address Unauthorized'],
                    '10008': [InvalidNonce, 'Timestamp for self request is invalid, timestamp must within 1 minute'],
                    '10009': [NetworkError, 'Unexist endpoint, please check endpoint URL'],
                    '10011': [AccountSuspended, 'ApiKey expired. Please go to client side to re-create an ApiKey'],
                    '20001': [PermissionDenied, 'Trade is not open for self trading pair'],
                    '20002': [PermissionDenied, 'Trade of self trading pair is suspended'],
                    '20003': [InvalidOrder, 'Invalid price or amount'],
                    '20007': [InvalidOrder, 'Price precision error'],
                    '20008': [InvalidOrder, 'Amount precision error'],
                    '20009': [InvalidOrder, 'Amount is less than the minimum requirement'],
                    '20010': [InvalidOrder, 'Cash Amount is less than the minimum requirement'],
                    '20011': [InsufficientFunds, 'Insufficient balance'],
                    '20012': [BadRequest, 'Invalid trade type, valid value: buy/sell)'],
                    '20013': [InvalidOrder, 'No order info found'],
                    '20014': [BadRequest, 'Invalid date, Valid format: 2018-07-25)'],
                    '20015': [BadRequest, 'Date exceeds the limit'],
                    '20018': [PermissionDenied, 'Your trading rights have been banned by the system'],
                    '20019': [BadRequest, 'Wrong trading pair symbol. Correct format:"usdt_btc". Quote asset is in the front'],
                    '20020': [DDoSProtection, "You have violated the API operation trading rules and temporarily forbid trading. At present, we have certain restrictions on the user's transaction rate and withdrawal rate."],
                    '50000': [ExchangeError, 'Exception error'],
                },
                'broad': {
                },
            },
            'options': {
                'defaultType': 'spot',
                'types': ['spot', 'margin', 'otc'],
            },
            'commonCurrencies': {
                'BHT': 'Black House Test',
            },
        })

    async def fetch_markets(self, params={}):
        options = self.safe_value(self.options, 'fetchMarkets', {})
        method = self.safe_string(options, 'method', 'fetch_markets_v2')
        return await getattr(self, method)(params)

    async def fetch_markets_v2(self, params={}):
        response = await self.publicGetTradesSymbols(params)
        #
        #     {
        #         "symbol_list":[
        #             {
        #                 "order_types":["LIMIT","MARKET"],
        #                 "quote_asset":"USDT",
        #                 "minimum_value":2,
        #                 "amount_precision":4,
        #                 "status":"TRADING",
        #                 "minimum_amount":0.0001,
        #                 "symbol":"BTC_USDT",
        #                 "is_allow":1,
        #                 "zone":"MAIN",
        #                 "base_asset":"BTC",
        #                 "price_precision":2
        #             }
        #         ],
        #         "code":0
        #     }
        #
        markets = self.safe_value(response, 'symbol_list', [])
        result = []
        for i in range(0, len(markets)):
            market = markets[i]
            id = self.safe_string(market, 'symbol')
            baseId = self.safe_string(market, 'base_asset')
            quoteId = self.safe_string(market, 'quote_asset')
            base = self.safe_currency_code(baseId)
            quote = self.safe_currency_code(quoteId)
            symbol = base + '/' + quote
            precision = {
                'amount': self.safe_integer(market, 'amount_precision'),
                'price': self.safe_integer(market, 'price_precision'),
            }
            limits = {
                'amount': {
                    'min': self.safe_float(market, 'minimum_amount'),
                    'max': None,
                },
                'price': {
                    'min': None,
                    'max': None,
                },
                'cost': {
                    'min': self.safe_float(market, 'minimum_value'),
                    'max': None,
                },
            }
            #
            # The status is documented in the exchange API docs as follows:
            # TRADING, HALT(delisted), BREAK(trading paused)
            # https://docs.digifinex.vip/en-ww/v3/#/public/spot/symbols
            # However, all spot markets actually have status == 'HALT'
            # despite that they appear to be active on the exchange website.
            # Apparently, we can't trust self status.
            # status = self.safe_string(market, 'status')
            # active = (status == 'TRADING')
            #
            isAllowed = self.safe_value(market, 'is_allow', 1)
            active = True if isAllowed else False
            type = 'spot'
            spot = (type == 'spot')
            margin = (type == 'margin')
            result.append({
                'id': id,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'baseId': baseId,
                'quoteId': quoteId,
                'active': active,
                'type': type,
                'spot': spot,
                'margin': margin,
                'precision': precision,
                'limits': limits,
                'info': market,
            })
        return result

    async def fetch_markets_v1(self, params={}):
        response = await self.publicGetMarkets(params)
        #
        #     {
        #         "data": [
        #             {
        #                 "volume_precision":4,
        #                 "price_precision":2,
        #                 "market":"btc_usdt",
        #                 "min_amount":2,
        #                 "min_volume":0.0001
        #             },
        #         ],
        #         "date":1564507456,
        #         "code":0
        #     }
        #
        markets = self.safe_value(response, 'data', [])
        result = []
        for i in range(0, len(markets)):
            market = markets[i]
            id = self.safe_string(market, 'market')
            baseId, quoteId = id.split('_')
            base = self.safe_currency_code(baseId)
            quote = self.safe_currency_code(quoteId)
            symbol = base + '/' + quote
            precision = {
                'amount': self.safe_integer(market, 'volume_precision'),
                'price': self.safe_integer(market, 'price_precision'),
            }
            limits = {
                'amount': {
                    'min': self.safe_float(market, 'min_volume'),
                    'max': None,
                },
                'price': {
                    'min': None,
                    'max': None,
                },
                'cost': {
                    'min': self.safe_float(market, 'min_amount'),
                    'max': None,
                },
            }
            active = None
            result.append({
                'id': id,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'baseId': baseId,
                'quoteId': quoteId,
                'active': active,
                'precision': precision,
                'limits': limits,
                'info': market,
            })
        return result

    async def fetch_balance(self, params={}):
        defaultType = self.safe_string(self.options, 'defaultType', 'spot')
        type = self.safe_string(params, 'type', defaultType)
        params = self.omit(params, 'type')
        method = 'privateGet' + self.capitalize(type) + 'Assets'
        response = await getattr(self, method)(params)
        #
        #     {
        #         "code": 0,
        #         "list": [
        #             {
        #                 "currency": "BTC",
        #                 "free": 4723846.89208129,
        #                 "total": 0
        #             }
        #         ]
        #     }
        balances = self.safe_value(response, 'list', [])
        result = {'info': response}
        for i in range(0, len(balances)):
            balance = balances[i]
            currencyId = self.safe_string(balance, 'currency')
            code = self.safe_currency_code(currencyId)
            account = self.account()
            account['used'] = self.safe_float(balance, 'frozen')
            account['free'] = self.safe_float(balance, 'free')
            account['total'] = self.safe_float(balance, 'total')
            result[code] = account
        return self.parse_balance(result)

    async def fetch_order_book(self, symbol, limit=None, params={}):
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        if limit is not None:
            request['limit'] = limit  # default 10, max 150
        response = await self.publicGetOrderBook(self.extend(request, params))
        #
        #     {
        #         "bids": [
        #             [9605.77,0.0016],
        #             [9605.46,0.0003],
        #             [9602.04,0.0127],
        #         ],
        #         "asks": [
        #             [9627.22,0.025803],
        #             [9627.12,0.168543],
        #             [9626.52,0.0011529],
        #         ],
        #         "date":1564509499,
        #         "code":0
        #     }
        #
        timestamp = self.safe_timestamp(response, 'date')
        return self.parse_order_book(response, timestamp)

    async def fetch_tickers(self, symbols=None, params={}):
        apiKey = self.safe_value(params, 'apiKey', self.apiKey)
        if not apiKey:
            raise ArgumentsRequired(self.id + ' fetchTicker is a private v2 endpoint that requires an `exchange.apiKey` credential or an `apiKey` extra parameter')
        await self.load_markets()
        request = {
            'apiKey': apiKey,
        }
        response = await self.v2GetTicker(self.extend(request, params))
        #
        #     {
        #         "ticker":{
        #             "btc_eth":{
        #                 "last":0.021957,
        #                 "base_vol":2249.3521732227,
        #                 "change":-0.6,
        #                 "vol":102443.5111,
        #                 "sell":0.021978,
        #                 "low":0.021791,
        #                 "buy":0.021946,
        #                 "high":0.022266
        #             }
        #         },
        #         "date":1564518452,
        #         "code":0
        #     }
        #
        result = {}
        tickers = self.safe_value(response, 'ticker', {})
        date = self.safe_integer(response, 'date')
        reversedMarketIds = list(tickers.keys())
        for i in range(0, len(reversedMarketIds)):
            reversedMarketId = reversedMarketIds[i]
            ticker = self.extend({
                'date': date,
            }, tickers[reversedMarketId])
            quoteId, baseId = reversedMarketId.split('_')
            marketId = baseId.upper() + '_' + quoteId.upper()
            market = None
            symbol = None
            if marketId in self.markets_by_id:
                market = self.markets_by_id[marketId]
                symbol = market['symbol']
            else:
                base = self.safe_currency_code(baseId)
                quote = self.safe_currency_code(quoteId)
                symbol = base + '/' + quote
            result[symbol] = self.parse_ticker(ticker, market)
        return self.filter_by_array(result, 'symbol', symbols)

    async def fetch_ticker(self, symbol, params={}):
        apiKey = self.safe_value(params, 'apiKey', self.apiKey)
        if not apiKey:
            raise ArgumentsRequired(self.id + ' fetchTicker is a private v2 endpoint that requires an `exchange.apiKey` credential or an `apiKey` extra parameter')
        await self.load_markets()
        market = self.market(symbol)
        # reversed base/quote in v2
        marketId = market['quoteId'].lower() + '_' + market['baseId'].lower()
        request = {
            'symbol': marketId,
            'apiKey': apiKey,
        }
        response = await self.v2GetTicker(self.extend(request, params))
        #
        #     {
        #         "ticker":{
        #             "btc_eth":{
        #                 "last":0.021957,
        #                 "base_vol":2249.3521732227,
        #                 "change":-0.6,
        #                 "vol":102443.5111,
        #                 "sell":0.021978,
        #                 "low":0.021791,
        #                 "buy":0.021946,
        #                 "high":0.022266
        #             }
        #         },
        #         "date":1564518452,
        #         "code":0
        #     }
        #
        date = self.safe_integer(response, 'date')
        ticker = self.safe_value(response, 'ticker', {})
        result = self.safe_value(ticker, marketId, {})
        result = self.extend({'date': date}, result)
        return self.parse_ticker(result, market)

    def parse_ticker(self, ticker, market=None):
        #
        # fetchTicker, fetchTickers
        #
        #     {
        #         "last":0.021957,
        #         "base_vol":2249.3521732227,
        #         "change":-0.6,
        #         "vol":102443.5111,
        #         "sell":0.021978,
        #         "low":0.021791,
        #         "buy":0.021946,
        #         "high":0.022266,
        #         "date"1564518452,  # injected from fetchTicker/fetchTickers
        #     }
        #
        symbol = None
        if market is not None:
            symbol = market['symbol']
        timestamp = self.safe_timestamp(ticker, 'date')
        last = self.safe_float(ticker, 'last')
        percentage = self.safe_float(ticker, 'change')
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'high': self.safe_float(ticker, 'high'),
            'low': self.safe_float(ticker, 'low'),
            'bid': self.safe_float(ticker, 'buy'),
            'bidVolume': None,
            'ask': self.safe_float(ticker, 'sell'),
            'askVolume': None,
            'vwap': None,
            'open': None,
            'close': last,
            'last': last,
            'previousClose': None,
            'change': None,
            'percentage': percentage,
            'average': None,
            'baseVolume': self.safe_float(ticker, 'vol'),
            'quoteVolume': self.safe_float(ticker, 'base_vol'),
            'info': ticker,
        }

    def parse_trade(self, trade, market=None):
        #
        # fetchTrades(public)
        #
        #     {
        #         "date":1564520003,
        #         "id":1596149203,
        #         "amount":0.7073,
        #         "type":"buy",
        #         "price":0.02193,
        #     }
        #
        # fetchMyTrades(private)
        #
        #     {
        #         "symbol": "BTC_USDT",
        #         "order_id": "6707cbdcda0edfaa7f4ab509e4cbf966",
        #         "id": 28457,
        #         "price": 0.1,
        #         "amount": 0,
        #         "fee": 0.096,
        #         "fee_currency": "USDT",
        #         "timestamp": 1499865549,
        #         "side": "buy",
        #         "is_maker": True
        #     }
        #
        id = self.safe_string(trade, 'id')
        orderId = self.safe_string(trade, 'order_id')
        timestamp = self.safe_timestamp_2(trade, 'date', 'timestamp')
        side = self.safe_string_2(trade, 'type', 'side')
        price = self.safe_float(trade, 'price')
        amount = self.safe_float(trade, 'amount')
        cost = None
        if price is not None:
            if amount is not None:
                cost = price * amount
        symbol = None
        marketId = self.safe_string(trade, 'symbol')
        if marketId is not None:
            if marketId in self.markets_by_id:
                market = self.markets_by_id[marketId]
                symbol = market['symbol']
            else:
                baseId, quoteId = marketId.split('_')
                base = self.safe_currency_code(baseId)
                quote = self.safe_currency_code(quoteId)
                symbol = base + '/' + quote
        if symbol is None:
            if market is not None:
                symbol = market['symbol']
        takerOrMaker = self.safe_value(trade, 'is_maker')
        feeCost = self.safe_float(trade, 'fee')
        fee = None
        if feeCost is not None:
            feeCurrencyId = self.safe_string(trade, 'fee_currency')
            feeCurrencyCode = self.safe_currency_code(feeCurrencyId)
            fee = {
                'cost': feeCost,
                'currency': feeCurrencyCode,
            }
        return {
            'id': id,
            'info': trade,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': symbol,
            'type': None,
            'order': orderId,
            'side': side,
            'price': price,
            'amount': amount,
            'cost': cost,
            'takerOrMaker': takerOrMaker,
            'fee': fee,
        }

    async def fetch_time(self, params={}):
        response = await self.publicGetTime(params)
        #
        #     {
        #         "server_time": 1589873762,
        #         "code": 0
        #     }
        #
        return self.safe_timestamp(response, 'server_time')

    async def fetch_status(self, params={}):
        await self.publicGetPing(params)
        #
        #     {
        #         "msg": "pong",
        #         "code": 0
        #     }
        #
        self.status = self.extend(self.status, {
            'status': 'ok',
            'updated': self.milliseconds(),
        })
        return self.status

    async def fetch_trades(self, symbol, since=None, limit=None, params={}):
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        if limit is not None:
            request['limit'] = limit  # default 100, max 500
        response = await self.publicGetTrades(self.extend(request, params))
        #
        #     {
        #         "data":[
        #             {
        #                 "date":1564520003,
        #                 "id":1596149203,
        #                 "amount":0.7073,
        #                 "type":"buy",
        #                 "price":0.02193,
        #             },
        #             {
        #                 "date":1564520002,
        #                 "id":1596149165,
        #                 "amount":0.3232,
        #                 "type":"sell",
        #                 "price":0.021927,
        #             },
        #         ],
        #         "code": 0,
        #         "date": 1564520003,
        #     }
        #
        data = self.safe_value(response, 'data', [])
        return self.parse_trades(data, market, since, limit)

    def parse_ohlcv(self, ohlcv, market=None):
        #
        #     [
        #         1556712900,
        #         2205.899,
        #         0.029967,
        #         0.02997,
        #         0.029871,
        #         0.029927
        #     ]
        #
        return [
            self.safe_timestamp(ohlcv, 0),
            self.safe_float(ohlcv, 5),  # open
            self.safe_float(ohlcv, 3),  # high
            self.safe_float(ohlcv, 4),  # low
            self.safe_float(ohlcv, 2),  # close
            self.safe_float(ohlcv, 1),  # volume
        ]

    async def fetch_ohlcv(self, symbol, timeframe='1m', since=None, limit=None, params={}):
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
            'period': self.timeframes[timeframe],
            # 'start_time': 1564520003,  # starting timestamp, 200 candles before end_time by default
            # 'end_time': 1564520003,  # ending timestamp, current timestamp by default
        }
        if since is not None:
            startTime = int(since / 1000)
            request['start_time'] = startTime
            if limit is not None:
                duration = self.parse_timeframe(timeframe)
                request['end_time'] = self.sum(startTime, limit * duration)
        elif limit is not None:
            endTime = self.seconds()
            duration = self.parse_timeframe(timeframe)
            request['startTime'] = self.sum(endTime, -limit * duration)
        response = await self.publicGetKline(self.extend(request, params))
        #
        #     {
        #         "code":0,
        #         "data":[
        #             [1556712900,2205.899,0.029967,0.02997,0.029871,0.029927],
        #             [1556713800,1912.9174,0.029992,0.030014,0.029955,0.02996],
        #             [1556714700,1556.4795,0.029974,0.030019,0.029969,0.02999],
        #         ]
        #     }
        #
        data = self.safe_value(response, 'data', [])
        return self.parse_ohlcvs(data, market, timeframe, since, limit)

    async def create_order(self, symbol, type, side, amount, price=None, params={}):
        await self.load_markets()
        market = self.market(symbol)
        defaultType = self.safe_string(self.options, 'defaultType', 'spot')
        orderType = self.safe_string(params, 'type', defaultType)
        params = self.omit(params, 'type')
        request = {
            'market': orderType,
            'symbol': market['id'],
            'amount': self.amount_to_precision(symbol, amount),
            # 'post_only': 0,  # 0 by default, if set to 1 the order will be canceled if it can be executed immediately, making sure there will be no market taking
        }
        suffix = ''
        if type == 'market':
            suffix = '_market'
        else:
            request['price'] = self.price_to_precision(symbol, price)
        request['type'] = side + suffix
        response = await self.privatePostMarketOrderNew(self.extend(request, params))
        #
        #     {
        #         "code": 0,
        #         "order_id": "198361cecdc65f9c8c9bb2fa68faec40"
        #     }
        #
        result = self.parse_order(response, market)
        return self.extend(result, {
            'symbol': symbol,
            'side': side,
            'type': type,
            'amount': amount,
            'price': price,
        })

    async def cancel_order(self, id, symbol=None, params={}):
        await self.load_markets()
        defaultType = self.safe_string(self.options, 'defaultType', 'spot')
        orderType = self.safe_string(params, 'type', defaultType)
        params = self.omit(params, 'type')
        request = {
            'market': orderType,
            'order_id': id,
        }
        response = await self.privatePostMarketOrderCancel(self.extend(request, params))
        #
        #     {
        #         "code": 0,
        #         "success": [
        #             "198361cecdc65f9c8c9bb2fa68faec40",
        #             "3fb0d98e51c18954f10d439a9cf57de0"
        #         ],
        #         "error": [
        #             "78a7104e3c65cc0c5a212a53e76d0205"
        #         ]
        #     }
        #
        canceledOrders = self.safe_value(response, 'success', [])
        numCanceledOrders = len(canceledOrders)
        if numCanceledOrders != 1:
            raise OrderNotFound(self.id + ' cancelOrder ' + id + ' not found')
        return response

    async def cancel_orders(self, ids, symbol=None, params={}):
        await self.load_markets()
        defaultType = self.safe_string(self.options, 'defaultType', 'spot')
        orderType = self.safe_string(params, 'type', defaultType)
        params = self.omit(params, 'type')
        request = {
            'market': orderType,
            'order_id': ','.join(ids),
        }
        response = await self.privatePostCancelOrder(self.extend(request, params))
        #
        #     {
        #         "code": 0,
        #         "success": [
        #             "198361cecdc65f9c8c9bb2fa68faec40",
        #             "3fb0d98e51c18954f10d439a9cf57de0"
        #         ],
        #         "error": [
        #             "78a7104e3c65cc0c5a212a53e76d0205"
        #         ]
        #     }
        #
        canceledOrders = self.safe_value(response, 'success', [])
        numCanceledOrders = len(canceledOrders)
        if numCanceledOrders < 1:
            raise OrderNotFound(self.id + ' cancelOrders error')
        return response

    def parse_order_status(self, status):
        statuses = {
            '0': 'open',
            '1': 'open',  # partially filled
            '2': 'closed',
            '3': 'canceled',
            '4': 'canceled',  # partially filled and canceled
        }
        return self.safe_string(statuses, status, status)

    def parse_order(self, order, market=None):
        #
        # createOrder
        #
        #     {
        #         "code": 0,
        #         "order_id": "198361cecdc65f9c8c9bb2fa68faec40"
        #     }
        #
        # fetchOrder, fetchOpenOrders, fetchOrders
        #
        #     {
        #         "symbol": "BTC_USDT",
        #         "order_id": "dd3164b333a4afa9d5730bb87f6db8b3",
        #         "created_date": 1562303547,
        #         "finished_date": 0,
        #         "price": 0.1,
        #         "amount": 1,
        #         "cash_amount": 1,
        #         "executed_amount": 0,
        #         "avg_price": 0,
        #         "status": 1,
        #         "type": "buy",
        #         "kind": "margin"
        #     }
        #
        id = self.safe_string(order, 'order_id')
        timestamp = self.safe_timestamp(order, 'created_date')
        lastTradeTimestamp = self.safe_timestamp(order, 'finished_date')
        side = self.safe_string(order, 'type')
        type = None
        if side is not None:
            parts = side.split('_')
            numParts = len(parts)
            if numParts > 1:
                side = parts[0]
                type = parts[1]
            else:
                type = 'limit'
        status = self.parse_order_status(self.safe_string(order, 'status'))
        if market is None:
            exchange = order['symbol'].upper()
            if exchange in self.markets_by_id:
                market = self.markets_by_id[exchange]
        symbol = None
        marketId = self.safe_string(order, 'symbol')
        if marketId is not None:
            if marketId in self.markets_by_id:
                market = self.markets_by_id[marketId]
                symbol = market['symbol']
            else:
                baseId, quoteId = marketId.split('_')
                base = self.safe_currency_code(baseId)
                quote = self.safe_currency_code(quoteId)
                symbol = base + '/' + quote
        amount = self.safe_float(order, 'amount')
        filled = self.safe_float(order, 'executed_amount')
        price = self.safe_float(order, 'price')
        average = self.safe_float(order, 'avg_price')
        remaining = None
        cost = None
        if filled is not None:
            if average is not None:
                cost = filled * average
            if amount is not None:
                remaining = max(0, amount - filled)
        return {
            'info': order,
            'id': id,
            'clientOrderId': None,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': lastTradeTimestamp,
            'symbol': symbol,
            'type': type,
            'side': side,
            'price': price,
            'amount': amount,
            'filled': filled,
            'remaining': remaining,
            'cost': cost,
            'average': average,
            'status': status,
            'fee': None,
            'trades': None,
        }

    async def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
        defaultType = self.safe_string(self.options, 'defaultType', 'spot')
        orderType = self.safe_string(params, 'type', defaultType)
        params = self.omit(params, 'type')
        await self.load_markets()
        market = None
        request = {
            'market': orderType,
        }
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        response = await self.privateGetMarketOrderCurrent(self.extend(request, params))
        #
        #     {
        #         "code": 0,
        #         "data": [
        #             {
        #                 "symbol": "BTC_USDT",
        #                 "order_id": "dd3164b333a4afa9d5730bb87f6db8b3",
        #                 "created_date": 1562303547,
        #                 "finished_date": 0,
        #                 "price": 0.1,
        #                 "amount": 1,
        #                 "cash_amount": 1,
        #                 "executed_amount": 0,
        #                 "avg_price": 0,
        #                 "status": 1,
        #                 "type": "buy",
        #                 "kind": "margin"
        #             }
        #         ]
        #     }
        #
        data = self.safe_value(response, 'data', [])
        return self.parse_orders(data, market, since, limit)

    async def fetch_orders(self, symbol=None, since=None, limit=None, params={}):
        defaultType = self.safe_string(self.options, 'defaultType', 'spot')
        orderType = self.safe_string(params, 'type', defaultType)
        params = self.omit(params, 'type')
        await self.load_markets()
        market = None
        request = {
            'market': orderType,
        }
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        if since is not None:
            request['start_time'] = int(since / 1000)  # default 3 days from now, max 30 days
        if limit is not None:
            request['limit'] = limit  # default 10, max 100
        response = await self.privateGetMarketOrderHistory(self.extend(request, params))
        #
        #     {
        #         "code": 0,
        #         "data": [
        #             {
        #                 "symbol": "BTC_USDT",
        #                 "order_id": "dd3164b333a4afa9d5730bb87f6db8b3",
        #                 "created_date": 1562303547,
        #                 "finished_date": 0,
        #                 "price": 0.1,
        #                 "amount": 1,
        #                 "cash_amount": 1,
        #                 "executed_amount": 0,
        #                 "avg_price": 0,
        #                 "status": 1,
        #                 "type": "buy",
        #                 "kind": "margin"
        #             }
        #         ]
        #     }
        #
        data = self.safe_value(response, 'data', [])
        return self.parse_orders(data, market, since, limit)

    async def fetch_order(self, id, symbol=None, params={}):
        defaultType = self.safe_string(self.options, 'defaultType', 'spot')
        orderType = self.safe_string(params, 'type', defaultType)
        params = self.omit(params, 'type')
        await self.load_markets()
        market = None
        if symbol is not None:
            market = self.market(symbol)
        request = {
            'market': orderType,
            'order_id': id,
        }
        response = await self.privateGetMarketOrder(self.extend(request, params))
        #
        #     {
        #         "code": 0,
        #         "data": [
        #             {
        #                 "symbol": "BTC_USDT",
        #                 "order_id": "dd3164b333a4afa9d5730bb87f6db8b3",
        #                 "created_date": 1562303547,
        #                 "finished_date": 0,
        #                 "price": 0.1,
        #                 "amount": 1,
        #                 "cash_amount": 1,
        #                 "executed_amount": 0,
        #                 "avg_price": 0,
        #                 "status": 1,
        #                 "type": "buy",
        #                 "kind": "margin"
        #             }
        #         ]
        #     }
        #
        data = self.safe_value(response, 'data', {})
        return self.parse_order(data, market)

    async def fetch_my_trades(self, symbol=None, since=None, limit=None, params={}):
        defaultType = self.safe_string(self.options, 'defaultType', 'spot')
        orderType = self.safe_string(params, 'type', defaultType)
        params = self.omit(params, 'type')
        await self.load_markets()
        market = None
        request = {
            'market': orderType,
        }
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        if since is not None:
            request['start_time'] = int(since / 1000)  # default 3 days from now, max 30 days
        if limit is not None:
            request['limit'] = limit  # default 10, max 100
        response = await self.privateGetMarketMytrades(self.extend(request, params))
        #
        #     {
        #         "code": 0,
        #         "list": [
        #             {
        #                 "symbol": "BTC_USDT",
        #                 "order_id": "6707cbdcda0edfaa7f4ab509e4cbf966",
        #                 "id": 28457,
        #                 "price": 0.1,
        #                 "amount": 0,
        #                 "fee": 0.096,
        #                 "fee_currency": "USDT",
        #                 "timestamp": 1499865549,
        #                 "side": "buy",
        #                 "is_maker": True
        #             }
        #         ]
        #     }
        #
        data = self.safe_value(response, 'list', [])
        return self.parse_trades(data, market, since, limit)

    def parse_ledger_entry_type(self, type):
        types = {}
        return self.safe_string(types, type, type)

    def parse_ledger_entry(self, item, currency=None):
        #
        #     {
        #         "currency_mark": "BTC",
        #         "type": 100234,
        #         "num": 28457,
        #         "balance": 0.1,
        #         "time": 1546272000
        #     }
        #
        id = self.safe_string(item, 'num')
        account = None
        type = self.parse_ledger_entry_type(self.safe_string(item, 'type'))
        code = self.safe_currency_code(self.safe_string(item, 'currency_mark'), currency)
        timestamp = self.safe_timestamp(item, 'time')
        before = None
        after = self.safe_float(item, 'balance')
        status = 'ok'
        return {
            'info': item,
            'id': id,
            'direction': None,
            'account': account,
            'referenceId': None,
            'referenceAccount': None,
            'type': type,
            'currency': code,
            'amount': None,
            'before': before,
            'after': after,
            'status': status,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'fee': None,
        }

    async def fetch_ledger(self, code=None, since=None, limit=None, params={}):
        defaultType = self.safe_string(self.options, 'defaultType', 'spot')
        orderType = self.safe_string(params, 'type', defaultType)
        params = self.omit(params, 'type')
        await self.load_markets()
        request = {
            'market': orderType,
        }
        currency = None
        if code is not None:
            currency = self.currency(code)
            request['currency_mark'] = currency['id']
        if since is not None:
            request['start_time'] = int(since / 1000)
        if limit is not None:
            request['limit'] = limit  # default 100, max 1000
        response = await self.privateGetMarketFinancelog(self.extend(request, params))
        #
        #     {
        #         "code": 0,
        #         "data": {
        #             "total": 521,
        #             "finance": [
        #                 {
        #                     "currency_mark": "BTC",
        #                     "type": 100234,
        #                     "num": 28457,
        #                     "balance": 0.1,
        #                     "time": 1546272000
        #                 }
        #             ]
        #         }
        #     }
        #
        data = self.safe_value(response, 'data', {})
        items = self.safe_value(data, 'finance', [])
        return self.parse_ledger(items, currency, since, limit)

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        version = api if (api == 'v2') else self.version
        url = self.urls['api'] + '/' + version + '/' + self.implode_params(path, params)
        query = self.omit(params, self.extract_params(path))
        urlencoded = self.urlencode(self.keysort(query))
        if api == 'private':
            nonce = str(self.nonce())
            auth = urlencoded
            # the signature is not time-limited :\
            signature = self.hmac(self.encode(auth), self.encode(self.secret))
            if method == 'GET':
                if urlencoded:
                    url += '?' + urlencoded
            elif method == 'POST':
                headers = {
                    'Content-Type': 'application/x-www-form-urlencoded',
                }
                if urlencoded:
                    body = urlencoded
            headers = {
                'ACCESS-KEY': self.apiKey,
                'ACCESS-SIGN': signature,
                'ACCESS-TIMESTAMP': nonce,
            }
        else:
            if urlencoded:
                url += '?' + urlencoded
        return {'url': url, 'method': method, 'body': body, 'headers': headers}

    def date_utc8(self, timestampMS):
        timedelta = self.safe_value(self.options, 'timedelta', 8 * 60 * 60 * 1000)  # eight hours
        return self.ymd(timestampMS + timedelta)

    def handle_errors(self, statusCode, statusText, url, method, responseHeaders, responseBody, response, requestHeaders, requestBody):
        if not response:
            return  # fall back to default error handler
        code = self.safe_string(response, 'code')
        if code == '0':
            return  # no error
        feedback = self.id + ' ' + responseBody
        if code is None:
            raise BadResponse(feedback)
        unknownError = [ExchangeError, feedback]
        ExceptionClass, message = self.safe_value(self.exceptions['exact'], code, unknownError)
        raise ExceptionClass(message)
