Files
MoFin/analyst-knowledge-log.md
T

334 lines
21 KiB
Markdown
Raw 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.
## 2026-07-19 20:58 XMPP bot on_disconnect 递归重连修复
**发现了什么:** zhiwei XMPP bot 从 20:53:57 开始反复输出 "XMPP 断开" × 27次,最后 "重连失败: maximum recursion depth exceeded"。slixmpp 的 `reconnect()` 在连接失败时会再次触发 disconnect 事件,而 `on_disconnect` 无条件调用 `reconnect()`,形成无限递归。
**修改了什么:**
- 文件: `MoFin/deploy/bot/xmpp_agent_core.py`
-`XmppAgent.__init__` 添加 `self._reconnecting = False`
- `on_disconnect` 判断 `_reconnecting` 为 True 时直接 return
- 重连前后用 try/finally 管理标志位
**效果预期:** 断线后最多一次 reconnect 尝试,不会爆栈。重连失败后保持静默,靠 systemd 的 Restart=always 自动恢复。
## 2026-07-02 11:30 macro_context_collector.py Pattern 9 百分比阈值修复 + 跨句涨幅匹配修复
**发现了什么:** 自愈执行器报告false positive——"中概领涨"被标记为HIGH风险。追踪发现Pattern 9 `指数[^。]*?(?:跌幅|下跌)[^。]*?[2-9]%`有两个Bug
1. 小数阈值Bug: `[2-9]%`在"0.3%"中提取"3%"匹配,误将<2%跌幅标为HIGH
2. 跨句匹配Bug: "指数集体下跌的背景下...中概收涨近3%"中,`[^。]*?`跨过整个句子把"下跌"和"3%"连起来,但"下跌"是指美股下跌,"3%"是指中概收涨——两个不同语境
**修改了什么:**
- 文件: `/home/hmo/MoFin/scripts/macro_context_collector.py`
- Pattern 9: `指数[^。]*?(?:跌幅|下跌)[^。]*?[2-9]%`
`指数[^。]*?(?:跌幅[^。]{0,20}(?:扩大至|达|至|超|为|逾)[^。]*?(?<![0-9.])(?:[2-9]|[1-9][0-9])(?:\.\d+)?%|下跌(?!.*?涨)[^。]*?(?<![0-9.])(?:[2-9]|[1-9][0-9])(?:\.\d+)?%)`
- "跌幅"分支:要求跌幅后有测量词(扩大至/达/至/超/为/逾),防止"跌幅"和其他分句的"%"跨句匹配
- "下跌"分支:用`(?!.*?涨)`负向前瞻阻断含有"涨"的上下文(如"收涨近3%"
- 阈值修复:`(?<![0-9.])(?:[2-9]|[1-9][0-9])(?:\.\d+)?%` 防止"0.3%"中"3%"被提取
- Pattern 10: `(?:暴跌|重挫|熔断).*[5-9]%` → 同样加小数排除 + 改成`[5-9]|[1-9][0-9]`范围
- `_PATTERN_CHECKS``_KNOWN_BAD_SIGS`同步更新
- `intraday_health_check.py`: 宏观风险HIGH去重,防止同一风险状态累积多个TODO
## 2026-06-23 09:00 数据采集脚本修复
**发现了什么:** `market_watch.py` cron任务报错,exit code 1
- 错误:`ModuleNotFoundError: No module named 'mofin_db'`
- 原因:脚本在 `/home/hmo/.hermes/scripts/` 下运行,Python路径不包含 `/home/hmo/MoFin/`
**修改了什么:**
- 文件:`/home/hmo/.hermes/scripts/market_watch.py`
- 在第19行(`from mofin_db import` 之前)插入:
```python
import sys
sys.path.insert(0, '/home/hmo/MoFin')
```
**效果预期:** 下次cron触发时脚本能正常导入mofin_db并完成市场数据采集+SQLite写入。
**同步发现的策略检查问题:**
- 自选股18只全部处于买入区(价格距离买入区<3%),属正常范围
- 其中2只策略为空(楚江新材、中谷物流)— 需补充
- 整体仓位93.02%,弱势+深套占比41.9%>40%
## 2026-06-25 11:50 价格监控updated_at修复
**发现了什么:** 健康检查误报"价格数据649秒未更新(portfolio.json"
**根因链:**
1. `price_monitor.py` 写 portfolio.json 时仅在价格变化时写入,且不更新 `updated_at` 字段
2. 盘中价格稳定(如午盘横盘)→ portfolio.json 长时间不更新 → `updated_at` 停留在上次变动时间
3. `intraday_health_check.py` 检查 `updated_at` 是否超过10分钟无更新 → 触发误报
**修改了什么:**
- 文件:`/home/hmo/MoFin/price_monitor.py`(同步到 profile scripts 副本)
- 逻辑变更:
- 价格变化写入时:同时更新 `pf['updated_at']` 为当前时间
- 价格无变化时:每10分钟强制刷新一次 `updated_at`,避免横盘期误报
**效果预期:** 价格稳定横盘时 portfolio.json 的 updated_at 仍每10分钟刷新一次,健康检查不再误报。
## 2026-06-25 13:20 stale_push_wlin 流程修复:先重评后推送
**发现了什么:** 自选买入提醒中推送的策略数据可能滞后。例如13:11报告中德明利止盈810.78但现价882已超过。
**根因链:**
1. stale_push_wlin 只在 `[STRATEGY_STALE]` 标记的票触发重评,非stale的票直接推旧策略
2. 策略的止盈/止损/信号在推送前未刷新,可能出现推了止盈位但价格已超过的情况
3. Dad 指出正确流程应该是:推荐前先重评 → 重评确认有效再推
**修改了什么:**
- 文件:`/home/hmo/.hermes/profiles/position-analyst/scripts/stale_push_wlin.py`
- 逻辑变更:
- 收集所有在买入区的自选(stale + 非stale),统一先调 trigger_regen_sync() 重评
- 重评完成后重新读取 decisions.json 获取最新策略数据
- 用重评后的 timing_signal 重新判断是否可推(不再用旧 is_stale 标记)
- 同一次推送中可能有多只票,全部用新数据出报告
**效果预期:** 每次推送自选提醒前,所有预推票都经过最新一次策略重评,止盈/止损/信号不滞后。
## 2026-06-25 14:00 三点联动修复
**发现了什么:** Dad反馈三个问题:(1) price_monitor的区进区出/重评告警太吵 (2) 现金只有total字段,没有区分可用/冻结,买力算错 (3) 德明利卖出后未及时更新止盈
**修改了什么:**
**① price_monitor XMPP推送降噪**
- 文件:`/home/hmo/MoFin/price_monitor.py`
- 原:所有outputs(⚡进入买入区 🔄重评 📊新策略 ✅全量重评)全部推XMPP
- 改:只推 ⚠️止损跌破 和 🌀情景切换,其余只local print不推
**② 现金三级结构**
- 文件:`/home/hmo/MoFin/data/portfolio.json`
- 新增 `cash_available`(可用买力)和 `cash_frozen`(冻结在途)字段
- stale_push_wlin.pyload_cash() 和 calc_position() 优先读 cash_availablefallback到 cash
- 推荐中的买力计算现在用可用现金而非总现金
**③ 德明利自选策略重评**
- 文件中:`/home/hmo/MoFin/data/decisions.json`
- 原止盈810.78(已被涨停突破)
- 新止损810.0 新止盈1153.26 新买入区873.18~908.82 | RR=3.24
- 已保证自选策略和其他自选一样走自动重评流程
**效果预期:** 系统不再推送区进出/重评噪音,推荐用可用现金算买力,德明利自选随时可重新入场。
## 2026-06-25 14:50 price_monitor 自选止损止盈告警屏蔽
**发现了什么:** 德明利(001309)已清仓转自选,但 price_monitor 仍对其推送止盈警报("⚡ 德明利进入止盈区间"),数据无误但信号无意义,在Dad卖飞后伤口撒盐。
**根因:** get_trigger_zones() 对自选和持仓一视同仁返回止损/止盈区间,price_monitor检查所有active decision。
**修改了什么:**
- 文件:`/home/hmo/MoFin/price_monitor.py`
- 函数 `get_trigger_zones(d)`
- 买入区间:自选和持仓都监控(自选需要zone to trigger重评)
- 止损+止盈:仅持仓(share>0)才监控,自选不生成止损/止盈zone
**效果预期:** 已清仓的德明利/药明康德等自选不再推送止损止盈告警。买入区进出仍触发重评(内部不推送),保证策略持续更新。
## 2026-06-27 三维分析框架固化(news-flow-analysis skill
**发现了什么:** 协鑫能科(002015)分析暴露三个系统性问题:
1. 之前分析脱离大盘环境看个股,给出孤立判断
2. 资金流数据只看单日暴量不看连续性,被6/24→6/25反向流误导
3. 没有离场预警框架,只关注买入机会不关注风险信号
**新设了什么:**
- Skill: finance/news-flow-analysis — 新闻+资金流+大盘三维分析法
- 四个层面印证框架(消息面→技术面异常→资金流验证→资金性质追踪)
- 反向框架(离场预警信号)
- 六项报告输出规则(大盘→板块→个股异常→资金验证→资金性质→明确结论)
**自检清单(所有报告产出必须检查):**
□ 有没有考虑大盘环境?
□ 有没有考虑板块背景?
□ 逆势信号有没有识别?
□ 资金流向有没有验证?
□ 是单日暴量还是连续趋势?
□ 有没有离场预警的反向思考?
□ 结论有没有具体价格?
□ RIP原则:数据是否已核实(非模拟伪造)?
**效果预期:** 所有个股分析自动走三维印证流程,避免孤立判断。资金流分析看趋势不看单日。每份报告自带离场预警条件。
## 2026-06-29 数据新鲜度铁律(数据准确零容忍 · 违反记录)
**违反了什么:** 周一(6/29) 15:20 和 15:48 两次分析使用了 multi_tf_cache.json(周五 23:05 缓存的收盘价)作为价格依据,未拉取腾讯实时价。
**导致的错误:** 中芯国际 H 建议止损(实际+10%盈利)、建滔积层板建议止损(实际盈利+11%)、中国神华建议止损(实际反弹+15%)。完全错误的操作建议。
**根因:** LLM 分析时图方便读了本地缓存文件,没有执行「先拉实时价」这个基本动作。
**修复规则(永久写入):**
- 任何分析、报告、回复,第一行代码必须是拉取腾讯实时报价
- 严禁从 multi_tf_cache.json / decisions.json 等缓存文件直接读取价格数据来推操作建议
- 唯一允许的价格源:腾讯实时 APIqt.gtimg.cn)或 price_monitor 刚写入的 events/state
- 违反这条 → 自动回滚,追加到违规记录
## 2026-07-02 10:00 盘中自检 NameError 修复
**发现了什么:** 自愈执行器报 `盘中自检: 价格数据新鲜度检查失败: name 'mo_data' is not defined`
- 根因:`intraday_health_check.py` 第11行用 `from mo_data import read_portfolio`(直接导入函数),但第117行写的是 `mo_data.read_portfolio()`(当模块名调用)
**修改了什么:**
- 文件:`/home/hmo/MoFin/scripts/intraday_health_check.py`
- 变更:`mo_data.read_portfolio()` → `read_portfolio()`
**效果预期:** 盘中自检不再因这个 NameError 报异常,能正常检查 portfolio.json 数据新鲜度。
## 2026-07-02 10:51 宏观风险HIGH升级应对
**发现了什么:** 自愈执行器上报当日第二次HIGH信号(科创50扩大至-5%、创业板-4%)。同时KOSPI从-7%收窄至-3%(改善方向信号)。
**修改了什么:**
- 文件:`macro_risk_state.json`
- 变更:升级模式更新state,分别标注恶化(科创50/创业板扩大跌幅)和改善(KOSPI收窄、恒指+1.3%)两个方向,避免笼统"附加新数据点"
**萃取知识:**
1. B类板块冲击下,防御仓位(紫金+6.8%、黄金ETF+2%、神华+1.2%)当日有效对冲科技股损失,验证了组合构建合理性
2. 中芯国际A(688981)是当日最危险持仓(距止损2.14%),中芯国际AH走势分化(A-2.96% vs H-8.05%)值得关注——A股有溢价保护,H股更直接暴露于全球半导体卖压
3. 分歧检测bias=opportunity + A/H科技背离>5pp → 极端背离时不能简单看空,A股科技暴跌时港股科技反而上涨,资金在跨市场迁移而非逃离科技板块
## 2026-07-02 盘中 价格数据新鲜度检查格式不兼容
**发现了什么:** 自愈执行器报 `盘中自检: 价格数据新鲜度检查失败: unconverted data remains: :06`
**根因:** `intraday_health_check.py` 的 `datetime.strptime(pf_updated, "%Y-%m-%d %H:%M:%S")` 硬编码要求带秒格式,但 `price_monitor.py` 等写入 `updated_at` 用 `strftime('%Y-%m-%d %H:%M')` 无秒格式。当 `read_portfolio()` 回退到 JSON 冷备时读到无秒时间戳 → 解析失败。
**修改了什么:**
- 文件:`/home/hmo/MoFin/scripts/intraday_health_check.py`
- 变更:引入 `_parse_updated_at()` 函数,先试带秒格式 `%Y-%m-%d %H:%M:%S`,失败则回退无秒格式 `%Y-%m-%d %H:%M`;两次都失败时明确记录"格式无法解析"而非抛异常
**效果预期:** 无论 `updated_at` 是 `'2026-07-02 10:43'`(无秒)还是 `'2026-07-02 10:43:53'`(有秒),都能正确解析并计算新鲜度。
## 2026-07-02 11:47 宏风险HIGH持续——系统无故障,已知市场事件延续
**发现了什么:** 自愈执行器报盘中自检→宏观风险HIGH。核实后发现该风险已在11:27向Dad推送过完整报告,且到损位均未触发。这不是系统故障,是已知市场状态(全球半导体抛售)的延续。
**附带的data issue** portfolio.json的`currency`字段将所有港股(8只)标为CNY而非HKD。影响跨币种计算准确性。
- 根因追踪:`import_holding_xls.py`(line67)正确设HKD,但`strategy_lifecycle.py`(line2027)的`setdefault('currency','CNY')`在策略更新时覆盖了币种标记
- 修复计划:盘后(非交易时段)统一排查并修正currency字段在数据管道中的传播路径
**萃取知识:**
1. 自愈执行器对宏风险的报警是设计行为(health check标记risk=high→写TODO→升级处理),需要按"已知市场状态"处理而非"系统故障"
| 腾讯(00700)距止损2.76%是所有持仓中最接近止损位的——本轮冲击前未被重点标记,暴露了"只关注风险最大的几个"忽略了中游风险
## 2026-07-04 周末宏观风险评估(首次独立周末扫描)
**发现了什么:** 采集器08:00输出2条HIGH信号,但经LLM深度分析确认为误报:
1. 「单一押注有色 百亿基金栽在盛达资源」→ LOW(基金级个股新闻,误报模式4:公司/基金事件误标为宏观风险)
2. 「北约领导人宣布伊朗不得拥核」→ MEDIUM(政治声明,但整体美伊趋势为降级降级谈判/石油恢复/海峡共管推进)
**执行操作:** signal_news ID=760 修正覆盖(UPDATE) + ID=761 插入正确MEDIUM信号
**综合判定:** MEDIUM(新闻维度MEDIUM+组合高暴露MEDIUM→2个MEDIUM
**关键发现:** 周末数据采集(Curl东方财富首页+macro_raw_news 50条)确认一周来最大的系统性风险变化是——美伊冲突全面降级(OPEC产量恢复、伊朗洽售石油、多哈会谈推进),而这在signal_news历史HIGH信号中完全未体现。采集器只抓取了北约声明这个单独事件,忽略了整体趋势改善的上下文。**周末扫描的核心价值在于提供采集器无法做到的语境整合判断。**
**萃取知识:**
1. 周末扫描的第一步(检查未评级新闻)如果为空,必须主动从curl数据中提取Fetched新事件INSERT入库——不能直接输出"无未评级新闻"
2. 背离检测器在非交易日持续运行,divergence_state的level=none+bias=opportunity为本次MEDIUM判定提供了关键支撑——无市场结构风险
3. 非农数据"弱于预期"是反向逻辑(鸽派利好),采集器关键词无法区分方向性语义,LLM修正时需特别注意误报模式11
## 2026-07-08 09:59 全面审计修复(JSON违规 + 策略表空 + 潜力股管道中断)
**发现了什么:** Dad质疑三个问题:(1)重点操作只关注持仓 (2)看不到新自选潜力股 (3)系统到底正不正常。全面审计后发现了系统性漏洞:
**问题1 — JSON违规(最严重):**
6个脚本仍直接读写portfolio.json/decisions.json/watchlist.json,违反Dad 2026-07-06铁律。根因:6月DB迁移后未审计所有引用文件。
修复:price_monitor.py、price_data_inject.py、market_insight.py、inspect_decisions.py、strategy_summary.py、fix_portfolio_prices.py、pre-flight-check.py 全部改为DB APImo_data.read_* / mofin_db.write_*)。同步清理4处误导性注释。
**问题2 — holding_strategies表空(策略丢失):**
19条策略全丢失,read_decisions()返回0条。根因不明(可能是regenerate_all覆盖或手动误操作)。
修复:从decisions.json重建holding_strategies表(INSERT 19条)。
**问题3 — 候选→自选提拔管道断裂:**
market_insight.py存在UnboundLocalErrormarket_path变量未定义),知微洞察生成cron自6/19起报错停摆。另portfolio.json直读违规也在此文件中。
修复:market_path前置定义+改为DB读持仓。
**问题4 — 开盘race condition误报:**
intraday_health_check在09:01检查价格新鲜度,price_monitor刚启动未完成首次更新。
修复:加09:00-09:10 grace period跳过检查。
**问题5 — 健康检查prompt检查JSON文件:**
「系统健康检查-开盘前」LLM cron prompt指示检查JSON文件是否存在,缺失时自动生成watchlist.json。
修复:prompt改为从DB检查数据完整性,明确禁止检查或修复JSON文件。
**涉及文件:**
scripts/price_monitor.py, price_data_inject.py, market_insight.py, inspect_decisions.py, strategy_summary.py, fix_portfolio_prices.py, pre-flight-check.py, intraday_health_check.py
cron prompt for 系统健康检查-开盘前 (job_id: 37c02f4d7df9)
|analyst-knowledge-log.md (本文件)
## 2026-07-09 stale_push_wlin.py — 按Dad确认流程重构推送逻辑
**发现了什么:**
- 推送流程没有重评冷却 — 每30分钟cron都调用per_stock_reassess重评同一批在买入区的股票
- Zone notes(进区间但不可操作的说明消息)冷却只有30分钟 → 太短
- 第582行 `if not actionable: return 0` 导致zone_notes为空时也早退,有区间说明时静默不发
- 标题和格式没有正确区分"有推荐操作"和"仅有区间说明"
**修改了什么:**
- 文件: `scripts/stale_push_wlin.py`
- 加 `REASSESS_COOLDOWN_HOURS = 4` + `get_last_reassess_time()` / `is_due_for_reassess()` — 查DB holding_strategies.updated_at 决定是否跳过重评
- 重评调用前过滤:只对冷却期外的股票调 trigger_regen_sync()
- Zone notes 冷却30分钟→4小时(匹配重评冷却)
- 早退bug修复:`if not actionable and not zone_notes: return 0`
- 标题自适应:有推荐→【知微】自选买入提醒 | 仅区间→【知微】操作区间提醒
|- 操作建议标题只在有可操作项时显示(`if actionable:`
## 2026-07-09 xiaoguo_signal_consumer — 信号堆积修复
**发现了什么:**
- 小果信号管道堆积33条未处理(阈值30),自愈系统报警
- 根因:xiaoguo_signal_consumer 的SQL用 `date(created_at)=today` 过滤,旧信号永远不被消费
- `SIGNAL_MAX_AGE_HOURS=4` 已定义但未被使用
- `mo_data.get_price` 导入在 cron 环境下失败(root mo_data.py 无 get_pricescripts/mo_data.py 有)
**修改了什么:**
- 文件: `scripts/xiaoguo_signal_consumer.py`, `scripts/intraday_health_check.py`, `scripts/system_audit.py`, `system_audit.py`
1. consumer: date(today) → created_at > -4h 年龄过滤(使用 SIGNAL_MAX_AGE_HOURS 常量)
2. consumer: 无时效内信号时自动标记过期信号为已处理(防堆积)
3. consumer: 修复 import 冲突 — 移除 mo_data.get_price,改用腾讯API直连 fallback
4. health_check/system_audit: 堆积检查也限定4h内
**效果预期:**
- 过期信号不再堆积 → 健康检查正常
- 消费者只处理时效内信号 → 最新信号优先
- 无信号时自动清理过期堆积 → 不积压
- 区间说明格式改为: "→ 进入操作区间,但重评结果: {reason}"### 2026-07-19 22:32 Dad资产更新
- **来源**: Dad XMPP截图+文字确认
- **现金**: 0→241,330 (Dad确认)
- **持仓**: 12只,与DB及截图一致(OCR确认)
- **总资产**: 783,321 CNY
- **仓位**: 69.19%
- **变更**: cash_log id=17, portfolio_summary已更新, portfolio.json已同步
- **问题**: XMPP bot此前3次超时/丢图导致Dad截图未处理,已人工补偿
### 2026-07-19 23:35 Dad重导holding.xls全量更新
- **来源**: Dad从券商重导holding.xls23:30更新)
- **持仓变化**: 12只→14只
- 新增: 300308 中际旭创 (100股, MV 97,946)
- 新增: 300035 中科电气 (1400股, MV 16,912)
- 之前我OCR截图漏了这两只,原因是字幕混淆(中科电气与宁德时代代码相邻被合并,中际旭创在截图折叠区)
- **现金**: 241,330.80 (不变)
- **冻结**: 0.05 (不变)
- **总市值**: 655,231 (holding.xls当前价)
- **总资产**: 896,562
- **仓位**: 73.08%
- **截图头部659,354与xls的655,231差异来源**: 截图是13:01价格,xls是23:30价格,中间价格波动导致
## 2026-07-28 13:51 盘中系统性大跌应对 — 韩国二次熔断+亚太科技股暴跌
**发现了什么:**
今天KOSPI两度触发熔断(一度跌超8%),日经-4%,MSCI亚太较6月高点跌10%,科创50-5.52%/创业板-6.55%。但上证仅-1.35%、恒指-0.34%、比亚迪/腾讯逆势上涨。
**关键判断:**
- 定性为板块冲击(B类),非全面系统性。上证/恒指有韧性是关键区分信号
- 比亚迪+0.51%、腾讯+0.23%逆势上涨验证了A/H股科技脱钩叙事
- 信号id=1441已处理(processed=1),自愈执行器报警的是状态文件过期导致的重复触发
**止损距扫描结果:**
- 中芯国际(688981)现价138.41,跌破旧止损141.21(-2.0%),但策略状态为closed,需发布新策略
- 丘钛科技(01478)距止损仅1.2%,临界
- 中国神华(01088)距止损仅1.6%,临界
- 中际旭创(300308)距止损5.2%,策略已closed
**萃取知识:**
1. KOSPI熔断+日韩暴跌传导至A股科技股,但上证/恒指未破位=非系统性+板块冲击。判断A股的不可一概而论。
2. 比亚迪、腾讯在科技暴跌日逆势上涨,说明个股基本面>板块情绪。不能简单用"科技股暴跌"标签覆盖所有科技持仓。
3. 自愈执行器重复报警的根本原因是state文件expired后未及时更新,导致管道认为这是未处理的新问题。修复措施:更新state文件。