zephyrdark 331ab2cc56 feat: add backtest Pydantic schemas
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 09:35:53 +09:00

129 lines
3.0 KiB
Python

"""
Backtest related Pydantic schemas.
"""
from datetime import date, datetime
from decimal import Decimal
from typing import Optional, List, Dict, Any
from enum import Enum
from pydantic import BaseModel, Field
class RebalancePeriod(str, Enum):
MONTHLY = "monthly"
QUARTERLY = "quarterly"
SEMI_ANNUAL = "semi_annual"
ANNUAL = "annual"
class BacktestStatus(str, Enum):
PENDING = "pending"
RUNNING = "running"
COMPLETED = "completed"
FAILED = "failed"
class BacktestCreate(BaseModel):
"""Request to create a new backtest."""
strategy_type: str = Field(..., description="multi_factor, quality, or value_momentum")
strategy_params: Dict[str, Any] = Field(default_factory=dict)
start_date: date
end_date: date
rebalance_period: RebalancePeriod = RebalancePeriod.QUARTERLY
initial_capital: Decimal = Field(default=Decimal("100000000"), gt=0)
commission_rate: Decimal = Field(default=Decimal("0.00015"), ge=0, le=1)
slippage_rate: Decimal = Field(default=Decimal("0.001"), ge=0, le=1)
benchmark: str = Field(default="KOSPI")
top_n: int = Field(default=30, ge=1, le=100)
class BacktestMetrics(BaseModel):
"""Backtest result metrics."""
total_return: Decimal
cagr: Decimal
mdd: Decimal
sharpe_ratio: Decimal
volatility: Decimal
benchmark_return: Decimal
excess_return: Decimal
class BacktestResponse(BaseModel):
"""Backtest response with status and optional results."""
id: int
user_id: int
strategy_type: str
strategy_params: Dict[str, Any]
start_date: date
end_date: date
rebalance_period: str
initial_capital: Decimal
commission_rate: Decimal
slippage_rate: Decimal
benchmark: str
status: str
created_at: datetime
completed_at: Optional[datetime] = None
error_message: Optional[str] = None
result: Optional[BacktestMetrics] = None
class Config:
from_attributes = True
class BacktestListItem(BaseModel):
"""Backtest item for list view."""
id: int
strategy_type: str
start_date: date
end_date: date
rebalance_period: str
status: str
created_at: datetime
total_return: Optional[Decimal] = None
cagr: Optional[Decimal] = None
mdd: Optional[Decimal] = None
class Config:
from_attributes = True
class EquityCurvePoint(BaseModel):
"""Single point in equity curve."""
date: date
portfolio_value: Decimal
benchmark_value: Decimal
drawdown: Decimal
class Config:
from_attributes = True
class HoldingItem(BaseModel):
"""Single holding at a rebalance date."""
ticker: str
name: str
weight: Decimal
shares: int
price: Decimal
class RebalanceHoldings(BaseModel):
"""Holdings at a specific rebalance date."""
rebalance_date: date
holdings: List[HoldingItem]
class TransactionItem(BaseModel):
"""Single transaction."""
id: int
date: date
ticker: str
action: str
shares: int
price: Decimal
commission: Decimal
class Config:
from_attributes = True