implement pairlock side further

This commit is contained in:
Matthias 2022-04-24 11:23:26 +02:00
parent 420836b1b2
commit fc201bb4ff
6 changed files with 28 additions and 16 deletions

View File

@ -1445,7 +1445,7 @@ class PairLock(_DECL_BASE):
f'lock_end_time={lock_end_time}, reason={self.reason}, active={self.active})') f'lock_end_time={lock_end_time}, reason={self.reason}, active={self.active})')
@staticmethod @staticmethod
def query_pair_locks(pair: Optional[str], now: datetime) -> Query: def query_pair_locks(pair: Optional[str], now: datetime, side: str = '*') -> Query:
""" """
Get all currently active locks for this pair Get all currently active locks for this pair
:param pair: Pair to check for. Returns all current locks if pair is empty :param pair: Pair to check for. Returns all current locks if pair is empty
@ -1456,6 +1456,9 @@ class PairLock(_DECL_BASE):
PairLock.active.is_(True), ] PairLock.active.is_(True), ]
if pair: if pair:
filters.append(PairLock.pair == pair) filters.append(PairLock.pair == pair)
if side != '*':
filters.append(PairLock.direction == side)
return PairLock.query.filter( return PairLock.query.filter(
*filters *filters
) )

View File

@ -31,7 +31,7 @@ class PairLocks():
@staticmethod @staticmethod
def lock_pair(pair: str, until: datetime, reason: str = None, *, def lock_pair(pair: str, until: datetime, reason: str = None, *,
now: datetime = None) -> PairLock: now: datetime = None, side: str) -> PairLock:
""" """
Create PairLock from now to "until". Create PairLock from now to "until".
Uses database by default, unless PairLocks.use_db is set to False, Uses database by default, unless PairLocks.use_db is set to False,
@ -40,12 +40,14 @@ class PairLocks():
:param until: End time of the lock. Will be rounded up to the next candle. :param until: End time of the lock. Will be rounded up to the next candle.
:param reason: Reason string that will be shown as reason for the lock :param reason: Reason string that will be shown as reason for the lock
:param now: Current timestamp. Used to determine lock start time. :param now: Current timestamp. Used to determine lock start time.
:param side: Side to lock pair, can be 'long', 'short' or '*'
""" """
lock = PairLock( lock = PairLock(
pair=pair, pair=pair,
lock_time=now or datetime.now(timezone.utc), lock_time=now or datetime.now(timezone.utc),
lock_end_time=timeframe_to_next_date(PairLocks.timeframe, until), lock_end_time=timeframe_to_next_date(PairLocks.timeframe, until),
reason=reason, reason=reason,
direction=side,
active=True active=True
) )
if PairLocks.use_db: if PairLocks.use_db:
@ -56,7 +58,8 @@ class PairLocks():
return lock return lock
@staticmethod @staticmethod
def get_pair_locks(pair: Optional[str], now: Optional[datetime] = None) -> List[PairLock]: def get_pair_locks(
pair: Optional[str], now: Optional[datetime] = None, side: str = '*') -> List[PairLock]:
""" """
Get all currently active locks for this pair Get all currently active locks for this pair
:param pair: Pair to check for. Returns all current locks if pair is empty :param pair: Pair to check for. Returns all current locks if pair is empty
@ -67,12 +70,13 @@ class PairLocks():
now = datetime.now(timezone.utc) now = datetime.now(timezone.utc)
if PairLocks.use_db: if PairLocks.use_db:
return PairLock.query_pair_locks(pair, now).all() return PairLock.query_pair_locks(pair, now, side).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)
and (side == '*' or lock.direction == side)
)] )]
return locks return locks
@ -134,7 +138,7 @@ class PairLocks():
lock.active = False lock.active = False
@staticmethod @staticmethod
def is_global_lock(now: Optional[datetime] = None) -> bool: def is_global_lock(now: Optional[datetime] = None, side: str = '*') -> bool:
""" """
:param now: Datetime object (generated via datetime.now(timezone.utc)). :param now: Datetime object (generated via datetime.now(timezone.utc)).
defaults to datetime.now(timezone.utc) defaults to datetime.now(timezone.utc)
@ -142,10 +146,10 @@ class PairLocks():
if not now: if not now:
now = datetime.now(timezone.utc) now = datetime.now(timezone.utc)
return len(PairLocks.get_pair_locks('*', now)) > 0 return len(PairLocks.get_pair_locks('*', now, side)) > 0
@staticmethod @staticmethod
def is_pair_locked(pair: str, now: Optional[datetime] = None) -> bool: def is_pair_locked(pair: str, now: Optional[datetime] = None, side: str = '*') -> bool:
""" """
:param pair: Pair to check for :param pair: Pair to check for
:param now: Datetime object (generated via datetime.now(timezone.utc)). :param now: Datetime object (generated via datetime.now(timezone.utc)).
@ -154,7 +158,10 @@ class PairLocks():
if not now: if not now:
now = datetime.now(timezone.utc) now = datetime.now(timezone.utc)
return len(PairLocks.get_pair_locks(pair, now)) > 0 or PairLocks.is_global_lock(now) return (
len(PairLocks.get_pair_locks(pair, now, side)) > 0
or PairLocks.is_global_lock(now, side)
)
@staticmethod @staticmethod
def get_all_locks() -> List[PairLock]: def get_all_locks() -> List[PairLock]:

