# -*- 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 BadRequest
from ccxt.base.errors import BadSymbol
from ccxt.base.errors import InvalidOrder
from ccxt.base.decimal_to_precision import SIGNIFICANT_DIGITS

import json
import sys
from datetime import datetime


class tprexchange(Exchange):

    def describe(self):
        return self.deep_extend(super(tprexchange, self).describe(), {
            'id': 'tprexchange',
            'name': 'TPR Exchange',
            # 'countries': ['US'],
            # 'rateLimit': 500,
            'version': 'v1',
            'certified': False,
            'has': {
                'loadMarkets': True,
                'cancelAllOrders': False,
                'cancelOrder': True,
                'cancelOrders': False,
                'CORS': False,
                'createDepositAddress': False,
                'createLimitOrder': False,
                'createMarketOrder': False,
                'createOrder': True,
                'deposit': False,
                'editOrder': 'emulated',
                'fetchBalance': True,
                'fetchBidsAsks': False,
                'fetchClosedOrders': True,
                'fetchCurrencies': True,
                'fetchDepositAddress': False,
                'fetchDeposits': False,
                'fetchFundingFees': False,
                'fetchL2OrderBook': False,
                'fetchLedger': False,
                'fetchMarkets': True,
                'fetchMyTrades': True,
                'fetchOHLCV': True,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'fetchOrderBook': True,
                'fetchOrderBooks': False,
                'fetchOrders': True,
                'fetchOrderTrades': False,
                'fetchStatus': True,
                'fetchTicker': True,
                'fetchTickers': True,
                'fetchTime': False,
                'fetchTrades': True,
                'fetchTradingFee': False,
                'fetchTradingFees': False,
                'fetchTradingLimits': False,
                'fetchTransactions': False,
                'fetchWithdrawals': False,
                'privateAPI': True,
                'publicAPI': False,
                'signIn': True,
                'withdraw': False,
                'getMarketPrice': True,
            },
            'timeframes': {
                '1m': '1',
                '1h': '60',
                '1d': '1440',
                '1w': '10080',
                '1mn': '43200',
            },
            'urls': {
                'logo': '',
                'api': '{hostname}',
                'www': '',
                'doc': '',
                'fees': '',
                'referral': '',
            },
            'api': {
                'private': {
                    'get': [
                    ],
                    'post': [
                        'ucenter/api-login',
                        'ucenter/member/balance',
                        'market/symbol-thumb',
                        'market/coins-info',
                        'market/symbol-info',
                        'exchange/order/add',
                        'exchange/order/find',
                        'exchange/order/all',
                        'exchange/order/apicancel',
                        'exchange/order/trades',
                        'exchange/order/my-trades',
                        'exchange/exchange-coin/base-symbol',
                    ],
                    'delete': [
                    ],
                },
                'feed': {
                    'get': [
                    ],
                },
            },
            'fees': {
                'trading': {
                },
            },
            'requiredCredentials': {
                'apiKey': True,
                'secret': True,
                'uid': False,
            },
            'precisionMode': SIGNIFICANT_DIGITS,
            'options': {
                'createMarketBuyOrderRequiresPrice': False,
            },
            'exceptions': {
                'exact': {
                    'Invalid cost': InvalidOrder,  # {"message":"Invalid cost","_links":{"self":{"href":"/orders","templated":false}}}
                    'Invalid order ID': InvalidOrder,  # {"message":"Invalid order ID","_links":{"self":{"href":"/orders/4a151805-d594-4a96-9d64-e3984f2441f7","templated":false}}}
                    'Invalid market !': BadSymbol,  # {"message":"Invalid market !","_links":{"self":{"href":"/markets/300/order-book","templated":false}}}
                },
                'broad': {
                    'Failed to convert argument': BadRequest,
                },
            },
        })

    def parse_ticker(self, response):
        if len(response) == 0:
            return []
        symbol = response[0].get('symbol')
        high = 0
        bidVolume = 0
        askVolume = 0
        vwap = 0
        vwapCost = 0
        vwapVolume = 0
        open_ = 'None'
        close = 0
        last = close
        previousClose = 'None'
        change = 'None' 
        percentage = 'None' 
        average = 'None'
        baseVolume = 0
        quoteVolume = 0
        time = 0
        lastDayTime = int((datetime.now().timestamp() - 86400) * 1000)
        currentTimestamp = int(datetime.now().timestamp() * 1000)
        currentDatetime = str(datetime.fromtimestamp(currentTimestamp * 0.001))
        low = response[0].get('price')
        bid = 0
        ask = sys.maxsize
        openSellOrdersCount = 0

        for order in response:

            price = order.get('price')
            amount = order.get('amount')
            timestamp = order.get('timestamp')

            if high < price:
                high = price
            if low > price:
                low = price
            
            if order.get('status') == 'open':
                if order.get('side') == 'buy':
                    if bid < price:
                        bid = price
                    if bidVolume < amount:
                        bidVolume = amount

            if order.get('status') == 'open':
                if order.get('side') == 'sell':
                    openSellOrdersCount += 1
                    if ask > price:
                        ask = price
                    if askVolume < amount:
                        askVolume = amount

            if order.get('info').get('status') == 'COMPLETED':
                vwapCost += price * amount
                vwapVolume += amount
                if time < timestamp:
                    time = timestamp
                    close = price
                if timestamp > lastDayTime:
                    quoteVolume += amount
                    baseVolume += price

        if vwapVolume != 0:
            vwap = vwapCost / vwapVolume
        if openSellOrdersCount == 0:
            ask = 0
        last = close

        result = {
            'symbol':        symbol,
            'info':          response,
            'timestamp':     currentTimestamp,
            'datetime':      currentDatetime,
            'high':          high,
            'low':           low,
            'bid':           bid,
            'bidVolume':     bidVolume,
            'ask':           ask,
            'askVolume':     askVolume,
            'vwap':          vwap,
            'open':          open_,
            'close':         close,
            'last':          last,
            'previousClose': previousClose,
            'change':        change,
            'percentage':    percentage,
            'average':       average,
            'baseVolume':    baseVolume,
            'quoteVolume':   quoteVolume,
            }
        return result

    def fetch_ticker(self, symbol, since=None, limit=None):
        response = self.fetch_orders(symbol, since, limit)
        # Response example:
        # {
        #     'symbol': 'BTC/USDT', 
        #     'info': [...], 
        #     'timestamp': 1615386851976, 
        #     'datetime': '2021-03-10 16:34:11.976000', 
        #     'high': 50.0, 
        #     'low': 1.0, 
        #     'bid': 30.0, 
        #     'bidVolume': 15.0, 
        #     'ask': 40.0, 
        #     'askVolume': 25.0, 
        #     'vwap': 11.0, 
        #     'open': 'None', 
        #     'close': 20.0, 
        #     'last': 20.0, 
        #     'previousClose': 'None', 
        #     'change': 'None', 
        #     'percentage': 'None', 
        #     'average': 'None', 
        #     'baseVolume': 60.0, 
        #     'quoteVolume': 30.0
        # }
        return self.parse_ticker(response)

    def fetch_tickers(self, since=None, limit=None):
        # Response example:
        # [
        #     {
        #         'symbol': 'BTC/USDT', 
        #         'info': [...], 
        #         'timestamp': 1615386851976, 
        #         'datetime': '2021-03-10 16:34:11.976000', 
        #         'high': 50.0, 
        #         'low': 1.0, 
        #         'bid': 30.0, 
        #         'bidVolume': 15.0, 
        #         'ask': 40.0, 
        #         'askVolume': 25.0, 
        #         'vwap': 11.0, 
        #         'open': 'None', 
        #         'close': 20.0, 
        #         'last': 20.0, 
        #         'previousClose': 'None', 
        #         'change': 'None', 
        #         'percentage': 'None', 
        #         'average': 'None', 
        #         'baseVolume': 60.0, 
        #         'quoteVolume': 30.0
        #     }, 
        #     ...
        # ]
        result = []
        symbols = self.fetch_markets()
        for symblol in symbols:
            response = self.fetch_orders(symblol.get('symbol'), since, limit)
            ticker = self.parse_ticker(response)
            if len(ticker) != 0:
                result.append(ticker)
        return result

    def fetch_order_book(self, symbol, limit, since=0):
        # Response example:
        # {
        #     'bids': 
        #     [
        #        [20.0, 10.0, 'E161538482263642'],   // [price, amount, orderId]
        #         [30.0, 15.0, 'E161538482271646']
        #     ], 
        #     'asks': 
        #     [
        #         [40.0, 20.0, 'E161538482278825'], 
        #         [50.0, 25.0, 'E161538482286085']
        #     ], 
        #     'timestamp': 1615390711695, 
        #     'datetime': '2021-03-10 17:38:31.695000', 
        #     'nonce': 1615390711695
        # }
        orders = self.fetch_open_orders(symbol, since, limit)
        bids = []
        asks = []
        for order in orders:
            temp = []
            temp.append(order.get('price'))
            temp.append(order.get('amount'))
            temp.append(order.get('id'))
            if order.get('side') == 'buy':
                bids.append(temp)
            else:
                asks.append(temp)

        currentTimestamp = int(datetime.now().timestamp() * 1000)
        currentDatetime = str(datetime.fromtimestamp(currentTimestamp * 0.001))

        result = {
            'bids': bids,
            'asks': asks,
            'timestamp': currentTimestamp,
            'datetime': currentDatetime,
            'nonce': currentTimestamp,
        }
        return result

    def parse_markets(self, response):
        listData = []
        for value in response:
            tmp = {
            "id": value.get("coinSymbol"),
            "symbol": value.get("symbol"),
            "base": value.get("coinSymbol"),
            "quote": value.get("baseSymbol"),
            "baseId": value.get("coinSymbol"),
            "quoteId": value.get("baseSymbol"),
            "type": value.get("publishType"),
            "active": value.get("enable"),
            "precision": {
                "amount": value.get("coinScale"),
                "price": value.get("baseCoinScale"),
                },
            "limits": {
                "amount": {"min": value.get("minVolume"), "max": value.get("maxVolume")},
                "price": {"min": value.get("minSellPrice"), "max": value.get("maxBuyPrice")},
                "cost": {"min": value.get("minVolume") * value.get("minSellPrice"), "max": value.get("maxVolume") * value.get("maxBuyPrice")},
                },
            "taker": value.get("fee"),
            "maker": value.get("fee"),
            "info": value,
            }
            listData.append(tmp)
        return listData

    def add_frame(self, timeFrameStart, timeFrameEnd, timeframe, highestPrice, lowestPrice, amount, result, openPrice, closePrice):
        frame = []
        frame.append(timeFrameStart)
        frame.append(openPrice)
        frame.append(highestPrice)
        frame.append(lowestPrice)
        frame.append(closePrice)
        frame.append(amount)
        result.append(frame)

    def parse_ohlcv(self, response, since, timeframe):
        highestPrice = 0
        lowestPrice = sys.maxsize
        price = 0
        amount = 0
        timeFrameStart = since
        timeFrameEnd = int((since * 0.001 + timeframe) * 1000)
        result = []
        i = 0
        orders = response.get('content')
        isOpenPrice = True
        openPrice = 0
        closePrice = 0

        while i < len(orders):
            if isOpenPrice == True:
                openPrice = orders[i].get('price')
                isOpenPrice = False
            time = orders[i].get('time')
            if time >= timeFrameStart and time <= timeFrameEnd:
                price = orders[i].get('price')
                closePrice = price
                if highestPrice < price:
                    highestPrice = price
                if lowestPrice > price:
                    lowestPrice = price
                amount += orders[i].get('amount')
                i += 1
                if i == len(orders):
                    self.add_frame(timeFrameStart, timeFrameEnd, timeframe, highestPrice, lowestPrice, amount, result, openPrice, closePrice)
            else:
                if lowestPrice == sys.maxsize:
                    lowestPrice = 0
                    openPrice = 0
                    closePrice = 0
                    i -= 1
                self.add_frame(timeFrameStart, timeFrameEnd, timeframe, highestPrice, lowestPrice, amount, result, openPrice, closePrice)
                timeFrameStart = timeFrameEnd + 1
                timeFrameEnd = int((timeFrameEnd * 0.001 + timeframe) * 1000)
                amount = 0
                highestPrice = 0
                lowestPrice = sys.maxsize
                isOpenPrice = True
                i += 1

        return result

    # timeframe variants: 
    # 1m (one minute);
    # 1h (one hour);
    # 1d (one day - 24 hours)
    # 1w (one week - 7 days)
    # 1mn (one mounth - 30 days)
    def fetch_ohlcv(self, symbol, timeframe=None, since=0, limit=None, params={}):
        # Response example:
        # [
        #     [
        #         1504541580000, // UTC timestamp in milliseconds, integer
        #         4235.4,        // (O)pen price, float
        #         4240.6,        // (H)ighest price, float
        #         4230.0,        // (L)owest price, float
        #         4230.7,        // (C)losing price, float
        #         37.72941911    // (V)olume (in terms of the base currency), float
        #     ],
        #     ...
        # ]
        inputDataCheck = False

        for frame in self.timeframes:
            if frame == timeframe:
                inputDataCheck = True
                break
        
        if inputDataCheck == False:
            return {'error': 'Incorrect timeframe'}

        tFrame = int(self.timeframes.get(timeframe)) * 60

        default_order_amount_limit = 100
        params['status'] = 'COMPLETED'
        if 'page' in params:
            params['pageNo'] = self.safe_string(params, 'page')
        else:
            params['pageNo'] = 0

        if since is None:
            since = 0
        if limit is None:
            limit = default_order_amount_limit
        
        request = {
            'symbol': symbol,
            'since': since,
            'pageSize': limit,
        }
        fullRequest = self.extend(request, params)
        response = self.privatePostExchangeOrderAll(fullRequest)
        return self.parse_ohlcv(response, since, tFrame)
        
    def fetch_markets(self, symbol=''):
        request = {
            'symbol': symbol,
        }
        response = self.privatePostMarketSymbolInfo(request)
        return self.parse_markets(response)
        # RETURN EXAMPLE:
        # [
        #     {
        #         'id': 'BTC', 
        #         'symbol': 'BTC/USDT', 
        #         'base': 'BTC', 
        #         'quote': 'USDT', 
        #         'baseId': 'BTC', 
        #         'quoteId': 'USDT', 
        #         'type': 'NONE', 
        #         'active': 1, 
        #         'precision': { 'amount': 2, 'price': 2 }, 
        #         'limits': 
        #         {
        #             'amount': { 'min': 0.0, 'max': 0.0 }, 
        #             'price': { 'min': 0.0, 'max': 0.0 }, 
        #             'cost': { 'min': 0.0, 'max': 0.0 }
        #         }, 
        #         'taker': 0.001, 
        #         'maker': 0.001, 
        #         'info': {backend response}
        #     },
        #     ...
        # ]

    def load_markets(self, reload=False, symbol=''):
        if not reload:
            if self.markets:
                if not self.markets_by_id:
                    return self.set_markets(self.markets)
                return self.markets
        currencies = None
        if self.has['fetchCurrencies']:
            currencies = self.fetch_currencies()
        markets = self.fetch_markets(symbol)
        return self.set_markets(markets, currencies)

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        # Check existance of authentication token
        # Just use empy one in case of an application is not signed in yet
        authToken = ''
        if 'token' in self.options:
            authToken = self.options['token']
        # Get URL
        url = self.implode_params(self.urls['api'], {'hostname': self.hostname}) + '/' + path
        # Calculate body and content type depending on method type: GET or POST
        keys = list(params.keys())
        keysLength = len(keys)
        # In case of body is still not assigned just make it empty string
        if body is None:
            body = ''
        # Prepare line for hashing
        # This hash sum is checked on backend side to verify API user
        # POST params should not be added as body
        query = method + ' /' + path + ' ' + self.urlencode(params) + ' ' + authToken + '\n' + body
        signed = self.hmac(self.encode(query), self.encode(self.secret))
        contentType = None
        if method == 'POST':
            contentType = 'application/x-www-form-urlencoded'
            if keysLength > 0:
                body = self.urlencode(params)
        else:
            if keysLength > 0:
                url += '?' + self.urlencode(params)
        headers = {
            'x-auth-sign': signed,
            'x-auth-token': authToken,
        }
        if authToken != '':
            headers['access-auth-token'] = authToken
        if contentType is not None:
            headers['Content-Type'] = contentType
        return {'url': url, 'method': method, 'body': body, 'headers': headers}

    def sign_in(self, params={}):
        params = {
            'key': self.key,
            'token': self.token,
        }
        response = self.privatePostUcenterApiLogin(params)
        loginData = response['data']
        self.options['token'] = self.safe_string(loginData, 'token')
        memberId = self.safe_string(loginData, 'id')
        return memberId

    def fetch_status(self):
        # Responce examples:
        # {'status': 'ok'}
        # or
        # {'status': 'shutdown', 'reason': 'ExchangeNotAvailable'}
        # or
        # {'status': 'shutdown', 'reason': 'Unknown reason'}
        result = False
        try:
            response = self.privatePostExchangeExchangeCoinBaseSymbol()
            for field in response.items():
                if field[0] == 'message':
                    if field[1] == 'SUCCESS':
                        result = True
            if result is True:
                return {"status": "ok"}
            else:
                return {"status": "shutdown", "reason": "ExchangeNotAvailable"}
        except:
            reason = str(sys.exc_info()[0])
            if reason.find('ExchangeNotAvailable') != -1:
                return {"status": "shutdown", "reason": "ExchangeNotAvailable"}
            else:
                return {"status": "shutdown", "reason": "Unknown reason"}

    def parse_currencies(self, response):
        listData = []
        for value in response:
            tmp = {
            'id': value.get('name'),
            'code': value.get('name').upper(),
            'name': value.get('name'),
            'active': bool(value.get('status')),
            'fee': 0.005,
            'precision': 0,
            'limits':
            {
                'amount':
                {
                    'min': 'None',
                    'max': 'None',
                },
                'price':    
                {
                    'min': 'None',
                    'max': 'None',
                }, 
                'cost':     
                {
                    'min': 'None',
                    'max': 'None',
                },
                'withdraw':
                {
                    'min': value.get('minWithdrawAmount'),
                    'max': value.get('maxWithdrawAmount'),
                },
            },
            'info': value
            }
            listData.append(tmp)
        return listData

    def fetch_currencies(self):
        # Responce example
        #[
        #    {
        #        'id': 'BTC', 
        #        'code': 'BTC', 
        #        'name': 'BTC', 
        #        'active': True, 
        #        'fee': 0.001, 
        #        'precision': 0, 
        #        'limits': // TPR exchange has no restrictions
        #        {
        #            'amount': 
        #            {
        #                'min': 'None', 
        #                'max': 'None'
        #            }, 
        #            'price': 
        #            {
        #                'min': 'None', 
        #                'max': 'None'
        #            }, 
        #            'cost': 
        #            {
        #                'min': 'None', 
        #                'max': 'None'
        #            }, 
        #            'withdraw': 
        #            {
        #                'min': 1.0, 
        #                'max': 5000.0
        #            }
        #        }, 
        #        'info': { }, 
        #    },
        #    ...
        #]
        try:
            response = self.privatePostMarketCoinsInfo()
            return self.parse_currencies(response)
        except:
            reason = str(sys.exc_info()[0])
            if reason.find('ExchangeNotAvailable') != -1:
                return {"Error": "ExchangeNotAvailable"}
            else:
                return {"Error": "Unknown reason"}

    def fetch_order(self, id, symbol=None, params={}):
        request = {
            'orderId': id,
        }
        response = self.privatePostExchangeOrderFind(request)
        return self.parse_order(response)

    def parse_order(self, order, market=None):
        # {
        #   'orderId':'E161183624377614',
        #   'memberId':2,
        #   'type':'LIMIT_PRICE',
        #   'amount':1000.0,
        #   'symbol':'BCH/USDT',
        #   'tradedAmount':1000.0,
        #   'turnover':1080.0,
        #   'coinSymbol':'BCH',
        #   'baseSymbol':'USDT',
        #   'status':'COMPLETED',
        #   'latestTradeTimestamp':1611836256242,
        #   'direction':'SELL',
        #   'price':1.0,
        #   'time':1611836243776,
        #   'completedTime':1611836256242,
        # },
        if not order:
            return None
        type = 'market'
        if order['type'] == 'LIMIT_PRICE':
            type = 'limit'
        side = order['direction'].lower()
        remaining = order['amount'] - order['tradedAmount']
        status = order['status']
        if status == 'COMPLETED':
            status = 'closed'
        elif status == 'TRADING' or status == 'PAUSED' or status == 'RESERVED':
            status = 'open'
        else:
            status = 'canceled'
        cost = order['tradedAmount'] * order['price']
        result = {
            'info': order,
            'id': order['orderId'],
            'clientOrderId': order['memberId'],
            'timestamp': order['time'],
            'datetime': self.iso8601(order['time']),
            'latestTradeTimestamp': order['latestTradeTimestamp'],
            'symbol': order['symbol'],
            'type': type,
            'timeInForce': None,
            'postOnly': None,
            'side': side,
            'price': order['price'],
            'stopPrice': None,
            'cost': cost,
            'average': None,
            'amount': order['amount'],
            'filled': order['tradedAmount'],
            'remaining': remaining,
            'status': status,
            'fee': None,
            'trades': None,
        }
        return result

    def create_order(self, symbol, type, side, amount, price=None, params={}):
        params['symbol'] = symbol
        params['price'] = price
        params['amount'] = amount
        if side == 'buy':
            params['direction'] = 'BUY'
        else:
            params['direction'] = 'SELL'
        if type == 'market':
            params['type'] = 'MARKET_PRICE'
        else:
            params['type'] = 'LIMIT_PRICE'
        params['useDiscount'] = '0'
        response = self.privatePostExchangeOrderAdd(params)
        orderId = self.safe_string(response, 'data')
        return self.fetch_order(orderId)

    def cancel_order(self, id, symbol=None, params={}):
        request = {
            'orderId': id,
        }
        response = self.privatePostExchangeOrderApicancel(self.extend(request, params))
        return self.parse_order(response['data'])

    def fetch_orders(self, symbol=None, since=None, limit=None, params={}):
        # Request structure
        # {
        #   'symbol': Parameter from method arguments
        #   'since': Timestamp of first order in list in Unix epoch format
        #   'limit': Response list size
        #   'memberId': May be set in params. May be not set
        #   'status': one of TRADING COMPLETED CANCELED OVERTIMED. May be set in params
        #   'page': for pagination. In self case limit is size of every page. May be set in params
        # }
        default_order_amount_limit = 10000
        if 'page' in params:
            params['pageNo'] = self.safe_string(params, 'page')
        else:
            params['pageNo'] = 0
        if symbol is None:
            symbol = ''
        if since is None:
            since = 0
        if limit is None:
            limit = default_order_amount_limit

        request = {
            'symbol': symbol,
            'since': since,
            'pageSize': limit,
        }

        fullRequest = self.extend(request, params)
        response = self.privatePostExchangeOrderAll(fullRequest)
        # {
        #     'content': [
        #         {
        #             'orderId':'E161183624377614',
        #             'memberId':2,
        #             'type':'LIMIT_PRICE',
        #             'amount':1000.0,
        #             'symbol':'BCH/USDT',
        #             'tradedAmount':1000.0,
        #             'turnover':1080.0,
        #             'coinSymbol':'BCH',
        #             'baseSymbol':'USDT',
        #             'status':'COMPLETED',
        #             'direction':'SELL',
        #             'price':1.0,
        #             'time':1611836243776,
        #             'completedTime':1611836256242,
        #         },
        #         ...
        #     ],
        #     'totalElements':41,
        #     'totalPages':3,
        #     'last':False,
        #     'size':20,
        #     'number':1,
        #     'first':False,
        #     'numberOfElements':20,
        #     'sort': [
        #         {
        #             'direction':'DESC',
        #             'property':'time',
        #             'ignoreCase':False,
        #             'nullHandling':'NATIVE',
        #             'ascending':False,
        #             'descending':True,
        #         }
        #     ]
        # }
        return self.parse_orders(response['content'])

    def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
        # Request structure
        # {
        #   'symbol': Parameter from method arguments
        #   'since': Timestamp of first order in list in Unix epoch format
        #   'limit': Response list size
        #   'memberId': May be set in params. May be not set
        #   'status': one of TRADING COMPLETED CANCELED OVERTIMED. May be set in params
        #   'page': for pagination. In self case limit is size of every page. May be set in params
        # }
        default_order_amount_limit = 20
        if 'page' in params:
            params['pageNo'] = self.safe_string(params, 'page')
        else:
            params['pageNo'] = 0

        if symbol is None:
            symbol = ''
        if since is None:
            since = 0
        if limit is None:
            limit = default_order_amount_limit
        
        request = {
            'symbol': symbol,
            'since': since,
            'pageSize': limit,
        }
        fullRequest = self.extend(request, params)
        response = self.privatePostExchangeOrderAll(fullRequest)
        # {
        #     'content': [
        #         {
        #             'orderId':'E161183624377614',
        #             'memberId':2,
        #             'type':'LIMIT_PRICE',
        #             'amount':1000.0,
        #             'symbol':'BCH/USDT',
        #             'tradedAmount':1000.0,
        #             'turnover':1080.0,
        #             'coinSymbol':'BCH',
        #             'baseSymbol':'USDT',
        #             'status':'COMPLETED',
        #             'direction':'SELL',
        #             'price':1.0,
        #             'time':1611836243776,
        #             'completedTime':1611836256242,
        #         },
        #         ...
        #     ],
        #     'totalElements':41,
        #     'totalPages':3,
        #     'last':False,
        #     'size':20,
        #     'number':1,
        #     'first':False,
        #     'numberOfElements':20,
        #     'sort': [
        #         {
        #             'direction':'DESC',
        #             'property':'time',
        #             'ignoreCase':False,
        #             'nullHandling':'NATIVE',
        #             'ascending':False,
        #             'descending':True,
        #         }
        #     ]
        # }
        return self.parse_orders(response['content'])

    def fetch_closed_orders(self, symbol=None, since=None, limit=None, params={}):
        # Request structure
        # {
        #   'symbol': Parameter from method arguments
        #   'since': Timestamp of first order in list in Unix epoch format
        #   'limit': Response list size
        #   'memberId': May be set in params. May be not set
        #   'status': one of TRADING COMPLETED CANCELED OVERTIMED. May be set in params
        #   'page': for pagination. In self case limit is size of every page. May be set in params
        # }
        default_order_amount_limit = 20
        params['status'] = 'CANCELED'
        if 'page' in params:
            params['pageNo'] = self.safe_string(params, 'page')
        else:
            params['pageNo'] = 0

        if symbol is None:
            symbol = ''
        if since is None:
            since = 0
        if limit is None:
            limit = default_order_amount_limit
        
        request = {
            'symbol': symbol,
            'since': since,
            'pageSize': limit,
        }
        fullRequest = self.extend(request, params)
        response = self.privatePostExchangeOrderAll(fullRequest)
        # {
        #     'content': [
        #         {
        #             'orderId':'E161183624377614',
        #             'memberId':2,
        #             'type':'LIMIT_PRICE',
        #             'amount':1000.0,
        #             'symbol':'BCH/USDT',
        #             'tradedAmount':1000.0,
        #             'turnover':1080.0,
        #             'coinSymbol':'BCH',
        #             'baseSymbol':'USDT',
        #             'status':'COMPLETED',
        #             'direction':'SELL',
        #             'price':1.0,
        #             'time':1611836243776,
        #             'completedTime':1611836256242,
        #         },
        #         ...
        #     ],
        #     'totalElements':41,
        #     'totalPages':3,
        #     'last':False,
        #     'size':20,
        #     'number':1,
        #     'first':False,
        #     'numberOfElements':20,
        #     'sort': [
        #         {
        #             'direction':'DESC',
        #             'property':'time',
        #             'ignoreCase':False,
        #             'nullHandling':'NATIVE',
        #             'ascending':False,
        #             'descending':True,
        #         }
        #     ]
        # }
        return self.parse_orders(response['content'])

    # If call without params the function returns balance of current user
    def fetch_balance(self, uid='-1', params={}):
        params = {
            'uid': uid
        }
        try:
            response = self.privatePostUcenterMemberBalance(params)
        except Exception as e:
            return e
        return self.parse_balance(response)

    def parse_balance(self, response):
        data = json.loads(json.dumps(response))
        if data['message'] == 'SUCCESS':
            result = { "free":{}, "used":{}, "total":{}}
            for row in data['data']['balances']:
                result['free'].update({row['coinName']:row['free']})
                result['used'].update({row['coinName']:row['used']})
                result['total'].update({row['coinName']:row['total']})
                result.update({row['coinName']:{'free':row['free'], 'used':row['used'], 'total':row['total']}})
            return result

    # Returns int or None
    def get_market_price(self, symbol):

        response = self.privatePostMarketSymbolThumb()
        for i in response:
            if i.get('symbol') == symbol:
                return i.get('close')

    def fetch_trades(self, orderId, since, pageNo=None, pageSize=None):
        # Responce example:
        # [
        #    {
        #       'info': { backend response }, 
        #       'id': 'E161460499516968', 
        #       'timestamp': 1614605187661, 
        #       'datetime': '2021-03-01 15:26:27.661000', 
        #       'symbol': 'BTC/USDT', 
        #       'order': 'E161460499516968', 
        #       'type': 'LIMIT_PRICE', 
        #       'side': 'SELL', 
        #       'takerOrMaker': 'None', (Have no this information inside TPR exchange)
        #       'price': 1.0, 
        #       'amount': 1.0, 
        #       'cost': 1.0, 
        #       'fee': 
        #       {
        #           'cost': 0.005, 
        #           'currency': 'BTC', 
        #           'rate': 'None' (Have no this information inside TPR exchange)
        #       }
        #    }
        # ]

        if pageNo is None:
            pageNo = 0
        
        if pageSize is None:
            pageSize = 100

        request = { 'orderId': orderId,
                    'since': since,
                    'pageNo': pageNo,
                    'pageSize': pageSize }
        return self.parse_trade(self.privatePostExchangeOrderTrades(request))

    def parse_trade(self, response):
        trades = []
        content = response.get('content')
        for exchangeTrade in content:
            timestamp = exchangeTrade.get('time')
            datetime_ = str(datetime.fromtimestamp(int(timestamp) * 0.001))
            price = exchangeTrade.get('price')
            amount = exchangeTrade.get('amount')
            cost = price * amount

            tmp = {
                'info': exchangeTrade,
                'id': exchangeTrade.get('orderId'),
                'timestamp': timestamp,
                'datetime': datetime_,
                'symbol': exchangeTrade.get('symbol'),
                'order': exchangeTrade.get('orderId'),
                'type': exchangeTrade.get('type'),
                'side': exchangeTrade.get('direction'),
                'takerOrMaker': 'None',
                'price': price,
                'amount': amount,
                'cost': cost,
                'fee':
                {
                    'cost': exchangeTrade.get('fee'),
                    'currency': exchangeTrade.get('coinSymbol'),
                    'rate': 'None',
                }
            }
            trades.append(tmp)
        return trades

    def parse_my_trades(self, response):
        listData = []
        for value in response:
            ExchangeOrder = response.get(value)
            id_ = ExchangeOrder.get('orderId')
            timestamp = ExchangeOrder.get('time')
            datetime_ = str(datetime.fromtimestamp(int(timestamp) * 0.001))
            price = ExchangeOrder.get('price')
            amount = ExchangeOrder.get('amount')
            cost = price * amount

            tmp = {
                'info': response.get(value),
                'id': id_,
                'timestamp': timestamp,
                'datetime': datetime_,
                'symbol': ExchangeOrder.get('symbol'),
                'order': id_,
                'type': ExchangeOrder.get('type'),
                'side': ExchangeOrder.get('direction'),
                'takerOrMaker': 'None',
                'price': price,
                'amount': amount,
                'cost': cost,
                'fee':
                {
                    'cost': ExchangeOrder.get('fee'),
                    'currency': ExchangeOrder.get('coinSymbol'),
                    'rate': 'None',
                }
                }
            listData.append(tmp)
        return listData

    def fetch_my_trades(self, pageNo=None, pageSize=None):
        # Responce example:
        # [
        #    {
        #       'info': { backend response }, 
        #       'id': 'E161460499516968', 
        #       'timestamp': 1614605187661, 
        #       'datetime': '2021-03-01 15:26:27.661000', 
        #       'symbol': 'BTC/USDT', 
        #       'order': 'E161460499516968', 
        #       'type': 'LIMIT_PRICE', 
        #       'side': 'SELL', 
        #       'takerOrMaker': 'None', (Have no this information inside TPR exchange)
        #       'price': 1.0, 
        #       'amount': 1.0, 
        #       'cost': 1.0, 
        #       'fee': 
        #       {
        #           'cost': 0.001, 
        #           'currency': 'BTC', 
        #           'rate': 'None'  (Have no this information inside TPR exchange)
        #       }
        #    }, 
        #    { ... },
        # ]

        if pageNo is None:
            pageNo = 0
        
        if pageSize is None:
            pageSize = 100

        request = { 'orderId': '',
                    'pageNo': pageNo,
                    'pageSize': pageSize }
        return self.parse_my_trades(self.privatePostExchangeOrderMyTrades(request))

    def handle_errors(self, httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody):
        if response is None:
            return  # fallback to default error handler
        if httpCode == 200:
            if 'code' in response:
                if response['code'] == 0:
                    return
            else:
                return
        # {
        #     "message": "Error text in case when HTTP code is not 200",
        #     ...
        # }
        message = self.safe_string(response, 'message')
        if message is not None:
            feedback = self.id + ' ' + body
            self.throw_exactly_matched_exception(self.exceptions['exact'], message, feedback)
            self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback)
            raise ExchangeError(feedback)  # unknown message

