예제 #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.symbol_amount_prec(pair, amount)
            needs_price = (ordertype != 'market' or self._api.options.get(
                "createMarketBuyOrderRequiresPrice", False))
            rate = self.symbol_price_prec(pair, rate) if needs_price else None

            return self._api.create_order(pair, ordertype, side, amount, rate,
                                          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} (total {rate * amount}).'
                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} (total {rate * amount}).'
                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
예제 #2
0
def setup_configuration(args: Namespace, 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)

    if method == RunMode.HYPEROPT:
        # Special cases for Hyperopt
        if config.get(
                'strategy') and config.get('strategy') != 'DefaultStrategy':
            logger.error("Please don't use --strategy for hyperopt.")
            logger.error(
                "Read the documentation at "
                "https://github.com/freqtrade/freqtrade/blob/develop/docs/hyperopt.md "
                "to understand how to configure hyperopt.")
            raise DependencyException(
                "--strategy configured but not supported for hyperopt")

    return config
예제 #3
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:
            return self._api.create_order(pair, 'stop_loss_limit', 'sell',
                                          amount, rate,
                                          {'stopPrice': stop_price})

        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)
예제 #4
0
파일: __init__.py 프로젝트: DimaK79/xtrader
    def buy(self, pair: str, rate: float, amount: float) -> 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': 'limit',
                '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
            symbol = pair.replace('/', '')
            amount, quaselltrade = self.symbol_amount_prec_str(pair, amount)
            if (float(amount) > 0):
                rate = self.symbol_price_prec_str(pair, rate)
                print(amount, rate)
                return self.client.order_limit_buy(symbol=symbol,
                                                   quantity=amount,
                                                   price=rate)
            else:
                return {'orderId': None}
                print('Error - Buy')

            #return self._api.create_limit_buy_order(pair, amount, rate)
        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)
        except OSError as e:
            return {'orderId': None}
            print(e)
            pass
예제 #5
0
    def sell(self,
             pair: str,
             ordertype: str,
             amount: float,
             rate: float,
             time_in_force='gtc') -> Dict:
        if self._conf['dry_run']:
            order_id = f'dry_run_sell_{randint(0, 10**6)}'
            self._dry_run_open_orders[order_id] = {
                'pair': pair,
                'price': rate,
                'amount': amount,
                'type': ordertype,
                'side': 'sell',
                'remaining': 0.0,
                'datetime': arrow.utcnow().isoformat(),
                'status': 'closed'
            }
            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, 'sell', amount,
                                              rate)
            else:
                return self._api.create_order(pair, ordertype, 'sell', amount,
                                              rate,
                                              {'timeInForce': time_in_force})

        except ccxt.InsufficientFunds as e:
            raise DependencyException(
                f'Insufficient funds to create limit sell order on market {pair}.'
                f'Tried to sell amount {amount} at rate {rate} (total {rate*amount}).'
                f'Message: {e}')
        except ccxt.InvalidOrder as e:
            raise DependencyException(
                f'Could not create limit sell order on market {pair}.'
                f'Tried to sell amount {amount} at rate {rate} (total {rate*amount}).'
                f'Message: {e}')
        except (ccxt.NetworkError, ccxt.ExchangeError) as e:
            raise TemporaryError(
                f'Could not place sell order due to {e.__class__.__name__}. Message: {e}'
            )
        except ccxt.BaseError as e:
            raise OperationalException(e)
예제 #6
0
def test_rpc_status_table(default_conf, ticker, fee, markets, mocker) -> None:
    patch_exchange(mocker)
    mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
    mocker.patch.multiple(
        'freqtrade.exchange.Exchange',
        get_ticker=ticker,
        get_fee=fee,
        markets=PropertyMock(return_value=markets)
    )

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

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

    freqtradebot.create_trades()
    result = rpc._rpc_status_table()
    assert 'instantly' in result['Since'].all()
    assert 'ETH/BTC' in result['Pair'].all()
    assert '-0.59%' in result['Profit'].all()

    mocker.patch('freqtrade.exchange.Exchange.get_ticker',
                 MagicMock(side_effect=DependencyException(f"Pair 'ETH/BTC' not available")))
    # invalidate ticker cache
    rpc._freqtrade.exchange._cached_ticker = {}
    result = rpc._rpc_status_table()
    assert 'instantly' in result['Since'].all()
    assert 'ETH/BTC' in result['Pair'].all()
    assert 'nan%' in result['Profit'].all()
