Files
ggx/config/datasource.yml
T
simon 14ec0c6c86 修复:量价单位 / 未来函数守卫 / 实时画像闸门;行情回补到 2005;手册补全流程
本轮会话的三项正确性改造(均为「不报错、只让结果静默错」的类型):

1) 修复 stock_daily 量价单位前后不一致
   - 现象:2015-2019 存 Tushare 原始单位(手/千元),2020 起存(股/元),2019 同日混合;
     而流动性阈值按「元」配置 → 早年门槛实际是「日均成交额 ≥ 200 亿元」,
     把 2015-2019 的股票池整体清空(实测 2016/2017/2018 各选出 0 只)。
   - 修复:写入端 sync/price.py 统一换算;读取端 units.normalize_ohlcv_units
     按行判定并幂等换算(price_history / avg_amount 都走它);
     审计新增 UNIT-OHLCV 防回归。
   - 效果:2016/2017/2018 的股票池变为 7/11/13 只。

2) 未来函数守卫(单次回测)
   - 股票池自带 asof:若晚于回测起点即**拒绝执行**(原先静默冻结套用),
     与 walk-forward 已有的拒绝理由一致;确需复现加 --allow-lookahead-universe,
     偏差写入 unimplemented_json。

3) 新增实时(PIT)个股画像闸门
   - profile/pit.py:每个决策日按当时可见数据重算过去 5 年画像,
     惰性(仅买入条件已触发的标的)、面板按 asof 缓存、
     规则不含财务指标时不查财报表;被剔除时产出 REJECT + 逐规则留痕。
   - 指标定义复用 ProfileBuilder._profile_one(与批量画像逐值等价的回归测试)。
   - profile/coverage.py:窗口覆盖率(按交易日历的真实开市天数),
     策略新增 entry.profile_gate.min_window_coverage(默认 0,不改变既有行为)。
   - core/metrics.py:闸门可用指标的唯一定义(配置期即校验,避免写错指标名静默失效)。

4) 行情回补到 2005(使 5/8/10 年窗口真正完整)
   - stock_daily / adjust_factor / daily_basic 补到 2005-01-04;
     hd_suspend / hd_limit 补到 2010-01-04。
   - 5 年窗口覆盖率:2018-05-18 由 67.0% → 99.1%,2016-12-30 由 39.8% → 99.0%;
     残差经逐日与 hd_suspend 交叉核实为真实停牌(16/16 命中)。
   - 审计 G2/G3 与断点续传原先用固定阈值(2000 / 1500 只),
     会把 2005-2009 的正常数据误判为异常 —— 改为按「当年应有上市股票数」成比例判定。
   - 节流修正:daily/adj_factor/daily_basic 限频 480 → 170(实测该 token 约 196/min 即被拒)。

5) 自我声明如实化
   - 原先「约束未生效」由「过滤后集合为空」判定,会把「这批股票恰好没停牌」
     误报成「hd_suspend 无数据」;改为按表级判定。
   - 补齐此前静默的「配置承诺但未实现」项:suspended_rule/limit_up_down_rule 的 defer、
     cash_mode=reinvest/reinvest_rule、handle_rights_issue、signal_to_execution、
     max_volume_pct、liquidity_limit_pct_adv —— 全部写入 unimplemented_json。

6) 手册:新增 §0「全流程操作(选股 → 画像 → 回测)」置于最前
   - 逐步说明「命令做了什么、数据从哪来、落了哪些库、有哪些坑」;
     含实时画像闸门 9 问 9 答、未来函数守卫表、成交与成本口径、验证 SQL。
   - 修正旧 §2.4 漏传 --universe-run(选了池子却没用于回测);
     修正两处声称「停牌顺延」「分红再投资」已实现的相反表述。

测试:403 项全部通过(含新增 test_units.py、test_profile_pit.py、
未实现声明诚实性测试、行序无关性回归测试)。

