Add dataframe parameter to custom_stoploss() and custom_sell() methods.
This commit is contained in:
parent
961b38636f
commit
595b8735f8
@ -11,6 +11,7 @@ from typing import Any, Dict, List, Optional
|
|||||||
|
|
||||||
import arrow
|
import arrow
|
||||||
from cachetools import TTLCache
|
from cachetools import TTLCache
|
||||||
|
from pandas import DataFrame
|
||||||
|
|
||||||
from freqtrade import __version__, constants
|
from freqtrade import __version__, constants
|
||||||
from freqtrade.configuration import validate_config_consistency
|
from freqtrade.configuration import validate_config_consistency
|
||||||
@ -783,10 +784,10 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
|
|
||||||
config_ask_strategy = self.config.get('ask_strategy', {})
|
config_ask_strategy = self.config.get('ask_strategy', {})
|
||||||
|
|
||||||
|
analyzed_df, _ = self.dataprovider.get_analyzed_dataframe(trade.pair,
|
||||||
|
self.strategy.timeframe)
|
||||||
if (config_ask_strategy.get('use_sell_signal', True) or
|
if (config_ask_strategy.get('use_sell_signal', True) or
|
||||||
config_ask_strategy.get('ignore_roi_if_buy_signal', False)):
|
config_ask_strategy.get('ignore_roi_if_buy_signal', False)):
|
||||||
analyzed_df, _ = self.dataprovider.get_analyzed_dataframe(trade.pair,
|
|
||||||
self.strategy.timeframe)
|
|
||||||
|
|
||||||
(buy, sell) = self.strategy.get_signal(trade.pair, self.strategy.timeframe, analyzed_df)
|
(buy, sell) = self.strategy.get_signal(trade.pair, self.strategy.timeframe, analyzed_df)
|
||||||
|
|
||||||
@ -813,13 +814,13 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
# resulting in outdated RPC messages
|
# resulting in outdated RPC messages
|
||||||
self._sell_rate_cache[trade.pair] = sell_rate
|
self._sell_rate_cache[trade.pair] = sell_rate
|
||||||
|
|
||||||
if self._check_and_execute_sell(trade, sell_rate, buy, sell):
|
if self._check_and_execute_sell(analyzed_df, trade, sell_rate, buy, sell):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
else:
|
else:
|
||||||
logger.debug('checking sell')
|
logger.debug('checking sell')
|
||||||
sell_rate = self.get_sell_rate(trade.pair, True)
|
sell_rate = self.get_sell_rate(trade.pair, True)
|
||||||
if self._check_and_execute_sell(trade, sell_rate, buy, sell):
|
if self._check_and_execute_sell(analyzed_df, trade, sell_rate, buy, sell):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
logger.debug('Found no sell signal for %s.', trade)
|
logger.debug('Found no sell signal for %s.', trade)
|
||||||
@ -950,13 +951,13 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
logger.warning(f"Could not create trailing stoploss order "
|
logger.warning(f"Could not create trailing stoploss order "
|
||||||
f"for pair {trade.pair}.")
|
f"for pair {trade.pair}.")
|
||||||
|
|
||||||
def _check_and_execute_sell(self, trade: Trade, sell_rate: float,
|
def _check_and_execute_sell(self, dataframe: DataFrame, trade: Trade, sell_rate: float,
|
||||||
buy: bool, sell: bool) -> bool:
|
buy: bool, sell: bool) -> bool:
|
||||||
"""
|
"""
|
||||||
Check and execute sell
|
Check and execute sell
|
||||||
"""
|
"""
|
||||||
should_sell = self.strategy.should_sell(
|
should_sell = self.strategy.should_sell(
|
||||||
trade, sell_rate, datetime.now(timezone.utc), buy, sell,
|
dataframe, trade, sell_rate, datetime.now(timezone.utc), buy, sell,
|
||||||
force_stoploss=self.edge.stoploss(trade.pair) if self.edge else 0
|
force_stoploss=self.edge.stoploss(trade.pair) if self.edge else 0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -247,9 +247,10 @@ class Backtesting:
|
|||||||
else:
|
else:
|
||||||
return sell_row[OPEN_IDX]
|
return sell_row[OPEN_IDX]
|
||||||
|
|
||||||
def _get_sell_trade_entry(self, trade: LocalTrade, sell_row: Tuple) -> Optional[LocalTrade]:
|
def _get_sell_trade_entry(self, dataframe: DataFrame, trade: LocalTrade,
|
||||||
|
sell_row: Tuple) -> Optional[LocalTrade]:
|
||||||
|
|
||||||
sell = self.strategy.should_sell(trade, sell_row[OPEN_IDX], # type: ignore
|
sell = self.strategy.should_sell(dataframe, trade, sell_row[OPEN_IDX], # type: ignore
|
||||||
sell_row[DATE_IDX], sell_row[BUY_IDX], sell_row[SELL_IDX],
|
sell_row[DATE_IDX], sell_row[BUY_IDX], sell_row[SELL_IDX],
|
||||||
low=sell_row[LOW_IDX], high=sell_row[HIGH_IDX])
|
low=sell_row[LOW_IDX], high=sell_row[HIGH_IDX])
|
||||||
|
|
||||||
@ -396,7 +397,7 @@ class Backtesting:
|
|||||||
|
|
||||||
for trade in open_trades[pair]:
|
for trade in open_trades[pair]:
|
||||||
# also check the buying candle for sell conditions.
|
# also check the buying candle for sell conditions.
|
||||||
trade_entry = self._get_sell_trade_entry(trade, row)
|
trade_entry = self._get_sell_trade_entry(processed[pair], trade, row)
|
||||||
# Sell occured
|
# Sell occured
|
||||||
if trade_entry:
|
if trade_entry:
|
||||||
# logger.debug(f"{pair} - Backtesting sell {trade}")
|
# logger.debug(f"{pair} - Backtesting sell {trade}")
|
||||||
|
@ -274,7 +274,7 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def custom_stoploss(self, pair: str, trade: Trade, current_time: datetime, current_rate: float,
|
def custom_stoploss(self, pair: str, trade: Trade, current_time: datetime, current_rate: float,
|
||||||
current_profit: float, **kwargs) -> float:
|
current_profit: float, dataframe: DataFrame, **kwargs) -> float:
|
||||||
"""
|
"""
|
||||||
Custom stoploss logic, returning the new distance relative to current_rate (as ratio).
|
Custom stoploss logic, returning the new distance relative to current_rate (as ratio).
|
||||||
e.g. returning -0.05 would create a stoploss 5% below current_rate.
|
e.g. returning -0.05 would create a stoploss 5% below current_rate.
|
||||||
@ -296,7 +296,8 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
return self.stoploss
|
return self.stoploss
|
||||||
|
|
||||||
def custom_sell(self, pair: str, trade: Trade, current_time: datetime, current_rate: float,
|
def custom_sell(self, pair: str, trade: Trade, current_time: datetime, current_rate: float,
|
||||||
current_profit: float, **kwargs) -> Optional[Union[str, bool]]:
|
current_profit: float, dataframe: DataFrame,
|
||||||
|
**kwargs) -> Optional[Union[str, bool]]:
|
||||||
"""
|
"""
|
||||||
Custom sell signal logic indicating that specified position should be sold. Returning a
|
Custom sell signal logic indicating that specified position should be sold. Returning a
|
||||||
string or True from this method is equal to setting sell signal on a candle at specified
|
string or True from this method is equal to setting sell signal on a candle at specified
|
||||||
@ -534,8 +535,8 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def should_sell(self, trade: Trade, rate: float, date: datetime, buy: bool,
|
def should_sell(self, dataframe: DataFrame, trade: Trade, rate: float, date: datetime,
|
||||||
sell: bool, low: float = None, high: float = None,
|
buy: bool, sell: bool, low: float = None, high: float = None,
|
||||||
force_stoploss: float = 0) -> SellCheckTuple:
|
force_stoploss: float = 0) -> SellCheckTuple:
|
||||||
"""
|
"""
|
||||||
This function evaluates if one of the conditions required to trigger a sell
|
This function evaluates if one of the conditions required to trigger a sell
|
||||||
@ -551,8 +552,9 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
|
|
||||||
trade.adjust_min_max_rates(high or current_rate)
|
trade.adjust_min_max_rates(high or current_rate)
|
||||||
|
|
||||||
stoplossflag = self.stop_loss_reached(current_rate=current_rate, trade=trade,
|
stoplossflag = self.stop_loss_reached(dataframe=dataframe, current_rate=current_rate,
|
||||||
current_time=date, current_profit=current_profit,
|
trade=trade, current_time=date,
|
||||||
|
current_profit=current_profit,
|
||||||
force_stoploss=force_stoploss, high=high)
|
force_stoploss=force_stoploss, high=high)
|
||||||
|
|
||||||
# Set current rate to high for backtesting sell
|
# Set current rate to high for backtesting sell
|
||||||
@ -576,7 +578,7 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
sell_signal = SellType.SELL_SIGNAL
|
sell_signal = SellType.SELL_SIGNAL
|
||||||
else:
|
else:
|
||||||
custom_reason = strategy_safe_wrapper(self.custom_sell, default_retval=False)(
|
custom_reason = strategy_safe_wrapper(self.custom_sell, default_retval=False)(
|
||||||
trade.pair, trade, date, current_rate, current_profit)
|
trade.pair, trade, date, current_rate, current_profit, dataframe)
|
||||||
if custom_reason:
|
if custom_reason:
|
||||||
sell_signal = SellType.CUSTOM_SELL
|
sell_signal = SellType.CUSTOM_SELL
|
||||||
if isinstance(custom_reason, str):
|
if isinstance(custom_reason, str):
|
||||||
@ -615,7 +617,7 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
# logger.debug(f"{trade.pair} - No sell signal.")
|
# logger.debug(f"{trade.pair} - No sell signal.")
|
||||||
return SellCheckTuple(sell_type=SellType.NONE)
|
return SellCheckTuple(sell_type=SellType.NONE)
|
||||||
|
|
||||||
def stop_loss_reached(self, current_rate: float, trade: Trade,
|
def stop_loss_reached(self, dataframe: DataFrame, current_rate: float, trade: Trade,
|
||||||
current_time: datetime, current_profit: float,
|
current_time: datetime, current_profit: float,
|
||||||
force_stoploss: float, high: float = None) -> SellCheckTuple:
|
force_stoploss: float, high: float = None) -> SellCheckTuple:
|
||||||
"""
|
"""
|
||||||
@ -633,7 +635,8 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
)(pair=trade.pair, trade=trade,
|
)(pair=trade.pair, trade=trade,
|
||||||
current_time=current_time,
|
current_time=current_time,
|
||||||
current_rate=current_rate,
|
current_rate=current_rate,
|
||||||
current_profit=current_profit)
|
current_profit=current_profit,
|
||||||
|
dataframe=dataframe)
|
||||||
# Sanity check - error cases will return None
|
# Sanity check - error cases will return None
|
||||||
if stop_loss_value:
|
if stop_loss_value:
|
||||||
# logger.info(f"{trade.pair} {stop_loss_value=} {current_profit=}")
|
# logger.info(f"{trade.pair} {stop_loss_value=} {current_profit=}")
|
||||||
|
@ -41,4 +41,5 @@ def test_default_strategy(result, fee):
|
|||||||
rate=20000, time_in_force='gtc', sell_reason='roi') is True
|
rate=20000, time_in_force='gtc', sell_reason='roi') is True
|
||||||
|
|
||||||
assert strategy.custom_stoploss(pair='ETH/BTC', trade=trade, current_time=datetime.now(),
|
assert strategy.custom_stoploss(pair='ETH/BTC', trade=trade, current_time=datetime.now(),
|
||||||
current_rate=20_000, current_profit=0.05) == strategy.stoploss
|
current_rate=20_000, current_profit=0.05, dataframe=None
|
||||||
|
) == strategy.stoploss
|
||||||
|
@ -360,7 +360,7 @@ def test_stop_loss_reached(default_conf, fee, profit, adjusted, expected, traili
|
|||||||
now = arrow.utcnow().datetime
|
now = arrow.utcnow().datetime
|
||||||
sl_flag = strategy.stop_loss_reached(current_rate=trade.open_rate * (1 + profit), trade=trade,
|
sl_flag = strategy.stop_loss_reached(current_rate=trade.open_rate * (1 + profit), trade=trade,
|
||||||
current_time=now, current_profit=profit,
|
current_time=now, current_profit=profit,
|
||||||
force_stoploss=0, high=None)
|
force_stoploss=0, high=None, dataframe=None)
|
||||||
assert isinstance(sl_flag, SellCheckTuple)
|
assert isinstance(sl_flag, SellCheckTuple)
|
||||||
assert sl_flag.sell_type == expected
|
assert sl_flag.sell_type == expected
|
||||||
if expected == SellType.NONE:
|
if expected == SellType.NONE:
|
||||||
@ -371,7 +371,7 @@ def test_stop_loss_reached(default_conf, fee, profit, adjusted, expected, traili
|
|||||||
|
|
||||||
sl_flag = strategy.stop_loss_reached(current_rate=trade.open_rate * (1 + profit2), trade=trade,
|
sl_flag = strategy.stop_loss_reached(current_rate=trade.open_rate * (1 + profit2), trade=trade,
|
||||||
current_time=now, current_profit=profit2,
|
current_time=now, current_profit=profit2,
|
||||||
force_stoploss=0, high=None)
|
force_stoploss=0, high=None, dataframe=None)
|
||||||
assert sl_flag.sell_type == expected2
|
assert sl_flag.sell_type == expected2
|
||||||
if expected2 == SellType.NONE:
|
if expected2 == SellType.NONE:
|
||||||
assert sl_flag.sell_flag is False
|
assert sl_flag.sell_flag is False
|
||||||
|
Loading…
Reference in New Issue
Block a user