# decisions.json → SQLite 数据库迁移需求

> **✅ 已完成 (2026-07-03)** — 全部迁移到 `holding_strategies` 表。
> decisions.json 不再被任何代码读写。详见 CHANGELOG.md。

## 背景

当前系统所有策略数据存在 `/home/hmo/web-dashboard/data/decisions.json`，一个约 50~60 条策略的 JSON 文件。

## 现状痛点

| 问题 | 举例 |
|------|------|
| 没有写入锁，并发写会损坏 | price_monitor + per_stock_reassess 同时写，JSON 截断 |
| 币种字段不统一 | 港股 price 曾经存 HKD 也存过 CNY，修了几轮才用 currency 标记 |
| 缺乏 schema 校验 | 空字段、类型错误（str 写成了 int）无声失败 |
| 无 changelog 审计 | 谁在什么时候改了哪个字段，查不了 |
| 没有事务回滚 | 写一半 crash，整个文件废了 |
| 只能全量读 | 50 条策略每次全部加载，浪费 token |
| 各脚本自拉价格 | stale_detector 拉一次腾讯 API，per_stock_reassess 又拉一次 |

## 需求目标

**单线程写入 / 多线程安全读**。
将 decisions.json 迁移到 `mofin.db`（已有该数据库），建 `strategies` 表。

## 表结构

```sql
CREATE TABLE strategies (
    code            TEXT PRIMARY KEY,           -- 股票代码，如 "00700"
    name            TEXT NOT NULL,              -- 股票名称
    type            TEXT DEFAULT '自选策略',    -- 持仓策略 / 自选策略
    status          TEXT DEFAULT 'active',      -- active / updated / stale
    currency        TEXT DEFAULT 'CNY',         -- CNY / HKD。港股固定 HKD
    
    -- 价格与仓位
    price           REAL,                       -- 最新价格（原始币种，港股=HKD，A股=CNY）
    price_cny       REAL,                       -- 折算为人民币的价格（统一口径用）
    cost            REAL,                       -- 持仓成本（有持仓时）
    shares          INTEGER,                    -- 持仓股数
    share           INTEGER,                    -- 同 shares，历史遗留字段
    
    -- 策略参数
    entry_low       REAL,                       -- 买入区间下沿（原始币种）
    entry_high      REAL,                       -- 买入区间上沿（原始币种）
    stop_loss       REAL,                       -- 止损价
    take_profit     REAL,                       -- 止盈/目标价
    stop_loss_cny   REAL,                       -- 止损（人民币，统一口径用）
    take_profit_cny REAL,                       -- 止盈（人民币）
    rr_ratio        REAL,                       -- 盈亏比
    timing_signal   TEXT,                       -- 短词信号：买入/加仓/持有/观望/冷却中
    
    -- 来源与状态
    trigger_reason  TEXT,                       -- 策略生成原由
    created_at      TEXT,                       -- ISO时间
    updated_at      TEXT,                       -- 最后更新时间
    reassessed_at   TEXT,                       -- 最近一次重评时间
    action          TEXT,                       -- 最新操作摘要文本
    
    -- JSON 嵌套字段（存为 TEXT，应用层 JSON parse）
    analysis        TEXT,                       -- JSON: 分析详情
    trigger         TEXT,                       -- JSON: 触发条件
    changelog       TEXT,                       -- JSON: 变更历史数组
    signal_factors  TEXT,                       -- JSON: 因子列表
    tech_snapshot   TEXT,                       -- JSON: 技术面快照
    action_note     TEXT,                       -- 长文本动作说明
    sector_context  TEXT                        -- 行业上下文
);
```

### 为什么不全部展开成列

analysis 和 trigger 有嵌套结构且未来可能加字段。存 JSON 字符串，应用层 parse。查询止损/止盈用 `json_extract()`。

## 读写接口需求

### 写操作（高频，每 2 分钟）

price_monitor 每轮更新所有持仓 + 自选的价格：

```sql
INSERT INTO strategies (code, price, price_cny, currency, updated_at)
VALUES (?, ?, ?, ?, ?)
ON CONFLICT(code) DO UPDATE SET
    price = excluded.price,
    price_cny = excluded.price_cny,
    updated_at = excluded.updated_at;
```

价格更新不涉及其他字段。港股：price=HKD，price_cny=HKD×汇率。
A股：price=price_cny。

### 写操作（低频，策略重评时）

per_stock_reassess 跑完单股重评后更新全部策略参数：

```sql
UPDATE strategies SET
    entry_low = ?, entry_high = ?, stop_loss = ?,
    take_profit = ?, rr_ratio = ?, timing_signal = ?,
    stop_loss_cny = ?, take_profit_cny = ?,
    currency = ?,
    analysis = ?,
    trigger = ?,
    reassessed_at = ?,
    changelog = json_insert(changelog, '$[#]', ?)
WHERE code = ?;
```

### 读操作（各报告脚本）

所有 LLM cron、no_agent 脚本统一从 `strategies` 表读，不再拉腾讯 API：

```sql
SELECT * FROM strategies WHERE status = 'active' ORDER BY code;
```

按币种过滤：
```sql
SELECT * FROM strategies WHERE currency = 'HKD';
```

读某只具体股票：
```sql
SELECT * FROM strategies WHERE code = '00700';
```

## 不变的输出

1. **保留 decisions.json 同步输出**（过渡期 2 周）。每次写 DB 后，同步写一份 decisions.json 给旧脚本兼容。
2. **输出格式不做大改**。JSON decode analysis/trigger 后保持现有字段名。

## 不允许的行为

1. ❌ 各脚本自行拉腾讯 API 获取价格。价格入口只有 price_monitor。
2. ❌ 直接写 decisions.json。全部走 DB。
3. ❌ 变更 decisions.json 的输出字段名/格式（过渡期兼容）。

## 验收标准

1. `strategies` 表有数据，decisions.json 和 `SELECT * FROM strategies` 内容一致
2. price_monitor 跑一轮后 DB 里的 price 更新正确（港股 HKD，A 股 CNY）
3. per_stock_reassess 跑完单股后 DB 里对应股票策略更新
4. stale_detector 从 DB 读数据，输出和从 JSON 读一样
5. 并发读写（price_monitor 2min + stale_detector 同时跑）不损坏数据
6. 迁移后旧 decisions.json 仍同步更新

## 相关文件路径

| 文件 | 说明 |
|------|------|
| `/home/hmo/MoFin/price_monitor.py` | 价格监控，每2分钟写price |
| `/home/hmo/MoFin/scripts/strategy_lifecycle.py` | 策略生命周期，reassess_strategy() |
| `/home/hmo/MoFin/scripts/per_stock_reassess.py` | 单股重评入口 |
| `/home/hmo/MoFin/scripts/stale_push_wlin.py` | 自选买入提醒 |
| `/home/hmo/web-dashboard/data/decisions.json` | 当前JSON文件 |
| `/home/hmo/MoFin/data/mofin.db` | 目标数据库（已有market/trend等表） |

## 联系人

有问题问 hmo（老爸）。笑笑负责代码实现，测试完成后通知老爸验收。
