예제 #1
0
    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'],
        }
예제 #2
0
    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]
예제 #3
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]
예제 #4
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)
예제 #5
0
    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
예제 #6
0
    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
예제 #7
0
    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
예제 #8
0
    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
예제 #9
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.
        """

        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)