diff --git a/archive/20260728-dedup/candidate_filter.py b/archive/20260728-dedup/candidate_filter.py new file mode 100644 index 00000000..4a48c068 --- /dev/null +++ b/archive/20260728-dedup/candidate_filter.py @@ -0,0 +1,354 @@ +#!/usr/bin/env python3 +"""candidate_filter.py — 候选股多级过滤管道 + +从 candidates 表读取未过滤的候选,逐级执行过滤: + Stage 2: 多日K线确认(量价连续性) + Stage 3: 技术位分析(MA位置) + Stage 4: 资金性质(大单流向) + Stage 5: 基本面(PE/PB/行业) + +用法: python3 candidate_filter.py [--stage 2|3|4|5] [--code XXXXXX] +""" +import sys, json, urllib.request, sqlite3, re, time +from pathlib import Path +from datetime import datetime + +DB_PATH = Path("/home/hmo/MoFin/data/mofin.db") +UA = "Mozilla/5.0" + +def get_conn(): + c = sqlite3.connect(str(DB_PATH), timeout=30) + c.execute("PRAGMA busy_timeout=30000") + return c + +def log_candidate(conn, code, stage, passed, detail): + """记录过滤日志""" + conn.execute( + "UPDATE candidates SET log = COALESCE(log, '[]')" + ) + # SQLite JSON操作 + existing = conn.execute("SELECT log FROM candidates WHERE code=?", (code,)).fetchone() + if existing and existing[0]: + try: + logs = json.loads(existing[0]) + except: + logs = [] + else: + logs = [] + logs.append({"stage": stage, "passed": passed, "detail": detail, "time": datetime.now().strftime("%m-%d %H:%M")}) + conn.execute("UPDATE candidates SET log=? WHERE code=?", (json.dumps(logs, ensure_ascii=False), code)) + + +# ── Stage 2: 多日K线确认 ── + +def fetch_daily_klines(code): + """拉取近10日日K线(Sina 240分钟线=日K)""" + raw = str(code).strip() + if raw.startswith(("6", "9")): + prefix = "sh" + elif raw.startswith(("0", "3")): + prefix = "sz" + else: + return None + + import subprocess as _sp, json as _json + url = f"http://money.finance.sina.com.cn/quotes_service/api/json_v2.php/CN_MarketData.getKLineData?symbol={prefix}{raw}&scale=240&ma=5&datalen=10" + try: + r = _sp.run(["curl", "-s", "--noproxy", "*", url], capture_output=True, timeout=10) + data = _json.loads(r.stdout) + if not data: + return None + result = [] + for k in data: + result.append({ + "date": k.get("day", "")[:10], + "open": float(k["open"]), + "close": float(k["close"]), + "high": float(k["high"]), + "low": float(k["low"]), + "volume": int(k["volume"]), + "price": float(k["close"]), + "change_pct": 0, + }) + # 计算涨跌幅 + for i in range(1, len(result)): + prev = result[i-1]["close"] + if prev > 0: + result[i]["change_pct"] = (result[i]["close"] / prev - 1) * 100 + return result + except Exception as e: + return None + return None + + +def stage2_confirm(code, name, klines): + """第二关:多日K线确认 + 检查:多日量价配合、建仓特征 + """ + if not klines or len(klines) < 3: + return False, 0, "K线不足3日" + + recent = klines[-5:] # 最近5日 + score = 0 + checks = [] + + # 1. 成交量连续递增 + vols = [k["volume"] for k in recent] + vol_rising = sum(1 for i in range(len(vols)-1) if vols[i] < vols[i+1]) + if vol_rising >= 3: + score += 2 + checks.append(f"量增{vol_rising}/4日") + elif vol_rising >= 2: + score += 1 + checks.append(f"量微增{vol_rising}/4日") + + # 2. 涨放量、跌缩量 + up_vol = sum(k["volume"] for k in recent if k["change_pct"] >= 0) + down_vol = sum(k["volume"] for k in recent if k["change_pct"] < 0) + if down_vol > 0 and up_vol / down_vol > 1.5: + score += 2 + checks.append(f"涨量/跌量={up_vol/down_vol:.1f}") + elif down_vol > 0 and up_vol / down_vol > 1: + score += 1 + + # 3. 价格趋势 + closes = [k["close"] for k in recent] + up_days = sum(1 for i in range(1, len(closes)) if closes[i] > closes[i-1]) + if up_days >= 3: + score += 2 + checks.append(f"涨{up_days}/4日") + elif up_days >= 2: + score += 1 + + # 4. 无异常放量(单日>3倍均量=可能出货) + avg_vol = sum(vols) / len(vols) if vols else 1 + max_ratio = max(v / avg_vol for v in vols) if avg_vol > 0 else 1 + if max_ratio < 2.5: + score += 1 + else: + checks.append(f"异常量{max_ratio:.0f}倍") + + passed = score >= 4 + detail = f"评分{score}/7 | {'; '.join(checks)}" + return passed, score, detail + + +# ── Stage 3: 技术位分析 ── + +def stage3_technical(code, name, klines): + """第三关:技术位(当日数据估算)""" + if not klines or len(klines) == 0: + return False, 0, "无数据" + + today = klines[-1] + price = today.get("price", 0) + high = today.get("high", 0) + low = today.get("low", 0) + + score = 0 + checks = [] + + if price <= 0: + return False, 0, "价格无效" + + # 日内位置(在高低点中下段还有空间) + if high > low: + pos = (price - low) / (high - low) + if pos < 0.7: + score += 1 + checks.append(f"日内位置{pos:.0%}") + + # 有明确支撑(今日低点作为参考支撑) + if low > 0 and price > low: + score += 1 + checks.append(f"支撑{low:.2f}") + + # 有上涨空间(今日高点作为参考阻力) + if high > price: + upside = (high / price - 1) * 100 + if upside > 2: + score += 1 + checks.append(f"空间{upside:.0f}%") + + passed = score >= 2 + return passed, score, "; ".join(checks) if checks else "基础通过" + + +# ── Stage 4: 资金性质分析 ── + +def stage4_capital_flow(code, name): + """第四关:资金性质(从腾讯实时行情提取外盘/内盘比)""" + raw = str(code).strip() + if raw.startswith(("6", "9")): + prefix = "sh" + elif raw.startswith(("0", "3")): + prefix = "sz" + else: + return False, 0, "非A股" + + import subprocess as _sp + url = f"http://qt.gtimg.cn/q={prefix}{raw}" + try: + r = _sp.run(["curl", "-s", url], capture_output=True, timeout=10) + text = r.stdout.decode("gbk", errors="ignore") + parts = text.split("~") + if len(parts) < 40: + return False, 0, "数据不足" + + # 腾讯字段:[7]=外盘(主动买,股),[8]=内盘(主动卖,股) + try: + outer = int(float(parts[7])) if parts[7] else 0 # 外盘 + inner = int(float(parts[8])) if parts[8] else 0 # 内盘 + except: + return False, 0, "解析失败" + + if outer <= 0 or inner <= 0: + return False, 0, "无盘口数据" + + score = 0 + ratio = outer / inner if inner > 0 else 1 + checks = [] + + if ratio > 1.3: + score += 2 + checks.append(f"外/内={ratio:.2f}") + elif ratio > 1.0: + score += 1 + checks.append(f"买稍强{ratio:.2f}") + else: + checks.append(f"卖稍强{ratio:.2f}") + + # 绝对量也说明资金活跃度 + total = outer + inner + if total > 50000000: # >5000万股 + score += 1 + checks.append(f"活跃{total/10000:.0f}万") + + return score >= 1, score, "; ".join(checks) + except: + return False, 0, "接口失败" + + +# ── Stage 5: 基本面 ── + +def stage5_fundamental(code, name, price): + """第五关:基本面 + 从已有数据判断,不调外部API + """ + conn = get_conn() + score = 0 + checks = [] + + # PE(从stocks表或live_prices) + r = conn.execute("SELECT 1 FROM holdings WHERE code=? AND is_active=1", (code,)).fetchone() + is_holding = r is not None + if is_holding: + checks.append("已持仓") + else: + score += 1 # 新标的加分 + + # 检查是否已被其他候选覆盖 + r2 = conn.execute("SELECT code FROM holding_strategies WHERE code=? AND status='active'", (code,)).fetchone() + if r2: + checks.append("已有策略") + else: + score += 1 + + conn.close() + return score >= 1, score, "; ".join(checks) if checks else "新标的" + + +# ── 主流程 ── + +def main(): + stage_filter = None + single_code = None + for i, arg in enumerate(sys.argv[1:]): + if arg == "--stage" and i+1 < len(sys.argv): + stage_filter = int(sys.argv[i+2]) + if arg == "--code" and i+1 < len(sys.argv): + single_code = sys.argv[i+2] + + conn = get_conn() + + # 读待过滤的候选 + query = "SELECT code, name, reason FROM candidates WHERE 1=1" + params = [] + if single_code: + query += " AND code=?" + params.append(single_code) + else: + query += " AND (pass_final IS NULL OR pass_final=0)" + + rows = conn.execute(query, params).fetchall() + print(f"[FILTER] 待处理候选: {len(rows)}只", flush=True) + + stages = [(2, stage2_confirm, "多日K线"), (3, stage3_technical, "技术位"), + (4, stage4_capital_flow, "资金流"), (5, stage5_fundamental, "基本面")] + + for code, name, reason in rows: + current_score = 0 + print(f" {code} {name}", flush=True) + + # 获取K线(多关需要) + klines = None + + for stage_num, stage_fn, stage_name in stages: + if stage_filter and stage_num != stage_filter: + continue + + # 检查是否已通过此关 + col = f"pass_s{stage_num}" + existing = conn.execute(f"SELECT {col} FROM candidates WHERE code=?", (code,)).fetchone() + if existing and existing[0]: + continue + + if stage_num in (2, 3) and klines is None: + klines = fetch_daily_klines(code) + + if stage_num == 2: + passed, sscore, detail = stage_fn(code, name, klines) + conn.execute("UPDATE candidates SET score_2nd=?, pass_s2=?, reason=? WHERE code=?", + (sscore, 1 if passed else 0, detail, code)) + log_candidate(conn, code, 2, passed, detail) + print(f" S2:{'✅' if passed else '❌'} {detail}", flush=True) + + elif stage_num == 3: + passed, sscore, detail = stage_fn(code, name, klines) + conn.execute("UPDATE candidates SET score_3rd=?, pass_s3=?, reason=? WHERE code=?", + (sscore, 1 if passed else 0, detail, code)) + log_candidate(conn, code, 3, passed, detail) + print(f" S3:{'✅' if passed else '❌'} {detail}", flush=True) + + elif stage_num == 4: + passed, sscore, detail = stage_fn(code, name) + conn.execute("UPDATE candidates SET score_4th=?, pass_s4=?, reason=? WHERE code=?", + (sscore, 1 if passed else 0, detail, code)) + log_candidate(conn, code, 4, passed, detail) + print(f" S4:{'✅' if passed else '❌'} {detail}", flush=True) + + elif stage_num == 5: + price = 0 # 从live_prices获取 + r = conn.execute("SELECT price FROM live_prices WHERE code=?", (code,)).fetchone() + if r: price = r[0] + passed, sscore, detail = stage_fn(code, name, price) + conn.execute("UPDATE candidates SET score_5th=?, pass_s5=?, reason=? WHERE code=?", + (sscore, 1 if passed else 0, detail, code)) + log_candidate(conn, code, 5, passed, detail) + print(f" S5:{'✅' if passed else '❌'} {detail}", flush=True) + + # 计算综合评分 + s2 = conn.execute("SELECT score_2nd FROM candidates WHERE code=?", (code,)).fetchone()[0] or 0 + s3 = conn.execute("SELECT score_3rd FROM candidates WHERE code=?", (code,)).fetchone()[0] or 0 + s4 = conn.execute("SELECT score_4th FROM candidates WHERE code=?", (code,)).fetchone()[0] or 0 + s5 = conn.execute("SELECT score_5th FROM candidates WHERE code=?", (code,)).fetchone()[0] or 0 + final = current_score + s2 + s3 + s4 + s5 + conn.execute("UPDATE candidates SET score_final=?, pass_final=1 WHERE code=?", + (final, code)) + + conn.commit() + conn.close() + print(f"[FILTER] 完成", flush=True) + +if __name__ == "__main__": + main() diff --git a/archive/20260728-dedup/mofin_collect.py b/archive/20260728-dedup/mofin_collect.py new file mode 100644 index 00000000..f234a11e --- /dev/null +++ b/archive/20260728-dedup/mofin_collect.py @@ -0,0 +1,272 @@ +#!/usr/bin/env python3 +"""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, json +from pathlib import Path +from datetime import datetime +from mo_data import get_price, get_prices_batch + +BASE = Path(__file__).parent.parent if "hermes" in str(Path(__file__).resolve()) else Path(__file__).parent + +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.cost, h.shares, 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: + stale_count += 1 + # 强制代码级约束:立即执行重评,不等TODO异步处理 + # 在LLM看到数据前,策略必须是新鲜的 + try: + from strategy_lifecycle import reassess_with_context + result = reassess_with_context( + code, name, r["price"], + r["cost"] if r["cost"] else 0, r["shares"] if r["shares"] else 0, + r["action"] or "" + ) + if result and result.get("action"): + print(f" 🔄 FORCE_REASSESS {code} {name}: {hours_stale:.0f}h过期→已立即重评→{result['action'][:60]}", flush=True) + # 写strategy_evaluations(独立短连接,防锁冲突) + try: + eval_conn = sqlite3.connect(str(Path(__file__).parent.parent / "data" / "mofin.db"), timeout=10) + eval_conn.execute(""" + INSERT INTO strategy_evaluations + (code, eval_type, status, new_stop_loss, new_tp, reason, created_at) + VALUES (?, 'reassess', 'completed', ?, ?, ?, ?) + """, ( + code, + result.get("stop_loss"), + result.get("take_profit"), + f"{result.get('action','')} RR={result.get('rr_ratio','?')} 信号={result.get('timing_signal','')}", + now.isoformat() + )) + eval_conn.commit() + eval_conn.close() + except Exception: + pass + else: + print(f" ⚠️ FORCE_REASSESS {code} {name}: 重评返回空结果", flush=True) + except Exception as e: + print(f" ❌ FORCE_REASSESS {code} {name} 失败: {e}", flush=True) + # 创建TODO作为兜底 + 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未更新 → 已强制重评", flush=True) + else: + fresh_count += 1 + print(f" ✅ FRESH {code} {name}: {hours_stale:.1f}h前更新", flush=True) + else: + no_strategy_count += 1 + # 无策略→立即执行重评创建策略(代码级约束:不允许无策略就输出建议) + try: + from strategy_lifecycle import reassess_with_context + result = reassess_with_context( + code, name, r["price"], + r["cost"] if r["cost"] else 0, r["shares"] if r["shares"] else 0, + "" + ) + if result and result.get("action"): + print(f" 🔄 CREATE_STRATEGY {code} {name}: 无策略→已创建→{result['action'][:60]}", flush=True) + try: + eval_conn = sqlite3.connect(str(Path(__file__).parent.parent / "data" / "mofin.db"), timeout=10) + eval_conn.execute(""" + INSERT INTO strategy_evaluations + (code, eval_type, status, new_stop_loss, new_tp, reason, created_at) + VALUES (?, 'reassess', 'completed', ?, ?, ?, ?) + """, ( + code, + result.get("stop_loss"), + result.get("take_profit"), + f"{result.get('action','')} RR={result.get('rr_ratio','?')} 信号={result.get('timing_signal','')}", + now.isoformat() + )) + eval_conn.commit() + eval_conn.close() + except Exception: + pass + else: + print(f" ⚠️ CREATE_STRATEGY {code} {name}: 重评返回空", flush=True) + except Exception as e: + print(f" ❌ CREATE_STRATEGY {code} {name} 失败: {e}", flush=True) + + conn.commit() + + # === 自选股策略检查 + 强制重评 === + wl_fresh = 0 + wl_stale = 0 + wl_error = 0 + try: + for wr in conn.execute("SELECT code, name, price, entry_low, entry_high, stop_loss FROM watchlist_stocks WHERE is_active=1"): + code = wr["code"] + name = wr["name"] + wl_price = wr["price"] or 0 + # 自选股price可能为0(新加入未更新),从实时API获取 + if wl_price <= 0: + try: + p, _ = get_price(code) + if p and p > 0: + wl_price = p + except Exception: + pass + # 自选股无cost/shares,传0 + try: + from strategy_lifecycle import reassess_with_context + result = reassess_with_context( + code, name, wl_price, + 0, 0, "" + ) + if result and result.get("action"): + wl_stale += 1 + print(f" 📋 WATCHLIST_REASSESS {code} {name}: →{result['action'][:60]}", flush=True) + try: + eval_conn = sqlite3.connect(str(Path(__file__).parent.parent / "data" / "mofin.db"), timeout=10) + eval_conn.execute(""" + INSERT INTO strategy_evaluations + (code, eval_type, status, new_stop_loss, new_tp, reason, created_at) + VALUES (?, 'reassess', 'completed', ?, ?, ?, ?) + """, ( + code, + result.get("stop_loss"), + result.get("take_profit"), + f"自选:{result.get('action','')} RR={result.get('rr_ratio','?')} 信号={result.get('timing_signal','')}", + now.isoformat() + )) + eval_conn.commit() + eval_conn.close() + except Exception: + pass + else: + wl_fresh += 1 + except Exception as e: + wl_error += 1 + print(f" ❌ WATCHLIST_REASSESS {code} {name} 失败: {e}", flush=True) + except Exception as e: + print(f" ⚠️ 自选股检查跳过: {e}", flush=True) + + conn.close() + + total = len(rows) + wl_total = wl_fresh + wl_stale + wl_error + print(f"策略检查完成: {total}只持仓({fresh_count}新鲜/{stale_count}过期/{no_strategy_count}无策略) + {wl_total}只自选({wl_fresh}无需/{wl_stale}已重评/{wl_error}失败)", flush=True) + if stale_count > 0 or no_strategy_count > 0 or wl_stale > 0: + print(f"⚠️ 重评完成: {stale_count + no_strategy_count}只已强制刷新, LLM可基于最新策略给出建议", flush=True) +except Exception as e: + print(f"WARN: strategy_freshness_check跳过 ({e})", flush=True) + +# 步骤1-3: 行业/新闻数据 +SCRIPTS = [] +if market_open: + SCRIPTS.append(("market_watch.py", 60)) + SCRIPTS.append(("trend_detector.py", 60)) +else: + print(f"[{now.strftime('%H:%M')}] 市场未开盘(9:30),跳过板块采集", flush=True) + +SCRIPTS.append(("mofin_news.py", 50)) + +for script, timeout in SCRIPTS: + path = BASE / script + if not path.exists(): + path = Path("/home/hmo/MoFin") / script + print(f"--- {script} ---", flush=True) + start = time.time() + try: + result = subprocess.run( + [sys.executable, str(path)], + capture_output=True, text=True, timeout=timeout + ) + elapsed = time.time() - start + if result.returncode == 0: + print(f"OK ({elapsed:.0f}s)", flush=True) + if result.stdout.strip(): + for line in result.stdout.strip().split("\n")[-3:]: + print(f" {line}", flush=True) + else: + print(f"FAIL ({elapsed:.0f}s): {result.stderr[:200]}", flush=True) + except subprocess.TimeoutExpired: + print(f"TIMEOUT ({timeout}s)", flush=True) + except Exception as e: + print(f"ERROR: {e}", flush=True) + +# ── 步骤4: 个股行情注入(唯一权威价格源)── +# 所有持仓最新行情,注入到 LLM context +# LLM 禁止自行调用原始API解析价格 +PRICE_SCRIPT = BASE / "stock_quote.py" +if not PRICE_SCRIPT.exists(): + PRICE_SCRIPT = Path("/home/hmo/MoFin/deploy/profile-scripts/stock_quote.py") +if PRICE_SCRIPT.exists(): + print("--- stock_quote.py ---", flush=True) + try: + result = subprocess.run( + [sys.executable, str(PRICE_SCRIPT), "--all-holdings"], + capture_output=True, text=True, timeout=30 + ) + if result.returncode == 0 and result.stdout.strip(): + lines = [l for l in result.stdout.strip().split("\n") if l.strip()] + print(f"OK ({len(lines)}只持仓)", flush=True) + for line in lines[:50]: + print(f" {line}", flush=True) + else: + print(f"WARN: stock_quote stderr={result.stderr[:100]}", flush=True) + except Exception as e: + print(f"WARN: stock_quote skipped ({e})", flush=True) +else: + print("WARN: stock_quote.py not found", flush=True) + +print("采集链完成", flush=True) diff --git a/archive/20260728-dedup/stale_detector.py b/archive/20260728-dedup/stale_detector.py new file mode 100644 index 00000000..fc805b83 --- /dev/null +++ b/archive/20260728-dedup/stale_detector.py @@ -0,0 +1,397 @@ +#!/usr/bin/env python3 +"""stale_detector.py — 检查所有策略,标记价格偏离/过期的策略 + +读取 holding_strategies + 自选策略的DB双源数据。 +可被 cron no_agent 模式调用:stdout 注入到后续 LLM 分析。 + +输出格式: + [FLAG] [自选/持仓] 股票名(代码) 价XX | 买入A~B | 问题 + +用法: + python3 stale_detector.py +""" +import json +import sys +import os +from datetime import datetime, timezone +sys.path.insert(0, '/home/hmo/MoFin') +from mo_data import read_portfolio, read_decisions, read_watchlist, get_price, get_prices_batch + + +def fetch_prices(codes): + """统一价格源:优先 stock_quote.py,腾讯API降级为兜底""" + if not codes: + return {} + # 尝试用 stock_quote.py 获取(脚本强制规范) + try: + import subprocess + script = None + for p in ["/home/hmo/MoFin/scripts/stock_quote.py", "/home/hmo/MoFin/stock_quote.py"]: + if os.path.exists(p): + script = p + break + if script: + result = subprocess.run( + [sys.executable, script] + [str(c) for c in codes], + capture_output=True, text=True, timeout=30 + ) + if result.returncode == 0 and result.stdout.strip(): + results = {} + for line in result.stdout.strip().split("\n"): + if not line.strip(): + continue + try: + item = json.loads(line) + code = str(item.get("code", "")) + price = item.get("price") + change = item.get("change_pct", 0) + if code and price is not None: + results[code] = (float(price), float(change)) + except (json.JSONDecodeError, ValueError): + continue + if results: + return results + except Exception as e: + print(f"[STALE] stock_quote.py 回退: {e}", file=sys.stderr) + + # 兜底:mo_data.get_prices_batch + try: + raw = get_prices_batch(codes) + if raw: + return {code: (p, chg) for code, (p, chg) in raw.items()} + except Exception as e: + print(f"FETCH_FAIL (fallback): {e}", file=sys.stderr) + return {} + + +def main(): + decisions_list = read_decisions() + if not isinstance(decisions_list, list): + decisions_list = decisions_list.get("decisions", []) if isinstance(decisions_list, dict) else [] + + # 只保留有买入区的条目,排除已关闭的(inactive/closed) + EXCLUDED_STATUSES = ("closed", "inactive") + to_check = [d for d in decisions_list if (d.get("entry_low") is not None or d.get("entry_high") is not None) and d.get("status") not in EXCLUDED_STATUSES] + + # ----- 补充自选(从 holding_strategies 读取,watchlist_stocks 已废弃) ----- + try: + import sqlite3 + db = sqlite3.connect('/home/hmo/MoFin/data/mofin.db') + db.row_factory = sqlite3.Row + wl_rows = db.execute( + "SELECT code, name, entry_low, entry_high, stop_loss, take_profit, rr_ratio, timing_signal, action " + "FROM holding_strategies WHERE status='active' AND decision_type='自选策略' " + "AND entry_low IS NOT NULL AND entry_high IS NOT NULL" + ).fetchall() + db.close() + existing_codes = {d["code"] for d in to_check} + for row in wl_rows: + code = str(row["code"]) + if code in existing_codes: + continue + entry_low = row["entry_low"] + entry_high = row["entry_high"] + if not entry_low or not entry_high or entry_low <= 0: + continue + action = row["action"] or "" + timing_signal = row["timing_signal"] or "买入" + wl_entry = { + "code": code, + "name": row["name"] or code, + "entry_low": entry_low, + "entry_high": entry_high, + "stop_loss": row["stop_loss"], + "type": "自选策略", + "action": action, + "timing_signal": timing_signal, + } + to_check.append(wl_entry) + except Exception as e: + print(f"[WATCHLIST_MERGE FAIL] {e}", file=sys.stderr) + + if not to_check: + print("[SILENT] 无需要检查的策略") + return 0 + + # ----- 自选股买入区偏离自动重评 (从 holding_strategies 读,watchlist_stocks 已废弃) ----- + try: + import subprocess, sqlite3 + db = sqlite3.connect('/home/hmo/MoFin/data/mofin.db') + db.row_factory = sqlite3.Row + wl_stocks = db.execute( + "SELECT code, name, entry_low, entry_high " + "FROM holding_strategies WHERE status='active' AND decision_type='自选策略' " + "AND entry_low IS NOT NULL AND entry_high IS NOT NULL AND entry_low > 0" + ).fetchall() + db.close() + reassess_scripts = [] + for ws in wl_stocks: + code, name, wl_el, wl_eh = ws + if not wl_el or not wl_el or wl_el <= 0: + continue + center = (wl_el + wl_eh) / 2 + # 从 decisions 拿实时价 + price_map = fetch_prices([code]) + cur_price = price_map.get(code, (None, None))[0] + if not cur_price or cur_price <= 0: + continue + drift = (cur_price / center - 1) * 100 + # 触发条件:价格偏离>15% 或 买入区明确错误(价格完全在区间外且偏离>50%) + price_outside = cur_price < wl_el or cur_price > wl_eh + if abs(drift) > 15 or (price_outside and abs(drift) > 50): + reassess_scripts.append(code) + print(f"[AUTO_REASSESS] {name}({code}) 价{cur_price:.2f}偏离买入区中心{center:.2f} {drift:+.0f}% → 触发重评") + if reassess_scripts: + # 调用 per_stock_reassess(每轮最多5只,防LLM慢导致整批超时;其余下轮继续) + reassess_path = None + for p in ['/home/hmo/MoFin/scripts/per_stock_reassess.py', + '/home/hmo/.hermes/profiles/position-analyst/scripts/per_stock_reassess.py']: + if os.path.exists(p): + reassess_path = p + break + if reassess_path: + MAX_PER_RUN = 5 + batch = reassess_scripts[:MAX_PER_RUN] + if len(reassess_scripts) > MAX_PER_RUN: + print(f"[AUTO_REASSESS] 本轮限{MAX_PER_RUN}只,剩余{len(reassess_scripts)-MAX_PER_RUN}只下轮继续") + for code in batch: + try: + # LLM 重评冷启动 20-100s,deepseek-v4-pro 更慢 → 480s + r = subprocess.run(['python3', reassess_path, code], + capture_output=True, text=True, timeout=480) + out = r.stdout.strip()[:200] if r.stdout else "" + err = r.stderr.strip()[:200] if r.stderr else "" + print(f" → {code}: exited={r.returncode} {out}") + except subprocess.TimeoutExpired: + print(f" → {code}: 超时480s(LLM仍慢),下轮重试") + except Exception as e: + print(f"[AUTO_REASSESS FAIL] {e}") + # ----- 结束 自选股重评 ----- + # 🔁 重评后重新从DB读取策略数据,刷新to_check + try: + decisions_list = read_decisions() + if not isinstance(decisions_list, list): + decisions_list = decisions_list.get("decisions", []) if isinstance(decisions_list, dict) else [] + to_check = [d for d in decisions_list if (d.get("entry_low") is not None or d.get("entry_high") is not None) and d.get("status") not in EXCLUDED_STATUSES] + # 重新合并自选(从 holding_strategies 读) + db2 = sqlite3.connect('/home/hmo/MoFin/data/mofin.db') + db2.row_factory = sqlite3.Row + wl_rows2 = db2.execute( + "SELECT code, name, entry_low, entry_high, stop_loss, take_profit, rr_ratio, timing_signal, action " + "FROM holding_strategies WHERE status='active' AND decision_type='自选策略' " + "AND entry_low IS NOT NULL AND entry_high IS NOT NULL AND entry_low > 0" + ).fetchall() + db2.close() + existing_codes2 = {d["code"] for d in to_check} + for row in wl_rows2: + code = str(row["code"]) + if code in existing_codes2: + continue + entry_low = row["entry_low"] + entry_high = row["entry_high"] + if not entry_low or not entry_high or entry_low <= 0: + continue + action = row["action"] or "" + timing_signal = row["timing_signal"] or "买入" + wl_entry = { + "code": code, + "name": row["name"] or code, + "entry_low": entry_low, + "entry_high": entry_high, + "stop_loss": row["stop_loss"], + "type": "自选策略", + "action": action, + "timing_signal": timing_signal, + } + to_check.append(wl_entry) + except Exception as e: + print(f"[RELOAD FAIL] {e}", file=sys.stderr) + + # ----- 组合级监测:读取总仓位 + 弱势比例 ----- + position_pct = 0 + cash = 0 + total_assets = 0 + try: + pf = read_portfolio() + position_pct = pf.get("position_pct", 0) + cash = pf.get("cash", 0) + total_assets = pf.get("total_assets", 0) + except Exception: + pass + # 统计持仓策略中弱势/深套的比例 + weak_count = 0 + holding_count = 0 + for d in decisions_list: + if d.get("type") == "持仓策略" and d.get("status") not in ("closed", "inactive"): + holding_count += 1 + cat = d.get("stock_category", "") + if cat in ("弱势", "深套"): + weak_count += 1 + weak_ratio = (weak_count / holding_count * 100) if holding_count > 0 else 0 + + prices = fetch_prices([d["code"] for d in to_check]) + now = datetime.now(timezone.utc).astimezone() + found = 0 + + for d in to_check: + code = d["code"] + name = d.get("name", code) + el = d.get("entry_low") + eh = d.get("entry_high") + sl = d.get("stop_loss") + tp = d.get("take_profit") + ts = d.get("created_at") or d.get("timestamp") or d.get("updated_at", "") + is_wl = "自选" in (d.get("type", "")) + + pi = prices.get(code) + if not pi: + continue + price, chg = pi + if price <= 0: + continue + + issues, flags = [], [] + tag = "[自选]" if is_wl else "[持仓]" + + # -- 偏离 -- + if is_wl and not issues and not flags: + # 自选在买入区上沿与20%之间(零标记漏洞):标记为小幅偏离 + if el and eh and price > eh: + flags.append("[WL_DRIFT]") + flags.append("[STRATEGY_STALE]") + issues.append(f"[STRATEGY_STALE] 价{price:.2f}超买入区上沿+{((price/eh)-1)*100:.1f}%,买入区需重评") + if is_wl and el and eh: + # 读取 timing_signal 判断策略有效性(timing_signal 字段优先,fallback to action) + current_str = d.get("current", "") or "" + timing_signal = d.get("timing_signal", "") or current_str + has_nonbuy_signal = any(kw in timing_signal for kw in [ + "等企稳再入", "等企稳", "弱势持有", "观望", + "不建议买入", "谨慎买入", + ]) + + # 直接计算 R/R(不依赖文本匹配) + rr_invalid = False + if sl and sl > 0 and tp and tp > 0 and price > sl: + rr = (tp - price) / (price - sl) + if rr < 1.5: + rr_invalid = True + # 也检查 tp 是否接近或低于成本(微盈/浮亏止盈) + cost = d.get("cost", 0) + if cost and cost > 0 and tp <= cost * 1.05: + rr_invalid = True + + strategy_deficient = has_nonbuy_signal or rr_invalid + # 对自选无止盈位的也标记(策略不完整) + if not tp or tp == 0: + strategy_deficient = True + + if el <= price <= eh: + flags.append("[WL_IN]") + if strategy_deficient: + flags.append("[STRATEGY_STALE]") + issues.append(f"[STRATEGY_STALE] 价{price:.2f}在买入区{el}~{eh}但策略不完整({'RR='+f'{rr:.2f}<1.5' if rr_invalid else '无止盈位' if not tp else '非买入信号'}),买入区需重评") + else: + issues.append(f"[PUSH] 价{price:.2f}入买入区{el}~{eh}") + elif price > eh * 1.35: + flags.append("[WL_HIGH]") + flags.append("[STRATEGY_STALE]") + issues.append(f"[STRATEGY_STALE] 价{price:.2f}高出买入区+{((price/eh)-1)*100:.0f}%,买入区需重评") + elif price > eh * 1.20: + flags.append("[WL_DRIFT]") + flags.append("[STRATEGY_STALE]") + issues.append(f"[STRATEGY_STALE] 价{price:.2f}高出买入区+{((price/eh)-1)*100:.0f}%,买入区需重评") + elif price > eh: + flags.append("[WL_DRIFT]") + flags.append("[STRATEGY_STALE]") + issues.append(f"[STRATEGY_STALE] 价{price:.2f}超买入区上沿+{((price/eh)-1)*100:.1f}%,买入区需重评") + elif not is_wl and eh: + dp = (price / eh - 1) * 100 + if dp > 35: + flags.append("[SEVERE]") + issues.append(f"偏离买入区上沿+{dp:.0f}%") + elif dp > 20: + flags.append("[DRIFT]") + issues.append(f"偏离买入区上沿+{dp:.0f}%") + elif dp > 10: + flags.append("[WARN]") + issues.append(f"偏离买入区上沿+{dp:.0f}%") + # 持仓在买入区内但 R/R 不达标 + if el and sl and sl > 0 and tp and tp > 0 and price > sl: + if el <= price <= eh: + rr = (tp - price) / (price - sl) + if rr < 1.5: + flags.append("[RR_WARN]") + issues.append(f"买入区内RR仅{rr:.2f}<1.5,策略需重评") + + # -- 距止损/止盈(仅持仓) -- + if not is_wl: + if sl and sl > 0: + dsl = (price / sl - 1) * 100 + if dsl < 5: + # 成本基准校验:浮盈>5%时止损是利润保护,不是危险信号 + # (mirrors NEAR_TP cost_check logic at line 195-198) + cost = d.get("cost") + if cost and cost > 0 and price > cost * 1.05: + flags.append("[PROFIT_PROTECT]") + pnl = (price / cost - 1) * 100 + issues.append(f"距止损仅{dsl:.1f}%(利润保护,浮盈{pnl:.0f}%)") + else: + flags.append("[NEAR_SL]") + issues.append(f"距止损仅{dsl:.1f}%") + if tp and tp > 0: + dtp = (tp / price - 1) * 100 + if dtp < 5: + # 成本基准校验:止盈标记只有在盈利≥5%时才有效 + cost_check = True + cost = d.get("cost") + if cost and cost > 0 and price < cost * 1.05: + cost_check = False + if cost_check: + flags.append("[NEAR_TP]") + issues.append(f"距止盈仅{dtp:.1f}%") + + # -- 过期 -- + stale_limit = 30 if is_wl else 14 + if ts: + try: + ud = datetime.fromisoformat(ts) + if ud.tzinfo is None: + ud = ud.replace(tzinfo=timezone.utc) + days = (now - ud).days + if days > stale_limit: + flags.append("[STALE]") + issues.append(f"{days}天未更新(>{stale_limit})") + except (ValueError, TypeError): + pass + + if issues: + # 仅输出有明确操作信号的行:[PUSH]=推荐买入, [STRATEGY_STALE]=需重评 + # 静默其他纯信息行(如仅"价XX高出/高于买入区"而无操作建议) + if any("[PUSH]" in i or "[STRATEGY_STALE]" in i for i in issues): + print(f"{' '.join(flags)} {tag} {name}({code}) 价{price:.2f}{chg} | 买入{el}~{eh} | {'; '.join(issues)}") + found += 1 + + if found == 0: + print("[SILENT] 所有策略正常") + + # ----- 组合级警报 ----- + portfolio_alerts = 0 + if holding_count > 0: + if weak_ratio > 40: + print(f"\n[PORTFOLIO_WEAK] 组合弱势比例{weak_ratio:.0f}% ({weak_count}/{holding_count})!仓位{position_pct:.1f}% → 建议系统性减仓") + portfolio_alerts += 1 + elif weak_ratio > 30: + print(f"\n[PORTFOLIO_WEAK_MILD] 组合弱势比例{weak_ratio:.0f}% ({weak_count}/{holding_count}),仓位{position_pct:.1f}%,关注") + portfolio_alerts += 1 + if position_pct > 80 and holding_count > 0: + # 仓位过满提醒 + print(f"[PORTFOLIO_FULL] 总仓位{position_pct:.1f}% > 80%,现金{cash:.0f}({cash/total_assets*100:.1f}%)") + portfolio_alerts += 1 + if portfolio_alerts > 0: + found += portfolio_alerts + + return found + + +if __name__ == "__main__": + main() diff --git a/data/strategy_staleness_report.json b/data/strategy_staleness_report.json index 853b55fb..fe63f9b4 100644 --- a/data/strategy_staleness_report.json +++ b/data/strategy_staleness_report.json @@ -1,94 +1,34 @@ { - "checked_at": "2026-07-20T09:00:56", - "total_active": 128, - "flagged_count": 66, + "checked_at": "2026-07-28T09:00:13", + "total_active": 24, + "flagged_count": 5, "flagged": [ { - "code": "000518", - "name": "四环生物", - "price": 3.82, + "code": "601998", + "name": "中信银行", + "price": 7.71, "flags": [ - "现价3.82在买入区4~4(是否可买需结合timing_signal判断)" + "现价7.71在买入区8~8(是否可买需结合timing_signal判断)" ], - "age_days": 5, - "last_update": "2026-07-14 09:29:52", - "entry_zone": "4~4", - "current": "盈利持有 | 量价齐升(建仓特征) | 近5日增量涨价=建仓型 | 目标4.04 | 止损3.71 | 买入区3.74~3.84 | 信号:关注", - "updated_by": "auto", + "age_days": 0, + "last_update": "2026-07-28 08:10:52", + "entry_zone": "8~8", + "current": "现价7.65已在买入区内,可直接建仓首笔,若回踩7.58附近可加仓,止损设7.50,目标8.03。换仓建议:当前持仓紫金矿业(601899)仓位9.2%,该股近期走势不明,可考虑减持部分换入本股以增强防御性。", + "updated_by": null, "updated_reason": "自动生成", "is_watchlist": true }, { - "code": "000711", - "name": "ST京蓝", - "price": 5.69, + "code": "603599", + "name": "广信股份", + "price": 10.32, "flags": [ - "现价5.69在买入区6~6(是否可买需结合timing_signal判断)" + "现价10.32在买入区10~11(是否可买需结合timing_signal判断)" ], - "age_days": 3, - "last_update": "2026-07-16 09:17:37", - "entry_zone": "6~6", - "current": "盈利持有 | ⚠️盈亏比不足1:1.5,不建议买入 | 量价齐升(建仓特征) | 近5日增量涨价=建仓型 | 目标参考0 | 止损5.46 | 买入区5.58~5.8", - "updated_by": "auto", - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "001309", - "name": "德明利", - "price": 596.16, - "flags": [ - "现价596.16在买入区560~610(是否可买需结合timing_signal判断)" - ], - "age_days": 3, - "last_update": "2026-07-16 21:11:09", - "entry_zone": "560~610", - "current": "盈利持有 | ⚠️盈亏比不足1:1.5,不建议买入 | 正常 | 目标625.97 | 止损596.16 | 买入区808.5~608.08", - "updated_by": "auto", - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "002594", - "name": "比亚迪", - "price": 87.8, - "flags": [ - "现价87.80在买入区86~88(是否可买需结合timing_signal判断)" - ], - "age_days": 9, - "last_update": "2026-07-10 11:45:31", - "entry_zone": "86~88", - "current": "盈利持有 | ⚠️盈亏比偏低(1:1.8),谨慎买入 | 正常 | 目标92.65 | 止损85.17 | 买入区86.04~88.16", - "updated_by": "auto", - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "601318", - "name": "中国平安", - "price": 49.54, - "flags": [ - "现价49.54在买入区49~50(是否可买需结合timing_signal判断)" - ], - "age_days": 3, - "last_update": "2026-07-16 09:33:23", - "entry_zone": "49~50", - "current": "盈利持有 | 量价背离 | 近5日减量涨价=⬆量价背离 | 目标53.52 | 止损48.05 | 买入区48.55~50.24 | 信号:关注", - "updated_by": "auto", - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "603501", - "name": "豪威集团", - "price": 100.95, - "flags": [ - "现价100.95在买入区99~103(是否可买需结合timing_signal判断)" - ], - "age_days": 6, - "last_update": "2026-07-13 09:51:37", - "entry_zone": "99~103", - "current": "盈利持有 | 正常 | 目标110.59 | 止损97.92 | 买入区98.93~102.97 | 信号:关注", + "age_days": 0, + "last_update": "2026-07-28 08:10:52", + "entry_zone": "10~11", + "current": "空仓·可建仓 | 正常 | 目标11.2 | 止损10.07 | 买入区10.17~10.52 | 信号:买入", "updated_by": "auto", "updated_reason": "自动生成", "is_watchlist": true @@ -96,912 +36,58 @@ { "code": "603766", "name": "隆鑫通用", - "price": 13.05, + "price": 13.95, "flags": [ - "现价13.05在买入区12~13(是否可买需结合timing_signal判断)" + "现价13.95在买入区14~14(是否可买需结合timing_signal判断)" ], "age_days": 0, - "last_update": "2026-07-20 01:27:04", - "entry_zone": "12~13", - "current": "买12.28~13.19 | 损11.9 | 盈14.87 | RR2.6 | 评分10.0", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "603885", - "name": "吉祥航空", - "price": 10.3, - "flags": [ - "现价10.30在买入区10~10(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 08:16:14", - "entry_zone": "10~10", - "current": "盈利持有 | 正常 | 目标11.23 | 止损9.99 | 买入区10.09~10.49 | 信号:关注", + "last_update": "2026-07-28 08:10:52", + "entry_zone": "14~14", + "current": "空仓关注 | 正常 | 目标14.83 | 止损13.42 | 买入区13.55~13.98 | 信号:关注", "updated_by": "auto", "updated_reason": "自动生成", "is_watchlist": true }, - { - "code": "603980", - "name": "吉华集团", - "price": 6.17, - "flags": [ - "现价6.17在买入区6~6(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 01:27:17", - "entry_zone": "6~6", - "current": "买5.82~6.25 | 损5.64 | 盈7.05 | RR2.6 | 评分8.0", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "605090", - "name": "九丰能源", - "price": 38.2, - "flags": [ - "现价38.20在买入区36~39(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 01:27:04", - "entry_zone": "36~39", - "current": "买35.89~38.54 | 损34.76 | 盈43.45 | RR2.5 | 评分10.0", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688009", - "name": "中国通号", - "price": 4.95, - "flags": [ - "现价4.95在买入区5~5(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 01:27:09", - "entry_zone": "5~5", - "current": "买4.68~5.03 | 损4.54 | 盈5.67 | RR2.6 | 评分9.0", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688065", - "name": "凯赛生物", - "price": 42.75, - "flags": [ - "现价42.75在买入区41~44(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 01:27:29", - "entry_zone": "41~44", - "current": "买40.63~43.63 | 损39.35 | 盈49.19 | RR2.5 | 评分6.0", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688076", - "name": "ST诺泰", - "price": 28.0, - "flags": [ - "现价28.00在买入区27~29(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 01:27:12", - "entry_zone": "27~29", - "current": "买26.99~28.98 | 损26.14 | 盈32.67 | RR2.5 | 评分8.0", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688087", - "name": "英科再生", - "price": 36.18, - "flags": [ - "现价36.18在买入区35~37(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 08:16:14", - "entry_zone": "35~37", - "current": "盈利持有 | 正常 | 目标39.69 | 止损35.09 | 买入区35.46~36.9", - "updated_by": "auto", - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688093", - "name": "世华科技", - "price": 27.5, - "flags": [ - "现价27.50在买入区27~29(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 01:27:33", - "entry_zone": "27~29", - "current": "买26.62~28.58 | 损25.78 | 盈32.22 | RR2.5 | 评分5.0", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688116", - "name": "天奈科技", - "price": 30.66, - "flags": [ - "现价30.66在买入区29~31(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 01:27:24", - "entry_zone": "29~31", - "current": "买28.95~31.08 | 损28.03 | 盈35.04 | RR2.5 | 评分7.0", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688169", - "name": "石头科技", - "price": 99.17, - "flags": [ - "现价99.17在买入区94~101(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 01:27:29", - "entry_zone": "94~101", - "current": "买94.36~101.32 | 损91.38 | 盈114.23 | RR2.5 | 评分6.0", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688247", - "name": "宣泰医药", - "price": 8.91, - "flags": [ - "现价8.91在买入区8~9(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 01:27:32", - "entry_zone": "8~9", - "current": "买8.46~9.09 | 损8.2 | 盈10.25 | RR2.6 | 评分5.0", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688267", - "name": "中触媒", - "price": 17.25, - "flags": [ - "现价17.25在买入区17~19(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 01:27:20", - "entry_zone": "17~19", - "current": "买17.24~18.51 | 损16.7 | 盈20.87 | RR2.5 | 评分7.0", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688271", - "name": "联影医疗", - "price": 110.37, - "flags": [ - "现价110.37在买入区108~111(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 08:16:14", - "entry_zone": "108~111", - "current": "盈利持有 | ⚠️盈亏比偏低(1:1.9),谨慎买入 | 正常 | 目标116.74 | 止损107.06 | 买入区108.16~110.93 | 信号:关注", - "updated_by": "auto", - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688289", - "name": "圣湘生物", - "price": 15.34, - "flags": [ - "现价15.34在买入区14~15(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 01:27:02", - "entry_zone": "14~15", - "current": "买14.35~15.41 | 损13.9 | 盈17.38 | RR2.6 | 评分10.0", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688312", - "name": "燕麦科技", - "price": 50.0, - "flags": [ - "现价50.00远低于买入区63~68,买入区需下移" - ], - "age_days": 0, - "last_update": "2026-07-20 01:27:34", - "entry_zone": "63~68", - "current": "买63.31~67.97 | 损61.31 | 盈76.64 | RR2.5 | 评分4.0", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688334", - "name": "西高院", - "price": 17.18, - "flags": [ - "现价17.18在买入区16~18(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 01:27:17", - "entry_zone": "16~18", - "current": "买16.41~17.62 | 损15.89 | 盈19.86 | RR2.5 | 评分8.0", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688349", - "name": "三一重能", - "price": 13.72, - "flags": [ - "现价13.72在买入区13~14(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 01:27:14", - "entry_zone": "13~14", - "current": "买13.06~14.03 | 损12.65 | 盈15.81 | RR2.5 | 评分8.0", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688372", - "name": "伟测科技", - "price": 120.73, - "flags": [ - "现价120.73在买入区118~123(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 08:16:14", - "entry_zone": "118~123", - "current": "盈利持有 | 正常 | 目标165.83 | 止损114.53 | 买入区118.32~123.14", - "updated_by": "auto", - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688377", - "name": "迪威尔", - "price": 19.68, - "flags": [ - "现价19.68在买入区19~21(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 01:27:23", - "entry_zone": "19~21", - "current": "买19.3~20.73 | 损18.69 | 盈23.37 | RR2.5 | 评分7.0", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688379", - "name": "XD华光新", - "price": 49.92, - "flags": [ - "现价49.92远低于买入区65~70,买入区需下移" - ], - "age_days": 0, - "last_update": "2026-07-20 01:27:30", - "entry_zone": "65~70", - "current": "买65.2~70.0 | 损63.14 | 盈78.92 | RR2.5 | 评分5.0", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688380", - "name": "中微半导", - "price": 39.51, - "flags": [ - "现价39.51远低于买入区56~60,买入区需下移" - ], - "age_days": 0, - "last_update": "2026-07-20 01:27:12", - "entry_zone": "56~60", - "current": "买55.77~59.87 | 损54.0 | 盈67.5 | RR2.5 | 评分8.0", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688396", - "name": "华润微", - "price": 62.01, - "flags": [ - "现价62.01在买入区62~67(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 01:27:16", - "entry_zone": "62~67", - "current": "买62.01~66.58 | 损60.05 | 盈75.06 | RR2.5 | 评分8.0", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, { "code": "688410", "name": "山外山", - "price": 10.44, + "price": 9.64, "flags": [ - "现价10.44在买入区10~11(是否可买需结合timing_signal判断)" + "现价9.64在买入区9~10(是否可买需结合timing_signal判断)" ], "age_days": 0, - "last_update": "2026-07-20 01:27:28", - "entry_zone": "10~11", - "current": "买10.0~10.74 | 损9.69 | 盈12.11 | RR2.6 | 评分6.0", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688425", - "name": "铁建重工", - "price": 4.08, - "flags": [ - "现价4.08在买入区4~4(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 01:27:01", - "entry_zone": "4~4", - "current": "买3.88~4.16 | 损3.75 | 盈4.69 | RR2.5 | 评分11.0", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688439", - "name": "振华风光", - "price": 39.58, - "flags": [ - "现价39.58在买入区39~40(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 08:16:14", - "entry_zone": "39~40", - "current": "盈利持有 | 正常 | 目标43.68 | 止损38.39 | 买入区38.79~40.37", - "updated_by": "auto", - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688466", - "name": "金科环境", - "price": 12.6, - "flags": [ - "现价12.60在买入区12~13(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 08:16:14", - "entry_zone": "12~13", - "current": "盈利持有 | 正常 | 目标13.49 | 止损12.22 | 买入区12.35~12.73", - "updated_by": "auto", - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688472", - "name": "阿特斯", - "price": 9.02, - "flags": [ - "现价9.02在买入区9~9(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 01:27:08", - "entry_zone": "9~9", - "current": "买8.77~9.41 | 损8.49 | 盈10.61 | RR2.5 | 评分9.0", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688475", - "name": "萤石网络", - "price": 26.64, - "flags": [ - "现价26.64在买入区26~27(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 08:16:14", - "entry_zone": "26~27", - "current": "盈利持有 | 正常 | 目标29.06 | 止损23.06 | 买入区26.11~27.13", - "updated_by": "auto", - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688501", - "name": "青达环保", - "price": 16.79, - "flags": [ - "现价16.79在买入区16~17(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 08:16:14", - "entry_zone": "16~17", - "current": "盈利持有 | 正常 | 目标18.43 | 止损16.29 | 买入区16.45~17.13", - "updated_by": "auto", - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688510", - "name": "航亚科技", - "price": 21.0, - "flags": [ - "现价21.00在买入区21~21(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 08:16:14", - "entry_zone": "21~21", - "current": "盈利持有 | 正常 | 目标24.23 | 止损20.27 | 买入区20.58~21.42", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688513", - "name": "苑东生物", - "price": 54.63, - "flags": [ - "现价54.63在买入区54~56(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 08:16:14", - "entry_zone": "54~56", - "current": "盈利持有 | 正常 | 目标66.84 | 止损52.79 | 买入区53.54~55.72", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688518", - "name": "联赢激光", - "price": 18.46, - "flags": [ - "现价18.46在买入区18~19(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 08:16:14", - "entry_zone": "18~19", - "current": "盈利持有 | 正常 | 目标22.25 | 止损17.91 | 买入区18.09~18.83", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688526", - "name": "科前生物", - "price": 12.77, - "flags": [ - "现价12.77在买入区12~13(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 01:27:27", - "entry_zone": "12~13", - "current": "买12.25~13.16 | 损11.87 | 盈14.83 | RR2.5 | 评分6.0", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688533", - "name": "上声电子", - "price": 20.59, - "flags": [ - "现价20.59在买入区20~21(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 08:16:14", - "entry_zone": "20~21", - "current": "盈利持有 | 正常 | 目标22.56 | 止损19.97 | 买入区20.18~21.0", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688566", - "name": "吉贝尔", - "price": 25.0, - "flags": [ - "现价25.00在买入区24~26(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 08:16:14", - "entry_zone": "24~26", - "current": "盈利持有 | 放量下跌⚠️ | 近5日增量跌价=⚠️出货型 | 目标28.64 | 止损24.25 | 买入区24.5~25.5", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688569", - "name": "铁科轨道", - "price": 16.56, - "flags": [ - "现价16.56在买入区16~17(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 01:27:14", - "entry_zone": "16~17", - "current": "买15.74~16.9 | 损15.24 | 盈19.06 | RR2.5 | 评分8.0", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688576", - "name": "西山科技", - "price": 38.71, - "flags": [ - "现价38.71在买入区38~41(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 01:27:03", - "entry_zone": "38~41", - "current": "买38.37~41.2 | 损37.16 | 盈46.45 | RR2.5 | 评分10.0", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688579", - "name": "地纬智能", - "price": 7.63, - "flags": [ - "现价7.63在买入区7~8(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 01:27:07", - "entry_zone": "7~8", - "current": "买7.15~7.68 | 损6.93 | 盈8.66 | RR2.6 | 评分9.0", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688581", - "name": "安杰思", - "price": 51.85, - "flags": [ - "现价51.85在买入区49~53(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 01:27:07", - "entry_zone": "49~53", - "current": "买49.3~52.94 | 损47.75 | 盈59.68 | RR2.5 | 评分9.0", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688582", - "name": "芯动联科", - "price": 38.61, - "flags": [ - "现价38.61在买入区38~39(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 08:16:14", - "entry_zone": "38~39", - "current": "盈利持有 | 正常 | 目标52.27 | 止损36.81 | 买入区37.84~39.38", - "updated_by": "auto", - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688588", - "name": "凌志软件", - "price": 8.6, - "flags": [ - "现价8.60在买入区8~9(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 08:16:14", - "entry_zone": "8~9", - "current": "盈利持有 | 正常 | 目标9.47 | 止损8.34 | 买入区8.43~8.77", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688616", - "name": "西力科技", - "price": 9.78, - "flags": [ - "现价9.78在买入区9~10(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 01:27:13", + "last_update": "2026-07-28 08:10:52", "entry_zone": "9~10", - "current": "买9.37~10.06 | 损9.07 | 盈11.34 | RR2.5 | 评分8.0", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688617", - "name": "惠泰医疗", - "price": 204.18, - "flags": [ - "现价204.18在买入区195~209(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 01:27:05", - "entry_zone": "195~209", - "current": "买194.99~209.35 | 损188.83 | 盈236.04 | RR2.5 | 评分9.0", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688621", - "name": "阳光诺和", - "price": 57.58, - "flags": [ - "现价57.58在买入区56~59(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 08:16:14", - "entry_zone": "56~59", - "current": "盈利持有 | 量价齐升(建仓特征) | 近5日增量涨价=建仓型 | 目标71.45 | 止损55.62 | 买入区56.43~58.73", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688623", - "name": "双元科技", - "price": 60.98, - "flags": [ - "现价60.98在买入区60~64(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 01:27:13", - "entry_zone": "60~64", - "current": "买59.8~64.21 | 损57.91 | 盈72.39 | RR2.5 | 评分8.0", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688626", - "name": "翔宇医疗", - "price": 43.7, - "flags": [ - "现价43.70在买入区43~47(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 01:27:09", - "entry_zone": "43~47", - "current": "买43.42~46.62 | 损42.05 | 盈52.57 | RR2.5 | 评分9.0", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688689", - "name": "银河微电", - "price": 39.94, - "flags": [ - "现价39.94远低于买入区55~59,买入区需下移" - ], - "age_days": 0, - "last_update": "2026-07-20 01:27:11", - "entry_zone": "55~59", - "current": "买55.36~59.44 | 损53.61 | 盈67.01 | RR2.5 | 评分8.0", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688692", - "name": "达梦数据", - "price": 215.4, - "flags": [ - "现价215.40在买入区211~220(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 08:16:14", - "entry_zone": "211~220", - "current": "盈利持有 | 正常 | 目标246.64 | 止损208.94 | 买入区211.09~219.71", + "current": "放弃建仓,继续等待均线走平或行业持续回暖、大盘企稳后再评估。", "updated_by": "auto", "updated_reason": "自动生成", "is_watchlist": true }, { - "code": "688696", - "name": "极米科技", - "price": 62.9, + "code": "688618", + "name": "三旺通信", + "price": 28.65, "flags": [ - "现价62.90在买入区62~64(是否可买需结合timing_signal判断)" + "现价28.65在买入区28~29(是否可买需结合timing_signal判断)" ], "age_days": 0, - "last_update": "2026-07-20 08:16:14", - "entry_zone": "62~64", - "current": "盈利持有 | 正常 | 目标69.75 | 止损61.01 | 买入区61.64~64.16", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688708", - "name": "佳驰科技", - "price": 44.09, - "flags": [ - "现价44.09在买入区43~45(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 08:16:14", - "entry_zone": "43~45", - "current": "盈利持有 | 缩量回踩(洗盘末端) | 近5日减量跌价=洗盘特征 | 目标48.3 | 止损42.77 | 买入区43.21~44.97", + "last_update": "2026-07-28 08:10:53", + "entry_zone": "28~29", + "current": "建议在当前价28.65附近执行买入,限价区间28.08~28.75内均可建仓。止损设在27.30,止盈设在30.49。若股价快速拉升至29.25以上,可考虑部分止盈锁定利润。注意月末窗口及高风险信号,持仓期间密切关注大盘和地缘消息变化。", "updated_by": "auto", "updated_reason": "自动生成", "is_watchlist": true - }, - { - "code": "688755", - "name": "汉邦科技", - "price": 24.01, - "flags": [ - "现价24.01在买入区24~24(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 08:16:14", - "entry_zone": "24~24", - "current": "盈利持有 | 正常 | 目标26.71 | 止损23.24 | 买入区23.53~24.49", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688772", - "name": "珠海冠宇", - "price": 14.21, - "flags": [ - "现价14.21在买入区14~14(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 08:16:14", - "entry_zone": "14~14", - "current": "盈利持有 | 正常 | 目标16.83 | 止损13.62 | 买入区13.93~14.49", - "updated_by": "auto", - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688775", - "name": "影石创新", - "price": 138.34, - "flags": [ - "现价138.34在买入区136~141(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 08:16:14", - "entry_zone": "136~141", - "current": "盈利持有 | 量价齐升(建仓特征) | 近5日增量涨价=建仓型 | 目标163.77 | 止损133.07 | 买入区135.57~141.11", - "updated_by": "auto", - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688778", - "name": "厦钨新能", - "price": 44.14, - "flags": [ - "现价44.14在买入区43~45(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 08:16:14", - "entry_zone": "43~45", - "current": "盈利持有 | 正常 | 目标49.16 | 止损42.82 | 买入区43.26~45.02 | 信号:关注", - "updated_by": "auto", - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688779", - "name": "五矿新能", - "price": 6.69, - "flags": [ - "现价6.69在买入区7~7(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 08:16:14", - "entry_zone": "7~7", - "current": "盈利持有 | 缩量回踩(洗盘末端) | 近5日减量跌价=洗盘特征 | 目标7.48 | 止损6.49 | 买入区6.56~6.82 | 信号:关注", - "updated_by": "auto", - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688789", - "name": "宏华数科", - "price": 46.1, - "flags": [ - "现价46.10在买入区45~46(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 08:16:14", - "entry_zone": "45~46", - "current": "盈利持有 | 正常 | 目标48.88 | 止损44.72 | 买入区45.18~46.38", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688795", - "name": "摩尔线程-U", - "price": 666.0, - "flags": [ - "现价666.00在买入区653~679(是否可买需结合timing_signal判断)" - ], - "age_days": 9, - "last_update": "2026-07-10 21:09:43", - "entry_zone": "653~679", - "current": "盈利持有 | 正常 | 目标788.13 | 止损646.02 | 买入区652.68~679.32 | 信号:关注", - "updated_by": "auto", - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688802", - "name": "沐曦股份-U", - "price": 840.08, - "flags": [ - "现价840.08在买入区823~848(是否可买需结合timing_signal判断)" - ], - "age_days": 3, - "last_update": "2026-07-16 09:32:17", - "entry_zone": "823~848", - "current": "盈利持有 | 正常 | 目标898.74 | 止损814.88 | 买入区823.28~848.42 | 信号:关注", - "updated_by": "auto", - "updated_reason": "自动生成", - "is_watchlist": true - }, - { - "code": "688819", - "name": "天能股份", - "price": 20.81, - "flags": [ - "现价20.81在买入区21~22(是否可买需结合timing_signal判断)" - ], - "age_days": 0, - "last_update": "2026-07-20 01:27:11", - "entry_zone": "21~22", - "current": "买20.59~22.1 | 损19.94 | 盈24.92 | RR2.5 | 评分8.0", - "updated_by": null, - "updated_reason": "自动生成", - "is_watchlist": true } ], "portfolio": { - "position_pct": 74.04, - "cash": 241330.8, - "weak_position_pct": 64.3, - "all_weak_pct": 27.3, + "position_pct": 84.96, + "cash": 139307.0, + "weak_position_pct": 66.7, + "all_weak_pct": 45.8, "signals": [ - "[PORTFOLIO_WEAK] 组合中弱势+深套分类持仓占比64.3%>40%,建议系统性减仓" + "[PORTFOLIO_WEAK] 组合中弱势+深套分类持仓占比66.7%>40%,建议系统性减仓", + "[PORTFOLIO_FULL] 总仓位84.96%(现金139307元),买入建议受限" ] }, - "summary": "扫描128个策略,66个需关注" + "summary": "扫描24个策略,5个需关注" } \ No newline at end of file diff --git a/mofin_collect.py b/mofin_collect.py index 904e7bdb..f234a11e 100644 --- a/mofin_collect.py +++ b/mofin_collect.py @@ -249,7 +249,7 @@ for script, timeout in SCRIPTS: # LLM 禁止自行调用原始API解析价格 PRICE_SCRIPT = BASE / "stock_quote.py" if not PRICE_SCRIPT.exists(): - PRICE_SCRIPT = Path("/home/hmo/MoFin/scripts/stock_quote.py") + PRICE_SCRIPT = Path("/home/hmo/MoFin/deploy/profile-scripts/stock_quote.py") if PRICE_SCRIPT.exists(): print("--- stock_quote.py ---", flush=True) try: