70 lines
2.2 KiB
TypeScript
70 lines
2.2 KiB
TypeScript
|
|
export interface KisRealtimeStockTick {
|
||
|
|
symbol: string; // 종목코드
|
||
|
|
time: string; // 체결시간
|
||
|
|
currentPrice: number; // 현재가
|
||
|
|
sign: string; // 전일대비부호 (1:상한, 2:상승, 3:보합, 4:하한, 5:하락)
|
||
|
|
change: number; // 전일대비
|
||
|
|
changeRate: number; // 전일대비율
|
||
|
|
accumulatedVolume: number; // 누적거래량
|
||
|
|
}
|
||
|
|
|
||
|
|
const STOCK_realtime_TR_ID = "H0STCNT0";
|
||
|
|
|
||
|
|
// H0STCNT0 Output format indices based on typical KIS Realtime API
|
||
|
|
// Format: MKSC_SHRN_ISCD^STCK_CNTG_HOUR^STCK_PRPR^PRDY_VRSS_SIGN^PRDY_VRSS^PRDY_CTRT^...
|
||
|
|
const STOCK_FIELD_INDEX = {
|
||
|
|
symbol: 0, // MKSC_SHRN_ISCD
|
||
|
|
time: 1, // STCK_CNTG_HOUR
|
||
|
|
currentPrice: 2, // STCK_PRPR
|
||
|
|
sign: 3, // PRDY_VRSS_SIGN
|
||
|
|
change: 4, // PRDY_VRSS
|
||
|
|
changeRate: 5, // PRDY_CTRT
|
||
|
|
accumulatedVolume: 12, // ACML_VOL (Usually at index 12 or similar, need to be careful here)
|
||
|
|
} as const;
|
||
|
|
|
||
|
|
export function parseKisRealtimeStockTick(
|
||
|
|
raw: string,
|
||
|
|
): KisRealtimeStockTick | null {
|
||
|
|
// Format: 0|H0STCNT0|001|SYMBOL^TIME^PRICE^SIGN^CHANGE^...
|
||
|
|
if (!/^([01])\|/.test(raw)) return null;
|
||
|
|
|
||
|
|
const parts = raw.split("|");
|
||
|
|
if (parts.length < 4) return null;
|
||
|
|
|
||
|
|
// Check TR ID
|
||
|
|
if (parts[1] !== STOCK_realtime_TR_ID) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
const values = parts[3].split("^");
|
||
|
|
if (values.length < 6) return null; // Ensure minimum fields exist
|
||
|
|
|
||
|
|
const symbol = values[STOCK_FIELD_INDEX.symbol];
|
||
|
|
const currentPrice = parseFloat(values[STOCK_FIELD_INDEX.currentPrice]);
|
||
|
|
const sign = values[STOCK_FIELD_INDEX.sign];
|
||
|
|
const changeRaw = parseFloat(values[STOCK_FIELD_INDEX.change]);
|
||
|
|
const changeRateRaw = parseFloat(values[STOCK_FIELD_INDEX.changeRate]);
|
||
|
|
|
||
|
|
// Adjust sign for negative values if necessary
|
||
|
|
const isNegative = sign === "5" || sign === "4"; // 5: 하락, 4: 하한
|
||
|
|
|
||
|
|
const change = isNegative ? -Math.abs(changeRaw) : Math.abs(changeRaw);
|
||
|
|
const changeRate = isNegative
|
||
|
|
? -Math.abs(changeRateRaw)
|
||
|
|
: Math.abs(changeRateRaw);
|
||
|
|
|
||
|
|
// Validate numeric values
|
||
|
|
if (isNaN(currentPrice)) return null;
|
||
|
|
|
||
|
|
return {
|
||
|
|
symbol,
|
||
|
|
time: values[STOCK_FIELD_INDEX.time],
|
||
|
|
currentPrice,
|
||
|
|
sign,
|
||
|
|
change,
|
||
|
|
changeRate,
|
||
|
|
accumulatedVolume:
|
||
|
|
parseFloat(values[STOCK_FIELD_INDEX.accumulatedVolume]) || 0,
|
||
|
|
};
|
||
|
|
}
|