Esempio n. 1
0
    def create_order(self,
                     pair: str,
                     ordertype: str,
                     side: str,
                     amount: float,
                     rate: float,
                     params: Dict = {}) -> Dict:
        try:
            # Set the precision for amount and price(rate) as accepted by the exchange
            amount = self.amount_to_precision(pair, amount)
            needs_price = (ordertype != 'market' or self._api.options.get(
                "createMarketBuyOrderRequiresPrice", False))
            rate_for_order = self.price_to_precision(
                pair, rate) if needs_price else None

            return self._api.create_order(pair, ordertype, side, amount,
                                          rate_for_order, params)

        except ccxt.InsufficientFunds as e:
            raise DependencyException(
                f'Insufficient funds to create {ordertype} {side} order on market {pair}.'
                f'Tried to {side} amount {amount} at rate {rate}.'
                f'Message: {e}') from e
        except ccxt.InvalidOrder as e:
            raise DependencyException(
                f'Could not create {ordertype} {side} order on market {pair}.'
                f'Tried to {side} amount {amount} at rate {rate}.'
                f'Message: {e}') from e
        except (ccxt.NetworkError, ccxt.ExchangeError) as e:
            raise TemporaryError(
                f'Could not place {side} order due to {e.__class__.__name__}. Message: {e}'
            ) from e
        except ccxt.BaseError as e:
            raise OperationalException(e) from e
Esempio n. 2
0
    def update_from_ccxt_object(self, order):
        """
        Update Order from ccxt response
        Only updates if fields are available from ccxt -
        """
        if self.order_id != str(order['id']):
            raise DependencyException("Order-id's don't match")

        self.status = order.get('status', self.status)
        self.symbol = order.get('symbol', self.symbol)
        self.order_type = order.get('type', self.order_type)
        self.side = order.get('side', self.side)
        self.price = order.get('price', self.price)
        self.amount = order.get('amount', self.amount)
        self.filled = order.get('filled', self.filled)
        self.remaining = order.get('remaining', self.remaining)
        self.cost = order.get('cost', self.cost)
        if 'timestamp' in order and order['timestamp'] is not None:
            self.order_date = datetime.fromtimestamp(order['timestamp'] / 1000,
                                                     tz=timezone.utc)

        self.ft_is_open = True
        if self.status in ('closed', 'canceled', 'cancelled'):
            self.ft_is_open = False
            if order.get('filled', 0) > 0:
                self.order_filled_date = arrow.utcnow().datetime
        self.order_update_date = arrow.utcnow().datetime
Esempio n. 3
0
    def _safe_sell_amount(self, pair: str, amount: float) -> float:
        """
        Get sellable amount.
        Should be trade.amount - but will fall back to the available amount if necessary.
        This should cover cases where get_real_amount() was not able to update the amount
        for whatever reason.
        :param pair: Pair we're trying to sell
        :param amount: amount we expect to be available
        :return: amount to sell
        :raise: DependencyException: if available balance is not within 2% of the available amount.
        """
        # Update wallets to ensure amounts tied up in a stoploss is now free!
        self.wallets.update()

        wallet_amount = self.wallets.get_free(pair.split('/')[0])
        logger.debug(
            f"{pair} - Wallet: {wallet_amount} - Trade-amount: {amount}")
        if wallet_amount >= amount:
            return amount
        elif wallet_amount > amount * 0.98:
            logger.info(f"{pair} - Falling back to wallet-amount.")
            return wallet_amount
        else:
            raise DependencyException(
                f"Not enough amount to sell. Trade-amount: {amount}, Wallet: {wallet_amount}"
            )
Esempio n. 4
0
    def _check_available_stake_amount(self, stake_amount: float) -> float:
        """
        Check if stake amount can be fulfilled with the available balance
        for the stake currency
        :return: float: Stake amount
        :raise: DependencyException if balance is lower than stake-amount
        """
        available_amount = self._get_available_stake_amount()

        if self._config['amend_last_stake_amount']:
            # Remaining amount needs to be at least stake_amount * last_stake_amount_min_ratio
            # Otherwise the remaining amount is too low to trade.
            if available_amount > (
                    stake_amount *
                    self._config['last_stake_amount_min_ratio']):
                stake_amount = min(stake_amount, available_amount)
            else:
                stake_amount = 0

        if available_amount < stake_amount:
            raise DependencyException(
                f"Available balance ({available_amount} {self._config['stake_currency']}) is "
                f"lower than stake amount ({stake_amount} {self._config['stake_currency']})"
            )

        return stake_amount
 def check_abort(self):
     """
     Check if abort was requested, raise DependencyException if that's the case
     Only applies to Interactive backtest mode (webserver mode)
     """
     if self.abort:
         self.abort = False
         raise DependencyException("Stop requested")
