Files
EMWebApi/utils/fin_free_holders.py
T
toor 797369bbed 新增三大股东接口, 补充 SKILLS.md 使用说明书
- 新增 十大流通股东/十大股东明细/股东户数 三个抓取模块并接入 API
- 冒烟测试扩展至 30 用例, 全部通过
- 新增 SKILLS.md (给其他 agent 使用的工具说明书)
- 补充待优化端点清单与文档对齐规范
2026-08-15 10:33:47 +08:00

65 lines
1.8 KiB
Python

"""东方财富 CHOICE API 十大流通股东抓取工具。
- 输入: 股票代码列表 (如 ['603233.SH'])
- 输出: pandas DataFrame (按报告期末的十大流通股东)
- 不需要登录, 不需要 cookie, 无文件落盘
"""
from __future__ import annotations
import pandas as pd
from utils._emchoice import fetch_report
_REPORT_NAME = "RPT_F10_EH_FREEHOLDERS"
_COLUMNS = "F10_FREEHOLDERS"
def _build_params(
secucode: str,
start_date: str,
end_date: str,
) -> dict[str, str]:
"""构造 RPT_F10_EH_FREEHOLDERS 查询参数."""
return {
"source": "CHOICE",
"reportName": _REPORT_NAME,
"columns": _COLUMNS,
"quoteColumns": "",
"pageNumber": "",
"pageSize": "",
"sortColumns": "END_DATE,HOLD_NUM",
"client": "SW",
"filter": f'(SECUCODE="{secucode}")(END_DATE>=\'{start_date}\')(END_DATE<=\'{end_date}\')',
"sortTypes": "-1,-1",
}
def _fetch_one(secucode: str, years: int) -> pd.DataFrame:
"""拉一只股票的十大流通股东."""
today = pd.Timestamp.today()
start = (today - pd.DateOffset(years=years)).strftime("%Y-%m-%d")
end = today.strftime("%Y-%m-%d")
rows = fetch_report(_build_params(secucode, start, end))
df = pd.DataFrame(rows)
if not df.empty:
df["SECUCODE"] = secucode
return df
def get_free_holders(secucodes: list[str], years: int = 3) -> pd.DataFrame:
"""抓取指定股票的十大流通股东并按股票合并。"""
codes = [code.strip() for code in secucodes if code.strip()]
if not codes:
return pd.DataFrame()
frames: list[pd.DataFrame] = []
for code in codes:
df = _fetch_one(code, years)
if not df.empty:
frames.append(df)
if not frames:
return pd.DataFrame()
return pd.concat(frames, ignore_index=True)