Initial commit: stock analysis backend and prototype UI.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
67
backend/alerts.py
Normal file
67
backend/alerts.py
Normal file
@@ -0,0 +1,67 @@
|
||||
"""预警引擎:拉取实时价格,评估规则,触发即生成站内事件。"""
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime as dt
|
||||
|
||||
from sqlalchemy import select
|
||||
|
||||
import akshare_service as svc
|
||||
import notifier
|
||||
from db import get_session
|
||||
from models import AlertRule, AlertEvent
|
||||
|
||||
KIND_LABEL = {
|
||||
"price_above": "价格突破",
|
||||
"price_below": "价格跌破",
|
||||
"pct_above": "涨幅达到",
|
||||
"pct_below": "跌幅达到",
|
||||
}
|
||||
|
||||
|
||||
def _hit(kind, threshold, q):
|
||||
price, pct = q["price"], q["pct"]
|
||||
if kind == "price_above":
|
||||
return price >= threshold, price
|
||||
if kind == "price_below":
|
||||
return price <= threshold, price
|
||||
if kind == "pct_above":
|
||||
return pct >= threshold, pct
|
||||
if kind == "pct_below":
|
||||
return pct <= -abs(threshold), pct
|
||||
return False, price
|
||||
|
||||
|
||||
def check_alerts():
|
||||
with get_session() as s:
|
||||
rules = s.execute(select(AlertRule).where(AlertRule.status == "active")).scalars().all()
|
||||
if not rules:
|
||||
return {"checked": 0, "triggered": 0}
|
||||
codes = list({r.code for r in rules})
|
||||
quotes = svc.realtime_quotes(codes)
|
||||
triggered = 0
|
||||
push_msgs = []
|
||||
for r in rules:
|
||||
q = quotes.get(r.code)
|
||||
if not q:
|
||||
continue
|
||||
hit, val = _hit(r.kind, r.threshold, q)
|
||||
r.last_value = q["price"]
|
||||
if hit:
|
||||
unit = "" if r.kind.startswith("price") else "%"
|
||||
msg = (f"{q['name']} {KIND_LABEL.get(r.kind, r.kind)} {r.threshold}{unit}"
|
||||
f"(现价 {q['price']},{q['pct']:+.2f}%)")
|
||||
s.add(AlertEvent(rule_id=r.id, code=r.code, name=q["name"], message=msg, value=val))
|
||||
r.status = "triggered"
|
||||
r.triggered_at = dt.datetime.now()
|
||||
triggered += 1
|
||||
push_msgs.append(msg)
|
||||
s.commit()
|
||||
|
||||
# 触发后向已配置渠道推送(站外)
|
||||
if push_msgs and notifier.any_enabled():
|
||||
try:
|
||||
notifier.notify("【智策预警】" + (push_msgs[0] if len(push_msgs) == 1 else f"{len(push_msgs)} 条预警触发"),
|
||||
"\n".join(push_msgs))
|
||||
except Exception:
|
||||
pass
|
||||
return {"checked": len(rules), "triggered": triggered}
|
||||
Reference in New Issue
Block a user