feat: 信号权威层级+推荐转场推送统一+🔔分级修正

1. 权威层级: 新鲜(<20h)12维动作级信号, 技术路径(regenerate_all/
   price_monitor)无权降级为关注/信号不充分/持有——只有LLM路径或
   风险信号(卖出/止盈)可覆盖。根治推荐区'出现-消失'抖动
2. 推荐转场推送统一收拢到 mofin_db.push_recommend_alert:
   tag新转为current_recommend时推送(全路径), 含质量门禁;
   移除batch_reassess内联推送(防双重告警)
3. price_monitor 🔔分级: 只有持仓股的卖出/止盈才ACTION直推,
   买入/加仓机会一律进INFO摘要(老爸: 这种消息不要再发了)
This commit is contained in:
hmo
2026-07-22 09:48:58 +08:00
parent a42e3a16e5
commit 8c2226e2b4
3 changed files with 86 additions and 49 deletions
+2 -40
View File
@@ -376,49 +376,11 @@ def save_result(code, full_text, parsed):
# ── 推荐操作 tag 同步(与 XMPP 动作级信号同源)──
sync_recommend_tag(conn, code, parsed.get("signal", ""))
# 买入信号→推XMPP通知(在conn close前执行)——推送质量门禁:
# 价格必须>0(live_prices实时价)、区间有效(下沿<上沿<下沿x3)、现价不超过上沿5%、
# 损<下沿、盈>上沿、损在(0.5x~1.0x)现价内。任何一项不过 → 不推,只记日志。
if parsed.get("signal") == "买入":
try:
_nr = conn.execute("SELECT name FROM holding_strategies WHERE code=? AND status='active'", (code,)).fetchone()
_lp = conn.execute("SELECT price FROM live_prices WHERE code=?", (code,)).fetchone()
_name = _nr[0] if _nr else code
_p = _lp[0] if _lp and _lp[0] else 0
_el = parsed.get("entry_low", 0)
_eh = parsed.get("entry_high", 0)
_sl = parsed.get("stop_loss", 0)
_tp = parsed.get("take_profit", 0)
_pos = parsed.get("position", "")
_ok, _why = _validate_buy_alert(_p, _el, _eh, _sl, _tp)
if _ok:
_msg = f"📈 {_name}({code}) 价{_p}→12维分析生成买入信号!区间{_el}~{_eh}{_sl}{_tp} 仓位{_pos}"
from alert_helper import notify as _notify, ACTION as _ACT
_notify("买入信号", _msg, _ACT)
print(f" \U0001f4e8 XMPP推送成功: {_msg[:60]}")
else:
print(f" ⚠️ 买入信号未过推送门禁({_why}),仅记日志不推送", flush=True)
except Exception as _e:
print(f" \u26a0\ufe0f XMPP推送失败: {_e}")
# 买入信号推送已统一收拢到 sync_recommend_tag 的转场推送(防双重告警)。
# 本路径只负责写库+tag,推送由 mofin_db.push_recommend_alert 在 tag 转场时触发。
conn.close()
def _validate_buy_alert(price, el, eh, sl, tp):
"""买入信号推送门禁(垃圾信号不发)。
返回 (ok, reason)"""
if not price or price <= 0:
return False, f"无实时价格({price})"
if not (el > 0 and eh > el and eh < el * 3):
return False, f"区间无效({el}~{eh})"
if price > eh * 1.05:
return False, f"现价{price}高于区间上沿{eh}超5%(追高信号不推)"
if not (sl > 0 and sl < el and price * 0.5 <= sl <= price):
return False, f"止损{sl}不合理(需0.5x~1.0x现价且<下沿{el})"
if not (tp > eh and tp > sl):
return False, f"止盈{tp}需>上沿{eh}且>止损{sl}"
return True, ""
def process_stock(code, force_today=False):
"""处理单只股票"""
print(f"\n{'='*50}")
+15 -6
View File
@@ -541,9 +541,13 @@ def run_once(round_label=""):
buy_lo = d.get("entry_low", 0)
buy_hi = d.get("entry_high", 0)
rr = result.get("rr_ratio", 0)
if _can_push(code, "stop_loss"):
msg = f"🔔 {name}({code}) 价{price}→触发操作区间{max(buy_lo,0):.2f}~{buy_hi:.2f},已触发重评|RR={rr}"
_push_action("操作信号", msg)
# 分级(老爸规则:只有持仓风控动作才ACTION直推;买入/加仓机会进摘要)
if timing_signal in ("卖出","止盈") and (d.get("shares") or 0) > 0:
if _can_push(code, "stop_loss"):
msg = f"🔔 {name}({code}) 价{price}→触发操作区间{max(buy_lo,0):.2f}~{buy_hi:.2f},已触发重评|RR={rr}"
_push_action("操作信号", msg)
else:
_zone_entries.append(f"{name}({code}) {price}→入区+重评{timing_signal}|RR={rr}")
outputs.append(f" 📨 止损重评→已推送Dad: {action}")
except Exception as e:
outputs.append(f" ⚠️ 止损重评失败: {e}")
@@ -581,10 +585,15 @@ def run_once(round_label=""):
zone_desc = f"操作区间{lo}~{hi}"
if "买入" in timing_signal or "加仓" in timing_signal or timing_signal in ("卖出","止盈"):
rr = result.get("rr_ratio", 0)
if _can_push(code, key):
msg = f"🔔 {name}({code}) 价{price}→触发{zone_desc},已触发重评|RR={rr}"
_push_action("操作信号", msg)
# 分级(老爸规则:只有持仓风控动作才ACTION直推;买入/加仓机会进摘要)
if timing_signal in ("卖出","止盈") and (d.get("shares") or 0) > 0:
if _can_push(code, key):
msg = f"🔔 {name}({code}) 价{price}→触发{zone_desc},已触发重评|RR={rr}"
_push_action("操作信号", msg)
outputs.append(f" 📨 区间触发重评→已推送Dad: {action}")
else:
_zone_entries.append(f"{name}({code}) {price}{zone_desc}+重评{timing_signal}|RR={rr}")
outputs.append(f" 📋 机会记入摘要: {timing_signal} RR={rr}")
else:
reason = f"重评结果:{timing_signal},不构成操作建议"
outputs.append(f" 📋 本地日志(不推): {reason}")