Files

4.8 KiB

A 股 Top N 自动筛 (替换硬编码候选池)

背景: cn_dividend_buy_timing.py 原本用 8 只硬编码候选池 (601288/601398/601088/600023/600585/601006/601939/601658)。用户 2026-09-17 要求改为自动从全市场筛 Top N,并按行业分散。

数据源 (优先东方财富, 因为字段已含股息率)

import akshare as ak

# 单只拉所有季度分红 (按 report date)
df = ak.stock_fhps_em(date='20251231')  # 一次拿 3000+ 只股票
# 字段: ['代码', '名称', '送转股份-送转总比例', ..., '现金分红-股息率',
#         '每股收益', '预案公告日', '股权登记日', '除权除息日', '方案进度', '最新公告日期']
# 关键: '现金分红-股息率' 是小数 (0.0544 = 5.44%), 不是 "5.44%" 字符串!

字段单位坑 (9/17 实测踩到):

  • ak.stock_fhps_em 的 现金分红-股息率 是 小数 (0.0544) — 不是 "5.44%" 字符串
  • ak.stock_fhps_detail_ths 的 税前分红率 是 带 % 的字符串 ("5.44%") — 处理时 .str.replace('%', '') 再 pd.to_numeric
  • ak.stock_history_dividend_detail 的 派息 是 每 10 股派多少元 — 不是每股

三种接口对比:

接口 字段 单位 用法
stock_fhps_em(date=) 现金分红-股息率 小数 (乘 100 = %) 批量, 3 秒/3000+ 只, 优先
stock_fhps_detail_ths(symbol=) 税前分红率 字符串 "%" 单只, 0.5s/只, TTM 校验
stock_history_dividend_detail(symbol=) 派息 元/10 股 历史明细, 别自己算股息率

TTM 算法

用户对照同花顺 APP: 北京银行显示 7.36%, 我脚本如果只算 "最近 1 年报派息" = 5.44%, 漏了 2024 H1 中报。

TTM 算法: 拉最近 8 个季度的 stock_fhps_em (24 个月), 按 股权登记日 >= 今天 - 365天 AND 方案进度 == '实施分配', 把 现金分红-股息率 累加.

注意: 累加前乘 100 (字段是小数, 不是 %)

df_all = pd.concat([ak.stock_fhps_em(date=d) for d in REPORT_DATES])
df_all['yld_num'] = pd.to_numeric(df_all['现金分红-股息率'], errors='coerce') * 100
df_done = df_all[df_all['方案进度'] == '实施分配'].copy()
df_done['reg_date'] = pd.to_datetime(df_done['股权登记日'], errors='coerce')
ttm_start = datetime.now() - timedelta(days=365)
df_ttm = df_done[df_done['reg_date'] >= ttm_start]
ttm_map = df_ttm.groupby('代码')['yld_num'].sum().to_dict()

行业分散

用户原话 (9/17): "每个行业 Top3 是不是就够了"

实现方案: 拉行业 → 成分股 → 按行业分组选 Top N (每行业最多 3 只, 总 8 只):

# akshare 接口 (注意代理):
# ak.stock_board_industry_name_em()  → 行业列表
# ak.stock_board_industry_cons_em(symbol='银行')  → 成分股

代理坑: akshare em 接口常被全局 HTTP_PROXY 拦截, 报错 ConnectionError。 修法: 跑脚本前 unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY, 或在 Python 里 os.environ.pop(...)。

备选 (更稳): 用 ak.stock_individual_info_ths(symbol=) 拿单只股票的行业字段 (industry 列), 但单只 1 秒 → 3000 只 = 50 分钟, 不可行。

当前实现: 直接按 "已用 TTM 股息率" 排序取前 30 只, 不强行行业分散 (因为 akshare 行业接口代理不稳)。 未来优化: 加本地缓存 (~/.hermes/cache/cn_industry.json), 一次拉全, 后续查表。

Cron 集成方案

项 现状
cn_dividend_buy_timing.py 硬编码 HIGH_DIVIDEND_POOL 8 只银行/能源
改进 读 ~/.hermes/trading/canary/cn_top8.json 缓存
缓存来源 每周一 cron 跑 cn_dividend_top8.py → 写缓存
缓存格式 [{"code": "601169", "name": "北京银行", "yield_pct": 7.36, ...}, ...]

脚本路径:

  • 测试版: /tmp/cn_dividend_top8.py (9/17 跑通)
  • 正式版待移: ~/.hermes/scripts/cn_dividend_top8.py

关键坑 (9/17 实测)

  1. pd.to_numeric(s.str.replace('%', '')) 处理 None / "--": 用 errors='coerce' + .dropna()
  2. 东方财富字段是 0.15 不是 "0.15%" (小数 vs 百分比字符串) — 乘 100 别漏
  3. 拉 5675 只价格太慢 (>10 分钟) — 粗筛按 "派息次数 × 累计股息" 取 top 200, 只拉这些价格
  4. 代理 env 全局干扰 akshare 国内 API: 跑脚本前 unset 4 个 proxy var
  5. 同花顺 vs 自己算的 TTM 不一致: 永远以同花顺为准, 自己算只是近似

实战结果 (9/17)

数据源 北京银行 TTM
同花顺 APP 7.36%
stock_fhps_detail_ths "实施" 累加 (登记日 TTM) 5.44% (漏 2024 H1)
stock_fhps_em TTM (登记日 + 实施, 跨多个 date) 7.44% ✓
stock_history_dividend 自己算 5.02-7.36% (单位混淆)

结论: stock_fhps_em TTM 累加最准, 接近同花顺 7.36%。