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

View File

@@ -7,10 +7,10 @@ from freqtrade.persistence.models import PairLock
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")
def test_PairLocks(use_db):
PairLocks.timeframe = '5m'
PairLocks.timeframe = "5m"
PairLocks.use_db = use_db
# No lock should be present
if use_db:
@@ -18,28 +18,28 @@ def test_PairLocks(use_db):
assert PairLocks.use_db == use_db
pair = 'ETH/BTC'
pair = "ETH/BTC"
assert not PairLocks.is_pair_locked(pair)
PairLocks.lock_pair(pair, dt_now() + timedelta(minutes=4))
# ETH/BTC locked for 4 minutes (on both sides)
assert PairLocks.is_pair_locked(pair)
assert PairLocks.is_pair_locked(pair, side='long')
assert PairLocks.is_pair_locked(pair, side='short')
assert PairLocks.is_pair_locked(pair, side="long")
assert PairLocks.is_pair_locked(pair, side="short")
pair = 'BNB/BTC'
PairLocks.lock_pair(pair, dt_now() + timedelta(minutes=4), side='long')
pair = "BNB/BTC"
PairLocks.lock_pair(pair, dt_now() + timedelta(minutes=4), side="long")
assert not PairLocks.is_pair_locked(pair)
assert PairLocks.is_pair_locked(pair, side='long')
assert not PairLocks.is_pair_locked(pair, side='short')
assert PairLocks.is_pair_locked(pair, side="long")
assert not PairLocks.is_pair_locked(pair, side="short")
pair = 'BNB/USDT'
PairLocks.lock_pair(pair, dt_now() + timedelta(minutes=4), side='short')
pair = "BNB/USDT"
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, side='long')
assert PairLocks.is_pair_locked(pair, side='short')
assert not PairLocks.is_pair_locked(pair, side="long")
assert PairLocks.is_pair_locked(pair, side="short")
# XRP/BTC should not be locked now
pair = 'XRP/BTC'
pair = "XRP/BTC"
assert not PairLocks.is_pair_locked(pair)
# Unlocking a pair that's not locked should not raise an error
PairLocks.unlock_pair(pair)
@@ -52,12 +52,12 @@ def test_PairLocks(use_db):
assert len(locks) == 2
# Unlock original pair
pair = 'ETH/BTC'
pair = "ETH/BTC"
PairLocks.unlock_pair(pair)
assert not PairLocks.is_pair_locked(pair)
assert not PairLocks.is_global_lock()
pair = 'BTC/USDT'
pair = "BTC/USDT"
# Lock until 14:30
lock_time = datetime(2020, 5, 1, 14, 30, 0, tzinfo=timezone.utc)
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))
assert len(locks) == 1
assert 'PairLock' in str(locks[0])
assert "PairLock" in str(locks[0])
# Unlock all
PairLocks.unlock_pair(pair, lock_time + timedelta(minutes=-2))
assert not PairLocks.is_global_lock(lock_time + timedelta(minutes=-50))
# Global lock
PairLocks.lock_pair('*', lock_time)
PairLocks.lock_pair("*", lock_time)
assert PairLocks.is_global_lock(lock_time + timedelta(minutes=-50))
# Global lock also locks every pair separately
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:
locks = PairLocks.get_all_locks()
@@ -100,10 +100,10 @@ def test_PairLocks(use_db):
PairLocks.use_db = True
@pytest.mark.parametrize('use_db', (False, True))
@pytest.mark.parametrize("use_db", (False, True))
@pytest.mark.usefixtures("init_persistence")
def test_PairLocks_getlongestlock(use_db):
PairLocks.timeframe = '5m'
PairLocks.timeframe = "5m"
# No lock should be present
PairLocks.use_db = use_db
if use_db:
@@ -111,7 +111,7 @@ def test_PairLocks_getlongestlock(use_db):
assert PairLocks.use_db == use_db
pair = 'ETH/BTC'
pair = "ETH/BTC"
assert not PairLocks.is_pair_locked(pair)
PairLocks.lock_pair(pair, dt_now() + timedelta(minutes=4))
# ETH/BTC locked for 4 minutes
@@ -132,10 +132,10 @@ def test_PairLocks_getlongestlock(use_db):
PairLocks.use_db = True
@pytest.mark.parametrize('use_db', (False, True))
@pytest.mark.parametrize("use_db", (False, True))
@pytest.mark.usefixtures("init_persistence")
def test_PairLocks_reason(use_db):
PairLocks.timeframe = '5m'
PairLocks.timeframe = "5m"
PairLocks.use_db = use_db
# No lock should be present
if use_db:
@@ -143,15 +143,15 @@ def test_PairLocks_reason(use_db):
assert PairLocks.use_db == use_db
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("XRP/USDT", dt_now() + timedelta(minutes=4), "TestLock1")
PairLocks.lock_pair("ETH/USDT", dt_now() + timedelta(minutes=4), "TestLock2")
assert PairLocks.is_pair_locked('XRP/USDT')
assert PairLocks.is_pair_locked('ETH/USDT')
assert PairLocks.is_pair_locked("XRP/USDT")
assert PairLocks.is_pair_locked("ETH/USDT")
PairLocks.unlock_reason('TestLock1')
assert not PairLocks.is_pair_locked('XRP/USDT')
assert PairLocks.is_pair_locked('ETH/USDT')
PairLocks.unlock_reason("TestLock1")
assert not PairLocks.is_pair_locked("XRP/USDT")
assert PairLocks.is_pair_locked("ETH/USDT")
PairLocks.reset_locks()
PairLocks.use_db = True

