diff --git a/freqtrade/optimize/__init__.py b/freqtrade/optimize/__init__.py index 1be7ce536..f36c28fbc 100644 --- a/freqtrade/optimize/__init__.py +++ b/freqtrade/optimize/__init__.py @@ -12,6 +12,27 @@ from freqtrade.analyze import populate_indicators, parse_ticker_dataframe logger = logging.getLogger(__name__) +def load_tickerdata_file(pair, ticker_interval): + """ + Load a pair from file, + :return dict OR empty if unsuccesful + """ + path = testdata_path() + file = '{abspath}/{pair}-{ticker_interval}.json'.format( + abspath=path, + pair=pair, + ticker_interval=ticker_interval, + ) + # The file does not exist we download it + if not os.path.isfile(file): + return None + + # Read the file, load the json + with open(file) as tickerdata: + pairdata = json.load(tickerdata) + return pairdata + + def load_data(ticker_interval: int = 5, pairs: Optional[List[str]] = None, refresh_pairs: Optional[bool] = False) -> Dict[str, List]: """ @@ -20,7 +41,6 @@ def load_data(ticker_interval: int = 5, pairs: Optional[List[str]] = None, :param pairs: list of pairs :return: dict """ - path = testdata_path() result = {} _pairs = pairs or hyperopt_optimize_conf()['exchange']['pair_whitelist'] @@ -31,18 +51,13 @@ def load_data(ticker_interval: int = 5, pairs: Optional[List[str]] = None, download_pairs(_pairs) for pair in _pairs: - file = '{abspath}/{pair}-{ticker_interval}.json'.format( - abspath=path, - pair=pair, - ticker_interval=ticker_interval, - ) - # The file does not exist we download it - if not os.path.isfile(file): + pairdata = load_tickerdata_file(pair, ticker_interval) + if not pairdata: + # download the tickerdata from exchange download_backtesting_testdata(pair=pair, interval=ticker_interval) - - # Read the file, load the json - with open(file) as tickerdata: - result[pair] = json.load(tickerdata) + # and retry reading the pair + pairdata = load_tickerdata_file(pair, ticker_interval) + result[pair] = pairdata return result diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 8524abf0b..afcc43367 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -12,8 +12,9 @@ from freqtrade import exchange from freqtrade.analyze import populate_buy_trend, populate_sell_trend from freqtrade.exchange import Bittrex from freqtrade.main import min_roi_reached -from freqtrade.misc import load_config -from freqtrade.optimize import load_data, preprocess +import freqtrade.misc as misc +from freqtrade.optimize import preprocess +import freqtrade.optimize as optimize from freqtrade.persistence import Trade logger = logging.getLogger(__name__) @@ -149,7 +150,7 @@ def start(args): exchange._API = Bittrex({'key': '', 'secret': ''}) logger.info('Using config: %s ...', args.config) - config = load_config(args.config) + config = misc.load_config(args.config) logger.info('Using ticker_interval: %s ...', args.ticker_interval) @@ -161,8 +162,8 @@ def start(args): data[pair] = exchange.get_ticker_history(pair, args.ticker_interval) else: logger.info('Using local backtesting data (using whitelist in given config) ...') - data = load_data(pairs=pairs, ticker_interval=args.ticker_interval, - refresh_pairs=args.refresh_pairs) + data = optimize.load_data(pairs=pairs, ticker_interval=args.ticker_interval, + refresh_pairs=args.refresh_pairs) logger.info('Using stake_currency: %s ...', config['stake_currency']) logger.info('Using stake_amount: %s ...', config['stake_amount']) diff --git a/freqtrade/tests/optimize/test_backtesting.py b/freqtrade/tests/optimize/test_backtesting.py index a24042c62..06623d433 100644 --- a/freqtrade/tests/optimize/test_backtesting.py +++ b/freqtrade/tests/optimize/test_backtesting.py @@ -1,13 +1,14 @@ # pragma pylint: disable=missing-docstring,W0212 +import logging import math import pandas as pd -# from unittest.mock import MagicMock +from unittest.mock import MagicMock from freqtrade import exchange, optimize from freqtrade.exchange import Bittrex from freqtrade.optimize import preprocess from freqtrade.optimize.backtesting import backtest, generate_text_table, get_timeframe -# import freqtrade.optimize.backtesting as backtesting +import freqtrade.optimize.backtesting as backtesting def test_generate_text_table(): @@ -61,7 +62,6 @@ def test_backtest_1min_ticker_interval(default_conf, mocker): def trim_dictlist(dl, num): new = {} for pair, pair_data in dl.items(): - # Can't figure out why -num wont work new[pair] = pair_data[num:] return new @@ -148,21 +148,29 @@ def test_backtest_pricecontours(default_conf, mocker): for [contour, numres] in tests: simple_backtest(default_conf, contour, numres) -# Please make this work, the load_config needs to be mocked -# and cleanups. -# def test_backtest_start(default_conf, mocker): -# default_conf['exchange']['pair_whitelist'] = ['BTC_UNITEST'] -# mocker.patch.dict('freqtrade.main._CONF', default_conf) -# # see https://pypi.python.org/pypi/pytest-mock/ -# # and http://www.voidspace.org.uk/python/mock/patch.html -# # No usage example of simple function mocking, -# # and no documentation of side_effect -# mocker.patch('freqtrade.misc.load_config', new=lambda s, t: {}) -# args = MagicMock() -# args.level = 10 -# #load_config('foo') -# backtesting.start(args) -# -# Check what sideeffect backtstesting has done. -# Probably need to capture standard-output and -# check for the generated report table. + +def mocked_load_data(pairs=[], ticker_interval=0, refresh_pairs=False): + tickerdata = optimize.load_tickerdata_file('BTC_UNITEST', 1) + pairdata = {'BTC_UNITEST': tickerdata} + return trim_dictlist(pairdata, -100) + + +def test_backtest_start(default_conf, mocker, caplog): + default_conf['exchange']['pair_whitelist'] = ['BTC_UNITEST'] + mocker.patch.dict('freqtrade.main._CONF', default_conf) + mocker.patch('freqtrade.misc.load_config', new=lambda s: default_conf) + mocker.patch.multiple('freqtrade.optimize', + load_data=mocked_load_data) + args = MagicMock() + args.ticker_interval = 1 + args.level = 10 + args.live = False + backtesting.start(args) + # check the logs, that will contain the backtest result + exists = ['Using max_open_trades: 1 ...', + 'Using stake_amount: 0.001 ...', + 'Measuring data from 2017-11-14T21:17:00+00:00 up to 2017-11-14T22:59:00+00:00 ...'] + for line in exists: + assert ('freqtrade.optimize.backtesting', + logging.INFO, + line) in caplog.record_tuples diff --git a/freqtrade/tests/optimize/test_optimize.py b/freqtrade/tests/optimize/test_optimize.py index aad20567e..62b798c2c 100644 --- a/freqtrade/tests/optimize/test_optimize.py +++ b/freqtrade/tests/optimize/test_optimize.py @@ -5,7 +5,11 @@ import logging from shutil import copyfile from freqtrade import exchange, optimize from freqtrade.exchange import Bittrex -from freqtrade.optimize.__init__ import testdata_path, download_pairs, download_backtesting_testdata +from freqtrade.optimize.__init__ import testdata_path, download_pairs,\ + download_backtesting_testdata, load_tickerdata_file + +# Change this if modifying BTC_UNITEST testdatafile +_btc_unittest_length = 13681 def _backup_file(file: str, copy_file: bool = False) -> None: @@ -164,3 +168,9 @@ def test_download_backtesting_testdata(default_conf, ticker_history, mocker): download_backtesting_testdata(pair="BTC-STORJ", interval=5) assert os.path.isfile(file2) is True _clean_test_file(file2) + + +def test_load_tickerdata_file(): + assert not load_tickerdata_file('BTC_UNITEST', 7) + tickerdata = load_tickerdata_file('BTC_UNITEST', 1) + assert _btc_unittest_length == len(tickerdata)