""" 自动化回测服务 - 数据模型 """ from enum import Enum from datetime import date, datetime from typing import Dict, Optional, Any, List from pydantic import BaseModel, Field class TaskStatus(str, Enum): """任务状态枚举""" PENDING = "pending" RUNNING = "running" COMPLETED = "completed" FAILED = "failed" class BacktestTask(BaseModel): """回测任务请求""" strategy_name: str = Field(..., description="策略名称") strategy_code: str = Field(..., description="策略完整Python代码") symbol: str = Field(..., description="交易品种,例如 000001.SSE") interval: str = Field(..., description="K线周期,例如 1d、1h") start_date: date = Field(..., description="回测开始日期") end_date: date = Field(..., description="回测结束日期") parameters: Dict[str, Any] = Field(default_factory=dict, description="策略参数字典") capital: float = Field(default=1_000_000, description="起始资金") tick_size: Optional[float] = Field(None, description="最小价格变动,不指定则自动获取") class BacktestTaskWithId(BacktestTask): """带ID和状态的回测任务""" task_id: str = Field(..., description="任务唯一ID") status: TaskStatus = Field(..., description="任务状态") created_at: str = Field(..., description="创建时间 ISO格式") started_at: Optional[str] = Field(None, description="开始时间") completed_at: Optional[str] = Field(None, description="完成时间") class BacktestStatistics(BaseModel): """回测结果统计""" start_date: str end_date: str total_days: int total_trades: int winning_trades: int losing_trades: int win_rate: float total_return: float # 总收益率 annual_return: float # 年化收益率 sharpe_ratio: float # 夏普比率 max_drawdown: float # 最大回撤 max_drawdown_start: Optional[str] = None max_drawdown_end: Optional[str] = None profit_factor: float # 收益因子(总盈利/总亏损) calmar_ratio: float # 卡玛比率(年化收益/最大回撤) class BacktestResult(BaseModel): """完整回测结果""" task_id: str strategy_name: str status: TaskStatus statistics: Optional[BacktestStatistics] = None result_csv_path: str # 每日净值CSV路径 equity_curve_png_path: Optional[str] = None # 收益曲线图片路径 trades_csv_path: Optional[str] = None # 成交记录CSV路径 error_message: Optional[str] = None # 失败时的错误信息 created_at: str started_at: Optional[str] = None completed_at: Optional[str] = None class TaskListResponse(BaseModel): """任务列表响应""" total: int page: int page_size: int tasks: List[BacktestTaskWithId] class ApiResponse[T](BaseModel): """通用API响应包装""" code: int = Field(0, description="0表示成功,非0表示错误") msg: str = Field("success", description="响应消息") data: Optional[T] = Field(None, description="响应数据") class HealthCheckResponse(BaseModel): """健康检查响应""" pending_count: int running_count: int completed_count: int failed_count: int max_workers: int