Files
trade/engine/data/models.py
T
Rekey 1c9339a4db feat(engine): 新增 Python 策略引擎模块
- 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:引擎架构文档
2026-06-08 18:19:50 +08:00

101 lines
3.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
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)