""" Python 数据模型 —— 镜像 data/db/entities/ 中的 TypeORM 实体。 命名约定: - 类名与 TypeORM 实体类名一致(Kline, Exchange, TradingPair) - 字段名使用 Python snake_case,对应 TypeORM 的 camelCase / snake_case - 所有模型使用 Pydantic,提供运行时校验 + IDE 智能提示 """ from datetime import datetime from typing import Optional from pydantic import BaseModel, Field class KlineRecord(BaseModel): """ K 线数据记录。 映射关系: TypeORM Kline.time → KlineRecord.time TypeORM Kline.exchange → KlineRecord.exchange TypeORM Kline.symbol → KlineRecord.symbol TypeORM Kline.interval → KlineRecord.interval TypeORM Kline.open → KlineRecord.open TypeORM Kline.high → KlineRecord.high TypeORM Kline.low → KlineRecord.low TypeORM Kline.close → KlineRecord.close TypeORM Kline.volume → KlineRecord.volume TypeORM Kline.quote_volume → KlineRecord.quote_volume TypeORM Kline.taker_buy_base_vol → KlineRecord.taker_buy_base_vol TypeORM Kline.taker_buy_quote_vol→ KlineRecord.taker_buy_quote_vol TypeORM Kline.trade_count → KlineRecord.trade_count TypeORM Kline.is_closed → KlineRecord.is_closed """ time: datetime exchange: str symbol: str interval: str open: float high: float low: float close: float volume: float quote_volume: Optional[float] = None taker_buy_base_vol: Optional[float] = None taker_buy_quote_vol: Optional[float] = None trade_count: Optional[int] = None is_closed: bool = True created_at: Optional[datetime] = None updated_at: Optional[datetime] = None @classmethod def from_record(cls, record) -> "KlineRecord": return cls( time=record["time"], exchange=record["exchange"], symbol=record["symbol"], interval=record["interval"], open=float(record["open"]), high=float(record["high"]), low=float(record["low"]), close=float(record["close"]), volume=float(record["volume"]), quote_volume=float(record["quote_volume"]) if record["quote_volume"] is not None else None, taker_buy_base_vol=float(record["taker_buy_base_vol"]) if record["taker_buy_base_vol"] is not None else None, taker_buy_quote_vol=float(record["taker_buy_quote_vol"]) if record["taker_buy_quote_vol"] is not None else None, trade_count=int(record["trade_count"]) if record["trade_count"] is not None else None, is_closed=bool(record["is_closed"]), created_at=record["created_at"] if "created_at" in record else None, updated_at=record["updated_at"] if "updated_at" in record else None, ) class TradingPairInfo(BaseModel): """交易对配置信息(轻量版,仅包含策略决策所需的字段)""" symbol: str exchange: str base_asset: str quote_asset: str price_precision: int quantity_precision: int min_qty: Optional[float] = None min_notional: Optional[float] = None active: bool = True class Signal(BaseModel): """交易信号""" symbol: str signal_type: str = Field(..., pattern=r"^(BUY|SELL|HOLD)$") price: Optional[float] = None quantity: Optional[float] = None reason: str = "" timestamp: datetime = Field(default_factory=datetime.utcnow)