Files
MoFin/docs/mofin-database-architecture.md
知微 (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

14 KiB
Raw Permalink Blame History

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文件

阶段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 实现,不重写