feat
This commit is contained in:
34
py-client/strategy/trend/watch.py
Normal file
34
py-client/strategy/trend/watch.py
Normal file
@@ -0,0 +1,34 @@
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, timedelta
|
||||
from threading import Lock
|
||||
import logging
|
||||
|
||||
|
||||
@dataclass
|
||||
class _Entry:
|
||||
last_close: float
|
||||
expires_at: datetime
|
||||
|
||||
|
||||
class DipWatch:
|
||||
def __init__(self, expire_seconds: float = 300, rebound_threshold: float = 0.61):
|
||||
self.expire_seconds, self.rebound_threshold = expire_seconds, rebound_threshold
|
||||
self.data: dict[str, _Entry] = {}; self.lock = Lock()
|
||||
|
||||
def triggered(self, tag: str, code: str, price: float, now: datetime | None = None) -> bool:
|
||||
if price <= 0: return False
|
||||
now = now or datetime.now()
|
||||
with self.lock:
|
||||
watch = self.data.get(code)
|
||||
if watch is None or now >= watch.expires_at:
|
||||
self.data[code] = _Entry(price, now + timedelta(seconds=self.expire_seconds)); return False
|
||||
if price < watch.last_close:
|
||||
self.data[code] = _Entry(price, now + timedelta(seconds=self.expire_seconds)); return False
|
||||
rebound = (price - watch.last_close) / watch.last_close * 100
|
||||
if rebound <= 0 or rebound < self.rebound_threshold: return False
|
||||
del self.data[code]
|
||||
logging.info("[%s-触发] %s 反弹=%.2f%%", tag, code, rebound)
|
||||
return True
|
||||
|
||||
def forget(self, code):
|
||||
with self.lock: self.data.pop(code, None)
|
||||
Reference in New Issue
Block a user