105 lines
3.0 KiB
TypeScript
105 lines
3.0 KiB
TypeScript
/**
|
|
* 업비트 최근 체결 (호가창 좌측 체결 목록용)
|
|
*/
|
|
import { useState, useEffect, useRef, useCallback } from 'react';
|
|
import { subscribeUpbitWs } from '../utils/upbitWsBroker';
|
|
|
|
export interface RecentTrade {
|
|
price: number;
|
|
volume: number;
|
|
side: 'ask' | 'bid';
|
|
time: number;
|
|
}
|
|
|
|
const MAX_TRADES_DEFAULT = 12;
|
|
|
|
interface UpbitTradeRaw {
|
|
market: string;
|
|
trade_price: number;
|
|
trade_volume: number;
|
|
ask_bid: 'ASK' | 'BID';
|
|
timestamp: number;
|
|
}
|
|
|
|
interface UpbitTradeWs {
|
|
type: string;
|
|
code: string;
|
|
trade_price: number;
|
|
trade_volume: number;
|
|
ask_bid: 'ASK' | 'BID';
|
|
timestamp: number;
|
|
}
|
|
|
|
export function useUpbitRecentTrades(market: string, enabled = true, maxTrades = MAX_TRADES_DEFAULT) {
|
|
const [trades, setTrades] = useState<RecentTrade[]>([]);
|
|
const [strength, setStrength] = useState<number | null>(null);
|
|
const maxRef = useRef(maxTrades);
|
|
maxRef.current = maxTrades;
|
|
|
|
const mountedRef = useRef(true);
|
|
const marketRef = useRef(market);
|
|
marketRef.current = market;
|
|
|
|
const pushTrade = useCallback((t: RecentTrade) => {
|
|
setTrades(prev => {
|
|
const cap = maxRef.current;
|
|
const next = [t, ...prev].slice(0, cap);
|
|
const buyVol = next.filter(x => x.side === 'bid').reduce((s, x) => s + x.volume, 0);
|
|
const sellVol = next.filter(x => x.side === 'ask').reduce((s, x) => s + x.volume, 0);
|
|
if (sellVol > 0) setStrength((buyVol / sellVol) * 100);
|
|
else if (buyVol > 0) setStrength(100);
|
|
return next;
|
|
});
|
|
}, []);
|
|
|
|
const fetchSnapshot = useCallback(async () => {
|
|
try {
|
|
const res = await fetch(`/upbit-api/v1/trades/ticks?market=${marketRef.current}&count=${maxRef.current}`);
|
|
if (!res.ok) return;
|
|
const data: UpbitTradeRaw[] = await res.json();
|
|
const list: RecentTrade[] = data.map(d => ({
|
|
price: d.trade_price,
|
|
volume: d.trade_volume,
|
|
side: d.ask_bid === 'ASK' ? 'ask' : 'bid',
|
|
time: d.timestamp,
|
|
}));
|
|
setTrades(list);
|
|
const buyVol = list.filter(x => x.side === 'bid').reduce((s, x) => s + x.volume, 0);
|
|
const sellVol = list.filter(x => x.side === 'ask').reduce((s, x) => s + x.volume, 0);
|
|
if (sellVol > 0) setStrength((buyVol / sellVol) * 100);
|
|
} catch { /* ignore */ }
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
mountedRef.current = true;
|
|
setTrades([]);
|
|
setStrength(null);
|
|
|
|
if (!enabled) {
|
|
return () => { mountedRef.current = false; };
|
|
}
|
|
|
|
fetchSnapshot();
|
|
|
|
const unsub = subscribeUpbitWs('trade', [market], raw => {
|
|
if (!mountedRef.current) return;
|
|
const msg = raw as unknown as UpbitTradeWs;
|
|
if (msg.type === 'trade' && msg.code === marketRef.current) {
|
|
pushTrade({
|
|
price: msg.trade_price,
|
|
volume: msg.trade_volume,
|
|
side: msg.ask_bid === 'ASK' ? 'ask' : 'bid',
|
|
time: msg.timestamp,
|
|
});
|
|
}
|
|
});
|
|
|
|
return () => {
|
|
mountedRef.current = false;
|
|
unsub();
|
|
};
|
|
}, [market, enabled, fetchSnapshot, pushTrade]);
|
|
|
|
return { trades, strength };
|
|
}
|