新增三大股东接口, 补充 SKILLS.md 使用说明书
- 新增 十大流通股东/十大股东明细/股东户数 三个抓取模块并接入 API - 冒烟测试扩展至 30 用例, 全部通过 - 新增 SKILLS.md (给其他 agent 使用的工具说明书) - 补充待优化端点清单与文档对齐规范
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
"""东方财富 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)
|
||||
@@ -0,0 +1,64 @@
|
||||
"""东方财富 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_HOLDERNUM"
|
||||
_COLUMNS = "CHOICEF9_EH_HOLDERNUM"
|
||||
|
||||
|
||||
def _build_params(
|
||||
secucode: str,
|
||||
start_date: str,
|
||||
end_date: str,
|
||||
) -> dict[str, str]:
|
||||
"""构造 RPT_F10_EH_HOLDERNUM 查询参数."""
|
||||
return {
|
||||
"source": "CHOICE",
|
||||
"reportName": _REPORT_NAME,
|
||||
"columns": _COLUMNS,
|
||||
"quoteColumns": "",
|
||||
"filter": f'(SECUCODE="{secucode}")(END_DATE>=\'{start_date}\')(END_DATE<=\'{end_date}\')',
|
||||
"pageNumber": "",
|
||||
"pageSize": "",
|
||||
"sortColumns": "END_DATE",
|
||||
"client": "SW",
|
||||
"sortTypes": "-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_holder_num(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)
|
||||
@@ -0,0 +1,70 @@
|
||||
"""东方财富 CHOICE API 十大股东明细抓取工具。
|
||||
- 输入: 股票代码列表 (如 ['603233.SH'])
|
||||
- 输出: pandas DataFrame (按报告期末的十大股东明细)
|
||||
- 不需要登录, 不需要 cookie, 无文件落盘
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import pandas as pd
|
||||
|
||||
from utils._emchoice import fetch_report
|
||||
|
||||
|
||||
_REPORT_NAME = "RPT_DMSK_HOLDERS"
|
||||
# 列清单沿用 docs/api_docs.md 抓包值
|
||||
_COLUMNS = (
|
||||
"SECUCODE,SECURITY_CODE,ORG_CODE,SECURITY_TYPE_CODE,END_DATE,RANK,"
|
||||
"HOLDER_CODE,HOLDER_NAME,HOLD_NUM,HOLD_RATIO,HOLD_NUM_CHANGE,"
|
||||
"HOLD_RATIO_CHANGE,DIRECTION,SHARES_TYPE,HOLDER_NATURE,REPORT_TYPE,"
|
||||
"HOLD_CHANGE,NOTICE_DATE,HOLD_RATIO_YOY,REPORT_DATE_NAME,REFERENCE_MARKET_CAP"
|
||||
)
|
||||
|
||||
|
||||
def _build_params(
|
||||
secucode: str,
|
||||
start_date: str,
|
||||
end_date: str,
|
||||
) -> dict[str, str]:
|
||||
"""构造 RPT_DMSK_HOLDERS 查询参数."""
|
||||
return {
|
||||
"reportName": _REPORT_NAME,
|
||||
"columns": _COLUMNS,
|
||||
"quoteColumns": "",
|
||||
"filter": f'(SECUCODE="{secucode}")(END_DATE>=\'{start_date}\')(END_DATE<=\'{end_date}\')',
|
||||
"pageNumber": "",
|
||||
"pageSize": "",
|
||||
"sortTypes": "-1,1",
|
||||
"sortColumns": "END_DATE,RANK",
|
||||
"client": "SW",
|
||||
"source": "CHOICE",
|
||||
}
|
||||
|
||||
|
||||
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_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)
|
||||
Reference in New Issue
Block a user