Esempio n. 1
0
 def get_order(self, order_id: str, pair: str) -> Dict:
     if self._conf['dry_run']:
         order = self._dry_run_open_orders[order_id]
         order.update({'id': order_id})
         return order
     try:
         return self._api.fetch_order(order_id, pair)
     except ccxt.InvalidOrder as e:
         raise DependencyException(f'Could not get order. Message: {e}')
     except (ccxt.NetworkError, ccxt.ExchangeError) as e:
         raise TemporaryError(
             f'Could not get order due to {e.__class__.__name__}. Message: {e}'
         )
     except ccxt.BaseError as e:
         raise OperationalException(e)
Esempio n. 2
0
    def cancel_order(self, order_id: str, pair: str) -> None:
        if self._config['dry_run']:
            return

        try:
            return self._api.cancel_order(order_id, pair)
        except ccxt.InvalidOrder as e:
            raise InvalidOrderException(
                f'Could not cancel order. Message: {e}')
        except (ccxt.NetworkError, ccxt.ExchangeError) as e:
            raise TemporaryError(
                f'Could not cancel order due to {e.__class__.__name__}. Message: {e}'
            )
        except ccxt.BaseError as e:
            raise OperationalException(e)
Esempio n. 3
0
 def get_order(self, order_id: str, pair: str) -> Dict:
     if self._config['dry_run']:
         order = self._dry_run_open_orders[order_id]
         return order
     try:
         return self._api.fetch_order(order_id, pair)
     except ccxt.InvalidOrder as e:
         raise InvalidOrderException(
             f'Tried to get an invalid order (id: {order_id}). Message: {e}'
         ) from e
     except (ccxt.NetworkError, ccxt.ExchangeError) as e:
         raise TemporaryError(
             f'Could not get order due to {e.__class__.__name__}. Message: {e}'
         ) from e
     except ccxt.BaseError as e:
         raise OperationalException(e) from e
Esempio n. 4
0
    def buy(self, pair: str, ordertype: str, amount: float, rate: float,
            time_in_force) -> Dict:
        if self._conf['dry_run']:
            order_id = f'dry_run_buy_{randint(0, 10**6)}'
            self._dry_run_open_orders[order_id] = {
                'pair': pair,
                'price': rate,
                'amount': amount,
                'type': ordertype,
                'side': 'buy',
                'remaining': 0.0,
                'datetime': arrow.utcnow().isoformat(),
                'status': 'closed',
                'fee': None
            }
            return {'id': order_id}

        try:
            # Set the precision for amount and price(rate) as accepted by the exchange
            amount = self.symbol_amount_prec(pair, amount)
            rate = self.symbol_price_prec(
                pair, rate) if ordertype != 'market' else None

            if time_in_force == 'gtc':
                return self._api.create_order(pair, ordertype, 'buy', amount,
                                              rate)
            else:
                return self._api.create_order(pair, ordertype, 'buy', amount,
                                              rate,
                                              {'timeInForce': time_in_force})

        except ccxt.InsufficientFunds as e:
            raise DependencyException(
                f'Insufficient funds to create limit buy order on market {pair}.'
                f'Tried to buy amount {amount} at rate {rate} (total {rate*amount}).'
                f'Message: {e}')
        except ccxt.InvalidOrder as e:
            raise DependencyException(
                f'Could not create limit buy order on market {pair}.'
                f'Tried to buy amount {amount} at rate {rate} (total {rate*amount}).'
                f'Message: {e}')
        except (ccxt.NetworkError, ccxt.ExchangeError) as e:
            raise TemporaryError(
                f'Could not place buy order due to {e.__class__.__name__}. Message: {e}'
            )
        except ccxt.BaseError as e:
            raise OperationalException(e)
