""" Quant strategy related Pydantic schemas. """ from datetime import date from decimal import Decimal from typing import Optional, List, Dict from pydantic import BaseModel, Field class FactorWeights(BaseModel): """Factor weights for multi-factor strategy.""" value: Decimal = Field(default=Decimal("0.25"), ge=0, le=1) quality: Decimal = Field(default=Decimal("0.25"), ge=0, le=1) momentum: Decimal = Field(default=Decimal("0.25"), ge=0, le=1) low_vol: Decimal = Field(default=Decimal("0.25"), ge=0, le=1) class UniverseFilter(BaseModel): """Stock universe filtering criteria.""" markets: List[str] = ["KOSPI", "KOSDAQ"] min_market_cap: Optional[int] = None # in 억원 max_market_cap: Optional[int] = None exclude_stock_types: List[str] = ["spac", "preferred", "reit"] exclude_sectors: List[str] = [] class StrategyRequest(BaseModel): """Base request for running a strategy.""" universe: UniverseFilter = UniverseFilter() top_n: int = Field(default=30, ge=1, le=100) base_date: Optional[date] = None class MultiFactorRequest(StrategyRequest): """Multi-factor strategy request.""" weights: FactorWeights = FactorWeights() class QualityRequest(StrategyRequest): """Super Quality strategy request.""" min_fscore: int = Field(default=7, ge=0, le=9) class ValueMomentumRequest(StrategyRequest): """Value-Momentum strategy request.""" value_weight: Decimal = Field(default=Decimal("0.5"), ge=0, le=1) momentum_weight: Decimal = Field(default=Decimal("0.5"), ge=0, le=1) class StockFactor(BaseModel): """Factor scores for a single stock.""" ticker: str name: str market: str sector_name: Optional[str] = None market_cap: Optional[int] = None close_price: Optional[Decimal] = None # Raw metrics per: Optional[Decimal] = None pbr: Optional[Decimal] = None psr: Optional[Decimal] = None pcr: Optional[Decimal] = None dividend_yield: Optional[Decimal] = None roe: Optional[Decimal] = None # Factor scores (z-scores) value_score: Optional[Decimal] = None quality_score: Optional[Decimal] = None momentum_score: Optional[Decimal] = None # Composite total_score: Optional[Decimal] = None rank: Optional[int] = None fscore: Optional[int] = None class StrategyResult(BaseModel): """Result from running a strategy.""" strategy_name: str base_date: date universe_count: int result_count: int stocks: List[StockFactor] class StockInfo(BaseModel): """Detailed stock information.""" ticker: str name: str market: str sector_name: Optional[str] = None stock_type: str close_price: Optional[Decimal] = None market_cap: Optional[int] = None # Valuation per: Optional[Decimal] = None pbr: Optional[Decimal] = None psr: Optional[Decimal] = None pcr: Optional[Decimal] = None dividend_yield: Optional[Decimal] = None # Per-share data eps: Optional[Decimal] = None bps: Optional[Decimal] = None base_date: Optional[date] = None class Config: from_attributes = True class StockSearchResult(BaseModel): """Stock search result.""" ticker: str name: str market: str class PriceData(BaseModel): """Price data point.""" date: date open: Decimal high: Decimal low: Decimal close: Decimal volume: int class Config: from_attributes = True