# MoFin 开发规范

> 版本：1.0 | 日期：2026-07-03 | 维护：Sisyphus + Zhiwei

---

## 一、代码结构

### 1.1 无重复代码

- **一个功能一个文件**。禁止出现两份同名/同功能的 `.py` 文件。
- 公共逻辑抽取到 `mo_models.py` / `mo_data.py` / `mofin_db.py`，各脚本只调不写。
- 每次新增功能前，先搜索是否已有类似实现。

### 1.2 文件职责

| 文件 | 职责 | 允许做的事 |
|------|------|-----------|
| `mo_models.py` | 统一数据模型 | `calc_total_assets`, `is_hk_stock`, `to_cny`, `get_hk_rate` |
| `mo_data.py` | 统一读取层 | `read_portfolio`, `read_decisions`, `read_watchlist` — 只读 DB |
| `mofin_db.py` | DB 层 | 建表、写函数、查询函数 — 所有 SQL 集中在这里 |
| `price_monitor.py` | 价格更新 | 从 API 拉价格，写入 holdings + portfolio_summary |
| `strategy_lifecycle.py` | 策略生命周期 | `regenerate_all` + quality gates |
| `scripts/` | 工具/一次性脚本 | 不通过 cron 调用的辅助工具 |

### 1.3 禁止事项

- ❌ 在业务脚本里直接写 SQL（必须通过 `mofin_db.py`）
- ❌ 在业务脚本里直接 `json.load(open(...))` 读数据（必须通过 `mo_data.py`）
- ❌ 自己实现 `calc_total_assets` / `is_hk_stock`（必须用 `mo_models.py`）
- ❌ 自己实现 `read_portfolio` / `read_decisions` / `read_watchlist`（必须用 `mo_data.py`）
- ❌ 新增 JSON 数据文件（所有持久化数据必须走 DB）

---

## 二、数据规范

### 2.1 币种

| 场景 | 港股 | A股 |
|------|------|-----|
| 个股 price/cost/stop_loss | HKD | CNY |
| `currency` 字段 | `'HKD'` | `'CNY'` |
| 总资产/总市值 | CNY（汇总时 × 汇率） | CNY |
| 个股 P&L 计算 | 同币种 `(price-cost)/cost` | 同币种 |
| API/报告输出 | 标注 `(HKD)` | 无标注 |

**铁律**：禁止跨币种比较或加减。`calc_total_assets` / `calc_total_mv` 是唯一可以做币种转换的地方。

### 2.2 汇率

- 汇率必须通过 `get_hk_rate()` 获取（实时 API + 缓存）
- 禁止硬编码 `0.87` / `0.866` 等值（仅 `hk_rate.py` 内部兜底例外）
- 汇率 API 不可达时自动使用上次缓存值

### 2.3 数据源

- **唯一写入源**：`price_monitor.py` 是唯一的价格写入者
- **DB 优先读取**：所有数据读取走 `mo_data.py` → DB
- **API 兜底**：DB 无数据时，`price_monitor` 从外部 API 拉取
- **禁止各脚本各自拉 API** 写数据

---

## 三、DB 规范

### 3.1 表设计

- 新增表必须在 `mofin_db.py` 的 `init_all_tables()` 中定义
- 新表必须有对应的 `write_*()` 函数和 `query_*()` 函数
- 涉及币种的列必须有 `CHECK(currency IN ('CNY','HKD'))` 约束
- `code` 列建议加 UNIQUE INDEX

### 3.2 迁移

- 加列用 `ALTER TABLE ADD COLUMN`，幂等（`IF NOT EXISTS` 或 try/except）
- 不直接改线上 DB 结构，通过 `mofin_db.py` 的 init 函数执行
- 数据修正脚本放到 `scripts/` 目录

---

## 四、LLM Prompt 规范

### 4.1 强制要求

| # | 规则 | 检查方法 |
|---|------|---------|
| S1 | 不引用 JSON 文件名 | grep `\.json` |
| S2 | 港股价格标注 `(HKD)` | 检查 prompt 模板 |
| S3 | 不指示跨币种直接比较 | 人工审查 |
| S4 | 数据源标注为 DB 表名 | `strategy_evaluations` 不是 `evaluation.json` |
| S5 | 不在 prompt 里硬编码路径 | grep `/home/hmo/` |

### 4.2 新增/修改 Prompt

- 在 `prompt_manager/init_registry.py` 注册新版本
- 旧版本标记 `status="deprecated"`，保留历史记录
- 更新后运行测试脚本验证无 JSON 引用

---

## 五、Cron 规范

### 5.1 调度

- Cron 脚本必须能独立运行（`python3 xxx.py` 不报错）
- 处理异常，不因单个 API 失败而崩溃
- 关键步骤加 try/except + `print(..., file=sys.stderr)` 日志

### 5.2 幂等性

- `price_monitor` 价格不变时不写入 DB
- `regenerate_all` 可重复运行不产生副作用
- 所有写操作支持重复执行

---

## 六、开发流程

### 6.1 日常开发步骤

```
1. 理解需求 → 检查现有代码 → 确定改动范围
2. 本地修改代码
3. 运行 scripts/run_all_tests.py（必须全部通过）
4. 如有新增功能，补充 TEST_PLAN.md 测试用例
5. scp 部署到服务器
6. 在服务器上再次运行 run_all_tests.py
7. git commit + push
8. 如涉及 server.py 修改，重启 Flask
9. 更新 CHANGELOG.md
```

### 6.2 测试要求

**每次代码变更后必须：**

```bash
# 在服务器上运行全面测试
cd /home/hmo/MoFin
python3 scripts/run_all_tests.py
```

**输出必须满足：**
- 0 个 `❌`
- 最多 1-2 个 `⚠️`（已知问题，已记录在案）

**如测试不通过：**
1. 修复问题
2. 重新测试
3. 不通过不提交

### 6.3 文档同步

**哪些文档需要同步更新：**

| 改动内容 | 需更新的文档 |
|---------|------------|
| 新增/修改表 | `docs/DATABASE_ARCHITECTURE.md` |
| 新增/修改 API | `docs/SYSTEM_ARCHITECTURE.md` |
| 架构变更 | `docs/SYSTEM_ARCHITECTURE.md` |
| 数据模型变更 | `docs/portfolio-data-model.md` |
| 任何功能变更 | `CHANGELOG.md`（追加顶部） |
| 新增测试用例 | `docs/TEST_PLAN.md` |

### 6.4 提交规范

- Commit message 格式：`type: 简短描述`
- 类型：`fix:` / `feat:` / `refactor:` / `docs:` / `test:` / `migrate:`
- 一次 commit 一个逻辑变更，不要混搭

---

## 七、快速检查清单

**每次提交前自检：**

```
□ run_all_tests.py 全部通过
□ 无新增 json.load / json.dump 数据文件
□ 港股 currency='HKD'、A股 currency='CNY'
□ 无硬编码汇率
□ 无重复代码
□ CHANGELOG.md 已更新
□ 无残留 print 调试语句
□ git status 确认只改了应改的文件
```

---

## 八、紧急联系人

| 角色 | 负责 |
|------|------|
| 小小莫 | 架构、DB、重构、price_monitor |
| 知微 | 策略、LLM prompt、cron、日常运营 |
