대시보드 실시간 기능 추가
This commit is contained in:
62
features/dashboard/utils/kis-index-realtime.utils.ts
Normal file
62
features/dashboard/utils/kis-index-realtime.utils.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
export interface KisRealtimeIndexTick {
|
||||
symbol: string; // 업종코드 (0001: KOSPI, 1001: KOSDAQ)
|
||||
price: number; // 현재가
|
||||
change: number; // 전일대비
|
||||
changeRate: number; // 전일대비율
|
||||
sign: string; // 대비부호
|
||||
time: string; // 체결시간
|
||||
}
|
||||
|
||||
const INDEX_REALTIME_TR_ID = "H0UPCNT0";
|
||||
|
||||
const INDEX_FIELD_INDEX = {
|
||||
symbol: 0, // bstp_cls_code
|
||||
time: 1, // bsop_hour
|
||||
price: 2, // prpr_nmix
|
||||
sign: 3, // prdy_vrss_sign
|
||||
change: 4, // bstp_nmix_prdy_vrss
|
||||
accumulatedVolume: 5, // acml_vol
|
||||
accumulatedAmount: 6, // acml_tr_pbmn
|
||||
changeRate: 9, // prdy_ctrt
|
||||
} as const;
|
||||
|
||||
export function parseKisRealtimeIndexTick(
|
||||
raw: string,
|
||||
): KisRealtimeIndexTick | null {
|
||||
// Format: 0|H0UPCNT0|001|0001^123456^...
|
||||
if (!/^([01])\|/.test(raw)) return null;
|
||||
|
||||
const parts = raw.split("|");
|
||||
if (parts.length < 4) return null;
|
||||
|
||||
// Check TR ID
|
||||
if (parts[1] !== INDEX_REALTIME_TR_ID) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const values = parts[3].split("^");
|
||||
if (values.length < 10) return null; // Ensure minimum fields exist
|
||||
|
||||
const symbol = values[INDEX_FIELD_INDEX.symbol];
|
||||
const price = parseFloat(values[INDEX_FIELD_INDEX.price]);
|
||||
const sign = values[INDEX_FIELD_INDEX.sign];
|
||||
const changeRaw = parseFloat(values[INDEX_FIELD_INDEX.change]);
|
||||
const changeRateRaw = parseFloat(values[INDEX_FIELD_INDEX.changeRate]);
|
||||
|
||||
// Adjust sign for negative values if necessary (usually API sends absolute values for change)
|
||||
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);
|
||||
|
||||
return {
|
||||
symbol,
|
||||
time: values[INDEX_FIELD_INDEX.time],
|
||||
price,
|
||||
change,
|
||||
changeRate,
|
||||
sign,
|
||||
};
|
||||
}
|
||||
69
features/dashboard/utils/kis-stock-realtime.utils.ts
Normal file
69
features/dashboard/utils/kis-stock-realtime.utils.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
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,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user