feat(data): 全量分钟 (full_minute) 数据集开放插件/自定义源接入

像其他能力一样可路由: 声明 full_minute 数据集并在设置页路由, 即接入
盘中全市场分钟落盘服务 (与 TickFlow Expert 同一能力键 intraday.universe)。

- policy: _DATASET_CAP_MAP 增补 full_minute → INTRADAY_UNIVERSE, 自定义源
  声明且被路由时自动补授能力, 服务门控对两类源统一口径
- capabilities 注册表: full_minute 从不可路由 (field=None) 改为
  full_minute_data_provider 偏好路由; preferences/settings API 收发新字段
- 插件契约: get_intraday_batch (修复轮, 未实现回退 get_minute 当日窗口) +
  get_intraday_latest (稳态增量, 可选; 未实现降级仅修复轮, 节奏下限 60s)
- YAML 声明式源: full_minute 数据集与 minute 同形, loader 白名单放行,
  源编辑器 UI 可配置; 仅修复轮语义
- kline_sync 边界: fetch_intraday_custom_batch / _fetch_intraday_custom_latest,
  帧过北京墙钟守卫, 与 TickFlow 边界函数同纪律
- minute_refresh: 删「自定义分钟源让位」逻辑, 改按路由取数;
  status 增 provider / provider_effective / repair_only
- 前端: 监控页卡片按能力驱动, 数据源页路由矩阵, api.ts 类型
- 测试: 门控/路由/降级/增广新增用例, 后端 1332 全过
- 文档: plugin-development / custom-data-source / configuration / features /
  CONTRIBUTING 契约细化 (AI 可照文档接入)
This commit is contained in:
shy3130
2026-08-31 22:53:24 +08:00
parent dab8a2c676
commit 010c484289
21 changed files with 503 additions and 95 deletions
+24 -2
View File
@@ -1,6 +1,6 @@
"""能力标准统一: 自定义/插件数据源能力增广回归测试。
对应 _augment_custom_sources 的数据集→能力映射 (daily/adj_factor/minute/financial):
对应 _augment_custom_sources 的数据集→能力映射 (daily/adj_factor/minute/financial/full_minute):
某数据集的当前 provider 非 tickflow 且声明了该数据集 → grant 对应能力;
取数路由仍按 preferences 分流, 不会误调 TickFlow。
"""
@@ -13,13 +13,14 @@ from app.tickflow.policy import _augment_custom_sources
def _set_providers(monkeypatch, *, daily="tickflow", adj="tickflow",
minute="tickflow", financial="tickflow") -> None:
minute="tickflow", financial="tickflow", full_minute="tickflow") -> None:
"""mock preferences 各数据集 provider getter。"""
from app.services import preferences
monkeypatch.setattr(preferences, "get_daily_data_provider", lambda: daily)
monkeypatch.setattr(preferences, "get_adj_factor_provider", lambda: adj)
monkeypatch.setattr(preferences, "get_minute_data_provider", lambda: minute)
monkeypatch.setattr(preferences, "get_financial_provider", lambda: financial)
monkeypatch.setattr(preferences, "get_full_minute_data_provider", lambda: full_minute)
def _set_datasets(monkeypatch, datasets: set[str]) -> None:
@@ -51,6 +52,27 @@ def test_adj_custom_source_grants_adj_factor(monkeypatch):
assert capset.has(Cap.ADJ_FACTOR)
def test_full_minute_custom_source_grants_intraday_universe(monkeypatch):
"""全量分钟: 声明 full_minute 数据集且被路由 → 补授 INTRADAY_UNIVERSE,
minute_refresh 服务门控与 TickFlow Expert 口径统一。"""
_set_providers(monkeypatch, full_minute="mock_src")
_set_datasets(monkeypatch, {"full_minute"})
capset = CapabilitySet()
_augment_custom_sources(capset)
assert capset.has(Cap.INTRADAY_UNIVERSE)
# 声明了 full_minute 不等于声明 minute → 不补逐标的分钟K能力
assert not capset.has(Cap.KLINE_MINUTE_BATCH)
def test_full_minute_dataset_without_routing_not_granted(monkeypatch):
"""源声明了 full_minute 但路由仍是 tickflow → 不增广 (TickFlow 档位自决)。"""
_set_providers(monkeypatch, full_minute="tickflow")
_set_datasets(monkeypatch, {"full_minute"})
capset = CapabilitySet()
_augment_custom_sources(capset)
assert not capset.has(Cap.INTRADAY_UNIVERSE)
def test_minute_custom_source_grants_minute_batch(monkeypatch):
"""原有 minute 增广行为保持。"""
_set_providers(monkeypatch, minute="mock_src")