feat(data): A档全量落地——热度三件+股东户数收尾(用户批「按此方式实施,nas验证后推vps」);①xueqiu_hot雪球5633行+sina_sector新浪行业49行对账源进ak-events 19:30(同款快照语义)②hot_rank东财人气榜fetcher/注册先行,wrapper挂载留待恢复窗测量拍板(墙未定论不带不确定单元上线防缺日告警噪音)③gdhs股东户数进ak-weekly周六窗=per-period新形状:build_gdhs_units只枚举距今>90天季度末(披露守卫,防未披露期拉空走真空+done语义被marker锁死永不重拉的09-02设计坑),单次全市场5342行,marker去重无新期零网络;vintage缺日名单+2快照族(hot_rank留位);+6测试(注册/None/df契约/gdhs守卫两形态/缺日名单)215全绿 [vps]
CI/CD / test (push) Successful in 2s
CI/CD / nas-deploy (push) Successful in 9s
CI/CD / nas-verify (push) Successful in 16s

This commit is contained in:
2026-09-02 09:16:14 +08:00
parent 00cfd45d1a
commit 46a0569b9a
6 changed files with 145 additions and 6 deletions
@@ -158,3 +158,50 @@ class TestSinaThsFamily:
for fetch in (mod.fetch_fund_flow_industry, mod.fetch_fund_flow_concept,
mod.fetch_ths_industry, mod.fetch_ths_concept):
assert fetch("20260902") is df
class TestHotAndGdhsFamily:
"""热度三件 + 股东户数 (09-02 用户批全量落地)。"""
def test_registry(self):
for t in ("hot_rank", "xueqiu_hot", "sina_sector"):
assert t in mod.PER_DATE_TYPES
assert mod.GDHS in mod.ALL_TYPES
def test_fetchers_none_on_retry_exhausted(self, monkeypatch):
monkeypatch.setattr(
mod, "call_ak_with_retry", lambda *a, **k: (None, "failed"))
assert mod.fetch_hot_rank("20260902") is None
assert mod.fetch_xueqiu_hot("20260902") is None
assert mod.fetch_sina_sector("20260902") is None
assert mod.fetch_gdhs("20250630") is None
def test_fetchers_df_on_ok(self, monkeypatch):
df = pd.DataFrame({"a": [1]})
monkeypatch.setattr(mod, "call_ak_with_retry",
lambda *a, **k: (df, "ok"))
assert mod.fetch_hot_rank("20260902") is df
assert mod.fetch_xueqiu_hot("20260902") is df
assert mod.fetch_sina_sector("20260902") is df
assert mod.fetch_gdhs("20250630") is df
def test_gdhs_units_disclosure_guard(self):
"""只枚举距今 >90 天的季度末: 90 天内的季度末绝不入列 (防空档标 done 陷阱)。"""
import datetime as _dt
now = _dt.date(2026, 9, 2)
units = mod.build_gdhs_units(now=now)
ids = [u[0] for u in units]
# cutoff = 2026-06-04: 20260630 在 90 天内必须缺席; 20250630 必须在列
assert "20250630_gdhs" in ids
assert all(not i.startswith(("20260630", "20260930")) for i in ids)
assert all(i[4:8] in ("0331", "0630", "0930", "1231") for i in ids)
# 近 3 年窗口
assert ids[0].startswith(("2024", "2025"))
def test_gdhs_units_empty_when_nothing_disclosed(self):
import datetime as _dt
# 年初 1 月: 最新的已披露季度末是去年 9/30 (1231 未满 90 天)
units = mod.build_gdhs_units(now=_dt.date(2026, 1, 15))
ids = [u[0] for u in units]
assert "20250930_gdhs" in ids
assert "20251231_gdhs" not in ids
@@ -200,6 +200,13 @@ class TestPanelGapCheck:
assert hole.isoformat() in (
status_panel["fund_flow_industry"]["holes_permanent"])
def test_panel_registry_snapshot_family(self):
"""09-02 全量落地批次注册正确: 快照族在列且不落可回补档; hot_rank 未挂载不入列。"""
for t in ("xueqiu_hot", "sina_sector", "fund_flow_concept", "ths_concept"):
assert t in svc.PANEL_TYPES
assert t not in svc.BACKFILLABLE_PANEL_TYPES
assert "hot_rank" not in svc.PANEL_TYPES # 拍板挂载后才入列
# ======================== main / JSON 契约 ========================