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

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

from ccxt.async_support.base.exchange import Exchange
import hashlib
import math
import json
from ccxt.base.errors import ExchangeError
from ccxt.base.errors import AuthenticationError
from ccxt.base.errors import AccountSuspended
from ccxt.base.errors import InsufficientFunds
from ccxt.base.errors import InvalidOrder
from ccxt.base.errors import OrderNotFound
from ccxt.base.errors import OrderNotCached
from ccxt.base.errors import CancelPending
from ccxt.base.errors import DDoSProtection
from ccxt.base.errors import ExchangeNotAvailable
from ccxt.base.errors import InvalidNonce
from ccxt.base.errors import RequestTimeout


class poloniex (Exchange):

    def describe(self):
        return self.deep_extend(super(poloniex, self).describe(), {
            'id': 'poloniex',
            'name': 'Poloniex',
            'countries': ['US'],
            'rateLimit': 1000,  # up to 6 calls per second
            'has': {
                'createDepositAddress': True,
                'fetchDepositAddress': True,
                'CORS': False,
                'editOrder': True,
                'createMarketOrder': False,
                'fetchOHLCV': True,
                'fetchOrderTrades': True,
                'fetchMyTrades': True,
                'fetchOrder': 'emulated',
                'fetchOrders': 'emulated',
                'fetchOpenOrders': True,
                'fetchClosedOrders': 'emulated',
                'fetchTickers': True,
                'fetchTradingFees': True,
                'fetchCurrencies': True,
                'withdraw': True,
            },
            'timeframes': {
                '5m': 300,
                '15m': 900,
                '30m': 1800,
                '2h': 7200,
                '4h': 14400,
                '1d': 86400,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/27766817-e9456312-5ee6-11e7-9b3c-b628ca5626a5.jpg',
                'api': {
                    'public': 'https://poloniex.com/public',
                    'private': 'https://poloniex.com/tradingApi',
                },
                'www': 'https://poloniex.com',
                'doc': [
                    'https://poloniex.com/support/api/',
                    'http://pastebin.com/dMX7mZE0',
                ],
                'fees': 'https://poloniex.com/fees',
            },
            'api': {
                'public': {
                    'get': [
                        'return24hVolume',
                        'returnChartData',
                        'returnCurrencies',
                        'returnLoanOrders',
                        'returnOrderBook',
                        'returnTicker',
                        'returnTradeHistory',
                    ],
                },
                'private': {
                    'post': [
                        'buy',
                        'cancelLoanOffer',
                        'cancelOrder',
                        'closeMarginPosition',
                        'createLoanOffer',
                        'generateNewAddress',
                        'getMarginPosition',
                        'marginBuy',
                        'marginSell',
                        'moveOrder',
                        'returnActiveLoans',
                        'returnAvailableAccountBalances',
                        'returnBalances',
                        'returnCompleteBalances',
                        'returnDepositAddresses',
                        'returnDepositsWithdrawals',
                        'returnFeeInfo',
                        'returnLendingHistory',
                        'returnMarginAccountSummary',
                        'returnOpenLoanOffers',
                        'returnOpenOrders',
                        'returnOrderTrades',
                        'returnTradableBalances',
                        'returnTradeHistory',
                        'sell',
                        'toggleAutoRenew',
                        'transferBalance',
                        'withdraw',
                    ],
                },
            },
            'fees': {
                'trading': {
                    'maker': 0.0015,
                    'taker': 0.0025,
                },
                'funding': {},
            },
            'limits': {
                'amount': {
                    'min': 0.00000001,
                    'max': 1000000000,
                },
                'price': {
                    'min': 0.00000001,
                    'max': 1000000000,
                },
                'cost': {
                    'min': 0.00000000,
                    'max': 1000000000,
                },
            },
            'precision': {
                'amount': 8,
                'price': 8,
            },
            'commonCurrencies': {
                'AIR': 'AirCoin',
                'APH': 'AphroditeCoin',
                'BCC': 'BTCtalkcoin',
                'BDG': 'Badgercoin',
                'BTM': 'Bitmark',
                'CON': 'Coino',
                'GOLD': 'GoldEagles',
                'GPUC': 'GPU',
                'HOT': 'Hotcoin',
                'ITC': 'Information Coin',
                'PLX': 'ParallaxCoin',
                'KEY': 'KEYCoin',
                'STR': 'XLM',
                'SOC': 'SOCC',
                'XAP': 'API Coin',
            },
            'options': {
                'limits': {
                    'cost': {
                        'min': {
                            'BTC': 0.0001,
                            'ETH': 0.0001,
                            'XMR': 0.0001,
                            'USDT': 1.0,
                        },
                    },
                },
            },
        })

    def calculate_fee(self, symbol, type, side, amount, price, takerOrMaker='taker', params={}):
        market = self.markets[symbol]
        key = 'quote'
        rate = market[takerOrMaker]
        cost = float(self.cost_to_precision(symbol, amount * rate))
        if side == 'sell':
            cost *= price
        else:
            key = 'base'
        return {
            'type': takerOrMaker,
            'currency': market[key],
            'rate': rate,
            'cost': float(self.fee_to_precision(symbol, cost)),
        }

    def parse_ohlcv(self, ohlcv, market=None, timeframe='5m', since=None, limit=None):
        return [
            ohlcv['date'] * 1000,
            ohlcv['open'],
            ohlcv['high'],
            ohlcv['low'],
            ohlcv['close'],
            ohlcv['quoteVolume'],
        ]

    async def fetch_ohlcv(self, symbol, timeframe='5m', since=None, limit=None, params={}):
        await self.load_markets()
        market = self.market(symbol)
        if since is None:
            since = 0
        request = {
            'currencyPair': market['id'],
            'period': self.timeframes[timeframe],
            'start': int(since / 1000),
        }
        if limit is not None:
            request['end'] = self.sum(request['start'], limit * self.timeframes[timeframe])
        response = await self.publicGetReturnChartData(self.extend(request, params))
        return self.parse_ohlcvs(response, market, timeframe, since, limit)

    async def fetch_markets(self):
        markets = await self.publicGetReturnTicker()
        keys = list(markets.keys())
        result = []
        for p in range(0, len(keys)):
            id = keys[p]
            market = markets[id]
            quote, base = id.split('_')
            base = self.common_currency_code(base)
            quote = self.common_currency_code(quote)
            symbol = base + '/' + quote
            minCost = self.safe_float(self.options['limits']['cost']['min'], quote, 0.0)
            result.append(self.extend(self.fees['trading'], {
                'id': id,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'active': True,
                'precision': {
                    'amount': 8,
                    'price': 8,
                },
                'limits': {
                    'amount': {
                        'min': 0.00000001,
                        'max': 1000000000,
                    },
                    'price': {
                        'min': 0.00000001,
                        'max': 1000000000,
                    },
                    'cost': {
                        'min': minCost,
                        'max': 1000000000,
                    },
                },
                'info': market,
            }))
        return result

    async def fetch_balance(self, params={}):
        await self.load_markets()
        balances = await self.privatePostReturnCompleteBalances(self.extend({
            'account': 'all',
        }, params))
        result = {'info': balances}
        currencies = list(balances.keys())
        for c in range(0, len(currencies)):
            id = currencies[c]
            balance = balances[id]
            currency = self.common_currency_code(id)
            account = {
                'free': float(balance['available']),
                'used': float(balance['onOrders']),
                'total': 0.0,
            }
            account['total'] = self.sum(account['free'], account['used'])
            result[currency] = account
        return self.parse_balance(result)

    async def fetch_trading_fees(self, params={}):
        await self.load_markets()
        fees = await self.privatePostReturnFeeInfo()
        return {
            'info': fees,
            'maker': self.safe_float(fees, 'makerFee'),
            'taker': self.safe_float(fees, 'takerFee'),
            'withdraw': {},
            'deposit': {},
        }

    async def fetch_order_book(self, symbol, limit=None, params={}):
        await self.load_markets()
        request = {
            'currencyPair': self.market_id(symbol),
        }
        if limit is not None:
            request['depth'] = limit  # 100
        response = await self.publicGetReturnOrderBook(self.extend(request, params))
        orderbook = self.parse_order_book(response)
        orderbook['nonce'] = self.safe_integer(response, 'sec')
        return orderbook

    def parse_ticker(self, ticker, market=None):
        timestamp = self.milliseconds()
        symbol = None
        if market:
            symbol = market['symbol']
        open = None
        change = None
        average = None
        last = self.safe_float(ticker, 'last')
        relativeChange = self.safe_float(ticker, 'percentChange')
        if relativeChange != -1:
            open = last / self.sum(1, relativeChange)
            change = last - open
            average = self.sum(last, open) / 2
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'high': self.safe_float(ticker, 'high24hr'),
            'low': self.safe_float(ticker, 'low24hr'),
            'bid': self.safe_float(ticker, 'highestBid'),
            'bidVolume': None,
            'ask': self.safe_float(ticker, 'lowestAsk'),
            'askVolume': None,
            'vwap': None,
            'open': open,
            'close': last,
            'last': last,
            'previousClose': None,
            'change': change,
            'percentage': relativeChange * 100,
            'average': average,
            'baseVolume': self.safe_float(ticker, 'quoteVolume'),
            'quoteVolume': self.safe_float(ticker, 'baseVolume'),
            'info': ticker,
        }

    async def fetch_tickers(self, symbols=None, params={}):
        await self.load_markets()
        tickers = await self.publicGetReturnTicker(params)
        ids = list(tickers.keys())
        result = {}
        for i in range(0, len(ids)):
            id = ids[i]
            market = self.markets_by_id[id]
            symbol = market['symbol']
            ticker = tickers[id]
            result[symbol] = self.parse_ticker(ticker, market)
        return result

    async def fetch_currencies(self, params={}):
        currencies = await self.publicGetReturnCurrencies(params)
        ids = list(currencies.keys())
        result = {}
        for i in range(0, len(ids)):
            id = ids[i]
            currency = currencies[id]
            # todo: will need to rethink the fees
            # to add support for multiple withdrawal/deposit methods and
            # differentiated fees for each particular method
            precision = 8  # default precision, todo: fix "magic constants"
            code = self.common_currency_code(id)
            active = (currency['delisted'] == 0) and not currency['disabled']
            result[code] = {
                'id': id,
                'code': code,
                'info': currency,
                'name': currency['name'],
                'active': active,
                'fee': self.safe_float(currency, 'txFee'),  # todo: redesign
                'precision': precision,
                'limits': {
                    'amount': {
                        'min': math.pow(10, -precision),
                        'max': math.pow(10, precision),
                    },
                    'price': {
                        'min': math.pow(10, -precision),
                        'max': math.pow(10, precision),
                    },
                    'cost': {
                        'min': None,
                        'max': None,
                    },
                    'withdraw': {
                        'min': currency['txFee'],
                        'max': math.pow(10, precision),
                    },
                },
            }
        return result

    async def fetch_ticker(self, symbol, params={}):
        await self.load_markets()
        market = self.market(symbol)
        tickers = await self.publicGetReturnTicker(params)
        ticker = tickers[market['id']]
        return self.parse_ticker(ticker, market)

    def parse_trade(self, trade, market=None):
        timestamp = self.parse8601(trade['date'])
        symbol = None
        base = None
        quote = None
        if (not market) and('currencyPair' in list(trade.keys())):
            currencyPair = trade['currencyPair']
            if currencyPair in self.markets_by_id:
                market = self.markets_by_id[currencyPair]
            else:
                parts = currencyPair.split('_')
                quote = parts[0]
                base = parts[1]
                symbol = base + '/' + quote
        if market is not None:
            symbol = market['symbol']
            base = market['base']
            quote = market['quote']
        side = trade['type']
        fee = None
        cost = self.safe_float(trade, 'total')
        amount = self.safe_float(trade, 'amount')
        if 'fee' in trade:
            rate = self.safe_float(trade, 'fee')
            feeCost = None
            currency = None
            if side == 'buy':
                currency = base
                feeCost = amount * rate
            else:
                currency = quote
                if cost is not None:
                    feeCost = cost * rate
            fee = {
                'type': None,
                'rate': rate,
                'cost': feeCost,
                'currency': currency,
            }
        return {
            'info': trade,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': symbol,
            'id': self.safe_string(trade, 'tradeID'),
            'order': self.safe_string(trade, 'orderNumber'),
            'type': 'limit',
            'side': side,
            'price': self.safe_float(trade, 'rate'),
            'amount': amount,
            'cost': cost,
            'fee': fee,
        }

    async def fetch_trades(self, symbol, since=None, limit=None, params={}):
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'currencyPair': market['id'],
        }
        if since is not None:
            request['start'] = int(since / 1000)
            request['end'] = self.seconds()  # last 50000 trades by default
        trades = await self.publicGetReturnTradeHistory(self.extend(request, params))
        return self.parse_trades(trades, market, since, limit)

    async def fetch_my_trades(self, symbol=None, since=None, limit=None, params={}):
        await self.load_markets()
        market = None
        if symbol is not None:
            market = self.market(symbol)
        pair = market['id'] if market else 'all'
        request = {'currencyPair': pair}
        if since is not None:
            request['start'] = int(since / 1000)
            request['end'] = self.seconds() + 1  # adding 1 is a fix for  #3411
        # limit is disabled(does not really work as expected)
        if limit is not None:
            request['limit'] = int(limit)
        response = await self.privatePostReturnTradeHistory(self.extend(request, params))
        result = []
        if market is not None:
            result = self.parse_trades(response, market)
        else:
            if response:
                ids = list(response.keys())
                for i in range(0, len(ids)):
                    id = ids[i]
                    market = None
                    if id in self.markets_by_id:
                        market = self.markets_by_id[id]
                    trades = self.parse_trades(response[id], market)
                    for j in range(0, len(trades)):
                        result.append(trades[j])
        return self.filter_by_since_limit(result, since, limit)

    def parse_order(self, order, market=None):
        timestamp = self.safe_integer(order, 'timestamp')
        if not timestamp:
            timestamp = self.parse8601(order['date'])
        trades = None
        if 'resultingTrades' in order:
            trades = self.parse_trades(order['resultingTrades'], market)
        symbol = None
        if market:
            symbol = market['symbol']
        price = self.safe_float(order, 'price')
        remaining = self.safe_float(order, 'amount')
        amount = self.safe_float(order, 'startingAmount', remaining)
        filled = None
        cost = 0
        if amount is not None:
            if remaining is not None:
                filled = amount - remaining
                if price is not None:
                    cost = filled * price
        if filled is None:
            if trades is not None:
                filled = 0
                cost = 0
                for i in range(0, len(trades)):
                    trade = trades[i]
                    tradeAmount = trade['amount']
                    tradePrice = trade['price']
                    filled = self.sum(filled, tradeAmount)
                    cost += tradePrice * tradeAmount
        return {
            'info': order,
            'id': order['orderNumber'],
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': None,
            'status': order['status'],
            'symbol': symbol,
            'type': order['type'],
            'side': order['side'],
            'price': price,
            'cost': cost,
            'amount': amount,
            'filled': filled,
            'remaining': remaining,
            'trades': trades,
            'fee': None,
        }

    def parse_open_orders(self, orders, market, result):
        for i in range(0, len(orders)):
            order = orders[i]
            extended = self.extend(order, {
                'status': 'open',
                'type': 'limit',
                'side': order['type'],
                'price': order['rate'],
            })
            result.append(self.parse_order(extended, market))
        return result

    async def fetch_orders(self, symbol=None, since=None, limit=None, params={}):
        await self.load_markets()
        market = None
        if symbol is not None:
            market = self.market(symbol)
        pair = market['id'] if market else 'all'
        response = await self.privatePostReturnOpenOrders(self.extend({
            'currencyPair': pair,
        }))
        openOrders = []
        if market is not None:
            openOrders = self.parse_open_orders(response, market, openOrders)
        else:
            marketIds = list(response.keys())
            for i in range(0, len(marketIds)):
                marketId = marketIds[i]
                orders = response[marketId]
                m = self.markets_by_id[marketId]
                openOrders = self.parse_open_orders(orders, m, openOrders)
        for j in range(0, len(openOrders)):
            self.orders[openOrders[j]['id']] = openOrders[j]
        openOrdersIndexedById = self.index_by(openOrders, 'id')
        cachedOrderIds = list(self.orders.keys())
        result = []
        for k in range(0, len(cachedOrderIds)):
            id = cachedOrderIds[k]
            if id in openOrdersIndexedById:
                self.orders[id] = self.extend(self.orders[id], openOrdersIndexedById[id])
            else:
                order = self.orders[id]
                if order['status'] == 'open':
                    order = self.extend(order, {
                        'status': 'closed',
                        'cost': None,
                        'filled': order['amount'],
                        'remaining': 0.0,
                    })
                    if order['cost'] is None:
                        if order['filled'] is not None:
                            order['cost'] = order['filled'] * order['price']
                    self.orders[id] = order
            order = self.orders[id]
            if market is not None:
                if order['symbol'] == symbol:
                    result.append(order)
            else:
                result.append(order)
        return self.filter_by_since_limit(result, since, limit)

    async def fetch_order(self, id, symbol=None, params={}):
        since = self.safe_value(params, 'since')
        limit = self.safe_value(params, 'limit')
        request = self.omit(params, ['since', 'limit'])
        orders = await self.fetch_orders(symbol, since, limit, request)
        for i in range(0, len(orders)):
            if orders[i]['id'] == id:
                return orders[i]
        raise OrderNotCached(self.id + ' order id ' + str(id) + ' is not in "open" state and not found in cache')

    def filter_orders_by_status(self, orders, status):
        result = []
        for i in range(0, len(orders)):
            if orders[i]['status'] == status:
                result.append(orders[i])
        return result

    async def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
        orders = await self.fetch_orders(symbol, since, limit, params)
        return self.filter_orders_by_status(orders, 'open')

    async def fetch_closed_orders(self, symbol=None, since=None, limit=None, params={}):
        orders = await self.fetch_orders(symbol, since, limit, params)
        return self.filter_orders_by_status(orders, 'closed')

    async def create_order(self, symbol, type, side, amount, price=None, params={}):
        if type == 'market':
            raise ExchangeError(self.id + ' allows limit orders only')
        await self.load_markets()
        method = 'privatePost' + self.capitalize(side)
        market = self.market(symbol)
        price = float(price)
        amount = float(amount)
        response = await getattr(self, method)(self.extend({
            'currencyPair': market['id'],
            'rate': self.price_to_precision(symbol, price),
            'amount': self.amount_to_precision(symbol, amount),
        }, params))
        timestamp = self.milliseconds()
        order = self.parse_order(self.extend({
            'timestamp': timestamp,
            'status': 'open',
            'type': type,
            'side': side,
            'price': price,
            'amount': amount,
        }, response), market)
        id = order['id']
        self.orders[id] = order
        return self.extend({'info': response}, order)

    async def edit_order(self, id, symbol, type, side, amount, price=None, params={}):
        await self.load_markets()
        price = float(price)
        request = {
            'orderNumber': id,
            'rate': self.price_to_precision(symbol, price),
        }
        if amount is not None:
            amount = float(amount)
            request['amount'] = self.amount_to_precision(symbol, amount)
        response = await self.privatePostMoveOrder(self.extend(request, params))
        result = None
        if id in self.orders:
            self.orders[id]['status'] = 'canceled'
            newid = response['orderNumber']
            self.orders[newid] = self.extend(self.orders[id], {
                'id': newid,
                'price': price,
                'status': 'open',
            })
            if amount is not None:
                self.orders[newid]['amount'] = amount
            result = self.extend(self.orders[newid], {'info': response})
        else:
            market = None
            if symbol is not None:
                market = self.market(symbol)
            result = self.parse_order(response, market)
            self.orders[result['id']] = result
        return result

    async def cancel_order(self, id, symbol=None, params={}):
        await self.load_markets()
        response = None
        try:
            response = await self.privatePostCancelOrder(self.extend({
                'orderNumber': id,
            }, params))
        except Exception as e:
            if isinstance(e, CancelPending):
                # A request to cancel the order has been sent already.
                # If we then attempt to cancel the order the second time
                # before the first request is processed the exchange will
                # raise a CancelPending exception. Poloniex won't show the
                # order in the list of active(open) orders and the cached
                # order will be marked as 'closed'(see  #1801 for details).
                # To avoid that we proactively mark the order as 'canceled'
                # here. If for some reason the order does not get canceled
                # and still appears in the active list then the order cache
                # will eventually get back in sync on a call to `fetchOrder`.
                if id in self.orders:
                    self.orders[id]['status'] = 'canceled'
            raise e
        if id in self.orders:
            self.orders[id]['status'] = 'canceled'
        return response

    async def fetch_order_status(self, id, symbol=None):
        await self.load_markets()
        orders = await self.fetch_open_orders(symbol)
        indexed = self.index_by(orders, 'id')
        return 'open' if (id in list(indexed.keys())) else 'closed'

    async def fetch_order_trades(self, id, symbol=None, since=None, limit=None, params={}):
        await self.load_markets()
        trades = await self.privatePostReturnOrderTrades(self.extend({
            'orderNumber': id,
        }, params))
        return self.parse_trades(trades)

    async def create_deposit_address(self, code, params={}):
        await self.load_markets()
        currency = self.currency(code)
        response = await self.privatePostGenerateNewAddress({
            'currency': currency['id'],
        })
        address = None
        if response['success'] == 1:
            address = self.safe_string(response, 'response')
        self.check_address(address)
        return {
            'currency': code,
            'address': address,
            'tag': None,
            'info': response,
        }

    async def fetch_deposit_address(self, code, params={}):
        await self.load_markets()
        currency = self.currency(code)
        response = await self.privatePostReturnDepositAddresses()
        currencyId = currency['id']
        address = self.safe_string(response, currencyId)
        self.check_address(address)
        return {
            'currency': code,
            'address': address,
            'tag': None,
            'info': response,
        }

    async def withdraw(self, code, amount, address, tag=None, params={}):
        self.check_address(address)
        await self.load_markets()
        currency = self.currency(code)
        request = {
            'currency': currency['id'],
            'amount': amount,
            'address': address,
        }
        if tag:
            request['paymentId'] = tag
        result = await self.privatePostWithdraw(self.extend(request, params))
        return {
            'info': result,
            'id': result['response'],
        }

    def nonce(self):
        return self.milliseconds()

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        url = self.urls['api'][api]
        query = self.extend({'command': path}, params)
        if api == 'public':
            url += '?' + self.urlencode(query)
        else:
            self.check_required_credentials()
            query['nonce'] = self.nonce()
            body = self.urlencode(query)
            headers = {
                'Content-Type': 'application/x-www-form-urlencoded',
                'Key': self.apiKey,
                'Sign': self.hmac(self.encode(body), self.encode(self.secret), hashlib.sha512),
            }
        return {'url': url, 'method': method, 'body': body, 'headers': headers}

    def handle_errors(self, code, reason, url, method, headers, body):
        response = None
        try:
            response = json.loads(body)
        except Exception as e:
            # syntax error, resort to default error handler
            return
        if 'error' in response:
            message = response['error']
            feedback = self.id + ' ' + self.json(response)
            if message == 'Invalid order number, or you are not the person who placed the order.':
                raise OrderNotFound(feedback)
            elif message == 'Connection timed out. Please try again.':
                raise RequestTimeout(feedback)
            elif message == 'Internal error. Please try again.':
                raise ExchangeNotAvailable(feedback)
            elif message == 'Order not found, or you are not the person who placed it.':
                raise OrderNotFound(feedback)
            elif message == 'Invalid API key/secret pair.':
                raise AuthenticationError(feedback)
            elif message == 'Please do not make more than 8 API calls per second.':
                raise DDoSProtection(feedback)
            elif message.find('Total must be at least') >= 0:
                raise InvalidOrder(feedback)
            elif message.find('This account is frozen.') >= 0:
                raise AccountSuspended(feedback)
            elif message.find('Not enough') >= 0:
                raise InsufficientFunds(feedback)
            elif message.find('Nonce must be greater') >= 0:
                raise InvalidNonce(feedback)
            elif message.find('You have already called cancelOrder or moveOrder on self order.') >= 0:
                raise CancelPending(feedback)
            else:
                raise ExchangeError(self.id + ' unknown error ' + self.json(response))
