import ScoreChips from './ScoreChips'; import { useIsMobile } from '../../../../hooks/useIsMobile'; const COL_TIPS = { rank: '순위 — 종합 점수가 높은 순서', name: '종목명과 종목 코드', total: '종합 점수 (0~100) — 활성 점수 노드들의 가중평균. 가중치는 좌측 패널에서 조정', nodes: '노드별 점수 칩 — 70점 이상이면 노란색 강조. 각 칩에 마우스 올리면 해당 노드 설명이 나옵니다', entry: '예상 진입가 (원) — 현재 종가의 +0.5%, 다음날 시초가 슬리피지 가정', stop: '손절가 (원) — 현재가 - 2 × ATR(14, Wilder smoothing). 변동성 기반 손절', target: '익절가 (원) — 진입가 + (진입가 - 손절가) × R:R 비율 (기본 2.0). 위험 1 대비 보상 2', r_pct: '손실 위험 % — (진입가 - 손절가) / 진입가 × 100. 클수록 변동성 큰 종목', delta_rank: '비교 대상 대비 순위 변화 — ▲(상승)·▼(하락)·NEW(이번에 새로 진입)·OUT(비교 대상에만 있음)', delta_score: '비교 대상 대비 점수 변화 — 양수면 상승', }; function Th({ k, children }) { return ( {children} ); } function buildCompareIndex(compareWith) { if (!compareWith?.results) return null; const idx = new Map(); for (const r of compareWith.results) idx.set(r.ticker, r); return idx; } function DeltaRank({ current, prev }) { if (!prev) { return NEW; } const diff = prev.rank - current.rank; if (diff === 0) return ; const up = diff > 0; return ( {up ? '▲' : '▼'} {Math.abs(diff)} ); } function DeltaScore({ current, prev }) { if (!prev) return -; const d = (current.total_score ?? 0) - (prev.total_score ?? 0); if (Math.abs(d) < 0.1) return ; const up = d > 0; return ( {up ? '+' : ''}{d.toFixed(1)} ); } function MobileCard({ r, prev, hasCompare }) { return (
#{r.rank}
{r.name}
{r.ticker}
{r.total_score?.toFixed(1)}
총점
{hasCompare && (
순위 점수
)}
진입{r.entry_price?.toLocaleString?.()}원
손절{r.stop_price?.toLocaleString?.()}원
익절{r.target_price?.toLocaleString?.()}원
위험{r.r_pct?.toFixed?.(1)}%
); } function MobileOutCard({ r }) { return (
OUT
{r.name}
{r.ticker}
{r.total_score?.toFixed(1)}
이전
); } export default function ResultTable({ result, compareWith, compareLabel }) { const isMobile = useIsMobile(); if (!result) { return (

아직 결과 없음. "지금 실행"을 눌러보세요.

💡 컬럼/칩에 마우스를 올리면 의미가 표시됩니다 (PC).

); } const cmpIdx = buildCompareIndex(compareWith); const hasCompare = !!cmpIdx; const currentTickers = new Set((result.results || []).map((r) => r.ticker)); const onlyInCompare = hasCompare ? (compareWith.results || []).filter((r) => !currentTickers.has(r.ticker)) : []; return (

Top {result.top_n} · 통과 {result.survivors_count} · {result.asof} {hasCompare && ( vs {compareLabel ?? '비교 대상'} (통과 {compareWith.survivors_count}) )}

{result.warnings?.length > 0 && (
⚠ {result.warnings.join(' · ')}
)}

{isMobile ? `💡 종목 카드를 위아래로 스크롤하며 확인${hasCompare ? ' · 비교 모드 ON' : ''}` : `💡 컬럼/칩에 마우스를 올리면 의미가 표시됩니다${hasCompare ? ' · 비교 모드 ON — ▲▼NEW/OUT 변화 표시' : ''}`}

{isMobile ? (
{(result.results || []).map((r) => ( ))} {hasCompare && onlyInCompare.length > 0 && ( <>
── 이번엔 빠진 종목 ──
{onlyInCompare.map((r) => )} )}
) : (
{hasCompare && } {hasCompare && } {(result.results || []).map((r) => { const prev = cmpIdx?.get(r.ticker); return ( {hasCompare && } {hasCompare && } ); })} {hasCompare && onlyInCompare.length > 0 && ( <> {onlyInCompare.map((r) => ( ))} )}
# 종목 총점순위Δ점수Δ노드 진입(원) 손절(원) 익절(원) R%
{r.rank} {r.name}
{r.ticker}
{r.total_score?.toFixed(1)} {r.entry_price?.toLocaleString?.()} {r.stop_price?.toLocaleString?.()} {r.target_price?.toLocaleString?.()} {r.r_pct?.toFixed?.(1)}
── 이번엔 빠진 종목 (비교 대상에만 존재) ──
{r.name}
{r.ticker}
{r.total_score?.toFixed(1)} OUT
)}
); }