Esempio n. 6
0
 def get_valid_pair_combination(self, curr_1: str, curr_2: str) -> str:
     """
     Get valid pair combination of curr_1 and curr_2 by trying both combinations.
     """
     for pair in [f"{curr_1}/{curr_2}", f"{curr_2}/{curr_1}"]:
         if pair in self.markets and self.markets[pair].get('active'):
             return pair
     raise DependencyException(f"Could not combine {curr_1} and {curr_2} to get a valid pair.")
Esempio n. 7
0
    def get_real_amount(self,
                        trade: Trade,
                        order: Dict,
                        order_amount: float = None) -> float:
        """
        Get real amount for the trade
        Necessary for exchanges which charge fees in base currency (e.g. binance)
        """
        if order_amount is None:
            order_amount = order['amount']
        # Only run for closed orders
        if trade.fee_open == 0 or order['status'] == 'open':
            return order_amount

        # use fee from order-dict if possible
        if ('fee' in order and order['fee'] is not None
                and (order['fee'].keys() >= {'currency', 'cost'})):
            if (order['fee']['currency'] is not None
                    and order['fee']['cost'] is not None
                    and trade.pair.startswith(order['fee']['currency'])):
                new_amount = order_amount - order['fee']['cost']
                logger.info(
                    "Applying fee on amount for %s (from %s to %s) from Order",
                    trade, order['amount'], new_amount)
                return new_amount

        # Fallback to Trades
        trades = self.exchange.get_trades_for_order(trade.open_order_id,
                                                    trade.pair,
                                                    trade.open_date)

        if len(trades) == 0:
            logger.info(
                "Applying fee on amount for %s failed: myTrade-Dict empty found",
                trade)
            return order_amount
        amount = 0
        fee_abs = 0
        for exectrade in trades:
            amount += exectrade['amount']
            if ("fee" in exectrade and exectrade['fee'] is not None
                    and (exectrade['fee'].keys() >= {'currency', 'cost'})):
                # only applies if fee is in quote currency!
                if (exectrade['fee']['currency'] is not None
                        and exectrade['fee']['cost'] is not None and
                        trade.pair.startswith(exectrade['fee']['currency'])):
                    fee_abs += exectrade['fee']['cost']

        if not isclose(amount, order_amount,
                       abs_tol=constants.MATH_CLOSE_PREC):
            logger.warning(
                f"Amount {amount} does not match amount {trade.amount}")
            raise DependencyException("Half bought? Amounts don't match")
        real_amount = amount - fee_abs
        if fee_abs != 0:
            logger.info(f"Applying fee on amount for {trade} "
                        f"(from {order_amount} to {real_amount}) from Trades")
        return real_amount
Esempio n. 8
0
    def stoploss_limit(self, pair: str, amount: float, stop_price: float,
                       rate: float) -> Dict:
        """
        creates a stoploss limit order.
        this stoploss-limit is binance-specific.
        It may work with a limited number of other exchanges, but this has not been tested yet.

        """
        ordertype = "stop_loss_limit"

        stop_price = self.price_to_precision(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._config['dry_run']:
            dry_order = self.dry_run_order(pair, ordertype, "sell", amount,
                                           stop_price)
            return dry_order

        try:
            params = self._params.copy()
            params.update({'stopPrice': stop_price})

            amount = self.amount_to_precision(pair, amount)

            rate = self.price_to_precision(pair, rate)

            order = self._api.create_order(pair, ordertype, 'sell', amount,
                                           rate, params)
            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 create {ordertype} sell order on market {pair}.'
                f'Tried to sell amount {amount} at rate {rate}. '
                f'Message: {e}') from e
        except ccxt.InvalidOrder as e:
            # Errors:
            # `binance Order would trigger immediately.`
            raise InvalidOrderException(
                f'Could not create {ordertype} sell order on market {pair}. '
                f'Tried to sell amount {amount} at rate {rate}. '
                f'Message: {e}') from e
        except (ccxt.NetworkError, ccxt.ExchangeError) as e:
            raise TemporaryError(
                f'Could not place sell order due to {e.__class__.__name__}. Message: {e}'
            ) from e
        except ccxt.BaseError as e:
            raise OperationalException(e) from e
