# MoFin Cron 架构深度审查与重构设计 > 创建:2026-08-11 | 审查人:小小莫 | 状态:✅ 审查完成 + P0/P1 已执行 > 背景:新策略(预测超跌反弹 p_oversold)引入前,必须清理 cron 架构的 > 重复调度、代码分叉、职责混乱问题,让架构清晰可维护。 --- ## 执行记录(2026-08-11) ### ✅ P0 已执行(消除代码多版本) | 动作 | commit | |------|--------| | 根目录 market_watch.py(旧版分叉)→ archive/20260811-cron-cleanup/ | refactor: P0清理 | | deploy/get_realtime_prices.py(死代码)→ archive | 同上 | | promote_candidates.py 备份残留删除 | 同上 | | 硬链接同步(110 scripts)| 已验证 | ### ✅ P1 已执行(策略扫描器独立调度) | 动作 | 结果 | |------|------| | market_watch.py 移除 mr/s2 嵌入(-42 行)| ✅ 已 push + 246 pull,守卫不再回滚 | | mr_scanner 独立 job(v_weak扫描-独立,9:35 交易日)| ✅ hermes cron list 确认 | | s2_scanner 独立 job(S2恐慌买扫描-独立,9:35 交易日)| ✅ hermes cron list 确认 | | market_watch 验证(90 板块 + regime,无 mr/s2 输出)| ✅ snapshot_id=1548 | ### ✅ 顶层 jobs 残留处理 - 16 个 default profile job(调度器 7/23 已死)→ 全部停用 - 备份:`~/.hermes/cron/jobs.json.bak_20260811_top_level_disabled` - position-analyst(65 jobs)/ mohe(2 jobs)未受影响 ### ✅ P2 已执行(午间重复采集判断) | job | 判断 | 处理 | |-----|------|------| | 行业富集-cninfo-午间 | 只补缺失行业映射(幂等去重,早间已补全)→ **冗余** | ✅ 已停用(备份 jobs.json.bak_20260811_sector_noon)| | 基本面刷新-午间 | 拉实时 PE/PB(随价格变动)→ **有意义** | 保留 | ### ✅ 嵌入调用全面扫描 - 11 个脚本有 subprocess 嵌入调用 - **仅 market_watch→mr/s2 是架构问题**(已修复);其余均为合理工具/流程调用 - 确认:generate_report(按需触发重评)/ sync_dashboard(包装工具)均职责清晰 ### ✅ 工具函数抽取(import 级解耦,2026-08-11) **问题**:s2_scanner import mr_scanner 的工具函数(fetch_tx_klines/calc_ma/calc_rsi/get_stock_pool) ——策略扫描器互相 import 是坏味道,新策略会越引越乱。 **重构**: | 新模块 | 内容 | 来源 | |--------|------|------| | `indicators.py` | calc_ma/calc_rsi/calc_atr/calc_obv | mr_scanner 抽取 | | `market_data.py` | fetch_tx_klines/get_stock_pool | mr_scanner 抽取 | **验证**:mr/s2 均改为从公共模块 import,语法 OK,运行正常(trend_up/非恐慌日正确跳过),s2 不再 import mr_scanner(0 处)。 **收益**:p_oversold 扫描器直接 import indicators/market_data,不依赖任何策略扫描器。 ### ✅ 指标函数重复实现扫描(2026-08-11) | 模块 | 函数 | 判断 | |------|------|------| | market_regime.py | calc_ma | 🔴 **与 indicators.calc_ma 完全相同**(纯重复,建议后续统一)| | multi_timeframe.py | calc_moving_averages | ⚠️ 增强版(多窗口 dict 返回),非纯重复 | | strategy_lifecycle.py | calc_atr | ⚠️ 不同实现(带 code 拉港股数据),非纯重复 | > 注:market_regime.calc_ma 是唯一纯重复,但改动它需回归 market_regime(运行中), > 收益小风险中——记录为后续优化项,不在本次强制重构。核心目标(p_oversold 用公共模块)已达成。 ### ✅ p_oversold 候选管道适配点(2026-08-11 确认,部署时执行) **管道现状**(实测): - candidate_filter 处理:`pass_final IS NULL OR pass_final=0`(396-403行) - promote_candidates 读取:`promoted=0 AND dropped=0 AND score_final>=7`(38-42行) - promote_candidates RR>=2.0 门槛:行130(`_rr < 2.0` 跳过)——**无 sector 分流** **p_oversold 扫描器必须写入**: 1. `score_final >= 7`(否则 promote 不读) 2. `pass_final = 1`(否则 candidate_filter 重新过滤) 3. `promoted = 0`(否则 promote 跳过) 4. `sector = 'p_oversold'`(标记策略归属) **promote_candidates 需改(部署时)**: 1. SELECT 加 `c.sector` 字段 2. RR>=2.0 门槛加 sector 例外:`if sector == 'p_oversold': 跳过 RR 门槛` (超跌反弹候选 RR 天然 <2,走专属评估替代) **验证方式**:p_oversold 候选写入后,手动跑 candidate_filter → promote_candidates,确认不被 RR 拦截。 ### ✅ predictive_oversold_scanner.py 已创建(2026-08-11) **文件**:`deploy/profile-scripts/predictive_oversold_scanner.py`(266 行,已入库 push) **实现**: - v5 信号条件(mkt_rsi<50 + mkt_dd60<=-5 + bias60<-20 + 小市值/低估值/新闻/行业弱) - 大盘门控(compute_market_filters + check_gate,含阴跌跳过判定) - 当日幂等(同股当日已写跳过)+ 单例守卫 + 单日限5 - **方案C 写入**:score_final=8 + pass_final=1 + sector='p_oversold'(绕过 candidate_filter 6阶段评分) - 基于 indicators/market_data 公共模块(不依赖任何策略扫描器) **验证**: - 语法 OK,硬链接同步(113 scripts) - 运行正确:大盘 RSI=55.1≥50 时正确跳过(当前非弱势);指数代码修复(000001 自动加 sh 前缀) **待部署**(老莫批准后): - 注册 hermes 独立 cron(9:35 交易日,对齐 mr/s2) - promote_candidates 加 sector 例外(RR>=2.0 对 p_oversold 跳过) - 真实分位数据(mcap_q/pe_q 目前简化,部署时用全市场分位) ### ✅ prompt 型 job 审查(2026-08-11,最后一类) **position-analyst prompt 型 job(无 script,LLM 指令)**: | job | 调度 | 功能 | 状态 | |-----|------|------|------| | 市场精选推荐-每日 | 16:00 | LLM 今日精选 | 保留 | | 宏观风险扫描 | 8:30 | LLM 宏观风险识别 | 保留 | | 宏观风险扫描-午间 | 11:30 | 同款 prompt(与早间重复)| ✅ **已停用**(5b91b0cd92ae)| | 宏观风险扫描-周末 | 周六10:00 | 周末风险复盘 | 保留 | **最终调度状态**:position-analyst 65 jobs(58 enabled,7 合理停用): 系统健康检查/自选买入区/小果筛选/300308×2/宏观午间/行业富集午间 ### 守卫教训(重要) - deploy_guard 每 15 分钟回滚 CODE_PATHS 未提交改动(07:00 实测回滚 market_watch) - **正确流程**:本地改 → commit+push → 246 pull(守卫看到 git 干净 → 不回滚) --- ## 一、现状全景 ### 1.1 调度系统 | 调度系统 | 任务数 | 说明 | |---------|--------|------| | hermes 顶层 jobs.json | 16 | ⚠️ 7/23 起调度器停摆(残留)| | hermes position-analyst | 63 | ✅ 主调度(活跃)| | hermes mohe | 2 | ✅ 少量 | | 系统 crontab | ~33 | ⚠️ 部分与 hermes 重复 | ### 1.2 脚本总数 - deploy/profile-scripts/:~100 个 py - hermes scripts/:硬链接(sync_profile_scripts.sh 维护) - MoFin 根目录:库文件 + 残留 --- ## 二、核心架构问题 ### 问题 1:策略扫描器嵌入数据采集器(违反单一职责) **现状**: ``` market_watch.py(行业板块采集,*/10) ├─ 1. 板块数据采集(EM→THS)→ market_snapshots ├─ 2. market_regime 计算 → market_regime 表 ├─ 3. subprocess 调 mr_scanner.py(v_weak,timeout=1500s) └─ 4. subprocess 调 s2_scanner.py(S2 恐慌买,timeout=1500s) ``` **问题**: - market_watch 职责 = 数据采集 + 市场阶段 + 2 个策略扫描,四合一 - mr_scanner/s2_scanner **无独立调度**,只能被 market_watch 触发 - timeout=1500s(25分钟)嵌入采集器,若超时影响采集 - 新策略 p_oversold 替换时无处安放(要改 market_watch 还是新建?) **重构方案**:策略扫描器独立调度 ``` market_watch.py(纯板块采集,*/10) mr_scanner.py(v_weak,独立 cron 0 9 * * 1-5 或并入扫描调度) s2_scanner.py(S2 恐慌买,独立 cron) predictive_oversold_scanner.py(新策略,独立 cron) ``` - 每个扫描器独立 job、独立日志、独立幂等 - market_watch 只做采集(单一职责) ### 问题 2:同一脚本多个调度入口 | 脚本 | 入口 | 建议 | |------|------|------| | market_watch.py | 顶层(0,30 9-16) + position(*/10) | 停顶层(调度器已死)| | stale_detector.py | 策略评估-每日(21:00) + 周末重评(30 10 0,6) | 保留(两用途不同)| | memory_guardian.py | 顶层 + position | 停顶层 | | cron_to_xmpp.py | 顶层(* 9-16) + position(*/5) | 停顶层(已停 crontab)| | divergence_detector.py | 工作日(*/30 8-15) + 周末(30 8-16/2) | 保留(两用途合理)| ### 问题 3:一份代码多版本(分叉) | 文件 | 副本 | 状态 | |------|------|------| | market_watch.py | MoFin根(旧) vs deploy(新) | ❌ 分叉,根目录残留 | | promote_candidates.py | docs/backup 残留 | ❌ 备份未清理 | | self_todo_executor.py | deploy vs hermes vs AgentsMeeting | ⚠️ 新旧版并存 | | agents_health_check.py | MoFin/AgentsMeeting/self-growing | ⚠️ 三项目各自维护 | **原则(红线)**: - 一份代码只存在于一个 canonical 位置(MoFin git 仓库) - deploy/profile-scripts ↔ hermes scripts 用硬链接(已规范) - 其他位置(根目录/backup/其他项目)不得有副本 ### 问题 4:健康检查体系过度层级化 **现状 8 个健康类脚本**: ``` L0: agents_health_check(Tier1,5min) L1: morning_health_check(8:00 体检)+ intraday_health_check(15min) L1: functional_health_check(L1 功能检查,15min) L1: mofin_health(dashboard 数据,15min) L2: system_hygiene_audit(卫生审计) L3: self_repair(LLM 修复) L4: meta_watchdog(检查检查的检查) + system_audit(全局审计 17:30) ``` **问题**:8 个脚本职责重叠,互相引用,排查复杂 **建议**:保留核心 4 层(Tier1/morning+intraday/functional/self_repair),合并冗余 --- ## 三、逐任务设计文档(分类) ### 3.1 候选管道(核心链路) ``` accumulation_scanner(*/10)→ candidates(sector=accumulation) → candidate_filter(*/30,6阶段评分) → promote_candidates(*/30,RR>=2 + 容量60) → per_stock_reassess(12维重评)→ holding_strategies 盘前: premarket_full_review(8:10)→ regenerate_all ``` | 脚本 | 职责 | 调度 | 数据流 | 问题 | |------|------|------|--------|------| | accumulation_scanner | 主力建仓扫描 | */10 | 行情→candidates | 与新策略无关,保留 | | candidate_filter | 6阶段过滤 | */30 | candidates→评分 | 耦合 accumulation 设计 | | promote_candidates | 候选提拔 | */30 | candidates→自选 | RR>=2 门槛需适配 | | premarket_full_review | 盘前重评 | 8:10 | 全量重评 | 依赖缓存预热 | ### 3.2 策略扫描器(问题 1 核心) | 脚本 | 职责 | 当前入口 | 重构后 | |------|------|---------|--------| | mr_scanner.py | v_weak 弱市扫描 | 嵌入 market_watch | 独立 cron | | s2_scanner.py | S2 恐慌买 | 嵌入 market_watch | 独立 cron | | **predictive_oversold_scanner** | **新策略(待建)** | 无 | 独立 cron | ### 3.3 数据采集 | 脚本 | 职责 | 调度 | 问题 | |------|------|------|------| | price_monitor | 价格(A+港股)| hermes */2 9-16 | ✅ 已统一 | | market_watch | 板块 | hermes */10 | ⚠️ 待拆策略扫描 | | capital_flow_collector | 资金流 | 1,31 9-15 | ✅ 已修锁 | | refresh_mtf_cache | 多周期缓存 | 50 7,11,14 | ✅ 已修 flush | | fundamentals_refresh | 基本面 | 早/午两次 | ⚠️ 午间可停?| | sector_enrich_cninfo | 行业富集 | 早/午两次 | ⚠️ 午间可停?| | macro_context_collector | 宏观新闻 | */30 | ✅ | | refresh_macro_context | 宏观上下文 | */30 | ✅ | | news_collector | 新闻 | crontab 30 9,13,17 | ✅ | | divergence_detector | 跨市场背离 | */30+周末 | ✅ | ### 3.4 健康自检(问题 4) | 脚本 | 职责 | 调度 | 保留 | |------|------|------|------| | agents_health_check | Tier1 4服务 | */5 | ✅ | | morning_health_check | 每日体检 8类40项 | 8:00 | ✅ | | intraday_health_check | 盘中自检 | */15 | ✅ | | functional_health_check | L1 功能 | */15 | ✅ | | mofin_health | dashboard 数据 | */15 | ✅ | | self_repair | L3 LLM 修复 | */30 | ✅ | | meta_watchdog | L4 元监控 | 5min | ✅ | | system_hygiene_audit | 卫生审计 | 8:20 | ⚠️ 与 system_audit 重叠 | | system_audit | 全局审计 | 17:30 | ⚠️ 与 hygiene 重叠 | | system_health_check | 传统检查 | 已停 | ❌ 可归档 | ### 3.5 报告简报 | 脚本 | 职责 | 调度 | 备注 | |------|------|------|------| | opening_brief/closing_brief | 开/收盘简报 | 9:35/16:10 | ✅ | | market_insight | 知微洞察 | 15:35 | ✅ | | strategy_review | 策略复盘 | 20:00 | ✅ | | strategy_evaluator | 策略评估 | 每周/每日 | ⚠️ 每日版可停 | | collect_evaluation_data | 评估数据 | 20:30 | ✅ | | prepare_report_data | 报告数据 | 周四 | ✅ | ### 3.6 自成长 | 脚本 | 职责 | 调度 | 备注 | |------|------|------|------| | branch_scanner | 分支自成长 | 15min | ✅ | | prune_branches | 分支剪枝 | 21:00 | ✅ | | meta_growth | 元自成长 | 0:45/12:45 | ✅ | | hardcode_scanner | 硬编码扫描 | 17:25 | ✅ | ### 3.7 系统维护 | 脚本 | 职责 | 调度 | 备注 | |------|------|------|------| | deploy_guard | 部署守卫 | */15 | ✅ 核心 | | fix_gateway_port | gateway 看门狗 | 10min | ✅ 核心 | | agent_spiral_watchdog | 螺旋监控 | */10 | ✅ | | db_daily_backup | DB 备份 | 7:50 | ✅ | | vacuum_state_db | state.db 整理 | 周六 | ✅ | | system_audit | 全局审计 | 17:30 | ✅ | --- ## 四、重构方案(待老莫批准) ### 4.1 立即执行(低风险) 1. **停顶层 jobs.json 残留**(16个 job 调度器已死)——enabled=False 或确认清理 2. **清理 market_watch.py 根目录分叉**(旧版移 archive) 3. **清理 promote_candidates.py backup 残留** 4. **统一 agents_health_check**(确认三项目各自维护是否必要) ### 4.2 重构(中等风险,需测试) 5. **拆 market_watch 策略扫描**:mr_scanner/s2_scanner 独立调度 6. **fundamentals/sector_enrich 午间版停用**(早间足够) 7. **评估每日版停用**(每周版足够) ### 4.3 为 p_oversold 准备 8. **新建 predictive_oversold_scanner.py**(独立 cron,对齐 accumulation_scanner 模式) 9. **候选管道适配**(p_oversold 走专属评估,绕过 RR>=2) ### 4.4 红线(杜绝多版本) - 一份代码一个 canonical 位置(MoFin git) - deploy↔hermes 硬链接(已规范) - 根目录不留脚本副本(库文件除外,且必须与 deploy 硬链) - 备份不进仓库 --- ## 五、执行优先级 | 优先级 | 动作 | 风险 | |--------|------|------| | P0 | 停顶层 jobs 残留 + 清 market_watch 分叉 | 低 | | P1 | 拆策略扫描器独立调度 | 中 | | P2 | 停午间重复采集 | 低 | | P3 | 建 p_oversold 扫描器 | 中 | > 待老莫确认后分批执行。 --- ## 六、补充发现(第二轮审查 2026-08-11) ### 6.1 JSON 引用问题 - ✅ 20 个脚本引用 decisions.json/portfolio.json/watchlist.json,但**多数通过 mo_data 兼容层**(read_decisions/read_portfolio 读 DB)——docstring 是旧注释,实际正常 - 🔴 **get_realtime_prices.py 直接 open('data/decisions.json')** —— decisions.json 已删除,运行必报错。**死代码**(无调度无 import),归档 - 🔴 import_holding_xls.py 有注释掉的旧 JSON 写操作(无影响) ### 6.2 死代码/残留清单 | 文件 | 状态 | |------|------| | get_realtime_prices.py | 死代码(读已删JSON),归档 | | market_watch.py(MoFin根)| 旧版分叉,归档 | | promote_candidates.py(docs/backup)| 备份残留,清理 | | system_health_check.py | 已暂停,被替代,归档 | | session_to_cron_bridge.py | 已停 crontab(顶层cron停摆)| | cron_to_xmpp.py crontab版 | 已停(hermes版保留)| ### 6.3 自成长类确认 | 脚本 | 职责 | 状态 | |------|------|------| | branch_scanner | 分支数据采集 | ✅ 活跃(15min)| | branch_evaluator | 分支适用性评估 | ✅ 读 DB(mo_data)| | prune_branches | 剪枝 | ✅ 活跃(21:00)| | meta_growth | 元层自成长 | ✅ 活跃(0:45/12:45)| | hardcode_scanner | 硬编码扫描 | ✅ 活跃(17:25)| ### 6.4 报告简报类确认 | 脚本 | 职责 | 状态 | |------|------|------| | opening/closing_brief | 开/收盘简报 wrapper | ✅ | | generate_report | 模板报告生成器 | ✅ | | market_insight | 知微洞察 | ✅ | | strategy_review | 三层复盘 | ✅ | | strategy_evaluator | 双维度评估 | ⚠️ 每日版可停 | | collect_evaluation_data | 评估数据采集 | ✅ | | prepare_report_data | 报告参数 | ✅ | | advice_reconciliation | 建议对账 | ✅ 读DB |