注意:本提交中 docs/*、README.md、src/hdiv/web/service.py 除本轮修改外,
也含此前遗留的未提交改动(无法按文件切分)。
2026-10-04 12:47:17 +08:00

108 lines
3.9 KiB
YAML
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.
# ============================================================
# 数据源配置
# 约定:本文件不含任何密钥;密钥经 *_env 字段引用根目录 .env
# 与 ~/project/qlib 共用同一 MariaDB 与 Tushare 账号。
# ============================================================
version: 1
database:
host: 127.0.0.1
port: 3306
db: qlib
user: qlib
password_env: MYSQL_PASSWORD
charset: utf8mb4
pool_size: 8
pool_recycle_sec: 3600
# ---- 只读表白名单(qlib 既有表)----
# db.py 会在 SQLAlchemy 事件层拦截对这些表的 INSERT/UPDATE/DELETE。
# 例外:index_weight 是空表,P0a 需要填充成分股权重(纯新增),
# 见 allow_write_tables。stock_daily/daily_basic/adjust_factor 的历史回补
# 由 P0b 专用通道处理,需显式开启 allow_backfill。
read_only_tables:
- stock
- stock_daily
- adjust_factor
- daily_basic
- financial_indicator
- stock_name_history
- trading_calendar
# 允许写入的 qlib 既有表(仅新增行,禁止 UPDATE/DELETE)
allow_write_tables:
- index_weight
# 历史回补专用允许列表(P0b 使用;默认关闭)
# 注意:回补通道使用 INSERT IGNORE,**绝不覆盖既有行**,
# 因此对 qlib 现有数据是纯追加,可安全用于 2015-2018 区间补齐。
backfill_tables:
- stock_daily
- adjust_factor
- daily_basic
allow_backfill: false
# ---- 本项目自有表前缀 ----
# ddl.py 拒绝创建非该前缀的表;写操作拒绝指向非该前缀的表
own_prefix: hd_
# qlib 的 alembic 版本链禁止被本项目触碰
forbidden_tables:
- alembic_version
forbid_delete: true
tushare:
api_url: http://api.tushare.pro
token_env: TUSHARE_TOKEN
# 限频是**按接口**计算的(实测:dividend 为 200 次/分钟,
# 超过会返回「抱歉,您访问接口(dividend)频率超限(200次/分钟)」并中断任务)。
# 因此这里为每个接口单独配置,取值留安全余量。
rate_limit_default: 380
rate_limits:
dividend: 180 # 实测上限 200/min
fina_indicator: 180
cashflow: 180
balancesheet: 180
income: 180
index_weight: 180
suspend_d: 180
stk_limit: 180
# daily/adj_factor/daily_basic:**不要设成 480**。
# 2026-10-04 回补 2005-2014 时实测:以约 196 次/分钟跑 `daily`,
# 300 次调用后即被 Tushare 拒绝(此 token 的实际上限 ≤ 200/min)。
# 一旦触发限频,客户端要冷却 62 秒再重试 —— 逐日回补 2,400+ 天时,
# 这种「撞墙再等」远比「按额度平滑配速」慢,而且有耗尽 4 次重试的风险。
daily: 170
adj_factor: 170
daily_basic: 170
index_daily: 180
retry: 4
retry_backoff_sec: 2
timeout_sec: 60
# 单次请求最大等待(长任务)
max_retry_backoff_sec: 30
# 命中限频时强制等待的秒数(Tushare 为滑动 60 秒窗口)
rate_limit_cooldown_sec: 62
paths:
output_dir: output
templates_dir: templates
assets_dir: assets
log_dir: logs
# ------------------------------------------------------------
# 行情同步的「完整性」判定(决定断点续传是否重拉整段历史)
#
# 一个交易日被视为已完整同步,要求:
# 当日股票数 >= max(min_symbols_floor, min_symbols_ratio × 当年应有上市股票数)
#
# 早年市场小得多(2005 年约 1,350 只、2010 年约 1,700 只、2024 年约 5,400 只),
# 用单一绝对阈值会把 2005-2009 的每一天都判成「未完成」——回补一旦中断就得
# 从第一天重来。所以改为「按当年规模成比例」判定。
# ------------------------------------------------------------
sync:
# 绝对下限:低于它一定判为不完整(防止比例判定在极早年失效)
min_symbols_floor: 200
# 相对下限:占「当年应有上市股票数」的比例
min_symbols_ratio: 0.6