Files
goldenChart/frontend/src/utils/upbitTickerFetch.ts
T
2026-05-31 23:15:19 +09:00

97 lines
3.0 KiB
TypeScript

/**
* KRW ticker 조회 — 백엔드 /api/markets/tickers 우선 (1회·캐시), 실패 시 /upbit-api 폴백
*/
import { API_BASE } from './backendApi';
import { UPBIT_API } from './upbitApi';
export interface UpbitTickerRaw {
market?: string;
code?: string;
trade_price: number;
signed_change_rate: number;
signed_change_price: number;
acc_trade_price_24h: number;
acc_trade_volume_24h?: number;
opening_price: number;
high_price: number;
low_price: number;
change?: 'RISE' | 'FALL' | 'EVEN';
}
const CHUNK_SIZE = 40;
const CHUNK_DELAY_MS = 200;
const MAX_CHUNK_ATTEMPTS = 4;
function sleep(ms: number): Promise<void> {
return new Promise(r => setTimeout(r, ms));
}
/** 백엔드 일괄 ticker (권장) */
async function fetchTickersFromBackend(markets: string[]): Promise<UpbitTickerRaw[] | null> {
try {
const qs = markets.length > 0
? `?markets=${encodeURIComponent(markets.join(','))}`
: '';
const res = await fetch(`${API_BASE}/markets/tickers${qs}`);
if (res.status === 429) {
await sleep(1500);
const retry = await fetch(`${API_BASE}/markets/tickers${qs}`);
if (!retry.ok) return null;
const data = (await retry.json()) as UpbitTickerRaw[];
return Array.isArray(data) ? data : null;
}
if (!res.ok) return null;
const data = (await res.json()) as UpbitTickerRaw[];
return Array.isArray(data) ? data : null;
} catch {
return null;
}
}
/** 레거시: nginx /upbit-api 직접 (청크·지연) */
async function fetchTickersFromUpbitDirect(markets: string[]): Promise<UpbitTickerRaw[]> {
const unique = [...new Set(markets.filter(m => m?.startsWith('KRW-')))];
if (unique.length === 0) return [];
const results: UpbitTickerRaw[] = [];
for (let i = 0; i < unique.length; i += CHUNK_SIZE) {
const chunk = unique.slice(i, i + CHUNK_SIZE);
let done = false;
for (let attempt = 0; attempt < MAX_CHUNK_ATTEMPTS && !done; attempt++) {
try {
const res = await fetch(`${UPBIT_API}/ticker?markets=${chunk.join(',')}`);
if (res.status === 429) {
await sleep(800 * (attempt + 1));
continue;
}
if (res.ok) {
const data = (await res.json()) as UpbitTickerRaw[];
if (Array.isArray(data)) results.push(...data);
}
done = true;
} catch {
await sleep(400 * (attempt + 1));
}
}
if (i + CHUNK_SIZE < unique.length) await sleep(CHUNK_DELAY_MS);
}
return results;
}
/**
* ticker 조회 — 전 종목은 백엔드 1회, 소량은 markets 쿼리
*/
export async function fetchTickersThrottled(markets: string[]): Promise<UpbitTickerRaw[]> {
const unique = [...new Set(markets.filter(m => m?.startsWith('KRW-')))];
const fromBackend = await fetchTickersFromBackend(unique);
if (fromBackend != null && fromBackend.length > 0) return fromBackend;
if (unique.length === 0) {
const all = await fetchTickersFromBackend([]);
if (all != null && all.length > 0) return all;
}
return fetchTickersFromUpbitDirect(unique);
}