Small refactorings, use only enter_long columns
This commit is contained in:
parent
b951f59f89
commit
6b93c71d15
@ -5,9 +5,9 @@ class SignalType(Enum):
|
|||||||
"""
|
"""
|
||||||
Enum to distinguish between buy and sell signals
|
Enum to distinguish between buy and sell signals
|
||||||
"""
|
"""
|
||||||
BUY = "buy" # To be renamed to enter_long
|
ENTER_LONG = "enter_long"
|
||||||
SELL = "sell" # To be renamed to exit_long
|
EXIT_LONG = "exit_long"
|
||||||
SHORT = "short" # Should be "enter_short"
|
ENTER_SHORT = "enter_short"
|
||||||
EXIT_SHORT = "exit_short"
|
EXIT_SHORT = "exit_short"
|
||||||
|
|
||||||
|
|
||||||
|
@ -420,7 +420,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
# running get_signal on historical data fetched
|
# 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
|
pair, self.strategy.timeframe, analyzed_df
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -594,18 +594,18 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
return False, False
|
return False, False
|
||||||
|
|
||||||
if is_short:
|
if is_short:
|
||||||
enter = latest[SignalType.SHORT] == 1
|
enter = latest.get(SignalType.ENTER_SHORT, 0) == 1
|
||||||
exit_ = latest[SignalType.EXIT_SHORT] == 1
|
exit_ = latest.get(SignalType.EXIT_SHORT, 0) == 1
|
||||||
else:
|
else:
|
||||||
enter = latest[SignalType.BUY] == 1
|
enter = latest[SignalType.ENTER_LONG] == 1
|
||||||
exit_ = latest[SignalType.SELL] == 1
|
exit_ = latest.get(SignalType.EXIT_LONG, 0) == 1
|
||||||
|
|
||||||
logger.debug(f"exit-trigger: {latest['date']} (pair={pair}) "
|
logger.debug(f"exit-trigger: {latest['date']} (pair={pair}) "
|
||||||
f"enter={enter} exit={exit_}")
|
f"enter={enter} exit={exit_}")
|
||||||
|
|
||||||
return enter, exit_
|
return enter, exit_
|
||||||
|
|
||||||
def get_enter_signal(
|
def get_entry_signal(
|
||||||
self,
|
self,
|
||||||
pair: str,
|
pair: str,
|
||||||
timeframe: str,
|
timeframe: str,
|
||||||
@ -624,19 +624,19 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
if latest is None or latest_date is None:
|
if latest is None or latest_date is None:
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
enter_long = latest[SignalType.BUY] == 1
|
enter_long = latest[SignalType.ENTER_LONG.value] == 1
|
||||||
exit_long = latest[SignalType.SELL] == 1
|
exit_long = latest.get(SignalType.EXIT_LONG.value, 0) == 1
|
||||||
enter_short = latest[SignalType.SHORT] == 1
|
enter_short = latest.get(SignalType.ENTER_SHORT.value, 0) == 1
|
||||||
exit_short = latest[SignalType.EXIT_SHORT] == 1
|
exit_short = latest.get(SignalType.EXIT_SHORT.value, 0) == 1
|
||||||
|
|
||||||
enter_signal: Optional[SignalDirection] = None
|
enter_signal: Optional[SignalDirection] = None
|
||||||
enter_tag_value: Optional[str] = None
|
enter_tag_value: Optional[str] = None
|
||||||
if enter_long == 1 and not any([exit_long, enter_short]):
|
if enter_long == 1 and not any([exit_long, enter_short]):
|
||||||
enter_signal = SignalDirection.LONG
|
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]):
|
if enter_short == 1 and not any([exit_short, enter_long]):
|
||||||
enter_signal = SignalDirection.SHORT
|
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)
|
timeframe_seconds = timeframe_to_seconds(timeframe)
|
||||||
|
|
||||||
|
@ -193,7 +193,7 @@ def patch_get_signal(freqtrade: FreqtradeBot, enter_long=True, exit_long=False,
|
|||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
# returns (Signal-direction, signaname)
|
# returns (Signal-direction, signaname)
|
||||||
def patched_get_enter_signal(*args, **kwargs):
|
def patched_get_entry_signal(*args, **kwargs):
|
||||||
direction = None
|
direction = None
|
||||||
if enter_long and not any([exit_long, enter_short]):
|
if enter_long and not any([exit_long, enter_short]):
|
||||||
direction = SignalDirection.LONG
|
direction = SignalDirection.LONG
|
||||||
@ -202,7 +202,7 @@ def patch_get_signal(freqtrade: FreqtradeBot, enter_long=True, exit_long=False,
|
|||||||
|
|
||||||
return direction, enter_tag
|
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):
|
def patched_get_exit_signal(pair, timeframe, dataframe, is_short):
|
||||||
if is_short:
|
if is_short:
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
# pragma pylint: disable=missing-docstring, C0103
|
# pragma pylint: disable=missing-docstring, C0103
|
||||||
|
from freqtrade.enums.signaltype import SignalDirection
|
||||||
import logging
|
import logging
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@ -30,7 +31,7 @@ _STRATEGY = DefaultStrategy(config={})
|
|||||||
_STRATEGY.dp = DataProvider({}, None, None)
|
_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()
|
ohlcv_history.loc[1, 'date'] = arrow.utcnow()
|
||||||
# Take a copy to correctly modify the call
|
# Take a copy to correctly modify the call
|
||||||
mocked_history = ohlcv_history.copy()
|
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)
|
assert log_has('Empty dataframe for pair ETH/BTC', caplog)
|
||||||
|
|
||||||
|
|
||||||
def test_get_signal_empty(default_conf, mocker, caplog):
|
def test_get_signal_empty(default_conf, caplog):
|
||||||
assert (False, False, None) == _STRATEGY.get_signal(
|
assert (None, None) == _STRATEGY.get_latest_candle(
|
||||||
'foo', default_conf['timeframe'], DataFrame()
|
'foo', default_conf['timeframe'], DataFrame()
|
||||||
)
|
)
|
||||||
assert log_has('Empty candle (OHLCV) data for pair foo', caplog)
|
assert log_has('Empty candle (OHLCV) data for pair foo', caplog)
|
||||||
caplog.clear()
|
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)
|
assert log_has('Empty candle (OHLCV) data for pair bar', caplog)
|
||||||
caplog.clear()
|
caplog.clear()
|
||||||
|
|
||||||
assert (False, False, None) == _STRATEGY.get_signal(
|
assert (None, None) == _STRATEGY.get_latest_candle(
|
||||||
'baz',
|
'baz',
|
||||||
default_conf['timeframe'],
|
default_conf['timeframe'],
|
||||||
DataFrame([])
|
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)
|
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)
|
caplog.set_level(logging.INFO)
|
||||||
mocker.patch.object(_STRATEGY.dp, 'ohlcv', return_value=ohlcv_history)
|
mocker.patch.object(_STRATEGY.dp, 'ohlcv', return_value=ohlcv_history)
|
||||||
mocker.patch.object(
|
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)
|
ohlcv_history.loc[1, 'date'] = arrow.utcnow().shift(minutes=-16)
|
||||||
# Take a copy to correctly modify the call
|
# Take a copy to correctly modify the call
|
||||||
mocked_history = ohlcv_history.copy()
|
mocked_history = ohlcv_history.copy()
|
||||||
mocked_history['sell'] = 0
|
mocked_history['exit_long'] = 0
|
||||||
mocked_history['buy'] = 0
|
mocked_history['enter_long'] = 0
|
||||||
mocked_history.loc[1, 'buy'] = 1
|
mocked_history.loc[1, 'enter_long'] = 1
|
||||||
|
|
||||||
caplog.set_level(logging.INFO)
|
caplog.set_level(logging.INFO)
|
||||||
mocker.patch.object(_STRATEGY, 'assert_df')
|
mocker.patch.object(_STRATEGY, 'assert_df')
|
||||||
|
|
||||||
assert (False, False, None) == _STRATEGY.get_signal(
|
assert (None, None) == _STRATEGY.get_latest_candle(
|
||||||
'xyz',
|
'xyz',
|
||||||
default_conf['timeframe'],
|
default_conf['timeframe'],
|
||||||
mocked_history
|
mocked_history
|
||||||
@ -134,13 +135,13 @@ def test_get_signal_no_sell_column(default_conf, mocker, caplog, ohlcv_history):
|
|||||||
mocked_history = ohlcv_history.copy()
|
mocked_history = ohlcv_history.copy()
|
||||||
# Intentionally don't set sell column
|
# Intentionally don't set sell column
|
||||||
# mocked_history['sell'] = 0
|
# mocked_history['sell'] = 0
|
||||||
mocked_history['buy'] = 0
|
mocked_history['enter_long'] = 0
|
||||||
mocked_history.loc[1, 'buy'] = 1
|
mocked_history.loc[1, 'enter_long'] = 1
|
||||||
|
|
||||||
caplog.set_level(logging.INFO)
|
caplog.set_level(logging.INFO)
|
||||||
mocker.patch.object(_STRATEGY, 'assert_df')
|
mocker.patch.object(_STRATEGY, 'assert_df')
|
||||||
|
|
||||||
assert (True, False, None) == _STRATEGY.get_signal(
|
assert (SignalDirection.LONG, None) == _STRATEGY.get_entry_signal(
|
||||||
'xyz',
|
'xyz',
|
||||||
default_conf['timeframe'],
|
default_conf['timeframe'],
|
||||||
mocked_history
|
mocked_history
|
||||||
@ -453,8 +454,7 @@ def test_custom_sell(default_conf, fee, caplog) -> None:
|
|||||||
|
|
||||||
now = arrow.utcnow().datetime
|
now = arrow.utcnow().datetime
|
||||||
res = strategy.should_exit(trade, 1, now,
|
res = strategy.should_exit(trade, 1, now,
|
||||||
enter_long=False, enter_short=False,
|
enter=False, exit_=False,
|
||||||
exit_long=False, exit_short=False,
|
|
||||||
low=None, high=None)
|
low=None, high=None)
|
||||||
|
|
||||||
assert res.sell_flag is False
|
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)
|
strategy.custom_sell = MagicMock(return_value=True)
|
||||||
res = strategy.should_exit(trade, 1, now,
|
res = strategy.should_exit(trade, 1, now,
|
||||||
enter_long=False, enter_short=False,
|
enter=False, exit_=False,
|
||||||
exit_long=False, exit_short=False,
|
|
||||||
low=None, high=None)
|
low=None, high=None)
|
||||||
assert res.sell_flag is True
|
assert res.sell_flag is True
|
||||||
assert res.sell_type == SellType.CUSTOM_SELL
|
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')
|
strategy.custom_sell = MagicMock(return_value='hello world')
|
||||||
|
|
||||||
res = strategy.should_exit(trade, 1, now,
|
res = strategy.should_exit(trade, 1, now,
|
||||||
enter_long=False, enter_short=False,
|
enter=False, exit_=False,
|
||||||
exit_long=False, exit_short=False,
|
|
||||||
low=None, high=None)
|
low=None, high=None)
|
||||||
assert res.sell_type == SellType.CUSTOM_SELL
|
assert res.sell_type == SellType.CUSTOM_SELL
|
||||||
assert res.sell_flag is True
|
assert res.sell_flag is True
|
||||||
@ -482,8 +480,7 @@ def test_custom_sell(default_conf, fee, caplog) -> None:
|
|||||||
caplog.clear()
|
caplog.clear()
|
||||||
strategy.custom_sell = MagicMock(return_value='h' * 100)
|
strategy.custom_sell = MagicMock(return_value='h' * 100)
|
||||||
res = strategy.should_exit(trade, 1, now,
|
res = strategy.should_exit(trade, 1, now,
|
||||||
enter_long=False, enter_short=False,
|
enter=False, exit_=False,
|
||||||
exit_long=False, exit_short=False,
|
|
||||||
low=None, high=None)
|
low=None, high=None)
|
||||||
assert res.sell_type == SellType.CUSTOM_SELL
|
assert res.sell_type == SellType.CUSTOM_SELL
|
||||||
assert res.sell_flag is True
|
assert res.sell_flag is True
|
||||||
|
@ -118,10 +118,12 @@ def test_strategy(result, default_conf):
|
|||||||
assert 'adx' in df_indicators
|
assert 'adx' in df_indicators
|
||||||
|
|
||||||
dataframe = strategy.advise_buy(df_indicators, metadata=metadata)
|
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)
|
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):
|
def test_strategy_override_minimal_roi(caplog, default_conf):
|
||||||
|
Loading…
Reference in New Issue
Block a user