mirror of
https://ghfast.top/https://github.com/aeroxw/tick-stock-panel.git
synced 2026-09-12 15:34:16 +08:00
fix(capabilities): redetect 同步刷新 app.state 快照并升级 tickflow SDK 0.1.25
- /api/capabilities/redetect 重新探测后同步写回 app.state.capabilities, minute_refresh 等门控服务即时读到新档位, 不再需要重启 - tickflow 0.1.24 缺 intraday_universe 方法导致 Expert 档全量分钟探测失败, 升级到 0.1.25 - 全量分钟轮次异常与模式补充 warning 日志, 便于线上诊断
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
"""API 路由 — Phase 0 仅 /health 与 /api/capabilities。"""
|
||||
from __future__ import annotations
|
||||
|
||||
from fastapi import APIRouter
|
||||
from fastapi import APIRouter, Request
|
||||
|
||||
from app import __version__
|
||||
from app.tickflow import client as tf_client
|
||||
@@ -31,9 +31,14 @@ def capabilities() -> dict:
|
||||
|
||||
|
||||
@router.post("/api/capabilities/redetect")
|
||||
def redetect() -> dict:
|
||||
def redetect(request: Request) -> dict:
|
||||
"""用户在设置页"重新检测"按钮。"""
|
||||
capset = detect_capabilities(force=True)
|
||||
# 同步刷新 app.state 快照 (minute_refresh 等服务的门控读这里) 与财务调度器,
|
||||
# 与 settings.py 各探测路径一致 — 否则重检测后服务侧仍读旧 capset 被错误门控
|
||||
request.app.state.capabilities = capset
|
||||
from app.api.settings import _sync_financial_scheduler_caps
|
||||
_sync_financial_scheduler_caps(request.app.state, capset)
|
||||
return {
|
||||
"label": tier_label(),
|
||||
"capabilities": capset.to_dict(),
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import contextlib
|
||||
import logging
|
||||
import threading
|
||||
import time
|
||||
from dataclasses import dataclass, field
|
||||
@@ -44,6 +45,8 @@ import polars as pl
|
||||
from app.market_time import cn_now, cn_today, in_continuous_session
|
||||
from app.services import preferences
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# 轮询间隔允许范围 (秒): 稳态轮单请求无并发脉冲, 下限 3s;
|
||||
# 上限 120s — universe 端点每标的只回最新 3 根, 间隔超过 3 分钟必留缺口,
|
||||
# 每轮都会触发修复轮, 稳态设计失效, 故不允许配到 120s 以上。
|
||||
@@ -187,6 +190,7 @@ class MinuteRefreshService:
|
||||
continue
|
||||
except Exception as e:
|
||||
self._state.last_error = f"round failed: {e}"
|
||||
logger.warning("全量分钟轮次异常: %s", e)
|
||||
self._stop.wait(_LOOP_STEP_S)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
@@ -231,8 +235,11 @@ class MinuteRefreshService:
|
||||
|
||||
t0 = time.perf_counter()
|
||||
mode = self._select_mode()
|
||||
mode_label = "增量" if mode == "increment" else "全天修复"
|
||||
with self._round_lock:
|
||||
if mode == "increment":
|
||||
# fetch 计时只覆盖网络取数; full 分支的 universe 维表读取不计入
|
||||
fetch_started = time.perf_counter()
|
||||
df, requests = kline_sync.fetch_intraday_universe_increment()
|
||||
self._state.last_symbols = (
|
||||
df["symbol"].n_unique() if not df.is_empty() else 0
|
||||
@@ -242,18 +249,27 @@ class MinuteRefreshService:
|
||||
self._state.last_symbols = len(symbols)
|
||||
if not symbols:
|
||||
self._state.last_error = "empty universe (instruments 未加载)"
|
||||
logger.warning("全量分钟[%s] 本轮中止: 标的池为空 (instruments 未加载)", mode_label)
|
||||
return
|
||||
capset = getattr(self._app_state, "capabilities", None) if self._app_state else None
|
||||
fetch_started = time.perf_counter()
|
||||
df, requests = kline_sync.fetch_intraday_full_market_burst(symbols, capset)
|
||||
fetch_ms = (time.perf_counter() - fetch_started) * 1000
|
||||
self._state.last_requests = requests
|
||||
if df.is_empty():
|
||||
self._empty_rounds += 1
|
||||
self._state.last_error = f"intraday {mode} returned no data"
|
||||
logger.warning(
|
||||
"全量分钟[%s] 本轮返回空数据: %d 请求, 取数 %.0fms",
|
||||
mode_label, requests, fetch_ms,
|
||||
)
|
||||
return
|
||||
self._empty_rounds = 0
|
||||
write_started = time.perf_counter()
|
||||
written = kline_sync._write_minute_partition(
|
||||
df, self._repo.store.data_dir / "kline_minute",
|
||||
)
|
||||
write_ms = (time.perf_counter() - write_started) * 1000
|
||||
|
||||
self._state.rounds += 1
|
||||
self._state.last_round_at = time.time()
|
||||
@@ -261,6 +277,12 @@ class MinuteRefreshService:
|
||||
self._state.last_rows = written
|
||||
self._state.last_mode = mode
|
||||
self._state.last_error = None
|
||||
logger.info(
|
||||
"全量分钟[%s] 第 %d 轮: 取数 %.0fms (%d 请求, %d 标的), "
|
||||
"落盘 %.0fms (%d 行), 总计 %.0fms",
|
||||
mode_label, self._state.rounds, fetch_ms, self._state.last_requests,
|
||||
self._state.last_symbols, write_ms, written, self._state.last_round_ms,
|
||||
)
|
||||
|
||||
def _universe(self) -> list[str]:
|
||||
"""全市场 A 股标的 (instruments 维表, 与盘后分钟同步同一来源)。"""
|
||||
|
||||
Generated
+1840
-1840
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user