Files
claude_dev 918bbed0fc fix: 修复登录500错误和移除明文密码提示
- 修复 deps.py 中 get_vn_service 的引用错误 (vn_service.vn_service -> vn_service)
- 移除登录页面上的明文密码提示
- 改进前端错误处理,避免数据加载失败导致登录显示错误
2026-07-02 12:23:55 +08:00

368 lines
8.7 KiB
Python

"""
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