Merge branch 'develop' into ci/ccxt.pro

This commit is contained in:
Matthias
2024-07-03 13:12:51 +02:00
66 changed files with 1327 additions and 434 deletions

View File

@@ -83,6 +83,12 @@ def test_download_data_main_trades(mocker):
assert dl_mock.call_count == 1
assert convert_mock.call_count == 1
# Exchange that doesn't support historic downloads
config["exchange"]["name"] = "bybit"
with pytest.raises(OperationalException, match=r"Trade history not available for .*"):
config
download_data_main(config)
def test_download_data_main_data_invalid(mocker):
patch_exchange(mocker, id="kraken")

View File

@@ -429,7 +429,7 @@ def test_backtesting_start_no_data(default_conf, mocker, caplog, testdatadir) ->
backtesting.start()
def test_backtesting_no_pair_left(default_conf, mocker, caplog, testdatadir) -> None:
def test_backtesting_no_pair_left(default_conf, mocker) -> None:
mocker.patch(f"{EXMS}.exchange_has", MagicMock(return_value=True))
mocker.patch(
"freqtrade.data.history.history_utils.load_pair_history",
@@ -449,13 +449,6 @@ def test_backtesting_no_pair_left(default_conf, mocker, caplog, testdatadir) ->
with pytest.raises(OperationalException, match="No pair in whitelist."):
Backtesting(default_conf)
default_conf["pairlists"] = [{"method": "VolumePairList", "number_assets": 5}]
with pytest.raises(
OperationalException,
match=r"VolumePairList not allowed for backtesting\..*StaticPairList.*",
):
Backtesting(default_conf)
default_conf.update(
{
"pairlists": [{"method": "StaticPairList"}],
@@ -469,7 +462,7 @@ def test_backtesting_no_pair_left(default_conf, mocker, caplog, testdatadir) ->
Backtesting(default_conf)
def test_backtesting_pairlist_list(default_conf, mocker, caplog, testdatadir, tickers) -> None:
def test_backtesting_pairlist_list(default_conf, mocker, tickers) -> None:
mocker.patch(f"{EXMS}.exchange_has", MagicMock(return_value=True))
mocker.patch(f"{EXMS}.get_tickers", tickers)
mocker.patch(f"{EXMS}.price_to_precision", lambda s, x, y: y)
@@ -495,12 +488,6 @@ def test_backtesting_pairlist_list(default_conf, mocker, caplog, testdatadir, ti
):
Backtesting(default_conf)
default_conf["pairlists"] = [{"method": "StaticPairList"}, {"method": "PerformanceFilter"}]
with pytest.raises(
OperationalException, match="PerformanceFilter not allowed for backtesting."
):
Backtesting(default_conf)
default_conf["pairlists"] = [
{"method": "StaticPairList"},
{"method": "PrecisionFilter"},

View File

@@ -38,6 +38,7 @@ TESTABLE_PAIRLISTS = [p for p in AVAILABLE_PAIRLISTS if p not in ["RemotePairLis
@pytest.fixture(scope="function")
def whitelist_conf(default_conf):
default_conf["runmode"] = "dry_run"
default_conf["stake_currency"] = "BTC"
default_conf["exchange"]["pair_whitelist"] = [
"ETH/BTC",
@@ -68,6 +69,7 @@ def whitelist_conf(default_conf):
@pytest.fixture(scope="function")
def whitelist_conf_2(default_conf):
default_conf["runmode"] = "dry_run"
default_conf["stake_currency"] = "BTC"
default_conf["exchange"]["pair_whitelist"] = [
"ETH/BTC",
@@ -94,6 +96,7 @@ def whitelist_conf_2(default_conf):
@pytest.fixture(scope="function")
def whitelist_conf_agefilter(default_conf):
default_conf["runmode"] = "dry_run"
default_conf["stake_currency"] = "BTC"
default_conf["exchange"]["pair_whitelist"] = [
"ETH/BTC",
@@ -773,7 +776,7 @@ def test_VolumePairList_whitelist_gen(
whitelist_result,
caplog,
) -> None:
whitelist_conf["runmode"] = "backtest"
whitelist_conf["runmode"] = "util_exchange"
whitelist_conf["pairlists"] = pairlists
whitelist_conf["stake_currency"] = base_currency
@@ -2387,3 +2390,65 @@ def test_MarketCapPairList_exceptions(mocker, default_conf_usdt):
OperationalException, match="This filter only support marketcap rank up to 250."
):
PairListManager(exchange, default_conf_usdt)
@pytest.mark.parametrize(
"pairlists,expected_error,expected_warning",
[
(
[{"method": "StaticPairList"}],
None, # Error
None, # Warning
),
(
[{"method": "VolumePairList", "number_assets": 10}],
"VolumePairList", # Error
None, # Warning
),
(
[{"method": "MarketCapPairList", "number_assets": 10}],
None, # Error
r"MarketCapPairList.*lookahead.*", # Warning
),
(
[{"method": "StaticPairList"}, {"method": "FullTradesFilter"}],
None, # Error
r"FullTradesFilter do not generate.*", # Warning
),
( # combi, fails and warns
[
{"method": "VolumePairList", "number_assets": 10},
{"method": "MarketCapPairList", "number_assets": 10},
],
"VolumePairList", # Error
r"MarketCapPairList.*lookahead.*", # Warning
),
],
)
def test_backtesting_modes(
mocker, default_conf_usdt, pairlists, expected_error, expected_warning, caplog, markets, tickers
):
default_conf_usdt["runmode"] = "dry_run"
default_conf_usdt["pairlists"] = pairlists
mocker.patch.multiple(
EXMS,
markets=PropertyMock(return_value=markets),
exchange_has=MagicMock(return_value=True),
get_tickers=tickers,
)
exchange = get_patched_exchange(mocker, default_conf_usdt)
# Dry run mode - works always
PairListManager(exchange, default_conf_usdt)
default_conf_usdt["runmode"] = "backtest"
if expected_error:
with pytest.raises(OperationalException, match=f"Pairlist Handlers {expected_error}.*"):
PairListManager(exchange, default_conf_usdt)
if not expected_error:
PairListManager(exchange, default_conf_usdt)
if expected_warning:
assert log_has_re(f"Pairlist Handlers {expected_warning}", caplog)

View File

@@ -1,5 +1,6 @@
# pragma pylint: disable=missing-docstring, C0103
import logging
import math
from datetime import datetime, timedelta, timezone
from pathlib import Path
from unittest.mock import MagicMock
@@ -458,55 +459,66 @@ def test_min_roi_reached3(default_conf, fee) -> None:
ExitType.TRAILING_STOP_LOSS,
None,
),
(0.01, 0.96, ExitType.NONE, None, True, False, 0.05, 1, ExitType.NONE, None),
(0.05, 1, ExitType.NONE, None, True, False, -0.01, 1, ExitType.TRAILING_STOP_LOSS, None),
(0.01, 0.96, ExitType.NONE, None, True, False, 0.05, 0.998, ExitType.NONE, None),
(
0.05,
0.998,
ExitType.NONE,
None,
True,
False,
-0.01,
0.998,
ExitType.TRAILING_STOP_LOSS,
None,
),
# Default custom case - trails with 10%
(0.05, 0.95, ExitType.NONE, None, False, True, -0.02, 0.95, ExitType.NONE, None),
(0.05, 0.945, ExitType.NONE, None, False, True, -0.02, 0.945, ExitType.NONE, None),
(
0.05,
0.95,
0.945,
ExitType.NONE,
None,
False,
True,
-0.06,
0.95,
0.945,
ExitType.TRAILING_STOP_LOSS,
None,
),
(
0.05,
1,
0.998,
ExitType.NONE,
None,
False,
True,
-0.06,
1,
0.998,
ExitType.TRAILING_STOP_LOSS,
lambda **kwargs: -0.05,
),
(
0.05,
1,
0.998,
ExitType.NONE,
None,
False,
True,
0.09,
1.04,
1.036,
ExitType.NONE,
lambda **kwargs: -0.05,
),
(
0.05,
0.95,
0.945,
ExitType.NONE,
None,
False,
True,
0.09,
0.98,
0.981,
ExitType.NONE,
lambda current_profit, **kwargs: (
-0.1 if current_profit < 0.6 else -(current_profit * 2)
@@ -525,6 +537,19 @@ def test_min_roi_reached3(default_conf, fee) -> None:
ExitType.NONE,
lambda **kwargs: None,
),
# Error case - Returning inf.
(
0.05,
0.9,
ExitType.NONE,
None,
False,
True,
0.09,
0.9,
ExitType.NONE,
lambda **kwargs: math.inf,
),
],
)
def test_ft_stoploss_reached(
@@ -552,6 +577,8 @@ def test_ft_stoploss_reached(
exchange="binance",
open_rate=1,
liquidation_price=liq,
price_precision=4,
precision_mode=2,
)
trade.adjust_min_max_rates(trade.open_rate, trade.open_rate)
strategy.trailing_stop = trailing
@@ -577,7 +604,7 @@ def test_ft_stoploss_reached(
assert sl_flag.exit_flag is False
else:
assert sl_flag.exit_flag is True
assert round(trade.stop_loss, 2) == adjusted
assert round(trade.stop_loss, 3) == adjusted
current_rate2 = trade.open_rate * (1 + profit2)
sl_flag = strategy.ft_stoploss_reached(
@@ -593,7 +620,7 @@ def test_ft_stoploss_reached(
assert sl_flag.exit_flag is False
else:
assert sl_flag.exit_flag is True
assert round(trade.stop_loss, 2) == adjusted2
assert round(trade.stop_loss, 3) == adjusted2
strategy.custom_stoploss = original_stopvalue