feat: add POST /rebalance/calculate endpoint with manual prices

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
zephyrdark 2026-02-11 23:30:22 +09:00
parent 8c00359a50
commit bffca88ce9
2 changed files with 29 additions and 3 deletions

View File

@ -16,6 +16,7 @@ from app.schemas.portfolio import (
HoldingCreate, HoldingResponse, HoldingWithValue,
TransactionCreate, TransactionResponse,
RebalanceResponse, RebalanceSimulationRequest, RebalanceSimulationResponse,
RebalanceCalculateRequest, RebalanceCalculateResponse,
)
from app.services.rebalance import RebalanceService
@ -319,6 +320,31 @@ async def simulate_rebalance(
return service.calculate_rebalance(portfolio, additional_amount=data.additional_amount)
@router.post("/{portfolio_id}/rebalance/calculate", response_model=RebalanceCalculateResponse)
async def calculate_rebalance_manual(
portfolio_id: int,
data: RebalanceCalculateRequest,
current_user: CurrentUser,
db: Session = Depends(get_db),
):
"""Calculate rebalancing with manual prices and strategy selection."""
portfolio = _get_portfolio(db, portfolio_id, current_user.id)
if data.strategy == "additional_buy" and not data.additional_amount:
raise HTTPException(
status_code=400,
detail="additional_amount is required for additional_buy strategy"
)
service = RebalanceService(db)
return service.calculate_with_prices(
portfolio,
strategy=data.strategy,
manual_prices=data.prices,
additional_amount=data.additional_amount,
)
@router.get("/{portfolio_id}/detail", response_model=PortfolioDetail)
async def get_portfolio_detail(
portfolio_id: int,

View File

@ -51,7 +51,7 @@ def test_calculate_rebalance_with_manual_prices(client: TestClient, auth_headers
assert response.status_code == 200
data = response.json()
assert data["portfolio_id"] == pid
assert data["total_assets"] > 0
assert float(data["total_assets"]) > 0
assert len(data["items"]) == 2
# Verify items have required fields
item = data["items"][0]
@ -78,8 +78,8 @@ def test_calculate_additional_buy_strategy(client: TestClient, auth_headers):
)
assert response.status_code == 200
data = response.json()
assert data["total_assets"] > 0
assert data["available_to_buy"] == 1000000
assert float(data["total_assets"]) > 0
assert float(data["available_to_buy"]) == 1000000
# Additional buy should never have "sell" actions
for item in data["items"]:
assert item["action"] in ("buy", "hold")