refactoring download_backtest_data.py

This commit is contained in:
hroff-1902 2019-05-29 21:57:14 +03:00
parent 6451feee0e
commit fb88953be3
4 changed files with 119 additions and 83 deletions

View File

@ -47,7 +47,7 @@ class Arguments(object):
return self.parsed_arg return self.parsed_arg
def parse_args(self) -> argparse.Namespace: def parse_args(self, no_default_config: bool = False) -> argparse.Namespace:
""" """
Parses given arguments and returns an argparse Namespace instance. Parses given arguments and returns an argparse Namespace instance.
""" """
@ -55,7 +55,7 @@ class Arguments(object):
# Workaround issue in argparse with action='append' and default value # Workaround issue in argparse with action='append' and default value
# (see https://bugs.python.org/issue16399) # (see https://bugs.python.org/issue16399)
if parsed_arg.config is None: if parsed_arg.config is None and not no_default_config:
parsed_arg.config = [constants.DEFAULT_CONFIG] parsed_arg.config = [constants.DEFAULT_CONFIG]
return parsed_arg return parsed_arg
@ -427,26 +427,24 @@ class Arguments(object):
default=None default=None
) )
def testdata_dl_options(self) -> None: def download_data_options(self) -> None:
""" """
Parses given arguments for testdata download Parses given arguments for testdata download
""" """
self.parser.add_argument( self.parser.add_argument(
'--pairs-file', '-v', '--verbose',
help='File containing a list of pairs to download.', help='Verbose mode (-vv for more, -vvv to get all messages).',
dest='pairs_file', action='count',
default=None, dest='loglevel',
metavar='PATH', default=0,
) )
self.parser.add_argument( self.parser.add_argument(
'--export', '--logfile',
help='Export files to given dir.', help='Log to the file specified',
dest='export', dest='logfile',
default=None, type=str,
metavar='PATH', metavar='FILE'
) )
self.parser.add_argument( self.parser.add_argument(
'-c', '--config', '-c', '--config',
help='Specify configuration file (default: %(default)s). ' help='Specify configuration file (default: %(default)s). '
@ -456,7 +454,21 @@ class Arguments(object):
type=str, type=str,
metavar='PATH', metavar='PATH',
) )
self.parser.add_argument(
'-d', '--datadir',
help='Path to backtest data.',
dest='datadir',
default=None,
type=str,
metavar='PATH',
)
self.parser.add_argument(
'--pairs-file',
help='File containing a list of pairs to download.',
dest='pairs_file',
default=None,
metavar='PATH',
)
self.parser.add_argument( self.parser.add_argument(
'--days', '--days',
help='Download data for given number of days.', help='Download data for given number of days.',
@ -465,7 +477,6 @@ class Arguments(object):
metavar='INT', metavar='INT',
default=None default=None
) )
self.parser.add_argument( self.parser.add_argument(
'--exchange', '--exchange',
help='Exchange name (default: %(default)s). Only valid if no config is provided.', help='Exchange name (default: %(default)s). Only valid if no config is provided.',
@ -473,7 +484,6 @@ class Arguments(object):
type=str, type=str,
default='bittrex' default='bittrex'
) )
self.parser.add_argument( self.parser.add_argument(
'-t', '--timeframes', '-t', '--timeframes',
help='Specify which tickers to download. Space separated list. \ help='Specify which tickers to download. Space separated list. \
@ -484,7 +494,6 @@ class Arguments(object):
nargs='+', nargs='+',
dest='timeframes', dest='timeframes',
) )
self.parser.add_argument( self.parser.add_argument(
'--erase', '--erase',
help='Clean all existing data for the selected exchange/pairs/timeframes.', help='Clean all existing data for the selected exchange/pairs/timeframes.',

View File

@ -122,12 +122,11 @@ class Configuration(object):
return conf return conf
def _load_common_config(self, config: Dict[str, Any]) -> Dict[str, Any]: def _load_logging_config(self, config: Dict[str, Any]) -> None:
""" """
Extract information for sys.argv and load common configuration Extract information for sys.argv and load logging configuration:
:return: configuration as dictionary the --loglevel, --logfile options
""" """
# Log level # Log level
if 'loglevel' in self.args and self.args.loglevel: if 'loglevel' in self.args and self.args.loglevel:
config.update({'verbosity': self.args.loglevel}) config.update({'verbosity': self.args.loglevel})
@ -153,6 +152,13 @@ class Configuration(object):
set_loggers(config['verbosity']) set_loggers(config['verbosity'])
logger.info('Verbosity set to %s', config['verbosity']) logger.info('Verbosity set to %s', config['verbosity'])
def _load_common_config(self, config: Dict[str, Any]) -> Dict[str, Any]:
"""
Extract information for sys.argv and load common configuration
:return: configuration as dictionary
"""
self._load_logging_config(config)
# Support for sd_notify # Support for sd_notify
if self.args.sd_notify: if self.args.sd_notify:
config['internals'].update({'sd_notify': True}) config['internals'].update({'sd_notify': True})
@ -228,6 +234,17 @@ class Configuration(object):
else: else:
logger.info(logstring.format(config[argname])) logger.info(logstring.format(config[argname]))
def _load_datadir_config(self, config: Dict[str, Any]) -> None:
"""
Extract information for sys.argv and load datadir configuration:
the --datadir option
"""
if 'datadir' in self.args and self.args.datadir:
config.update({'datadir': self._create_datadir(config, self.args.datadir)})
else:
config.update({'datadir': self._create_datadir(config, None)})
logger.info('Using data folder: %s ...', config.get('datadir'))
def _load_optimize_config(self, config: Dict[str, Any]) -> Dict[str, Any]: def _load_optimize_config(self, config: Dict[str, Any]) -> Dict[str, Any]:
""" """
Extract information for sys.argv and load Optimize configuration Extract information for sys.argv and load Optimize configuration
@ -263,11 +280,7 @@ class Configuration(object):
self._args_to_config(config, argname='timerange', self._args_to_config(config, argname='timerange',
logstring='Parameter --timerange detected: {} ...') logstring='Parameter --timerange detected: {} ...')
if 'datadir' in self.args and self.args.datadir: self._load_datadir_config(config)
config.update({'datadir': self._create_datadir(config, self.args.datadir)})
else:
config.update({'datadir': self._create_datadir(config, None)})
logger.info('Using data folder: %s ...', config.get('datadir'))
self._args_to_config(config, argname='refresh_pairs', self._args_to_config(config, argname='refresh_pairs',
logstring='Parameter -r/--refresh-pairs-cached detected ...') logstring='Parameter -r/--refresh-pairs-cached detected ...')

View File

@ -170,18 +170,18 @@ def test_parse_args_hyperopt_custom() -> None:
assert call_args.func is not None assert call_args.func is not None
def test_testdata_dl_options() -> None: def test_download_data_options() -> None:
args = [ args = [
'--pairs-file', 'file_with_pairs', '--pairs-file', 'file_with_pairs',
'--export', 'export/folder', '--datadir', 'datadir/folder',
'--days', '30', '--days', '30',
'--exchange', 'binance' '--exchange', 'binance'
] ]
arguments = Arguments(args, '') arguments = Arguments(args, '')
arguments.testdata_dl_options() arguments.download_data_options()
args = arguments.parse_args() args = arguments.parse_args()
assert args.pairs_file == 'file_with_pairs' assert args.pairs_file == 'file_with_pairs'
assert args.export == 'export/folder' assert args.datadir == 'datadir/folder'
assert args.days == 30 assert args.days == 30
assert args.exchange == 'binance' assert args.exchange == 'binance'

View File

@ -1,39 +1,39 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
This script generates json data This script generates json files with pairs history data
""" """
import arrow
import json import json
import sys import sys
from pathlib import Path from pathlib import Path
import arrow from typing import Any, Dict, List
from typing import Any, Dict
from freqtrade.arguments import Arguments from freqtrade.arguments import Arguments, TimeRange
from freqtrade.arguments import TimeRange from freqtrade.configuration import Configuration
from freqtrade.exchange import Exchange
from freqtrade.data.history import download_pair_history from freqtrade.data.history import download_pair_history
from freqtrade.configuration import Configuration, set_loggers from freqtrade.exchange import Exchange
from freqtrade.misc import deep_merge_dicts from freqtrade.misc import deep_merge_dicts
import logging import logging
logging.basicConfig(
level=logging.INFO, logger = logging.getLogger('download_backtest_data')
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
)
set_loggers(0)
DEFAULT_DL_PATH = 'user_data/data' DEFAULT_DL_PATH = 'user_data/data'
arguments = Arguments(sys.argv[1:], 'download utility') arguments = Arguments(sys.argv[1:], 'download utility')
arguments.testdata_dl_options() arguments.download_data_options()
args = arguments.parse_args()
# Do not read the default config if config is not specified
# in the command line options explicitely
args = arguments.parse_args(no_default_config=True)
timeframes = args.timeframes timeframes = args.timeframes
pairs: List = []
configuration = Configuration(args)
config: Dict[str, Any] = {}
if args.config: if args.config:
configuration = Configuration(args)
config: Dict[str, Any] = {}
# Now expecting a list of config filenames here, not a string # Now expecting a list of config filenames here, not a string
for path in args.config: for path in args.config:
print(f"Using config: {path}...") print(f"Using config: {path}...")
@ -42,9 +42,19 @@ if args.config:
config['stake_currency'] = '' config['stake_currency'] = ''
# Ensure we do not use Exchange credentials # Ensure we do not use Exchange credentials
config['exchange']['dry_run'] = True
config['exchange']['key'] = '' config['exchange']['key'] = ''
config['exchange']['secret'] = '' config['exchange']['secret'] = ''
if args.exchange:
config['exchange']['name'] = args.exchange
pairs = config['exchange']['pair_whitelist']
timeframes = [config['ticker_interval']]
else: else:
if not args.exchange:
sys.exit("No exchange specified.")
config = { config = {
'stake_currency': '', 'stake_currency': '',
'dry_run': True, 'dry_run': True,
@ -60,37 +70,39 @@ else:
} }
} }
configuration._load_logging_config(config)
configuration._load_datadir_config(config)
dl_path = Path(DEFAULT_DL_PATH).joinpath(config['exchange']['name']) dl_path = Path(config['datadir'])
if args.export:
dl_path = Path(args.export)
if not dl_path.is_dir():
sys.exit(f'Directory {dl_path} does not exist.')
pairs_file = Path(args.pairs_file) if args.pairs_file else dl_path.joinpath('pairs.json') pairs_file = Path(args.pairs_file) if args.pairs_file else dl_path.joinpath('pairs.json')
if not pairs or args.pairs_file:
print(f'Reading pairs file "{pairs_file}".')
# Download pairs from the pairs file if no config is specified
# or if pairs file is specified explicitely
if not pairs_file.exists(): if not pairs_file.exists():
sys.exit(f'No pairs file found with path {pairs_file}.') sys.exit(f'No pairs file found with path "{pairs_file}".')
with pairs_file.open() as file: with pairs_file.open() as file:
PAIRS = list(set(json.load(file))) pairs = list(set(json.load(file)))
PAIRS.sort()
pairs.sort()
timerange = TimeRange() timerange = TimeRange()
if args.days: if args.days:
time_since = arrow.utcnow().shift(days=-args.days).strftime("%Y%m%d") time_since = arrow.utcnow().shift(days=-args.days).strftime("%Y%m%d")
timerange = arguments.parse_timerange(f'{time_since}-') timerange = arguments.parse_timerange(f'{time_since}-')
print(f'About to download pairs: {pairs}, intervals: {timeframes} to {dl_path}')
print(f'About to download pairs: {PAIRS} to {dl_path}')
# Init exchange
exchange = Exchange(config)
pairs_not_available = [] pairs_not_available = []
for pair in PAIRS: try:
# Init exchange
exchange = Exchange(config)
for pair in pairs:
if pair not in exchange._api.markets: if pair not in exchange._api.markets:
pairs_not_available.append(pair) pairs_not_available.append(pair)
print(f"skipping pair {pair}") print(f"skipping pair {pair}")
@ -105,10 +117,12 @@ for pair in PAIRS:
print(f'downloading pair {pair}, interval {ticker_interval}') print(f'downloading pair {pair}, interval {ticker_interval}')
download_pair_history(datadir=dl_path, exchange=exchange, download_pair_history(datadir=dl_path, exchange=exchange,
pair=pair, pair=pair, ticker_interval=ticker_interval,
ticker_interval=ticker_interval,
timerange=timerange) timerange=timerange)
except KeyboardInterrupt:
sys.exit("SIGINT received, aborting ...")
finally:
if pairs_not_available: if pairs_not_available:
print(f"Pairs [{','.join(pairs_not_available)}] not availble.") print(f"Pairs [{','.join(pairs_not_available)}] not availble.")