Merge pull request #324 from kryofly/parse-common

Parsing: common options, reduce function scope
This commit is contained in:
Gérald LONLAS 2018-01-06 15:11:30 -08:00 committed by GitHub
commit 2432c9f290
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 132 additions and 100 deletions

View File

@ -398,14 +398,18 @@ def cleanup() -> None:
exit(0) exit(0)
def main() -> None: def main(sysargv=sys.argv[1:]) -> None:
""" """
Loads and validates the config and handles the main loop Loads and validates the config and handles the main loop
:return: None :return: None
""" """
global _CONF global _CONF
args = parse_args(sys.argv[1:]) args = parse_args(sysargv,
if not args: 'Simple High Frequency Trading Bot for crypto currencies')
# A subcommand has been issued
if hasattr(args, 'func'):
args.func(args)
exit(0) exit(0)
# Initialize logger # Initialize logger

View File

@ -81,21 +81,12 @@ def throttle(func: Callable[..., Any], min_secs: float, *args, **kwargs) -> Any:
return result return result
def parse_args(args: List[str]): def parse_args_common(args: List[str], description: str):
""" """
Parses given arguments and returns an argparse Namespace instance. Parses given common arguments and returns them as a parsed object.
Returns None if a sub command has been selected and executed.
""" """
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description='Simple High Frequency Trading Bot for crypto currencies' description=description
)
parser.add_argument(
'-c', '--config',
help='specify configuration file (default: config.json)',
dest='config',
default='config.json',
type=str,
metavar='PATH',
) )
parser.add_argument( parser.add_argument(
'-v', '--verbose', '-v', '--verbose',
@ -110,6 +101,30 @@ def parse_args(args: List[str]):
action='version', action='version',
version='%(prog)s {}'.format(__version__), version='%(prog)s {}'.format(__version__),
) )
parser.add_argument(
'-c', '--config',
help='specify configuration file (default: config.json)',
dest='config',
default='config.json',
type=str,
metavar='PATH',
)
return parser
def parse_args(args: List[str], description: str):
"""
Parses given arguments and returns an argparse Namespace instance.
Returns None if a sub command has been selected and executed.
"""
parser = parse_args_common(args, description)
parser.add_argument(
'--dry-run-db',
help='Force dry run to use a local DB "tradesv3.dry_run.sqlite" instead of memory DB. Work only if dry_run is \
enabled.', # noqa
action='store_true',
dest='dry_run_db',
)
parser.add_argument( parser.add_argument(
'--dynamic-whitelist', '--dynamic-whitelist',
help='dynamically generate and update whitelist based on 24h BaseVolume (Default 20 currencies)', # noqa help='dynamically generate and update whitelist based on 24h BaseVolume (Default 20 currencies)', # noqa
@ -119,22 +134,9 @@ def parse_args(args: List[str]):
metavar='INT', metavar='INT',
nargs='?', nargs='?',
) )
parser.add_argument(
'--dry-run-db',
help='Force dry run to use a local DB "tradesv3.dry_run.sqlite" instead of memory DB. Work only if dry_run is \
enabled.', # noqa
action='store_true',
dest='dry_run_db',
)
build_subcommands(parser) build_subcommands(parser)
parsed_args = parser.parse_args(args) return parser.parse_args(args)
# No subcommand as been selected
if not hasattr(parsed_args, 'func'):
return parsed_args
parsed_args.func(parsed_args)
return None
def build_subcommands(parser: argparse.ArgumentParser) -> None: def build_subcommands(parser: argparse.ArgumentParser) -> None:

View File

