ruff format: tests/plugins

This commit is contained in:
Matthias
2024-05-12 15:57:44 +02:00
parent 5a94817721
commit ca1fe06035
4 changed files with 2077 additions and 1315 deletions
File diff suppressed because it is too large Load Diff
+31 -31
View File
@@ -7,10 +7,10 @@ from freqtrade.persistence.models import PairLock
from freqtrade.util import dt_now from freqtrade.util import dt_now
@pytest.mark.parametrize('use_db', (False, True)) @pytest.mark.parametrize("use_db", (False, True))
@pytest.mark.usefixtures("init_persistence") @pytest.mark.usefixtures("init_persistence")
def test_PairLocks(use_db): def test_PairLocks(use_db):
PairLocks.timeframe = '5m' PairLocks.timeframe = "5m"
PairLocks.use_db = use_db PairLocks.use_db = use_db
# No lock should be present # No lock should be present
if use_db: if use_db:
@@ -18,28 +18,28 @@ def test_PairLocks(use_db):
assert PairLocks.use_db == use_db assert PairLocks.use_db == use_db
pair = 'ETH/BTC' pair = "ETH/BTC"
assert not PairLocks.is_pair_locked(pair) assert not PairLocks.is_pair_locked(pair)
PairLocks.lock_pair(pair, dt_now() + timedelta(minutes=4)) PairLocks.lock_pair(pair, dt_now() + timedelta(minutes=4))
# ETH/BTC locked for 4 minutes (on both sides) # ETH/BTC locked for 4 minutes (on both sides)
assert PairLocks.is_pair_locked(pair) assert PairLocks.is_pair_locked(pair)
assert PairLocks.is_pair_locked(pair, side='long') assert PairLocks.is_pair_locked(pair, side="long")
assert PairLocks.is_pair_locked(pair, side='short') assert PairLocks.is_pair_locked(pair, side="short")
pair = 'BNB/BTC' pair = "BNB/BTC"
PairLocks.lock_pair(pair, dt_now() + timedelta(minutes=4), side='long') PairLocks.lock_pair(pair, dt_now() + timedelta(minutes=4), side="long")
assert not PairLocks.is_pair_locked(pair) assert not PairLocks.is_pair_locked(pair)
assert PairLocks.is_pair_locked(pair, side='long') assert PairLocks.is_pair_locked(pair, side="long")
assert not PairLocks.is_pair_locked(pair, side='short') assert not PairLocks.is_pair_locked(pair, side="short")
pair = 'BNB/USDT' pair = "BNB/USDT"
PairLocks.lock_pair(pair, dt_now() + timedelta(minutes=4), side='short') PairLocks.lock_pair(pair, dt_now() + timedelta(minutes=4), side="short")
assert not PairLocks.is_pair_locked(pair) assert not PairLocks.is_pair_locked(pair)
assert not PairLocks.is_pair_locked(pair, side='long') assert not PairLocks.is_pair_locked(pair, side="long")
assert PairLocks.is_pair_locked(pair, side='short') assert PairLocks.is_pair_locked(pair, side="short")
# XRP/BTC should not be locked now # XRP/BTC should not be locked now
pair = 'XRP/BTC' pair = "XRP/BTC"
assert not PairLocks.is_pair_locked(pair) assert not PairLocks.is_pair_locked(pair)
# Unlocking a pair that's not locked should not raise an error # Unlocking a pair that's not locked should not raise an error
PairLocks.unlock_pair(pair) PairLocks.unlock_pair(pair)
@@ -52,12 +52,12 @@ def test_PairLocks(use_db):
assert len(locks) == 2 assert len(locks) == 2
# Unlock original pair # Unlock original pair
pair = 'ETH/BTC' pair = "ETH/BTC"
PairLocks.unlock_pair(pair) PairLocks.unlock_pair(pair)
assert not PairLocks.is_pair_locked(pair) assert not PairLocks.is_pair_locked(pair)
assert not PairLocks.is_global_lock() assert not PairLocks.is_global_lock()
pair = 'BTC/USDT' pair = "BTC/USDT"
# Lock until 14:30 # Lock until 14:30
lock_time = datetime(2020, 5, 1, 14, 30, 0, tzinfo=timezone.utc) lock_time = datetime(2020, 5, 1, 14, 30, 0, tzinfo=timezone.utc)
PairLocks.lock_pair(pair, lock_time) PairLocks.lock_pair(pair, lock_time)
@@ -73,18 +73,18 @@ def test_PairLocks(use_db):
locks = PairLocks.get_pair_locks(pair, lock_time + timedelta(minutes=-2)) locks = PairLocks.get_pair_locks(pair, lock_time + timedelta(minutes=-2))
assert len(locks) == 1 assert len(locks) == 1
assert 'PairLock' in str(locks[0]) assert "PairLock" in str(locks[0])
# Unlock all # Unlock all
PairLocks.unlock_pair(pair, lock_time + timedelta(minutes=-2)) PairLocks.unlock_pair(pair, lock_time + timedelta(minutes=-2))
assert not PairLocks.is_global_lock(lock_time + timedelta(minutes=-50)) assert not PairLocks.is_global_lock(lock_time + timedelta(minutes=-50))
# Global lock # Global lock
PairLocks.lock_pair('*', lock_time) PairLocks.lock_pair("*", lock_time)
assert PairLocks.is_global_lock(lock_time + timedelta(minutes=-50)) assert PairLocks.is_global_lock(lock_time + timedelta(minutes=-50))
# Global lock also locks every pair separately # Global lock also locks every pair separately
assert PairLocks.is_pair_locked(pair, lock_time + timedelta(minutes=-50)) assert PairLocks.is_pair_locked(pair, lock_time + timedelta(minutes=-50))
assert PairLocks.is_pair_locked('XRP/USDT', lock_time + timedelta(minutes=-50)) assert PairLocks.is_pair_locked("XRP/USDT", lock_time + timedelta(minutes=-50))
if use_db: if use_db:
locks = PairLocks.get_all_locks() locks = PairLocks.get_all_locks()
@@ -100,10 +100,10 @@ def test_PairLocks(use_db):
PairLocks.use_db = True PairLocks.use_db = True
@pytest.mark.parametrize('use_db', (False, True)) @pytest.mark.parametrize("use_db", (False, True))
@pytest.mark.usefixtures("init_persistence") @pytest.mark.usefixtures("init_persistence")
def test_PairLocks_getlongestlock(use_db): def test_PairLocks_getlongestlock(use_db):
PairLocks.timeframe = '5m' PairLocks.timeframe = "5m"
# No lock should be present # No lock should be present
PairLocks.use_db = use_db PairLocks.use_db = use_db
if use_db: if use_db:
@@ -111,7 +111,7 @@ def test_PairLocks_getlongestlock(use_db):
assert PairLocks.use_db == use_db assert PairLocks.use_db == use_db
pair = 'ETH/BTC' pair = "ETH/BTC"
assert not PairLocks.is_pair_locked(pair) assert not PairLocks.is_pair_locked(pair)
PairLocks.lock_pair(pair, dt_now() + timedelta(minutes=4)) PairLocks.lock_pair(pair, dt_now() + timedelta(minutes=4))
# ETH/BTC locked for 4 minutes # ETH/BTC locked for 4 minutes
@@ -132,10 +132,10 @@ def test_PairLocks_getlongestlock(use_db):
PairLocks.use_db = True PairLocks.use_db = True
@pytest.mark.parametrize('use_db', (False, True)) @pytest.mark.parametrize("use_db", (False, True))
@pytest.mark.usefixtures("init_persistence") @pytest.mark.usefixtures("init_persistence")
def test_PairLocks_reason(use_db): def test_PairLocks_reason(use_db):
PairLocks.timeframe = '5m' PairLocks.timeframe = "5m"
PairLocks.use_db = use_db PairLocks.use_db = use_db
# No lock should be present # No lock should be present
if use_db: if use_db:
@@ -143,15 +143,15 @@ def test_PairLocks_reason(use_db):
assert PairLocks.use_db == use_db assert PairLocks.use_db == use_db
PairLocks.lock_pair('XRP/USDT', dt_now() + timedelta(minutes=4), 'TestLock1') PairLocks.lock_pair("XRP/USDT", dt_now() + timedelta(minutes=4), "TestLock1")
PairLocks.lock_pair('ETH/USDT', dt_now() + timedelta(minutes=4), 'TestLock2') PairLocks.lock_pair("ETH/USDT", dt_now() + timedelta(minutes=4), "TestLock2")
assert PairLocks.is_pair_locked('XRP/USDT') assert PairLocks.is_pair_locked("XRP/USDT")
assert PairLocks.is_pair_locked('ETH/USDT') assert PairLocks.is_pair_locked("ETH/USDT")
PairLocks.unlock_reason('TestLock1') PairLocks.unlock_reason("TestLock1")
assert not PairLocks.is_pair_locked('XRP/USDT') assert not PairLocks.is_pair_locked("XRP/USDT")
assert PairLocks.is_pair_locked('ETH/USDT') assert PairLocks.is_pair_locked("ETH/USDT")
PairLocks.reset_locks() PairLocks.reset_locks()
PairLocks.use_db = True PairLocks.use_db = True
+389 -213
View File
@@ -11,12 +11,16 @@ from freqtrade.plugins.protectionmanager import ProtectionManager
from tests.conftest import get_patched_freqtradebot, log_has_re from tests.conftest import get_patched_freqtradebot, log_has_re
def generate_mock_trade(pair: str, fee: float, is_open: bool, def generate_mock_trade(
exit_reason: str = ExitType.EXIT_SIGNAL, pair: str,
min_ago_open: int = None, min_ago_close: int = None, fee: float,
profit_rate: float = 0.9, is_open: bool,
is_short: bool = False, exit_reason: str = ExitType.EXIT_SIGNAL,
): min_ago_open: int = None,
min_ago_close: int = None,
profit_rate: float = 0.9,
is_short: bool = False,
):
open_rate = random.random() open_rate = random.random()
trade = Trade( trade = Trade(
@@ -29,32 +33,15 @@ def generate_mock_trade(pair: str, fee: float, is_open: bool,
open_rate=open_rate, open_rate=open_rate,
is_open=is_open, is_open=is_open,
amount=0.01 / open_rate, amount=0.01 / open_rate,
exchange='binance', exchange="binance",
is_short=is_short, is_short=is_short,
leverage=1, leverage=1,
) )
trade.orders.append(Order( trade.orders.append(
ft_order_side=trade.entry_side, Order(
order_id=f'{pair}-{trade.entry_side}-{trade.open_date}', ft_order_side=trade.entry_side,
ft_is_open=False, order_id=f"{pair}-{trade.entry_side}-{trade.open_date}",
ft_pair=pair,
ft_amount=trade.amount,
ft_price=trade.open_rate,
amount=trade.amount,
filled=trade.amount,
remaining=0,
price=open_rate,
average=open_rate,
status="closed",
order_type="market",
side=trade.entry_side,
))
if not is_open:
close_price = open_rate * (2 - profit_rate if is_short else profit_rate)
trade.orders.append(Order(
ft_order_side=trade.exit_side,
order_id=f'{pair}-{trade.exit_side}-{trade.close_date}',
ft_is_open=False, ft_is_open=False,
ft_pair=pair, ft_pair=pair,
ft_amount=trade.amount, ft_amount=trade.amount,
@@ -62,12 +49,33 @@ def generate_mock_trade(pair: str, fee: float, is_open: bool,
amount=trade.amount, amount=trade.amount,
filled=trade.amount, filled=trade.amount,
remaining=0, remaining=0,
price=close_price, price=open_rate,
average=close_price, average=open_rate,
status="closed", status="closed",
order_type="market", order_type="market",
side=trade.exit_side, side=trade.entry_side,
)) )
)
if not is_open:
close_price = open_rate * (2 - profit_rate if is_short else profit_rate)
trade.orders.append(
Order(
ft_order_side=trade.exit_side,
order_id=f"{pair}-{trade.exit_side}-{trade.close_date}",
ft_is_open=False,
ft_pair=pair,
ft_amount=trade.amount,
ft_price=trade.open_rate,
amount=trade.amount,
filled=trade.amount,
remaining=0,
price=close_price,
average=close_price,
status="closed",
order_type="market",
side=trade.exit_side,
)
)
trade.recalc_open_trade_value() trade.recalc_open_trade_value()
if not is_open: if not is_open:
@@ -80,54 +88,79 @@ def generate_mock_trade(pair: str, fee: float, is_open: bool,
def test_protectionmanager(mocker, default_conf): def test_protectionmanager(mocker, default_conf):
default_conf['protections'] = [{'method': protection} default_conf["protections"] = [
for protection in constants.AVAILABLE_PROTECTIONS] {"method": protection} for protection in constants.AVAILABLE_PROTECTIONS
]
freqtrade = get_patched_freqtradebot(mocker, default_conf) freqtrade = get_patched_freqtradebot(mocker, default_conf)
for handler in freqtrade.protections._protection_handlers: for handler in freqtrade.protections._protection_handlers:
assert handler.name in constants.AVAILABLE_PROTECTIONS assert handler.name in constants.AVAILABLE_PROTECTIONS
if not handler.has_global_stop: if not handler.has_global_stop:
assert handler.global_stop(datetime.now(timezone.utc), '*') is None assert handler.global_stop(datetime.now(timezone.utc), "*") is None
if not handler.has_local_stop: if not handler.has_local_stop:
assert handler.stop_per_pair('XRP/BTC', datetime.now(timezone.utc), '*') is None assert handler.stop_per_pair("XRP/BTC", datetime.now(timezone.utc), "*") is None
@pytest.mark.parametrize('timeframe,expected,protconf', [ @pytest.mark.parametrize(
('1m', [20, 10], "timeframe,expected,protconf",
[{"method": "StoplossGuard", "lookback_period_candles": 20, "stop_duration": 10}]), [
('5m', [100, 15], (
[{"method": "StoplossGuard", "lookback_period_candles": 20, "stop_duration": 15}]), "1m",
('1h', [1200, 40], [20, 10],
[{"method": "StoplossGuard", "lookback_period_candles": 20, "stop_duration": 40}]), [{"method": "StoplossGuard", "lookback_period_candles": 20, "stop_duration": 10}],
('1d', [1440, 5], ),
[{"method": "StoplossGuard", "lookback_period_candles": 1, "stop_duration": 5}]), (
('1m', [20, 5], "5m",
[{"method": "StoplossGuard", "lookback_period": 20, "stop_duration_candles": 5}]), [100, 15],
('5m', [15, 25], [{"method": "StoplossGuard", "lookback_period_candles": 20, "stop_duration": 15}],
[{"method": "StoplossGuard", "lookback_period": 15, "stop_duration_candles": 5}]), ),
('1h', [50, 600], (
[{"method": "StoplossGuard", "lookback_period": 50, "stop_duration_candles": 10}]), "1h",
('1h', [60, 540], [1200, 40],
[{"method": "StoplossGuard", "lookback_period_candles": 1, "stop_duration_candles": 9}]), [{"method": "StoplossGuard", "lookback_period_candles": 20, "stop_duration": 40}],
]) ),
(
"1d",
[1440, 5],
[{"method": "StoplossGuard", "lookback_period_candles": 1, "stop_duration": 5}],
),
(
"1m",
[20, 5],
[{"method": "StoplossGuard", "lookback_period": 20, "stop_duration_candles": 5}],
),
(
"5m",
[15, 25],
[{"method": "StoplossGuard", "lookback_period": 15, "stop_duration_candles": 5}],
),
(
"1h",
[50, 600],
[{"method": "StoplossGuard", "lookback_period": 50, "stop_duration_candles": 10}],
),
(
"1h",
[60, 540],
[{"method": "StoplossGuard", "lookback_period_candles": 1, "stop_duration_candles": 9}],
),
],
)
def test_protections_init(default_conf, timeframe, expected, protconf): def test_protections_init(default_conf, timeframe, expected, protconf):
default_conf['timeframe'] = timeframe default_conf["timeframe"] = timeframe
man = ProtectionManager(default_conf, protconf) man = ProtectionManager(default_conf, protconf)
assert len(man._protection_handlers) == len(protconf) assert len(man._protection_handlers) == len(protconf)
assert man._protection_handlers[0]._lookback_period == expected[0] assert man._protection_handlers[0]._lookback_period == expected[0]
assert man._protection_handlers[0]._stop_duration == expected[1] assert man._protection_handlers[0]._stop_duration == expected[1]
@pytest.mark.parametrize('is_short', [False, True]) @pytest.mark.parametrize("is_short", [False, True])
@pytest.mark.usefixtures("init_persistence") @pytest.mark.usefixtures("init_persistence")
def test_stoploss_guard(mocker, default_conf, fee, caplog, is_short): def test_stoploss_guard(mocker, default_conf, fee, caplog, is_short):
# Active for both sides (long and short) # Active for both sides (long and short)
default_conf['protections'] = [{ default_conf["protections"] = [
"method": "StoplossGuard", {"method": "StoplossGuard", "lookback_period": 60, "stop_duration": 40, "trade_limit": 3}
"lookback_period": 60, ]
"stop_duration": 40,
"trade_limit": 3
}]
freqtrade = get_patched_freqtradebot(mocker, default_conf) freqtrade = get_patched_freqtradebot(mocker, default_conf)
message = r"Trading stopped due to .*" message = r"Trading stopped due to .*"
assert not freqtrade.protections.global_stop() assert not freqtrade.protections.global_stop()
@@ -135,8 +168,13 @@ def test_stoploss_guard(mocker, default_conf, fee, caplog, is_short):
caplog.clear() caplog.clear()
generate_mock_trade( generate_mock_trade(
'XRP/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, "XRP/BTC",
min_ago_open=200, min_ago_close=30, is_short=is_short, fee.return_value,
False,
exit_reason=ExitType.STOP_LOSS.value,
min_ago_open=200,
min_ago_close=30,
is_short=is_short,
) )
assert not freqtrade.protections.global_stop() assert not freqtrade.protections.global_stop()
@@ -144,13 +182,23 @@ def test_stoploss_guard(mocker, default_conf, fee, caplog, is_short):
caplog.clear() caplog.clear()
# This trade does not count, as it's closed too long ago # This trade does not count, as it's closed too long ago
generate_mock_trade( generate_mock_trade(
'BCH/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, "BCH/BTC",
min_ago_open=250, min_ago_close=100, is_short=is_short, fee.return_value,
False,
exit_reason=ExitType.STOP_LOSS.value,
min_ago_open=250,
min_ago_close=100,
is_short=is_short,
) )
generate_mock_trade( generate_mock_trade(
'ETH/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, "ETH/BTC",
min_ago_open=240, min_ago_close=30, is_short=is_short, fee.return_value,
False,
exit_reason=ExitType.STOP_LOSS.value,
min_ago_open=240,
min_ago_close=30,
is_short=is_short,
) )
# 3 Trades closed - but the 2nd has been closed too long ago. # 3 Trades closed - but the 2nd has been closed too long ago.
assert not freqtrade.protections.global_stop() assert not freqtrade.protections.global_stop()
@@ -158,8 +206,13 @@ def test_stoploss_guard(mocker, default_conf, fee, caplog, is_short):
caplog.clear() caplog.clear()
generate_mock_trade( generate_mock_trade(
'LTC/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, "LTC/BTC",
min_ago_open=180, min_ago_close=30, is_short=is_short, fee.return_value,
False,
exit_reason=ExitType.STOP_LOSS.value,
min_ago_open=180,
min_ago_close=30,
is_short=is_short,
) )
assert freqtrade.protections.global_stop() assert freqtrade.protections.global_stop()
@@ -168,36 +221,44 @@ def test_stoploss_guard(mocker, default_conf, fee, caplog, is_short):
# Test 5m after lock-period - this should try and relock the pair, but end-time # Test 5m after lock-period - this should try and relock the pair, but end-time
# should be the previous end-time # should be the previous end-time
end_time = PairLocks.get_pair_longest_lock('*').lock_end_time + timedelta(minutes=5) end_time = PairLocks.get_pair_longest_lock("*").lock_end_time + timedelta(minutes=5)
freqtrade.protections.global_stop(end_time) freqtrade.protections.global_stop(end_time)
assert not PairLocks.is_global_lock(end_time) assert not PairLocks.is_global_lock(end_time)
@pytest.mark.parametrize('only_per_pair', [False, True]) @pytest.mark.parametrize("only_per_pair", [False, True])
@pytest.mark.parametrize('only_per_side', [False, True]) @pytest.mark.parametrize("only_per_side", [False, True])
@pytest.mark.usefixtures("init_persistence") @pytest.mark.usefixtures("init_persistence")
def test_stoploss_guard_perpair(mocker, default_conf, fee, caplog, only_per_pair, only_per_side): def test_stoploss_guard_perpair(mocker, default_conf, fee, caplog, only_per_pair, only_per_side):
default_conf['protections'] = [{ default_conf["protections"] = [
"method": "StoplossGuard", {
"lookback_period": 60, "method": "StoplossGuard",
"trade_limit": 2, "lookback_period": 60,
"stop_duration": 60, "trade_limit": 2,
"only_per_pair": only_per_pair, "stop_duration": 60,
"only_per_side": only_per_side, "only_per_pair": only_per_pair,
}] "only_per_side": only_per_side,
check_side = 'long' if only_per_side else '*' }
]
check_side = "long" if only_per_side else "*"
is_short = False is_short = False
freqtrade = get_patched_freqtradebot(mocker, default_conf) freqtrade = get_patched_freqtradebot(mocker, default_conf)
message = r"Trading stopped due to .*" message = r"Trading stopped due to .*"
pair = 'XRP/BTC' pair = "XRP/BTC"
assert not freqtrade.protections.stop_per_pair(pair) assert not freqtrade.protections.stop_per_pair(pair)
assert not freqtrade.protections.global_stop() assert not freqtrade.protections.global_stop()
assert not log_has_re(message, caplog) assert not log_has_re(message, caplog)
caplog.clear() caplog.clear()
generate_mock_trade( generate_mock_trade(
pair, fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, pair,
min_ago_open=200, min_ago_close=30, profit_rate=0.9, is_short=is_short fee.return_value,
False,
exit_reason=ExitType.STOP_LOSS.value,
min_ago_open=200,
min_ago_close=30,
profit_rate=0.9,
is_short=is_short,
) )
assert not freqtrade.protections.stop_per_pair(pair) assert not freqtrade.protections.stop_per_pair(pair)
@@ -206,13 +267,25 @@ def test_stoploss_guard_perpair(mocker, default_conf, fee, caplog, only_per_pair
caplog.clear() caplog.clear()
# This trade does not count, as it's closed too long ago # This trade does not count, as it's closed too long ago
generate_mock_trade( generate_mock_trade(
pair, fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, pair,
min_ago_open=250, min_ago_close=100, profit_rate=0.9, is_short=is_short fee.return_value,
False,
exit_reason=ExitType.STOP_LOSS.value,
min_ago_open=250,
min_ago_close=100,
profit_rate=0.9,
is_short=is_short,
) )
# Trade does not count for per pair stop as it's the wrong pair. # Trade does not count for per pair stop as it's the wrong pair.
generate_mock_trade( generate_mock_trade(
'ETH/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, "ETH/BTC",
min_ago_open=240, min_ago_close=30, profit_rate=0.9, is_short=is_short fee.return_value,
False,
exit_reason=ExitType.STOP_LOSS.value,
min_ago_open=240,
min_ago_close=30,
profit_rate=0.9,
is_short=is_short,
) )
# 3 Trades closed - but the 2nd has been closed too long ago. # 3 Trades closed - but the 2nd has been closed too long ago.
assert not freqtrade.protections.stop_per_pair(pair) assert not freqtrade.protections.stop_per_pair(pair)
@@ -226,23 +299,35 @@ def test_stoploss_guard_perpair(mocker, default_conf, fee, caplog, only_per_pair
# Trade does not count potentially, as it's in the wrong direction # Trade does not count potentially, as it's in the wrong direction
generate_mock_trade( generate_mock_trade(
pair, fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, pair,
min_ago_open=150, min_ago_close=25, profit_rate=0.9, is_short=not is_short fee.return_value,
False,
exit_reason=ExitType.STOP_LOSS.value,
min_ago_open=150,
min_ago_close=25,
profit_rate=0.9,
is_short=not is_short,
) )
freqtrade.protections.stop_per_pair(pair) freqtrade.protections.stop_per_pair(pair)
assert freqtrade.protections.global_stop() != only_per_pair assert freqtrade.protections.global_stop() != only_per_pair
assert PairLocks.is_pair_locked(pair, side=check_side) != (only_per_side and only_per_pair) assert PairLocks.is_pair_locked(pair, side=check_side) != (only_per_side and only_per_pair)
assert PairLocks.is_global_lock(side=check_side) != only_per_pair assert PairLocks.is_global_lock(side=check_side) != only_per_pair
if only_per_side: if only_per_side:
assert not PairLocks.is_pair_locked(pair, side='*') assert not PairLocks.is_pair_locked(pair, side="*")
assert not PairLocks.is_global_lock(side='*') assert not PairLocks.is_global_lock(side="*")
caplog.clear() caplog.clear()
# 2nd Trade that counts with correct pair # 2nd Trade that counts with correct pair
generate_mock_trade( generate_mock_trade(
pair, fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, pair,
min_ago_open=180, min_ago_close=31, profit_rate=0.9, is_short=is_short fee.return_value,
False,
exit_reason=ExitType.STOP_LOSS.value,
min_ago_open=180,
min_ago_close=31,
profit_rate=0.9,
is_short=is_short,
) )
freqtrade.protections.stop_per_pair(pair) freqtrade.protections.stop_per_pair(pair)
@@ -250,174 +335,239 @@ def test_stoploss_guard_perpair(mocker, default_conf, fee, caplog, only_per_pair
assert PairLocks.is_pair_locked(pair, side=check_side) assert PairLocks.is_pair_locked(pair, side=check_side)
assert PairLocks.is_global_lock(side=check_side) != only_per_pair assert PairLocks.is_global_lock(side=check_side) != only_per_pair
if only_per_side: if only_per_side:
assert not PairLocks.is_pair_locked(pair, side='*') assert not PairLocks.is_pair_locked(pair, side="*")
assert not PairLocks.is_global_lock(side='*') assert not PairLocks.is_global_lock(side="*")
@pytest.mark.usefixtures("init_persistence") @pytest.mark.usefixtures("init_persistence")
def test_CooldownPeriod(mocker, default_conf, fee, caplog): def test_CooldownPeriod(mocker, default_conf, fee, caplog):
default_conf['protections'] = [{ default_conf["protections"] = [
"method": "CooldownPeriod", {
"stop_duration": 60, "method": "CooldownPeriod",
}] "stop_duration": 60,
}
]
freqtrade = get_patched_freqtradebot(mocker, default_conf) freqtrade = get_patched_freqtradebot(mocker, default_conf)
message = r"Trading stopped due to .*" message = r"Trading stopped due to .*"
assert not freqtrade.protections.global_stop() assert not freqtrade.protections.global_stop()
assert not freqtrade.protections.stop_per_pair('XRP/BTC') assert not freqtrade.protections.stop_per_pair("XRP/BTC")
assert not log_has_re(message, caplog) assert not log_has_re(message, caplog)
caplog.clear() caplog.clear()
generate_mock_trade( generate_mock_trade(
'XRP/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, "XRP/BTC",
min_ago_open=200, min_ago_close=30, fee.return_value,
False,
exit_reason=ExitType.STOP_LOSS.value,
min_ago_open=200,
min_ago_close=30,
) )
assert not freqtrade.protections.global_stop() assert not freqtrade.protections.global_stop()
assert freqtrade.protections.stop_per_pair('XRP/BTC') assert freqtrade.protections.stop_per_pair("XRP/BTC")
assert PairLocks.is_pair_locked('XRP/BTC') assert PairLocks.is_pair_locked("XRP/BTC")
assert not PairLocks.is_global_lock() assert not PairLocks.is_global_lock()
generate_mock_trade( generate_mock_trade(
'ETH/BTC', fee.return_value, False, exit_reason=ExitType.ROI.value, "ETH/BTC",
min_ago_open=205, min_ago_close=35, fee.return_value,
False,
exit_reason=ExitType.ROI.value,
min_ago_open=205,
min_ago_close=35,
) )
assert not freqtrade.protections.global_stop() assert not freqtrade.protections.global_stop()
assert not PairLocks.is_pair_locked('ETH/BTC') assert not PairLocks.is_pair_locked("ETH/BTC")
assert freqtrade.protections.stop_per_pair('ETH/BTC') assert freqtrade.protections.stop_per_pair("ETH/BTC")
assert PairLocks.is_pair_locked('ETH/BTC') assert PairLocks.is_pair_locked("ETH/BTC")
assert not PairLocks.is_global_lock() assert not PairLocks.is_global_lock()
@pytest.mark.parametrize('only_per_side', [False, True]) @pytest.mark.parametrize("only_per_side", [False, True])
@pytest.mark.usefixtures("init_persistence") @pytest.mark.usefixtures("init_persistence")
def test_LowProfitPairs(mocker, default_conf, fee, caplog, only_per_side): def test_LowProfitPairs(mocker, default_conf, fee, caplog, only_per_side):
default_conf['protections'] = [{ default_conf["protections"] = [
"method": "LowProfitPairs", {
"lookback_period": 400, "method": "LowProfitPairs",
"stop_duration": 60, "lookback_period": 400,
"trade_limit": 2, "stop_duration": 60,
"required_profit": 0.0, "trade_limit": 2,
"only_per_side": only_per_side, "required_profit": 0.0,
}] "only_per_side": only_per_side,
}
]
freqtrade = get_patched_freqtradebot(mocker, default_conf) freqtrade = get_patched_freqtradebot(mocker, default_conf)
message = r"Trading stopped due to .*" message = r"Trading stopped due to .*"
assert not freqtrade.protections.global_stop() assert not freqtrade.protections.global_stop()
assert not freqtrade.protections.stop_per_pair('XRP/BTC') assert not freqtrade.protections.stop_per_pair("XRP/BTC")
assert not log_has_re(message, caplog) assert not log_has_re(message, caplog)
caplog.clear() caplog.clear()
generate_mock_trade( generate_mock_trade(
'XRP/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, "XRP/BTC",
min_ago_open=800, min_ago_close=450, profit_rate=0.9, fee.return_value,
False,
exit_reason=ExitType.STOP_LOSS.value,
min_ago_open=800,
min_ago_close=450,
profit_rate=0.9,
) )
Trade.commit() Trade.commit()
# Not locked with 1 trade # Not locked with 1 trade
assert not freqtrade.protections.global_stop() assert not freqtrade.protections.global_stop()
assert not freqtrade.protections.stop_per_pair('XRP/BTC') assert not freqtrade.protections.stop_per_pair("XRP/BTC")
assert not PairLocks.is_pair_locked('XRP/BTC') assert not PairLocks.is_pair_locked("XRP/BTC")
assert not PairLocks.is_global_lock() assert not PairLocks.is_global_lock()
generate_mock_trade( generate_mock_trade(
'XRP/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, "XRP/BTC",
min_ago_open=200, min_ago_close=120, profit_rate=0.9, fee.return_value,
False,
exit_reason=ExitType.STOP_LOSS.value,
min_ago_open=200,
min_ago_close=120,
profit_rate=0.9,
) )
Trade.commit() Trade.commit()
# Not locked with 1 trade (first trade is outside of lookback_period) # Not locked with 1 trade (first trade is outside of lookback_period)
assert not freqtrade.protections.global_stop() assert not freqtrade.protections.global_stop()
assert not freqtrade.protections.stop_per_pair('XRP/BTC') assert not freqtrade.protections.stop_per_pair("XRP/BTC")
assert not PairLocks.is_pair_locked('XRP/BTC') assert not PairLocks.is_pair_locked("XRP/BTC")
assert not PairLocks.is_global_lock() assert not PairLocks.is_global_lock()
# Add positive trade # Add positive trade
generate_mock_trade( generate_mock_trade(
'XRP/BTC', fee.return_value, False, exit_reason=ExitType.ROI.value, "XRP/BTC",
min_ago_open=20, min_ago_close=10, profit_rate=1.15, is_short=True fee.return_value,
False,
exit_reason=ExitType.ROI.value,
min_ago_open=20,
min_ago_close=10,
profit_rate=1.15,
is_short=True,
) )
Trade.commit() Trade.commit()
assert freqtrade.protections.stop_per_pair('XRP/BTC') != only_per_side assert freqtrade.protections.stop_per_pair("XRP/BTC") != only_per_side
assert not PairLocks.is_pair_locked('XRP/BTC', side='*') assert not PairLocks.is_pair_locked("XRP/BTC", side="*")
assert PairLocks.is_pair_locked('XRP/BTC', side='long') == only_per_side assert PairLocks.is_pair_locked("XRP/BTC", side="long") == only_per_side
generate_mock_trade( generate_mock_trade(
'XRP/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, "XRP/BTC",
min_ago_open=110, min_ago_close=21, profit_rate=0.8, fee.return_value,
False,
exit_reason=ExitType.STOP_LOSS.value,
min_ago_open=110,
min_ago_close=21,
profit_rate=0.8,
) )
Trade.commit() Trade.commit()
# Locks due to 2nd trade # Locks due to 2nd trade
assert freqtrade.protections.global_stop() != only_per_side assert freqtrade.protections.global_stop() != only_per_side
assert freqtrade.protections.stop_per_pair('XRP/BTC') != only_per_side assert freqtrade.protections.stop_per_pair("XRP/BTC") != only_per_side
assert PairLocks.is_pair_locked('XRP/BTC', side='long') assert PairLocks.is_pair_locked("XRP/BTC", side="long")
assert PairLocks.is_pair_locked('XRP/BTC', side='*') != only_per_side assert PairLocks.is_pair_locked("XRP/BTC", side="*") != only_per_side
assert not PairLocks.is_global_lock() assert not PairLocks.is_global_lock()
Trade.commit() Trade.commit()
@pytest.mark.usefixtures("init_persistence") @pytest.mark.usefixtures("init_persistence")
def test_MaxDrawdown(mocker, default_conf, fee, caplog): def test_MaxDrawdown(mocker, default_conf, fee, caplog):
default_conf['protections'] = [{ default_conf["protections"] = [
"method": "MaxDrawdown", {
"lookback_period": 1000, "method": "MaxDrawdown",
"stop_duration": 60, "lookback_period": 1000,
"trade_limit": 3, "stop_duration": 60,
"max_allowed_drawdown": 0.15 "trade_limit": 3,
}] "max_allowed_drawdown": 0.15,
}
]
freqtrade = get_patched_freqtradebot(mocker, default_conf) freqtrade = get_patched_freqtradebot(mocker, default_conf)
message = r"Trading stopped due to Max.*" message = r"Trading stopped due to Max.*"
assert not freqtrade.protections.global_stop() assert not freqtrade.protections.global_stop()
assert not freqtrade.protections.stop_per_pair('XRP/BTC') assert not freqtrade.protections.stop_per_pair("XRP/BTC")
caplog.clear() caplog.clear()
generate_mock_trade( generate_mock_trade(
'XRP/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, "XRP/BTC",
min_ago_open=1000, min_ago_close=900, profit_rate=1.1, fee.return_value,
False,
exit_reason=ExitType.STOP_LOSS.value,
min_ago_open=1000,
min_ago_close=900,
profit_rate=1.1,
) )
generate_mock_trade( generate_mock_trade(
'ETH/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, "ETH/BTC",
min_ago_open=1000, min_ago_close=900, profit_rate=1.1, fee.return_value,
False,
exit_reason=ExitType.STOP_LOSS.value,
min_ago_open=1000,
min_ago_close=900,
profit_rate=1.1,
) )
generate_mock_trade( generate_mock_trade(
'NEO/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, "NEO/BTC",
min_ago_open=1000, min_ago_close=900, profit_rate=1.1, fee.return_value,
False,
exit_reason=ExitType.STOP_LOSS.value,
min_ago_open=1000,
min_ago_close=900,
profit_rate=1.1,
) )
Trade.commit() Trade.commit()
# No losing trade yet ... so max_drawdown will raise exception # No losing trade yet ... so max_drawdown will raise exception
assert not freqtrade.protections.global_stop() assert not freqtrade.protections.global_stop()
assert not freqtrade.protections.stop_per_pair('XRP/BTC') assert not freqtrade.protections.stop_per_pair("XRP/BTC")
generate_mock_trade( generate_mock_trade(
'XRP/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, "XRP/BTC",
min_ago_open=500, min_ago_close=400, profit_rate=0.9, fee.return_value,
False,
exit_reason=ExitType.STOP_LOSS.value,
min_ago_open=500,
min_ago_close=400,
profit_rate=0.9,
) )
# Not locked with one trade # Not locked with one trade
assert not freqtrade.protections.global_stop() assert not freqtrade.protections.global_stop()
assert not freqtrade.protections.stop_per_pair('XRP/BTC') assert not freqtrade.protections.stop_per_pair("XRP/BTC")
assert not PairLocks.is_pair_locked('XRP/BTC') assert not PairLocks.is_pair_locked("XRP/BTC")
assert not PairLocks.is_global_lock() assert not PairLocks.is_global_lock()
generate_mock_trade( generate_mock_trade(
'XRP/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, "XRP/BTC",
min_ago_open=1200, min_ago_close=1100, profit_rate=0.5, fee.return_value,
False,
exit_reason=ExitType.STOP_LOSS.value,
min_ago_open=1200,
min_ago_close=1100,
profit_rate=0.5,
) )
Trade.commit() Trade.commit()
# Not locked with 1 trade (2nd trade is outside of lookback_period) # Not locked with 1 trade (2nd trade is outside of lookback_period)
assert not freqtrade.protections.global_stop() assert not freqtrade.protections.global_stop()
assert not freqtrade.protections.stop_per_pair('XRP/BTC') assert not freqtrade.protections.stop_per_pair("XRP/BTC")
assert not PairLocks.is_pair_locked('XRP/BTC') assert not PairLocks.is_pair_locked("XRP/BTC")
assert not PairLocks.is_global_lock() assert not PairLocks.is_global_lock()
assert not log_has_re(message, caplog) assert not log_has_re(message, caplog)
# Winning trade ... (should not lock, does not change drawdown!) # Winning trade ... (should not lock, does not change drawdown!)
generate_mock_trade( generate_mock_trade(
'XRP/BTC', fee.return_value, False, exit_reason=ExitType.ROI.value, "XRP/BTC",
min_ago_open=320, min_ago_close=410, profit_rate=1.5, fee.return_value,
False,
exit_reason=ExitType.ROI.value,
min_ago_open=320,
min_ago_close=410,
profit_rate=1.5,
) )
Trade.commit() Trade.commit()
assert not freqtrade.protections.global_stop() assert not freqtrade.protections.global_stop()
@@ -427,63 +577,89 @@ def test_MaxDrawdown(mocker, default_conf, fee, caplog):
# Add additional negative trade, causing a loss of > 15% # Add additional negative trade, causing a loss of > 15%
generate_mock_trade( generate_mock_trade(
'XRP/BTC', fee.return_value, False, exit_reason=ExitType.ROI.value, "XRP/BTC",
min_ago_open=20, min_ago_close=10, profit_rate=0.8, fee.return_value,
False,
exit_reason=ExitType.ROI.value,
min_ago_open=20,
min_ago_close=10,
profit_rate=0.8,
) )
Trade.commit() Trade.commit()
assert not freqtrade.protections.stop_per_pair('XRP/BTC') assert not freqtrade.protections.stop_per_pair("XRP/BTC")
# local lock not supported # local lock not supported
assert not PairLocks.is_pair_locked('XRP/BTC') assert not PairLocks.is_pair_locked("XRP/BTC")
assert freqtrade.protections.global_stop() assert freqtrade.protections.global_stop()
assert PairLocks.is_global_lock() assert PairLocks.is_global_lock()
assert log_has_re(message, caplog) assert log_has_re(message, caplog)
@pytest.mark.parametrize("protectionconf,desc_expected,exception_expected", [ @pytest.mark.parametrize(
({"method": "StoplossGuard", "lookback_period": 60, "trade_limit": 2, "stop_duration": 60}, "protectionconf,desc_expected,exception_expected",
"[{'StoplossGuard': 'StoplossGuard - Frequent Stoploss Guard, " [
"2 stoplosses with profit < 0.00% within 60 minutes.'}]", (
None {
), "method": "StoplossGuard",
({"method": "CooldownPeriod", "stop_duration": 60}, "lookback_period": 60,
"[{'CooldownPeriod': 'CooldownPeriod - Cooldown period of 60 minutes.'}]", "trade_limit": 2,
None "stop_duration": 60,
), },
({"method": "LowProfitPairs", "lookback_period": 60, "stop_duration": 60}, "[{'StoplossGuard': 'StoplossGuard - Frequent Stoploss Guard, "
"[{'LowProfitPairs': 'LowProfitPairs - Low Profit Protection, locks pairs with " "2 stoplosses with profit < 0.00% within 60 minutes.'}]",
"profit < 0.0 within 60 minutes.'}]", None,
None ),
), (
({"method": "MaxDrawdown", "lookback_period": 60, "stop_duration": 60}, {"method": "CooldownPeriod", "stop_duration": 60},
"[{'MaxDrawdown': 'MaxDrawdown - Max drawdown protection, stop trading " "[{'CooldownPeriod': 'CooldownPeriod - Cooldown period of 60 minutes.'}]",
"if drawdown is > 0.0 within 60 minutes.'}]", None,
None ),
), (
({"method": "StoplossGuard", "lookback_period_candles": 12, "trade_limit": 2, {"method": "LowProfitPairs", "lookback_period": 60, "stop_duration": 60},
"required_profit": -0.05, "stop_duration": 60}, "[{'LowProfitPairs': 'LowProfitPairs - Low Profit Protection, locks pairs with "
"[{'StoplossGuard': 'StoplossGuard - Frequent Stoploss Guard, " "profit < 0.0 within 60 minutes.'}]",
"2 stoplosses with profit < -5.00% within 12 candles.'}]", None,
None ),
), (
({"method": "CooldownPeriod", "stop_duration_candles": 5}, {"method": "MaxDrawdown", "lookback_period": 60, "stop_duration": 60},
"[{'CooldownPeriod': 'CooldownPeriod - Cooldown period of 5 candles.'}]", "[{'MaxDrawdown': 'MaxDrawdown - Max drawdown protection, stop trading "
None "if drawdown is > 0.0 within 60 minutes.'}]",
), None,
({"method": "LowProfitPairs", "lookback_period_candles": 11, "stop_duration": 60}, ),
"[{'LowProfitPairs': 'LowProfitPairs - Low Profit Protection, locks pairs with " (
"profit < 0.0 within 11 candles.'}]", {
None "method": "StoplossGuard",
), "lookback_period_candles": 12,
({"method": "MaxDrawdown", "lookback_period_candles": 20, "stop_duration": 60}, "trade_limit": 2,
"[{'MaxDrawdown': 'MaxDrawdown - Max drawdown protection, stop trading " "required_profit": -0.05,
"if drawdown is > 0.0 within 20 candles.'}]", "stop_duration": 60,
None },
), "[{'StoplossGuard': 'StoplossGuard - Frequent Stoploss Guard, "
]) "2 stoplosses with profit < -5.00% within 12 candles.'}]",
def test_protection_manager_desc(mocker, default_conf, protectionconf, None,
desc_expected, exception_expected): ),
(
default_conf['protections'] = [protectionconf] {"method": "CooldownPeriod", "stop_duration_candles": 5},
"[{'CooldownPeriod': 'CooldownPeriod - Cooldown period of 5 candles.'}]",
None,
),
(
{"method": "LowProfitPairs", "lookback_period_candles": 11, "stop_duration": 60},
"[{'LowProfitPairs': 'LowProfitPairs - Low Profit Protection, locks pairs with "
"profit < 0.0 within 11 candles.'}]",
None,
),
(
{"method": "MaxDrawdown", "lookback_period_candles": 20, "stop_duration": 60},
"[{'MaxDrawdown': 'MaxDrawdown - Max drawdown protection, stop trading "
"if drawdown is > 0.0 within 20 candles.'}]",
None,
),
],
)
def test_protection_manager_desc(
mocker, default_conf, protectionconf, desc_expected, exception_expected
):
default_conf["protections"] = [protectionconf]
freqtrade = get_patched_freqtradebot(mocker, default_conf) freqtrade = get_patched_freqtradebot(mocker, default_conf)
short_desc = str(freqtrade.protections.short_desc()) short_desc = str(freqtrade.protections.short_desc())
+102 -103
View File
@@ -12,59 +12,57 @@ from tests.conftest import EXMS, get_patched_exchange, get_patched_freqtradebot,
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def rpl_config(default_conf): def rpl_config(default_conf):
default_conf['stake_currency'] = 'USDT' default_conf["stake_currency"] = "USDT"
default_conf['exchange']['pair_whitelist'] = [ default_conf["exchange"]["pair_whitelist"] = [
'ETH/USDT', "ETH/USDT",
'XRP/USDT', "XRP/USDT",
]
default_conf['exchange']['pair_blacklist'] = [
'BLK/USDT'
] ]
default_conf["exchange"]["pair_blacklist"] = ["BLK/USDT"]
return default_conf return default_conf
def test_gen_pairlist_with_local_file(mocker, rpl_config): def test_gen_pairlist_with_local_file(mocker, rpl_config):
mock_file = MagicMock() mock_file = MagicMock()
mock_file.read.return_value = '{"pairs": ["TKN/USDT","ETH/USDT","NANO/USDT"]}' mock_file.read.return_value = '{"pairs": ["TKN/USDT","ETH/USDT","NANO/USDT"]}'
mocker.patch('freqtrade.plugins.pairlist.RemotePairList.open', return_value=mock_file) mocker.patch("freqtrade.plugins.pairlist.RemotePairList.open", return_value=mock_file)
mock_file_path = mocker.patch('freqtrade.plugins.pairlist.RemotePairList.Path') mock_file_path = mocker.patch("freqtrade.plugins.pairlist.RemotePairList.Path")
mock_file_path.exists.return_value = True mock_file_path.exists.return_value = True
jsonparse = json.loads(mock_file.read.return_value) jsonparse = json.loads(mock_file.read.return_value)
mocker.patch('freqtrade.plugins.pairlist.RemotePairList.rapidjson.load', return_value=jsonparse) mocker.patch("freqtrade.plugins.pairlist.RemotePairList.rapidjson.load", return_value=jsonparse)
rpl_config['pairlists'] = [ rpl_config["pairlists"] = [
{ {
"method": "RemotePairList", "method": "RemotePairList",
'number_assets': 2, "number_assets": 2,
'refresh_period': 1800, "refresh_period": 1800,
'keep_pairlist_on_failure': True, "keep_pairlist_on_failure": True,
'pairlist_url': 'file:///pairlist.json', "pairlist_url": "file:///pairlist.json",
'bearer_token': '', "bearer_token": "",
'read_timeout': 60 "read_timeout": 60,
} }
] ]
exchange = get_patched_exchange(mocker, rpl_config) exchange = get_patched_exchange(mocker, rpl_config)
pairlistmanager = PairListManager(exchange, rpl_config) pairlistmanager = PairListManager(exchange, rpl_config)
remote_pairlist = RemotePairList(exchange, pairlistmanager, rpl_config, remote_pairlist = RemotePairList(
rpl_config['pairlists'][0], 0) exchange, pairlistmanager, rpl_config, rpl_config["pairlists"][0], 0
)
result = remote_pairlist.gen_pairlist([]) result = remote_pairlist.gen_pairlist([])
assert result == ['TKN/USDT', 'ETH/USDT'] assert result == ["TKN/USDT", "ETH/USDT"]
def test_fetch_pairlist_mock_response_html(mocker, rpl_config): def test_fetch_pairlist_mock_response_html(mocker, rpl_config):
mock_response = MagicMock() mock_response = MagicMock()
mock_response.headers = {'content-type': 'text/html'} mock_response.headers = {"content-type": "text/html"}
rpl_config['pairlists'] = [ rpl_config["pairlists"] = [
{ {
"method": "RemotePairList", "method": "RemotePairList",
"pairlist_url": "http://example.com/pairlist", "pairlist_url": "http://example.com/pairlist",
@@ -77,17 +75,19 @@ def test_fetch_pairlist_mock_response_html(mocker, rpl_config):
exchange = get_patched_exchange(mocker, rpl_config) exchange = get_patched_exchange(mocker, rpl_config)
pairlistmanager = PairListManager(exchange, rpl_config) pairlistmanager = PairListManager(exchange, rpl_config)
mocker.patch("freqtrade.plugins.pairlist.RemotePairList.requests.get", mocker.patch(
return_value=mock_response) "freqtrade.plugins.pairlist.RemotePairList.requests.get", return_value=mock_response
remote_pairlist = RemotePairList(exchange, pairlistmanager, rpl_config, )
rpl_config['pairlists'][0], 0) remote_pairlist = RemotePairList(
exchange, pairlistmanager, rpl_config, rpl_config["pairlists"][0], 0
)
with pytest.raises(OperationalException, match='RemotePairList is not of type JSON.'): with pytest.raises(OperationalException, match="RemotePairList is not of type JSON."):
remote_pairlist.fetch_pairlist() remote_pairlist.fetch_pairlist()
def test_fetch_pairlist_timeout_keep_last_pairlist(mocker, rpl_config, caplog): def test_fetch_pairlist_timeout_keep_last_pairlist(mocker, rpl_config, caplog):
rpl_config['pairlists'] = [ rpl_config["pairlists"] = [
{ {
"method": "RemotePairList", "method": "RemotePairList",
"pairlist_url": "http://example.com/pairlist", "pairlist_url": "http://example.com/pairlist",
@@ -100,25 +100,27 @@ def test_fetch_pairlist_timeout_keep_last_pairlist(mocker, rpl_config, caplog):
exchange = get_patched_exchange(mocker, rpl_config) exchange = get_patched_exchange(mocker, rpl_config)
pairlistmanager = PairListManager(exchange, rpl_config) pairlistmanager = PairListManager(exchange, rpl_config)
mocker.patch("freqtrade.plugins.pairlist.RemotePairList.requests.get", mocker.patch(
side_effect=requests.exceptions.RequestException) "freqtrade.plugins.pairlist.RemotePairList.requests.get",
side_effect=requests.exceptions.RequestException,
)
remote_pairlist = RemotePairList(exchange, pairlistmanager, rpl_config, remote_pairlist = RemotePairList(
rpl_config['pairlists'][0], 0) exchange, pairlistmanager, rpl_config, rpl_config["pairlists"][0], 0
)
remote_pairlist._last_pairlist = ["BTC/USDT", "ETH/USDT", "LTC/USDT"] remote_pairlist._last_pairlist = ["BTC/USDT", "ETH/USDT", "LTC/USDT"]
remote_pairlist._init_done = True remote_pairlist._init_done = True
pairlist_url = rpl_config['pairlists'][0]['pairlist_url'] pairlist_url = rpl_config["pairlists"][0]["pairlist_url"]
pairs, _time_elapsed = remote_pairlist.fetch_pairlist() pairs, _time_elapsed = remote_pairlist.fetch_pairlist()
assert log_has(f'Error: Was not able to fetch pairlist from: ' f'{pairlist_url}', caplog) assert log_has(f"Error: Was not able to fetch pairlist from: " f"{pairlist_url}", caplog)
assert log_has("Keeping last fetched pairlist", caplog) assert log_has("Keeping last fetched pairlist", caplog)
assert pairs == ["BTC/USDT", "ETH/USDT", "LTC/USDT"] assert pairs == ["BTC/USDT", "ETH/USDT", "LTC/USDT"]
def test_remote_pairlist_init_no_pairlist_url(mocker, rpl_config): def test_remote_pairlist_init_no_pairlist_url(mocker, rpl_config):
rpl_config["pairlists"] = [
rpl_config['pairlists'] = [
{ {
"method": "RemotePairList", "method": "RemotePairList",
"number_assets": 10, "number_assets": 10,
@@ -127,14 +129,16 @@ def test_remote_pairlist_init_no_pairlist_url(mocker, rpl_config):
] ]
get_patched_exchange(mocker, rpl_config) get_patched_exchange(mocker, rpl_config)
with pytest.raises(OperationalException, match=r'`pairlist_url` not specified.' with pytest.raises(
r' Please check your configuration for "pairlist.config.pairlist_url"'): OperationalException,
match=r"`pairlist_url` not specified."
r' Please check your configuration for "pairlist.config.pairlist_url"',
):
get_patched_freqtradebot(mocker, rpl_config) get_patched_freqtradebot(mocker, rpl_config)
def test_remote_pairlist_init_no_number_assets(mocker, rpl_config): def test_remote_pairlist_init_no_number_assets(mocker, rpl_config):
rpl_config["pairlists"] = [
rpl_config['pairlists'] = [
{ {
"method": "RemotePairList", "method": "RemotePairList",
"pairlist_url": "http://example.com/pairlist", "pairlist_url": "http://example.com/pairlist",
@@ -144,14 +148,16 @@ def test_remote_pairlist_init_no_number_assets(mocker, rpl_config):
get_patched_exchange(mocker, rpl_config) get_patched_exchange(mocker, rpl_config)
with pytest.raises(OperationalException, match=r'`number_assets` not specified. ' with pytest.raises(
'Please check your configuration for "pairlist.config.number_assets"'): OperationalException,
match=r"`number_assets` not specified. "
'Please check your configuration for "pairlist.config.number_assets"',
):
get_patched_freqtradebot(mocker, rpl_config) get_patched_freqtradebot(mocker, rpl_config)
def test_fetch_pairlist_mock_response_valid(mocker, rpl_config): def test_fetch_pairlist_mock_response_valid(mocker, rpl_config):
rpl_config["pairlists"] = [
rpl_config['pairlists'] = [
{ {
"method": "RemotePairList", "method": "RemotePairList",
"pairlist_url": "http://example.com/pairlist", "pairlist_url": "http://example.com/pairlist",
@@ -166,21 +172,21 @@ def test_fetch_pairlist_mock_response_valid(mocker, rpl_config):
mock_response.json.return_value = { mock_response.json.return_value = {
"pairs": ["ETH/USDT", "XRP/USDT", "LTC/USDT", "EOS/USDT"], "pairs": ["ETH/USDT", "XRP/USDT", "LTC/USDT", "EOS/USDT"],
"refresh_period": 60 "refresh_period": 60,
} }
mock_response.headers = { mock_response.headers = {"content-type": "application/json"}
"content-type": "application/json"
}
mock_response.elapsed.total_seconds.return_value = 0.4 mock_response.elapsed.total_seconds.return_value = 0.4
mocker.patch("freqtrade.plugins.pairlist.RemotePairList.requests.get", mocker.patch(
return_value=mock_response) "freqtrade.plugins.pairlist.RemotePairList.requests.get", return_value=mock_response
)
exchange = get_patched_exchange(mocker, rpl_config) exchange = get_patched_exchange(mocker, rpl_config)
pairlistmanager = PairListManager(exchange, rpl_config) pairlistmanager = PairListManager(exchange, rpl_config)
remote_pairlist = RemotePairList(exchange, pairlistmanager, rpl_config, remote_pairlist = RemotePairList(
rpl_config['pairlists'][0], 0) exchange, pairlistmanager, rpl_config, rpl_config["pairlists"][0], 0
)
pairs, time_elapsed = remote_pairlist.fetch_pairlist() pairs, time_elapsed = remote_pairlist.fetch_pairlist()
assert pairs == ["ETH/USDT", "XRP/USDT", "LTC/USDT", "EOS/USDT"] assert pairs == ["ETH/USDT", "XRP/USDT", "LTC/USDT", "EOS/USDT"]
@@ -189,7 +195,7 @@ def test_fetch_pairlist_mock_response_valid(mocker, rpl_config):
def test_remote_pairlist_init_wrong_mode(mocker, rpl_config): def test_remote_pairlist_init_wrong_mode(mocker, rpl_config):
rpl_config['pairlists'] = [ rpl_config["pairlists"] = [
{ {
"method": "RemotePairList", "method": "RemotePairList",
"mode": "blacklis", "mode": "blacklis",
@@ -201,11 +207,11 @@ def test_remote_pairlist_init_wrong_mode(mocker, rpl_config):
with pytest.raises( with pytest.raises(
OperationalException, OperationalException,
match=r'`mode` not configured correctly. Supported Modes are "whitelist","blacklist"' match=r'`mode` not configured correctly. Supported Modes are "whitelist","blacklist"',
): ):
get_patched_freqtradebot(mocker, rpl_config) get_patched_freqtradebot(mocker, rpl_config)
rpl_config['pairlists'] = [ rpl_config["pairlists"] = [
{ {
"method": "RemotePairList", "method": "RemotePairList",
"mode": "blacklist", "mode": "blacklist",
@@ -216,14 +222,13 @@ def test_remote_pairlist_init_wrong_mode(mocker, rpl_config):
] ]
with pytest.raises( with pytest.raises(
OperationalException, OperationalException, match=r"A `blacklist` mode RemotePairList can not be.*first.*"
match=r'A `blacklist` mode RemotePairList can not be.*first.*'
): ):
get_patched_freqtradebot(mocker, rpl_config) get_patched_freqtradebot(mocker, rpl_config)
def test_remote_pairlist_init_wrong_proc_mode(mocker, rpl_config): def test_remote_pairlist_init_wrong_proc_mode(mocker, rpl_config):
rpl_config['pairlists'] = [ rpl_config["pairlists"] = [
{ {
"method": "RemotePairList", "method": "RemotePairList",
"processing_mode": "filler", "processing_mode": "filler",
@@ -237,25 +242,19 @@ def test_remote_pairlist_init_wrong_proc_mode(mocker, rpl_config):
get_patched_exchange(mocker, rpl_config) get_patched_exchange(mocker, rpl_config)
with pytest.raises( with pytest.raises(
OperationalException, OperationalException,
match=r'`processing_mode` not configured correctly. Supported Modes are "filter","append"' match=r'`processing_mode` not configured correctly. Supported Modes are "filter","append"',
): ):
get_patched_freqtradebot(mocker, rpl_config) get_patched_freqtradebot(mocker, rpl_config)
def test_remote_pairlist_blacklist(mocker, rpl_config, caplog, markets, tickers): def test_remote_pairlist_blacklist(mocker, rpl_config, caplog, markets, tickers):
mock_response = MagicMock() mock_response = MagicMock()
mock_response.json.return_value = { mock_response.json.return_value = {"pairs": ["XRP/USDT"], "refresh_period": 60}
"pairs": ["XRP/USDT"],
"refresh_period": 60
}
mock_response.headers = { mock_response.headers = {"content-type": "application/json"}
"content-type": "application/json"
}
rpl_config['pairlists'] = [ rpl_config["pairlists"] = [
{ {
"method": "StaticPairList", "method": "StaticPairList",
}, },
@@ -263,31 +262,34 @@ def test_remote_pairlist_blacklist(mocker, rpl_config, caplog, markets, tickers)
"method": "RemotePairList", "method": "RemotePairList",
"mode": "blacklist", "mode": "blacklist",
"pairlist_url": "http://example.com/pairlist", "pairlist_url": "http://example.com/pairlist",
"number_assets": 3 "number_assets": 3,
} },
] ]
mocker.patch.multiple(EXMS, mocker.patch.multiple(
markets=PropertyMock(return_value=markets), EXMS,
exchange_has=MagicMock(return_value=True), markets=PropertyMock(return_value=markets),
get_tickers=tickers exchange_has=MagicMock(return_value=True),
) get_tickers=tickers,
)
mocker.patch("freqtrade.plugins.pairlist.RemotePairList.requests.get", mocker.patch(
return_value=mock_response) "freqtrade.plugins.pairlist.RemotePairList.requests.get", return_value=mock_response
)
exchange = get_patched_exchange(mocker, rpl_config) exchange = get_patched_exchange(mocker, rpl_config)
pairlistmanager = PairListManager(exchange, rpl_config) pairlistmanager = PairListManager(exchange, rpl_config)
remote_pairlist = RemotePairList(exchange, pairlistmanager, rpl_config, remote_pairlist = RemotePairList(
rpl_config["pairlists"][1], 1) exchange, pairlistmanager, rpl_config, rpl_config["pairlists"][1], 1
)
pairs, _time_elapsed = remote_pairlist.fetch_pairlist() pairs, _time_elapsed = remote_pairlist.fetch_pairlist()
assert pairs == ["XRP/USDT"] assert pairs == ["XRP/USDT"]
whitelist = remote_pairlist.filter_pairlist(rpl_config['exchange']['pair_whitelist'], {}) whitelist = remote_pairlist.filter_pairlist(rpl_config["exchange"]["pair_whitelist"], {})
assert whitelist == ["ETH/USDT"] assert whitelist == ["ETH/USDT"]
assert log_has(f"Blacklist - Filtered out pairs: {pairs}", caplog) assert log_has(f"Blacklist - Filtered out pairs: {pairs}", caplog)
@@ -295,19 +297,13 @@ def test_remote_pairlist_blacklist(mocker, rpl_config, caplog, markets, tickers)
@pytest.mark.parametrize("processing_mode", ["filter", "append"]) @pytest.mark.parametrize("processing_mode", ["filter", "append"])
def test_remote_pairlist_whitelist(mocker, rpl_config, processing_mode, markets, tickers): def test_remote_pairlist_whitelist(mocker, rpl_config, processing_mode, markets, tickers):
mock_response = MagicMock() mock_response = MagicMock()
mock_response.json.return_value = { mock_response.json.return_value = {"pairs": ["XRP/USDT"], "refresh_period": 60}
"pairs": ["XRP/USDT"],
"refresh_period": 60
}
mock_response.headers = { mock_response.headers = {"content-type": "application/json"}
"content-type": "application/json"
}
rpl_config['pairlists'] = [ rpl_config["pairlists"] = [
{ {
"method": "StaticPairList", "method": "StaticPairList",
}, },
@@ -316,29 +312,32 @@ def test_remote_pairlist_whitelist(mocker, rpl_config, processing_mode, markets,
"mode": "whitelist", "mode": "whitelist",
"processing_mode": processing_mode, "processing_mode": processing_mode,
"pairlist_url": "http://example.com/pairlist", "pairlist_url": "http://example.com/pairlist",
"number_assets": 3 "number_assets": 3,
} },
] ]
mocker.patch.multiple(EXMS, mocker.patch.multiple(
markets=PropertyMock(return_value=markets), EXMS,
exchange_has=MagicMock(return_value=True), markets=PropertyMock(return_value=markets),
get_tickers=tickers exchange_has=MagicMock(return_value=True),
) get_tickers=tickers,
)
mocker.patch("freqtrade.plugins.pairlist.RemotePairList.requests.get", mocker.patch(
return_value=mock_response) "freqtrade.plugins.pairlist.RemotePairList.requests.get", return_value=mock_response
)
exchange = get_patched_exchange(mocker, rpl_config) exchange = get_patched_exchange(mocker, rpl_config)
pairlistmanager = PairListManager(exchange, rpl_config) pairlistmanager = PairListManager(exchange, rpl_config)
remote_pairlist = RemotePairList(exchange, pairlistmanager, rpl_config, remote_pairlist = RemotePairList(
rpl_config["pairlists"][1], 1) exchange, pairlistmanager, rpl_config, rpl_config["pairlists"][1], 1
)
pairs, _time_elapsed = remote_pairlist.fetch_pairlist() pairs, _time_elapsed = remote_pairlist.fetch_pairlist()
assert pairs == ["XRP/USDT"] assert pairs == ["XRP/USDT"]
whitelist = remote_pairlist.filter_pairlist(rpl_config['exchange']['pair_whitelist'], {}) whitelist = remote_pairlist.filter_pairlist(rpl_config["exchange"]["pair_whitelist"], {})
assert whitelist == (["XRP/USDT"] if processing_mode == "filter" else ['ETH/USDT', 'XRP/USDT']) assert whitelist == (["XRP/USDT"] if processing_mode == "filter" else ["ETH/USDT", "XRP/USDT"])