feat(analysis): 손익 Top5 에 종목명 표시 — trade_history.stock_name 공백 보강

trade_history.stock_name 이 비어 있는 행이 있어 분석 화면의 최대 손실/수익
Top5 가 종목코드만 노출됐다. stock_analysis → portfolio 순으로 종목명을
보강 조회하고, 없으면 코드를 그대로 쓴다(부가 정보이므로 조회 실패는
warning 후 분석 계속).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-28 11:51:45 +09:00
parent 43b4b1a7aa
commit 418371e1c8
2 changed files with 73 additions and 13 deletions

View File

@@ -27,8 +27,8 @@ interface PerformanceData {
summary: PerformanceSummary
by_strategy: Record<string, { total: number; win_rate: number; avg_return: number }>
by_exit_type: Record<string, { count: number; avg_return: number; win_rate: number }>
worst_stocks: { stock_code: string; net_pnl: number }[]
best_stocks: { stock_code: string; net_pnl: number }[]
worst_stocks: { stock_code: string; stock_name?: string; net_pnl: number; trade_count?: number }[]
best_stocks: { stock_code: string; stock_name?: string; net_pnl: number; trade_count?: number }[]
period_days: number
total_trades: number
generated_at: string
@@ -464,9 +464,13 @@ export default function Analysis() {
<h2 className="text-base font-semibold text-red-400 mb-3">최대 손실 종목 Top5</h2>
<div className="space-y-2">
{perf.worst_stocks.map((s) => (
<div key={s.stock_code} className="flex justify-between text-sm">
<span className="text-gray-200">{s.stock_code}</span>
<span className={s.net_pnl < 0 ? "text-red-400" : "text-green-400"}>{fmtKRW(s.net_pnl)}</span>
<div key={s.stock_code} className="flex justify-between items-baseline gap-2 text-sm">
<span className="text-gray-200 truncate">
{s.stock_name && s.stock_name !== s.stock_code ? s.stock_name : "종목명 미확인"}
<span className="ml-1.5 text-xs text-gray-500 font-mono">{s.stock_code}</span>
{s.trade_count ? <span className="ml-1.5 text-xs text-gray-500">{s.trade_count}건</span> : null}
</span>
<span className={`shrink-0 ${s.net_pnl < 0 ? "text-red-400" : "text-green-400"}`}>{fmtKRW(s.net_pnl)}</span>
</div>
))}
</div>
@@ -475,9 +479,13 @@ export default function Analysis() {
<h2 className="text-base font-semibold text-green-400 mb-3">최대 수익 종목 Top5</h2>
<div className="space-y-2">
{perf.best_stocks.map((s) => (
<div key={s.stock_code} className="flex justify-between text-sm">
<span className="text-gray-200">{s.stock_code}</span>
<span className={s.net_pnl > 0 ? "text-green-400" : "text-red-400"}>{fmtKRW(s.net_pnl)}</span>
<div key={s.stock_code} className="flex justify-between items-baseline gap-2 text-sm">
<span className="text-gray-200 truncate">
{s.stock_name && s.stock_name !== s.stock_code ? s.stock_name : "종목명 미확인"}
<span className="ml-1.5 text-xs text-gray-500 font-mono">{s.stock_code}</span>
{s.trade_count ? <span className="ml-1.5 text-xs text-gray-500">{s.trade_count}건</span> : null}
</span>
<span className={`shrink-0 ${s.net_pnl > 0 ? "text-green-400" : "text-red-400"}`}>{fmtKRW(s.net_pnl)}</span>
</div>
))}
</div>