276 lines
12 KiB
Markdown
276 lines
12 KiB
Markdown
# MoFin 架构改革 — 变更日志
|
||
|
||
> 日期:2026-06-29 ~ 2026-06-30
|
||
> 执行:Sisyphus (小小莫)
|
||
> 背景:total_assets 频繁计算错误(遗漏 frozen_cash)、币种混淆(CNY/HKD)、港股 15 分钟延迟
|
||
|
||
---
|
||
|
||
## 新增文件
|
||
|
||
| 文件 | 用途 |
|
||
|------|------|
|
||
| `mo_models.py` | 统一数据模型 —— `calc_total_assets()`, `is_hk_stock()`, `get_hk_rate()`, `to_cny()`, `validate_portfolio()` |
|
||
| `mo_provider.py` | DSA 数据源适配器 —— 包装 daily_stock_analysis 的 16 个 Fetcher,TDX 优先、DSA 自动 fallback |
|
||
| `mo_config.py` | 配置单例 —— 替代散落在 10+ 文件中的硬编码路径 |
|
||
| `mo_bridge.py` | DSA 集成桥 —— 从 DSA 获取大盘复盘/新闻,注入 MoFin 分析 prompt |
|
||
| `scripts/data_validate.py` | 数据自检脚本(知微创建) |
|
||
| `scripts/holdings_reconciliation.py` | 持仓一致性校验(知微创建) |
|
||
| `scripts/process_trade.py` | 成交处理脚本(知微创建) |
|
||
| `docs/portfolio-data-model.md` | 数据模型文档(知微创建) |
|
||
| `data_freshness.py` | 数据新鲜度校验(知微创建) |
|
||
|
||
## 修改文件
|
||
|
||
| 文件 | 变更 |
|
||
|------|------|
|
||
| `price_monitor.py` | ① 合并两处重复 total_assets 计算 → 统一调用 `mo_models.calc_total_assets()` ② is_hk_stock 检测切换为 `mo_models.is_hk_stock()` ③ HK_RATE 获取切换为 `mo_models.get_hk_rate()` ④ **港股行情来源从腾讯 gtimg(15分钟延迟)切到东方财富 push2.eastmoney.com(实时)** |
|
||
| `server.py:960` | `total_assets` 公式补上 `frozen_cash` |
|
||
| `strategy_lifecycle.py:428` | `currency_utils`(不存在)→ `mo_models` |
|
||
| `scripts/stale_push_wlin.py` | ① is_hk_stock 统一走 mo_models ② lot_cost 去掉硬编码 0.866 ③ fallback total_assets 改为 mo_models |
|
||
| `scripts/stock_scorer.py` | `len(code)<=5` 误判 → `mo_models.is_hk_stock()` |
|
||
| `scripts/holdings_reconciliation.py:98` | total_assets 公式补上 frozen_cash |
|
||
| `scripts/import_holding_xls.py:93` | total_assets 公式补上 frozen_cash |
|
||
| `mo_bridge.py` | DSA 路径自动检测(服务器 /home/hmo/daily-stock-analysis) |
|
||
| `mo_provider.py` | DSA 路径自动检测 + Tencent API 集成 |
|
||
|
||
## 服务器部署
|
||
|
||
| 操作 | 状态 |
|
||
|------|------|
|
||
| `git pull` 拉取最新代码 | ✅ |
|
||
| DSA 源码上传到 `/home/hmo/daily-stock-analysis/` | ✅ |
|
||
| DSA .env 配置(opencode-go 三 Key:新Key → 旧Key → Key3) | ✅ |
|
||
| `pip install` 安装 DSA 依赖(litellm, sqlalchemy, pandas, akshare 等) | ✅ |
|
||
| Python 语法检查(37 个文件) | ✅ |
|
||
| 实盘数据验证(total_assets=967712.85 零误差) | ✅ |
|
||
| 港股实时行情测试(8/8 全成功) | ✅ |
|
||
| DSA Web 服务启动(FastAPI + Swagger) | ✅ |
|
||
|
||
### DSA Web 访问
|
||
|
||
- **地址**:`http://192.168.1.246:8001`
|
||
- **API 文档**:`http://192.168.1.246:8001/docs`(Swagger 交互界面)
|
||
- **触发分析**:`POST /api/v1/analysis/analyze`
|
||
- **开机自启**:已加入 crontab `@reboot`
|
||
|
||
> 注意:React 前端未构建(apps/dsa-web/ 未上传,需要 Node.js),目前只提供 API 服务和 Swagger 文档界面。 |
|
||
|
||
## 架构变化总结
|
||
|
||
**之前**:
|
||
```
|
||
total_assets 在 6+ 个文件中各自计算,3 个漏了 frozen_cash
|
||
is_hk_stock 有 3 种不同实现,len(code)<=5 会误判深市A股
|
||
hk_rate 硬编码 4 个不同值(0.866/0.87/0.8664/0.8700)
|
||
30+ 文件绕过 mofin_db 直接读写 JSON
|
||
港股走腾讯 gtimg.cn — 15分钟延迟
|
||
data_dir 在 10+ 个文件中各自硬编码
|
||
```
|
||
|
||
**之后**:
|
||
```
|
||
total_assets → 唯一入口 mo_models.calc_total_assets(pf)
|
||
is_hk_stock → 唯一入口 mo_models.is_hk_stock(code)
|
||
hk_rate → 唯一入口 mo_models.get_hk_rate()(API → 缓存 → 0.87 fallback)
|
||
港股行情 → 东方财富 push2.eastmoney.com(实时,免费)
|
||
→ fallback: 腾讯 gtimg.cn(15分钟延迟兜底)
|
||
DSA 数据源 → mo_provider(16 Fetcher 自动 fallback)
|
||
DSA 情报 → mo_bridge(大盘复盘注入 MoFin 分析)
|
||
配置路径 → mo_config(单例,不再散落)
|
||
```
|
||
|
||
## 待办(服务器端)
|
||
|
||
```bash
|
||
# 知微需要确认的:
|
||
# 1. 明天开盘后验证港股价格是实时的(不再滞后15分钟)
|
||
# 2. migrate_all.py 需要跑一次(补齐 stock_sector_map/watchlist/market_context 表)
|
||
cd /home/hmo/MoFin && python3 migrate_all.py
|
||
```
|
||
|
||
## 测试记录
|
||
|
||
- 语法检查:37/37 文件通过
|
||
- 导入链路:16/17 核心模块通过
|
||
- mo_models 自检:is_hk_stock 7 用例全通过、calc_total_assets 零误差
|
||
- 实盘验证:total_assets=967712.85 = stored 967712.85
|
||
- 港股实时:8/8 港股东方财富拉取成功
|
||
- DSA 集成:DataFetcherManager 加载成功(6 Fetcher)
|
||
|
||
---
|
||
|
||
## 2026-06-30 — DSA Web + 选股链路 + 小果EasyTier
|
||
|
||
### DSA Web 界面上线
|
||
|
||
- DSA 完整源码上传到 `/home/hmo/daily-stock-analysis/`
|
||
- React 前端构建并部署(40个静态文件)
|
||
- FastAPI 服务运行在 `http://192.168.1.246:8001`
|
||
- Swagger API 文档: `http://192.168.1.246:8001/docs`
|
||
- 开机自启已加入 crontab
|
||
- 防火墙 8001 端口已开放
|
||
|
||
### DSA 三项功能对接 MoFin
|
||
|
||
| 功能 | 文件 | 说明 |
|
||
|------|------|------|
|
||
| 新闻搜索 | `mo_bridge.get_stock_news()` | DSA SearchService → akshare 东方财富 fallback |
|
||
| 大盘复盘 | `mo_bridge.get_market_review()` | 缓存优先(24h),cron 不阻塞 |
|
||
| 策略参考 | `mo_bridge.get_stock_analysis()` | 独立调用,`mo_dsa_opinion.py 00700 腾讯控股` |
|
||
| 策略注入 | `strategy_lifecycle.reassess_with_context()` | DSA 上下文自动追加到 MoFin 分析 prompt |
|
||
|
||
### DSA 策略注入
|
||
|
||
`strategy_lifecycle.py` 的 `reassess_with_context()` 中增加了 DSA 上下文注入:
|
||
- 自动识别港股/A股 → 选择对应区域大盘复盘
|
||
- `enrich_analysis_context()` 追加到 `macro_desc`
|
||
- DSA 不可用时静默跳过,不影响 MoFin
|
||
|
||
### AlphaSift 选股(默认关闭)
|
||
|
||
- AlphaSift 已安装(`pip install alphasift`)
|
||
- 8 种策略可用
|
||
- `mo_alphasift_bridge.py` 支持多策略并行选股
|
||
- 默认关闭(`ALPHASIFT_ENABLED=false`)
|
||
- 启用: `ALPHASIFT_ENABLED=true python3 mo_alphasift_bridge.py`
|
||
- 每条自选股记录包含:来源策略、评分、日期、因子得分、选股理由
|
||
|
||
### 原有选股链路修复
|
||
|
||
**问题**:`market_watch.py` 和 `market_screener.py` 从未加入 cron,导致零产出。
|
||
|
||
**修复**:
|
||
- `market_watch.py` → `market.json`(90个板块,每30分钟更新)
|
||
- `market_screener.py` → 小果 LLM → `candidate_pool.json`(每30分钟)
|
||
- 已加入 crontab:`*/30 9-15 * * 1-5`
|
||
|
||
### 小果连接统一(EasyTier 兼容)
|
||
|
||
**问题**:5个文件硬编码 `192.168.1.122`,小果离开局域网后无法连接。
|
||
|
||
**修复**:
|
||
- 全部改为 `node122`(机器名)
|
||
- `/etc/hosts` 配置:
|
||
- LAN: `192.168.1.122 node122`
|
||
- EasyTier VPN: `10.144.144.2 node122`
|
||
- 涉及文件:`market_screener.py`, `xiaoguo_scanner.py`, `xiaoguo_news_processor.py`, `intraday_health_check.py`, `ocr_client.py`
|
||
- `mo_config.py` 统一管理:`xiaoguo_host="node122"`, `xiaoguo_port=18003`
|
||
|
||
### 待办
|
||
|
||
```bash
|
||
# 知微需要确认:
|
||
# 1. 明天开盘验证 market_screener 产出有效候选股
|
||
# 2. 如需启用 AlphaSift: ALPHASIFT_ENABLED=true python3 mo_alphasift_bridge.py
|
||
```
|
||
- LLM 连通:opencode-go 三 Key 正常
|
||
|
||
---
|
||
|
||
## 2026-07-01 — DB 迁移 + 唯一价格源 + 保活机制
|
||
|
||
### JSON → DB 完整迁移(币种约束)
|
||
|
||
**4 张核心表加 `currency` 列**(`NOT NULL DEFAULT 'CNY'`):
|
||
- `holdings` — 新增 price, market_value, change_pct, currency
|
||
- `holding_strategies` — 新增 15 列(currency, action, trigger_json, changelog_json 等)
|
||
- `portfolio_summary` — 新增 total_mv, frozen_cash, currency
|
||
- `watchlist_stocks` — 新增 10 列(price, entry_low/high, stop_loss, currency, source 等)
|
||
|
||
**写入保护**:所有 `write_*` 函数校验币种,写入 USD 直接拒绝。
|
||
|
||
**所有高危写入者已迁移到 DB**:
|
||
- `price_monitor.py` — holdings + portfolio_summary + watchlist
|
||
- `strategy_lifecycle.py` — regenerate_all → holdings + strategies + watchlist + summary
|
||
- `holdings_reconciliation.py` — holdings + strategies + summary
|
||
- `process_trade.py` — holdings + strategies
|
||
- `import_holding_xls.py` — holdings + summary
|
||
- JSON 保留作为冷备(DB 写失败不影响 JSON 写入)
|
||
|
||
### 唯一价格源(消除多入口拉价)
|
||
|
||
**问题**:17 个文件各自调用腾讯 `qt.gtimg.cn` 拉价,互不通信,币种转换不一致。
|
||
|
||
**修复**:所有价格读取改为 DB 优先,腾讯 API 仅作 fallback。
|
||
|
||
| 文件 | 改动 |
|
||
|------|------|
|
||
| `mofin_db.py` | 新增 `get_price_from_db()` / `get_prices_batch_from_db()` 通用工具 |
|
||
| `strategy_lifecycle.py` | `batch_fetch_prices()` + `get_price_tencent()` DB 优先 |
|
||
| `stale_push_wlin.py` | `fetch_trend_data()` + 移除硬编码 0.87 fallback |
|
||
| `per_stock_reassess.py` | 价格从 DB 读,不再自拉腾讯 |
|
||
| `stale_detector.py` | `fetch_prices()` DB 优先 |
|
||
| `server.py` | `/api/portfolio` DB 优先 |
|
||
| `strategy_evaluator.py` | `fetch_prices()` DB 优先 |
|
||
| `technical_analysis.py` | `get_quote()` DB 优先,移除 60s 缓存 |
|
||
| `stock_profile.py` | `get_quote()` DB 优先 |
|
||
| `collect_evaluation_data.py` | `fetch_tencent_data()` DB 优先 |
|
||
| `branch_scanner.py` (×2) | `get_price()` DB 优先 |
|
||
| `strategy_review.py` | `fetch_price()` DB 优先 |
|
||
| `xiaoguo_signal_consumer.py` | `fetch_quote()` DB 优先 |
|
||
|
||
### price_monitor 保活
|
||
|
||
- **cron**:交易日 9:00-16:00 每 2 分钟 `price_monitor.py`
|
||
- **健康检查**:每 10 分钟检查进程 + DB 新鲜度
|
||
- **健康检查脚本**:`scripts/check_price_monitor.py`
|
||
|
||
### NEAR_SL 误报修复
|
||
|
||
- `stale_detector.py`:距止损 <5% 时加浮盈判断
|
||
- 浮盈 >5% → `[PROFIT_PROTECT]`(利润保护,不报警)
|
||
- 浮盈 ≤5% → `[NEAR_SL]`(真正危险)
|
||
|
||
### 小果连接统一
|
||
|
||
- 全部改为 `node122`(机器名),`/etc/hosts` 自动解析 LAN/EasyTier
|
||
- 涉及 5 个文件:market_screener, xiaoguo_scanner, xiaoguo_news_processor, intraday_health_check, ocr_client
|
||
|
||
### 待办(知微)
|
||
|
||
- [ ] 明天开盘验证 price_monitor 正常更新 DB
|
||
- [ ] 验证 `[PROFIT_PROTECT]` 标记取代 `[NEAR_SL]` 误报
|
||
- [ ] 如果启用 AlphaSift:`ALPHASIFT_ENABLED=true python3 mo_alphasift_bridge.py`
|
||
|
||
---
|
||
|
||
## 2026-07-01 (续) — 统一读取层 + 现金日志
|
||
|
||
### mo_data.py — 统一数据读取层
|
||
|
||
替代所有 `json.load(open(portfolio.json))` 等直接读 JSON 的方式。
|
||
|
||
```
|
||
mo_data.read_portfolio() → 返回 portfolio.json 等价 dict(数据来自 DB)
|
||
mo_data.read_decisions() → 返回 decisions.json 等价 dict
|
||
mo_data.read_watchlist() → 返回 watchlist.json 等价 dict
|
||
```
|
||
|
||
- DB 优先,JSON 冷备(DB 不可用时自动 fallback)
|
||
- 返回结构完全兼容旧代码,无需改调用方
|
||
- 16 个文件已通过批量替换迁移
|
||
|
||
### cash_log 表 — 现金变更追踪
|
||
|
||
新表 `cash_log`,记录每次现金变动:
|
||
|
||
| 字段 | 说明 |
|
||
|------|------|
|
||
| `cash_before/after` | 变更前后可用现金 |
|
||
| `frozen_before/after` | 变更前后冻结资金 |
|
||
| `change_amount` | 变动额(正=入金/卖股,负=出金/买股) |
|
||
| `source` | 来源:screenshot / manual / import_xls / trade |
|
||
| `note` | 备注 |
|
||
| `verified` | 是否经 Dad 确认 |
|
||
|
||
写入:`write_cash_log(conn, data)`
|
||
查询:`query_cash_log(conn, limit=20)`
|
||
|
||
### 待办(知微)
|
||
|
||
- [ ] 明天开盘验证 price_monitor 正常更新 DB
|
||
- [ ] 验证 `[PROFIT_PROTECT]` 标记取代 `[NEAR_SL]` 误报
|
||
- [ ] cash_log 目前只是建表,需要在截图导入/手动改现金时调用 `write_cash_log` 记录变更
|
||
- [ ] 如果启用 AlphaSift:`ALPHASIFT_ENABLED=true python3 mo_alphasift_bridge.py`
|