import { useCallback, useState, useTransition } from "react"; import type { KisRuntimeCredentials } from "@/features/dashboard/store/use-kis-runtime-store"; import type { DashboardMarketPhase, DashboardPriceSource, DashboardRealtimeTradeTick, DashboardStockSearchItem, DashboardStockItem, } from "@/features/dashboard/types/dashboard.types"; import { fetchStockOverview } from "@/features/dashboard/apis/kis-stock.api"; interface OverviewMeta { priceSource: DashboardPriceSource; marketPhase: DashboardMarketPhase; fetchedAt: string; } export function useStockOverview() { const [selectedStock, setSelectedStock] = useState( null, ); const [meta, setMeta] = useState(null); const [error, setError] = useState(null); const [isLoading, startTransition] = useTransition(); const loadOverview = useCallback( ( symbol: string, credentials: KisRuntimeCredentials | null, marketHint?: DashboardStockSearchItem["market"], ) => { if (!credentials) return; startTransition(async () => { try { setError(null); const data = await fetchStockOverview(symbol, credentials); setSelectedStock({ ...data.stock, market: marketHint ?? data.stock.market, }); setMeta({ priceSource: data.priceSource, marketPhase: data.marketPhase, fetchedAt: data.fetchedAt, }); } catch (err) { const message = err instanceof Error ? err.message : "종목 조회 중 오류가 발생했습니다."; setError(message); setMeta(null); } }); }, [], ); // 실시간 체결 데이터 수신 시 헤더/차트 기준 가격을 갱신합니다. const updateRealtimeTradeTick = useCallback( (tick: DashboardRealtimeTradeTick) => { setSelectedStock((prev) => { if (!prev) return prev; const { price, accumulatedVolume, change, changeRate, tickTime } = tick; const pointTime = tickTime && tickTime.length === 6 ? `${tickTime.slice(0, 2)}:${tickTime.slice(2, 4)}` : "실시간"; const nextChange = change; const nextChangeRate = Number.isFinite(changeRate) ? changeRate : prev.prevClose > 0 ? (nextChange / prev.prevClose) * 100 : prev.changeRate; const nextHigh = prev.high > 0 ? Math.max(prev.high, price) : price; const nextLow = prev.low > 0 ? Math.min(prev.low, price) : price; const nextCandles = prev.candles.length > 0 && prev.candles[prev.candles.length - 1]?.time === pointTime ? [ ...prev.candles.slice(0, -1), { ...prev.candles[prev.candles.length - 1], time: pointTime, price, }, ] : [...prev.candles, { time: pointTime, price }].slice(-80); return { ...prev, currentPrice: price, change: nextChange, changeRate: nextChangeRate, high: nextHigh, low: nextLow, volume: accumulatedVolume > 0 ? accumulatedVolume : prev.volume, candles: nextCandles, }; }); }, [], ); return { selectedStock, setSelectedStock, meta, setMeta, error, setError, isLoading, loadOverview, updateRealtimeTradeTick, }; }