Merge pull request #2015 from hroff-1902/refactor/config2

Make configuration a module
This commit is contained in:
Matthias 2019-07-15 19:41:57 +02:00 committed by GitHub
commit 5144e98a82
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 269 additions and 303 deletions

View File

@ -0,0 +1,2 @@
from freqtrade.configuration.arguments import Arguments, TimeRange # noqa: F401
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

@ -3,17 +3,14 @@ This module contains the configuration class
"""
import json
import logging
import os
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.create_datadir import create_datadir
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,21 +167,10 @@ 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
def _create_datadir(self, config: Dict[str, Any], datadir: Optional[str] = None) -> str:
if not datadir:
# set datadir
exchange_name = config.get('exchange', {}).get('name').lower()
datadir = os.path.join('user_data', 'data', exchange_name)
if not os.path.isdir(datadir):
os.makedirs(datadir)
logger.info(f'Created data directory: {datadir}')
return datadir
def _args_to_config(self, config: Dict[str, Any], argname: str,
logstring: str, logfun: Optional[Callable] = None) -> None:
"""
@ -225,9 +196,9 @@ class Configuration(object):
the --datadir option
"""
if 'datadir' in self.args and self.args.datadir:
config.update({'datadir': self._create_datadir(config, self.args.datadir)})
config.update({'datadir': create_datadir(config, self.args.datadir)})
else:
config.update({'datadir': self._create_datadir(config, None)})
config.update({'datadir': create_datadir(config, None)})
logger.info('Using data directory: %s ...', config.get('datadir'))
def _load_optimize_config(self, config: Dict[str, Any]) -> Dict[str, Any]:
@ -337,24 +308,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 +336,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,18 @@
import logging
import os
from typing import Any, Dict, Optional
logger = logging.getLogger(__name__)
def create_datadir(config: Dict[str, Any], datadir: Optional[str] = None) -> str:
if not datadir:
# set datadir
exchange_name = config.get('exchange', {}).get('name').lower()
datadir = os.path.join('user_data', 'data', exchange_name)
if not os.path.isdir(datadir):
os.makedirs(datadir)
logger.info(f'Created data directory: {datadir}')
return datadir

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

@ -6,7 +6,6 @@ from copy import deepcopy
from datetime import datetime
from functools import reduce
from pathlib import Path
from typing import List
from unittest.mock import MagicMock, PropertyMock
import arrow
@ -14,7 +13,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 +21,7 @@ from freqtrade.freqtradebot import FreqtradeBot
from freqtrade.resolvers import ExchangeResolver
from freqtrade.worker import Worker
logging.getLogger('').setLevel(logging.INFO)
@ -39,10 +39,17 @@ def log_has_re(line, logs):
False)
def get_args(args) -> List[str]:
def get_args(args):
return Arguments(args, '').get_parsed_arg()
def patched_configuration_load_config_file(mocker, config) -> None:
mocker.patch(
'freqtrade.configuration.configuration.Configuration._load_config_file',
lambda *args, **kwargs: config
)
def patch_exchange(mocker, api_mock=None, id='bittrex') -> None:
mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={}))
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())

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

@ -1,6 +1,5 @@
# pragma pylint: disable=missing-docstring, W0212, line-too-long, C0103, unused-argument
import json
import math
import random
from unittest.mock import MagicMock
@ -11,7 +10,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
@ -22,7 +21,8 @@ from freqtrade.optimize.backtesting import Backtesting
from freqtrade.state import RunMode
from freqtrade.strategy.default_strategy import DefaultStrategy
from freqtrade.strategy.interface import SellType
from freqtrade.tests.conftest import get_args, log_has, log_has_re, patch_exchange
from freqtrade.tests.conftest import (get_args, log_has, log_has_re, patch_exchange,
patched_configuration_load_config_file)
def trim_dictlist(dict_list, num):
@ -165,9 +165,7 @@ def _trend_alternate(dataframe=None, metadata=None):
# Unit tests
def test_setup_configuration_without_arguments(mocker, default_conf, caplog) -> None:
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf)
))
patched_configuration_load_config_file(mocker, default_conf)
args = [
'--config', 'config.json',
@ -205,10 +203,11 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) ->
def test_setup_bt_configuration_with_arguments(mocker, default_conf, caplog) -> None:
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf)
))
mocker.patch('freqtrade.configuration.Configuration._create_datadir', lambda s, c, x: x)
patched_configuration_load_config_file(mocker, default_conf)
mocker.patch(
'freqtrade.configuration.configuration.create_datadir',
lambda c, x: x
)
args = [
'--config', 'config.json',
@ -276,9 +275,7 @@ def test_setup_bt_configuration_with_arguments(mocker, default_conf, caplog) ->
def test_setup_configuration_unlimited_stake_amount(mocker, default_conf, caplog) -> None:
default_conf['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf)
))
patched_configuration_load_config_file(mocker, default_conf)
args = [
'--config', 'config.json',
@ -295,9 +292,8 @@ def test_start(mocker, fee, default_conf, caplog) -> None:
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
patch_exchange(mocker)
mocker.patch('freqtrade.optimize.backtesting.Backtesting.start', start_mock)
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf)
))
patched_configuration_load_config_file(mocker, default_conf)
args = [
'--config', 'config.json',
'--strategy', 'DefaultStrategy',
@ -828,9 +824,7 @@ def test_backtest_start_live(default_conf, mocker, caplog):
patch_exchange(mocker, api_mock)
mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest', MagicMock())
mocker.patch('freqtrade.optimize.backtesting.Backtesting._generate_text_table', MagicMock())
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf)
))
patched_configuration_load_config_file(mocker, default_conf)
args = [
'--config', 'config.json',
@ -880,9 +874,7 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog):
gen_strattable_mock = MagicMock()
mocker.patch('freqtrade.optimize.backtesting.Backtesting._generate_text_table_strategy',
gen_strattable_mock)
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf)
))
patched_configuration_load_config_file(mocker, default_conf)
args = [
'--config', 'config.json',

View File

@ -1,20 +1,18 @@
# pragma pylint: disable=missing-docstring, C0103, C0330
# pragma pylint: disable=protected-access, too-many-lines, invalid-name, too-many-arguments
import json
from unittest.mock import MagicMock
from freqtrade.edge import PairInfo
from freqtrade.optimize import setup_configuration, start_edge
from freqtrade.optimize.edge_cli import EdgeCli
from freqtrade.state import RunMode
from freqtrade.tests.conftest import get_args, log_has, log_has_re, patch_exchange
from freqtrade.tests.conftest import (get_args, log_has, log_has_re, patch_exchange,
patched_configuration_load_config_file)
def test_setup_configuration_without_arguments(mocker, default_conf, caplog) -> None:
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf)
))
patched_configuration_load_config_file(mocker, default_conf)
args = [
'--config', 'config.json',
@ -46,10 +44,11 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) ->
def test_setup_edge_configuration_with_arguments(mocker, edge_conf, caplog) -> None:
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(edge_conf)
))
mocker.patch('freqtrade.configuration.Configuration._create_datadir', lambda s, c, x: x)
patched_configuration_load_config_file(mocker, edge_conf)
mocker.patch(
'freqtrade.configuration.configuration.create_datadir',
lambda c, x: x
)
args = [
'--config', 'config.json',
@ -92,9 +91,8 @@ def test_start(mocker, fee, edge_conf, caplog) -> None:
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
patch_exchange(mocker)
mocker.patch('freqtrade.optimize.edge_cli.EdgeCli.start', start_mock)
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(edge_conf)
))
patched_configuration_load_config_file(mocker, edge_conf)
args = [
'--config', 'config.json',
'--strategy', 'DefaultStrategy',

View File

@ -1,5 +1,4 @@
# pragma pylint: disable=missing-docstring,W0212,C0103
import json
import os
from datetime import datetime
from unittest.mock import MagicMock
@ -16,7 +15,8 @@ from freqtrade.optimize.hyperopt import Hyperopt, HYPEROPT_LOCKFILE
from freqtrade.optimize import setup_configuration, start_hyperopt
from freqtrade.resolvers.hyperopt_resolver import HyperOptResolver
from freqtrade.state import RunMode
from freqtrade.tests.conftest import get_args, log_has, log_has_re, patch_exchange
from freqtrade.tests.conftest import (get_args, log_has, log_has_re, patch_exchange,
patched_configuration_load_config_file)
@pytest.fixture(scope='function')
@ -44,9 +44,7 @@ def create_trials(mocker, hyperopt) -> None:
def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, caplog) -> None:
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf)
))
patched_configuration_load_config_file(mocker, default_conf)
args = [
'--config', 'config.json',
@ -82,10 +80,11 @@ def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, ca
def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplog) -> None:
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf)
))
mocker.patch('freqtrade.configuration.Configuration._create_datadir', lambda s, c, x: x)
patched_configuration_load_config_file(mocker, default_conf)
mocker.patch(
'freqtrade.configuration.configuration.create_datadir',
lambda c, x: x
)
args = [
'--config', 'config.json',
@ -148,11 +147,8 @@ def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplo
def test_hyperoptresolver(mocker, default_conf, caplog) -> None:
patched_configuration_load_config_file(mocker, default_conf)
mocker.patch(
'freqtrade.configuration.Configuration._load_config_file',
lambda *args, **kwargs: default_conf
)
hyperopts = DefaultHyperOpts
delattr(hyperopts, 'populate_buy_trend')
delattr(hyperopts, 'populate_sell_trend')
@ -172,10 +168,7 @@ def test_hyperoptresolver(mocker, default_conf, caplog) -> None:
def test_start(mocker, default_conf, caplog) -> None:
start_mock = MagicMock()
mocker.patch(
'freqtrade.configuration.Configuration._load_config_file',
lambda *args, **kwargs: default_conf
)
patched_configuration_load_config_file(mocker, default_conf)
mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.start', start_mock)
patch_exchange(mocker)
@ -198,10 +191,7 @@ def test_start(mocker, default_conf, caplog) -> None:
def test_start_no_data(mocker, default_conf, caplog) -> None:
mocker.patch(
'freqtrade.configuration.Configuration._load_config_file',
lambda *args, **kwargs: default_conf
)
patched_configuration_load_config_file(mocker, default_conf)
mocker.patch('freqtrade.optimize.hyperopt.load_data', MagicMock(return_value={}))
mocker.patch(
'freqtrade.optimize.hyperopt.get_timeframe',
@ -226,10 +216,7 @@ def test_start_no_data(mocker, default_conf, caplog) -> None:
def test_start_failure(mocker, default_conf, caplog) -> None:
start_mock = MagicMock()
mocker.patch(
'freqtrade.configuration.Configuration._load_config_file',
lambda *args, **kwargs: default_conf
)
patched_configuration_load_config_file(mocker, default_conf)
mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.start', start_mock)
patch_exchange(mocker)
@ -250,10 +237,7 @@ def test_start_failure(mocker, default_conf, caplog) -> None:
def test_start_filelock(mocker, default_conf, caplog) -> None:
start_mock = MagicMock(side_effect=Timeout(HYPEROPT_LOCKFILE))
mocker.patch(
'freqtrade.configuration.Configuration._load_config_file',
lambda *args, **kwargs: default_conf
)
patched_configuration_load_config_file(mocker, default_conf)
mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.start', start_mock)
patch_exchange(mocker)

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.arguments 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

@ -1,5 +1,4 @@
# pragma pylint: disable=missing-docstring, protected-access, invalid-name
import json
import logging
from argparse import Namespace
@ -11,12 +10,15 @@ 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.create_datadir import create_datadir
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
from freqtrade.tests.conftest import log_has, log_has_re
from freqtrade.tests.conftest import (log_has, log_has_re,
patched_configuration_load_config_file)
@pytest.fixture(scope="function")
@ -32,28 +34,25 @@ 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:
file_mock = mocker.patch('freqtrade.configuration.open', mocker.mock_open(
file_mock = mocker.patch('freqtrade.configuration.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf)
))
@ -65,9 +64,7 @@ def test_load_config_file(default_conf, mocker, caplog) -> None:
def test_load_config_max_open_trades_zero(default_conf, mocker, caplog) -> None:
default_conf['max_open_trades'] = 0
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf)
))
patched_configuration_load_config_file(mocker, default_conf)
args = Arguments([], '').get_parsed_arg()
configuration = Configuration(args)
@ -89,7 +86,10 @@ def test_load_config_combine_dicts(default_conf, mocker, caplog) -> None:
config_files = [conf1, conf2]
configsmock = MagicMock(side_effect=config_files)
mocker.patch('freqtrade.configuration.Configuration._load_config_file', configsmock)
mocker.patch(
'freqtrade.configuration.configuration.Configuration._load_config_file',
configsmock
)
arg_list = ['-c', 'test_conf.json', '--config', 'test2_conf.json', ]
args = Arguments(arg_list, '').get_parsed_arg()
@ -109,9 +109,7 @@ def test_load_config_combine_dicts(default_conf, mocker, caplog) -> None:
def test_load_config_max_open_trades_minus_one(default_conf, mocker, caplog) -> None:
default_conf['max_open_trades'] = -1
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf)
))
patched_configuration_load_config_file(mocker, default_conf)
args = Arguments([], '').get_parsed_arg()
configuration = Configuration(args)
@ -126,7 +124,7 @@ def test_load_config_max_open_trades_minus_one(default_conf, mocker, caplog) ->
def test_load_config_file_exception(mocker) -> None:
mocker.patch(
'freqtrade.configuration.open',
'freqtrade.configuration.configuration.open',
MagicMock(side_effect=FileNotFoundError('File not found'))
)
configuration = Configuration(Namespace())
@ -136,9 +134,7 @@ def test_load_config_file_exception(mocker) -> None:
def test_load_config(default_conf, mocker) -> None:
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf)
))
patched_configuration_load_config_file(mocker, default_conf)
args = Arguments([], '').get_parsed_arg()
configuration = Configuration(args)
@ -150,9 +146,8 @@ def test_load_config(default_conf, mocker) -> None:
def test_load_config_with_params(default_conf, mocker) -> None:
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf)
))
patched_configuration_load_config_file(mocker, default_conf)
arglist = [
'--dynamic-whitelist', '10',
'--strategy', 'TestStrategy',
@ -173,9 +168,7 @@ def test_load_config_with_params(default_conf, mocker) -> None:
conf = default_conf.copy()
conf["dry_run"] = False
conf["db_url"] = "sqlite:///path/to/db.sqlite"
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(conf)
))
patched_configuration_load_config_file(mocker, conf)
arglist = [
'--strategy', 'TestStrategy',
@ -191,9 +184,7 @@ def test_load_config_with_params(default_conf, mocker) -> None:
conf = default_conf.copy()
conf["dry_run"] = True
conf["db_url"] = "sqlite:///path/to/db.sqlite"
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(conf)
))
patched_configuration_load_config_file(mocker, conf)
arglist = [
'--strategy', 'TestStrategy',
@ -209,9 +200,7 @@ def test_load_config_with_params(default_conf, mocker) -> None:
conf = default_conf.copy()
conf["dry_run"] = False
del conf["db_url"]
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(conf)
))
patched_configuration_load_config_file(mocker, conf)
arglist = [
'--strategy', 'TestStrategy',
@ -229,9 +218,7 @@ def test_load_config_with_params(default_conf, mocker) -> None:
conf = default_conf.copy()
conf["dry_run"] = True
conf["db_url"] = DEFAULT_DB_PROD_URL
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(conf)
))
patched_configuration_load_config_file(mocker, conf)
arglist = [
'--strategy', 'TestStrategy',
@ -249,9 +236,7 @@ def test_load_custom_strategy(default_conf, mocker) -> None:
'strategy': 'CustomStrategy',
'strategy_path': '/tmp/strategies',
})
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf)
))
patched_configuration_load_config_file(mocker, default_conf)
args = Arguments([], '').get_parsed_arg()
configuration = Configuration(args)
@ -262,9 +247,8 @@ def test_load_custom_strategy(default_conf, mocker) -> None:
def test_show_info(default_conf, mocker, caplog) -> None:
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf)
))
patched_configuration_load_config_file(mocker, default_conf)
arglist = [
'--dynamic-whitelist', '10',
'--strategy', 'TestStrategy',
@ -287,9 +271,8 @@ def test_show_info(default_conf, mocker, caplog) -> None:
def test_setup_configuration_without_arguments(mocker, default_conf, caplog) -> None:
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf)
))
patched_configuration_load_config_file(mocker, default_conf)
arglist = [
'--config', 'config.json',
'--strategy', 'DefaultStrategy',
@ -327,10 +310,11 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) ->
def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> None:
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf)
))
mocker.patch('freqtrade.configuration.Configuration._create_datadir', lambda s, c, x: x)
patched_configuration_load_config_file(mocker, default_conf)
mocker.patch(
'freqtrade.configuration.configuration.create_datadir',
lambda c, x: x
)
arglist = [
'--config', 'config.json',
@ -393,9 +377,7 @@ def test_setup_configuration_with_stratlist(mocker, default_conf, caplog) -> Non
"""
Test setup_configuration() function
"""
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf)
))
patched_configuration_load_config_file(mocker, default_conf)
arglist = [
'--config', 'config.json',
@ -443,9 +425,8 @@ def test_setup_configuration_with_stratlist(mocker, default_conf, caplog) -> Non
def test_hyperopt_with_arguments(mocker, default_conf, caplog) -> None:
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf)
))
patched_configuration_load_config_file(mocker, default_conf)
arglist = [
'hyperopt',
'--epochs', '10',
@ -469,25 +450,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 +474,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 +482,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,19 +490,18 @@ 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:
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf)))
patched_configuration_load_config_file(mocker, default_conf)
# Prevent setting loggers
mocker.patch('freqtrade.loggers._set_loggers', MagicMock)
arglist = ['-vvv']
@ -575,8 +553,7 @@ def test_set_loggers() -> None:
def test_set_logfile(default_conf, mocker):
mocker.patch('freqtrade.configuration.open',
mocker.mock_open(read_data=json.dumps(default_conf)))
patched_configuration_load_config_file(mocker, default_conf)
arglist = [
'--logfile', 'test_file.log',
@ -593,9 +570,7 @@ def test_set_logfile(default_conf, mocker):
def test_load_config_warn_forcebuy(default_conf, mocker, caplog) -> None:
default_conf['forcebuy_enable'] = True
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf)
))
patched_configuration_load_config_file(mocker, default_conf)
args = Arguments([], '').get_parsed_arg()
configuration = Configuration(args)
@ -609,12 +584,11 @@ def test_validate_default_conf(default_conf) -> None:
validate(default_conf, constants.CONF_SCHEMA, Draft4Validator)
def test__create_datadir(mocker, default_conf, caplog) -> None:
def test_create_datadir(mocker, default_conf, caplog) -> None:
mocker.patch('os.path.isdir', MagicMock(return_value=False))
md = MagicMock()
mocker.patch('os.makedirs', md)
cfg = Configuration(Namespace())
cfg._create_datadir(default_conf, '/foo/bar')
create_datadir(default_conf, '/foo/bar')
assert md.call_args[0][0] == "/foo/bar"
assert log_has('Created data directory: /foo/bar', caplog.record_tuples)
@ -656,8 +630,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 +644,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 +667,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,11 +6,12 @@ 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
from freqtrade.tests.conftest import log_has, patch_exchange
from freqtrade.tests.conftest import (log_has, patch_exchange,
patched_configuration_load_config_file)
from freqtrade.worker import Worker
@ -50,10 +51,7 @@ def test_main_fatal_exception(mocker, default_conf, caplog) -> None:
patch_exchange(mocker)
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.cleanup', MagicMock())
mocker.patch('freqtrade.worker.Worker._worker', MagicMock(side_effect=Exception))
mocker.patch(
'freqtrade.configuration.Configuration._load_config_file',
lambda *args, **kwargs: default_conf
)
patched_configuration_load_config_file(mocker, default_conf)
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock())
@ -70,10 +68,7 @@ def test_main_keyboard_interrupt(mocker, default_conf, caplog) -> None:
patch_exchange(mocker)
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.cleanup', MagicMock())
mocker.patch('freqtrade.worker.Worker._worker', MagicMock(side_effect=KeyboardInterrupt))
mocker.patch(
'freqtrade.configuration.Configuration._load_config_file',
lambda *args, **kwargs: default_conf
)
patched_configuration_load_config_file(mocker, default_conf)
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock())
@ -93,10 +88,7 @@ def test_main_operational_exception(mocker, default_conf, caplog) -> None:
'freqtrade.worker.Worker._worker',
MagicMock(side_effect=OperationalException('Oh snap!'))
)
mocker.patch(
'freqtrade.configuration.Configuration._load_config_file',
lambda *args, **kwargs: default_conf
)
patched_configuration_load_config_file(mocker, default_conf)
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock())
@ -118,10 +110,7 @@ def test_main_reload_conf(mocker, default_conf, caplog) -> None:
State.RUNNING,
OperationalException("Oh snap!")])
mocker.patch('freqtrade.worker.Worker._worker', worker_mock)
mocker.patch(
'freqtrade.configuration.Configuration._load_config_file',
lambda *args, **kwargs: default_conf
)
patched_configuration_load_config_file(mocker, default_conf)
reconfigure_mock = mocker.patch('freqtrade.main.Worker._reconfigure', MagicMock())
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
@ -145,10 +134,7 @@ def test_reconfigure(mocker, default_conf) -> None:
'freqtrade.worker.Worker._worker',
MagicMock(side_effect=OperationalException('Oh snap!'))
)
mocker.patch(
'freqtrade.configuration.Configuration._load_config_file',
lambda *args, **kwargs: default_conf
)
patched_configuration_load_config_file(mocker, default_conf)
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock())
@ -159,10 +145,7 @@ def test_reconfigure(mocker, default_conf) -> None:
# Renew mock to return modified data
conf = deepcopy(default_conf)
conf['stake_amount'] += 1
mocker.patch(
'freqtrade.configuration.Configuration._load_config_file',
lambda *args, **kwargs: conf
)
patched_configuration_load_config_file(mocker, conf)
worker._config = conf
# reconfigure should return a new instance

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 Configuration
from freqtrade.configuration.arguments import ARGS_DOWNLOADER
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,8 @@ from typing import Any, Dict, List
import pandas as pd
from freqtrade.arguments import ARGS_PLOT_DATAFRAME, Arguments
from freqtrade.configuration import Arguments
from freqtrade.configuration.arguments import 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,8 @@ import logging
import sys
from typing import Any, Dict, List
from freqtrade.arguments import ARGS_PLOT_PROFIT, Arguments
from freqtrade.configuration import Arguments
from freqtrade.configuration.arguments import 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