galaxis-po/backend/app/api/position_sizing.py

84 lines
2.8 KiB
Python
Raw Normal View History

"""
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)