Esempio n. 9
0
 def fetch_ticker(self, pair: str) -> dict:
     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)
         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
Esempio n. 10
0
    def stoploss(self, pair: str, amount: float, stop_price: float,
                 order_types: Dict) -> Dict:
        """
        Creates a stoploss order.
        depending on order_types.stoploss configuration, uses 'market' or limit order.

        Limit orders are defined by having orderPrice set, otherwise a market order is used.
        """
        limit_price_pct = order_types.get('stoploss_on_exchange_limit_ratio',
                                          0.99)
        limit_rate = stop_price * limit_price_pct

        ordertype = "stop"

        stop_price = self.price_to_precision(pair, stop_price)

        if self._config['dry_run']:
            dry_order = self.dry_run_order(pair, ordertype, "sell", amount,
                                           stop_price)
            return dry_order

        try:
            params = self._params.copy()
            if order_types.get('stoploss', 'market') == 'limit':
                # set orderPrice to place limit order, otherwise it's a market order
                params['orderPrice'] = limit_rate

            amount = self.amount_to_precision(pair, amount)

            order = self._api.create_order(symbol=pair,
                                           type=ordertype,
                                           side='sell',
                                           amount=amount,
                                           price=stop_price,
                                           params=params)
            logger.info('stoploss order added for %s. '
                        'stop price: %s.', pair, stop_price)
            return order
        except ccxt.InsufficientFunds as e:
            raise DependencyException(
                f'Insufficient funds to create {ordertype} sell order on market {pair}. '
                f'Tried to create stoploss with amount {amount} at stoploss {stop_price}. '
                f'Message: {e}') from e
        except ccxt.InvalidOrder as e:
            raise InvalidOrderException(
                f'Could not create {ordertype} sell order on market {pair}. '
                f'Tried to create stoploss with amount {amount} at stoploss {stop_price}. '
                f'Message: {e}') from e
        except (ccxt.NetworkError, ccxt.ExchangeError) as e:
            raise TemporaryError(
                f'Could not place sell order due to {e.__class__.__name__}. Message: {e}'
            ) from e
        except ccxt.BaseError as e:
            raise OperationalException(e) from e
Esempio n. 11
0
def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None:
    mocker.patch.multiple(
        'freqtrade.rpc.fiat_convert.CoinGeckoAPI',
        get_price=MagicMock(return_value={'bitcoin': {
            'usd': 15000.0
        }}),
    )
    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',
        fetch_ticker=ticker,
        get_fee=fee,
    )

    freqtradebot = get_patched_freqtradebot(mocker, default_conf)
    patch_get_signal(freqtradebot, (True, False))
    rpc = RPC(freqtradebot)

    freqtradebot.state = State.RUNNING
    with pytest.raises(RPCException, match=r'.*no active trade*'):
        rpc._rpc_status_table(default_conf['stake_currency'], 'USD')

    freqtradebot.enter_positions()

    result, headers = rpc._rpc_status_table(default_conf['stake_currency'],
                                            'USD')
    assert "Since" in headers
    assert "Pair" in headers
    assert 'instantly' == result[0][2]
    assert 'ETH/BTC' in result[0][1]
    assert '-0.41%' == result[0][3]
    # Test with fiatconvert

    rpc._fiat_converter = CryptoToFiatConverter()
    result, headers = rpc._rpc_status_table(default_conf['stake_currency'],
                                            'USD')
    assert "Since" in headers
    assert "Pair" in headers
    assert 'instantly' == result[0][2]
    assert 'ETH/BTC' in result[0][1]
    assert '-0.41% (-0.06)' == result[0][3]

    mocker.patch(
        'freqtrade.freqtradebot.FreqtradeBot.get_sell_rate',
        MagicMock(
            side_effect=DependencyException("Pair 'ETH/BTC' not available")))
    result, headers = rpc._rpc_status_table(default_conf['stake_currency'],
                                            'USD')
    assert 'instantly' == result[0][2]
    assert 'ETH/BTC' in result[0][1]
    assert 'nan%' == result[0][3]
