diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index c0506203f..6c7d78864 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -704,7 +704,7 @@ To verify if a pair is currently locked, use `self.is_pair_locked(pair)`. Locked pairs will always be rounded up to the next candle. So assuming a `5m` timeframe, a lock with `until` set to 10:18 will lock the pair until the candle from 10:15-10:20 will be finished. !!! Warning - Locking pairs is not functioning during backtesting. + Locking pairs is not available during backtesting. #### Pair locking example diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 5a399801a..ae46d335b 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -4,7 +4,7 @@ Freqtrade is the main module of this bot. It contains the class Freqtrade() import copy import logging import traceback -from datetime import datetime +from datetime import datetime, timezone from math import isclose from threading import Lock from typing import Any, Dict, List, Optional @@ -19,10 +19,10 @@ from freqtrade.data.dataprovider import DataProvider from freqtrade.edge import Edge from freqtrade.exceptions import (DependencyException, ExchangeError, InsufficientFundsError, InvalidOrderException, PricingError) -from freqtrade.exchange import timeframe_to_minutes, timeframe_to_next_date +from freqtrade.exchange import timeframe_to_minutes from freqtrade.misc import safe_value_fallback, safe_value_fallback2 from freqtrade.pairlist.pairlistmanager import PairListManager -from freqtrade.persistence import Order, Trade, cleanup_db, init_db +from freqtrade.persistence import Order, Trade, cleanup_db, init_db, PairLocks from freqtrade.resolvers import ExchangeResolver, StrategyResolver from freqtrade.rpc import RPCManager, RPCMessageType from freqtrade.state import State @@ -72,6 +72,8 @@ class FreqtradeBot: self.wallets = Wallets(self.config, self.exchange) + PairLocks.timeframe = self.config['timeframe'] + self.pairlists = PairListManager(self.exchange, self.config) self.dataprovider = DataProvider(self.config, self.exchange, self.pairlists) @@ -363,9 +365,9 @@ class FreqtradeBot: except DependencyException as exception: logger.warning('Unable to create trade for %s: %s', pair, exception) - if not trades_created: - logger.debug("Found no buy signals for whitelisted currencies. " - "Trying again...") + if not trades_created: + logger.debug("Found no buy signals for whitelisted currencies. " + "Trying again...") return trades_created @@ -937,7 +939,7 @@ class FreqtradeBot: self.update_trade_state(trade, trade.stoploss_order_id, stoploss_order, stoploss_order=True) # Lock pair for one candle to prevent immediate rebuys - self.strategy.lock_pair(trade.pair, timeframe_to_next_date(self.config['timeframe']), + self.strategy.lock_pair(trade.pair, datetime.now(timezone.utc), reason='Auto lock') self._notify_sell(trade, "stoploss") return True @@ -1264,7 +1266,7 @@ class FreqtradeBot: Trade.session.flush() # Lock pair for one candle to prevent immediate rebuys - self.strategy.lock_pair(trade.pair, timeframe_to_next_date(self.config['timeframe']), + self.strategy.lock_pair(trade.pair, datetime.now(timezone.utc), reason='Auto lock') self._notify_sell(trade, order_type) diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py index 62b033bdf..3c62a7268 100644 --- a/freqtrade/persistence/models.py +++ b/freqtrade/persistence/models.py @@ -694,7 +694,7 @@ class PairLock(_DECL_BASE): if not now: now = datetime.now(timezone.utc) - filters = [func.datetime(PairLock.lock_end_time) >= now, + filters = [PairLock.lock_end_time > now, # Only active locks PairLock.active.is_(True), ] if pair: diff --git a/freqtrade/persistence/pairlock_middleware.py b/freqtrade/persistence/pairlock_middleware.py index ca2c31e36..c1acc2423 100644 --- a/freqtrade/persistence/pairlock_middleware.py +++ b/freqtrade/persistence/pairlock_middleware.py @@ -5,6 +5,7 @@ from datetime import datetime, timezone from typing import List, Optional from freqtrade.persistence.models import PairLock +from freqtrade.exchange import timeframe_to_next_date logger = logging.getLogger(__name__) @@ -19,12 +20,14 @@ class PairLocks(): use_db = True locks: List[PairLock] = [] + timeframe: str = '' + @staticmethod def lock_pair(pair: str, until: datetime, reason: str = None) -> None: lock = PairLock( pair=pair, lock_time=datetime.now(timezone.utc), - lock_end_time=until, + lock_end_time=timeframe_to_next_date(PairLocks.timeframe, until), reason=reason, active=True ) @@ -49,7 +52,7 @@ class PairLocks(): return PairLock.query_pair_locks(pair, now).all() else: locks = [lock for lock in PairLocks.locks if ( - lock.lock_end_time > now + lock.lock_end_time >= now and lock.active is True and (pair is None or lock.pair == pair) )] diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index e87fb7182..7cf9a0624 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -388,7 +388,8 @@ def test_is_pair_locked(default_conf): pair = 'BTC/USDT' # Lock until 14:30 lock_time = datetime(2020, 5, 1, 14, 30, 0, tzinfo=timezone.utc) - strategy.lock_pair(pair, lock_time) + # Subtract 2 seconds, as locking rounds up to the next candle. + strategy.lock_pair(pair, lock_time - timedelta(seconds=2)) assert not strategy.is_pair_locked(pair) # latest candle is from 14:20, lock goes to 14:30 diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 29df9c012..1f5b3ecaa 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -15,7 +15,7 @@ from freqtrade.exceptions import (DependencyException, ExchangeError, Insufficie InvalidOrderException, OperationalException, PricingError, TemporaryError) from freqtrade.freqtradebot import FreqtradeBot -from freqtrade.persistence import Order, PairLocks, Trade +from freqtrade.persistence import Order, Trade from freqtrade.persistence.models import PairLock from freqtrade.rpc import RPCMessageType from freqtrade.state import RunMode, State