Update buy_timeout and sell_timeout methods

This commit is contained in:
Matthias
2022-03-25 19:46:56 +01:00
parent 973644de66
commit 6f1b14c013
11 changed files with 175 additions and 106 deletions

View File

@@ -1138,13 +1138,12 @@ class FreqtradeBot(LoggingMixin):
fully_cancelled = self.update_trade_state(trade, trade.open_order_id, order)
is_entering = order['side'] == trade.enter_side
not_closed = order['status'] == 'open' or fully_cancelled
time_method = 'sell' if order['side'] == 'sell' else 'buy'
max_timeouts = self.config.get('unfilledtimeout', {}).get('exit_timeout_count', 0)
order_obj = trade.select_order_by_order_id(trade.open_order_id)
if not_closed and (fully_cancelled or (order_obj and self.strategy.ft_check_timed_out(
time_method, trade, order_obj, datetime.now(timezone.utc)))
trade, order_obj, datetime.now(timezone.utc)))
):
if is_entering:
self.handle_cancel_enter(trade, order, constants.CANCEL_REASON['TIMEOUT'])

View File

@@ -853,7 +853,7 @@ class Backtesting:
"""
for order in [o for o in trade.orders if o.ft_is_open]:
timedout = self.strategy.ft_check_timed_out(order.side, trade, order, current_time)
timedout = self.strategy.ft_check_timed_out(trade, order, current_time)
if timedout:
if order.side == trade.enter_side:
self.timedout_entry_orders += 1

View File

@@ -169,6 +169,51 @@ class StrategyResolver(IResolver):
" in your strategy. Please note that short signals will be ignored in that case."
)
@staticmethod
def validate_strategy(strategy: IStrategy) -> IStrategy:
if strategy.config.get('trading_mode', TradingMode.SPOT) != TradingMode.SPOT:
# Require new method
if not check_override(strategy, IStrategy, 'populate_entry_trend'):
raise OperationalException("`populate_entry_trend` must be implemented.")
if not check_override(strategy, IStrategy, 'populate_exit_trend'):
raise OperationalException("`populate_exit_trend` must be implemented.")
if check_override(strategy, IStrategy, 'check_buy_timeout'):
raise OperationalException("Please migrate your implementation "
"of `check_buy_timeout` to `check_entry_timeout`.")
if check_override(strategy, IStrategy, 'check_sell_timeout'):
raise OperationalException("Please migrate your implementation "
"of `check_sell_timeout` to `check_exit_timeout`.")
if check_override(strategy, IStrategy, 'custom_sell'):
raise OperationalException(
"Please migrate your implementation of `custom_sell` to `custom_exit`.")
else:
# TODO: Implementing one of the following methods should show a deprecation warning
# buy_trend and sell_trend, custom_sell
if (
not check_override(strategy, IStrategy, 'populate_buy_trend')
and not check_override(strategy, IStrategy, 'populate_entry_trend')
):
raise OperationalException(
"`populate_entry_trend` or `populate_buy_trend` must be implemented.")
if (
not check_override(strategy, IStrategy, 'populate_sell_trend')
and not check_override(strategy, IStrategy, 'populate_exit_trend')
):
raise OperationalException(
"`populate_exit_trend` or `populate_sell_trend` must be implemented.")
strategy._populate_fun_len = len(getfullargspec(strategy.populate_indicators).args)
strategy._buy_fun_len = len(getfullargspec(strategy.populate_buy_trend).args)
strategy._sell_fun_len = len(getfullargspec(strategy.populate_sell_trend).args)
if any(x == 2 for x in [
strategy._populate_fun_len,
strategy._buy_fun_len,
strategy._sell_fun_len
]):
strategy.INTERFACE_VERSION = 1
return strategy
@staticmethod
def _load_strategy(strategy_name: str,
config: dict, extra_dir: Optional[str] = None) -> IStrategy:
@@ -208,42 +253,8 @@ class StrategyResolver(IResolver):
)
if strategy:
if strategy.config.get('trading_mode', TradingMode.SPOT) != TradingMode.SPOT:
# Require new method
if not check_override(strategy, IStrategy, 'populate_entry_trend'):
raise OperationalException("`populate_entry_trend` must be implemented.")
if not check_override(strategy, IStrategy, 'populate_exit_trend'):
raise OperationalException("`populate_exit_trend` must be implemented.")
if check_override(strategy, IStrategy, 'custom_sell'):
raise OperationalException(
"Please migrate your implementation of `custom_sell` to `custom_exit`.")
else:
# TODO: Implementing one of the following methods should show a deprecation warning
# buy_trend and sell_trend, custom_sell
if (
not check_override(strategy, IStrategy, 'populate_buy_trend')
and not check_override(strategy, IStrategy, 'populate_entry_trend')
):
raise OperationalException(
"`populate_entry_trend` or `populate_buy_trend` must be implemented.")
if (
not check_override(strategy, IStrategy, 'populate_sell_trend')
and not check_override(strategy, IStrategy, 'populate_exit_trend')
):
raise OperationalException(
"`populate_exit_trend` or `populate_sell_trend` must be implemented.")
strategy._populate_fun_len = len(getfullargspec(strategy.populate_indicators).args)
strategy._buy_fun_len = len(getfullargspec(strategy.populate_buy_trend).args)
strategy._sell_fun_len = len(getfullargspec(strategy.populate_sell_trend).args)
if any(x == 2 for x in [
strategy._populate_fun_len,
strategy._buy_fun_len,
strategy._sell_fun_len
]):
strategy.INTERFACE_VERSION = 1
return strategy
return StrategyResolver.validate_strategy(strategy)
raise OperationalException(
f"Impossible to load Strategy '{strategy_name}'. This class does not exist "

View File

@@ -209,7 +209,14 @@ class IStrategy(ABC, HyperStrategyMixin):
def check_buy_timeout(self, pair: str, trade: Trade, order: dict,
current_time: datetime, **kwargs) -> bool:
"""
Check buy timeout function callback.
DEPRECATED: Please use `check_entry_timeout` instead.
"""
return False
def check_entry_timeout(self, pair: str, trade: Trade, order: dict,
current_time: datetime, **kwargs) -> bool:
"""
Check entry timeout function callback.
This method can be used to override the enter-timeout.
It is called whenever a limit entry order has been created,
and is not yet fully filled.
@@ -224,11 +231,19 @@ class IStrategy(ABC, HyperStrategyMixin):
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
:return bool: When True is returned, then the entry order is cancelled.
"""
return False
return self.check_buy_timeout(
pair=pair, trade=trade, order=order, current_time=current_time)
def check_sell_timeout(self, pair: str, trade: Trade, order: dict,
current_time: datetime, **kwargs) -> bool:
"""
DEPRECATED: Please use `check_exit_timeout` instead.
"""
return False
def check_exit_timeout(self, pair: str, trade: Trade, order: dict,
current_time: datetime, **kwargs) -> bool:
"""
Check sell timeout function callback.
This method can be used to override the exit-timeout.
It is called whenever a (long) limit sell order or (short) limit buy
@@ -244,7 +259,8 @@ class IStrategy(ABC, HyperStrategyMixin):
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
:return bool: When True is returned, then the (long)sell/(short)buy-order is cancelled.
"""
return False
return self.check_exit_timeout(
pair=pair, trade=trade, order=order, current_time=current_time)
def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float,
time_in_force: str, current_time: datetime, entry_tag: Optional[str],
@@ -1023,12 +1039,13 @@ class IStrategy(ABC, HyperStrategyMixin):
else:
return current_profit > roi
def ft_check_timed_out(self, side: str, trade: LocalTrade, order: Order,
def ft_check_timed_out(self, trade: LocalTrade, order: Order,
current_time: datetime) -> bool:
"""
FT Internal method.
Check if timeout is active, and if the order is still open and timed out
"""
side = 'buy' if order.side == 'buy' else 'sell'
timeout = self.config.get('unfilledtimeout', {}).get(side)
if timeout is not None:
timeout_unit = self.config.get('unfilledtimeout', {}).get('unit', 'minutes')
@@ -1038,7 +1055,8 @@ class IStrategy(ABC, HyperStrategyMixin):
and order.order_date_utc < timeout_threshold)
if timedout:
return True
time_method = self.check_sell_timeout if order.side == 'sell' else self.check_buy_timeout
time_method = (self.check_exit_timeout if order.side == trade.exit_side
else self.check_entry_timeout)
return strategy_safe_wrapper(time_method,
default_retval=False)(

View File

@@ -170,11 +170,11 @@ def confirm_trade_exit(self, pair: str, trade: 'Trade', order_type: str, amount:
"""
return True
def check_buy_timeout(self, pair: str, trade: 'Trade', order: dict, **kwargs) -> bool:
def check_entry_timeout(self, pair: str, trade: 'Trade', order: dict, **kwargs) -> bool:
"""
Check buy timeout function callback.
This method can be used to override the buy-timeout.
It is called whenever a limit buy order has been created,
Check entry timeout function callback.
This method can be used to override the entry-timeout.
It is called whenever a limit entry order has been created,
and is not yet fully filled.
Configuration options in `unfilledtimeout` will be verified before this,
so ensure to set these timeouts high enough.
@@ -190,11 +190,11 @@ def check_buy_timeout(self, pair: str, trade: 'Trade', order: dict, **kwargs) ->
"""
return False
def check_sell_timeout(self, pair: str, trade: 'Trade', order: dict, **kwargs) -> bool:
def check_exit_timeout(self, pair: str, trade: 'Trade', order: dict, **kwargs) -> bool:
"""
Check sell timeout function callback.
This method can be used to override the sell-timeout.
It is called whenever a limit sell order has been created,
Check exit timeout function callback.
This method can be used to override the exit-timeout.
It is called whenever a limit exit order has been created,
and is not yet fully filled.
Configuration options in `unfilledtimeout` will be verified before this,
so ensure to set these timeouts high enough.