make configuration a sep. module, including arguments

This commit is contained in:
hroff-1902 2019-07-11 21:23:23 +03:00
parent 7763b4cf5b
commit 1bdffcc73b
24 changed files with 164 additions and 143 deletions

View File

@ -0,0 +1,8 @@
from freqtrade.configuration.arguments import Arguments, TimeRange # noqa: F401
from freqtrade.configuration.arguments import ( # noqa: F401
ARGS_DOWNLOADER,
ARGS_PLOT_DATAFRAME,
ARGS_PLOT_PROFIT)
from freqtrade.configuration.configuration import Configuration # noqa: F401

View File

@ -5,6 +5,7 @@ import argparse
import os
import re
from typing import List, NamedTuple, Optional
import arrow
from freqtrade import __version__, constants

View File

@ -0,0 +1,48 @@
import logging
from typing import Any, Dict
from freqtrade import OperationalException
from freqtrade.exchange import (is_exchange_bad, is_exchange_available,
is_exchange_officially_supported, available_exchanges)
logger = logging.getLogger(__name__)
def check_exchange(config: Dict[str, Any], check_for_bad: bool = True) -> bool:
"""
Check if the exchange name in the config file is supported by Freqtrade
:param check_for_bad: if True, check the exchange against the list of known 'bad'
exchanges
:return: False if exchange is 'bad', i.e. is known to work with the bot with
critical issues or does not work at all, crashes, etc. True otherwise.
raises an exception if the exchange if not supported by ccxt
and thus is not known for the Freqtrade at all.
"""
logger.info("Checking exchange...")
exchange = config.get('exchange', {}).get('name').lower()
if not is_exchange_available(exchange):
raise OperationalException(
f'Exchange "{exchange}" is not supported by ccxt '
f'and therefore not available for the bot.\n'
f'The following exchanges are supported by ccxt: '
f'{", ".join(available_exchanges())}'
)
if check_for_bad and is_exchange_bad(exchange):
logger.warning(f'Exchange "{exchange}" is known to not work with the bot yet. '
f'Use it only for development and testing purposes.')
return False
if is_exchange_officially_supported(exchange):
logger.info(f'Exchange "{exchange}" is officially supported '
f'by the Freqtrade development team.')
else:
logger.warning(f'Exchange "{exchange}" is supported by ccxt '
f'and therefore available for the bot but not officially supported '
f'by the Freqtrade development team. '
f'It may work flawlessly (please report back) or have serious issues. '
f'Use it at your own discretion.')
return True

View File

