from __future__ import annotations from datetime import datetime, time as dt_time def dashboard_has_usable_data(dashboard: dict[str, object]) -> bool: if not isinstance(dashboard, dict) or dashboard.get("status") == "failed": return False meta = dashboard.get("meta") or {} overview = dashboard.get("overview") or {} if isinstance(meta, dict) and (meta.get("trade_date") or meta.get("carried_forward")): return True return bool(isinstance(overview, dict) and overview) def verified_dashboard_result(dashboard: dict[str, object]) -> dict[str, object]: """Manual refresh and automatic catch-up share this rule. Derived limit lists or a previous usable snapshot are not whole-job failures. Only a payload with no displayable market data is recorded as failed. """ if dashboard_has_usable_data(dashboard): return dashboard meta = dashboard.get("meta") if isinstance(dashboard, dict) else None notice = "" if isinstance(meta, dict): notice = str(meta.get("notice") or meta.get("display_notice") or "") return { "status": "failed", "error": notice or "未获取到可用行情", } def official_catchup_due(today: str, snapshot: dict[str, object]) -> bool: now = datetime.now().astimezone().time().replace(tzinfo=None) if not (dt_time(15, 5) <= now < dt_time(22, 0)): return False meta = snapshot.get("meta") if isinstance(snapshot.get("meta"), dict) else {} actual = str(meta.get("trade_date") or "").replace("-", "") if ( actual == today and meta.get("limit_data_source") != "derived" and not meta.get("carried_forward") ): return False return True