# -*- 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 BadRequest
from ccxt.base.errors import BadSymbol
from ccxt.base.errors import InvalidOrder
from ccxt.base.decimal_to_precision import SIGNIFICANT_DIGITS

import json
import sys
from datetime import datetime


class tprexchange(Exchange):

    def describe(self):
        return self.deep_extend(super(tprexchange, self).describe(), {
            'id': 'tprexchange',
            'name': 'TPR Exchange',
            # 'countries': ['US'],
            # 'rateLimit': 500,
            'version': 'v1',
            'certified': False,
            'has': {
                'loadMarkets': True,
                'cancelAllOrders': False,
                'cancelOrder': True,
                'cancelOrders': False,
                'CORS': False,
                'createDepositAddress': False,
                'createLimitOrder': False,
                'createMarketOrder': False,
                'createOrder': True,
                'deposit': False,
                'editOrder': 'emulated',
                'fetchBalance': True,
                'fetchBidsAsks': False,
                'fetchClosedOrders': True,
                'fetchCurrencies': True,
                'fetchDepositAddress': False,
                'fetchDeposits': False,
                'fetchFundingFees': False,
                'fetchL2OrderBook': False,
                'fetchLedger': False,
                'fetchMarkets': True,
                'fetchMyTrades': True,
                'fetchOHLCV': True,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'fetchOrderBook': True,
                'fetchOrderBooks': False,
                'fetchOrders': True,
                'fetchOrderTrades': False,
                'fetchStatus': True,
                'fetchTicker': True,
                'fetchTickers': True,
                'fetchTime': False,
                'fetchTrades': True,
                'fetchTradingFee': False,
                'fetchTradingFees': False,
                'fetchTradingLimits': False,
                'fetchTransactions': False,
                'fetchWithdrawals': False,
                'privateAPI': True,
                'publicAPI': False,
                'signIn': True,
                'withdraw': False,
                'getMarketPrice': True,
            },
            'timeframes': {
                '1m': '1',
                '1h': '60',
                '1d': '1440',
                '1w': '10080',
                '1mn': '43200',
            },
            'urls': {
                'logo': '',
                'api': '{hostname}',
                'www': '',
                'doc': '',
                'fees': '',
                'referral': '',
            },
            'api': {
                'private': {
                    'get': [
                    ],
                    'post': [
                        'ucenter/api-login',
                        'ucenter/member/balance',
                        'market/symbol-thumb',
                        'market/coins-info',
                        'market/symbol-info',
                        'exchange/order/add',
                        'exchange/order/find',
                        'exchange/order/all',
                        'exchange/order/apicancel',
                        'exchange/order/trades',
                        'exchange/order/my-trades',
                        'exchange/exchange-coin/base-symbol',
                    ],
                    'delete': [
                    ],
                },
                'feed': {
                    'get': [
                    ],
                },
            },
            'fees': {
                'trading': {
                },
            },
            'requiredCredentials': {
                'apiKey': True,
                'secret': True,
                'uid': False,
            },
            'precisionMode': SIGNIFICANT_DIGITS,
            'options': {
                'createMarketBuyOrderRequiresPrice': False,
            },
            'exceptions': {
                'exact': {
                    'Invalid cost': InvalidOrder,  # {"message":"Invalid cost","_links":{"self":{"href":"/orders","templated":false}}}
                    'Invalid order ID': InvalidOrder,  # {"message":"Invalid order ID","_links":{"self":{"href":"/orders/4a151805-d594-4a96-9d64-e3984f2441f7","templated":false}}}
                    'Invalid market !': BadSymbol,  # {"message":"Invalid market !","_links":{"self":{"href":"/markets/300/order-book","templated":false}}}
                },
                'broad': {
                    'Failed to convert argument': BadRequest,
                },
            },
        })

    def parse_ticker(self, response):
        if len(response) == 0:
            return []
        symbol = response[0].get('symbol')
        high = 0
        bidVolume = 0
        askVolume = 0
        vwap = 0
        vwapCost = 0
        vwapVolume = 0
        open_ = 'None'
        close = 0
        last = close
        previousClose = 'None'
        change = 'None' 
        percentage = 'None' 
        average = 'None'
        baseVolume = 0
        quoteVolume = 0
        time = 0
        lastDayTime = int((datetime.now().timestamp() - 86400) * 1000)
        currentTimestamp = int(datetime.now().timestamp() * 1000)
        currentDatetime = str(datetime.fromtimestamp(currentTimestamp * 0.001))
        low = response[0].get('price')
        bid = 0
        ask = sys.maxsize
        openSellOrdersCount = 0

        for order in response:

            price = order.get('price')
            amount = order.get('amount')
            timestamp = order.get('timestamp')

            if high < price:
                high = price
            if low > price:
                low = price
            
            if order.get('status') == 'open':
                if order.get('side') == 'buy':
                    if bid < price:
                        bid = price
                    if bidVolume < amount:
                        bidVolume = amount

            if order.get('status') == 'open':
                if order.get('side') == 'sell':
                    openSellOrdersCount += 1
                    if ask > price:
                        ask = price
                    if askVolume < amount:
                        askVolume = amount

            if order.get('info').get('status') == 'COMPLETED':
                vwapCost += price * amount
                vwapVolume += amount
                if time < timestamp:
                    time = timestamp
                    close = price
                if timestamp > lastDayTime:
                    quoteVolume += amount
                    baseVolume += price

        if vwapVolume != 0:
            vwap = vwapCost / vwapVolume
        if openSellOrdersCount == 0:
            ask = 0
        last = close

        result = {
            'symbol':        symbol,
            'info':          response,
            'timestamp':     currentTimestamp,
            'datetime':      currentDatetime,
            'high':          high,
            'low':           low,
            'bid':           bid,
            'bidVolume':     bidVolume,
            'ask':           ask,
            'askVolume':     askVolume,
            'vwap':          vwap,
            'open':          open_,
            'close':         close,
            'last':          last,
            'previousClose': previousClose,
            'change':        change,
            'percentage':    percentage,
            'average':       average,
            'baseVolume':    baseVolume,
            'quoteVolume':   quoteVolume,
            }
        return result

    def fetch_ticker(self, symbol, since=None, limit=None):
        response = self.fetch_orders(symbol, since, limit)
        # Response example:
        # {
        #     'symbol': 'BTC/USDT', 
        #     'info': [...], 
        #     'timestamp': 1615386851976, 
        #     'datetime': '2021-03-10 16:34:11.976000', 
        #     'high': 50.0, 
        #     'low': 1.0, 
        #     'bid': 30.0, 
        #     'bidVolume': 15.0, 
        #     'ask': 40.0, 
        #     'askVolume': 25.0, 
        #     'vwap': 11.0, 
        #     'open': 'None', 
        #     'close': 20.0, 
        #     'last': 20.0, 
        #     'previousClose': 'None', 
        #     'change': 'None', 
        #     'percentage': 'None', 
        #     'average': 'None', 
        #     'baseVolume': 60.0, 
        #     'quoteVolume': 30.0
        # }
        return self.parse_ticker(response)

    def fetch_tickers(self, since=None, limit=None):
        # Response example:
        # [
        #     {
        #         'symbol': 'BTC/USDT', 
        #         'info': [...], 
        #         'timestamp': 1615386851976, 
        #         'datetime': '2021-03-10 16:34:11.976000', 
        #         'high': 50.0, 
        #         'low': 1.0, 
        #         'bid': 30.0, 
        #         'bidVolume': 15.0, 
        #         'ask': 40.0, 
        #         'askVolume': 25.0, 
        #         'vwap': 11.0, 
        #         'open': 'None', 
        #         'close': 20.0, 
        #         'last': 20.0, 
        #         'previousClose': 'None', 
        #         'change': 'None', 
        #         'percentage': 'None', 
        #         'average': 'None', 
        #         'baseVolume': 60.0, 
        #         'quoteVolume': 30.0
        #     }, 
        #     ...
        # ]
        result = []
        symbols = self.fetch_markets()
        for symblol in symbols:
            response = self.fetch_orders(symblol.get('symbol'), since, limit)
            ticker = self.parse_ticker(response)
            if len(ticker) != 0:
                result.append(ticker)
        return result

    def fetch_order_book(self, symbol, limit, since=0):
        # Response example:
        # {
        #     'bids': 
        #     [
        #        [20.0, 10.0, 'E161538482263642'],   // [price, amount, orderId]
        #         [30.0, 15.0, 'E161538482271646']
        #     ], 
        #     'asks': 
        #     [
        #         [40.0, 20.0, 'E161538482278825'], 
        #         [50.0, 25.0, 'E161538482286085']
        #     ], 
        #     'timestamp': 1615390711695, 
        #     'datetime': '2021-03-10 17:38:31.695000', 
        #     'nonce': 1615390711695
        # }
        orders = self.fetch_open_orders(symbol, since, limit)
        bids = []
        asks = []
        for order in orders:
            temp = []
            temp.append(order.get('price'))
            temp.append(order.get('amount'))
            temp.append(order.get('id'))
            if order.get('side') == 'buy':
                bids.append(temp)
            else:
                asks.append(temp)

        currentTimestamp = int(datetime.now().timestamp() * 1000)
        currentDatetime = str(datetime.fromtimestamp(currentTimestamp * 0.001))

        result = {
            'bids': bids,
            'asks': asks,
            'timestamp': currentTimestamp,
            'datetime': currentDatetime,
            'nonce': currentTimestamp,
        }
        return result

    def parse_markets(self, response):
        listData = []
        for value in response:
            tmp = {
            "id": value.get("coinSymbol"),
            "symbol": value.get("symbol"),
            "base": value.get("coinSymbol"),
            "quote": value.get("baseSymbol"),
            "baseId": value.get("coinSymbol"),
            "quoteId": value.get("baseSymbol"),
            "type": value.get("publishType"),
            "active": value.get("enable"),
            "precision": {
                "amount": value.get("coinScale"),
                "price": value.get("baseCoinScale"),
                },
            "limits": {
                "amount": {"min": value.get("minVolume"), "max": value.get("maxVolume")},
                "price": {"min": value.get("minSellPrice"), "max": value.get("maxBuyPrice")},
                "cost": {"min": value.get("minVolume") * value.get("minSellPrice"), "max": value.get("maxVolume") * value.get("maxBuyPrice")},
                },
            "taker": value.get("fee"),
            "maker": value.get("fee"),
            "info": value,
            }
            listData.append(tmp)
        return listData

    def add_frame(self, timeFrameStart, timeFrameEnd, timeframe, highestPrice, lowestPrice, amount, result, openPrice, closePrice):
        frame = []
        frame.append(timeFrameStart)
        frame.append(openPrice)
        frame.append(highestPrice)
        frame.append(lowestPrice)
        frame.append(closePrice)
        frame.append(amount)
        result.append(frame)

    def parse_ohlcv(self, response, since, timeframe):
        highestPrice = 0
        lowestPrice = sys.maxsize
        price = 0
        amount = 0
        timeFrameStart = since
        timeFrameEnd = int((since * 0.001 + timeframe) * 1000)
        result = []
        i = 0
        orders = response.get('content')
        isOpenPrice = True
        openPrice = 0
        closePrice = 0

        while i < len(orders):
            if isOpenPrice == True:
                openPrice = orders[i].get('price')
                isOpenPrice = False
            time = orders[i].get('time')
            if time >= timeFrameStart and time <= timeFrameEnd:
                price = orders[i].get('price')
                closePrice = price
                if highestPrice < price:
                    highestPrice = price
                if lowestPrice > price:
                    lowestPrice = price
                amount += orders[i].get('amount')
                i += 1
                if i == len(orders):
                    self.add_frame(timeFrameStart, timeFrameEnd, timeframe, highestPrice, lowestPrice, amount, result, openPrice, closePrice)
            else:
                if lowestPrice == sys.maxsize:
                    lowestPrice = 0
                    openPrice = 0
                    closePrice = 0
                    i -= 1
                self.add_frame(timeFrameStart, timeFrameEnd, timeframe, highestPrice, lowestPrice, amount, result, openPrice, closePrice)
                timeFrameStart = timeFrameEnd + 1
                timeFrameEnd = int((timeFrameEnd * 0.001 + timeframe) * 1000)
                amount = 0
                highestPrice = 0
                lowestPrice = sys.maxsize
                isOpenPrice = True
                i += 1

        return result

    # timeframe variants: 
    # 1m (one minute);
    # 1h (one hour);
    # 1d (one day - 24 hours)
    # 1w (one week - 7 days)
    # 1mn (one mounth - 30 days)
    def fetch_ohlcv(self, symbol, timeframe=None, since=0, limit=None, params={}):
        # Response example:
        # [
        #     [
        #         1504541580000, // UTC timestamp in milliseconds, integer
        #         4235.4,        // (O)pen price, float
        #         4240.6,        // (H)ighest price, float
        #         4230.0,        // (L)owest price, float
        #         4230.7,        // (C)losing price, float
        #         37.72941911    // (V)olume (in terms of the base currency), float
        #     ],
        #     ...
        # ]
        inputDataCheck = False

        for frame in self.timeframes:
            if frame == timeframe:
                inputDataCheck = True
                break
        
        if inputDataCheck == False:
            return {'error': 'Incorrect timeframe'}

        tFrame = int(self.timeframes.get(timeframe)) * 60

        default_order_amount_limit = 100
        params['status'] = 'COMPLETED'
        if 'page' in params:
            params['pageNo'] = self.safe_string(params, 'page')
        else:
            params['pageNo'] = 0

        if since is None:
            since = 0
        if limit is None:
            limit = default_order_amount_limit
        
        request = {
            'symbol': symbol,
            'since': since,
            'pageSize': limit,
        }
        fullRequest = self.extend(request, params)
        response = self.privatePostExchangeOrderAll(fullRequest)
        return self.parse_ohlcv(response, since, tFrame)
        
    def fetch_markets(self, symbol=''):
        request = {
            'symbol': symbol,
        }
        response = self.privatePostMarketSymbolInfo(request)
        return self.parse_markets(response)
        # RETURN EXAMPLE:
        # [
        #     {
        #         'id': 'BTC', 
        #         'symbol': 'BTC/USDT', 
        #         'base': 'BTC', 
        #         'quote': 'USDT', 
        #         'baseId': 'BTC', 
        #         'quoteId': 'USDT', 
        #         'type': 'NONE', 
        #         'active': 1, 
        #         'precision': { 'amount': 2, 'price': 2 }, 
        #         'limits': 
        #         {
        #             'amount': { 'min': 0.0, 'max': 0.0 }, 
        #             'price': { 'min': 0.0, 'max': 0.0 }, 
        #             'cost': { 'min': 0.0, 'max': 0.0 }
        #         }, 
        #         'taker': 0.001, 
        #         'maker': 0.001, 
        #         'info': {backend response}
        #     },
        #     ...
        # ]

    def load_markets(self, reload=False, symbol=''):
        if not reload:
            if self.markets:
                if not self.markets_by_id:
                    return self.set_markets(self.markets)
                return self.markets
        currencies = None
        if self.has['fetchCurrencies']:
            currencies = self.fetch_currencies()
        markets = self.fetch_markets(symbol)
        return self.set_markets(markets, currencies)

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        # Check existance of authentication token
        # Just use empy one in case of an application is not signed in yet
        authToken = ''
        if 'token' in self.options:
            authToken = self.options['token']
        # Get URL
        url = self.implode_params(self.urls['api'], {'hostname': self.hostname}) + '/' + path
        # Calculate body and content type depending on method type: GET or POST
        keys = list(params.keys())
        keysLength = len(keys)
        # In case of body is still not assigned just make it empty string
        if body is None:
            body = ''
        # Prepare line for hashing
        # This hash sum is checked on backend side to verify API user
        # POST params should not be added as body
        query = method + ' /' + path + ' ' + self.urlencode(params) + ' ' + authToken + '\n' + body
        signed = self.hmac(self.encode(query), self.encode(self.secret))
        contentType = None
        if method == 'POST':
            contentType = 'application/x-www-form-urlencoded'
            if keysLength > 0:
                body = self.urlencode(params)
        else:
            if keysLength > 0:
                url += '?' + self.urlencode(params)
        headers = {
            'x-auth-sign': signed,
            'x-auth-token': authToken,
        }
        if authToken != '':
            headers['access-auth-token'] = authToken
        if contentType is not None:
            headers['Content-Type'] = contentType
        return {'url': url, 'method': method, 'body': body, 'headers': headers}

    def sign_in(self, params={}):
        params = {
            'key': self.key,
            'token': self.token,
        }
        response = self.privatePostUcenterApiLogin(params)
        loginData = response['data']
        self.options['token'] = self.safe_string(loginData, 'token')
        memberId = self.safe_string(loginData, 'id')
        return memberId

    def fetch_status(self):
        # Responce examples:
        # {'status': 'ok'}
        # or
        # {'status': 'shutdown', 'reason': 'ExchangeNotAvailable'}
        # or
        # {'status': 'shutdown', 'reason': 'Unknown reason'}
        result = False
        try:
            response = self.privatePostExchangeExchangeCoinBaseSymbol()
            for field in response.items():
                if field[0] == 'message':
                    if field[1] == 'SUCCESS':
                        result = True
            if result is True:
                return {"status": "ok"}
            else:
                return {"status": "shutdown", "reason": "ExchangeNotAvailable"}
        except:
            reason = str(sys.exc_info()[0])
            if reason.find('ExchangeNotAvailable') != -1:
                return {"status": "shutdown", "reason": "ExchangeNotAvailable"}
            else:
                return {"status": "shutdown", "reason": "Unknown reason"}

    def parse_currencies(self, response):
        listData = []
        for value in response:
            tmp = {
            'id': value.get('name'),
            'code': value.get('name').upper(),
            'name': value.get('name'),
            'active': bool(value.get('status')),
            'fee': 0.005,
            'precision': 0,
            'limits':
            {
                'amount':
                {
                    'min': 'None',
                    'max': 'None',
                },
                'price':    
                {
                    'min': 'None',
                    'max': 'None',
                }, 
                'cost':     
                {
                    'min': 'None',
                    'max': 'None',
                },
                'withdraw':
                {
                    'min': value.get('minWithdrawAmount'),
                    'max': value.get('maxWithdrawAmount'),
                },
            },
            'info': value
            }
            listData.append(tmp)
        return listData

    def fetch_currencies(self):
        # Responce example
        #[
        #    {
        #        'id': 'BTC', 
        #        'code': 'BTC', 
        #        'name': 'BTC', 
        #        'active': True, 
        #        'fee': 0.001, 
        #        'precision': 0, 
        #        'limits': // TPR exchange has no restrictions
        #        {
        #            'amount': 
        #            {
        #                'min': 'None', 
        #                'max': 'None'
        #            }, 
        #            'price': 
        #            {
        #                'min': 'None', 
        #                'max': 'None'
        #            }, 
        #            'cost': 
        #            {
        #                'min': 'None', 
        #                'max': 'None'
        #            }, 
        #            'withdraw': 
        #            {
        #                'min': 1.0, 
        #                'max': 5000.0
        #            }
        #        }, 
        #        'info': { }, 
        #    },
        #    ...
        #]
        try:
            response = self.privatePostMarketCoinsInfo()
            return self.parse_currencies(response)
        except:
            reason = str(sys.exc_info()[0])
            if reason.find('ExchangeNotAvailable') != -1:
                return {"Error": "ExchangeNotAvailable"}
            else:
                return {"Error": "Unknown reason"}

    def fetch_order(self, id, symbol=None, params={}):
        request = {
            'orderId': id,
        }
        response = self.privatePostExchangeOrderFind(request)
        return self.parse_order(response)

    def parse_order(self, order, market=None):
        # {
        #   'orderId':'E161183624377614',
        #   'memberId':2,
        #   'type':'LIMIT_PRICE',
        #   'amount':1000.0,
        #   'symbol':'BCH/USDT',
        #   'tradedAmount':1000.0,
        #   'turnover':1080.0,
        #   'coinSymbol':'BCH',
        #   'baseSymbol':'USDT',
        #   'status':'COMPLETED',
        #   'latestTradeTimestamp':1611836256242,
        #   'direction':'SELL',
        #   'price':1.0,
        #   'time':1611836243776,
        #   'completedTime':1611836256242,
        # },
        if not order:
            return None
        type = 'market'
        if order['type'] == 'LIMIT_PRICE':
            type = 'limit'
        side = order['direction'].lower()
        remaining = order['amount'] - order['tradedAmount']
        status = order['status']
        if status == 'COMPLETED':
            status = 'closed'
        elif status == 'TRADING' or status == 'PAUSED' or status == 'RESERVED':
            status = 'open'
        else:
            status = 'canceled'
        cost = order['tradedAmount'] * order['price']
        result = {
            'info': order,
            'id': order['orderId'],
            'clientOrderId': order['memberId'],
            'timestamp': order['time'],
            'datetime': self.iso8601(order['time']),
            'latestTradeTimestamp': order['latestTradeTimestamp'],
            'symbol': order['symbol'],
            'type': type,
            'timeInForce': None,
            'postOnly': None,
            'side': side,
            'price': order['price'],
            'stopPrice': None,
            'cost': cost,
            'average': None,
            'amount': order['amount'],
            'filled': order['tradedAmount'],
            'remaining': remaining,
            'status': status,
            'fee': None,
            'trades': None,
        }
        return result

    def create_order(self, symbol, type, side, amount, price=None, params={}):
        params['symbol'] = symbol
        params['price'] = price
        params['amount'] = amount
        if side == 'buy':
            params['direction'] = 'BUY'
        else:
            params['direction'] = 'SELL'
        if type == 'market':
            params['type'] = 'MARKET_PRICE'
        else:
            params['type'] = 'LIMIT_PRICE'
        params['useDiscount'] = '0'
        response = self.privatePostExchangeOrderAdd(params)
        orderId = self.safe_string(response, 'data')
        return self.fetch_order(orderId)

    def cancel_order(self, id, symbol=None, params={}):
        request = {
            'orderId': id,
        }
        response = self.privatePostExchangeOrderApicancel(self.extend(request, params))
        return self.parse_order(response['data'])

    def fetch_orders(self, symbol=None, since=None, limit=None, params={}):
        # Request structure
        # {
        #   'symbol': Parameter from method arguments
        #   'since': Timestamp of first order in list in Unix epoch format
        #   'limit': Response list size
        #   'memberId': May be set in params. May be not set
        #   'status': one of TRADING COMPLETED CANCELED OVERTIMED. May be set in params
        #   'page': for pagination. In self case limit is size of every page. May be set in params
        # }
        default_order_amount_limit = 10000
        if 'page' in params:
            params['pageNo'] = self.safe_string(params, 'page')
        else:
            params['pageNo'] = 0
        if symbol is None:
            symbol = ''
        if since is None:
            since = 0
        if limit is None:
            limit = default_order_amount_limit

        request = {
            'symbol': symbol,
            'since': since,
            'pageSize': limit,
        }

        fullRequest = self.extend(request, params)
        response = self.privatePostExchangeOrderAll(fullRequest)
        # {
        #     'content': [
        #         {
        #             'orderId':'E161183624377614',
        #             'memberId':2,
        #             'type':'LIMIT_PRICE',
        #             'amount':1000.0,
        #             'symbol':'BCH/USDT',
        #             'tradedAmount':1000.0,
        #             'turnover':1080.0,
        #             'coinSymbol':'BCH',
        #             'baseSymbol':'USDT',
        #             'status':'COMPLETED',
        #             'direction':'SELL',
        #             'price':1.0,
        #             'time':1611836243776,
        #             'completedTime':1611836256242,
        #         },
        #         ...
        #     ],
        #     'totalElements':41,
        #     'totalPages':3,
        #     'last':False,
        #     'size':20,
        #     'number':1,
        #     'first':False,
        #     'numberOfElements':20,
        #     'sort': [
        #         {
        #             'direction':'DESC',
        #             'property':'time',
        #             'ignoreCase':False,
        #             'nullHandling':'NATIVE',
        #             'ascending':False,
        #             'descending':True,
        #         }
        #     ]
        # }
        return self.parse_orders(response['content'])

    def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
        # Request structure
        # {
        #   'symbol': Parameter from method arguments
        #   'since': Timestamp of first order in list in Unix epoch format
        #   'limit': Response list size
        #   'memberId': May be set in params. May be not set
        #   'status': one of TRADING COMPLETED CANCELED OVERTIMED. May be set in params
        #   'page': for pagination. In self case limit is size of every page. May be set in params
        # }
        default_order_amount_limit = 20
        if 'page' in params:
            params['pageNo'] = self.safe_string(params, 'page')
        else:
            params['pageNo'] = 0

        if symbol is None:
            symbol = ''
        if since is None:
            since = 0
        if limit is None:
            limit = default_order_amount_limit
        
        request = {
            'symbol': symbol,
            'since': since,
            'pageSize': limit,
        }
        fullRequest = self.extend(request, params)
        response = self.privatePostExchangeOrderAll(fullRequest)
        # {
        #     'content': [
        #         {
        #             'orderId':'E161183624377614',
        #             'memberId':2,
        #             'type':'LIMIT_PRICE',
        #             'amount':1000.0,
        #             'symbol':'BCH/USDT',
        #             'tradedAmount':1000.0,
        #             'turnover':1080.0,
        #             'coinSymbol':'BCH',
        #             'baseSymbol':'USDT',
        #             'status':'COMPLETED',
        #             'direction':'SELL',
        #             'price':1.0,
        #             'time':1611836243776,
        #             'completedTime':1611836256242,
        #         },
        #         ...
        #     ],
        #     'totalElements':41,
        #     'totalPages':3,
        #     'last':False,
        #     'size':20,
        #     'number':1,
        #     'first':False,
        #     'numberOfElements':20,
        #     'sort': [
        #         {
        #             'direction':'DESC',
        #             'property':'time',
        #             'ignoreCase':False,
        #             'nullHandling':'NATIVE',
        #             'ascending':False,
        #             'descending':True,
        #         }
        #     ]
        # }
        return self.parse_orders(response['content'])

    def fetch_closed_orders(self, symbol=None, since=None, limit=None, params={}):
        # Request structure
        # {
        #   'symbol': Parameter from method arguments
        #   'since': Timestamp of first order in list in Unix epoch format
        #   'limit': Response list size
        #   'memberId': May be set in params. May be not set
        #   'status': one of TRADING COMPLETED CANCELED OVERTIMED. May be set in params
        #   'page': for pagination. In self case limit is size of every page. May be set in params
        # }
        default_order_amount_limit = 20
        params['status'] = 'CANCELED'
        if 'page' in params:
            params['pageNo'] = self.safe_string(params, 'page')
        else:
            params['pageNo'] = 0

        if symbol is None:
            symbol = ''
        if since is None:
            since = 0
        if limit is None:
            limit = default_order_amount_limit
        
        request = {
            'symbol': symbol,
            'since': since,
            'pageSize': limit,
        }
        fullRequest = self.extend(request, params)
        response = self.privatePostExchangeOrderAll(fullRequest)
        # {
        #     'content': [
        #         {
        #             'orderId':'E161183624377614',
        #             'memberId':2,
        #             'type':'LIMIT_PRICE',
        #             'amount':1000.0,
        #             'symbol':'BCH/USDT',
        #             'tradedAmount':1000.0,
        #             'turnover':1080.0,
        #             'coinSymbol':'BCH',
        #             'baseSymbol':'USDT',
        #             'status':'COMPLETED',
        #             'direction':'SELL',
        #             'price':1.0,
        #             'time':1611836243776,
        #             'completedTime':1611836256242,
        #         },
        #         ...
        #     ],
        #     'totalElements':41,
        #     'totalPages':3,
        #     'last':False,
        #     'size':20,
        #     'number':1,
        #     'first':False,
        #     'numberOfElements':20,
        #     'sort': [
        #         {
        #             'direction':'DESC',
        #             'property':'time',
        #             'ignoreCase':False,
        #             'nullHandling':'NATIVE',
        #             'ascending':False,
        #             'descending':True,
        #         }
        #     ]
        # }
        return self.parse_orders(response['content'])

    # If call without params the function returns balance of current user
    def fetch_balance(self, uid='-1', params={}):
        params = {
            'uid': uid
        }
        try:
            response = self.privatePostUcenterMemberBalance(params)
        except Exception as e:
            return e
        return self.parse_balance(response)

    def parse_balance(self, response):
        data = json.loads(json.dumps(response))
        if data['message'] == 'SUCCESS':
            result = { "free":{}, "used":{}, "total":{}}
            for row in data['data']['balances']:
                result['free'].update({row['coinName']:row['free']})
                result['used'].update({row['coinName']:row['used']})
                result['total'].update({row['coinName']:row['total']})
                result.update({row['coinName']:{'free':row['free'], 'used':row['used'], 'total':row['total']}})
            return result

    # Returns int or None
    def get_market_price(self, symbol):

        response = self.privatePostMarketSymbolThumb()
        for i in response:
            if i.get('symbol') == symbol:
                return i.get('close')

    def fetch_trades(self, orderId, since, pageNo=None, pageSize=None):
        # Responce example:
        # [
        #    {
        #       'info': { backend response }, 
        #       'id': 'E161460499516968', 
        #       'timestamp': 1614605187661, 
        #       'datetime': '2021-03-01 15:26:27.661000', 
        #       'symbol': 'BTC/USDT', 
        #       'order': 'E161460499516968', 
        #       'type': 'LIMIT_PRICE', 
        #       'side': 'SELL', 
        #       'takerOrMaker': 'None', (Have no this information inside TPR exchange)
        #       'price': 1.0, 
        #       'amount': 1.0, 
        #       'cost': 1.0, 
        #       'fee': 
        #       {
        #           'cost': 0.005, 
        #           'currency': 'BTC', 
        #           'rate': 'None' (Have no this information inside TPR exchange)
        #       }
        #    }
        # ]

        if pageNo is None:
            pageNo = 0
        
        if pageSize is None:
            pageSize = 100

        request = { 'orderId': orderId,
                    'since': since,
                    'pageNo': pageNo,
                    'pageSize': pageSize }
        return self.parse_trade(self.privatePostExchangeOrderTrades(request))

    def parse_trade(self, response):
        trades = []
        content = response.get('content')
        for exchangeTrade in content:
            timestamp = exchangeTrade.get('time')
            datetime_ = str(datetime.fromtimestamp(int(timestamp) * 0.001))
            price = exchangeTrade.get('price')
            amount = exchangeTrade.get('amount')
            cost = price * amount

            tmp = {
                'info': exchangeTrade,
                'id': exchangeTrade.get('orderId'),
                'timestamp': timestamp,
                'datetime': datetime_,
                'symbol': exchangeTrade.get('symbol'),
                'order': exchangeTrade.get('orderId'),
                'type': exchangeTrade.get('type'),
                'side': exchangeTrade.get('direction'),
                'takerOrMaker': 'None',
                'price': price,
                'amount': amount,
                'cost': cost,
                'fee':
                {
                    'cost': exchangeTrade.get('fee'),
                    'currency': exchangeTrade.get('coinSymbol'),
                    'rate': 'None',
                }
            }
            trades.append(tmp)
        return trades

    def parse_my_trades(self, response):
        listData = []
        for value in response:
            ExchangeOrder = response.get(value)
            id_ = ExchangeOrder.get('orderId')
            timestamp = ExchangeOrder.get('time')
            datetime_ = str(datetime.fromtimestamp(int(timestamp) * 0.001))
            price = ExchangeOrder.get('price')
            amount = ExchangeOrder.get('amount')
            cost = price * amount

            tmp = {
                'info': response.get(value),
                'id': id_,
                'timestamp': timestamp,
                'datetime': datetime_,
                'symbol': ExchangeOrder.get('symbol'),
                'order': id_,
                'type': ExchangeOrder.get('type'),
                'side': ExchangeOrder.get('direction'),
                'takerOrMaker': 'None',
                'price': price,
                'amount': amount,
                'cost': cost,
                'fee':
                {
                    'cost': ExchangeOrder.get('fee'),
                    'currency': ExchangeOrder.get('coinSymbol'),
                    'rate': 'None',
                }
                }
            listData.append(tmp)
        return listData

    def fetch_my_trades(self, pageNo=None, pageSize=None):
        # Responce example:
        # [
        #    {
        #       'info': { backend response }, 
        #       'id': 'E161460499516968', 
        #       'timestamp': 1614605187661, 
        #       'datetime': '2021-03-01 15:26:27.661000', 
        #       'symbol': 'BTC/USDT', 
        #       'order': 'E161460499516968', 
        #       'type': 'LIMIT_PRICE', 
        #       'side': 'SELL', 
        #       'takerOrMaker': 'None', (Have no this information inside TPR exchange)
        #       'price': 1.0, 
        #       'amount': 1.0, 
        #       'cost': 1.0, 
        #       'fee': 
        #       {
        #           'cost': 0.001, 
        #           'currency': 'BTC', 
        #           'rate': 'None'  (Have no this information inside TPR exchange)
        #       }
        #    }, 
        #    { ... },
        # ]

        if pageNo is None:
            pageNo = 0
        
        if pageSize is None:
            pageSize = 100

        request = { 'orderId': '',
                    'pageNo': pageNo,
                    'pageSize': pageSize }
        return self.parse_my_trades(self.privatePostExchangeOrderMyTrades(request))

    def handle_errors(self, httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody):
        if response is None:
            return  # fallback to default error handler
        if httpCode == 200:
            if 'code' in response:
                if response['code'] == 0:
                    return
            else:
                return
        # {
        #     "message": "Error text in case when HTTP code is not 200",
        #     ...
        # }
        message = self.safe_string(response, 'message')
        if message is not None:
            feedback = self.id + ' ' + body
            self.throw_exactly_matched_exception(self.exceptions['exact'], message, feedback)
            self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback)
            raise ExchangeError(feedback)  # unknown message

