From 3ed682a9c630dd1d2899f12258f20052a2469e58 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 14 Aug 2023 16:20:54 +0200 Subject: [PATCH] Allow None from custom_stop --- docs/strategy-callbacks.md | 19 ++++++++++--------- docs/strategy-customization.md | 4 ++-- docs/strategy_migration.md | 3 ++- freqtrade/strategy/interface.py | 4 ++-- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/docs/strategy-callbacks.md b/docs/strategy-callbacks.md index 93be196d4..a64c392fa 100644 --- a/docs/strategy-callbacks.md +++ b/docs/strategy-callbacks.md @@ -164,6 +164,7 @@ E.g. If the `current_rate` is 200 USD, then returning `0.02` will set the stoplo During backtesting, `current_rate` (and `current_profit`) are provided against the candle's high (or low for short trades) - while the resulting stoploss is evaluated against the candle's low (or high for short trades). The absolute value of the return value is used (the sign is ignored), so returning `0.05` or `-0.05` have the same result, a stoploss 5% below the current price. +Returning None will be interpreted as "no desire to change", and is the only safe way to return when you'd like to not modify the stoploss. To simulate a regular trailing stoploss of 4% (trailing 4% behind the maximum reached price) you would use the following very simple method: @@ -180,7 +181,7 @@ class AwesomeStrategy(IStrategy): def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float, current_profit: float, after_fill: bool, - **kwargs) -> float: + **kwargs) -> Optional[float]: """ 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. @@ -232,14 +233,14 @@ class AwesomeStrategy(IStrategy): def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float, current_profit: float, after_fill: bool, - **kwargs) -> float: + **kwargs) -> Optional[float]: # Make sure you have the longest interval first - these conditions are evaluated from top to bottom. if current_time - timedelta(minutes=120) > trade.open_date_utc: return -0.05 elif current_time - timedelta(minutes=60) > trade.open_date_utc: return -0.10 - return 1 + return None ``` #### Different stoploss per pair @@ -259,7 +260,7 @@ class AwesomeStrategy(IStrategy): def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float, current_profit: float, after_fill: bool, - **kwargs) -> float: + **kwargs) -> Optional[float]: if pair in ('ETH/BTC', 'XRP/BTC'): return -0.10 @@ -286,7 +287,7 @@ class AwesomeStrategy(IStrategy): def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float, current_profit: float, after_fill: bool, - **kwargs) -> float: + **kwargs) -> Optional[float]: if current_profit < 0.04: return -1 # return a value bigger than the initial stoploss to keep using the initial stoploss @@ -320,7 +321,7 @@ class AwesomeStrategy(IStrategy): def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float, current_profit: float, after_fill: bool, - **kwargs) -> float: + **kwargs) -> Optional[float]: # evaluate highest to lowest, so that highest possible stop is used if current_profit > 0.40: @@ -331,7 +332,7 @@ class AwesomeStrategy(IStrategy): return stoploss_from_open(0.07, current_profit, is_short=trade.is_short, leverage=trade.leverage) # return maximum stoploss value, keeping current stoploss price unchanged - return 1 + return None ``` #### Custom stoploss using an indicator from dataframe example @@ -349,7 +350,7 @@ class AwesomeStrategy(IStrategy): def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float, current_profit: float, after_fill: bool, - **kwargs) -> float: + **kwargs) -> Optional[float]: dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) last_candle = dataframe.iloc[-1].squeeze() @@ -362,7 +363,7 @@ class AwesomeStrategy(IStrategy): return stoploss_from_absolute(stoploss_price, current_rate, is_short=trade.is_short) # return maximum stoploss value, keeping current stoploss price unchanged - return 1 + return None ``` See [Dataframe access](strategy-advanced.md#dataframe-access) for more information about dataframe use in strategy callbacks. diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index f81e07c7a..c77b59788 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -902,7 +902,7 @@ Stoploss values returned from `custom_stoploss` must specify a percentage relati def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float, current_profit: float, after_fill: bool, - **kwargs) -> float: + **kwargs) -> Optional[float]: # once the profit has risen above 10%, keep the stoploss at 7% above the open price if current_profit > 0.10: @@ -945,7 +945,7 @@ In some situations it may be confusing to deal with stops relative to current ra def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float, current_profit: float, after_fill: bool, - **kwargs) -> float: + **kwargs) -> Optional[float]: dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) candle = dataframe.iloc[-1].squeeze() return stoploss_from_absolute(current_rate - (candle['atr'] * 2), current_rate, is_short=trade.is_short) diff --git a/docs/strategy_migration.md b/docs/strategy_migration.md index 87ccf7667..0af901bb3 100644 --- a/docs/strategy_migration.md +++ b/docs/strategy_migration.md @@ -311,7 +311,8 @@ After: ``` python hl_lines="5 7" def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, - current_rate: float, current_profit: float, after_fill: bool, **kwargs) -> float: + current_rate: float, current_profit: float, after_fill: bool, + **kwargs) -> Optional[float]: # once the profit has risen above 10%, keep the stoploss at 7% above the open price if current_profit > 0.10: return stoploss_from_open(0.07, current_profit, is_short=trade.is_short) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index cbd241a02..5010f3db7 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -373,7 +373,7 @@ class IStrategy(ABC, HyperStrategyMixin): return True def custom_stoploss(self, pair: str, trade: Trade, current_time: datetime, current_rate: float, - current_profit: float, after_fill: bool, **kwargs) -> float: + current_profit: float, after_fill: bool, **kwargs) -> Optional[float]: """ 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. @@ -1200,7 +1200,7 @@ class IStrategy(ABC, HyperStrategyMixin): trade.adjust_stop_loss(bound or current_rate, stop_loss_value, allow_refresh=after_fill) else: - logger.warning("CustomStoploss function did not return valid stoploss") + logger.debug("CustomStoploss function did not return valid stoploss") if self.trailing_stop and dir_correct: # trailing stoploss handling