from __future__ import annotations from dataclasses import dataclass from datetime import datetime from enum import StrEnum from typing import Any class DataSource(StrEnum): TUSHARE = "tushare" IFIND = "ifind" EASTMONEY = "eastmoney" TENCENT = "tencent" LOCAL = "local" class DataUsage(StrEnum): DISPLAY = "display" CALCULATION = "calculation" class SnapshotState(StrEnum): REALTIME = "realtime" FINAL = "final" ARCHIVE = "archive" @dataclass(frozen=True, slots=True) class ObservationMetadata: source: DataSource observed_at: datetime unit: str adjustment: str freshness_seconds: int coverage: float state: SnapshotState usage: DataUsage def __post_init__(self) -> None: if not 0 <= self.coverage <= 1: raise ValueError("coverage must be between zero and one") if self.freshness_seconds < 0: raise ValueError("freshness_seconds cannot be negative") @dataclass(frozen=True, slots=True) class ProviderResult: rows: tuple[dict[str, Any], ...] metadata: ObservationMetadata @dataclass(frozen=True, slots=True) class TradeContext: requested_date: str actual_date: str | None previous_date: str | None observed_at: datetime | None state: SnapshotState | None carried_forward: bool message: str @dataclass(frozen=True, slots=True) class MarketEntity: entity_type: str identifier: str code: str name: str sector: str | None = None @dataclass(frozen=True, slots=True) class ChartPoint: time: str open: float high: float low: float close: float volume: float amount: float average: float | None = None @dataclass(frozen=True, slots=True) class ChartSeries: entity: MarketEntity interval: str trade_date: str previous_close: float | None points: tuple[ChartPoint, ...] metadata: ObservationMetadata