diff --git a/src/pages/RealtimeKline.tsx b/src/pages/RealtimeKline.tsx new file mode 100644 index 0000000..02c2964 --- /dev/null +++ b/src/pages/RealtimeKline.tsx @@ -0,0 +1,71 @@ +import { useEffect, useMemo, useRef, useState } from 'react' +import { createChart, ISeriesApi, UTCTimestamp } from 'lightweight-charts' +import { getBinanceKlines } from '../lib/api' +import { useBinanceKline } from '../lib/ws' + +export default function RealtimeKline() { + const symbol = 'BTCUSDT' + const interval = '1m' + const containerRef = useRef(null) + const seriesRef = useRef | null>(null) + const [ready, setReady] = useState(false) + + // 初始化历史K线 + useEffect(() => { + let dispose = () => {} + ;(async () => { + if (!containerRef.current) return + const chart = createChart(containerRef.current, { + autoSize: true, + layout: { textColor: '#ccc', background: { type: 'Solid', color: 'transparent' } }, + grid: { vertLines: { visible: false }, horzLines: { visible: false } }, + timeScale: { secondsVisible: false, borderVisible: false }, + rightPriceScale: { borderVisible: false }, + }) + const candle = chart.addCandlestickSeries() + seriesRef.current = candle + + const raw = await getBinanceKlines(symbol, interval, 500) + const data = raw.map((k) => ({ + time: Math.floor(k[0] / 1000) as UTCTimestamp, + open: parseFloat(k[1]), + high: parseFloat(k[2]), + low: parseFloat(k[3]), + close: parseFloat(k[4]), + })) + candle.setData(data) + setReady(true) + + const onResize = () => chart.applyOptions({ autoSize: true }) + window.addEventListener('resize', onResize) + dispose = () => { + window.removeEventListener('resize', onResize) + chart.remove() + } + })() + return () => dispose() + }, [symbol, interval]) + + // 实时推送更新 + const tick = useBinanceKline(symbol.toLowerCase(), interval) + useEffect(() => { + if (!ready || !seriesRef.current || !tick) return + seriesRef.current.update({ + time: Math.floor(tick.k.start / 1000) as UTCTimestamp, + open: tick.k.open, + high: tick.k.high, + low: tick.k.low, + close: tick.k.close, + }) + }, [tick, ready]) + + const title = useMemo(() => `${symbol} • ${interval.toUpperCase()} 实时K线`, [symbol, interval]) + + return ( +
+
{title}
+
+
数据来源:Binance WebSocket + REST
+
+ ) +}