105 lines
3.1 KiB
TypeScript
105 lines
3.1 KiB
TypeScript
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<DashboardStockItem | null>(
|
|
null,
|
|
);
|
|
const [meta, setMeta] = useState<OverviewMeta | null>(null);
|
|
const [error, setError] = useState<string | null>(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);
|
|
}
|
|
});
|
|
},
|
|
[],
|
|
);
|
|
|
|
/**
|
|
* 실시간 체결 수신 시 헤더/주요 시세 상태만 갱신합니다.
|
|
* 차트 캔들은 StockLineChart 내부 API 응답을 기준으로 유지합니다.
|
|
* @see features/dashboard/components/DashboardContainer.tsx useKisTradeWebSocket onTick 전달
|
|
* @see features/dashboard/components/chart/StockLineChart.tsx 차트 데이터 fetchStockChart 기준 렌더링
|
|
*/
|
|
const updateRealtimeTradeTick = useCallback(
|
|
(tick: DashboardRealtimeTradeTick) => {
|
|
setSelectedStock((prev) => {
|
|
if (!prev) return prev;
|
|
|
|
const { price, accumulatedVolume, change, changeRate } = tick;
|
|
const nextChange = change;
|
|
const nextChangeRate = Number.isFinite(changeRate)
|
|
? changeRate
|
|
: prev.prevClose > 0
|
|
? (nextChange / prev.prevClose) * 100
|
|
: prev.changeRate;
|
|
|
|
return {
|
|
...prev,
|
|
currentPrice: price,
|
|
change: nextChange,
|
|
changeRate: nextChangeRate,
|
|
high: prev.high > 0 ? Math.max(prev.high, price) : price,
|
|
low: prev.low > 0 ? Math.min(prev.low, price) : price,
|
|
volume: accumulatedVolume > 0 ? accumulatedVolume : prev.volume,
|
|
};
|
|
});
|
|
},
|
|
[],
|
|
);
|
|
|
|
return {
|
|
selectedStock,
|
|
setSelectedStock,
|
|
meta,
|
|
setMeta,
|
|
error,
|
|
setError,
|
|
isLoading,
|
|
loadOverview,
|
|
updateRealtimeTradeTick,
|
|
};
|
|
}
|