async def get_order(self, pair: str, order_id: str): """ TODO: Binance does not return anything useful for the price, may need to try GET /api/v3/myTrades. """ base, quote = common.get_pair_split(pair) symbol = '{}{}'.format(quote, base) results, status = await self.call_extract([ "['result']['status']", "['result']['origQty']", "['result']['executedQty']", "['result']['price']", ], 'getOrder', params=[symbol, order_id], log=True) if status != 200 or results is None or not results[0]: return None status = results[0] quantity = float(results[1]) exec_quantity = float(results[2]) price = float(results[3]) return { 'open': status in ["NEW", "PARTIALLY_FILLED"], 'quantity': quantity, 'remaining': quantity - exec_quantity, 'value': price, 'fees': price * exec_quantity * config['trade_fee_percent'], }
async def sell_limit(self, pair: str, quantity: float, value: float): """ FIXME: This actually submits a market sell and ignores the given value, as the intent is to submit a market sell anyway and the Binance API complains frequently with small values. """ base, quote = common.get_pair_split(pair) symbol = '{}{}'.format(quote, base) step_size, _ = await self._get_step_tick_sizes(symbol) if step_size is None: self.log.error("Could not get step and tick sizes for {}.", symbol) return None quantity_str = self._to_precision(quantity, self._precision(step_size)) results, status = await self.call_extract([ "['result']['clientOrderId']", ], 'newOrderSimple', params=[symbol, 'SELL', 'MARKET', quantity_str], log=True) if status == 400: # Binance still has this dust issue even when using BNB for fees. quantity_str = self._to_precision(quantity - float(step_size), self._precision(step_size)) results, status = await self.call_extract([ "['result']['clientOrderId']", ], 'newOrderSimple', params=[symbol, 'SELL', 'MARKET', quantity_str], log=True) if status != 200 or results is None or results[0] is None: return None return results[0]
async def buy_limit(self, pair: str, quantity: float, value: float): """ """ base, quote = common.get_pair_split(pair) symbol = '{}{}'.format(quote, base) step_size, tick_size = await self._get_step_tick_sizes(symbol) if step_size is None: self.log.error("Could not get step and tick sizes for {}.", symbol) return None quantity_str = self._to_precision(quantity, self._precision(step_size)) value_str = self._to_precision(value, self._precision(tick_size)) results, status = await self.call_extract([ "['result']['clientOrderId']", ], 'newOrder', params=[symbol, 'BUY', 'LIMIT', quantity_str, value_str, 'GTC'], log=True) if status == 400: quantity -= float(step_size) quantity_str = self._to_precision(quantity, self._precision(step_size)) self.log.warning("{} re-trying buy with next lowest step size.", pair) results, status = await self.call_extract([ "['result']['clientOrderId']", ], 'newOrder', params=[symbol, 'BUY', 'LIMIT', quantity_str, value_str, 'GTC'], log=True) if status != 200 or results is None or results[0] is None: return None return results[0]
async def get_last_values(self, pair: str) -> Tuple[float, float]: """ Get the last price and 24-hour volume for a currency pair from the API. Arguments: pair: Currency pair name eg. 'BTC-ETH' Returns: (tuple): A tuple containing: float: The current close price, or None if an error occurred. float: The current 24 hour volume, or None if an error occurred. """ base, quote = common.get_pair_split(pair) symbol = '{}{}'.format(quote, base) prices = await self._get_prices() if prices is None: self.log.error('Could not get price data.') price = None else: price = float(prices[symbol]['price']) changes_24hr = await self._get_24hr_changes() if changes_24hr is None: self.log.error('Could not get 24-hour change data.') volume = None else: volume = float(changes_24hr[symbol]['quoteVolume']) return (price, volume)
async def get_ticks(self, pair: str, length=None) -> List[Dict[str, Any]]: """ Get ticks (closing values and closing times) for a pair from the OKex API. Arguments: pair: The currency pair eg. 'BTC-ETH'. length: Maximum number of ticks to return. Is rounded up to the nearest 500. Defaults to the global minimum needed to perform operations as returned by :meth:`common.get_min_tick_length`. Returns: A list of the raw tick data from the API, or None if an error occurred or no ticks are available. """ base, quote = common.get_pair_split(pair) symbol = '{}_{}'.format(quote, base).lower() tick_length = length if length else common.get_min_tick_length() start_time = time.time() - (tick_length * config['tick_interval_secs']) results, status = await self.call_extract( [ "['data']", ], 'getKlines', params=[0, self.tick_interval_str], path_params=[symbol], retry_data=True) if status != 200 or results is None or results[ 0] is None or not results[0]: return None ticks = [] for result in results[0]: close = float(result['close']) volume = float(result['volume']) ticks.append({ 'T': result['createdDate'] / 1000, 'H': float(result['high']), 'L': float(result['low']), 'O': float(result['open']), 'C': close, 'V': volume, 'BV': volume * close }) if tick_length - len(ticks) > 0: ticks = await self._get_upscaled_ticks(symbol, start_time, ticks[0]['T']) + ticks return ticks if ticks else None
async def cancel_order(self, pair: str, order_id: str): """ """ base, quote = common.get_pair_split(pair) symbol = '{}{}'.format(quote, base) results, status = await self.call_extract([ "['result']['clientOrderId']", ], 'cancelOrder', params=[symbol, order_id], log=True) if status != 200 or results is None or results[0] is None: return None return True
async def get_ticks(self, pair: str, length: int=None) -> List[Dict[str, Any]]: """ Get ticks (closing values and closing times) for a pair from the Binance API. Arguments: pair: The currency pair eg. 'BTC-ETH'. length: Maximum number of ticks to return. Is rounded up to the nearest 500. Defaults to the global minimum needed to perform operations as returned by :meth:`common.get_min_tick_length`. Returns: A list of the raw tick data from the API, or None if an error occurred or no ticks are available. """ base, quote = common.get_pair_split(pair) tick_length = length if length else common.get_min_tick_length() symbol = '{}{}'.format(quote, base) end_time = int(time.time() * 1000) ticks = [] while len(ticks) < tick_length: tick_batch = [] results, status = await self.call_extract([ "['result']", ], 'getKlines', params=[symbol, self.tick_interval_str, end_time], retry_data=True) if status != 200 or results is None or results[0] is None: self.log.error("Failed getting klines: status {}, results {}.", status, results) break if not results[0]: break for result in results[0]: tick_batch.append({ 'T': result[0] / 1000, 'O': float(result[1]), 'H': float(result[2]), 'L': float(result[3]), 'C': float(result[4]), 'V': float(result[5]), 'BV': float(result[7]) }) end_time = results[0][0][0] - (config['tick_interval_secs'] * 1000) ticks = tick_batch + ticks return ticks if ticks else None
async def get_tick_range(self, pair: str, start_time: float, end_time: float) -> List[Dict[str, Any]]: """ Get a range of ticks (closing values and closing times) for a pair from the Binance API. Arguments: pair: The currency pair eg. 'BTC-ETH'. start_time: Timestamp to start at. end_time: Timestamp to start at. Returns: A list of the raw tick data from the API. """ base, quote = common.get_pair_split(pair) symbol = '{}{}'.format(quote, base) start_param = int(start_time * 1000) completed = False ticks = [] while not completed: tick_batch = [] results, status = await self.call_extract([ "['result']", ], 'getOldKlines', params=[symbol, self.tick_interval_str, start_param], retry_data=True) if status != 200 or results is None or results[0] is None or not results[0]: if not ticks: ticks = None break for result in results[0]: tick_batch.append({ 'T': result[0] / 1000, 'O': float(result[1]), 'H': float(result[2]), 'L': float(result[3]), 'C': float(result[4]), 'V': float(result[5]), 'BV': float(result[7]) }) start_param = results[0][-1][0] + (config['tick_interval_secs'] * 1000) completed = tick_batch[-1]['T'] > end_time ticks.extend(tick_batch) return ticks
async def get_last_values(self, pair: str) -> Tuple[float, float]: """ Get the last price and 24-hour volume for a currency pair from the API. Arguments: pair: Currency pair name eg. 'BTC-ETH' Returns: (tuple): A tuple containing: float: The current close price, or None if an error occurred. float: The current 24 hour volume, or None if an error occurred. """ tickers = await self._get_tickers() if tickers is None: return None base, quote = common.get_pair_split(pair) symbol = '{}_{}'.format(quote, base).lower() last_value = float(tickers[symbol]['last']) base_volume = float(tickers[symbol]['volume']) * last_value return (last_value, base_volume)