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

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

from ccxt.async_support.base.exchange import Exchange
import math
from ccxt.base.errors import ExchangeError
from ccxt.base.errors import AuthenticationError
from ccxt.base.errors import PermissionDenied
from ccxt.base.errors import ArgumentsRequired
from ccxt.base.errors import BadRequest
from ccxt.base.errors import InsufficientFunds
from ccxt.base.errors import OrderNotFound
from ccxt.base.errors import DDoSProtection


class stex(Exchange):

    def describe(self):
        return self.deep_extend(super(stex, self).describe(), {
            'id': 'stex',
            'name': 'STEX',  # formerly known as stocks.exchange
            'countries': ['EE'],  # Estonia
            'rateLimit': 500,  # https://help.stex.com/en/articles/2815043-api-3-rate-limits
            'certified': False,
            # new metainfo interface
            'has': {
                'cancelAllOrders': True,
                'cancelOrder': True,
                'CORS': False,
                'createDepositAddress': True,
                'createMarketOrder': False,  # limit orders only
                'createOrder': True,
                'fetchBalance': True,
                'fetchCurrencies': True,
                'fetchDepositAddress': True,
                'fetchDeposits': True,
                'fetchFundingFees': True,
                'fetchMarkets': True,
                'fetchMyTrades': True,
                'fetchOHLCV': True,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'fetchOrderBook': True,
                'fetchOrderTrades': True,
                'fetchTicker': True,
                'fetchTickers': True,
                'fetchTime': True,
                'fetchTrades': True,
                'fetchWithdrawals': True,
                'withdraw': True,
            },
            'version': 'v3',
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/69680782-03fd0b80-10bd-11ea-909e-7f603500e9cc.jpg',
                'api': 'https://api3.stex.com',
                'www': 'https://www.stex.com',
                'doc': [
                    'https://help.stex.com/en/collections/1593608-api-v3-documentation',
                ],
                'fees': 'https://app.stex.com/en/pairs-specification',
                'referral': 'https://app.stex.com?ref=36416021',
            },
            'requiredCredentials': {
                'apiKey': False,
                'secret': False,
                'token': True,
            },
            'timeframes': {
                '1m': '1',
                '5m': '5',
                '30m': '30',
                '1h': '60',
                '4h': '240',
                '12h': '720',
                '1d': '1D',  # default
            },
            'api': {
                'public': {
                    'get': [
                        'currencies',  # Available Currencies
                        'currencies/{currencyId}',  # Get currency info
                        'markets',  # Available markets
                        'pairs-groups',  # Available currency pairs groups(as displayed at stex trading page)
                        'currency_pairs/list/{code}',  # Available currency pairs
                        'currency_pairs/group/{currencyPairGroupId}',  # Available currency pairs for a given group
                        'currency_pairs/{currencyPairId}',  # Get currency pair information
                        'ticker',  # Tickers list for all currency pairs
                        'ticker/{currencyPairId}',  # Ticker for currency pair
                        'trades/{currencyPairId}',  # Trades for given currency pair
                        'orderbook/{currencyPairId}',  # Orderbook for given currency pair
                        'chart/{currencyPairId}/{candlesType}',  # A list of candles for given currency pair
                        'deposit-statuses',  # Available Deposit Statuses
                        'deposit-statuses/{statusId}',  # Get deposit status info
                        'withdrawal-statuses',  # Available Withdrawal Statuses
                        'withdrawal-statuses/{statusId}',  # Get status info
                        'ping',  # Test API is working and get server time
                        'mobile-versions',  # Shows the official mobile applications data
                    ],
                },
                'trading': {
                    'get': [
                        'fees/{currencyPairId}',  # Returns the user's fees for a given currency pair
                        'orders',  # List your currently open orders
                        'orders/{currencyPairId}',  # List your currently open orders for given currency pair
                        'order/{orderId}',  # Get a single order
                    ],
                    'post': [
                        'orders/{currencyPairId}',  # Create new order and put it to the orders processing queue
                    ],
                    'delete': [
                        'orders',  # Delete all active orders
                        'orders/{currencyPairId}',  # Delete active orders for given currency pair
                        'order/{orderId}',  # Cancel order
                    ],
                },
                'reports': {
                    'get': [
                        'orders',  # Get past orders
                        'orders/{orderId}',  # Get specified order details
                        'trades/{currencyPairId}',  # Get a list of user trades according to request parameters
                        'background/{listMode}',  # Get reports list for category
                        'background/{id}',  # Get some report info
                        'background/download/{id}',  # Get file by id
                    ],
                    'post': [
                        'background/create',  # Create new report
                    ],
                    'delete': [
                        'background/{id}',  # Remove report by id
                    ],
                },
                'profile': {
                    'get': [
                        'info',  # Account information
                        'wallets',  # Get a list of user wallets
                        'wallets/{walletId}',  # Single wallet information
                        'wallets/address/{walletId}',  # Get deposit address for given wallet
                        'deposits',  # Get a list of deposits made by user
                        'deposits/{id}',  # Get deposit by id
                        'withdrawals',  # Get a list of withdrawals made by user
                        'withdrawals/{id}',  # Get withdrawal by id
                        'notifications',  # Get notifications
                        'favorite/currency_pairs',  # Get favorite currency pairs
                        'token-scopes',  # Get current token scopes
                    ],
                    'post': [
                        'wallets/burn/{walletId}',  # Burns the given wallet
                        'wallets/{currencyId}',  # Create a wallet for given currency
                        'wallets/address/{walletId}',  # Create new deposit address
                        'withdraw',  # Create withdrawal request
                        'referral/program',  # Create referral program
                        'referral/insert/{code}',  # Insert referral code
                        'referral/bonus_transfer/{currencyId}',  # Transfer referral bonuses balance to main balance for given currency
                    ],
                    'put': [
                        'profile/favorite/currency_pairs/set',  # Set favorite currency pairs
                    ],
                    'delete': [
                        'profile/withdraw/{withdrawalId}',  # Cancel unconfirmed withdrawal
                    ],
                },
                'verification': {
                    'get': [
                        'verification/countries',  # Countries list, beta
                        'verification/stex',  # Get information about your KYC, beta
                    ],
                    'post': [
                        'verification/stex',  # Update information regarding of your KYC verification, beta
                    ],
                },
                'settings': {
                    'get': [
                        'notifications/{event}',  # User event notification settings
                        'notifications',  # User events notification settings
                    ],
                    'put': [
                        'notifications',  # Set notification settings
                        'notifications/set',
                    ],
                },
            },
            'fees': {
                'trading': {
                    'tierBased': False,
                    'percentage': True,
                    'taker': 0.002,
                    'maker': 0.002,
                },
            },
            'commonCurrencies': {
                'BC': 'Bitcoin Confidential',
                'BITS': 'Bitcoinus',
                'BITSW': 'BITS',
                'BHD': 'Bithold',
                'BTH': 'Bithereum',
                'SBTC': 'SBTCT',  # SiamBitcoin
            },
            'options': {
                'parseOrderToPrecision': False,
            },
            'exceptions': {
                'exact': {
                    # {"success":false,"message":"Wrong parameters","errors":{"candleType":["Invalid Candle Type!"]}}
                    # {"success":false,"message":"Wrong parameters","errors":{"time":["timeStart or timeEnd is less then 1"]}}
                    'Wrong parameters': BadRequest,
                    'Unauthenticated.': AuthenticationError,  # {"message":"Unauthenticated."}
                    'Server Error': ExchangeError,  # {"message": "Server Error"}
                    'This feature is only enabled for users verifies by Cryptonomica': PermissionDenied,  # {"success":false,"message":"This feature is only enabled for users verifies by Cryptonomica"}
                    'Too Many Attempts.': DDoSProtection,  # {"message": "Too Many Attempts."}
                },
                'broad': {
                    'Not enough': InsufficientFunds,  # {"success":false,"message":"Not enough  ETH"}
                },
            },
        })

    async def fetch_currencies(self, params={}):
        response = await self.publicGetCurrencies(params)
        #
        #     {
        #         "success":true,
        #         "data":[
        #             {
        #                 "id":1,
        #                 "code":"BTC",
        #                 "name":"Bitcoin",
        #                 "active":true,
        #                 "delisted":false,
        #                 "precision":8,
        #                 "minimum_tx_confirmations":1,
        #                 "minimum_withdrawal_amount":"0.00200000",
        #                 "minimum_deposit_amount":"0.00000000",
        #                 "deposit_fee_currency_id":1,
        #                 "deposit_fee_currency_code":"BTC",
        #                 "deposit_fee_const":"0.00000000",
        #                 "deposit_fee_percent":"0.00000000",
        #                 "withdrawal_fee_currency_id":1,
        #                 "withdrawal_fee_currency_code":"BTC",
        #                 "withdrawal_fee_const":"0.00100000",
        #                 "withdrawal_fee_percent":"0.00000000",
        #                 "block_explorer_url":"https:\/\/blockchain.info\/tx\/",
        #                 "protocol_specific_settings":null
        #             },
        #         ]
        #     }
        #
        result = {}
        currencies = self.safe_value(response, 'data', [])
        for i in range(0, len(currencies)):
            currency = currencies[i]
            id = self.safe_string(currency, 'id')
            numericId = self.safe_integer(currency, 'id')
            # todo: will need to rethink the fees
            # to add support for multiple withdrawal/deposit methods and
            # differentiated fees for each particular method
            code = self.safe_currency_code(self.safe_string(currency, 'code'))
            precision = self.safe_integer(currency, 'precision')
            fee = self.safe_float(currency, 'withdrawal_fee_const')  # todo: redesign
            active = self.safe_value(currency, 'active', True)
            result[code] = {
                'id': id,
                'numericId': numericId,
                'code': code,
                'info': currency,
                'type': None,
                'name': self.safe_string(currency, 'name'),
                'active': active,
                'fee': fee,
                'precision': precision,
                'limits': {
                    'amount': {'min': math.pow(10, -precision), 'max': None},
                    'price': {'min': math.pow(10, -precision), 'max': None},
                    'cost': {'min': None, 'max': None},
                    'deposit': {
                        'min': self.safe_float(currency, 'minimum_deposit_amount'),
                        'max': None,
                    },
                    'withdraw': {
                        'min': self.safe_float(currency, 'minimum_withdrawal_amount'),
                        'max': None,
                    },
                },
            }
        return result

    async def fetch_markets(self, params={}):
        request = {
            'code': 'ALL',
        }
        response = await self.publicGetCurrencyPairsListCode(self.extend(request, params))
        #
        #     {
        #         "success":true,
        #         "data":[
        #             {
        #                 "id":935,
        #                 "currency_id":662,
        #                 "currency_code":"ABET",
        #                 "currency_name":"Altbet",
        #                 "market_currency_id":1,
        #                 "market_code":"BTC",
        #                 "market_name":"Bitcoin",
        #                 "min_order_amount":"0.00000010",
        #                 "min_buy_price":"0.00000001",
        #                 "min_sell_price":"0.00000001",
        #                 "buy_fee_percent":"0.20000000",
        #                 "sell_fee_percent":"0.20000000",
        #                 "active":true,
        #                 "delisted":false,
        #                 "pair_message":"",
        #                 "currency_precision":8,
        #                 "market_precision":8,
        #                 "symbol":"ABET_BTC",
        #                 "group_name":"BTC",
        #                 "group_id":1
        #             }
        #         ]
        #     }
        #
        result = []
        markets = self.safe_value(response, 'data', [])
        for i in range(0, len(markets)):
            market = markets[i]
            id = self.safe_string(market, 'id')
            numericId = self.safe_integer(market, 'id')
            baseId = self.safe_string(market, 'currency_id')
            quoteId = self.safe_string(market, 'market_currency_id')
            baseNumericId = self.safe_integer(market, 'currency_id')
            quoteNumericId = self.safe_integer(market, 'market_currency_id')
            base = self.safe_currency_code(self.safe_string(market, 'currency_code'))
            quote = self.safe_currency_code(self.safe_string(market, 'market_code'))
            symbol = base + '/' + quote
            precision = {
                'amount': self.safe_integer(market, 'currency_precision'),
                'price': self.safe_integer(market, 'market_precision'),
            }
            active = self.safe_value(market, 'active')
            minBuyPrice = self.safe_float(market, 'min_buy_price')
            minSellPrice = self.safe_float(market, 'min_sell_price')
            minPrice = max(minBuyPrice, minSellPrice)
            buyFee = self.safe_float(market, 'buy_fee_percent') / 100
            sellFee = self.safe_float(market, 'sell_fee_percent') / 100
            fee = max(buyFee, sellFee)
            result.append({
                'id': id,
                'numericId': numericId,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'baseId': baseId,
                'quoteId': quoteId,
                'baseNumericId': baseNumericId,
                'quoteNumericId': quoteNumericId,
                'info': market,
                'active': active,
                'maker': fee,
                'taker': fee,
                'precision': precision,
                'limits': {
                    'amount': {
                        'min': self.safe_float(market, 'min_order_amount'),
                        'max': None,
                    },
                    'price': {'min': minPrice, 'max': None},
                    'cost': {
                        'min': None,
                        'max': None,
                    },
                },
            })
        return result

    async def fetch_ticker(self, symbol, params={}):
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'currencyPairId': market['id'],
        }
        response = await self.publicGetTickerCurrencyPairId(self.extend(request, params))
        #
        #     {
        #         "success": True,
        #         "data": {
        #             "id": 2,
        #             "amount_multiplier": 1,
        #             "currency_code": "ETH",
        #             "market_code": "BTC",
        #             "currency_name": "Ethereum",
        #             "market_name": "Bitcoin",
        #             "symbol": "ETH_BTC",
        #             "group_name": "BTC",
        #             "group_id": 1,
        #             "ask": "0.02069998",
        #             "bid": "0.02028622",
        #             "last": "0.02049224",
        #             "open": "0.02059605",
        #             "low": "0.01977744",
        #             "high": "0.02097005",
        #             "volume": "480.43248971",
        #             "volumeQuote": "23491.29826130",
        #             "count": "7384",
        #             "fiatsRate": {
        #                 "USD": 7230.86,
        #                 "EUR": 6590.79,
        #                 "UAH": 173402,
        #                 "AUD": 10595.51,
        #                 "IDR": 101568085,
        #                 "CNY": 50752,
        #                 "KRW": 8452295,
        #                 "JPY": 784607,
        #                 "VND": 167315119,
        #                 "INR": 517596,
        #                 "GBP": 5607.25,
        #                 "CAD": 9602.63,
        #                 "BRL": 30472,
        #                 "RUB": 460718
        #             },
        #             "timestamp": 1574698235601
        #         }
        #     }
        #
        ticker = self.safe_value(response, 'data', {})
        return self.parse_ticker(ticker, market)

    async def fetch_time(self, params={}):
        response = await self.publicGetPing(params)
        #
        #     {
        #         "success": True,
        #         "data": {
        #             "server_datetime": {
        #                 "date": "2019-01-22 15:13:34.233796",
        #                 "timezone_type": 3,
        #                 "timezone": "UTC"
        #             },
        #             "server_timestamp": 1548170014
        #         }
        #     }
        #
        data = self.safe_value(response, 'data', {})
        serverDatetime = self.safe_value(data, 'server_datetime', {})
        return self.parse8601(self.safe_string(serverDatetime, 'date'))

    async def fetch_order_book(self, symbol, limit=None, params={}):
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'currencyPairId': market['id'],
        }
        if limit is not None:
            request['limit_bids'] = limit  # returns all if set to 0, default 100
            request['limit_asks'] = limit  # returns all if set to 0, default 100
        response = await self.publicGetOrderbookCurrencyPairId(self.extend(request, params))
        #
        #     {
        #         "success": True,
        #         "data": {
        #             "ask": [
        #                 {"currency_pair_id": 2, "amount": "2.17865373", "price": "0.02062917", "amount2": "0.04494382", "count": 1, "cumulative_amount": 2.17865373},
        #                 {"currency_pair_id": 2, "amount": "2.27521743", "price": "0.02062918", "amount2": "0.04693587", "count": 1, "cumulative_amount": 4.45387116},
        #                 {"currency_pair_id": 2, "amount": "1.26980049", "price": "0.02063170", "amount2": "0.02619814", "count": 1, "cumulative_amount": 5.72367165},
        #             ],
        #             "bid": [
        #                 {"currency_pair_id": 2, "amount": "0.00978005", "price": "0.02057000", "amount2": "0.00020118", "count": 1, "cumulative_amount": 0.00978005},
        #                 {"currency_pair_id": 2, "amount": "0.00500000", "price": "0.02056000", "amount2": "0.00010280", "count": 1, "cumulative_amount": 0.01478005},
        #                 {"currency_pair_id": 2, "amount": "0.77679882", "price": "0.02054001", "amount2": "0.01595546", "count": 1, "cumulative_amount": 0.79157887},
        #             ],
        #             "ask_total_amount": 2555.749174609999,
        #             "bid_total_amount": 29.180037330000005
        #         }
        #     }
        #
        orderbook = self.safe_value(response, 'data', {})
        return self.parse_order_book(orderbook, None, 'bid', 'ask', 'price', 'amount')

    def parse_ticker(self, ticker, market=None):
        #
        #     {
        #         "id": 2,
        #         "amount_multiplier": 1,
        #         "currency_code": "ETH",
        #         "market_code": "BTC",
        #         "currency_name": "Ethereum",
        #         "market_name": "Bitcoin",
        #         "symbol": "ETH_BTC",
        #         "group_name": "BTC",
        #         "group_id": 1,
        #         "ask": "0.02069998",
        #         "bid": "0.02028622",
        #         "last": "0.02049224",
        #         "open": "0.02059605",
        #         "low": "0.01977744",
        #         "high": "0.02097005",
        #         "volume": "480.43248971",
        #         "volumeQuote": "23491.29826130",
        #         "count": "7384",
        #         "fiatsRate": {
        #             "USD": 7230.86,
        #             "EUR": 6590.79,
        #             "UAH": 173402,
        #             "AUD": 10595.51,
        #             "IDR": 101568085,
        #             "CNY": 50752,
        #             "KRW": 8452295,
        #             "JPY": 784607,
        #             "VND": 167315119,
        #             "INR": 517596,
        #             "GBP": 5607.25,
        #             "CAD": 9602.63,
        #             "BRL": 30472,
        #             "RUB": 460718
        #         },
        #         "timestamp": 1574698235601
        #     }
        #
        timestamp = self.safe_integer(ticker, 'timestamp')
        marketId = self.safe_string_2(ticker, 'id', 'symbol')
        symbol = self.safe_symbol(marketId, market, '_')
        last = self.safe_float(ticker, 'last')
        open = self.safe_float(ticker, 'open')
        change = None
        percentage = None
        if last is not None:
            if (open is not None) and (open > 0):
                change = last - open
                percentage = ((100 / open) * last) - 100
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'high': self.safe_float(ticker, 'high'),
            'low': self.safe_float(ticker, 'low'),
            'bid': self.safe_float(ticker, 'bid'),
            'bidVolume': None,
            'ask': self.safe_float(ticker, 'ask'),
            'askVolume': None,
            'vwap': None,
            'open': open,
            'close': last,
            'last': last,
            'previousClose': None,  # previous day close
            'change': change,
            'percentage': percentage,
            'average': None,
            'baseVolume': self.safe_float(ticker, 'volumeQuote'),
            'quoteVolume': self.safe_float(ticker, 'volume'),
            'info': ticker,
        }

    def parse_tickers(self, tickers, symbols=None):
        result = []
        for i in range(0, len(tickers)):
            result.append(self.parse_ticker(tickers[i]))
        return self.filter_by_array(result, 'symbol', symbols)

    async def fetch_tickers(self, symbols=None, params={}):
        await self.load_markets()
        response = await self.publicGetTicker(params)
        #
        #     {
        #         "success":true,
        #         "data":[
        #             {
        #                 "id":262,
        #                 "amount_multiplier":1,
        #                 "currency_code":"ARDR",
        #                 "market_code":"BTC",
        #                 "currency_name":"ARDOR",
        #                 "market_name":"Bitcoin",
        #                 "symbol":"ARDR_BTC",
        #                 "group_name":"BTC",
        #                 "group_id":1,
        #                 "ask":"0.00000630",
        #                 "bid":"0.00000613",
        #                 "last":"0.00000617",
        #                 "open":"0.00000620",
        #                 "low":"0.00000614",
        #                 "high":"0.00000630",
        #                 "volume":"30.37795305",
        #                 "volumeQuote":"4911487.01996544",
        #                 "count":"710",
        #                 "fiatsRate":{
        #                     "USD":7230.86,
        #                     "EUR":6590.79,
        #                     "UAH":173402,
        #                     "AUD":10744.52,
        #                     "IDR":101568085,
        #                     "CNY":50752,
        #                     "KRW":8452295,
        #                     "JPY":784607,
        #                     "VND":167315119,
        #                     "INR":517596,
        #                     "GBP":5607.25,
        #                     "CAD":9602.63,
        #                     "BRL":30472,
        #                     "RUB":467358
        #                 },
        #                 "timestamp":1574698617304,
        #                 "group_position":1
        #             },
        #         ]
        #     }
        #
        tickers = self.safe_value(response, 'data', [])
        return self.parse_tickers(tickers, symbols)

    def parse_ohlcv(self, ohlcv, market=None):
        #
        #     {
        #         "time": 1566086400000,
        #         "close": 0.01895,
        #         "open": 0.01812427,
        #         "high": 0.0191588,
        #         "low": 0.01807001,
        #         "volume": 2588.597813750006
        #     }
        #
        return [
            self.safe_integer(ohlcv, 'time'),
            self.safe_float(ohlcv, 'open'),
            self.safe_float(ohlcv, 'high'),
            self.safe_float(ohlcv, 'low'),
            self.safe_float(ohlcv, 'close'),
            self.safe_float(ohlcv, 'volume'),
        ]

    async def fetch_ohlcv(self, symbol, timeframe='1d', since=None, limit=None, params={}):
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'currencyPairId': market['id'],
            'candlesType': self.timeframes[timeframe],  # default 1d
            # 'timeStart': 1574709092,  # unix timestamp in seconds, required
            # 'timeEnd': 1574709092,  # unix timestamp in seconds, required
            # 'limit': 100,  # default 100, optional
            # 'offset' 100,  # optional, pagination within timerange
        }
        if limit is None:
            limit = 100
        else:
            request['limit'] = limit
        duration = self.parse_timeframe(timeframe)
        timerange = limit * duration
        if since is None:
            request['timeEnd'] = self.seconds()
            request['timeStart'] = request['timeEnd'] - timerange
        else:
            request['timeStart'] = int(since / 1000)
            request['timeEnd'] = self.sum(request['timeStart'], timerange)
        response = await self.publicGetChartCurrencyPairIdCandlesType(self.extend(request, params))
        #
        #     {
        #         "success": True,
        #         "data": [
        #             {
        #                 "time": 1566086400000,
        #                 "close": 0.01895,
        #                 "open": 0.01812427,
        #                 "high": 0.0191588,
        #                 "low": 0.01807001,
        #                 "volume": 2588.597813750006
        #             },
        #         ]
        #     }
        #
        data = self.safe_value(response, 'data', [])
        return self.parse_ohlcvs(data, market, timeframe, since, limit)

    def parse_trade(self, trade, market=None):
        #
        # public fetchTrades
        #
        #     {
        #         "id": 35989317,
        #         "price": "0.02033813",
        #         "amount": "3.60000000",
        #         "type": "BUY",
        #         "timestamp": "1574713503"
        #     }
        #
        # private fetchMyTrades, fetchClosedOrder, fetchOrderTrades
        #
        #     {
        #         "id": 658745,
        #         "buy_order_id": 6587453,
        #         "sell_order_id": 6587459,
        #         "price": 0.012285,
        #         "amount": 6.35,
        #         "trade_type": "SELL",
        #         "timestamp": "1538737692"
        #     }
        #
        id = self.safe_string(trade, 'id')
        timestamp = self.safe_timestamp(trade, 'timestamp')
        price = self.safe_float(trade, 'price')
        amount = self.safe_float(trade, 'amount')
        cost = None
        if (price is not None) and (amount is not None):
            cost = price * amount
        symbol = None
        if (symbol is None) and (market is not None):
            symbol = market['symbol']
        side = self.safe_string_lower_2(trade, 'type', 'trade_type')
        return {
            'info': trade,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': symbol,
            'id': id,
            'order': None,
            'type': None,
            'takerOrMaker': None,
            'side': side,
            'price': price,
            'amount': amount,
            'cost': cost,
            'fee': None,
        }

    async def fetch_trades(self, symbol, since=None, limit=None, params={}):
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'currencyPairId': market['id'],
            # 'sort': 'ASC',  # ASC or DESC, default DESC
            # 'from': 1574709092,  # unix timestamp, optional
            # 'till': 1574709092,  # unix timestamp, optional
            # 'limit': 100,  # default 100, optional
            # 'offset': 100,  # optional
        }
        if limit is not None:
            request['limit'] = limit  # currently limited to 100 or fewer
        if since is not None:
            request['sort'] = 'ASC'  # needed to make the from param work
            request['from'] = int(since / 1000)
        response = await self.publicGetTradesCurrencyPairId(self.extend(request, params))
        #
        #     {
        #         "success": True,
        #         "data": [
        #             {
        #                 "id": 35989317,
        #                 "price": "0.02033813",
        #                 "amount": "3.60000000",
        #                 "type": "BUY",
        #                 "timestamp": "1574713503"
        #             },
        #         ]
        #     }
        #
        trades = self.safe_value(response, 'data', [])
        return self.parse_trades(trades, market, since, limit)

    async def fetch_balance(self, params={}):
        await self.load_markets()
        # await self.load_accounts()
        response = await self.profileGetWallets(params)
        #
        #     {
        #         "success": True,
        #         "data": [
        #             {
        #                 "id": null,
        #                 "currency_id": 665,
        #                 "delisted": False,
        #                 "disabled": False,
        #                 "disable_deposits": False,
        #                 "currency_code": "ORM",
        #                 "currency_name": "Orium",
        #                 "currency_type_id": 5,
        #                 "balance": "0",
        #                 "frozen_balance": "0",
        #                 "bonus_balance": "0",
        #                 "total_balance": "0",
        #                 "protocol_specific_settings": null,
        #                 "rates": {"BTC": "0.00000000020", "USD": "0.00000147"},
        #             },
        #             {
        #                 "id": null,
        #                 "currency_id": 272,
        #                 "delisted": False,
        #                 "disabled": False,
        #                 "disable_deposits": False,
        #                 "currency_code": "USDT",
        #                 "currency_name": "TetherUSD",
        #                 "currency_type_id": 23,
        #                 "balance": "0",
        #                 "frozen_balance": "0",
        #                 "bonus_balance": "0",
        #                 "total_balance": "0",
        #                 "protocol_specific_settings": [
        #                     {"protocol_name": "OMNI", "protocol_id": 10, "active": True, "withdrawal_fee_currency_id": 272, "withdrawal_fee_const": 10, "withdrawal_fee_percent": 0, "block_explorer_url": "https://omniexplorer.info/search/"},
        #                     {"protocol_name": "ERC20", "protocol_id": 5, "active": True, "withdrawal_fee_const": 1.2, "withdrawal_fee_percent": 0, "block_explorer_url": "https://etherscan.io/tx/"},
        #                     {"protocol_name": "TRON", "protocol_id": 24, "active": True, "withdrawal_fee_currency_id": 272, "withdrawal_fee_const": 0.2, "withdrawal_fee_percent": 0, "block_explorer_url": "https://tronscan.org/#/transaction/"}
        #                 ],
        #                 "rates": {"BTC": "0.00013893", "USD": "1"},
        #             },
        #         ]
        #     }
        #
        result = {'info': response}
        balances = self.safe_value(response, 'data', [])
        for i in range(0, len(balances)):
            balance = balances[i]
            code = self.safe_currency_code(self.safe_string(balance, 'currency_id'))
            account = self.account()
            account['free'] = self.safe_float(balance, 'balance')
            account['used'] = self.safe_float(balance, 'frozen_balance')
            result[code] = account
        return self.parse_balance(result)

    def parse_order_status(self, status):
        statuses = {
            'PROCESSING': 'open',
            'PENDING': 'open',
            'PARTIAL': 'open',
            'FINISHED': 'closed',
            'CANCELLED': 'canceled',
        }
        return self.safe_string(statuses, status, status)

    def parse_order(self, order, market=None):
        #
        # createOrder, fetchOpenOrders, fetchClosedOrders, cancelOrder, fetchOrder, fetchClosedOrder
        #
        #     {
        #         "id": 828680665,
        #         "currency_pair_id": 1,
        #         "currency_pair_name": "NXT_BTC",
        #         "price": "0.011384",
        #         "trigger_price": 0.011385,
        #         "initial_amount": "13.942",
        #         "processed_amount": "3.724",  # missing in fetchClosedOrder
        #         "type": "SELL",
        #         "original_type": "STOP_LIMIT_SELL",
        #         "created": "2019-01-17 10:14:48",
        #         "timestamp": "1547720088",
        #         "status": "PARTIAL"
        #         # fetchClosedOrder only
        #         "trades": [
        #             {
        #                 "id": 658745,
        #                 "buy_order_id": 658745,
        #                 "sell_order_id": 828680665,
        #                 "price": 0.012285,
        #                 "amount": 6.35,
        #                 "trade_type": "SELL",
        #                 "timestamp": "1538737692"
        #             }
        #         ],
        #         # fetchClosedOrder only
        #         "fees": [
        #             {
        #                 "id": 1234567,
        #                 "currency_id": 1,
        #                 "amount": 0.00025,
        #                 "timestamp": "1548149238"
        #             }
        #         ]
        #     }
        #
        id = self.safe_string(order, 'id')
        status = self.parse_order_status(self.safe_string(order, 'status'))
        marketId = self.safe_string_2(order, 'currency_pair_id', 'currency_pair_name')
        symbol = self.safe_symbol(marketId, market, '_')
        timestamp = self.safe_timestamp(order, 'timestamp')
        price = self.safe_float(order, 'price')
        amount = self.safe_float(order, 'initial_amount')
        filled = self.safe_float(order, 'processed_amount')
        remaining = None
        cost = None
        if filled is not None:
            if amount is not None:
                remaining = amount - filled
                if self.options['parseOrderToPrecision']:
                    remaining = float(self.amount_to_precision(symbol, remaining))
                remaining = max(remaining, 0.0)
            if price is not None:
                if cost is None:
                    cost = price * filled
        type = self.safe_string(order, 'original_type')
        if (type == 'BUY') or (type == 'SELL'):
            type = None
        side = self.safe_string_lower(order, 'type')
        rawTrades = self.safe_value(order, 'trades')
        trades = None
        if rawTrades is not None:
            trades = self.parse_trades(rawTrades, market, None, None, {
                'symbol': symbol,
                'order': id,
            })
        stopPrice = self.safe_float(order, 'trigger_price')
        result = {
            'info': order,
            'id': id,
            'clientOrderId': None,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': None,
            'symbol': symbol,
            'type': type,
            'timeInForce': None,
            'postOnly': None,
            'side': side,
            'price': price,
            'stopPrice': stopPrice,
            'amount': amount,
            'cost': cost,
            'average': None,
            'filled': filled,
            'remaining': remaining,
            'status': status,
            'trades': trades,
        }
        fees = self.safe_value(order, 'fees')
        if fees is None:
            result['fee'] = None
        else:
            numFees = len(fees)
            if numFees > 0:
                result['fees'] = []
                for i in range(0, len(fees)):
                    feeCost = self.safe_float(fees[i], 'amount')
                    if feeCost is not None:
                        feeCurrencyId = self.safe_string(fees[i], 'currency_id')
                        feeCurrencyCode = self.safe_currency_code(feeCurrencyId)
                        result['fees'].append({
                            'cost': feeCost,
                            'currency': feeCurrencyCode,
                        })
            else:
                result['fee'] = None
        return result

    async def create_order(self, symbol, type, side, amount, price=None, params={}):
        if type == 'market':
            raise ExchangeError(self.id + ' createOrder allows limit orders only')
        await self.load_markets()
        market = self.market(symbol)
        if type == 'limit':
            type = side
        request = {
            'currencyPairId': market['id'],
            'type': type.upper(),  # 'BUY', 'SELL', 'STOP_LIMIT_BUY', 'STOP_LIMIT_SELL'
            'amount': float(self.amount_to_precision(symbol, amount)),  # required
            'price': float(self.price_to_precision(symbol, price)),  # required
            # 'trigger_price': 123.45  # required for STOP_LIMIT_BUY or STOP_LIMIT_SELL
        }
        response = await self.tradingPostOrdersCurrencyPairId(self.extend(request, params))
        #
        #     {
        #         "success": True,
        #         "data": {
        #             "id": 828680665,
        #             "currency_pair_id": 1,
        #             "currency_pair_name": "NXT_BTC",
        #             "price": "0.011384",
        #             "trigger_price": 0.011385,
        #             "initial_amount": "13.942",
        #             "processed_amount": "3.724",
        #             "type": "SELL",
        #             "original_type": "STOP_LIMIT_SELL",
        #             "created": "2019-01-17 10:14:48",
        #             "timestamp": "1547720088",
        #             "status": "PARTIAL"
        #         }
        #     }
        #
        data = self.safe_value(response, 'data', {})
        return self.parse_order(data, market)

    async def fetch_order(self, id, symbol=None, params={}):
        await self.load_markets()
        request = {
            'orderId': id,
        }
        response = await self.tradingGetOrderOrderId(self.extend(request, params))
        #
        #     {
        #         "success": True,
        #         "data": {
        #             "id": 828680665,
        #             "currency_pair_id": 1,
        #             "currency_pair_name": "NXT_BTC",
        #             "price": "0.011384",
        #             "trigger_price": 0.011385,
        #             "initial_amount": "13.942",
        #             "processed_amount": "3.724",
        #             "type": "SELL",
        #             "original_type": "STOP_LIMIT_SELL",
        #             "created": "2019-01-17 10:14:48",
        #             "timestamp": "1547720088",
        #             "status": "PARTIAL"
        #         }
        #     }
        #
        data = self.safe_value(response, 'data', {})
        market = None
        if symbol is not None:
            market = self.market(symbol)
        return self.parse_order(data, market)

    async def fetch_closed_order(self, id, symbol=None, params={}):
        await self.load_markets()
        request = {
            'orderId': id,
        }
        response = await self.reportsGetOrdersOrderId(self.extend(request, params))
        #
        #     {
        #         "success": True,
        #         "data": {
        #             "id": 5478965,
        #             "currency_pair_id": 1,
        #             "currency_pair_name": "NXT_BTC",
        #             "price": "0.00013800",
        #             "initial_amount": "1.00000000",
        #             "type": "BUY",
        #             "created": "2019-01-22 09:27:17",
        #             "timestamp": 1548149237,
        #             "status": "FINISHED",
        #             "trades": [
        #                 {
        #                     "id": 658745,
        #                     "buy_order_id": 6587453,
        #                     "sell_order_id": 6587459,
        #                     "price": 0.012285,
        #                     "amount": 6.35,
        #                     "trade_type": "SELL",
        #                     "timestamp": "1538737692"
        #                 }
        #             ],
        #             "fees": [
        #                 {
        #                     "id": 1234567,
        #                     "currency_id": 1,
        #                     "amount": 0.00025,
        #                     "timestamp": "1548149238"
        #                 }
        #             ]
        #         }
        #     }
        #
        data = self.safe_value(response, 'data', {})
        market = None
        if symbol is not None:
            market = self.market(symbol)
        return self.parse_order(data, market)

    async def fetch_order_trades(self, id, symbol=None, since=None, limit=None, params={}):
        order = await self.fetch_closed_order(id, symbol, params)
        return order['trades']

    async def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
        await self.load_markets()
        market = None
        method = 'tradingGetOrders'
        request = {
            # 'limit': 100,  # default 100
            # 'offset': 100,
        }
        if symbol is not None:
            method = 'tradingGetOrdersCurrencyPairId'
            market = self.market(symbol)
            request['currencyPairId'] = market['id']
        if limit is not None:
            request['limit'] = limit
        response = await getattr(self, method)(self.extend(request, params))
        #
        #     {
        #         "success": True,
        #         "data": [
        #             {
        #                 "id": 828680665,
        #                 "currency_pair_id": 1,
        #                 "currency_pair_name": "NXT_BTC",
        #                 "price": "0.011384",
        #                 "trigger_price": 0.011385,
        #                 "initial_amount": "13.942",
        #                 "processed_amount": "3.724",
        #                 "type": "SELL",
        #                 "original_type": "STOP_LIMIT_SELL",
        #                 "created": "2019-01-17 10:14:48",
        #                 "timestamp": "1547720088",
        #                 "status": "PARTIAL"
        #             }
        #         ]
        #     }
        #
        data = self.safe_value(response, 'data', [])
        return self.parse_orders(data, market, since, limit)

    async def cancel_order(self, id, symbol=None, params={}):
        await self.load_markets()
        request = {
            'orderId': id,
        }
        response = await self.tradingDeleteOrderOrderId(self.extend(request, params))
        #
        #     {
        #         "success": True,
        #         "data": {
        #             "put_into_processing_queue": [
        #                 {
        #                     "id": 828680665,
        #                     "currency_pair_id": 1,
        #                     "currency_pair_name": "NXT_BTC",
        #                     "price": "0.011384",
        #                     "trigger_price": 0.011385,
        #                     "initial_amount": "13.942",
        #                     "processed_amount": "3.724",
        #                     "type": "SELL",
        #                     "original_type": "STOP_LIMIT_SELL",
        #                     "created": "2019-01-17 10:14:48",
        #                     "timestamp": "1547720088",
        #                     "status": "PARTIAL"
        #                 }
        #             ],
        #             "not_put_into_processing_queue": [
        #                 {
        #                     "id": 828680665,
        #                     "currency_pair_id": 1,
        #                     "currency_pair_name": "NXT_BTC",
        #                     "price": "0.011384",
        #                     "trigger_price": 0.011385,
        #                     "initial_amount": "13.942",
        #                     "processed_amount": "3.724",
        #                     "type": "SELL",
        #                     "original_type": "STOP_LIMIT_SELL",
        #                     "created": "2019-01-17 10:14:48",
        #                     "timestamp": "1547720088",
        #                     "status": "PARTIAL"
        #                 }
        #             ],
        #             "message": "string"
        #         }
        #     }
        #
        data = self.safe_value(response, 'data', {})
        acceptedOrders = self.safe_value(data, 'put_into_processing_queue', [])
        rejectedOrders = self.safe_value(data, 'not_put_into_processing_queue', [])
        numAcceptedOrders = len(acceptedOrders)
        numRejectedOrders = len(rejectedOrders)
        if numAcceptedOrders < 1:
            if numRejectedOrders < 1:
                raise OrderNotFound(self.id + ' cancelOrder received an empty response: ' + self.json(response))
            else:
                return self.parse_order(rejectedOrders[0])
        else:
            if numRejectedOrders < 1:
                return self.parse_order(acceptedOrders[0])
            else:
                raise OrderNotFound(self.id + ' cancelOrder received an empty response: ' + self.json(response))

    async def cancel_all_orders(self, symbol=None, params={}):
        await self.load_markets()
        request = {}
        method = 'tradingDeleteOrders'
        if symbol is not None:
            market = self.market(symbol)
            request['currencyPairId'] = market['id']
            method = 'tradingDeleteOrdersCurrencyPairId'
        response = await getattr(self, method)(self.extend(request, params))
        #
        #     {
        #         "success":true,
        #         "data":{
        #             "put_into_processing_queue":[],
        #             "not_put_into_processing_queue":[],
        #             "message":"Orders operations are handled in processing queue, therefore cancelling is not immediate."
        #         }
        #     }
        #
        return response

    async def fetch_my_trades(self, symbol=None, since=None, limit=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchMyTrades() requires a symbol argument')
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'currencyPairId': market['id'],
            # 'timeStart': '2019-11-26T19:54:55.901Z',  # datetime in iso format
            # 'timeEnd': '2019-11-26T19:54:55.901Z',  # datetime in iso format
            # 'limit': 100,  # default 100
            # 'offset': 100,
        }
        if since is not None:
            request['timeStart'] = self.iso8601(since)
        if limit is not None:
            request['limit'] = limit
        response = await self.reportsGetTradesCurrencyPairId(self.extend(request, params))
        #
        #     {
        #         "success": True,
        #         "data": [
        #             {
        #                 "id": 658745,
        #                 "buy_order_id": 6587453,
        #                 "sell_order_id": 6587459,
        #                 "price": 0.012285,
        #                 "amount": 6.35,
        #                 "trade_type": "SELL",
        #                 "timestamp": "1538737692"
        #             }
        #         ]
        #     }
        #
        trades = self.safe_value(response, 'data', [])
        return self.parse_trades(trades, market, since, limit)

    async def create_deposit_address(self, code, params={}):
        await self.load_markets()
        currency = self.currency(code)
        request = {
            'currencyId': currency['id'],
            # Default value is the value that represents legacy protocol.
            # In case of USDT it is 10 as Tether OMNI was the default previously.
            # The list of protocols can be obtained from the /public/currencies/{currencyId}
            # 'protocol_id': 10,
        }
        response = await self.profilePostWalletsCurrencyId(self.extend(request, params))
        #
        #     {
        #         "success": True,
        #         "data": {
        #             "id": 45875,
        #             "currency_id": 1,
        #             "delisted": False,
        #             "disabled": False,
        #             "disable_deposits": False,
        #             "code": "BTC",
        #             "balance": "0.198752",
        #             "frozen_balance": "1.5784",
        #             "bonus_balance": "0.000",
        #             "deposit_address": {
        #                 "address": "0X12WERTYUIIJHGFVBNMJHGDFGHJ765SDFGHJ",
        #                 "address_name": "Address",
        #                 "additional_address_parameter": "qwertyuiopasdfghjkl",
        #                 "additional_address_parameter_name": "Destination Tag",
        #                 "notification": "",
        #                 "protocol_id": 10,
        #                 "protocol_name": "Tether OMNI",
        #                 "supports_new_address_creation": False
        #                 },
        #             "multi_deposit_addresses": [
        #                 {
        #                     "address": "0X12WERTYUIIJHGFVBNMJHGDFGHJ765SDFGHJ",
        #                     "address_name": "Address",
        #                     "additional_address_parameter": "qwertyuiopasdfghjkl",
        #                     "additional_address_parameter_name": "Destination Tag",
        #                     "notification": "",
        #                     "protocol_id": 10,
        #                     "protocol_name": "Tether OMNI",
        #                     "supports_new_address_creation": False
        #                 }
        #             ],
        #             "withdrawal_additional_field_name": "Payment ID(optional)",
        #             "rates": {"BTC": 0.000001},
        #             "protocol_specific_settings": [
        #                 {
        #                     "protocol_name": "Tether OMNI",
        #                     "protocol_id": 10,
        #                     "active": True,
        #                     "withdrawal_fee_currency_id": 1,
        #                     "withdrawal_fee_const": 0.002,
        #                     "withdrawal_fee_percent": 0,
        #                     "block_explorer_url": "https://omniexplorer.info/search/"
        #                 }
        #             ]
        #         }
        #     }
        #
        data = self.safe_value(response, 'data', {})
        depositAddress = self.safe_value(data, 'deposit_address', {})
        address = self.safe_string(depositAddress, 'address')
        tag = self.safe_string(depositAddress, 'additional_address_parameter')
        self.check_address(address)
        return {
            'currency': code,
            'address': address,
            'tag': tag,
            'info': response,
        }

    async def fetch_deposit_address(self, code, params={}):
        await self.load_markets()
        balance = await self.fetch_balance()
        wallets = self.safe_value(balance['info'], 'data', [])
        walletsByCurrencyId = self.index_by(wallets, 'currency_id')
        currency = self.currency(code)
        wallet = self.safe_value(walletsByCurrencyId, currency['id'])
        if wallet is None:
            raise ExchangeError(self.id + ' fetchDepositAddress() could not find the wallet id for currency code ' + code + ', try to call createDepositAddress() first')
        walletId = self.safe_integer(wallet, 'id')
        if walletId is None:
            raise ExchangeError(self.id + ' fetchDepositAddress() could not find the wallet id for currency code ' + code + ', try to call createDepositAddress() first')
        request = {
            'walletId': walletId,
        }
        response = await self.profileGetWalletsWalletId(self.extend(request, params))
        #
        #     {
        #         "success": True,
        #         "data": {
        #             "id": 45875,
        #             "currency_id": 1,
        #             "delisted": False,
        #             "disabled": False,
        #             "disable_deposits": False,
        #             "code": "BTC",
        #             "balance": "0.198752",
        #             "frozen_balance": "1.5784",
        #             "bonus_balance": "0.000",
        #             "deposit_address": {
        #                 "address": "0X12WERTYUIIJHGFVBNMJHGDFGHJ765SDFGHJ",
        #                 "address_name": "Address",
        #                 "additional_address_parameter": "qwertyuiopasdfghjkl",
        #                 "additional_address_parameter_name": "Destination Tag",
        #                 "notification": "",
        #                 "protocol_id": 10,
        #                 "protocol_name": "Tether OMNI",
        #                 "supports_new_address_creation": False
        #             },
        #             "multi_deposit_addresses": [
        #                 {
        #                     "address": "0X12WERTYUIIJHGFVBNMJHGDFGHJ765SDFGHJ",
        #                     "address_name": "Address",
        #                     "additional_address_parameter": "qwertyuiopasdfghjkl",
        #                     "additional_address_parameter_name": "Destination Tag",
        #                     "notification": "",
        #                     "protocol_id": 10,
        #                     "protocol_name": "Tether OMNI",
        #                     "supports_new_address_creation": False
        #                 }
        #             ],
        #             "withdrawal_additional_field_name": "Payment ID(optional)",
        #             "rates": {"BTC": 0.000001},
        #             "protocol_specific_settings": [
        #                 {
        #                     "protocol_name": "Tether OMNI",
        #                     "protocol_id": 10,
        #                     "active": True,
        #                     "withdrawal_fee_currency_id": 1,
        #                     "withdrawal_fee_const": 0.002,
        #                     "withdrawal_fee_percent": 0,
        #                     "block_explorer_url": "https://omniexplorer.info/search/"
        #                 }
        #             ]
        #         }
        #     }
        #
        data = self.safe_value(response, 'data', [])
        depositAddress = self.safe_value(data, 'deposit_address', {})
        address = self.safe_string(depositAddress, 'address')
        tag = self.safe_string(depositAddress, 'additional_address_parameter')
        self.check_address(address)
        return {
            'currency': code,
            'address': address,
            'tag': tag,
            'info': response,
        }

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        url = self.urls['api'] + '/' + api + '/' + self.implode_params(path, params)
        query = self.omit(params, self.extract_params(path))
        if api == 'public':
            if query:
                url += '?' + self.urlencode(query)
        else:
            self.check_required_credentials()
            headers = {
                'Authorization': 'Bearer ' + self.token,
            }
            if method == 'GET' or method == 'DELETE':
                if query:
                    url += '?' + self.urlencode(query)
            else:
                body = self.json(query)
                if query:
                    headers['Content-Type'] = 'application/json'
        return {'url': url, 'method': method, 'body': body, 'headers': headers}

    def parse_transaction_status(self, status):
        statuses = {
            'processing': 'pending',
            'checking by system': 'pending',
            'hodl': 'pending',
            'amount too low': 'failed',
            'not confirmed': 'pending',
            'cancelled by User': 'canceled',
            'approved': 'pending',
            'finished': 'ok',
            'withdrawal error': 'failed',
            'deposit error': 'failed',
            'cancelled by admin': 'canceled',
            'awaiting': 'pending',
        }
        return self.safe_string(statuses, status, status)

    def parse_transaction(self, transaction, currency=None):
        #
        # fetchDeposits
        #
        #     {
        #         "id": 123654789,
        #         "currency_id": 1,
        #         "currency_code": "BTC",
        #         "deposit_fee_currency_id": 1,
        #         "deposit_fee_currency_code": "BTC",
        #         "amount": 0.25,
        #         "fee": 0.00025,
        #         "txid": "qwertyuhgfdsasdfgh",
        #         "protocol_id": 0,
        #         "deposit_status_id": 1,
        #         "status": "PROCESSING",
        #         "status_color": "#BC3D51",
        #         "created_at": "2018-11-28 12:32:08",
        #         "timestamp": "1543409389",
        #         "confirmations": "1 of 2"
        #     }
        #
        # fetchWithdrawals
        #
        #     {
        #         "id": 65899,
        #         "amount": "0.00600000",
        #         "currency_id": 1,
        #         "currency_code": "BTC",
        #         "fee": "0.00400000",
        #         "fee_currency_id": 1,
        #         "fee_currency_code": "BTC",
        #         "withdrawal_status_id": 1,
        #         "status": "Not Confirmed",
        #         "status_color": "#BC3D51",
        #         "created_at": "2019-01-21 09:36:05",
        #         "created_ts": "1548063365",
        #         "updated_at": "2019-01-21 09:36:05",
        #         "updated_ts": "1548063365",
        #         "txid": null,
        #         "protocol_id": 0,
        #         "withdrawal_address": {
        #             "address": "0X12WERTYUIIJHGFVBNMJHGDFGHJ765SDFGHJ",
        #             "address_name": "Address",
        #             "additional_address_parameter": "qwertyuiopasdfghjkl",
        #             "additional_address_parameter_name": "Destination Tag",
        #             "notification": "",
        #             "protocol_id": 10,
        #             "protocol_name": "Tether OMNI",
        #             "supports_new_address_creation": False
        #         }
        #     }
        #
        id = self.safe_string(transaction, 'id')
        withdrawalAddress = self.safe_value(transaction, 'withdrawal_address', {})
        address = self.safe_string(withdrawalAddress, 'address')
        tag = self.safe_string(withdrawalAddress, 'additional_address_parameter')
        currencyId = self.safe_string(transaction, 'currency_id')
        code = None
        if currencyId in self.currencies_by_id:
            currency = self.currencies_by_id[currencyId]
        else:
            code = self.common_currency_code(self.safe_string(transaction, 'currency_code'))
        if (code is None) and (currency is not None):
            code = currency['code']
        type = 'deposit' if ('deposit_status_id' in transaction) else 'withdrawal'
        amount = self.safe_float(transaction, 'amount')
        status = self.parse_transaction_status(self.safe_string_lower(transaction, 'status'))
        timestamp = self.safe_timestamp_2(transaction, 'timestamp', 'created_ts')
        updated = self.safe_timestamp(transaction, 'updated_ts')
        txid = self.safe_string(transaction, 'txid')
        fee = None
        feeCost = self.safe_float(transaction, 'fee')
        if feeCost is not None:
            feeCurrencyId = self.safe_string(transaction, 'fee_currency_id', 'deposit_fee_currency_id')
            feeCurrencyCode = self.safe_currency_code(feeCurrencyId)
            fee = {
                'cost': feeCost,
                'currency': feeCurrencyCode,
            }
        return {
            'info': transaction,
            'id': id,
            'txid': txid,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'addressFrom': None,
            'address': address,
            'addressTo': address,
            'tagFrom': None,
            'tag': tag,
            'tagTo': tag,
            'type': type,
            'amount': amount,
            'currency': code,
            'status': status,
            'updated': updated,
            'fee': fee,
        }

    async def fetch_deposits(self, code=None, since=None, limit=None, params={}):
        await self.load_markets()
        currency = None
        request = {}
        if code is not None:
            currency = self.currency(code)
            request['currencyId'] = currency['id']
        if limit is not None:
            request['limit'] = limit
        if since is not None:
            request['timeStart'] = since
        response = await self.profileGetDeposits(self.extend(request, params))
        #
        #     {
        #         "success": True,
        #         "data": [
        #             {
        #                 "id": 123654789,
        #                 "currency_id": 1,
        #                 "currency_code": "BTC",
        #                 "deposit_fee_currency_id": 1,
        #                 "deposit_fee_currency_code": "BTC",
        #                 "amount": 0.25,
        #                 "fee": 0.00025,
        #                 "txid": "qwertyuhgfdsasdfgh",
        #                 "protocol_id": 0,
        #                 "deposit_status_id": 1,
        #                 "status": "PROCESSING",
        #                 "status_color": "#BC3D51",
        #                 "created_at": "2018-11-28 12:32:08",
        #                 "timestamp": "1543409389",
        #                 "confirmations": "1 of 2",
        #                 "protocol_specific_settings": {
        #                     "protocol_name": "Tether OMNI",
        #                     "protocol_id": 10,
        #                     "block_explorer_url": "https://omniexplorer.info/search/"
        #                 }
        #             }
        #         ]
        #     }
        #
        deposits = self.safe_value(response, 'data', [])
        return self.parse_transactions(deposits, code, since, limit)

    async def fetch_withdrawals(self, code=None, since=None, limit=None, params={}):
        await self.load_markets()
        currency = None
        request = {}
        if code is not None:
            currency = self.currency(code)
            request['currencyId'] = currency['id']
        if limit is not None:
            request['limit'] = limit
        if since is not None:
            request['timeStart'] = since
        response = await self.profileGetWithdrawals(self.extend(request, params))
        #
        #     {
        #         "success": True,
        #         "data": [
        #             {
        #                 "id": 65899,
        #                 "amount": "0.00600000",
        #                 "currency_id": 1,
        #                 "currency_code": "BTC",
        #                 "fee": "0.00400000",
        #                 "fee_currency_id": 1,
        #                 "fee_currency_code": "BTC",
        #                 "withdrawal_status_id": 1,
        #                 "status": "Not Confirmed",
        #                 "status_color": "#BC3D51",
        #                 "created_at": "2019-01-21 09:36:05",
        #                 "created_ts": "1548063365",
        #                 "updated_at": "2019-01-21 09:36:05",
        #                 "updated_ts": "1548063365",
        #                 "txid": null,
        #                 "protocol_id": 0,
        #                 "withdrawal_address": {
        #                     "address": "0X12WERTYUIIJHGFVBNMJHGDFGHJ765SDFGHJ",
        #                     "address_name": "Address",
        #                     "additional_address_parameter": "qwertyuiopasdfghjkl",
        #                     "additional_address_parameter_name": "Destination Tag",
        #                     "notification": "",
        #                     "protocol_id": 10,
        #                     "protocol_name": "Tether OMNI",
        #                     "supports_new_address_creation": False
        #                 },
        #                 "protocol_specific_settings": {
        #                     "protocol_name": "Tether OMNI",
        #                     "protocol_id": 10,
        #                     "block_explorer_url": "https://omniexplorer.info/search/"
        #                 }
        #             }
        #         ]
        #     }
        #
        withdrawals = self.safe_value(response, 'data', [])
        return self.parse_transactions(withdrawals, code, since, limit)

    async def withdraw(self, code, amount, address, tag=None, params={}):
        self.check_address(address)
        await self.load_markets()
        currency = self.currency(code)
        request = {
            'currency_id': currency['id'],
            'amount': float(self.currency_to_precision(code, amount)),
            'address': address,
            # 'protocol_id': 10,  # optional, to be used with multicurrency wallets like USDT
            # 'additional_address_parameter': tag,  # optional
        }
        if tag is not None:
            request['additional_address_parameter'] = tag
        response = await self.profilePostWithdraw(self.extend(request, params))
        #
        #     {
        #         "success": True,
        #         "data": {
        #             "id": 65899,
        #             "amount": "0.00600000",
        #             "currency_id": 1,
        #             "currency_code": "BTC",
        #             "fee": "0.00400000",
        #             "fee_currency_id": 1,
        #             "fee_currency_code": "BTC",
        #             "withdrawal_status_id": 1,
        #             "status": "Not Confirmed",
        #             "status_color": "#BC3D51",
        #             "created_at": "2019-01-21 09:36:05",
        #             "created_ts": "1548063365",
        #             "updated_at": "2019-01-21 09:36:05",
        #             "updated_ts": "1548063365",
        #             "txid": null,
        #             "protocol_id": 0,
        #             "withdrawal_address": {
        #                 "address": "0X12WERTYUIIJHGFVBNMJHGDFGHJ765SDFGHJ",
        #                 "address_name": "Address",
        #                 "additional_address_parameter": "qwertyuiopasdfghjkl",
        #                 "additional_address_parameter_name": "Destination Tag",
        #                 "notification": "",
        #                 "protocol_id": 10,
        #                 "protocol_name": "Tether OMNI",
        #                 "supports_new_address_creation": False
        #             }
        #         }
        #     }
        #
        data = self.safe_value(response, 'data', {})
        return self.parse_transaction(data, currency)

    async def fetch_funding_fees(self, codes=None, params={}):
        response = await self.publicGetCurrencies(params)
        #
        #     {
        #         "success": True,
        #         "data": [
        #             {
        #                 "id": 1,
        #                 "code": "BTC",
        #                 "name": "Bitcoin",
        #                 "active": True,
        #                 "delisted": False,
        #                 "precision": 8,
        #                 "minimum_tx_confirmations": 24,
        #                 "minimum_withdrawal_amount": "0.009",
        #                 "minimum_deposit_amount": "0.000003",
        #                 "deposit_fee_currency_id": 1,
        #                 "deposit_fee_currency_code": "ETH",
        #                 "deposit_fee_const": "0.00001",
        #                 "deposit_fee_percent": "0",
        #                 "withdrawal_fee_currency_id": 1,
        #                 "withdrawal_fee_currency_code": "ETH",
        #                 "withdrawal_fee_const": "0.0015",
        #                 "withdrawal_fee_percent": "0",
        #                 "withdrawal_limit": "string",
        #                 "block_explorer_url": "https://blockchain.info/tx/",
        #                 "protocol_specific_settings": [
        #                     {
        #                         "protocol_name": "Tether OMNI",
        #                         "protocol_id": 10,
        #                         "active": True,
        #                         "withdrawal_fee_currency_id": 1,
        #                         "withdrawal_fee_const": 0.002,
        #                         "withdrawal_fee_percent": 0,
        #                         "block_explorer_url": "https://omniexplorer.info/search/"
        #                     }
        #                 ]
        #             }
        #         ]
        #     }
        #
        data = self.safe_value(response, 'data', [])
        withdrawFees = {}
        depositFees = {}
        for i in range(0, len(data)):
            id = self.safe_string(data[i], 'id')
            code = self.safe_currency_code(id)
            withdrawFees[code] = self.safe_float(data[i], 'withdrawal_fee_const')
            depositFees[code] = self.safe_float(data[i], 'deposit_fee_const')
        return {
            'withdraw': withdrawFees,
            'deposit': depositFees,
            'info': response,
        }

    def handle_errors(self, httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody):
        if response is None:
            return  # fallback to default error handler
        #
        #     {"success":false,"message":"Wrong parameters","errors":{"candleType":["Invalid Candle Type!"]}}
        #     {"success":false,"message":"Wrong parameters","errors":{"time":["timeStart or timeEnd is less then 1"]}}
        #     {"success":false,"message":"Not enough  ETH"}
        #
        success = self.safe_value(response, 'success', False)
        if not success:
            message = self.safe_string(response, 'message')
            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