Esempio n. 5
0
    async def _async_get_candle_history(
            self,
            pair: str,
            ticker_interval: str,
            since_ms: Optional[int] = None) -> Tuple[str, str, List]:
        """
        Asyncronously gets candle histories using fetch_ohlcv
        returns tuple: (pair, ticker_interval, ohlcv_list)
        """
        try:
            # fetch ohlcv asynchronously
            s = '(' + arrow.get(
                since_ms //
                1000).isoformat() + ') ' if since_ms is not None else ''
            logger.debug("Fetching pair %s, interval %s, since %s %s...", pair,
                         ticker_interval, since_ms, s)

            data = await self._api_async.fetch_ohlcv(pair,
                                                     timeframe=ticker_interval,
                                                     since=since_ms)

            # Because some exchange sort Tickers ASC and other DESC.
            # Ex: Bittrex returns a list of tickers ASC (oldest first, newest last)
            # when GDAX returns a list of tickers DESC (newest first, oldest last)
            # Only sort if necessary to save computing time
            try:
                if data and data[0][0] > data[-1][0]:
                    data = sorted(data, key=lambda x: x[0])
            except IndexError:
                logger.exception("Error loading %s. Result was %s.", pair,
                                 data)
                return pair, ticker_interval, []
            logger.debug("Done fetching pair %s, interval %s ...", pair,
                         ticker_interval)
            return pair, ticker_interval, data

        except ccxt.NotSupported as e:
            raise OperationalException(
                f'Exchange {self._api.name} does not support fetching historical candlestick data.'
                f'Message: {e}') from e
        except (ccxt.NetworkError, ccxt.ExchangeError) as e:
            raise TemporaryError(
                f'Could not load ticker history due to {e.__class__.__name__}. '
                f'Message: {e}') from e
        except ccxt.BaseError as e:
            raise OperationalException(
                f'Could not fetch ticker data. Msg: {e}') from e
Esempio n. 6
0
def test_rpc_balance_handle(default_conf, mocker):
    mock_balance = {
        'BTC': {
            'free': 10.0,
            'total': 12.0,
            'used': 2.0,
        },
        'ETH': {
            'free': 1.0,
            'total': 5.0,
            'used': 4.0,
        }
    }
    # ETH will be skipped due to mocked Error below

    mocker.patch.multiple(
        'freqtrade.fiat_convert.Market',
        ticker=MagicMock(return_value={'price_usd': 15000.0}),
    )
    patch_coinmarketcap(mocker)
    patch_exchange(mocker)
    mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price',
                 return_value=15000.0)
    mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
    mocker.patch.multiple(
        'freqtrade.exchange.Exchange',
        get_balances=MagicMock(return_value=mock_balance),
        get_ticker=MagicMock(
            side_effect=TemporaryError('Could not load ticker due to xxx')))

    freqtradebot = FreqtradeBot(default_conf)
    patch_get_signal(freqtradebot, (True, False))
    rpc = RPC(freqtradebot)
    rpc._fiat_converter = CryptoToFiatConverter()

    result = rpc._rpc_balance(default_conf['fiat_display_currency'])
    assert prec_satoshi(result['total'], 12)
    assert prec_satoshi(result['value'], 180000)
    assert 'USD' == result['symbol']
    assert result['currencies'] == [{
        'currency': 'BTC',
        'available': 10.0,
        'balance': 12.0,
        'pending': 2.0,
        'est_btc': 12.0,
    }]
    assert result['total'] == 12.0
Esempio n. 7
0
    def get_balances(self) -> dict:
        if self._conf['dry_run']:
            return {}

        try:
            balances = self._api.fetch_balance()
            # Remove additional info from ccxt results
            balances.pop("info", None)
            balances.pop("free", None)
            balances.pop("total", None)
            balances.pop("used", None)

            return balances
        except (ccxt.NetworkError, ccxt.ExchangeError) as e:
            raise TemporaryError(
                f'Could not get balance due to {e.__class__.__name__}. Message: {e}'
            )
        except ccxt.BaseError as e:
            raise OperationalException(e)
Esempio n. 8
0
    def get_trades_for_order(self, order_id: str, pair: str,
                             since: datetime) -> List:
        if self._conf['dry_run']:
            return []
        if not self.exchange_has('fetchMyTrades'):
            return []
        try:
            my_trades = self._api.fetch_my_trades(pair, since.timestamp())
            matched_trades = [
                trade for trade in my_trades if trade['order'] == order_id
            ]

            return matched_trades

        except ccxt.NetworkError as e:
            raise TemporaryError(
                f'Could not get trades due to networking error. Message: {e}')
        except ccxt.BaseError as e:
            raise OperationalException(e)
