feat: add portfolio detail API with calculated values

This commit is contained in:
zephyrdark 2026-02-03 07:11:19 +09:00
parent 5f3c6061c9
commit 7edc152491

View File

@ -1,6 +1,7 @@
"""
Portfolio management API endpoints.
"""
from decimal import Decimal
from typing import List
from fastapi import APIRouter, Depends, HTTPException, status
@ -12,7 +13,7 @@ from app.models.portfolio import Portfolio, PortfolioType, Target, Holding, Tran
from app.schemas.portfolio import (
PortfolioCreate, PortfolioUpdate, PortfolioResponse, PortfolioDetail,
TargetCreate, TargetResponse,
HoldingCreate, HoldingResponse,
HoldingCreate, HoldingResponse, HoldingWithValue,
TransactionCreate, TransactionResponse,
RebalanceResponse, RebalanceSimulationRequest, RebalanceSimulationResponse,
)
@ -316,3 +317,63 @@ async def simulate_rebalance(
portfolio = _get_portfolio(db, portfolio_id, current_user.id)
service = RebalanceService(db)
return service.calculate_rebalance(portfolio, additional_amount=data.additional_amount)
@router.get("/{portfolio_id}/detail", response_model=PortfolioDetail)
async def get_portfolio_detail(
portfolio_id: int,
current_user: CurrentUser,
db: Session = Depends(get_db),
):
"""Get portfolio with calculated values."""
portfolio = _get_portfolio(db, portfolio_id, current_user.id)
# Get current prices
tickers = [h.ticker for h in portfolio.holdings]
service = RebalanceService(db)
prices = service.get_current_prices(tickers)
# Calculate holding values
holdings_with_value = []
total_value = Decimal("0")
total_invested = Decimal("0")
for holding in portfolio.holdings:
current_price = prices.get(holding.ticker, Decimal("0"))
value = current_price * holding.quantity
invested = Decimal(str(holding.avg_price)) * holding.quantity
profit_loss = value - invested
profit_loss_ratio = (profit_loss / invested * 100) if invested > 0 else Decimal("0")
total_value += value
total_invested += invested
holdings_with_value.append(HoldingWithValue(
ticker=holding.ticker,
quantity=holding.quantity,
avg_price=Decimal(str(holding.avg_price)),
current_price=current_price,
value=value,
current_ratio=Decimal("0"), # Will be calculated after total
profit_loss=profit_loss,
profit_loss_ratio=profit_loss_ratio.quantize(Decimal("0.01")),
))
# Calculate current ratios
for h in holdings_with_value:
if total_value > 0:
h.current_ratio = (h.value / total_value * 100).quantize(Decimal("0.01"))
return PortfolioDetail(
id=portfolio.id,
user_id=portfolio.user_id,
name=portfolio.name,
portfolio_type=portfolio.portfolio_type.value,
created_at=portfolio.created_at,
updated_at=portfolio.updated_at,
targets=portfolio.targets,
holdings=holdings_with_value,
total_value=total_value,
total_invested=total_invested,
total_profit_loss=total_value - total_invested,
)