# 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）
- LLM 连通：opencode-go 三 Key 正常
