From 1b49e45222547e701b8b90a6cc6d2f905c6c8853 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Jul 2022 03:01:32 +0000 Subject: [PATCH 01/14] Bump orjson from 3.7.7 to 3.7.8 Bumps [orjson](https://github.com/ijl/orjson) from 3.7.7 to 3.7.8. - [Release notes](https://github.com/ijl/orjson/releases) - [Changelog](https://github.com/ijl/orjson/blob/master/CHANGELOG.md) - [Commits](https://github.com/ijl/orjson/compare/3.7.7...3.7.8) --- updated-dependencies: - dependency-name: orjson dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b27c8f559..a41c50f84 100644 --- a/requirements.txt +++ b/requirements.txt @@ -28,7 +28,7 @@ py_find_1st==1.1.5 # Load ticker files 30% faster python-rapidjson==1.8 # Properly format api responses -orjson==3.7.7 +orjson==3.7.8 # Notify systemd sdnotify==0.3.2 From d5933fb2affac545a7b462cb50f4479fd964d55b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Jul 2022 03:01:37 +0000 Subject: [PATCH 02/14] Bump mkdocs from 1.3.0 to 1.3.1 Bumps [mkdocs](https://github.com/mkdocs/mkdocs) from 1.3.0 to 1.3.1. - [Release notes](https://github.com/mkdocs/mkdocs/releases) - [Commits](https://github.com/mkdocs/mkdocs/compare/1.3.0...1.3.1) --- updated-dependencies: - dependency-name: mkdocs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- docs/requirements-docs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index a07f4f944..ea09b853b 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,5 +1,5 @@ markdown==3.4.1 -mkdocs==1.3.0 +mkdocs==1.3.1 mkdocs-material==8.3.9 mdx_truly_sane_lists==1.3 pymdown-extensions==9.5 From 98d0ad76bf1ed599ea3e3abd7da15a11c8c3caec Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Jul 2022 03:01:44 +0000 Subject: [PATCH 03/14] Bump types-requests from 2.28.1 to 2.28.3 Bumps [types-requests](https://github.com/python/typeshed) from 2.28.1 to 2.28.3. - [Release notes](https://github.com/python/typeshed/releases) - [Commits](https://github.com/python/typeshed/commits) --- updated-dependencies: - dependency-name: types-requests dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 3d91f29fd..fa56f1df9 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -24,6 +24,6 @@ nbconvert==6.5.0 # mypy types types-cachetools==5.2.1 types-filelock==3.2.7 -types-requests==2.28.1 +types-requests==2.28.3 types-tabulate==0.8.11 types-python-dateutil==2.8.18 From f93a3a5fca28b3c1677af181cfe7e4fa0443483c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Jul 2022 03:01:52 +0000 Subject: [PATCH 04/14] Bump ccxt from 1.90.89 to 1.91.22 Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.90.89 to 1.91.22. - [Release notes](https://github.com/ccxt/ccxt/releases) - [Changelog](https://github.com/ccxt/ccxt/blob/master/exchanges.cfg) - [Commits](https://github.com/ccxt/ccxt/compare/1.90.89...1.91.22) --- updated-dependencies: - dependency-name: ccxt dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b27c8f559..4e1c477cb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ numpy==1.23.1 pandas==1.4.3 pandas-ta==0.3.14b -ccxt==1.90.89 +ccxt==1.91.22 # Pin cryptography for now due to rust build errors with piwheels cryptography==37.0.4 aiohttp==3.8.1 From 3ce46ff09e980570c72ede2b2359fc8c92db4996 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 25 Jul 2022 07:19:21 +0200 Subject: [PATCH 05/14] Bump types-requests in pre-commit --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a23181c37..cbdeef780 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,7 +15,7 @@ repos: additional_dependencies: - types-cachetools==5.2.1 - types-filelock==3.2.7 - - types-requests==2.28.1 + - types-requests==2.28.3 - types-tabulate==0.8.11 - types-python-dateutil==2.8.18 # stages: [push] From 43343d0e55391fde8c0a469e23b387a346a37e38 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 25 Jul 2022 07:21:12 +0200 Subject: [PATCH 06/14] Revert markdown to 3.3.7 --- docs/requirements-docs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index ea09b853b..205516d6d 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,4 +1,4 @@ -markdown==3.4.1 +markdown==3.3.7 mkdocs==1.3.1 mkdocs-material==8.3.9 mdx_truly_sane_lists==1.3 From 93340f546b2395d4e50bb51391299496a7620f4b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Jul 2022 05:53:10 +0000 Subject: [PATCH 07/14] Bump mypy from 0.961 to 0.971 Bumps [mypy](https://github.com/python/mypy) from 0.961 to 0.971. - [Release notes](https://github.com/python/mypy/releases) - [Commits](https://github.com/python/mypy/compare/v0.961...v0.971) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index fa56f1df9..41e4722aa 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -7,7 +7,7 @@ coveralls==3.3.1 flake8==4.0.1 flake8-tidy-imports==4.8.0 -mypy==0.961 +mypy==0.971 pre-commit==2.20.0 pytest==7.1.2 pytest-asyncio==0.19.0 From 40969f20bfbfe2c84821a4553dc9c6963ca91415 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Jul 2022 05:53:15 +0000 Subject: [PATCH 08/14] Bump types-python-dateutil from 2.8.18 to 2.8.19 Bumps [types-python-dateutil](https://github.com/python/typeshed) from 2.8.18 to 2.8.19. - [Release notes](https://github.com/python/typeshed/releases) - [Commits](https://github.com/python/typeshed/commits) --- updated-dependencies: - dependency-name: types-python-dateutil dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index fa56f1df9..425937d6e 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -26,4 +26,4 @@ types-cachetools==5.2.1 types-filelock==3.2.7 types-requests==2.28.3 types-tabulate==0.8.11 -types-python-dateutil==2.8.18 +types-python-dateutil==2.8.19 From 47b52d4bab6d0d378759eabc72df94f424cfacd6 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 25 Jul 2022 07:58:16 +0200 Subject: [PATCH 09/14] Bump types-dateutil in pre-commit --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cbdeef780..759ac0a6a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,7 +17,7 @@ repos: - types-filelock==3.2.7 - types-requests==2.28.3 - types-tabulate==0.8.11 - - types-python-dateutil==2.8.18 + - types-python-dateutil==2.8.19 # stages: [push] - repo: https://github.com/pycqa/isort From ea112fb58399a0d00ca0582d462fe3c639d33a7b Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 25 Jul 2022 17:47:28 +0200 Subject: [PATCH 10/14] Add test for empty order (cancelled order) --- tests/exchange/test_exchange.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index ff8b4b40c..9252040ea 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -2910,6 +2910,9 @@ def test_check_order_canceled_empty(mocker, default_conf, exchange_name, order, ({'amount': 10.0, 'fee': {}}, False), ({'result': 'testest123'}, False), ('hello_world', False), + ({'status': 'canceled', 'amount': None, 'fee': None}, False), + ({'status': 'canceled', 'filled': None, 'amount': None, 'fee': None}, False), + ]) def test_is_cancel_order_result_suitable(mocker, default_conf, exchange_name, order, result): exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) From 4c68bec171c2849afbb5363d9e9bd48d178de854 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 25 Jul 2022 17:47:52 +0200 Subject: [PATCH 11/14] Fix problem in `is_cancel_order_result_suitable` fixes #7119 --- freqtrade/exchange/exchange.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 11e37b953..79bc769e6 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -1264,7 +1264,7 @@ class Exchange: return False required = ('fee', 'status', 'amount') - return all(k in corder for k in required) + return all(corder.get(k, None) is not None for k in required) def cancel_order_with_result(self, order_id: str, pair: str, amount: float) -> Dict: """ From a0b9388757e00ab04e29e87d265867009ccf6415 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 26 Jul 2022 17:57:25 +0200 Subject: [PATCH 12/14] Bump ccxt to 1.91.29 closes #7132 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 411827b62..b9e87749d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ numpy==1.23.1 pandas==1.4.3 pandas-ta==0.3.14b -ccxt==1.91.22 +ccxt==1.91.29 # Pin cryptography for now due to rust build errors with piwheels cryptography==37.0.4 aiohttp==3.8.1 From 2595e40e47f72970974fff7cc27331bf2387975a Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 27 Jul 2022 06:47:16 +0200 Subject: [PATCH 13/14] Remove unused test-strategy --- tests/rpc/test_rpc_apiserver.py | 1 - .../strats/strategy_test_v3_analysis.py | 175 ------------------ tests/strategy/test_strategy_loading.py | 6 +- 3 files changed, 3 insertions(+), 179 deletions(-) delete mode 100644 tests/strategy/strats/strategy_test_v3_analysis.py diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 57c08f48e..57ba8e9f1 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -1402,7 +1402,6 @@ def test_api_strategies(botclient): 'InformativeDecoratorTest', 'StrategyTestV2', 'StrategyTestV3', - 'StrategyTestV3Analysis', 'StrategyTestV3Futures' ]} diff --git a/tests/strategy/strats/strategy_test_v3_analysis.py b/tests/strategy/strats/strategy_test_v3_analysis.py deleted file mode 100644 index 290fef156..000000000 --- a/tests/strategy/strats/strategy_test_v3_analysis.py +++ /dev/null @@ -1,175 +0,0 @@ -# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement - -import talib.abstract as ta -from pandas import DataFrame - -import freqtrade.vendor.qtpylib.indicators as qtpylib -from freqtrade.strategy import (BooleanParameter, DecimalParameter, IntParameter, IStrategy, - RealParameter) - - -class StrategyTestV3Analysis(IStrategy): - """ - Strategy used by tests freqtrade bot. - Please do not modify this strategy, it's intended for internal use only. - Please look at the SampleStrategy in the user_data/strategy directory - or strategy repository https://github.com/freqtrade/freqtrade-strategies - for samples and inspiration. - """ - INTERFACE_VERSION = 3 - - # Minimal ROI designed for the strategy - minimal_roi = { - "40": 0.0, - "30": 0.01, - "20": 0.02, - "0": 0.04 - } - - # Optimal stoploss designed for the strategy - stoploss = -0.10 - - # Optimal timeframe for the strategy - timeframe = '5m' - - # Optional order type mapping - order_types = { - 'entry': 'limit', - 'exit': 'limit', - 'stoploss': 'limit', - 'stoploss_on_exchange': False - } - - # Number of candles the strategy requires before producing valid signals - startup_candle_count: int = 20 - - # Optional time in force for orders - order_time_in_force = { - 'entry': 'gtc', - 'exit': 'gtc', - } - - buy_params = { - 'buy_rsi': 35, - # Intentionally not specified, so "default" is tested - # 'buy_plusdi': 0.4 - } - - sell_params = { - 'sell_rsi': 74, - 'sell_minusdi': 0.4 - } - - buy_rsi = IntParameter([0, 50], default=30, space='buy') - buy_plusdi = RealParameter(low=0, high=1, default=0.5, space='buy') - sell_rsi = IntParameter(low=50, high=100, default=70, space='sell') - sell_minusdi = DecimalParameter(low=0, high=1, default=0.5001, decimals=3, space='sell', - load=False) - protection_enabled = BooleanParameter(default=True) - protection_cooldown_lookback = IntParameter([0, 50], default=30) - - # TODO: Can this work with protection tests? (replace HyperoptableStrategy implicitly ... ) - # @property - # def protections(self): - # prot = [] - # if self.protection_enabled.value: - # prot.append({ - # "method": "CooldownPeriod", - # "stop_duration_candles": self.protection_cooldown_lookback.value - # }) - # return prot - - bot_started = False - - def bot_start(self): - self.bot_started = True - - def informative_pairs(self): - - return [] - - def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: - - # Momentum Indicator - # ------------------------------------ - - # ADX - dataframe['adx'] = ta.ADX(dataframe) - - # MACD - macd = ta.MACD(dataframe) - dataframe['macd'] = macd['macd'] - dataframe['macdsignal'] = macd['macdsignal'] - dataframe['macdhist'] = macd['macdhist'] - - # Minus Directional Indicator / Movement - dataframe['minus_di'] = ta.MINUS_DI(dataframe) - - # Plus Directional Indicator / Movement - dataframe['plus_di'] = ta.PLUS_DI(dataframe) - - # RSI - dataframe['rsi'] = ta.RSI(dataframe) - - # Stoch fast - stoch_fast = ta.STOCHF(dataframe) - dataframe['fastd'] = stoch_fast['fastd'] - dataframe['fastk'] = stoch_fast['fastk'] - - # Bollinger bands - bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2) - dataframe['bb_lowerband'] = bollinger['lower'] - dataframe['bb_middleband'] = bollinger['mid'] - dataframe['bb_upperband'] = bollinger['upper'] - - # EMA - Exponential Moving Average - dataframe['ema10'] = ta.EMA(dataframe, timeperiod=10) - - return dataframe - - def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: - - dataframe.loc[ - ( - (dataframe['rsi'] < self.buy_rsi.value) & - (dataframe['fastd'] < 35) & - (dataframe['adx'] > 30) & - (dataframe['plus_di'] > self.buy_plusdi.value) - ) | - ( - (dataframe['adx'] > 65) & - (dataframe['plus_di'] > self.buy_plusdi.value) - ), - ['enter_long', 'enter_tag']] = 1, 'enter_tag_long' - - dataframe.loc[ - ( - qtpylib.crossed_below(dataframe['rsi'], self.sell_rsi.value) - ), - ['enter_short', 'enter_tag']] = 1, 'enter_tag_short' - - return dataframe - - def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: - dataframe.loc[ - ( - ( - (qtpylib.crossed_above(dataframe['rsi'], self.sell_rsi.value)) | - (qtpylib.crossed_above(dataframe['fastd'], 70)) - ) & - (dataframe['adx'] > 10) & - (dataframe['minus_di'] > 0) - ) | - ( - (dataframe['adx'] > 70) & - (dataframe['minus_di'] > self.sell_minusdi.value) - ), - ['exit_long', 'exit_tag']] = 1, 'exit_tag_long' - - dataframe.loc[ - ( - qtpylib.crossed_above(dataframe['rsi'], self.buy_rsi.value) - ), - ['exit_long', 'exit_tag']] = 1, 'exit_tag_short' - - return dataframe diff --git a/tests/strategy/test_strategy_loading.py b/tests/strategy/test_strategy_loading.py index bdfcf3211..666ae2b05 100644 --- a/tests/strategy/test_strategy_loading.py +++ b/tests/strategy/test_strategy_loading.py @@ -34,7 +34,7 @@ def test_search_all_strategies_no_failed(): directory = Path(__file__).parent / "strats" strategies = StrategyResolver.search_all_objects(directory, enum_failed=False) assert isinstance(strategies, list) - assert len(strategies) == 7 + assert len(strategies) == 6 assert isinstance(strategies[0], dict) @@ -42,10 +42,10 @@ def test_search_all_strategies_with_failed(): directory = Path(__file__).parent / "strats" strategies = StrategyResolver.search_all_objects(directory, enum_failed=True) assert isinstance(strategies, list) - assert len(strategies) == 8 + assert len(strategies) == 7 # with enum_failed=True search_all_objects() shall find 2 good strategies # and 1 which fails to load - assert len([x for x in strategies if x['class'] is not None]) == 7 + assert len([x for x in strategies if x['class'] is not None]) == 6 assert len([x for x in strategies if x['class'] is None]) == 1 From cc3ead9d7bd289bfceb97a8c165b6b519b4710c4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 27 Jul 2022 19:52:39 +0200 Subject: [PATCH 14/14] Set required_profit for stoploss guard, allowing to ignore small stoplosses. closes #7076 --- docs/includes/protections.md | 3 +++ freqtrade/plugins/protections/stoploss_guard.py | 5 +++-- tests/plugins/test_protections.py | 6 +++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/includes/protections.md b/docs/includes/protections.md index d67924cfe..e0ad8189f 100644 --- a/docs/includes/protections.md +++ b/docs/includes/protections.md @@ -50,6 +50,8 @@ This applies across all pairs, unless `only_per_pair` is set to true, which will Similarly, this protection will by default look at all trades (long and short). For futures bots, setting `only_per_side` will make the bot only consider one side, and will then only lock this one side, allowing for example shorts to continue after a series of long stoplosses. +`required_profit` will determine the required relative profit (or loss) for stoplosses to consider. This should normally not be set and defaults to 0.0 - which means all losing stoplosses will be triggering a block. + The below example stops trading for all pairs for 4 candles after the last trade if the bot hit stoploss 4 times within the last 24 candles. ``` python @@ -61,6 +63,7 @@ def protections(self): "lookback_period_candles": 24, "trade_limit": 4, "stop_duration_candles": 4, + "required_profit": 0.0, "only_per_pair": False, "only_per_side": False } diff --git a/freqtrade/plugins/protections/stoploss_guard.py b/freqtrade/plugins/protections/stoploss_guard.py index 713a2da07..abc90a685 100644 --- a/freqtrade/plugins/protections/stoploss_guard.py +++ b/freqtrade/plugins/protections/stoploss_guard.py @@ -23,13 +23,14 @@ class StoplossGuard(IProtection): self._trade_limit = protection_config.get('trade_limit', 10) self._disable_global_stop = protection_config.get('only_per_pair', False) self._only_per_side = protection_config.get('only_per_side', False) + self._profit_limit = protection_config.get('required_profit', 0.0) def short_desc(self) -> str: """ Short method description - used for startup-messages """ return (f"{self.name} - Frequent Stoploss Guard, {self._trade_limit} stoplosses " - f"within {self.lookback_period_str}.") + f"with profit < {self._profit_limit:.2%} within {self.lookback_period_str}.") def _reason(self) -> str: """ @@ -49,7 +50,7 @@ class StoplossGuard(IProtection): trades = [trade for trade in trades1 if (str(trade.exit_reason) in ( ExitType.TRAILING_STOP_LOSS.value, ExitType.STOP_LOSS.value, ExitType.STOPLOSS_ON_EXCHANGE.value) - and trade.close_profit and trade.close_profit < 0)] + and trade.close_profit and trade.close_profit < self._profit_limit)] if self._only_per_side: # Long or short trades only diff --git a/tests/plugins/test_protections.py b/tests/plugins/test_protections.py index 3c333200c..4cebb6492 100644 --- a/tests/plugins/test_protections.py +++ b/tests/plugins/test_protections.py @@ -424,7 +424,7 @@ def test_MaxDrawdown(mocker, default_conf, fee, caplog): @pytest.mark.parametrize("protectionconf,desc_expected,exception_expected", [ ({"method": "StoplossGuard", "lookback_period": 60, "trade_limit": 2, "stop_duration": 60}, "[{'StoplossGuard': 'StoplossGuard - Frequent Stoploss Guard, " - "2 stoplosses within 60 minutes.'}]", + "2 stoplosses with profit < 0.00% within 60 minutes.'}]", None ), ({"method": "CooldownPeriod", "stop_duration": 60}, @@ -442,9 +442,9 @@ def test_MaxDrawdown(mocker, default_conf, fee, caplog): None ), ({"method": "StoplossGuard", "lookback_period_candles": 12, "trade_limit": 2, - "stop_duration": 60}, + "required_profit": -0.05, "stop_duration": 60}, "[{'StoplossGuard': 'StoplossGuard - Frequent Stoploss Guard, " - "2 stoplosses within 12 candles.'}]", + "2 stoplosses with profit < -5.00% within 12 candles.'}]", None ), ({"method": "CooldownPeriod", "stop_duration_candles": 5},