Esempio n. 12
0
    def handle_trade(self, trade: Trade) -> bool:
        """
        Sells the current pair if the threshold is reached and updates the trade record.
        :return: True if trade has been sold, False otherwise
        """
        if not trade.is_open:
            raise DependencyException(
                f'Attempt to handle closed trade: {trade}')

        logger.debug('Handling %s ...', trade)

        (buy, sell) = (False, False)

        config_ask_strategy = self.config.get('ask_strategy', {})

        if (config_ask_strategy.get('use_sell_signal', True)
                or config_ask_strategy.get('ignore_roi_if_buy_signal')):
            (buy, sell) = self.strategy.get_signal(
                trade.pair, self.strategy.ticker_interval,
                self.dataprovider.ohlcv(trade.pair,
                                        self.strategy.ticker_interval))

        if config_ask_strategy.get('use_order_book', False):
            logger.info('Using order book for selling...')
            # logger.debug('Order book %s',orderBook)
            order_book_min = config_ask_strategy.get('order_book_min', 1)
            order_book_max = config_ask_strategy.get('order_book_max', 1)

            order_book = self.exchange.get_order_book(trade.pair,
                                                      order_book_max)

            for i in range(order_book_min, order_book_max + 1):
                order_book_rate = order_book['asks'][i - 1][0]
                logger.info('  order book asks top %s: %0.8f', i,
                            order_book_rate)
                sell_rate = order_book_rate

                if self._check_and_execute_sell(trade, sell_rate, buy, sell):
                    return True

        else:
            logger.debug('checking sell')
            sell_rate = self.get_sell_rate(trade.pair, True)
            if self._check_and_execute_sell(trade, sell_rate, buy, sell):
                return True

        logger.debug('Found no sell signal for %s.', trade)
        return False
Esempio n. 13
0
def setup_optimize_configuration(args: Dict[str, Any],
                                 method: RunMode) -> Dict[str, Any]:
    """
    Prepare the configuration for the Hyperopt module
    :param args: Cli args from Arguments()
    :return: Configuration
    """
    config = setup_utils_configuration(args, method)

    if method == RunMode.BACKTEST:
        if config['stake_amount'] == constants.UNLIMITED_STAKE_AMOUNT:
            raise DependencyException(
                'stake amount could not be "%s" for backtesting' %
                constants.UNLIMITED_STAKE_AMOUNT)

    return config
Esempio n. 14
0
def test_ticker(mocker, default_conf, tickers):
    ticker_mock = MagicMock(return_value=tickers()['ETH/BTC'])
    mocker.patch("freqtrade.exchange.Exchange.fetch_ticker", ticker_mock)
    exchange = get_patched_exchange(mocker, default_conf)
    dp = DataProvider(default_conf, exchange)
    res = dp.ticker('ETH/BTC')
    assert type(res) is dict
    assert 'symbol' in res
    assert res['symbol'] == 'ETH/BTC'

    ticker_mock = MagicMock(side_effect=DependencyException('Pair not found'))
    mocker.patch("freqtrade.exchange.Exchange.fetch_ticker", ticker_mock)
    exchange = get_patched_exchange(mocker, default_conf)
    dp = DataProvider(default_conf, exchange)
    res = dp.ticker('UNITTEST/BTC')
    assert res == {}
