rebuild(stage-8): deliver market insight workspaces

This commit is contained in:
leefer
2026-07-30 04:12:04 +08:00
parent a18e8e9d27
commit 976a5cac03
39 changed files with 3671 additions and 14 deletions
@@ -0,0 +1,128 @@
from __future__ import annotations
from datetime import date, datetime, time
from typing import Any
from zoneinfo import ZoneInfo
from backend.data.contracts import ProviderResult
SHANGHAI = ZoneInfo("Asia/Shanghai")
def auction_phase(requested: str, trade_date: str, clock: datetime) -> str:
if requested != clock.date().isoformat() or trade_date != clock.date().isoformat():
return "archive"
local = clock.time().replace(tzinfo=None)
if local < time(9, 15):
return "pending"
if local < time(9, 25):
return "observing"
if local < time(9, 30):
return "selection"
return "finalized"
def decorate(
payload: dict[str, Any],
requested: str,
phase: str,
carried_forward: bool,
message: str,
current_available: bool = True,
) -> dict[str, Any]:
return {
**payload,
"requested_date": requested,
"phase": phase,
"carried_forward": carried_forward,
"message": message or str(payload.get("message") or ""),
"current_available": current_available,
}
def standard(payload: dict[str, Any], requested: str, message: str = "") -> dict[str, Any]:
trade_date = str(payload.get("trade_date") or "")
return {
**payload,
"requested_date": requested,
"carried_forward": trade_date != requested,
"message": message or str(payload.get("message") or ""),
}
def empty_standard(requested: str, trade_date: str, message: str) -> dict[str, Any]:
return {
"requested_date": requested,
"trade_date": trade_date,
"observed_at": None,
"state": None,
"carried_forward": trade_date != requested,
"message": message,
"summary": {},
"items": [],
"combined": [],
"ths": [],
"dc": [],
}
def empty_auction(
requested: str, trade_date: str, phase: str, message: str
) -> dict[str, Any]:
return {
**empty_standard(requested, trade_date, message),
"phase": phase,
"current_available": False,
"expectations": {"超预期": 0, "符合预期": 0, "低于预期": 0},
"themes": {"carry": [], "new_themes": []},
"amount_history": [],
"focus_rows": [],
"one_price_rows": [],
"rows": [],
"watchlist_rows": [],
"watchlist_ready": False,
}
def result(value: Any) -> ProviderResult | None:
return value if isinstance(value, ProviderResult) else None
def rows(value: Any) -> tuple[dict[str, Any], ...]:
provider_result = result(value)
return provider_result.rows if provider_result else ()
def serialized_rows(value: Any) -> list[dict[str, Any]] | None:
provider_result = result(value)
return [dict(row) for row in provider_result.rows] if provider_result else None
def tuple_or_none(value: Any) -> tuple[dict[str, Any], ...] | None:
if value is None:
return None
return tuple(dict(row) for row in value)
def valid_date(value: str) -> str:
try:
return date.fromisoformat(value).isoformat()
except ValueError as exc:
raise ValueError("日期格式无效") from exc
def clock(value: datetime | None) -> datetime:
current = value or datetime.now(SHANGHAI)
return (
current.replace(tzinfo=SHANGHAI)
if current.tzinfo is None
else current.astimezone(SHANGHAI)
)
def number(value: Any, default: float = 0.0) -> float:
try:
parsed = float(value)
return parsed if parsed == parsed else default
except (TypeError, ValueError):
return default