129 lines
3.5 KiB
Python
129 lines
3.5 KiB
Python
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
|