From 82a4a3c237c25551ae92403cdc40a71c4313906e Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Sun, 3 Aug 2025 14:53:24 +0200 Subject: [PATCH] perf(qav3): readd stoploss guard protection with sensible configuration MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérôme Benoit --- .../user_data/strategies/RLAgentStrategy.py | 26 +++++++++--- .../user_data/strategies/QuickAdapterV3.py | 40 +++++++++++++------ quickadapter/user_data/strategies/Utils.py | 2 +- 3 files changed, 50 insertions(+), 18 deletions(-) diff --git a/ReforceXY/user_data/strategies/RLAgentStrategy.py b/ReforceXY/user_data/strategies/RLAgentStrategy.py index a4f4f53..fa14346 100644 --- a/ReforceXY/user_data/strategies/RLAgentStrategy.py +++ b/ReforceXY/user_data/strategies/RLAgentStrategy.py @@ -37,8 +37,24 @@ class RLAgentStrategy(IStrategy): # @cached_property # def protections(self) -> list[dict[str, Any]]: - # fit_live_predictions_candles = self.freqai_info.get( - # "fit_live_predictions_candles", 100 + # fit_live_predictions_candles = int( + # self.freqai_info.get("fit_live_predictions_candles", 100) + # ) + # estimated_trade_duration_candles = int( + # self.config.get("estimated_trade_duration_candles", 36) + # ) + # stoploss_guard_lookback_period_candles = int(fit_live_predictions_candles / 2) + # stoploss_guard_trade_limit = max( + # 1, + # int( + # round( + # ( + # stoploss_guard_lookback_period_candles + # / estimated_trade_duration_candles + # ) + # * 0.75 + # ) + # ), # ) # return [ # {"method": "CooldownPeriod", "stop_duration_candles": 4}, @@ -51,9 +67,9 @@ class RLAgentStrategy(IStrategy): # }, # { # "method": "StoplossGuard", - # "lookback_period_candles": int(fit_live_predictions_candles / 2), - # "trade_limit": 1, - # "stop_duration_candles": int(fit_live_predictions_candles / 2), + # "lookback_period_candles": stoploss_guard_lookback_period_candles, + # "trade_limit": stoploss_guard_trade_limit, + # "stop_duration_candles": stoploss_guard_lookback_period_candles, # "only_per_pair": True, # }, # ] diff --git a/quickadapter/user_data/strategies/QuickAdapterV3.py b/quickadapter/user_data/strategies/QuickAdapterV3.py index 0cbf0b9..6c8f11b 100644 --- a/quickadapter/user_data/strategies/QuickAdapterV3.py +++ b/quickadapter/user_data/strategies/QuickAdapterV3.py @@ -31,7 +31,7 @@ from Utils import ( top_change_percent, get_distance, get_odd_window, - get_gaussian_std_from_window, + get_gaussian_std, zlema, ) @@ -64,7 +64,7 @@ class QuickAdapterV3(IStrategy): INTERFACE_VERSION = 3 def version(self) -> str: - return "3.3.127" + return "3.3.128" timeframe = "5m" @@ -130,8 +130,24 @@ class QuickAdapterV3(IStrategy): @cached_property def protections(self) -> list[dict[str, Any]]: - fit_live_predictions_candles = self.freqai_info.get( - "fit_live_predictions_candles", 100 + fit_live_predictions_candles = int( + self.freqai_info.get("fit_live_predictions_candles", 100) + ) + estimated_trade_duration_candles = int( + self.config.get("estimated_trade_duration_candles", 36) + ) + stoploss_guard_lookback_period_candles = int(fit_live_predictions_candles / 2) + stoploss_guard_trade_limit = max( + 1, + int( + round( + ( + stoploss_guard_lookback_period_candles + / estimated_trade_duration_candles + ) + * 0.75 + ) + ), ) return [ {"method": "CooldownPeriod", "stop_duration_candles": 4}, @@ -142,13 +158,13 @@ class QuickAdapterV3(IStrategy): "stop_duration_candles": fit_live_predictions_candles, "max_allowed_drawdown": 0.2, }, - # { - # "method": "StoplossGuard", - # "lookback_period_candles": int(fit_live_predictions_candles / 2), - # "trade_limit": 1, - # "stop_duration_candles": int(fit_live_predictions_candles / 2), - # "only_per_pair": True, - # }, + { + "method": "StoplossGuard", + "lookback_period_candles": stoploss_guard_lookback_period_candles, + "trade_limit": stoploss_guard_trade_limit, + "stop_duration_candles": stoploss_guard_lookback_period_candles, + "only_per_pair": True, + }, ] use_exit_signal = True @@ -1035,7 +1051,7 @@ class QuickAdapterV3(IStrategy): window: int, ) -> Series: extrema_smoothing = str(self.freqai_info.get("extrema_smoothing", "gaussian")) - std = get_gaussian_std_from_window(window) + std = get_gaussian_std(window) extrema_smoothing_beta = float( self.freqai_info.get("extrema_smoothing_beta", 8.0) ) diff --git a/quickadapter/user_data/strategies/Utils.py b/quickadapter/user_data/strategies/Utils.py index 350d453..5bd0d73 100644 --- a/quickadapter/user_data/strategies/Utils.py +++ b/quickadapter/user_data/strategies/Utils.py @@ -30,7 +30,7 @@ def get_odd_window(window: int) -> int: @lru_cache(maxsize=8) -def get_gaussian_std_from_window(window: int) -> float: +def get_gaussian_std(window: int) -> float: # Assuming window = 6 * std + 1 => std = (window - 1) / 6 return (window - 1) / 6.0 if window > 1 else 0.5 -- 2.43.0