This commit is contained in:
Anton Rudau 2018-04-30 21:27:14 +00:00 committed by GitHub
commit 6cd5a7faad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 343 additions and 63 deletions

View File

@ -36,7 +36,7 @@ python3 ./freqtrade/main.py backtesting --realistic-simulation
python3 ./freqtrade/main.py backtesting --realistic-simulation --ticker-interval 1m python3 ./freqtrade/main.py backtesting --realistic-simulation --ticker-interval 1m
``` ```
**Reload your testdata files** **Update cached pairs with the latest data**
```bash ```bash
python3 ./freqtrade/main.py backtesting --realistic-simulation --refresh-pairs-cached python3 ./freqtrade/main.py backtesting --realistic-simulation --refresh-pairs-cached
``` ```
@ -80,12 +80,9 @@ The full timerange specification:
- Use last 123 tickframes of data: `--timerange=-123` - Use last 123 tickframes of data: `--timerange=-123`
- Use first 123 tickframes of data: `--timerange=123-` - Use first 123 tickframes of data: `--timerange=123-`
- Use tickframes from line 123 through 456: `--timerange=123-456` - Use tickframes from line 123 through 456: `--timerange=123-456`
- Use tickframes till 2018/01/31: `--timerange=-20180131`
- Use tickframes since 2018/01/31: `--timerange=20180131-`
Incoming feature, not implemented yet: - Use tickframes since 2018/01/31 till 2018/03/01 : `--timerange=20180131-20180301`
- `--timerange=-20180131`
- `--timerange=20180101-`
- `--timerange=20180101-20181231`
**Update testdata directory** **Update testdata directory**

View File

@ -124,7 +124,7 @@ optional arguments:
world limitations world limitations
-r, --refresh-pairs-cached -r, --refresh-pairs-cached
refresh the pairs files in tests/testdata with 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. to run your backtesting with up-to-date data.
``` ```

View File