View File

@ -54,8 +54,9 @@ class ProtectionManager():
if protection_handler.has_global_stop: if protection_handler.has_global_stop:
lock = protection_handler.global_stop(date_now=now, side=side) lock = protection_handler.global_stop(date_now=now, side=side)
if lock and lock.until: if lock and lock.until:
if not PairLocks.is_global_lock(lock.until): if not PairLocks.is_global_lock(lock.until, lock.lock_side):
result = PairLocks.lock_pair('*', lock.until, lock.reason, now=now) result = PairLocks.lock_pair(
'*', lock.until, lock.reason, now=now, side=lock.lock_side)
return result return result
def stop_per_pair(self, pair, now: Optional[datetime] = None, def stop_per_pair(self, pair, now: Optional[datetime] = None,
@ -68,6 +69,7 @@ class ProtectionManager():
lock = protection_handler.stop_per_pair( lock = protection_handler.stop_per_pair(
pair=pair, date_now=now, side=side) pair=pair, date_now=now, side=side)
if lock and lock.until: if lock and lock.until:
if not PairLocks.is_pair_locked(pair, lock.until): if not PairLocks.is_pair_locked(pair, lock.until, lock.lock_side):
result = PairLocks.lock_pair(pair, lock.until, lock.reason, now=now) result = PairLocks.lock_pair(
pair, lock.until, lock.reason, now=now, side=lock.lock_side)
return result return result

View File

@ -20,7 +20,7 @@ class ProtectionReturn:
lock: bool lock: bool
until: datetime until: datetime
reason: Optional[str] reason: Optional[str]
lock_side: Optional[str] = None lock_side: str = '*'
class IProtection(LoggingMixin, ABC): class IProtection(LoggingMixin, ABC):

View File

@ -65,7 +65,7 @@ class StoplossGuard(IProtection):
lock=True, lock=True,
until=until, until=until,
reason=self._reason(), reason=self._reason(),
lock_side=(side if self._only_per_side else None) lock_side=(side if self._only_per_side else '*')
) )
def global_stop(self, date_now: datetime, side: LongShort) -> Optional[ProtectionReturn]: def global_stop(self, date_now: datetime, side: LongShort) -> Optional[ProtectionReturn]:

View File

@ -541,7 +541,7 @@ class IStrategy(ABC, HyperStrategyMixin):
""" """
return self.__class__.__name__ return self.__class__.__name__
def lock_pair(self, pair: str, until: datetime, reason: str = None) -> None: def lock_pair(self, pair: str, until: datetime, reason: str = None, side: str = '*') -> None:
""" """
Locks pair until a given timestamp happens. Locks pair until a given timestamp happens.
Locked pairs are not analyzed, and are prevented from opening new trades. Locked pairs are not analyzed, and are prevented from opening new trades.
@ -552,7 +552,7 @@ class IStrategy(ABC, HyperStrategyMixin):
Needs to be timezone aware `datetime.now(timezone.utc)` Needs to be timezone aware `datetime.now(timezone.utc)`
:param reason: Optional string explaining why the pair was locked. :param reason: Optional string explaining why the pair was locked.
""" """
PairLocks.lock_pair(pair, until, reason) PairLocks.lock_pair(pair, until, reason, side=side)
def unlock_pair(self, pair: str) -> None: def unlock_pair(self, pair: str) -> None:
""" """