From 6675120324c24787894345b6a8d0992e8ce8cad3 Mon Sep 17 00:00:00 2001 From: Anton Date: Wed, 25 Apr 2018 02:11:07 +0300 Subject: [PATCH 1/8] Add time range support to download_backtest_data --- freqtrade/arguments.py | 14 +++++++++++++ freqtrade/exchange/__init__.py | 8 ++++---- scripts/download_backtest_data.py | 34 ++++++++++++++++++++++++++----- 3 files changed, 47 insertions(+), 9 deletions(-) diff --git a/freqtrade/arguments.py b/freqtrade/arguments.py index 574deb304..7fba2840f 100644 --- a/freqtrade/arguments.py +++ b/freqtrade/arguments.py @@ -265,3 +265,17 @@ class Arguments(object): help='Export files to given dir', dest='export', default=None) + + self.parser.add_argument( + '--days', + help='Download data for number of days', + dest='days', + type=int, + default=None) + + self.parser.add_argument( + '--exchange', + help='Exchange name', + dest='exchange', + type=str, + default='bittrex') \ No newline at end of file diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 91d2493cc..465b25b4a 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -45,7 +45,7 @@ def retrier(f): return wrapper -def init_ccxt(exchange_config: dict) -> ccxt: +def init_ccxt(exchange_config: dict) -> ccxt.Exchange: """ Initialize ccxt with given config and return valid ccxt instance. @@ -62,7 +62,7 @@ def init_ccxt(exchange_config: dict) -> ccxt: 'apiKey': exchange_config.get('key'), 'secret': exchange_config.get('secret'), 'password': exchange_config.get('password'), - 'uid': exchange_config.get('uid' ''), + 'uid': exchange_config.get('uid', ''), 'enableRateLimit': True, }) except (KeyError, AttributeError): @@ -269,9 +269,9 @@ def get_ticker(pair: str, refresh: Optional[bool] = True) -> dict: @retrier -def get_ticker_history(pair: str, tick_interval: str) -> List[Dict]: +def get_ticker_history(pair: str, tick_interval: str, since: Optional[int] = None) -> List[Dict]: try: - return _API.fetch_ohlcv(pair, timeframe=tick_interval) + return _API.fetch_ohlcv(pair, timeframe=tick_interval, since=since) except ccxt.NetworkError as e: raise NetworkException( 'Could not load ticker history due to networking error. Message: {}'.format(e) diff --git a/scripts/download_backtest_data.py b/scripts/download_backtest_data.py index 8230c1349..a36a61f5e 100755 --- a/scripts/download_backtest_data.py +++ b/scripts/download_backtest_data.py @@ -4,8 +4,8 @@ import json import sys import os - -from freqtrade import (exchange, arguments, misc) +import time +import datetime DEFAULT_DL_PATH = 'freqtrade/tests/testdata' @@ -15,6 +15,9 @@ args = arguments.parse_args() TICKER_INTERVALS = ['1m', '5m'] PAIRS = [] +MIN_SECCONDS = 60 +HOUR_SECCONDS = 60 * MIN_SECCONDS +DAY_SECCONDS = 24 * HOUR_SECCONDS if args.pairs_file: with open(args.pairs_file) as file: @@ -27,15 +30,36 @@ if args.export and os.path.exists(args.export): print(f'About to download pairs: {PAIRS} to {dl_path}') -# Init Bittrex exchange +# Init exchange exchange._API = exchange.init_ccxt({'key': '', 'secret': '', - 'name': 'bittrex'}) + 'name': args.exchange}) for pair in PAIRS: for tick_interval in TICKER_INTERVALS: print(f'downloading pair {pair}, interval {tick_interval}') - data = exchange.get_ticker_history(pair, tick_interval) + + since_time = None + if args.days: + since_time = int((time.time() - args.days * DAY_SECCONDS) * 1000) + + # download data until it reaches today now time + data = [] + while not since_time or since_time < (time.time() - 10 * MIN_SECCONDS) * 1000: + data_part = exchange.get_ticker_history(pair, tick_interval, since=since_time) + + if not data_part: + print('\tNo data since %s' % datetime.datetime.utcfromtimestamp(since_time / 1000).strftime('%Y-%m-%dT%H:%M:%S')) + break + + print('\tData received for period %s - %s' % + (datetime.datetime.utcfromtimestamp(data_part[0][0] / 1000).strftime('%Y-%m-%dT%H:%M:%S'), + datetime.datetime.utcfromtimestamp(data_part[-1][0] / 1000).strftime('%Y-%m-%dT%H:%M:%S'))) + + data.extend(data_part) + since_time = data[-1][0] + 1 + + # save data pair_print = pair.replace('/', '_') filename = f'{pair_print}-{tick_interval}.json' misc.file_dump_json(os.path.join(dl_path, filename), data) From 2fe7812e20934474aad3e362e3ad67979420b5a5 Mon Sep 17 00:00:00 2001 From: Anton Date: Wed, 25 Apr 2018 10:32:58 +0300 Subject: [PATCH 2/8] Fix codestyle --- freqtrade/arguments.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/arguments.py b/freqtrade/arguments.py index 7fba2840f..94c23ae10 100644 --- a/freqtrade/arguments.py +++ b/freqtrade/arguments.py @@ -278,4 +278,4 @@ class Arguments(object): help='Exchange name', dest='exchange', type=str, - default='bittrex') \ No newline at end of file + default='bittrex') From 82ea56c8fd489237e3e542b50d0c327dfea12c55 Mon Sep 17 00:00:00 2001 From: Anton Date: Sat, 28 Apr 2018 00:16:34 +0300 Subject: [PATCH 3/8] Fix review comments. Add support of datetime timeganges --- docs/backtesting.md | 9 +-- freqtrade/arguments.py | 9 ++- freqtrade/exchange/__init__.py | 21 ++++- freqtrade/optimize/__init__.py | 94 +++++++++++++++++------ freqtrade/tests/exchange/test_exchange.py | 13 +++- freqtrade/tests/optimize/test_optimize.py | 63 ++++++++++++++- freqtrade/tests/test_arguments.py | 6 ++ scripts/download_backtest_data.py | 39 ++++------ 8 files changed, 194 insertions(+), 60 deletions(-) diff --git a/docs/backtesting.md b/docs/backtesting.md index 2bcaae40c..184112f33 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -80,12 +80,9 @@ The full timerange specification: - Use last 123 tickframes of data: `--timerange=-123` - Use first 123 tickframes of data: `--timerange=123-` - Use tickframes from line 123 through 456: `--timerange=123-456` - - -Incoming feature, not implemented yet: -- `--timerange=-20180131` -- `--timerange=20180101-` -- `--timerange=20180101-20181231` +- Use tickframes till 2018/01/31: `--timerange=-20180131` +- Use tickframes since 2018/01/31: `--timerange=20180131-` +- Use tickframes since 2018/01/31 till 2018/03/01 : `--timerange=20180131-20180301` **Update testdata directory** diff --git a/freqtrade/arguments.py b/freqtrade/arguments.py index 94c23ae10..8b3cbf72d 100644 --- a/freqtrade/arguments.py +++ b/freqtrade/arguments.py @@ -6,6 +6,7 @@ import argparse import logging import os import re +import arrow from typing import List, Tuple, Optional from freqtrade import __version__ @@ -228,12 +229,16 @@ class Arguments(object): stop = None if stype[0]: start = rvals[index] - if stype[0] != 'date': + if stype[0] == 'date': + start = arrow.get(start, 'YYYYMMDD').timestamp + else: start = int(start) index += 1 if stype[1]: stop = rvals[index] - if stype[1] != 'date': + if stype[1] == 'date': + stop = arrow.get(stop, 'YYYYMMDD').timestamp + else: stop = int(stop) return stype, start, stop raise Exception('Incorrect syntax for timerange "%s"' % text) diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 465b25b4a..dc8e817f5 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -271,7 +271,26 @@ def get_ticker(pair: str, refresh: Optional[bool] = True) -> dict: @retrier def get_ticker_history(pair: str, tick_interval: str, since: Optional[int] = None) -> List[Dict]: try: - return _API.fetch_ohlcv(pair, timeframe=tick_interval, since=since) + # download data until it reaches today now time + # + # it looks as if some exchanges return cached data + # and update it with some delay so 10 mins interval is added + data = [] + while not since or since < arrow.utcnow().shift(minutes=-10).timestamp * 1000: + data_part = _API.fetch_ohlcv(pair, timeframe=tick_interval, since=since) + + if not data_part: + break + + logger.info('Downloaded data for time range [%s, %s]', + arrow.get(data_part[0][0] / 1000).format(), + arrow.get(data_part[-1][0] / 1000).format()) + + data.extend(data_part) + since = data[-1][0] + 1 + + return data + except ccxt.NetworkError as e: raise NetworkException( 'Could not load ticker history due to networking error. Message: {}'.format(e) diff --git a/freqtrade/optimize/__init__.py b/freqtrade/optimize/__init__.py index 98267ce77..10043bf4d 100644 --- a/freqtrade/optimize/__init__.py +++ b/freqtrade/optimize/__init__.py @@ -4,10 +4,13 @@ import gzip import json import logging import os +import arrow from typing import Optional, List, Dict, Tuple from freqtrade import misc from freqtrade.exchange import get_ticker_history +from freqtrade.constants import Constants + from user_data.hyperopt_conf import hyperopt_optimize_conf logger = logging.getLogger(__name__) @@ -15,14 +18,30 @@ logger = logging.getLogger(__name__) def trim_tickerlist(tickerlist: List[Dict], timerange: Tuple[Tuple, int, int]) -> List[Dict]: stype, start, stop = timerange - if stype == (None, 'line'): - return tickerlist[stop:] - elif stype == ('line', None): - return tickerlist[0:start] - elif stype == ('index', 'index'): - return tickerlist[start:stop] - return tickerlist + start_index = 0 + stop_index = len(tickerlist) + + if stype[0] == 'line': + stop_index = start + if stype[0] == 'index': + start_index = start + elif stype[0] == 'date': + while tickerlist[start_index][0] < start * 1000: + start_index += 1 + + if stype[1] == 'line': + start_index = len(tickerlist) + stop + if stype[1] == 'index': + stop_index = stop + elif stype[1] == 'date': + while tickerlist[stop_index-1][0] > stop * 1000: + stop_index -= 1 + + if start_index > stop_index: + raise ValueError(f'The timerange [{start},{stop}] is incorrect') + + return tickerlist[start_index:stop_index] def load_tickerdata_file( @@ -75,7 +94,7 @@ def load_data(datadir: str, # If the user force the refresh of pairs if refresh_pairs: logger.info('Download data for all pairs and store them in %s', datadir) - download_pairs(datadir, _pairs, ticker_interval) + download_pairs(datadir, _pairs, ticker_interval, timerange=timerange) for pair in _pairs: pairdata = load_tickerdata_file(datadir, pair, ticker_interval, timerange=timerange) @@ -97,11 +116,13 @@ def make_testdata_path(datadir: str) -> str: ) -def download_pairs(datadir, pairs: List[str], ticker_interval: str) -> bool: +def download_pairs(datadir, pairs: List[str], + ticker_interval: str, + timerange: Optional[Tuple[Tuple, int, int]] = None) -> bool: """For each pairs passed in parameters, download the ticker intervals""" for pair in pairs: try: - download_backtesting_testdata(datadir, pair=pair, interval=ticker_interval) + download_backtesting_testdata(datadir, pair=pair, interval=ticker_interval, timerange=timerange) except BaseException: logger.info( 'Failed to download the pair: "%s", Interval: %s', @@ -112,12 +133,31 @@ def download_pairs(datadir, pairs: List[str], ticker_interval: str) -> bool: return True +def get_start_ts_from_timerange(timerange: Tuple[Tuple, int, int], interval: str) -> int: + if not timerange: + return None + + if timerange[0][0] == 'date': + return timerange[1] * 1000 + + if timerange[0][1] == 'line': + num_minutes = timerange[2] * Constants.TICKER_INTERVAL_MINUTES[interval] + return arrow.utcnow().shift(minutes=num_minutes).timestamp * 1000 + + return None + + # FIX: 20180110, suggest rename interval to tick_interval -def download_backtesting_testdata(datadir: str, pair: str, interval: str = '5m') -> bool: +def download_backtesting_testdata(datadir: str, + pair: str, + interval: str = '5m', + timerange: Optional[Tuple[Tuple, int, int]] = None) -> bool: """ - Download the latest 1 and 5 ticker intervals from Bittrex for the pairs passed in parameters + Download the latest ticker intervals from the exchange for the pairs passed in parameters Based on @Rybolov work: https://github.com/rybolov/freqtrade-data :param pairs: list of pairs to download + :param interval: ticker interval + :param timerange: range of time to download :return: bool """ @@ -134,23 +174,33 @@ def download_backtesting_testdata(datadir: str, pair: str, interval: str = '5m') interval=interval, )) + since = get_start_ts_from_timerange(timerange, interval) + if os.path.isfile(filename): with open(filename, "rt") as file: data = json.load(file) - logger.debug("Current Start: %s", misc.format_ms_time(data[1][0])) - logger.debug("Current End: %s", misc.format_ms_time(data[-1:][0][0])) + + if since: + if since < data[0][0]: + # fully update the data + data = [] + else: + # download unexist data only + since = max(since, data[-1][0] + 1) + else: + # download unexist data only + since = data[-1][0] + 1 else: data = [] - logger.debug("Current Start: None") - logger.debug("Current End: None") - new_data = get_ticker_history(pair=pair, tick_interval=interval) - for row in new_data: - if row not in data: - data.append(row) + logger.debug("Current Start: %s", misc.format_ms_time(data[1][0]) if data else 'None') + logger.debug("Current End: %s", misc.format_ms_time(data[-1][0]) if data else 'None') + + new_data = get_ticker_history(pair=pair, tick_interval=interval, since=since) + data.extend(new_data) + logger.debug("New Start: %s", misc.format_ms_time(data[0][0])) - logger.debug("New End: %s", misc.format_ms_time(data[-1:][0][0])) - data = sorted(data, key=lambda data: data[0]) + logger.debug("New End: %s", misc.format_ms_time(data[-1][0])) misc.file_dump_json(filename, data) diff --git a/freqtrade/tests/exchange/test_exchange.py b/freqtrade/tests/exchange/test_exchange.py index e959c91b6..e194cb072 100644 --- a/freqtrade/tests/exchange/test_exchange.py +++ b/freqtrade/tests/exchange/test_exchange.py @@ -321,6 +321,15 @@ def test_get_ticker(default_conf, mocker): get_ticker(pair='ETH/BTC', refresh=True) +def make_fetch_ohlcv_mock(data): + def fetch_ohlcv_mock(pair, timeframe, since): + if since: + assert since > data[-1][0] + return [] + return data + return fetch_ohlcv_mock + + def test_get_ticker_history(default_conf, mocker): api_mock = MagicMock() tick = [ @@ -334,7 +343,7 @@ def test_get_ticker_history(default_conf, mocker): ] ] type(api_mock).has = PropertyMock(return_value={'fetchOHLCV': True}) - api_mock.fetch_ohlcv = MagicMock(return_value=tick) + api_mock.fetch_ohlcv = MagicMock(side_effect=make_fetch_ohlcv_mock(tick)) mocker.patch('freqtrade.exchange._API', api_mock) # retrieve original ticker @@ -357,7 +366,7 @@ def test_get_ticker_history(default_conf, mocker): 10, # volume (in quote currency) ] ] - api_mock.fetch_ohlcv = MagicMock(return_value=new_tick) + api_mock.fetch_ohlcv = MagicMock(side_effect=make_fetch_ohlcv_mock(new_tick)) mocker.patch('freqtrade.exchange._API', api_mock) ticks = get_ticker_history('ETH/BTC', default_conf['ticker_interval']) diff --git a/freqtrade/tests/optimize/test_optimize.py b/freqtrade/tests/optimize/test_optimize.py index 411714ec8..a488f320e 100644 --- a/freqtrade/tests/optimize/test_optimize.py +++ b/freqtrade/tests/optimize/test_optimize.py @@ -3,12 +3,14 @@ import json import os import uuid +import arrow from shutil import copyfile from freqtrade import optimize from freqtrade.misc import file_dump_json from freqtrade.optimize.__init__ import make_testdata_path, download_pairs, \ - download_backtesting_testdata, load_tickerdata_file, trim_tickerlist + download_backtesting_testdata, load_tickerdata_file, trim_tickerlist, \ + get_start_ts_from_timerange from freqtrade.tests.conftest import log_has # Change this if modifying UNITTEST/BTC testdatafile @@ -145,6 +147,28 @@ def test_download_pairs(ticker_history, mocker) -> None: _clean_test_file(file2_5) +def test_get_start_ts_from_timerange(mocker) -> None: + start = get_start_ts_from_timerange(None, '1m') + assert start is None + + # check 'date' + start = get_start_ts_from_timerange((('date', 'date'), 1000, 2000), '1m') + assert start == 1000 * 1000 + + start = get_start_ts_from_timerange((('date', 'date'), 1000, 2000), '5m') + assert start == 1000 * 1000 + + # check 'line' + mock_now = arrow.get(1367900664) + mocker.patch('arrow.utcnow', return_value=mock_now) + + start = get_start_ts_from_timerange(((None, 'line'), None, -200), '1m') + assert start == (1367900664 - 200 * 60) * 1000 + + start = get_start_ts_from_timerange(((None, 'line'), None, -200), '5m') + assert start == (1367900664 - 5 * 200 * 60) * 1000 + + def test_download_pairs_exception(ticker_history, mocker, caplog) -> None: mocker.patch('freqtrade.optimize.__init__.get_ticker_history', return_value=ticker_history) mocker.patch('freqtrade.optimize.__init__.download_backtesting_testdata', @@ -221,12 +245,12 @@ def test_trim_tickerlist() -> None: ticker_list_len = len(ticker_list) # Test the pattern ^(-\d+)$ - # This pattern remove X element from the beginning - timerange = ((None, 'line'), None, 5) + # This pattern uses the latest N elements + timerange = ((None, 'line'), None, -5) ticker = trim_tickerlist(ticker_list, timerange) ticker_len = len(ticker) - assert ticker_list_len == ticker_len + 5 + assert ticker_len == 5 assert ticker_list[0] is not ticker[0] # The first element should be different assert ticker_list[-1] is ticker[-1] # The last element must be the same @@ -251,6 +275,37 @@ def test_trim_tickerlist() -> None: assert ticker_list[5] is ticker[0] # The list starts at the index 5 assert ticker_list[9] is ticker[-1] # The list ends at the index 9 (5 elements) + # Test the pattern ^(\d{8})-(\d{8})$ + # This pattern extract a window between the dates + timerange = (('date', 'date'), ticker_list[5][0] / 1000, ticker_list[10][0] / 1000 - 1) + ticker = trim_tickerlist(ticker_list, timerange) + ticker_len = len(ticker) + + assert ticker_len == 5 + assert ticker_list[0] is not ticker[0] # The first element should be different + assert ticker_list[5] is ticker[0] # The list starts at the index 5 + assert ticker_list[9] is ticker[-1] # The list ends at the index 9 (5 elements) + + # Test the pattern ^-(\d{8})$ + # This pattern extracts elements from the start to the date + timerange = ((None, 'date'), None, ticker_list[10][0] / 1000 - 1) + ticker = trim_tickerlist(ticker_list, timerange) + ticker_len = len(ticker) + + assert ticker_len == 10 + assert ticker_list[0] is ticker[0] # The start of the list is included + assert ticker_list[9] is ticker[-1] # The element 10 is not included + + # Test the pattern ^(\d{8})-$ + # This pattern extracts elements from the date to now + timerange = (('date', None), ticker_list[10][0] / 1000 - 1, None) + ticker = trim_tickerlist(ticker_list, timerange) + ticker_len = len(ticker) + + assert ticker_len == ticker_list_len - 10 + assert ticker_list[10] is ticker[0] # The first element is element #10 + assert ticker_list[-1] is ticker[-1] # The last element is the same + # Test a wrong pattern # This pattern must return the list unchanged timerange = ((None, None), None, 5) diff --git a/freqtrade/tests/test_arguments.py b/freqtrade/tests/test_arguments.py index 881129887..e28aa010f 100644 --- a/freqtrade/tests/test_arguments.py +++ b/freqtrade/tests/test_arguments.py @@ -89,6 +89,12 @@ def test_parse_args_dynamic_whitelist_invalid_values() -> None: def test_parse_timerange_incorrect() -> None: assert ((None, 'line'), None, -200) == Arguments.parse_timerange('-200') assert (('line', None), 200, None) == Arguments.parse_timerange('200-') + assert (('index', 'index'), 200, 500) == Arguments.parse_timerange('200-500') + + assert (('date', None), 1274486400, None) == Arguments.parse_timerange('20100522-') + assert ((None, 'date'), None, 1274486400) == Arguments.parse_timerange('-20100522') + assert (('date', 'date'), 1274486400, 1438214400) == Arguments.parse_timerange('20100522-20150730') + with pytest.raises(Exception, match=r'Incorrect syntax.*'): Arguments.parse_timerange('-') diff --git a/scripts/download_backtest_data.py b/scripts/download_backtest_data.py index a36a61f5e..3878f6a34 100755 --- a/scripts/download_backtest_data.py +++ b/scripts/download_backtest_data.py @@ -4,8 +4,9 @@ import json import sys import os -import time -import datetime +import arrow + +from freqtrade import (exchange, arguments, misc) DEFAULT_DL_PATH = 'freqtrade/tests/testdata' @@ -15,9 +16,6 @@ args = arguments.parse_args() TICKER_INTERVALS = ['1m', '5m'] PAIRS = [] -MIN_SECCONDS = 60 -HOUR_SECCONDS = 60 * MIN_SECCONDS -DAY_SECCONDS = 24 * HOUR_SECCONDS if args.pairs_file: with open(args.pairs_file) as file: @@ -28,6 +26,11 @@ dl_path = DEFAULT_DL_PATH if args.export and os.path.exists(args.export): dl_path = args.export +since_time = None +if args.days: + since_time = arrow.utcnow().shift(days=-args.days).timestamp * 1000 + + print(f'About to download pairs: {PAIRS} to {dl_path}') # Init exchange @@ -35,29 +38,19 @@ exchange._API = exchange.init_ccxt({'key': '', 'secret': '', 'name': args.exchange}) + for pair in PAIRS: for tick_interval in TICKER_INTERVALS: print(f'downloading pair {pair}, interval {tick_interval}') - since_time = None - if args.days: - since_time = int((time.time() - args.days * DAY_SECCONDS) * 1000) - - # download data until it reaches today now time - data = [] - while not since_time or since_time < (time.time() - 10 * MIN_SECCONDS) * 1000: - data_part = exchange.get_ticker_history(pair, tick_interval, since=since_time) - - if not data_part: - print('\tNo data since %s' % datetime.datetime.utcfromtimestamp(since_time / 1000).strftime('%Y-%m-%dT%H:%M:%S')) - break + data = exchange.get_ticker_history(pair, tick_interval, since=since_time) + if not data: + print('\tNo data was downloaded') + break - print('\tData received for period %s - %s' % - (datetime.datetime.utcfromtimestamp(data_part[0][0] / 1000).strftime('%Y-%m-%dT%H:%M:%S'), - datetime.datetime.utcfromtimestamp(data_part[-1][0] / 1000).strftime('%Y-%m-%dT%H:%M:%S'))) - - data.extend(data_part) - since_time = data[-1][0] + 1 + print('\tData was downloaded for period %s - %s' % ( + arrow.get(data[0][0] / 1000).format(), + arrow.get(data[-1][0] / 1000).format())) # save data pair_print = pair.replace('/', '_') From 2267a420a4f20d77b6adbf4dcb0d7a615a1bddb6 Mon Sep 17 00:00:00 2001 From: Anton Date: Sat, 28 Apr 2018 00:30:42 +0300 Subject: [PATCH 4/8] Fix codestyle --- freqtrade/optimize/__init__.py | 9 ++++++--- freqtrade/tests/optimize/test_optimize.py | 2 +- freqtrade/tests/test_arguments.py | 3 ++- scripts/download_backtest_data.py | 2 +- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/freqtrade/optimize/__init__.py b/freqtrade/optimize/__init__.py index 10043bf4d..58eb28cb1 100644 --- a/freqtrade/optimize/__init__.py +++ b/freqtrade/optimize/__init__.py @@ -122,7 +122,10 @@ def download_pairs(datadir, pairs: List[str], """For each pairs passed in parameters, download the ticker intervals""" for pair in pairs: try: - download_backtesting_testdata(datadir, pair=pair, interval=ticker_interval, timerange=timerange) + download_backtesting_testdata(datadir, + pair=pair, + interval=ticker_interval, + timerange=timerange) except BaseException: logger.info( 'Failed to download the pair: "%s", Interval: %s', @@ -139,7 +142,7 @@ def get_start_ts_from_timerange(timerange: Tuple[Tuple, int, int], interval: str if timerange[0][0] == 'date': return timerange[1] * 1000 - + if timerange[0][1] == 'line': num_minutes = timerange[2] * Constants.TICKER_INTERVAL_MINUTES[interval] return arrow.utcnow().shift(minutes=num_minutes).timestamp * 1000 @@ -156,7 +159,7 @@ def download_backtesting_testdata(datadir: str, Download the latest ticker intervals from the exchange for the pairs passed in parameters Based on @Rybolov work: https://github.com/rybolov/freqtrade-data :param pairs: list of pairs to download - :param interval: ticker interval + :param interval: ticker interval :param timerange: range of time to download :return: bool """ diff --git a/freqtrade/tests/optimize/test_optimize.py b/freqtrade/tests/optimize/test_optimize.py index a488f320e..e58f5bb2d 100644 --- a/freqtrade/tests/optimize/test_optimize.py +++ b/freqtrade/tests/optimize/test_optimize.py @@ -167,7 +167,7 @@ def test_get_start_ts_from_timerange(mocker) -> None: start = get_start_ts_from_timerange(((None, 'line'), None, -200), '5m') assert start == (1367900664 - 5 * 200 * 60) * 1000 - + def test_download_pairs_exception(ticker_history, mocker, caplog) -> None: mocker.patch('freqtrade.optimize.__init__.get_ticker_history', return_value=ticker_history) diff --git a/freqtrade/tests/test_arguments.py b/freqtrade/tests/test_arguments.py index e28aa010f..756f48778 100644 --- a/freqtrade/tests/test_arguments.py +++ b/freqtrade/tests/test_arguments.py @@ -93,7 +93,8 @@ def test_parse_timerange_incorrect() -> None: assert (('date', None), 1274486400, None) == Arguments.parse_timerange('20100522-') assert ((None, 'date'), None, 1274486400) == Arguments.parse_timerange('-20100522') - assert (('date', 'date'), 1274486400, 1438214400) == Arguments.parse_timerange('20100522-20150730') + timerange = Arguments.parse_timerange('20100522-20150730') + assert timerange == (('date', 'date'), 1274486400, 1438214400) with pytest.raises(Exception, match=r'Incorrect syntax.*'): Arguments.parse_timerange('-') diff --git a/scripts/download_backtest_data.py b/scripts/download_backtest_data.py index 3878f6a34..472442efb 100755 --- a/scripts/download_backtest_data.py +++ b/scripts/download_backtest_data.py @@ -47,7 +47,7 @@ for pair in PAIRS: if not data: print('\tNo data was downloaded') break - + print('\tData was downloaded for period %s - %s' % ( arrow.get(data[0][0] / 1000).format(), arrow.get(data[-1][0] / 1000).format())) From a127e1db073e8b1c16fd2fc8c69256bf4c550c55 Mon Sep 17 00:00:00 2001 From: Anton Date: Sat, 28 Apr 2018 01:40:48 +0300 Subject: [PATCH 5/8] Fix case with empty dict --- freqtrade/optimize/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/freqtrade/optimize/__init__.py b/freqtrade/optimize/__init__.py index 58eb28cb1..1f32ae2d5 100644 --- a/freqtrade/optimize/__init__.py +++ b/freqtrade/optimize/__init__.py @@ -17,6 +17,9 @@ logger = logging.getLogger(__name__) def trim_tickerlist(tickerlist: List[Dict], timerange: Tuple[Tuple, int, int]) -> List[Dict]: + if not tickerlist: + return tickerlist + stype, start, stop = timerange start_index = 0 From 24ab1b5be5457dabbbcda574a8996e5b67366301 Mon Sep 17 00:00:00 2001 From: Anton Date: Tue, 1 May 2018 00:27:05 +0300 Subject: [PATCH 6/8] Fix review comments, documenation update --- docs/backtesting.md | 2 +- docs/bot-usage.md | 2 +- freqtrade/arguments.py | 2 +- freqtrade/exchange/__init__.py | 22 ++-- freqtrade/optimize/__init__.py | 92 +++++++++------- freqtrade/tests/optimize/test_optimize.py | 121 ++++++++++++++++++---- scripts/download_backtest_data.py | 2 +- 7 files changed, 172 insertions(+), 71 deletions(-) diff --git a/docs/backtesting.md b/docs/backtesting.md index 184112f33..b3783a665 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -36,7 +36,7 @@ python3 ./freqtrade/main.py backtesting --realistic-simulation python3 ./freqtrade/main.py backtesting --realistic-simulation --ticker-interval 1m ``` -**Reload your testdata files** +**Update cached pairs with the latest data** ```bash python3 ./freqtrade/main.py backtesting --realistic-simulation --refresh-pairs-cached ``` diff --git a/docs/bot-usage.md b/docs/bot-usage.md index 76e693592..ce5bb46a9 100644 --- a/docs/bot-usage.md +++ b/docs/bot-usage.md @@ -124,7 +124,7 @@ optional arguments: world limitations -r, --refresh-pairs-cached refresh the pairs files in tests/testdata with - the latest data from Bittrex. Use it if you want + the latest data from the exchange. Use it if you want to run your backtesting with up-to-date data. ``` diff --git a/freqtrade/arguments.py b/freqtrade/arguments.py index 8b3cbf72d..e82ec05b5 100644 --- a/freqtrade/arguments.py +++ b/freqtrade/arguments.py @@ -118,7 +118,7 @@ class Arguments(object): ) parser.add_argument( '-r', '--refresh-pairs-cached', - help='refresh the pairs files in tests/testdata with the latest data from Bittrex. \ + help='refresh the pairs files in tests/testdata with the latest data from the exchange. \ Use it if you want to run your backtesting with up-to-date data.', action='store_true', dest='refresh_pairs', diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index dc8e817f5..2601cb836 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -8,7 +8,7 @@ import ccxt import arrow from freqtrade import OperationalException, DependencyException, NetworkException - +from freqtrade.constants import Constants logger = logging.getLogger(__name__) @@ -269,15 +269,21 @@ def get_ticker(pair: str, refresh: Optional[bool] = True) -> dict: @retrier -def get_ticker_history(pair: str, tick_interval: str, since: Optional[int] = None) -> List[Dict]: +def get_ticker_history(pair: str, tick_interval: str, since_ms: Optional[int] = None) -> List[Dict]: try: - # download data until it reaches today now time - # + # last item should be in the time interval [now - tick_interval, now] + till_time_ms = arrow.utcnow().shift( + minutes=-Constants.TICKER_INTERVAL_MINUTES[tick_interval] + ).timestamp * 1000 # it looks as if some exchanges return cached data - # and update it with some delay so 10 mins interval is added + # and they update it one in several minute, so 10 mins interval + # is necessary to skeep downloading of an empty array when all + # chached data was already downloaded + till_time_ms = min(till_time_ms, arrow.utcnow().shift(minutes=-10).timestamp * 1000) + data = [] - while not since or since < arrow.utcnow().shift(minutes=-10).timestamp * 1000: - data_part = _API.fetch_ohlcv(pair, timeframe=tick_interval, since=since) + while not since_ms or since_ms < till_time_ms: + data_part = _API.fetch_ohlcv(pair, timeframe=tick_interval, since=since_ms) if not data_part: break @@ -287,7 +293,7 @@ def get_ticker_history(pair: str, tick_interval: str, since: Optional[int] = Non arrow.get(data_part[-1][0] / 1000).format()) data.extend(data_part) - since = data[-1][0] + 1 + since_ms = data[-1][0] + 1 return data diff --git a/freqtrade/optimize/__init__.py b/freqtrade/optimize/__init__.py index 1f32ae2d5..fcbe0dc5b 100644 --- a/freqtrade/optimize/__init__.py +++ b/freqtrade/optimize/__init__.py @@ -103,7 +103,10 @@ def load_data(datadir: str, pairdata = load_tickerdata_file(datadir, pair, ticker_interval, timerange=timerange) if not pairdata: # download the tickerdata from exchange - download_backtesting_testdata(datadir, pair=pair, interval=ticker_interval) + download_backtesting_testdata(datadir, + pair=pair, + tick_interval=ticker_interval, + timerange=timerange) # and retry reading the pair pairdata = load_tickerdata_file(datadir, pair, ticker_interval, timerange=timerange) result[pair] = pairdata @@ -127,7 +130,7 @@ def download_pairs(datadir, pairs: List[str], try: download_backtesting_testdata(datadir, pair=pair, - interval=ticker_interval, + tick_interval=ticker_interval, timerange=timerange) except BaseException: logger.info( @@ -139,70 +142,81 @@ def download_pairs(datadir, pairs: List[str], return True -def get_start_ts_from_timerange(timerange: Tuple[Tuple, int, int], interval: str) -> int: - if not timerange: - return None +def load_cached_data_for_updating(filename: str, + tick_interval: str, + timerange: Optional[Tuple[Tuple, int, int]]) -> Tuple[list, int]: + """ + Load cached data and choose what part of the data should be updated + """ - if timerange[0][0] == 'date': - return timerange[1] * 1000 + since_ms = None - if timerange[0][1] == 'line': - num_minutes = timerange[2] * Constants.TICKER_INTERVAL_MINUTES[interval] - return arrow.utcnow().shift(minutes=num_minutes).timestamp * 1000 + # user sets timerange, so find the start time + if timerange: + if timerange[0][0] == 'date': + since_ms = timerange[1] * 1000 + elif timerange[0][1] == 'line': + num_minutes = timerange[2] * Constants.TICKER_INTERVAL_MINUTES[tick_interval] + since_ms = arrow.utcnow().shift(minutes=num_minutes).timestamp * 1000 - return None + # read the cached file + if os.path.isfile(filename): + with open(filename, "rt") as file: + data = json.load(file) + # remove the last item, because we are not sure if it is correct + # it could be fetched when the candle was incompleted + if data: + data.pop() + else: + data = [] + + if data: + if since_ms and since_ms < data[0][0]: + # the data is requested for earlier period than the cache has + # so fully redownload all the data + data = [] + else: + # a part of the data was already downloaded, so + # download unexist data only + since_ms = data[-1][0] + 1 + + return (data, since_ms) # FIX: 20180110, suggest rename interval to tick_interval def download_backtesting_testdata(datadir: str, pair: str, - interval: str = '5m', + tick_interval: str = '5m', timerange: Optional[Tuple[Tuple, int, int]] = None) -> bool: """ Download the latest ticker intervals from the exchange for the pairs passed in parameters + The data is downloaded starting from the last correct ticker interval data that + esists in a cache. If timerange starts earlier than the data in the cache, + the full data will be redownloaded + Based on @Rybolov work: https://github.com/rybolov/freqtrade-data :param pairs: list of pairs to download - :param interval: ticker interval + :param tick_interval: ticker interval :param timerange: range of time to download :return: bool """ path = make_testdata_path(datadir) + filepair = pair.replace("/", "_") + filename = os.path.join(path, f'{filepair}-{tick_interval}.json') + logger.info( 'Download the pair: "%s", Interval: %s', pair, - interval + tick_interval ) - filepair = pair.replace("/", "_") - filename = os.path.join(path, '{pair}-{interval}.json'.format( - pair=filepair, - interval=interval, - )) - - since = get_start_ts_from_timerange(timerange, interval) - - if os.path.isfile(filename): - with open(filename, "rt") as file: - data = json.load(file) - - if since: - if since < data[0][0]: - # fully update the data - data = [] - else: - # download unexist data only - since = max(since, data[-1][0] + 1) - else: - # download unexist data only - since = data[-1][0] + 1 - else: - data = [] + data, since_ms = load_cached_data_for_updating(filename, tick_interval, timerange) logger.debug("Current Start: %s", misc.format_ms_time(data[1][0]) if data else 'None') logger.debug("Current End: %s", misc.format_ms_time(data[-1][0]) if data else 'None') - new_data = get_ticker_history(pair=pair, tick_interval=interval, since=since) + new_data = get_ticker_history(pair=pair, tick_interval=tick_interval, since_ms=since_ms) data.extend(new_data) logger.debug("New Start: %s", misc.format_ms_time(data[0][0])) diff --git a/freqtrade/tests/optimize/test_optimize.py b/freqtrade/tests/optimize/test_optimize.py index e58f5bb2d..f0c429792 100644 --- a/freqtrade/tests/optimize/test_optimize.py +++ b/freqtrade/tests/optimize/test_optimize.py @@ -10,7 +10,7 @@ from freqtrade import optimize from freqtrade.misc import file_dump_json from freqtrade.optimize.__init__ import make_testdata_path, download_pairs, \ download_backtesting_testdata, load_tickerdata_file, trim_tickerlist, \ - get_start_ts_from_timerange + load_cached_data_for_updating from freqtrade.tests.conftest import log_has # Change this if modifying UNITTEST/BTC testdatafile @@ -147,26 +147,107 @@ def test_download_pairs(ticker_history, mocker) -> None: _clean_test_file(file2_5) -def test_get_start_ts_from_timerange(mocker) -> None: - start = get_start_ts_from_timerange(None, '1m') - assert start is None +def test_load_cached_data_for_updating(mocker) -> None: + datadir = os.path.join(os.path.dirname(__file__), '..', 'testdata') - # check 'date' - start = get_start_ts_from_timerange((('date', 'date'), 1000, 2000), '1m') - assert start == 1000 * 1000 + test_data = None + test_filename = os.path.join(datadir, 'UNITTEST_BTC-1m.json') + with open(test_filename, "rt") as file: + test_data = json.load(file) - start = get_start_ts_from_timerange((('date', 'date'), 1000, 2000), '5m') - assert start == 1000 * 1000 + # change now time to test 'line' cases + # now = last cached item + 1 hour + now_ts = test_data[-1][0] / 1000 + 60 * 60 + mocker.patch('arrow.utcnow', return_value=arrow.get(now_ts)) - # check 'line' - mock_now = arrow.get(1367900664) - mocker.patch('arrow.utcnow', return_value=mock_now) + # timeframe starts earlier than the cached data + # should fully update data + timerange = (('date', None), test_data[0][0] / 1000 - 1, None) + data, start_ts = load_cached_data_for_updating(test_filename, + '1m', + timerange) + assert data == [] + assert start_ts == test_data[0][0] - 1000 - start = get_start_ts_from_timerange(((None, 'line'), None, -200), '1m') - assert start == (1367900664 - 200 * 60) * 1000 + # same with 'line' timeframe + num_lines = (test_data[-1][0] - test_data[1][0]) / 1000 / 60 + 120 + data, start_ts = load_cached_data_for_updating(test_filename, + '1m', + ((None, 'line'), None, -num_lines)) + assert data == [] + assert start_ts < test_data[0][0] - 1 - start = get_start_ts_from_timerange(((None, 'line'), None, -200), '5m') - assert start == (1367900664 - 5 * 200 * 60) * 1000 + # timeframe starts in the center of the cached data + # should return the chached data w/o the last item + timerange = (('date', None), test_data[0][0] / 1000 + 1, None) + data, start_ts = load_cached_data_for_updating(test_filename, + '1m', + timerange) + assert data == test_data[:-1] + assert test_data[-2][0] < start_ts < test_data[-1][0] + + # same with 'line' timeframe + num_lines = (test_data[-1][0] - test_data[1][0]) / 1000 / 60 + 30 + timerange = ((None, 'line'), None, -num_lines) + data, start_ts = load_cached_data_for_updating(test_filename, + '1m', + timerange) + assert data == test_data[:-1] + assert test_data[-2][0] < start_ts < test_data[-1][0] + + # timeframe starts after the chached data + # should return the chached data w/o the last item + timerange = (('date', None), test_data[-1][0] / 1000 + 1, None) + data, start_ts = load_cached_data_for_updating(test_filename, + '1m', + timerange) + assert data == test_data[:-1] + assert test_data[-2][0] < start_ts < test_data[-1][0] + + # same with 'line' timeframe + num_lines = 30 + timerange = ((None, 'line'), None, -num_lines) + data, start_ts = load_cached_data_for_updating(test_filename, + '1m', + timerange) + assert data == test_data[:-1] + assert test_data[-2][0] < start_ts < test_data[-1][0] + + # no timeframe is set + # should return the chached data w/o the last item + num_lines = 30 + timerange = ((None, 'line'), None, -num_lines) + data, start_ts = load_cached_data_for_updating(test_filename, + '1m', + timerange) + assert data == test_data[:-1] + assert test_data[-2][0] < start_ts < test_data[-1][0] + + # no datafile exist + # should return timestamp start time + timerange = (('date', None), now_ts - 10000, None) + data, start_ts = load_cached_data_for_updating(test_filename + 'unexist', + '1m', + timerange) + assert data == [] + assert start_ts == (now_ts - 10000) * 1000 + + # same with 'line' timeframe + num_lines = 30 + timerange = ((None, 'line'), None, -num_lines) + data, start_ts = load_cached_data_for_updating(test_filename + 'unexist', + '1m', + timerange) + assert data == [] + assert start_ts == (now_ts - num_lines * 60) * 1000 + + # no datafile exist, no timeframe is set + # should return an empty array and None + data, start_ts = load_cached_data_for_updating(test_filename + 'unexist', + '1m', + None) + assert data == [] + assert start_ts is None def test_download_pairs_exception(ticker_history, mocker, caplog) -> None: @@ -192,7 +273,7 @@ def test_download_backtesting_testdata(ticker_history, mocker) -> None: # Download a 1 min ticker file file1 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'XEL_BTC-1m.json') _backup_file(file1) - download_backtesting_testdata(None, pair="XEL/BTC", interval='1m') + download_backtesting_testdata(None, pair="XEL/BTC", tick_interval='1m') assert os.path.isfile(file1) is True _clean_test_file(file1) @@ -200,7 +281,7 @@ def test_download_backtesting_testdata(ticker_history, mocker) -> None: file2 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'STORJ_BTC-5m.json') _backup_file(file2) - download_backtesting_testdata(None, pair="STORJ/BTC", interval='5m') + download_backtesting_testdata(None, pair="STORJ/BTC", tick_interval='5m') assert os.path.isfile(file2) is True _clean_test_file(file2) @@ -212,8 +293,8 @@ def test_download_backtesting_testdata2(mocker) -> None: ] mocker.patch('freqtrade.misc.file_dump_json', return_value=None) mocker.patch('freqtrade.optimize.__init__.get_ticker_history', return_value=tick) - assert download_backtesting_testdata(None, pair="UNITTEST/BTC", interval='1m') - assert download_backtesting_testdata(None, pair="UNITTEST/BTC", interval='3m') + assert download_backtesting_testdata(None, pair="UNITTEST/BTC", tick_interval='1m') + assert download_backtesting_testdata(None, pair="UNITTEST/BTC", tick_interval='3m') def test_load_tickerdata_file() -> None: diff --git a/scripts/download_backtest_data.py b/scripts/download_backtest_data.py index 472442efb..1c73eae03 100755 --- a/scripts/download_backtest_data.py +++ b/scripts/download_backtest_data.py @@ -43,7 +43,7 @@ for pair in PAIRS: for tick_interval in TICKER_INTERVALS: print(f'downloading pair {pair}, interval {tick_interval}') - data = exchange.get_ticker_history(pair, tick_interval, since=since_time) + data = exchange.get_ticker_history(pair, tick_interval, since_ms=since_time) if not data: print('\tNo data was downloaded') break From 2bfce64e6a9ac83bf933e630077cac8a0ba7d5df Mon Sep 17 00:00:00 2001 From: Anton Date: Fri, 4 May 2018 13:38:51 +0300 Subject: [PATCH 7/8] Fix conflicts --- freqtrade/exchange/__init__.py | 5 ++--- freqtrade/optimize/__init__.py | 5 ++--- scripts/convert_backtestdata.py | 5 ++--- scripts/plot_profit.py | 13 +++---------- 4 files changed, 9 insertions(+), 19 deletions(-) diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index d0d6d4c04..54b88c79c 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -8,8 +8,7 @@ from datetime import datetime import ccxt import arrow -from freqtrade import OperationalException, DependencyException, TemporaryError -from freqtrade.constants import Constants +from freqtrade import constants, OperationalException, DependencyException, TemporaryError logger = logging.getLogger(__name__) @@ -283,7 +282,7 @@ def get_ticker_history(pair: str, tick_interval: str, since_ms: Optional[int] = try: # last item should be in the time interval [now - tick_interval, now] till_time_ms = arrow.utcnow().shift( - minutes=-Constants.TICKER_INTERVAL_MINUTES[tick_interval] + minutes=-constants.TICKER_INTERVAL_MINUTES[tick_interval] ).timestamp * 1000 # it looks as if some exchanges return cached data # and they update it one in several minute, so 10 mins interval diff --git a/freqtrade/optimize/__init__.py b/freqtrade/optimize/__init__.py index a93564fd4..168481920 100644 --- a/freqtrade/optimize/__init__.py +++ b/freqtrade/optimize/__init__.py @@ -7,9 +7,8 @@ import os import arrow from typing import Optional, List, Dict, Tuple -from freqtrade import misc +from freqtrade import misc, constants from freqtrade.exchange import get_ticker_history -from freqtrade.constants import Constants from user_data.hyperopt_conf import hyperopt_optimize_conf @@ -156,7 +155,7 @@ def load_cached_data_for_updating(filename: str, if timerange[0][0] == 'date': since_ms = timerange[1] * 1000 elif timerange[0][1] == 'line': - num_minutes = timerange[2] * Constants.TICKER_INTERVAL_MINUTES[tick_interval] + num_minutes = timerange[2] * constants.TICKER_INTERVAL_MINUTES[tick_interval] since_ms = arrow.utcnow().shift(minutes=num_minutes).timestamp * 1000 # read the cached file diff --git a/scripts/convert_backtestdata.py b/scripts/convert_backtestdata.py index d93356cf6..698c1c829 100755 --- a/scripts/convert_backtestdata.py +++ b/scripts/convert_backtestdata.py @@ -21,9 +21,8 @@ from typing import List, Dict import gzip from freqtrade.arguments import Arguments -from freqtrade import misc +from freqtrade import misc, constants from pandas import DataFrame -from freqtrade.constants import Constants import dateutil.parser @@ -139,7 +138,7 @@ def convert_main(args: Namespace) -> None: # default to adding 'm' to end of minutes for new interval name interval = str(minutes) + 'm' # but check if there is a mapping between int and string also - for str_interval, minutes_interval in Constants.TICKER_INTERVAL_MINUTES.items(): + for str_interval, minutes_interval in constants.TICKER_INTERVAL_MINUTES.items(): if minutes_interval == minutes: interval = str_interval break diff --git a/scripts/plot_profit.py b/scripts/plot_profit.py index 19cf8b11a..c1e1d068f 100755 --- a/scripts/plot_profit.py +++ b/scripts/plot_profit.py @@ -24,21 +24,14 @@ import plotly.graph_objs as go from freqtrade.arguments import Arguments from freqtrade.configuration import Configuration from freqtrade.analyze import Analyze -<<<<<<< HEAD -from freqtrade.constants import Constants -======= ->>>>>>> bddf009a2b6d0e1a19cca558887ce972e99a6238 +from freqtradeimport constants + import freqtrade.optimize as optimize import freqtrade.misc as misc -<<<<<<< HEAD -logger = logging.getLogger('freqtrade') -======= logger = logging.getLogger(__name__) ->>>>>>> bddf009a2b6d0e1a19cca558887ce972e99a6238 - # data:: [ pair, profit-%, enter, exit, time, duration] # data:: ["ETH/BTC", 0.0023975, "1515598200", "1515602100", "2018-01-10 07:30:00+00:00", 65] @@ -198,7 +191,7 @@ def define_index(min_date: int, max_date: int, interval: str) -> int: """ Return the index of a specific date """ - interval_minutes = Constants.TICKER_INTERVAL_MINUTES[interval] + interval_minutes = constants.TICKER_INTERVAL_MINUTES[interval] return int((max_date - min_date) / (interval_minutes * 60)) From 932b65da2798e5d64581724e2d44faf852095fa7 Mon Sep 17 00:00:00 2001 From: Anton Date: Fri, 4 May 2018 13:59:50 +0300 Subject: [PATCH 8/8] Fix test_optimize.py --- freqtrade/tests/optimize/test_optimize.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/tests/optimize/test_optimize.py b/freqtrade/tests/optimize/test_optimize.py index 5331df5e7..e7f3b18fd 100644 --- a/freqtrade/tests/optimize/test_optimize.py +++ b/freqtrade/tests/optimize/test_optimize.py @@ -294,8 +294,8 @@ def test_download_backtesting_testdata2(mocker) -> None: json_dump_mock = mocker.patch('freqtrade.misc.file_dump_json', return_value=None) mocker.patch('freqtrade.optimize.__init__.get_ticker_history', return_value=tick) - download_backtesting_testdata(None, pair="UNITTEST/BTC", interval='1m') - download_backtesting_testdata(None, pair="UNITTEST/BTC", interval='3m') + download_backtesting_testdata(None, pair="UNITTEST/BTC", tick_interval='1m') + download_backtesting_testdata(None, pair="UNITTEST/BTC", tick_interval='3m') assert json_dump_mock.call_count == 2