@ -6,6 +6,7 @@ import argparse
import logging import logging
import os import os
import re import re
import arrow
from typing import List, Tuple, Optional from typing import List, Tuple, Optional
from freqtrade import __version__ from freqtrade import __version__
@ -117,7 +118,7 @@ class Arguments(object):
) )
parser.add_argument( parser.add_argument(
'-r', '--refresh-pairs-cached', '-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.', Use it if you want to run your backtesting with up-to-date data.',
action='store_true', action='store_true',
dest='refresh_pairs', dest='refresh_pairs',
@ -228,12 +229,16 @@ class Arguments(object):
stop = None stop = None
if stype[0]: if stype[0]:
start = rvals[index] start = rvals[index]
if stype[0] != 'date': if stype[0] == 'date':
start = arrow.get(start, 'YYYYMMDD').timestamp
else:
start = int(start) start = int(start)
index += 1 index += 1
if stype[1]: if stype[1]:
stop = rvals[index] stop = rvals[index]
if stype[1] != 'date': if stype[1] == 'date':
stop = arrow.get(stop, 'YYYYMMDD').timestamp
else:
stop = int(stop) stop = int(stop)
return stype, start, stop return stype, start, stop
raise Exception('Incorrect syntax for timerange "%s"' % text) raise Exception('Incorrect syntax for timerange "%s"' % text)
@ -265,3 +270,17 @@ class Arguments(object):
help='Export files to given dir', help='Export files to given dir',
dest='export', dest='export',
default=None) 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')

View File

@ -8,7 +8,7 @@ import ccxt
import arrow import arrow
from freqtrade import OperationalException, DependencyException, NetworkException from freqtrade import OperationalException, DependencyException, NetworkException
from freqtrade.constants import Constants
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -269,9 +269,34 @@ def get_ticker(pair: str, refresh: Optional[bool] = True) -> dict:
@retrier @retrier
def get_ticker_history(pair: str, tick_interval: str) -> List[Dict]: def get_ticker_history(pair: str, tick_interval: str, since_ms: Optional[int] = None) -> List[Dict]:
try: try:
return _API.fetch_ohlcv(pair, timeframe=tick_interval) # 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 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_ms or since_ms < till_time_ms:
data_part = _API.fetch_ohlcv(pair, timeframe=tick_interval, since=since_ms)
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_ms = data[-1][0] + 1
return data
except ccxt.NetworkError as e: except ccxt.NetworkError as e:
raise NetworkException( raise NetworkException(
'Could not load ticker history due to networking error. Message: {}'.format(e) 'Could not load ticker history due to networking error. Message: {}'.format(e)

View File

@ -4,25 +4,47 @@ import gzip
import json import json
import logging import logging
import os import os
import arrow
from typing import Optional, List, Dict, Tuple from typing import Optional, List, Dict, Tuple
from freqtrade import misc from freqtrade import misc
from freqtrade.exchange import get_ticker_history from freqtrade.exchange import get_ticker_history
from freqtrade.constants import Constants
from user_data.hyperopt_conf import hyperopt_optimize_conf from user_data.hyperopt_conf import hyperopt_optimize_conf
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def trim_tickerlist(tickerlist: List[Dict], timerange: Tuple[Tuple, int, int]) -> List[Dict]: def trim_tickerlist(tickerlist: List[Dict], timerange: Tuple[Tuple, int, int]) -> List[Dict]:
stype, start, stop = timerange if not tickerlist:
if stype == (None, 'line'): return tickerlist
return tickerlist[stop:]
elif stype == ('line', None):
return tickerlist[0:start]
elif stype == ('index', 'index'):
return tickerlist[start:stop]
return tickerlist stype, start, stop = timerange
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( def load_tickerdata_file(
@ -75,13 +97,16 @@ def load_data(datadir: str,
# If the user force the refresh of pairs # If the user force the refresh of pairs
if refresh_pairs: if refresh_pairs:
logger.info('Download data for all pairs and store them in %s', datadir) 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: for pair in _pairs:
pairdata = load_tickerdata_file(datadir, pair, ticker_interval, timerange=timerange) pairdata = load_tickerdata_file(datadir, pair, ticker_interval, timerange=timerange)
if not pairdata: if not pairdata:
# download the tickerdata from exchange # 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 # and retry reading the pair
pairdata = load_tickerdata_file(datadir, pair, ticker_interval, timerange=timerange) pairdata = load_tickerdata_file(datadir, pair, ticker_interval, timerange=timerange)
result[pair] = pairdata result[pair] = pairdata
@ -97,11 +122,16 @@ 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 each pairs passed in parameters, download the ticker intervals"""
for pair in pairs: for pair in pairs:
try: try:
download_backtesting_testdata(datadir, pair=pair, interval=ticker_interval) download_backtesting_testdata(datadir,
pair=pair,
tick_interval=ticker_interval,
timerange=timerange)
except BaseException: except BaseException:
logger.info( logger.info(
'Failed to download the pair: "%s", Interval: %s', 'Failed to download the pair: "%s", Interval: %s',
@ -112,45 +142,85 @@ def download_pairs(datadir, pairs: List[str], ticker_interval: str) -> bool:
return True return True
# FIX: 20180110, suggest rename interval to tick_interval def load_cached_data_for_updating(filename: str,
def download_backtesting_testdata(datadir: str, pair: str, interval: str = '5m') -> bool: tick_interval: str,
timerange: Optional[Tuple[Tuple, int, int]]) -> Tuple[list, int]:
""" """
Download the latest 1 and 5 ticker intervals from Bittrex for the pairs passed in parameters Load cached data and choose what part of the data should be updated
"""
since_ms = None
# 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
# 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,
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 Based on @Rybolov work: https://github.com/rybolov/freqtrade-data
:param pairs: list of pairs to download :param pairs: list of pairs to download
:param tick_interval: ticker interval
:param timerange: range of time to download
:return: bool :return: bool
""" """
path = make_testdata_path(datadir) path = make_testdata_path(datadir)
filepair = pair.replace("/", "_")
filename = os.path.join(path, f'{filepair}-{tick_interval}.json')
logger.info( logger.info(
'Download the pair: "%s", Interval: %s', 'Download the pair: "%s", Interval: %s',
pair, pair,
interval tick_interval
) )
filepair = pair.replace("/", "_") data, since_ms = load_cached_data_for_updating(filename, tick_interval, timerange)
filename = os.path.join(path, '{pair}-{interval}.json'.format(
pair=filepair,
interval=interval,
))
if os.path.isfile(filename): logger.debug("Current Start: %s", misc.format_ms_time(data[1][0]) if data else 'None')
with open(filename, "rt") as file: logger.debug("Current End: %s", misc.format_ms_time(data[-1][0]) if data else 'None')
data = json.load(file)
logger.debug("Current Start: %s", misc.format_ms_time(data[1][0])) new_data = get_ticker_history(pair=pair, tick_interval=tick_interval, since_ms=since_ms)
logger.debug("Current End: %s", misc.format_ms_time(data[-1:][0][0])) data.extend(new_data)
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("New Start: %s", misc.format_ms_time(data[0][0])) 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])) logger.debug("New End: %s", misc.format_ms_time(data[-1][0]))
data = sorted(data, key=lambda data: data[0])
misc.file_dump_json(filename, data) misc.file_dump_json(filename, data)

View File

@ -321,6 +321,15 @@ def test_get_ticker(default_conf, mocker):
get_ticker(pair='ETH/BTC', refresh=True) 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): def test_get_ticker_history(default_conf, mocker):
api_mock = MagicMock() api_mock = MagicMock()
tick = [ tick = [
@ -334,7 +343,7 @@ def test_get_ticker_history(default_conf, mocker):
] ]
] ]
type(api_mock).has = PropertyMock(return_value={'fetchOHLCV': True}) 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) mocker.patch('freqtrade.exchange._API', api_mock)
# retrieve original ticker # retrieve original ticker
@ -357,7 +366,7 @@ def test_get_ticker_history(default_conf, mocker):
10, # volume (in quote currency) 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) mocker.patch('freqtrade.exchange._API', api_mock)
ticks = get_ticker_history('ETH/BTC', default_conf['ticker_interval']) ticks = get_ticker_history('ETH/BTC', default_conf['ticker_interval'])

