Merge remote-tracking branch 'upstream/develop' into feature/fetch-public-trades
This commit is contained in:
@@ -17,7 +17,8 @@ from freqtrade.data.history import (get_timerange, load_data, load_pair_history,
|
||||
validate_backtest_data)
|
||||
from freqtrade.data.history.idatahandler import IDataHandler
|
||||
from freqtrade.enums import CandleType
|
||||
from tests.conftest import generate_test_data, log_has, log_has_re
|
||||
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds
|
||||
from tests.conftest import generate_test_data, generate_trades_history, log_has, log_has_re
|
||||
from tests.data.test_history import _clean_test_file
|
||||
|
||||
|
||||
@@ -51,6 +52,49 @@ def test_trades_to_ohlcv(trades_history_df, caplog):
|
||||
assert 'close' in df.columns
|
||||
assert df.iloc[0, :]['high'] == 0.019627
|
||||
assert df.iloc[0, :]['low'] == 0.019626
|
||||
assert df.iloc[0, :]['date'] == pd.Timestamp('2019-08-14 15:59:00+0000')
|
||||
|
||||
df_1h = trades_to_ohlcv(trades_history_df, '1h')
|
||||
assert len(df_1h) == 1
|
||||
assert df_1h.iloc[0, :]['high'] == 0.019627
|
||||
assert df_1h.iloc[0, :]['low'] == 0.019626
|
||||
assert df_1h.iloc[0, :]['date'] == pd.Timestamp('2019-08-14 15:00:00+0000')
|
||||
|
||||
df_1s = trades_to_ohlcv(trades_history_df, '1s')
|
||||
assert len(df_1s) == 2
|
||||
assert df_1s.iloc[0, :]['high'] == 0.019627
|
||||
assert df_1s.iloc[0, :]['low'] == 0.019627
|
||||
assert df_1s.iloc[0, :]['date'] == pd.Timestamp('2019-08-14 15:59:49+0000')
|
||||
assert df_1s.iloc[-1, :]['date'] == pd.Timestamp('2019-08-14 15:59:59+0000')
|
||||
|
||||
|
||||
@pytest.mark.parametrize('timeframe,rows,days,candles,start,end,weekday', [
|
||||
('1s', 20_000, 5, 19522, '2020-01-01 00:00:05', '2020-01-05 23:59:27', None),
|
||||
('1m', 20_000, 5, 6745, '2020-01-01 00:00:00', '2020-01-05 23:59:00', None),
|
||||
('5m', 20_000, 5, 1440, '2020-01-01 00:00:00', '2020-01-05 23:55:00', None),
|
||||
('15m', 20_000, 5, 480, '2020-01-01 00:00:00', '2020-01-05 23:45:00', None),
|
||||
('1h', 20_000, 5, 120, '2020-01-01 00:00:00', '2020-01-05 23:00:00', None),
|
||||
('2h', 20_000, 5, 60, '2020-01-01 00:00:00', '2020-01-05 22:00:00', None),
|
||||
('4h', 20_000, 5, 30, '2020-01-01 00:00:00', '2020-01-05 20:00:00', None),
|
||||
('8h', 20_000, 5, 15, '2020-01-01 00:00:00', '2020-01-05 16:00:00', None),
|
||||
('12h', 20_000, 5, 10, '2020-01-01 00:00:00', '2020-01-05 12:00:00', None),
|
||||
('1d', 20_000, 5, 5, '2020-01-01 00:00:00', '2020-01-05 00:00:00', 'Sunday'),
|
||||
('7d', 20_000, 37, 6, '2020-01-06 00:00:00', '2020-02-10 00:00:00', 'Monday'),
|
||||
('1w', 20_000, 37, 6, '2020-01-06 00:00:00', '2020-02-10 00:00:00', 'Monday'),
|
||||
('1M', 20_000, 74, 3, '2020-01-01 00:00:00', '2020-03-01 00:00:00', None),
|
||||
('3M', 20_000, 100, 2, '2020-01-01 00:00:00', '2020-04-01 00:00:00', None),
|
||||
('1y', 20_000, 1000, 3, '2020-01-01 00:00:00', '2022-01-01 00:00:00', None),
|
||||
])
|
||||
def test_trades_to_ohlcv_multi(timeframe, rows, days, candles, start, end, weekday):
|
||||
trades_history = generate_trades_history(n_rows=rows, days=days)
|
||||
df = trades_to_ohlcv(trades_history, timeframe)
|
||||
assert not df.empty
|
||||
assert len(df) == candles
|
||||
assert df.iloc[0, :]['date'] == pd.Timestamp(f'{start}+0000')
|
||||
assert df.iloc[-1, :]['date'] == pd.Timestamp(f'{end}+0000')
|
||||
if weekday:
|
||||
# Weekday is only relevant for daily and weekly candles.
|
||||
assert df.iloc[-1, :]['date'].day_name() == weekday
|
||||
|
||||
|
||||
def test_ohlcv_fill_up_missing_data(testdatadir, caplog):
|
||||
@@ -132,6 +176,45 @@ def test_ohlcv_fill_up_missing_data2(caplog):
|
||||
f"{len(data)} - after: {len(data2)}.*", caplog)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('timeframe', [
|
||||
'1s', '1m', '5m', '15m', '1h', '2h', '4h', '8h', '12h', '1d', '7d', '1w', '1M', '3M', '1y'
|
||||
])
|
||||
def test_ohlcv_to_dataframe_multi(timeframe):
|
||||
data = generate_test_data(timeframe, 180)
|
||||
assert len(data) == 180
|
||||
df = ohlcv_to_dataframe(data, timeframe, 'UNITTEST/USDT')
|
||||
assert len(df) == len(data) - 1
|
||||
df1 = ohlcv_to_dataframe(data, timeframe, 'UNITTEST/USDT', drop_incomplete=False)
|
||||
assert len(df1) == len(data)
|
||||
assert data.equals(df1)
|
||||
|
||||
data1 = data.copy()
|
||||
if timeframe in ('1M', '3M', '1y'):
|
||||
data1.loc[:, 'date'] = data1.loc[:, 'date'] + pd.to_timedelta('1w')
|
||||
else:
|
||||
# Shift by half a timeframe
|
||||
data1.loc[:, 'date'] = data1.loc[:, 'date'] + (pd.to_timedelta(timeframe) / 2)
|
||||
df2 = ohlcv_to_dataframe(data1, timeframe, 'UNITTEST/USDT')
|
||||
|
||||
assert len(df2) == len(data) - 1
|
||||
tfs = timeframe_to_seconds(timeframe)
|
||||
tfm = timeframe_to_minutes(timeframe)
|
||||
if 1 <= tfm < 10000:
|
||||
# minute based resampling does not work on timeframes >= 1 week
|
||||
ohlcv_dict = {
|
||||
'open': 'first',
|
||||
'high': 'max',
|
||||
'low': 'min',
|
||||
'close': 'last',
|
||||
'volume': 'sum'
|
||||
}
|
||||
dfs = data1.resample(f"{tfs}s", on='date').agg(ohlcv_dict).reset_index(drop=False)
|
||||
dfm = data1.resample(f"{tfm}min", on='date').agg(ohlcv_dict).reset_index(drop=False)
|
||||
|
||||
assert dfs.equals(dfm)
|
||||
assert dfs.equals(df1)
|
||||
|
||||
|
||||
def test_ohlcv_to_dataframe_1M():
|
||||
|
||||
# Monthly ticks from 2019-09-01 to 2023-07-01
|
||||
|
||||
@@ -148,19 +148,25 @@ def test_jsondatahandler_ohlcv_load(testdatadir, caplog):
|
||||
def test_datahandler_ohlcv_data_min_max(testdatadir):
|
||||
dh = JsonDataHandler(testdatadir)
|
||||
min_max = dh.ohlcv_data_min_max('UNITTEST/BTC', '5m', 'spot')
|
||||
assert len(min_max) == 2
|
||||
assert len(min_max) == 3
|
||||
|
||||
# Empty pair
|
||||
min_max = dh.ohlcv_data_min_max('UNITTEST/BTC', '8m', 'spot')
|
||||
assert len(min_max) == 2
|
||||
assert len(min_max) == 3
|
||||
assert min_max[0] == datetime.fromtimestamp(0, tz=timezone.utc)
|
||||
assert min_max[0] == min_max[1]
|
||||
# Empty pair2
|
||||
min_max = dh.ohlcv_data_min_max('NOPAIR/XXX', '4m', 'spot')
|
||||
assert len(min_max) == 2
|
||||
min_max = dh.ohlcv_data_min_max('NOPAIR/XXX', '41m', 'spot')
|
||||
assert len(min_max) == 3
|
||||
assert min_max[0] == datetime.fromtimestamp(0, tz=timezone.utc)
|
||||
assert min_max[0] == min_max[1]
|
||||
|
||||
# Existing pair ...
|
||||
min_max = dh.ohlcv_data_min_max('UNITTEST/BTC', '1m', 'spot')
|
||||
assert len(min_max) == 3
|
||||
assert min_max[0] == datetime(2017, 11, 4, 23, 2, tzinfo=timezone.utc)
|
||||
assert min_max[1] == datetime(2017, 11, 14, 22, 59, tzinfo=timezone.utc)
|
||||
|
||||
|
||||
def test_datahandler__check_empty_df(testdatadir, caplog):
|
||||
dh = JsonDataHandler(testdatadir)
|
||||
@@ -513,11 +519,11 @@ def test_gethandlerclass():
|
||||
|
||||
def test_get_datahandler(testdatadir):
|
||||
dh = get_datahandler(testdatadir, 'json')
|
||||
assert type(dh) == JsonDataHandler
|
||||
assert isinstance(dh, JsonDataHandler)
|
||||
dh = get_datahandler(testdatadir, 'jsongz')
|
||||
assert type(dh) == JsonGzDataHandler
|
||||
assert isinstance(dh, JsonGzDataHandler)
|
||||
dh1 = get_datahandler(testdatadir, 'jsongz', dh)
|
||||
assert id(dh1) == id(dh)
|
||||
|
||||
dh = get_datahandler(testdatadir, 'hdf5')
|
||||
assert type(dh) == HDF5DataHandler
|
||||
assert isinstance(dh, HDF5DataHandler)
|
||||
|
||||
@@ -194,7 +194,7 @@ def test_get_producer_df(default_conf):
|
||||
assert la == empty_la
|
||||
|
||||
# non existent timeframe, empty dataframe
|
||||
datframe, la = dataprovider.get_producer_df(pair, timeframe='1h')
|
||||
_dataframe, la = dataprovider.get_producer_df(pair, timeframe='1h')
|
||||
assert dataframe.empty
|
||||
assert la == empty_la
|
||||
|
||||
@@ -508,16 +508,13 @@ def test_dp_get_required_startup(default_conf_usdt):
|
||||
dp = DataProvider(default_conf_usdt, None)
|
||||
|
||||
# No FreqAI config
|
||||
assert dp.get_required_startup('5m', False) == 0
|
||||
assert dp.get_required_startup('1h', False) == 0
|
||||
assert dp.get_required_startup('1d', False) == 0
|
||||
assert dp.get_required_startup('1d', True) == 0
|
||||
assert dp.get_required_startup('5m') == 0
|
||||
assert dp.get_required_startup('1h') == 0
|
||||
assert dp.get_required_startup('1d') == 0
|
||||
|
||||
dp._config['startup_candle_count'] = 20
|
||||
assert dp.get_required_startup('5m', False) == 20
|
||||
assert dp.get_required_startup('5m', True) == 20
|
||||
assert dp.get_required_startup('1h', False) == 20
|
||||
assert dp.get_required_startup('5m') == 20
|
||||
assert dp.get_required_startup('1h') == 20
|
||||
assert dp.get_required_startup('1h') == 20
|
||||
|
||||
# With freqAI config
|
||||
@@ -532,37 +529,19 @@ def test_dp_get_required_startup(default_conf_usdt):
|
||||
]
|
||||
}
|
||||
}
|
||||
assert dp.get_required_startup('5m', False) == 20
|
||||
assert dp.get_required_startup('5m', True) == 5780
|
||||
|
||||
assert dp.get_required_startup('1h', False) == 20
|
||||
assert dp.get_required_startup('1h', True) == 500
|
||||
|
||||
assert dp.get_required_startup('1d', False) == 20
|
||||
assert dp.get_required_startup('1d', True) == 40
|
||||
assert dp.get_required_startup('5m') == 5780
|
||||
assert dp.get_required_startup('1h') == 500
|
||||
assert dp.get_required_startup('1d') == 40
|
||||
|
||||
# FreqAI kindof ignores startup_candle_count if it's below indicator_periods_candles
|
||||
dp._config['startup_candle_count'] = 0
|
||||
assert dp.get_required_startup('5m', False) == 20
|
||||
assert dp.get_required_startup('5m', True) == 5780
|
||||
|
||||
assert dp.get_required_startup('1h', False) == 20
|
||||
assert dp.get_required_startup('1h', True) == 500
|
||||
|
||||
assert dp.get_required_startup('1d', False) == 20
|
||||
assert dp.get_required_startup('1d', True) == 40
|
||||
assert dp.get_required_startup('5m') == 5780
|
||||
assert dp.get_required_startup('1h') == 500
|
||||
assert dp.get_required_startup('1d') == 40
|
||||
|
||||
dp._config['freqai']['feature_parameters']['indicator_periods_candles'][1] = 50
|
||||
assert dp.get_required_startup('5m', False) == 50
|
||||
assert dp.get_required_startup('5m', True) == 5810
|
||||
|
||||
assert dp.get_required_startup('1h', False) == 50
|
||||
assert dp.get_required_startup('1h', True) == 530
|
||||
|
||||
assert dp.get_required_startup('1d', False) == 50
|
||||
assert dp.get_required_startup('1d', True) == 70
|
||||
assert dp.get_required_startup('5m') == 5810
|
||||
assert dp.get_required_startup('1h') == 530
|
||||
assert dp.get_required_startup('1d') == 70
|
||||
|
||||
# scenario from issue https://github.com/freqtrade/freqtrade/issues/9432
|
||||
@@ -577,12 +556,6 @@ def test_dp_get_required_startup(default_conf_usdt):
|
||||
}
|
||||
}
|
||||
dp._config['startup_candle_count'] = 40
|
||||
assert dp.get_required_startup('5m', False) == 40
|
||||
assert dp.get_required_startup('5m', True) == 51880
|
||||
|
||||
assert dp.get_required_startup('1h', False) == 40
|
||||
assert dp.get_required_startup('1h', True) == 4360
|
||||
|
||||
assert dp.get_required_startup('1d', False) == 40
|
||||
assert dp.get_required_startup('1d', True) == 220
|
||||
assert dp.get_required_startup('5m') == 51880
|
||||
assert dp.get_required_startup('1h') == 4360
|
||||
assert dp.get_required_startup('1d') == 220
|
||||
|
||||
@@ -38,7 +38,7 @@ def test_download_data_main_all_pairs(mocker, markets):
|
||||
"timeframes": ["5m", "1h"]
|
||||
})
|
||||
download_data_main(config)
|
||||
expected = set(['ETH/USDT', 'XRP/USDT', 'NEO/USDT', 'TKN/USDT'])
|
||||
expected = set(['BTC/USDT', 'ETH/USDT', 'XRP/USDT', 'NEO/USDT', 'TKN/USDT'])
|
||||
assert set(dl_mock.call_args_list[0][1]['pairs']) == expected
|
||||
assert dl_mock.call_count == 1
|
||||
|
||||
@@ -50,7 +50,7 @@ def test_download_data_main_all_pairs(mocker, markets):
|
||||
"include_inactive": True
|
||||
})
|
||||
download_data_main(config)
|
||||
expected = set(['ETH/USDT', 'LTC/USDT', 'XRP/USDT', 'NEO/USDT', 'TKN/USDT'])
|
||||
expected = set(['BTC/USDT', 'ETH/USDT', 'LTC/USDT', 'XRP/USDT', 'NEO/USDT', 'TKN/USDT'])
|
||||
assert set(dl_mock.call_args_list[0][1]['pairs']) == expected
|
||||
|
||||
|
||||
|
||||
@@ -508,8 +508,9 @@ def test_refresh_backtest_ohlcv_data(
|
||||
|
||||
mocker.patch.object(Path, "exists", MagicMock(return_value=True))
|
||||
mocker.patch.object(Path, "unlink", MagicMock())
|
||||
default_conf['trading_mode'] = trademode
|
||||
|
||||
ex = get_patched_exchange(mocker, default_conf)
|
||||
ex = get_patched_exchange(mocker, default_conf, id='bybit')
|
||||
timerange = TimeRange.parse_timerange("20190101-20190102")
|
||||
refresh_backtest_ohlcv_data(exchange=ex, pairs=["ETH/BTC", "XRP/BTC"],
|
||||
timeframes=["1m", "5m"], datadir=testdatadir,
|
||||
@@ -521,6 +522,9 @@ def test_refresh_backtest_ohlcv_data(
|
||||
assert dl_mock.call_args[1]['timerange'].starttype == 'date'
|
||||
|
||||
assert log_has_re(r"Downloading pair ETH/BTC, .* interval 1m\.", caplog)
|
||||
if trademode == 'futures':
|
||||
assert log_has_re(r"Downloading pair ETH/BTC, funding_rate, interval 8h\.", caplog)
|
||||
assert log_has_re(r"Downloading pair ETH/BTC, mark, interval 4h\.", caplog)
|
||||
|
||||
|
||||
def test_download_data_no_markets(mocker, default_conf, caplog, testdatadir):
|
||||
|
||||
Reference in New Issue
Block a user