Merge pull request #6275 from freqtrade/extract_timedout_from_ftbot
Extract timedout from ftbot
This commit is contained in:
commit
4b6f9121ca
@ -413,7 +413,7 @@ It applies a tight timeout for higher priced assets, while allowing more time to
|
||||
The function must return either `True` (cancel order) or `False` (keep order alive).
|
||||
|
||||
``` python
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from datetime import datetime, timedelta
|
||||
from freqtrade.persistence import Trade
|
||||
|
||||
class AwesomeStrategy(IStrategy):
|
||||
@ -426,22 +426,24 @@ class AwesomeStrategy(IStrategy):
|
||||
'sell': 60 * 25
|
||||
}
|
||||
|
||||
def check_buy_timeout(self, pair: str, trade: 'Trade', order: dict, **kwargs) -> bool:
|
||||
if trade.open_rate > 100 and trade.open_date_utc < datetime.now(timezone.utc) - timedelta(minutes=5):
|
||||
def check_buy_timeout(self, pair: str, trade: 'Trade', order: dict,
|
||||
current_time: datetime, **kwargs) -> bool:
|
||||
if trade.open_rate > 100 and trade.open_date_utc < current_time - timedelta(minutes=5):
|
||||
return True
|
||||
elif trade.open_rate > 10 and trade.open_date_utc < datetime.now(timezone.utc) - timedelta(minutes=3):
|
||||
elif trade.open_rate > 10 and trade.open_date_utc < current_time - timedelta(minutes=3):
|
||||
return True
|
||||
elif trade.open_rate < 1 and trade.open_date_utc < datetime.now(timezone.utc) - timedelta(hours=24):
|
||||
elif trade.open_rate < 1 and trade.open_date_utc < current_time - timedelta(hours=24):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def check_sell_timeout(self, pair: str, trade: 'Trade', order: dict, **kwargs) -> bool:
|
||||
if trade.open_rate > 100 and trade.open_date_utc < datetime.now(timezone.utc) - timedelta(minutes=5):
|
||||
def check_sell_timeout(self, pair: str, trade: Trade, order: dict,
|
||||
current_time: datetime, **kwargs) -> bool:
|
||||
if trade.open_rate > 100 and trade.open_date_utc < current_time - timedelta(minutes=5):
|
||||
return True
|
||||
elif trade.open_rate > 10 and trade.open_date_utc < datetime.now(timezone.utc) - timedelta(minutes=3):
|
||||
elif trade.open_rate > 10 and trade.open_date_utc < current_time - timedelta(minutes=3):
|
||||
return True
|
||||
elif trade.open_rate < 1 and trade.open_date_utc < datetime.now(timezone.utc) - timedelta(hours=24):
|
||||
elif trade.open_rate < 1 and trade.open_date_utc < current_time - timedelta(hours=24):
|
||||
return True
|
||||
return False
|
||||
```
|
||||
|
@ -9,8 +9,6 @@ from math import isclose
|
||||
from threading import Lock
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
import arrow
|
||||
|
||||
from freqtrade import __version__, constants
|
||||
from freqtrade.configuration import validate_config_consistency
|
||||
from freqtrade.data.converter import order_book_to_dataframe
|
||||
@ -959,20 +957,6 @@ class FreqtradeBot(LoggingMixin):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _check_timed_out(self, side: str, order: dict) -> bool:
|
||||
"""
|
||||
Check if timeout is active, and if the order is still open and timed out
|
||||
"""
|
||||
timeout = self.config.get('unfilledtimeout', {}).get(side)
|
||||
ordertime = arrow.get(order['datetime']).datetime
|
||||
if timeout is not None:
|
||||
timeout_unit = self.config.get('unfilledtimeout', {}).get('unit', 'minutes')
|
||||
timeout_kwargs = {timeout_unit: -timeout}
|
||||
timeout_threshold = arrow.utcnow().shift(**timeout_kwargs).datetime
|
||||
return (order['status'] == 'open' and order['side'] == side
|
||||
and ordertime < timeout_threshold)
|
||||
return False
|
||||
|
||||
def check_handle_timedout(self) -> None:
|
||||
"""
|
||||
Check if any orders are timed out and cancel if necessary
|
||||
@ -993,20 +977,16 @@ class FreqtradeBot(LoggingMixin):
|
||||
|
||||
if (order['side'] == 'buy' and (order['status'] == 'open' or fully_cancelled) and (
|
||||
fully_cancelled
|
||||
or self._check_timed_out('buy', order)
|
||||
or strategy_safe_wrapper(self.strategy.check_buy_timeout,
|
||||
default_retval=False)(pair=trade.pair,
|
||||
trade=trade,
|
||||
order=order))):
|
||||
or self.strategy.ft_check_timed_out(
|
||||
'buy', trade, order, datetime.now(timezone.utc))
|
||||
)):
|
||||
self.handle_cancel_enter(trade, order, constants.CANCEL_REASON['TIMEOUT'])
|
||||
|
||||
elif (order['side'] == 'sell' and (order['status'] == 'open' or fully_cancelled) and (
|
||||
fully_cancelled
|
||||
or self._check_timed_out('sell', order)
|
||||
or strategy_safe_wrapper(self.strategy.check_sell_timeout,
|
||||
default_retval=False)(pair=trade.pair,
|
||||
trade=trade,
|
||||
order=order))):
|
||||
or self.strategy.ft_check_timed_out(
|
||||
'sell', trade, order, datetime.now(timezone.utc)))
|
||||
):
|
||||
self.handle_cancel_exit(trade, order, constants.CANCEL_REASON['TIMEOUT'])
|
||||
canceled_count = trade.get_exit_order_count()
|
||||
max_timeouts = self.config.get('unfilledtimeout', {}).get('exit_timeout_count', 0)
|
||||
|
@ -188,7 +188,8 @@ class IStrategy(ABC, HyperStrategyMixin):
|
||||
"""
|
||||
return dataframe
|
||||
|
||||
def check_buy_timeout(self, pair: str, trade: Trade, order: dict, **kwargs) -> bool:
|
||||
def check_buy_timeout(self, pair: str, trade: Trade, order: dict,
|
||||
current_time: datetime, **kwargs) -> bool:
|
||||
"""
|
||||
Check buy timeout function callback.
|
||||
This method can be used to override the buy-timeout.
|
||||
@ -201,12 +202,14 @@ class IStrategy(ABC, HyperStrategyMixin):
|
||||
:param pair: Pair the trade is for
|
||||
:param trade: trade object.
|
||||
:param order: Order dictionary as returned from CCXT.
|
||||
:param current_time: datetime object, containing the current datetime
|
||||
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
|
||||
:return bool: When True is returned, then the buy-order is cancelled.
|
||||
"""
|
||||
return False
|
||||
|
||||
def check_sell_timeout(self, pair: str, trade: Trade, order: dict, **kwargs) -> bool:
|
||||
def check_sell_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 sell-timeout.
|
||||
@ -219,6 +222,7 @@ class IStrategy(ABC, HyperStrategyMixin):
|
||||
:param pair: Pair the trade is for
|
||||
:param trade: trade object.
|
||||
:param order: Order dictionary as returned from CCXT.
|
||||
:param current_time: datetime object, containing the current datetime
|
||||
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
|
||||
:return bool: When True is returned, then the sell-order is cancelled.
|
||||
"""
|
||||
@ -852,6 +856,29 @@ class IStrategy(ABC, HyperStrategyMixin):
|
||||
else:
|
||||
return current_profit > roi
|
||||
|
||||
def ft_check_timed_out(self, side: str, trade: Trade, order: Dict,
|
||||
current_time: datetime) -> bool:
|
||||
"""
|
||||
FT Internal method.
|
||||
Check if timeout is active, and if the order is still open and timed out
|
||||
"""
|
||||
timeout = self.config.get('unfilledtimeout', {}).get(side)
|
||||
ordertime = arrow.get(order['datetime']).datetime
|
||||
if timeout is not None:
|
||||
timeout_unit = self.config.get('unfilledtimeout', {}).get('unit', 'minutes')
|
||||
timeout_kwargs = {timeout_unit: -timeout}
|
||||
timeout_threshold = current_time + timedelta(**timeout_kwargs)
|
||||
timedout = (order['status'] == 'open' and order['side'] == side
|
||||
and ordertime < timeout_threshold)
|
||||
if timedout:
|
||||
return True
|
||||
time_method = self.check_sell_timeout if order['side'] == 'sell' else self.check_buy_timeout
|
||||
|
||||
return strategy_safe_wrapper(time_method,
|
||||
default_retval=False)(
|
||||
pair=trade.pair, trade=trade, order=order,
|
||||
current_time=current_time)
|
||||
|
||||
def advise_all_indicators(self, data: Dict[str, DataFrame]) -> Dict[str, DataFrame]:
|
||||
"""
|
||||
Populates indicators for given candle (OHLCV) data (for multiple pairs)
|
||||
|
Loading…
Reference in New Issue
Block a user