@ -8,12 +8,9 @@ import sys
from argparse import Namespace
from typing import Any, Callable, Dict, Optional
from jsonschema import Draft4Validator, validators
from jsonschema.exceptions import ValidationError, best_match
from freqtrade import OperationalException, constants
from freqtrade.exchange import (is_exchange_bad, is_exchange_available,
is_exchange_officially_supported, available_exchanges)
from freqtrade.configuration.check_exchange import check_exchange
from freqtrade.configuration.json_schema import validate_config_schema
from freqtrade.loggers import setup_logging
from freqtrade.misc import deep_merge_dicts
from freqtrade.state import RunMode
@ -22,31 +19,6 @@ from freqtrade.state import RunMode
logger = logging.getLogger(__name__)
def _extend_validator(validator_class):
"""
Extended validator for the Freqtrade configuration JSON Schema.
Currently it only handles defaults for subschemas.
"""
validate_properties = validator_class.VALIDATORS['properties']
def set_defaults(validator, properties, instance, schema):
for prop, subschema in properties.items():
if 'default' in subschema:
instance.setdefault(prop, subschema['default'])
for error in validate_properties(
validator, properties, instance, schema,
):
yield error
return validators.extend(
validator_class, {'properties': set_defaults}
)
FreqtradeValidator = _extend_validator(Draft4Validator)
class Configuration(object):
"""
Class to read and init the bot configuration
@ -58,6 +30,16 @@ class Configuration(object):
self.config: Optional[Dict[str, Any]] = None
self.runmode = runmode
def get_config(self) -> Dict[str, Any]:
"""
Return the config. Use this method to get the bot config
:return: Dict: Bot config
"""
if self.config is None:
self.config = self.load_config()
return self.config
def load_config(self) -> Dict[str, Any]:
"""
Extract information for sys.argv and load the bot configuration
@ -75,7 +57,7 @@ class Configuration(object):
config['internals'] = {}
logger.info('Validating configuration ...')
self._validate_config_schema(config)
validate_config_schema(config)
self._validate_config_consistency(config)
# Set strategy if not specified in config and or if it's non default
@ -185,7 +167,7 @@ class Configuration(object):
logger.info(f'Using DB: "{config["db_url"]}"')
# Check if the exchange set by the user is supported
self.check_exchange(config)
check_exchange(config)
return config
@ -337,24 +319,6 @@ class Configuration(object):
logstring='Using trades from: {}')
return config
def _validate_config_schema(self, conf: Dict[str, Any]) -> Dict[str, Any]:
"""
Validate the configuration follow the Config Schema
:param conf: Config in JSON format
:return: Returns the config if valid, otherwise throw an exception
"""
try:
FreqtradeValidator(constants.CONF_SCHEMA).validate(conf)
return conf
except ValidationError as exception:
logger.critical(
'Invalid configuration. See config.json.example. Reason: %s',
exception
)
raise ValidationError(
best_match(Draft4Validator(constants.CONF_SCHEMA).iter_errors(conf)).message
)
def _validate_config_consistency(self, conf: Dict[str, Any]) -> None:
"""
Validate the configuration consistency
@ -383,51 +347,3 @@ class Configuration(object):
raise OperationalException(
f'The config trailing_stop_positive_offset needs '
'to be greater than trailing_stop_positive_offset in your config.')
def get_config(self) -> Dict[str, Any]:
"""
Return the config. Use this method to get the bot config
:return: Dict: Bot config
"""
if self.config is None:
self.config = self.load_config()
return self.config
def check_exchange(self, config: Dict[str, Any], check_for_bad: bool = True) -> bool:
"""
Check if the exchange name in the config file is supported by Freqtrade
:param check_for_bad: if True, check the exchange against the list of known 'bad'
exchanges
:return: False if exchange is 'bad', i.e. is known to work with the bot with
critical issues or does not work at all, crashes, etc. True otherwise.
raises an exception if the exchange if not supported by ccxt
and thus is not known for the Freqtrade at all.
"""
logger.info("Checking exchange...")
exchange = config.get('exchange', {}).get('name').lower()
if not is_exchange_available(exchange):
raise OperationalException(
f'Exchange "{exchange}" is not supported by ccxt '
f'and therefore not available for the bot.\n'
f'The following exchanges are supported by ccxt: '
f'{", ".join(available_exchanges())}'
)
if check_for_bad and is_exchange_bad(exchange):
logger.warning(f'Exchange "{exchange}" is known to not work with the bot yet. '
f'Use it only for development and testing purposes.')
return False
if is_exchange_officially_supported(exchange):
logger.info(f'Exchange "{exchange}" is officially supported '
f'by the Freqtrade development team.')
else:
logger.warning(f'Exchange "{exchange}" is supported by ccxt '
f'and therefore available for the bot but not officially supported '
f'by the Freqtrade development team. '
f'It may work flawlessly (please report back) or have serious issues. '
f'Use it at your own discretion.')
return True

View File

@ -0,0 +1,53 @@
import logging
from typing import Any, Dict
from jsonschema import Draft4Validator, validators
from jsonschema.exceptions import ValidationError, best_match
from freqtrade import constants
logger = logging.getLogger(__name__)
def _extend_validator(validator_class):
"""
Extended validator for the Freqtrade configuration JSON Schema.
Currently it only handles defaults for subschemas.
"""
validate_properties = validator_class.VALIDATORS['properties']
def set_defaults(validator, properties, instance, schema):
for prop, subschema in properties.items():
if 'default' in subschema:
instance.setdefault(prop, subschema['default'])
for error in validate_properties(
validator, properties, instance, schema,
):
yield error
return validators.extend(
validator_class, {'properties': set_defaults}
)
FreqtradeValidator = _extend_validator(Draft4Validator)
def validate_config_schema(conf: Dict[str, Any]) -> Dict[str, Any]:
"""
Validate the configuration follow the Config Schema
:param conf: Config in JSON format
:return: Returns the config if valid, otherwise throw an exception
"""
try:
FreqtradeValidator(constants.CONF_SCHEMA).validate(conf)
return conf
except ValidationError as e:
logger.critical(
f"Invalid configuration. See config.json.example. Reason: {e}"
)
raise ValidationError(
best_match(Draft4Validator(constants.CONF_SCHEMA).iter_errors(conf)).message
)

View File

@ -16,7 +16,7 @@ import arrow
from pandas import DataFrame
from freqtrade import OperationalException, misc
from freqtrade.arguments import TimeRange
from freqtrade.configuration import TimeRange
from freqtrade.data.converter import parse_ticker_dataframe
from freqtrade.exchange import Exchange, timeframe_to_minutes

View File

@ -10,8 +10,7 @@ import utils_find_1st as utf1st
from pandas import DataFrame
from freqtrade import constants, OperationalException
from freqtrade.arguments import Arguments
from freqtrade.arguments import TimeRange
from freqtrade.configuration import Arguments, TimeRange
from freqtrade.data import history
from freqtrade.strategy.interface import SellType

View File

@ -15,7 +15,7 @@ from argparse import Namespace
from typing import Any, List
from freqtrade import OperationalException
from freqtrade.arguments import Arguments
from freqtrade.configuration import Arguments
from freqtrade.worker import Worker

View File

@ -12,7 +12,7 @@ from typing import Any, Dict, List, NamedTuple, Optional
from pandas import DataFrame
from tabulate import tabulate
from freqtrade.arguments import Arguments
from freqtrade.configuration import Arguments
from freqtrade.data import history
from freqtrade.data.dataprovider import DataProvider
from freqtrade.exchange import timeframe_to_minutes

View File

@ -9,7 +9,7 @@ from tabulate import tabulate
from freqtrade import constants
from freqtrade.edge import Edge
from freqtrade.arguments import Arguments
from freqtrade.configuration import Arguments
from freqtrade.exchange import Exchange
from freqtrade.resolvers import StrategyResolver

View File

@ -18,7 +18,7 @@ from pandas import DataFrame
from skopt import Optimizer
from skopt.space import Dimension
from freqtrade.arguments import Arguments
from freqtrade.configuration import Arguments
from freqtrade.data.history import load_data, get_timeframe
from freqtrade.optimize.backtesting import Backtesting
from freqtrade.resolvers.hyperopt_resolver import HyperOptResolver

View File

@ -4,7 +4,7 @@ from typing import Dict, List, Optional
import pandas as pd
from freqtrade.arguments import Arguments
from freqtrade.configuration import Arguments
from freqtrade.data import history
from freqtrade.data.btanalysis import (combine_tickers_with_mean,
create_cum_profit, load_trades)

View File

@ -14,7 +14,7 @@ import pytest
from telegram import Chat, Message, Update
from freqtrade import constants, persistence
from freqtrade.arguments import Arguments
from freqtrade.configuration import Arguments
from freqtrade.data.converter import parse_ticker_dataframe
from freqtrade.edge import Edge, PairInfo
from freqtrade.exchange import Exchange
@ -22,6 +22,7 @@ from freqtrade.freqtradebot import FreqtradeBot
from freqtrade.resolvers import ExchangeResolver
from freqtrade.worker import Worker
logging.getLogger('').setLevel(logging.INFO)
@ -39,7 +40,7 @@ def log_has_re(line, logs):
False)
def get_args(args) -> List[str]:
def get_args(args):
return Arguments(args, '').get_parsed_arg()

View File

@ -4,7 +4,7 @@ import pytest
from arrow import Arrow
from pandas import DataFrame, to_datetime
from freqtrade.arguments import Arguments, TimeRange
from freqtrade.configuration import Arguments, TimeRange
from freqtrade.data.btanalysis import (BT_DATA_COLUMNS,
combine_tickers_with_mean,
create_cum_profit,

View File

@ -12,7 +12,7 @@ import pytest
from pandas import DataFrame
from freqtrade import OperationalException
from freqtrade.arguments import TimeRange
from freqtrade.configuration import TimeRange
from freqtrade.data import history
from freqtrade.data.history import (download_pair_history,
load_cached_data_for_updating,

View File

@ -11,7 +11,7 @@ import pytest
from arrow import Arrow
from freqtrade import DependencyException, constants
from freqtrade.arguments import TimeRange
from freqtrade.configuration import TimeRange
from freqtrade.data import history
from freqtrade.data.btanalysis import evaluate_result_multi
from freqtrade.data.converter import parse_ticker_dataframe

View File

@ -6,7 +6,7 @@ from unittest.mock import MagicMock
import arrow
from pandas import DataFrame
from freqtrade.arguments import TimeRange
from freqtrade.configuration import TimeRange
from freqtrade.data.converter import parse_ticker_dataframe
from freqtrade.data.history import load_tickerdata_file
from freqtrade.persistence import Trade

View File

@ -3,8 +3,9 @@ import argparse
import pytest
from freqtrade.arguments import (ARGS_DOWNLOADER, ARGS_PLOT_DATAFRAME,
Arguments, TimeRange, check_int_positive)
from freqtrade.configuration import Arguments, TimeRange
from freqtrade.configuration import ARGS_DOWNLOADER, ARGS_PLOT_DATAFRAME
from freqtrade.configuration.arguments import check_int_positive
# Parse common command-line-arguments. Used for all tools

View File

@ -11,8 +11,9 @@ import pytest
from jsonschema import Draft4Validator, ValidationError, validate
from freqtrade import OperationalException, constants
from freqtrade.arguments import Arguments
from freqtrade.configuration import Configuration
from freqtrade.configuration import Arguments, Configuration
from freqtrade.configuration.check_exchange import check_exchange
from freqtrade.configuration.json_schema import validate_config_schema
from freqtrade.constants import DEFAULT_DB_DRYRUN_URL, DEFAULT_DB_PROD_URL
from freqtrade.loggers import _set_loggers
from freqtrade.state import RunMode
@ -32,24 +33,21 @@ def test_load_config_invalid_pair(default_conf) -> None:
default_conf['exchange']['pair_whitelist'].append('ETH-BTC')
with pytest.raises(ValidationError, match=r'.*does not match.*'):
configuration = Configuration(Namespace())
configuration._validate_config_schema(default_conf)
validate_config_schema(default_conf)
def test_load_config_missing_attributes(default_conf) -> None:
default_conf.pop('exchange')
with pytest.raises(ValidationError, match=r'.*\'exchange\' is a required property.*'):
configuration = Configuration(Namespace())
configuration._validate_config_schema(default_conf)
validate_config_schema(default_conf)
def test_load_config_incorrect_stake_amount(default_conf) -> None:
default_conf['stake_amount'] = 'fake'
with pytest.raises(ValidationError, match=r'.*\'fake\' does not match \'unlimited\'.*'):
configuration = Configuration(Namespace())
configuration._validate_config_schema(default_conf)
validate_config_schema(default_conf)
def test_load_config_file(default_conf, mocker, caplog) -> None:
@ -469,25 +467,23 @@ def test_hyperopt_with_arguments(mocker, default_conf, caplog) -> None:
def test_check_exchange(default_conf, caplog) -> None:
configuration = Configuration(Namespace())
# Test an officially supported by Freqtrade team exchange
default_conf.get('exchange').update({'name': 'BITTREX'})
assert configuration.check_exchange(default_conf)
assert check_exchange(default_conf)
assert log_has_re(r"Exchange .* is officially supported by the Freqtrade development team\.",
caplog.record_tuples)
caplog.clear()
# Test an officially supported by Freqtrade team exchange
default_conf.get('exchange').update({'name': 'binance'})
assert configuration.check_exchange(default_conf)
assert check_exchange(default_conf)
assert log_has_re(r"Exchange .* is officially supported by the Freqtrade development team\.",
caplog.record_tuples)
caplog.clear()
# Test an available exchange, supported by ccxt
default_conf.get('exchange').update({'name': 'kraken'})
assert configuration.check_exchange(default_conf)
assert check_exchange(default_conf)
assert log_has_re(r"Exchange .* is supported by ccxt and .* not officially supported "
r"by the Freqtrade development team\. .*",
caplog.record_tuples)
@ -495,7 +491,7 @@ def test_check_exchange(default_conf, caplog) -> None:
# Test a 'bad' exchange, which known to have serious problems
default_conf.get('exchange').update({'name': 'bitmex'})
assert not configuration.check_exchange(default_conf)
assert not check_exchange(default_conf)
assert log_has_re(r"Exchange .* is known to not work with the bot yet\. "
r"Use it only for development and testing purposes\.",
caplog.record_tuples)
@ -503,7 +499,7 @@ def test_check_exchange(default_conf, caplog) -> None:
# Test a 'bad' exchange with check_for_bad=False
default_conf.get('exchange').update({'name': 'bitmex'})
assert configuration.check_exchange(default_conf, False)
assert check_exchange(default_conf, False)
assert log_has_re(r"Exchange .* is supported by ccxt and .* not officially supported "
r"by the Freqtrade development team\. .*",
caplog.record_tuples)
@ -511,14 +507,13 @@ def test_check_exchange(default_conf, caplog) -> None:
# Test an invalid exchange
default_conf.get('exchange').update({'name': 'unknown_exchange'})
configuration.config = default_conf
with pytest.raises(
OperationalException,
match=r'.*Exchange "unknown_exchange" is not supported by ccxt '
r'and therefore not available for the bot.*'
):
configuration.check_exchange(default_conf)
check_exchange(default_conf)
def test_cli_verbose_with_params(default_conf, mocker, caplog) -> None:
@ -656,8 +651,7 @@ def test_load_config_default_exchange(all_conf) -> None:
with pytest.raises(ValidationError,
match=r'\'exchange\' is a required property'):
configuration = Configuration(Namespace())
configuration._validate_config_schema(all_conf)
validate_config_schema(all_conf)
def test_load_config_default_exchange_name(all_conf) -> None:
@ -671,8 +665,7 @@ def test_load_config_default_exchange_name(all_conf) -> None:
with pytest.raises(ValidationError,
match=r'\'name\' is a required property'):
configuration = Configuration(Namespace())
configuration._validate_config_schema(all_conf)
validate_config_schema(all_conf)
@pytest.mark.parametrize("keys", [("exchange", "sandbox", False),
@ -695,7 +688,6 @@ def test_load_config_default_subkeys(all_conf, keys) -> None:
assert subkey not in all_conf[key]
configuration = Configuration(Namespace())
configuration._validate_config_schema(all_conf)
validate_config_schema(all_conf)
assert subkey in all_conf[key]
assert all_conf[key][subkey] == keys[2]

View File

@ -6,7 +6,7 @@ from unittest.mock import MagicMock
import pytest
from freqtrade import OperationalException
from freqtrade.arguments import Arguments
from freqtrade.configuration import Arguments
from freqtrade.freqtradebot import FreqtradeBot
from freqtrade.main import main
from freqtrade.state import State

View File

@ -5,7 +5,7 @@ from unittest.mock import MagicMock
import plotly.graph_objs as go
from plotly import tools
from freqtrade.arguments import Arguments, TimeRange
from freqtrade.configuration import Arguments, TimeRange
from freqtrade.data import history
from freqtrade.data.btanalysis import create_cum_profit, load_backtest_data
from freqtrade.plot.plotting import (add_indicators, add_profit,

View File

@ -8,8 +8,10 @@ import sys
from pathlib import Path
from typing import Any, Dict, List
from freqtrade.arguments import Arguments, TimeRange, ARGS_DOWNLOADER
from freqtrade.configuration import Arguments, TimeRange
from freqtrade.configuration import ARGS_DOWNLOADER
from freqtrade.configuration import Configuration
from freqtrade.configuration.check_exchange import check_exchange
from freqtrade.data.history import download_pair_history
from freqtrade.exchange import Exchange
from freqtrade.misc import deep_merge_dicts
@ -79,7 +81,7 @@ if args.config and args.exchange:
"using exchange settings from the configuration file.")
# Check if the exchange set by the user is supported
configuration.check_exchange(config)
check_exchange(config)
configuration._load_datadir_config(config)

View File

@ -18,7 +18,7 @@ from typing import Any, Dict, List
import pandas as pd
from freqtrade.arguments import ARGS_PLOT_DATAFRAME, Arguments
from freqtrade.configuration import Arguments, ARGS_PLOT_DATAFRAME
from freqtrade.data.btanalysis import extract_trades_of_period
from freqtrade.optimize import setup_configuration
from freqtrade.plot.plotting import (init_plotscript, generate_candlestick_graph,

View File

@ -8,7 +8,7 @@ import logging
import sys
from typing import Any, Dict, List
from freqtrade.arguments import ARGS_PLOT_PROFIT, Arguments
from freqtrade.configuration import Arguments, ARGS_PLOT_PROFIT
from freqtrade.optimize import setup_configuration
from freqtrade.plot.plotting import init_plotscript, generate_profit_graph, store_plot_file
from freqtrade.state import RunMode