Files
wtf-frontend/apps/web/src/components/TradingViewChart.tsx
T

157 lines
5.2 KiB
TypeScript

import React, { useEffect, useRef, useState } from 'react';
import { createChart, ColorType, IChartApi, ISeriesApi, CandlestickSeries } from 'lightweight-charts';
import { Activity, RefreshCw } from 'lucide-react';
interface TradingViewChartProps {
marketAddress: string;
outcomeIndex: number;
outcomeLabel: string;
currentPrice: number;
}
export const TradingViewChart: React.FC<TradingViewChartProps> = ({
marketAddress,
outcomeIndex,
outcomeLabel,
currentPrice
}) => {
const chartContainerRef = useRef<HTMLDivElement>(null);
const chartRef = useRef<IChartApi | null>(null);
const seriesRef = useRef<ISeriesApi<'Candlestick'> | null>(null);
const [timeframe, setTimeframe] = useState<string>('5s');
useEffect(() => {
if (!chartContainerRef.current) return;
// 1. 初始化 TradingView Lightweight Chart
const chart = createChart(chartContainerRef.current, {
layout: {
background: { type: ColorType.Solid, color: '#090A0F' },
textColor: '#94A3B8',
fontSize: 11,
},
grid: {
vertLines: { color: '#131722' },
horzLines: { color: '#131722' },
},
width: chartContainerRef.current.clientWidth,
height: 280,
timeScale: {
timeVisible: true,
secondsVisible: true,
borderColor: '#1E293B',
},
rightPriceScale: {
borderColor: '#1E293B',
scaleMargins: {
top: 0.1,
bottom: 0.1,
},
},
crosshair: {
vertLine: { color: '#38BDF8', width: 1, style: 2 },
horzLine: { color: '#38BDF8', width: 1, style: 2 },
},
});
// 2. 添加专业蜡烛图 (Candlestick Series - GMGN / Pump.fun 风格)
const candlestickSeries = chart.addSeries(CandlestickSeries, {
upColor: '#10B981', // 涨 (绿色)
downColor: '#F43F5E', // 跌 (红色)
borderVisible: false,
wickUpColor: '#10B981',
wickDownColor: '#F43F5E',
});
chartRef.current = chart;
seriesRef.current = candlestickSeries;
// 3. 生成初始秒级 K 线柱子
const now = Math.floor(Date.now() / 1000);
const initialData = [];
let p = currentPrice > 0 ? currentPrice : 0.50;
for (let i = 80; i >= 0; i--) {
const t = (now - i * 5) as any;
const noise = (Math.sin(i * 0.3) * 0.02) + ((Math.random() - 0.5) * 0.015);
const open = Math.max(0.01, Math.min(0.99, p + noise));
const close = Math.max(0.01, Math.min(0.99, open + ((Math.random() - 0.48) * 0.02)));
const high = Math.max(open, close) + Math.random() * 0.01;
const low = Math.min(open, close) - Math.random() * 0.01;
initialData.push({ time: t, open, high, low, close });
p = close;
}
candlestickSeries.setData(initialData);
chart.timeScale().fitContent();
// 4. 自适应窗口大小
const handleResize = () => {
if (chartContainerRef.current && chartRef.current) {
chartRef.current.applyOptions({ width: chartContainerRef.current.clientWidth });
}
};
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
chart.remove();
};
}, [marketAddress, outcomeIndex]);
// 5. 实时价格脉冲更新
useEffect(() => {
if (seriesRef.current && currentPrice > 0) {
const now = Math.floor(Date.now() / 1000) as any;
seriesRef.current.update({
time: now,
open: currentPrice,
high: currentPrice * 1.01,
low: currentPrice * 0.99,
close: currentPrice,
});
}
}, [currentPrice]);
return (
<div className="bg-[#0f111d] border border-slate-800 rounded-3xl p-5 shadow-2xl space-y-4">
<div className="flex items-center justify-between border-b border-slate-800/80 pb-3.5">
<div className="flex items-center space-x-3">
<div className="p-2 bg-cyan-500/10 rounded-xl border border-cyan-500/20 text-cyan-400">
<Activity className="w-4 h-4" />
</div>
<div>
<div className="flex items-center space-x-2">
<span className="font-extrabold text-sm text-white">{outcomeLabel} 实时 K 线行情</span>
<span className="font-mono text-xs font-bold px-2 py-0.5 rounded bg-emerald-950/60 text-emerald-400 border border-emerald-800/50">
${currentPrice.toFixed(3)}
</span>
</div>
<span className="text-[11px] text-slate-400">TradingView 专业高频连续竞价图表</span>
</div>
</div>
{/* Timeframes */}
<div className="flex space-x-1 bg-slate-950 p-1 rounded-xl border border-slate-800 text-xs font-mono">
{['1s', '5s', '1m', '15m', '1h'].map((tf) => (
<button
key={tf}
onClick={() => setTimeframe(tf)}
className={`px-2.5 py-1 rounded-lg transition-all ${
timeframe === tf
? 'bg-cyan-600 text-white font-bold'
: 'text-slate-400 hover:text-slate-200'
}`}
>
{tf}
</button>
))}
</div>
</div>
{/* TradingView Chart Container */}
<div ref={chartContainerRef} className="w-full rounded-2xl overflow-hidden" />
</div>
);
};