Fix locking - should round before storing to have a consistent picture

This commit is contained in:
Matthias 2020-10-26 07:37:07 +01:00
parent 9c54c9a2bf
commit 6c913fa617
6 changed files with 20 additions and 14 deletions

View File

@ -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. 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 !!! Warning
Locking pairs is not functioning during backtesting. Locking pairs is not available during backtesting.
#### Pair locking example #### Pair locking example

View File

@ -4,7 +4,7 @@ Freqtrade is the main module of this bot. It contains the class Freqtrade()
import copy import copy
import logging import logging
import traceback import traceback
from datetime import datetime from datetime import datetime, timezone
from math import isclose from math import isclose
from threading import Lock from threading import Lock
from typing import Any, Dict, List, Optional from typing import Any, Dict, List, Optional
@ -19,10 +19,10 @@ from freqtrade.data.dataprovider import DataProvider
from freqtrade.edge import Edge from freqtrade.edge import Edge
from freqtrade.exceptions import (DependencyException, ExchangeError, InsufficientFundsError, from freqtrade.exceptions import (DependencyException, ExchangeError, InsufficientFundsError,
InvalidOrderException, PricingError) 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.misc import safe_value_fallback, safe_value_fallback2
from freqtrade.pairlist.pairlistmanager import PairListManager 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.resolvers import ExchangeResolver, StrategyResolver
from freqtrade.rpc import RPCManager, RPCMessageType from freqtrade.rpc import RPCManager, RPCMessageType
from freqtrade.state import State from freqtrade.state import State
@ -72,6 +72,8 @@ class FreqtradeBot:
self.wallets = Wallets(self.config, self.exchange) self.wallets = Wallets(self.config, self.exchange)
PairLocks.timeframe = self.config['timeframe']
self.pairlists = PairListManager(self.exchange, self.config) self.pairlists = PairListManager(self.exchange, self.config)
self.dataprovider = DataProvider(self.config, self.exchange, self.pairlists) self.dataprovider = DataProvider(self.config, self.exchange, self.pairlists)
@ -363,9 +365,9 @@ class FreqtradeBot:
except DependencyException as exception: except DependencyException as exception:
logger.warning('Unable to create trade for %s: %s', pair, exception) logger.warning('Unable to create trade for %s: %s', pair, exception)
if not trades_created: if not trades_created:
logger.debug("Found no buy signals for whitelisted currencies. " logger.debug("Found no buy signals for whitelisted currencies. "
"Trying again...") "Trying again...")
return trades_created return trades_created
@ -937,7 +939,7 @@ class FreqtradeBot:
self.update_trade_state(trade, trade.stoploss_order_id, stoploss_order, self.update_trade_state(trade, trade.stoploss_order_id, stoploss_order,
stoploss_order=True) stoploss_order=True)
# Lock pair for one candle to prevent immediate rebuys # 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') reason='Auto lock')
self._notify_sell(trade, "stoploss") self._notify_sell(trade, "stoploss")
return True return True
@ -1264,7 +1266,7 @@ class FreqtradeBot:
Trade.session.flush() Trade.session.flush()
# Lock pair for one candle to prevent immediate rebuys # 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') reason='Auto lock')
self._notify_sell(trade, order_type) self._notify_sell(trade, order_type)

View File

@ -694,7 +694,7 @@ class PairLock(_DECL_BASE):
if not now: if not now:
now = datetime.now(timezone.utc) now = datetime.now(timezone.utc)
filters = [func.datetime(PairLock.lock_end_time) >= now, filters = [PairLock.lock_end_time > now,
# Only active locks # Only active locks
PairLock.active.is_(True), ] PairLock.active.is_(True), ]
if pair: if pair:

View File

@ -5,6 +5,7 @@ from datetime import datetime, timezone
from typing import List, Optional from typing import List, Optional
from freqtrade.persistence.models import PairLock from freqtrade.persistence.models import PairLock
from freqtrade.exchange import timeframe_to_next_date
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -19,12 +20,14 @@ class PairLocks():
use_db = True use_db = True
locks: List[PairLock] = [] locks: List[PairLock] = []
timeframe: str = ''
@staticmethod @staticmethod
def lock_pair(pair: str, until: datetime, reason: str = None) -> None: def lock_pair(pair: str, until: datetime, reason: str = None) -> None:
lock = PairLock( lock = PairLock(
pair=pair, pair=pair,
lock_time=datetime.now(timezone.utc), lock_time=datetime.now(timezone.utc),
lock_end_time=until, lock_end_time=timeframe_to_next_date(PairLocks.timeframe, until),
reason=reason, reason=reason,
active=True active=True
) )
@ -49,7 +52,7 @@ class PairLocks():
return PairLock.query_pair_locks(pair, now).all() return PairLock.query_pair_locks(pair, now).all()
else: else:
locks = [lock for lock in PairLocks.locks if ( locks = [lock for lock in PairLocks.locks if (
lock.lock_end_time > now lock.lock_end_time >= now
and lock.active is True and lock.active is True
and (pair is None or lock.pair == pair) and (pair is None or lock.pair == pair)
)] )]

View File

@ -388,7 +388,8 @@ def test_is_pair_locked(default_conf):
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)
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) assert not strategy.is_pair_locked(pair)
# latest candle is from 14:20, lock goes to 14:30 # latest candle is from 14:20, lock goes to 14:30

View File

@ -15,7 +15,7 @@ from freqtrade.exceptions import (DependencyException, ExchangeError, Insufficie
InvalidOrderException, OperationalException, PricingError, InvalidOrderException, OperationalException, PricingError,
TemporaryError) TemporaryError)
from freqtrade.freqtradebot import FreqtradeBot 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.persistence.models import PairLock
from freqtrade.rpc import RPCMessageType from freqtrade.rpc import RPCMessageType
from freqtrade.state import RunMode, State from freqtrade.state import RunMode, State