Document *candles settings, implement validations

This commit is contained in:
Matthias 2020-12-07 10:21:03 +01:00
parent eb952d77be
commit a93bb6853b
5 changed files with 56 additions and 11 deletions

View File

@ -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

View File

@ -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')}"
)

View File

@ -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'],
}

View File

@ -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

View File

@ -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:
"""