""" Pydantic 数据模型定义 用于 API 请求和响应的数据验证 """ from datetime import datetime from typing import Optional, List, Any from enum import Enum from pydantic import BaseModel, Field, field_validator # ============================================ # 认证模型 # ============================================ class LoginRequest(BaseModel): """登录请求""" username: str = Field(..., min_length=1, max_length=50) password: str = Field(..., min_length=1, max_length=100) @field_validator('username', 'password') def no_whitespace(cls, v): if v.strip() != v: raise ValueError('不能包含前后空格') return v class TokenResponse(BaseModel): """Token 响应""" access_token: str token_type: str = "bearer" expires_in: int # 秒数 refresh_token: Optional[str] = None class TokenVerifyRequest(BaseModel): """Token 验证请求""" token: str class TokenVerifyResponse(BaseModel): """Token 验证响应""" valid: bool user_info: Optional[dict] = None # ============================================ # 用户模型 # ============================================ class User(BaseModel): """用户信息""" username: str email: Optional[str] = None full_name: Optional[str] = None is_active: bool = True created_at: datetime class UserCreate(BaseModel): """创建用户""" username: str = Field(..., min_length=3, max_length=50) password: str = Field(..., min_length=6, max_length=100) email: Optional[str] = None full_name: Optional[str] = None class UserUpdate(BaseModel): """更新用户""" email: Optional[str] = None full_name: Optional[str] = None password: Optional[str] = None # ============================================ # 交易模型 # ============================================ class OrderDirection(str, Enum): """订单方向""" BUY = "buy" SELL = "sell" class OrderType(str, Enum): """订单类型""" LIMIT = "limit" MARKET = "market" STOP = "stop" class SendOrderRequest(BaseModel): """发送订单请求""" symbol: str = Field(..., description="交易品种代码") exchange: str = Field(..., description="交易所代码") direction: OrderDirection order_type: OrderType volume: float = Field(..., gt=0, description="数量") price: Optional[float] = Field(None, gt=0, description="价格(限价单必填)") stop_price: Optional[float] = Field(None, gt=0, description="止损价") reference: Optional[str] = Field(None, description="客户引用") class CancelOrderRequest(BaseModel): """撤单请求""" order_id: str = Field(..., description="订单号") class OrderResponse(BaseModel): """订单响应""" order_id: str symbol: str exchange: str direction: str order_type: str volume: float price: Optional[float] traded: float status: str time: datetime reference: Optional[str] = None # ============================================ # 行情数据模型 # ============================================ class TickData(BaseModel): """Tick 数据""" symbol: str exchange: str datetime: datetime name: Optional[str] = None last_price: float bid_price_1: Optional[float] = None ask_price_1: Optional[float] = None bid_volume_1: Optional[float] = None ask_volume_1: Optional[float] = None volume: Optional[float] = None open_interest: Optional[float] = None class KlineRequest(BaseModel): """K线数据请求""" symbol: str = Field(..., description="交易品种代码") exchange: str = Field(..., description="交易所代码") interval: str = Field(..., description="周期: 1m, 5m, 15m, 1h, 4h, 1d") start: Optional[datetime] = Field(None, description="开始时间") end: Optional[datetime] = Field(None, description="结束时间") limit: int = Field(1000, ge=1, le=10000, description="数据条数限制") class KlineData(BaseModel): """K线数据""" symbol: str exchange: str datetime: datetime interval: str open_price: float high_price: float low_price: float close_price: float volume: float open_interest: Optional[float] = None class SubscribeRequest(BaseModel): """订阅行情请求""" symbol: str = Field(..., description="交易品种代码") exchange: str = Field(..., description="交易所代码") gateway_name: Optional[str] = Field(None, description="网关名称") class UnsubscribeRequest(BaseModel): """取消订阅请求""" symbol: str = Field(..., description="交易品种代码") exchange: str = Field(..., description="交易所代码") gateway_name: Optional[str] = Field(None, description="网关名称") # ============================================ # 持仓数据模型 # ============================================ class PositionData(BaseModel): """持仓数据""" symbol: str exchange: str direction: str volume: float price: float pnl: float pnl_ratio: float frozen: float = 0.0 yd_volume: float = 0.0 # ============================================ # 账户数据模型 # ============================================ class AccountData(BaseModel): """账户数据""" account_id: str balance: float available: float frozen: float = 0.0 margin: float = 0.0 class TradeData(BaseModel): """成交数据""" trade_id: str order_id: str symbol: str exchange: str direction: str volume: float price: float time: datetime class AccountResponse(BaseModel): """账户响应""" account: AccountData positions: List[PositionData] orders: List[OrderResponse] # ============================================ # 网关模型 # ============================================ class GatewayType(str, Enum): """网关类型""" CTP = "ctp" IB = "ib" OKX = "okx" BINAANCE = "binance" TEST = "test" class GatewayStatus(BaseModel): """网关状态""" gateway_name: str gateway_type: GatewayType status: str # connected, disconnected, connecting connected_time: Optional[datetime] = None message: Optional[str] = None class GatewayConnectRequest(BaseModel): """连接网关请求""" gateway_name: str = Field(..., description="网关名称") gateway_type: GatewayType setting: dict = Field(..., description="网关配置参数") class GatewayDisconnectRequest(BaseModel): """断开网关请求""" gateway_name: str # ============================================ # 策略模型 # ============================================ class StrategyStatus(str, Enum): """策略状态""" CREATED = "created" RUNNING = "running" STOPPED = "stopped" PAUSED = "paused" class StrategyInfo(BaseModel): """策略信息""" strategy_name: str class_name: str status: StrategyStatus created_at: datetime variables: Optional[dict] = None class StrategyCreateRequest(BaseModel): """创建策略请求""" strategy_name: str = Field(..., min_length=1, max_length=50) class_name: str = Field(..., description="策略类名") setting: dict = Field(default_factory=dict, description="策略参数") class StrategyInitRequest(BaseModel): """初始化策略请求""" strategy_name: str class StrategyStartRequest(BaseModel): """启动策略请求""" strategy_name: str class StrategyStopRequest(BaseModel): """停止策略请求""" strategy_name: str class StrategyEditRequest(BaseModel): """编辑策略请求""" strategy_name: str setting: dict # ============================================ # 响应模型 # ============================================ class ApiResponse(BaseModel): """通用 API 响应""" success: bool message: Optional[str] = None data: Optional[Any] = None class PaginatedResponse(BaseModel): """分页响应""" total: int page: int page_size: int data: List[Any] # ============================================ # 系统模型 # ============================================ class HealthResponse(BaseModel): """健康检查响应""" status: str service: str version: str timestamp: datetime class SystemInfo(BaseModel): """系统信息""" version: str vnpy_version: str python_version: str uptime: float connected_gateways: int active_strategies: int # ============================================ # WebSocket 模型 # ============================================ class WSSubscribeRequest(BaseModel): """WebSocket 订阅请求""" type: str # tick, order, position, trade symbols: Optional[List[str]] = None class WSMessage(BaseModel): """WebSocket 消息""" type: str data: Any timestamp: datetime