Esempio n. 15
0
    def stoploss(self, pair: str, amount: float, stop_price: float,
                 order_types: Dict) -> Dict:
        """
        Creates a stoploss market order.
        Stoploss market orders is the only stoploss type supported by kraken.
        """

        ordertype = "stop-loss"

        stop_price = self.price_to_precision(pair, stop_price)

        if self._config['dry_run']:
            dry_order = self.dry_run_order(pair, ordertype, "sell", amount,
                                           stop_price)
            return dry_order

        try:
            params = self._params.copy()

            amount = self.amount_to_precision(pair, amount)

            order = self._api.create_order(symbol=pair,
                                           type=ordertype,
                                           side='sell',
                                           amount=amount,
                                           price=stop_price,
                                           params=params)
            logger.info('stoploss order added for %s. '
                        'stop price: %s.', pair, stop_price)
            return order
        except ccxt.InsufficientFunds as e:
            raise DependencyException(
                f'Insufficient funds to create {ordertype} sell order on market {pair}.'
                f'Tried to create stoploss with amount {amount} at stoploss {stop_price}. '
                f'Message: {e}') from e
        except ccxt.InvalidOrder as e:
            raise InvalidOrderException(
                f'Could not create {ordertype} sell order on market {pair}. '
                f'Tried to create stoploss with amount {amount} at stoploss {stop_price}. '
                f'Message: {e}') from e
        except (ccxt.NetworkError, ccxt.ExchangeError) as e:
            raise TemporaryError(
                f'Could not place sell order due to {e.__class__.__name__}. Message: {e}'
            ) from e
        except ccxt.BaseError as e:
            raise OperationalException(e) from e
def setup_optimize_configuration(args: Dict[str, Any], method: RunMode) -> Dict[str, Any]:
    """
    Prepare the configuration for the Hyperopt module
    :param args: Cli args from Arguments()
    :return: Configuration
    """
    config = setup_utils_configuration(args, method)

    no_unlimited_runmodes = {
        RunMode.BACKTEST: 'backtesting',
        RunMode.HYPEROPT: 'hyperoptimization',
    }
    if (method in no_unlimited_runmodes.keys() and
            config['stake_amount'] == constants.UNLIMITED_STAKE_AMOUNT):
        raise DependencyException(
            f'The value of `stake_amount` cannot be set as "{constants.UNLIMITED_STAKE_AMOUNT}" '
            f'for {no_unlimited_runmodes[method]}')

    return config
Esempio n. 17
0
 def fetch_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. 18
0
def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
    mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
    mocker.patch.multiple(
        'freqtrade.exchange.Exchange',
        fetch_ticker=ticker,
        get_fee=fee,
    )

    freqtradebot = get_patched_freqtradebot(mocker, default_conf)
    patch_get_signal(freqtradebot, (True, False))
    rpc = RPC(freqtradebot)

    freqtradebot.state = State.RUNNING
    with pytest.raises(RPCException, match=r'.*no active trade*'):
        rpc._rpc_trade_status()

    freqtradebot.enter_positions()
    results = rpc._rpc_trade_status()
    assert {
        'trade_id': 1,
        'pair': 'ETH/BTC',
        'base_currency': 'BTC',
        'open_date': ANY,
        'open_date_hum': ANY,
        'is_open': ANY,
        'fee_open': ANY,
        'fee_close': ANY,
        'open_rate_requested': ANY,
        'open_trade_price': ANY,
        'close_rate_requested': ANY,
        'sell_reason': ANY,
        'min_rate': ANY,
        'max_rate': ANY,
        'strategy': ANY,
        'ticker_interval': ANY,
        'open_order_id': ANY,
        'close_date': None,
        'close_date_hum': None,
        'open_rate': 1.098e-05,
        'close_rate': None,
        'current_rate': 1.099e-05,
        'amount': 91.07468124,
        'stake_amount': 0.001,
        'close_profit': None,
        'current_profit': -0.41,
        'stop_loss': 0.0,
        'initial_stop_loss': 0.0,
        'initial_stop_loss_pct': None,
        'stop_loss_pct': None,
        'open_order': '(limit buy rem=0.00000000)'
    } == results[0]

    mocker.patch(
        'freqtrade.freqtradebot.FreqtradeBot.get_sell_rate',
        MagicMock(
            side_effect=DependencyException(f"Pair 'ETH/BTC' not available")))
    results = rpc._rpc_trade_status()
    assert isnan(results[0]['current_profit'])
    assert isnan(results[0]['current_rate'])
    assert {
        'trade_id': 1,
        'pair': 'ETH/BTC',
        'base_currency': 'BTC',
        'open_date': ANY,
        'open_date_hum': ANY,
        'is_open': ANY,
        'fee_open': ANY,
        'fee_close': ANY,
        'open_rate_requested': ANY,
        'open_trade_price': ANY,
        'close_rate_requested': ANY,
        'sell_reason': ANY,
        'min_rate': ANY,
        'max_rate': ANY,
        'strategy': ANY,
        'ticker_interval': ANY,
        'open_order_id': ANY,
        'close_date': None,
        'close_date_hum': None,
        'open_rate': 1.098e-05,
        'close_rate': None,
        'current_rate': ANY,
        'amount': 91.07468124,
        'stake_amount': 0.001,
        'close_profit': None,
        'current_profit': ANY,
        'stop_loss': 0.0,
        'initial_stop_loss': 0.0,
        'initial_stop_loss_pct': None,
        'stop_loss_pct': None,
        'open_order': '(limit buy rem=0.00000000)'
    } == results[0]