@ -15,6 +15,39 @@ from freqtrade.main import create_trade, handle_trade, init, \
get_target_bid, _process, execute_sell, check_handle_timedout get_target_bid, _process, execute_sell, check_handle_timedout
from freqtrade.misc import get_state, State from freqtrade.misc import get_state, State
from freqtrade.persistence import Trade from freqtrade.persistence import Trade
import freqtrade.main as main
# Test that main() can start backtesting or hyperopt.
# and also ensure we can pass some specific arguments
# argument parsing is done in test_misc.py
def test_parse_args_backtesting(mocker):
backtesting_mock = mocker.patch(
'freqtrade.optimize.backtesting.start', MagicMock())
with pytest.raises(SystemExit, match=r'0'):
main.main(['backtesting'])
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_main_start_hyperopt(mocker):
hyperopt_mock = mocker.patch(
'freqtrade.optimize.hyperopt.start', MagicMock())
with pytest.raises(SystemExit, match=r'0'):
main.main(['hyperopt'])
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'
assert call_args.func is not None
def test_process_trade_creation(default_conf, ticker, limit_buy_order, health, mocker): def test_process_trade_creation(default_conf, ticker, limit_buy_order, health, mocker):

View File

@ -1,13 +1,14 @@
# pragma pylint: disable=missing-docstring,C0103 # pragma pylint: disable=missing-docstring,C0103
import json import json
import time import time
import argparse
from copy import deepcopy from copy import deepcopy
from unittest.mock import MagicMock
import pytest import pytest
from jsonschema import ValidationError from jsonschema import ValidationError
from freqtrade.misc import throttle, parse_args, load_config from freqtrade.misc import throttle, parse_args, load_config,\
parse_args_common
def test_throttle(): def test_throttle():
@ -38,89 +39,83 @@ def test_throttle_with_assets():
assert result == -1 assert result == -1
# Parse common command-line-arguments
# used for all tools
def test_parse_args_none():
args = parse_args_common([], '')
assert isinstance(args, argparse.ArgumentParser)
def test_parse_args_defaults(): def test_parse_args_defaults():
args = parse_args([]) args = parse_args([], '')
assert args is not None
assert args.config == 'config.json' assert args.config == 'config.json'
assert args.dynamic_whitelist is None assert args.dynamic_whitelist is None
assert args.loglevel == 20 assert args.loglevel == 20
def test_parse_args_invalid():
with pytest.raises(SystemExit, match=r'2'):
parse_args(['-c'])
def test_parse_args_config(): def test_parse_args_config():
args = parse_args(['-c', '/dev/null']) args = parse_args(['-c', '/dev/null'], '')
assert args is not None
assert args.config == '/dev/null' assert args.config == '/dev/null'
args = parse_args(['--config', '/dev/null']) args = parse_args(['--config', '/dev/null'], '')
assert args is not None
assert args.config == '/dev/null' assert args.config == '/dev/null'
def test_parse_args_verbose(): def test_parse_args_verbose():
args = parse_args(['-v']) args = parse_args(['-v'], '')
assert args is not None assert args.loglevel == 10
args = parse_args(['--verbose'], '')
assert args.loglevel == 10 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
def test_parse_args_dynamic_whitelist(): def test_parse_args_dynamic_whitelist():
args = parse_args(['--dynamic-whitelist']) args = parse_args(['--dynamic-whitelist'], '')
assert args is not None
assert args.dynamic_whitelist is 20 assert args.dynamic_whitelist is 20
def test_parse_args_dynamic_whitelist_10(): def test_parse_args_dynamic_whitelist_10():
args = parse_args(['--dynamic-whitelist', '10']) args = parse_args(['--dynamic-whitelist', '10'], '')
assert args is not None
assert args.dynamic_whitelist is 10 assert args.dynamic_whitelist is 10
def test_parse_args_dynamic_whitelist_invalid_values(): def test_parse_args_dynamic_whitelist_invalid_values():
with pytest.raises(SystemExit, match=r'2'): with pytest.raises(SystemExit, match=r'2'):
parse_args(['--dynamic-whitelist', 'abc']) parse_args(['--dynamic-whitelist', 'abc'], '')
def test_parse_args_backtesting(mocker):
backtesting_mock = mocker.patch(
'freqtrade.optimize.backtesting.start', MagicMock())
args = parse_args(['backtesting'])
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(): def test_parse_args_backtesting_invalid():
with pytest.raises(SystemExit, match=r'2'): with pytest.raises(SystemExit, match=r'2'):
parse_args(['backtesting --ticker-interval']) parse_args(['backtesting --ticker-interval'], '')
with pytest.raises(SystemExit, match=r'2'): with pytest.raises(SystemExit, match=r'2'):
parse_args(['backtesting --ticker-interval', 'abc']) parse_args(['backtesting --ticker-interval', 'abc'], '')
def test_parse_args_backtesting_custom(mocker): def test_parse_args_backtesting_custom():
backtesting_mock = mocker.patch( args = [
'freqtrade.optimize.backtesting.start', MagicMock())
args = parse_args([
'-c', 'test_conf.json', '-c', 'test_conf.json',
'backtesting', 'backtesting',
'--live', '--live',
'--ticker-interval', '1', '--ticker-interval', '1',
'--refresh-pairs-cached']) '--refresh-pairs-cached']
assert args is None call_args = parse_args(args, '')
assert backtesting_mock.call_count == 1
call_args = backtesting_mock.call_args[0][0]
assert call_args.config == 'test_conf.json' assert call_args.config == 'test_conf.json'
assert call_args.live is True assert call_args.live is True
assert call_args.loglevel == 20 assert call_args.loglevel == 20
@ -130,28 +125,9 @@ def test_parse_args_backtesting_custom(mocker):
assert call_args.refresh_pairs is True assert call_args.refresh_pairs is True
def test_parse_args_hyperopt(mocker):
hyperopt_mock = mocker.patch(
'freqtrade.optimize.hyperopt.start', MagicMock())
args = parse_args(['hyperopt'])
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'
assert call_args.func is not None
def test_parse_args_hyperopt_custom(mocker): def test_parse_args_hyperopt_custom(mocker):
hyperopt_mock = mocker.patch( args = ['-c', 'test_conf.json', 'hyperopt', '--epochs', '20']
'freqtrade.optimize.hyperopt.start', MagicMock()) call_args = parse_args(args, '')
args = parse_args(['-c', 'test_conf.json', 'hyperopt', '--epochs', '20'])
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.config == 'test_conf.json'
assert call_args.epochs == 20 assert call_args.epochs == 20
assert call_args.loglevel == 20 assert call_args.loglevel == 20

