diff --git a/freqtrade/enums/signaltype.py b/freqtrade/enums/signaltype.py index 28f0676dd..23316c15a 100644 --- a/freqtrade/enums/signaltype.py +++ b/freqtrade/enums/signaltype.py @@ -5,9 +5,9 @@ class SignalType(Enum): """ Enum to distinguish between buy and sell signals """ - BUY = "buy" # To be renamed to enter_long - SELL = "sell" # To be renamed to exit_long - SHORT = "short" # Should be "enter_short" + ENTER_LONG = "enter_long" + EXIT_LONG = "exit_long" + ENTER_SHORT = "enter_short" EXIT_SHORT = "exit_short" diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index e6be897f2..ab5ae383a 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -420,7 +420,7 @@ class FreqtradeBot(LoggingMixin): return False # running get_signal on historical data fetched - (side, enter_tag) = self.strategy.get_enter_signal( + (side, enter_tag) = self.strategy.get_entry_signal( pair, self.strategy.timeframe, analyzed_df ) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 04740b845..7daec6b8f 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -594,18 +594,18 @@ class IStrategy(ABC, HyperStrategyMixin): return False, False if is_short: - enter = latest[SignalType.SHORT] == 1 - exit_ = latest[SignalType.EXIT_SHORT] == 1 + enter = latest.get(SignalType.ENTER_SHORT, 0) == 1 + exit_ = latest.get(SignalType.EXIT_SHORT, 0) == 1 else: - enter = latest[SignalType.BUY] == 1 - exit_ = latest[SignalType.SELL] == 1 + enter = latest[SignalType.ENTER_LONG] == 1 + exit_ = latest.get(SignalType.EXIT_LONG, 0) == 1 logger.debug(f"exit-trigger: {latest['date']} (pair={pair}) " f"enter={enter} exit={exit_}") return enter, exit_ - def get_enter_signal( + def get_entry_signal( self, pair: str, timeframe: str, @@ -624,19 +624,19 @@ class IStrategy(ABC, HyperStrategyMixin): if latest is None or latest_date is None: return None, None - enter_long = latest[SignalType.BUY] == 1 - exit_long = latest[SignalType.SELL] == 1 - enter_short = latest[SignalType.SHORT] == 1 - exit_short = latest[SignalType.EXIT_SHORT] == 1 + enter_long = latest[SignalType.ENTER_LONG.value] == 1 + exit_long = latest.get(SignalType.EXIT_LONG.value, 0) == 1 + enter_short = latest.get(SignalType.ENTER_SHORT.value, 0) == 1 + exit_short = latest.get(SignalType.EXIT_SHORT.value, 0) == 1 enter_signal: Optional[SignalDirection] = None enter_tag_value: Optional[str] = None if enter_long == 1 and not any([exit_long, enter_short]): enter_signal = SignalDirection.LONG - enter_tag_value = latest.get(SignalTagType.BUY_TAG, None) + enter_tag_value = latest.get(SignalTagType.BUY_TAG.value, None) if enter_short == 1 and not any([exit_short, enter_long]): enter_signal = SignalDirection.SHORT - enter_tag_value = latest.get(SignalTagType.SHORT_TAG, None) + enter_tag_value = latest.get(SignalTagType.SHORT_TAG.value, None) timeframe_seconds = timeframe_to_seconds(timeframe) diff --git a/tests/conftest.py b/tests/conftest.py index 03859d05c..c146fd9ce 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -193,7 +193,7 @@ def patch_get_signal(freqtrade: FreqtradeBot, enter_long=True, exit_long=False, :return: None """ # returns (Signal-direction, signaname) - def patched_get_enter_signal(*args, **kwargs): + def patched_get_entry_signal(*args, **kwargs): direction = None if enter_long and not any([exit_long, enter_short]): direction = SignalDirection.LONG @@ -202,7 +202,7 @@ def patch_get_signal(freqtrade: FreqtradeBot, enter_long=True, exit_long=False, return direction, enter_tag - freqtrade.strategy.get_enter_signal = patched_get_enter_signal + freqtrade.strategy.get_entry_signal = patched_get_entry_signal def patched_get_exit_signal(pair, timeframe, dataframe, is_short): if is_short: diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index bfdf88dbb..831a06991 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -1,4 +1,5 @@ # pragma pylint: disable=missing-docstring, C0103 +from freqtrade.enums.signaltype import SignalDirection import logging from datetime import datetime, timedelta, timezone from pathlib import Path @@ -30,7 +31,7 @@ _STRATEGY = DefaultStrategy(config={}) _STRATEGY.dp = DataProvider({}, None, None) -def test_returns_latest_signal(mocker, default_conf, ohlcv_history): +def test_returns_latest_signal(default_conf, ohlcv_history): ohlcv_history.loc[1, 'date'] = arrow.utcnow() # Take a copy to correctly modify the call mocked_history = ohlcv_history.copy() @@ -67,18 +68,18 @@ def test_analyze_pair_empty(default_conf, mocker, caplog, ohlcv_history): assert log_has('Empty dataframe for pair ETH/BTC', caplog) -def test_get_signal_empty(default_conf, mocker, caplog): - assert (False, False, None) == _STRATEGY.get_signal( +def test_get_signal_empty(default_conf, caplog): + assert (None, None) == _STRATEGY.get_latest_candle( 'foo', default_conf['timeframe'], DataFrame() ) assert log_has('Empty candle (OHLCV) data for pair foo', caplog) caplog.clear() - assert (False, False, None) == _STRATEGY.get_signal('bar', default_conf['timeframe'], None) + assert (None, None) == _STRATEGY.get_latest_candle('bar', default_conf['timeframe'], None) assert log_has('Empty candle (OHLCV) data for pair bar', caplog) caplog.clear() - assert (False, False, None) == _STRATEGY.get_signal( + assert (None, None) == _STRATEGY.get_latest_candle( 'baz', default_conf['timeframe'], DataFrame([]) @@ -86,7 +87,7 @@ def test_get_signal_empty(default_conf, mocker, caplog): assert log_has('Empty candle (OHLCV) data for pair baz', caplog) -def test_get_signal_exception_valueerror(default_conf, mocker, caplog, ohlcv_history): +def test_get_signal_exception_valueerror(mocker, caplog, ohlcv_history): caplog.set_level(logging.INFO) mocker.patch.object(_STRATEGY.dp, 'ohlcv', return_value=ohlcv_history) mocker.patch.object( @@ -111,14 +112,14 @@ def test_get_signal_old_dataframe(default_conf, mocker, caplog, ohlcv_history): ohlcv_history.loc[1, 'date'] = arrow.utcnow().shift(minutes=-16) # Take a copy to correctly modify the call mocked_history = ohlcv_history.copy() - mocked_history['sell'] = 0 - mocked_history['buy'] = 0 - mocked_history.loc[1, 'buy'] = 1 + mocked_history['exit_long'] = 0 + mocked_history['enter_long'] = 0 + mocked_history.loc[1, 'enter_long'] = 1 caplog.set_level(logging.INFO) mocker.patch.object(_STRATEGY, 'assert_df') - assert (False, False, None) == _STRATEGY.get_signal( + assert (None, None) == _STRATEGY.get_latest_candle( 'xyz', default_conf['timeframe'], mocked_history @@ -134,13 +135,13 @@ def test_get_signal_no_sell_column(default_conf, mocker, caplog, ohlcv_history): mocked_history = ohlcv_history.copy() # Intentionally don't set sell column # mocked_history['sell'] = 0 - mocked_history['buy'] = 0 - mocked_history.loc[1, 'buy'] = 1 + mocked_history['enter_long'] = 0 + mocked_history.loc[1, 'enter_long'] = 1 caplog.set_level(logging.INFO) mocker.patch.object(_STRATEGY, 'assert_df') - assert (True, False, None) == _STRATEGY.get_signal( + assert (SignalDirection.LONG, None) == _STRATEGY.get_entry_signal( 'xyz', default_conf['timeframe'], mocked_history @@ -453,8 +454,7 @@ def test_custom_sell(default_conf, fee, caplog) -> None: now = arrow.utcnow().datetime res = strategy.should_exit(trade, 1, now, - enter_long=False, enter_short=False, - exit_long=False, exit_short=False, + enter=False, exit_=False, low=None, high=None) assert res.sell_flag is False @@ -462,8 +462,7 @@ def test_custom_sell(default_conf, fee, caplog) -> None: strategy.custom_sell = MagicMock(return_value=True) res = strategy.should_exit(trade, 1, now, - enter_long=False, enter_short=False, - exit_long=False, exit_short=False, + enter=False, exit_=False, low=None, high=None) assert res.sell_flag is True assert res.sell_type == SellType.CUSTOM_SELL @@ -472,8 +471,7 @@ def test_custom_sell(default_conf, fee, caplog) -> None: strategy.custom_sell = MagicMock(return_value='hello world') res = strategy.should_exit(trade, 1, now, - enter_long=False, enter_short=False, - exit_long=False, exit_short=False, + enter=False, exit_=False, low=None, high=None) assert res.sell_type == SellType.CUSTOM_SELL assert res.sell_flag is True @@ -482,8 +480,7 @@ def test_custom_sell(default_conf, fee, caplog) -> None: caplog.clear() strategy.custom_sell = MagicMock(return_value='h' * 100) res = strategy.should_exit(trade, 1, now, - enter_long=False, enter_short=False, - exit_long=False, exit_short=False, + enter=False, exit_=False, low=None, high=None) assert res.sell_type == SellType.CUSTOM_SELL assert res.sell_flag is True diff --git a/tests/strategy/test_strategy_loading.py b/tests/strategy/test_strategy_loading.py index 7e94b7ccc..7a15f8c0c 100644 --- a/tests/strategy/test_strategy_loading.py +++ b/tests/strategy/test_strategy_loading.py @@ -118,10 +118,12 @@ def test_strategy(result, default_conf): assert 'adx' in df_indicators dataframe = strategy.advise_buy(df_indicators, metadata=metadata) - assert 'buy' in dataframe.columns + assert 'buy' not in dataframe.columns + assert 'enter_long' in dataframe.columns dataframe = strategy.advise_sell(df_indicators, metadata=metadata) - assert 'sell' in dataframe.columns + assert 'sell' not in dataframe.columns + assert 'exit_long' in dataframe.columns def test_strategy_override_minimal_roi(caplog, default_conf):