Document *candles settings, implement validations
This commit is contained in:
parent
eb952d77be
commit
a93bb6853b
@ -21,10 +21,14 @@ All protection end times are rounded up to the next candle to avoid sudden, unex
|
||||
|
||||
### Common settings to all Protections
|
||||
|
||||
* `method` - Protection name to use.
|
||||
* `stop_duration` (minutes) - how long should protections be locked.
|
||||
* `lookback_period` (minutes) - Only trades that completed after `current_time - lookback_period` will be considered (may be ignored by some Protections).
|
||||
* `trade_limit` - How many trades are required at minimum (not used by all Protections).
|
||||
| Parameter| Description |
|
||||
|------------|-------------|
|
||||
| method | Protection name to use. <br> **Datatype:** String, selected from [available Protections](#available-protections)
|
||||
| stop_duration_candles | For how many candles should the lock be set? <br> **Datatype:** Positive integer (in candles)
|
||||
| stop_duration | how many minutes should protections be locked. <br>Cannot be used together with `stop_duration_candles`. <br> **Datatype:** Float (in minutes)
|
||||
| `lookback_period_candles` | Only trades that completed within the last `lookback_period_candles` candles will be considered. This setting may be ignored by some Protections. <br> **Datatype:** Positive integer (in candles).
|
||||
| lookback_period | Only trades that completed after `current_time - lookback_period` will be considered. <br>Cannot be used together with `lookback_period_candles`. <br>This setting may be ignored by some Protections. <br> **Datatype:** Float (in minutes)
|
||||
| trade_limit | Number of trades required at minimum (not used by all Protections). <br> **Datatype:** Positive integer
|
||||
|
||||
#### Stoploss Guard
|
||||
|
||||
|
@ -74,6 +74,7 @@ def validate_config_consistency(conf: Dict[str, Any]) -> None:
|
||||
_validate_trailing_stoploss(conf)
|
||||
_validate_edge(conf)
|
||||
_validate_whitelist(conf)
|
||||
_validate_protections(conf)
|
||||
_validate_unlimited_amount(conf)
|
||||
|
||||
# validate configuration before returning
|
||||
@ -155,3 +156,22 @@ def _validate_whitelist(conf: Dict[str, Any]) -> None:
|
||||
if (pl.get('method') == 'StaticPairList'
|
||||
and not conf.get('exchange', {}).get('pair_whitelist')):
|
||||
raise OperationalException("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', []):
|
||||
if ('stop_duration' in prot and 'stop_duration_candles' in prot):
|
||||
raise OperationalException(
|
||||
"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_candle' in prot):
|
||||
raise OperationalException(
|
||||
"Protections must specify either `lookback_period` or `lookback_period_candles`.\n"
|
||||
f"Please fix the protection {prot.get('method')}"
|
||||
)
|
||||
|
@ -204,8 +204,10 @@ CONF_SCHEMA = {
|
||||
'properties': {
|
||||
'method': {'type': 'string', 'enum': AVAILABLE_PROTECTIONS},
|
||||
'stop_duration': {'type': 'number', 'minimum': 0.0},
|
||||
'stop_duration_candles': {'type': 'number', 'minimum': 0},
|
||||
'trade_limit': {'type': 'number', 'minimum': 1},
|
||||
'lookback_period': {'type': 'number', 'minimum': 1},
|
||||
'lookback_period_candles': {'type': 'number', 'minimum': 1},
|
||||
},
|
||||
'required': ['method'],
|
||||
}
|
||||
|
@ -103,6 +103,7 @@ def test_stoploss_guard_perpair(mocker, default_conf, fee, caplog, only_per_pair
|
||||
"method": "StoplossGuard",
|
||||
"lookback_period": 60,
|
||||
"trade_limit": 1,
|
||||
"stop_duration": 60,
|
||||
"only_per_pair": only_per_pair
|
||||
}]
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||
@ -158,7 +159,7 @@ def test_stoploss_guard_perpair(mocker, default_conf, fee, caplog, only_per_pair
|
||||
def test_CooldownPeriod(mocker, default_conf, fee, caplog):
|
||||
default_conf['protections'] = [{
|
||||
"method": "CooldownPeriod",
|
||||
"stopduration": 60,
|
||||
"stop_duration": 60,
|
||||
}]
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||
message = r"Trading stopped due to .*"
|
||||
@ -195,7 +196,7 @@ def test_LowProfitPairs(mocker, default_conf, fee, caplog):
|
||||
default_conf['protections'] = [{
|
||||
"method": "LowProfitPairs",
|
||||
"lookback_period": 400,
|
||||
"stopduration": 60,
|
||||
"stop_duration": 60,
|
||||
"trade_limit": 2,
|
||||
"required_profit": 0.0,
|
||||
}]
|
||||
@ -254,7 +255,7 @@ def test_MaxDrawdown(mocker, default_conf, fee, caplog):
|
||||
default_conf['protections'] = [{
|
||||
"method": "MaxDrawdown",
|
||||
"lookback_period": 1000,
|
||||
"stopduration": 60,
|
||||
"stop_duration": 60,
|
||||
"trade_limit": 3,
|
||||
"max_allowed_drawdown": 0.15
|
||||
}]
|
||||
@ -315,21 +316,21 @@ def test_MaxDrawdown(mocker, default_conf, fee, caplog):
|
||||
|
||||
|
||||
@pytest.mark.parametrize("protectionconf,desc_expected,exception_expected", [
|
||||
({"method": "StoplossGuard", "lookback_period": 60, "trade_limit": 2},
|
||||
({"method": "StoplossGuard", "lookback_period": 60, "trade_limit": 2, "stop_duration": 60},
|
||||
"[{'StoplossGuard': 'StoplossGuard - Frequent Stoploss Guard, "
|
||||
"2 stoplosses within 60 minutes.'}]",
|
||||
None
|
||||
),
|
||||
({"method": "CooldownPeriod", "stopduration": 60},
|
||||
({"method": "CooldownPeriod", "stop_duration": 60},
|
||||
"[{'CooldownPeriod': 'CooldownPeriod - Cooldown period of 60 min.'}]",
|
||||
None
|
||||
),
|
||||
({"method": "LowProfitPairs", "stopduration": 60},
|
||||
({"method": "LowProfitPairs", "lookback_period": 60, "stop_duration": 60},
|
||||
"[{'LowProfitPairs': 'LowProfitPairs - Low Profit Protection, locks pairs with "
|
||||
"profit < 0.0 within 60 minutes.'}]",
|
||||
None
|
||||
),
|
||||
({"method": "MaxDrawdown", "stopduration": 60},
|
||||
({"method": "MaxDrawdown", "lookback_period": 60, "stop_duration": 60},
|
||||
"[{'MaxDrawdown': 'MaxDrawdown - Max drawdown protection, stop trading if drawdown is > 0.0 "
|
||||
"within 60 minutes.'}]",
|
||||
None
|
||||
|
@ -879,6 +879,24 @@ 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_candle": 20, "stop_duration": 10}], None),
|
||||
([{"method": "StoplossGuard", "lookback_period_candle": 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`.*'),
|
||||
])
|
||||
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_load_config_test_comments() -> None:
|
||||
"""
|
||||
|
Loading…
Reference in New Issue
Block a user