import { useEffect, useRef, useState } from "react"; import * as LightweightCharts from "lightweight-charts"; type ChartTime = string | number; interface ChartDataPoint { time: ChartTime; open: number; high: number; low: number; close: number; } interface TradingViewChartProps { ticker: string; data?: ChartDataPoint[]; timeframe?: string; currentPrice?: number; // priceStream.subscribe(cb) should return an unsubscribe function priceStream?: { subscribe: (cb: (price: number) => void) => () => void }; } const TIMEFRAME_HEIGHTS: Record = { "1D": 300, "5Min": 250, "15Min": 250, "1H": 350, "1W": 400, }; export default function TradingViewChart({ ticker, data, timeframe = "1D", currentPrice, priceStream }: TradingViewChartProps) { const containerRef = useRef(null); const [livePrice, setLivePrice] = useState(undefined); const height = TIMEFRAME_HEIGHTS[timeframe] ?? 400; const isIntraday = ["1Min", "5Min", "15Min", "30Min", "1H"].includes(timeframe); useEffect(() => { if (!containerRef.current) { return; } const chart = LightweightCharts.createChart(containerRef.current, { height, autoSize: true, }); // Configure time scale based on timeframe and range chart.timeScale().applyOptions({ timeVisible: isIntraday, secondsVisible: timeframe === "1Min", }); const candlestickSeries = chart.addSeries(LightweightCharts.CandlestickSeries, { upColor: "#26a69a", downColor: "#ef5350", borderUpColor: "#26a69a", borderDownColor: "#ef5350", wickUpColor: "#26a69a", wickDownColor: "#ef5350", }); if (data && data.length > 0) { try { candlestickSeries.setData(data as any); // Fit the visible data range chart.timeScale().fitContent(); } catch (err) { console.error(`TradingViewChart: error setting data for ${ticker}`, err); } } return () => chart.remove(); }, [data, ticker, isIntraday, timeframe]); // Subscribe to a streaming price if provided useEffect(() => { if (!priceStream) return; let unsub: (() => void) | void = undefined; try { unsub = priceStream.subscribe((p: number) => { setLivePrice(p); }); } catch (e) { console.warn("TradingViewChart: priceStream subscribe failed", e); } return () => { try { if (typeof unsub === "function") unsub(); } catch (e) { /* ignore */ } }; }, [priceStream]); const derivedPrice = currentPrice ?? livePrice ?? (data && data.length ? data[data.length - 1].close : undefined); return (

{ticker} Price Chart

{typeof derivedPrice === "number" ? (
${derivedPrice.toFixed(2)}
) : null}
); }