Update get_signal
This commit is contained in:
parent
46285cd77e
commit
9a03cb96f5
@ -4,6 +4,6 @@ from freqtrade.enums.collateral import Collateral
|
|||||||
from freqtrade.enums.rpcmessagetype import RPCMessageType
|
from freqtrade.enums.rpcmessagetype import RPCMessageType
|
||||||
from freqtrade.enums.runmode import NON_UTIL_MODES, OPTIMIZE_MODES, TRADING_MODES, RunMode
|
from freqtrade.enums.runmode import NON_UTIL_MODES, OPTIMIZE_MODES, TRADING_MODES, RunMode
|
||||||
from freqtrade.enums.selltype import SellType
|
from freqtrade.enums.selltype import SellType
|
||||||
from freqtrade.enums.signaltype import SignalTagType, SignalType
|
from freqtrade.enums.signaltype import SignalDirection, SignalTagType, SignalType
|
||||||
from freqtrade.enums.state import State
|
from freqtrade.enums.state import State
|
||||||
from freqtrade.enums.tradingmode import TradingMode
|
from freqtrade.enums.tradingmode import TradingMode
|
||||||
|
@ -17,3 +17,8 @@ class SignalTagType(Enum):
|
|||||||
"""
|
"""
|
||||||
BUY_TAG = "buy_tag"
|
BUY_TAG = "buy_tag"
|
||||||
SHORT_TAG = "short_tag"
|
SHORT_TAG = "short_tag"
|
||||||
|
|
||||||
|
|
||||||
|
class SignalDirection(Enum):
|
||||||
|
LONG = 'long'
|
||||||
|
SHORT = 'short'
|
||||||
|
@ -420,19 +420,19 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
# running get_signal on historical data fetched
|
# running get_signal on historical data fetched
|
||||||
(enter, exit_, enter_tag) = self.strategy.get_signal(
|
(side, enter_tag) = self.strategy.get_enter_signal(
|
||||||
pair,
|
pair, self.strategy.timeframe, analyzed_df
|
||||||
self.strategy.timeframe,
|
)
|
||||||
analyzed_df
|
|
||||||
)
|
|
||||||
|
|
||||||
if enter and not exit_:
|
if side:
|
||||||
stake_amount = self.wallets.get_trade_stake_amount(pair, self.edge)
|
stake_amount = self.wallets.get_trade_stake_amount(pair, self.edge)
|
||||||
|
|
||||||
bid_check_dom = self.config.get('bid_strategy', {}).get('check_depth_of_market', {})
|
bid_check_dom = self.config.get('bid_strategy', {}).get('check_depth_of_market', {})
|
||||||
if ((bid_check_dom.get('enabled', False)) and
|
if ((bid_check_dom.get('enabled', False)) and
|
||||||
(bid_check_dom.get('bids_to_ask_delta', 0) > 0)):
|
(bid_check_dom.get('bids_to_ask_delta', 0) > 0)):
|
||||||
|
# TODO-lev: Does the below need to be adjusted for shorts?
|
||||||
if self._check_depth_of_market_buy(pair, bid_check_dom):
|
if self._check_depth_of_market_buy(pair, bid_check_dom):
|
||||||
|
# TODO-lev: pass in "enter" as side.
|
||||||
return self.execute_buy(pair, stake_amount, enter_tag=enter_tag)
|
return self.execute_buy(pair, stake_amount, enter_tag=enter_tag)
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
@ -707,7 +707,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
analyzed_df, _ = self.dataprovider.get_analyzed_dataframe(trade.pair,
|
analyzed_df, _ = self.dataprovider.get_analyzed_dataframe(trade.pair,
|
||||||
self.strategy.timeframe)
|
self.strategy.timeframe)
|
||||||
|
|
||||||
(buy, sell, _) = self.strategy.get_signal(
|
(buy, sell) = self.strategy.get_exit_signal(
|
||||||
trade.pair,
|
trade.pair,
|
||||||
self.strategy.timeframe,
|
self.strategy.timeframe,
|
||||||
analyzed_df
|
analyzed_df
|
||||||
|
@ -13,7 +13,7 @@ from pandas import DataFrame
|
|||||||
|
|
||||||
from freqtrade.constants import ListPairsWithTimeframes
|
from freqtrade.constants import ListPairsWithTimeframes
|
||||||
from freqtrade.data.dataprovider import DataProvider
|
from freqtrade.data.dataprovider import DataProvider
|
||||||
from freqtrade.enums import SellType, SignalTagType, SignalType
|
from freqtrade.enums import SellType, SignalTagType, SignalType, SignalDirection
|
||||||
from freqtrade.exceptions import OperationalException, StrategyError
|
from freqtrade.exceptions import OperationalException, StrategyError
|
||||||
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds
|
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds
|
||||||
from freqtrade.exchange.exchange import timeframe_to_next_date
|
from freqtrade.exchange.exchange import timeframe_to_next_date
|
||||||
@ -538,22 +538,18 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
else:
|
else:
|
||||||
raise StrategyError(message)
|
raise StrategyError(message)
|
||||||
|
|
||||||
def get_signal(
|
def get_latest_candle(
|
||||||
self,
|
self,
|
||||||
pair: str,
|
pair: str,
|
||||||
timeframe: str,
|
timeframe: str,
|
||||||
dataframe: DataFrame,
|
dataframe: DataFrame,
|
||||||
is_short: bool = False
|
) -> Tuple[Optional[DataFrame], arrow.Arrow]:
|
||||||
) -> Tuple[bool, bool, Optional[str]]:
|
|
||||||
"""
|
"""
|
||||||
Calculates current signal based based on the buy/short or sell/exit_short
|
Get the latest candle. Used only during real mode
|
||||||
columns of the dataframe.
|
|
||||||
Used by Bot to get the signal to buy, sell, short, or exit_short
|
|
||||||
:param pair: pair in format ANT/BTC
|
:param pair: pair in format ANT/BTC
|
||||||
:param timeframe: timeframe to use
|
:param timeframe: timeframe to use
|
||||||
:param dataframe: Analyzed dataframe to get signal from.
|
:param dataframe: Analyzed dataframe to get signal from.
|
||||||
:return: (Buy, Sell)/(Short, Exit_short) A bool-tuple indicating
|
:return: (None, None) or (Dataframe, latest_date) - corresponding to the last candle
|
||||||
(buy/sell)/(short/exit_short) signal
|
|
||||||
"""
|
"""
|
||||||
if not isinstance(dataframe, DataFrame) or dataframe.empty:
|
if not isinstance(dataframe, DataFrame) or dataframe.empty:
|
||||||
logger.warning(f'Empty candle (OHLCV) data for pair {pair}')
|
logger.warning(f'Empty candle (OHLCV) data for pair {pair}')
|
||||||
@ -572,34 +568,89 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
'Outdated history for pair %s. Last tick is %s minutes old',
|
'Outdated history for pair %s. Last tick is %s minutes old',
|
||||||
pair, int((arrow.utcnow() - latest_date).total_seconds() // 60)
|
pair, int((arrow.utcnow() - latest_date).total_seconds() // 60)
|
||||||
)
|
)
|
||||||
|
return None, None
|
||||||
|
return latest, latest_date
|
||||||
|
|
||||||
|
def get_exit_signal(
|
||||||
|
self,
|
||||||
|
pair: str,
|
||||||
|
timeframe: str,
|
||||||
|
dataframe: DataFrame,
|
||||||
|
is_short: bool = None
|
||||||
|
) -> Tuple[bool, bool]:
|
||||||
|
"""
|
||||||
|
Calculates current exit signal based based on the buy/short or sell/exit_short
|
||||||
|
columns of the dataframe.
|
||||||
|
Used by Bot to get the signal to exit.
|
||||||
|
depending on is_short, looks at "short" or "long" columns.
|
||||||
|
:param pair: pair in format ANT/BTC
|
||||||
|
:param timeframe: timeframe to use
|
||||||
|
:param dataframe: Analyzed dataframe to get signal from.
|
||||||
|
:param is_short: Indicating existing trade direction.
|
||||||
|
:return: (enter, exit) A bool-tuple with enter / exit values.
|
||||||
|
"""
|
||||||
|
latest, latest_date = self.get_latest_candle(pair, timeframe, dataframe)
|
||||||
|
if latest is None:
|
||||||
return False, False, None
|
return False, False, None
|
||||||
|
|
||||||
(enter_type, enter_tag) = (
|
if is_short:
|
||||||
(SignalType.SHORT, SignalTagType.SHORT_TAG)
|
enter = latest[SignalType.SHORT] == 1
|
||||||
if is_short else
|
exit_ = latest[SignalType.EXIT_SHORT] == 1
|
||||||
(SignalType.BUY, SignalTagType.BUY_TAG)
|
else:
|
||||||
)
|
enter = latest[SignalType.BUY] == 1
|
||||||
exit_type = SignalType.EXIT_SHORT if is_short else SignalType.SELL
|
exit_ = latest[SignalType.SELL] == 1
|
||||||
|
|
||||||
enter = latest[enter_type.value] == 1
|
logger.debug(f"exit-trigger: {latest['date']} (pair={pair}) "
|
||||||
|
f"enter={enter} exit={exit_}")
|
||||||
|
|
||||||
exit = False
|
return enter, exit_
|
||||||
if exit_type.value in latest:
|
|
||||||
exit = latest[exit_type.value] == 1
|
|
||||||
|
|
||||||
enter_tag_value = latest.get(enter_tag.value, None)
|
def get_enter_signal(
|
||||||
|
self,
|
||||||
|
pair: str,
|
||||||
|
timeframe: str,
|
||||||
|
dataframe: DataFrame,
|
||||||
|
) -> Tuple[Optional[SignalDirection], Optional[str]]:
|
||||||
|
"""
|
||||||
|
Calculates current entry signal based based on the buy/short or sell/exit_short
|
||||||
|
columns of the dataframe.
|
||||||
|
Used by Bot to get the signal to buy, sell, short, or exit_short
|
||||||
|
:param pair: pair in format ANT/BTC
|
||||||
|
:param timeframe: timeframe to use
|
||||||
|
:param dataframe: Analyzed dataframe to get signal from.
|
||||||
|
:return: (SignalDirection, entry_tag)
|
||||||
|
"""
|
||||||
|
latest, latest_date = self.get_latest_candle(pair, timeframe, dataframe)
|
||||||
|
if latest is None:
|
||||||
|
return False, False, 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_signal: Optional[SignalDirection] = None
|
||||||
|
enter_tag_value = 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)
|
||||||
|
if enter_short == 1 and not any([exit_short, enter_long]):
|
||||||
|
enter_signal = SignalDirection.SHORT
|
||||||
|
enter_tag_value = latest.get(SignalTagType.SHORT_TAG, None)
|
||||||
|
|
||||||
logger.debug(f'trigger: %s (pair=%s) {enter_type.value}=%s {exit_type.value}=%s',
|
|
||||||
latest['date'], pair, str(enter), str(exit))
|
|
||||||
timeframe_seconds = timeframe_to_seconds(timeframe)
|
timeframe_seconds = timeframe_to_seconds(timeframe)
|
||||||
|
|
||||||
if self.ignore_expired_candle(
|
if self.ignore_expired_candle(
|
||||||
latest_date=latest_date,
|
latest_date=latest_date,
|
||||||
current_time=datetime.now(timezone.utc),
|
current_time=datetime.now(timezone.utc),
|
||||||
timeframe_seconds=timeframe_seconds,
|
timeframe_seconds=timeframe_seconds,
|
||||||
enter=enter
|
enter=enter_signal
|
||||||
):
|
):
|
||||||
return False, exit, enter_tag_value
|
return False, enter_tag_value
|
||||||
return enter, exit, enter_tag_value
|
|
||||||
|
logger.debug(f"entry trigger: {latest['date']} (pair={pair}) "
|
||||||
|
f"enter={enter_long} enter_tag_value={enter_tag_value}")
|
||||||
|
return enter_signal, enter_tag_value
|
||||||
|
|
||||||
def ignore_expired_candle(
|
def ignore_expired_candle(
|
||||||
self,
|
self,
|
||||||
|
Loading…
Reference in New Issue
Block a user