""" Position sizing API endpoints. """ from fastapi import APIRouter, HTTPException from app.api.deps import CurrentUser from app.schemas.position_sizing import ( SizingMethod, PositionSizeRequest, PositionSizeResponse, MethodInfo, MethodsResponse, ) from app.services.position_sizing import fixed_ratio, kelly_criterion, atr_based router = APIRouter(prefix="/api/position-sizing", tags=["position-sizing"]) METHODS = [ MethodInfo( name="fixed", label="균등 분배 (Fixed Ratio)", description="자본금을 종목 수로 균등 분배. 현금 비중 설정 가능. quant.md 기본 방식.", ), MethodInfo( name="kelly", label="켈리 기준 (Kelly Criterion)", description="승률과 손익비를 기반으로 최적 베팅 비율 계산. 보수적 1/4 켈리 기본.", ), MethodInfo( name="atr", label="ATR 변동성 (ATR-Based)", description="ATR(평균 진폭)을 이용한 변동성 기반 포지션 사이징.", ), ] @router.post("/calculate", response_model=PositionSizeResponse) async def calculate_position_size( request: PositionSizeRequest, current_user: CurrentUser, ): """Calculate position size based on selected method.""" try: if request.method == SizingMethod.FIXED: result = fixed_ratio( capital=request.capital, num_positions=request.num_positions, cash_ratio=request.cash_ratio, ) elif request.method == SizingMethod.KELLY: if not all([request.win_rate, request.avg_win, request.avg_loss]): raise HTTPException( status_code=422, detail="Kelly method requires win_rate, avg_win, avg_loss", ) result = kelly_criterion( win_rate=request.win_rate, avg_win=request.avg_win, avg_loss=request.avg_loss, ) elif request.method == SizingMethod.ATR: if not request.atr: raise HTTPException( status_code=422, detail="ATR method requires atr value", ) result = atr_based( capital=request.capital, atr=request.atr, risk_pct=request.risk_pct, ) else: raise HTTPException(status_code=422, detail=f"Unknown method: {request.method}") except ValueError as e: raise HTTPException(status_code=422, detail=str(e)) return PositionSizeResponse(**result) @router.get("/methods", response_model=MethodsResponse) async def get_methods(current_user: CurrentUser): """List supported position sizing methods.""" return MethodsResponse(methods=METHODS)