Files
simon fce725e13c 初始提交:高股息策略研究与回测系统
从 Point-in-Time 股票筛选到统一 Web 前端的完整链路:
筛选 → 画像 → 策略 → 回测 → Walk-forward → 绩效分析 → 报告/前端。

架构
- 数据层与策略层分离;策略代码不写 SQL,只经 data/repo.py 取数
- 所有业务阈值集中在 config/*.yml,代码零硬编码(字段写错直接报错)
- 报告只做「run_id → SQL → 渲染」,不做任何计算,数字可追溯
- 前后端分离:output/ 静态站点 + hdiv web 提供的 REST API

数据安全
- 只增不删:SQL 钩子拦截 DELETE/DROP/TRUNCATE,并有源码扫描测试守护
- qlib 原有表只读,本项目数据写入 hd_ 前缀表
- 回补使用 INSERT IGNORE,保证既有行零改动
- .env 存密钥且已 gitignore;output/、logs/、.venv/ 不入库

交付物
- 30 张 hd_* 表、7 个 YAML 配置、283 项自动化测试
- 统一 Web 前端(hash 路由 SPA)+ nginx 部署配置与 launchd 托管脚本

如实声明的限制
- 策略缺少稳定的样本外超额收益(Walk-forward 7 窗口均值 -0.95%,
  基准 +2.29%);其价值体现在回撤控制,而非超额收益
- 涨跌停/停牌约束仅覆盖 2019 年起;index_weight 尚未填充
- AI Agent 层(plan.md 第四版 P8)未实现

详见 docs/user-guide.md 与 docs/implementation-status.md。
2026-10-03 13:54:56 +08:00

622 lines
32 KiB
SQL
Raw Permalink 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.
-- 全部 hd_* 表结构(幂等)
-- 生成自 src/hdiv/data/schema.py
-- 分红送转全明细(plan.md §14/§30 的数据基座)
CREATE TABLE IF NOT EXISTS `hd_dividend` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`symbol` VARCHAR(12) NOT NULL,
`end_date` DATE NOT NULL COMMENT '分红对应报告期',
`ann_date` DATE NULL COMMENT '预案公告日',
`imp_ann_date` DATE NULL COMMENT '实施公告日 —— PIT 关键字段',
`div_proc` VARCHAR(16) NOT NULL COMMENT '预案/股东大会通过/实施/取消',
`dedup_key` VARCHAR(80) NOT NULL COMMENT '去重键:NULL 不参与 MySQL 唯一约束,故显式构造',
`stk_div` DECIMAL(16,6) NULL COMMENT '每股送转股',
`stk_bo_rate` DECIMAL(16,6) NULL COMMENT '每股送股比例',
`stk_co_rate` DECIMAL(16,6) NULL COMMENT '每股转增比例',
`cash_div` DECIMAL(16,6) NULL COMMENT '每股税后现金分红(元)',
`cash_div_tax` DECIMAL(16,6) NULL COMMENT '每股税前现金分红(元)—— 连续性判定口径',
`record_date` DATE NULL COMMENT '股权登记日',
`ex_date` DATE NULL COMMENT '除权除息日',
`pay_date` DATE NULL COMMENT '派息日',
`div_listdate` DATE NULL COMMENT '红股上市日',
`base_share` DECIMAL(24,4) NULL COMMENT '基准股本(万股)',
`source` VARCHAR(16) NOT NULL DEFAULT 'tushare',
`created_at` DATETIME NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_hd_div_key` (`dedup_key`),
KEY `ix_hd_div_ex` (`symbol`,`ex_date`),
KEY `ix_hd_div_pit` (`symbol`,`imp_ann_date`),
KEY `ix_hd_div_proc` (`div_proc`,`ex_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='分红送转全明细';
-- 扩展财务指标(G4;带 announce_date 以支持 PIT)
CREATE TABLE IF NOT EXISTS `hd_fina_indicator` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`symbol` VARCHAR(12) NOT NULL,
`end_date` DATE NOT NULL COMMENT '报告期',
`ann_date` DATE NOT NULL COMMENT '公告日 —— PIT 关键字段',
`eps` DECIMAL(16,6) NULL,
`roe` DECIMAL(16,6) NULL COMMENT '净资产收益率 %',
`roe_waa` DECIMAL(16,6) NULL COMMENT '加权平均 ROE %',
`roe_dt` DECIMAL(16,6) NULL COMMENT '扣非 ROE %',
`roa` DECIMAL(16,6) NULL,
`roic` DECIMAL(16,6) NULL COMMENT '投入资本回报率 %',
`grossprofit_margin` DECIMAL(16,6) NULL COMMENT '毛利率 %',
`netprofit_margin` DECIMAL(16,6) NULL COMMENT '净利率 %',
`debt_to_assets` DECIMAL(16,6) NULL COMMENT '资产负债率 %',
`current_ratio` DECIMAL(16,6) NULL,
`quick_ratio` DECIMAL(16,6) NULL,
`ocf_to_or` DECIMAL(16,6) NULL COMMENT '经营现金流/营收 %',
`ocf_to_opincome` DECIMAL(16,6) NULL COMMENT '经营现金流/经营利润 %',
`ocf_to_profit` DECIMAL(16,6) NULL COMMENT '经营现金流/净利润(盈利质量)',
`netdebt` DECIMAL(24,4) NULL,
`interestdebt` DECIMAL(24,4) NULL,
`ebit` DECIMAL(24,4) NULL,
`ebitda` DECIMAL(24,4) NULL,
`interest_cover` DECIMAL(16,6) NULL COMMENT '利息保障倍数',
`fcff` DECIMAL(24,4) NULL COMMENT '企业自由现金流',
`fcfe` DECIMAL(24,4) NULL COMMENT '股权自由现金流',
`source` VARCHAR(16) NOT NULL DEFAULT 'tushare',
`created_at` DATETIME NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_hd_fina` (`symbol`,`end_date`,`ann_date`),
KEY `ix_hd_fina_pit` (`symbol`,`ann_date`),
KEY `ix_hd_fina_end` (`end_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='扩展财务指标(PIT)';
-- 现金流量表(G4;FCF 与分红覆盖的来源)
CREATE TABLE IF NOT EXISTS `hd_cashflow` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`symbol` VARCHAR(12) NOT NULL,
`end_date` DATE NOT NULL,
`ann_date` DATE NOT NULL,
`report_type` VARCHAR(4) NOT NULL DEFAULT '1'
COMMENT '1=合并报表 2=单季合并 6=母公司;实测全为 1',
`n_cashflow_act` DECIMAL(24,4) NULL COMMENT '经营活动现金流净额',
`n_cashflow_inv_act` DECIMAL(24,4) NULL COMMENT '投资活动现金流净额',
`n_cash_flows_fnc_act` DECIMAL(24,4) NULL COMMENT '筹资活动现金流净额',
`c_pay_dist_dpcp_int_exp` DECIMAL(24,4) NULL COMMENT '分配股利/利润/偿付利息支付的现金',
`free_cashflow` DECIMAL(24,4) NULL COMMENT 'Tushare 口径自由现金流',
`c_fr_sale_sg_ar` DECIMAL(24,4) NULL COMMENT '销售商品收到的现金',
`source` VARCHAR(16) NOT NULL DEFAULT 'tushare',
`created_at` DATETIME NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_hd_cf_key` (`symbol`,`end_date`,`ann_date`,`report_type`),
KEY `ix_hd_cf_pit` (`symbol`,`ann_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='现金流量表';
-- 资产负债表(G4;负债率与净资产判定的来源)
CREATE TABLE IF NOT EXISTS `hd_balancesheet` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`symbol` VARCHAR(12) NOT NULL,
`end_date` DATE NOT NULL,
`ann_date` DATE NOT NULL,
`report_type` VARCHAR(4) NOT NULL DEFAULT '1'
COMMENT '1=合并报表 2=单季合并 6=母公司;实测全为 1',
`total_assets` DECIMAL(24,4) NULL,
`total_liab` DECIMAL(24,4) NULL,
`total_hldr_eqy_exc_min_int` DECIMAL(24,4) NULL COMMENT '归母净资产',
`total_cur_assets` DECIMAL(24,4) NULL,
`total_cur_liab` DECIMAL(24,4) NULL,
`money_cap` DECIMAL(24,4) NULL COMMENT '货币资金',
`goodwill` DECIMAL(24,4) NULL COMMENT '商誉',
`source` VARCHAR(16) NOT NULL DEFAULT 'tushare',
`created_at` DATETIME NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_hd_bs_key` (`symbol`,`end_date`,`ann_date`,`report_type`),
KEY `ix_hd_bs_pit` (`symbol`,`ann_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='资产负债表';
-- 利润表(G4;分红支付率的分母来源)
CREATE TABLE IF NOT EXISTS `hd_income` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`symbol` VARCHAR(12) NOT NULL,
`end_date` DATE NOT NULL,
`ann_date` DATE NOT NULL,
`report_type` VARCHAR(4) NOT NULL DEFAULT '1'
COMMENT '1=合并报表 2=单季合并 6=母公司;实测全为 1',
`total_revenue` DECIMAL(24,4) NULL,
`revenue` DECIMAL(24,4) NULL,
`operate_profit` DECIMAL(24,4) NULL,
`n_income` DECIMAL(24,4) NULL COMMENT '净利润(含少数股东)',
`n_income_attr_p` DECIMAL(24,4) NULL COMMENT '归母净利润',
`basic_eps` DECIMAL(16,6) NULL,
`source` VARCHAR(16) NOT NULL DEFAULT 'tushare',
`created_at` DATETIME NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_hd_inc_key` (`symbol`,`end_date`,`ann_date`,`report_type`),
KEY `ix_hd_inc_pit` (`symbol`,`ann_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='利润表';
-- 基准指数行情(G5;plan.md §28.4 基准比较)
CREATE TABLE IF NOT EXISTS `hd_index_daily` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`index_code` VARCHAR(16) NOT NULL,
`index_name` VARCHAR(64) NULL,
`trade_date` DATE NOT NULL,
`open` DECIMAL(16,4) NULL,
`high` DECIMAL(16,4) NULL,
`low` DECIMAL(16,4) NULL,
`close` DECIMAL(16,4) NULL,
`pre_close` DECIMAL(16,4) NULL,
`pct_chg` DECIMAL(12,6) NULL,
`vol` DECIMAL(24,4) NULL,
`amount` DECIMAL(24,4) NULL,
`source` VARCHAR(16) NOT NULL DEFAULT 'tushare',
`created_at` DATETIME NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_hd_idx` (`index_code`,`trade_date`),
KEY `ix_hd_idx_date` (`trade_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='基准指数行情';
-- 停牌记录(G6;成交可行性约束)
CREATE TABLE IF NOT EXISTS `hd_suspend` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`symbol` VARCHAR(12) NOT NULL,
`trade_date` DATE NOT NULL,
`suspend_type` VARCHAR(4) NOT NULL COMMENT 'S=停牌 R=复牌',
`suspend_timing` VARCHAR(48) NULL,
`source` VARCHAR(16) NOT NULL DEFAULT 'tushare',
`created_at` DATETIME NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_hd_susp` (`symbol`,`trade_date`,`suspend_type`),
KEY `ix_hd_susp_date` (`trade_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='停牌记录';
-- 涨跌停价(G6;plan.md §29 成交现实性)
CREATE TABLE IF NOT EXISTS `hd_limit` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`symbol` VARCHAR(12) NOT NULL,
`trade_date` DATE NOT NULL,
`up_limit` DECIMAL(16,4) NULL,
`down_limit` DECIMAL(16,4) NULL,
`source` VARCHAR(16) NOT NULL DEFAULT 'tushare',
`created_at` DATETIME NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_hd_limit` (`symbol`,`trade_date`),
KEY `ix_hd_limit_date` (`trade_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='涨跌停价';
-- 本项目同步台账(含回补前后行数基线,供审计)
CREATE TABLE IF NOT EXISTS `hd_sync_log` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`job` VARCHAR(64) NOT NULL COMMENT '同步器名',
`api` VARCHAR(96) NULL COMMENT 'Tushare 接口名(交错模式为 a+b+c 形式)',
`params_json` TEXT NULL,
`started_at` DATETIME NOT NULL,
`finished_at` DATETIME NULL,
`status` VARCHAR(16) NOT NULL DEFAULT 'RUNNING' COMMENT 'RUNNING/OK/FAIL/SKIP',
`rows_written` BIGINT NOT NULL DEFAULT 0,
`rows_before` BIGINT NULL COMMENT '回补前基线行数',
`rows_after` BIGINT NULL COMMENT '回补后行数(审计:只增不减)',
`data_start` DATE NULL,
`data_end` DATE NULL,
`failure_reason` TEXT NULL,
PRIMARY KEY (`id`),
KEY `ix_hd_synclog_job` (`job`,`started_at`),
KEY `ix_hd_synclog_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='同步台账';
-- 数据完整性问题清单(P0 验收依据)
CREATE TABLE IF NOT EXISTS `hd_data_audit` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`audit_id` VARCHAR(32) NOT NULL,
`check_code` VARCHAR(32) NOT NULL COMMENT 'G1..G6 或自定义检查码',
`check_name` VARCHAR(128) NOT NULL,
`severity` VARCHAR(16) NOT NULL COMMENT 'OK/WARN/FAIL',
`table_name` VARCHAR(64) NULL,
`metric_json` TEXT NULL,
`message` TEXT NULL,
`created_at` DATETIME NOT NULL,
PRIMARY KEY (`id`),
KEY `ix_hd_audit_run` (`audit_id`),
KEY `ix_hd_audit_code` (`check_code`,`severity`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='数据审计结果';
-- 一次筛选运行(股票池快照头)
CREATE TABLE IF NOT EXISTS `hd_universe_run` (
`run_id` VARCHAR(32) NOT NULL,
`name` VARCHAR(64) NOT NULL,
`asof_date` DATE NOT NULL,
`mode` VARCHAR(16) NOT NULL COMMENT 'point_in_time/latest',
`config_json` MEDIUMTEXT NOT NULL COMMENT '生效配置快照(含 override 后)',
`config_hash` VARCHAR(64) NOT NULL,
`data_version` VARCHAR(64) NULL,
`code_version` VARCHAR(32) NULL,
`member_count` INT NOT NULL DEFAULT 0,
`candidate_count` INT NOT NULL DEFAULT 0 COMMENT '进入筛选的候选总数',
`stats_json` TEXT NULL COMMENT '逐滤网淘汰统计',
`status` VARCHAR(16) NOT NULL DEFAULT 'OK' COMMENT 'OK/WARN/FAIL',
`display_name` VARCHAR(128) NULL COMMENT '用户在界面上给这条筛选记录起的名字',
`notes` VARCHAR(500) NULL COMMENT '用户备注',
`archived_at` DATETIME NULL COMMENT '归档时间;NULL=未归档',
`deleted_at` DATETIME NULL COMMENT '软删除时间;NULL=未删除(不做物理删除)',
`created_at` DATETIME NOT NULL,
PRIMARY KEY (`run_id`),
KEY `ix_hd_uni_run_asof` (`asof_date`),
KEY `ix_hd_uni_run_hash` (`config_hash`),
KEY `ix_hd_uni_run_state` (`deleted_at`,`archived_at`,`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='股票池运行头';
-- 股票池成员 + 逐滤网命中留痕(可回答「为什么没选上」)
CREATE TABLE IF NOT EXISTS `hd_universe_member` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`run_id` VARCHAR(32) NOT NULL,
`symbol` VARCHAR(12) NOT NULL,
`name` VARCHAR(64) NULL,
`industry` VARCHAR(64) NULL,
`passed` TINYINT(1) NOT NULL COMMENT '1=入选 0=被淘汰',
`fail_stage` VARCHAR(32) NULL COMMENT '首个未通过的滤网',
`fail_reason` VARCHAR(255) NULL,
`values_json` TEXT NULL COMMENT '各滤网实际取值快照',
`filter_json` TEXT NULL COMMENT '各滤网通过位',
`created_at` DATETIME NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_hd_uni_mem` (`run_id`,`symbol`),
KEY `ix_hd_uni_mem_pass` (`run_id`,`passed`),
KEY `ix_hd_uni_mem_sym` (`symbol`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='股票池成员留痕';
-- 决策时点因子值(长表;避免 1.9 亿行日频面板,见 plan §3.2)
CREATE TABLE IF NOT EXISTS `hd_factor_snapshot` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`snapshot_id` VARCHAR(32) NOT NULL COMMENT '一次快照(通常绑定 universe run 或 rebalance 日)',
`asof_date` DATE NOT NULL,
`symbol` VARCHAR(12) NOT NULL,
`factor_code` VARCHAR(48) NOT NULL,
`value` DECIMAL(24,8) NULL,
`source` VARCHAR(32) NULL COMMENT 'computed/dv_ttm/daily_basic 等口径来源',
`created_at` DATETIME NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_hd_fac` (`snapshot_id`,`symbol`,`factor_code`),
KEY `ix_hd_fac_asof` (`asof_date`,`factor_code`),
KEY `ix_hd_fac_sym` (`symbol`,`factor_code`,`asof_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='决策时点因子快照';
-- 画像运行头
CREATE TABLE IF NOT EXISTS `hd_profile_run` (
`run_id` VARCHAR(32) NOT NULL,
`name` VARCHAR(64) NOT NULL,
`asof_date` DATE NOT NULL,
`universe_run_id` VARCHAR(32) NULL COMMENT '来源股票池 run',
`config_json` MEDIUMTEXT NOT NULL,
`config_hash` VARCHAR(64) NOT NULL,
`data_version` VARCHAR(64) NULL,
`symbol_count` INT NOT NULL DEFAULT 0,
`status` VARCHAR(16) NOT NULL DEFAULT 'OK',
`created_at` DATETIME NOT NULL,
PRIMARY KEY (`run_id`),
KEY `ix_hd_prof_run_asof` (`asof_date`),
KEY `ix_hd_prof_run_hash` (`config_hash`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='画像运行头';
-- 逐股逐指标历史分布统计(plan.md §9 明确要求 P10/P25/P50/P75/P90)
CREATE TABLE IF NOT EXISTS `hd_profile_stat` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`run_id` VARCHAR(32) NOT NULL,
`symbol` VARCHAR(12) NOT NULL,
`metric_code` VARCHAR(48) NOT NULL,
`window_years` INT NOT NULL DEFAULT 0 COMMENT '0=全历史;NULL 会绕过唯一约束故禁用',
`n_obs` INT NOT NULL DEFAULT 0,
`min_value` DECIMAL(24,8) NULL,
`max_value` DECIMAL(24,8) NULL,
`mean_value` DECIMAL(24,8) NULL,
`median_value` DECIMAL(24,8) NULL,
`std_value` DECIMAL(24,8) NULL,
`p10` DECIMAL(24,8) NULL,
`p25` DECIMAL(24,8) NULL,
`p50` DECIMAL(24,8) NULL,
`p75` DECIMAL(24,8) NULL,
`p90` DECIMAL(24,8) NULL,
`extra_percentiles_json` TEXT NULL COMMENT '配置中超出默认 5 档的分位',
`current_value` DECIMAL(24,8) NULL COMMENT 'asof 当日取值',
`current_percentile` DECIMAL(12,6) NULL COMMENT '当前值在历史分布中的分位 %',
`status` VARCHAR(16) NOT NULL DEFAULT 'OK' COMMENT 'OK/INSUFFICIENT',
`created_at` DATETIME NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_hd_prof_stat` (`run_id`,`symbol`,`metric_code`,`window_years`),
KEY `ix_hd_prof_stat_sym` (`symbol`,`metric_code`),
KEY `ix_hd_prof_stat_q` (`run_id`,`metric_code`,`current_percentile`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='个股历史分布统计';
-- 画像时间序列(供 HTML 画图;仅筛选后股票,体积可控)
CREATE TABLE IF NOT EXISTS `hd_profile_series` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`run_id` VARCHAR(32) NOT NULL,
`symbol` VARCHAR(12) NOT NULL,
`metric_code` VARCHAR(48) NOT NULL,
`series_date` DATE NOT NULL,
`value` DECIMAL(24,8) NULL,
`created_at` DATETIME NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_hd_prof_ser` (`run_id`,`symbol`,`metric_code`,`series_date`),
KEY `ix_hd_prof_ser_sym` (`run_id`,`symbol`,`metric_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='个股画像序列';
-- 安全边际分项得分(plan.md §15)
CREATE TABLE IF NOT EXISTS `hd_profile_score` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`run_id` VARCHAR(32) NOT NULL,
`symbol` VARCHAR(12) NOT NULL,
`score_code` VARCHAR(48) NOT NULL COMMENT 'dividend_yield/valuation/financial_quality/balance_sheet/dividend_quality/composite',
`score` DECIMAL(12,6) NULL,
`detail_json` TEXT NULL COMMENT '分项明细与所用锚点',
`created_at` DATETIME NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_hd_prof_score` (`run_id`,`symbol`,`score_code`),
KEY `ix_hd_prof_score_v` (`run_id`,`score_code`,`score`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='安全边际得分';
-- 策略身份与版本(plan.md §38/§39 生命周期)
CREATE TABLE IF NOT EXISTS `hd_strategy` (
`strategy_id` VARCHAR(32) NOT NULL,
`name` VARCHAR(64) NOT NULL,
`version` VARCHAR(16) NOT NULL,
`status` VARCHAR(16) NOT NULL DEFAULT 'DRAFT'
COMMENT 'DRAFT/RESEARCH/BACKTEST/VALIDATED/PAPER/LIVE/ARCHIVED',
`description` VARCHAR(500) NOT NULL DEFAULT '',
`yaml_path` VARCHAR(255) NULL,
`config_json` MEDIUMTEXT NOT NULL,
`config_hash` VARCHAR(64) NOT NULL,
`parent_id` VARCHAR(32) NULL COMMENT '上一版本 strategy_id',
`spec_type` VARCHAR(16) NOT NULL DEFAULT 'yaml',
`created_at` DATETIME NOT NULL,
`updated_at` DATETIME NOT NULL,
PRIMARY KEY (`strategy_id`,`version`),
UNIQUE KEY `uq_hd_strat_hash` (`config_hash`),
KEY `ix_hd_strat_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='策略版本';
-- 策略参数扁平化(path → value),便于跨版本 diff 与敏感性扫描
CREATE TABLE IF NOT EXISTS `hd_strategy_param` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`strategy_id` VARCHAR(32) NOT NULL,
`version` VARCHAR(16) NOT NULL,
`param_path` VARCHAR(255) NOT NULL,
`param_value` VARCHAR(500) NULL,
`value_type` VARCHAR(16) NULL COMMENT 'int/float/str/bool/null',
`created_at` DATETIME NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_hd_sparam` (`strategy_id`,`version`,`param_path`),
KEY `ix_hd_sparam_path` (`param_path`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='策略参数扁平表';
-- 回测运行头(plan.md §47 原则 6:可复现四元组)
CREATE TABLE IF NOT EXISTS `hd_backtest_run` (
`run_id` VARCHAR(32) NOT NULL,
`strategy_id` VARCHAR(32) NOT NULL,
`strategy_version` VARCHAR(16) NOT NULL,
`mode` VARCHAR(16) NOT NULL DEFAULT 'single'
COMMENT 'single/in_sample/out_of_sample/walkforward_window',
`wf_id` VARCHAR(32) NULL,
`window_index` INT NULL,
`start_date` DATE NOT NULL,
`end_date` DATE NOT NULL,
`initial_capital` DECIMAL(24,4) NOT NULL,
`final_capital` DECIMAL(24,4) NULL,
`total_dividend_net` DECIMAL(24,4) NOT NULL DEFAULT 0 COMMENT '累计现金分红(税后)',
`total_dividend_tax` DECIMAL(24,4) NOT NULL DEFAULT 0 COMMENT '累计代扣红利税',
`total_fees` DECIMAL(24,4) NOT NULL DEFAULT 0 COMMENT '累计交易费用',
`cash_final` DECIMAL(24,4) NULL COMMENT '期末现金(用于资金对账)',
`strategy_config_json` MEDIUMTEXT NOT NULL,
`config_hash` VARCHAR(64) NOT NULL,
`backtest_config_json` TEXT NULL,
`cost_json` TEXT NULL,
`data_version` VARCHAR(64) NULL,
`code_version` VARCHAR(32) NULL,
`seed` INT NULL,
`status` VARCHAR(16) NOT NULL DEFAULT 'OK' COMMENT 'OK/FAIL/PARTIAL',
`unimplemented_json` TEXT NULL COMMENT '未建模部分显式声明(如涨跌停近似)',
`display_name` VARCHAR(128) NULL COMMENT '用户命名',
`notes` VARCHAR(500) NULL COMMENT '用户备注',
`universe_run_id` VARCHAR(32) NULL COMMENT '关联的股票池筛选记录(--universe-run 指定时写入)',
`archived_at` DATETIME NULL COMMENT '归档时间;NULL=未归档',
`deleted_at` DATETIME NULL COMMENT '软删除时间;NULL=未删除',
`created_at` DATETIME NOT NULL,
PRIMARY KEY (`run_id`),
KEY `ix_hd_bt_strategy` (`strategy_id`,`strategy_version`),
KEY `ix_hd_bt_wf` (`wf_id`,`window_index`),
KEY `ix_hd_bt_period` (`start_date`,`end_date`),
KEY `ix_hd_bt_universe` (`universe_run_id`),
KEY `ix_hd_bt_state` (`deleted_at`,`archived_at`,`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='回测运行头';
-- 逐日净值曲线(含基准对齐,plan.md §28.4)
CREATE TABLE IF NOT EXISTS `hd_backtest_equity` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`run_id` VARCHAR(32) NOT NULL,
`trade_date` DATE NOT NULL,
`nav` DECIMAL(24,8) NOT NULL COMMENT '组合净值(初始=1)',
`total_value` DECIMAL(24,4) NOT NULL,
`cash` DECIMAL(24,4) NOT NULL,
`position_value` DECIMAL(24,4) NOT NULL,
`daily_return` DECIMAL(18,10) NULL,
`cum_return` DECIMAL(18,10) NULL,
`drawdown` DECIMAL(18,10) NULL COMMENT '相对历史高点回撤(负值)',
`holding_count` INT NOT NULL DEFAULT 0,
`benchmark_code` VARCHAR(16) NULL,
`benchmark_close` DECIMAL(16,4) NULL,
`benchmark_nav` DECIMAL(24,8) NULL,
`created_at` DATETIME NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_hd_bt_eq` (`run_id`,`trade_date`),
KEY `ix_hd_bt_eq_date` (`trade_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='回测净值曲线';
-- 逐日持仓快照
CREATE TABLE IF NOT EXISTS `hd_backtest_position` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`run_id` VARCHAR(32) NOT NULL,
`trade_date` DATE NOT NULL,
`symbol` VARCHAR(12) NOT NULL,
`quantity` DECIMAL(20,4) NOT NULL,
`avg_cost` DECIMAL(16,4) NULL,
`close` DECIMAL(16,4) NULL,
`market_value` DECIMAL(24,4) NULL,
`weight` DECIMAL(12,8) NULL,
`unrealized_pnl` DECIMAL(24,4) NULL,
`holding_days` INT NULL,
`created_at` DATETIME NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_hd_bt_pos` (`run_id`,`trade_date`,`symbol`),
KEY `ix_hd_bt_pos_sym` (`run_id`,`symbol`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='逐日持仓';
-- 逐笔成交(plan.md §32;reason_json 为 AI 可解释性关键)
CREATE TABLE IF NOT EXISTS `hd_backtest_trade` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`run_id` VARCHAR(32) NOT NULL,
`trade_id` VARCHAR(32) NOT NULL,
`symbol` VARCHAR(12) NOT NULL,
`signal_date` DATE NOT NULL,
`execution_date` DATE NOT NULL,
`side` VARCHAR(4) NOT NULL COMMENT 'BUY/SELL',
`price` DECIMAL(16,4) NOT NULL,
`quantity` DECIMAL(20,4) NOT NULL,
`amount` DECIMAL(24,4) NOT NULL,
`commission` DECIMAL(16,4) NOT NULL DEFAULT 0,
`stamp_tax` DECIMAL(16,4) NOT NULL DEFAULT 0,
`transfer_fee` DECIMAL(16,4) NOT NULL DEFAULT 0,
`slippage_cost` DECIMAL(16,4) NOT NULL DEFAULT 0,
`total_cost` DECIMAL(16,4) NOT NULL DEFAULT 0,
`realized_pnl` DECIMAL(24,4) NULL,
`holding_days` INT NULL,
`reason_json` TEXT NULL COMMENT 'BUY/SELL 的完整理由快照',
`created_at` DATETIME NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_hd_bt_trade` (`run_id`,`trade_id`),
KEY `ix_hd_bt_trade_sym` (`run_id`,`symbol`,`execution_date`),
KEY `ix_hd_bt_trade_date` (`execution_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='回测成交流水';
-- 逐信号(含 executed/skip_reason,用于区分「想买」与「买到了」)
CREATE TABLE IF NOT EXISTS `hd_backtest_signal` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`run_id` VARCHAR(32) NOT NULL,
`symbol` VARCHAR(12) NOT NULL,
`signal_date` DATE NOT NULL,
`signal_type` VARCHAR(8) NOT NULL COMMENT 'BUY/SELL/HOLD/WATCH',
`yield_value` DECIMAL(18,8) NULL COMMENT '触发时的股息率',
`yield_percentile` DECIMAL(12,6) NULL COMMENT '触发时的历史分位 %',
`target_weight` DECIMAL(12,8) NULL,
`price` DECIMAL(16,4) NULL,
`executed` TINYINT(1) NOT NULL DEFAULT 0,
`skip_reason` VARCHAR(64) NULL COMMENT 'LIMIT_UP/LIMIT_DOWN/SUSPENDED/NO_CASH/MIN_COMMISSION',
`reason_json` TEXT NULL,
`created_at` DATETIME NOT NULL,
PRIMARY KEY (`id`),
KEY `ix_hd_bt_sig` (`run_id`,`symbol`,`signal_date`),
KEY `ix_hd_bt_sig_type` (`run_id`,`signal_type`,`executed`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='回测信号流水';
-- 绩效指标长表(scope 区分样本内/外/窗口;plan.md §28)
CREATE TABLE IF NOT EXISTS `hd_backtest_metric` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`run_id` VARCHAR(32) NOT NULL,
`scope` VARCHAR(24) NOT NULL COMMENT 'all/train/test/window_k/year_YYYY',
`category` VARCHAR(16) NOT NULL COMMENT 'return/risk/trade/benchmark',
`metric_code` VARCHAR(48) NOT NULL,
`metric_value` DECIMAL(24,8) NULL,
`benchmark_code` VARCHAR(16) NOT NULL DEFAULT '' COMMENT '空串=策略自身;NULL 会绕过唯一约束故禁用',
`unit` VARCHAR(16) NULL COMMENT 'ratio/pct/money/count/days',
`created_at` DATETIME NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_hd_bt_metric` (`run_id`,`scope`,`metric_code`,`benchmark_code`),
KEY `ix_hd_bt_metric_code` (`metric_code`),
KEY `ix_hd_bt_metric_scope` (`run_id`,`scope`,`category`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='回测绩效指标';
-- Walk-forward 运行头(plan.md §23)
CREATE TABLE IF NOT EXISTS `hd_walkforward_run` (
`wf_id` VARCHAR(32) NOT NULL,
`strategy_id` VARCHAR(32) NOT NULL,
`strategy_version` VARCHAR(16) NOT NULL,
`config_json` MEDIUMTEXT NOT NULL,
`config_hash` VARCHAR(64) NOT NULL,
`scheme` VARCHAR(16) NOT NULL COMMENT 'rolling/expanding',
`train_years` INT NOT NULL,
`test_years` INT NOT NULL,
`step_months` INT NOT NULL,
`window_count` INT NOT NULL DEFAULT 0,
`data_version` VARCHAR(64) NULL,
`code_version` VARCHAR(32) NULL,
`status` VARCHAR(16) NOT NULL DEFAULT 'OK',
`created_at` DATETIME NOT NULL,
PRIMARY KEY (`wf_id`),
KEY `ix_hd_wf_strategy` (`strategy_id`,`strategy_version`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='Walk-forward 运行头';
-- Walk-forward 每个窗口(train 定参 → test 冻结,plan.md §25)
CREATE TABLE IF NOT EXISTS `hd_walkforward_window` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`wf_id` VARCHAR(32) NOT NULL,
`window_index` INT NOT NULL,
`train_start` DATE NOT NULL,
`train_end` DATE NOT NULL,
`test_start` DATE NOT NULL,
`test_end` DATE NOT NULL,
`frozen_params_json` TEXT NOT NULL COMMENT '由 train 段确定、在 test 段冻结的参数',
`train_run_id` VARCHAR(32) NULL,
`test_run_id` VARCHAR(32) NULL,
`created_at` DATETIME NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_hd_wf_win` (`wf_id`,`window_index`),
KEY `ix_hd_wf_win_period` (`test_start`,`test_end`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='Walk-forward 窗口';
-- 参数敏感性扫描(plan.md §27 过拟合控制)
CREATE TABLE IF NOT EXISTS `hd_sensitivity_run` (
`sens_id` VARCHAR(32) NOT NULL,
`base_strategy_id` VARCHAR(32) NOT NULL,
`base_version` VARCHAR(16) NOT NULL,
`sweep_json` TEXT NOT NULL COMMENT '扫描网格定义',
`point_count` INT NOT NULL DEFAULT 0,
`period_start` DATE NULL,
`period_end` DATE NULL,
`data_version` VARCHAR(64) NULL,
`code_version` VARCHAR(32) NULL,
`status` VARCHAR(16) NOT NULL DEFAULT 'OK',
`created_at` DATETIME NOT NULL,
PRIMARY KEY (`sens_id`),
KEY `ix_hd_sens_strategy` (`base_strategy_id`,`base_version`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='敏感性扫描头';
-- 敏感性扫描的每个参数组合及其绩效
CREATE TABLE IF NOT EXISTS `hd_sensitivity_point` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`sens_id` VARCHAR(32) NOT NULL,
`point_index` INT NOT NULL,
`param_values_json` TEXT NOT NULL,
`param_key` VARCHAR(128) NULL COMMENT '单参数扫描时的主键(便于画曲线)',
`run_id` VARCHAR(32) NULL COMMENT '对应回测 run',
`cagr` DECIMAL(18,8) NULL,
`total_return` DECIMAL(18,8) NULL,
`max_drawdown` DECIMAL(18,8) NULL,
`sharpe` DECIMAL(18,8) NULL,
`calmar` DECIMAL(18,8) NULL,
`sortino` DECIMAL(18,8) NULL,
`trade_count` INT NULL,
`turnover` DECIMAL(18,8) NULL,
`extra_json` TEXT NULL,
`created_at` DATETIME NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_hd_sens_pt` (`sens_id`,`point_index`),
KEY `ix_hd_sens_pt_key` (`sens_id`,`param_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='敏感性扫描点';
-- 报告元数据(含 SQL 溯源,保证页面数字可复现)
CREATE TABLE IF NOT EXISTS `hd_report` (
`report_id` VARCHAR(32) NOT NULL,
`report_type` VARCHAR(24) NOT NULL COMMENT 'audit/universe/profile/backtest/walkforward/sensitivity/strategy/index',
`title` VARCHAR(255) NOT NULL,
`html_path` VARCHAR(500) NOT NULL,
`source_run_ids` VARCHAR(500) NULL COMMENT '逗号分隔的来源 run_id',
`context_json` MEDIUMTEXT NULL COMMENT '渲染上下文快照',
`sql_provenance_json` MEDIUMTEXT NULL COMMENT '页面数据的来源 SQL(可复现性)',
`data_version` VARCHAR(64) NULL,
`generated_at` DATETIME NOT NULL,
`file_bytes` BIGINT NULL,
PRIMARY KEY (`report_id`),
KEY `ix_hd_report_type` (`report_type`,`generated_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='报告元数据';