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

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# -*- 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)