docs: update CHANGELOG + data model + mark decisions migration complete

This commit is contained in:
知微
2026-07-03 23:34:52 +08:00
parent 9337130ca2
commit b6959f73bc
3 changed files with 315 additions and 539 deletions
+88 -89
View File
@@ -1,115 +1,114 @@
# portfolio.json 数据模型
# MoFin 数据模型
## 一句话
> 数据已从 JSON 迁移到 SQLite。portfolio.json / decisions.json / watchlist.json 不再使用。
**总资产 = 持仓市值 + 可用现金 + 冻结资金**,三个数字必须全对,缺少任何一项数字就不对。
## 核心表
## 字段说明
| 表 | 对应旧 JSON | 用途 |
|----|-----------|------|
| `holdings` + `portfolio_summary` | portfolio.json | 持仓 + 汇总 |
| `holding_strategies` | decisions.json | 策略/决策 |
| `watchlist_stocks` | watchlist.json | 自选股 |
| `live_prices` | live_prices.json | 实时价格快照 |
| `mtf_cache` | multi_tf_cache.json | 多周期缓存 |
| `market_snapshots` | market.json | 大盘数据 |
| `capital_flow_cache` | capital_flow_cache.json | 资金流缓存 |
| `cash_log` | 无 | 现金变更日志 |
```jsonc
{
// 持仓列表(price_monitor 每2分钟更新价格)
"holdings": [
{
"code": "01888", // 股票代码
"name": "建滔积层板", // 股票名称
"shares": 500, // 持股数
"price": 83.59, // 最新价(CNY!所有股票统一人民币计价)
"cost": 88.23, // 成本价(CNY
"market_value": 41795.0 // 市值 = shares × price
}
// ...
],
## 总资产公式
// 现金(从 Dad 截图来源更新,price_monitor 不碰!)
"cash": 92678.85, // 可用资金(人民币)
"frozen_cash": 39481.40, // 冻结资金(T+2未交收/挂单占用)
**总资产 = 持仓总市值(CNY) + 可用现金 + 冻结资金**
// 汇总(price_monitor 每2分钟自动重算)
"total_mv": 835552.6, // 持仓市值 = Σ(shares × price)
"total_assets": 967712.85, // 总资产 = total_mv + cash + frozen_cash
"position_pct": 86.3, // 仓位% = total_mv / total_assets × 100
// 元数据
"currency": "CNY", // 本文件所有价格均为人民币
"updated_at": "2026-06-29 22:33:00", // 最近一次更新
"source": "holding.xls / manual", // 持仓来源
"data_model_version": "2" // 数据模型版本(防止新旧数据混淆)
}
```
## 现金流
### 现金如何被更新?
| 渠道 | 操作 | 更新字段 | 频次 |
|------|------|---------|------|
| holding.xls 导入 | `import_holding_xls.py` | cash, frozen_cash | Dad更新后 |
| 成交截图 | 手动解析 + 对比旧数据计算变更 | cash(+/-), frozen_cash | Dad发送时 |
| price_monitor | 只更新价格,不动现金 | 不更新 | 每2分钟 |
| 手动修正 | Dad告知准确数字 | cash, frozen_cash | Dad告知时 |
### 现金变更追踪
portfolio.json 的 `cash_history` 数组记录了每次现金变更:
```jsonc
"cash_history": [
{
"time": "2026-06-29 22:23:47",
"cash": 92678.85, // 更新后的可用资金
"frozen": 39481.40, // 更新后的冻结资金
"source": "manual:post-法拉电子-sell", // 变更来源
"formula": "初始73758.85 + 法拉电子18920" // 计算过程
}
]
```
### 规则
1. **price_monitor 绝不能修改 cash / frozen_cash** — 它只更新 price 字段和 total_mv / total_assets
2. 现金变更只能来自 Dad 截图、holding.xls 导入、或 Dad 手动告知
3. 每次现金变更必须记录到 cash_history,注明来源和计算过程
4. 所有报告脚本读总资产时,从 `portfolio.json.total_assets` 取,不要自己算
`calc_total_assets()` (mo_models.py) 是唯一正确公式。
## 币种
- portfolio.json 全部存 CNY(港股价格 × HK_RATE 转人民币)
- decisions.json 港股价格存 HKD 原值(带 `currency: "HKD"` 标记)
- price_monitor 比较 decisions.json 中的价格和止损时:同币种(都是 HKD),直接比较
- 报告输出港股价格时显示 HKD 并标注「(HKD)」
### 个股层面
| 品种 | price | cost | currency | 说明 |
|------|-------|------|----------|------|
| 港股 | **HKD** | **HKD** | `HKD` | 跟股软显示一致,方便操作 |
| A股 | **CNY** | **CNY** | `CNY` | |
技术位(stop_loss / take_profit / entry_low / entry_high)与 price 同币种。
### 汇总层面
`calc_total_mv()` / `calc_total_assets()` 汇总时自动将港股 HKD × `HK_RATE`(实时 API)转为 CNY。
```python
# 个股 P&L:港股用 HKD 算,A股用 CNY 算
profit_pct = (price - cost) / cost * 100 # 同币种,无需转换
# 总资产:港股市值自动转 CNY
total_assets = calc_total_assets(pf) # 已处理 HKD→CNY
```
### 禁止
- ❌ 港股 price(HKD) 和 A 股 price(CNY) 直接比较/相加
- ❌ 港股 cost(HKD) 和 CNY price 混算 P&L
- ❌ 硬编码汇率(`calc_total_mv` 内部调 `get_hk_rate()` 走实时 API
## 数据流
```
price_monitor (cron: */2 9-16)
→ 东财/腾讯拉价格
→ write_holdings_batch() → holdings 表 (price 更新)
→ write_portfolio_summary() → portfolio_summary (total_mv/total_assets 重算)
regenerate_all (cron: 手动/定时)
→ batch_fetch_prices() → 从 DB 读价格
→ 技术分析 → 止损/止盈/买入区
→ write_holding_strategy() → holding_strategies 表
server.py API
→ 写端点: _save_portfolio / _save_decision / _save_watchlist → DB
→ 读端点: mo_data.read_*() → DB
```
## 现金
- `price_monitor` 只更新价格和汇总,不动现金
- 现金变更通过截图导入 / holding.xls 导入 / 手动调整
- `cash_log` 表记录每次变更(来源、before/after、备注)
## 常见错误
### ❌ total_assets 漏了冻结资金
### ❌ 港股价格转 CNY 再存
```python
# WRONG — 只加了可用现金,冻结资金漏了
total_assets = market_value + cash
# WRONG — 港股个股存 CNY 后股软对不上
if is_hk_stock(code):
price = price * HK_RATE
# RIGHT — 可用 + 冻结
total_assets = market_value + cash + frozen_cash
# RIGHT — 存 HKD 原值,汇总时由 calc_total_assets 转 CNY
if is_hk_stock(code):
currency = 'HKD'
```
### ❌ 港股硬编码 ×0.866
### ❌ 混币计算
```python
# WRONG — 价格本身已经是 CNYprice_monitor在写入时就转了)
mv = shares * price * 0.866
# WRONG — price 是 HKDcost 是 CNY,算出来没意义
pnl = (price_hkd - cost_cny) / cost_cny
# RIGHT — price 已经是 CNY
mv = shares * price
# RIGHT — 同币种比较
pnl = (price_hkd - cost_hkd) / cost_hkd
```
### ❌ LLM 报告自己算总资产
### ❌ 硬编码汇率
```python
# WRONG
mv = shares * price * 0.87
```
WRONG: 报告里写 "总资产 = 持仓市值 + 现金"
RIGHT: 报告里写 "总资产 = portfolio.json.total_assets"
# RIGHT — 用 calc_total_assets(内部调实时汇率)
mv = calc_total_mv(holdings)
```
## 版本记录
## 版本
| 版本 | 日期 | 变更 |
|------|------|------|
| 1 | 2026-06-29以前 | 无规范,cash字段含义模糊 |
| 2 | 2026-06-29 | 明确 cash=可用, frozen_cash=冻结, total_assets=市值+可用+冻结 |
| 3 | 2026-07-03 | JSON→DB 迁移完成。港股个股存 HKD,汇总时转 CNY。 |
| 2 | 2026-06-29 | 明确 cash/frozen_cash 字段含义 |
| 1 | - | 无规范 |