feat(data): 财务/日线同步增量 + 新浪「两边一致」校验兜底 + 逐只进度
- financial 默认增量:按 A 股披露节奏判断已最新并跳过;--full 强制全量重拉 - Tushare fina_indicator 增加报告期窗口与 100 条/请求自动分页(修复老报告期静默截断) - 新浪兜底收紧为校验兜底:两源重叠历史一致才导入缺失键,行标记 source=sina; 财务可比字段取 eps/销售毛利率(ROE 两端口径不同不作依据),日线只比较最近重叠交易日 - CLI 输出逐只进度与导入内容描述(来源/行数/报告期与公告区间),失败股票留待重跑 - financial_indicator 增 source 列(迁移 d3f6c9a21b04);新增一致性/分页/服务测试
This commit is contained in:
@@ -69,11 +69,15 @@ class FailoverProvider:
|
||||
fallback_call=lambda: self.fallback.get_adjust_factor(symbol, start, end),
|
||||
)
|
||||
|
||||
def get_financial(self, symbol: str) -> list:
|
||||
def get_financial(
|
||||
self, symbol: str, start: date | None = None, end: date | None = None
|
||||
) -> list:
|
||||
return self._with_failover(
|
||||
"get_financial",
|
||||
primary_call=lambda: self.primary.get_financial(symbol),
|
||||
fallback_call=lambda: self.fallback.get_financial(symbol),
|
||||
start=start,
|
||||
end=end,
|
||||
primary_call=lambda: self.primary.get_financial(symbol, start, end),
|
||||
fallback_call=lambda: self.fallback.get_financial(symbol, start, end),
|
||||
)
|
||||
|
||||
# ---- 内部 ----
|
||||
|
||||
@@ -128,8 +128,17 @@ class SinaProvider:
|
||||
|
||||
# ---- 财务(兜底 Tushare fina_indicator) ----
|
||||
|
||||
def get_financial(self, symbol: str) -> list[FinancialIndicator]:
|
||||
"""新浪关键指标(source=gjzb),含披露日 publish_date → announce_date。"""
|
||||
def get_financial(
|
||||
self,
|
||||
symbol: str,
|
||||
start: date | None = None,
|
||||
end: date | None = None,
|
||||
) -> list[FinancialIndicator]:
|
||||
"""新浪关键指标(source=gjzb),含披露日 publish_date → announce_date。
|
||||
|
||||
新浪不支持按报告期窗口拉取:忽略 start/end 时返回其全部返回的
|
||||
报告期;传入窗口则按 report_date 客户端过滤(新浪行 source=sina)。
|
||||
"""
|
||||
params = {
|
||||
"paperCode": _to_sina_symbol(symbol),
|
||||
"source": "gjzb",
|
||||
@@ -153,6 +162,11 @@ class SinaProvider:
|
||||
announce = entry.get("publish_date")
|
||||
if not announce:
|
||||
continue # 无披露日不可用于研究(防未来函数)
|
||||
report_day = _to_date(str(rd))
|
||||
if start is not None and report_day < start:
|
||||
continue
|
||||
if end is not None and report_day > end:
|
||||
continue
|
||||
fields: dict[str, Decimal | None] = {
|
||||
"eps": None,
|
||||
"roe": None,
|
||||
@@ -167,8 +181,9 @@ class SinaProvider:
|
||||
rows.append(
|
||||
FinancialIndicator(
|
||||
symbol=symbol,
|
||||
report_date=_to_date(str(rd)),
|
||||
report_date=report_day,
|
||||
announce_date=_to_date(str(announce)),
|
||||
source="sina",
|
||||
eps=fields["eps"],
|
||||
roe=fields["roe"],
|
||||
total_revenue=fields["total_revenue"],
|
||||
|
||||
@@ -10,7 +10,7 @@ from __future__ import annotations
|
||||
import importlib
|
||||
import logging
|
||||
import time
|
||||
from datetime import date, datetime
|
||||
from datetime import date, datetime, timedelta
|
||||
from decimal import Decimal
|
||||
from typing import Any
|
||||
|
||||
@@ -183,9 +183,42 @@ class TushareProvider:
|
||||
)
|
||||
return self.normalize_adj_factor(records)
|
||||
|
||||
def get_financial(self, symbol: str) -> list[FinancialIndicator]:
|
||||
records = self._call("fina_indicator", ts_code=symbol)
|
||||
return self.normalize_financial(records)
|
||||
def get_financial(
|
||||
self,
|
||||
symbol: str,
|
||||
start: date | None = None,
|
||||
end: date | None = None,
|
||||
) -> list[FinancialIndicator]:
|
||||
"""fina_indicator:报告期窗口 + 100 条/请求上限自动分页。
|
||||
|
||||
Tushare 单次请求最多返回 100 条(超出按最新 100 条截断),因此
|
||||
全量历史必须按报告期窗口回卷分页,否则老报告期会被静默丢弃。
|
||||
"""
|
||||
lo = start or date(1990, 1, 1)
|
||||
hi = end or date.today()
|
||||
raw: list[dict[str, Any]] = []
|
||||
while lo <= hi:
|
||||
batch = self._call(
|
||||
"fina_indicator",
|
||||
ts_code=symbol,
|
||||
start_date=lo.strftime(_TS_DATE),
|
||||
end_date=hi.strftime(_TS_DATE),
|
||||
)
|
||||
raw += batch
|
||||
if len(batch) < 100:
|
||||
break
|
||||
ends = [
|
||||
datetime.strptime(str(r["end_date"])[:8], _TS_DATE).date()
|
||||
for r in batch
|
||||
if r.get("end_date")
|
||||
]
|
||||
if not ends:
|
||||
break
|
||||
next_hi = min(ends) - timedelta(days=1)
|
||||
if next_hi < lo: # 无进展保护(边界簇被截断等极端情况)
|
||||
break
|
||||
hi = next_hi
|
||||
return self.normalize_financial(raw)
|
||||
|
||||
# ---- 内部 ----
|
||||
|
||||
|
||||
Reference in New Issue
Block a user