import type { TickerData } from '../hooks/useMarketTicker'; import { getKoreanName } from '../utils/marketNameCache'; import { safePercentFromRate } from '../utils/safeFormat'; export type MoverSortKey = 'gainers' | 'losers' | 'volume'; export function tickerListFromMap(tickers: Map): TickerData[] { return [...tickers.values()].filter(t => t.market.startsWith('KRW-') && t.tradePrice != null); } export function sortTickers(items: TickerData[], key: MoverSortKey, limit = 15): TickerData[] { const copy = [...items]; if (key === 'gainers') { return copy .filter(t => (t.changeRate ?? 0) > 0) .sort((a, b) => (b.changeRate ?? 0) - (a.changeRate ?? 0)) .slice(0, limit); } if (key === 'losers') { return copy .filter(t => (t.changeRate ?? 0) < 0) .sort((a, b) => (a.changeRate ?? 0) - (b.changeRate ?? 0)) .slice(0, limit); } return copy .sort((a, b) => (b.accTradePrice24 ?? 0) - (a.accTradePrice24 ?? 0)) .slice(0, limit); } export function changeColorClass(rate: number | null | undefined): string { if (rate == null || rate === 0) return 'wd-muted'; return rate > 0 ? 'wd-up' : 'wd-down'; } export function fmtPct(rate: number | null | undefined): string { return safePercentFromRate(rate, 2).replace('—', '-'); } export function fmtKrwShort(n: number | null | undefined): string { if (n == null || !Number.isFinite(n)) return '-'; if (n >= 1_0000_0000) return `${(n / 1_0000_0000).toFixed(1)}억`; if (n >= 1_0000) return `${Math.round(n / 1_0000).toLocaleString('ko-KR')}만`; return n.toLocaleString('ko-KR', { maximumFractionDigits: 0 }); } export function resolveName(market: string, ticker?: TickerData): string { return ticker?.koreanName || getKoreanName(market) || market.replace('KRW-', ''); } export function heatmapColor(rate: number | null | undefined): string { if (rate == null || rate === 0) return 'rgba(120, 130, 150, 0.35)'; const intensity = Math.min(1, Math.abs(rate) * 8); if (rate > 0) return `rgba(231, 76, 60, ${0.25 + intensity * 0.55})`; return `rgba(52, 152, 219, ${0.25 + intensity * 0.55})`; }