goldenChat base source add

This commit is contained in:
aidev
2026-05-23 15:11:48 +09:00
commit a4ea7762b5
2081 changed files with 1155760 additions and 0 deletions
+119
View File
@@ -0,0 +1,119 @@
/**
* 업비트 최근 체결 (호가창 좌측 체결 목록용)
*/
import { useState, useEffect, useRef, useCallback } from 'react';
export interface RecentTrade {
price: number;
volume: number;
side: 'ask' | 'bid'; // ask=매도체결, bid=매수체결
time: number;
}
const MAX_TRADES = 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) {
const [trades, setTrades] = useState<RecentTrade[]>([]);
const [strength, setStrength] = useState<number | null>(null);
const wsRef = useRef<WebSocket | null>(null);
const mountedRef = useRef(true);
const pushTrade = useCallback((t: RecentTrade) => {
setTrades(prev => {
const next = [t, ...prev].slice(0, MAX_TRADES);
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=${market}&count=${MAX_TRADES}`);
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 */ }
}, [market]);
useEffect(() => {
mountedRef.current = true;
setTrades([]);
setStrength(null);
fetchSnapshot();
const proto = window.location.protocol === 'https:' ? 'wss' : 'ws';
const ws = new WebSocket(`${proto}://${window.location.host}/upbit-ws`);
ws.binaryType = 'arraybuffer';
wsRef.current = ws;
ws.onopen = () => {
ws.send(JSON.stringify([
{ ticket: `tr-${market}-${Date.now()}` },
{ type: 'trade', codes: [market] },
]));
};
ws.onmessage = async (e: MessageEvent) => {
if (!mountedRef.current) return;
try {
let text: string;
if (typeof e.data === 'string') text = e.data;
else if (e.data instanceof ArrayBuffer) text = new TextDecoder().decode(e.data);
else if (e.data instanceof Blob) text = await e.data.text();
else return;
const msg: UpbitTradeWs = JSON.parse(text);
if (msg.type === 'trade' && msg.code === market) {
pushTrade({
price: msg.trade_price,
volume: msg.trade_volume,
side: msg.ask_bid === 'ASK' ? 'ask' : 'bid',
time: msg.timestamp,
});
}
} catch { /* ignore */ }
};
return () => {
mountedRef.current = false;
if (wsRef.current) {
wsRef.current.onopen = null;
wsRef.current.onmessage = null;
wsRef.current.onclose = null;
wsRef.current.close();
wsRef.current = null;
}
};
}, [market, fetchSnapshot, pushTrade]);
return { trades, strength };
}