diff --git a/build_helpers/schema.json b/build_helpers/schema.json
index 8438dc3a0..6a73e75b0 100644
--- a/build_helpers/schema.json
+++ b/build_helpers/schema.json
@@ -579,57 +579,6 @@
]
}
},
- "protections": {
- "description": "Configuration for various protections.",
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "method": {
- "description": "Method used for the protection.",
- "type": "string",
- "enum": [
- "CooldownPeriod",
- "LowProfitPairs",
- "MaxDrawdown",
- "StoplossGuard"
- ]
- },
- "stop_duration": {
- "description": "Duration to lock the pair after a protection is triggered, in minutes.",
- "type": "number",
- "minimum": 0.0
- },
- "stop_duration_candles": {
- "description": "Duration to lock the pair after a protection is triggered, in number of candles.",
- "type": "number",
- "minimum": 0
- },
- "unlock_at": {
- "description": "Time when trading will be unlocked regularly. Format: HH:MM",
- "type": "string"
- },
- "trade_limit": {
- "description": "Minimum number of trades required during lookback period.",
- "type": "number",
- "minimum": 1
- },
- "lookback_period": {
- "description": "Period to look back for protection checks, in minutes.",
- "type": "number",
- "minimum": 1
- },
- "lookback_period_candles": {
- "description": "Period to look back for protection checks, in number of candles.",
- "type": "number",
- "minimum": 1
- }
- },
- "required": [
- "method"
- ]
- }
- },
"telegram": {
"description": "Telegram settings.",
"type": "object",
diff --git a/docs/configuration.md b/docs/configuration.md
index b05b1dcaa..074c9a577 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -229,7 +229,6 @@ Mandatory parameters are marked as **Required**, which means that they are requi
| | **Plugins**
| `edge.*` | Please refer to [edge configuration document](edge.md) for detailed explanation of all possible configuration options.
| `pairlists` | Define one or more pairlists to be used. [More information](plugins.md#pairlists-and-pairlist-handlers).
*Defaults to `StaticPairList`.*
**Datatype:** List of Dicts
-| `protections` | Define one or more protections to be used. [More information](plugins.md#protections).
**Datatype:** List of Dicts
| | **Telegram**
| `telegram.enabled` | Enable the usage of Telegram.
**Datatype:** Boolean
| `telegram.token` | Your Telegram bot token. Only required if `telegram.enabled` is `true`.
**Keep it in secret, do not disclose publicly.**
**Datatype:** String
diff --git a/docs/deprecated.md b/docs/deprecated.md
index 6719ce56d..5357acc62 100644
--- a/docs/deprecated.md
+++ b/docs/deprecated.md
@@ -75,7 +75,10 @@ Webhook terminology changed from "sell" to "exit", and from "buy" to "entry", re
* `webhooksellfill`, `webhookexitfill` -> `exit_fill`
* `webhooksellcancel`, `webhookexitcancel` -> `exit_cancel`
-
## Removal of `populate_any_indicators`
version 2023.3 saw the removal of `populate_any_indicators` in favor of split methods for feature engineering and targets. Please read the [migration document](strategy_migration.md#freqai-strategy) for full details.
+
+## Removal of `protections` from configuration
+
+ Setting protections from the configuration via `"protections": [],` has been removed in 2024.10, after having raised deprecation warnings for over 3 years.
diff --git a/docs/developer.md b/docs/developer.md
index 127e8e5d5..401de07c1 100644
--- a/docs/developer.md
+++ b/docs/developer.md
@@ -241,7 +241,6 @@ No protection should use datetime directly, but use the provided `date_now` vari
!!! Tip "Writing a new Protection"
Best copy one of the existing Protections to have a good example.
- Don't forget to register your protection in `constants.py` under the variable `AVAILABLE_PROTECTIONS` - otherwise it will not be selectable.
#### Implementation of a new protection
diff --git a/docs/hyperopt.md b/docs/hyperopt.md
index f88928344..43085029c 100644
--- a/docs/hyperopt.md
+++ b/docs/hyperopt.md
@@ -445,7 +445,6 @@ While this strategy is most likely too simple to provide consistent profit, it s
Whether you are using `.range` functionality or the alternatives above, you should try to use space ranges as small as possible since this will improve CPU/RAM usage.
-
## Optimizing protections
Freqtrade can also optimize protections. How you optimize protections is up to you, and the following should be considered as example only.
diff --git a/docs/includes/protections.md b/docs/includes/protections.md
index a4cb9d3cc..c32846165 100644
--- a/docs/includes/protections.md
+++ b/docs/includes/protections.md
@@ -1,24 +1,16 @@
## Protections
-!!! Warning "Beta feature"
- This feature is still in it's testing phase. Should you notice something you think is wrong please let us know via Discord or via Github Issue.
-
Protections will protect your strategy from unexpected events and market conditions by temporarily stop trading for either one pair, or for all pairs.
All protection end times are rounded up to the next candle to avoid sudden, unexpected intra-candle buys.
-!!! Note
+!!! Tip "Usage tips"
Not all Protections will work for all strategies, and parameters will need to be tuned for your strategy to improve performance.
-!!! Tip
Each Protection can be configured multiple times with different parameters, to allow different levels of protection (short-term / long-term).
!!! Note "Backtesting"
Protections are supported by backtesting and hyperopt, but must be explicitly enabled by using the `--enable-protections` flag.
-!!! Warning "Setting protections from the configuration"
- Setting protections from the configuration via `"protections": [],` key should be considered deprecated and will be removed in a future version.
- It is also no longer guaranteed that your protections apply to the strategy in cases where the strategy defines [protections as property](hyperopt.md#optimizing-protections).
-
### Available Protections
* [`StoplossGuard`](#stoploss-guard) Stop trading if a certain amount of stoploss occurred within a certain time window.
diff --git a/freqtrade/configuration/config_schema.py b/freqtrade/configuration/config_schema.py
index cd349daed..13f8b8703 100644
--- a/freqtrade/configuration/config_schema.py
+++ b/freqtrade/configuration/config_schema.py
@@ -4,7 +4,6 @@ from typing import Dict
from freqtrade.constants import (
AVAILABLE_DATAHANDLERS,
AVAILABLE_PAIRLISTS,
- AVAILABLE_PROTECTIONS,
BACKTEST_BREAKDOWNS,
DRY_RUN_WALLET,
EXPORT_OPTIONS,
@@ -449,60 +448,6 @@ CONF_SCHEMA = {
"required": ["method"],
},
},
- "protections": {
- "description": "Configuration for various protections.",
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "method": {
- "description": "Method used for the protection.",
- "type": "string",
- "enum": AVAILABLE_PROTECTIONS,
- },
- "stop_duration": {
- "description": (
- "Duration to lock the pair after a protection is triggered, "
- "in minutes."
- ),
- "type": "number",
- "minimum": 0.0,
- },
- "stop_duration_candles": {
- "description": (
- "Duration to lock the pair after a protection is triggered, in "
- "number of candles."
- ),
- "type": "number",
- "minimum": 0,
- },
- "unlock_at": {
- "description": (
- "Time when trading will be unlocked regularly. Format: HH:MM"
- ),
- "type": "string",
- },
- "trade_limit": {
- "description": "Minimum number of trades required during lookback period.",
- "type": "number",
- "minimum": 1,
- },
- "lookback_period": {
- "description": "Period to look back for protection checks, in minutes.",
- "type": "number",
- "minimum": 1,
- },
- "lookback_period_candles": {
- "description": (
- "Period to look back for protection checks, in number " "of candles."
- ),
- "type": "number",
- "minimum": 1,
- },
- },
- "required": ["method"],
- },
- },
# RPC section
"telegram": {
"description": "Telegram settings.",
diff --git a/freqtrade/configuration/config_validation.py b/freqtrade/configuration/config_validation.py
index 6a14841ff..536feb535 100644
--- a/freqtrade/configuration/config_validation.py
+++ b/freqtrade/configuration/config_validation.py
@@ -1,7 +1,6 @@
import logging
from collections import Counter
from copy import deepcopy
-from datetime import datetime
from typing import Any, Dict
from jsonschema import Draft4Validator, validators
@@ -84,7 +83,6 @@ def validate_config_consistency(conf: Dict[str, Any], *, preliminary: bool = Fal
_validate_price_config(conf)
_validate_edge(conf)
_validate_whitelist(conf)
- _validate_protections(conf)
_validate_unlimited_amount(conf)
_validate_ask_orderbook(conf)
_validate_freqai_hyperopt(conf)
@@ -196,41 +194,6 @@ def _validate_whitelist(conf: Dict[str, Any]) -> None:
raise ConfigurationError("StaticPairList requires pair_whitelist to be set.")
-def _validate_protections(conf: Dict[str, Any]) -> None:
- """
- Validate protection configuration validity
- """
-
- for prot in conf.get("protections", []):
- parsed_unlock_at = None
- if (config_unlock_at := prot.get("unlock_at")) is not None:
- try:
- parsed_unlock_at = datetime.strptime(config_unlock_at, "%H:%M")
- except ValueError:
- raise ConfigurationError(f"Invalid date format for unlock_at: {config_unlock_at}.")
-
- if "stop_duration" in prot and "stop_duration_candles" in prot:
- raise ConfigurationError(
- "Protections must specify either `stop_duration` or `stop_duration_candles`.\n"
- f"Please fix the protection {prot.get('method')}."
- )
-
- if "lookback_period" in prot and "lookback_period_candles" in prot:
- raise ConfigurationError(
- "Protections must specify either `lookback_period` or `lookback_period_candles`.\n"
- f"Please fix the protection {prot.get('method')}."
- )
-
- if parsed_unlock_at is not None and (
- "stop_duration" in prot or "stop_duration_candles" in prot
- ):
- raise ConfigurationError(
- "Protections must specify either `unlock_at`, `stop_duration` or "
- "`stop_duration_candles`.\n"
- f"Please fix the protection {prot.get('method')}."
- )
-
-
def _validate_ask_orderbook(conf: Dict[str, Any]) -> None:
ask_strategy = conf.get("exit_pricing", {})
ob_min = ask_strategy.get("order_book_min")
diff --git a/freqtrade/configuration/deprecated_settings.py b/freqtrade/configuration/deprecated_settings.py
index 6a0901ed7..c4d78e588 100644
--- a/freqtrade/configuration/deprecated_settings.py
+++ b/freqtrade/configuration/deprecated_settings.py
@@ -177,4 +177,6 @@ def process_temporary_deprecated_settings(config: Config) -> None:
)
if "protections" in config:
- logger.warning("DEPRECATED: Setting 'protections' in the configuration is deprecated.")
+ raise ConfigurationError(
+ "DEPRECATED: Setting 'protections' in the configuration is deprecated."
+ )
diff --git a/freqtrade/constants.py b/freqtrade/constants.py
index 120f463f3..f1c44d204 100644
--- a/freqtrade/constants.py
+++ b/freqtrade/constants.py
@@ -57,7 +57,6 @@ AVAILABLE_PAIRLISTS = [
"SpreadFilter",
"VolatilityFilter",
]
-AVAILABLE_PROTECTIONS = ["CooldownPeriod", "LowProfitPairs", "MaxDrawdown", "StoplossGuard"]
AVAILABLE_DATAHANDLERS = ["json", "jsongz", "hdf5", "feather", "parquet"]
BACKTEST_BREAKDOWNS = ["day", "week", "month"]
BACKTEST_CACHE_AGE = ["none", "day", "week", "month"]
diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py
index 20116f670..75c0ac075 100644
--- a/freqtrade/optimize/backtesting.py
+++ b/freqtrade/optimize/backtesting.py
@@ -273,10 +273,6 @@ class Backtesting:
def _load_protections(self, strategy: IStrategy):
if self.config.get("enable_protections", False):
- conf = self.config
- if hasattr(strategy, "protections"):
- conf = deepcopy(conf)
- conf["protections"] = strategy.protections
self.protections = ProtectionManager(self.config, strategy.protections)
def load_bt_data(self) -> Tuple[Dict[str, DataFrame], TimeRange]:
diff --git a/freqtrade/plugins/protectionmanager.py b/freqtrade/plugins/protectionmanager.py
index 4f60ae0e0..1b07261d4 100644
--- a/freqtrade/plugins/protectionmanager.py
+++ b/freqtrade/plugins/protectionmanager.py
@@ -4,9 +4,10 @@ Protection manager class
import logging
from datetime import datetime, timezone
-from typing import Dict, List, Optional
+from typing import Any, Dict, List, Optional
from freqtrade.constants import Config, LongShort
+from freqtrade.exceptions import ConfigurationError
from freqtrade.persistence import PairLocks
from freqtrade.persistence.models import PairLock
from freqtrade.plugins.protections import IProtection
@@ -21,6 +22,7 @@ class ProtectionManager:
self._config = config
self._protection_handlers: List[IProtection] = []
+ self.validate_protections(protections)
for protection_handler_config in protections:
protection_handler = ProtectionResolver.load_protection(
protection_handler_config["method"],
@@ -76,3 +78,40 @@ class ProtectionManager:
pair, lock.until, lock.reason, now=now, side=lock.lock_side
)
return result
+
+ @staticmethod
+ def validate_protections(protections: List[Dict[str, Any]]) -> None:
+ """
+ Validate protection setup validity
+ """
+
+ for prot in protections:
+ parsed_unlock_at = None
+ if (config_unlock_at := prot.get("unlock_at")) is not None:
+ try:
+ parsed_unlock_at = datetime.strptime(config_unlock_at, "%H:%M")
+ except ValueError:
+ raise ConfigurationError(
+ f"Invalid date format for unlock_at: {config_unlock_at}."
+ )
+
+ if "stop_duration" in prot and "stop_duration_candles" in prot:
+ raise ConfigurationError(
+ "Protections must specify either `stop_duration` or `stop_duration_candles`.\n"
+ f"Please fix the protection {prot.get('method')}."
+ )
+
+ if "lookback_period" in prot and "lookback_period_candles" in prot:
+ raise ConfigurationError(
+ "Protections must specify either `lookback_period` or "
+ f"`lookback_period_candles`.\n Please fix the protection {prot.get('method')}."
+ )
+
+ if parsed_unlock_at is not None and (
+ "stop_duration" in prot or "stop_duration_candles" in prot
+ ):
+ raise ConfigurationError(
+ "Protections must specify either `unlock_at`, `stop_duration` or "
+ "`stop_duration_candles`.\n"
+ f"Please fix the protection {prot.get('method')}."
+ )
diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py
index d234a680f..6cd0cef23 100644
--- a/freqtrade/resolvers/strategy_resolver.py
+++ b/freqtrade/resolvers/strategy_resolver.py
@@ -69,7 +69,6 @@ class StrategyResolver(IResolver):
("order_time_in_force", None),
("stake_currency", None),
("stake_amount", None),
- ("protections", None),
("startup_candle_count", None),
("unfilledtimeout", None),
("use_exit_signal", True),
diff --git a/freqtrade/rpc/api_server/api_backtest.py b/freqtrade/rpc/api_server/api_backtest.py
index e4b598807..4f6484538 100644
--- a/freqtrade/rpc/api_server/api_backtest.py
+++ b/freqtrade/rpc/api_server/api_backtest.py
@@ -77,7 +77,6 @@ def __run_backtest_bg(btconfig: Config):
lastconfig["timerange"] = btconfig["timerange"]
lastconfig["timeframe"] = strat.timeframe
- lastconfig["protections"] = btconfig.get("protections", [])
lastconfig["enable_protections"] = btconfig.get("enable_protections")
lastconfig["dry_run_wallet"] = btconfig.get("dry_run_wallet")
diff --git a/tests/freqtradebot/test_freqtradebot.py b/tests/freqtradebot/test_freqtradebot.py
index 8587e7f9d..08fb3db00 100644
--- a/tests/freqtradebot/test_freqtradebot.py
+++ b/tests/freqtradebot/test_freqtradebot.py
@@ -553,7 +553,7 @@ def test_enter_positions_global_pairlock(
@pytest.mark.parametrize("is_short", [False, True])
def test_handle_protections(mocker, default_conf_usdt, fee, is_short):
- default_conf_usdt["protections"] = [
+ default_conf_usdt["_strategy_protections"] = [
{"method": "CooldownPeriod", "stop_duration": 60},
{
"method": "StoplossGuard",
diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py
index b25230791..f064247c6 100644
--- a/tests/optimize/test_backtesting.py
+++ b/tests/optimize/test_backtesting.py
@@ -1299,7 +1299,7 @@ def test_backtest_pricecontours_protections(default_conf, fee, mocker, testdatad
# While this test IS a copy of test_backtest_pricecontours, it's needed to ensure
# results do not carry-over to the next run, which is not given by using parametrize.
patch_exchange(mocker)
- default_conf["protections"] = [
+ default_conf["_strategy_protections"] = [
{
"method": "CooldownPeriod",
"stop_duration": 3,
@@ -1358,7 +1358,7 @@ def test_backtest_pricecontours(
default_conf, mocker, testdatadir, protections, contour, expected
) -> None:
if protections:
- default_conf["protections"] = protections
+ default_conf["_strategy_protections"] = protections
default_conf["enable_protections"] = True
patch_exchange(mocker)
diff --git a/tests/plugins/test_protections.py b/tests/plugins/test_protections.py
index 3fb27ce3d..bec2671eb 100644
--- a/tests/plugins/test_protections.py
+++ b/tests/plugins/test_protections.py
@@ -3,14 +3,17 @@ from datetime import datetime, timedelta, timezone
import pytest
-from freqtrade import constants
from freqtrade.enums import ExitType
+from freqtrade.exceptions import OperationalException
from freqtrade.persistence import PairLocks, Trade
from freqtrade.persistence.trade_model import Order
from freqtrade.plugins.protectionmanager import ProtectionManager
from tests.conftest import get_patched_freqtradebot, log_has_re
+AVAILABLE_PROTECTIONS = ["CooldownPeriod", "LowProfitPairs", "MaxDrawdown", "StoplossGuard"]
+
+
def generate_mock_trade(
pair: str,
fee: float,
@@ -88,19 +91,76 @@ def generate_mock_trade(
def test_protectionmanager(mocker, default_conf):
- default_conf["protections"] = [
- {"method": protection} for protection in constants.AVAILABLE_PROTECTIONS
+ default_conf["_strategy_protections"] = [
+ {"method": protection} for protection in AVAILABLE_PROTECTIONS
]
freqtrade = get_patched_freqtradebot(mocker, default_conf)
for handler in freqtrade.protections._protection_handlers:
- assert handler.name in constants.AVAILABLE_PROTECTIONS
+ assert handler.name in AVAILABLE_PROTECTIONS
if not handler.has_global_stop:
assert handler.global_stop(datetime.now(timezone.utc), "*") is None
if not handler.has_local_stop:
assert handler.stop_per_pair("XRP/BTC", datetime.now(timezone.utc), "*") is None
+@pytest.mark.parametrize(
+ "protconf,expected",
+ [
+ ([], None),
+ ([{"method": "StoplossGuard", "lookback_period": 2000, "stop_duration_candles": 10}], None),
+ ([{"method": "StoplossGuard", "lookback_period_candles": 20, "stop_duration": 10}], None),
+ (
+ [
+ {
+ "method": "StoplossGuard",
+ "lookback_period_candles": 20,
+ "lookback_period": 2000,
+ "stop_duration": 10,
+ }
+ ],
+ r"Protections must specify either `lookback_period`.*",
+ ),
+ (
+ [
+ {
+ "method": "StoplossGuard",
+ "lookback_period": 20,
+ "stop_duration": 10,
+ "stop_duration_candles": 10,
+ }
+ ],
+ r"Protections must specify either `stop_duration`.*",
+ ),
+ (
+ [
+ {
+ "method": "StoplossGuard",
+ "lookback_period": 20,
+ "stop_duration": 10,
+ "unlock_at": "20:02",
+ }
+ ],
+ r"Protections must specify either `unlock_at`, `stop_duration` or.*",
+ ),
+ (
+ [{"method": "StoplossGuard", "lookback_period_candles": 20, "unlock_at": "20:02"}],
+ None,
+ ),
+ (
+ [{"method": "StoplossGuard", "lookback_period_candles": 20, "unlock_at": "55:102"}],
+ "Invalid date format for unlock_at: 55:102.",
+ ),
+ ],
+)
+def test_validate_protections(protconf, expected):
+ if expected:
+ with pytest.raises(OperationalException, match=expected):
+ ProtectionManager.validate_protections(protconf)
+ else:
+ ProtectionManager.validate_protections(protconf)
+
+
@pytest.mark.parametrize(
"timeframe,expected_lookback,expected_stop,protconf",
[
@@ -196,7 +256,7 @@ def test_protections_init(default_conf, timeframe, expected_lookback, expected_s
@pytest.mark.usefixtures("init_persistence")
def test_stoploss_guard(mocker, default_conf, fee, caplog, is_short):
# Active for both sides (long and short)
- default_conf["protections"] = [
+ default_conf["_strategy_protections"] = [
{"method": "StoplossGuard", "lookback_period": 60, "stop_duration": 40, "trade_limit": 3}
]
freqtrade = get_patched_freqtradebot(mocker, default_conf)
@@ -268,7 +328,7 @@ def test_stoploss_guard(mocker, default_conf, fee, caplog, is_short):
@pytest.mark.parametrize("only_per_side", [False, True])
@pytest.mark.usefixtures("init_persistence")
def test_stoploss_guard_perpair(mocker, default_conf, fee, caplog, only_per_pair, only_per_side):
- default_conf["protections"] = [
+ default_conf["_strategy_protections"] = [
{
"method": "StoplossGuard",
"lookback_period": 60,
@@ -379,7 +439,7 @@ def test_stoploss_guard_perpair(mocker, default_conf, fee, caplog, only_per_pair
@pytest.mark.usefixtures("init_persistence")
def test_CooldownPeriod(mocker, default_conf, fee, caplog):
- default_conf["protections"] = [
+ default_conf["_strategy_protections"] = [
{
"method": "CooldownPeriod",
"stop_duration": 60,
@@ -425,7 +485,7 @@ def test_CooldownPeriod(mocker, default_conf, fee, caplog):
@pytest.mark.usefixtures("init_persistence")
def test_CooldownPeriod_unlock_at(mocker, default_conf, fee, caplog, time_machine):
- default_conf["protections"] = [
+ default_conf["_strategy_protections"] = [
{
"method": "CooldownPeriod",
"unlock_at": "05:00",
@@ -509,7 +569,7 @@ def test_CooldownPeriod_unlock_at(mocker, default_conf, fee, caplog, time_machin
@pytest.mark.parametrize("only_per_side", [False, True])
@pytest.mark.usefixtures("init_persistence")
def test_LowProfitPairs(mocker, default_conf, fee, caplog, only_per_side):
- default_conf["protections"] = [
+ default_conf["_strategy_protections"] = [
{
"method": "LowProfitPairs",
"lookback_period": 400,
@@ -599,7 +659,7 @@ def test_LowProfitPairs(mocker, default_conf, fee, caplog, only_per_side):
@pytest.mark.usefixtures("init_persistence")
def test_MaxDrawdown(mocker, default_conf, fee, caplog):
- default_conf["protections"] = [
+ default_conf["_strategy_protections"] = [
{
"method": "MaxDrawdown",
"lookback_period": 1000,
@@ -812,7 +872,7 @@ def test_MaxDrawdown(mocker, default_conf, fee, caplog):
def test_protection_manager_desc(
mocker, default_conf, protectionconf, desc_expected, exception_expected
):
- default_conf["protections"] = [protectionconf]
+ default_conf["_strategy_protections"] = [protectionconf]
freqtrade = get_patched_freqtradebot(mocker, default_conf)
short_desc = str(freqtrade.protections.short_desc())
diff --git a/tests/rpc/test_rpc_manager.py b/tests/rpc/test_rpc_manager.py
index 2792fd082..67755255f 100644
--- a/tests/rpc/test_rpc_manager.py
+++ b/tests/rpc/test_rpc_manager.py
@@ -173,7 +173,7 @@ def test_startupmessages_telegram_enabled(mocker, default_conf) -> None:
telegram_mock.reset_mock()
default_conf["dry_run"] = True
default_conf["whitelist"] = {"method": "VolumePairList", "config": {"number_assets": 20}}
- default_conf["protections"] = [
+ default_conf["_strategy_protections"] = [
{"method": "StoplossGuard", "lookback_period": 60, "trade_limit": 2, "stop_duration": 60}
]
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
diff --git a/tests/strategy/strats/strategy_test_v3.py b/tests/strategy/strats/strategy_test_v3.py
index 71404242a..007c7655e 100644
--- a/tests/strategy/strats/strategy_test_v3.py
+++ b/tests/strategy/strats/strategy_test_v3.py
@@ -75,15 +75,13 @@ class StrategyTestV3(IStrategy):
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
+ @property
+ def protections(self):
+ prot = []
+ if self.protection_enabled.value:
+ # Workaround to simplify tests. This will not work in real scenarios.
+ prot = self.config.get("_strategy_protections", {})
+ return prot
bot_started = False
diff --git a/tests/test_configuration.py b/tests/test_configuration.py
index d77fae6a8..829bf699a 100644
--- a/tests/test_configuration.py
+++ b/tests/test_configuration.py
@@ -812,65 +812,6 @@ def test_validate_whitelist(default_conf):
validate_config_consistency(conf)
-@pytest.mark.parametrize(
- "protconf,expected",
- [
- ([], None),
- ([{"method": "StoplossGuard", "lookback_period": 2000, "stop_duration_candles": 10}], None),
- ([{"method": "StoplossGuard", "lookback_period_candles": 20, "stop_duration": 10}], None),
- (
- [
- {
- "method": "StoplossGuard",
- "lookback_period_candles": 20,
- "lookback_period": 2000,
- "stop_duration": 10,
- }
- ],
- r"Protections must specify either `lookback_period`.*",
- ),
- (
- [
- {
- "method": "StoplossGuard",
- "lookback_period": 20,
- "stop_duration": 10,
- "stop_duration_candles": 10,
- }
- ],
- r"Protections must specify either `stop_duration`.*",
- ),
- (
- [
- {
- "method": "StoplossGuard",
- "lookback_period": 20,
- "stop_duration": 10,
- "unlock_at": "20:02",
- }
- ],
- r"Protections must specify either `unlock_at`, `stop_duration` or.*",
- ),
- (
- [{"method": "StoplossGuard", "lookback_period_candles": 20, "unlock_at": "20:02"}],
- None,
- ),
- (
- [{"method": "StoplossGuard", "lookback_period_candles": 20, "unlock_at": "55:102"}],
- "Invalid date format for unlock_at: 55:102.",
- ),
- ],
-)
-def test_validate_protections(default_conf, protconf, expected):
- conf = deepcopy(default_conf)
- conf["protections"] = protconf
- if expected:
- with pytest.raises(OperationalException, match=expected):
- validate_config_consistency(conf)
- else:
- validate_config_consistency(conf)
-
-
def test_validate_ask_orderbook(default_conf, caplog) -> None:
conf = deepcopy(default_conf)
conf["exit_pricing"]["use_order_book"] = True
@@ -1533,8 +1474,8 @@ def test_process_deprecated_protections(default_conf, caplog):
assert not log_has(message, caplog)
config["protections"] = []
- process_temporary_deprecated_settings(config)
- assert log_has(message, caplog)
+ with pytest.raises(ConfigurationError, match=message):
+ process_temporary_deprecated_settings(config)
def test_flat_vars_to_nested_dict(caplog):