Add dc_only parameter to all strategy endpoints. When true, filters results to include only tickers present in the ETF table, supporting DC pension investment constraints where only ETFs are allowed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
141 lines
3.7 KiB
Python
141 lines
3.7 KiB
Python
"""
|
|
Quant strategy related Pydantic schemas.
|
|
"""
|
|
from datetime import date
|
|
from decimal import Decimal
|
|
from typing import Optional, List, Dict
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
from app.schemas.portfolio import FloatDecimal
|
|
|
|
|
|
class FactorWeights(BaseModel):
|
|
"""Factor weights for multi-factor strategy."""
|
|
value: FloatDecimal = Field(default=Decimal("0.25"), ge=0, le=1)
|
|
quality: FloatDecimal = Field(default=Decimal("0.25"), ge=0, le=1)
|
|
momentum: FloatDecimal = Field(default=Decimal("0.25"), ge=0, le=1)
|
|
low_vol: FloatDecimal = 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
|
|
dc_only: bool = False
|
|
|
|
|
|
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: FloatDecimal = Field(default=Decimal("0.5"), ge=0, le=1)
|
|
momentum_weight: FloatDecimal = Field(default=Decimal("0.5"), ge=0, le=1)
|
|
|
|
|
|
class KJBRequest(StrategyRequest):
|
|
"""KJB strategy request."""
|
|
pass
|
|
|
|
|
|
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[FloatDecimal] = None
|
|
|
|
# Raw metrics
|
|
per: Optional[FloatDecimal] = None
|
|
pbr: Optional[FloatDecimal] = None
|
|
psr: Optional[FloatDecimal] = None
|
|
pcr: Optional[FloatDecimal] = None
|
|
dividend_yield: Optional[FloatDecimal] = None
|
|
roe: Optional[FloatDecimal] = None
|
|
|
|
# Factor scores (z-scores)
|
|
value_score: Optional[FloatDecimal] = None
|
|
quality_score: Optional[FloatDecimal] = None
|
|
momentum_score: Optional[FloatDecimal] = None
|
|
|
|
# Composite
|
|
total_score: Optional[FloatDecimal] = 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[FloatDecimal] = None
|
|
market_cap: Optional[int] = None
|
|
|
|
# Valuation
|
|
per: Optional[FloatDecimal] = None
|
|
pbr: Optional[FloatDecimal] = None
|
|
psr: Optional[FloatDecimal] = None
|
|
pcr: Optional[FloatDecimal] = None
|
|
dividend_yield: Optional[FloatDecimal] = None
|
|
|
|
# Per-share data
|
|
eps: Optional[FloatDecimal] = None
|
|
bps: Optional[FloatDecimal] = 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: FloatDecimal
|
|
high: FloatDecimal
|
|
low: FloatDecimal
|
|
close: FloatDecimal
|
|
volume: int
|
|
|
|
class Config:
|
|
from_attributes = True
|