Files
MoFin/docs/mofin-database-architecture.md
T
知微 (MoFin) aa0f740381 MoFin 初始提交
完整数据采集+分析管道:
- market_watch.py:90行业板块采集(同花顺/东方财富)
- 市场精选推荐 cron:全市场分析+候选池+星级推荐
- price_monitor.py:持仓/自选高频价格监控
- refresh_mtf_cache.py:多周期K线缓存
- 策略评估/知识萃取管道

文档:docs/ 含完整需求+架构设计
注意:尚未配置 git remote,笑笑接手后自行配置
2026-06-20 12:04:21 +08:00

396 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# MoFin 统一数据库架构设计
## 目标
将 MoFin 系统目前散落在各 JSON 文件中的数据统一纳入 SQLite 数据库,实现:
- 数据关系化(板块 ↔ 个股 ↔ 持仓 ↔ 策略,SQL join 直连)
- 历史可追溯(板块快照、价格序列、评分变更全部时序化)
- 操作 no_agent 化(纯 SQL 即可完成大部分查询和分析)
- 增量迁移(现有 JSON 不动,新数据双写,逐步切换)
---
## 一、数据库概览
```
数据库文件: /home/hmo/web-dashboard/data/mofin.db
引擎: SQLite 3
```
### 逻辑分组
| 组 | 表 | 说明 |
|----|-----|------|
| 市场 | market_snapshots, sector_snapshots | 全市场板块快照(每30分) |
| 个股 | stocks, stock_daily, stock_weekly, stock_monthly, stock_fundamentals | 个股K线+基本面 |
| 板块成分 | stock_sectors | 个股所属板块映射 |
| 持仓 | holdings, holding_strategies | 持仓股+策略参数 |
| 自选 | watchlist_stocks | 自选股+买入区 |
| 候选池 | candidates, candidate_score_history | 小果+知微潜力股推荐 |
| 事件 | price_events, zone_breaches | 价格预警、区间触发 |
| 策略评估 | strategy_evaluations | 策略重评记录 |
---
## 二、表结构详述
### 2.1 市场/板块快照
核心时间序列表。每次 market_watch 执行(每30分)写一条 snapshot,再把每个板块的数据写入 sector_snapshots。
```sql
-- 每次采集的元信息
CREATE TABLE market_snapshots (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp TEXT NOT NULL, -- '2026-06-19 15:30'
source TEXT NOT NULL DEFAULT 'ths', -- ths / eastmoney
up_ratio REAL, -- 上涨板块占比(%)
mood TEXT, -- bullish / neutral / bearish
created_at TEXT DEFAULT (datetime('now','localtime'))
);
CREATE INDEX idx_snapshots_time ON market_snapshots(timestamp);
-- 每个板块在每次采集中的快照数据
CREATE TABLE sector_snapshots (
id INTEGER PRIMARY KEY AUTOINCREMENT,
snapshot_id INTEGER NOT NULL REFERENCES market_snapshots(id),
name TEXT NOT NULL, -- 板块名称(同花顺行业名)
change_pct REAL, -- 涨跌幅(%)
up_count INTEGER, -- 上涨家数
down_count INTEGER, -- 下跌家数
net_inflow REAL, -- 资金净流入(亿)
lead_stock TEXT, -- 领涨股名
lead_stock_change REAL, -- 领涨股涨跌幅(%)
volume REAL, -- 成交量(万手)
turnover REAL -- 成交额(亿)
);
CREATE INDEX idx_sector_name ON sector_snapshots(name);
CREATE INDEX idx_sector_snapshot ON sector_snapshots(snapshot_id);
CREATE INDEX idx_sector_name_time ON sector_snapshots(name, snapshot_id);
```
**典型查询:**
```sql
-- 半导体板块最近5天的涨跌幅趋势
SELECT s.timestamp, ss.change_pct, ss.net_inflow
FROM sector_snapshots ss
JOIN market_snapshots s ON ss.snapshot_id = s.id
WHERE ss.name = '半导体'
ORDER BY s.timestamp DESC LIMIT 10;
-- 资金连续3天净流入的板块(最近3次采集)
SELECT name, COUNT(*) as times, AVG(net_inflow) as avg_inflow
FROM sector_snapshots ss
JOIN market_snapshots s ON ss.snapshot_id = s.id
WHERE s.timestamp >= date('now', '-3 days')
AND net_inflow > 0
GROUP BY name
HAVING COUNT(*) >= 3
ORDER BY avg_inflow DESC;
```
---
### 2.2 个股K线 + 基本面
替代现有的 multi_tf_cache.json 和 data/stocks/*.json。
```sql
CREATE TABLE stocks (
code TEXT PRIMARY KEY, -- '688981'
name TEXT NOT NULL, -- '中芯国际'
exchange TEXT DEFAULT 'SH', -- SH / SZ / HK
type TEXT DEFAULT 'A', -- A / H
updated_at TEXT
);
-- 日线数据(时间序列)
CREATE TABLE stock_daily (
code TEXT NOT NULL REFERENCES stocks(code),
date TEXT NOT NULL, -- '2026-06-19'
open REAL,
close REAL,
high REAL,
low REAL,
volume REAL, -- 成交量
amount REAL, -- 成交额
PRIMARY KEY (code, date)
);
-- 周线数据
CREATE TABLE stock_weekly (
code TEXT NOT NULL REFERENCES stocks(code),
date TEXT NOT NULL, -- 周结束日期
open REAL, close REAL, high REAL, low REAL,
volume REAL,
PRIMARY KEY (code, date)
);
-- 月线数据
CREATE TABLE stock_monthly (
code TEXT NOT NULL REFERENCES stocks(code),
date TEXT NOT NULL, -- 月结束日期
open REAL, close REAL, high REAL, low REAL,
volume REAL,
PRIMARY KEY (code, date)
);
-- 基本面(最新,非时序)
CREATE TABLE stock_fundamentals (
code TEXT PRIMARY KEY REFERENCES stocks(code),
pe REAL, -- 市盈率
pb REAL, -- 市净率
eps REAL, -- 每股收益
mcap_total REAL, -- 总市值
mcap_flow REAL, -- 流通市值
updated_at TEXT
);
```
---
### 2.3 板块成分股映射
个股和板块的关系表。目前股票不知道自己在哪个板块,板块不知道有哪些成分股。这使 join 分析无法进行。
```sql
CREATE TABLE stock_sectors (
code TEXT NOT NULL REFERENCES stocks(code),
sector_name TEXT NOT NULL, -- 板块名称(与 sector_snapshots.name 一致)
source TEXT DEFAULT 'ths', -- 数据来源
updated_at TEXT DEFAULT (datetime('now','localtime')),
PRIMARY KEY (code, sector_name)
);
CREATE INDEX idx_stock_sector ON stock_sectors(sector_name);
```
**典型查询:**
```sql
-- 我的持仓股所在的板块今天表现如何
SELECT h.code, h.name, ss.change_pct, ss.net_inflow
FROM holdings h
JOIN stock_sectors ss_map ON h.code = ss_map.code
JOIN sector_snapshots ss ON ss_map.sector_name = ss.name
JOIN market_snapshots ms ON ss.snapshot_id = ms.id
WHERE ms.timestamp = (SELECT MAX(timestamp) FROM market_snapshots)
ORDER BY ss.change_pct DESC;
```
---
### 2.4 持仓
```sql
CREATE TABLE holdings (
code TEXT PRIMARY KEY REFERENCES stocks(code),
name TEXT NOT NULL,
shares INTEGER NOT NULL, -- 持股数
cost REAL, -- 成本价
position_pct REAL, -- 仓位占比(%)
added_at TEXT, -- 买入时间
is_active INTEGER DEFAULT 1, -- 1=持仓中, 0=已卖出
closed_at TEXT, -- 卖出时间
close_pnl REAL -- 最终盈亏(%)
);
-- 持仓策略参数(可重评,保留历史)
CREATE TABLE holding_strategies (
id INTEGER PRIMARY KEY AUTOINCREMENT,
code TEXT NOT NULL REFERENCES holdings(code),
version INTEGER DEFAULT 1, -- 策略版本号
stop_loss REAL, -- 止损价
take_profit REAL, -- 止盈价
entry_low REAL, -- 买入区下沿
entry_high REAL, -- 买入区上沿
strategy_type TEXT DEFAULT 'holding', -- holding / watch
source TEXT, -- reassess / manual / initial
reason TEXT, -- 策略理由
created_at TEXT DEFAULT (datetime('now','localtime')),
superseded_at TEXT -- 被新版本替代的时间
);
CREATE INDEX idx_strategy_code ON holding_strategies(code);
```
---
### 2.5 自选股
```sql
CREATE TABLE watchlist_stocks (
code TEXT PRIMARY KEY REFERENCES stocks(code),
name TEXT NOT NULL,
added_at TEXT DEFAULT (datetime('now','localtime')),
is_active INTEGER DEFAULT 1
);
-- 自选股买入区策略(关联到 holding_strategies,共用策略表)
```
说明:自选股的买入区策略直接使用 `holding_strategies` 表(`strategy_type='watch'`),不需要单独建表。
---
### 2.6 候选池
```sql
CREATE TABLE candidates (
code TEXT PRIMARY KEY REFERENCES stocks(code),
name TEXT NOT NULL,
sector TEXT, -- 所属板块
reason TEXT, -- 推荐理由
entry_range TEXT, -- '49.00-52.00'
stop_loss REAL,
target REAL,
zhiwei_star REAL, -- 知微星级 5.0/4.5/4.0/3.5/3.0
zhiwei_reviewed INTEGER DEFAULT 0,
zhiwei_reviewed_at TEXT,
promoted INTEGER DEFAULT 0, -- 是否已向老爸推荐过
promoted_at TEXT,
dropped INTEGER DEFAULT 0,
drop_reason TEXT,
created_at TEXT DEFAULT (datetime('now','localtime'))
);
-- 评分历史(每次小果/知微评分的记录)
CREATE TABLE candidate_score_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
code TEXT NOT NULL REFERENCES candidates(code),
score REAL NOT NULL, -- 评分 1-10
source TEXT NOT NULL, -- xiaoguo / zhiwei
reason TEXT,
created_at TEXT DEFAULT (datetime('now','localtime'))
);
CREATE INDEX idx_candidate_history ON candidate_score_history(code, created_at);
```
**典型查询:**
```sql
-- 候选池状态(活跃候选及其最新评分)
SELECT c.code, c.name, c.sector, c.zhiwei_star, c.promoted,
(SELECT score FROM candidate_score_history
WHERE code = c.code ORDER BY created_at DESC LIMIT 1) as latest_score
FROM candidates c
WHERE c.dropped = 0
ORDER BY c.zhiwei_star DESC NULLS LAST;
-- 某候选的评分趋势
SELECT created_at, score, source
FROM candidate_score_history
WHERE code = '688981'
ORDER BY created_at;
```
---
### 2.7 价格事件
```sql
CREATE TABLE price_events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
code TEXT NOT NULL REFERENCES stocks(code),
name TEXT,
event_type TEXT NOT NULL, -- entry_zone / stop_breach / take_profit / zone_warning
price REAL, -- 触发时价格
trigger_value TEXT, -- '15.36-16.49' 区间描述
event_label TEXT, -- '买入区' / '止损预警'
created_at TEXT DEFAULT (datetime('now','localtime')),
date TEXT -- 触发日期
);
CREATE INDEX idx_events_code ON price_events(code);
CREATE INDEX idx_events_date ON price_events(date);
```
---
### 2.8 策略评估记录
```sql
CREATE TABLE strategy_evaluations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
code TEXT NOT NULL REFERENCES stocks(code),
eval_type TEXT NOT NULL, -- daily / weekly / adhoc
status TEXT DEFAULT 'pending', -- pending / completed / error
old_stop_loss REAL,
new_stop_loss REAL,
old_tp REAL,
new_tp REAL,
reason TEXT,
created_at TEXT DEFAULT (datetime('now','localtime'))
);
```
---
## 三、与现有系统的关系
### 3.1 分步迁移计划
```
阶段1:新库就位
→ 创建 mofin.db + 所有表
→ market_watch.py 改:写 market.json(保留兼容)+ 同时写 market_snapshots / sector_snapshots
→ 验证:数据正常双写,旧管道不受影响
阶段2:个股数据迁移
→ refresh_mtf_cache.py 改:写 multi_tf_cache.json + 同时写 stock_daily/weekly/monthly
→ 新增 stock_sectors 填充(从 market.json 的板块数据提取成分股)
→ 验证:K线数据在SQLite可查
阶段3:业务表迁移
→ portfolio / watchlist / candidate_pool 逐步迁移
→ 每个模块:先写JSON+SQLite双写,确认无问题后切到SQLite
→ 淘汰对应JSON文件
阶段4no_agent 化
→ 通用查询脚本:mofin_query.py "SELECT ..." → 输出JSON/TEXT
→ 定时报表:纯SQL生成,无需LLM
→ 预警规则:SQL条件 → 推送通知
```
### 3.2 JSON 文件保留策略
| 文件 | 阶段1 | 阶段2 | 阶段3 | 阶段4 |
|------|-------|-------|-------|-------|
| market.json | ✅ 双写 | ✅ 双写 | ❌ 淘汰 | — |
| multi_tf_cache.json | — | ✅ 双写 | ❌ 淘汰 | — |
| portfolio.json | — | — | ✅ 双写 | ❌ 淘汰 |
| watchlist.json | — | — | ✅ 双写 | ❌ 淘汰 |
| candidate_pool.json | — | — | ✅ 双写 | ❌ 淘汰 |
| price_events.json | — | — | ✅ 双写 | ❌ 淘汰 |
| decisions.json | — | — | ⚠️ 按需迁移 | — |
---
## 四、关键关联路径
以下是最有价值的跨表查询链:
```
持仓股 → stock_sectors(查所属板块)→ sector_snapshots(板块最近表现)
sector_snapshots 历史(板块趋势)
stock_daily(个股K线与板块对比)
candidate_score_history(候选评分变化)
事件触发 → stock_daily(看当日量价)→ holding_strategies(检查策略偏离)
strategy_evaluations(策略重评记录)
```
---
## 五、迁移风险控制
- 每条写操作先写 JSON(现有流程不受影响),再写 SQLite
- SQLite 写失败时降级报警,不阻塞 JSON 写
- 全部验证通过后才切掉 JSON
- 所有变更通过 market_watch.py / refresh_mtf_cache.py 的 patch 实现,不重写