修复支撑阻力企业微信重复
This commit is contained in:
+57
-10
@@ -115,6 +115,56 @@ def rs_break_infer_from_close(close: float, upper: float, lower: float) -> dict[
|
||||
}
|
||||
|
||||
|
||||
def _parse_notify_datetime(raw: Optional[str]) -> Optional[datetime]:
|
||||
s = str(raw or "").strip()
|
||||
if not s:
|
||||
return None
|
||||
try:
|
||||
dt = datetime.fromisoformat(s.replace("Z", "+00:00"))
|
||||
if dt.tzinfo is not None:
|
||||
dt = dt.replace(tzinfo=None)
|
||||
return dt
|
||||
except Exception:
|
||||
pass
|
||||
for fmt in ("%Y-%m-%d %H:%M:%S", "%Y-%m-%dT%H:%M:%S"):
|
||||
try:
|
||||
return datetime.strptime(s[:19], fmt)
|
||||
except Exception:
|
||||
continue
|
||||
return None
|
||||
|
||||
|
||||
def claim_rs_level_notify(
|
||||
conn: Any,
|
||||
monitor_id: int,
|
||||
notify_index: int,
|
||||
direction: str,
|
||||
notified_at: str,
|
||||
bar_ts: Optional[int],
|
||||
*,
|
||||
prior_count: Optional[int] = None,
|
||||
) -> bool:
|
||||
"""
|
||||
原子占位:仅在 notification_count 仍为 prior_count 时推进到 notify_index。
|
||||
须在发送企业微信之前调用并 commit,避免 (2/3) 重复刷屏。
|
||||
"""
|
||||
prior = int(prior_count if prior_count is not None else notify_index - 1)
|
||||
if prior < 0 or notify_index != prior + 1:
|
||||
return False
|
||||
bar_val: Optional[int] = None
|
||||
if bar_ts is not None:
|
||||
try:
|
||||
bar_val = int(bar_ts)
|
||||
except (TypeError, ValueError):
|
||||
bar_val = None
|
||||
cur = conn.execute(
|
||||
"UPDATE key_monitors SET notification_count=?, direction=?, last_notified_at=?, last_rs_bar_ts=? "
|
||||
"WHERE id=? AND COALESCE(notification_count,0)=?",
|
||||
(notify_index, direction, notified_at, bar_val, int(monitor_id), prior),
|
||||
)
|
||||
return int(cur.rowcount or 0) > 0
|
||||
|
||||
|
||||
def parse_last_rs_bar_ts(row: Any) -> Optional[int]:
|
||||
if row is None:
|
||||
return None
|
||||
@@ -142,7 +192,7 @@ def run_rs_level_alert_tick(
|
||||
) -> Optional[dict[str, Any]]:
|
||||
"""
|
||||
判定本轮回合是否应推送阻力/支撑提醒。
|
||||
首条:仅在新 5m 闭合 K 越线时触发,并 need_claim_first 防 3 秒轮询刷屏。
|
||||
首条:仅在新闭合 K 越线时触发;发送前须 claim_rs_level_notify 占位防轮询/多进程重复。
|
||||
"""
|
||||
up, lo = float(row["upper"]), float(row["lower"])
|
||||
if up <= lo:
|
||||
@@ -170,10 +220,10 @@ def run_rs_level_alert_tick(
|
||||
return {
|
||||
"break_info": br,
|
||||
"notify_index": 1,
|
||||
"prior_count": 0,
|
||||
"notify_max": max_n,
|
||||
"interval_min": interval,
|
||||
"bar_ts": bar_ts_i,
|
||||
"need_claim_first": True,
|
||||
}
|
||||
|
||||
if not notify_interval_elapsed(row["last_notified_at"], interval, now_dt):
|
||||
@@ -184,10 +234,10 @@ def run_rs_level_alert_tick(
|
||||
return {
|
||||
"break_info": br,
|
||||
"notify_index": count + 1,
|
||||
"prior_count": count,
|
||||
"notify_max": max_n,
|
||||
"interval_min": interval,
|
||||
"bar_ts": bar_ts_i,
|
||||
"need_claim_first": False,
|
||||
}
|
||||
|
||||
|
||||
@@ -223,13 +273,10 @@ def notify_interval_elapsed(
|
||||
now_dt: datetime,
|
||||
) -> bool:
|
||||
if not last_notified_at:
|
||||
return True
|
||||
try:
|
||||
last_dt = datetime.fromisoformat(str(last_notified_at).replace("Z", "+00:00"))
|
||||
if last_dt.tzinfo is not None:
|
||||
last_dt = last_dt.replace(tzinfo=None)
|
||||
except Exception:
|
||||
return True
|
||||
return False
|
||||
last_dt = _parse_notify_datetime(last_notified_at)
|
||||
if last_dt is None:
|
||||
return False
|
||||
return (now_dt - last_dt).total_seconds() >= max(1, int(interval_min)) * 60
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user