From ffd49e0e59c15b7586b15dd77a3292c9a8423feb Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 12 May 2024 16:00:45 +0200 Subject: [PATCH] ruff format: tests/data --- tests/data/test_btanalysis.py | 331 +++++++++------- tests/data/test_converter.py | 402 +++++++++---------- tests/data/test_datahandler.py | 516 +++++++++++++------------ tests/data/test_dataprovider.py | 262 +++++++------ tests/data/test_download_data.py | 99 ++--- tests/data/test_entryexitanalysis.py | 326 +++++++++------- tests/data/test_history.py | 556 +++++++++++++++------------ 7 files changed, 1335 insertions(+), 1157 deletions(-) diff --git a/tests/data/test_btanalysis.py b/tests/data/test_btanalysis.py index 430e1e4c5..022713e0f 100644 --- a/tests/data/test_btanalysis.py +++ b/tests/data/test_btanalysis.py @@ -41,18 +41,17 @@ from tests.conftest_trades import MOCK_TRADE_COUNT def test_get_latest_backtest_filename(testdatadir, mocker): with pytest.raises(ValueError, match=r"Directory .* does not exist\."): - get_latest_backtest_filename(testdatadir / 'does_not_exist') + get_latest_backtest_filename(testdatadir / "does_not_exist") - with pytest.raises(ValueError, - match=r"Directory .* does not seem to contain .*"): + with pytest.raises(ValueError, match=r"Directory .* does not seem to contain .*"): get_latest_backtest_filename(testdatadir) testdir_bt = testdatadir / "backtest_results" res = get_latest_backtest_filename(testdir_bt) - assert res == 'backtest-result.json' + assert res == "backtest-result.json" res = get_latest_backtest_filename(str(testdir_bt)) - assert res == 'backtest-result.json' + assert res == "backtest-result.json" mocker.patch("freqtrade.data.btanalysis.json_load", return_value={}) @@ -61,8 +60,8 @@ def test_get_latest_backtest_filename(testdatadir, mocker): def test_get_latest_hyperopt_file(testdatadir): - res = get_latest_hyperopt_file(testdatadir / 'does_not_exist', 'testfile.pickle') - assert res == testdatadir / 'does_not_exist/testfile.pickle' + res = get_latest_hyperopt_file(testdatadir / "does_not_exist", "testfile.pickle") + assert res == testdatadir / "does_not_exist/testfile.pickle" res = get_latest_hyperopt_file(testdatadir.parent) assert res == testdatadir.parent / "hyperopt_results.pickle" @@ -73,33 +72,35 @@ def test_get_latest_hyperopt_file(testdatadir): # Test with absolute path with pytest.raises( OperationalException, - match="--hyperopt-filename expects only the filename, not an absolute path."): + match="--hyperopt-filename expects only the filename, not an absolute path.", + ): get_latest_hyperopt_file(str(testdatadir.parent), str(testdatadir.parent)) def test_load_backtest_metadata(mocker, testdatadir): - res = load_backtest_metadata(testdatadir / 'nonexistant.file.json') + res = load_backtest_metadata(testdatadir / "nonexistant.file.json") assert res == {} - mocker.patch('freqtrade.data.btanalysis.get_backtest_metadata_filename') - mocker.patch('freqtrade.data.btanalysis.json_load', side_effect=Exception()) - with pytest.raises(OperationalException, - match=r"Unexpected error.*loading backtest metadata\."): - load_backtest_metadata(testdatadir / 'nonexistant.file.json') + mocker.patch("freqtrade.data.btanalysis.get_backtest_metadata_filename") + mocker.patch("freqtrade.data.btanalysis.json_load", side_effect=Exception()) + with pytest.raises( + OperationalException, match=r"Unexpected error.*loading backtest metadata\." + ): + load_backtest_metadata(testdatadir / "nonexistant.file.json") def test_load_backtest_data_old_format(testdatadir, mocker): - filename = testdatadir / "backtest-result_test222.json" - mocker.patch('freqtrade.data.btanalysis.load_backtest_stats', return_value=[]) + mocker.patch("freqtrade.data.btanalysis.load_backtest_stats", return_value=[]) - with pytest.raises(OperationalException, - match=r"Backtest-results with only trades data are no longer supported."): + with pytest.raises( + OperationalException, + match=r"Backtest-results with only trades data are no longer supported.", + ): load_backtest_data(filename) def test_load_backtest_data_new_format(testdatadir): - filename = testdatadir / "backtest_results/backtest-result.json" bt_data = load_backtest_data(filename) assert isinstance(bt_data, DataFrame) @@ -122,13 +123,11 @@ def test_load_backtest_data_new_format(testdatadir): def test_load_backtest_data_multi(testdatadir): - filename = testdatadir / "backtest_results/backtest-result_multistrat.json" - for strategy in ('StrategyTestV2', 'TestStrategy'): + for strategy in ("StrategyTestV2", "TestStrategy"): bt_data = load_backtest_data(filename, strategy=strategy) assert isinstance(bt_data, DataFrame) - assert set(bt_data.columns) == set( - BT_DATA_COLUMNS) + assert set(bt_data.columns) == set(BT_DATA_COLUMNS) assert len(bt_data) == 179 # Test loading from string (must yield same result) @@ -136,21 +135,20 @@ def test_load_backtest_data_multi(testdatadir): assert bt_data.equals(bt_data2) with pytest.raises(ValueError, match=r"Strategy XYZ not available in the backtest result\."): - load_backtest_data(filename, strategy='XYZ') + load_backtest_data(filename, strategy="XYZ") with pytest.raises(ValueError, match=r"Detected backtest result with more than one strategy.*"): load_backtest_data(filename) @pytest.mark.usefixtures("init_persistence") -@pytest.mark.parametrize('is_short', [False, True]) +@pytest.mark.parametrize("is_short", [False, True]) def test_load_trades_from_db(default_conf, fee, is_short, mocker): - create_mock_trades(fee, is_short) # remove init so it does not init again - init_mock = mocker.patch('freqtrade.data.btanalysis.init_db', MagicMock()) + init_mock = mocker.patch("freqtrade.data.btanalysis.init_db", MagicMock()) - trades = load_trades_from_db(db_url=default_conf['db_url']) + trades = load_trades_from_db(db_url=default_conf["db_url"]) assert init_mock.call_count == 1 assert len(trades) == MOCK_TRADE_COUNT assert isinstance(trades, DataFrame) @@ -159,38 +157,46 @@ def test_load_trades_from_db(default_conf, fee, is_short, mocker): assert "profit_ratio" in trades.columns for col in BT_DATA_COLUMNS: - if col not in ['index', 'open_at_end']: + if col not in ["index", "open_at_end"]: assert col in trades.columns - trades = load_trades_from_db(db_url=default_conf['db_url'], strategy=CURRENT_TEST_STRATEGY) + trades = load_trades_from_db(db_url=default_conf["db_url"], strategy=CURRENT_TEST_STRATEGY) assert len(trades) == 4 - trades = load_trades_from_db(db_url=default_conf['db_url'], strategy='NoneStrategy') + trades = load_trades_from_db(db_url=default_conf["db_url"], strategy="NoneStrategy") assert len(trades) == 0 def test_extract_trades_of_period(testdatadir): pair = "UNITTEST/BTC" # 2018-11-14 06:07:00 - timerange = TimeRange('date', None, 1510639620, 0) + timerange = TimeRange("date", None, 1510639620, 0) - data = load_pair_history(pair=pair, timeframe='1m', - datadir=testdatadir, timerange=timerange) + data = load_pair_history(pair=pair, timeframe="1m", datadir=testdatadir, timerange=timerange) trades = DataFrame( - {'pair': [pair, pair, pair, pair], - 'profit_ratio': [0.0, 0.1, -0.2, -0.5], - 'profit_abs': [0.0, 1, -2, -5], - 'open_date': to_datetime([datetime(2017, 11, 13, 15, 40, 0, tzinfo=timezone.utc), - datetime(2017, 11, 14, 9, 41, 0, tzinfo=timezone.utc), - datetime(2017, 11, 14, 14, 20, 0, tzinfo=timezone.utc), - datetime(2017, 11, 15, 3, 40, 0, tzinfo=timezone.utc), - ], utc=True - ), - 'close_date': to_datetime([datetime(2017, 11, 13, 16, 40, 0, tzinfo=timezone.utc), - datetime(2017, 11, 14, 10, 41, 0, tzinfo=timezone.utc), - datetime(2017, 11, 14, 15, 25, 0, tzinfo=timezone.utc), - datetime(2017, 11, 15, 3, 55, 0, tzinfo=timezone.utc), - ], utc=True) - }) + { + "pair": [pair, pair, pair, pair], + "profit_ratio": [0.0, 0.1, -0.2, -0.5], + "profit_abs": [0.0, 1, -2, -5], + "open_date": to_datetime( + [ + datetime(2017, 11, 13, 15, 40, 0, tzinfo=timezone.utc), + datetime(2017, 11, 14, 9, 41, 0, tzinfo=timezone.utc), + datetime(2017, 11, 14, 14, 20, 0, tzinfo=timezone.utc), + datetime(2017, 11, 15, 3, 40, 0, tzinfo=timezone.utc), + ], + utc=True, + ), + "close_date": to_datetime( + [ + datetime(2017, 11, 13, 16, 40, 0, tzinfo=timezone.utc), + datetime(2017, 11, 14, 10, 41, 0, tzinfo=timezone.utc), + datetime(2017, 11, 14, 15, 25, 0, tzinfo=timezone.utc), + datetime(2017, 11, 15, 3, 55, 0, tzinfo=timezone.utc), + ], + utc=True, + ), + } + ) trades1 = extract_trades_of_period(data, trades) # First and last trade are dropped as they are out of range assert len(trades1) == 2 @@ -206,44 +212,47 @@ def test_analyze_trade_parallelism(testdatadir): res = analyze_trade_parallelism(bt_data, "5m") assert isinstance(res, DataFrame) - assert 'open_trades' in res.columns - assert res['open_trades'].max() == 3 - assert res['open_trades'].min() == 0 + assert "open_trades" in res.columns + assert res["open_trades"].max() == 3 + assert res["open_trades"].min() == 0 def test_load_trades(default_conf, mocker): db_mock = mocker.patch("freqtrade.data.btanalysis.load_trades_from_db", MagicMock()) bt_mock = mocker.patch("freqtrade.data.btanalysis.load_backtest_data", MagicMock()) - load_trades("DB", - db_url=default_conf.get('db_url'), - exportfilename=default_conf.get('exportfilename'), - no_trades=False, - strategy=CURRENT_TEST_STRATEGY, - ) + load_trades( + "DB", + db_url=default_conf.get("db_url"), + exportfilename=default_conf.get("exportfilename"), + no_trades=False, + strategy=CURRENT_TEST_STRATEGY, + ) assert db_mock.call_count == 1 assert bt_mock.call_count == 0 db_mock.reset_mock() bt_mock.reset_mock() - default_conf['exportfilename'] = Path("testfile.json") - load_trades("file", - db_url=default_conf.get('db_url'), - exportfilename=default_conf.get('exportfilename'), - ) + default_conf["exportfilename"] = Path("testfile.json") + load_trades( + "file", + db_url=default_conf.get("db_url"), + exportfilename=default_conf.get("exportfilename"), + ) assert db_mock.call_count == 0 assert bt_mock.call_count == 1 db_mock.reset_mock() bt_mock.reset_mock() - default_conf['exportfilename'] = "testfile.json" - load_trades("file", - db_url=default_conf.get('db_url'), - exportfilename=default_conf.get('exportfilename'), - no_trades=True - ) + default_conf["exportfilename"] = "testfile.json" + load_trades( + "file", + db_url=default_conf.get("db_url"), + exportfilename=default_conf.get("exportfilename"), + no_trades=True, + ) assert db_mock.call_count == 0 assert bt_mock.call_count == 0 @@ -251,7 +260,7 @@ def test_load_trades(default_conf, mocker): def test_calculate_market_change(testdatadir): pairs = ["ETH/BTC", "ADA/BTC"] - data = load_data(datadir=testdatadir, pairs=pairs, timeframe='5m') + data = load_data(datadir=testdatadir, pairs=pairs, timeframe="5m") result = calculate_market_change(data) assert isinstance(result, float) assert pytest.approx(result) == 0.01100002 @@ -259,7 +268,7 @@ def test_calculate_market_change(testdatadir): def test_combine_dataframes_with_mean(testdatadir): pairs = ["ETH/BTC", "ADA/BTC"] - data = load_data(datadir=testdatadir, pairs=pairs, timeframe='5m') + data = load_data(datadir=testdatadir, pairs=pairs, timeframe="5m") df = combine_dataframes_with_mean(data) assert isinstance(df, DataFrame) assert "ETH/BTC" in df.columns @@ -269,11 +278,9 @@ def test_combine_dataframes_with_mean(testdatadir): def test_combined_dataframes_with_rel_mean(testdatadir): pairs = ["ETH/BTC", "ADA/BTC"] - data = load_data(datadir=testdatadir, pairs=pairs, timeframe='5m') + data = load_data(datadir=testdatadir, pairs=pairs, timeframe="5m") df = combined_dataframes_with_rel_mean( - data, - datetime(2018, 1, 12, tzinfo=timezone.utc), - datetime(2018, 1, 28, tzinfo=timezone.utc) + data, datetime(2018, 1, 12, tzinfo=timezone.utc), datetime(2018, 1, 28, tzinfo=timezone.utc) ) assert isinstance(df, DataFrame) assert "ETH/BTC" not in df.columns @@ -281,14 +288,14 @@ def test_combined_dataframes_with_rel_mean(testdatadir): assert "mean" in df.columns assert "rel_mean" in df.columns assert "count" in df.columns - assert df.iloc[0]['count'] == 2 - assert df.iloc[-1]['count'] == 2 - assert len(df) < len(data['ETH/BTC']) + assert df.iloc[0]["count"] == 2 + assert df.iloc[-1]["count"] == 2 + assert len(df) < len(data["ETH/BTC"]) def test_combine_dataframes_with_mean_no_data(testdatadir): pairs = ["ETH/BTC", "ADA/BTC"] - data = load_data(datadir=testdatadir, pairs=pairs, timeframe='6m') + data = load_data(datadir=testdatadir, pairs=pairs, timeframe="6m") with pytest.raises(ValueError, match=r"No data provided\."): combine_dataframes_with_mean(data) @@ -298,60 +305,63 @@ def test_create_cum_profit(testdatadir): bt_data = load_backtest_data(filename) timerange = TimeRange.parse_timerange("20180110-20180112") - df = load_pair_history(pair="TRX/BTC", timeframe='5m', - datadir=testdatadir, timerange=timerange) + df = load_pair_history(pair="TRX/BTC", timeframe="5m", datadir=testdatadir, timerange=timerange) - cum_profits = create_cum_profit(df.set_index('date'), - bt_data[bt_data["pair"] == 'TRX/BTC'], - "cum_profits", timeframe="5m") + cum_profits = create_cum_profit( + df.set_index("date"), bt_data[bt_data["pair"] == "TRX/BTC"], "cum_profits", timeframe="5m" + ) assert "cum_profits" in cum_profits.columns - assert cum_profits.iloc[0]['cum_profits'] == 0 - assert pytest.approx(cum_profits.iloc[-1]['cum_profits']) == 9.0225563e-05 + assert cum_profits.iloc[0]["cum_profits"] == 0 + assert pytest.approx(cum_profits.iloc[-1]["cum_profits"]) == 9.0225563e-05 def test_create_cum_profit1(testdatadir): filename = testdatadir / "backtest_results/backtest-result.json" bt_data = load_backtest_data(filename) # Move close-time to "off" the candle, to make sure the logic still works - bt_data['close_date'] = bt_data.loc[:, 'close_date'] + DateOffset(seconds=20) + bt_data["close_date"] = bt_data.loc[:, "close_date"] + DateOffset(seconds=20) timerange = TimeRange.parse_timerange("20180110-20180112") - df = load_pair_history(pair="TRX/BTC", timeframe='5m', - datadir=testdatadir, timerange=timerange) + df = load_pair_history(pair="TRX/BTC", timeframe="5m", datadir=testdatadir, timerange=timerange) - cum_profits = create_cum_profit(df.set_index('date'), - bt_data[bt_data["pair"] == 'TRX/BTC'], - "cum_profits", timeframe="5m") + cum_profits = create_cum_profit( + df.set_index("date"), bt_data[bt_data["pair"] == "TRX/BTC"], "cum_profits", timeframe="5m" + ) assert "cum_profits" in cum_profits.columns - assert cum_profits.iloc[0]['cum_profits'] == 0 - assert pytest.approx(cum_profits.iloc[-1]['cum_profits']) == 9.0225563e-05 + assert cum_profits.iloc[0]["cum_profits"] == 0 + assert pytest.approx(cum_profits.iloc[-1]["cum_profits"]) == 9.0225563e-05 - with pytest.raises(ValueError, match='Trade dataframe empty.'): - create_cum_profit(df.set_index('date'), bt_data[bt_data["pair"] == 'NOTAPAIR'], - "cum_profits", timeframe="5m") + with pytest.raises(ValueError, match="Trade dataframe empty."): + create_cum_profit( + df.set_index("date"), + bt_data[bt_data["pair"] == "NOTAPAIR"], + "cum_profits", + timeframe="5m", + ) def test_calculate_max_drawdown(testdatadir): filename = testdatadir / "backtest_results/backtest-result.json" bt_data = load_backtest_data(filename) _, hdate, lowdate, hval, lval, drawdown = calculate_max_drawdown( - bt_data, value_col="profit_abs") + bt_data, value_col="profit_abs" + ) assert isinstance(drawdown, float) assert pytest.approx(drawdown) == 0.29753914 assert isinstance(hdate, Timestamp) assert isinstance(lowdate, Timestamp) assert isinstance(hval, float) assert isinstance(lval, float) - assert hdate == Timestamp('2018-01-16 19:30:00', tz='UTC') - assert lowdate == Timestamp('2018-01-16 22:25:00', tz='UTC') + assert hdate == Timestamp("2018-01-16 19:30:00", tz="UTC") + assert lowdate == Timestamp("2018-01-16 22:25:00", tz="UTC") underwater = calculate_underwater(bt_data) assert isinstance(underwater, DataFrame) - with pytest.raises(ValueError, match='Trade dataframe empty.'): + with pytest.raises(ValueError, match="Trade dataframe empty."): calculate_max_drawdown(DataFrame()) - with pytest.raises(ValueError, match='Trade dataframe empty.'): + with pytest.raises(ValueError, match="Trade dataframe empty."): calculate_underwater(DataFrame()) @@ -370,7 +380,7 @@ def test_calculate_csum(testdatadir): assert csum_min1 == csum_min + 5 assert csum_max1 == csum_max + 5 - with pytest.raises(ValueError, match='Trade dataframe empty.'): + with pytest.raises(ValueError, match="Trade dataframe empty."): csum_min, csum_max = calculate_csum(DataFrame()) @@ -388,9 +398,7 @@ def test_calculate_expectancy(testdatadir): assert pytest.approx(expectancy) == 5.820687070932315e-06 assert pytest.approx(expectancy_ratio) == 0.07151374226574791 - data = { - 'profit_abs': [100, 200, 50, -150, 300, -100, 80, -30] - } + data = {"profit_abs": [100, 200, 50, -150, 300, -100, 80, -30]} df = DataFrame(data) expectancy, expectancy_ratio = calculate_expectancy(df) @@ -407,10 +415,10 @@ def test_calculate_sortino(testdatadir): sortino = calculate_sortino( bt_data, - bt_data['open_date'].min(), - bt_data['close_date'].max(), + bt_data["open_date"].min(), + bt_data["close_date"].max(), 0.01, - ) + ) assert isinstance(sortino, float) assert pytest.approx(sortino) == 35.17722 @@ -424,10 +432,10 @@ def test_calculate_sharpe(testdatadir): sharpe = calculate_sharpe( bt_data, - bt_data['open_date'].min(), - bt_data['close_date'].max(), + bt_data["open_date"].min(), + bt_data["close_date"].max(), 0.01, - ) + ) assert isinstance(sharpe, float) assert pytest.approx(sharpe) == 44.5078669 @@ -441,40 +449,69 @@ def test_calculate_calmar(testdatadir): calmar = calculate_calmar( bt_data, - bt_data['open_date'].min(), - bt_data['close_date'].max(), + bt_data["open_date"].min(), + bt_data["close_date"].max(), 0.01, - ) + ) assert isinstance(calmar, float) assert pytest.approx(calmar) == 559.040508 -@pytest.mark.parametrize('start,end,days, expected', [ - (64900, 176000, 3 * 365, 0.3945), - (64900, 176000, 365, 1.7119), - (1000, 1000, 365, 0.0), - (1000, 1500, 365, 0.5), - (1000, 1500, 100, 3.3927), # sub year - (0.01000000, 0.01762792, 120, 4.6087), # sub year BTC values -]) +@pytest.mark.parametrize( + "start,end,days, expected", + [ + (64900, 176000, 3 * 365, 0.3945), + (64900, 176000, 365, 1.7119), + (1000, 1000, 365, 0.0), + (1000, 1500, 365, 0.5), + (1000, 1500, 100, 3.3927), # sub year + (0.01000000, 0.01762792, 120, 4.6087), # sub year BTC values + ], +) def test_calculate_cagr(start, end, days, expected): - assert round(calculate_cagr(days, start, end), 4) == expected def test_calculate_max_drawdown2(): - values = [0.011580, 0.010048, 0.011340, 0.012161, 0.010416, 0.010009, 0.020024, - -0.024662, -0.022350, 0.020496, -0.029859, -0.030511, 0.010041, 0.010872, - -0.025782, 0.010400, 0.012374, 0.012467, 0.114741, 0.010303, 0.010088, - -0.033961, 0.010680, 0.010886, -0.029274, 0.011178, 0.010693, 0.010711] + values = [ + 0.011580, + 0.010048, + 0.011340, + 0.012161, + 0.010416, + 0.010009, + 0.020024, + -0.024662, + -0.022350, + 0.020496, + -0.029859, + -0.030511, + 0.010041, + 0.010872, + -0.025782, + 0.010400, + 0.012374, + 0.012467, + 0.114741, + 0.010303, + 0.010088, + -0.033961, + 0.010680, + 0.010886, + -0.029274, + 0.011178, + 0.010693, + 0.010711, + ] dates = [dt_utc(2020, 1, 1) + timedelta(days=i) for i in range(len(values))] - df = DataFrame(zip(values, dates), columns=['profit', 'open_date']) + df = DataFrame(zip(values, dates), columns=["profit", "open_date"]) # sort by profit and reset index - df = df.sort_values('profit').reset_index(drop=True) + df = df.sort_values("profit").reset_index(drop=True) df1 = df.copy() drawdown, hdate, ldate, hval, lval, drawdown_rel = calculate_max_drawdown( - df, date_col='open_date', value_col='profit') + df, date_col="open_date", value_col="profit" + ) # Ensure df has not been altered. assert df.equals(df1) @@ -486,23 +523,26 @@ def test_calculate_max_drawdown2(): assert hval > lval assert drawdown == 0.091755 - df = DataFrame(zip(values[:5], dates[:5]), columns=['profit', 'open_date']) - with pytest.raises(ValueError, match='No losing trade, therefore no drawdown.'): - calculate_max_drawdown(df, date_col='open_date', value_col='profit') + df = DataFrame(zip(values[:5], dates[:5]), columns=["profit", "open_date"]) + with pytest.raises(ValueError, match="No losing trade, therefore no drawdown."): + calculate_max_drawdown(df, date_col="open_date", value_col="profit") - df1 = DataFrame(zip(values[:5], dates[:5]), columns=['profit', 'open_date']) - df1.loc[:, 'profit'] = df1['profit'] * -1 + df1 = DataFrame(zip(values[:5], dates[:5]), columns=["profit", "open_date"]) + df1.loc[:, "profit"] = df1["profit"] * -1 # No winning trade ... drawdown, hdate, ldate, hval, lval, drawdown_rel = calculate_max_drawdown( - df1, date_col='open_date', value_col='profit') + df1, date_col="open_date", value_col="profit" + ) assert drawdown == 0.043965 -@pytest.mark.parametrize('profits,relative,highd,lowdays,result,result_rel', [ - ([0.0, -500.0, 500.0, 10000.0, -1000.0], False, 3, 4, 1000.0, 0.090909), - ([0.0, -500.0, 500.0, 10000.0, -1000.0], True, 0, 1, 500.0, 0.5), - -]) +@pytest.mark.parametrize( + "profits,relative,highd,lowdays,result,result_rel", + [ + ([0.0, -500.0, 500.0, 10000.0, -1000.0], False, 3, 4, 1000.0, 0.090909), + ([0.0, -500.0, 500.0, 10000.0, -1000.0], True, 0, 1, 500.0, 0.5), + ], +) def test_calculate_max_drawdown_abs(profits, relative, highd, lowdays, result, result_rel): """ Test case from issue https://github.com/freqtrade/freqtrade/issues/6655 @@ -511,12 +551,13 @@ def test_calculate_max_drawdown_abs(profits, relative, highd, lowdays, result, r """ init_date = datetime(2020, 1, 1, tzinfo=timezone.utc) dates = [init_date + timedelta(days=i) for i in range(len(profits))] - df = DataFrame(zip(profits, dates), columns=['profit_abs', 'open_date']) + df = DataFrame(zip(profits, dates), columns=["profit_abs", "open_date"]) # sort by profit and reset index - df = df.sort_values('profit_abs').reset_index(drop=True) + df = df.sort_values("profit_abs").reset_index(drop=True) df1 = df.copy() drawdown, hdate, ldate, hval, lval, drawdown_rel = calculate_max_drawdown( - df, date_col='open_date', starting_balance=1000, relative=relative) + df, date_col="open_date", starting_balance=1000, relative=relative + ) # Ensure df has not been altered. assert df.equals(df1) diff --git a/tests/data/test_converter.py b/tests/data/test_converter.py index 112a99b3c..9c6b7d875 100644 --- a/tests/data/test_converter.py +++ b/tests/data/test_converter.py @@ -34,102 +34,105 @@ from tests.data.test_history import _clean_test_file def test_dataframe_correct_columns(dataframe_1m): - assert dataframe_1m.columns.tolist() == ['date', 'open', 'high', 'low', 'close', 'volume'] + assert dataframe_1m.columns.tolist() == ["date", "open", "high", "low", "close", "volume"] def test_ohlcv_to_dataframe(ohlcv_history_list, caplog): - columns = ['date', 'open', 'high', 'low', 'close', 'volume'] + columns = ["date", "open", "high", "low", "close", "volume"] caplog.set_level(logging.DEBUG) # Test file with BV data - dataframe = ohlcv_to_dataframe(ohlcv_history_list, '5m', pair="UNITTEST/BTC", - fill_missing=True) + dataframe = ohlcv_to_dataframe(ohlcv_history_list, "5m", pair="UNITTEST/BTC", fill_missing=True) assert dataframe.columns.tolist() == columns - assert log_has('Converting candle (OHLCV) data to dataframe for pair UNITTEST/BTC.', caplog) + assert log_has("Converting candle (OHLCV) data to dataframe for pair UNITTEST/BTC.", caplog) def test_trades_to_ohlcv(trades_history_df, caplog): - caplog.set_level(logging.DEBUG) with pytest.raises(ValueError, match="Trade-list empty."): - trades_to_ohlcv(pd.DataFrame(columns=trades_history_df.columns), '1m') + trades_to_ohlcv(pd.DataFrame(columns=trades_history_df.columns), "1m") - df = trades_to_ohlcv(trades_history_df, '1m') + df = trades_to_ohlcv(trades_history_df, "1m") assert not df.empty assert len(df) == 1 - assert 'open' in df.columns - assert 'high' in df.columns - assert 'low' in df.columns - 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') + assert "open" in df.columns + assert "high" in df.columns + assert "low" in df.columns + 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') + 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') + 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') + 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') + 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), -]) +@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') + 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 + assert df.iloc[-1, :]["date"].day_name() == weekday def test_ohlcv_fill_up_missing_data(testdatadir, caplog): - data = load_pair_history(datadir=testdatadir, - timeframe='1m', - pair='UNITTEST/BTC', - fill_up_missing=False) + data = load_pair_history( + datadir=testdatadir, timeframe="1m", pair="UNITTEST/BTC", fill_up_missing=False + ) caplog.set_level(logging.DEBUG) - data2 = ohlcv_fill_up_missing_data(data, '1m', 'UNITTEST/BTC') + data2 = ohlcv_fill_up_missing_data(data, "1m", "UNITTEST/BTC") assert len(data2) > len(data) # Column names should not change assert (data.columns == data2.columns).all() - assert log_has_re(f"Missing data fillup for UNITTEST/BTC, 1m: before: " - f"{len(data)} - after: {len(data2)}.*", caplog) + assert log_has_re( + f"Missing data fillup for UNITTEST/BTC, 1m: before: " + f"{len(data)} - after: {len(data2)}.*", + caplog, + ) # Test fillup actually fixes invalid backtest data - min_date, max_date = get_timerange({'UNITTEST/BTC': data}) - assert validate_backtest_data(data, 'UNITTEST/BTC', min_date, max_date, 1) - assert not validate_backtest_data(data2, 'UNITTEST/BTC', min_date, max_date, 1) + min_date, max_date = get_timerange({"UNITTEST/BTC": data}) + assert validate_backtest_data(data, "UNITTEST/BTC", min_date, max_date, 1) + assert not validate_backtest_data(data2, "UNITTEST/BTC", min_date, max_date, 1) def test_ohlcv_fill_up_missing_data2(caplog): - timeframe = '5m' + timeframe = "5m" ticks = [ [ 1511686200000, # 8:50:00 @@ -153,7 +156,7 @@ def test_ohlcv_fill_up_missing_data2(caplog): 8.893e-05, 8.875e-05, 8.877e-05, - 2251 + 2251, ], [ 1511687400000, # 9:10:00 @@ -161,51 +164,54 @@ def test_ohlcv_fill_up_missing_data2(caplog): 8.883e-05, 8.895e-05, 8.817e-05, - 123551 - ] + 123551, + ], ] # Generate test-data without filling missing - data = ohlcv_to_dataframe(ticks, timeframe, pair="UNITTEST/BTC", - fill_missing=False) + data = ohlcv_to_dataframe(ticks, timeframe, pair="UNITTEST/BTC", fill_missing=False) assert len(data) == 3 caplog.set_level(logging.DEBUG) data2 = ohlcv_fill_up_missing_data(data, timeframe, "UNITTEST/BTC") assert len(data2) == 4 # 3rd candle has been filled row = data2.loc[2, :] - assert row['volume'] == 0 + assert row["volume"] == 0 # close should match close of previous candle - assert row['close'] == data.loc[1, 'close'] - assert row['open'] == row['close'] - assert row['high'] == row['close'] - assert row['low'] == row['close'] + assert row["close"] == data.loc[1, "close"] + assert row["open"] == row["close"] + assert row["high"] == row["close"] + assert row["low"] == row["close"] # Column names should not change assert (data.columns == data2.columns).all() - assert log_has_re(f"Missing data fillup for UNITTEST/BTC, {timeframe}: before: " - f"{len(data)} - after: {len(data2)}.*", caplog) + assert log_has_re( + f"Missing data fillup for UNITTEST/BTC, {timeframe}: before: " + 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' -]) +@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') + 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) + 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') + 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') + 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) @@ -213,21 +219,20 @@ def test_ohlcv_to_dataframe_multi(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' + "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) + 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 ticks = [ [1567296000000, 8042.08, 10475.54, 7700.67, 8041.96, 608742.1109999999], @@ -276,25 +281,27 @@ def test_ohlcv_to_dataframe_1M(): [1680307200000, 28454.8, 31059.0, 26919.3, 29223.0, 14654208.219], [1682899200000, 29223.0, 29840.0, 25751.0, 27201.1, 13328157.284], [1685577600000, 27201.1, 31500.0, 24777.0, 30460.2, 14099299.273], - [1688169600000, 30460.2, 31850.0, 28830.0, 29338.8, 8760361.377] + [1688169600000, 30460.2, 31850.0, 28830.0, 29338.8, 8760361.377], ] - data = ohlcv_to_dataframe(ticks, '1M', pair="UNITTEST/USDT", - fill_missing=False, drop_incomplete=False) + data = ohlcv_to_dataframe( + ticks, "1M", pair="UNITTEST/USDT", fill_missing=False, drop_incomplete=False + ) assert len(data) == len(ticks) - assert data.iloc[0]['date'].strftime('%Y-%m-%d') == '2019-09-01' - assert data.iloc[-1]['date'].strftime('%Y-%m-%d') == '2023-07-01' + assert data.iloc[0]["date"].strftime("%Y-%m-%d") == "2019-09-01" + assert data.iloc[-1]["date"].strftime("%Y-%m-%d") == "2023-07-01" # Test with filling missing data - data = ohlcv_to_dataframe(ticks, '1M', pair="UNITTEST/USDT", - fill_missing=True, drop_incomplete=False) + data = ohlcv_to_dataframe( + ticks, "1M", pair="UNITTEST/USDT", fill_missing=True, drop_incomplete=False + ) assert len(data) == len(ticks) - assert data.iloc[0]['date'].strftime('%Y-%m-%d') == '2019-09-01' - assert data.iloc[-1]['date'].strftime('%Y-%m-%d') == '2023-07-01' + assert data.iloc[0]["date"].strftime("%Y-%m-%d") == "2019-09-01" + assert data.iloc[-1]["date"].strftime("%Y-%m-%d") == "2023-07-01" def test_ohlcv_drop_incomplete(caplog): - timeframe = '1d' + timeframe = "1d" ticks = [ [ 1559750400000, # 2019-06-04 @@ -318,7 +325,7 @@ def test_ohlcv_drop_incomplete(caplog): 8.893e-05, 8.875e-05, 8.877e-05, - 2251 + 2251, ], [ 1560009600000, # 2019-06-07 @@ -326,35 +333,33 @@ def test_ohlcv_drop_incomplete(caplog): 8.883e-05, 8.895e-05, 8.817e-05, - 123551 - ] + 123551, + ], ] caplog.set_level(logging.DEBUG) - data = ohlcv_to_dataframe(ticks, timeframe, pair="UNITTEST/BTC", - fill_missing=False, drop_incomplete=False) + data = ohlcv_to_dataframe( + ticks, timeframe, pair="UNITTEST/BTC", fill_missing=False, drop_incomplete=False + ) assert len(data) == 4 assert not log_has("Dropping last candle", caplog) # Drop last candle - data = ohlcv_to_dataframe(ticks, timeframe, pair="UNITTEST/BTC", - fill_missing=False, drop_incomplete=True) + data = ohlcv_to_dataframe( + ticks, timeframe, pair="UNITTEST/BTC", fill_missing=False, drop_incomplete=True + ) assert len(data) == 3 assert log_has("Dropping last candle", caplog) def test_trim_dataframe(testdatadir) -> None: - data = load_data( - datadir=testdatadir, - timeframe='1m', - pairs=['UNITTEST/BTC'] - )['UNITTEST/BTC'] - min_date = int(data.iloc[0]['date'].timestamp()) - max_date = int(data.iloc[-1]['date'].timestamp()) + data = load_data(datadir=testdatadir, timeframe="1m", pairs=["UNITTEST/BTC"])["UNITTEST/BTC"] + min_date = int(data.iloc[0]["date"].timestamp()) + max_date = int(data.iloc[-1]["date"].timestamp()) data_modify = data.copy() # Remove first 30 minutes (1800 s) - tr = TimeRange('date', None, min_date + 1800, 0) + tr = TimeRange("date", None, min_date + 1800, 0) data_modify = trim_dataframe(data_modify, tr) assert not data_modify.equals(data) assert len(data_modify) < len(data) @@ -363,7 +368,7 @@ def test_trim_dataframe(testdatadir) -> None: assert all(data_modify.iloc[0] == data.iloc[30]) data_modify = data.copy() - tr = TimeRange('date', None, min_date + 1800, 0) + tr = TimeRange("date", None, min_date + 1800, 0) # Remove first 20 candles - ignores min date data_modify = trim_dataframe(data_modify, tr, startup_candles=20) assert not data_modify.equals(data) @@ -374,7 +379,7 @@ def test_trim_dataframe(testdatadir) -> None: data_modify = data.copy() # Remove last 30 minutes (1800 s) - tr = TimeRange(None, 'date', 0, max_date - 1800) + tr = TimeRange(None, "date", 0, max_date - 1800) data_modify = trim_dataframe(data_modify, tr) assert not data_modify.equals(data) assert len(data_modify) < len(data) @@ -384,7 +389,7 @@ def test_trim_dataframe(testdatadir) -> None: data_modify = data.copy() # Remove first 25 and last 30 minutes (1800 s) - tr = TimeRange('date', 'date', min_date + 1500, max_date - 1800) + tr = TimeRange("date", "date", min_date + 1500, max_date - 1800) data_modify = trim_dataframe(data_modify, tr) assert not data_modify.equals(data) assert len(data_modify) < len(data) @@ -394,8 +399,9 @@ def test_trim_dataframe(testdatadir) -> None: def test_trades_df_remove_duplicates(trades_history_df): - trades_history1 = pd.concat([trades_history_df, trades_history_df, trades_history_df] - ).reset_index(drop=True) + trades_history1 = pd.concat( + [trades_history_df, trades_history_df, trades_history_df] + ).reset_index(drop=True) assert len(trades_history1) == len(trades_history_df) * 3 res = trades_df_remove_duplicates(trades_history1) assert len(res) == len(trades_history_df) @@ -407,55 +413,55 @@ def test_trades_dict_to_list(fetch_trades_result): assert isinstance(res, list) assert isinstance(res[0], list) for i, t in enumerate(res): - assert t[0] == fetch_trades_result[i]['timestamp'] - assert t[1] == fetch_trades_result[i]['id'] - assert t[2] == fetch_trades_result[i]['type'] - assert t[3] == fetch_trades_result[i]['side'] - assert t[4] == fetch_trades_result[i]['price'] - assert t[5] == fetch_trades_result[i]['amount'] - assert t[6] == fetch_trades_result[i]['cost'] + assert t[0] == fetch_trades_result[i]["timestamp"] + assert t[1] == fetch_trades_result[i]["id"] + assert t[2] == fetch_trades_result[i]["type"] + assert t[3] == fetch_trades_result[i]["side"] + assert t[4] == fetch_trades_result[i]["price"] + assert t[5] == fetch_trades_result[i]["amount"] + assert t[6] == fetch_trades_result[i]["cost"] def test_convert_trades_format(default_conf, testdatadir, tmp_path): - files = [{'old': tmp_path / "XRP_ETH-trades.json.gz", - 'new': tmp_path / "XRP_ETH-trades.json"}, - {'old': tmp_path / "XRP_OLD-trades.json.gz", - 'new': tmp_path / "XRP_OLD-trades.json"}, - ] + files = [ + {"old": tmp_path / "XRP_ETH-trades.json.gz", "new": tmp_path / "XRP_ETH-trades.json"}, + {"old": tmp_path / "XRP_OLD-trades.json.gz", "new": tmp_path / "XRP_OLD-trades.json"}, + ] for file in files: - copyfile(testdatadir / file['old'].name, file['old']) - assert not file['new'].exists() + copyfile(testdatadir / file["old"].name, file["old"]) + assert not file["new"].exists() - default_conf['datadir'] = tmp_path + default_conf["datadir"] = tmp_path - convert_trades_format(default_conf, convert_from='jsongz', - convert_to='json', erase=False) + convert_trades_format(default_conf, convert_from="jsongz", convert_to="json", erase=False) for file in files: - assert file['new'].exists() - assert file['old'].exists() + assert file["new"].exists() + assert file["old"].exists() # Remove original file - file['old'].unlink() + file["old"].unlink() # Convert back - convert_trades_format(default_conf, convert_from='json', - convert_to='jsongz', erase=True) + convert_trades_format(default_conf, convert_from="json", convert_to="jsongz", erase=True) for file in files: - assert file['old'].exists() - assert not file['new'].exists() + assert file["old"].exists() + assert not file["new"].exists() - _clean_test_file(file['old']) - if file['new'].exists(): - file['new'].unlink() + _clean_test_file(file["old"]) + if file["new"].exists(): + file["new"].unlink() -@pytest.mark.parametrize('file_base,candletype', [ - (['XRP_ETH-5m', 'XRP_ETH-1m'], CandleType.SPOT), - (['UNITTEST_USDT_USDT-1h-mark', 'XRP_USDT_USDT-1h-mark'], CandleType.MARK), - (['XRP_USDT_USDT-1h-futures'], CandleType.FUTURES), -]) +@pytest.mark.parametrize( + "file_base,candletype", + [ + (["XRP_ETH-5m", "XRP_ETH-1m"], CandleType.SPOT), + (["UNITTEST_USDT_USDT-1h-mark", "XRP_USDT_USDT-1h-mark"], CandleType.MARK), + (["XRP_USDT_USDT-1h-futures"], CandleType.FUTURES), + ], +) def test_convert_ohlcv_format(default_conf, testdatadir, tmp_path, file_base, candletype): - prependix = '' if candletype == CandleType.SPOT else 'futures/' + prependix = "" if candletype == CandleType.SPOT else "futures/" files_orig = [] files_temp = [] files_new = [] @@ -470,77 +476,77 @@ def test_convert_ohlcv_format(default_conf, testdatadir, tmp_path, file_base, ca files_temp.append(file_temp) files_new.append(file_new) - default_conf['datadir'] = tmp_path - default_conf['candle_types'] = [candletype] + default_conf["datadir"] = tmp_path + default_conf["candle_types"] = [candletype] if candletype == CandleType.SPOT: - default_conf['pairs'] = ['XRP/ETH', 'XRP/USDT', 'UNITTEST/USDT'] + default_conf["pairs"] = ["XRP/ETH", "XRP/USDT", "UNITTEST/USDT"] else: - default_conf['pairs'] = ['XRP/ETH:ETH', 'XRP/USDT:USDT', 'UNITTEST/USDT:USDT'] - default_conf['timeframes'] = ['1m', '5m', '1h'] + default_conf["pairs"] = ["XRP/ETH:ETH", "XRP/USDT:USDT", "UNITTEST/USDT:USDT"] + default_conf["timeframes"] = ["1m", "5m", "1h"] assert not file_new.exists() convert_ohlcv_format( default_conf, - convert_from='feather', - convert_to='jsongz', + convert_from="feather", + convert_to="jsongz", erase=False, ) - for file in (files_temp + files_new): + for file in files_temp + files_new: assert file.exists() # Remove original files - for file in (files_temp): + for file in files_temp: file.unlink() # Convert back convert_ohlcv_format( default_conf, - convert_from='jsongz', - convert_to='feather', + convert_from="jsongz", + convert_to="feather", erase=True, ) - for file in (files_temp): + for file in files_temp: assert file.exists() - for file in (files_new): + for file in files_new: assert not file.exists() def test_reduce_dataframe_footprint(): - data = generate_test_data('15m', 40) + data = generate_test_data("15m", 40) - data['open_copy'] = data['open'] - data['close_copy'] = data['close'] - data['close_copy'] = data['close'] + data["open_copy"] = data["open"] + data["close_copy"] = data["close"] + data["close_copy"] = data["close"] - assert data['open'].dtype == np.float64 - assert data['open_copy'].dtype == np.float64 - assert data['close_copy'].dtype == np.float64 + assert data["open"].dtype == np.float64 + assert data["open_copy"].dtype == np.float64 + assert data["close_copy"].dtype == np.float64 df2 = reduce_dataframe_footprint(data) # Does not modify original dataframe - assert data['open'].dtype == np.float64 - assert data['open_copy'].dtype == np.float64 - assert data['close_copy'].dtype == np.float64 + assert data["open"].dtype == np.float64 + assert data["open_copy"].dtype == np.float64 + assert data["close_copy"].dtype == np.float64 # skips ohlcv columns - assert df2['open'].dtype == np.float64 - assert df2['high'].dtype == np.float64 - assert df2['low'].dtype == np.float64 - assert df2['close'].dtype == np.float64 - assert df2['volume'].dtype == np.float64 + assert df2["open"].dtype == np.float64 + assert df2["high"].dtype == np.float64 + assert df2["low"].dtype == np.float64 + assert df2["close"].dtype == np.float64 + assert df2["volume"].dtype == np.float64 # Changes dtype of returned dataframe - assert df2['open_copy'].dtype == np.float32 - assert df2['close_copy'].dtype == np.float32 + assert df2["open_copy"].dtype == np.float32 + assert df2["close_copy"].dtype == np.float32 def test_convert_trades_to_ohlcv(testdatadir, tmp_path, caplog): - pair = 'XRP/ETH' - file1 = tmp_path / 'XRP_ETH-1m.feather' - file5 = tmp_path / 'XRP_ETH-5m.feather' - filetrades = tmp_path / 'XRP_ETH-trades.json.gz' + pair = "XRP/ETH" + file1 = tmp_path / "XRP_ETH-1m.feather" + file5 = tmp_path / "XRP_ETH-5m.feather" + filetrades = tmp_path / "XRP_ETH-trades.json.gz" copyfile(testdatadir / file1.name, file1) copyfile(testdatadir / file5.name, file5) copyfile(testdatadir / filetrades.name, filetrades) @@ -549,13 +555,18 @@ def test_convert_trades_to_ohlcv(testdatadir, tmp_path, caplog): dfbak_1m = load_pair_history(datadir=tmp_path, timeframe="1m", pair=pair) dfbak_5m = load_pair_history(datadir=tmp_path, timeframe="5m", pair=pair) - tr = TimeRange.parse_timerange('20191011-20191012') + tr = TimeRange.parse_timerange("20191011-20191012") - convert_trades_to_ohlcv([pair], timeframes=['1m', '5m'], - data_format_trades='jsongz', - datadir=tmp_path, timerange=tr, erase=True, - data_format_ohlcv='feather', - candle_type=CandleType.SPOT) + convert_trades_to_ohlcv( + [pair], + timeframes=["1m", "5m"], + data_format_trades="jsongz", + datadir=tmp_path, + timerange=tr, + erase=True, + data_format_ohlcv="feather", + candle_type=CandleType.SPOT, + ) assert log_has("Deleting existing data for pair XRP/ETH, interval 1m.", caplog) # Load new data @@ -564,12 +575,17 @@ def test_convert_trades_to_ohlcv(testdatadir, tmp_path, caplog): assert_frame_equal(dfbak_1m, df_1m, check_exact=True) assert_frame_equal(dfbak_5m, df_5m, check_exact=True) - msg = 'Could not convert NoDatapair to OHLCV.' + msg = "Could not convert NoDatapair to OHLCV." assert not log_has(msg, caplog) - convert_trades_to_ohlcv(['NoDatapair'], timeframes=['1m', '5m'], - data_format_trades='jsongz', - datadir=tmp_path, timerange=tr, erase=True, - data_format_ohlcv='feather', - candle_type=CandleType.SPOT) + convert_trades_to_ohlcv( + ["NoDatapair"], + timeframes=["1m", "5m"], + data_format_trades="jsongz", + datadir=tmp_path, + timerange=tr, + erase=True, + data_format_ohlcv="feather", + candle_type=CandleType.SPOT, + ) assert log_has(msg, caplog) diff --git a/tests/data/test_datahandler.py b/tests/data/test_datahandler.py index 20f83c4ac..1f66d1b1e 100644 --- a/tests/data/test_datahandler.py +++ b/tests/data/test_datahandler.py @@ -25,39 +25,53 @@ from tests.conftest import log_has, log_has_re def test_datahandler_ohlcv_get_pairs(testdatadir): - pairs = FeatherDataHandler.ohlcv_get_pairs(testdatadir, '5m', candle_type=CandleType.SPOT) + pairs = FeatherDataHandler.ohlcv_get_pairs(testdatadir, "5m", candle_type=CandleType.SPOT) # Convert to set to avoid failures due to sorting - assert set(pairs) == {'UNITTEST/BTC', 'XLM/BTC', 'ETH/BTC', 'TRX/BTC', 'LTC/BTC', - 'XMR/BTC', 'ZEC/BTC', 'ADA/BTC', 'ETC/BTC', 'NXT/BTC', - 'DASH/BTC', 'XRP/ETH'} + assert set(pairs) == { + "UNITTEST/BTC", + "XLM/BTC", + "ETH/BTC", + "TRX/BTC", + "LTC/BTC", + "XMR/BTC", + "ZEC/BTC", + "ADA/BTC", + "ETC/BTC", + "NXT/BTC", + "DASH/BTC", + "XRP/ETH", + } - pairs = JsonGzDataHandler.ohlcv_get_pairs(testdatadir, '8m', candle_type=CandleType.SPOT) - assert set(pairs) == {'UNITTEST/BTC'} + pairs = JsonGzDataHandler.ohlcv_get_pairs(testdatadir, "8m", candle_type=CandleType.SPOT) + assert set(pairs) == {"UNITTEST/BTC"} - pairs = HDF5DataHandler.ohlcv_get_pairs(testdatadir, '5m', candle_type=CandleType.SPOT) - assert set(pairs) == {'UNITTEST/BTC'} + pairs = HDF5DataHandler.ohlcv_get_pairs(testdatadir, "5m", candle_type=CandleType.SPOT) + assert set(pairs) == {"UNITTEST/BTC"} - pairs = FeatherDataHandler.ohlcv_get_pairs(testdatadir, '1h', candle_type=CandleType.MARK) - assert set(pairs) == {'UNITTEST/USDT:USDT', 'XRP/USDT:USDT'} + pairs = FeatherDataHandler.ohlcv_get_pairs(testdatadir, "1h", candle_type=CandleType.MARK) + assert set(pairs) == {"UNITTEST/USDT:USDT", "XRP/USDT:USDT"} - pairs = JsonGzDataHandler.ohlcv_get_pairs(testdatadir, '1h', candle_type=CandleType.FUTURES) - assert set(pairs) == {'XRP/USDT:USDT'} + pairs = JsonGzDataHandler.ohlcv_get_pairs(testdatadir, "1h", candle_type=CandleType.FUTURES) + assert set(pairs) == {"XRP/USDT:USDT"} - pairs = HDF5DataHandler.ohlcv_get_pairs(testdatadir, '1h', candle_type=CandleType.MARK) - assert set(pairs) == {'UNITTEST/USDT:USDT'} + pairs = HDF5DataHandler.ohlcv_get_pairs(testdatadir, "1h", candle_type=CandleType.MARK) + assert set(pairs) == {"UNITTEST/USDT:USDT"} -@pytest.mark.parametrize('filename,pair,timeframe,candletype', [ - ('XMR_BTC-5m.json', 'XMR_BTC', '5m', ''), - ('XMR_USDT-1h.h5', 'XMR_USDT', '1h', ''), - ('BTC-PERP-1h.h5', 'BTC-PERP', '1h', ''), - ('BTC_USDT-2h.jsongz', 'BTC_USDT', '2h', ''), - ('BTC_USDT-2h-mark.jsongz', 'BTC_USDT', '2h', 'mark'), - ('XMR_USDT-1h-mark.h5', 'XMR_USDT', '1h', 'mark'), - ('XMR_USDT-1h-random.h5', 'XMR_USDT', '1h', 'random'), - ('BTC-PERP-1h-index.h5', 'BTC-PERP', '1h', 'index'), - ('XMR_USDT_USDT-1h-mark.h5', 'XMR_USDT_USDT', '1h', 'mark'), -]) +@pytest.mark.parametrize( + "filename,pair,timeframe,candletype", + [ + ("XMR_BTC-5m.json", "XMR_BTC", "5m", ""), + ("XMR_USDT-1h.h5", "XMR_USDT", "1h", ""), + ("BTC-PERP-1h.h5", "BTC-PERP", "1h", ""), + ("BTC_USDT-2h.jsongz", "BTC_USDT", "2h", ""), + ("BTC_USDT-2h-mark.jsongz", "BTC_USDT", "2h", "mark"), + ("XMR_USDT-1h-mark.h5", "XMR_USDT", "1h", "mark"), + ("XMR_USDT-1h-random.h5", "XMR_USDT", "1h", "random"), + ("BTC-PERP-1h-index.h5", "BTC-PERP", "1h", "index"), + ("XMR_USDT_USDT-1h-mark.h5", "XMR_USDT_USDT", "1h", "mark"), + ], +) def test_datahandler_ohlcv_regex(filename, pair, timeframe, candletype): regex = JsonDataHandler._OHLCV_REGEX @@ -68,18 +82,20 @@ def test_datahandler_ohlcv_regex(filename, pair, timeframe, candletype): assert match[3] == candletype -@pytest.mark.parametrize('input,expected', [ - ('XMR_USDT', 'XMR/USDT'), - ('BTC_USDT', 'BTC/USDT'), - ('USDT_BUSD', 'USDT/BUSD'), - ('BTC_USDT_USDT', 'BTC/USDT:USDT'), # Futures - ('XRP_USDT_USDT', 'XRP/USDT:USDT'), # futures - ('BTC-PERP', 'BTC-PERP'), - ('BTC-PERP_USDT', 'BTC-PERP:USDT'), - ('UNITTEST_USDT', 'UNITTEST/USDT'), -]) +@pytest.mark.parametrize( + "input,expected", + [ + ("XMR_USDT", "XMR/USDT"), + ("BTC_USDT", "BTC/USDT"), + ("USDT_BUSD", "USDT/BUSD"), + ("BTC_USDT_USDT", "BTC/USDT:USDT"), # Futures + ("XRP_USDT_USDT", "XRP/USDT:USDT"), # futures + ("BTC-PERP", "BTC-PERP"), + ("BTC-PERP_USDT", "BTC-PERP:USDT"), + ("UNITTEST_USDT", "UNITTEST/USDT"), + ], +) def test_rebuild_pair_from_filename(input, expected): - assert IDataHandler.rebuild_pair_from_filename(input) == expected @@ -87,63 +103,63 @@ def test_datahandler_ohlcv_get_available_data(testdatadir): paircombs = FeatherDataHandler.ohlcv_get_available_data(testdatadir, TradingMode.SPOT) # Convert to set to avoid failures due to sorting assert set(paircombs) == { - ('UNITTEST/BTC', '5m', CandleType.SPOT), - ('ETH/BTC', '5m', CandleType.SPOT), - ('XLM/BTC', '5m', CandleType.SPOT), - ('TRX/BTC', '5m', CandleType.SPOT), - ('LTC/BTC', '5m', CandleType.SPOT), - ('XMR/BTC', '5m', CandleType.SPOT), - ('ZEC/BTC', '5m', CandleType.SPOT), - ('UNITTEST/BTC', '1m', CandleType.SPOT), - ('ADA/BTC', '5m', CandleType.SPOT), - ('ETC/BTC', '5m', CandleType.SPOT), - ('NXT/BTC', '5m', CandleType.SPOT), - ('DASH/BTC', '5m', CandleType.SPOT), - ('XRP/ETH', '1m', CandleType.SPOT), - ('XRP/ETH', '5m', CandleType.SPOT), - ('UNITTEST/BTC', '30m', CandleType.SPOT), - ('UNITTEST/BTC', '8m', CandleType.SPOT), + ("UNITTEST/BTC", "5m", CandleType.SPOT), + ("ETH/BTC", "5m", CandleType.SPOT), + ("XLM/BTC", "5m", CandleType.SPOT), + ("TRX/BTC", "5m", CandleType.SPOT), + ("LTC/BTC", "5m", CandleType.SPOT), + ("XMR/BTC", "5m", CandleType.SPOT), + ("ZEC/BTC", "5m", CandleType.SPOT), + ("UNITTEST/BTC", "1m", CandleType.SPOT), + ("ADA/BTC", "5m", CandleType.SPOT), + ("ETC/BTC", "5m", CandleType.SPOT), + ("NXT/BTC", "5m", CandleType.SPOT), + ("DASH/BTC", "5m", CandleType.SPOT), + ("XRP/ETH", "1m", CandleType.SPOT), + ("XRP/ETH", "5m", CandleType.SPOT), + ("UNITTEST/BTC", "30m", CandleType.SPOT), + ("UNITTEST/BTC", "8m", CandleType.SPOT), } paircombs = FeatherDataHandler.ohlcv_get_available_data(testdatadir, TradingMode.FUTURES) # Convert to set to avoid failures due to sorting assert set(paircombs) == { - ('UNITTEST/USDT:USDT', '1h', 'mark'), - ('XRP/USDT:USDT', '5m', 'futures'), - ('XRP/USDT:USDT', '1h', 'futures'), - ('XRP/USDT:USDT', '1h', 'mark'), - ('XRP/USDT:USDT', '8h', 'mark'), - ('XRP/USDT:USDT', '8h', 'funding_rate'), + ("UNITTEST/USDT:USDT", "1h", "mark"), + ("XRP/USDT:USDT", "5m", "futures"), + ("XRP/USDT:USDT", "1h", "futures"), + ("XRP/USDT:USDT", "1h", "mark"), + ("XRP/USDT:USDT", "8h", "mark"), + ("XRP/USDT:USDT", "8h", "funding_rate"), } paircombs = JsonGzDataHandler.ohlcv_get_available_data(testdatadir, TradingMode.SPOT) - assert set(paircombs) == {('UNITTEST/BTC', '8m', CandleType.SPOT)} + assert set(paircombs) == {("UNITTEST/BTC", "8m", CandleType.SPOT)} paircombs = HDF5DataHandler.ohlcv_get_available_data(testdatadir, TradingMode.SPOT) - assert set(paircombs) == {('UNITTEST/BTC', '5m', CandleType.SPOT)} + assert set(paircombs) == {("UNITTEST/BTC", "5m", CandleType.SPOT)} def test_jsondatahandler_ohlcv_purge(mocker, testdatadir): mocker.patch.object(Path, "exists", MagicMock(return_value=False)) unlinkmock = mocker.patch.object(Path, "unlink", MagicMock()) dh = JsonGzDataHandler(testdatadir) - assert not dh.ohlcv_purge('UNITTEST/NONEXIST', '5m', '') - assert not dh.ohlcv_purge('UNITTEST/NONEXIST', '5m', candle_type='mark') + assert not dh.ohlcv_purge("UNITTEST/NONEXIST", "5m", "") + assert not dh.ohlcv_purge("UNITTEST/NONEXIST", "5m", candle_type="mark") assert unlinkmock.call_count == 0 mocker.patch.object(Path, "exists", MagicMock(return_value=True)) - assert dh.ohlcv_purge('UNITTEST/NONEXIST', '5m', '') - assert dh.ohlcv_purge('UNITTEST/NONEXIST', '5m', candle_type='mark') + assert dh.ohlcv_purge("UNITTEST/NONEXIST", "5m", "") + assert dh.ohlcv_purge("UNITTEST/NONEXIST", "5m", candle_type="mark") assert unlinkmock.call_count == 2 def test_jsondatahandler_ohlcv_load(testdatadir, caplog): dh = JsonDataHandler(testdatadir) - df = dh.ohlcv_load('UNITTEST/BTC', '1m', 'spot') + df = dh.ohlcv_load("UNITTEST/BTC", "1m", "spot") assert len(df) > 0 -# # Failure case (empty array) - df1 = dh.ohlcv_load('NOPAIR/XXX', '4m', 'spot') + # # Failure case (empty array) + df1 = dh.ohlcv_load("NOPAIR/XXX", "4m", "spot") assert len(df1) == 0 assert log_has("Could not load data for NOPAIR/XXX.", caplog) assert df.columns.equals(df1.columns) @@ -151,22 +167,22 @@ 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') + min_max = dh.ohlcv_data_min_max("UNITTEST/BTC", "5m", "spot") assert len(min_max) == 3 # Empty pair - min_max = dh.ohlcv_data_min_max('UNITTEST/BTC', '8m', 'spot') + min_max = dh.ohlcv_data_min_max("UNITTEST/BTC", "8m", "spot") 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', '41m', 'spot') + 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') + 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) @@ -175,181 +191,192 @@ def test_datahandler_ohlcv_data_min_max(testdatadir): def test_datahandler__check_empty_df(testdatadir, caplog): dh = JsonDataHandler(testdatadir) expected_text = r"Price jump in UNITTEST/USDT, 1h, spot between" - df = DataFrame([ + df = DataFrame( [ - 1511686200000, # 8:50:00 - 8.794, # open - 8.948, # high - 8.794, # low - 8.88, # close - 2255, # volume (in quote currency) + [ + 1511686200000, # 8:50:00 + 8.794, # open + 8.948, # high + 8.794, # low + 8.88, # close + 2255, # volume (in quote currency) + ], + [ + 1511686500000, # 8:55:00 + 8.88, + 8.942, + 8.88, + 8.893, + 9911, + ], + [ + 1511687100000, # 9:05:00 + 8.891, + 8.893, + 8.875, + 8.877, + 2251, + ], + [ + 1511687400000, # 9:10:00 + 8.877, + 8.883, + 8.895, + 8.817, + 123551, + ], ], - [ - 1511686500000, # 8:55:00 - 8.88, - 8.942, - 8.88, - 8.893, - 9911, - ], - [ - 1511687100000, # 9:05:00 - 8.891, - 8.893, - 8.875, - 8.877, - 2251 - ], - [ - 1511687400000, # 9:10:00 - 8.877, - 8.883, - 8.895, - 8.817, - 123551 - ] - ], columns=['date', 'open', 'high', 'low', 'close', 'volume']) + columns=["date", "open", "high", "low", "close", "volume"], + ) - dh._check_empty_df(df, 'UNITTEST/USDT', '1h', CandleType.SPOT, True, True) + dh._check_empty_df(df, "UNITTEST/USDT", "1h", CandleType.SPOT, True, True) assert not log_has_re(expected_text, caplog) - df = DataFrame([ + df = DataFrame( [ - 1511686200000, # 8:50:00 - 8.794, # open - 8.948, # high - 8.794, # low - 8.88, # close - 2255, # volume (in quote currency) + [ + 1511686200000, # 8:50:00 + 8.794, # open + 8.948, # high + 8.794, # low + 8.88, # close + 2255, # volume (in quote currency) + ], + [ + 1511686500000, # 8:55:00 + 8.88, + 8.942, + 8.88, + 8.893, + 9911, + ], + [ + 1511687100000, # 9:05:00 + 889.1, # Price jump by several decimals + 889.3, + 887.5, + 887.7, + 2251, + ], + [ + 1511687400000, # 9:10:00 + 8.877, + 8.883, + 8.895, + 8.817, + 123551, + ], ], - [ - 1511686500000, # 8:55:00 - 8.88, - 8.942, - 8.88, - 8.893, - 9911, - ], - [ - 1511687100000, # 9:05:00 - 889.1, # Price jump by several decimals - 889.3, - 887.5, - 887.7, - 2251 - ], - [ - 1511687400000, # 9:10:00 - 8.877, - 8.883, - 8.895, - 8.817, - 123551 - ] - ], columns=['date', 'open', 'high', 'low', 'close', 'volume']) + columns=["date", "open", "high", "low", "close", "volume"], + ) - dh._check_empty_df(df, 'UNITTEST/USDT', '1h', CandleType.SPOT, True, True) + dh._check_empty_df(df, "UNITTEST/USDT", "1h", CandleType.SPOT, True, True) assert log_has_re(expected_text, caplog) # @pytest.mark.parametrize('datahandler', []) @pytest.mark.skip("All datahandlers currently support trades data.") -def test_datahandler_trades_not_supported(datahandler, testdatadir, ): +def test_datahandler_trades_not_supported( + datahandler, + testdatadir, +): # Currently disabled. Re-enable should a new provider not support trades data. dh = get_datahandler(testdatadir, datahandler) with pytest.raises(NotImplementedError): - dh.trades_load('UNITTEST/ETH') + dh.trades_load("UNITTEST/ETH") with pytest.raises(NotImplementedError): - dh.trades_store('UNITTEST/ETH', MagicMock()) + dh.trades_store("UNITTEST/ETH", MagicMock()) def test_jsondatahandler_trades_load(testdatadir, caplog): dh = JsonGzDataHandler(testdatadir) logmsg = "Old trades format detected - converting" - dh.trades_load('XRP/ETH', TradingMode.SPOT) + dh.trades_load("XRP/ETH", TradingMode.SPOT) assert not log_has(logmsg, caplog) # Test conversation is happening - dh.trades_load('XRP/OLD', TradingMode.SPOT) + dh.trades_load("XRP/OLD", TradingMode.SPOT) assert log_has(logmsg, caplog) -@pytest.mark.parametrize('datahandler', AVAILABLE_DATAHANDLERS) -def test_datahandler_ohlcv_append(datahandler, testdatadir, ): +@pytest.mark.parametrize("datahandler", AVAILABLE_DATAHANDLERS) +def test_datahandler_ohlcv_append( + datahandler, + testdatadir, +): dh = get_datahandler(testdatadir, datahandler) with pytest.raises(NotImplementedError): - dh.ohlcv_append('UNITTEST/ETH', '5m', DataFrame(), CandleType.SPOT) + dh.ohlcv_append("UNITTEST/ETH", "5m", DataFrame(), CandleType.SPOT) with pytest.raises(NotImplementedError): - dh.ohlcv_append('UNITTEST/ETH', '5m', DataFrame(), CandleType.MARK) + dh.ohlcv_append("UNITTEST/ETH", "5m", DataFrame(), CandleType.MARK) -@pytest.mark.parametrize('datahandler', AVAILABLE_DATAHANDLERS) +@pytest.mark.parametrize("datahandler", AVAILABLE_DATAHANDLERS) def test_datahandler_trades_append(datahandler, testdatadir): dh = get_datahandler(testdatadir, datahandler) with pytest.raises(NotImplementedError): - dh.trades_append('UNITTEST/ETH', DataFrame()) + dh.trades_append("UNITTEST/ETH", DataFrame()) -@pytest.mark.parametrize('datahandler,expected', [ - ('jsongz', {'XRP/ETH', 'XRP/OLD'}), - ('hdf5', {'XRP/ETH'}), - ('feather', {'XRP/ETH'}), - ('parquet', {'XRP/ETH'}), -]) +@pytest.mark.parametrize( + "datahandler,expected", + [ + ("jsongz", {"XRP/ETH", "XRP/OLD"}), + ("hdf5", {"XRP/ETH"}), + ("feather", {"XRP/ETH"}), + ("parquet", {"XRP/ETH"}), + ], +) def test_datahandler_trades_get_pairs(testdatadir, datahandler, expected): - pairs = get_datahandlerclass(datahandler).trades_get_pairs(testdatadir) # Convert to set to avoid failures due to sorting assert set(pairs) == expected def test_hdf5datahandler_trades_load(testdatadir): - dh = get_datahandler(testdatadir, 'hdf5') - trades = dh.trades_load('XRP/ETH', TradingMode.SPOT) + dh = get_datahandler(testdatadir, "hdf5") + trades = dh.trades_load("XRP/ETH", TradingMode.SPOT) assert isinstance(trades, DataFrame) - trades1 = dh.trades_load('UNITTEST/NONEXIST', TradingMode.SPOT) + trades1 = dh.trades_load("UNITTEST/NONEXIST", TradingMode.SPOT) assert isinstance(trades1, DataFrame) assert trades1.empty # data goes from 2019-10-11 - 2019-10-13 - timerange = TimeRange.parse_timerange('20191011-20191012') + timerange = TimeRange.parse_timerange("20191011-20191012") - trades2 = dh._trades_load('XRP/ETH', TradingMode.SPOT, timerange) + trades2 = dh._trades_load("XRP/ETH", TradingMode.SPOT, timerange) assert len(trades) > len(trades2) # Check that ID is None (If it's nan, it's wrong) - assert trades2.iloc[0]['type'] is None + assert trades2.iloc[0]["type"] is None # unfiltered load has trades before starttime - assert len(trades.loc[trades['timestamp'] < timerange.startts * 1000]) >= 0 + assert len(trades.loc[trades["timestamp"] < timerange.startts * 1000]) >= 0 # filtered list does not have trades before starttime - assert len(trades2.loc[trades2['timestamp'] < timerange.startts * 1000]) == 0 + assert len(trades2.loc[trades2["timestamp"] < timerange.startts * 1000]) == 0 # unfiltered load has trades after endtime - assert len(trades.loc[trades['timestamp'] > timerange.stopts * 1000]) >= 0 + assert len(trades.loc[trades["timestamp"] > timerange.stopts * 1000]) >= 0 # filtered list does not have trades after endtime - assert len(trades2.loc[trades2['timestamp'] > timerange.stopts * 1000]) == 0 + assert len(trades2.loc[trades2["timestamp"] > timerange.stopts * 1000]) == 0 # assert len([t for t in trades2 if t[0] > timerange.stopts * 1000]) == 0 -@pytest.mark.parametrize('pair,timeframe,candle_type,candle_append,startdt,enddt', [ - # Data goes from 2018-01-10 - 2018-01-30 - ('UNITTEST/BTC', '5m', 'spot', '', '2018-01-15', '2018-01-19'), - # Mark data goes from to 2021-11-15 2021-11-19 - ('UNITTEST/USDT:USDT', '1h', 'mark', '-mark', '2021-11-16', '2021-11-18'), -]) +@pytest.mark.parametrize( + "pair,timeframe,candle_type,candle_append,startdt,enddt", + [ + # Data goes from 2018-01-10 - 2018-01-30 + ("UNITTEST/BTC", "5m", "spot", "", "2018-01-15", "2018-01-19"), + # Mark data goes from to 2021-11-15 2021-11-19 + ("UNITTEST/USDT:USDT", "1h", "mark", "-mark", "2021-11-16", "2021-11-18"), + ], +) def test_hdf5datahandler_ohlcv_load_and_resave( - testdatadir, - tmp_path, - pair, - timeframe, - candle_type, - candle_append, - startdt, enddt + testdatadir, tmp_path, pair, timeframe, candle_type, candle_append, startdt, enddt ): tmpdir2 = tmp_path - if candle_type not in ('', 'spot'): - tmpdir2 = tmp_path / 'futures' + if candle_type not in ("", "spot"): + tmpdir2 = tmp_path / "futures" tmpdir2.mkdir() - dh = get_datahandler(testdatadir, 'hdf5') + dh = get_datahandler(testdatadir, "hdf5") ohlcv = dh._ohlcv_load(pair, timeframe, None, candle_type=candle_type) assert isinstance(ohlcv, DataFrame) assert len(ohlcv) > 0 @@ -357,50 +384,46 @@ def test_hdf5datahandler_ohlcv_load_and_resave( file = tmpdir2 / f"UNITTEST_NEW-{timeframe}{candle_append}.h5" assert not file.is_file() - dh1 = get_datahandler(tmp_path, 'hdf5') - dh1.ohlcv_store('UNITTEST/NEW', timeframe, ohlcv, candle_type=candle_type) + dh1 = get_datahandler(tmp_path, "hdf5") + dh1.ohlcv_store("UNITTEST/NEW", timeframe, ohlcv, candle_type=candle_type) assert file.is_file() - assert not ohlcv[ohlcv['date'] < startdt].empty + assert not ohlcv[ohlcv["date"] < startdt].empty timerange = TimeRange.parse_timerange(f"{startdt.replace('-', '')}-{enddt.replace('-', '')}") # Call private function to ensure timerange is filtered in hdf5 ohlcv = dh._ohlcv_load(pair, timeframe, timerange, candle_type=candle_type) - ohlcv1 = dh1._ohlcv_load('UNITTEST/NEW', timeframe, timerange, candle_type=candle_type) + ohlcv1 = dh1._ohlcv_load("UNITTEST/NEW", timeframe, timerange, candle_type=candle_type) assert len(ohlcv) == len(ohlcv1) assert ohlcv.equals(ohlcv1) - assert ohlcv[ohlcv['date'] < startdt].empty - assert ohlcv[ohlcv['date'] > enddt].empty + assert ohlcv[ohlcv["date"] < startdt].empty + assert ohlcv[ohlcv["date"] > enddt].empty # Try loading inexisting file - ohlcv = dh.ohlcv_load('UNITTEST/NONEXIST', timeframe, candle_type=candle_type) + ohlcv = dh.ohlcv_load("UNITTEST/NONEXIST", timeframe, candle_type=candle_type) assert ohlcv.empty -@pytest.mark.parametrize('pair,timeframe,candle_type,candle_append,startdt,enddt', [ - # Data goes from 2018-01-10 - 2018-01-30 - ('UNITTEST/BTC', '5m', 'spot', '', '2018-01-15', '2018-01-19'), - # Mark data goes from to 2021-11-15 2021-11-19 - ('UNITTEST/USDT:USDT', '1h', 'mark', '-mark', '2021-11-16', '2021-11-18'), -]) -@pytest.mark.parametrize('datahandler', ['hdf5', 'feather', 'parquet']) +@pytest.mark.parametrize( + "pair,timeframe,candle_type,candle_append,startdt,enddt", + [ + # Data goes from 2018-01-10 - 2018-01-30 + ("UNITTEST/BTC", "5m", "spot", "", "2018-01-15", "2018-01-19"), + # Mark data goes from to 2021-11-15 2021-11-19 + ("UNITTEST/USDT:USDT", "1h", "mark", "-mark", "2021-11-16", "2021-11-18"), + ], +) +@pytest.mark.parametrize("datahandler", ["hdf5", "feather", "parquet"]) def test_generic_datahandler_ohlcv_load_and_resave( - datahandler, - testdatadir, - tmp_path, - pair, - timeframe, - candle_type, - candle_append, - startdt, enddt + datahandler, testdatadir, tmp_path, pair, timeframe, candle_type, candle_append, startdt, enddt ): tmpdir2 = tmp_path - if candle_type not in ('', 'spot'): - tmpdir2 = tmp_path / 'futures' + if candle_type not in ("", "spot"): + tmpdir2 = tmp_path / "futures" tmpdir2.mkdir() # Load data from one common file - dhbase = get_datahandler(testdatadir, 'feather') + dhbase = get_datahandler(testdatadir, "feather") ohlcv = dhbase._ohlcv_load(pair, timeframe, None, candle_type=candle_type) assert isinstance(ohlcv, DataFrame) assert len(ohlcv) > 0 @@ -412,122 +435,123 @@ def test_generic_datahandler_ohlcv_load_and_resave( assert not file.is_file() dh1 = get_datahandler(tmp_path, datahandler) - dh1.ohlcv_store('UNITTEST/NEW', timeframe, ohlcv, candle_type=candle_type) + dh1.ohlcv_store("UNITTEST/NEW", timeframe, ohlcv, candle_type=candle_type) assert file.is_file() - assert not ohlcv[ohlcv['date'] < startdt].empty + assert not ohlcv[ohlcv["date"] < startdt].empty timerange = TimeRange.parse_timerange(f"{startdt.replace('-', '')}-{enddt.replace('-', '')}") ohlcv = dhbase.ohlcv_load(pair, timeframe, timerange=timerange, candle_type=candle_type) - if datahandler == 'hdf5': - ohlcv1 = dh1._ohlcv_load('UNITTEST/NEW', timeframe, timerange, candle_type=candle_type) - if candle_type == 'mark': - ohlcv1['volume'] = 0.0 + if datahandler == "hdf5": + ohlcv1 = dh1._ohlcv_load("UNITTEST/NEW", timeframe, timerange, candle_type=candle_type) + if candle_type == "mark": + ohlcv1["volume"] = 0.0 else: - ohlcv1 = dh1.ohlcv_load('UNITTEST/NEW', timeframe, - timerange=timerange, candle_type=candle_type) + ohlcv1 = dh1.ohlcv_load( + "UNITTEST/NEW", timeframe, timerange=timerange, candle_type=candle_type + ) assert len(ohlcv) == len(ohlcv1) assert ohlcv.equals(ohlcv1) - assert ohlcv[ohlcv['date'] < startdt].empty - assert ohlcv[ohlcv['date'] > enddt].empty + assert ohlcv[ohlcv["date"] < startdt].empty + assert ohlcv[ohlcv["date"] > enddt].empty # Try loading inexisting file - ohlcv = dh.ohlcv_load('UNITTEST/NONEXIST', timeframe, candle_type=candle_type) + ohlcv = dh.ohlcv_load("UNITTEST/NONEXIST", timeframe, candle_type=candle_type) assert ohlcv.empty def test_hdf5datahandler_ohlcv_purge(mocker, testdatadir): mocker.patch.object(Path, "exists", MagicMock(return_value=False)) unlinkmock = mocker.patch.object(Path, "unlink", MagicMock()) - dh = get_datahandler(testdatadir, 'hdf5') - assert not dh.ohlcv_purge('UNITTEST/NONEXIST', '5m', '') - assert not dh.ohlcv_purge('UNITTEST/NONEXIST', '5m', candle_type='mark') + dh = get_datahandler(testdatadir, "hdf5") + assert not dh.ohlcv_purge("UNITTEST/NONEXIST", "5m", "") + assert not dh.ohlcv_purge("UNITTEST/NONEXIST", "5m", candle_type="mark") assert unlinkmock.call_count == 0 mocker.patch.object(Path, "exists", MagicMock(return_value=True)) - assert dh.ohlcv_purge('UNITTEST/NONEXIST', '5m', '') - assert dh.ohlcv_purge('UNITTEST/NONEXIST', '5m', candle_type='mark') + assert dh.ohlcv_purge("UNITTEST/NONEXIST", "5m", "") + assert dh.ohlcv_purge("UNITTEST/NONEXIST", "5m", candle_type="mark") assert unlinkmock.call_count == 2 -@pytest.mark.parametrize('datahandler', ['jsongz', 'hdf5', 'feather', 'parquet']) +@pytest.mark.parametrize("datahandler", ["jsongz", "hdf5", "feather", "parquet"]) def test_datahandler_trades_load(testdatadir, datahandler): dh = get_datahandler(testdatadir, datahandler) - trades = dh.trades_load('XRP/ETH', TradingMode.SPOT) + trades = dh.trades_load("XRP/ETH", TradingMode.SPOT) assert isinstance(trades, DataFrame) - assert trades.iloc[0]['timestamp'] == 1570752011620 - assert trades.iloc[0]['date'] == Timestamp('2019-10-11 00:00:11.620000+0000') - assert trades.iloc[-1]['cost'] == 0.1986231 + assert trades.iloc[0]["timestamp"] == 1570752011620 + assert trades.iloc[0]["date"] == Timestamp("2019-10-11 00:00:11.620000+0000") + assert trades.iloc[-1]["cost"] == 0.1986231 - trades1 = dh.trades_load('UNITTEST/NONEXIST', TradingMode.SPOT) + trades1 = dh.trades_load("UNITTEST/NONEXIST", TradingMode.SPOT) assert isinstance(trades, DataFrame) assert trades1.empty -@pytest.mark.parametrize('datahandler', ['jsongz', 'hdf5', 'feather', 'parquet']) +@pytest.mark.parametrize("datahandler", ["jsongz", "hdf5", "feather", "parquet"]) def test_datahandler_trades_store(testdatadir, tmp_path, datahandler): dh = get_datahandler(testdatadir, datahandler) - trades = dh.trades_load('XRP/ETH', TradingMode.SPOT) + trades = dh.trades_load("XRP/ETH", TradingMode.SPOT) dh1 = get_datahandler(tmp_path, datahandler) - dh1.trades_store('XRP/NEW', trades, TradingMode.SPOT) + dh1.trades_store("XRP/NEW", trades, TradingMode.SPOT) - file = tmp_path / f'XRP_NEW-trades.{dh1._get_file_extension()}' + file = tmp_path / f"XRP_NEW-trades.{dh1._get_file_extension()}" assert file.is_file() # Load trades back - trades_new = dh1.trades_load('XRP/NEW', TradingMode.SPOT) + trades_new = dh1.trades_load("XRP/NEW", TradingMode.SPOT) assert_frame_equal(trades, trades_new, check_exact=True) assert len(trades_new) == len(trades) -@pytest.mark.parametrize('datahandler', ['jsongz', 'hdf5', 'feather', 'parquet']) +@pytest.mark.parametrize("datahandler", ["jsongz", "hdf5", "feather", "parquet"]) def test_datahandler_trades_purge(mocker, testdatadir, datahandler): mocker.patch.object(Path, "exists", MagicMock(return_value=False)) unlinkmock = mocker.patch.object(Path, "unlink", MagicMock()) dh = get_datahandler(testdatadir, datahandler) - assert not dh.trades_purge('UNITTEST/NONEXIST', TradingMode.SPOT) + assert not dh.trades_purge("UNITTEST/NONEXIST", TradingMode.SPOT) assert unlinkmock.call_count == 0 mocker.patch.object(Path, "exists", MagicMock(return_value=True)) - assert dh.trades_purge('UNITTEST/NONEXIST', TradingMode.SPOT) + assert dh.trades_purge("UNITTEST/NONEXIST", TradingMode.SPOT) assert unlinkmock.call_count == 1 def test_gethandlerclass(): - cl = get_datahandlerclass('json') + cl = get_datahandlerclass("json") assert cl == JsonDataHandler assert issubclass(cl, IDataHandler) - cl = get_datahandlerclass('jsongz') + cl = get_datahandlerclass("jsongz") assert cl == JsonGzDataHandler assert issubclass(cl, IDataHandler) assert issubclass(cl, JsonDataHandler) - cl = get_datahandlerclass('hdf5') + cl = get_datahandlerclass("hdf5") assert cl == HDF5DataHandler assert issubclass(cl, IDataHandler) - cl = get_datahandlerclass('feather') + cl = get_datahandlerclass("feather") assert cl == FeatherDataHandler assert issubclass(cl, IDataHandler) - cl = get_datahandlerclass('parquet') + cl = get_datahandlerclass("parquet") assert cl == ParquetDataHandler assert issubclass(cl, IDataHandler) with pytest.raises(ValueError, match=r"No datahandler for .*"): - get_datahandlerclass('DeadBeef') + get_datahandlerclass("DeadBeef") def test_get_datahandler(testdatadir): - dh = get_datahandler(testdatadir, 'json') + dh = get_datahandler(testdatadir, "json") assert isinstance(dh, JsonDataHandler) - dh = get_datahandler(testdatadir, 'jsongz') + dh = get_datahandler(testdatadir, "jsongz") assert isinstance(dh, JsonGzDataHandler) - dh1 = get_datahandler(testdatadir, 'jsongz', dh) + dh1 = get_datahandler(testdatadir, "jsongz", dh) assert id(dh1) == id(dh) - dh = get_datahandler(testdatadir, 'hdf5') + dh = get_datahandler(testdatadir, "hdf5") assert isinstance(dh, HDF5DataHandler) diff --git a/tests/data/test_dataprovider.py b/tests/data/test_dataprovider.py index f9c56b62b..11c69f918 100644 --- a/tests/data/test_dataprovider.py +++ b/tests/data/test_dataprovider.py @@ -11,10 +11,13 @@ from freqtrade.plugins.pairlistmanager import PairListManager from tests.conftest import EXMS, generate_test_data, get_patched_exchange -@pytest.mark.parametrize('candle_type', [ - 'mark', - '', -]) +@pytest.mark.parametrize( + "candle_type", + [ + "mark", + "", + ], +) def test_dp_ohlcv(mocker, default_conf, ohlcv_history, candle_type): default_conf["runmode"] = RunMode.DRY_RUN timeframe = default_conf["timeframe"] @@ -33,11 +36,9 @@ def test_dp_ohlcv(mocker, default_conf, ohlcv_history, candle_type): assert dp.ohlcv("NONSENSE/AAA", timeframe, candle_type=candletype).empty # Test with and without parameter - assert dp.ohlcv( - "UNITTEST/BTC", - timeframe, - candle_type=candletype - ).equals(dp.ohlcv("UNITTEST/BTC", candle_type=candle_type)) + assert dp.ohlcv("UNITTEST/BTC", timeframe, candle_type=candletype).equals( + dp.ohlcv("UNITTEST/BTC", candle_type=candle_type) + ) default_conf["runmode"] = RunMode.LIVE dp = DataProvider(default_conf, exchange) @@ -66,10 +67,12 @@ def test_historic_ohlcv_dataformat(mocker, default_conf, ohlcv_history): featherloadmock = MagicMock(return_value=ohlcv_history) mocker.patch( "freqtrade.data.history.datahandlers.hdf5datahandler.HDF5DataHandler._ohlcv_load", - hdf5loadmock) + hdf5loadmock, + ) mocker.patch( "freqtrade.data.history.datahandlers.featherdatahandler.FeatherDataHandler._ohlcv_load", - featherloadmock) + featherloadmock, + ) default_conf["runmode"] = RunMode.BACKTEST exchange = get_patched_exchange(mocker, default_conf) @@ -90,11 +93,14 @@ def test_historic_ohlcv_dataformat(mocker, default_conf, ohlcv_history): featherloadmock.assert_not_called() -@pytest.mark.parametrize('candle_type', [ - 'mark', - 'futures', - '', -]) +@pytest.mark.parametrize( + "candle_type", + [ + "mark", + "futures", + "", + ], +) def test_get_pair_dataframe(mocker, default_conf, ohlcv_history, candle_type): default_conf["runmode"] = RunMode.DRY_RUN timeframe = default_conf["timeframe"] @@ -105,26 +111,33 @@ def test_get_pair_dataframe(mocker, default_conf, ohlcv_history, candle_type): dp = DataProvider(default_conf, exchange) assert dp.runmode == RunMode.DRY_RUN - assert ohlcv_history.equals(dp.get_pair_dataframe( - "UNITTEST/BTC", timeframe, candle_type=candle_type)) - assert ohlcv_history.equals(dp.get_pair_dataframe( - "UNITTEST/BTC", timeframe, candle_type=candletype)) - assert isinstance(dp.get_pair_dataframe( - "UNITTEST/BTC", timeframe, candle_type=candle_type), DataFrame) - assert dp.get_pair_dataframe("UNITTEST/BTC", timeframe, - candle_type=candle_type) is not ohlcv_history + assert ohlcv_history.equals( + dp.get_pair_dataframe("UNITTEST/BTC", timeframe, candle_type=candle_type) + ) + assert ohlcv_history.equals( + dp.get_pair_dataframe("UNITTEST/BTC", timeframe, candle_type=candletype) + ) + assert isinstance( + dp.get_pair_dataframe("UNITTEST/BTC", timeframe, candle_type=candle_type), DataFrame + ) + assert ( + dp.get_pair_dataframe("UNITTEST/BTC", timeframe, candle_type=candle_type) + is not ohlcv_history + ) assert not dp.get_pair_dataframe("UNITTEST/BTC", timeframe, candle_type=candle_type).empty assert dp.get_pair_dataframe("NONSENSE/AAA", timeframe, candle_type=candle_type).empty # Test with and without parameter - assert dp.get_pair_dataframe("UNITTEST/BTC", timeframe, candle_type=candle_type)\ - .equals(dp.get_pair_dataframe("UNITTEST/BTC", candle_type=candle_type)) + assert dp.get_pair_dataframe("UNITTEST/BTC", timeframe, candle_type=candle_type).equals( + dp.get_pair_dataframe("UNITTEST/BTC", candle_type=candle_type) + ) default_conf["runmode"] = RunMode.LIVE dp = DataProvider(default_conf, exchange) assert dp.runmode == RunMode.LIVE - assert isinstance(dp.get_pair_dataframe( - "UNITTEST/BTC", timeframe, candle_type=candle_type), DataFrame) + assert isinstance( + dp.get_pair_dataframe("UNITTEST/BTC", timeframe, candle_type=candle_type), DataFrame + ) assert dp.get_pair_dataframe("NONSENSE/AAA", timeframe, candle_type=candle_type).empty historymock = MagicMock(return_value=ohlcv_history) @@ -136,7 +149,7 @@ def test_get_pair_dataframe(mocker, default_conf, ohlcv_history, candle_type): assert isinstance(df, DataFrame) assert len(df) == 3 # ohlcv_history mock has just 3 rows - dp._set_dataframe_max_date(ohlcv_history.iloc[-1]['date']) + dp._set_dataframe_max_date(ohlcv_history.iloc[-1]["date"]) df = dp.get_pair_dataframe("UNITTEST/BTC", timeframe, candle_type=candle_type) assert isinstance(df, DataFrame) assert len(df) == 2 # ohlcv_history is limited to 2 rows now @@ -150,7 +163,10 @@ def test_available_pairs(mocker, default_conf, ohlcv_history): dp = DataProvider(default_conf, exchange) assert len(dp.available_pairs) == 2 - assert dp.available_pairs == [("XRP/BTC", timeframe), ("UNITTEST/BTC", timeframe), ] + assert dp.available_pairs == [ + ("XRP/BTC", timeframe), + ("UNITTEST/BTC", timeframe), + ] def test_producer_pairs(default_conf): @@ -172,9 +188,9 @@ def test_producer_pairs(default_conf): def test_get_producer_df(default_conf): dataprovider = DataProvider(default_conf, None) - ohlcv_history = generate_test_data('5m', 150) - pair = 'BTC/USDT' - timeframe = default_conf['timeframe'] + ohlcv_history = generate_test_data("5m", 150) + pair = "BTC/USDT" + timeframe = default_conf["timeframe"] candle_type = CandleType.SPOT empty_la = datetime.fromtimestamp(0, tz=timezone.utc) @@ -192,20 +208,20 @@ def test_get_producer_df(default_conf): assert la > empty_la # no data on this producer, should return empty dataframe - dataframe, la = dataprovider.get_producer_df(pair, producer_name='bad') + dataframe, la = dataprovider.get_producer_df(pair, producer_name="bad") assert dataframe.empty assert la == empty_la # non existent timeframe, empty dataframe - _dataframe, la = dataprovider.get_producer_df(pair, timeframe='1h') + _dataframe, la = dataprovider.get_producer_df(pair, timeframe="1h") assert dataframe.empty assert la == empty_la def test_emit_df(mocker, default_conf, ohlcv_history): - mocker.patch('freqtrade.rpc.rpc_manager.RPCManager.__init__', MagicMock()) - rpc_mock = mocker.patch('freqtrade.rpc.rpc_manager.RPCManager', MagicMock()) - send_mock = mocker.patch('freqtrade.rpc.rpc_manager.RPCManager.send_msg', MagicMock()) + mocker.patch("freqtrade.rpc.rpc_manager.RPCManager.__init__", MagicMock()) + rpc_mock = mocker.patch("freqtrade.rpc.rpc_manager.RPCManager", MagicMock()) + send_mock = mocker.patch("freqtrade.rpc.rpc_manager.RPCManager.send_msg", MagicMock()) dataprovider = DataProvider(default_conf, exchange=None, rpc=rpc_mock) dataprovider_no_rpc = DataProvider(default_conf, exchange=None) @@ -262,14 +278,14 @@ def test_orderbook(mocker, default_conf, order_book_l2): exchange = get_patched_exchange(mocker, default_conf, api_mock=api_mock) dp = DataProvider(default_conf, exchange) - res = dp.orderbook('ETH/BTC', 5) + res = dp.orderbook("ETH/BTC", 5) assert order_book_l2.call_count == 1 - assert order_book_l2.call_args_list[0][0][0] == 'ETH/BTC' + assert order_book_l2.call_args_list[0][0][0] == "ETH/BTC" assert order_book_l2.call_args_list[0][0][1] >= 5 assert isinstance(res, dict) - assert 'bids' in res - assert 'asks' in res + assert "bids" in res + assert "asks" in res def test_market(mocker, default_conf, markets): @@ -278,41 +294,39 @@ def test_market(mocker, default_conf, markets): exchange = get_patched_exchange(mocker, default_conf, api_mock=api_mock) dp = DataProvider(default_conf, exchange) - res = dp.market('ETH/BTC') + res = dp.market("ETH/BTC") assert isinstance(res, dict) - assert 'symbol' in res - assert res['symbol'] == 'ETH/BTC' + assert "symbol" in res + assert res["symbol"] == "ETH/BTC" - res = dp.market('UNITTEST/BTC') + res = dp.market("UNITTEST/BTC") assert res is None def test_ticker(mocker, default_conf, tickers): - ticker_mock = MagicMock(return_value=tickers()['ETH/BTC']) + ticker_mock = MagicMock(return_value=tickers()["ETH/BTC"]) mocker.patch(f"{EXMS}.fetch_ticker", ticker_mock) exchange = get_patched_exchange(mocker, default_conf) dp = DataProvider(default_conf, exchange) - res = dp.ticker('ETH/BTC') + res = dp.ticker("ETH/BTC") assert isinstance(res, dict) - assert 'symbol' in res - assert res['symbol'] == 'ETH/BTC' + assert "symbol" in res + assert res["symbol"] == "ETH/BTC" - ticker_mock = MagicMock(side_effect=ExchangeError('Pair not found')) + ticker_mock = MagicMock(side_effect=ExchangeError("Pair not found")) mocker.patch(f"{EXMS}.fetch_ticker", ticker_mock) exchange = get_patched_exchange(mocker, default_conf) dp = DataProvider(default_conf, exchange) - res = dp.ticker('UNITTEST/BTC') + res = dp.ticker("UNITTEST/BTC") assert res == {} def test_current_whitelist(mocker, default_conf, tickers): # patch default conf to volumepairlist - default_conf['pairlists'][0] = {'method': 'VolumePairList', "number_assets": 5} + default_conf["pairlists"][0] = {"method": "VolumePairList", "number_assets": 5} - mocker.patch.multiple(EXMS, - exchange_has=MagicMock(return_value=True), - get_tickers=tickers) + mocker.patch.multiple(EXMS, exchange_has=MagicMock(return_value=True), get_tickers=tickers) exchange = get_patched_exchange(mocker, default_conf) pairlist = PairListManager(exchange, default_conf) @@ -331,7 +345,6 @@ def test_current_whitelist(mocker, default_conf, tickers): def test_get_analyzed_dataframe(mocker, default_conf, ohlcv_history): - default_conf["runmode"] = RunMode.DRY_RUN timeframe = default_conf["timeframe"] @@ -384,28 +397,27 @@ def test_no_exchange_mode(default_conf): dp.refresh([()]) with pytest.raises(OperationalException, match=message): - dp.ohlcv('XRP/USDT', '5m', '') + dp.ohlcv("XRP/USDT", "5m", "") with pytest.raises(OperationalException, match=message): - dp.market('XRP/USDT') + dp.market("XRP/USDT") with pytest.raises(OperationalException, match=message): - dp.ticker('XRP/USDT') + dp.ticker("XRP/USDT") with pytest.raises(OperationalException, match=message): - dp.orderbook('XRP/USDT', 20) + dp.orderbook("XRP/USDT", 20) with pytest.raises(OperationalException, match=message): dp.available_pairs() def test_dp_send_msg(default_conf): - default_conf["runmode"] = RunMode.DRY_RUN - default_conf["timeframe"] = '1h' + default_conf["timeframe"] = "1h" dp = DataProvider(default_conf, None) - msg = 'Test message' + msg = "Test message" dp.send_msg(msg) assert msg in dp._msg_queue @@ -424,81 +436,81 @@ def test_dp_send_msg(default_conf): def test_dp__add_external_df(default_conf_usdt): - timeframe = '1h' + timeframe = "1h" default_conf_usdt["timeframe"] = timeframe dp = DataProvider(default_conf_usdt, None) - df = generate_test_data(timeframe, 24, '2022-01-01 00:00:00+00:00') + df = generate_test_data(timeframe, 24, "2022-01-01 00:00:00+00:00") last_analyzed = datetime.now(timezone.utc) - res = dp._add_external_df('ETH/USDT', df, last_analyzed, timeframe, CandleType.SPOT) + res = dp._add_external_df("ETH/USDT", df, last_analyzed, timeframe, CandleType.SPOT) assert res[0] is False # Why 1000 ?? assert res[1] == 1000 # Hard add dataframe - dp._replace_external_df('ETH/USDT', df, last_analyzed, timeframe, CandleType.SPOT) + dp._replace_external_df("ETH/USDT", df, last_analyzed, timeframe, CandleType.SPOT) # BTC is not stored yet - res = dp._add_external_df('BTC/USDT', df, last_analyzed, timeframe, CandleType.SPOT) + res = dp._add_external_df("BTC/USDT", df, last_analyzed, timeframe, CandleType.SPOT) assert res[0] is False - df_res, _ = dp.get_producer_df('ETH/USDT', timeframe, CandleType.SPOT) + df_res, _ = dp.get_producer_df("ETH/USDT", timeframe, CandleType.SPOT) assert len(df_res) == 24 # Add the same dataframe again - dataframe size shall not change. - res = dp._add_external_df('ETH/USDT', df, last_analyzed, timeframe, CandleType.SPOT) + res = dp._add_external_df("ETH/USDT", df, last_analyzed, timeframe, CandleType.SPOT) assert res[0] is True assert isinstance(res[1], int) assert res[1] == 0 - df, _ = dp.get_producer_df('ETH/USDT', timeframe, CandleType.SPOT) + df, _ = dp.get_producer_df("ETH/USDT", timeframe, CandleType.SPOT) assert len(df) == 24 # Add a new day. - df2 = generate_test_data(timeframe, 24, '2022-01-02 00:00:00+00:00') + df2 = generate_test_data(timeframe, 24, "2022-01-02 00:00:00+00:00") - res = dp._add_external_df('ETH/USDT', df2, last_analyzed, timeframe, CandleType.SPOT) + res = dp._add_external_df("ETH/USDT", df2, last_analyzed, timeframe, CandleType.SPOT) assert res[0] is True assert isinstance(res[1], int) assert res[1] == 0 - df, _ = dp.get_producer_df('ETH/USDT', timeframe, CandleType.SPOT) + df, _ = dp.get_producer_df("ETH/USDT", timeframe, CandleType.SPOT) assert len(df) == 48 # Add a dataframe with a 12 hour offset - so 12 candles are overlapping, and 12 valid. - df3 = generate_test_data(timeframe, 24, '2022-01-02 12:00:00+00:00') + df3 = generate_test_data(timeframe, 24, "2022-01-02 12:00:00+00:00") - res = dp._add_external_df('ETH/USDT', df3, last_analyzed, timeframe, CandleType.SPOT) + res = dp._add_external_df("ETH/USDT", df3, last_analyzed, timeframe, CandleType.SPOT) assert res[0] is True assert isinstance(res[1], int) assert res[1] == 0 - df, _ = dp.get_producer_df('ETH/USDT', timeframe, CandleType.SPOT) + df, _ = dp.get_producer_df("ETH/USDT", timeframe, CandleType.SPOT) # New length = 48 + 12 (since we have a 12 hour offset). assert len(df) == 60 - assert df.iloc[-1]['date'] == df3.iloc[-1]['date'] - assert df.iloc[-1]['date'] == Timestamp('2022-01-03 11:00:00+00:00') + assert df.iloc[-1]["date"] == df3.iloc[-1]["date"] + assert df.iloc[-1]["date"] == Timestamp("2022-01-03 11:00:00+00:00") # Generate 1 new candle - df4 = generate_test_data(timeframe, 1, '2022-01-03 12:00:00+00:00') - res = dp._add_external_df('ETH/USDT', df4, last_analyzed, timeframe, CandleType.SPOT) + df4 = generate_test_data(timeframe, 1, "2022-01-03 12:00:00+00:00") + res = dp._add_external_df("ETH/USDT", df4, last_analyzed, timeframe, CandleType.SPOT) # assert res[0] is True # assert res[1] == 0 - df, _ = dp.get_producer_df('ETH/USDT', timeframe, CandleType.SPOT) + df, _ = dp.get_producer_df("ETH/USDT", timeframe, CandleType.SPOT) # New length = 61 + 1 assert len(df) == 61 - assert df.iloc[-2]['date'] == Timestamp('2022-01-03 11:00:00+00:00') - assert df.iloc[-1]['date'] == Timestamp('2022-01-03 12:00:00+00:00') + assert df.iloc[-2]["date"] == Timestamp("2022-01-03 11:00:00+00:00") + assert df.iloc[-1]["date"] == Timestamp("2022-01-03 12:00:00+00:00") # Gap in the data ... - df4 = generate_test_data(timeframe, 1, '2022-01-05 00:00:00+00:00') - res = dp._add_external_df('ETH/USDT', df4, last_analyzed, timeframe, CandleType.SPOT) + df4 = generate_test_data(timeframe, 1, "2022-01-05 00:00:00+00:00") + res = dp._add_external_df("ETH/USDT", df4, last_analyzed, timeframe, CandleType.SPOT) assert res[0] is False # 36 hours - from 2022-01-03 12:00:00+00:00 to 2022-01-05 00:00:00+00:00 assert isinstance(res[1], int) assert res[1] == 36 - df, _ = dp.get_producer_df('ETH/USDT', timeframe, CandleType.SPOT) + df, _ = dp.get_producer_df("ETH/USDT", timeframe, CandleType.SPOT) # New length = 61 + 1 assert len(df) == 61 # Empty dataframe - df4 = generate_test_data(timeframe, 0, '2022-01-05 00:00:00+00:00') - res = dp._add_external_df('ETH/USDT', df4, last_analyzed, timeframe, CandleType.SPOT) + df4 = generate_test_data(timeframe, 0, "2022-01-05 00:00:00+00:00") + res = dp._add_external_df("ETH/USDT", df4, last_analyzed, timeframe, CandleType.SPOT) assert res[0] is False # 36 hours - from 2022-01-03 12:00:00+00:00 to 2022-01-05 00:00:00+00:00 assert isinstance(res[1], int) @@ -506,59 +518,59 @@ def test_dp__add_external_df(default_conf_usdt): def test_dp_get_required_startup(default_conf_usdt): - timeframe = '1h' + timeframe = "1h" default_conf_usdt["timeframe"] = timeframe dp = DataProvider(default_conf_usdt, None) # No FreqAI config - assert dp.get_required_startup('5m') == 0 - assert dp.get_required_startup('1h') == 0 - assert dp.get_required_startup('1d') == 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') == 20 - assert dp.get_required_startup('1h') == 20 - assert dp.get_required_startup('1h') == 20 + dp._config["startup_candle_count"] = 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 - dp._config['freqai'] = { - 'enabled': True, - 'train_period_days': 20, - 'feature_parameters': { - 'indicator_periods_candles': [ + dp._config["freqai"] = { + "enabled": True, + "train_period_days": 20, + "feature_parameters": { + "indicator_periods_candles": [ 5, 20, ] - } + }, } - assert dp.get_required_startup('5m') == 5780 - assert dp.get_required_startup('1h') == 500 - assert dp.get_required_startup('1d') == 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') == 5780 - assert dp.get_required_startup('1h') == 500 - assert dp.get_required_startup('1d') == 40 + dp._config["startup_candle_count"] = 0 + 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') == 5810 - assert dp.get_required_startup('1h') == 530 - assert dp.get_required_startup('1d') == 70 + dp._config["freqai"]["feature_parameters"]["indicator_periods_candles"][1] = 50 + 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 - dp._config['freqai'] = { - 'enabled': True, - 'train_period_days': 180, - 'feature_parameters': { - 'indicator_periods_candles': [ + dp._config["freqai"] = { + "enabled": True, + "train_period_days": 180, + "feature_parameters": { + "indicator_periods_candles": [ 10, 20, ] - } + }, } - dp._config['startup_candle_count'] = 40 - assert dp.get_required_startup('5m') == 51880 - assert dp.get_required_startup('1h') == 4360 - assert dp.get_required_startup('1d') == 220 + dp._config["startup_candle_count"] = 40 + assert dp.get_required_startup("5m") == 51880 + assert dp.get_required_startup("1h") == 4360 + assert dp.get_required_startup("1d") == 220 diff --git a/tests/data/test_download_data.py b/tests/data/test_download_data.py index 1518b28f3..cd7f4ab8f 100644 --- a/tests/data/test_download_data.py +++ b/tests/data/test_download_data.py @@ -10,83 +10,84 @@ from tests.conftest import EXMS, log_has, patch_exchange def test_download_data_main_no_markets(mocker, caplog): - dl_mock = mocker.patch('freqtrade.data.history.history_utils.refresh_backtest_ohlcv_data', - MagicMock(return_value=["ETH/BTC", "XRP/BTC"])) - patch_exchange(mocker, id='binance') - mocker.patch(f'{EXMS}.get_markets', return_value={}) + dl_mock = mocker.patch( + "freqtrade.data.history.history_utils.refresh_backtest_ohlcv_data", + MagicMock(return_value=["ETH/BTC", "XRP/BTC"]), + ) + patch_exchange(mocker, id="binance") + mocker.patch(f"{EXMS}.get_markets", return_value={}) config = setup_utils_configuration({"exchange": "binance"}, RunMode.UTIL_EXCHANGE) - config.update({ - "days": 20, - "pairs": ["ETH/BTC", "XRP/BTC"], - "timeframes": ["5m", "1h"] - }) + config.update({"days": 20, "pairs": ["ETH/BTC", "XRP/BTC"], "timeframes": ["5m", "1h"]}) download_data_main(config) - assert dl_mock.call_args[1]['timerange'].starttype == "date" + assert dl_mock.call_args[1]["timerange"].starttype == "date" assert log_has("Pairs [ETH/BTC,XRP/BTC] not available on exchange Binance.", caplog) def test_download_data_main_all_pairs(mocker, markets): - - dl_mock = mocker.patch('freqtrade.data.history.history_utils.refresh_backtest_ohlcv_data', - MagicMock(return_value=["ETH/BTC", "XRP/BTC"])) + dl_mock = mocker.patch( + "freqtrade.data.history.history_utils.refresh_backtest_ohlcv_data", + MagicMock(return_value=["ETH/BTC", "XRP/BTC"]), + ) patch_exchange(mocker) - mocker.patch(f'{EXMS}.markets', PropertyMock(return_value=markets)) + mocker.patch(f"{EXMS}.markets", PropertyMock(return_value=markets)) config = setup_utils_configuration({"exchange": "binance"}, RunMode.UTIL_EXCHANGE) - config.update({ - "pairs": [".*/USDT"], - "timeframes": ["5m", "1h"] - }) + config.update({"pairs": [".*/USDT"], "timeframes": ["5m", "1h"]}) download_data_main(config) - expected = set(['BTC/USDT', 'ETH/USDT', 'XRP/USDT', 'NEO/USDT', 'TKN/USDT']) - assert set(dl_mock.call_args_list[0][1]['pairs']) == expected + 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 dl_mock.reset_mock() - config.update({ - "pairs": [".*/USDT"], - "timeframes": ["5m", "1h"], - "include_inactive": True - }) + config.update({"pairs": [".*/USDT"], "timeframes": ["5m", "1h"], "include_inactive": True}) download_data_main(config) - 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 + 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 def test_download_data_main_trades(mocker): - dl_mock = mocker.patch('freqtrade.data.history.history_utils.refresh_backtest_trades_data', - MagicMock(return_value=[])) - convert_mock = mocker.patch('freqtrade.data.history.history_utils.convert_trades_to_ohlcv', - MagicMock(return_value=[])) + dl_mock = mocker.patch( + "freqtrade.data.history.history_utils.refresh_backtest_trades_data", + MagicMock(return_value=[]), + ) + convert_mock = mocker.patch( + "freqtrade.data.history.history_utils.convert_trades_to_ohlcv", MagicMock(return_value=[]) + ) patch_exchange(mocker) - mocker.patch(f'{EXMS}.get_markets', return_value={}) + mocker.patch(f"{EXMS}.get_markets", return_value={}) config = setup_utils_configuration({"exchange": "binance"}, RunMode.UTIL_EXCHANGE) - config.update({ - "days": 20, - "pairs": ["ETH/BTC", "XRP/BTC"], - "timeframes": ["5m", "1h"], - "download_trades": True, - }) + config.update( + { + "days": 20, + "pairs": ["ETH/BTC", "XRP/BTC"], + "timeframes": ["5m", "1h"], + "download_trades": True, + } + ) download_data_main(config) - assert dl_mock.call_args[1]['timerange'].starttype == "date" + assert dl_mock.call_args[1]["timerange"].starttype == "date" assert dl_mock.call_count == 1 assert convert_mock.call_count == 1 - config.update({ - "download_trades": True, - "trading_mode": "futures", - }) + config.update( + { + "download_trades": True, + "trading_mode": "futures", + } + ) def test_download_data_main_data_invalid(mocker): patch_exchange(mocker, id="kraken") - mocker.patch(f'{EXMS}.get_markets', return_value={}) + mocker.patch(f"{EXMS}.get_markets", return_value={}) config = setup_utils_configuration({"exchange": "kraken"}, RunMode.UTIL_EXCHANGE) - config.update({ - "days": 20, - "pairs": ["ETH/BTC", "XRP/BTC"], - "timeframes": ["5m", "1h"], - }) + config.update( + { + "days": 20, + "pairs": ["ETH/BTC", "XRP/BTC"], + "timeframes": ["5m", "1h"], + } + ) with pytest.raises(OperationalException, match=r"Historic klines not available for .*"): download_data_main(config) diff --git a/tests/data/test_entryexitanalysis.py b/tests/data/test_entryexitanalysis.py index 810e2c53b..49ef74c0a 100644 --- a/tests/data/test_entryexitanalysis.py +++ b/tests/data/test_entryexitanalysis.py @@ -20,198 +20,228 @@ def entryexitanalysis_cleanup() -> None: def test_backtest_analysis_nomock(default_conf, mocker, caplog, testdatadir, user_dir, capsys): caplog.set_level(logging.INFO) - (user_dir / 'backtest_results').mkdir(parents=True, exist_ok=True) + (user_dir / "backtest_results").mkdir(parents=True, exist_ok=True) - default_conf.update({ - "use_exit_signal": True, - "exit_profit_only": False, - "exit_profit_offset": 0.0, - "ignore_roi_if_entry_signal": False, - }) - patch_exchange(mocker) - result1 = pd.DataFrame({'pair': ['ETH/BTC', 'LTC/BTC', 'ETH/BTC', 'LTC/BTC'], - 'profit_ratio': [0.025, 0.05, -0.1, -0.05], - 'profit_abs': [0.5, 2.0, -4.0, -2.0], - 'open_date': pd.to_datetime(['2018-01-29 18:40:00', - '2018-01-30 03:30:00', - '2018-01-30 08:10:00', - '2018-01-31 13:30:00', ], utc=True - ), - 'close_date': pd.to_datetime(['2018-01-29 20:45:00', - '2018-01-30 05:35:00', - '2018-01-30 09:10:00', - '2018-01-31 15:00:00', ], utc=True), - 'trade_duration': [235, 40, 60, 90], - 'is_open': [False, False, False, False], - 'stake_amount': [0.01, 0.01, 0.01, 0.01], - 'open_rate': [0.104445, 0.10302485, 0.10302485, 0.10302485], - 'close_rate': [0.104969, 0.103541, 0.102041, 0.102541], - "is_short": [False, False, False, False], - 'enter_tag': ["enter_tag_long_a", - "enter_tag_long_b", - "enter_tag_long_a", - "enter_tag_long_b"], - 'exit_reason': [ExitType.ROI, - ExitType.EXIT_SIGNAL, - ExitType.STOP_LOSS, - ExitType.TRAILING_STOP_LOSS] - }) - - backtestmock = MagicMock(side_effect=[ + default_conf.update( { - 'results': result1, - 'config': default_conf, - 'locks': [], - 'rejected_signals': 20, - 'timedout_entry_orders': 0, - 'timedout_exit_orders': 0, - 'canceled_trade_entries': 0, - 'canceled_entry_orders': 0, - 'replaced_entry_orders': 0, - 'final_balance': 1000, + "use_exit_signal": True, + "exit_profit_only": False, + "exit_profit_offset": 0.0, + "ignore_roi_if_entry_signal": False, } - ]) - mocker.patch('freqtrade.plugins.pairlistmanager.PairListManager.whitelist', - PropertyMock(return_value=['ETH/BTC', 'LTC/BTC', 'DASH/BTC'])) - mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest', backtestmock) + ) + patch_exchange(mocker) + result1 = pd.DataFrame( + { + "pair": ["ETH/BTC", "LTC/BTC", "ETH/BTC", "LTC/BTC"], + "profit_ratio": [0.025, 0.05, -0.1, -0.05], + "profit_abs": [0.5, 2.0, -4.0, -2.0], + "open_date": pd.to_datetime( + [ + "2018-01-29 18:40:00", + "2018-01-30 03:30:00", + "2018-01-30 08:10:00", + "2018-01-31 13:30:00", + ], + utc=True, + ), + "close_date": pd.to_datetime( + [ + "2018-01-29 20:45:00", + "2018-01-30 05:35:00", + "2018-01-30 09:10:00", + "2018-01-31 15:00:00", + ], + utc=True, + ), + "trade_duration": [235, 40, 60, 90], + "is_open": [False, False, False, False], + "stake_amount": [0.01, 0.01, 0.01, 0.01], + "open_rate": [0.104445, 0.10302485, 0.10302485, 0.10302485], + "close_rate": [0.104969, 0.103541, 0.102041, 0.102541], + "is_short": [False, False, False, False], + "enter_tag": [ + "enter_tag_long_a", + "enter_tag_long_b", + "enter_tag_long_a", + "enter_tag_long_b", + ], + "exit_reason": [ + ExitType.ROI, + ExitType.EXIT_SIGNAL, + ExitType.STOP_LOSS, + ExitType.TRAILING_STOP_LOSS, + ], + } + ) + + backtestmock = MagicMock( + side_effect=[ + { + "results": result1, + "config": default_conf, + "locks": [], + "rejected_signals": 20, + "timedout_entry_orders": 0, + "timedout_exit_orders": 0, + "canceled_trade_entries": 0, + "canceled_entry_orders": 0, + "replaced_entry_orders": 0, + "final_balance": 1000, + } + ] + ) + mocker.patch( + "freqtrade.plugins.pairlistmanager.PairListManager.whitelist", + PropertyMock(return_value=["ETH/BTC", "LTC/BTC", "DASH/BTC"]), + ) + mocker.patch("freqtrade.optimize.backtesting.Backtesting.backtest", backtestmock) patched_configuration_load_config_file(mocker, default_conf) args = [ - 'backtesting', - '--config', 'config.json', - '--datadir', str(testdatadir), - '--user-data-dir', str(user_dir), - '--timeframe', '5m', - '--timerange', '1515560100-1517287800', - '--export', 'signals', - '--cache', 'none', + "backtesting", + "--config", + "config.json", + "--datadir", + str(testdatadir), + "--user-data-dir", + str(user_dir), + "--timeframe", + "5m", + "--timerange", + "1515560100-1517287800", + "--export", + "signals", + "--cache", + "none", ] args = get_args(args) start_backtesting(args) captured = capsys.readouterr() - assert 'BACKTESTING REPORT' in captured.out - assert 'EXIT REASON STATS' in captured.out - assert 'LEFT OPEN TRADES REPORT' in captured.out + assert "BACKTESTING REPORT" in captured.out + assert "EXIT REASON STATS" in captured.out + assert "LEFT OPEN TRADES REPORT" in captured.out base_args = [ - 'backtesting-analysis', - '--config', 'config.json', - '--datadir', str(testdatadir), - '--user-data-dir', str(user_dir), + "backtesting-analysis", + "--config", + "config.json", + "--datadir", + str(testdatadir), + "--user-data-dir", + str(user_dir), ] # test group 0 and indicator list - args = get_args(base_args + - ['--analysis-groups', "0", - '--indicator-list', "close", "rsi", "profit_abs"] - ) + args = get_args( + base_args + ["--analysis-groups", "0", "--indicator-list", "close", "rsi", "profit_abs"] + ) start_analysis_entries_exits(args) captured = capsys.readouterr() - assert 'LTC/BTC' in captured.out - assert 'ETH/BTC' in captured.out - assert 'enter_tag_long_a' in captured.out - assert 'enter_tag_long_b' in captured.out - assert 'exit_signal' in captured.out - assert 'roi' in captured.out - assert 'stop_loss' in captured.out - assert 'trailing_stop_loss' in captured.out - assert '0.5' in captured.out - assert '-4' in captured.out - assert '-2' in captured.out - assert '-3.5' in captured.out - assert '50' in captured.out - assert '0' in captured.out - assert '0.01616' in captured.out - assert '34.049' in captured.out - assert '0.104411' in captured.out - assert '52.8292' in captured.out + assert "LTC/BTC" in captured.out + assert "ETH/BTC" in captured.out + assert "enter_tag_long_a" in captured.out + assert "enter_tag_long_b" in captured.out + assert "exit_signal" in captured.out + assert "roi" in captured.out + assert "stop_loss" in captured.out + assert "trailing_stop_loss" in captured.out + assert "0.5" in captured.out + assert "-4" in captured.out + assert "-2" in captured.out + assert "-3.5" in captured.out + assert "50" in captured.out + assert "0" in captured.out + assert "0.01616" in captured.out + assert "34.049" in captured.out + assert "0.104411" in captured.out + assert "52.8292" in captured.out # test group 1 - args = get_args(base_args + ['--analysis-groups', "1"]) + args = get_args(base_args + ["--analysis-groups", "1"]) start_analysis_entries_exits(args) captured = capsys.readouterr() - assert 'enter_tag_long_a' in captured.out - assert 'enter_tag_long_b' in captured.out - assert 'total_profit_pct' in captured.out - assert '-3.5' in captured.out - assert '-1.75' in captured.out - assert '-7.5' in captured.out - assert '-3.75' in captured.out - assert '0' in captured.out + assert "enter_tag_long_a" in captured.out + assert "enter_tag_long_b" in captured.out + assert "total_profit_pct" in captured.out + assert "-3.5" in captured.out + assert "-1.75" in captured.out + assert "-7.5" in captured.out + assert "-3.75" in captured.out + assert "0" in captured.out # test group 2 - args = get_args(base_args + ['--analysis-groups', "2"]) + args = get_args(base_args + ["--analysis-groups", "2"]) start_analysis_entries_exits(args) captured = capsys.readouterr() - assert 'enter_tag_long_a' in captured.out - assert 'enter_tag_long_b' in captured.out - assert 'exit_signal' in captured.out - assert 'roi' in captured.out - assert 'stop_loss' in captured.out - assert 'trailing_stop_loss' in captured.out - assert 'total_profit_pct' in captured.out - assert '-10' in captured.out - assert '-5' in captured.out - assert '2.5' in captured.out + assert "enter_tag_long_a" in captured.out + assert "enter_tag_long_b" in captured.out + assert "exit_signal" in captured.out + assert "roi" in captured.out + assert "stop_loss" in captured.out + assert "trailing_stop_loss" in captured.out + assert "total_profit_pct" in captured.out + assert "-10" in captured.out + assert "-5" in captured.out + assert "2.5" in captured.out # test group 3 - args = get_args(base_args + ['--analysis-groups', "3"]) + args = get_args(base_args + ["--analysis-groups", "3"]) start_analysis_entries_exits(args) captured = capsys.readouterr() - assert 'LTC/BTC' in captured.out - assert 'ETH/BTC' in captured.out - assert 'enter_tag_long_a' in captured.out - assert 'enter_tag_long_b' in captured.out - assert 'total_profit_pct' in captured.out - assert '-7.5' in captured.out - assert '-3.75' in captured.out - assert '-1.75' in captured.out - assert '0' in captured.out - assert '2' in captured.out + assert "LTC/BTC" in captured.out + assert "ETH/BTC" in captured.out + assert "enter_tag_long_a" in captured.out + assert "enter_tag_long_b" in captured.out + assert "total_profit_pct" in captured.out + assert "-7.5" in captured.out + assert "-3.75" in captured.out + assert "-1.75" in captured.out + assert "0" in captured.out + assert "2" in captured.out # test group 4 - args = get_args(base_args + ['--analysis-groups', "4"]) + args = get_args(base_args + ["--analysis-groups", "4"]) start_analysis_entries_exits(args) captured = capsys.readouterr() - assert 'LTC/BTC' in captured.out - assert 'ETH/BTC' in captured.out - assert 'enter_tag_long_a' in captured.out - assert 'enter_tag_long_b' in captured.out - assert 'exit_signal' in captured.out - assert 'roi' in captured.out - assert 'stop_loss' in captured.out - assert 'trailing_stop_loss' in captured.out - assert 'total_profit_pct' in captured.out - assert '-10' in captured.out - assert '-5' in captured.out - assert '-4' in captured.out - assert '0.5' in captured.out - assert '1' in captured.out - assert '2.5' in captured.out + assert "LTC/BTC" in captured.out + assert "ETH/BTC" in captured.out + assert "enter_tag_long_a" in captured.out + assert "enter_tag_long_b" in captured.out + assert "exit_signal" in captured.out + assert "roi" in captured.out + assert "stop_loss" in captured.out + assert "trailing_stop_loss" in captured.out + assert "total_profit_pct" in captured.out + assert "-10" in captured.out + assert "-5" in captured.out + assert "-4" in captured.out + assert "0.5" in captured.out + assert "1" in captured.out + assert "2.5" in captured.out # test group 5 - args = get_args(base_args + ['--analysis-groups', "5"]) + args = get_args(base_args + ["--analysis-groups", "5"]) start_analysis_entries_exits(args) captured = capsys.readouterr() - assert 'exit_signal' in captured.out - assert 'roi' in captured.out - assert 'stop_loss' in captured.out - assert 'trailing_stop_loss' in captured.out + assert "exit_signal" in captured.out + assert "roi" in captured.out + assert "stop_loss" in captured.out + assert "trailing_stop_loss" in captured.out # test date filtering - args = get_args(base_args + - ['--analysis-groups', "0", "1", "2", - '--timerange', "20180129-20180130"] - ) + args = get_args( + base_args + ["--analysis-groups", "0", "1", "2", "--timerange", "20180129-20180130"] + ) start_analysis_entries_exits(args) captured = capsys.readouterr() - assert 'enter_tag_long_a' in captured.out - assert 'enter_tag_long_b' not in captured.out + assert "enter_tag_long_a" in captured.out + assert "enter_tag_long_b" not in captured.out # Due to the backtest mock, there's no rejected signals generated. - args = get_args(base_args + ['--rejected-signals']) + args = get_args(base_args + ["--rejected-signals"]) start_analysis_entries_exits(args) captured = capsys.readouterr() - assert 'no rejected signals' in captured.out + assert "no rejected signals" in captured.out diff --git a/tests/data/test_history.py b/tests/data/test_history.py index a816e2a5e..2fe82ea25 100644 --- a/tests/data/test_history.py +++ b/tests/data/test_history.py @@ -50,7 +50,7 @@ def _clean_test_file(file: Path) -> None: :param file: complete path to the file :return: None """ - file_swp = Path(str(file) + '.swp') + file_swp = Path(str(file) + ".swp") # 1. Delete file from the test if file.is_file(): file.unlink() @@ -61,181 +61,197 @@ def _clean_test_file(file: Path) -> None: def test_load_data_30min_timeframe(caplog, testdatadir) -> None: - ld = load_pair_history(pair='UNITTEST/BTC', timeframe='30m', datadir=testdatadir) + ld = load_pair_history(pair="UNITTEST/BTC", timeframe="30m", datadir=testdatadir) assert isinstance(ld, DataFrame) assert not log_has( - 'Download history data for pair: "UNITTEST/BTC", timeframe: 30m ' - 'and store in None.', caplog + 'Download history data for pair: "UNITTEST/BTC", timeframe: 30m ' "and store in None.", + caplog, ) def test_load_data_7min_timeframe(caplog, testdatadir) -> None: - ld = load_pair_history(pair='UNITTEST/BTC', timeframe='7m', datadir=testdatadir) + ld = load_pair_history(pair="UNITTEST/BTC", timeframe="7m", datadir=testdatadir) assert isinstance(ld, DataFrame) assert ld.empty assert log_has( - 'No history for UNITTEST/BTC, spot, 7m found. ' - 'Use `freqtrade download-data` to download the data', caplog + "No history for UNITTEST/BTC, spot, 7m found. " + "Use `freqtrade download-data` to download the data", + caplog, ) def test_load_data_1min_timeframe(ohlcv_history, mocker, caplog, testdatadir) -> None: - mocker.patch(f'{EXMS}.get_historic_ohlcv', return_value=ohlcv_history) - file = testdatadir / 'UNITTEST_BTC-1m.feather' - load_data(datadir=testdatadir, timeframe='1m', pairs=['UNITTEST/BTC']) + mocker.patch(f"{EXMS}.get_historic_ohlcv", return_value=ohlcv_history) + file = testdatadir / "UNITTEST_BTC-1m.feather" + load_data(datadir=testdatadir, timeframe="1m", pairs=["UNITTEST/BTC"]) assert file.is_file() assert not log_has( - 'Download history data for pair: "UNITTEST/BTC", interval: 1m ' - 'and store in None.', caplog + 'Download history data for pair: "UNITTEST/BTC", interval: 1m ' "and store in None.", caplog ) def test_load_data_mark(ohlcv_history, mocker, caplog, testdatadir) -> None: - mocker.patch(f'{EXMS}.get_historic_ohlcv', return_value=ohlcv_history) - file = testdatadir / 'futures/UNITTEST_USDT_USDT-1h-mark.feather' - load_data(datadir=testdatadir, timeframe='1h', pairs=['UNITTEST/BTC'], candle_type='mark') + mocker.patch(f"{EXMS}.get_historic_ohlcv", return_value=ohlcv_history) + file = testdatadir / "futures/UNITTEST_USDT_USDT-1h-mark.feather" + load_data(datadir=testdatadir, timeframe="1h", pairs=["UNITTEST/BTC"], candle_type="mark") assert file.is_file() assert not log_has( - 'Download history data for pair: "UNITTEST/USDT:USDT", interval: 1m ' - 'and store in None.', caplog + 'Download history data for pair: "UNITTEST/USDT:USDT", interval: 1m ' "and store in None.", + caplog, ) def test_load_data_startup_candles(mocker, testdatadir) -> None: ltfmock = mocker.patch( - 'freqtrade.data.history.datahandlers.featherdatahandler.FeatherDataHandler._ohlcv_load', - MagicMock(return_value=DataFrame())) - timerange = TimeRange('date', None, 1510639620, 0) - load_pair_history(pair='UNITTEST/BTC', timeframe='1m', - datadir=testdatadir, timerange=timerange, - startup_candles=20,) + "freqtrade.data.history.datahandlers.featherdatahandler.FeatherDataHandler._ohlcv_load", + MagicMock(return_value=DataFrame()), + ) + timerange = TimeRange("date", None, 1510639620, 0) + load_pair_history( + pair="UNITTEST/BTC", + timeframe="1m", + datadir=testdatadir, + timerange=timerange, + startup_candles=20, + ) assert ltfmock.call_count == 1 - assert ltfmock.call_args_list[0][1]['timerange'] != timerange + assert ltfmock.call_args_list[0][1]["timerange"] != timerange # startts is 20 minutes earlier - assert ltfmock.call_args_list[0][1]['timerange'].startts == timerange.startts - 20 * 60 + assert ltfmock.call_args_list[0][1]["timerange"].startts == timerange.startts - 20 * 60 -@pytest.mark.parametrize('candle_type', ['mark', '']) -def test_load_data_with_new_pair_1min(ohlcv_history_list, mocker, caplog, - default_conf, tmp_path, candle_type) -> None: +@pytest.mark.parametrize("candle_type", ["mark", ""]) +def test_load_data_with_new_pair_1min( + ohlcv_history_list, mocker, caplog, default_conf, tmp_path, candle_type +) -> None: """ Test load_pair_history() with 1 min timeframe """ - mocker.patch(f'{EXMS}.get_historic_ohlcv', return_value=ohlcv_history_list) + mocker.patch(f"{EXMS}.get_historic_ohlcv", return_value=ohlcv_history_list) exchange = get_patched_exchange(mocker, default_conf) - file = tmp_path / 'MEME_BTC-1m.feather' + file = tmp_path / "MEME_BTC-1m.feather" # do not download a new pair if refresh_pairs isn't set - load_pair_history(datadir=tmp_path, timeframe='1m', pair='MEME/BTC', candle_type=candle_type) + load_pair_history(datadir=tmp_path, timeframe="1m", pair="MEME/BTC", candle_type=candle_type) assert not file.is_file() assert log_has( f"No history for MEME/BTC, {candle_type}, 1m found. " - "Use `freqtrade download-data` to download the data", caplog + "Use `freqtrade download-data` to download the data", + caplog, ) # download a new pair if refresh_pairs is set - refresh_data(datadir=tmp_path, timeframe='1m', pairs=['MEME/BTC'], - exchange=exchange, candle_type=CandleType.SPOT - ) - load_pair_history(datadir=tmp_path, timeframe='1m', pair='MEME/BTC', candle_type=candle_type) + refresh_data( + datadir=tmp_path, + timeframe="1m", + pairs=["MEME/BTC"], + exchange=exchange, + candle_type=CandleType.SPOT, + ) + load_pair_history(datadir=tmp_path, timeframe="1m", pair="MEME/BTC", candle_type=candle_type) assert file.is_file() assert log_has_re( - r'\(0/1\) - Download history data for "MEME/BTC", 1m, ' - r'spot and store in .*', caplog + r'\(0/1\) - Download history data for "MEME/BTC", 1m, ' r"spot and store in .*", caplog ) def test_testdata_path(testdatadir) -> None: - assert str(Path('tests') / 'testdata') in str(testdatadir) + assert str(Path("tests") / "testdata") in str(testdatadir) -@pytest.mark.parametrize("pair,timeframe,expected_result,candle_type", [ - ("ETH/BTC", "5m", "freqtrade/hello/world/ETH_BTC-5m.json", ""), - ("ETH/USDT", "1M", "freqtrade/hello/world/ETH_USDT-1Mo.json", ""), - ("Fabric Token/ETH", "5m", "freqtrade/hello/world/Fabric_Token_ETH-5m.json", ""), - ("ETHH20", "5m", "freqtrade/hello/world/ETHH20-5m.json", ""), - (".XBTBON2H", "5m", "freqtrade/hello/world/_XBTBON2H-5m.json", ""), - ("ETHUSD.d", "5m", "freqtrade/hello/world/ETHUSD_d-5m.json", ""), - ("ACC_OLD/BTC", "5m", "freqtrade/hello/world/ACC_OLD_BTC-5m.json", ""), - ("ETH/BTC", "5m", "freqtrade/hello/world/futures/ETH_BTC-5m-mark.json", "mark"), - ("ACC_OLD/BTC", "5m", "freqtrade/hello/world/futures/ACC_OLD_BTC-5m-index.json", "index"), -]) +@pytest.mark.parametrize( + "pair,timeframe,expected_result,candle_type", + [ + ("ETH/BTC", "5m", "freqtrade/hello/world/ETH_BTC-5m.json", ""), + ("ETH/USDT", "1M", "freqtrade/hello/world/ETH_USDT-1Mo.json", ""), + ("Fabric Token/ETH", "5m", "freqtrade/hello/world/Fabric_Token_ETH-5m.json", ""), + ("ETHH20", "5m", "freqtrade/hello/world/ETHH20-5m.json", ""), + (".XBTBON2H", "5m", "freqtrade/hello/world/_XBTBON2H-5m.json", ""), + ("ETHUSD.d", "5m", "freqtrade/hello/world/ETHUSD_d-5m.json", ""), + ("ACC_OLD/BTC", "5m", "freqtrade/hello/world/ACC_OLD_BTC-5m.json", ""), + ("ETH/BTC", "5m", "freqtrade/hello/world/futures/ETH_BTC-5m-mark.json", "mark"), + ("ACC_OLD/BTC", "5m", "freqtrade/hello/world/futures/ACC_OLD_BTC-5m-index.json", "index"), + ], +) def test_json_pair_data_filename(pair, timeframe, expected_result, candle_type): fn = JsonDataHandler._pair_data_filename( - Path('freqtrade/hello/world'), - pair, - timeframe, - CandleType.from_string(candle_type) + Path("freqtrade/hello/world"), pair, timeframe, CandleType.from_string(candle_type) ) assert isinstance(fn, Path) assert fn == Path(expected_result) fn = JsonGzDataHandler._pair_data_filename( - Path('freqtrade/hello/world'), + Path("freqtrade/hello/world"), pair, timeframe, - candle_type=CandleType.from_string(candle_type) + candle_type=CandleType.from_string(candle_type), ) assert isinstance(fn, Path) - assert fn == Path(expected_result + '.gz') + assert fn == Path(expected_result + ".gz") -@pytest.mark.parametrize("pair,trading_mode,expected_result", [ - ("ETH/BTC", '', 'freqtrade/hello/world/ETH_BTC-trades.json'), - ("ETH/USDT:USDT", 'futures', 'freqtrade/hello/world/futures/ETH_USDT_USDT-trades.json'), - ("Fabric Token/ETH", '', 'freqtrade/hello/world/Fabric_Token_ETH-trades.json'), - ("ETHH20", '', 'freqtrade/hello/world/ETHH20-trades.json'), - (".XBTBON2H", '', 'freqtrade/hello/world/_XBTBON2H-trades.json'), - ("ETHUSD.d", '', 'freqtrade/hello/world/ETHUSD_d-trades.json'), - ("ACC_OLD_BTC", '', 'freqtrade/hello/world/ACC_OLD_BTC-trades.json'), -]) +@pytest.mark.parametrize( + "pair,trading_mode,expected_result", + [ + ("ETH/BTC", "", "freqtrade/hello/world/ETH_BTC-trades.json"), + ("ETH/USDT:USDT", "futures", "freqtrade/hello/world/futures/ETH_USDT_USDT-trades.json"), + ("Fabric Token/ETH", "", "freqtrade/hello/world/Fabric_Token_ETH-trades.json"), + ("ETHH20", "", "freqtrade/hello/world/ETHH20-trades.json"), + (".XBTBON2H", "", "freqtrade/hello/world/_XBTBON2H-trades.json"), + ("ETHUSD.d", "", "freqtrade/hello/world/ETHUSD_d-trades.json"), + ("ACC_OLD_BTC", "", "freqtrade/hello/world/ACC_OLD_BTC-trades.json"), + ], +) def test_json_pair_trades_filename(pair, trading_mode, expected_result): - fn = JsonDataHandler._pair_trades_filename(Path('freqtrade/hello/world'), pair, trading_mode) + fn = JsonDataHandler._pair_trades_filename(Path("freqtrade/hello/world"), pair, trading_mode) assert isinstance(fn, Path) assert fn == Path(expected_result) - fn = JsonGzDataHandler._pair_trades_filename(Path('freqtrade/hello/world'), pair, trading_mode) + fn = JsonGzDataHandler._pair_trades_filename(Path("freqtrade/hello/world"), pair, trading_mode) assert isinstance(fn, Path) - assert fn == Path(expected_result + '.gz') + assert fn == Path(expected_result + ".gz") def test_load_cached_data_for_updating(mocker, testdatadir) -> None: - - data_handler = get_datahandler(testdatadir, 'json') + data_handler = get_datahandler(testdatadir, "json") test_data = None - test_filename = testdatadir.joinpath('UNITTEST_BTC-1m.json') + test_filename = testdatadir.joinpath("UNITTEST_BTC-1m.json") with test_filename.open("rt") as file: test_data = json.load(file) - test_data_df = ohlcv_to_dataframe(test_data, '1m', 'UNITTEST/BTC', - fill_missing=False, drop_incomplete=False) + test_data_df = ohlcv_to_dataframe( + test_data, "1m", "UNITTEST/BTC", fill_missing=False, drop_incomplete=False + ) # now = last cached item + 1 hour now_ts = test_data[-1][0] / 1000 + 60 * 60 # timeframe starts earlier than the cached data # should fully update data - timerange = TimeRange('date', None, test_data[0][0] / 1000 - 1, 0) + timerange = TimeRange("date", None, test_data[0][0] / 1000 - 1, 0) data, start_ts, end_ts = _load_cached_data_for_updating( - 'UNITTEST/BTC', '1m', timerange, data_handler, CandleType.SPOT) + "UNITTEST/BTC", "1m", timerange, data_handler, CandleType.SPOT + ) assert data.empty assert start_ts == test_data[0][0] - 1000 assert end_ts is None # timeframe starts earlier than the cached data - prepending - timerange = TimeRange('date', None, test_data[0][0] / 1000 - 1, 0) + timerange = TimeRange("date", None, test_data[0][0] / 1000 - 1, 0) data, start_ts, end_ts = _load_cached_data_for_updating( - 'UNITTEST/BTC', '1m', timerange, data_handler, CandleType.SPOT, True) + "UNITTEST/BTC", "1m", timerange, data_handler, CandleType.SPOT, True + ) assert_frame_equal(data, test_data_df.iloc[:-1]) assert start_ts == test_data[0][0] - 1000 assert end_ts == test_data[0][0] # timeframe starts in the center of the cached data # should return the cached data w/o the last item - timerange = TimeRange('date', None, test_data[0][0] / 1000 + 1, 0) + timerange = TimeRange("date", None, test_data[0][0] / 1000 + 1, 0) data, start_ts, end_ts = _load_cached_data_for_updating( - 'UNITTEST/BTC', '1m', timerange, data_handler, CandleType.SPOT) + "UNITTEST/BTC", "1m", timerange, data_handler, CandleType.SPOT + ) assert_frame_equal(data, test_data_df.iloc[:-1]) assert test_data[-2][0] <= start_ts < test_data[-1][0] @@ -243,27 +259,30 @@ def test_load_cached_data_for_updating(mocker, testdatadir) -> None: # timeframe starts after the cached data # should return the cached data w/o the last item - timerange = TimeRange('date', None, test_data[-1][0] / 1000 + 100, 0) + timerange = TimeRange("date", None, test_data[-1][0] / 1000 + 100, 0) data, start_ts, end_ts = _load_cached_data_for_updating( - 'UNITTEST/BTC', '1m', timerange, data_handler, CandleType.SPOT) + "UNITTEST/BTC", "1m", timerange, data_handler, CandleType.SPOT + ) assert_frame_equal(data, test_data_df.iloc[:-1]) assert test_data[-2][0] <= start_ts < test_data[-1][0] assert end_ts is None # no datafile exist # should return timestamp start time - timerange = TimeRange('date', None, now_ts - 10000, 0) + timerange = TimeRange("date", None, now_ts - 10000, 0) data, start_ts, end_ts = _load_cached_data_for_updating( - 'NONEXIST/BTC', '1m', timerange, data_handler, CandleType.SPOT) + "NONEXIST/BTC", "1m", timerange, data_handler, CandleType.SPOT + ) assert data.empty assert start_ts == (now_ts - 10000) * 1000 assert end_ts is None # no datafile exist # should return timestamp start and end time time - timerange = TimeRange('date', 'date', now_ts - 1000000, now_ts - 100000) + timerange = TimeRange("date", "date", now_ts - 1000000, now_ts - 100000) data, start_ts, end_ts = _load_cached_data_for_updating( - 'NONEXIST/BTC', '1m', timerange, data_handler, CandleType.SPOT) + "NONEXIST/BTC", "1m", timerange, data_handler, CandleType.SPOT + ) assert data.empty assert start_ts == (now_ts - 1000000) * 1000 assert end_ts == (now_ts - 100000) * 1000 @@ -271,43 +290,43 @@ def test_load_cached_data_for_updating(mocker, testdatadir) -> None: # no datafile exist, no timeframe is set # should return an empty array and None data, start_ts, end_ts = _load_cached_data_for_updating( - 'NONEXIST/BTC', '1m', None, data_handler, CandleType.SPOT) + "NONEXIST/BTC", "1m", None, data_handler, CandleType.SPOT + ) assert data.empty assert start_ts is None assert end_ts is None -@pytest.mark.parametrize('candle_type,subdir,file_tail', [ - ('mark', 'futures/', '-mark'), - ('spot', '', ''), -]) +@pytest.mark.parametrize( + "candle_type,subdir,file_tail", + [ + ("mark", "futures/", "-mark"), + ("spot", "", ""), + ], +) def test_download_pair_history( - ohlcv_history_list, - mocker, - default_conf, - tmp_path, - candle_type, - subdir, - file_tail + ohlcv_history_list, mocker, default_conf, tmp_path, candle_type, subdir, file_tail ) -> None: - mocker.patch(f'{EXMS}.get_historic_ohlcv', return_value=ohlcv_history_list) + mocker.patch(f"{EXMS}.get_historic_ohlcv", return_value=ohlcv_history_list) exchange = get_patched_exchange(mocker, default_conf) - file1_1 = tmp_path / f'{subdir}MEME_BTC-1m{file_tail}.feather' - file1_5 = tmp_path / f'{subdir}MEME_BTC-5m{file_tail}.feather' - file2_1 = tmp_path / f'{subdir}CFI_BTC-1m{file_tail}.feather' - file2_5 = tmp_path / f'{subdir}CFI_BTC-5m{file_tail}.feather' + file1_1 = tmp_path / f"{subdir}MEME_BTC-1m{file_tail}.feather" + file1_5 = tmp_path / f"{subdir}MEME_BTC-5m{file_tail}.feather" + file2_1 = tmp_path / f"{subdir}CFI_BTC-1m{file_tail}.feather" + file2_5 = tmp_path / f"{subdir}CFI_BTC-5m{file_tail}.feather" assert not file1_1.is_file() assert not file2_1.is_file() - assert _download_pair_history(datadir=tmp_path, exchange=exchange, - pair='MEME/BTC', - timeframe='1m', - candle_type=candle_type) - assert _download_pair_history(datadir=tmp_path, exchange=exchange, - pair='CFI/BTC', - timeframe='1m', - candle_type=candle_type) + assert _download_pair_history( + datadir=tmp_path, + exchange=exchange, + pair="MEME/BTC", + timeframe="1m", + candle_type=candle_type, + ) + assert _download_pair_history( + datadir=tmp_path, exchange=exchange, pair="CFI/BTC", timeframe="1m", candle_type=candle_type + ) assert not exchange._pairs_last_refresh_time assert file1_1.is_file() assert file2_1.is_file() @@ -319,14 +338,16 @@ def test_download_pair_history( assert not file1_5.is_file() assert not file2_5.is_file() - assert _download_pair_history(datadir=tmp_path, exchange=exchange, - pair='MEME/BTC', - timeframe='5m', - candle_type=candle_type) - assert _download_pair_history(datadir=tmp_path, exchange=exchange, - pair='CFI/BTC', - timeframe='5m', - candle_type=candle_type) + assert _download_pair_history( + datadir=tmp_path, + exchange=exchange, + pair="MEME/BTC", + timeframe="5m", + candle_type=candle_type, + ) + assert _download_pair_history( + datadir=tmp_path, exchange=exchange, pair="CFI/BTC", timeframe="5m", candle_type=candle_type + ) assert not exchange._pairs_last_refresh_time assert file1_5.is_file() assert file2_5.is_file() @@ -335,30 +356,45 @@ def test_download_pair_history( def test_download_pair_history2(mocker, default_conf, testdatadir) -> None: tick = [ [1509836520000, 0.00162008, 0.00162008, 0.00162008, 0.00162008, 108.14853839], - [1509836580000, 0.00161, 0.00161, 0.00161, 0.00161, 82.390199] + [1509836580000, 0.00161, 0.00161, 0.00161, 0.00161, 82.390199], ] json_dump_mock = mocker.patch( - 'freqtrade.data.history.datahandlers.featherdatahandler.FeatherDataHandler.ohlcv_store', - return_value=None) - mocker.patch(f'{EXMS}.get_historic_ohlcv', return_value=tick) + "freqtrade.data.history.datahandlers.featherdatahandler.FeatherDataHandler.ohlcv_store", + return_value=None, + ) + mocker.patch(f"{EXMS}.get_historic_ohlcv", return_value=tick) exchange = get_patched_exchange(mocker, default_conf) - _download_pair_history(datadir=testdatadir, exchange=exchange, pair="UNITTEST/BTC", - timeframe='1m', candle_type='spot') - _download_pair_history(datadir=testdatadir, exchange=exchange, pair="UNITTEST/BTC", - timeframe='3m', candle_type='spot') - _download_pair_history(datadir=testdatadir, exchange=exchange, pair="UNITTEST/USDT", - timeframe='1h', candle_type='mark') + _download_pair_history( + datadir=testdatadir, + exchange=exchange, + pair="UNITTEST/BTC", + timeframe="1m", + candle_type="spot", + ) + _download_pair_history( + datadir=testdatadir, + exchange=exchange, + pair="UNITTEST/BTC", + timeframe="3m", + candle_type="spot", + ) + _download_pair_history( + datadir=testdatadir, + exchange=exchange, + pair="UNITTEST/USDT", + timeframe="1h", + candle_type="mark", + ) assert json_dump_mock.call_count == 3 def test_download_backtesting_data_exception(mocker, caplog, default_conf, tmp_path) -> None: - mocker.patch(f'{EXMS}.get_historic_ohlcv', - side_effect=Exception('File Error')) + mocker.patch(f"{EXMS}.get_historic_ohlcv", side_effect=Exception("File Error")) exchange = get_patched_exchange(mocker, default_conf) - assert not _download_pair_history(datadir=tmp_path, exchange=exchange, - pair='MEME/BTC', - timeframe='1m', candle_type='spot') + assert not _download_pair_history( + datadir=tmp_path, exchange=exchange, pair="MEME/BTC", timeframe="1m", candle_type="spot" + ) assert log_has('Failed to download history data for pair: "MEME/BTC", timeframe: 1m.', caplog) @@ -366,41 +402,46 @@ def test_load_partial_missing(testdatadir, caplog) -> None: # Make sure we start fresh - test missing data at start start = dt_utc(2018, 1, 1) end = dt_utc(2018, 1, 11) - data = load_data(testdatadir, '5m', ['UNITTEST/BTC'], startup_candles=20, - timerange=TimeRange('date', 'date', start.timestamp(), end.timestamp())) - assert log_has( - 'Using indicator startup period: 20 ...', caplog + data = load_data( + testdatadir, + "5m", + ["UNITTEST/BTC"], + startup_candles=20, + timerange=TimeRange("date", "date", start.timestamp(), end.timestamp()), ) + assert log_has("Using indicator startup period: 20 ...", caplog) # timedifference in 5 minutes td = ((end - start).total_seconds() // 60 // 5) + 1 - assert td != len(data['UNITTEST/BTC']) - start_real = data['UNITTEST/BTC'].iloc[0, 0] - assert log_has(f'UNITTEST/BTC, spot, 5m, ' - f'data starts at {start_real.strftime(DATETIME_PRINT_FORMAT)}', - caplog) + assert td != len(data["UNITTEST/BTC"]) + start_real = data["UNITTEST/BTC"].iloc[0, 0] + assert log_has( + f"UNITTEST/BTC, spot, 5m, " f"data starts at {start_real.strftime(DATETIME_PRINT_FORMAT)}", + caplog, + ) # Make sure we start fresh - test missing data at end caplog.clear() start = dt_utc(2018, 1, 10) end = dt_utc(2018, 2, 20) - data = load_data(datadir=testdatadir, timeframe='5m', pairs=['UNITTEST/BTC'], - timerange=TimeRange('date', 'date', start.timestamp(), end.timestamp())) + data = load_data( + datadir=testdatadir, + timeframe="5m", + pairs=["UNITTEST/BTC"], + timerange=TimeRange("date", "date", start.timestamp(), end.timestamp()), + ) # timedifference in 5 minutes td = ((end - start).total_seconds() // 60 // 5) + 1 - assert td != len(data['UNITTEST/BTC']) + assert td != len(data["UNITTEST/BTC"]) # Shift endtime with +5 - end_real = data['UNITTEST/BTC'].iloc[-1, 0].to_pydatetime() - assert log_has(f'UNITTEST/BTC, spot, 5m, ' - f'data ends at {end_real.strftime(DATETIME_PRINT_FORMAT)}', - caplog) + end_real = data["UNITTEST/BTC"].iloc[-1, 0].to_pydatetime() + assert log_has( + f"UNITTEST/BTC, spot, 5m, " f"data ends at {end_real.strftime(DATETIME_PRINT_FORMAT)}", + caplog, + ) def test_init(default_conf) -> None: - assert {} == load_data( - datadir=Path(), - pairs=[], - timeframe=default_conf['timeframe'] - ) + assert {} == load_data(datadir=Path(), pairs=[], timeframe=default_conf["timeframe"]) def test_init_with_refresh(default_conf, mocker) -> None: @@ -408,20 +449,16 @@ def test_init_with_refresh(default_conf, mocker) -> None: refresh_data( datadir=Path(), pairs=[], - timeframe=default_conf['timeframe'], + timeframe=default_conf["timeframe"], exchange=exchange, - candle_type=CandleType.SPOT - ) - assert {} == load_data( - datadir=Path(), - pairs=[], - timeframe=default_conf['timeframe'] + candle_type=CandleType.SPOT, ) + assert {} == load_data(datadir=Path(), pairs=[], timeframe=default_conf["timeframe"]) def test_file_dump_json_tofile(testdatadir) -> None: - file = testdatadir / f'test_{uuid.uuid4()}.json' - data = {'bar': 'foo'} + file = testdatadir / f"test_{uuid.uuid4()}.json" + data = {"bar": "foo"} # check the file we will create does not exist assert not file.is_file() @@ -436,8 +473,8 @@ def test_file_dump_json_tofile(testdatadir) -> None: with file.open() as data_file: json_from_file = json.load(data_file) - assert 'bar' in json_from_file - assert json_from_file['bar'] == 'foo' + assert "bar" in json_from_file + assert json_from_file["bar"] == "foo" # Remove the file _clean_test_file(file) @@ -446,113 +483,116 @@ def test_file_dump_json_tofile(testdatadir) -> None: def test_get_timerange(default_conf, mocker, testdatadir) -> None: patch_exchange(mocker) - default_conf.update({'strategy': CURRENT_TEST_STRATEGY}) + default_conf.update({"strategy": CURRENT_TEST_STRATEGY}) strategy = StrategyResolver.load_strategy(default_conf) data = strategy.advise_all_indicators( - load_data( - datadir=testdatadir, - timeframe='1m', - pairs=['UNITTEST/BTC'] - ) + load_data(datadir=testdatadir, timeframe="1m", pairs=["UNITTEST/BTC"]) ) min_date, max_date = get_timerange(data) - assert min_date.isoformat() == '2017-11-04T23:02:00+00:00' - assert max_date.isoformat() == '2017-11-14T22:59:00+00:00' + assert min_date.isoformat() == "2017-11-04T23:02:00+00:00" + assert max_date.isoformat() == "2017-11-14T22:59:00+00:00" def test_validate_backtest_data_warn(default_conf, mocker, caplog, testdatadir) -> None: patch_exchange(mocker) - default_conf.update({'strategy': CURRENT_TEST_STRATEGY}) + default_conf.update({"strategy": CURRENT_TEST_STRATEGY}) strategy = StrategyResolver.load_strategy(default_conf) data = strategy.advise_all_indicators( load_data( - datadir=testdatadir, - timeframe='1m', - pairs=['UNITTEST/BTC'], - fill_up_missing=False + datadir=testdatadir, timeframe="1m", pairs=["UNITTEST/BTC"], fill_up_missing=False ) ) min_date, max_date = get_timerange(data) caplog.clear() - assert validate_backtest_data(data['UNITTEST/BTC'], 'UNITTEST/BTC', - min_date, max_date, timeframe_to_minutes('1m')) + assert validate_backtest_data( + data["UNITTEST/BTC"], "UNITTEST/BTC", min_date, max_date, timeframe_to_minutes("1m") + ) assert len(caplog.record_tuples) == 1 assert log_has( "UNITTEST/BTC has missing frames: expected 14397, got 13681, that's 716 missing values", - caplog) + caplog, + ) def test_validate_backtest_data(default_conf, mocker, caplog, testdatadir) -> None: patch_exchange(mocker) - default_conf.update({'strategy': CURRENT_TEST_STRATEGY}) + default_conf.update({"strategy": CURRENT_TEST_STRATEGY}) strategy = StrategyResolver.load_strategy(default_conf) timerange = TimeRange() data = strategy.advise_all_indicators( - load_data( - datadir=testdatadir, - timeframe='5m', - pairs=['UNITTEST/BTC'], - timerange=timerange - ) + load_data(datadir=testdatadir, timeframe="5m", pairs=["UNITTEST/BTC"], timerange=timerange) ) min_date, max_date = get_timerange(data) caplog.clear() - assert not validate_backtest_data(data['UNITTEST/BTC'], 'UNITTEST/BTC', - min_date, max_date, timeframe_to_minutes('5m')) + assert not validate_backtest_data( + data["UNITTEST/BTC"], "UNITTEST/BTC", min_date, max_date, timeframe_to_minutes("5m") + ) assert len(caplog.record_tuples) == 0 -@pytest.mark.parametrize('trademode,callcount', [ - ('spot', 4), - ('margin', 4), - ('futures', 8), # Called 8 times - 4 normal, 2 funding and 2 mark/index calls -]) +@pytest.mark.parametrize( + "trademode,callcount", + [ + ("spot", 4), + ("margin", 4), + ("futures", 8), # Called 8 times - 4 normal, 2 funding and 2 mark/index calls + ], +) def test_refresh_backtest_ohlcv_data( - mocker, default_conf, markets, caplog, testdatadir, trademode, callcount): + mocker, default_conf, markets, caplog, testdatadir, trademode, callcount +): caplog.set_level(logging.DEBUG) - dl_mock = mocker.patch('freqtrade.data.history.history_utils._download_pair_history') - mocker.patch(f'{EXMS}.markets', PropertyMock(return_value=markets)) + dl_mock = mocker.patch("freqtrade.data.history.history_utils._download_pair_history") + mocker.patch(f"{EXMS}.markets", PropertyMock(return_value=markets)) mocker.patch.object(Path, "exists", MagicMock(return_value=True)) mocker.patch.object(Path, "unlink", MagicMock()) - default_conf['trading_mode'] = trademode + default_conf["trading_mode"] = trademode - ex = get_patched_exchange(mocker, default_conf, id='bybit') + 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, - timerange=timerange, erase=True, - trading_mode=trademode - ) + refresh_backtest_ohlcv_data( + exchange=ex, + pairs=["ETH/BTC", "XRP/BTC"], + timeframes=["1m", "5m"], + datadir=testdatadir, + timerange=timerange, + erase=True, + trading_mode=trademode, + ) assert dl_mock.call_count == callcount - assert dl_mock.call_args[1]['timerange'].starttype == 'date' + assert dl_mock.call_args[1]["timerange"].starttype == "date" assert log_has_re(r"Downloading pair ETH/BTC, .* interval 1m\.", caplog) - if trademode == 'futures': + 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): - dl_mock = mocker.patch('freqtrade.data.history.history_utils._download_pair_history', - MagicMock()) + dl_mock = mocker.patch( + "freqtrade.data.history.history_utils._download_pair_history", MagicMock() + ) ex = get_patched_exchange(mocker, default_conf) - mocker.patch(f'{EXMS}.markets', PropertyMock(return_value={})) + mocker.patch(f"{EXMS}.markets", PropertyMock(return_value={})) timerange = TimeRange.parse_timerange("20190101-20190102") - unav_pairs = refresh_backtest_ohlcv_data(exchange=ex, pairs=["BTT/BTC", "LTC/USDT"], - timeframes=["1m", "5m"], - datadir=testdatadir, - timerange=timerange, erase=False, - trading_mode='spot' - ) + unav_pairs = refresh_backtest_ohlcv_data( + exchange=ex, + pairs=["BTT/BTC", "LTC/USDT"], + timeframes=["1m", "5m"], + datadir=testdatadir, + timerange=timerange, + erase=False, + trading_mode="spot", + ) assert dl_mock.call_count == 0 assert "BTT/BTC" in unav_pairs @@ -561,90 +601,104 @@ def test_download_data_no_markets(mocker, default_conf, caplog, testdatadir): def test_refresh_backtest_trades_data(mocker, default_conf, markets, caplog, testdatadir): - dl_mock = mocker.patch('freqtrade.data.history.history_utils._download_trades_history', - MagicMock()) - mocker.patch(f'{EXMS}.markets', PropertyMock(return_value=markets)) + dl_mock = mocker.patch( + "freqtrade.data.history.history_utils._download_trades_history", MagicMock() + ) + mocker.patch(f"{EXMS}.markets", PropertyMock(return_value=markets)) mocker.patch.object(Path, "exists", MagicMock(return_value=True)) mocker.patch.object(Path, "unlink", MagicMock()) ex = get_patched_exchange(mocker, default_conf) timerange = TimeRange.parse_timerange("20190101-20190102") - unavailable_pairs = refresh_backtest_trades_data(exchange=ex, - pairs=["ETH/BTC", "XRP/BTC", "XRP/ETH"], - datadir=testdatadir, - timerange=timerange, erase=True, - trading_mode=TradingMode.SPOT, - ) + unavailable_pairs = refresh_backtest_trades_data( + exchange=ex, + pairs=["ETH/BTC", "XRP/BTC", "XRP/ETH"], + datadir=testdatadir, + timerange=timerange, + erase=True, + trading_mode=TradingMode.SPOT, + ) assert dl_mock.call_count == 2 - assert dl_mock.call_args[1]['timerange'].starttype == 'date' + assert dl_mock.call_args[1]["timerange"].starttype == "date" assert log_has("Downloading trades for pair ETH/BTC.", caplog) assert unavailable_pairs == ["XRP/ETH"] assert log_has("Skipping pair XRP/ETH...", caplog) -def test_download_trades_history(trades_history, mocker, default_conf, testdatadir, caplog, - tmp_path, time_machine) -> None: +def test_download_trades_history( + trades_history, mocker, default_conf, testdatadir, caplog, tmp_path, time_machine +) -> None: start_dt = dt_utc(2023, 1, 1) time_machine.move_to(start_dt, tick=False) ght_mock = MagicMock(side_effect=lambda pair, *args, **kwargs: (pair, trades_history)) - mocker.patch(f'{EXMS}.get_historic_trades', ght_mock) + mocker.patch(f"{EXMS}.get_historic_trades", ght_mock) exchange = get_patched_exchange(mocker, default_conf) - file1 = tmp_path / 'ETH_BTC-trades.json.gz' - data_handler = get_datahandler(tmp_path, data_format='jsongz') + file1 = tmp_path / "ETH_BTC-trades.json.gz" + data_handler = get_datahandler(tmp_path, data_format="jsongz") assert not file1.is_file() - assert _download_trades_history(data_handler=data_handler, exchange=exchange, - pair='ETH/BTC', trading_mode=TradingMode.SPOT) + assert _download_trades_history( + data_handler=data_handler, exchange=exchange, pair="ETH/BTC", trading_mode=TradingMode.SPOT + ) assert log_has("Current Amount of trades: 0", caplog) assert log_has("New Amount of trades: 6", caplog) assert ght_mock.call_count == 1 # Default "since" - 30 days before current day. - assert ght_mock.call_args_list[0][1]['since'] == dt_ts(start_dt - timedelta(days=30)) + assert ght_mock.call_args_list[0][1]["since"] == dt_ts(start_dt - timedelta(days=30)) assert file1.is_file() caplog.clear() ght_mock.reset_mock() since_time = int(trades_history[-3][0] // 1000) since_time2 = int(trades_history[-1][0] // 1000) - timerange = TimeRange('date', None, since_time, 0) + timerange = TimeRange("date", None, since_time, 0) assert _download_trades_history( - data_handler=data_handler, exchange=exchange, pair='ETH/BTC', - timerange=timerange, trading_mode=TradingMode.SPOT) + data_handler=data_handler, + exchange=exchange, + pair="ETH/BTC", + timerange=timerange, + trading_mode=TradingMode.SPOT, + ) assert ght_mock.call_count == 1 # Check this in seconds - since we had to convert to seconds above too. - assert int(ght_mock.call_args_list[0][1]['since'] // 1000) == since_time2 - 5 - assert ght_mock.call_args_list[0][1]['from_id'] is not None + assert int(ght_mock.call_args_list[0][1]["since"] // 1000) == since_time2 - 5 + assert ght_mock.call_args_list[0][1]["from_id"] is not None file1.unlink() - mocker.patch(f'{EXMS}.get_historic_trades', MagicMock(side_effect=ValueError)) + mocker.patch(f"{EXMS}.get_historic_trades", MagicMock(side_effect=ValueError)) caplog.clear() - assert not _download_trades_history(data_handler=data_handler, exchange=exchange, - pair='ETH/BTC', trading_mode=TradingMode.SPOT) + assert not _download_trades_history( + data_handler=data_handler, exchange=exchange, pair="ETH/BTC", trading_mode=TradingMode.SPOT + ) assert log_has_re('Failed to download historic trades for pair: "ETH/BTC".*', caplog) - file2 = tmp_path / 'XRP_ETH-trades.json.gz' + file2 = tmp_path / "XRP_ETH-trades.json.gz" copyfile(testdatadir / file2.name, file2) ght_mock.reset_mock() - mocker.patch(f'{EXMS}.get_historic_trades', ght_mock) + mocker.patch(f"{EXMS}.get_historic_trades", ght_mock) # Since before first start date since_time = int(trades_history[0][0] // 1000) - 500 - timerange = TimeRange('date', None, since_time, 0) + timerange = TimeRange("date", None, since_time, 0) assert _download_trades_history( - data_handler=data_handler, exchange=exchange, pair='XRP/ETH', - timerange=timerange, trading_mode=TradingMode.SPOT) + data_handler=data_handler, + exchange=exchange, + pair="XRP/ETH", + timerange=timerange, + trading_mode=TradingMode.SPOT, + ) assert ght_mock.call_count == 1 - assert int(ght_mock.call_args_list[0][1]['since'] // 1000) == since_time - assert ght_mock.call_args_list[0][1]['from_id'] is None - assert log_has_re(r'Start .* earlier than available data. Redownloading trades for.*', caplog) + assert int(ght_mock.call_args_list[0][1]["since"] // 1000) == since_time + assert ght_mock.call_args_list[0][1]["from_id"] is None + assert log_has_re(r"Start .* earlier than available data. Redownloading trades for.*", caplog) _clean_test_file(file2)