View File

@ -3,12 +3,14 @@
import json import json
import os import os
import uuid import uuid
import arrow
from shutil import copyfile from shutil import copyfile
from freqtrade import optimize from freqtrade import optimize
from freqtrade.misc import file_dump_json from freqtrade.misc import file_dump_json
from freqtrade.optimize.__init__ import make_testdata_path, download_pairs, \ 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, \
load_cached_data_for_updating
from freqtrade.tests.conftest import log_has from freqtrade.tests.conftest import log_has
# Change this if modifying UNITTEST/BTC testdatafile # Change this if modifying UNITTEST/BTC testdatafile
@ -145,6 +147,109 @@ def test_download_pairs(ticker_history, mocker) -> None:
_clean_test_file(file2_5) _clean_test_file(file2_5)
def test_load_cached_data_for_updating(mocker) -> None:
datadir = os.path.join(os.path.dirname(__file__), '..', 'testdata')
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)
# 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))
# 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
# 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
# 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: 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__.get_ticker_history', return_value=ticker_history)
mocker.patch('freqtrade.optimize.__init__.download_backtesting_testdata', mocker.patch('freqtrade.optimize.__init__.download_backtesting_testdata',
@ -168,7 +273,7 @@ def test_download_backtesting_testdata(ticker_history, mocker) -> None:
# Download a 1 min ticker file # Download a 1 min ticker file
file1 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'XEL_BTC-1m.json') file1 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'XEL_BTC-1m.json')
_backup_file(file1) _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 assert os.path.isfile(file1) is True
_clean_test_file(file1) _clean_test_file(file1)
@ -176,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') file2 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'STORJ_BTC-5m.json')
_backup_file(file2) _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 assert os.path.isfile(file2) is True
_clean_test_file(file2) _clean_test_file(file2)
@ -188,8 +293,8 @@ def test_download_backtesting_testdata2(mocker) -> None:
] ]
mocker.patch('freqtrade.misc.file_dump_json', return_value=None) mocker.patch('freqtrade.misc.file_dump_json', return_value=None)
mocker.patch('freqtrade.optimize.__init__.get_ticker_history', return_value=tick) 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", tick_interval='1m')
assert download_backtesting_testdata(None, pair="UNITTEST/BTC", interval='3m') assert download_backtesting_testdata(None, pair="UNITTEST/BTC", tick_interval='3m')
def test_load_tickerdata_file() -> None: def test_load_tickerdata_file() -> None:
@ -221,12 +326,12 @@ def test_trim_tickerlist() -> None:
ticker_list_len = len(ticker_list) ticker_list_len = len(ticker_list)
# Test the pattern ^(-\d+)$ # Test the pattern ^(-\d+)$
# This pattern remove X element from the beginning # This pattern uses the latest N elements
timerange = ((None, 'line'), None, 5) timerange = ((None, 'line'), None, -5)
ticker = trim_tickerlist(ticker_list, timerange) ticker = trim_tickerlist(ticker_list, timerange)
ticker_len = len(ticker) 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[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 assert ticker_list[-1] is ticker[-1] # The last element must be the same
@ -251,6 +356,37 @@ def test_trim_tickerlist() -> None:
assert ticker_list[5] is ticker[0] # The list starts at the index 5 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) 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 # Test a wrong pattern
# This pattern must return the list unchanged # This pattern must return the list unchanged
timerange = ((None, None), None, 5) timerange = ((None, None), None, 5)

View File

@ -89,6 +89,13 @@ def test_parse_args_dynamic_whitelist_invalid_values() -> None:
def test_parse_timerange_incorrect() -> None: def test_parse_timerange_incorrect() -> None:
assert ((None, 'line'), None, -200) == Arguments.parse_timerange('-200') assert ((None, 'line'), None, -200) == Arguments.parse_timerange('-200')
assert (('line', None), 200, None) == 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')
timerange = Arguments.parse_timerange('20100522-20150730')
assert timerange == (('date', 'date'), 1274486400, 1438214400)
with pytest.raises(Exception, match=r'Incorrect syntax.*'): with pytest.raises(Exception, match=r'Incorrect syntax.*'):
Arguments.parse_timerange('-') Arguments.parse_timerange('-')

View File

@ -4,6 +4,7 @@
import json import json
import sys import sys
import os import os
import arrow
from freqtrade import (exchange, arguments, misc) from freqtrade import (exchange, arguments, misc)
@ -25,17 +26,33 @@ dl_path = DEFAULT_DL_PATH
if args.export and os.path.exists(args.export): if args.export and os.path.exists(args.export):
dl_path = 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}') print(f'About to download pairs: {PAIRS} to {dl_path}')
# Init Bittrex exchange # Init exchange
exchange._API = exchange.init_ccxt({'key': '', exchange._API = exchange.init_ccxt({'key': '',
'secret': '', 'secret': '',
'name': 'bittrex'}) 'name': args.exchange})
for pair in PAIRS: for pair in PAIRS:
for tick_interval in TICKER_INTERVALS: for tick_interval in TICKER_INTERVALS:
print(f'downloading pair {pair}, interval {tick_interval}') print(f'downloading pair {pair}, interval {tick_interval}')
data = exchange.get_ticker_history(pair, tick_interval)
data = exchange.get_ticker_history(pair, tick_interval, since_ms=since_time)
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()))
# save data
pair_print = pair.replace('/', '_') pair_print = pair.replace('/', '_')
filename = f'{pair_print}-{tick_interval}.json' filename = f'{pair_print}-{tick_interval}.json'
misc.file_dump_json(os.path.join(dl_path, filename), data) misc.file_dump_json(os.path.join(dl_path, filename), data)