diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index 0bfc0a2f6..84d6b2320 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -894,7 +894,8 @@ Sometimes it may be desired to lock a pair after certain events happen (e.g. mul Freqtrade has an easy method to do this from within the strategy, by calling `self.lock_pair(pair, until, [reason])`. `until` must be a datetime object in the future, after which trading will be re-enabled for that pair, while `reason` is an optional string detailing why the pair was locked. -Locks can also be lifted manually, by calling `self.unlock_pair(pair)`. +Locks can also be lifted manually, by calling `self.unlock_pair(pair)` or `self.unlock_reason()` - providing reason the pair was locked with. +`self.unlock_reason()` will unlock all pairs currently locked with the provided reason. To verify if a pair is currently locked, use `self.is_pair_locked(pair)`. diff --git a/freqtrade/persistence/pairlock_middleware.py b/freqtrade/persistence/pairlock_middleware.py index e74948813..afbd9781b 100644 --- a/freqtrade/persistence/pairlock_middleware.py +++ b/freqtrade/persistence/pairlock_middleware.py @@ -116,14 +116,14 @@ class PairLocks(): if PairLocks.use_db: # used in live modes - logger.info(f"Releasing all locks with reason \'{reason}\':") + logger.info(f"Releasing all locks with reason '{reason}':") filters = [PairLock.lock_end_time > now, PairLock.active.is_(True), PairLock.reason == reason ] locks = PairLock.query.filter(*filters) for lock in locks: - logger.info(f"Releasing lock for {lock.pair} with reason \'{reason}\'.") + logger.info(f"Releasing lock for {lock.pair} with reason '{reason}'.") lock.active = False PairLock.query.session.commit() else: diff --git a/tests/plugins/test_pairlocks.py b/tests/plugins/test_pairlocks.py index c694fd7c1..f9e5583ed 100644 --- a/tests/plugins/test_pairlocks.py +++ b/tests/plugins/test_pairlocks.py @@ -116,3 +116,28 @@ def test_PairLocks_getlongestlock(use_db): PairLocks.reset_locks() PairLocks.use_db = True + + +@pytest.mark.parametrize('use_db', (False, True)) +@pytest.mark.usefixtures("init_persistence") +def test_PairLocks_reason(use_db): + PairLocks.timeframe = '5m' + PairLocks.use_db = use_db + # No lock should be present + if use_db: + assert len(PairLock.query.all()) == 0 + + assert PairLocks.use_db == use_db + + PairLocks.lock_pair('XRP/USDT', arrow.utcnow().shift(minutes=4).datetime, 'TestLock1') + PairLocks.lock_pair('ETH/USDT', arrow.utcnow().shift(minutes=4).datetime, 'TestLock2') + + 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.reset_locks() + PairLocks.use_db = True diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index dcb9e3e64..ebd950fd6 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -575,6 +575,13 @@ def test_is_pair_locked(default_conf): strategy.unlock_pair(pair) assert not strategy.is_pair_locked(pair) + # Lock with reason + reason = "TestLockR" + strategy.lock_pair(pair, arrow.now(timezone.utc).shift(minutes=4).datetime, reason) + assert strategy.is_pair_locked(pair) + strategy.unlock_reason(reason) + assert not strategy.is_pair_locked(pair) + pair = 'BTC/USDT' # Lock until 14:30 lock_time = datetime(2020, 5, 1, 14, 30, 0, tzinfo=timezone.utc)