Files
ai_stock/DATA_SCHEMA.md
2025-12-08 15:30:19 +08:00

10 KiB
Raw Permalink Blame History

量化交易数据表设计(建议版)

本文档给出一个兼顾本地研究SQLite与生产化部署PostgreSQL的数据模型。目标可重复、可扩展、便于回测和线上实时监控支持行情、基本面、新闻情绪、信号、订单、成交、持仓与风控度量。

设计原则

  • 时间统一:全部时间戳使用 UTC列命名统一为 *_utcas_of_dateUTC 日期)。
  • 主键稳定:优先采用业务自然键的组合主键(如 (symbol_id, ts_utc)),跨系统对齐使用 surrogate key自增或 UUID
  • 数值约定:
    • 价格/金额使用 NUMERIC(18,6)PGREAL/DECIMALSQLite
    • 涨跌幅/比率存“小数形式”:如 4.02% 存 0.0402
  • 幂等写入:所有采集/计算表支持 Upsert通过唯一键 + ON CONFLICT DO UPDATEPGINSERT OR REPLACESQLite
  • 分区与保留高频表按时间分区PG并制定数据保留策略如 Tick 保留 30 天1 分钟 Bar 保留 180 天,日线长期保留)。

表清单与字段

1) symbols(标的主表)

  • 用途:统一管理股票/ETF 元数据。
  • 主键:idPK自增/UUID唯一约束(symbol, exchange)
  • 字段:
    • id PK
    • symbol 股票代码(如 AAPL
    • name 名称
    • exchange 交易所(如 NASDAQ
    • currency 货币(如 USD
    • tick_sizelot_size
    • sectorindustry
    • is_active 布尔
    • first_seen_utclast_seen_utc

2) calendars(交易日历)

  • 主键:(exchange, date)
  • 字段:is_trading_dayopen_time_utcclose_time_utcnotes

3) bars_1m1 分钟 K 线)

  • 主键:(symbol_id, ts_utc)ts_utc 为该分钟起始时刻)。
  • 索引:(ts_utc)(symbol_id, ts_utc DESC)
  • 字段:openhighlowclosevolumevwaptrades_countsource

4) bars_1d(日线 K 线)

  • 主键:(symbol_id, as_of_date)
  • 字段:openhighlowcloseadj_closevolumedividendsplit_ratiosource

5) ticks(逐笔/Level-1 快照,可选)

  • 主键:id(自增/UUID唯一键建议(symbol_id, ts_utc, source, seq)
  • 字段:pricesizebidaskbid_sizeask_sizeconditionseqsource

6) corporate_actions(公司行为)

  • 主键:(symbol_id, ex_date, type)
  • 字段:typesplit/dividend/...)、amountratiocurrencynotes

7) fundamentals_snapshot(基本面快照)

  • 主键:(symbol_id, as_of_date)
  • 字段示例:market_cappe_ttmps_ttmpbeps_ttmrevenue_ttmshares_outstandingupdated_at_utc

8) newsnews_symbols(新闻与关联表)

  • news 主键:idUUID/自增);字段:published_at_utcsourcetitleurlsummarysentiment_scoretopics
  • news_symbols 主键:(news_id, symbol_id)

9) signals(策略信号)

  • 主键:idUUID/自增);唯一建议:(symbol_id, generated_at_utc, model_name, version)
  • 字段:
    • symbol_idgenerated_at_utc
    • signal_type(如 momentum/reversal
    • directionBUY/SELL/HOLD
    • score01 或 z-score
    • horizon(如 1d/1h
    • params_json(策略参数 JSON
    • model_nameversion
    • expires_at_utc(过期时间,可空)

10) orders(订单)

  • 主键:id;唯一建议:broker_order_id(如接入实盘)。
  • 字段:signal_idsymbol_idsideorder_typeqtypricetime_in_forcestatuscreated_at_utcupdated_at_utcbroker_order_id

11) executions(成交/回执)

  • 主键:id;索引:(order_id)(exec_time_utc)
  • 字段:order_idexec_time_utcpriceqtyfeeliquiditymaker/taker)。

