diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index be4b35a20..8b7f16334 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -649,7 +649,7 @@ class FreqtradeBot(object): if should_sell.sell_flag: self.execute_sell(trade, sell_rate, should_sell.sell_type) - logger.info('excuted sell') + logger.info('executed sell, reason: %s', should_sell.sell_type) return True return False diff --git a/freqtrade/tests/conftest.py b/freqtrade/tests/conftest.py index f6f067a41..8e691e3c8 100644 --- a/freqtrade/tests/conftest.py +++ b/freqtrade/tests/conftest.py @@ -12,6 +12,7 @@ from telegram import Chat, Message, Update from freqtrade.exchange.exchange_helpers import parse_ticker_dataframe from freqtrade.exchange import Exchange +from freqtrade.edge import Edge from freqtrade.freqtradebot import FreqtradeBot logging.getLogger('').setLevel(logging.INFO) @@ -42,6 +43,25 @@ def get_patched_exchange(mocker, config, api_mock=None) -> Exchange: return exchange +def patch_edge(mocker) -> None: + # "ETH/BTC", + # "LTC/BTC", + # "XRP/BTC", + # "NEO/BTC" + mocker.patch('freqtrade.edge.Edge._cached_pairs', mocker.PropertyMock( + return_value=[ + ['NEO/BTC', -0.20, 0.66, 3.71, 0.50, 1.71], + ['LTC/BTC', -0.21, 0.66, 3.71, 0.50, 1.71], + ] + )) + mocker.patch('freqtrade.edge.Edge.stoploss', MagicMock(return_value=-0.20)) + mocker.patch('freqtrade.edge.Edge.calculate', MagicMock(return_value=True)) + +def get_patched_edge(mocker, config) -> Edge: + patch_edge(mocker) + edge = Edge(config) + return edge + # Functions for recurrent object patching def get_patched_freqtradebot(mocker, config) -> FreqtradeBot: """ diff --git a/freqtrade/tests/edge/test_edge.py b/freqtrade/tests/edge/test_edge.py index 15fc38114..b6dc60000 100644 --- a/freqtrade/tests/edge/test_edge.py +++ b/freqtrade/tests/edge/test_edge.py @@ -130,6 +130,8 @@ def test_process_expectancy(mocker, default_conf): final = edge._process_expectancy(trades_df) assert len(final) == 1 + # TODO: check expectancy + win rate etc + def test_three_complete_trades(mocker, default_conf): exchange = get_patched_exchange(mocker, default_conf) diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index c302eeb43..9d5de004d 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -18,7 +18,7 @@ from freqtrade.persistence import Trade from freqtrade.rpc import RPCMessageType from freqtrade.state import State from freqtrade.strategy.interface import SellType, SellCheckTuple -from freqtrade.tests.conftest import log_has, patch_exchange +from freqtrade.tests.conftest import log_has, patch_exchange, patch_edge # Functions for recurrent object patching @@ -251,6 +251,100 @@ def test_get_trade_stake_amount_unlimited_amount(default_conf, assert result is None +def test_edge_overrides_stake_amount(mocker, default_conf) -> None: + default_conf['edge']['enabled'] = True + patch_RPCManager(mocker) + patch_exchange(mocker) + patch_edge(mocker) + freqtrade = FreqtradeBot(default_conf) + + # strategy stoploss should be ignored + freqtrade.strategy.stoploss = -0.05 + + with pytest.raises(IndexError): + freqtrade._get_trade_stake_amount('ETH/BTC') + + assert freqtrade._get_trade_stake_amount('NEO/BTC') == 0.025 + assert freqtrade._get_trade_stake_amount('LTC/BTC') == 0.02381 + + +def test_edge_overrides_stoploss(limit_buy_order, fee, markets, caplog, mocker, default_conf) -> None: + default_conf['edge']['enabled'] = True + patch_RPCManager(mocker) + patch_exchange(mocker) + patch_edge(mocker) + + # Strategy stoploss is -0.1 but Edge imposes a stoploss at -0.2 + # Thus, if price falls 21%, stoploss should be triggered + # + # mocking the ticker: price is falling ... + buy_price = limit_buy_order['price'] + mocker.patch.multiple( + 'freqtrade.exchange.Exchange', + get_ticker=MagicMock(return_value={ + 'bid': buy_price * 0.79, + 'ask': buy_price * 0.79, + 'last': buy_price * 0.79 + }), + buy=MagicMock(return_value={'id': limit_buy_order['id']}), + get_fee=fee, + get_markets=markets, + ) + ############################################# + + # Create a trade with "limit_buy_order" price + freqtrade = FreqtradeBot(default_conf) + patch_get_signal(freqtrade) + freqtrade.strategy.min_roi_reached = lambda trade, current_profit, current_time: False + freqtrade.create_trade() + trade = Trade.query.first() + trade.update(limit_buy_order) + ############################################# + + # stoploss shoud be hit + assert freqtrade.handle_trade(trade) is True + + assert log_has('executed sell, reason: SellType.STOP_LOSS', caplog.record_tuples) + assert trade.sell_reason == SellType.STOP_LOSS.value + + +def test_edge_should_ignore_strategy_stoploss(limit_buy_order, fee, markets, + mocker, default_conf) -> None: + default_conf['edge']['enabled'] = True + patch_RPCManager(mocker) + patch_exchange(mocker) + patch_edge(mocker) + + # Strategy stoploss is -0.1 but Edge imposes a stoploss at -0.2 + # Thus, if price falls 15%, stoploss should not be triggered + # + # mocking the ticker: price is falling ... + buy_price = limit_buy_order['price'] + mocker.patch.multiple( + 'freqtrade.exchange.Exchange', + get_ticker=MagicMock(return_value={ + 'bid': buy_price * 0.85, + 'ask': buy_price * 0.85, + 'last': buy_price * 0.85 + }), + buy=MagicMock(return_value={'id': limit_buy_order['id']}), + get_fee=fee, + get_markets=markets, + ) + ############################################# + + # Create a trade with "limit_buy_order" price + freqtrade = FreqtradeBot(default_conf) + patch_get_signal(freqtrade) + freqtrade.strategy.min_roi_reached = lambda trade, current_profit, current_time: False + freqtrade.create_trade() + trade = Trade.query.first() + trade.update(limit_buy_order) + ############################################# + + # stoploss shoud be hit + assert freqtrade.handle_trade(trade) is False + def test_get_min_pair_stake_amount(mocker, default_conf) -> None: patch_RPCManager(mocker) patch_exchange(mocker)