Esempio n. 9
0
    def get_order_book(self, pair: str, limit: int = 100) -> dict:
        """
        get order book level 2 from exchange

        Notes:
        20180619: bittrex doesnt support limits -.-
        """
        try:

            return self._api.fetch_l2_order_book(pair, limit)
        except ccxt.NotSupported as e:
            raise OperationalException(
                f'Exchange {self._api.name} does not support fetching order book.'
                f'Message: {e}') from e
        except (ccxt.NetworkError, ccxt.ExchangeError) as e:
            raise TemporaryError(
                f'Could not get order book due to {e.__class__.__name__}. Message: {e}'
            ) from e
        except ccxt.BaseError as e:
            raise OperationalException(e) from e
Esempio n. 10
0
 def get_ticker(self, pair: str, refresh: Optional[bool] = True) -> dict:
     if refresh or pair not in self._cached_ticker.keys():
         try:
             data = self._api.fetch_ticker(pair)
             try:
                 self._cached_ticker[pair] = {
                     'bid': float(data['bid']),
                     'ask': float(data['ask']),
                 }
             except KeyError:
                 logger.debug("Could not cache ticker data for %s", pair)
             return data
         except (ccxt.NetworkError, ccxt.ExchangeError) as e:
             raise TemporaryError(
                 f'Could not load ticker history due to {e.__class__.__name__}. Message: {e}'
             )
         except ccxt.BaseError as e:
             raise OperationalException(e)
     else:
         logger.info("returning cached ticker-data for %s", pair)
         return self._cached_ticker[pair]