Esempio n. 19
0
def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee,
                              limit_buy_order, limit_sell_order,
                              mocker) -> None:
    mocker.patch.multiple(
        'freqtrade.rpc.fiat_convert.CoinGeckoAPI',
        get_price=MagicMock(return_value={'bitcoin': {
            'usd': 15000.0
        }}),
    )
    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',
        fetch_ticker=ticker,
        get_fee=fee,
    )

    freqtradebot = get_patched_freqtradebot(mocker, default_conf)
    patch_get_signal(freqtradebot, (True, False))
    stake_currency = default_conf['stake_currency']
    fiat_display_currency = default_conf['fiat_display_currency']

    rpc = RPC(freqtradebot)
    rpc._fiat_converter = CryptoToFiatConverter()

    res = rpc._rpc_trade_statistics(stake_currency, fiat_display_currency)
    assert res['trade_count'] == 0
    assert res['first_trade_date'] == ''
    assert res['first_trade_timestamp'] == 0
    assert res['latest_trade_date'] == ''
    assert res['latest_trade_timestamp'] == 0

    # Create some test data
    freqtradebot.enter_positions()
    trade = Trade.query.first()
    # Simulate fulfilled LIMIT_BUY order for trade
    trade.update(limit_buy_order)

    # Update the ticker with a market going up
    mocker.patch.multiple('freqtrade.exchange.Exchange',
                          fetch_ticker=ticker_sell_up)
    trade.update(limit_sell_order)
    trade.close_date = datetime.utcnow()
    trade.is_open = False

    freqtradebot.enter_positions()
    trade = Trade.query.first()
    # Simulate fulfilled LIMIT_BUY order for trade
    trade.update(limit_buy_order)

    # Update the ticker with a market going up
    mocker.patch.multiple('freqtrade.exchange.Exchange',
                          fetch_ticker=ticker_sell_up)
    trade.update(limit_sell_order)
    trade.close_date = datetime.utcnow()
    trade.is_open = False

    stats = rpc._rpc_trade_statistics(stake_currency, fiat_display_currency)
    assert prec_satoshi(stats['profit_closed_coin'], 6.217e-05)
    assert prec_satoshi(stats['profit_closed_percent'], 6.2)
    assert prec_satoshi(stats['profit_closed_fiat'], 0.93255)
    assert prec_satoshi(stats['profit_all_coin'], 5.802e-05)
    assert prec_satoshi(stats['profit_all_percent'], 2.89)
    assert prec_satoshi(stats['profit_all_fiat'], 0.8703)
    assert stats['trade_count'] == 2
    assert stats['first_trade_date'] == 'just now'
    assert stats['latest_trade_date'] == 'just now'
    assert stats['avg_duration'] == '0:00:00'
    assert stats['best_pair'] == 'ETH/BTC'
    assert prec_satoshi(stats['best_rate'], 6.2)

    # Test non-available pair
    mocker.patch(
        'freqtrade.freqtradebot.FreqtradeBot.get_sell_rate',
        MagicMock(
            side_effect=DependencyException("Pair 'ETH/BTC' not available")))
    stats = rpc._rpc_trade_statistics(stake_currency, fiat_display_currency)
    assert stats['trade_count'] == 2
    assert stats['first_trade_date'] == 'just now'
    assert stats['latest_trade_date'] == 'just now'
    assert stats['avg_duration'] == '0:00:00'
    assert stats['best_pair'] == 'ETH/BTC'
    assert prec_satoshi(stats['best_rate'], 6.2)
    assert isnan(stats['profit_all_coin'])