예제 #7
0
    def _get_trade_stake_amount(self) -> Optional[float]:
        """
        Check if stake amount can be fulfilled with the available balance
        for the stake currency
        :return: float: Stake Amount
        """
        stake_amount = self.config['stake_amount']
        avaliable_amount = self.exchange.get_balance(
            self.config['stake_currency'])

        if stake_amount == constants.UNLIMITED_STAKE_AMOUNT:
            open_trades = len(
                Trade.query.filter(Trade.is_open.is_(True)).all())
            if open_trades >= self.config['max_open_trades']:
                logger.warning(
                    'Can\'t open a new trade: max number of trades is reached')
                return None
            return avaliable_amount / (self.config['max_open_trades'] -
                                       open_trades)

        # Check if stake_amount is fulfilled
        if avaliable_amount < stake_amount:
            raise DependencyException(
                'Available balance(%f %s) is lower than stake amount(%f %s)' %
                (avaliable_amount, self.config['stake_currency'], stake_amount,
                 self.config['stake_currency']))

        return stake_amount
예제 #8
0
    def create_trade(self) -> bool:
        """
        Checks the implemented trading indicator(s) for a randomly picked pair,
        if one pair triggers the buy_signal a new trade record gets created
        :return: True if a trade object has been created and persisted, False otherwise
        """
        interval = self.strategy.ticker_interval
        stake_amount = self._get_trade_stake_amount()

        if not stake_amount:
            return False

        logger.info(
            'Checking buy signals to create a new trade with stake_amount: %f ...',
            stake_amount)
        whitelist = copy.deepcopy(self.config['exchange']['pair_whitelist'])

        # Remove currently opened and latest pairs from whitelist
        for trade in Trade.query.filter(Trade.is_open.is_(True)).all():
            if trade.pair in whitelist:
                whitelist.remove(trade.pair)
                logger.debug('Ignoring %s in pair whitelist', trade.pair)

        if not whitelist:
            raise DependencyException('No currency pairs in whitelist')

        # Pick pair based on buy signals
        for _pair in whitelist:
            thistory = self.exchange.get_candle_history(_pair, interval)
            (buy, sell) = self.strategy.get_signal(_pair, interval, thistory)

            if buy and not sell:
                return self.execute_buy(_pair, stake_amount)
        return False
예제 #9
0
    def _get_trade_stake_amount(self, pair) -> Optional[float]:
        """
        Check if stake amount can be fulfilled with the available balance
        for the stake currency
        :return: float: Stake Amount
        """
        if self.edge:
            return self.edge.stake_amount(
                pair,
                self.wallets.get_free(self.config['stake_currency']),
                self.wallets.get_total(self.config['stake_currency']),
                Trade.total_open_trades_stakes()
            )
        else:
            stake_amount = self.config['stake_amount']

        available_amount = self.wallets.get_free(self.config['stake_currency'])

        if stake_amount == constants.UNLIMITED_STAKE_AMOUNT:
            open_trades = len(Trade.get_open_trades())
            if open_trades >= self.config['max_open_trades']:
                logger.warning('Can\'t open a new trade: max number of trades is reached')
                return None
            return available_amount / (self.config['max_open_trades'] - open_trades)

        # Check if stake_amount is fulfilled
        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
예제 #10
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
예제 #11
0
 def get_valid_pair_combination(self, curr_1, curr_2) -> 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.")