Esempio n. 11
0
 async def _async_fetch_trades(self,
                               pair: str,
                               since: Optional[int] = None,
                               params: Optional[dict] = None) -> List[Dict]:
     """
     Asyncronously gets trade history using fetch_trades.
     Handles exchange errors, does one call to the exchange.
     :param pair: Pair to fetch trade data for
     :param since: Since as integer timestamp in milliseconds
     returns: List of dicts containing trades
     """
     try:
         # fetch trades asynchronously
         if params:
             logger.debug("Fetching trades for pair %s, params: %s ", pair,
                          params)
             trades = await self._api_async.fetch_trades(pair,
                                                         params=params,
                                                         limit=1000)
         else:
             logger.debug(
                 "Fetching trades for pair %s, since %s %s...", pair, since,
                 '(' + arrow.get(since // 1000).isoformat() +
                 ') ' if since is not None else '')
             trades = await self._api_async.fetch_trades(pair,
                                                         since=since,
                                                         limit=1000)
         return trades
     except ccxt.NotSupported as e:
         raise OperationalException(
             f'Exchange {self._api.name} does not support fetching historical trade data.'
             f'Message: {e}') from e
     except (ccxt.NetworkError, ccxt.ExchangeError) as e:
         raise TemporaryError(
             f'Could not load trade history due to {e.__class__.__name__}. '
             f'Message: {e}') from e
     except ccxt.BaseError as e:
         raise OperationalException(
             f'Could not fetch trade data. Msg: {e}') from e
Esempio n. 12
0
    def get_trades_for_order(self, order_id: str, pair: str,
                             since: datetime) -> List:
        if self._config['dry_run']:
            return []
        if not self.exchange_has('fetchMyTrades'):
            return []
        try:
            # Allow 5s offset to catch slight time offsets (discovered in #1185)
            # since needs to be int in milliseconds
            my_trades = self._api.fetch_my_trades(
                pair, int((since.timestamp() - 5) * 1000))
            matched_trades = [
                trade for trade in my_trades if trade['order'] == order_id
            ]

            return matched_trades

        except ccxt.NetworkError as e:
            raise TemporaryError(
                f'Could not get trades due to networking error. Message: {e}'
            ) from e
        except ccxt.BaseError as e:
            raise OperationalException(e) from e
Esempio n. 13
0
    def get_fee(self,
                symbol='ETH/BTC',
                type='',
                side='',
                amount=1,
                price=1,
                taker_or_maker='maker') -> float:
        try:
            # validate that markets are loaded before trying to get fee
            if self._api.markets is None or len(self._api.markets) == 0:
                self._api.load_markets()

            return self._api.calculate_fee(symbol=symbol,
                                           type=type,
                                           side=side,
                                           amount=amount,
                                           price=price,
                                           takerOrMaker=taker_or_maker)['rate']
        except (ccxt.NetworkError, ccxt.ExchangeError) as e:
            raise TemporaryError(
                f'Could not get fee info due to {e.__class__.__name__}. Message: {e}'
            )
        except ccxt.BaseError as e:
            raise OperationalException(e)
Esempio n. 14
0
 def get_ticker(self, pair: str, refresh: Optional[bool] = True) -> dict:
     if refresh or pair not in self._cached_ticker.keys():
         try:
             if pair not in self._api.markets or not self._api.markets[
                     pair].get('active'):
                 raise DependencyException(f"Pair {pair} not available")
             data = self._api.fetch_ticker(pair)
             try:
                 self._cached_ticker[pair] = {
                     'bid': float(data['bid']),
                     'ask': float(data['ask']),
                 }
             except KeyError:
                 logger.debug("Could not cache ticker data for %s", pair)
             return data
         except (ccxt.NetworkError, ccxt.ExchangeError) as e:
             raise TemporaryError(
                 f'Could not load ticker due to {e.__class__.__name__}. Message: {e}'
             ) from e
         except ccxt.BaseError as e:
             raise OperationalException(e) from e
     else:
         logger.info("returning cached ticker-data for %s", pair)
         return self._cached_ticker[pair]
Esempio n. 15
0
    def stoploss_limit(self, pair: str, amount: float, stop_price: float,
                       rate: float) -> Dict:
        """
        creates a stoploss limit order.
        NOTICE: it is not supported by all exchanges. only binance is tested for now.
        """

        # Set the precision for amount and price(rate) as accepted by the exchange
        amount = self.symbol_amount_prec(pair, amount)
        rate = self.symbol_price_prec(pair, rate)
        stop_price = self.symbol_price_prec(pair, stop_price)

        # Ensure rate is less than stop price
        if stop_price <= rate:
            raise OperationalException(
                'In stoploss limit order, stop price should be more than limit price'
            )

        if self._conf['dry_run']:
            order_id = f'dry_run_buy_{randint(0, 10**6)}'
            self._dry_run_open_orders[order_id] = {
                'info': {},
                'id': order_id,
                'pair': pair,
                'price': stop_price,
                'amount': amount,
                'type': 'stop_loss_limit',
                'side': 'sell',
                'remaining': amount,
                'datetime': arrow.utcnow().isoformat(),
                'status': 'open',
                'fee': None
            }
            return self._dry_run_open_orders[order_id]

        try:
            order = self._api.create_order(pair, 'stop_loss_limit', 'sell',
                                           amount, rate,
                                           {'stopPrice': stop_price})
            logger.info('stoploss limit order added for %s. '
                        'stop price: %s. limit: %s' % (pair, stop_price, rate))
            return order

        except ccxt.InsufficientFunds as e:
            raise DependencyException(
                f'Insufficient funds to place stoploss limit order on market {pair}. '
                f'Tried to put a stoploss amount {amount} with '
                f'stop {stop_price} and limit {rate} (total {rate*amount}).'
                f'Message: {e}')
        except ccxt.InvalidOrder as e:
            raise DependencyException(
                f'Could not place stoploss limit order on market {pair}.'
                f'Tried to place stoploss amount {amount} with '
                f'stop {stop_price} and limit {rate} (total {rate*amount}).'
                f'Message: {e}')
        except (ccxt.NetworkError, ccxt.ExchangeError) as e:
            raise TemporaryError(
                f'Could not place stoploss limit order due to {e.__class__.__name__}. Message: {e}'
            )
        except ccxt.BaseError as e:
            raise OperationalException(e)