From 4477b06f1269ae32cc98dae1b8145fd5ad3da61f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=9F=A5=E5=BE=AE?= Date: Sun, 28 Jun 2026 00:15:37 +0800 Subject: [PATCH] =?UTF-8?q?Watchlist=E2=86=94Holdings=E5=8F=8C=E5=90=91?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E8=BF=81=E7=A7=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit regenerate_all() 每次全量重评时自动: ① 自选股变成持仓(>0股) → 从watchlist移除 ② 持仓清仓(股数归零) → 自动加回watchlist(有买入区才加) Dad要求的双向自动同步,避免手动清理遗忘 --- strategy_lifecycle.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/strategy_lifecycle.py b/strategy_lifecycle.py index c1adb7a..e626f0e 100644 --- a/strategy_lifecycle.py +++ b/strategy_lifecycle.py @@ -1847,6 +1847,41 @@ def regenerate_all(stdout=True): _h['change_pct'] = _old.get('change_pct', 0) existing_pf["holdings"] = _new_holdings existing_pf["updated_at"] = datetime.now().strftime('%Y-%m-%d %H:%M') + + # ── Watchlist ↔ Holdings 双向自动迁移(2026-06-27 Dad要求)── + # ① 持仓已有 → 从自选移除(买入自动清除) + wl_codes = {s.get("code") for s in wl.get("stocks", []) if s.get("code")} + pf_codes = {h.get("code") for h in _new_holdings if h.get("code") and h.get("shares", 0) > 0} + removed_from_wl = [] + for h_code in wl_codes & pf_codes: + # 持仓>0且量够 → 自选移除 + wl["stocks"] = [s for s in wl.get("stocks", []) if s.get("code") != h_code] + removed_from_wl.append(h_code) + if removed_from_wl and stdout: + print(f" 自选→持仓自动移除: {', '.join(removed_from_wl)}") + + # ② 清仓/卖光 → 加回自选(只要仍有关注价值) + added_to_wl = [] + old_pf_codes = {_h.get("code") for _h in existing_pf.get("holdings", []) if _h.get("code")} + sold_codes = old_pf_codes - pf_codes # 曾持仓但现在没有(或不在了) + for sc in sold_codes: + # 已有自选就不重复加 + if sc in wl_codes: + continue + # 从现有decisions看是否有关注价值 + for d in decisions: + if d.get("code") == sc and d.get("entry_low") and d.get("entry_high"): + wl["stocks"].append({ + "code": sc, "name": d.get("name", sc), + "entry_low": d.get("entry_low"), "entry_high": d.get("entry_high"), + "stop_loss": d.get("stop_loss", 0), + "analysis": {"action": d.get("action", ""), "tech_snapshot": d.get("tech_snapshot", "")} + }) + added_to_wl.append(sc) + break + if added_to_wl and stdout: + print(f" 清仓→自选自动加入: {', '.join(added_to_wl)}") + json.dump(existing_pf, open(PORTFOLIO_PATH, "w"), ensure_ascii=False, indent=2) json.dump(wl, open(WATCHLIST_PATH, "w"), ensure_ascii=False, indent=2)