예제 #12
0
def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None:
    patch_coinmarketcap(mocker)
    mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
    mocker.patch.multiple('freqtrade.exchange.Exchange',
                          _load_markets=MagicMock(return_value={}),
                          get_ticker=ticker,
                          get_fee=fee,
                          get_markets=markets)

    freqtradebot = FreqtradeBot(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.create_trade()
    results = rpc._rpc_trade_status()

    assert {
        'trade_id': 1,
        'pair': 'ETH/BTC',
        'market_url': 'https://bittrex.com/Market/Index?MarketName=BTC-ETH',
        'date': ANY,
        'open_rate': 1.099e-05,
        'close_rate': None,
        'current_rate': 1.098e-05,
        'amount': 90.99181074,
        'close_profit': None,
        'current_profit': -0.59,
        'open_order': '(limit buy rem=0.00000000)'
    } == results[0]

    mocker.patch(
        'freqtrade.exchange.Exchange.get_ticker',
        MagicMock(
            side_effect=DependencyException(f"Pair 'ETH/BTC' not available")))
    # invalidate ticker cache
    rpc._freqtrade.exchange._cached_ticker = {}
    results = rpc._rpc_trade_status()
    assert isnan(results[0]['current_profit'])
    assert isnan(results[0]['current_rate'])
    assert {
        'trade_id': 1,
        'pair': 'ETH/BTC',
        'market_url': 'https://bittrex.com/Market/Index?MarketName=BTC-ETH',
        'date': ANY,
        'open_rate': 1.099e-05,
        'close_rate': None,
        'current_rate': ANY,
        'amount': 90.99181074,
        'close_profit': None,
        'current_profit': ANY,
        'open_order': '(limit buy rem=0.00000000)'
    } == results[0]
예제 #13
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.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._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.symbol_amount_prec(pair, amount)

            rate = self.symbol_price_prec(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
예제 #14
0
def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None:
    mocker.patch.multiple(
        'freqtrade.rpc.fiat_convert.Market',
        ticker=MagicMock(return_value={'price_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',
        get_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 order*'):
        rpc._rpc_status_table(default_conf['stake_currency'], 'USD')

    freqtradebot.create_trades()

    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' == result[0][1]
    assert '-0.59%' == 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' == result[0][1]
    assert '-0.59% (-0.09)' == result[0][3]

    mocker.patch(
        'freqtrade.exchange.Exchange.get_ticker',
        MagicMock(
            side_effect=DependencyException(f"Pair 'ETH/BTC' not available")))
    # invalidate ticker cache
    rpc._freqtrade.exchange._cached_ticker = {}
    result, headers = rpc._rpc_status_table(default_conf['stake_currency'],
                                            'USD')
    assert 'instantly' == result[0][2]
    assert 'ETH/BTC' == result[0][1]
    assert 'nan%' == result[0][3]
예제 #15
0
    def cancel_order(self, order_id: str, pair: str) -> None:
        if self._conf['dry_run']:
            return

        try:
            return self._api.cancel_order(order_id, pair)
        except ccxt.InvalidOrder as e:
            raise DependencyException(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)
예제 #16
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)
예제 #17
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
예제 #18
0
def setup_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
예제 #19
0
    def create_trade(self) -> bool:
        """
        Checks the implemented trading indicator(s) for a randomly picked pair,
        if one pair triggers the buy_signal a new trade record gets created
        :return: True if a trade object has been created and persisted, False otherwise
        """
        interval = self.strategy.ticker_interval
        whitelist = copy.deepcopy(self.active_pair_whitelist)

        # Remove currently opened and latest pairs from whitelist
        for trade in Trade.query.filter(Trade.is_open.is_(True)).all():
            if trade.pair in whitelist:
                whitelist.remove(trade.pair)
                logger.debug('Ignoring %s in pair whitelist', trade.pair)

        if not whitelist:
            raise DependencyException('No currency pairs in whitelist')

        # running get_signal on historical data fetched
        for _pair in whitelist:
            (buy, sell) = self.strategy.get_signal(
                _pair, interval,
                self.dataprovider.ohlcv(_pair, self.strategy.ticker_interval))

            if buy and not sell:
                stake_amount = self._get_trade_stake_amount(_pair)
                if not stake_amount:
                    return False

                logger.info(
                    f"Buy signal found: about create a new trade with stake_amount: "
                    f"{stake_amount} ...")

                bidstrat_check_depth_of_market = self.config.get('bid_strategy', {}).\
                    get('check_depth_of_market', {})
                if (bidstrat_check_depth_of_market.get('enabled', False)) and\
                        (bidstrat_check_depth_of_market.get('bids_to_ask_delta', 0) > 0):
                    if self._check_depth_of_market_buy(
                            _pair, bidstrat_check_depth_of_market):
                        return self.execute_buy(_pair, stake_amount)
                    else:
                        return False
                return self.execute_buy(_pair, stake_amount)

        return False
예제 #20
0
def setup_configuration(args: Namespace) -> Dict[str, Any]:
    """
    Prepare the configuration for the backtesting
    :param args: Cli args from Arguments()
    :return: Configuration
    """
    configuration = Configuration(args)
    config = configuration.get_config()

    # Ensure we do not use Exchange credentials
    config['exchange']['key'] = ''
    config['exchange']['secret'] = ''

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

    return config
예제 #21
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:
                 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}'
             )
         except ccxt.BaseError as e:
             raise OperationalException(e)
     else:
         logger.info("returning cached ticker-data for %s", pair)
         return self._cached_ticker[pair]
예제 #22
0
def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None:
    mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
    patch_exchange(mocker)
    mocker.patch.multiple(
        'freqtrade.exchange.Exchange',
        get_ticker=ticker,
        get_fee=fee,
        markets=PropertyMock(return_value=markets)
    )

    freqtradebot = FreqtradeBot(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.create_trades()
    results = rpc._rpc_trade_status()
    assert {
        'trade_id': 1,
        'pair': 'ETH/BTC',
        'base_currency': 'BTC',
        'open_date': ANY,
        'open_date_hum': ANY,
        'close_date': None,
        'close_date_hum': None,
        'open_rate': 1.099e-05,
        'close_rate': None,
        'current_rate': 1.098e-05,
        'amount': 90.99181074,
        'stake_amount': 0.001,
        'close_profit': None,
        'current_profit': -0.59,
        '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.exchange.Exchange.get_ticker',
                 MagicMock(side_effect=DependencyException(f"Pair 'ETH/BTC' not available")))
    # invalidate ticker cache
    rpc._freqtrade.exchange._cached_ticker = {}
    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,
        'close_date': None,
        'close_date_hum': None,
        'open_rate': 1.099e-05,
        'close_rate': None,
        'current_rate': ANY,
        'amount': 90.99181074,
        '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]
예제 #23
0
    def create_trade(self) -> bool:
        """
        Checks the implemented trading indicator(s) for a randomly picked pair,
        if one pair triggers the buy_signal a new trade record gets created
        :return: True if a trade object has been created and persisted, False otherwise
        """
        interval = self.analyze.get_ticker_interval()
        stake_amount = self._get_trade_stake_amount()

        if not stake_amount:
            return False
        stake_currency = self.config['stake_currency']
        fiat_currency = self.config['fiat_display_currency']
        exc_name = self.exchange.name

        logger.info(
            'Checking buy signals to create a new trade with stake_amount: %f ...',
            stake_amount)
        whitelist = copy.deepcopy(self.config['exchange']['pair_whitelist'])

        # Remove currently opened and latest pairs from whitelist
        for trade in Trade.query.filter(Trade.is_open.is_(True)).all():
            if trade.pair in whitelist:
                whitelist.remove(trade.pair)
                logger.debug('Ignoring %s in pair whitelist', trade.pair)

        if not whitelist:
            raise DependencyException('No currency pairs in whitelist')

        # Pick pair based on buy signals
        for _pair in whitelist:
            (buy, sell) = self.analyze.get_signal(self.exchange, _pair,
                                                  interval)
            if buy and not sell:
                pair = _pair
                break
        else:
            return False
        pair_s = pair.replace('_', '/')
        pair_url = self.exchange.get_pair_detail_url(pair)

        # Calculate amount
        buy_limit = self.get_target_bid(self.exchange.get_ticker(pair))

        min_stake_amount = self._get_min_pair_stake_amount(pair_s, buy_limit)
        if min_stake_amount is not None and min_stake_amount > stake_amount:
            logger.warning(
                f'Can\'t open a new trade for {pair_s}: stake amount'
                f' is too small ({stake_amount} < {min_stake_amount})')
            return False

        amount = stake_amount / buy_limit

        order_id = self.exchange.buy(pair, buy_limit, amount)['id']

        stake_amount_fiat = self.fiat_converter.convert_amount(
            stake_amount, stake_currency, fiat_currency)

        # Create trade entity and return
        self.rpc.send_msg(f"""*{exc_name}:* Buying [{pair_s}]({pair_url}) \
with limit `{buy_limit:.8f} ({stake_amount:.6f} \
{stake_currency}, {stake_amount_fiat:.3f} {fiat_currency})`""")
        # Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL
        fee = self.exchange.get_fee(symbol=pair, taker_or_maker='maker')
        trade = Trade(pair=pair,
                      stake_amount=stake_amount,
                      amount=amount,
                      fee_open=fee,
                      fee_close=fee,
                      open_rate=buy_limit,
                      open_rate_requested=buy_limit,
                      open_date=datetime.utcnow(),
                      exchange=self.exchange.id,
                      open_order_id=order_id)
        Trade.session.add(trade)
        Trade.session.flush()
        return True
예제 #24
0
    def create_trade(self) -> bool:
        """
        Checks the implemented trading indicator(s) for a randomly picked pair,
        if one pair triggers the buy_signal a new trade record gets created
        :param stake_amount: amount of btc to spend
        :param interval: Ticker interval used for Analyze
        :return: True if a trade object has been created and persisted, False otherwise
        """
        stake_amount = self.config['stake_amount']
        interval = self.analyze.get_ticker_interval()

        logger.info(
            'Checking buy signals to create a new trade with stake_amount: %f ...',
            stake_amount)
        whitelist = copy.deepcopy(self.config['exchange']['pair_whitelist'])
        # Check if stake_amount is fulfilled
        if exchange.get_balance(self.config['stake_currency']) < stake_amount:
            raise DependencyException(
                'stake amount is not fulfilled (currency={})'.format(
                    self.config['stake_currency']))

        # Remove currently opened and latest pairs from whitelist
        for trade in Trade.query.filter(Trade.is_open.is_(True)).all():
            if trade.pair in whitelist:
                whitelist.remove(trade.pair)
                logger.debug('Ignoring %s in pair whitelist', trade.pair)

        if not whitelist:
            raise DependencyException('No currency pairs in whitelist')

        # Pick pair based on StochRSI buy signals
        for _pair in whitelist:
            (buy, sell) = self.analyze.get_signal(_pair, interval)
            if buy and not sell:
                pair = _pair
                break
        else:
            return False

        # Calculate amount
        buy_limit = self.get_target_bid(exchange.get_ticker(pair))
        amount = stake_amount / buy_limit

        order_id = exchange.buy(pair, buy_limit, amount)

        stake_amount_fiat = self.fiat_converter.convert_amount(
            stake_amount, self.config['stake_currency'],
            self.config['fiat_display_currency'])

        # Create trade entity and return
        self.rpc.send_msg(
            '*{}:* Buying [{}]({}) with limit `{:.8f} ({:.6f} {}, {:.3f} {})` '
            .format(exchange.get_name().upper(), pair.replace('_', '/'),
                    exchange.get_pair_detail_url(pair), buy_limit,
                    stake_amount, self.config['stake_currency'],
                    stake_amount_fiat, self.config['fiat_display_currency']))
        # Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL
        trade = Trade(pair=pair,
                      stake_amount=stake_amount,
                      amount=amount,
                      fee=exchange.get_fee(),
                      open_rate=buy_limit,
                      open_date=datetime.utcnow(),
                      exchange=exchange.get_name().upper(),
                      open_order_id=order_id)
        Trade.session.add(trade)
        Trade.session.flush()
        return True
예제 #25
0
def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee,
                              limit_buy_order, limit_sell_order, markets, mocker) -> None:
    mocker.patch.multiple(
        'freqtrade.rpc.fiat_convert.Market',
        ticker=MagicMock(return_value={'price_usd': 15000.0}),
    )
    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_ticker=ticker,
        get_fee=fee,
        markets=PropertyMock(return_value=markets)
    )

    freqtradebot = FreqtradeBot(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()

    with pytest.raises(RPCException, match=r'.*no closed trade*'):
        rpc._rpc_trade_statistics(stake_currency, fiat_display_currency)

    # Create some test data
    freqtradebot.create_trades()
    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',
        get_ticker=ticker_sell_up
    )
    trade.update(limit_sell_order)
    trade.close_date = datetime.utcnow()
    trade.is_open = False

    freqtradebot.create_trades()
    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',
        get_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.632e-05)
    assert prec_satoshi(stats['profit_all_percent'], 2.81)
    assert prec_satoshi(stats['profit_all_fiat'], 0.8448)
    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.exchange.Exchange.get_ticker',
                 MagicMock(side_effect=DependencyException(f"Pair 'ETH/BTC' not available")))
    # invalidate ticker cache
    rpc._freqtrade.exchange._cached_ticker = {}
    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'])