diff --git a/backend/app/api/portfolio.py b/backend/app/api/portfolio.py index b5f28a8..4d92ad0 100644 --- a/backend/app/api/portfolio.py +++ b/backend/app/api/portfolio.py @@ -354,10 +354,11 @@ async def get_portfolio_detail( """Get portfolio with calculated values.""" portfolio = _get_portfolio(db, portfolio_id, current_user.id) - # Get current prices + # Get current prices and stock names tickers = [h.ticker for h in portfolio.holdings] service = RebalanceService(db) prices = service.get_current_prices(tickers) + names = service.get_stock_names(tickers) # Calculate holding values holdings_with_value = [] @@ -376,6 +377,7 @@ async def get_portfolio_detail( holdings_with_value.append(HoldingWithValue( ticker=holding.ticker, + name=names.get(holding.ticker), quantity=holding.quantity, avg_price=Decimal(str(holding.avg_price)), current_price=current_price, diff --git a/backend/app/schemas/portfolio.py b/backend/app/schemas/portfolio.py index 0762dce..eafb9df 100644 --- a/backend/app/schemas/portfolio.py +++ b/backend/app/schemas/portfolio.py @@ -47,6 +47,7 @@ class HoldingResponse(HoldingBase): class HoldingWithValue(HoldingResponse): """Holding with calculated values.""" + name: str | None = None current_price: FloatDecimal | None = None value: FloatDecimal | None = None current_ratio: FloatDecimal | None = None diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx index 9a179ac..8519ccc 100644 --- a/frontend/src/app/page.tsx +++ b/frontend/src/app/page.tsx @@ -11,6 +11,7 @@ import { api } from '@/lib/api'; interface HoldingWithValue { ticker: string; + name: string | null; current_ratio: number | null; value: number | null; profit_loss_ratio: number | null; @@ -93,22 +94,27 @@ export default function DashboardPage() { // Aggregate holdings for donut chart const allHoldings = portfolios.flatMap((p) => p.holdings); - const holdingsByTicker: Record = {}; + const holdingsByTicker: Record = {}; for (const h of allHoldings) { if (h.value && h.value > 0) { - holdingsByTicker[h.ticker] = (holdingsByTicker[h.ticker] ?? 0) + h.value; + const existing = holdingsByTicker[h.ticker]; + holdingsByTicker[h.ticker] = { + value: (existing?.value ?? 0) + h.value, + name: h.name || existing?.name || h.ticker, + }; } } const donutData = Object.entries(holdingsByTicker) - .sort((a, b) => b[1] - a[1]) + .sort((a, b) => b[1].value - a[1].value) .slice(0, 6) - .map(([ticker, value], i) => ({ - name: ticker, + .map(([, { value, name }], i) => ({ + name, value: totalValue > 0 ? (value / totalValue) * 100 : 0, color: CHART_COLORS[i % CHART_COLORS.length], })); // Add "기타" if there are more holdings + const totalHoldingsCount = Object.keys(holdingsByTicker).length; const topValue = donutData.reduce((s, d) => s + d.value, 0); if (topValue < 100 && topValue > 0) { donutData.push({ name: '기타', value: 100 - topValue, color: '#6b7280' }); @@ -172,7 +178,7 @@ export default function DashboardPage() { -
{Object.keys(holdingsByTicker).length}
+
{totalHoldingsCount}

보유 중인 종목

diff --git a/frontend/src/app/portfolio/[id]/page.tsx b/frontend/src/app/portfolio/[id]/page.tsx index ca7f895..bdd6403 100644 --- a/frontend/src/app/portfolio/[id]/page.tsx +++ b/frontend/src/app/portfolio/[id]/page.tsx @@ -15,6 +15,7 @@ import type { AreaData, Time } from 'lightweight-charts'; interface HoldingWithValue { ticker: string; + name: string | null; quantity: number; avg_price: number; current_price: number | null; @@ -163,7 +164,7 @@ export default function PortfolioDetailPage() { return portfolio.holdings .filter((h) => h.current_ratio !== null && h.current_ratio > 0) .map((h, index) => ({ - name: h.ticker, + name: h.name || h.ticker, value: h.current_ratio ?? 0, color: CHART_COLORS[index % CHART_COLORS.length], })); @@ -339,7 +340,12 @@ export default function PortfolioDetailPage() { {portfolio.holdings.map((holding, index) => ( - {holding.ticker} + +
{holding.name || holding.ticker}
+ {holding.name && ( +
{holding.ticker}
+ )} + {holding.quantity.toLocaleString()} @@ -513,7 +519,7 @@ export default function PortfolioDetailPage() { return (
- {target.ticker} + {holding?.name || target.ticker} {actualRatio.toFixed(1)}% / {target.target_ratio.toFixed(1)}%