diff --git a/mofin_db.py b/mofin_db.py index 56ddc30d..3fe479a9 100644 --- a/mofin_db.py +++ b/mofin_db.py @@ -1309,6 +1309,41 @@ def flush_rec_digest(max_items=5): if not items: return 0 _os.remove(qf) + + # ── 快照回库校验(2026-07-24 老爸:推荐和XMPP同步)── + # 队列是打标瞬间的快照;flush 前回库读实时 信号/RR/tag, + # 信号降级为弱信号或RR跌破2.0的条目直接丢弃——XMPP说的必须和盯盘一致。 + import sqlite3 as _sq0 + _vconn = _sq0.connect("/home/hmo/MoFin/data/mofin.db") + _WEAK = ("信号不充分", "关注", "弱势持有", "观望", "持有", "") + _live = [] + for it in items: + r = _vconn.execute( + "SELECT timing_signal, rr_ratio, tag FROM holding_strategies WHERE code=? AND status='active'", + (it['code'],)).fetchone() + if not r: + print(f" [REC] {it['code']} 已不在库,丢弃", flush=True) + continue + cur_sig, cur_rr, cur_tag = r[0] or "", r[1] or 0, r[2] or "" + if cur_tag != 'current_recommend': + print(f" [REC] {it['code']} tag已撤销({cur_tag}),丢弃", flush=True) + continue + if it.get('signal') in ("买入", "可买入", "可加仓"): + if cur_sig in _WEAK: + print(f" [REC] {it['code']} 信号降级为'{cur_sig}',丢弃", flush=True) + continue + if cur_rr < 2.0: + print(f" [REC] {it['code']} 实时RR={cur_rr}<2.0,丢弃", flush=True) + continue + it['signal'] = cur_sig # 用实时信号发 + it['rr'] = cur_rr + _live.append(it) + _vconn.close() + items = _live + if not items: + print(" [REC] 快照校验后无有效推荐,不发digest", flush=True) + return 0 + _SELL_SIGS = ("卖出", "止盈") # 卖出/止盈是释放现金的操作,不占买入预算,单独一组排最前 sells = [x for x in items if x.get('signal') in _SELL_SIGS] @@ -1320,6 +1355,8 @@ def flush_rec_digest(max_items=5): # ── 现金预算(决定操盘建议 + 换仓策略):只对买入项计算,卖出不占预算 ── cash_note = "" rotation_note = "" + buys = [] + queued = [] try: conn = _sq.connect("/home/hmo/MoFin/data/mofin.db") conn.row_factory = _sq.Row @@ -1334,7 +1371,7 @@ def flush_rec_digest(max_items=5): import re as _re m = _re.search(r'(\d+(?:\.\d+)?)\s*%', it.get('position') or '') pct = float(m.group(1)) if m else 8.0 - if (it.get('rr') or 0) >= 1.5 and cum + pct <= budget_pct + 1e-9: + if (it.get('rr') or 0) >= 2.0 and cum + pct <= budget_pct + 1e-9: buys.append((it, pct)) cum += pct else: @@ -1374,12 +1411,15 @@ def flush_rec_digest(max_items=5): print(f" [REC] 换仓计算异常: {_re}", flush=True) lines = [f"📈 新增推荐 {len(items)} 只(按RR排序):"] + # 与盯盘推荐区一致的 可执行/排队 徽章(2026-07-24 老爸:推荐和XMPP同步) + _exec_codes = {b[0]['code'] for b in buys} | {s['code'] for s in sells} for i, it in enumerate(top): _rr_mid = it.get('rr') or 0 _rr_lo, _rr_hi = it.get('rr_low') or 0, it.get('rr_high') or 0 _rr_txt = (f"RR={_rr_mid}({_rr_lo}~{_rr_hi})" if _rr_lo and _rr_hi and _rr_lo != _rr_hi else f"RR={_rr_mid}") - lines.append(f"• {it.get('name') or it['code']}({it['code']}) {it['signal']}" + _badge = "💰可执行" if it['code'] in _exec_codes else "⏳排队" + lines.append(f"• {_badge} {it.get('name') or it['code']}({it['code']}) {it['signal']}" f" 区{it.get('entry_low') or '—'}~{it.get('entry_high') or '—'}" f" 损{it.get('stop_loss') or '—'} 盈{it.get('take_profit') or '—'}" f" {_rr_txt} 仓位{it.get('position') or '—'}")