17 KiB
Stock Name Display Implementation Plan
For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
Goal: Replace stock code displays with stock name as primary text across all portfolio-related views, with stock codes available via tooltip.
Architecture: Backend schemas and API endpoints that currently lack stock names (SnapshotHoldingResponse, TransactionResponse, backtest TransactionItem) get name fields added, with name resolution via the existing RebalanceService.get_stock_names(). Frontend swaps display pattern from ticker (primary) + name (subtitle) to name (primary) with title={ticker} tooltip.
Tech Stack: FastAPI + SQLAlchemy (backend), Next.js 15 + React 19 + TypeScript (frontend)
Task 1: Add name field to SnapshotHoldingResponse schema
Files:
- Modify:
backend/app/schemas/portfolio.py:114-122
Step 1: Add name field to schema
In backend/app/schemas/portfolio.py, add name field to SnapshotHoldingResponse:
class SnapshotHoldingResponse(BaseModel):
ticker: str
name: str | None = None
quantity: int
price: FloatDecimal
value: FloatDecimal
current_ratio: FloatDecimal
class Config:
from_attributes = True
Step 2: Commit
git add backend/app/schemas/portfolio.py
git commit -m "feat: add name field to SnapshotHoldingResponse schema"
Task 2: Add name field to TransactionResponse schema
Files:
- Modify:
backend/app/schemas/portfolio.py:72-76
Step 1: Add name field to schema
In backend/app/schemas/portfolio.py, add name field to TransactionResponse:
class TransactionResponse(TransactionBase):
id: int
name: str | None = None
class Config:
from_attributes = True
Step 2: Commit
git add backend/app/schemas/portfolio.py
git commit -m "feat: add name field to TransactionResponse schema"
Task 3: Add name field to backtest TransactionItem schema
Files:
- Modify:
backend/app/schemas/backtest.py:119-131
Step 1: Add name field to schema
In backend/app/schemas/backtest.py, add name field to TransactionItem:
class TransactionItem(BaseModel):
"""Single transaction."""
id: int
date: date
ticker: str
name: str | None = None
action: str
shares: int
price: FloatDecimal
commission: FloatDecimal
class Config:
from_attributes = True
Step 2: Commit
git add backend/app/schemas/backtest.py
git commit -m "feat: add name field to backtest TransactionItem schema"
Task 4: Populate snapshot holdings with stock names in snapshot API
Files:
- Modify:
backend/app/api/snapshot.py:53-128(create_snapshot endpoint) - Modify:
backend/app/api/snapshot.py:131-168(get_snapshot endpoint)
Step 1: Update imports
Add RebalanceService import at top of backend/app/api/snapshot.py:
from app.services.rebalance import RebalanceService
Step 2: Update create_snapshot endpoint to include names
In the create_snapshot function, after getting prices and before creating snapshot, resolve names and include them in the response. Replace the return statement (lines 113-128):
# Get stock names
name_service = RebalanceService(db)
names = name_service.get_stock_names(tickers)
return SnapshotResponse(
id=snapshot.id,
portfolio_id=snapshot.portfolio_id,
total_value=snapshot.total_value,
snapshot_date=snapshot.snapshot_date,
holdings=[
SnapshotHoldingResponse(
ticker=h.ticker,
name=names.get(h.ticker),
quantity=h.quantity,
price=h.price,
value=h.value,
current_ratio=h.current_ratio,
)
for h in snapshot.holdings
],
)
Step 3: Update get_snapshot endpoint to include names
In the get_snapshot function, resolve names before returning. Replace lines 153-168:
# Get stock names
tickers = [h.ticker for h in snapshot.holdings]
name_service = RebalanceService(db)
names = name_service.get_stock_names(tickers)
return SnapshotResponse(
id=snapshot.id,
portfolio_id=snapshot.portfolio_id,
total_value=snapshot.total_value,
snapshot_date=snapshot.snapshot_date,
holdings=[
SnapshotHoldingResponse(
ticker=h.ticker,
name=names.get(h.ticker),
quantity=h.quantity,
price=h.price,
value=h.value,
current_ratio=h.current_ratio,
)
for h in snapshot.holdings
],
)
Step 4: Commit
git add backend/app/api/snapshot.py
git commit -m "feat: include stock names in snapshot API responses"
Task 5: Populate transactions with stock names in portfolio API
Files:
- Modify:
backend/app/api/portfolio.py:218-234
Step 1: Update imports
At top of backend/app/api/portfolio.py, RebalanceService is already imported (line 21). No change needed.
Step 2: Update get_transactions endpoint
Replace the get_transactions function body to resolve names:
@router.get("/{portfolio_id}/transactions", response_model=List[TransactionResponse])
async def get_transactions(
portfolio_id: int,
current_user: CurrentUser,
db: Session = Depends(get_db),
limit: int = 50,
):
"""Get transaction history for a portfolio."""
_get_portfolio(db, portfolio_id, current_user.id)
transactions = (
db.query(Transaction)
.filter(Transaction.portfolio_id == portfolio_id)
.order_by(Transaction.executed_at.desc())
.limit(limit)
.all()
)
# Resolve stock names
tickers = list({tx.ticker for tx in transactions})
service = RebalanceService(db)
names = service.get_stock_names(tickers)
return [
TransactionResponse(
id=tx.id,
ticker=tx.ticker,
name=names.get(tx.ticker),
tx_type=tx.tx_type.value,
quantity=tx.quantity,
price=tx.price,
executed_at=tx.executed_at,
memo=tx.memo,
)
for tx in transactions
]
Step 3: Commit
git add backend/app/api/portfolio.py
git commit -m "feat: include stock names in transaction API responses"
Task 6: Populate backtest transactions with stock names
Files:
- Modify:
backend/app/api/backtest.py:209-242
Step 1: Add import
Add RebalanceService import at top of backend/app/api/backtest.py:
from app.services.rebalance import RebalanceService
Step 2: Update get_transactions endpoint
Replace the return logic in the backtest get_transactions endpoint:
transactions = (
db.query(BacktestTransaction)
.filter(BacktestTransaction.backtest_id == backtest_id)
.order_by(BacktestTransaction.date, BacktestTransaction.id)
.all()
)
# Resolve stock names
tickers = list({t.ticker for t in transactions})
name_service = RebalanceService(db)
names = name_service.get_stock_names(tickers)
return [
TransactionItem(
id=t.id,
date=t.date,
ticker=t.ticker,
name=names.get(t.ticker),
action=t.action,
shares=t.shares,
price=t.price,
commission=t.commission,
)
for t in transactions
]
Step 3: Commit
git add backend/app/api/backtest.py
git commit -m "feat: include stock names in backtest transaction responses"
Task 7: Update portfolio-card.tsx - show name instead of ticker
Files:
- Modify:
frontend/src/components/portfolio/portfolio-card.tsx
Step 1: Add name to Holding interface
interface Holding {
ticker: string;
name: string | null;
current_ratio: number | null;
}
Step 2: Update pieData mapping to use name
Change line 64 from name: h.ticker to:
name: h.name || h.ticker,
Step 3: Add title attribute to holdings preview badges
Change the badge span (line 144-149) to include a title:
<span
key={index}
className="text-xs px-2 py-0.5 rounded bg-muted text-muted-foreground"
title={pieData[index]?.name !== holdings[index]?.name ? holdings.find(h => (h.name || h.ticker) === item.name)?.ticker : undefined}
>
{item.name}
</span>
Actually, simpler approach - since pieData is derived from holdings, we can track the original ticker. Let's use a simpler approach: map pieData to include the original ticker, then use title={item.ticker}:
Change the pieData mapping (lines 60-67):
const pieData = holdings
.filter((h) => h.current_ratio !== null && h.current_ratio > 0)
.slice(0, 6)
.map((h, index) => ({
name: h.name || h.ticker,
ticker: h.ticker,
value: h.current_ratio ?? 0,
color: CHART_COLORS[index % CHART_COLORS.length],
}));
Then update the badge (lines 143-149):
{pieData.slice(0, 4).map((item, index) => (
<span
key={index}
className="text-xs px-2 py-0.5 rounded bg-muted text-muted-foreground"
title={item.ticker}
>
{item.name}
</span>
))}
Step 4: Also update HoldingWithValue in portfolio list page
In frontend/src/app/portfolio/page.tsx, add name to the HoldingWithValue interface (line 12-15):
interface HoldingWithValue {
ticker: string;
name: string | null;
current_ratio: number | null;
}
Step 5: Commit
git add frontend/src/components/portfolio/portfolio-card.tsx frontend/src/app/portfolio/page.tsx
git commit -m "feat: show stock names in portfolio cards"
Task 8: Update portfolio detail page - fix holdings ticker subtitle removal + transactions name
Files:
- Modify:
frontend/src/app/portfolio/[id]/page.tsx
Step 1: Add name to Transaction interface
interface Transaction {
id: number;
ticker: string;
name: string | null;
tx_type: string;
quantity: number;
price: number;
executed_at: string;
}
Step 2: Update holdings table - remove ticker subtitle, add title tooltip
Change lines 343-348 from:
<td className="px-4 py-3">
<div className="font-medium text-sm">{holding.name || holding.ticker}</div>
{holding.name && (
<div className="text-xs text-muted-foreground">{holding.ticker}</div>
)}
</td>
To:
<td className="px-4 py-3">
<span className="font-medium text-sm" title={holding.ticker}>{holding.name || holding.ticker}</span>
</td>
Step 3: Update transactions table - show name instead of ticker
Change line 457 from:
<td className="px-4 py-3 text-sm font-medium">{tx.ticker}</td>
To:
<td className="px-4 py-3 text-sm font-medium" title={tx.ticker}>{tx.name || tx.ticker}</td>
Step 4: Update Target vs Actual section - add title tooltip
Change line 522 from:
<span className="font-medium">{holding?.name || target.ticker}</span>
To:
<span className="font-medium" title={target.ticker}>{holding?.name || target.ticker}</span>
Step 5: Commit
git add frontend/src/app/portfolio/[id]/page.tsx
git commit -m "feat: show stock names as primary display in portfolio detail"
Task 9: Update rebalance page - name as primary display
Files:
- Modify:
frontend/src/app/portfolio/[id]/rebalance/page.tsx
Step 1: Fetch stock names for price input labels
The rebalance page needs names for the price input section. The targets and holdings don't have names, but the rebalance result does. We need to store a name map. Add state and fetch names when targets/holdings load.
Add nameMap state after existing state declarations (after line 61):
const [nameMap, setNameMap] = useState<Record<string, string>>({});
In the init function, after setting targets and holdings, fetch portfolio detail to get names:
// Fetch stock names from portfolio detail
try {
const detail = await api.get<{ holdings: { ticker: string; name: string | null }[] }>(`/api/portfolios/${portfolioId}/detail`);
const names: Record<string, string> = {};
for (const h of detail.holdings) {
if (h.name) names[h.ticker] = h.name;
}
setNameMap(names);
} catch {
// Names are optional, continue without
}
Step 2: Update price input labels to use name
Change line 183 from:
{ticker} {target ? `(목표 ${target.target_ratio}%)` : ''} - 보유 {getHoldingQty(ticker)}주
To:
{nameMap[ticker] || ticker} {target ? `(목표 ${target.target_ratio}%)` : ''} - 보유 {getHoldingQty(ticker)}주
Step 3: Update results table - name as primary
Change lines 299-304 from:
<td className="px-3 py-3">
<div className="font-medium">{item.ticker}</div>
{item.name && (
<div className="text-xs text-muted-foreground">{item.name}</div>
)}
</td>
To:
<td className="px-3 py-3">
<span className="font-medium" title={item.ticker}>{item.name || item.ticker}</span>
</td>
Step 4: Also update nameMap from rebalance results
After the calculate function sets the result, update nameMap with any names from the result:
In the calculate function, after setResult(data) (line 117), add:
// Update name map from results
const newNames = { ...nameMap };
for (const item of data.items) {
if (item.name) newNames[item.ticker] = item.name;
}
setNameMap(newNames);
Step 5: Commit
git add frontend/src/app/portfolio/[id]/rebalance/page.tsx
git commit -m "feat: show stock names as primary display in rebalance page"
Task 10: Update portfolio history page - show names in snapshot
Files:
- Modify:
frontend/src/app/portfolio/[id]/history/page.tsx
Step 1: Add name to SnapshotDetail holdings interface
Change the holdings type in SnapshotDetail interface (lines 23-29):
holdings: {
ticker: string;
name: string | null;
quantity: number;
price: string;
value: string;
current_ratio: string;
}[];
Step 2: Update snapshot detail modal table
Change line 428 from:
<td className="px-4 py-2 text-sm text-foreground">
{holding.ticker}
</td>
To:
<td className="px-4 py-2 text-sm text-foreground" title={holding.ticker}>
{holding.name || holding.ticker}
</td>
Step 3: Commit
git add frontend/src/app/portfolio/[id]/history/page.tsx
git commit -m "feat: show stock names in portfolio history snapshots"
Task 11: Update strategy pages - name as primary (3 files)
Files:
- Modify:
frontend/src/app/strategy/multi-factor/page.tsx:209-211 - Modify:
frontend/src/app/strategy/quality/page.tsx:174-176 - Modify:
frontend/src/app/strategy/value-momentum/page.tsx:190-192
Step 1: Update multi-factor page
Change lines 209-211 from:
<td className="px-4 py-3">
<div className="font-medium">{stock.ticker}</div>
<div className="text-xs text-muted-foreground">{stock.name}</div>
</td>
To:
<td className="px-4 py-3">
<span className="font-medium" title={stock.ticker}>{stock.name || stock.ticker}</span>
</td>
Step 2: Update quality page
Same change at lines 174-176.
Step 3: Update value-momentum page
Same change at lines 190-192.
Step 4: Commit
git add frontend/src/app/strategy/multi-factor/page.tsx frontend/src/app/strategy/quality/page.tsx frontend/src/app/strategy/value-momentum/page.tsx
git commit -m "feat: show stock names as primary display in strategy pages"
Task 12: Update backtest detail page - name as primary
Files:
- Modify:
frontend/src/app/backtest/[id]/page.tsx
Step 1: Add name to TransactionItem interface
interface TransactionItem {
id: number;
date: string;
ticker: string;
name: string | null;
action: string;
shares: number;
price: number;
commission: number;
}
Step 2: Update holdings tab display
Change lines 392-394 from:
<td className="px-4 py-3">
<div className="font-medium">{h.ticker}</div>
<div className="text-xs text-muted-foreground">{h.name}</div>
</td>
To:
<td className="px-4 py-3">
<span className="font-medium" title={h.ticker}>{h.name || h.ticker}</span>
</td>
Step 3: Update transactions tab display
Change line 425 from:
<td className="px-4 py-3 text-sm font-medium">{t.ticker}</td>
To:
<td className="px-4 py-3 text-sm font-medium" title={t.ticker}>{t.name || t.ticker}</td>
Step 4: Commit
git add frontend/src/app/backtest/[id]/page.tsx
git commit -m "feat: show stock names as primary display in backtest detail"
Task 13: Verify frontend build
Step 1: Run frontend build to check for TypeScript errors
cd frontend && npm run build
Expected: Build succeeds with no type errors.
Step 2: If build fails, fix any TypeScript errors and re-run
Step 3: Commit any fixes
git add -A && git commit -m "fix: resolve build errors from stock name display changes"