Esempio n. 20
0
def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
    mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
    mocker.patch.multiple(
        'freqtrade.exchange.Exchange',
        fetch_ticker=ticker,
        get_fee=fee,
    )

    freqtradebot = get_patched_freqtradebot(mocker, default_conf)
    patch_get_signal(freqtradebot, (True, False))
    rpc = RPC(freqtradebot)

    freqtradebot.state = State.RUNNING
    with pytest.raises(RPCException, match=r'.*no active trade*'):
        rpc._rpc_trade_status()

    freqtradebot.enter_positions()
    trades = Trade.get_open_trades()
    trades[0].open_order_id = None
    freqtradebot.exit_positions(trades)

    results = rpc._rpc_trade_status()
    assert results[0] == {
        'trade_id': 1,
        'pair': 'ETH/BTC',
        'base_currency': 'BTC',
        'open_date': ANY,
        'open_date_hum': ANY,
        'open_timestamp': ANY,
        'is_open': ANY,
        'fee_open': ANY,
        'fee_open_cost': ANY,
        'fee_open_currency': ANY,
        'fee_close': fee.return_value,
        'fee_close_cost': ANY,
        'fee_close_currency': ANY,
        'open_rate_requested': ANY,
        'open_trade_price': 0.0010025,
        'close_rate_requested': ANY,
        'sell_reason': ANY,
        'sell_order_status': ANY,
        'min_rate': ANY,
        'max_rate': ANY,
        'strategy': ANY,
        'ticker_interval': ANY,
        'timeframe': ANY,
        'open_order_id': ANY,
        'close_date': None,
        'close_date_hum': None,
        'close_timestamp': None,
        'open_rate': 1.098e-05,
        'close_rate': None,
        'current_rate': 1.099e-05,
        'amount': 91.07468124,
        'stake_amount': 0.001,
        'close_profit': None,
        'close_profit_pct': None,
        'close_profit_abs': None,
        'current_profit': -0.00408133,
        'current_profit_pct': -0.41,
        'current_profit_abs': -4.09e-06,
        'stop_loss': 9.882e-06,
        'stop_loss_abs': 9.882e-06,
        'stop_loss_pct': -10.0,
        'stop_loss_ratio': -0.1,
        'stoploss_order_id': None,
        'stoploss_last_update': ANY,
        'stoploss_last_update_timestamp': ANY,
        'initial_stop_loss': 9.882e-06,
        'initial_stop_loss_abs': 9.882e-06,
        'initial_stop_loss_pct': -10.0,
        'initial_stop_loss_ratio': -0.1,
        'stoploss_current_dist': -1.1080000000000002e-06,
        'stoploss_current_dist_ratio': -0.10081893,
        'stoploss_entry_dist': -0.00010475,
        'stoploss_entry_dist_ratio': -0.10448878,
        'open_order': None,
        'exchange': 'bittrex',
    }

    mocker.patch(
        'freqtrade.freqtradebot.FreqtradeBot.get_sell_rate',
        MagicMock(
            side_effect=DependencyException("Pair 'ETH/BTC' not available")))
    results = rpc._rpc_trade_status()
    assert isnan(results[0]['current_profit'])
    assert isnan(results[0]['current_rate'])
    assert results[0] == {
        'trade_id': 1,
        'pair': 'ETH/BTC',
        'base_currency': 'BTC',
        'open_date': ANY,
        'open_date_hum': ANY,
        'open_timestamp': ANY,
        'is_open': ANY,
        'fee_open': ANY,
        'fee_open_cost': ANY,
        'fee_open_currency': ANY,
        'fee_close': fee.return_value,
        'fee_close_cost': ANY,
        'fee_close_currency': ANY,
        'open_rate_requested': ANY,
        'open_trade_price': ANY,
        'close_rate_requested': ANY,
        'sell_reason': ANY,
        'sell_order_status': ANY,
        'min_rate': ANY,
        'max_rate': ANY,
        'strategy': ANY,
        'ticker_interval': ANY,
        'timeframe': ANY,
        'open_order_id': ANY,
        'close_date': None,
        'close_date_hum': None,
        'close_timestamp': None,
        'open_rate': 1.098e-05,
        'close_rate': None,
        'current_rate': ANY,
        'amount': 91.07468124,
        'stake_amount': 0.001,
        'close_profit': None,
        'close_profit_pct': None,
        'close_profit_abs': None,
        'current_profit': ANY,
        'current_profit_pct': ANY,
        'current_profit_abs': ANY,
        'stop_loss': 9.882e-06,
        'stop_loss_abs': 9.882e-06,
        'stop_loss_pct': -10.0,
        'stop_loss_ratio': -0.1,
        'stoploss_order_id': None,
        'stoploss_last_update': ANY,
        'stoploss_last_update_timestamp': ANY,
        'initial_stop_loss': 9.882e-06,
        'initial_stop_loss_abs': 9.882e-06,
        'initial_stop_loss_pct': -10.0,
        'initial_stop_loss_ratio': -0.1,
        'stoploss_current_dist': ANY,
        'stoploss_current_dist_ratio': ANY,
        'stoploss_entry_dist': -0.00010475,
        'stoploss_entry_dist_ratio': -0.10448878,
        'open_order': None,
        'exchange': 'bittrex',
    }
