Merge branch 'develop' into pr/hroff-1902/1804

This commit is contained in:
Matthias 2019-05-01 12:54:36 +02:00
commit b9d7bb2d8e
13 changed files with 121 additions and 163 deletions

View File

@ -22,7 +22,7 @@ requirements:
- requirements.txt - requirements.txt
- requirements-dev.txt - requirements-dev.txt
- requirements-plot.txt - requirements-plot.txt
- requirements-pi.txt - requirements-common.txt
# configure the branch prefix the bot is using # configure the branch prefix the bot is using

View File

@ -16,7 +16,7 @@ RUN cd /tmp && /tmp/install_ta-lib.sh && rm -r /tmp/*ta-lib*
ENV LD_LIBRARY_PATH /usr/local/lib ENV LD_LIBRARY_PATH /usr/local/lib
# Install dependencies # Install dependencies
COPY requirements.txt /freqtrade/ COPY requirements.txt requirements-common.txt /freqtrade/
RUN pip install numpy --no-cache-dir \ RUN pip install numpy --no-cache-dir \
&& pip install -r requirements.txt --no-cache-dir && pip install -r requirements.txt --no-cache-dir

View File

@ -27,9 +27,9 @@ RUN wget https://github.com/jjhelmus/berryconda/releases/download/v2.0.0/Berryco
&& rm Berryconda3-2.0.0-Linux-armv7l.sh && rm Berryconda3-2.0.0-Linux-armv7l.sh
# Install dependencies # Install dependencies
COPY requirements-pi.txt /freqtrade/ COPY requirements-common.txt /freqtrade/
RUN ~/berryconda3/bin/conda install -y numpy pandas scipy \ RUN ~/berryconda3/bin/conda install -y numpy pandas scipy \
&& ~/berryconda3/bin/pip install -r requirements-pi.txt --no-cache-dir && ~/berryconda3/bin/pip install -r requirements-common.txt --no-cache-dir
# Install and execute # Install and execute
COPY . /freqtrade/ COPY . /freqtrade/

View File

@ -326,7 +326,7 @@ conda activate freqtrade
conda install scipy pandas numpy conda install scipy pandas numpy
sudo apt install libffi-dev sudo apt install libffi-dev
python3 -m pip install -r requirements-pi.txt python3 -m pip install -r requirements-common.txt
python3 -m pip install -e . python3 -m pip install -e .
``` ```

View File

@ -7,7 +7,7 @@ import os
import sys import sys
from argparse import Namespace from argparse import Namespace
from logging.handlers import RotatingFileHandler from logging.handlers import RotatingFileHandler
from typing import Any, Dict, List, Optional from typing import Any, Callable, Dict, List, Optional
from jsonschema import Draft4Validator, validators from jsonschema import Draft4Validator, validators
from jsonschema.exceptions import ValidationError, best_match from jsonschema.exceptions import ValidationError, best_match
@ -17,7 +17,6 @@ from freqtrade.exchange import is_exchange_supported, supported_exchanges
from freqtrade.misc import deep_merge_dicts from freqtrade.misc import deep_merge_dicts
from freqtrade.state import RunMode from freqtrade.state import RunMode
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -95,14 +94,8 @@ class Configuration(object):
# Load Common configuration # Load Common configuration
config = self._load_common_config(config) config = self._load_common_config(config)
# Load Backtesting # Load Optimize configurations
config = self._load_backtesting_config(config) config = self._load_optimize_config(config)
# Load Edge
config = self._load_edge_config(config)
# Load Hyperopt
config = self._load_hyperopt_config(config)
# Set runmode # Set runmode
if not self.runmode: if not self.runmode:
@ -216,25 +209,41 @@ class Configuration(object):
logger.info(f'Created data directory: {datadir}') logger.info(f'Created data directory: {datadir}')
return datadir return datadir
def _load_backtesting_config(self, config: Dict[str, Any]) -> Dict[str, Any]: # noqa: C901 def _args_to_config(self, config: Dict[str, Any], argname: str,
logstring: str, logfun: Optional[Callable] = None) -> None:
""" """
Extract information for sys.argv and load Backtesting configuration :param config: Configuration dictionary
:param argname: Argumentname in self.args - will be copied to config dict.
:param logstring: Logging String
:param logfun: logfun is applied to the configuration entry before passing
that entry to the log string using .format().
sample: logfun=len (prints the length of the found
configuration instead of the content)
"""
if argname in self.args and getattr(self.args, argname):
config.update({argname: getattr(self.args, argname)})
if logfun:
logger.info(logstring.format(logfun(config[argname])))
else:
logger.info(logstring.format(config[argname]))
def _load_optimize_config(self, config: Dict[str, Any]) -> Dict[str, Any]:
"""
Extract information for sys.argv and load Optimize configuration
:return: configuration as dictionary :return: configuration as dictionary
""" """
# This will override the strategy configuration # This will override the strategy configuration
if 'ticker_interval' in self.args and self.args.ticker_interval: self._args_to_config(config, argname='ticker_interval',
config.update({'ticker_interval': self.args.ticker_interval}) logstring='Parameter -i/--ticker-interval detected ... '
logger.info('Parameter -i/--ticker-interval detected ...') 'Using ticker_interval: {} ...')
logger.info('Using ticker_interval: %s ...', config.get('ticker_interval'))
if 'live' in self.args and self.args.live: self._args_to_config(config, argname='live',
config.update({'live': True}) logstring='Parameter -l/--live detected ...')
logger.info('Parameter -l/--live detected ...')
if 'position_stacking' in self.args and self.args.position_stacking: self._args_to_config(config, argname='position_stacking',
config.update({'position_stacking': True}) logstring='Parameter --enable-position-stacking detected ...')
logger.info('Parameter --enable-position-stacking detected ...')
if 'use_max_market_positions' in self.args and not self.args.use_max_market_positions: if 'use_max_market_positions' in self.args and not self.args.use_max_market_positions:
config.update({'use_max_market_positions': False}) config.update({'use_max_market_positions': False})
@ -247,14 +256,12 @@ class Configuration(object):
else: else:
logger.info('Using max_open_trades: %s ...', config.get('max_open_trades')) logger.info('Using max_open_trades: %s ...', config.get('max_open_trades'))
if 'stake_amount' in self.args and self.args.stake_amount: self._args_to_config(config, argname='stake_amount',
config.update({'stake_amount': self.args.stake_amount}) logstring='Parameter --stake_amount detected, '
logger.info('Parameter --stake_amount detected, overriding stake_amount to: %s ...', 'overriding stake_amount to: {} ...')
config.get('stake_amount'))
if 'timerange' in self.args and self.args.timerange: self._args_to_config(config, argname='timerange',
config.update({'timerange': self.args.timerange}) logstring='Parameter --timerange detected: {} ...')
logger.info('Parameter --timerange detected: %s ...', self.args.timerange)
if 'datadir' in self.args and self.args.datadir: if 'datadir' in self.args and self.args.datadir:
config.update({'datadir': self._create_datadir(config, self.args.datadir)}) config.update({'datadir': self._create_datadir(config, self.args.datadir)})
@ -262,38 +269,22 @@ class Configuration(object):
config.update({'datadir': self._create_datadir(config, None)}) config.update({'datadir': self._create_datadir(config, None)})
logger.info('Using data folder: %s ...', config.get('datadir')) logger.info('Using data folder: %s ...', config.get('datadir'))
if 'refresh_pairs' in self.args and self.args.refresh_pairs: self._args_to_config(config, argname='refresh_pairs',
config.update({'refresh_pairs': True}) logstring='Parameter -r/--refresh-pairs-cached detected ...')
logger.info('Parameter -r/--refresh-pairs-cached detected ...')
if 'strategy_list' in self.args and self.args.strategy_list: self._args_to_config(config, argname='strategy_list',
config.update({'strategy_list': self.args.strategy_list}) logstring='Using strategy list of {} Strategies', logfun=len)
logger.info('Using strategy list of %s Strategies', len(self.args.strategy_list))
if 'ticker_interval' in self.args and self.args.ticker_interval: self._args_to_config(config, argname='ticker_interval',
config.update({'ticker_interval': self.args.ticker_interval}) logstring='Overriding ticker interval with Command line argument')
logger.info('Overriding ticker interval with Command line argument')
if 'export' in self.args and self.args.export: self._args_to_config(config, argname='export',
config.update({'export': self.args.export}) logstring='Parameter --export detected: {} ...')
logger.info('Parameter --export detected: %s ...', self.args.export)
if 'export' in config and 'exportfilename' in self.args and self.args.exportfilename: self._args_to_config(config, argname='exportfilename',
config.update({'exportfilename': self.args.exportfilename}) logstring='Storing backtest results to {} ...')
logger.info('Storing backtest results to %s ...', self.args.exportfilename)
return config
def _load_edge_config(self, config: Dict[str, Any]) -> Dict[str, Any]:
"""
Extract information for sys.argv and load Edge configuration
:return: configuration as dictionary
"""
if 'timerange' in self.args and self.args.timerange:
config.update({'timerange': self.args.timerange})
logger.info('Parameter --timerange detected: %s ...', self.args.timerange)
# Edge section:
if 'stoploss_range' in self.args and self.args.stoploss_range: if 'stoploss_range' in self.args and self.args.stoploss_range:
txt_range = eval(self.args.stoploss_range) txt_range = eval(self.args.stoploss_range)
config['edge'].update({'stoploss_range_min': txt_range[0]}) config['edge'].update({'stoploss_range_min': txt_range[0]})
@ -301,48 +292,26 @@ class Configuration(object):
config['edge'].update({'stoploss_range_step': txt_range[2]}) config['edge'].update({'stoploss_range_step': txt_range[2]})
logger.info('Parameter --stoplosses detected: %s ...', self.args.stoploss_range) logger.info('Parameter --stoplosses detected: %s ...', self.args.stoploss_range)
if 'refresh_pairs' in self.args and self.args.refresh_pairs: # Hyperopt section
config.update({'refresh_pairs': True}) self._args_to_config(config, argname='hyperopt',
logger.info('Parameter -r/--refresh-pairs-cached detected ...') logstring='Using Hyperopt file {}')
return config self._args_to_config(config, argname='epochs',
logstring='Parameter --epochs detected ... '
'Will run Hyperopt with for {} epochs ...'
)
def _load_hyperopt_config(self, config: Dict[str, Any]) -> Dict[str, Any]: self._args_to_config(config, argname='spaces',
""" logstring='Parameter -s/--spaces detected: {}')
Extract information for sys.argv and load Hyperopt configuration
:return: configuration as dictionary
"""
if "hyperopt" in self.args: self._args_to_config(config, argname='print_all',
# Add the hyperopt file to use logstring='Parameter --print-all detected ...')
config.update({'hyperopt': self.args.hyperopt})
if 'epochs' in self.args and self.args.epochs: self._args_to_config(config, argname='hyperopt_jobs',
config.update({'epochs': self.args.epochs}) logstring='Parameter -j/--job-workers detected: {}')
logger.info('Parameter --epochs detected ...')
logger.info('Will run Hyperopt with for %s epochs ...', config.get('epochs'))
if 'spaces' in self.args and self.args.spaces:
config.update({'spaces': self.args.spaces})
logger.info('Parameter -s/--spaces detected: %s', config.get('spaces'))
if 'print_all' in self.args and self.args.print_all:
config.update({'print_all': self.args.print_all})
logger.info('Parameter --print-all detected: %s', config.get('print_all'))
if 'hyperopt_jobs' in self.args and self.args.hyperopt_jobs:
config.update({'hyperopt_jobs': self.args.hyperopt_jobs})
logger.info('Parameter -j/--job-workers detected: %s', config.get('hyperopt_jobs'))
if 'refresh_pairs' in self.args and self.args.refresh_pairs:
config.update({'refresh_pairs': True})
logger.info('Parameter -r/--refresh-pairs-cached detected ...')
if 'hyperopt_random_state' in self.args and self.args.hyperopt_random_state is not None:
config.update({'hyperopt_random_state': self.args.hyperopt_random_state})
logger.info("Parameter --random-state detected: %s",
config.get('hyperopt_random_state'))
self._args_to_config(config, argname='hyperopt_random_state',
logstring='Parameter --random-state detected: {}')
return config return config
def _validate_config_schema(self, conf: Dict[str, Any]) -> Dict[str, Any]: def _validate_config_schema(self, conf: Dict[str, Any]) -> Dict[str, Any]:

View File

@ -23,7 +23,7 @@ from freqtrade.optimize.backtesting import (Backtesting, setup_configuration,
from freqtrade.state import RunMode from freqtrade.state import RunMode
from freqtrade.strategy.default_strategy import DefaultStrategy from freqtrade.strategy.default_strategy import DefaultStrategy
from freqtrade.strategy.interface import SellType from freqtrade.strategy.interface import SellType
from freqtrade.tests.conftest import log_has, patch_exchange from freqtrade.tests.conftest import log_has, log_has_re, patch_exchange
def get_args(args) -> List[str]: def get_args(args) -> List[str]:
@ -190,7 +190,7 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) ->
caplog.record_tuples caplog.record_tuples
) )
assert 'ticker_interval' in config assert 'ticker_interval' in config
assert not log_has('Parameter -i/--ticker-interval detected ...', caplog.record_tuples) assert not log_has_re('Parameter -i/--ticker-interval detected .*', caplog.record_tuples)
assert 'live' not in config assert 'live' not in config
assert not log_has('Parameter -l/--live detected ...', caplog.record_tuples) assert not log_has('Parameter -l/--live detected ...', caplog.record_tuples)
@ -242,11 +242,8 @@ def test_setup_bt_configuration_with_arguments(mocker, default_conf, caplog) ->
caplog.record_tuples caplog.record_tuples
) )
assert 'ticker_interval' in config assert 'ticker_interval' in config
assert log_has('Parameter -i/--ticker-interval detected ...', caplog.record_tuples) assert log_has('Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...',
assert log_has( caplog.record_tuples)
'Using ticker_interval: 1m ...',
caplog.record_tuples
)
assert 'live' in config assert 'live' in config
assert log_has('Parameter -l/--live detected ...', caplog.record_tuples) assert log_has('Parameter -l/--live detected ...', caplog.record_tuples)
@ -853,8 +850,7 @@ def test_backtest_start_live(default_conf, mocker, caplog):
start(args) start(args)
# check the logs, that will contain the backtest result # check the logs, that will contain the backtest result
exists = [ exists = [
'Parameter -i/--ticker-interval detected ...', 'Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...',
'Using ticker_interval: 1m ...',
'Parameter -l/--live detected ...', 'Parameter -l/--live detected ...',
'Ignoring max_open_trades (--disable-max-market-positions was used) ...', 'Ignoring max_open_trades (--disable-max-market-positions was used) ...',
'Parameter --timerange detected: -100 ...', 'Parameter --timerange detected: -100 ...',
@ -912,8 +908,7 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog):
# check the logs, that will contain the backtest result # check the logs, that will contain the backtest result
exists = [ exists = [
'Parameter -i/--ticker-interval detected ...', 'Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...',
'Using ticker_interval: 1m ...',
'Parameter -l/--live detected ...', 'Parameter -l/--live detected ...',
'Ignoring max_open_trades (--disable-max-market-positions was used) ...', 'Ignoring max_open_trades (--disable-max-market-positions was used) ...',
'Parameter --timerange detected: -100 ...', 'Parameter --timerange detected: -100 ...',

View File

@ -1,14 +1,15 @@
# pragma pylint: disable=missing-docstring, C0103, C0330 # pragma pylint: disable=missing-docstring, C0103, C0330
# pragma pylint: disable=protected-access, too-many-lines, invalid-name, too-many-arguments # pragma pylint: disable=protected-access, too-many-lines, invalid-name, too-many-arguments
from unittest.mock import MagicMock
import json import json
from typing import List from typing import List
from freqtrade.edge import PairInfo from unittest.mock import MagicMock
from freqtrade.arguments import Arguments from freqtrade.arguments import Arguments
from freqtrade.optimize.edge_cli import (EdgeCli, setup_configuration, start) from freqtrade.edge import PairInfo
from freqtrade.optimize.edge_cli import EdgeCli, setup_configuration, start
from freqtrade.state import RunMode from freqtrade.state import RunMode
from freqtrade.tests.conftest import log_has, patch_exchange from freqtrade.tests.conftest import log_has, log_has_re, patch_exchange
def get_args(args) -> List[str]: def get_args(args) -> List[str]:
@ -40,7 +41,7 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) ->
caplog.record_tuples caplog.record_tuples
) )
assert 'ticker_interval' in config assert 'ticker_interval' in config
assert not log_has('Parameter -i/--ticker-interval detected ...', caplog.record_tuples) assert not log_has_re('Parameter -i/--ticker-interval detected .*', caplog.record_tuples)
assert 'refresh_pairs' not in config assert 'refresh_pairs' not in config
assert not log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog.record_tuples) assert not log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog.record_tuples)
@ -79,11 +80,8 @@ def test_setup_edge_configuration_with_arguments(mocker, edge_conf, caplog) -> N
caplog.record_tuples caplog.record_tuples
) )
assert 'ticker_interval' in config assert 'ticker_interval' in config
assert log_has('Parameter -i/--ticker-interval detected ...', caplog.record_tuples) assert log_has('Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...',
assert log_has( caplog.record_tuples)
'Using ticker_interval: 1m ...',
caplog.record_tuples
)
assert 'refresh_pairs' in config assert 'refresh_pairs' in config
assert log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog.record_tuples) assert log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog.record_tuples)

View File

@ -1,7 +1,7 @@
# pragma pylint: disable=missing-docstring,W0212,C0103 # pragma pylint: disable=missing-docstring,W0212,C0103
from datetime import datetime
import json import json
import os import os
from datetime import datetime
from unittest.mock import MagicMock from unittest.mock import MagicMock
import pandas as pd import pandas as pd
@ -10,11 +10,11 @@ import pytest
from freqtrade import DependencyException from freqtrade import DependencyException
from freqtrade.data.converter import parse_ticker_dataframe from freqtrade.data.converter import parse_ticker_dataframe
from freqtrade.data.history import load_tickerdata_file from freqtrade.data.history import load_tickerdata_file
from freqtrade.optimize.hyperopt import Hyperopt, start, setup_configuration
from freqtrade.optimize.default_hyperopt import DefaultHyperOpts from freqtrade.optimize.default_hyperopt import DefaultHyperOpts
from freqtrade.resolvers import StrategyResolver, HyperOptResolver from freqtrade.optimize.hyperopt import Hyperopt, setup_configuration, start
from freqtrade.resolvers import HyperOptResolver
from freqtrade.state import RunMode from freqtrade.state import RunMode
from freqtrade.tests.conftest import log_has, patch_exchange from freqtrade.tests.conftest import log_has, log_has_re, patch_exchange
from freqtrade.tests.optimize.test_backtesting import get_args from freqtrade.tests.optimize.test_backtesting import get_args
@ -64,7 +64,7 @@ def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, ca
caplog.record_tuples caplog.record_tuples
) )
assert 'ticker_interval' in config assert 'ticker_interval' in config
assert not log_has('Parameter -i/--ticker-interval detected ...', caplog.record_tuples) assert not log_has_re('Parameter -i/--ticker-interval detected .*', caplog.record_tuples)
assert 'live' not in config assert 'live' not in config
assert not log_has('Parameter -l/--live detected ...', caplog.record_tuples) assert not log_has('Parameter -l/--live detected ...', caplog.record_tuples)
@ -114,11 +114,8 @@ def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplo
caplog.record_tuples caplog.record_tuples
) )
assert 'ticker_interval' in config assert 'ticker_interval' in config
assert log_has('Parameter -i/--ticker-interval detected ...', caplog.record_tuples) assert log_has('Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...',
assert log_has( caplog.record_tuples)
'Using ticker_interval: 1m ...',
caplog.record_tuples
)
assert 'position_stacking' in config assert 'position_stacking' in config
assert log_has('Parameter --enable-position-stacking detected ...', caplog.record_tuples) assert log_has('Parameter --enable-position-stacking detected ...', caplog.record_tuples)
@ -137,7 +134,8 @@ def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplo
) )
assert 'epochs' in config assert 'epochs' in config
assert log_has('Parameter --epochs detected ...', caplog.record_tuples) assert log_has('Parameter --epochs detected ... Will run Hyperopt with for 1000 epochs ...',
caplog.record_tuples)
assert 'spaces' in config assert 'spaces' in config
assert log_has( assert log_has(
@ -145,7 +143,7 @@ def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplo
caplog.record_tuples caplog.record_tuples
) )
assert 'print_all' in config assert 'print_all' in config
assert log_has('Parameter --print-all detected: True', caplog.record_tuples) assert log_has('Parameter --print-all detected ...', caplog.record_tuples)
def test_hyperoptresolver(mocker, default_conf, caplog) -> None: def test_hyperoptresolver(mocker, default_conf, caplog) -> None:
@ -185,7 +183,6 @@ def test_start(mocker, default_conf, caplog) -> None:
'--epochs', '5' '--epochs', '5'
] ]
args = get_args(args) args = get_args(args)
StrategyResolver({'strategy': 'DefaultStrategy'})
start(args) start(args)
import pprint import pprint
@ -214,7 +211,6 @@ def test_start_failure(mocker, default_conf, caplog) -> None:
'--epochs', '5' '--epochs', '5'
] ]
args = get_args(args) args = get_args(args)
StrategyResolver({'strategy': 'DefaultStrategy'})
with pytest.raises(DependencyException): with pytest.raises(DependencyException):
start(args) start(args)
assert log_has( assert log_has(
@ -224,7 +220,6 @@ def test_start_failure(mocker, default_conf, caplog) -> None:
def test_loss_calculation_prefer_correct_trade_count(hyperopt) -> None: def test_loss_calculation_prefer_correct_trade_count(hyperopt) -> None:
StrategyResolver({'strategy': 'DefaultStrategy'})
correct = hyperopt.calculate_loss(1, hyperopt.target_trades, 20) correct = hyperopt.calculate_loss(1, hyperopt.target_trades, 20)
over = hyperopt.calculate_loss(1, hyperopt.target_trades + 100, 20) over = hyperopt.calculate_loss(1, hyperopt.target_trades + 100, 20)

View File

@ -360,11 +360,8 @@ def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> Non
caplog.record_tuples caplog.record_tuples
) )
assert 'ticker_interval' in config assert 'ticker_interval' in config
assert log_has('Parameter -i/--ticker-interval detected ...', caplog.record_tuples) assert log_has('Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...',
assert log_has( caplog.record_tuples)
'Using ticker_interval: 1m ...',
caplog.record_tuples
)
assert 'live' in config assert 'live' in config
assert log_has('Parameter -l/--live detected ...', caplog.record_tuples) assert log_has('Parameter -l/--live detected ...', caplog.record_tuples)
@ -425,11 +422,8 @@ def test_setup_configuration_with_stratlist(mocker, default_conf, caplog) -> Non
caplog.record_tuples caplog.record_tuples
) )
assert 'ticker_interval' in config assert 'ticker_interval' in config
assert log_has('Parameter -i/--ticker-interval detected ...', caplog.record_tuples) assert log_has('Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...',
assert log_has( caplog.record_tuples)
'Using ticker_interval: 1m ...',
caplog.record_tuples
)
assert 'strategy_list' in config assert 'strategy_list' in config
assert log_has('Using strategy list of 2 Strategies', caplog.record_tuples) assert log_has('Using strategy list of 2 Strategies', caplog.record_tuples)
@ -463,8 +457,8 @@ def test_hyperopt_with_arguments(mocker, default_conf, caplog) -> None:
assert 'epochs' in config assert 'epochs' in config
assert int(config['epochs']) == 10 assert int(config['epochs']) == 10
assert log_has('Parameter --epochs detected ...', caplog.record_tuples) assert log_has('Parameter --epochs detected ... Will run Hyperopt with for 10 epochs ...',
assert log_has('Will run Hyperopt with for 10 epochs ...', caplog.record_tuples) caplog.record_tuples)
assert 'spaces' in config assert 'spaces' in config
assert config['spaces'] == ['all'] assert config['spaces'] == ['all']

View File

@ -7,10 +7,11 @@ import pytest
from freqtrade import OperationalException from freqtrade import OperationalException
from freqtrade.arguments import Arguments from freqtrade.arguments import Arguments
from freqtrade.worker import Worker from freqtrade.freqtradebot import FreqtradeBot
from freqtrade.main import main from freqtrade.main import main
from freqtrade.state import State from freqtrade.state import State
from freqtrade.tests.conftest import log_has, patch_exchange from freqtrade.tests.conftest import log_has, patch_exchange
from freqtrade.worker import Worker
def test_parse_args_backtesting(mocker) -> None: def test_parse_args_backtesting(mocker) -> None:
@ -107,24 +108,30 @@ def test_main_operational_exception(mocker, default_conf, caplog) -> None:
def test_main_reload_conf(mocker, default_conf, caplog) -> None: def test_main_reload_conf(mocker, default_conf, caplog) -> None:
patch_exchange(mocker) patch_exchange(mocker)
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.cleanup', MagicMock()) mocker.patch('freqtrade.freqtradebot.FreqtradeBot.cleanup', MagicMock())
mocker.patch('freqtrade.worker.Worker._worker', MagicMock(return_value=State.RELOAD_CONF)) # Simulate Running, reload, running workflow
worker_mock = MagicMock(side_effect=[State.RUNNING,
State.RELOAD_CONF,
State.RUNNING,
OperationalException("Oh snap!")])
mocker.patch('freqtrade.worker.Worker._worker', worker_mock)
mocker.patch( mocker.patch(
'freqtrade.configuration.Configuration._load_config_file', 'freqtrade.configuration.Configuration._load_config_file',
lambda *args, **kwargs: default_conf lambda *args, **kwargs: default_conf
) )
reconfigure_mock = mocker.patch('freqtrade.main.Worker._reconfigure', MagicMock())
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock()) mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock())
# Raise exception as side effect to avoid endless loop args = Arguments(['-c', 'config.json.example'], '').get_parsed_arg()
reconfigure_mock = mocker.patch( worker = Worker(args=args, config=default_conf)
'freqtrade.main.Worker._reconfigure', MagicMock(side_effect=Exception)
)
with pytest.raises(SystemExit): with pytest.raises(SystemExit):
main(['-c', 'config.json.example']) main(['-c', 'config.json.example'])
assert reconfigure_mock.call_count == 1
assert log_has('Using config: config.json.example ...', caplog.record_tuples) assert log_has('Using config: config.json.example ...', caplog.record_tuples)
assert worker_mock.call_count == 4
assert reconfigure_mock.call_count == 1
assert isinstance(worker.freqtrade, FreqtradeBot)
def test_reconfigure(mocker, default_conf) -> None: def test_reconfigure(mocker, default_conf) -> None:

View File

@ -39,7 +39,7 @@ class Worker(object):
logger.debug("sd_notify: READY=1") logger.debug("sd_notify: READY=1")
self._sd_notify.notify("READY=1") self._sd_notify.notify("READY=1")
def _init(self, reconfig: bool): def _init(self, reconfig: bool) -> None:
""" """
Also called from the _reconfigure() method (with reconfig=True). Also called from the _reconfigure() method (with reconfig=True).
""" """
@ -63,17 +63,17 @@ class Worker(object):
return self.freqtrade.state return self.freqtrade.state
@state.setter @state.setter
def state(self, value: State): def state(self, value: State) -> None:
self.freqtrade.state = value self.freqtrade.state = value
def run(self): def run(self) -> None:
state = None state = None
while True: while True:
state = self._worker(old_state=state) state = self._worker(old_state=state)
if state == State.RELOAD_CONF: if state == State.RELOAD_CONF:
self.freqtrade = self._reconfigure() self._reconfigure()
def _worker(self, old_state: State, throttle_secs: Optional[float] = None) -> State: def _worker(self, old_state: Optional[State], throttle_secs: Optional[float] = None) -> State:
""" """
Trading routine that must be run at each loop Trading routine that must be run at each loop
:param old_state: the previous service state from the previous call :param old_state: the previous service state from the previous call
@ -148,7 +148,7 @@ class Worker(object):
# state_changed = True # state_changed = True
return state_changed return state_changed
def _reconfigure(self): def _reconfigure(self) -> None:
""" """
Cleans up current freqtradebot instance, reloads the configuration and Cleans up current freqtradebot instance, reloads the configuration and
replaces it with the new instance replaces it with the new instance
@ -174,7 +174,7 @@ class Worker(object):
logger.debug("sd_notify: READY=1") logger.debug("sd_notify: READY=1")
self._sd_notify.notify("READY=1") self._sd_notify.notify("READY=1")
def exit(self): def exit(self) -> None:
# Tell systemd that we are exiting now # Tell systemd that we are exiting now
if self._sd_notify: if self._sd_notify:
logger.debug("sd_notify: STOPPING=1") logger.debug("sd_notify: STOPPING=1")

View File

@ -1,6 +1,6 @@
# requirements without requirements installable via conda # requirements without requirements installable via conda
# mainly used for Raspberry pi installs # mainly used for Raspberry pi installs
ccxt==1.18.489 ccxt==1.18.496
SQLAlchemy==1.3.3 SQLAlchemy==1.3.3
python-telegram-bot==11.1.0 python-telegram-bot==11.1.0
arrow==0.13.1 arrow==0.13.1

View File

@ -1,5 +1,5 @@
# Load common requirements # Load common requirements
-r requirements-pi.txt -r requirements-common.txt
numpy==1.16.3 numpy==1.16.3
pandas==0.24.2 pandas==0.24.2