"""Portfolio schemas.""" from pydantic import BaseModel, Field, validator from typing import List, Dict, Optional from datetime import datetime from uuid import UUID class PortfolioAssetCreate(BaseModel): """포트폴리오 자산 생성 요청.""" ticker: str = Field(..., description="종목코드") target_ratio: float = Field(..., ge=0, le=100, description="목표 비율 (%)") class PortfolioAssetResponse(BaseModel): """포트폴리오 자산 응답.""" id: UUID ticker: str target_ratio: float class Config: from_attributes = True class PortfolioCreate(BaseModel): """포트폴리오 생성 요청.""" name: str = Field(..., min_length=1, max_length=100, description="포트폴리오 이름") description: Optional[str] = Field(None, description="포트폴리오 설명") assets: List[PortfolioAssetCreate] = Field(..., min_items=1, description="자산 목록") @validator('assets') def validate_total_ratio(cls, v): """목표 비율 합계가 100%인지 검증.""" total = sum(asset.target_ratio for asset in v) if abs(total - 100.0) > 0.01: # 부동소수점 오차 허용 raise ValueError(f'목표 비율의 합은 100%여야 합니다 (현재: {total}%)') return v class PortfolioUpdate(BaseModel): """포트폴리오 수정 요청.""" name: Optional[str] = Field(None, min_length=1, max_length=100) description: Optional[str] = None assets: Optional[List[PortfolioAssetCreate]] = None @validator('assets') def validate_total_ratio(cls, v): """목표 비율 합계가 100%인지 검증.""" if v is not None: total = sum(asset.target_ratio for asset in v) if abs(total - 100.0) > 0.01: raise ValueError(f'목표 비율의 합은 100%여야 합니다 (현재: {total}%)') return v class PortfolioResponse(BaseModel): """포트폴리오 응답.""" id: UUID name: str description: Optional[str] user_id: Optional[str] assets: List[PortfolioAssetResponse] created_at: datetime class Config: from_attributes = True class CurrentHolding(BaseModel): """현재 보유 자산.""" ticker: str = Field(..., description="종목코드") quantity: float = Field(..., ge=0, description="보유 수량") class RebalancingRequest(BaseModel): """리밸런싱 요청.""" portfolio_id: UUID = Field(..., description="포트폴리오 ID") current_holdings: List[CurrentHolding] = Field(..., description="현재 보유 자산") cash: float = Field(default=0, ge=0, description="현금 (원)") class RebalancingRecommendation(BaseModel): """리밸런싱 추천.""" ticker: str name: str current_quantity: float current_value: float current_ratio: float target_ratio: float target_value: float delta_value: float delta_quantity: float action: str # 'buy', 'sell', 'hold' current_price: float class RebalancingResponse(BaseModel): """리밸런싱 응답.""" portfolio: PortfolioResponse total_value: float cash: float recommendations: List[RebalancingRecommendation] summary: Dict[str, int] # {'buy': N, 'sell': M, 'hold': K} class PortfolioListResponse(BaseModel): """포트폴리오 목록 응답.""" items: List[PortfolioResponse] total: int