You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

95 lines
3.1 KiB

# -*- coding: utf-8 -*-
"""Backtest API schemas."""
from __future__ import annotations
from typing import Any, Dict, List, Optional
from pydantic import BaseModel, Field
class BacktestRunRequest(BaseModel):
code: Optional[str] = Field(None, description="仅回测指定股票")
force: bool = Field(False, description="强制重新计算")
eval_window_days: Optional[int] = Field(None, ge=1, le=120, description="评估窗口(交易日数)")
min_age_days: Optional[int] = Field(None, ge=0, le=365, description="分析记录最小天龄0=不限)")
limit: int = Field(200, ge=1, le=2000, description="最多处理的分析记录数")
class BacktestRunResponse(BaseModel):
processed: int = Field(..., description="候选记录数")
saved: int = Field(..., description="写入回测结果数")
completed: int = Field(..., description="完成回测数")
insufficient: int = Field(..., description="数据不足数")
errors: int = Field(..., description="错误数")
class BacktestResultItem(BaseModel):
analysis_history_id: int
code: str
analysis_date: Optional[str] = None
eval_window_days: int
engine_version: str
eval_status: str
evaluated_at: Optional[str] = None
operation_advice: Optional[str] = None
position_recommendation: Optional[str] = None
start_price: Optional[float] = None
end_close: Optional[float] = None
max_high: Optional[float] = None
min_low: Optional[float] = None
stock_return_pct: Optional[float] = None
direction_expected: Optional[str] = None
direction_correct: Optional[bool] = None
outcome: Optional[str] = None
stop_loss: Optional[float] = None
take_profit: Optional[float] = None
hit_stop_loss: Optional[bool] = None
hit_take_profit: Optional[bool] = None
first_hit: Optional[str] = None
first_hit_date: Optional[str] = None
first_hit_trading_days: Optional[int] = None
simulated_entry_price: Optional[float] = None
simulated_exit_price: Optional[float] = None
simulated_exit_reason: Optional[str] = None
simulated_return_pct: Optional[float] = None
class BacktestResultsResponse(BaseModel):
total: int
page: int
limit: int
items: List[BacktestResultItem] = Field(default_factory=list)
class PerformanceMetrics(BaseModel):
scope: str
code: Optional[str] = None
eval_window_days: int
engine_version: str
computed_at: Optional[str] = None
total_evaluations: int
completed_count: int
insufficient_count: int
long_count: int
cash_count: int
win_count: int
loss_count: int
neutral_count: int
direction_accuracy_pct: Optional[float] = None
win_rate_pct: Optional[float] = None
neutral_rate_pct: Optional[float] = None
avg_stock_return_pct: Optional[float] = None
avg_simulated_return_pct: Optional[float] = None
stop_loss_trigger_rate: Optional[float] = None
take_profit_trigger_rate: Optional[float] = None
ambiguous_rate: Optional[float] = None
avg_days_to_first_hit: Optional[float] = None
advice_breakdown: Dict[str, Any] = Field(default_factory=dict)
diagnostics: Dict[str, Any] = Field(default_factory=dict)