From c728f2368c3c91e88e2123c69a57c1f245946241 Mon Sep 17 00:00:00 2001 From: claude_dev Date: Fri, 4 Sep 2026 18:46:46 +0800 Subject: [PATCH] =?UTF-8?q?feat(data):=20hot=5Frank=20=E6=8C=82=E8=BD=BD+w?= =?UTF-8?q?rapper=20=E6=97=B6=E9=97=B4=E5=AE=88=E5=8D=AB=20[vps]=E2=80=94?= =?UTF-8?q?=E2=80=94=E5=A2=99=E6=B5=8B=E9=87=8F5=E8=BD=AE=E6=94=B6?= =?UTF-8?q?=E5=AE=98=E7=94=A8=E6=88=B7=E6=8B=8D=E6=9D=BF=E6=96=B9=E6=A1=88?= =?UTF-8?q?A:emappdata=20=E5=A4=9C=E9=97=B4=E5=BC=80=E7=AA=97(22:28=20?= =?UTF-8?q?=E5=AE=9E=E6=B5=8B=20OK,=E6=99=A8=E5=8D=884=E8=BD=AE=E5=85=A8?= =?UTF-8?q?=20FAIL),19:30=20=E6=A1=A3=E5=A4=B1=E8=B4=A5=E9=9B=B6=E8=BF=9E?= =?UTF-8?q?=E5=B8=A6(=E5=8D=95=20unit=20=E5=A4=B1=E8=B4=A5=E4=B8=8D?= =?UTF-8?q?=E8=A7=A6=E6=96=AD=E8=B7=AF=E5=99=A8=20rc=20=E4=BB=8D=200,?= =?UTF-8?q?=E4=B8=8D=E5=86=99=E4=B8=8D=E6=A0=87=E6=AC=A1=E5=A4=9C=E8=87=AA?= =?UTF-8?q?=E6=84=88),=E9=A6=96=E5=A4=9C=E8=B5=B7=20gap=5Fcheck=20?= =?UTF-8?q?=E5=8D=B3=E5=85=8D=E8=B4=B9=E6=8C=81=E7=BB=AD=E6=B5=8B=E9=87=8F?= =?UTF-8?q?;=E4=B8=89=E5=A4=84=E5=90=8C=E6=AD=A5=3Dwrapper=20--types+PANEL?= =?UTF-8?q?=5FTYPES+EVENT=5FPANEL=5FTYPES(15=E7=B1=BB),=E9=9B=B6=E6=96=87?= =?UTF-8?q?=E4=BB=B6=3D=E9=9B=B6=E5=91=8A=E8=AD=A6=E9=A6=96=E6=97=A5?= =?UTF-8?q?=E6=88=90=E5=8A=9F=E5=8D=B3=E6=88=90=E5=9F=BA=E7=BA=BF;?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E5=AE=88=E5=8D=AB=3Dparam=20-Force+=E9=9D=9E?= =?UTF-8?q?=2019:00-23:59=20=E6=8B=92=E7=BB=9D=E6=89=A7=E8=A1=8C=E7=95=99?= =?UTF-8?q?=E7=97=95=20guard.log(=E6=B2=BB=E7=9B=98=E5=89=8D=E6=89=8B?= =?UTF-8?q?=E5=8A=A8=E8=AF=AF=E8=B7=91=E4=B8=A4=E7=8A=AF:09-02=2008:08/09-?= =?UTF-8?q?04=2008:00,=E7=9B=98=E5=89=8D=E6=8B=89=E5=BD=93=E6=97=A5=3D?= =?UTF-8?q?=E6=98=A8=E6=97=A5=E6=80=81=E9=94=99=E6=A0=87+marker=20?= =?UTF-8?q?=E9=94=81=E6=AD=BB=E5=BD=93=E6=99=9A=E7=9C=9F=E6=95=B0=E6=8D=AE?= =?UTF-8?q?);TDD=202=20=E6=B5=8B=E7=BF=BB=E6=96=B0(=E7=99=BD=E5=90=8D?= =?UTF-8?q?=E5=8D=95=E6=8B=92=E2=86=92=E6=94=BE=E8=A1=8C+=E6=B3=A8?= =?UTF-8?q?=E5=86=8C=E6=96=AD=E8=A8=80)=E5=85=88=E7=BA=A2=E5=90=8E?= =?UTF-8?q?=E7=BB=BF+806=20=E5=85=A8=E7=BB=BF;VPS=20Parse=20=E9=A2=84?= =?UTF-8?q?=E6=A3=80=E8=BF=87+=E5=8F=8C=E8=84=9A=E6=9C=AC=E5=B7=B2=20scp?= =?UTF-8?q?=20=E7=94=9F=E6=95=88=E4=BD=8D+wall=5Fprobe.py=20=E5=B7=B2?= =?UTF-8?q?=E5=88=A0=20[vps]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sanguo_portfolio/providers/fetchers/base.py | 4 +++- scripts/data_platform/ak_events_wrapper.ps1 | 19 +++++++++++++++++-- scripts/data_platform/static_vintage_check.py | 2 +- .../test_static_vintage_check.py | 5 +++-- tests/portfolio/test_event_panel_fetchers.py | 13 +++++++++---- 5 files changed, 33 insertions(+), 10 deletions(-) diff --git a/sanguo_portfolio/providers/fetchers/base.py b/sanguo_portfolio/providers/fetchers/base.py index ba2e4da..b9aec6b 100644 --- a/sanguo_portfolio/providers/fetchers/base.py +++ b/sanguo_portfolio/providers/fetchers/base.py @@ -212,13 +212,15 @@ class FundamentalsQueryParams(_QueryBase): # 事件/快照 panel 白名单(2026-09-02 A 档使用层出口):与采集注册表 # (akshare_static_download.py)及 static_vintage_check.PANEL_TYPES 口径一致; -# hot_rank 墙测量未拍板挂载,故不在列(拍板后三处同步加)。 +# hot_rank 2026-09-04 拍板挂载(墙测量5轮收官:emappdata 夜间开窗,19:30 档 +# 失败零连带——单 unit 失败不触断路器,次夜自动重试)。 EVENT_PANEL_TYPES = frozenset({ "dragon_tiger", "block_trade", "margin_sse", "restricted", "zt_pool", "zt_pool_zbgc", "zt_pool_dtgc", "fund_flow_industry", "fund_flow_concept", "ths_industry", "ths_concept", "xueqiu_hot", "sina_sector", "gdhs", + "hot_rank", }) diff --git a/scripts/data_platform/ak_events_wrapper.ps1 b/scripts/data_platform/ak_events_wrapper.ps1 index 9c0742b..5fbb256 100644 --- a/scripts/data_platform/ak_events_wrapper.ps1 +++ b/scripts/data_platform/ak_events_wrapper.ps1 @@ -1,11 +1,26 @@ # ak_events_wrapper.ps1 — sanguo-ak-events schtask wrapper (daily 19:30 akshare 事件类 per-date 当日) # dragon_tiger/block_trade/restricted/zt_pool 三件套/新浪资金流×2/同花顺行业概念/雪球热度/新浪行业对账 --start today --end today; # per-date 每类1 unit 快; zt_pool 系 (2026-09-02 双机实测 push2ex 集群稳定) 供情绪/打板因子; -# hot_rank (东财人气榜) 墙待恢复窗测量, 拍板后加入 --types (fetcher/注册已就位); +# hot_rank (东财人气榜) 已于 2026-09-04 挂载进 --types (fetcher/注册早已就位); # 新浪资金流+同花顺榜 (2026-09-02 用户批 A 档: 东财 push2 墙死后免费无墙替代, 秒级零限流) # margin_sse 单独拆出 (2026-09-02 巡查+用户批准): SSE 融资融券 T+1 供应,当晚拉当天→空响应 # akshare Length mismatch 崩(07-22 起 43 天慢性失血,已 --force 回补 0722~0901); # 改拉 T-1~T 两天窗:每晚稳定写昨日;当天失败不写不标,次晚同窗自动重试补上 +# hot_rank 2026-09-04 拍板挂载 (墙测量5轮收官: emappdata 夜间开窗, 22:28 实测 OK; +# 19:30 档失败零连带——单 unit 失败不触断路器, 不写不标, 次夜自动重试) +param([switch]$Force) +# time guard (2026-09-04, user approved): pre-market runs save LAST close state +# under today's filename + marker locks out the real 19:30 pull (hit twice: +# 09-02 08:08, 09-04 08:00). Refuse outside 19:00-23:59 unless -Force. +$guardHour = [int](Get-Date -Format HH) +if (-not $Force -and $guardHour -lt 19) { + $logDir = 'C:\sanguo_vnpy_v2\data\migration_logs' + if (-not (Test-Path $logDir)) { New-Item -ItemType Directory -Path $logDir -Force | Out-Null } + $msg = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') REFUSED: ak-events window is 19:00-23:59, now outside. Use -Force to override." + Add-Content -Path (Join-Path $logDir 'ak_events_guard.log') -Value $msg + Write-Output $msg + exit 1 +} $env:http_proxy = '' $env:https_proxy = '' $env:all_proxy = '' @@ -16,7 +31,7 @@ $ts = Get-Date -Format 'yyyyMMdd_HHmmss' $logDir = 'C:\sanguo_vnpy_v2\data\migration_logs' if (-not (Test-Path $logDir)) { New-Item -ItemType Directory -Path $logDir -Force | Out-Null } $log = Join-Path $logDir "ak_events_$ts.txt" -C:\Python310\python.exe -X utf8 C:\sanguo_vnpy_v2\scripts\data_platform\akshare_static_download.py --types dragon_tiger,block_trade,restricted,zt_pool,zt_pool_zbgc,zt_pool_dtgc,fund_flow_industry,fund_flow_concept,ths_industry,ths_concept,xueqiu_hot,sina_sector --start $today --end $today *>> $log +C:\Python310\python.exe -X utf8 C:\sanguo_vnpy_v2\scripts\data_platform\akshare_static_download.py --types dragon_tiger,block_trade,restricted,zt_pool,zt_pool_zbgc,zt_pool_dtgc,fund_flow_industry,fund_flow_concept,ths_industry,ths_concept,xueqiu_hot,sina_sector,hot_rank --start $today --end $today *>> $log $rc1 = $LASTEXITCODE C:\Python310\python.exe -X utf8 C:\sanguo_vnpy_v2\scripts\data_platform\akshare_static_download.py --types margin_sse --start $yesterday --end $today *>> $log $rc2 = $LASTEXITCODE diff --git a/scripts/data_platform/static_vintage_check.py b/scripts/data_platform/static_vintage_check.py index ff6dc51..4db75da 100644 --- a/scripts/data_platform/static_vintage_check.py +++ b/scripts/data_platform/static_vintage_check.py @@ -39,7 +39,7 @@ PANEL_TYPES = ( "fund_flow_industry", "fund_flow_concept", # 快照族 (永久) "ths_industry", "ths_concept", "xueqiu_hot", "sina_sector", # 快照族 (09-02 全量落地) - # hot_rank 待恢复窗测量拍板挂载后再入列 (避免未上线就缺日告警) + "hot_rank", # 快照族 (09-04 拍板挂载;零文件=零告警,首日成功即成告警基线) ) BACKFILLABLE_PANEL_TYPES = frozenset({"zt_pool", "zt_pool_zbgc", "zt_pool_dtgc"}) PANEL_BACKFILL_CALENDAR_DAYS = 42 # ≈30 交易日回补窗的日历日近似 diff --git a/tests/data_platform/test_static_vintage_check.py b/tests/data_platform/test_static_vintage_check.py index 2764cce..37c9b20 100644 --- a/tests/data_platform/test_static_vintage_check.py +++ b/tests/data_platform/test_static_vintage_check.py @@ -201,11 +201,12 @@ class TestPanelGapCheck: status_panel["fund_flow_industry"]["holes_permanent"]) def test_panel_registry_snapshot_family(self): - """09-02 全量落地批次注册正确: 快照族在列且不落可回补档; hot_rank 未挂载不入列。""" + """快照族在列且不落可回补档; hot_rank 09-04 拍板挂载入列(零文件=零告警)。""" 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 # 拍板挂载后才入列 + assert "hot_rank" in svc.PANEL_TYPES # 09-04 拍板挂载 + assert "hot_rank" not in svc.BACKFILLABLE_PANEL_TYPES # ======================== main / JSON 契约 ======================== diff --git a/tests/portfolio/test_event_panel_fetchers.py b/tests/portfolio/test_event_panel_fetchers.py index a38c69d..790559d 100644 --- a/tests/portfolio/test_event_panel_fetchers.py +++ b/tests/portfolio/test_event_panel_fetchers.py @@ -29,6 +29,7 @@ from sanguo_portfolio.providers.fetchers import ( EventPanelFetcher, LimitPoolFetcher, ) +from sanguo_portfolio.providers.fetchers.base import EVENT_PANEL_TYPES from sanguo_portfolio.providers.local_unified_provider import LocalUnifiedProvider # 真实列名(VPS 20260819_zt_pool_dtgc.parquet 实测) @@ -123,10 +124,14 @@ class TestEventPanelQueryParams: with pytest.raises(ValidationError, match="no_such_type"): provider.get_event_panel("no_such_type", date="2024-06-18") - def test_hot_rank_not_mounted(self, provider): - """hot_rank 墙测量未拍板挂载——白名单拒绝(与 PANEL_TYPES 口径一致)。""" - with pytest.raises(ValidationError, match="hot_rank"): - provider.get_event_panel("hot_rank", date="2024-06-18") + def test_hot_rank_mounted(self, provider): + """hot_rank 2026-09-04 拍板挂载(墙测量5轮收官,19:30档)——白名单放行。 + + fixture 无 hot_rank 目录=合法缺失,返回空 df(与缺目录语义一致)。 + """ + assert "hot_rank" in EVENT_PANEL_TYPES + df = provider.get_event_panel("hot_rank", date="2024-06-18") + assert df.empty def test_date_and_start_exclusive(self, provider): with pytest.raises(ValidationError, match="互斥"):