feat(pipeline): 盘后管道默认 15:35, 与盘后固定价终点 (15:30) 拉开缓冲

实测 2026-09-04 数据: 836 只可比标的中 24 只日K量大于分钟K合计 (最大
5.4%), 差额产生于 15:00 收盘后的盘后时段 (盘后固定价/大宗类成交计入
日成交量但不产生分钟K) —— 盘中实时落盘的日K注定缺失该尾巴, 只有
15:30 之后的官方日线才完整。

- 默认调度 15:30 → 15:35: 避开供应商日线未定稿窗口, 也与 quote 定版
  重试窗口终点 (15:30) 不再精确重合
- set_pipeline_schedule 保存下限从 15:00 收紧到 15:35 (存量已存配置
  不迁移, 读取不钳制, 仅下次显式保存时生效)
- 定时复盘默认 15:10 (即时复盘走实时快照缓存, 不依赖管道) 刻意不动

验证: set 钳制冒烟 (15:30→15:35, 15:35/15:36 原样); ruff 与 HEAD 基线
一致无新增; pytest prefer/pipeline/daily 相关 128 通过。
This commit is contained in:
shy3130
2026-09-06 22:35:24 +08:00
parent cd9e20610f
commit ecbb45b8ef
3 changed files with 17 additions and 10 deletions
+3 -2
View File
@@ -2,7 +2,8 @@
调度: 调度:
09:10 盘前 — 同步个股维表 instruments (全量覆盖) 09:10 盘前 — 同步个股维表 instruments (全量覆盖)
15:30 盘后 — 日K同步 + 增量除权因子 + enriched 计算 + 刷新视图 15:35 盘后 — 日K同步 + 增量除权因子 + enriched 计算 + 刷新视图
(默认 15:35: 盘后固定价 15:30 终止 + 供应商日线定稿缓冲, 见 preferences)
盘后同步策略: 盘后同步策略:
日 K: QuoteService 交易时段已实时落盘 → 有数据时跳过 batch,首次拉 1 年区间 日 K: QuoteService 交易时段已实时落盘 → 有数据时跳过 batch,首次拉 1 年区间
@@ -1123,7 +1124,7 @@ def start_scheduler(repo: KlineRepository, capset: CapabilitySet) -> AsyncIOSche
"""启动调度器。 """启动调度器。
工作日 09:10 — 同步个股维表 工作日 09:10 — 同步个股维表
工作日 HH:MM — 盘后管道(时间由用户偏好决定,默认 15:30 工作日 HH:MM — 盘后管道(时间由用户偏好决定,默认 15:35
""" """
from app.services import preferences from app.services import preferences
sched = preferences.get_pipeline_schedule() sched = preferences.get_pipeline_schedule()
+12 -6
View File
@@ -487,17 +487,23 @@ def set_pipeline_index_symbols(symbols: str) -> str:
def get_pipeline_schedule() -> dict: def get_pipeline_schedule() -> dict:
"""返回盘后管道调度时间 {"hour": 15, "minute": 30}。""" """返回盘后管道调度时间 {"hour": 15, "minute": 35}。
d = load().get("pipeline_schedule", {"hour": 15, "minute": 30})
return {"hour": d.get("hour", 15), "minute": d.get("minute", 30)} 默认 15:35 而非 15:30 整: 盘后固定价交易 15:30 才彻底结束, 且供应商
聚合含盘后量的官方日K需要时间 —— 整点即拉可能写入不含盘后成交的
日线, 也与 quote 定版重试窗口终点 (15:30) 精确重合。留 5 分钟缓冲。
"""
d = load().get("pipeline_schedule", {"hour": 15, "minute": 35})
return {"hour": d.get("hour", 15), "minute": d.get("minute", 35)}
def set_pipeline_schedule(hour: int, minute: int) -> dict: def set_pipeline_schedule(hour: int, minute: int) -> dict:
h = max(0, min(23, hour)) h = max(0, min(23, hour))
m = max(0, min(59, minute)) m = max(0, min(59, minute))
# 盘后不早于 15:00 # 盘后管道不早于 15:35: 15:30 盘后固定价才终止 (量/额此前仍会变),
if h * 60 + m < 15 * 60: # 且供应商官方日线定稿需要缓冲 —— 更早启动可能固化不含盘后量的当日分区
h, m = 15, 0 if h * 60 + m < 15 * 60 + 35:
h, m = 15, 35
save({"pipeline_schedule": {"hour": h, "minute": m}}) save({"pipeline_schedule": {"hour": h, "minute": m}})
return {"hour": h, "minute": m} return {"hour": h, "minute": m}
+2 -2
View File
@@ -34,7 +34,7 @@
- **原子信号**:MA / MACD 金叉死叉 · N 日新高新低 · 布林突破 - **原子信号**:MA / MACD 金叉死叉 · N 日新高新低 · 布林突破
- **复权**:基于除权因子自动前复权,回测与指标口径一致 - **复权**:基于除权因子自动前复权,回测与指标口径一致
盘后管道(15:30 CST 自动触发)会重新拉日 K + 重算 enriched 表。 盘后管道(默认 15:35 CST 自动触发; 盘后固定价 15:30 终止 + 供应商日线定稿缓冲)会重新拉日 K + 重算 enriched 表。
--- ---
@@ -202,7 +202,7 @@ timeseries 模式的表 (如人气排行) 默认只能从开启拉取之日起
### 盘后定时管道 ### 盘后定时管道
APScheduler 15:30 CST 自动:拉日 K → 重算 enriched 表 → 跑监控规则。 APScheduler 默认 15:35 CST 自动:拉日 K → 重算 enriched 表 → 跑监控规则。
### 令牌桶限流 ### 令牌桶限流