1c9339a4db
- config/settings.py:Pydantic 解析 env.yaml - data/db.py:asyncpg 连接池管理 - data/reader.py:KlineReader 只读查询 TimescaleDB - data/models.py:KlineRecord 等 Pydantic 模型,镜像 TypeORM 实体 - example/test_db.py:数据库查询验证示例 - README.md:引擎架构文档
101 lines
3.6 KiB
Python
101 lines
3.6 KiB
Python
"""
|
||
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)
|