12) positions(持仓快照)

  • 主键:(portfolio_id, symbol_id) 或附带 as_of_date 做日终表。
  • 字段:qtyavg_costunrealized_pnlrealized_pnllast_updated_utc

13) portfolios / portfolio_nav_daily(组合与净值)

  • portfoliosid PK、namebase_currencycreated_at_utc
  • portfolio_nav_daily 主键:(portfolio_id, as_of_date);字段:cashequity_valuenavdaily_returngross_exposurenet_exposure

14) risk_metrics_daily(风险指标)

  • 主键:(portfolio_id, as_of_date);字段:var_95betasharpemax_drawdownvolatility_20d 等。

15) etl_runs(任务运行元数据)

  • 主键:run_id;字段:task_namestarted_at_utcfinished_at_utcstatusrows_affectedchecksum

PostgreSQL 示例 DDL核心表

-- 1) 标的
CREATE TABLE symbols (
  id              BIGSERIAL PRIMARY KEY,
  symbol          TEXT NOT NULL,
  name            TEXT,
  exchange        TEXT NOT NULL,
  currency        TEXT DEFAULT 'USD',
  tick_size       NUMERIC(18,6),
  lot_size        NUMERIC(18,6),
  sector          TEXT,
  industry        TEXT,
  is_active       BOOLEAN DEFAULT TRUE,
  first_seen_utc  TIMESTAMPTZ,
  last_seen_utc   TIMESTAMPTZ,
  UNIQUE(symbol, exchange)
);

-- 2) 1 分钟 K 线
CREATE TABLE bars_1m (
  symbol_id   BIGINT NOT NULL REFERENCES symbols(id),
  ts_utc      TIMESTAMPTZ NOT NULL,
  open        NUMERIC(18,6) NOT NULL,
  high        NUMERIC(18,6) NOT NULL,
  low         NUMERIC(18,6) NOT NULL,
  close       NUMERIC(18,6) NOT NULL,
  volume      BIGINT,
  vwap        NUMERIC(18,6),
  trades_count INTEGER,
  source      TEXT,
  PRIMARY KEY(symbol_id, ts_utc)
);
CREATE INDEX ON bars_1m (ts_utc);
CREATE INDEX ON bars_1m (symbol_id, ts_utc DESC);

-- 3) 日线 K 线
CREATE TABLE bars_1d (
  symbol_id   BIGINT NOT NULL REFERENCES symbols(id),
  as_of_date  DATE NOT NULL,
  open        NUMERIC(18,6) NOT NULL,
  high        NUMERIC(18,6) NOT NULL,
  low         NUMERIC(18,6) NOT NULL,
  close       NUMERIC(18,6) NOT NULL,
  adj_close   NUMERIC(18,6),
  volume      BIGINT,
  dividend    NUMERIC(18,6),
  split_ratio NUMERIC(18,6),
  source      TEXT,
  PRIMARY KEY(symbol_id, as_of_date)
);

-- 4) 信号表(涨跌幅等指标请用小数存储)
CREATE TABLE signals (
  id               BIGSERIAL PRIMARY KEY,
  symbol_id        BIGINT NOT NULL REFERENCES symbols(id),
  generated_at_utc TIMESTAMPTZ NOT NULL,
  signal_type      TEXT NOT NULL,
  direction        TEXT NOT NULL CHECK (direction IN ('BUY','SELL','HOLD')),
  score            NUMERIC(18,6),
  horizon          TEXT,
  params_json      JSONB,
  model_name       TEXT,
  version          TEXT,
  expires_at_utc   TIMESTAMPTZ,
  UNIQUE(symbol_id, generated_at_utc, model_name, version)
);
CREATE INDEX ON signals (symbol_id, generated_at_utc DESC);

-- 5) 订单/成交
CREATE TABLE orders (
  id              BIGSERIAL PRIMARY KEY,
  signal_id       BIGINT REFERENCES signals(id),
  symbol_id       BIGINT NOT NULL REFERENCES symbols(id),
  side            TEXT NOT NULL CHECK (side IN ('BUY','SELL')),
  order_type      TEXT NOT NULL CHECK (order_type IN ('MKT','LMT')),
  qty             NUMERIC(18,6) NOT NULL,
  price           NUMERIC(18,6),
  time_in_force   TEXT,
  status          TEXT NOT NULL,
  broker_order_id TEXT,
  created_at_utc  TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at_utc  TIMESTAMPTZ
);
CREATE UNIQUE INDEX IF NOT EXISTS orders_broker_unique ON orders(broker_order_id) WHERE broker_order_id IS NOT NULL;

