diff --git a/deploy/profile-scripts/per_stock_reassess.py b/deploy/profile-scripts/per_stock_reassess.py index bd3a3354..62def05e 100644 --- a/deploy/profile-scripts/per_stock_reassess.py +++ b/deploy/profile-scripts/per_stock_reassess.py @@ -390,179 +390,28 @@ def main(): if _fa and _fa[0]: print(f" ✅ full_analysis已写入({len(_fa[0])}字)") else: print(f" ⚠️ full_analysis为空") _v.close() - # LLM生成完整12维分析 - _macro_desc = "" - _pe_val = "" - _pb_val = "" + # LLM生成完整12维分析(2026-07-23:统一走 batch 的 collect_data+build_prompt。 + # 单一 prompt 源头,根治双 prompt 漂移——per_stock 曾缺技术位锚/持仓上下文/参数自检) + import sys as _sys2 + if '/home/hmo/MoFin/deploy/profile-scripts' not in _sys2.path: + _sys2.path.insert(0, '/home/hmo/MoFin/deploy/profile-scripts') try: - _mdb = __import__('sqlite3').connect("/home/hmo/MoFin/data/mofin.db") - _mr = _mdb.execute("SELECT structure FROM macro_context_log ORDER BY id DESC LIMIT 1").fetchone() - if _mr and _mr[0]: - _st = __import__('json').loads(_mr[0]) - _macro_desc = _st.get("description", "") - _mood = _mr[1] if len(_mr) > 1 else "" - if _mood: _macro_desc += f" 情绪={_mood}" - # 基本面从腾讯API - _p = "sh" if str(code).startswith(("6","9")) else "sz" - _cr = __import__('subprocess').run(["curl","-s",f"http://qt.gtimg.cn/q={_p}{code}"], capture_output=True, timeout=10) - _ct = _cr.stdout.decode("gbk", errors="ignore").split("~") - if len(_ct) > 39 and _ct[39]: _pe_val = f"PE={_ct[39]}" - if len(_ct) > 44 and _ct[44]: _pb_val = f"PB≈{float(_ct[44])/10000:.1f}亿" - _mdb.close() - except: - pass - - # 拉取资金流数据 - _flow_note = "暂无资金流数据" - try: - _fdb = __import__('sqlite3').connect("/home/hmo/MoFin/data/mofin.db") - _fr = _fdb.execute("SELECT cache_json FROM capital_flow_cache ORDER BY id DESC LIMIT 1").fetchone() - if _fr and _fr[0]: - _fc = __import__('json').loads(_fr[0]) - _s = _fc.get("stocks", {}).get(code, {}) - if _s and _s.get("analysis"): - _a = _s["analysis"] - _flow_note = f"净流入{_a.get('net_flow',0):.0f}万 主力{_a.get('main_force',0):.0f}万 趋势{_a.get('trend','中性')}" - _fdb.close() - except: - pass - - # 拉取近期消息面(不限情绪标签) - _news_note = "暂无近期消息" - try: - _ndb = __import__('sqlite3').connect("/home/hmo/MoFin/data/mofin.db") - _nr2 = _ndb.execute( - "SELECT summary, overall_sentiment, created_at FROM signal_news " - "WHERE searched_stocks LIKE ? OR sector LIKE ? " - "ORDER BY id DESC LIMIT 3", - (f'%{code}%', f'%{entry.get("name","")[:4]}%') - ).fetchall() - if not _nr2: - _nr2 = _ndb.execute( - "SELECT summary, overall_sentiment, created_at FROM signal_news " - "ORDER BY id DESC LIMIT 3").fetchall() - if _nr2: - _news_note = " | ".join([f"{r[2][:10]} [{r[1] or '未标注'}] {r[0][:40]}" for r in _nr2]) - _ndb.close() - except: - pass - - # ── 拉取已有策略全文 + 最近变更 ── - _existing_full_analysis = "" - _existing_changelog_text = "无变更记录" - try: - _edb = __import__('sqlite3').connect("/home/hmo/MoFin/data/mofin.db") - _er = _edb.execute( - "SELECT full_analysis, changelog_json FROM holding_strategies " - "WHERE code=? AND status='active'", (code,) - ).fetchone() - if _er: - _existing_full_analysis = _er[0] or "" - _cl_raw = _er[1] or "" - if _cl_raw: - _cl = __import__('json').loads(_cl_raw) if isinstance(_cl_raw, str) else _cl_raw - if isinstance(_cl, list) and _cl: - _recent = _cl[-3:] - _existing_changelog_text = "\n".join( - [f" [{c.get('timestamp','?')}] {c.get('action','?')}: {c.get('reason','')}"[:120] - for c in reversed(_recent)] - ) - _edb.close() - except: - pass - - # ── 确定性技术位锚(与 batch 同源,2026-07-23:双 prompt 曾漂移导致per_stock无锚)── - _ta_sec_ps = "【技术位锚】本次计算不可用,仍须满足:止损<区间下沿<区间上沿<止盈。" - try: - import technical_analysis as _ta_mod - _ta_ps = _ta_mod.full_analysis(code) - if _ta_ps and "error" not in _ta_ps: - _sr_ps = _ta_ps.get("support_resistance", {}) or {} - if _sr_ps.get("weak_support"): - _ta_sec_ps = f"""【技术位锚】(系统基于K线确定性计算,客观事实,非估计值) -强撑={_sr_ps.get('strong_support')} 弱撑={_sr_ps.get('weak_support')} 枢轴={_sr_ps.get('pivot')} -弱压={_sr_ps.get('weak_resist')} 强压={_sr_ps.get('strong_resist')} 有效区间={_sr_ps.get('effective_range')} -⚠️ 参数锚定纪律:买入区下沿参考弱撑/强撑;止损必须严格低于买入区下沿(放弱撑下方1-3%或强撑附近,严禁止损≥区间下沿);止盈参考弱压/强压;偏离须在【修改点及理由】明确说明。""" - except Exception: - pass - - _prompt = f"""你是一个资深股票分析师。请对股票{code}评估现有策略是否仍然有效,并输出完整的新策略。 - -╔══════════════════════════════════════════════╗ -║ 📋 第一步:审阅原策略 ║ -╚══════════════════════════════════════════════╝ - -【原策略全文】(上次完整分析): -{_existing_full_analysis or '暂无完整策略分析'} - -【当前策略参数】: - 价格={price} 信号={result.get("timing_signal") or entry.get("timing_signal","")} - 买入区间={entry.get("entry_low",0)}~{entry.get("entry_high",0)} - 止损={entry.get("stop_loss",0)} 止盈={entry.get("take_profit",0)} - RR={result.get("rr_ratio", entry.get("rr_ratio", 0))} - 策略={result.get("action") or entry.get("action","")} - 行业={(result.get("sector_context") or entry.get("sector_context",""))[:50]}(当日实时) - 技术={(result.get("tech_snapshot") or entry.get("tech_snapshot",""))[:200]}(MA=5/10/20/60日 支撑阻力=近20日 量价=当日+近5日趋势) - -【最近变更记录】: -{_existing_changelog_text} - -╔══════════════════════════════════════════════╗ -║ 📊 第二步:12维矩阵交叉分析 ║ -╚══════════════════════════════════════════════╝ - -⚠️ 重要:12个维度必须交叉对比,找出矛盾/共振点,给出综合判断。 - -{_ta_sec_ps} - -当前实时数据(每条标注时间窗口,禁止使用模型训练数据): -大盘={_macro_desc or "震荡"}(当日实时) | PE/市值={_pe_val} {_pb_val}(最新财报) -资金流={_flow_note}(近5日累计) -消息面={_news_note}(最近3条,自动标注抓取时间) - -╔══════════════════════════════════════════════╗ -║ 📝 第三步:决策输出 ║ -╚══════════════════════════════════════════════╝ - -请严格按以下顺序输出: - -【维持或修改】判断当前策略是否仍然有效,回答「维持」或「修改」。 - -【修改点及理由】(如果维持,写「无需修改」;如果修改,逐条列出): - - 修改什么参数/方向 - - 理由(引用具体维度矛盾或共振) - -【最终新策略】(完整策略全文,self-contained,可直接存入DB) - -【交叉分析】哪些维度矛盾/共振,关键信号 -① 大盘×基本面 ② 大盘×消息面 ③ 大盘×技术面 ④ 大盘×资金面 -⑤ 行业×基本面 ⑥ 行业×消息面 ⑦ 行业×技术面 ⑧ 行业×资金面 -⑨ 个股×基本面 ⑩ 个股×消息面 ⑪ 个股×技术面 ⑫ 个股×资金面 - -最后必须输出: -【综合结论】(买入/关注/观望/卖出) -【操作建议】 -【买入区间】最低价~最高价(锚定技术位:下沿参考弱撑/强撑,上沿参考枢轴/弱压) -【建议止损】(必须严格低于买入区下沿,放弱撑下方1-3%或强撑附近) -【建议止盈】(参考弱压/强压) -【参数自检】一行,格式"止损X < 区下沿Y < 区上沿Z < 止盈W:通过/不通过+原因" -【建议仓位】⚠️不可省略,非"买入"时写"不新建仓" - -⚠️ 输出纪律(必须遵守): -1. 直接以【维持或修改】开头,禁止任何寒暄、开场白、分隔线 -2. 禁止输出 或任何 XML/JSON/代码块 -3. 所有【】节标题一个都不能少 -4. 止损<区间下沿<区间上沿<止盈,违反任一条=输出作废重想""" + from batch_reassess import collect_data as _cd, build_prompt as _bp + _prompt = _bp(_cd(code)) + except Exception as _pe: + print(f" ⚠️ 统一prompt构建失败: {_pe}", flush=True) + _prompt = None _full_analysis_text = None - try: - _llm_result = call_llm(_prompt, max_tokens=4096, timeout=150, retries=1, backoff=20) - if _llm_result["ok"]: - _full_analysis_text = _llm_result["content"] - print(f" ✅ LLM12维分析完成({len(_full_analysis_text)}字, {_llm_result['elapsed']:.1f}s)", flush=True) - else: - print(f" ❌ LLM12维分析失败({_llm_result['attempts']}次): {_llm_result['error'][:200]}", flush=True) - except Exception as _e: - print(f" ❌ LLM12维分析异常: {_e}", flush=True) + if _prompt: + try: + _llm_result = call_llm(_prompt, max_tokens=4096, timeout=150, retries=1, backoff=20) + if _llm_result["ok"]: + _full_analysis_text = _llm_result["content"] + print(f" ✅ LLM12维分析完成({len(_full_analysis_text)}字, {_llm_result['elapsed']:.1f}s)", flush=True) + else: + print(f" ❌ LLM12维分析失败({_llm_result['attempts']}次): {_llm_result['error'][:200]}", flush=True) + except Exception as _e: + print(f" ❌ LLM12维分析异常: {_e}", flush=True) # ── 保存到DB(覆写前先快照)── _fa_conn = __import__('sqlite3').connect("/home/hmo/MoFin/data/mofin.db")