View File

@ -1,17 +1,33 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import sys
import argparse
import matplotlib # Install PYQT5 manually if you want to test this helper function import matplotlib # Install PYQT5 manually if you want to test this helper function
matplotlib.use("Qt5Agg") matplotlib.use("Qt5Agg")
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from freqtrade import exchange, analyze from freqtrade import exchange, analyze
from freqtrade.misc import parse_args_common
def plot_analyzed_dataframe(pair: str) -> None: def plot_parse_args(args ):
parser = parse_args_common(args, 'Graph utility')
parser.add_argument(
'-p', '--pair',
help = 'What currency pair',
dest = 'pair',
default = 'BTC_ETH',
type = str,
)
return parser.parse_args(args)
def plot_analyzed_dataframe(args) -> None:
""" """
Calls analyze() and plots the returned dataframe Calls analyze() and plots the returned dataframe
:param pair: pair as str :param pair: pair as str
:return: None :return: None
""" """
pair = args.pair
# Init Bittrex to use public API # Init Bittrex to use public API
exchange._API = exchange.Bittrex({'key': '', 'secret': ''}) exchange._API = exchange.Bittrex({'key': '', 'secret': ''})
@ -50,4 +66,5 @@ def plot_analyzed_dataframe(pair: str) -> None:
if __name__ == '__main__': if __name__ == '__main__':
plot_analyzed_dataframe('BTC_ETH') args = plot_parse_args(sys.argv[1:])
plot_analyzed_dataframe(args)