CREATE TABLE executions (
  id             BIGSERIAL PRIMARY KEY,
  order_id       BIGINT NOT NULL REFERENCES orders(id),
  exec_time_utc  TIMESTAMPTZ NOT NULL,
  price          NUMERIC(18,6) NOT NULL,
  qty            NUMERIC(18,6) NOT NULL,
  fee            NUMERIC(18,6),
  liquidity      TEXT
);
CREATE INDEX ON executions(order_id);
CREATE INDEX ON executions(exec_time_utc);

SQLite 示例 DDL简化版

CREATE TABLE symbols (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  symbol TEXT NOT NULL,
  name TEXT,
  exchange TEXT NOT NULL,
  currency TEXT,
  tick_size REAL,
  lot_size REAL,
  sector TEXT,
  industry TEXT,
  is_active INTEGER DEFAULT 1,
  first_seen_utc TEXT,
  last_seen_utc TEXT,
  UNIQUE(symbol, exchange)
);

CREATE TABLE bars_1m (
  symbol_id INTEGER NOT NULL,
  ts_utc TEXT NOT NULL,
  open REAL NOT NULL,
  high REAL NOT NULL,
  low REAL NOT NULL,
  close REAL NOT NULL,
  volume INTEGER,
  vwap REAL,
  trades_count INTEGER,
  source TEXT,
  PRIMARY KEY(symbol_id, ts_utc)
);

CREATE TABLE signals (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  symbol_id INTEGER NOT NULL,
  generated_at_utc TEXT NOT NULL,
  signal_type TEXT NOT NULL,
  direction TEXT NOT NULL,
  score REAL,
  horizon TEXT,
  params_json TEXT,
  model_name TEXT,
  version TEXT,
  expires_at_utc TEXT,
  UNIQUE(symbol_id, generated_at_utc, model_name, version)
);

典型索引与查询

  • 最新信号:
SELECT DISTINCT ON (s.symbol_id)
  s.*
FROM signals s
ORDER BY s.symbol_id, s.generated_at_utc DESC;
  • 连接 1 分钟线与信号(取信号后最近 30 分钟):
SELECT b.*
FROM signals s
JOIN bars_1m b
  ON b.symbol_id = s.symbol_id
 AND b.ts_utc BETWEEN s.generated_at_utc AND s.generated_at_utc + INTERVAL '30 minutes'
WHERE s.generated_at_utc >= NOW() - INTERVAL '1 day';
  • 计算日收益(简单取 close
SELECT symbol_id,
       as_of_date,
       close / LAG(close) OVER (PARTITION BY symbol_id ORDER BY as_of_date) - 1 AS daily_return
FROM bars_1d;

数据保留与分区建议PG

  • ticks:分区粒度=日,保留 3090 天。
  • bars_1m:分区粒度=月,保留 180365 天。
  • bars_1dsignalsordersexecutions:长期保留。

与当前项目的对接要点

  • 数值规范:change_ratio 等“比率”统一用小数存储(代码已修正),写入时无需 %
  • 表落地策略:
    • 初期SQLite 单文件,开发/回测方便;
    • 扩展PostgreSQL + 分区 + 指标物化视图(如聚合分钟线)。
  • ETL 幂等:抓取任务用 (symbol_id, ts_utc)(symbol_id, as_of_date) 做 Upsert避免重复数据导致回测偏差。

后续扩展

  • features_*:因子特征宽表(按频率区分:分钟/日)。
  • models_registry:模型注册与版本追踪。
  • backtest_runs:回测任务与指标结果表(收益、回撤、卡方检验等)。

如需,我可以按上述 DDL 直接生成 SQLite 数据库,并将 monitor.py 在每轮拉取后把 Top N 的 1 分钟 bar、信号落到库中Upsert用于后续回测与可视化分析。