View File

@@ -11,12 +11,16 @@ from freqtrade.plugins.protectionmanager import ProtectionManager
from tests.conftest import get_patched_freqtradebot, log_has_re
def generate_mock_trade(pair: str, fee: float, is_open: bool,
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,
):
def generate_mock_trade(
pair: str,
fee: float,
is_open: bool,
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()
trade = Trade(
@@ -29,32 +33,15 @@ def generate_mock_trade(pair: str, fee: float, is_open: bool,
open_rate=open_rate,
is_open=is_open,
amount=0.01 / open_rate,
exchange='binance',
exchange="binance",
is_short=is_short,
leverage=1,
)
trade.orders.append(Order(
ft_order_side=trade.entry_side,
order_id=f'{pair}-{trade.entry_side}-{trade.open_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=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}',
trade.orders.append(
Order(
ft_order_side=trade.entry_side,
order_id=f"{pair}-{trade.entry_side}-{trade.open_date}",
ft_is_open=False,
ft_pair=pair,
ft_amount=trade.amount,
@@ -62,12 +49,33 @@ def generate_mock_trade(pair: str, fee: float, is_open: bool,
amount=trade.amount,
filled=trade.amount,
remaining=0,
price=close_price,
average=close_price,
price=open_rate,
average=open_rate,
status="closed",
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()
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):
default_conf['protections'] = [{'method': protection}
for protection in constants.AVAILABLE_PROTECTIONS]
default_conf["protections"] = [
{"method": protection} for protection in constants.AVAILABLE_PROTECTIONS
]
freqtrade = get_patched_freqtradebot(mocker, default_conf)
for handler in freqtrade.protections._protection_handlers:
assert handler.name in constants.AVAILABLE_PROTECTIONS
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:
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', [
('1m', [20, 10],
[{"method": "StoplossGuard", "lookback_period_candles": 20, "stop_duration": 10}]),
('5m', [100, 15],
[{"method": "StoplossGuard", "lookback_period_candles": 20, "stop_duration": 15}]),
('1h', [1200, 40],
[{"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}]),
])
@pytest.mark.parametrize(
"timeframe,expected,protconf",
[
(
"1m",
[20, 10],
[{"method": "StoplossGuard", "lookback_period_candles": 20, "stop_duration": 10}],
),
(
"5m",
[100, 15],
[{"method": "StoplossGuard", "lookback_period_candles": 20, "stop_duration": 15}],
),
(
"1h",
[1200, 40],
[{"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):
default_conf['timeframe'] = timeframe
default_conf["timeframe"] = timeframe
man = ProtectionManager(default_conf, protconf)
assert len(man._protection_handlers) == len(protconf)
assert man._protection_handlers[0]._lookback_period == expected[0]
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")
def test_stoploss_guard(mocker, default_conf, fee, caplog, is_short):
# Active for both sides (long and short)
default_conf['protections'] = [{
"method": "StoplossGuard",
"lookback_period": 60,
"stop_duration": 40,
"trade_limit": 3
}]
default_conf["protections"] = [
{"method": "StoplossGuard", "lookback_period": 60, "stop_duration": 40, "trade_limit": 3}
]
freqtrade = get_patched_freqtradebot(mocker, default_conf)
message = r"Trading stopped due to .*"
assert not freqtrade.protections.global_stop()
@@ -135,8 +168,13 @@ def test_stoploss_guard(mocker, default_conf, fee, caplog, is_short):
caplog.clear()
generate_mock_trade(
'XRP/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value,
min_ago_open=200, min_ago_close=30, is_short=is_short,
"XRP/BTC",
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()
@@ -144,13 +182,23 @@ def test_stoploss_guard(mocker, default_conf, fee, caplog, is_short):
caplog.clear()
# This trade does not count, as it's closed too long ago
generate_mock_trade(
'BCH/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value,
min_ago_open=250, min_ago_close=100, is_short=is_short,
"BCH/BTC",
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(
'ETH/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value,
min_ago_open=240, min_ago_close=30, is_short=is_short,
"ETH/BTC",
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.
assert not freqtrade.protections.global_stop()
@@ -158,8 +206,13 @@ def test_stoploss_guard(mocker, default_conf, fee, caplog, is_short):
caplog.clear()
generate_mock_trade(
'LTC/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value,
min_ago_open=180, min_ago_close=30, is_short=is_short,
"LTC/BTC",
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()
@@ -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
# 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)
assert not PairLocks.is_global_lock(end_time)
@pytest.mark.parametrize('only_per_pair', [False, True])
@pytest.mark.parametrize('only_per_side', [False, True])
@pytest.mark.parametrize("only_per_pair", [False, True])
@pytest.mark.parametrize("only_per_side", [False, True])
@pytest.mark.usefixtures("init_persistence")
def test_stoploss_guard_perpair(mocker, default_conf, fee, caplog, only_per_pair, only_per_side):
default_conf['protections'] = [{
"method": "StoplossGuard",
"lookback_period": 60,
"trade_limit": 2,
"stop_duration": 60,
"only_per_pair": only_per_pair,
"only_per_side": only_per_side,
}]
check_side = 'long' if only_per_side else '*'
default_conf["protections"] = [
{
"method": "StoplossGuard",
"lookback_period": 60,
"trade_limit": 2,
"stop_duration": 60,
"only_per_pair": only_per_pair,
"only_per_side": only_per_side,
}
]
check_side = "long" if only_per_side else "*"
is_short = False
freqtrade = get_patched_freqtradebot(mocker, default_conf)
message = r"Trading stopped due to .*"
pair = 'XRP/BTC'
pair = "XRP/BTC"
assert not freqtrade.protections.stop_per_pair(pair)
assert not freqtrade.protections.global_stop()
assert not log_has_re(message, caplog)
caplog.clear()
generate_mock_trade(
pair, 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
pair,
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)
@@ -206,13 +267,25 @@ def test_stoploss_guard_perpair(mocker, default_conf, fee, caplog, only_per_pair
caplog.clear()
# This trade does not count, as it's closed too long ago
generate_mock_trade(
pair, 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
pair,
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.
generate_mock_trade(
'ETH/BTC', 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
"ETH/BTC",
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.
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
generate_mock_trade(
pair, 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
pair,
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)
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_global_lock(side=check_side) != only_per_pair
if only_per_side:
assert not PairLocks.is_pair_locked(pair, side='*')
assert not PairLocks.is_global_lock(side='*')
assert not PairLocks.is_pair_locked(pair, side="*")
assert not PairLocks.is_global_lock(side="*")
caplog.clear()
# 2nd Trade that counts with correct pair
generate_mock_trade(
pair, 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
pair,
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)
@@ -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_global_lock(side=check_side) != only_per_pair
if only_per_side:
assert not PairLocks.is_pair_locked(pair, side='*')
assert not PairLocks.is_global_lock(side='*')
assert not PairLocks.is_pair_locked(pair, side="*")
assert not PairLocks.is_global_lock(side="*")
@pytest.mark.usefixtures("init_persistence")
def test_CooldownPeriod(mocker, default_conf, fee, caplog):
default_conf['protections'] = [{
"method": "CooldownPeriod",
"stop_duration": 60,
}]
default_conf["protections"] = [
{
"method": "CooldownPeriod",
"stop_duration": 60,
}
]
freqtrade = get_patched_freqtradebot(mocker, default_conf)
message = r"Trading stopped due to .*"
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)
caplog.clear()
generate_mock_trade(
'XRP/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value,
min_ago_open=200, min_ago_close=30,
"XRP/BTC",
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 freqtrade.protections.stop_per_pair('XRP/BTC')
assert PairLocks.is_pair_locked('XRP/BTC')
assert freqtrade.protections.stop_per_pair("XRP/BTC")
assert PairLocks.is_pair_locked("XRP/BTC")
assert not PairLocks.is_global_lock()
generate_mock_trade(
'ETH/BTC', fee.return_value, False, exit_reason=ExitType.ROI.value,
min_ago_open=205, min_ago_close=35,
"ETH/BTC",
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 PairLocks.is_pair_locked('ETH/BTC')
assert freqtrade.protections.stop_per_pair('ETH/BTC')
assert PairLocks.is_pair_locked('ETH/BTC')
assert not PairLocks.is_pair_locked("ETH/BTC")
assert freqtrade.protections.stop_per_pair("ETH/BTC")
assert PairLocks.is_pair_locked("ETH/BTC")
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")
def test_LowProfitPairs(mocker, default_conf, fee, caplog, only_per_side):
default_conf['protections'] = [{
"method": "LowProfitPairs",
"lookback_period": 400,
"stop_duration": 60,
"trade_limit": 2,
"required_profit": 0.0,
"only_per_side": only_per_side,
}]
default_conf["protections"] = [
{
"method": "LowProfitPairs",
"lookback_period": 400,
"stop_duration": 60,
"trade_limit": 2,
"required_profit": 0.0,
"only_per_side": only_per_side,
}
]
freqtrade = get_patched_freqtradebot(mocker, default_conf)
message = r"Trading stopped due to .*"
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)
caplog.clear()
generate_mock_trade(
'XRP/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value,
min_ago_open=800, min_ago_close=450, profit_rate=0.9,
"XRP/BTC",
fee.return_value,
False,
exit_reason=ExitType.STOP_LOSS.value,
min_ago_open=800,
min_ago_close=450,
profit_rate=0.9,
)
Trade.commit()
# Not locked with 1 trade
assert not freqtrade.protections.global_stop()
assert not freqtrade.protections.stop_per_pair('XRP/BTC')
assert not PairLocks.is_pair_locked('XRP/BTC')
assert not freqtrade.protections.stop_per_pair("XRP/BTC")
assert not PairLocks.is_pair_locked("XRP/BTC")
assert not PairLocks.is_global_lock()
generate_mock_trade(
'XRP/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value,
min_ago_open=200, min_ago_close=120, profit_rate=0.9,
"XRP/BTC",
fee.return_value,
False,
exit_reason=ExitType.STOP_LOSS.value,
min_ago_open=200,
min_ago_close=120,
profit_rate=0.9,
)
Trade.commit()
# Not locked with 1 trade (first trade is outside of lookback_period)
assert not freqtrade.protections.global_stop()
assert not freqtrade.protections.stop_per_pair('XRP/BTC')
assert not PairLocks.is_pair_locked('XRP/BTC')
assert not freqtrade.protections.stop_per_pair("XRP/BTC")
assert not PairLocks.is_pair_locked("XRP/BTC")
assert not PairLocks.is_global_lock()
# Add positive trade
generate_mock_trade(
'XRP/BTC', fee.return_value, False, exit_reason=ExitType.ROI.value,
min_ago_open=20, min_ago_close=10, profit_rate=1.15, is_short=True
"XRP/BTC",
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()
assert freqtrade.protections.stop_per_pair('XRP/BTC') != only_per_side
assert not PairLocks.is_pair_locked('XRP/BTC', side='*')
assert PairLocks.is_pair_locked('XRP/BTC', side='long') == only_per_side
assert freqtrade.protections.stop_per_pair("XRP/BTC") != only_per_side
assert not PairLocks.is_pair_locked("XRP/BTC", side="*")
assert PairLocks.is_pair_locked("XRP/BTC", side="long") == only_per_side
generate_mock_trade(
'XRP/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value,
min_ago_open=110, min_ago_close=21, profit_rate=0.8,
"XRP/BTC",
fee.return_value,
False,
exit_reason=ExitType.STOP_LOSS.value,
min_ago_open=110,
min_ago_close=21,
profit_rate=0.8,
)
Trade.commit()
# Locks due to 2nd trade
assert freqtrade.protections.global_stop() != 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='*') != 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="*") != only_per_side
assert not PairLocks.is_global_lock()
Trade.commit()
@pytest.mark.usefixtures("init_persistence")
def test_MaxDrawdown(mocker, default_conf, fee, caplog):
default_conf['protections'] = [{
"method": "MaxDrawdown",
"lookback_period": 1000,
"stop_duration": 60,
"trade_limit": 3,
"max_allowed_drawdown": 0.15
}]
default_conf["protections"] = [
{
"method": "MaxDrawdown",
"lookback_period": 1000,
"stop_duration": 60,
"trade_limit": 3,
"max_allowed_drawdown": 0.15,
}
]
freqtrade = get_patched_freqtradebot(mocker, default_conf)
message = r"Trading stopped due to Max.*"
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()
generate_mock_trade(
'XRP/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value,
min_ago_open=1000, min_ago_close=900, profit_rate=1.1,
"XRP/BTC",
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(
'ETH/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value,
min_ago_open=1000, min_ago_close=900, profit_rate=1.1,
"ETH/BTC",
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(
'NEO/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value,
min_ago_open=1000, min_ago_close=900, profit_rate=1.1,
"NEO/BTC",
fee.return_value,
False,
exit_reason=ExitType.STOP_LOSS.value,
min_ago_open=1000,
min_ago_close=900,
profit_rate=1.1,
)
Trade.commit()
# No losing trade yet ... so max_drawdown will raise exception
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(
'XRP/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value,
min_ago_open=500, min_ago_close=400, profit_rate=0.9,
"XRP/BTC",
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
assert not freqtrade.protections.global_stop()
assert not freqtrade.protections.stop_per_pair('XRP/BTC')
assert not PairLocks.is_pair_locked('XRP/BTC')
assert not freqtrade.protections.stop_per_pair("XRP/BTC")
assert not PairLocks.is_pair_locked("XRP/BTC")
assert not PairLocks.is_global_lock()
generate_mock_trade(
'XRP/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value,
min_ago_open=1200, min_ago_close=1100, profit_rate=0.5,
"XRP/BTC",
fee.return_value,
False,
exit_reason=ExitType.STOP_LOSS.value,
min_ago_open=1200,
min_ago_close=1100,
profit_rate=0.5,
)
Trade.commit()
# Not locked with 1 trade (2nd trade is outside of lookback_period)
assert not freqtrade.protections.global_stop()
assert not freqtrade.protections.stop_per_pair('XRP/BTC')
assert not PairLocks.is_pair_locked('XRP/BTC')
assert not freqtrade.protections.stop_per_pair("XRP/BTC")
assert not PairLocks.is_pair_locked("XRP/BTC")
assert not PairLocks.is_global_lock()
assert not log_has_re(message, caplog)
# Winning trade ... (should not lock, does not change drawdown!)
generate_mock_trade(
'XRP/BTC', fee.return_value, False, exit_reason=ExitType.ROI.value,
min_ago_open=320, min_ago_close=410, profit_rate=1.5,
"XRP/BTC",
fee.return_value,
False,
exit_reason=ExitType.ROI.value,
min_ago_open=320,
min_ago_close=410,
profit_rate=1.5,
)
Trade.commit()
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%
generate_mock_trade(
'XRP/BTC', fee.return_value, False, exit_reason=ExitType.ROI.value,
min_ago_open=20, min_ago_close=10, profit_rate=0.8,
"XRP/BTC",
fee.return_value,
False,
exit_reason=ExitType.ROI.value,
min_ago_open=20,
min_ago_close=10,
profit_rate=0.8,
)
Trade.commit()
assert not freqtrade.protections.stop_per_pair('XRP/BTC')
assert not freqtrade.protections.stop_per_pair("XRP/BTC")
# 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 PairLocks.is_global_lock()
assert log_has_re(message, caplog)
@pytest.mark.parametrize("protectionconf,desc_expected,exception_expected", [
({"method": "StoplossGuard", "lookback_period": 60, "trade_limit": 2, "stop_duration": 60},
"[{'StoplossGuard': 'StoplossGuard - Frequent Stoploss Guard, "
"2 stoplosses with profit < 0.00% within 60 minutes.'}]",
None
),
({"method": "CooldownPeriod", "stop_duration": 60},
"[{'CooldownPeriod': 'CooldownPeriod - Cooldown period of 60 minutes.'}]",
None
),
({"method": "LowProfitPairs", "lookback_period": 60, "stop_duration": 60},
"[{'LowProfitPairs': 'LowProfitPairs - Low Profit Protection, locks pairs with "
"profit < 0.0 within 60 minutes.'}]",
None
),
({"method": "MaxDrawdown", "lookback_period": 60, "stop_duration": 60},
"[{'MaxDrawdown': 'MaxDrawdown - Max drawdown protection, stop trading "
"if drawdown is > 0.0 within 60 minutes.'}]",
None
),
({"method": "StoplossGuard", "lookback_period_candles": 12, "trade_limit": 2,
"required_profit": -0.05, "stop_duration": 60},
"[{'StoplossGuard': 'StoplossGuard - Frequent Stoploss Guard, "
"2 stoplosses with profit < -5.00% within 12 candles.'}]",
None
),
({"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]
@pytest.mark.parametrize(
"protectionconf,desc_expected,exception_expected",
[
(
{
"method": "StoplossGuard",
"lookback_period": 60,
"trade_limit": 2,
"stop_duration": 60,
},
"[{'StoplossGuard': 'StoplossGuard - Frequent Stoploss Guard, "
"2 stoplosses with profit < 0.00% within 60 minutes.'}]",
None,
),
(
{"method": "CooldownPeriod", "stop_duration": 60},
"[{'CooldownPeriod': 'CooldownPeriod - Cooldown period of 60 minutes.'}]",
None,
),
(
{"method": "LowProfitPairs", "lookback_period": 60, "stop_duration": 60},
"[{'LowProfitPairs': 'LowProfitPairs - Low Profit Protection, locks pairs with "
"profit < 0.0 within 60 minutes.'}]",
None,
),
(
{"method": "MaxDrawdown", "lookback_period": 60, "stop_duration": 60},
"[{'MaxDrawdown': 'MaxDrawdown - Max drawdown protection, stop trading "
"if drawdown is > 0.0 within 60 minutes.'}]",
None,
),
(
{
"method": "StoplossGuard",
"lookback_period_candles": 12,
"trade_limit": 2,
"required_profit": -0.05,
"stop_duration": 60,
},
"[{'StoplossGuard': 'StoplossGuard - Frequent Stoploss Guard, "
"2 stoplosses with profit < -5.00% within 12 candles.'}]",
None,
),
(
{"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)
short_desc = str(freqtrade.protections.short_desc())

View File

@@ -12,59 +12,57 @@ from tests.conftest import EXMS, get_patched_exchange, get_patched_freqtradebot,
@pytest.fixture(scope="function")
def rpl_config(default_conf):
default_conf['stake_currency'] = 'USDT'
default_conf["stake_currency"] = "USDT"
default_conf['exchange']['pair_whitelist'] = [
'ETH/USDT',
'XRP/USDT',
]
default_conf['exchange']['pair_blacklist'] = [
'BLK/USDT'
default_conf["exchange"]["pair_whitelist"] = [
"ETH/USDT",
"XRP/USDT",
]
default_conf["exchange"]["pair_blacklist"] = ["BLK/USDT"]
return default_conf
def test_gen_pairlist_with_local_file(mocker, rpl_config):
mock_file = MagicMock()
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
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",
'number_assets': 2,
'refresh_period': 1800,
'keep_pairlist_on_failure': True,
'pairlist_url': 'file:///pairlist.json',
'bearer_token': '',
'read_timeout': 60
"number_assets": 2,
"refresh_period": 1800,
"keep_pairlist_on_failure": True,
"pairlist_url": "file:///pairlist.json",
"bearer_token": "",
"read_timeout": 60,
}
]
exchange = get_patched_exchange(mocker, rpl_config)
pairlistmanager = PairListManager(exchange, rpl_config)
remote_pairlist = RemotePairList(exchange, pairlistmanager, rpl_config,
rpl_config['pairlists'][0], 0)
remote_pairlist = RemotePairList(
exchange, pairlistmanager, rpl_config, rpl_config["pairlists"][0], 0
)
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):
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",
"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)
pairlistmanager = PairListManager(exchange, rpl_config)
mocker.patch("freqtrade.plugins.pairlist.RemotePairList.requests.get",
return_value=mock_response)
remote_pairlist = RemotePairList(exchange, pairlistmanager, rpl_config,
rpl_config['pairlists'][0], 0)
mocker.patch(
"freqtrade.plugins.pairlist.RemotePairList.requests.get", return_value=mock_response
)
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()
def test_fetch_pairlist_timeout_keep_last_pairlist(mocker, rpl_config, caplog):
rpl_config['pairlists'] = [
rpl_config["pairlists"] = [
{
"method": "RemotePairList",
"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)
pairlistmanager = PairListManager(exchange, rpl_config)
mocker.patch("freqtrade.plugins.pairlist.RemotePairList.requests.get",
side_effect=requests.exceptions.RequestException)
mocker.patch(
"freqtrade.plugins.pairlist.RemotePairList.requests.get",
side_effect=requests.exceptions.RequestException,
)
remote_pairlist = RemotePairList(exchange, pairlistmanager, rpl_config,
rpl_config['pairlists'][0], 0)
remote_pairlist = RemotePairList(
exchange, pairlistmanager, rpl_config, rpl_config["pairlists"][0], 0
)
remote_pairlist._last_pairlist = ["BTC/USDT", "ETH/USDT", "LTC/USDT"]
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()
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 pairs == ["BTC/USDT", "ETH/USDT", "LTC/USDT"]
def test_remote_pairlist_init_no_pairlist_url(mocker, rpl_config):
rpl_config['pairlists'] = [
rpl_config["pairlists"] = [
{
"method": "RemotePairList",
"number_assets": 10,
@@ -127,14 +129,16 @@ def test_remote_pairlist_init_no_pairlist_url(mocker, rpl_config):
]
get_patched_exchange(mocker, rpl_config)
with pytest.raises(OperationalException, match=r'`pairlist_url` not specified.'
r' Please check your configuration for "pairlist.config.pairlist_url"'):
with pytest.raises(
OperationalException,
match=r"`pairlist_url` not specified."
r' Please check your configuration for "pairlist.config.pairlist_url"',
):
get_patched_freqtradebot(mocker, rpl_config)
def test_remote_pairlist_init_no_number_assets(mocker, rpl_config):
rpl_config['pairlists'] = [
rpl_config["pairlists"] = [
{
"method": "RemotePairList",
"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)
with pytest.raises(OperationalException, match=r'`number_assets` not specified. '
'Please check your configuration for "pairlist.config.number_assets"'):
with pytest.raises(
OperationalException,
match=r"`number_assets` not specified. "
'Please check your configuration for "pairlist.config.number_assets"',
):
get_patched_freqtradebot(mocker, rpl_config)
def test_fetch_pairlist_mock_response_valid(mocker, rpl_config):
rpl_config['pairlists'] = [
rpl_config["pairlists"] = [
{
"method": "RemotePairList",
"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 = {
"pairs": ["ETH/USDT", "XRP/USDT", "LTC/USDT", "EOS/USDT"],
"refresh_period": 60
"refresh_period": 60,
}
mock_response.headers = {
"content-type": "application/json"
}
mock_response.headers = {"content-type": "application/json"}
mock_response.elapsed.total_seconds.return_value = 0.4
mocker.patch("freqtrade.plugins.pairlist.RemotePairList.requests.get",
return_value=mock_response)
mocker.patch(
"freqtrade.plugins.pairlist.RemotePairList.requests.get", return_value=mock_response
)
exchange = get_patched_exchange(mocker, rpl_config)
pairlistmanager = PairListManager(exchange, rpl_config)
remote_pairlist = RemotePairList(exchange, pairlistmanager, rpl_config,
rpl_config['pairlists'][0], 0)
remote_pairlist = RemotePairList(
exchange, pairlistmanager, rpl_config, rpl_config["pairlists"][0], 0
)
pairs, time_elapsed = remote_pairlist.fetch_pairlist()
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):
rpl_config['pairlists'] = [
rpl_config["pairlists"] = [
{
"method": "RemotePairList",
"mode": "blacklis",
@@ -201,11 +207,11 @@ def test_remote_pairlist_init_wrong_mode(mocker, rpl_config):
with pytest.raises(
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)
rpl_config['pairlists'] = [
rpl_config["pairlists"] = [
{
"method": "RemotePairList",
"mode": "blacklist",
@@ -216,14 +222,13 @@ def test_remote_pairlist_init_wrong_mode(mocker, rpl_config):
]
with pytest.raises(
OperationalException,
match=r'A `blacklist` mode RemotePairList can not be.*first.*'
OperationalException, match=r"A `blacklist` mode RemotePairList can not be.*first.*"
):
get_patched_freqtradebot(mocker, rpl_config)
def test_remote_pairlist_init_wrong_proc_mode(mocker, rpl_config):
rpl_config['pairlists'] = [
rpl_config["pairlists"] = [
{
"method": "RemotePairList",
"processing_mode": "filler",
@@ -237,25 +242,19 @@ def test_remote_pairlist_init_wrong_proc_mode(mocker, rpl_config):
get_patched_exchange(mocker, rpl_config)
with pytest.raises(
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)
def test_remote_pairlist_blacklist(mocker, rpl_config, caplog, markets, tickers):
mock_response = MagicMock()
mock_response.json.return_value = {
"pairs": ["XRP/USDT"],
"refresh_period": 60
}
mock_response.json.return_value = {"pairs": ["XRP/USDT"], "refresh_period": 60}
mock_response.headers = {
"content-type": "application/json"
}
mock_response.headers = {"content-type": "application/json"}
rpl_config['pairlists'] = [
rpl_config["pairlists"] = [
{
"method": "StaticPairList",
},
@@ -263,31 +262,34 @@ def test_remote_pairlist_blacklist(mocker, rpl_config, caplog, markets, tickers)
"method": "RemotePairList",
"mode": "blacklist",
"pairlist_url": "http://example.com/pairlist",
"number_assets": 3
}
"number_assets": 3,
},
]
mocker.patch.multiple(EXMS,
markets=PropertyMock(return_value=markets),
exchange_has=MagicMock(return_value=True),
get_tickers=tickers
)
mocker.patch.multiple(
EXMS,
markets=PropertyMock(return_value=markets),
exchange_has=MagicMock(return_value=True),
get_tickers=tickers,
)
mocker.patch("freqtrade.plugins.pairlist.RemotePairList.requests.get",
return_value=mock_response)
mocker.patch(
"freqtrade.plugins.pairlist.RemotePairList.requests.get", return_value=mock_response
)
exchange = get_patched_exchange(mocker, rpl_config)
pairlistmanager = PairListManager(exchange, rpl_config)
remote_pairlist = RemotePairList(exchange, pairlistmanager, rpl_config,
rpl_config["pairlists"][1], 1)
remote_pairlist = RemotePairList(
exchange, pairlistmanager, rpl_config, rpl_config["pairlists"][1], 1
)
pairs, _time_elapsed = remote_pairlist.fetch_pairlist()
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 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"])
def test_remote_pairlist_whitelist(mocker, rpl_config, processing_mode, markets, tickers):
mock_response = MagicMock()
mock_response.json.return_value = {
"pairs": ["XRP/USDT"],
"refresh_period": 60
}
mock_response.json.return_value = {"pairs": ["XRP/USDT"], "refresh_period": 60}
mock_response.headers = {
"content-type": "application/json"
}
mock_response.headers = {"content-type": "application/json"}
rpl_config['pairlists'] = [
rpl_config["pairlists"] = [
{
"method": "StaticPairList",
},
@@ -316,29 +312,32 @@ def test_remote_pairlist_whitelist(mocker, rpl_config, processing_mode, markets,
"mode": "whitelist",
"processing_mode": processing_mode,
"pairlist_url": "http://example.com/pairlist",
"number_assets": 3
}
"number_assets": 3,
},
]
mocker.patch.multiple(EXMS,
markets=PropertyMock(return_value=markets),
exchange_has=MagicMock(return_value=True),
get_tickers=tickers
)
mocker.patch.multiple(
EXMS,
markets=PropertyMock(return_value=markets),
exchange_has=MagicMock(return_value=True),
get_tickers=tickers,
)
mocker.patch("freqtrade.plugins.pairlist.RemotePairList.requests.get",
return_value=mock_response)
mocker.patch(
"freqtrade.plugins.pairlist.RemotePairList.requests.get", return_value=mock_response
)
exchange = get_patched_exchange(mocker, rpl_config)
pairlistmanager = PairListManager(exchange, rpl_config)
remote_pairlist = RemotePairList(exchange, pairlistmanager, rpl_config,
rpl_config["pairlists"][1], 1)
remote_pairlist = RemotePairList(
exchange, pairlistmanager, rpl_config, rpl_config["pairlists"][1], 1
)
pairs, _time_elapsed = remote_pairlist.fetch_pairlist()
assert pairs == ["XRP/USDT"]
whitelist = remote_pairlist.filter_pairlist(rpl_config['exchange']['pair_whitelist'], {})
assert whitelist == (["XRP/USDT"] if processing_mode == "filter" else ['ETH/USDT', 'XRP/USDT'])
whitelist = remote_pairlist.filter_pairlist(rpl_config["exchange"]["pair_whitelist"], {})
assert whitelist == (["XRP/USDT"] if processing_mode == "filter" else ["ETH/USDT", "XRP/USDT"])