mirror of
https://github.com/ZhuLinsen/daily_stock_analysis.git
synced 2026-10-07 16:38:18 +08:00
fix: align US realtime request priority
This commit is contained in:
@@ -36,7 +36,7 @@ Web/API 提供 `GET /api/v1/data/overview` 和等价别名 `GET /api/v1/data/cap
|
||||
首版契约只读取配置和 `DataFetcherManager` 的 fetcher 快照,不触发外部行情请求,也不返回任何原始密钥。响应分三层:
|
||||
|
||||
- `providers`:每个 provider 的 `enabled`、`configured`、`status`、`markets`、`datasets`、精确的 `dataset_markets` 和非敏感 warning。`markets` 与 `datasets` 只是聚合索引,不能推断为笛卡尔积;例如 YFinance 可参与 A 股行情,但 A 股基本面固定走 AkShare,因此 `dataset_markets.financial.snapshot` 不包含 `cn`。
|
||||
- `datasets`:`quote.realtime`、`kline.daily`、`index.daily`、`market.overview`、`financial.snapshot`、`news.events`、`strategy.screening`、`alert.monitor`、`portfolio.account` 的 `status/source/fallback_from/stale/warnings`;其中 `quote.realtime`、`kline.daily`、`market.overview` 和 `financial.snapshot` 都聚合市场级 coverage,避免把单市场健康度误报成全局可用。A 股实时额外拆分 `cn` 股票优先级、`cn.index.exchange` 固定指数链和 `cn.index.csi` Efinance-only 链;AkShare Tencent/Sina/EM 子源级熔断及日线 breaker 会先于 provider-wide unknown 判定,使 overview 与运行时跳过行为一致。YFinance 的港股实时声明对应其实际 `.HK` 执行路径;未配置 OpenD 时 HK priority 与运行时一致地跳过 Futu;PyTDX 不声明未接入统一 route 的实时能力,YFinance 指数日线只声明实际可执行的 CN/US route。任何按运行时顺序尝试的数据集在首个优先源尚未探测且没有更具体的 open breaker 证据时保持 `unknown`,不会越过它宣称后续源已被选中。`alert.monitor` 只有在开关启用且当前 API 进程的 scheduler 已实际注册并运行 `agent_event_monitor` 时才为 `ok`;开关关闭返回 `agent_event_monitor_disabled`,scheduler 未运行则返回 `agent_event_monitor_not_running`。
|
||||
- `datasets`:`quote.realtime`、`kline.daily`、`index.daily`、`market.overview`、`financial.snapshot`、`news.events`、`strategy.screening`、`alert.monitor`、`portfolio.account` 的 `status/source/fallback_from/stale/warnings`;其中 `quote.realtime`、`kline.daily`、`market.overview` 和 `financial.snapshot` 都聚合市场级 coverage,避免把单市场健康度误报成全局可用。A 股实时额外拆分 `cn` 股票优先级、`cn.index.exchange` 固定指数链和 `cn.index.csi` Efinance-only 链;AkShare Tencent/Sina/EM 子源级熔断及日线 breaker 会先于 provider-wide unknown 判定,使 overview 与运行时跳过行为一致。YFinance 的港股实时声明对应其实际 `.HK` 执行路径;未配置 OpenD 时 HK priority 与运行时一致地跳过 Futu;美股实时也按当前请求可用性跳过处于连接冷却的 Longbridge,使 YFinance 成为实际主源而不是伪 fallback。PyTDX 不声明未接入统一 route 的实时能力,YFinance 指数日线只声明实际可执行的 CN/US route。任何按运行时顺序尝试的数据集在首个优先源尚未探测且没有更具体的 open breaker 证据时保持 `unknown`,不会越过它宣称后续源已被选中。`alert.monitor` 只有在开关启用且当前 API 进程的 scheduler 已实际注册并运行 `agent_event_monitor` 时才为 `ok`;开关关闭返回 `agent_event_monitor_disabled`,scheduler 未运行则返回 `agent_event_monitor_not_running`。
|
||||
- `priorities`:`cn.realtime`、`hk.realtime`、`us.realtime`、`daily.generic`、`cn.index.daily`、`market.overview`、`screening.snapshot`、`news.events` 的当前 source order。
|
||||
- `index.daily` 额外区分 `cn.exchange`、`cn.csi` 与 `us` coverage:沪深交易所指数使用 Tencent、AkShare、TickFlow、YFinance 固定多源链,CSI 指数按运行时契约只认 AkShare,美股指数使用 YFinance → Finnhub;Tushare 不声明指数日线能力。CN/HK realtime 都只接受对应运行时实际有 handler 的 source token,Tushare realtime 能力仅声明 A 股。
|
||||
|
||||
|
||||
@@ -427,7 +427,7 @@ class DataCapabilityService:
|
||||
),
|
||||
self._priority_view(
|
||||
"us.realtime",
|
||||
self._us_realtime_priority(),
|
||||
self._us_realtime_priority(fetchers),
|
||||
"DataFetcherManager US realtime route",
|
||||
known_sources={"longbridge", "yfinance"},
|
||||
),
|
||||
@@ -484,9 +484,12 @@ class DataCapabilityService:
|
||||
logger.debug("Failed to resolve screening snapshot priority: %s", exc)
|
||||
return _split_priority("sina,efinance,akshare_em,em_datacenter")
|
||||
|
||||
def _us_realtime_priority(self) -> List[str]:
|
||||
if self._is_provider_configured(
|
||||
next(item for item in _PROVIDER_DEFINITIONS if item.name == "longbridge")
|
||||
def _us_realtime_priority(self, fetchers: Sequence[Any]) -> List[str]:
|
||||
fetcher_map = {str(getattr(fetcher, "name", "")): fetcher for fetcher in fetchers}
|
||||
longbridge = fetcher_map.get("LongbridgeFetcher")
|
||||
if longbridge is not None and self._fetcher_available_for_capability(
|
||||
longbridge,
|
||||
capability="realtime_quote",
|
||||
):
|
||||
return ["longbridge", "yfinance"]
|
||||
return ["yfinance", "longbridge"]
|
||||
|
||||
@@ -160,6 +160,26 @@ def test_provider_runtime_probe_honors_request_time_unavailable_over_cached_avai
|
||||
assert _provider(overview, "longbridge")["warnings"] == ["provider_marked_unavailable"]
|
||||
|
||||
|
||||
def test_us_realtime_priority_skips_longbridge_during_request_cooldown() -> None:
|
||||
manager = _FetcherManager([
|
||||
_Fetcher("LongbridgeFetcher", 1, available=True, is_available_for_request=False),
|
||||
_Fetcher("YfinanceFetcher", 4, available=True),
|
||||
])
|
||||
service = DataCapabilityService(
|
||||
config=_config(longbridge_app_key="key"),
|
||||
fetcher_manager=manager,
|
||||
)
|
||||
|
||||
overview = service.get_overview()
|
||||
priorities = {item["scenario"]: item for item in overview["priorities"]}
|
||||
us_quality = _dataset(overview, "quote.realtime")["coverage"]["markets"]["us"]
|
||||
|
||||
assert priorities["us.realtime"]["providers"] == ["yfinance", "longbridge"]
|
||||
assert us_quality["status"] == "ok"
|
||||
assert us_quality["source"] == "yfinance"
|
||||
assert us_quality["fallback_from"] == []
|
||||
|
||||
|
||||
def test_provider_dataset_market_matrix_matches_fundamental_runtime_routes() -> None:
|
||||
service = DataCapabilityService(
|
||||
config=_config(
|
||||
|
||||
Reference in New Issue
Block a user