Files
MoFin/docs/cron-architecture-review-20260811.md

395 lines
18 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 独立 jobv_weak扫描-独立,9:35 交易日)| ✅ hermes cron list 确认 |
| s2_scanner 独立 jobS2恐慌买扫描-独立,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-analyst65 jobs/ mohe2 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_scanner0 处)。
**收益**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 独立 cron9:35 交易日,对齐 mr/s2~~ ✅ **已注册**b44a607a811dactive
- promote_candidates 加 sector 例外(RR>=2.0 对 p_oversold 跳过)
- 真实分位数据(mcap_q/pe_q 目前简化,部署时用全市场分位)
**策略扫描器调度就绪(2026-08-11**
| job | id | 调度 |
|-----|-----|------|
| v_weak扫描-独立 | 7104a0b39338 | 35 9 * * 1-5 |
| S2恐慌买扫描-独立 | 5662e91d03a0 | 35 9 * * 1-5 |
| 预测超跌反弹扫描-独立 | b44a607a811d | 35 9 * * 1-5 |
> 三个扫描器均带大盘门控(非弱势自动跳过)+ 幂等 + 单日限流,enabled 安全(当前 RSI=55.1 会跳过)。
### ✅ prompt 型 job 审查(2026-08-11,最后一类)
**position-analyst prompt 型 job(无 scriptLLM 指令)**
| job | 调度 | 功能 | 状态 |
|-----|------|------|------|
| 市场精选推荐-每日 | 16:00 | LLM 今日精选 | 保留 |
| 宏观风险扫描 | 8:30 | LLM 宏观风险识别 | 保留 |
| 宏观风险扫描-午间 | 11:30 | 同款 prompt(与早间重复)| ✅ **已停用**5b91b0cd92ae|
| 宏观风险扫描-周末 | 周六10:00 | 周末风险复盘 | 保留 |
**最终调度状态**position-analyst 65 jobs58 enabled7 合理停用):
系统健康检查/自选买入区/小果筛选/300308×2/宏观午间/行业富集午间
### ✅ mohe profile 审查(2026-08-11,全覆盖补完)
| job | 调度 | 功能 | 状态 |
|-----|------|------|------|
| memory-weekly-cleanup | 周一9:00 | 记忆清理(mohe专属)| 保留 |
| 量化交易进阶系列自动追更 | 每日9:00 | 公众号系列追更(mohe专属)| 保留 |
**全部 job 审查覆盖率:100%**position-analyst 65 + mohe 2 + 顶层 16 + crontab 33
- 顶层 16:已停用(default 调度器死)
- position-analyst58 启用(7 合理停用)
- mohe 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.pyv_weaktimeout=1500s
└─ 4. subprocess 调 s2_scanner.pyS2 恐慌买,timeout=1500s
```
**问题**
- market_watch 职责 = 数据采集 + 市场阶段 + 2 个策略扫描,四合一
- mr_scanner/s2_scanner **无独立调度**,只能被 market_watch 触发
- timeout=1500s(25分钟)嵌入采集器,若超时影响采集
- 新策略 p_oversold 替换时无处安放(要改 market_watch 还是新建?)
**重构方案**:策略扫描器独立调度
```
market_watch.py(纯板块采集,*/10
mr_scanner.pyv_weak,独立 cron 0 9 * * 1-5 或并入扫描调度)
s2_scanner.pyS2 恐慌买,独立 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_checkTier15min
L1: morning_health_check8:00 体检)+ intraday_health_check15min
L1: functional_health_checkL1 功能检查,15min
L1: mofin_healthdashboard 数据,15min
L2: system_hygiene_audit(卫生审计)
L3: self_repairLLM 修复)
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*/306阶段评分)
→ promote_candidates*/30RR>=2 + 容量60
→ per_stock_reassess12维重评)→ holding_strategies
盘前: premarket_full_review8: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.pyMoFin根)| 旧版分叉,归档 |
| promote_candidates.pydocs/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 | 分支适用性评估 | ✅ 读 DBmo_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 |