Merge pull request #324 from kryofly/parse-common
Parsing: common options, reduce function scope
This commit is contained in:
commit
2432c9f290
@ -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
|
||||||
|
@ -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:
|
||||||
|
@ -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):
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user