diff --git a/scripts/mofin_collect.py b/scripts/mofin_collect.py index a8d1aff9..f1956b08 100644 --- a/scripts/mofin_collect.py +++ b/scripts/mofin_collect.py @@ -2,13 +2,14 @@ """mofin_collect.py — MoFin 数据采集链 每轮盯盘 cron 前运行,顺序执行: + 0. 策略新鲜度强制检查(代码级约束:推荐前必须先检查策略是否最新) 1. market_watch — 拉90个行业板块数据(9:30前跳过,市场未开) 2. trend_detector — 检测17种信号(依赖板块数据,同跳) 3. mofin_news — 搜新闻+小果分析 4. stock_quote — 所有持仓最新行情(CRITICAL: LLM唯一价格源) """ -import subprocess, sys, time +import subprocess, sys, time, json from pathlib import Path from datetime import datetime @@ -17,6 +18,80 @@ BASE = Path(__file__).parent.parent if "hermes" in str(Path(__file__).resolve()) now = datetime.now() market_open = (now.hour >= 9 and now.minute >= 30) or now.hour >= 10 +# ── 步骤0: 策略新鲜度强制检查(代码级约束)── +# 在LLM看到任何数据前,先确保所有持仓策略是新鲜的 +# 策略过期(>4h未更新) → 创建TODO触发重评 + 注入STALE标记到LLM context +# LLM看到STALE标记:禁止对该股给出任何操作建议 +print("--- strategy_freshness_check ---", flush=True) +try: + sys.path.insert(0, str(BASE)) + from mofin_db import get_conn + conn = get_conn() + cur = conn.cursor() + # 读所有活跃持仓及其最新策略更新时间 + rows = cur.execute(""" + SELECT h.code, h.name, h.price, h.position_pct, + hs.stop_loss, hs.take_profit, hs.entry_low, hs.entry_high, + hs.created_at, hs.action + FROM holdings h + LEFT JOIN ( + SELECT code, stop_loss, take_profit, entry_low, entry_high, + created_at, action, + ROW_NUMBER() OVER (PARTITION BY code ORDER BY id DESC) AS rn + FROM holding_strategies + ) hs ON h.code = hs.code AND hs.rn = 1 + WHERE h.is_active = 1 + ORDER BY h.code + """).fetchall() + + stale_count = 0 + fresh_count = 0 + no_strategy_count = 0 + + for r in rows: + code = r["code"] + name = r["name"] + last_update = r["created_at"] + has_strategy = last_update is not None + + if has_strategy: + try: + last_dt = datetime.fromisoformat(last_update) + hours_stale = (now - last_dt).total_seconds() / 3600 + except: + hours_stale = 999 + + if hours_stale > 4: + # 策略过期→创建TODO触发重评 + stale_count += 1 + todo_sql = """ + INSERT OR IGNORE INTO todos + (title, code, fix_action, source, priority, status, created_at) + VALUES (?, ?, 'reassess_strategy', 'freshness_check', 'high', 'pending', ?) + """ + cur.execute(todo_sql, ( + f"策略过期需重评: {code} {name} ({hours_stale:.0f}h未更新)", + code, + now.isoformat() + )) + print(f" ⚠️ STALE {code} {name}: {hours_stale:.0f}h未更新 → 已创建重评TODO", flush=True) + else: + fresh_count += 1 + print(f" ✅ FRESH {code} {name}: {hours_stale:.1f}h前更新", flush=True) + else: + no_strategy_count += 1 + print(f" ❌ NO_STRATEGY {code} {name}: 无策略记录", flush=True) + + conn.commit() + conn.close() + + total = len(rows) + print(f"策略检查完成: {total}只持仓, {fresh_count}新鲜, {stale_count}过期(已创TODO), {no_strategy_count}无策略", flush=True) + if stale_count > 0 or no_strategy_count > 0: + print(f"⚠️ LLM注意: 有{stale_count + no_strategy_count}只持仓策略不是最新状态, 禁止对这些股给出操作建议", flush=True) +except Exception as e: + print(f"WARN: strategy_freshness_check跳过 ({e})", flush=True) + # 步骤1-3: 行业/新闻数据 SCRIPTS = [] if market_open: