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

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

from ccxt.async_support.base.exchange import Exchange
from ccxt.base.errors import ExchangeError
from ccxt.base.errors import AuthenticationError
from ccxt.base.errors import PermissionDenied
from ccxt.base.errors import ArgumentsRequired
from ccxt.base.errors import InsufficientFunds
from ccxt.base.errors import InvalidOrder
from ccxt.base.errors import OrderNotFound
from ccxt.base.errors import DDoSProtection
from ccxt.base.errors import RateLimitExceeded
from ccxt.base.errors import ExchangeNotAvailable
from ccxt.base.errors import OnMaintenance
from ccxt.base.precise import Precise


class bitz(Exchange):

    def describe(self):
        return self.deep_extend(super(bitz, self).describe(), {
            'id': 'bitz',
            'name': 'Bit-Z',
            'countries': ['HK'],
            'rateLimit': 2000,
            'version': 'v2',
            'userAgent': self.userAgents['chrome'],
            'has': {
                'cancelOrder': True,
                'cancelOrders': True,
                'createOrder': True,
                'createMarketOrder': False,
                'fetchBalance': True,
                'fetchDeposits': True,
                'fetchClosedOrders': True,
                'fetchMarkets': True,
                'fetchOHLCV': True,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'fetchOrderBook': True,
                'fetchOrders': True,
                'fetchTicker': True,
                'fetchTickers': True,
                'fetchTime': True,
                'fetchTrades': True,
                'fetchTransactions': False,
                'fetchWithdrawals': True,
                'withdraw': True,
            },
            'timeframes': {
                '1m': '1min',
                '5m': '5min',
                '15m': '15min',
                '30m': '30min',
                '1h': '60min',
                '4h': '4hour',
                '1d': '1day',
                '5d': '5day',
                '1w': '1week',
                '1M': '1mon',
            },
            'hostname': 'apiv2.bitz.com',
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/51840849/87443304-fec5e000-c5fd-11ea-98f8-ba8e67f7eaff.jpg',
                'api': {
                    'market': 'https://{hostname}',
                    'trade': 'https://{hostname}',
                    'assets': 'https://{hostname}',
                },
                'www': 'https://www.bitz.com',
                'doc': 'https://apidocv2.bitz.plus/en/',
                'fees': 'https://www.bitz.com/fee?type=1',
                'referral': 'https://u.bitz.com/register?invite_code=1429193',
            },
            'api': {
                'market': {
                    'get': [
                        'ticker',
                        'depth',
                        'order',  # trades
                        'tickerall',
                        'kline',
                        'symbolList',
                        'getServerTime',
                        'currencyRate',
                        'currencyCoinRate',
                        'coinRate',
                        'getContractCoin',
                        'getContractKline',
                        'getContractOrderBook',
                        'getContractTradesHistory',
                        'getContractTickers',
                    ],
                },
                'trade': {
                    'post': [
                        'addEntrustSheet',
                        'cancelEntrustSheet',
                        'cancelAllEntrustSheet',
                        'coinOut',  # withdraw
                        'getUserHistoryEntrustSheet',  # closed orders
                        'getUserNowEntrustSheet',  # open orders
                        'getEntrustSheetInfo',  # order
                        'depositOrWithdraw',  # transactions
                        'getCoinAddress',
                        'getCoinAddressList',
                        'marketTrade',
                        'addEntrustSheetBatch',
                    ],
                },
                'assets': {
                    'post': [
                        'getUserAssets',
                    ],
                },
                'contract': {
                    'post': [
                        'addContractTrade',
                        'cancelContractTrade',
                        'getContractActivePositions',
                        'getContractAccountInfo',
                        'getContractMyPositions',
                        'getContractOrderResult',
                        'getContractTradeResult',
                        'getContractOrder',
                        'getContractMyHistoryTrade',
                        'getContractMyTrades',
                    ],
                },
            },
            'fees': {
                'trading': {
                    'maker': 0.002,
                    'taker': 0.002,
                },
                'funding': {
                    'withdraw': {
                        'BTC': '0.5%',
                        'DKKT': '0.5%',
                        'ETH': 0.01,
                        'USDT': '0.5%',
                        'LTC': '0.5%',
                        'FCT': '0.5%',
                        'LSK': '0.5%',
                        'HXI': '0.8%',
                        'ZEC': '0.5%',
                        'DOGE': '0.5%',
                        'MZC': '0.5%',
                        'ETC': '0.5%',
                        'GXS': '0.5%',
                        'XPM': '0.5%',
                        'PPC': '0.5%',
                        'BLK': '0.5%',
                        'XAS': '0.5%',
                        'HSR': '0.5%',
                        'NULS': 5.0,
                        'VOISE': 350.0,
                        'PAY': 1.5,
                        'EOS': 0.6,
                        'YBCT': 35.0,
                        'OMG': 0.3,
                        'OTN': 0.4,
                        'BTX': '0.5%',
                        'QTUM': '0.5%',
                        'DASH': '0.5%',
                        'GAME': '0.5%',
                        'BCH': '0.5%',
                        'GNT': 9.0,
                        'SSS': 1500.0,
                        'ARK': '0.5%',
                        'PART': '0.5%',
                        'LEO': '0.5%',
                        'DGB': '0.5%',
                        'ZSC': 130.0,
                        'VIU': 350.0,
                        'BTG': '0.5%',
                        'ARN': 10.0,
                        'VTC': '0.5%',
                        'BCD': '0.5%',
                        'TRX': 200.0,
                        'HWC': '0.5%',
                        'UNIT': '0.5%',
                        'OXY': '0.5%',
                        'MCO': 0.3500,
                        'SBTC': '0.5%',
                        'BCX': '0.5%',
                        'ETF': '0.5%',
                        'PYLNT': 0.4000,
                        'XRB': '0.5%',
                        'ETP': '0.5%',
                    },
                },
            },
            'precision': {
                'amount': 8,
                'price': 8,
            },
            'options': {
                'fetchOHLCVVolume': True,
                'fetchOHLCVWarning': True,
                'lastNonceTimestamp': 0,
            },
            'commonCurrencies': {
                # https://github.com/ccxt/ccxt/issues/3881
                # https://support.bit-z.pro/hc/en-us/articles/360007500654-BOX-BOX-Token-
                'BOX': 'BOX Token',
                'LEO': 'LeoCoin',
                'XRB': 'NANO',
                'PXC': 'Pixiecoin',
                'VTC': 'VoteCoin',
                'TTC': 'TimesChain',
            },
            'exceptions': {
                # '200': Success
                '-102': ExchangeError,  # Invalid parameter
                '-103': AuthenticationError,  # Verification failed
                '-104': ExchangeNotAvailable,  # Network Error-1
                '-105': AuthenticationError,  # Invalid api signature
                '-106': ExchangeNotAvailable,  # Network Error-2
                '-109': AuthenticationError,  # Invalid scretKey
                '-110': DDoSProtection,  # The number of access requests exceeded
                '-111': PermissionDenied,  # Current IP is not in the range of trusted IP
                '-112': OnMaintenance,  # Service is under maintenance
                '-114': RateLimitExceeded,  # The number of daily requests has reached the limit
                '-117': AuthenticationError,  # The apikey expires
                '-100015': AuthenticationError,  # Trade password error
                '-100044': ExchangeError,  # Fail to request data
                '-100101': ExchangeError,  # Invalid symbol
                '-100201': ExchangeError,  # Invalid symbol
                '-100301': ExchangeError,  # Invalid symbol
                '-100401': ExchangeError,  # Invalid symbol
                '-100302': ExchangeError,  # Type of K-line error
                '-100303': ExchangeError,  # Size of K-line error
                '-200003': AuthenticationError,  # Please set trade password
                '-200005': PermissionDenied,  # This account can not trade
                '-200025': ExchangeNotAvailable,  # Temporary trading halt
                '-200027': InvalidOrder,  # Price Error
                '-200028': InvalidOrder,  # Amount must be greater than 0
                '-200029': InvalidOrder,  # Number must be between %s and %d
                '-200030': InvalidOrder,  # Over price range
                '-200031': InsufficientFunds,  # Insufficient assets
                '-200032': ExchangeError,  # System error. Please contact customer service
                '-200033': ExchangeError,  # Fail to trade
                '-200034': OrderNotFound,  # The order does not exist
                '-200035': OrderNotFound,  # Cancellation error, order filled
                '-200037': InvalidOrder,  # Trade direction error
                '-200038': ExchangeError,  # Trading Market Error
                '-200055': OrderNotFound,  # Order record does not exist
                '-300069': AuthenticationError,  # api_key is illegal
                '-300101': ExchangeError,  # Transaction type error
                '-300102': InvalidOrder,  # Price or number cannot be less than 0
                '-300103': AuthenticationError,  # Trade password error
                '-301001': ExchangeNotAvailable,  # Network Error-3
            },
        })

    async def fetch_markets(self, params={}):
        response = await self.marketGetSymbolList(params)
        #
        #     {   status:    200,
        #             msg:   "",
        #            data: {  ltc_btc: {         id: "1",
        #                                        name: "ltc_btc",
        #                                    coinFrom: "ltc",
        #                                      coinTo: "btc",
        #                                 numberFloat: "4",
        #                                  priceFloat: "8",
        #                                      status: "1",
        #                                    minTrade: "0.010",
        #                                    maxTrade: "500000000.000"},
        #                    qtum_usdt: {         id: "196",
        #                                        name: "qtum_usdt",
        #                                    coinFrom: "qtum",
        #                                      coinTo: "usdt",
        #                                 numberFloat: "4",
        #                                  priceFloat: "2",
        #                                      status: "1",
        #                                    minTrade: "0.100",
        #                                    maxTrade: "500000000.000"},  },
        #            time:    1535969146,
        #       microtime:   "0.66955600 1535969146",
        #          source:   "api"                                           }
        #
        markets = self.safe_value(response, 'data')
        ids = list(markets.keys())
        result = []
        for i in range(0, len(ids)):
            id = ids[i]
            market = markets[id]
            numericId = self.safe_string(market, 'id')
            baseId = self.safe_string(market, 'coinFrom')
            quoteId = self.safe_string(market, 'coinTo')
            base = baseId.upper()
            quote = quoteId.upper()
            base = self.safe_currency_code(base)
            quote = self.safe_currency_code(quote)
            symbol = base + '/' + quote
            pricePrecisionString = self.safe_string(market, 'priceFloat')
            minPrice = self.parse_precision(pricePrecisionString)
            precision = {
                'amount': self.safe_integer(market, 'numberFloat'),
                'price': int(pricePrecisionString),
            }
            minAmount = self.safe_string(market, 'minTrade')
            result.append({
                'info': market,
                'id': id,
                'numericId': numericId,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'baseId': baseId,
                'quoteId': quoteId,
                'active': True,
                'precision': precision,
                'limits': {
                    'amount': {
                        'min': self.parse_number(minAmount),
                        'max': self.safe_number(market, 'maxTrade'),
                    },
                    'price': {
                        'min': self.parse_number(minPrice),
                        'max': None,
                    },
                    'cost': {
                        'min': self.parse_number(Precise.string_mul(minPrice, minAmount)),
                        'max': None,
                    },
                },
            })
        return result

    async def fetch_balance(self, params={}):
        await self.load_markets()
        response = await self.assetsPostGetUserAssets(params)
        #
        #     {
        #         status: 200,
        #         msg: "",
        #         data: {
        #             cny: 0,
        #             usd: 0,
        #             btc_total: 0,
        #             info: [{
        #                 "name": "zpr",
        #                 "num": "37.49067275",
        #                 "over": "37.49067275",
        #                 "lock": "0.00000000",
        #                 "btc": "0.00000000",
        #                 "usd": "0.00000000",
        #                 "cny": "0.00000000",
        #             }],
        #         },
        #         time: 1535983966,
        #         microtime: "0.70400500 1535983966",
        #         source: "api",
        #     }
        #
        balances = self.safe_value(response['data'], 'info')
        timestamp = self.parse_microtime(self.safe_string(response, 'microtime'))
        result = {
            'info': response,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
        }
        for i in range(0, len(balances)):
            balance = balances[i]
            currencyId = self.safe_string(balance, 'name')
            code = self.safe_currency_code(currencyId)
            account = self.account()
            account['used'] = self.safe_string(balance, 'lock')
            account['total'] = self.safe_string(balance, 'num')
            account['free'] = self.safe_string(balance, 'over')
            result[code] = account
        return self.parse_balance(result, False)

    def parse_ticker(self, ticker, market=None):
        #
        #      {         symbol: "eth_btc",
        #            quoteVolume: "3905.72",
        #                 volume: "97058.21",
        #            priceChange: "-1.72",
        #         priceChange24h: "-1.65",
        #               askPrice: "0.03971272",
        #                 askQty: "0.0663",
        #               bidPrice: "0.03961469",
        #                 bidQty: "19.5451",
        #                   open: "0.04036769",
        #                   high: "0.04062988",
        #                    low: "0.03956123",
        #                    now: "0.03970100",
        #                firstId:  115567767,
        #                 lastId:  115795316,
        #              dealCount:  14078,
        #        numberPrecision:  4,
        #         pricePrecision:  8,
        #                    cny: "1959.05",
        #                    usd: "287.10",
        #                    krw: "318655.82"   }
        #
        timestamp = None
        marketId = self.safe_string(ticker, 'symbol')
        symbol = self.safe_symbol(marketId, market, '_')
        last = self.safe_number(ticker, 'now')
        open = self.safe_number(ticker, 'open')
        change = None
        average = None
        if last is not None and open is not None:
            change = last - open
            average = self.sum(last, open) / 2
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'high': self.safe_number(ticker, 'high'),
            'low': self.safe_number(ticker, 'low'),
            'bid': self.safe_number(ticker, 'bidPrice'),
            'bidVolume': self.safe_number(ticker, 'bidQty'),
            'ask': self.safe_number(ticker, 'askPrice'),
            'askVolume': self.safe_number(ticker, 'askQty'),
            'vwap': None,
            'open': open,
            'close': last,
            'last': last,
            'previousClose': None,
            'change': change,
            'percentage': self.safe_number(ticker, 'priceChange24h'),
            'average': average,
            'baseVolume': self.safe_number(ticker, 'volume'),
            'quoteVolume': self.safe_number(ticker, 'quoteVolume'),
            'info': ticker,
        }

    def parse_microtime(self, microtime):
        if microtime is None:
            return microtime
        parts = microtime.split(' ')
        milliseconds = float(parts[0])
        seconds = int(parts[1])
        total = self.sum(seconds, milliseconds)
        return int(total * 1000)

    async def fetch_ticker(self, symbol, params={}):
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        response = await self.marketGetTicker(self.extend(request, params))
        #
        #     {   status:    200,
        #             msg:   "",
        #            data: {         symbol: "eth_btc",
        #                        quoteVolume: "3905.72",
        #                             volume: "97058.21",
        #                        priceChange: "-1.72",
        #                     priceChange24h: "-1.65",
        #                           askPrice: "0.03971272",
        #                             askQty: "0.0663",
        #                           bidPrice: "0.03961469",
        #                             bidQty: "19.5451",
        #                               open: "0.04036769",
        #                               high: "0.04062988",
        #                                low: "0.03956123",
        #                                now: "0.03970100",
        #                            firstId:  115567767,
        #                             lastId:  115795316,
        #                          dealCount:  14078,
        #                    numberPrecision:  4,
        #                     pricePrecision:  8,
        #                                cny: "1959.05",
        #                                usd: "287.10",
        #                                krw: "318655.82"   },
        #            time:    1535970397,
        #       microtime:   "0.76341900 1535970397",
        #          source:   "api"                             }
        #
        ticker = self.parse_ticker(response['data'], market)
        timestamp = self.parse_microtime(self.safe_string(response, 'microtime'))
        return self.extend(ticker, {
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
        })

    async def fetch_tickers(self, symbols=None, params={}):
        await self.load_markets()
        request = {}
        if symbols is not None:
            ids = self.market_ids(symbols)
            request['symbols'] = ','.join(ids)
        response = await self.marketGetTickerall(self.extend(request, params))
        #
        #     {   status:    200,
        #             msg:   "",
        #            data: {  ela_btc: {         symbol: "ela_btc",
        #                                     quoteVolume: "0.00",
        #                                          volume: "3.28",
        #                                     priceChange: "0.00",
        #                                  priceChange24h: "0.00",
        #                                        askPrice: "0.00147984",
        #                                          askQty: "5.4580",
        #                                        bidPrice: "0.00120230",
        #                                          bidQty: "12.5384",
        #                                            open: "0.00149078",
        #                                            high: "0.00149078",
        #                                             low: "0.00149078",
        #                                             now: "0.00149078",
        #                                         firstId:  115581219,
        #                                          lastId:  115581219,
        #                                       dealCount:  1,
        #                                 numberPrecision:  4,
        #                                  pricePrecision:  8,
        #                                             cny: "73.66",
        #                                             usd: "10.79",
        #                                             krw: "11995.03"    }     },
        #            time:    1535971578,
        #       microtime:   "0.39854200 1535971578",
        #          source:   "api"                                                }
        #
        tickers = self.safe_value(response, 'data')
        timestamp = self.parse_microtime(self.safe_string(response, 'microtime'))
        result = {}
        ids = list(tickers.keys())
        for i in range(0, len(ids)):
            id = ids[i]
            ticker = tickers[id]
            market = None
            if id in self.markets_by_id:
                market = self.markets_by_id[id]
            ticker = self.parse_ticker(tickers[id], market)
            symbol = ticker['symbol']
            if symbol is None:
                if market is not None:
                    symbol = market['symbol']
                else:
                    baseId, quoteId = id.split('_')
                    base = self.safe_currency_code(baseId)
                    quote = self.safe_currency_code(quoteId)
                    symbol = base + '/' + quote
            if symbol is not None:
                result[symbol] = self.extend(ticker, {
                    'timestamp': timestamp,
                    'datetime': self.iso8601(timestamp),
                })
        return self.filter_by_array(result, 'symbol', symbols)

    async def fetch_time(self, params={}):
        response = await self.marketGetGetServerTime(params)
        #
        #     {
        #         "status":200,
        #         "msg":"",
        #         "data":[],
        #         "time":1555490875,
        #         "microtime":"0.35994200 1555490875",
        #         "source":"api"
        #     }
        #
        return self.safe_timestamp(response, 'time')

    async def fetch_order_book(self, symbol, limit=None, params={}):
        await self.load_markets()
        request = {
            'symbol': self.market_id(symbol),
        }
        response = await self.marketGetDepth(self.extend(request, params))
        #
        #     {   status:    200,
        #             msg:   "",
        #            data: {    asks: [["10.00000000", "0.4426", "4.4260"],
        #                                ["1.00000000", "0.8339", "0.8339"],
        #                                ["0.91700000", "0.0500", "0.0458"],
        #                                ["0.20000000", "0.1000", "0.0200"],
        #                                ["0.03987120", "16.1262", "0.6429"],
        #                                ["0.03986120", "9.7523", "0.3887"]   ],
        #                        bids: [["0.03976145", "0.0359", "0.0014"],
        #                                ["0.03973401", "20.9493", "0.8323"],
        #                                ["0.03967970", "0.0328", "0.0013"],
        #                                ["0.00000002", "10000.0000", "0.0002"],
        #                                ["0.00000001", "231840.7500", "0.0023"]],
        #                    coinPair:   "eth_btc"                                  },
        #            time:    1535974778,
        #       microtime:   "0.04017400 1535974778",
        #          source:   "api"                                                     }
        #
        orderbook = self.safe_value(response, 'data')
        timestamp = self.parse_microtime(self.safe_string(response, 'microtime'))
        return self.parse_order_book(orderbook, symbol, timestamp)

    def parse_trade(self, trade, market=None):
        #
        # fetchTrades(public)
        #
        #    {id:  115807453,
        #       t: "19:36:24",
        #       T:  1535974584,
        #       p: "0.03983296",
        #       n: "0.1000",
        #       s: "buy"         },
        #
        id = self.safe_string(trade, 'id')
        timestamp = self.safe_timestamp(trade, 'T')
        symbol = None
        if market is not None:
            symbol = market['symbol']
        priceString = self.safe_string(trade, 'p')
        amountString = self.safe_string(trade, 'n')
        price = self.parse_number(priceString)
        amount = self.parse_number(amountString)
        cost = self.parse_number(Precise.string_mul(priceString, amountString))
        side = self.safe_string(trade, 's')
        return {
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': symbol,
            'id': id,
            'order': None,
            'type': 'limit',
            'side': side,
            'takerOrMaker': None,
            'price': price,
            'amount': amount,
            'cost': cost,
            'fee': None,
            'info': trade,
        }

    async def fetch_trades(self, symbol, since=None, limit=None, params={}):
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        response = await self.marketGetOrder(self.extend(request, params))
        #
        #     {   status:    200,
        #             msg:   "",
        #            data: [{id:  115807453,
        #                       t: "19:36:24",
        #                       T:  1535974584,
        #                       p: "0.03983296",
        #                       n: "0.1000",
        #                       s: "buy"         },
        #                    {id:  115806811,
        #                       t: "19:33:19",
        #                       T:  1535974399,
        #                       p: "0.03981135",
        #                       n: "9.4612",
        #                       s: "sell"        }  ],
        #            time:    1535974583,
        #       microtime:   "0.57118100 1535974583",
        #          source:   "api"                     }
        #
        return self.parse_trades(response['data'], market, since, limit)

    def parse_ohlcv(self, ohlcv, market=None):
        #
        #     {
        #         time: "1535973420000",
        #         open: "0.03975084",
        #         high: "0.03975084",
        #         low: "0.03967700",
        #         close: "0.03967700",
        #         volume: "12.4733",
        #         datetime: "2018-09-03 19:17:00"
        #     }
        #
        return [
            self.safe_integer(ohlcv, 'time'),
            self.safe_number(ohlcv, 'open'),
            self.safe_number(ohlcv, 'high'),
            self.safe_number(ohlcv, 'low'),
            self.safe_number(ohlcv, 'close'),
            self.safe_number(ohlcv, 'volume'),
        ]

    async def fetch_ohlcv(self, symbol, timeframe='1m', since=None, limit=None, params={}):
        await self.load_markets()
        duration = self.parse_timeframe(timeframe) * 1000
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
            'resolution': self.timeframes[timeframe],
        }
        if limit is not None:
            request['size'] = min(limit, 300)  # 1-300
            if since is not None:
                request['to'] = self.sum(since, limit * duration * 1000)
        else:
            if since is not None:
                raise ArgumentsRequired(self.id + ' fetchOHLCV() requires a limit argument if the since argument is specified')
        response = await self.marketGetKline(self.extend(request, params))
        #
        #     {
        #         status: 200,
        #         msg: "",
        #         data: {
        #             bars: [
        #                 {time: "1535973420000", open: "0.03975084", high: "0.03975084", low: "0.03967700", close: "0.03967700", volume: "12.4733", datetime: "2018-09-03 19:17:00"},
        #                 {time: "1535955480000", open: "0.04009900", high: "0.04016745", low: "0.04009900", close: "0.04012074", volume: "74.4803", datetime: "2018-09-03 14:18:00"},
        #             ],
        #             resolution: "1min",
        #             symbol: "eth_btc",
        #             from: "1535973420000",
        #             to: "1535955480000",
        #             size: 300
        #         },
        #         time: 1535973435,
        #         microtime: "0.56462100 1535973435",
        #         source: "api"
        #     }
        #
        data = self.safe_value(response, 'data', {})
        bars = self.safe_value(data, 'bars', [])
        return self.parse_ohlcvs(bars, market, timeframe, since, limit)

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

    def parse_order(self, order, market=None):
        #
        # createOrder
        #
        #    {
        #         "id": "693248739",   # order id
        #         "uId": "2074056",    # uid
        #         "price": "100",      # price
        #         "number": "10",      # number
        #         "numberOver": "10",  # undealed
        #         "flag": "sale",      # flag
        #         "status": "0",       # unfilled
        #         "coinFrom": "vtc",
        #         "coinTo": "dkkt",
        #         "numberDeal": "0"    # dealed
        #     }
        #
        id = self.safe_string(order, 'id')
        symbol = None
        if market is None:
            baseId = self.safe_string(order, 'coinFrom')
            quoteId = self.safe_string(order, 'coinTo')
            if (baseId is not None) and (quoteId is not None):
                marketId = baseId + '_' + quoteId
                if marketId in self.markets_by_id:
                    market = self.safe_value(self.markets_by_id, marketId)
                else:
                    base = self.safe_currency_code(baseId)
                    quote = self.safe_currency_code(quoteId)
                    symbol = base + '/' + quote
        if market is not None:
            symbol = market['symbol']
        side = self.safe_string(order, 'flag')
        if side is not None:
            side = 'sell' if (side == 'sale') else 'buy'
        price = self.safe_number(order, 'price')
        amount = self.safe_number(order, 'number')
        remaining = self.safe_number(order, 'numberOver')
        filled = self.safe_number(order, 'numberDeal')
        timestamp = self.safe_integer(order, 'timestamp')
        if timestamp is None:
            timestamp = self.safe_timestamp(order, 'created')
        cost = self.safe_number(order, 'orderTotalPrice')
        status = self.parse_order_status(self.safe_string(order, 'status'))
        return self.safe_order({
            'id': id,
            'clientOrderId': None,
            'datetime': self.iso8601(timestamp),
            'timestamp': timestamp,
            'lastTradeTimestamp': None,
            'status': status,
            'symbol': symbol,
            'type': 'limit',
            'timeInForce': None,
            'postOnly': None,
            'side': side,
            'price': price,
            'stopPrice': None,
            'cost': cost,
            'amount': amount,
            'filled': filled,
            'remaining': remaining,
            'trades': None,
            'fee': None,
            'info': order,
            'average': None,
        })

    async def create_order(self, symbol, type, side, amount, price=None, params={}):
        await self.load_markets()
        if type != 'limit':
            raise ExchangeError(self.id + ' createOrder allows limit orders only')
        market = self.market(symbol)
        orderType = '1' if (side == 'buy') else '2'
        if not self.password:
            raise ExchangeError(self.id + ' createOrder() requires you to set exchange.password = "YOUR_TRADING_PASSWORD"(a trade password is NOT THE SAME as your login password)')
        request = {
            'symbol': market['id'],
            'type': orderType,
            'price': self.price_to_precision(symbol, price),
            'number': self.amount_to_precision(symbol, amount),
            'tradePwd': self.password,
        }
        response = await self.tradePostAddEntrustSheet(self.extend(request, params))
        #
        #     {
        #         "status": 200,
        #         "msg": "",
        #         "data": {
        #             "id": "693248739",   # order id
        #             "uId": "2074056",    # uid
        #             "price": "100",      # price
        #             "number": "10",      # number
        #             "numberOver": "10",  # undealed
        #             "flag": "sale",      # flag
        #             "status": "0",       # unfilled
        #             "coinFrom": "vtc",
        #             "coinTo": "dkkt",
        #             "numberDeal": "0"    # dealed
        #         },
        #         "time": "1533035297",
        #         "microtime": "0.41892000 1533035297",
        #         "source": "api",
        #     }
        #
        timestamp = self.parse_microtime(self.safe_string(response, 'microtime'))
        order = self.extend({
            'timestamp': timestamp,
        }, response['data'])
        return self.parse_order(order, market)

    async def cancel_order(self, id, symbol=None, params={}):
        await self.load_markets()
        request = {
            'entrustSheetId': id,
        }
        response = await self.tradePostCancelEntrustSheet(self.extend(request, params))
        #
        #     {
        #         "status":200,
        #         "msg":"",
        #         "data":{
        #             "updateAssetsData":{
        #                 "coin":"bz",
        #                 "over":"1000.00000000",
        #                 "lock":"-1000.00000000"
        #             },
        #             "assetsInfo":{
        #                 "coin":"bz",
        #                 "over":"9999.99999999",
        #                 "lock":"9999.99999999"
        #             }
        #         },
        #         "time":"1535464383",
        #         "microtime":"0.91558000 1535464383",
        #         "source":"api"
        #     }
        #
        return response

    async def cancel_orders(self, ids, symbol=None, params={}):
        await self.load_markets()
        request = {
            'ids': ','.join(ids),
        }
        response = await self.tradePostCancelEntrustSheet(self.extend(request, params))
        #
        #     {
        #         "status":200,
        #         "msg":"",
        #         "data":{
        #             "744173808":{
        #                 "updateAssetsData":{
        #                     "coin":"bz",
        #                     "over":"100.00000000",
        #                     "lock":"-100.00000000"
        #                 },
        #                 "assetsInfo":{
        #                     "coin":"bz",
        #                     "over":"899.99999999",
        #                     "lock":"19099.99999999"
        #                 }
        #             },
        #             "744173809":{
        #                 "updateAssetsData":{
        #                     "coin":"bz",
        #                     "over":"100.00000000",
        #                     "lock":"-100.00000000"
        #                 },
        #                 "assetsInfo":{
        #                     "coin":"bz",
        #                     "over":"999.99999999",
        #                     "lock":"18999.99999999"
        #                 }
        #             }
        #         },
        #         "time":"1535525649",
        #         "microtime":"0.05009400 1535525649",
        #         "source":"api"
        #     }
        #
        return response

    async def fetch_order(self, id, symbol=None, params={}):
        await self.load_markets()
        request = {
            'entrustSheetId': id,
        }
        response = await self.tradePostGetEntrustSheetInfo(self.extend(request, params))
        #
        #     {
        #         "status":200,
        #         "msg":"",
        #         "data":{
        #             "id":"708279852",
        #             "uId":"2074056",
        #             "price":"100.00000000",
        #             "number":"10.0000",
        #             "total":"0.00000000",
        #             "numberOver":"10.0000",
        #             "numberDeal":"0.0000",
        #             "flag":"sale",
        #             "status":"0",  #0:unfilled, 1:partial deal, 2:all transactions, 3:already cancelled
        #             "coinFrom":"bz",
        #             "coinTo":"usdt",
        #             "orderTotalPrice":"0",
        #             "created":"1533279876"
        #         },
        #         "time":"1533280294",
        #         "microtime":"0.36859200 1533280294",
        #         "source":"api"
        #     }
        #
        return self.parse_order(response['data'])

    async def fetch_orders_with_method(self, method, symbol=None, since=None, limit=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOpenOrders() requires a symbol argument')
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'coinFrom': market['baseId'],
            'coinTo': market['quoteId'],
            # 'type': 1,  # optional integer, 1 = buy, 2 = sell
            # 'page': 1,  # optional integer
            # 'pageSize': 100,  # optional integer, max 100
            # 'startTime': 1510235730,  # optional integer timestamp in seconds
            # 'endTime': 1510235730,  # optional integer timestamp in seconds
        }
        if limit is not None:
            request['page'] = 1
            request['pageSize'] = limit
        if since is not None:
            request['startTime'] = int(since / 1000)
            # request['endTime'] = int(since / 1000)
        response = await getattr(self, method)(self.extend(request, params))
        #
        #     {
        #         "status": 200,
        #         "msg": "",
        #         "data": {
        #             "data": [
        #                 {
        #                     "id": "693248739",
        #                     "uid": "2074056",
        #                     "price": "100.00000000",
        #                     "number": "10.0000",
        #                     "total": "0.00000000",
        #                     "numberOver": "0.0000",
        #                     "numberDeal": "0.0000",
        #                     "flag": "sale",
        #                     "status": "3",  # 0:unfilled, 1:partial deal, 2:all transactions, 3:already cancelled
        #                     "isNew": "N",
        #                     "coinFrom": "vtc",
        #                     "coinTo": "dkkt",
        #                     "created": "1533035300",
        #                 },
        #                 {
        #                     "id": "723086996",
        #                     "uid": "2074056",
        #                     "price": "100.00000000",
        #                     "number": "10.0000",
        #                     "total": "0.00000000",
        #                     "numberOver": "0.0000",
        #                     "numberDeal": "0.0000",
        #                     "flag": "sale",
        #                     "status": "3",
        #                     "isNew": "N",
        #                     "coinFrom": "bz",
        #                     "coinTo": "usdt",
        #                     "created": "1533523568",
        #                 },
        #             ],
        #             "pageInfo": {
        #                 "limit": "10",
        #                 "offest": "0",
        #                 "current_page": "1",
        #                 "page_size": "10",
        #                 "total_count": "17",
        #                 "page_count": "2",
        #             }
        #         },
        #         "time": "1533279329",
        #         "microtime": "0.15305300 1533279329",
        #         "source": "api"
        #     }
        #
        orders = self.safe_value(response['data'], 'data', [])
        return self.parse_orders(orders, None, since, limit)

    async def fetch_orders(self, symbol=None, since=None, limit=None, params={}):
        return await self.fetch_orders_with_method('tradePostGetUserHistoryEntrustSheet', symbol, since, limit, params)

    async def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
        return await self.fetch_orders_with_method('tradePostGetUserNowEntrustSheet', symbol, since, limit, params)

    async def fetch_closed_orders(self, symbol=None, since=None, limit=None, params={}):
        return await self.fetch_orders_with_method('tradePostGetUserHistoryEntrustSheet', symbol, since, limit, params)

    def parse_transaction_status(self, status):
        statuses = {
            '1': 'pending',
            '2': 'pending',
            '3': 'pending',
            '4': 'ok',
            '5': 'canceled',
        }
        return self.safe_string(statuses, status, status)

    def parse_transaction(self, transaction, currency=None):
        #
        #     {
        #         "id": '96275',
        #         "uid": '2109073',
        #         "wallet": '0xf4c4141c0127bc37b1d0c409a091920eba13ada7',
        #         "txid": '0xb7adfa52aa566f9ac112e3c01f77bd91179b19eab12092a9a5a8b33d5086e31d',
        #         "confirm": '12',
        #         "number": '0.50000000',
        #         "status": 4,
        #         "updated": '1534944168605',
        #         "addressUrl": 'https://etherscan.io/address/',
        #         "txidUrl": 'https://etherscan.io/tx/',
        #         "description": 'Ethereum',
        #         "coin": 'eth',
        #         "memo": ''
        #     }
        #
        #     {
        #         "id":"397574",
        #         "uid":"2033056",
        #         "wallet":"1AG1gZvQAYu3WBvgg7p4BMMghQD2gE693k",
        #         "txid":"",
        #         "confirm":"0",
        #         "number":"1000.00000000",
        #         "status":1,
        #         "updated":"0",
        #         "addressUrl":"http://omniexplorer.info/lookupadd.aspx?address=",
        #         "txidUrl":"http://omniexplorer.info/lookuptx.aspx?txid=",
        #         "description":"Tether",
        #         "coin":"usdt",
        #         "memo":""
        #     }
        #
        #     {
        #         "id":"153606",
        #         "uid":"2033056",
        #         "wallet":"1AG1gZvQAYu3WBvgg7p4BMMghQD2gE693k",
        #         "txid":"aa2b179f84cd6dedafd41845e0fbf7f01e14c0d71ea3140d03d6f5a9ccd93199",
        #         "confirm":"0",
        #         "number":"761.11110000",
        #         "status":4,
        #         "updated":"1536726133579",
        #         "addressUrl":"http://omniexplorer.info/lookupadd.aspx?address=",
        #         "txidUrl":"http://omniexplorer.info/lookuptx.aspx?txid=",
        #         "description":"Tether",
        #         "coin":"usdt",
        #         "memo":""
        #     }
        #
        # withdraw
        #
        #     {
        #         "id":397574,
        #         "email":"***@email.com",
        #         "coin":"usdt",
        #         "network_fee":"",
        #         "eid":23112
        #     }
        #
        timestamp = self.safe_integer(transaction, 'updated')
        if timestamp == 0:
            timestamp = None
        currencyId = self.safe_string(transaction, 'coin')
        code = self.safe_currency_code(currencyId, currency)
        type = self.safe_string_lower(transaction, 'type')
        status = self.parse_transaction_status(self.safe_string(transaction, 'status'))
        fee = None
        feeCost = self.safe_number(transaction, 'network_fee')
        if feeCost is not None:
            fee = {
                'cost': feeCost,
                'code': code,
            }
        return {
            'id': self.safe_string(transaction, 'id'),
            'txid': self.safe_string(transaction, 'txid'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'address': self.safe_string(transaction, 'wallet'),
            'tag': self.safe_string(transaction, 'memo'),
            'type': type,
            'amount': self.safe_number(transaction, 'number'),
            'currency': code,
            'status': status,
            'updated': timestamp,
            'fee': fee,
            'info': transaction,
        }

    def parse_transactions_by_type(self, type, transactions, code=None, since=None, limit=None):
        result = []
        for i in range(0, len(transactions)):
            transaction = self.parse_transaction(self.extend({
                'type': type,
            }, transactions[i]))
            result.append(transaction)
        return self.filter_by_currency_since_limit(result, code, since, limit)

    def parse_transaction_type(self, type):
        types = {
            'deposit': 1,
            'withdrawal': 2,
        }
        return self.safe_integer(types, type, type)

    async def fetch_deposits(self, code=None, since=None, limit=None, params={}):
        return await self.fetch_transactions_for_type('deposit', code, since, limit, params)

    async def fetch_withdrawals(self, code=None, since=None, limit=None, params={}):
        return await self.fetch_transactions_for_type('withdrawal', code, since, limit, params)

    async def fetch_transactions_for_type(self, type, code=None, since=None, limit=None, params={}):
        if code is None:
            raise ArgumentsRequired(self.id + ' fetchTransactions() requires a currency `code` argument')
        await self.load_markets()
        currency = self.currency(code)
        request = {
            'coin': currency['id'],
            'type': self.parse_transaction_type(type),
        }
        if since is not None:
            request['startTime'] = int(since / str(1000))
        if limit is not None:
            request['page'] = 1
            request['pageSize'] = limit
        response = await self.tradePostDepositOrWithdraw(self.extend(request, params))
        transactions = self.safe_value(response['data'], 'data', [])
        return self.parse_transactions_by_type(type, transactions, 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 = {
            'coin': currency['id'],
            'number': self.currency_to_precision(code, amount),
            'address': address,
            # 'type': 'erc20',  # omni, trc20, optional
        }
        if tag is not None:
            request['memo'] = tag
        response = await self.tradePostCoinOut(self.extend(request, params))
        #
        #     {
        #         "status":200,
        #         "msg":"",
        #         "data":{
        #             "id":397574,
        #             "email":"***@email.com",
        #             "coin":"usdt",
        #             "network_fee":"",
        #             "eid":23112
        #         },
        #         "time":1552641646,
        #         "microtime":"0.70304500 1552641646",
        #         "source":"api"
        #     }
        #
        data = self.safe_value(response, 'data', {})
        return self.parse_transaction(data, currency)

    def nonce(self):
        currentTimestamp = self.seconds()
        if currentTimestamp > self.options['lastNonceTimestamp']:
            self.options['lastNonceTimestamp'] = currentTimestamp
            self.options['lastNonce'] = 100000
        self.options['lastNonce'] = self.sum(self.options['lastNonce'], 1)
        return self.options['lastNonce']

    def sign(self, path, api='market', method='GET', params={}, headers=None, body=None):
        baseUrl = self.implode_params(self.urls['api'][api], {'hostname': self.hostname})
        url = baseUrl + '/' + self.capitalize(api) + '/' + path
        query = None
        if api == 'market':
            query = self.urlencode(params)
            if len(query):
                url += '?' + query
        else:
            self.check_required_credentials()
            body = self.rawencode(self.keysort(self.extend({
                'apiKey': self.apiKey,
                'timeStamp': self.seconds(),
                'nonce': self.nonce(),
            }, params)))
            body += '&sign=' + self.hash(self.encode(body + self.secret))
            headers = {'Content-type': 'application/x-www-form-urlencoded'}
        return {'url': url, 'method': method, 'body': body, 'headers': headers}

    def handle_errors(self, httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody):
        if response is None:
            return  # fallback to default error handler
        status = self.safe_string(response, 'status')
        if status is not None:
            feedback = self.id + ' ' + body
            #
            #     {"status":-107,"msg":"","data":"","time":1535968848,"microtime":"0.89092200 1535968848","source":"api"}
            #
            if status == '200':
                #
                #     {"status":200,"msg":"","data":-200031,"time":1535999806,"microtime":"0.85476800 1535999806","source":"api"}
                #
                code = self.safe_integer(response, 'data')
                if code is not None:
                    self.throw_exactly_matched_exception(self.exceptions, code, feedback)
                    raise ExchangeError(feedback)
                else:
                    return  # no error
            self.throw_exactly_matched_exception(self.exceptions, status, feedback)
            raise ExchangeError(feedback)