# -*- 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 # 'countries': ['US'], # 'rateLimit': 500, # {"message":"Invalid cost","_links":{"self":{"href":"/orders","templated":false}}} # {"message":"Invalid order ID","_links":{"self":{"href":"/orders/4a151805-d594-4a96-9d64-e3984f2441f7","templated":false}}} # {"message":"Invalid market !","_links":{"self":{"href":"/markets/300/order-book","templated":false}}} # Response example: # { #     'symbol': 'BTC/USDT', #     'info': [...], #     'timestamp': 1615386851976, #     'datetime': '2021-03-10 16:34:11.976000', #     'high': 50.0, #     'low': 1.0, #     'bid': 30.0, #     'bidVolume': 15.0, #     'ask': 40.0, #     'askVolume': 25.0, #     'vwap': 11.0, #     'open': 'None', #     'close': 20.0, #     'last': 20.0, #     'previousClose': 'None', #     'change': 'None', #     'percentage': 'None', #     'average': 'None', #     'baseVolume': 60.0, #     'quoteVolume': 30.0 # } # Response example: # [ #     { #         'symbol': 'BTC/USDT', #         'info': [...], #         'timestamp': 1615386851976, #         'datetime': '2021-03-10 16:34:11.976000', #         'high': 50.0, #         'low': 1.0, #         'bid': 30.0, #         'bidVolume': 15.0, #         'ask': 40.0, #         'askVolume': 25.0, #         'vwap': 11.0, #         'open': 'None', #         'close': 20.0, #         'last': 20.0, #         'previousClose': 'None', #         'change': 'None', #         'percentage': 'None', #         'average': 'None', #         'baseVolume': 60.0, #         'quoteVolume': 30.0 #     }, #     ... # ] # Response example: # { #     'bids': #     [ #        [20.0, 10.0, 'E161538482263642'],   // [price, amount, orderId] #         [30.0, 15.0, 'E161538482271646'] #     ], #     'asks': #     [ #         [40.0, 20.0, 'E161538482278825'], #         [50.0, 25.0, 'E161538482286085'] #     ], #     'timestamp': 1615390711695, #     'datetime': '2021-03-10 17:38:31.695000', #     'nonce': 1615390711695 # } # timeframe variants: # 1m (one minute); # 1h (one hour); # 1d (one day - 24 hours) # 1w (one week - 7 days) # 1mn (one mounth - 30 days) # Response example: # [ #     [ #         1504541580000, // UTC timestamp in milliseconds, integer #         4235.4,        // (O)pen price, float #         4240.6,        // (H)ighest price, float #         4230.0,        // (L)owest price, float #         4230.7,        // (C)losing price, float #         37.72941911    // (V)olume (in terms of the base currency), float #     ], #     ... # ] # RETURN EXAMPLE: # [ #     { #         'id': 'BTC', #         'symbol': 'BTC/USDT', #         'base': 'BTC', #         'quote': 'USDT', #         'baseId': 'BTC', #         'quoteId': 'USDT', #         'type': 'NONE', #         'active': 1, #         'precision': { 'amount': 2, 'price': 2 }, #         'limits': #         { #             'amount': { 'min': 0.0, 'max': 0.0 }, #             'price': { 'min': 0.0, 'max': 0.0 }, #             'cost': { 'min': 0.0, 'max': 0.0 } #         }, #         'taker': 0.001, #         'maker': 0.001, #         'info': {backend response} #     }, #     ... # ] # Check existance of authentication token # Just use empy one in case of an application is not signed in yet # Get URL # Calculate body and content type depending on method type: GET or POST # In case of body is still not assigned just make it empty string # Prepare line for hashing # This hash sum is checked on backend side to verify API user # POST params should not be added as body # Responce examples: # {'status': 'ok'} # or # {'status': 'shutdown', 'reason': 'ExchangeNotAvailable'} # or # {'status': 'shutdown', 'reason': 'Unknown reason'} # Responce example #[ #    { #        'id': 'BTC', #        'code': 'BTC', #        'name': 'BTC', #        'active': True, #        'fee': 0.001, #        'precision': 0, #        'limits': // TPR exchange has no restrictions #        { #            'amount': #            { #                'min': 'None', #                'max': 'None' #            }, #            'price': #            { #                'min': 'None', #                'max': 'None' #            }, #            'cost': #            { #                'min': 'None', #                'max': 'None' #            }, #            'withdraw': #            { #                'min': 1.0, #                'max': 5000.0 #            } #        }, #        'info': { }, #    }, #    ... #] # { #   'orderId':'E161183624377614', #   'memberId':2, #   'type':'LIMIT_PRICE', #   'amount':1000.0, #   'symbol':'BCH/USDT', #   'tradedAmount':1000.0, #   'turnover':1080.0, #   'coinSymbol':'BCH', #   'baseSymbol':'USDT', #   'status':'COMPLETED', #   'latestTradeTimestamp':1611836256242, #   'direction':'SELL', #   'price':1.0, #   'time':1611836243776, #   'completedTime':1611836256242, # }, # Request structure # { #   'symbol': Parameter from method arguments #   'since': Timestamp of first order in list in Unix epoch format #   'limit': Response list size #   'memberId': May be set in params. May be not set #   'status': one of TRADING COMPLETED CANCELED OVERTIMED. May be set in params #   'page': for pagination. In self case limit is size of every page. May be set in params # } # { #     'content': [ #         { #             'orderId':'E161183624377614', #             'memberId':2, #             'type':'LIMIT_PRICE', #             'amount':1000.0, #             'symbol':'BCH/USDT', #             'tradedAmount':1000.0, #             'turnover':1080.0, #             'coinSymbol':'BCH', #             'baseSymbol':'USDT', #             'status':'COMPLETED', #             'direction':'SELL', #             'price':1.0, #             'time':1611836243776, #             'completedTime':1611836256242, #         }, #         ... #     ], #     'totalElements':41, #     'totalPages':3, #     'last':False, #     'size':20, #     'number':1, #     'first':False, #     'numberOfElements':20, #     'sort': [ #         { #             'direction':'DESC', #             'property':'time', #             'ignoreCase':False, #             'nullHandling':'NATIVE', #             'ascending':False, #             'descending':True, #         } #     ] # } # Request structure # { #   'symbol': Parameter from method arguments #   'since': Timestamp of first order in list in Unix epoch format #   'limit': Response list size #   'memberId': May be set in params. May be not set #   'status': one of TRADING COMPLETED CANCELED OVERTIMED. May be set in params #   'page': for pagination. In self case limit is size of every page. May be set in params # } # { #     'content': [ #         { #             'orderId':'E161183624377614', #             'memberId':2, #             'type':'LIMIT_PRICE', #             'amount':1000.0, #             'symbol':'BCH/USDT', #             'tradedAmount':1000.0, #             'turnover':1080.0, #             'coinSymbol':'BCH', #             'baseSymbol':'USDT', #             'status':'COMPLETED', #             'direction':'SELL', #             'price':1.0, #             'time':1611836243776, #             'completedTime':1611836256242, #         }, #         ... #     ], #     'totalElements':41, #     'totalPages':3, #     'last':False, #     'size':20, #     'number':1, #     'first':False, #     'numberOfElements':20, #     'sort': [ #         { #             'direction':'DESC', #             'property':'time', #             'ignoreCase':False, #             'nullHandling':'NATIVE', #             'ascending':False, #             'descending':True, #         } #     ] # } # Request structure # { #   'symbol': Parameter from method arguments #   'since': Timestamp of first order in list in Unix epoch format #   'limit': Response list size #   'memberId': May be set in params. May be not set #   'status': one of TRADING COMPLETED CANCELED OVERTIMED. May be set in params #   'page': for pagination. In self case limit is size of every page. May be set in params # } # { #     'content': [ #         { #             'orderId':'E161183624377614', #             'memberId':2, #             'type':'LIMIT_PRICE', #             'amount':1000.0, #             'symbol':'BCH/USDT', #             'tradedAmount':1000.0, #             'turnover':1080.0, #             'coinSymbol':'BCH', #             'baseSymbol':'USDT', #             'status':'COMPLETED', #             'direction':'SELL', #             'price':1.0, #             'time':1611836243776, #             'completedTime':1611836256242, #         }, #         ... #     ], #     'totalElements':41, #     'totalPages':3, #     'last':False, #     'size':20, #     'number':1, #     'first':False, #     'numberOfElements':20, #     'sort': [ #         { #             'direction':'DESC', #             'property':'time', #             'ignoreCase':False, #             'nullHandling':'NATIVE', #             'ascending':False, #             'descending':True, #         } #     ] # } # If call without params the function returns balance of current user # Returns int or None # Responce example: # [ #    { #       'info': { backend response }, #       'id': 'E161460499516968', #       'timestamp': 1614605187661, #       'datetime': '2021-03-01 15:26:27.661000', #       'symbol': 'BTC/USDT', #       'order': 'E161460499516968', #       'type': 'LIMIT_PRICE', #       'side': 'SELL', #       'takerOrMaker': 'None', (Have no this information inside TPR exchange) #       'price': 1.0, #       'amount': 1.0, #       'cost': 1.0, #       'fee': #       { #           'cost': 0.005, #           'currency': 'BTC', #           'rate': 'None' (Have no this information inside TPR exchange) #       } #    } # ] # Responce example: # [ #    { #       'info': { backend response }, #       'id': 'E161460499516968', #       'timestamp': 1614605187661, #       'datetime': '2021-03-01 15:26:27.661000', #       'symbol': 'BTC/USDT', #       'order': 'E161460499516968', #       'type': 'LIMIT_PRICE', #       'side': 'SELL', #       'takerOrMaker': 'None', (Have no this information inside TPR exchange) #       'price': 1.0, #       'amount': 1.0, #       'cost': 1.0, #       'fee': #       { #           'cost': 0.001, #           'currency': 'BTC', #           'rate': 'None'  (Have no this information inside TPR exchange) #       } #    }, #    { ... }, # ] # fallback to default error handler # { #     "message": "Error text in case when HTTP code is not 200", #     ... # } # unknown message