2017-11-18 07:45:01 +00:00
|
|
|
# pragma pylint: disable=missing-docstring,C0103
|
2017-11-20 18:37:25 +00:00
|
|
|
import json
|
|
|
|
import time
|
2018-01-06 07:27:44 +00:00
|
|
|
import argparse
|
2017-11-20 18:37:25 +00:00
|
|
|
from copy import deepcopy
|
2017-11-17 16:19:00 +00:00
|
|
|
from unittest.mock import MagicMock
|
2017-11-11 15:47:19 +00:00
|
|
|
|
2017-11-17 16:19:00 +00:00
|
|
|
import pytest
|
2017-11-20 18:37:25 +00:00
|
|
|
from jsonschema import ValidationError
|
2017-11-17 16:19:00 +00:00
|
|
|
|
2018-01-06 07:27:44 +00:00
|
|
|
from freqtrade.misc import throttle, parse_args, load_config,\
|
|
|
|
parse_args_common
|
2017-11-11 15:47:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_throttle():
|
|
|
|
|
|
|
|
def func():
|
|
|
|
return 42
|
|
|
|
|
|
|
|
start = time.time()
|
2017-12-30 14:55:49 +00:00
|
|
|
result = throttle(func, min_secs=0.1)
|
2017-11-11 15:47:19 +00:00
|
|
|
end = time.time()
|
|
|
|
|
|
|
|
assert result == 42
|
|
|
|
assert end - start > 0.1
|
|
|
|
|
2017-12-30 14:55:49 +00:00
|
|
|
result = throttle(func, min_secs=-1)
|
2017-11-11 15:47:19 +00:00
|
|
|
assert result == 42
|
2017-11-17 16:19:00 +00:00
|
|
|
|
2017-12-30 14:55:49 +00:00
|
|
|
|
2017-12-30 14:43:22 +00:00
|
|
|
def test_throttle_with_assets():
|
|
|
|
|
2017-12-30 14:55:49 +00:00
|
|
|
def func(nb_assets=-1):
|
2017-12-30 14:43:22 +00:00
|
|
|
return nb_assets
|
|
|
|
|
2017-12-30 14:55:49 +00:00
|
|
|
result = throttle(func, min_secs=0.1, nb_assets=666)
|
2017-12-30 14:43:22 +00:00
|
|
|
assert result == 666
|
|
|
|
|
2017-12-30 14:55:49 +00:00
|
|
|
result = throttle(func, min_secs=0.1)
|
2017-12-30 14:43:22 +00:00
|
|
|
assert result == -1
|
|
|
|
|
|
|
|
|
2018-01-06 07:27:44 +00:00
|
|
|
# Parse common command-line-arguments
|
|
|
|
# used for all tools
|
|
|
|
|
|
|
|
|
|
|
|
def test_parse_args_none():
|
|
|
|
args = parse_args_common([], '')
|
|
|
|
assert isinstance(args, argparse.ArgumentParser)
|
|
|
|
|
|
|
|
|
2017-11-17 16:19:00 +00:00
|
|
|
def test_parse_args_defaults():
|
2018-01-06 06:39:05 +00:00
|
|
|
args = parse_args([], '')
|
2017-11-17 16:19:00 +00:00
|
|
|
assert args.config == 'config.json'
|
2017-12-11 07:56:03 +00:00
|
|
|
assert args.dynamic_whitelist is None
|
2017-11-17 16:19:00 +00:00
|
|
|
assert args.loglevel == 20
|
|
|
|
|
|
|
|
|
|
|
|
def test_parse_args_config():
|
2018-01-06 06:39:05 +00:00
|
|
|
args = parse_args(['-c', '/dev/null'], '')
|
2017-11-17 16:19:00 +00:00
|
|
|
assert args.config == '/dev/null'
|
|
|
|
|
2018-01-06 06:39:05 +00:00
|
|
|
args = parse_args(['--config', '/dev/null'], '')
|
2017-11-17 16:19:00 +00:00
|
|
|
assert args.config == '/dev/null'
|
|
|
|
|
|
|
|
|
|
|
|
def test_parse_args_verbose():
|
2018-01-06 06:39:05 +00:00
|
|
|
args = parse_args(['-v'], '')
|
2017-11-17 16:19:00 +00:00
|
|
|
assert args.loglevel == 10
|
|
|
|
|
2018-01-06 07:27:44 +00:00
|
|
|
args = parse_args(['--verbose'], '')
|
|
|
|
assert args.loglevel == 10
|
|
|
|
|
|
|
|
|
|
|
|
def test_parse_args_version():
|
|
|
|
with pytest.raises(SystemExit, match=r'0'):
|
|
|
|
parse_args(['--version'], '')
|
|
|
|
|
|
|
|
|
|
|
|
def test_parse_args_invalid():
|
|
|
|
with pytest.raises(SystemExit, match=r'2'):
|
|
|
|
parse_args(['-c'], '')
|
|
|
|
|
|
|
|
|
|
|
|
# Parse command-line-arguments
|
|
|
|
# used for main, backtesting and hyperopt
|
|
|
|
|
2017-11-17 16:19:00 +00:00
|
|
|
|
|
|
|
def test_parse_args_dynamic_whitelist():
|
2018-01-06 06:39:05 +00:00
|
|
|
args = parse_args(['--dynamic-whitelist'], '')
|
2017-12-11 07:56:03 +00:00
|
|
|
assert args.dynamic_whitelist is 20
|
|
|
|
|
2017-12-16 02:39:47 +00:00
|
|
|
|
2017-12-11 07:56:03 +00:00
|
|
|
def test_parse_args_dynamic_whitelist_10():
|
2018-01-06 06:39:05 +00:00
|
|
|
args = parse_args(['--dynamic-whitelist', '10'], '')
|
2017-12-11 07:56:03 +00:00
|
|
|
assert args.dynamic_whitelist is 10
|
|
|
|
|
2017-12-16 02:39:47 +00:00
|
|
|
|
2017-12-11 07:56:03 +00:00
|
|
|
def test_parse_args_dynamic_whitelist_invalid_values():
|
|
|
|
with pytest.raises(SystemExit, match=r'2'):
|
2018-01-06 06:39:05 +00:00
|
|
|
parse_args(['--dynamic-whitelist', 'abc'], '')
|
2017-11-17 16:19:00 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_parse_args_backtesting(mocker):
|
2017-12-30 14:55:49 +00:00
|
|
|
backtesting_mock = mocker.patch(
|
|
|
|
'freqtrade.optimize.backtesting.start', MagicMock())
|
2018-01-06 06:39:05 +00:00
|
|
|
args = parse_args(['backtesting'], '')
|
2017-11-17 16:19:00 +00:00
|
|
|
assert args is None
|
|
|
|
assert backtesting_mock.call_count == 1
|
|
|
|
|
|
|
|
call_args = backtesting_mock.call_args[0][0]
|
|
|
|
assert call_args.config == 'config.json'
|
|
|
|
assert call_args.live is False
|
|
|
|
assert call_args.loglevel == 20
|
|
|
|
assert call_args.subparser == 'backtesting'
|
|
|
|
assert call_args.func is not None
|
|
|
|
assert call_args.ticker_interval == 5
|
|
|
|
|
|
|
|
|
|
|
|
def test_parse_args_backtesting_invalid():
|
|
|
|
with pytest.raises(SystemExit, match=r'2'):
|
2018-01-06 06:39:05 +00:00
|
|
|
parse_args(['backtesting --ticker-interval'], '')
|
2017-11-17 16:19:00 +00:00
|
|
|
|
|
|
|
with pytest.raises(SystemExit, match=r'2'):
|
2018-01-06 06:39:05 +00:00
|
|
|
parse_args(['backtesting --ticker-interval', 'abc'], '')
|
2017-11-17 16:19:00 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_parse_args_backtesting_custom(mocker):
|
2017-12-30 14:55:49 +00:00
|
|
|
backtesting_mock = mocker.patch(
|
|
|
|
'freqtrade.optimize.backtesting.start', MagicMock())
|
2017-12-16 14:42:28 +00:00
|
|
|
args = parse_args([
|
|
|
|
'-c', 'test_conf.json',
|
|
|
|
'backtesting',
|
|
|
|
'--live',
|
|
|
|
'--ticker-interval', '1',
|
2018-01-06 06:39:05 +00:00
|
|
|
'--refresh-pairs-cached'], '')
|
2017-11-17 16:19:00 +00:00
|
|
|
assert args is None
|
|
|
|
assert backtesting_mock.call_count == 1
|
|
|
|
|
|
|
|
call_args = backtesting_mock.call_args[0][0]
|
|
|
|
assert call_args.config == 'test_conf.json'
|
|
|
|
assert call_args.live is True
|
|
|
|
assert call_args.loglevel == 20
|
|
|
|
assert call_args.subparser == 'backtesting'
|
|
|
|
assert call_args.func is not None
|
|
|
|
assert call_args.ticker_interval == 1
|
2017-12-16 14:42:28 +00:00
|
|
|
assert call_args.refresh_pairs is True
|
2017-11-17 16:19:00 +00:00
|
|
|
|
|
|
|
|
2017-11-25 00:04:11 +00:00
|
|
|
def test_parse_args_hyperopt(mocker):
|
2017-12-30 14:55:49 +00:00
|
|
|
hyperopt_mock = mocker.patch(
|
|
|
|
'freqtrade.optimize.hyperopt.start', MagicMock())
|
2018-01-06 06:39:05 +00:00
|
|
|
args = parse_args(['hyperopt'], '')
|
2017-11-25 00:04:11 +00:00
|
|
|
assert args is None
|
|
|
|
assert hyperopt_mock.call_count == 1
|
|
|
|
|
|
|
|
call_args = hyperopt_mock.call_args[0][0]
|
|
|
|
assert call_args.config == 'config.json'
|
|
|
|
assert call_args.loglevel == 20
|
|
|
|
assert call_args.subparser == 'hyperopt'
|
2017-11-25 00:12:44 +00:00
|
|
|
assert call_args.func is not None
|
|
|
|
|
|
|
|
|
|
|
|
def test_parse_args_hyperopt_custom(mocker):
|
2017-12-30 14:55:49 +00:00
|
|
|
hyperopt_mock = mocker.patch(
|
|
|
|
'freqtrade.optimize.hyperopt.start', MagicMock())
|
2018-01-06 06:39:05 +00:00
|
|
|
args = parse_args(['-c', 'test_conf.json', 'hyperopt', '--epochs', '20'], '')
|
2017-11-25 00:12:44 +00:00
|
|
|
assert args is None
|
|
|
|
assert hyperopt_mock.call_count == 1
|
|
|
|
|
|
|
|
call_args = hyperopt_mock.call_args[0][0]
|
|
|
|
assert call_args.config == 'test_conf.json'
|
|
|
|
assert call_args.epochs == 20
|
|
|
|
assert call_args.loglevel == 20
|
|
|
|
assert call_args.subparser == 'hyperopt'
|
2017-11-25 00:04:11 +00:00
|
|
|
assert call_args.func is not None
|
|
|
|
|
|
|
|
|
2017-11-20 18:37:25 +00:00
|
|
|
def test_load_config(default_conf, mocker):
|
|
|
|
file_mock = mocker.patch('freqtrade.misc.open', mocker.mock_open(
|
|
|
|
read_data=json.dumps(default_conf)
|
|
|
|
))
|
|
|
|
validated_conf = load_config('somefile')
|
|
|
|
assert file_mock.call_count == 1
|
|
|
|
assert validated_conf.items() >= default_conf.items()
|
|
|
|
|
|
|
|
|
|
|
|
def test_load_config_invalid_pair(default_conf, mocker):
|
|
|
|
conf = deepcopy(default_conf)
|
|
|
|
conf['exchange']['pair_whitelist'].append('BTC-ETH')
|
2017-12-30 14:55:49 +00:00
|
|
|
mocker.patch(
|
|
|
|
'freqtrade.misc.open',
|
|
|
|
mocker.mock_open(
|
|
|
|
read_data=json.dumps(conf)))
|
2017-11-20 18:37:25 +00:00
|
|
|
with pytest.raises(ValidationError, match=r'.*does not match.*'):
|
|
|
|
load_config('somefile')
|
|
|
|
|
|
|
|
|
|
|
|
def test_load_config_missing_attributes(default_conf, mocker):
|
|
|
|
conf = deepcopy(default_conf)
|
|
|
|
conf.pop('exchange')
|
2017-12-30 14:55:49 +00:00
|
|
|
mocker.patch(
|
|
|
|
'freqtrade.misc.open',
|
|
|
|
mocker.mock_open(
|
|
|
|
read_data=json.dumps(conf)))
|
2017-11-20 18:37:25 +00:00
|
|
|
with pytest.raises(ValidationError, match=r'.*\'exchange\' is a required property.*'):
|
|
|
|
load_config('somefile')
|