2026-02-13 12:17:35 +09:00
|
|
|
import { useEffect, useMemo } from "react";
|
|
|
|
|
import { type KisRuntimeCredentials } from "@/features/settings/store/use-kis-runtime-store";
|
2026-02-10 11:16:39 +09:00
|
|
|
import type {
|
|
|
|
|
DashboardRealtimeTradeTick,
|
|
|
|
|
DashboardStockOrderBookResponse,
|
2026-02-11 16:31:28 +09:00
|
|
|
} from "@/features/trade/types/trade.types";
|
2026-02-13 12:17:35 +09:00
|
|
|
import { resolveTradeTrIds } from "@/features/trade/utils/kisRealtimeUtils";
|
|
|
|
|
import { useKisWebSocketStore } from "@/features/kis-realtime/stores/kisWebSocketStore";
|
|
|
|
|
import { useDomesticSession } from "@/features/trade/hooks/useDomesticSession";
|
|
|
|
|
import { useTradeTickSubscription } from "@/features/trade/hooks/useTradeTickSubscription";
|
|
|
|
|
import { useOrderbookSubscription } from "@/features/trade/hooks/useOrderbookSubscription";
|
2026-02-12 10:24:03 +09:00
|
|
|
|
|
|
|
|
/**
|
2026-02-13 12:17:35 +09:00
|
|
|
* @description KIS 실시간 매매/호가 데이터를 통합 구독하는 커스텀 훅입니다.
|
|
|
|
|
* @summary
|
|
|
|
|
* - `useDomesticSession`: 장 운영 시간(정규장, 시간외 등) 세션 관리
|
|
|
|
|
* - `useTradeTickSubscription`: 실시간 체결가(Tick) 데이터 구독 및 상태 관리
|
|
|
|
|
* - `useOrderbookSubscription`: 실시간 호가(Orderbook) 데이터 구독
|
|
|
|
|
* - `useKisWebSocketStore`: 전역 웹소켓 연결 상태 및 에러 관리
|
|
|
|
|
*
|
|
|
|
|
* 위 훅들을 조합(Composition)하여 트레이딩 화면에 필요한 모든 실시간 데이터를 제공합니다.
|
2026-02-10 11:16:39 +09:00
|
|
|
*/
|
|
|
|
|
export function useKisTradeWebSocket(
|
|
|
|
|
symbol: string | undefined,
|
|
|
|
|
credentials: KisRuntimeCredentials | null,
|
|
|
|
|
isVerified: boolean,
|
|
|
|
|
onTick?: (tick: DashboardRealtimeTradeTick) => void,
|
|
|
|
|
options?: {
|
|
|
|
|
orderBookSymbol?: string;
|
|
|
|
|
onOrderBookMessage?: (data: DashboardStockOrderBookResponse) => void;
|
|
|
|
|
},
|
|
|
|
|
) {
|
2026-02-13 12:17:35 +09:00
|
|
|
const marketSession = useDomesticSession();
|
|
|
|
|
const { isConnected, error } = useKisWebSocketStore();
|
|
|
|
|
|
|
|
|
|
const { latestTick, recentTradeTicks, lastTickAt } = useTradeTickSubscription(
|
|
|
|
|
{
|
|
|
|
|
symbol,
|
|
|
|
|
isVerified,
|
|
|
|
|
credentials,
|
|
|
|
|
marketSession,
|
|
|
|
|
onTick,
|
|
|
|
|
},
|
2026-02-11 11:18:15 +09:00
|
|
|
);
|
2026-02-10 11:16:39 +09:00
|
|
|
|
2026-02-13 12:17:35 +09:00
|
|
|
useOrderbookSubscription({
|
|
|
|
|
symbol: options?.orderBookSymbol,
|
|
|
|
|
isVerified,
|
|
|
|
|
credentials,
|
|
|
|
|
marketSession,
|
|
|
|
|
onOrderBookMessage: options?.onOrderBookMessage,
|
|
|
|
|
});
|
2026-02-10 11:16:39 +09:00
|
|
|
|
2026-02-13 12:17:35 +09:00
|
|
|
// Connection/Data warning
|
2026-02-10 11:16:39 +09:00
|
|
|
useEffect(() => {
|
2026-02-13 12:17:35 +09:00
|
|
|
if (!isConnected || lastTickAt || !symbol) return;
|
2026-02-10 11:16:39 +09:00
|
|
|
const timer = window.setTimeout(() => {
|
2026-02-13 12:17:35 +09:00
|
|
|
// Just a warning, not blocking
|
2026-02-10 11:16:39 +09:00
|
|
|
}, 8000);
|
|
|
|
|
return () => window.clearTimeout(timer);
|
2026-02-13 12:17:35 +09:00
|
|
|
}, [isConnected, lastTickAt, symbol]);
|
2026-02-10 11:16:39 +09:00
|
|
|
|
2026-02-13 12:17:35 +09:00
|
|
|
const realtimeTrId = useMemo(() => {
|
|
|
|
|
if (!credentials) return "H0STCNT0";
|
|
|
|
|
const ids = resolveTradeTrIds(credentials.tradingEnv, marketSession);
|
|
|
|
|
return ids[0] ?? "H0STCNT0";
|
|
|
|
|
}, [credentials, marketSession]);
|
2026-02-10 11:16:39 +09:00
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
latestTick,
|
|
|
|
|
recentTradeTicks,
|
|
|
|
|
isConnected,
|
|
|
|
|
error,
|
|
|
|
|
lastTickAt,
|
2026-02-11 11:18:15 +09:00
|
|
|
realtimeTrId,
|
2026-02-10 11:16:39 +09:00
|
|
|
};
|
|
|
|
|
}
|