def create_order(self, asset, amount, is_buy, style): """ Creating order on the exchange. :param asset: :param amount: :param is_buy: :param style: :return: """ exchange_symbol = self.get_symbol(asset) if isinstance(style, ExchangeLimitOrder) \ or isinstance(style, ExchangeStopLimitOrder): price = style.get_limit_price(is_buy) order_type = 'limit' elif isinstance(style, ExchangeStopOrder): price = style.get_stop_price(is_buy) order_type = 'stop' else: raise InvalidOrderStyle(exchange=self.name, style=style.__class__.__name__) req = dict( symbol=exchange_symbol, amount=str(float(abs(amount))), price="{:.20f}".format(float(price)), side='buy' if is_buy else 'sell', type='exchange ' + order_type, # TODO: support margin trades exchange=self.name, is_hidden=False, is_postonly=False, use_all_available=0, ocoorder=False, buy_price_oco=0, sell_price_oco=0) date = pd.Timestamp.utcnow() try: self.ask_request() response = self._request('order/new', req) order_status = response.json() except Exception as e: raise ExchangeRequestError(error=e) if 'message' in order_status: raise ExchangeRequestError( error='unable to create Bitfinex order {}'.format( order_status['message'])) order_id = str(order_status['id']) order = Order(dt=date, asset=asset, amount=amount, stop=style.get_stop_price(is_buy), limit=style.get_limit_price(is_buy), id=order_id) return order
def get_order(self, order_id): """Lookup an order based on the order id returned from one of the order functions. Parameters ---------- order_id : str The unique identifier for the order. Returns ------- order : Order The order object. """ try: self.ask_request() response = self._request('order/status', {'order_id': int(order_id)}) order_status = response.json() except Exception as e: raise ExchangeRequestError(error=e) if 'message' in order_status: raise ExchangeRequestError( error='Unable to retrieve order status: {}'.format( order_status['message'])) return self._create_order(order_status)
def get_open_orders(self, asset=None): """Retrieve all of the current open orders. Parameters ---------- asset : Asset If passed and not None, return only the open orders for the given asset instead of all open orders. Returns ------- open_orders : dict[list[Order]] or list[Order] If no asset is passed this will return a dict mapping Assets to a list containing all the open orders for the asset. If an asset is passed then this will return a list of the open orders for this asset. """ try: response = self._request('orders', None) order_statuses = response.json() except Exception as e: raise ExchangeRequestError(error=e) if 'message' in order_statuses: raise ExchangeRequestError( error='Unable to retrieve open orders: {}'.format( order_statuses['message'])) orders = list() for order_status in order_statuses: order, executed_price = self._create_order(order_status) if asset is None or asset == order.sid: orders.append(order) return orders
def get_open_orders(self, asset='all'): """Retrieve all of the current open orders. Parameters ---------- asset : Asset If passed and not 'all', return only the open orders for the given asset instead of all open orders. Returns ------- open_orders : dict[list[Order]] or list[Order] If 'all' is passed this will return a dict mapping Assets to a list containing all the open orders for the asset. If an asset is passed then this will return a list of the open orders for this asset. """ return self.portfolio.open_orders """ TODO: Why going to the exchange if we already have this info locally? And why creating all these Orders if we later discard them? """ try: if (asset == 'all'): response = self.api.returnopenorders('all') else: response = self.api.returnopenorders(self.get_symbol(asset)) except Exception as e: raise ExchangeRequestError(error=e) if 'error' in response: raise ExchangeRequestError( error='Unable to retrieve open orders: {}'.format( order_statuses['message']) ) print(self.portfolio.open_orders) # TODO: Need to handle openOrders for 'all' orders = list() for order_status in response: order, executed_price = self._create_order( order_status) # will Throw error b/c Polo doesn't track order['symbol'] if asset is None or asset == order.sid: orders.append(order) return orders
def tickers(self, assets): """ Fetch ticket data for assets https://docs.bitfinex.com/v2/reference#rest-public-tickers :param assets: :return: """ symbols = self._get_v2_symbols(assets) log.debug('fetching tickers {}'.format(symbols)) try: self.ask_request() response = requests.get( '{url}/v2/tickers?symbols={symbols}'.format( url=self.url, symbols=','.join(symbols), )) except Exception as e: raise ExchangeRequestError(error=e) if 'error' in response.content: raise ExchangeRequestError(error='Unable to retrieve tickers: {}'. format(response.content)) try: tickers = response.json() except Exception as e: raise ExchangeRequestError(error=e) ticks = dict() for index, ticker in enumerate(tickers): if not len(ticker) == 11: raise ExchangeRequestError( error='Invalid ticker in response: {}'.format(ticker)) ticks[assets[index]] = dict( timestamp=pd.Timestamp.utcnow(), bid=ticker[1], ask=ticker[3], last_price=ticker[7], low=ticker[10], high=ticker[9], volume=ticker[8], ) log.debug('got tickers {}'.format(ticks)) return ticks
def get_order(self, order_id): """Lookup an order based on the order id returned from one of the order functions. Parameters ---------- order_id : str The unique identifier for the order. Returns ------- order : Order The order object. """ try: order = self._portfolio.open_orders[order_id] except Exception as e: raise OrphanOrderError(order_id=order_id, exchange=self.name) return order # TODO: Need to decide whether we fetch orders locally or from exchnage # The code below is ignored try: response = self.api.returnopenorders(self.get_symbol(order.sid)) except Exception as e: raise ExchangeRequestError(error=e) for o in response: if (int(o['orderNumber']) == int(order_id)): return order return None
def cancel_order(self, order_param): """Cancel an open order. Parameters ---------- order_param : str or Order The order_id or order object to cancel. """ if (isinstance(order_param, Order)): order = order_param else: order = self._portfolio.open_orders[order_param] try: response = self.api.cancelorder(order.id) except Exception as e: raise ExchangeRequestError(error=e) if 'error' in response: log.info( 'Unable to cancel order {order_id} on exchange {exchange} ' '{error}.'.format(order_id=order.id, exchange=self.name, error=response['error'])) # raise OrderCancelError( # order_id=order.id, # exchange=self.name, # error=response['error'] # ) self.portfolio.remove_order(order)
def tickers(self, assets): """ As of v1.1, Bittrex only allows one ticker at the time. So we have to make multiple calls to fetch multiple assets. :param assets: :return: """ log.info('retrieving tickers') ticks = dict() for asset in assets: symbol = self.get_symbol(asset) try: self.ask_request() ticker = self.api.getticker(symbol) except Exception as e: raise ExchangeRequestError(error=e) # TODO: catch invalid ticker ticks[asset] = dict(timestamp=pd.Timestamp.utcnow(), bid=ticker['Bid'], ask=ticker['Ask'], last_price=ticker['Last']) log.debug('got tickers {}'.format(ticks)) return ticks
def get_trades(self, asset, my_trades=True, start_dt=None, limit=100): if not my_trades: raise NotImplemented( 'get_trades only supports "my trades"' ) # TODO: is it possible to sort this? Limit is useless otherwise. ccxt_symbol = self.get_symbol(asset) try: trades = self.api.fetch_my_trades( symbol=ccxt_symbol, since=start_dt, limit=limit, ) except RequestTimeout as e: log.warn( 'unable to fetch trades {} / {}: {}'.format( self.name, asset.symbol, e ) ) raise e except (ExchangeError, NetworkError) as e: log.warn( 'unable to fetch trades {} / {}: {}'.format( self.name, asset.symbol, e ) ) raise ExchangeRequestError(error=e) return trades
def init(self): if self._is_init: return exchange_folder = get_exchange_folder(self.name) filename = os.path.join(exchange_folder, 'cctx_markets.json') if os.path.exists(filename): timestamp = os.path.getmtime(filename) dt = pd.to_datetime(timestamp, unit='s', utc=True) if dt >= pd.Timestamp.utcnow().floor('1D'): with open(filename) as f: self.markets = json.load(f) log.debug('loaded markets for {}'.format(self.name)) if self.markets is None: try: markets_symbols = self.api.load_markets() log.debug( 'fetching {} markets:\n{}'.format( self.name, markets_symbols ) ) self.markets = self.api.fetch_markets() with open(filename, 'w+') as f: json.dump(self.markets, f, indent=4) except ExchangeNotAvailable as e: raise ExchangeRequestError(error=e) self.load_assets() self._is_init = True
def get_order(self, order_id, asset_or_symbol=None, return_price=False): if asset_or_symbol is None: log.debug( 'order not found in memory, the request might fail ' 'on some exchanges.' ) try: symbol = self.get_symbol(asset_or_symbol) \ if asset_or_symbol is not None else None order_status = self.api.fetch_order(id=order_id, symbol=symbol) order, executed_price = self._create_order(order_status) if return_price: return order, executed_price else: return order except (ExchangeError, NetworkError) as e: log.warn( 'unable to fetch order {} / {}: {}'.format( self.name, order_id, e ) ) raise ExchangeRequestError(error=e)
def create_order(self, asset, amount, is_buy, style): log.info('creating {} order'.format('buy' if is_buy else 'sell')) exchange_symbol = self.get_symbol(asset) if isinstance(style, LimitOrder) or isinstance(style, StopLimitOrder): if isinstance(style, StopLimitOrder): log.warn('{} will ignore the stop price'.format(self.name)) price = style.get_limit_price(is_buy) try: if is_buy: order_status = self.api.buylimit(exchange_symbol, amount, price) else: order_status = self.api.selllimit(exchange_symbol, abs(amount), price) except Exception as e: raise ExchangeRequestError(error=e) if 'uuid' in order_status: order_id = order_status['uuid'] order = Order(dt=pd.Timestamp.utcnow(), asset=asset, amount=amount, stop=style.get_stop_price(is_buy), limit=style.get_limit_price(is_buy), id=order_id) return order else: raise CreateOrderError(exchange=self.name, error=order_status) else: raise InvalidOrderStyle(exchange=self.name, style=style.__class__.__name__)
def get_symbol_start_date(self, symbol): print(symbol) symbol_v2 = 't' + symbol.upper() """ For each symbol we retrieve candles with Monhtly resolution We get the first month, and query again with daily resolution around that date, and we get the first date """ url = '{url}/v2/candles/trade:1M:{symbol}/hist'.format( url=self.url, symbol=symbol_v2) try: self.ask_request() response = requests.get(url) except Exception as e: raise ExchangeRequestError(error=e) """ If we don't get any data back for our monthly-resolution query it means that symbol started trading less than a month ago, so arbitrarily set the ref. date to 15 days ago to be safe with +/- 31 days """ if (len(response.json())): startmonth = response.json()[-1][0] else: startmonth = int((time.time() - 15 * 24 * 3600) * 1000) """ Query again with daily resolution setting the start and end around the startmonth we got above. Avoid end dates greater than now: time.time() """ url = ('{url}/v2/candles/trade:1D:{symbol}/hist?start={start}' '&end={end}').format(url=self.url, symbol=symbol_v2, start=startmonth - 3600 * 24 * 31 * 1000, end=min(startmonth + 3600 * 24 * 31 * 1000, int(time.time() * 1000))) try: self.ask_request() response = requests.get(url) except Exception as e: raise ExchangeRequestError(error=e) return time.strftime('%Y-%m-%d', time.gmtime(int(response.json()[-1][0] / 1000)))
def get_symbol_start_date(self, symbol): try: r = self.api.returnchartdata(symbol, 86400, pd.to_datetime( '2010-1-1').value // 10 ** 9) except Exception as e: raise ExchangeRequestError(error=e) return time.strftime('%Y-%m-%d', time.gmtime(int(r[0]['date'])))
def get_balances(self): balances = self.api.returnbalances() try: log.debug('retrieving wallets balances') except Exception as e: log.debug(e) raise ExchangeRequestError(error=e) if 'error' in balances: raise ExchangeRequestError( error='unable to fetch balance {}'.format(balances['error'])) std_balances = dict() for (key, value) in iteritems(balances): currency = key.lower() std_balances[currency] = float(value) return std_balances
def tickers(self, assets): """ Retrieve current tick data for the given assets Parameters ---------- assets: list[TradingPair] Returns ------- list[dict[str, float] """ tickers = dict() try: for asset in assets: symbol = self.get_symbol(asset) # TODO: use fetch_tickers() for efficiency # I tried using fetch_tickers() but noticed some # inconsistencies, see issue: # https://github.com/ccxt/ccxt/issues/870 ticker = self.api.fetch_ticker(symbol=symbol) if not ticker: log.warn('ticker not found for {} {}'.format( self.name, symbol )) continue ticker['last_traded'] = from_ms_timestamp(ticker['timestamp']) if 'last_price' not in ticker: # TODO: any more exceptions? ticker['last_price'] = ticker['last'] if 'baseVolume' in ticker and ticker['baseVolume'] is not None: # Using the volume represented in the base currency ticker['volume'] = ticker['baseVolume'] elif 'info' in ticker and 'bidQty' in ticker['info'] \ and 'askQty' in ticker['info']: ticker['volume'] = float(ticker['info']['bidQty']) + \ float(ticker['info']['askQty']) else: ticker['volume'] = 0 tickers[asset] = ticker except ExchangeNotAvailable as e: log.warn( 'unable to fetch ticker: {} {}'.format( self.name, asset.symbol ) ) raise ExchangeRequestError(error=e) return tickers
def create_order(self, asset, amount, is_buy, style): symbol = self.get_symbol(asset) if isinstance(style, ExchangeLimitOrder): price = style.get_limit_price(is_buy) order_type = 'limit' elif isinstance(style, MarketOrder): price = None order_type = 'market' else: raise InvalidOrderStyle(exchange=self.name, style=style.__class__.__name__) side = 'buy' if amount > 0 else 'sell' if hasattr(self.api, 'amount_to_lots'): adj_amount = self.api.amount_to_lots( symbol=symbol, amount=abs(amount), ) if adj_amount != abs(amount): log.info( 'adjusted order amount {} to {} based on lot size'.format( abs(amount), adj_amount, )) else: adj_amount = abs(amount) try: result = self.api.create_order(symbol=symbol, type=order_type, side=side, amount=adj_amount, price=price) except ExchangeNotAvailable as e: log.debug('unable to create order: {}'.format(e)) raise ExchangeRequestError(error=e) except InvalidOrder as e: log.warn('the exchange rejected the order: {}'.format(e)) raise CreateOrderError(exchange=self.name, error=e) if 'info' not in result: raise ValueError('cannot use order without info attribute') final_amount = adj_amount if side == 'buy' else -adj_amount order_id = result['id'] order = Order(dt=pd.Timestamp.utcnow(), asset=asset, amount=final_amount, stop=style.get_stop_price(is_buy), limit=style.get_limit_price(is_buy), id=order_id) return order
def get_balances(self): log.debug('retrieving wallets balances') try: response = self._request('balances', None) balances = response.json() except Exception as e: raise ExchangeRequestError(error=e) if 'message' in balances: raise ExchangeRequestError( error='unable to fetch balance {}'.format(balances['message'])) std_balances = dict() for balance in balances: currency = balance['currency'].lower() std_balances[currency] = float(balance['available']) return std_balances
def get_balances(self): balances = self.api.getbalances() try: log.debug('retrieving wallet balances') self.ask_request() except Exception as e: raise ExchangeRequestError(error=e) std_balances = dict() try: for balance in balances: currency = balance['Currency'].lower() std_balances[currency] = balance['Available'] except TypeError: raise ExchangeRequestError(error=balances) return std_balances
def get_order(self, order_id): log.info('retrieving order {}'.format(order_id)) try: order_status = self.api.getorder(order_id) except Exception as e: raise ExchangeRequestError(error=e) if order_status is None: raise OrderNotFound(order_id=order_id, exchange=self.name) return self._create_order(order_status)
def get_order(self, order_id, asset_or_symbol=None, return_price=False, params={}): """Lookup an order based on the order id returned from one of the order functions. Parameters ---------- order_id : str The unique identifier for the order. asset_or_symbol: Asset or str The asset or the tradingPair symbol of the order. return_price: bool get the trading price in addition to the order params: dict, optional Extra parameters to pass to the exchange Returns ------- order : Order The order object. execution_price: float The execution price per unit of the order if return_price is True """ if asset_or_symbol is None: log.debug('order not found in memory, the request might fail ' 'on some exchanges.') try: symbol = self.get_symbol(asset_or_symbol) \ if asset_or_symbol is not None else None # TODO review if Kucoin still needs this exception with API2.0 if self.api.id == "kucoin": order_status = self.api.fetch_order(id=order_id, symbol=symbol, params=params) else: order_status = self.api.fetch_order(id=order_id, symbol=symbol, params={}) order, executed_price = self._create_order(order_status) if return_price: return order, executed_price else: return order except (ExchangeError, NetworkError) as e: log.warn('unable to fetch order {} / {}: {}'.format( self.name, order_id, e)) raise ExchangeRequestError(error=e)
def tickers(self, assets): """ Fetch ticket data for assets https://docs.bitfinex.com/v2/reference#rest-public-tickers :param assets: :return: """ symbols = self.get_symbols(assets) log.debug('fetching tickers {}'.format(symbols)) try: response = self.api.returnticker() except Exception as e: raise ExchangeRequestError(error=e) if 'error' in response: raise ExchangeRequestError( error='Unable to retrieve tickers: {}'.format( response['error']) ) ticks = dict() for index, symbol in enumerate(symbols): ticks[assets[index]] = dict( timestamp=pd.Timestamp.utcnow(), bid=float(response[symbol]['highestBid']), ask=float(response[symbol]['lowestAsk']), last_price=float(response[symbol]['last']), low=float(response[symbol]['lowestAsk']), # TODO: Polo does not provide low high=float(response[symbol]['highestBid']), # TODO: Polo does not provide high volume=float(response[symbol]['baseVolume']), ) log.debug('got tickers {}'.format(ticks)) return ticks
def get_open_orders(self, asset): symbol = self.get_symbol(asset) try: open_orders = self.api.getopenorders(symbol) except Exception as e: raise ExchangeRequestError(error=e) orders = list() for order_status in open_orders: order = self._create_order(order_status) orders.append(order) return orders
def get_balances(self): try: log.debug('retrieving wallets balances') balances = self.api.fetch_balance() balances_lower = dict() for key in balances: balances_lower[key.lower()] = balances[key] except (ExchangeError, NetworkError) as e: log.warn('unable to fetch balance {}: {}'.format(self.name, e)) raise ExchangeRequestError(error=e) return balances_lower
def process_order(self, order): # TODO: move to parent class after tracking features in the parent if not self.api.has['fetchMyTrades']: return self._process_order_fallback(order) try: all_trades = self.get_trades(order.asset) except RequestTimeout as e: raise ExchangeRequestError(error="Received timeout from exchange") except ExchangeRequestError as e: log.warn('unable to fetch account trades, trying an alternate ' 'method to find executed order {} / {}: {}'.format( order.id, order.asset.symbol, e)) return self._process_order_fallback(order) transactions = [] trades = [t for t in all_trades if t['order'] == order.id] if not trades: log.debug('order {} / {} not found in trades'.format( order.id, order.asset.symbol)) return transactions trades.sort(key=lambda t: t['timestamp'], reverse=False) order.filled = 0 order.commission = 0 for trade in trades: # status property will update automatically filled = trade['amount'] * order.direction order.filled += filled commission = 0 if 'fee' in trade and 'cost' in trade['fee']: commission = trade['fee']['cost'] order.commission += commission order.check_triggers( price=trade['price'], dt=pd.to_datetime(trade['timestamp'], unit='ms', utc=True), ) transaction = Transaction(asset=order.asset, amount=filled, dt=pd.Timestamp.utcnow(), price=trade['price'], order_id=order.id, commission=commission) transactions.append(transaction) order.filled = round(order.filled, order.asset.decimals) order.broker_order_id = ', '.join([t['id'] for t in trades]) return transactions
def cancel_order(self, order_param): order_id = order_param.id \ if isinstance(order_param, Order) else order_param log.info('cancelling order {}'.format(order_id)) try: status = self.api.cancel(order_id) except Exception as e: raise ExchangeRequestError(error=e) if 'message' in status: raise OrderCancelError(order_id=order_id, exchange=self.name, error=status['message'])
def get_balances(self): try: log.debug('retrieving wallets balances') balances = self.api.fetch_balance() balances_lower = dict() for key in balances: balances_lower[key.lower()] = balances[key] except Exception as e: log.debug('error retrieving balances: {}', e) raise ExchangeRequestError(error=e) return balances_lower
def cancel_order(self, order_param, asset_or_symbol=None): order_id = order_param.id \ if isinstance(order_param, Order) else order_param if asset_or_symbol is None: log.debug('order not found in memory, cancelling order might fail ' 'on some exchanges.') try: symbol = self.get_symbol(asset_or_symbol) \ if asset_or_symbol is not None else None self.api.cancel_order(id=order_id, symbol=symbol) except Exception as e: raise ExchangeRequestError(error=e)
def get_order(self, order_id, asset_or_symbol=None): if asset_or_symbol is None: log.debug('order not found in memory, the request might fail ' 'on some exchanges.') try: symbol = self.get_symbol(asset_or_symbol) \ if asset_or_symbol is not None else None order_status = self.api.fetch_order(id=order_id, symbol=symbol) order, executed_price = self._create_order(order_status) except Exception as e: raise ExchangeRequestError(error=e) return order, executed_price
def create_order(self, asset, amount, is_buy, style): """ Creating order on the exchange. :param asset: :param amount: :param is_buy: :param style: :return: """ exchange_symbol = self.get_symbol(asset) if isinstance(style, ExchangeLimitOrder) or isinstance(style, ExchangeStopLimitOrder): if isinstance(style, ExchangeStopLimitOrder): log.warn('{} will ignore the stop price'.format(self.name)) price = style.get_limit_price(is_buy) try: if (is_buy): response = self.api.buy(exchange_symbol, amount, price) else: response = self.api.sell(exchange_symbol, -amount, price) except Exception as e: raise ExchangeRequestError(error=e) date = pd.Timestamp.utcnow() if ('orderNumber' in response): order_id = str(response['orderNumber']) order = Order( dt=date, asset=asset, amount=amount, stop=style.get_stop_price(is_buy), limit=style.get_limit_price(is_buy), id=order_id ) return order else: log.warn( '{} order failed: {}'.format('buy' if is_buy else 'sell', response['error'])) return None else: raise InvalidOrderStyle(exchange=self.name, style=style.__class__.__name__)