# -*- 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
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"}
                },
            },
        })

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

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

    def fetch_ticker(self, symbol, params={}):
        self.load_markets()
        market = self.market(symbol)
        request = {
            'currencyPairId': market['id'],
        }
        response = 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)

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

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

    def fetch_tickers(self, symbols=None, params={}):
        self.load_markets()
        response = 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'),
        ]

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

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

    def fetch_balance(self, params={}):
        self.load_markets()
        # self.load_accounts()
        response = 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

    def create_order(self, symbol, type, side, amount, price=None, params={}):
        if type == 'market':
            raise ExchangeError(self.id + ' createOrder allows limit orders only')
        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 = 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)

    def fetch_order(self, id, symbol=None, params={}):
        self.load_markets()
        request = {
            'orderId': id,
        }
        response = 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)

    def fetch_closed_order(self, id, symbol=None, params={}):
        self.load_markets()
        request = {
            'orderId': id,
        }
        response = 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)

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

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

    def cancel_order(self, id, symbol=None, params={}):
        self.load_markets()
        request = {
            'orderId': id,
        }
        response = 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))

    def cancel_all_orders(self, symbol=None, params={}):
        self.load_markets()
        request = {}
        method = 'tradingDeleteOrders'
        if symbol is not None:
            market = self.market(symbol)
            request['currencyPairId'] = market['id']
            method = 'tradingDeleteOrdersCurrencyPairId'
        response = 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

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

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

    def fetch_deposit_address(self, code, params={}):
        self.load_markets()
        balance = 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 = 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,
        }

    def fetch_deposits(self, code=None, since=None, limit=None, params={}):
        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 = 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)

    def fetch_withdrawals(self, code=None, since=None, limit=None, params={}):
        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 = 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)

    def withdraw(self, code, amount, address, tag=None, params={}):
        self.check_address(address)
        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 = 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)

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