fix(web): poll klines API every 5s so new on-chain candles render live
This commit is contained in:
@@ -26,6 +26,8 @@ export const TradingViewChart: React.FC<TradingViewChartProps> = ({
|
||||
// 用 ref 保活最新真实价格,避免 5s 定时器闭包捕获到过期值
|
||||
const priceRef = useRef<number>(currentPrice);
|
||||
const lastTickRef = useRef<{ time: number; price: number } | null>(null);
|
||||
// 已绘制到哪根柱(防止轮询/心跳重复写入同一根柱)
|
||||
const lastBarTimeRef = useRef<number>(0);
|
||||
const [timeframe, setTimeframe] = useState<string>('5s');
|
||||
|
||||
// 同步最新真实价格到 ref(同时立即打点一次,保证首根柱出现)
|
||||
@@ -91,35 +93,61 @@ export const TradingViewChart: React.FC<TradingViewChartProps> = ({
|
||||
seriesRef.current = candlestickSeries;
|
||||
|
||||
// 2. 从后端 API 异步获取真实 K 线(Indexer 聚合链上成交)
|
||||
const loadRealKlines = async () => {
|
||||
const fetchKlines = async (): Promise<any[] | null> => {
|
||||
try {
|
||||
const res = await fetch(
|
||||
`${API_BASE}/api/v1/charts/klines?market_address=${marketAddress}&outcome_index=${outcomeIndex}&timeframe=${timeframe}`
|
||||
);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (data.bars && data.bars.length > 0) {
|
||||
candlestickSeries.setData(data.bars);
|
||||
chart.timeScale().fitContent();
|
||||
const last = data.bars[data.bars.length - 1];
|
||||
lastTickRef.current = { time: last.time, price: Number(last.close) };
|
||||
return;
|
||||
}
|
||||
if (data.bars && data.bars.length > 0) return data.bars;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Load klines failed:', e);
|
||||
}
|
||||
// 无历史成交:不画任何假柱,等 5s 心跳用真实边际价打点
|
||||
return null;
|
||||
};
|
||||
|
||||
const loadRealKlines = async () => {
|
||||
const bars = await fetchKlines();
|
||||
if (bars && bars.length > 0) {
|
||||
candlestickSeries.setData(bars as any);
|
||||
chart.timeScale().fitContent();
|
||||
const last = bars[bars.length - 1];
|
||||
lastBarTimeRef.current = last.time;
|
||||
lastTickRef.current = { time: last.time, price: Number(last.close) };
|
||||
}
|
||||
// 无历史成交:不画任何假柱,等心跳用真实边际价打点
|
||||
};
|
||||
|
||||
loadRealKlines();
|
||||
|
||||
// 3. 每 5 秒用最新真实边际价打点(价格未变化则不重复写入,绝不伪造曲线)
|
||||
const interval = setInterval(() => {
|
||||
// 3. 每 5 秒轮询后端:把 indexer 最新聚合出的成交柱增量追加/更新到图表
|
||||
const refreshInterval = setInterval(async () => {
|
||||
const bars = await fetchKlines();
|
||||
if (!bars || bars.length === 0) return;
|
||||
const series = seriesRef.current;
|
||||
if (!series) return;
|
||||
const lastBarTime = lastBarTimeRef.current;
|
||||
// 仅处理「当前已绘制柱」与「其后新增柱」,避免重复写历史
|
||||
for (const b of bars) {
|
||||
if (b.time < lastBarTime) continue;
|
||||
series.update({ time: b.time, open: b.open, high: b.high, low: b.low, close: b.close });
|
||||
}
|
||||
const newest = bars[bars.length - 1];
|
||||
if (newest.time >= lastBarTime) {
|
||||
lastBarTimeRef.current = newest.time;
|
||||
lastTickRef.current = { time: newest.time, price: Number(newest.close) };
|
||||
}
|
||||
}, BAR_SECONDS * 1000);
|
||||
|
||||
// 4. 每 5 秒用最新真实边际价打点(仅在最新成交柱之后,价格未变化则不重复写入,绝不伪造曲线)
|
||||
const heartbeatInterval = setInterval(() => {
|
||||
const p = priceRef.current;
|
||||
const series = seriesRef.current;
|
||||
if (p <= 0 || !series) return;
|
||||
const barTime = barTimeOfNow();
|
||||
if (barTime < lastBarTimeRef.current) return;
|
||||
const last = lastTickRef.current;
|
||||
if (last && last.time === barTime && last.price === p) return;
|
||||
series.update({
|
||||
@@ -129,6 +157,9 @@ export const TradingViewChart: React.FC<TradingViewChartProps> = ({
|
||||
low: p,
|
||||
close: p,
|
||||
});
|
||||
if (barTime > lastBarTimeRef.current) {
|
||||
lastBarTimeRef.current = barTime;
|
||||
}
|
||||
lastTickRef.current = { time: barTime, price: p };
|
||||
}, BAR_SECONDS * 1000);
|
||||
|
||||
@@ -140,11 +171,13 @@ export const TradingViewChart: React.FC<TradingViewChartProps> = ({
|
||||
window.addEventListener('resize', handleResize);
|
||||
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
clearInterval(refreshInterval);
|
||||
clearInterval(heartbeatInterval);
|
||||
window.removeEventListener('resize', handleResize);
|
||||
chart.remove();
|
||||
chartRef.current = null;
|
||||
seriesRef.current = null;
|
||||
lastBarTimeRef.current = 0;
|
||||
};
|
||||
}, [marketAddress, outcomeIndex, timeframe]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user