# -*- 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.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': {
                'cancelOrders': True,
                'fetchOrders': True,
                'fetchOHLCV': True,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'fetchTickers': True,
                'fetchMyTrades': True,
                'fetchLedger': 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/1294454/62184319-304e8880-b366-11e9-99fe-8011d6929195.jpg',
                'api': 'https://openapi.digifinex.vip',
                'www': 'https://www.digifinex.vip',
                'doc': [
                    'https://docs.digifinex.vip',
                ],
                'fees': 'https://digifinex.zendesk.com/hc/en-us/articles/360000328482-Fee-Structure-on-DigiFinex',
                'referral': 'https://www.digifinex.vip/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',
                    ],
                },
                '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',
                    ],
                },
            },
            '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'],
            },
        })

    def fetch_markets_by_type(self, type, params={}):
        method = 'publicGet' + self.capitalize(type) + 'Symbols'
        response = getattr(self, method)(params)
        #
        #     {
        #         "symbol_list": [
        #             {
        #                 "order_types":["LIMIT","MARKET"],
        #                 "quote_asset":"USDT",
        #                 "minimum_value":2,
        #                 "amount_precision":4,
        #                 "status":"TRADING",
        #                 "minimum_amount":0.001,
        #                 "symbol":"LTC_USDT",
        #                 "margin_rate":0.3,
        #                 "zone":"MAIN",
        #                 "base_asset":"LTC",
        #                 "price_precision":2
        #             },
        #         ],
        #         "code":0
        #     }
        #
        markets = self.safe_value(response, 'symbols_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')
            #
            active = None
            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

    def fetch_markets(self, params={}):
        response = 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

    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 = 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)

    def fetch_order_book(self, symbol, limit=None, params={}):
        self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        if limit is not None:
            request['limit'] = limit  # default 10, max 150
        response = 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)

    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')
        self.load_markets()
        request = {
            'apiKey': apiKey,
        }
        response = 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 + '_' + quoteId
            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 result

    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')
        self.load_markets()
        market = self.market(symbol)
        # reversed base/quote in v2
        marketId = market['quoteId'] + '_' + market['baseId']
        request = {
            'symbol': marketId,
            'apiKey': apiKey,
        }
        response = 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, 'base_vol'),
            'quoteVolume': self.safe_float(ticker, '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(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[market]
                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,
        }

    def fetch_trades(self, symbol, since=None, limit=None, params={}):
        self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        if limit is not None:
            request['limit'] = limit  # default 100, max 500
        response = 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, timeframe='1m', since=None, limit=None):
        return [
            ohlcv[0] * 1000,  # timestamp
            ohlcv[5],  # open
            ohlcv[3],  # high
            ohlcv[4],  # low
            ohlcv[2],  # close
            ohlcv[1],  # volume
        ]

    def fetch_ohlcv(self, symbol, timeframe='1m', since=None, limit=None, params={}):
        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 = 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)

    def create_order(self, symbol, type, side, amount, price=None, params={}):
        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 = 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,
        })

    def cancel_order(self, id, symbol=None, params={}):
        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 = 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

    def cancel_orders(self, ids, symbol=None, params={}):
        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 = 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,
            '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,
        }

    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')
        self.load_markets()
        market = None
        request = {
            'market': orderType,
        }
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        response = 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)

    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')
        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 = 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)

    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')
        self.load_markets()
        request = {
            'market': orderType,
            'order_id': id,
        }
        response = 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"
        #             }
        #         ]
        #     }
        #
        return self.parse_order(response)

    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')
        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 = 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,
        }

    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')
        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 = 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)
