차트 수정

This commit is contained in:
2026-02-11 11:18:15 +09:00
parent e5a518b211
commit 89bad1d141
13 changed files with 927 additions and 333 deletions

View File

@@ -9,6 +9,10 @@ import {
hasKisConfig,
normalizeTradingEnv,
} from "@/lib/kis/config";
import {
DOMESTIC_KIS_SESSION_OVERRIDE_HEADER,
parseDomesticKisSession,
} from "@/lib/kis/domestic-market-session";
/**
* @file app/api/kis/domestic/orderbook/route.ts
@@ -38,15 +42,22 @@ export async function GET(request: NextRequest) {
}
try {
const raw = await getDomesticOrderBook(symbol, credentials);
const sessionOverride = readSessionOverrideFromHeaders(request.headers);
const raw = await getDomesticOrderBook(symbol, credentials, {
sessionOverride,
});
const levels = Array.from({ length: 10 }, (_, i) => {
const idx = i + 1;
return {
askPrice: readOrderBookNumber(raw, `askp${idx}`),
bidPrice: readOrderBookNumber(raw, `bidp${idx}`),
askSize: readOrderBookNumber(raw, `askp_rsqn${idx}`),
bidSize: readOrderBookNumber(raw, `bidp_rsqn${idx}`),
askPrice: readOrderBookNumber(raw, `askp${idx}`, `ovtm_untp_askp${idx}`),
bidPrice: readOrderBookNumber(raw, `bidp${idx}`, `ovtm_untp_bidp${idx}`),
askSize: readOrderBookNumber(
raw,
`askp_rsqn${idx}`,
`ovtm_untp_askp_rsqn${idx}`,
),
bidSize: readOrderBookNumber(raw, ...resolveBidSizeKeys(idx)),
};
});
@@ -54,8 +65,20 @@ export async function GET(request: NextRequest) {
symbol,
source: "kis",
levels,
totalAskSize: readOrderBookNumber(raw, "total_askp_rsqn"),
totalBidSize: readOrderBookNumber(raw, "total_bidp_rsqn"),
totalAskSize: readOrderBookNumber(
raw,
"total_askp_rsqn",
"ovtm_untp_total_askp_rsqn",
"ovtm_total_askp_rsqn",
),
totalBidSize: readOrderBookNumber(
raw,
"total_bidp_rsqn",
"ovtm_untp_total_bidp_rsqn",
"ovtm_total_bidp_rsqn",
),
businessHour: readOrderBookString(raw, "bsop_hour", "ovtm_untp_last_hour"),
hourClassCode: readOrderBookString(raw, "hour_cls_code"),
tradingEnv: normalizeTradingEnv(credentials.tradingEnv),
fetchedAt: new Date().toISOString(),
};
@@ -88,15 +111,19 @@ function readKisCredentialsFromHeaders(headers: Headers): KisCredentialInput {
};
}
function readSessionOverrideFromHeaders(headers: Headers) {
if (process.env.NODE_ENV === "production") return null;
const raw = headers.get(DOMESTIC_KIS_SESSION_OVERRIDE_HEADER);
return parseDomesticKisSession(raw);
}
/**
* @description 호가 응답 필드를 대소문자 모두 허용해 숫자로 읽습니다.
* @see app/api/kis/domestic/orderbook/route.ts GET에서 output/output1 키 차이 방어 로직으로 사용합니다.
*/
function readOrderBookNumber(raw: KisDomesticOrderBookOutput, key: string) {
function readOrderBookNumber(raw: KisDomesticOrderBookOutput, ...keys: string[]) {
const record = raw as Record<string, unknown>;
const direct = record[key];
const upper = record[key.toUpperCase()];
const value = direct ?? upper ?? "0";
const value = resolveOrderBookValue(record, keys) ?? "0";
const normalized =
typeof value === "string"
? value.replaceAll(",", "").trim()
@@ -104,3 +131,35 @@ function readOrderBookNumber(raw: KisDomesticOrderBookOutput, key: string) {
const parsed = Number(normalized);
return Number.isFinite(parsed) ? parsed : 0;
}
/**
* @description 호가 응답 필드를 문자열로 읽습니다.
* @see app/api/kis/domestic/orderbook/route.ts GET 응답 생성 시 businessHour/hourClassCode 추출
*/
function readOrderBookString(raw: KisDomesticOrderBookOutput, ...keys: string[]) {
const record = raw as Record<string, unknown>;
const value = resolveOrderBookValue(record, keys);
if (value === undefined || value === null) return undefined;
const text = String(value).trim();
return text.length > 0 ? text : undefined;
}
function resolveOrderBookValue(record: Record<string, unknown>, keys: string[]) {
for (const key of keys) {
const direct = record[key];
if (direct !== undefined && direct !== null) return direct;
const upper = record[key.toUpperCase()];
if (upper !== undefined && upper !== null) return upper;
}
return undefined;
}
function resolveBidSizeKeys(index: number) {
if (index === 2) {
return [`bidp_rsqn${index}`, `ovtm_untp_bidp_rsqn${index}`, "ovtm_untp_bidp_rsqn"];
}
return [`bidp_rsqn${index}`, `ovtm_untp_bidp_rsqn${index}`];
}