# MoFin 架构改革 — 变更日志 > 日期:2026-06-29 ~ 2026-07-03 > 执行:Sisyphus (小小莫) + Zhiwei (知微) --- ## 2026-07-09 — 信号堆积修复 ### 根因 xiaoguo_signal_consumer 仅处理今日信号(`date(created_at)=today`),之前日期的信号永远不被消费 → 33条堆积。SIGNAL_MAX_AGE_HOURS=4 已定义但未被使用。 ### 修复清单 1. **xiaoguo_signal_consumer**: `date(today)` → `created_at > -4h` 年龄过滤 2. **xiaoguo_signal_consumer**: 无时效内信号时自动标记过期信号为已处理 3. **xiaoguo_signal_consumer**: 移除 `mo_data.get_price` 导入(import path 冲突,root mo_data.py 无 get_price),改用腾讯 API 直连 fallback 4. **intraday_health_check**: 信号堆积检查限定 4h 内,避免过期信号误报 5. **system_audit**: 同上 ### 效果 - 已清理 20 条过期信号(>4h),剩余 0 条未处理 - 盘中健康检查不会再因过期信号误报堆积 - 信号管道只监控时效内的处理速度 ### [2026-07-09 跟进] 监控盲区修复 + 信号积压根治 - **发现**: intraday_health_check 和 system_audit 仅监控 source LIKE 'xiaoguo%',完全忽略 divergence_watch(80条)、trend(46条) 等来源信号积压 - **修复① (当日监控层)**: 两端都增加全量未处理查询(total_unproc),区分 xiaoguo 和其他来源分别报告 - **修复② (同天根治)**: divergence_detector.py 写入 signal_news 时标记 processed=1(已有macro_divergence_state.json自用,signal_news只做归档);trend 46条空summary死数据标记已处理 - **效果**: signal_news 1077条全量已处理,健康检查 PASS,后续不会再次积压 ## 2026-07-03 — 代码重构 ### 删除重复文件 - `scripts/strategy_lifecycle.py` — 旧版本,缺少 quality gate,root 版本是规范 - `scripts/price_monitor.py` — 旧版本,root 版本是规范 ### 清理 JSON 路径常量 - 移除 scripts/ 中 12 个文件的 DECISIONS_PATH / PORTFOLIO_PATH / WATCHLIST_PATH 声明 - 移除 root level 中无用的 JSON 路径常量 ### 文档更新 - `docs/SYSTEM_ARCHITECTURE.md` — 完整重写(v5.0) - `CHANGELOG.md` — 补充重构记录 --- ## 2026-07-03 — JSON 彻底移除 + 币种架构修正 ### JSON→DB 全面迁移 (32+ 文件) **数据层** - `mo_data.py`:删除所有 JSON fallback,纯 SQLite 读取 - `mofin_db.py`: - `write_holding_strategy` 改为 DELETE+INSERT(旧 ON CONFLICT 静默失败) - `holding_strategies` 新增 10 列:avg_price, decision_timestamp, note, quality_check, quality_checked_at, quality_issues_json, position_advice, signal_factors_json, time_horizon, decision_type - 新增 `live_prices` / `mtf_cache` / `capital_flow_cache` 三张表 + 读写函数 - 已有 `market_snapshots` 表接替 market.json **server.py** - 新增 `_save_portfolio` / `_save_decision` / `_save_watchlist` 写函数 - 所有 13 个 API 写端点切到 DB,读端点删除 JSON fallback **脚本层** - 15 个文件 `json.load → mo_data.read_*()` - 12 个文件 `json.dump → mofin_db.write_*()` 或直接删除 - strategy_lifecycle JSON 冷备行全部删除 - branch_scanner / prune_branches 切到 DB - system_audit / stale_push_wlin / refresh_mtf_cache JSON→DB **LLM Prompt** - evaluation-daily v2:`evaluation.json` → DB 表 - knowledge-extraction v1:`decisions.json` → DB 表 - analytics.py:删除 JSON 路径常量 **已移除的 JSON 文件**(不再被任何代码读写) - portfolio.json / decisions.json / watchlist.json - live_prices.json / multi_tf_cache.json / market.json / capital_flow_cache.json ### 币种架构 **港股** — 个股 price/cost/stop_loss 存 **HKD**,`currency='HKD'` **A股** — 个股 price/cost/stop_loss 存 **CNY**,`currency='CNY'` **总资产** — `calc_total_assets()` 汇总时港股 HKD×汇率→CNY | 场景 | 港股 | A股 | |------|------|-----| | 个股显示(股软对照) | HKD | CNY | | 技术位(止损/止盈/买入区) | HKD | CNY | | 总资产 / 总市值 / P&L | CNY(汇总时转换) | CNY | ### 关键修复 - 根 `price_monitor.py` 与 `scripts/price_monitor.py` 不一致(前者存 HKD,后者转 CNY)→ 统一:存 HKD,不转换 - `s.get('price', 0)` 在 price 为 None 时返回 None 导致 TypeError → 改为 `s.get('price') or 0` - 东财 API 限流(之前 0.1s 间隔疯狂刷被封)→ 第一只失败立刻切腾讯兜底 ### 验证 ``` holdings: 19 (8 HKD + 11 CNY) decisions: 19 (8 HKD + 11 CNY) total_assets: stored = calculated ✅ ``` --- ## 2026-07-01 — 统一读取层 + 现金日志 ### 新增 - `mo_data.py` — `read_portfolio()` / `read_decisions()` / `read_watchlist()` - `mofin_db.py` — `cash_log` 表 + `write_cash_log` / `query_cash_log` ### 修改 - 16 个文件 `json.load → mo_data.read_*()` - HK 股成本 HKD→CNY 修复(import_holding_xls 导入时转换) --- ## 2026-06-30 — 初始架构重构 ### 新增 - `mo_models.py` — `calc_total_assets()`, `is_hk_stock()`, `get_hk_rate()`, `to_cny()`, `validate_portfolio()` - `mo_config.py` — 配置单例 - `mo_bridge.py` — DSA 集成桥 - `mo_provider.py` — DSA 数据源适配器 - `scripts/data_validate.py`, `holdings_reconciliation.py`, `process_trade.py`(知微) - DSA Web UI: `http://192.168.1.246:8001` ### 修改 - `price_monitor.py`:港股腾讯→东方财富 + 统一 total_assets - 8 个文件散落 `is_hk_stock` / `total_assets` 统一到 mo_models --- ## 2026-07-06 — 全面系统修复(知微) ### 1. XMPP Bot 稳定性修复 - **去自ping心跳**:之前bot每15秒给自己(zhiwei@yoin.fun)发XEP-0199 ping检测连接。gateway阻塞时自ping也超时,导致bot误判连接断开→断线重连→死循环。改为纯TCP 30秒检查。 - **永续重试**:gateway超时消息最多重试3次就丢弃 → 永不丢弃+指数退避(1s→2s→4s→...→120s),5次失败日志报警。 - **跨重连引用**:消息队列中 `bot_ref` 在重连后指向旧bot实例(已断开)→ 加 `_current_bot` 模块级变量,处理消息时优先用当前活跃bot实例。 ### 2. Cron Delivery 修复(10个job) - 宏观风险扫描 ×3(早/午/周末):`deliver=all` → `local` - 小果信号消费-盘中、策略复盘-每日、数据治理-每周、记忆守卫-每日、盘中自检-高频、系统常规体检-开盘前、自愈执行器:`deliver=origin` → `local` - 根因:cron job没有上游渠道可推送,`origin/all`解析失败 ### 3. 脚本Bug修复 - **refresh_mtf_cache.py**:导入 `strategy_lifecycle.PORTFOLIO_PATH` 报错(DB-only重构后常量已删除)。改为从 `mofin.db` 的holdings/watchlist_stocks表直接读股票代码列表,走腾讯API拉K线。 - **clean_watchlist.py**:`mo_data.read_portfolio()` 调用名错误(`mo_data`前缀有多余)+ 硬编码JSON路径。修复为纯DB读取。 ### 4. 脚本目录同步 - MoFin/scripts/(git仓库)与~/.hermes/profiles/position-analyst/scripts/(cron运行版本)双向补全 - 57个文件从profile→MoFin同步,全部md5校验一致 ### 5. Git提交 - `66962ae` 全面系统修复:包含以上所有变更 - `e055485` 加 `.gitignore`(取自code_only分支,排除`__pycache__/ *.pyc data/reports/`) - 已推送到origin master ### 6. 数据 - watchlist新增 000850 华茂股份(金融股权低估逻辑,PB 0.83破净,2026-07-03公告分红3675万) ### 已知未解决 - ejabberd容器重启46次(exit code 0+无日志,系统负载高峰时被外部信号杀死) ## 2026-07-09 **stale_push_wlin 推送流程重构** - 加4小时重评冷却:查DB `holding_strategies.updated_at`,冷却期内跳过 per_stock_reassess - Zone notes冷却30分→4小时,匹配重评冷却 - 修早退bug:`if not actionable: return 0` → `if not actionable and not zone_notes` - 标题自适应:有推荐→"自选买入提醒" | 仅区间→"操作区间提醒" - 操作建议标题只在有可操作项时显示 - 区间说明格式改为 Dad 确认的"进入操作区间,但重评结果: xxx" --- ### 修正的活代码路径(唯一真正在读写 JSON 的路径) - `price_monitor.py` — 每2分钟读 decisions.json 改为 mo_data.read_decisions()(DB) - 其他18处代码注释写"写入 portfolio.json/decisions.json"但代码已实际写 DB,只修了注释 ### 健康检查脚本 - `system_health_check.py`(主+scripts/两个版本)— 数据条目标签从"portfolio.json DB记录"改为"DB持仓记录" - 消除 LLM cron 误读标签后 stat JSON 文件大小的问题 ### 配置层标记废弃 - `mo_config.py`(主+scripts/两个版本)— 所有 JSON 路径 property 加 ⚠️ 已废弃 docstring ### 磁盘残留 JSON 文件 - portfolio.json / decisions.json / watchlist.json → 全部改为废弃 stub - 保留空文件防导入报错,内容仅含废弃声明 ### 变更总结 - 核心数据(持仓/自选/策略)的读和写已 100% 走 DB - 唯一仍用 JSON 的非核心文件:reports/*.json(报告缓存)、stocks/*.json(个股详情缓存)、market.json(大盘数据缓存)、accuracy_stats.json(统计缓存) ## 2026-07-08 — BUGFIX: per_stock_reassess.py holding_strategies DB 写回 + JSON 最终移除 ### 发现 Dad 反馈我的 v4 手动分析(买入区3.62~3.72)与 13:01 cron 报告(买入区3.78~3.87)不一致。经审计发现两个问题: 1. **per_stock_reassess.py** 调用 reassess_with_context() 后不写 holding_strategies 表(只写 watchlist_stocks 表)。而 read_decisions() 从 holding_strategies 读 → 新策略数据不可见 2. **decisions.json** 仍有残余依赖,2条旧数据的文件被当作权威源 ### 修改 - `per_stock_reassess.py`:策略更新后追加 `write_holding_strategy()` 写入 DB,持仓/自选全部写回 holding_strategies 表 - `per_stock_reassess.py`:移除 DECISIONS_PATH 常量 + 清理 JSON 死代码注释 - 同步修复 profile 副本(`~/.hermes/profiles/position-analyst/scripts/per_stock_reassess.py`) - `sync_db_to_json.py`:删除(DB→JSON 反向同步,无调用者) - `decisions.json`(两处):删除,备份为 .bak ### 审计确认 - MoFin/scripts/ + web-dashboard/ + profile/scripts/ 三个目录下的 active python 代码 **均不再直接读写 decisions.json** - 所有策略数据通过 `mo_data.read_decisions()` → DB holding_strategies 表 读取 - 所有策略写入通过 `mofin_db.write_holding_strategy()` → DB holding_strategies 表 ### 验证 - `per_stock_reassess 688981` → 输出 `[DB] holding_strategies 已更新: 688981` - DB holding_strategies 从 1 条 → 17 条 - web-dashboard PID 4003859 正常运行,API 通过 DB 读取