aa0f740381
完整数据采集+分析管道: - market_watch.py:90行业板块采集(同花顺/东方财富) - 市场精选推荐 cron:全市场分析+候选池+星级推荐 - price_monitor.py:持仓/自选高频价格监控 - refresh_mtf_cache.py:多周期K线缓存 - 策略评估/知识萃取管道 文档:docs/ 含完整需求+架构设计 注意:尚未配置 git remote,笑笑接手后自行配置
14 KiB
14 KiB
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。
-- 每次采集的元信息
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);
典型查询:
-- 半导体板块最近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。
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 分析无法进行。
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);
典型查询:
-- 我的持仓股所在的板块今天表现如何
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 持仓
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 自选股
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 候选池
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);
典型查询:
-- 候选池状态(活跃候选及其最新评分)
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 价格事件
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 策略评估记录
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文件
阶段4:no_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 实现,不重写