실시간 웹소켓 리팩토링

This commit is contained in:
2026-02-23 15:37:22 +09:00
parent 276ef09d89
commit c17797061e
8 changed files with 408 additions and 71 deletions

View File

@@ -56,6 +56,16 @@ const TRADE_TR_ID_TOTAL_EXPECTED = "H0UNANC0";
const ORDERBOOK_TR_ID = "H0STASP0";
const ORDERBOOK_TR_ID_TOTAL = "H0UNASP0";
const ORDERBOOK_TR_ID_OVERTIME = "H0STOAA0";
const DEFAULT_REALTIME_TR_STALE_MS = 3_000;
interface RealtimeTrPriorityDecisionParams {
incomingTrId: string;
preferredTrIds: string[];
activeTrId: string | null;
activeTrUpdatedAtMs: number;
nowMs?: number;
staleAfterMs?: number;
}
export function parseKisRealtimeTickBatch(raw: string, expectedSymbol: string) {
if (!/^([01])\|/.test(raw)) return [] as DashboardRealtimeTradeTick[];
@@ -197,6 +207,7 @@ export function resolveTradeTrIds(
export function resolveOrderBookTrIds(
env: "real" | "mock",
session: DomesticKisSession,
market?: "KOSPI" | "KOSDAQ",
) {
if (env === "mock") return [ORDERBOOK_TR_ID];
@@ -218,9 +229,60 @@ export function resolveOrderBookTrIds(
]);
}
// 통합장(통합호가) 값이 체결앱과 더 잘 맞는 케이스가 있어
// KOSPI는 통합(H0UNASP0) 우선, KOSDAQ은 KRX(H0STASP0) 우선으로 둡니다.
if (market === "KOSPI") {
return uniqueTrIds([ORDERBOOK_TR_ID_TOTAL, ORDERBOOK_TR_ID]);
}
return uniqueTrIds([ORDERBOOK_TR_ID, ORDERBOOK_TR_ID_TOTAL]);
}
/**
* @description 웹소켓 원문에서 TR ID만 빠르게 추출합니다.
* @param raw KIS 웹소켓 원문
* @returns TR ID. 포맷이 다르면 null
* @see features/trade/hooks/useTradeTickSubscription.ts handleTradeMessage 소스 우선순위 필터
* @see features/trade/hooks/useOrderbookSubscription.ts handleOrderBookMessage 소스 우선순위 필터
*/
export function extractKisRealtimeTrId(raw: string) {
if (!/^([01])\|/.test(raw)) return null;
const parts = raw.split("|", 3);
const trId = parts[1]?.trim();
return trId ? trId : null;
}
/**
* @description 다중 TR 구독 시 우선순위/유휴시간 기반으로 현재 메시지를 반영할지 결정합니다.
* @remarks 높은 우선순위 TR(ST 계열)은 즉시 승격하고, 현재 소스가 일정 시간 멈췄을 때만 하위 TR(UN 계열)로 폴백합니다.
* @see features/trade/hooks/useTradeTickSubscription.ts handleTradeMessage 체결 소스 고정 로직
* @see features/trade/hooks/useOrderbookSubscription.ts handleOrderBookMessage 호가 소스 고정 로직
*/
export function shouldAcceptRealtimeMessageByPriority({
incomingTrId,
preferredTrIds,
activeTrId,
activeTrUpdatedAtMs,
nowMs = Date.now(),
staleAfterMs = DEFAULT_REALTIME_TR_STALE_MS,
}: RealtimeTrPriorityDecisionParams) {
const incomingPriority = preferredTrIds.indexOf(incomingTrId);
if (incomingPriority < 0) return false;
if (!activeTrId) return true;
if (incomingTrId === activeTrId) return true;
const activePriority = preferredTrIds.indexOf(activeTrId);
if (activePriority < 0) return true;
if (incomingPriority < activePriority) {
return true;
}
const isActiveStale = nowMs - activeTrUpdatedAtMs > staleAfterMs;
return isActiveStale;
}
/**
* @description 호가 패킷이 실제 표시 가능한 값(호가/잔량/총잔량)을 포함하는지 확인합니다.
*/