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

144 lines
6.2 KiB
YAML
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.
# ============================================================
# 股票筛选条件(★ 可直接修改)
# 对应 plan.md §4/§5 的 MarketFilter / RiskFilter / DividendFilter /
# FinancialQualityFilter。
#
# 所有阈值只允许出现在本文件;代码中不得硬编码。
# 修改后 config_hash 变化,历史 run 仍可完整复现。
# ============================================================
version: 1
name: high_dividend_universe
evaluation:
# point_in_time —— 每个 asof 日期独立求值(回测必须用这个)
# latest —— 仅在最新交易日求值一次
mode: point_in_time
asof: latest
# ------------------------------------------------------------
# MarketFilter —— 市场属性
# ------------------------------------------------------------
market:
# 交易所白名单。需要北交所时加 BSE。
exchanges: [SZSE, SSE]
# 板块白名单:主板 / 创业板 / 科创板 / 北交所
markets: [主板, 创业板, 科创板]
# 上市年限下限(年)—— plan.md §4「上市 ≥ 10 年」
min_listing_years: 10
# 总市值下限(元)—— plan.md §4「市值 ≥ 500 亿」
min_market_cap: 50000000000
# 流通市值下限(元);null = 不限制
min_float_market_cap: null
# 近 20 日日均成交额下限(元)—— 流动性过滤
min_avg_amount_20d: 20000000
# 要求 asof 当日正常交易(非停牌)
require_trading_on_asof: true
# ------------------------------------------------------------
# 行业豁免(对 RiskFilter 与 FinancialQualityFilter 同时生效)
# 银行/保险/证券的负债率天然在 90% 上下、且没有「自由现金流」的概念,
# 若不做豁免,高股息策略里最重要的银行板块会被全部误杀
# (实测:招商银行 90.2% 会被 risk.max_debt_to_assets 直接淘汰)。
# ------------------------------------------------------------
industry_exemptions:
# 豁免资产负债率上限的行业
leverage: [银行, 保险, 证券, 信托]
# 豁免「自由现金流为正 / FCF 覆盖分红 / 经营现金流对利润」要求的行业
free_cash_flow: [银行, 保险, 证券, 信托]
# ------------------------------------------------------------
# RiskFilter —— 风险过滤
# ------------------------------------------------------------
risk:
# 排除 ST / *ST(依据 stock_name_history 按日期还原,而非只看当前名)
exclude_st: true
st_lookback_from_history: true
# 排除退市整理 / 已退市
exclude_delisting: true
# 排除 asof 当日停牌
exclude_suspended: true
# 排除净资产为负
exclude_negative_equity: true
# 资产负债率上限;null = 不限制。金融股由 industry_exemptions.leverage 豁免
max_debt_to_assets: 0.80
# 大股东质押比例上限;null = 不限制(需额外数据源)
max_pledge_ratio: null
# 重大诉讼;当前无数据源,保持 false
exclude_major_litigation: false
# 商誉异常;当前无数据源,保持 false
exclude_goodwill_anomaly: false
# ------------------------------------------------------------
# DividendFilter —— 分红过滤
# ------------------------------------------------------------
dividend:
# 股息率口径:
# computed —— 自算 PIT-TTM(按 imp_ann_date 生效),无未来函数【回测主口径】
# dv_ttm —— 直接用 daily_basic.dv_ttm(仅交叉校验)
yield_source: computed
# 当前股息率下限 —— plan.md §4「股息率 ≥ 3%」
min_dividend_yield: 0.03
# 连续分红年限下限 —— plan.md §4「连续现金分红 ≥ 5 年」
min_continuous_years: 5
# 认定一次分红的规则(D6 决策):
# cash_div_tax_gt_0 —— 税前现金分红 > 0,且 div_proc='实施',且有 ex_date 或 pay_date
continuous_rule: cash_div_tax_gt_0
# 考察窗口(年)
window_years: 6
# 窗口内最少分红年数
min_dividend_years_in_window: 5
# 分红支付率上下限;null = 不限制
max_payout_ratio: 1.00
min_payout_ratio: null
# 5 年 DPS 复合增速下限;null = 不限制
min_dps_cagr_5y: null
# 要求自由现金流为正
require_positive_fcf: true
# 自由现金流对分红的覆盖倍数下限(FCF / 现金分红总额)
min_fcf_dividend_cover: 1.0
# 当「验证所需的数据缺失」时如何处理(安全边际策略的关键取舍):
# pass —— 放行(宽松)。避免因数据未同步而误杀,但会让「无法验证现金流」
# 的股票进入组合,与「安全边际」的初衷相悖。
# reject —— 淘汰(严格)。忠于「FCF 必须为正」的策略逻辑,
# 但结果会依赖数据完整度(数据补齐前后股票池会变化)。
# 实测:当前全部在市股票均有现金流数据,缺失的 10 只均为 1990 年代退市股
# (已被 risk.exclude_delisting 排除),因此两种设置结果相同。
# 若你希望「宁可错过、不可踩雷」,改为 reject。
on_missing_data: pass
# ------------------------------------------------------------
# FinancialQualityFilter —— 财务质量过滤
# ------------------------------------------------------------
quality:
# 5 年平均 ROE 下限
min_roe_5y_avg: 0.08
# 5 年平均 ROIC 下限;null = 不限制
min_roic_5y_avg: null
min_gross_margin: null
min_net_margin: null
# 经营现金流 / 净利润 下限(盈利质量)
min_ocf_to_profit: 0.60
# 覆盖 risk.max_debt_to_assets;null = 不覆盖
max_debt_to_assets: null
# 利息保障倍数下限;null = 不限制
min_interest_cover: null
# 兼容字段:留空则使用顶层的 industry_exemptions
exempt_industries: []
# PIT 纪律:只使用公告日 <= asof 的财报
pit_rule: announce_date_le_asof
# 用哪一期财报:latest_announced(最近已公告)/ ttm
report_basis: latest_announced
# ------------------------------------------------------------
# 输出控制
# ------------------------------------------------------------
output:
# 成员数少于该值触发 WARN(可能是条件过严或数据缺口)
min_members: 10
# 成员数上限;null = 不限制。用于回测时控制持仓候选规模
max_members: 200
warn_on_empty: true