diff --git a/mofin_db.py b/mofin_db.py index cb18f17c..7c1750f1 100644 --- a/mofin_db.py +++ b/mofin_db.py @@ -1237,29 +1237,60 @@ def flush_rec_digest(max_items=5): items.sort(key=lambda x: x.get('rr') or 0, reverse=True) top = items[:max_items] - # ── 现金预算(决定操盘建议)── + # ── 现金预算(决定操盘建议 + 换仓策略)── cash_note = "" + rotation_note = "" try: conn = _sq.connect("/home/hmo/MoFin/data/mofin.db") + conn.row_factory = _sq.Row r = conn.execute("SELECT cash, total_assets FROM portfolio_summary WHERE id=1").fetchone() - conn.close() if r and r[1]: cash, total = r[0] or 0, r[1] budget_pct = cash / total * 100 cum = 0.0 buys = [] + queued = [] for it in top: 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 cum + pct <= budget_pct + 1e-9: - buys.append(f"{it.get('name') or it['code']}≈{pct:.0f}%") + buys.append((it, pct)) cum += pct + else: + queued.append((it, pct)) cash_note = (f"现金{cash/10000:.1f}万({budget_pct:.1f}%)|按预算本次可执行: " - + ("、".join(buys) if buys else "无(预算不足,先排队观察)") + + ("、".join(f"{b[0].get('name') or b[0]['code']}≈{b[1]:.0f}%" for b in buys) if buys else "无") + (f"(合计≈{cum:.0f}%)" if buys else "")) - except Exception: - pass + # ── 换仓策略:有排队推荐时,找可减的弱持仓来腾挪 ── + if queued: + weak = conn.execute(""" + SELECT hs.code, hs.name, hs.timing_signal, h.position_pct, h.cost, lp.price, lp.change_pct + FROM holding_strategies hs + JOIN holdings h ON hs.code = h.code AND h.is_active = 1 + LEFT JOIN live_prices lp ON hs.code = lp.code + WHERE hs.status='active' AND h.shares > 0 + AND hs.timing_signal IN ('弱势持有','观望','持有') + ORDER BY CASE hs.timing_signal WHEN '弱势持有' THEN 0 WHEN '观望' THEN 1 ELSE 2 END, + h.position_pct DESC + """).fetchall() + if weak: + need_pct = queued[0][1] + plan = [] + freed = 0.0 + for w in weak: + if freed >= need_pct: + break + plan.append(w) + freed += w["position_pct"] or 0 + q0 = queued[0][0] + rotation_note = (f"🔄 换仓建议:现金不足买 {q0.get('name') or q0['code']}(RR={q0.get('rr') or 0})→ " + f"可减 {'+'.join(f\"{w['name']}\" for w in plan)}" + f"({','.join(sorted({w['timing_signal'] for w in plan}))}," + f"腾出≈{freed:.0f}%仓位)换入") + conn.close() + except Exception as _re: + print(f" [REC] 换仓计算异常: {_re}", flush=True) lines = [f"📈 新增推荐 {len(items)} 只(按RR排序):"] for i, it in enumerate(top): @@ -1277,6 +1308,8 @@ def flush_rec_digest(max_items=5): lines.append(f"…另有 {len(items) - max_items} 只详见盯盘推荐操作区") if cash_note: lines.append("💰 " + cash_note) + if rotation_note: + lines.append(rotation_note) try: import sys as _s, os as _o2 _s.path.insert(0, '/home/hmo/MoFin/deploy/profile-scripts')