Esempio n. 21
0
def test_api_backtesting(botclient, mocker, fee, caplog):
    ftbot, client = botclient
    mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)

    # Backtesting not started yet
    rc = client_get(client, f"{BASE_URI}/backtest")
    assert_response(rc)

    result = rc.json()
    assert result['status'] == 'not_started'
    assert not result['running']
    assert result['status_msg'] == 'Backtest not yet executed'
    assert result['progress'] == 0

    # Reset backtesting
    rc = client_delete(client, f"{BASE_URI}/backtest")
    assert_response(rc)
    result = rc.json()
    assert result['status'] == 'reset'
    assert not result['running']
    assert result['status_msg'] == 'Backtest reset'

    # start backtesting
    data = {
        "strategy": "StrategyTestV2",
        "timeframe": "5m",
        "timerange": "20180110-20180111",
        "max_open_trades": 3,
        "stake_amount": 100,
        "dry_run_wallet": 1000,
        "enable_protections": False
    }
    rc = client_post(client, f"{BASE_URI}/backtest", data=json.dumps(data))
    assert_response(rc)
    result = rc.json()

    assert result['status'] == 'running'
    assert result['progress'] == 0
    assert result['running']
    assert result['status_msg'] == 'Backtest started'

    rc = client_get(client, f"{BASE_URI}/backtest")
    assert_response(rc)

    result = rc.json()
    assert result['status'] == 'ended'
    assert not result['running']
    assert result['status_msg'] == 'Backtest ended'
    assert result['progress'] == 1
    assert result['backtest_result']

    rc = client_get(client, f"{BASE_URI}/backtest/abort")
    assert_response(rc)
    result = rc.json()
    assert result['status'] == 'not_running'
    assert not result['running']
    assert result['status_msg'] == 'Backtest ended'

    # Simulate running backtest
    ApiServer._bgtask_running = True
    rc = client_get(client, f"{BASE_URI}/backtest/abort")
    assert_response(rc)
    result = rc.json()
    assert result['status'] == 'stopping'
    assert not result['running']
    assert result['status_msg'] == 'Backtest ended'

    # Get running backtest...
    rc = client_get(client, f"{BASE_URI}/backtest")
    assert_response(rc)
    result = rc.json()
    assert result['status'] == 'running'
    assert result['running']
    assert result['step'] == "backtest"
    assert result['status_msg'] == "Backtest running"

    # Try delete with task still running
    rc = client_delete(client, f"{BASE_URI}/backtest")
    assert_response(rc)
    result = rc.json()
    assert result['status'] == 'running'

    # Post to backtest that's still running
    rc = client_post(client, f"{BASE_URI}/backtest", data=json.dumps(data))
    assert_response(rc, 502)
    result = rc.json()
    assert 'Bot Background task already running' in result['error']

    ApiServer._bgtask_running = False

    mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest_one_strategy',
                 side_effect=DependencyException())
    rc = client_post(client, f"{BASE_URI}/backtest", data=json.dumps(data))
    assert log_has("Backtesting caused an error: ", caplog)

    # Delete backtesting to avoid leakage since the backtest-object may stick around.
    rc = client_delete(client, f"{BASE_URI}/backtest")
    assert_response(rc)

    result = rc.json()
    assert result['status'] == 'reset'
    assert not result['running']
    assert result['status_msg'] == 'Backtest reset'