Validate configuration consistency after loading strategy

This commit is contained in:
Matthias 2019-08-18 16:10:10 +02:00
parent 9005447590
commit ddfadbb69e
5 changed files with 47 additions and 43 deletions

View File

@ -1,3 +1,4 @@
from freqtrade.configuration.arguments import Arguments # noqa: F401 from freqtrade.configuration.arguments import Arguments # noqa: F401
from freqtrade.configuration.timerange import TimeRange # noqa: F401 from freqtrade.configuration.timerange import TimeRange # noqa: F401
from freqtrade.configuration.configuration import Configuration # noqa: F401 from freqtrade.configuration.configuration import Configuration # noqa: F401
from freqtrade.configuration.config_validation import validate_config_consistency # noqa: F401

View File

@ -4,7 +4,7 @@ from typing import Any, Dict
from jsonschema import Draft4Validator, validators from jsonschema import Draft4Validator, validators
from jsonschema.exceptions import ValidationError, best_match from jsonschema.exceptions import ValidationError, best_match
from freqtrade import constants from freqtrade import constants, OperationalException
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -51,3 +51,36 @@ def validate_config_schema(conf: Dict[str, Any]) -> Dict[str, Any]:
raise ValidationError( raise ValidationError(
best_match(Draft4Validator(constants.CONF_SCHEMA).iter_errors(conf)).message best_match(Draft4Validator(constants.CONF_SCHEMA).iter_errors(conf)).message
) )
def validate_config_consistency(conf: Dict[str, Any]) -> None:
"""
Validate the configuration consistency.
Should be ran after loading both configuration and strategy,
since strategies can set certain configuration settings too.
:param conf: Config in JSON format
:return: Returns None if everything is ok, otherwise throw an OperationalException
"""
# validating trailing stoploss
_validate_trailing_stoploss(conf)
def _validate_trailing_stoploss(conf: Dict[str, Any]) -> None:
# Skip if trailing stoploss is not activated
if not conf.get('trailing_stop', False):
return
tsl_positive = float(conf.get('trailing_stop_positive', 0))
tsl_offset = float(conf.get('trailing_stop_positive_offset', 0))
tsl_only_offset = conf.get('trailing_only_offset_is_reached', False)
if tsl_only_offset:
if tsl_positive == 0.0:
raise OperationalException(
f'The config trailing_only_offset_is_reached needs '
'trailing_stop_positive_offset to be more than 0 in your config.')
if tsl_positive > 0 and 0 < tsl_offset <= tsl_positive:
raise OperationalException(
f'The config trailing_stop_positive_offset needs '
'to be greater than trailing_stop_positive_offset in your config.')

View File

@ -6,10 +6,11 @@ import warnings
from argparse import Namespace from argparse import Namespace
from typing import Any, Callable, Dict, List, Optional from typing import Any, Callable, Dict, List, Optional
from freqtrade import OperationalException, constants from freqtrade import constants
from freqtrade.configuration.check_exchange import check_exchange from freqtrade.configuration.check_exchange import check_exchange
from freqtrade.configuration.create_datadir import create_datadir from freqtrade.configuration.create_datadir import create_datadir
from freqtrade.configuration.json_schema import validate_config_schema from freqtrade.configuration.config_validation import (validate_config_schema,
validate_config_consistency)
from freqtrade.configuration.load_config import load_config_file from freqtrade.configuration.load_config import load_config_file
from freqtrade.loggers import setup_logging from freqtrade.loggers import setup_logging
from freqtrade.misc import deep_merge_dicts from freqtrade.misc import deep_merge_dicts
@ -77,8 +78,6 @@ class Configuration(object):
# Load all configs # Load all configs
config: Dict[str, Any] = Configuration.from_files(self.args.config) config: Dict[str, Any] = Configuration.from_files(self.args.config)
self._validate_config_consistency(config)
self._process_common_options(config) self._process_common_options(config)
self._process_optimize_options(config) self._process_optimize_options(config)
@ -87,6 +86,8 @@ class Configuration(object):
self._process_runmode(config) self._process_runmode(config)
validate_config_consistency(config)
return config return config
def _process_logging_options(self, config: Dict[str, Any]) -> None: def _process_logging_options(self, config: Dict[str, Any]) -> None:
@ -285,35 +286,6 @@ class Configuration(object):
config.update({'runmode': self.runmode}) config.update({'runmode': self.runmode})
def _validate_config_consistency(self, conf: Dict[str, Any]) -> None:
"""
Validate the configuration consistency
:param conf: Config in JSON format
:return: Returns None if everything is ok, otherwise throw an OperationalException
"""
# validating trailing stoploss
self._validate_trailing_stoploss(conf)
def _validate_trailing_stoploss(self, conf: Dict[str, Any]) -> None:
# Skip if trailing stoploss is not activated
if not conf.get('trailing_stop', False):
return
tsl_positive = float(conf.get('trailing_stop_positive', 0))
tsl_offset = float(conf.get('trailing_stop_positive_offset', 0))
tsl_only_offset = conf.get('trailing_only_offset_is_reached', False)
if tsl_only_offset:
if tsl_positive == 0.0:
raise OperationalException(
f'The config trailing_only_offset_is_reached needs '
'trailing_stop_positive_offset to be more than 0 in your config.')
if tsl_positive > 0 and 0 < tsl_offset <= tsl_positive:
raise OperationalException(
f'The config trailing_stop_positive_offset needs '
'to be greater than trailing_stop_positive_offset in your config.')
def _args_to_config(self, config: Dict[str, Any], argname: str, def _args_to_config(self, config: Dict[str, Any], argname: str,
logstring: str, logfun: Optional[Callable] = None, logstring: str, logfun: Optional[Callable] = None,
deprecated_msg: Optional[str] = None) -> None: deprecated_msg: Optional[str] = None) -> None:

