You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
88 lines
2.7 KiB
88 lines
2.7 KiB
"""东方财富 CHOICE API 季度财务摘要抓取工具。"""
|
|
from __future__ import annotations
|
|
|
|
import pandas as pd
|
|
import requests
|
|
|
|
from utils.fin_summary import BASE_URL, HEADERS, TIMEOUT
|
|
|
|
|
|
_REPORT_NAME = "RPT_HSF9_FIN_SUMMARYQUARTERNEW"
|
|
_COLUMNS = "HSF9_FIN_SUMMARYQUARTER"
|
|
|
|
|
|
def _build_filter(
|
|
secucode: str,
|
|
years: int,
|
|
report_dates: list[str] | None,
|
|
) -> str:
|
|
if report_dates is not None:
|
|
dates = ",".join(f"'{date}'" for date in report_dates)
|
|
return f'(secuCode="{secucode}")(REPORTDATE in ({dates}))'
|
|
|
|
today = pd.Timestamp.today()
|
|
start = (today - pd.DateOffset(years=years)).strftime("%Y-%m-%d")
|
|
end = today.strftime("%Y-%m-%d")
|
|
return (
|
|
f'(secuCode="{secucode}")'
|
|
f"((REPORTDATE>='{start}')(REPORTDATE<='{end}')"
|
|
'(dateType in ("1","2","3","4","7"))(|IS_NEWEST="1"))'
|
|
'(TYPE_CODE in ("1"))'
|
|
)
|
|
|
|
|
|
def _fetch_one(
|
|
secucode: str,
|
|
years: int,
|
|
report_dates: list[str] | None,
|
|
) -> pd.DataFrame:
|
|
params = {
|
|
"reportName": _REPORT_NAME,
|
|
"columns": _COLUMNS,
|
|
"filter": _build_filter(secucode, years, report_dates),
|
|
"pageNumber": "1",
|
|
"pageSize": "500",
|
|
"sortTypes": "-1,1" if report_dates is None else "-1",
|
|
"sortColumns": "REPORTDATE,TYPE" if report_dates is None else "REPORTDATE",
|
|
"source": "CHOICE",
|
|
"client": "SW",
|
|
}
|
|
response = requests.post(BASE_URL, headers=HEADERS, data=params, timeout=TIMEOUT)
|
|
response.raise_for_status()
|
|
payload = response.json()
|
|
if not payload.get("success"):
|
|
raise RuntimeError(f"{secucode}: 接口返回失败: {payload}")
|
|
|
|
rows = payload.get("result", {}).get("data") or []
|
|
df = pd.DataFrame(rows)
|
|
if not df.empty:
|
|
df["SECUCODE"] = secucode
|
|
return df
|
|
|
|
|
|
def get_fin_quarterly_summary(
|
|
secucodes: list[str],
|
|
years: int = 3,
|
|
report_dates: list[str] | None = None,
|
|
) -> 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, report_dates)
|
|
if not df.empty:
|
|
frames.append(df)
|
|
|
|
if not frames:
|
|
return pd.DataFrame()
|
|
|
|
result = pd.concat(frames, ignore_index=True)
|
|
if {"SECUCODE", "REPORT_DATE"}.issubset(result.columns):
|
|
result = result.drop_duplicates(["SECUCODE", "REPORT_DATE"], keep="first")
|
|
result = result.sort_values(
|
|
["SECUCODE", "REPORT_DATE"], ascending=[True, False]
|
|
)
|
|
return result.reset_index(drop=True)
|
|
|