diff --git a/deploy/profile-scripts/clean_watchlist.py b/deploy/profile-scripts/clean_watchlist.py index cd08ae18..3635aa91 100644 --- a/deploy/profile-scripts/clean_watchlist.py +++ b/deploy/profile-scripts/clean_watchlist.py @@ -1,18 +1,18 @@ #!/usr/bin/env python3 -"""Remove held stocks from watchlist""" +"""clean_watchlist.py - 自选池进出管理 -import json, os, sys +逻辑: +1. 持仓里有 + 自选里也有 → 从自选移除 +2. 曾持仓但已清仓 → 加回自选 -# 确保 MoFin 根目录在模块搜索路径中(兼容 cron 环境) +被 import_holding_xls 调用(持仓变动的副作用),不再独立 cron 触发。 +""" +import os, sys sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from mo_data import read_portfolio, read_decisions, read_watchlist from mofin_db import get_conn, write_watchlist_stock, write_holding_strategy -WL = "/home/hmo/web-dashboard/data/watchlist.json" # 路径保留用于历史备份兼容,数据实际走DB -# 决策数据全部从DB读取,json文件已移除 -DEC = "/home/hmo/web-dashboard/data/decisions.json" # 保留常量但不再使用,防止引用报错 - def main(): holding_codes = set() @@ -21,112 +21,81 @@ def main(): c = h.get("code", "") if c: holding_codes.add(c) - print(f"持仓 codes: {sorted(holding_codes)}") - # Load watchlist wl = read_watchlist() stocks = wl.get("stocks", []) before = len(stocks) - # Remove held stocks new_stocks = [s for s in stocks if s.get("code") not in holding_codes] removed = [s for s in stocks if s.get("code") in holding_codes] - after = len(new_stocks) wl["stocks"] = new_stocks - # Backup — DB 版,不再碰JSON文件 - # DB 写入 conn = get_conn() for s in wl.get("stocks", []): - s.setdefault("currency", "CNY") - write_watchlist_stock(conn, s) + s.setdefault("currency", "CNY") + write_watchlist_stock(conn, s) conn.close() - # [migrated to DB] — cold backup removed - # json.dump(wl, open(WL, "w"), indent=2, ensure_ascii=False) - print(f"\n自选: {before} → {after} 只") + print(f"\n自选: {before} -> {after} 只") print(f"移除 {len(removed)} 只:") for r in removed: - print(f" {r['code']} {r.get('name','')}") + print(f" {r['code']} {r.get('name', '')}") - # Also update decisions.json - set them to "managed_by_holdings" or remove watchlist-only fields dec = read_decisions() dec_changed = 0 for d in dec.get("decisions", []): - code = d.get("code", "") - if code in [r["code"] for r in removed]: - # Remove watchlist-specific tags - if d.get("tag") == "watchlist": - d["tag"] = "managed_by_holdings" + code = d.get("code", "") + if code in [r["code"] for r in removed]: + if d.get("tag") == "watchlist": + d["tag"] = "managed_by_holdings" dec_changed += 1 - - if dec_changed: - # DB 写入(不再碰JSON文件) - conn = get_conn() - for d in dec.get("decisions", []): - write_holding_strategy(conn, d.get("code", ""), d.get("name", ""), d) - conn.close() - # [migrated to DB] — cold backup removed - # json.dump(dec, open(DEC, "w"), indent=2, ensure_ascii=False) - print(f"\ndecisions数据: {dec_changed} 只更新标签") - else: - print(f"\ndecisions数据: 无需更新") - # ── 反过程:清仓股自动加回自选 ── - # 找出曾持仓但现已不在 portfolio 的股票 - prev_held = {} # code → last_execution info + if dec_changed: + conn = get_conn() + for d in dec.get("decisions", []): + write_holding_strategy(conn, d.get("code", ""), d.get("name", ""), d) + conn.close() + print(f"\ndecisions: {dec_changed} 只更新标签") + + prev_held = {} for d in dec.get("decisions", []): - code = d.get("code", "") - exec_info = d.get("execution", {}) - if exec_info and exec_info.get("status") in ("executing", "partial_exit"): - # 当前仍持仓但不在 portfolio?说明 portfolio 数据落后,跳过 - pass - elif exec_info and exec_info.get("status") in ("sold", "closed") and code not in holding_codes: + code = d.get("code", "") + exec_info = d.get("execution", {}) + if exec_info and exec_info.get("status") in ("sold", "closed") and code not in holding_codes: prev_held[code] = { - "name": d.get("name", code), - "entry_low": d.get("entry_low", 0), - "entry_high": d.get("entry_high", 0), - } + "name": d.get("name", code), + "entry_low": d.get("entry_low", 0), + "entry_high": d.get("entry_high", 0), + } if prev_held: - wl_stock_codes = set(s.get("code", "") for s in new_stocks) - added = 0 - for code, info in sorted(prev_held.items()): - if code not in wl_stock_codes and code not in holding_codes: - # 确保买入区有值 - entry_low = info.get("entry_low", 0) or 0 - entry_high = info.get("entry_high", 0) or 0 + wl_stock_codes = set(s.get("code", "") for s in new_stocks) + added = 0 + for code, info in sorted(prev_held.items()): + if code not in wl_stock_codes and code not in holding_codes: new_stocks.append({ - "code": code, - "name": info["name"], - "entry_low": entry_low, - "entry_high": entry_high, - "stop_loss": 0, - "tag": "recovered_from_sold", - }) - added += 1 - print(f" ← 已清仓→加回自选: {code} {info['name']}") - if added: - wl["stocks"] = new_stocks - # DB 写入 + "code": code, + "name": info["name"], + "entry_low": info.get("entry_low", 0) or 0, + "entry_high": info.get("entry_high", 0) or 0, + "stop_loss": 0, + "tag": "recovered_from_sold", + }) + added += 1 + print(f" <- 已清仓加回自选: {code} {info['name']}") + if added: + wl["stocks"] = new_stocks conn = get_conn() - for s in wl.get("stocks", []): - s.setdefault("currency", "CNY") - write_watchlist_stock(conn, s) + for s in wl.get("stocks", []): + s.setdefault("currency", "CNY") + write_watchlist_stock(conn, s) conn.close() - # [migrated to DB] — cold backup removed - # json.dump(wl, open(WL, "w"), indent=2, ensure_ascii=False) print(f"\n反过程: {added} 只清仓股已加回自选") - else: - print("\n反过程: 无清仓股需加回") - else: - print("\n反过程: 无已清仓记录") print("\nDONE") - - if __name__ == "__main__": - main() \ No newline at end of file +if __name__ == "__main__": + main()