View File

@ -16,6 +16,7 @@ from freqtrade import (DependencyException, OperationalException, InvalidOrderEx
from freqtrade.data.converter import order_book_to_dataframe from freqtrade.data.converter import order_book_to_dataframe
from freqtrade.data.dataprovider import DataProvider from freqtrade.data.dataprovider import DataProvider
from freqtrade.edge import Edge from freqtrade.edge import Edge
from freqtrade.configuration import validate_config_consistency
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_next_date from freqtrade.exchange import timeframe_to_minutes, timeframe_to_next_date
from freqtrade.persistence import Trade from freqtrade.persistence import Trade
from freqtrade.rpc import RPCManager, RPCMessageType from freqtrade.rpc import RPCManager, RPCMessageType
@ -50,6 +51,7 @@ class FreqtradeBot(object):
self.config = config self.config = config
self.strategy: IStrategy = StrategyResolver(self.config).strategy self.strategy: IStrategy = StrategyResolver(self.config).strategy
validate_config_consistency(config)
self.rpc: RPCManager = RPCManager(self) self.rpc: RPCManager = RPCManager(self)

View File

@ -2,7 +2,6 @@
import json import json
import logging import logging
import warnings import warnings
from argparse import Namespace
from copy import deepcopy from copy import deepcopy
from pathlib import Path from pathlib import Path
from unittest.mock import MagicMock from unittest.mock import MagicMock
@ -11,10 +10,10 @@ import pytest
from jsonschema import Draft4Validator, ValidationError, validate from jsonschema import Draft4Validator, ValidationError, validate
from freqtrade import OperationalException, constants from freqtrade import OperationalException, constants
from freqtrade.configuration import Arguments, Configuration from freqtrade.configuration import Arguments, Configuration, validate_config_consistency
from freqtrade.configuration.check_exchange import check_exchange from freqtrade.configuration.check_exchange import check_exchange
from freqtrade.configuration.config_validation import validate_config_schema
from freqtrade.configuration.create_datadir import create_datadir from freqtrade.configuration.create_datadir import create_datadir
from freqtrade.configuration.json_schema import validate_config_schema
from freqtrade.configuration.load_config import load_config_file from freqtrade.configuration.load_config import load_config_file
from freqtrade.constants import DEFAULT_DB_DRYRUN_URL, DEFAULT_DB_PROD_URL from freqtrade.constants import DEFAULT_DB_DRYRUN_URL, DEFAULT_DB_PROD_URL
from freqtrade.loggers import _set_loggers from freqtrade.loggers import _set_loggers
@ -625,21 +624,18 @@ def test_validate_tsl(default_conf):
with pytest.raises(OperationalException, with pytest.raises(OperationalException,
match=r'The config trailing_only_offset_is_reached needs ' match=r'The config trailing_only_offset_is_reached needs '
'trailing_stop_positive_offset to be more than 0 in your config.'): 'trailing_stop_positive_offset to be more than 0 in your config.'):
configuration = Configuration(Namespace()) validate_config_consistency(default_conf)
configuration._validate_config_consistency(default_conf)
default_conf['trailing_stop_positive_offset'] = 0.01 default_conf['trailing_stop_positive_offset'] = 0.01
default_conf['trailing_stop_positive'] = 0.015 default_conf['trailing_stop_positive'] = 0.015
with pytest.raises(OperationalException, with pytest.raises(OperationalException,
match=r'The config trailing_stop_positive_offset needs ' match=r'The config trailing_stop_positive_offset needs '
'to be greater than trailing_stop_positive_offset in your config.'): 'to be greater than trailing_stop_positive_offset in your config.'):
configuration = Configuration(Namespace()) validate_config_consistency(default_conf)
configuration._validate_config_consistency(default_conf)
default_conf['trailing_stop_positive'] = 0.01 default_conf['trailing_stop_positive'] = 0.01
default_conf['trailing_stop_positive_offset'] = 0.015 default_conf['trailing_stop_positive_offset'] = 0.015
Configuration(Namespace()) validate_config_consistency(default_conf)
configuration._validate_config_consistency(default_conf)
def test_load_config_test_comments() -> None: def test_load_config_test_comments() -> None: