'use client' import React, { useEffect, useRef, useState } from 'react' interface Position { id: string symbol: string side: 'LONG' | 'SHORT' size: number entryPrice: number stopLoss?: number takeProfit?: number pnl: number pnlPercentage: number } interface TradingChartProps { symbol?: string positions?: Position[] onPriceUpdate?: (price: number) => void } export default function TradingChart({ symbol = 'SOL/USDC', positions = [], onPriceUpdate }: TradingChartProps) { const chartContainerRef = useRef(null) const chart = useRef(null) const candlestickSeries = useRef(null) const positionLines = useRef([]) const [isLoading, setIsLoading] = useState(true) // Initialize chart with dynamic import useEffect(() => { if (!chartContainerRef.current) return const initChart = async () => { try { // Dynamic import to avoid SSR issues const LightweightCharts = await import('lightweight-charts') const { createChart, ColorType, CrosshairMode, LineStyle } = LightweightCharts chart.current = createChart(chartContainerRef.current!, { layout: { background: { type: ColorType.Solid, color: '#1a1a1a' }, textColor: '#ffffff', }, width: chartContainerRef.current!.clientWidth, height: 600, grid: { vertLines: { color: 'rgba(42, 46, 57, 0.5)' }, horzLines: { color: 'rgba(42, 46, 57, 0.5)' }, }, crosshair: { mode: CrosshairMode.Normal, }, rightPriceScale: { borderColor: 'rgba(197, 203, 206, 0.8)', }, timeScale: { borderColor: 'rgba(197, 203, 206, 0.8)', }, }) // Create candlestick series candlestickSeries.current = chart.current.addCandlestickSeries({ upColor: '#26a69a', downColor: '#ef5350', borderDownColor: '#ef5350', borderUpColor: '#26a69a', wickDownColor: '#ef5350', wickUpColor: '#26a69a', }) // Generate sample data console.log('Generating sample data...') const data = generateSampleData() console.log('Sample data generated:', data.length, 'points') console.log('First few data points:', data.slice(0, 3)) console.log('Setting chart data...') candlestickSeries.current.setData(data) console.log('Chart data set successfully') // Call onPriceUpdate with the latest price if provided if (onPriceUpdate && data.length > 0) { const latestPrice = data[data.length - 1].close onPriceUpdate(latestPrice) } // Add position overlays console.log('Adding position overlays...') addPositionOverlays(LineStyle) console.log('Position overlays added') console.log('Chart initialization complete') setIsLoading(false) // Handle resize const handleResize = () => { if (chart.current && chartContainerRef.current) { chart.current.applyOptions({ width: chartContainerRef.current.clientWidth, }) } } window.addEventListener('resize', handleResize) return () => { window.removeEventListener('resize', handleResize) if (chart.current) { chart.current.remove() } } } catch (error) { console.error('Failed to initialize chart:', error) console.error('Error details:', { message: error instanceof Error ? error.message : String(error), stack: error instanceof Error ? error.stack : undefined }) setIsLoading(false) } } const addPositionOverlays = (LineStyle: any) => { if (!chart.current) return // Clear existing lines positionLines.current.forEach(line => { if (line && chart.current) { chart.current.removePriceLine(line) } }) positionLines.current = [] // Add new position lines positions.forEach(position => { // Entry price line const entryLine = chart.current.addPriceLine({ price: position.entryPrice, color: '#2196F3', lineWidth: 2, lineStyle: LineStyle.Solid, axisLabelVisible: true, title: `Entry: $${position.entryPrice.toFixed(2)}`, }) positionLines.current.push(entryLine) // Stop loss line if (position.stopLoss) { const slLine = chart.current.addPriceLine({ price: position.stopLoss, color: '#f44336', lineWidth: 2, lineStyle: LineStyle.Dashed, axisLabelVisible: true, title: `SL: $${position.stopLoss.toFixed(2)}`, }) positionLines.current.push(slLine) } // Take profit line if (position.takeProfit) { const tpLine = chart.current.addPriceLine({ price: position.takeProfit, color: '#4caf50', lineWidth: 2, lineStyle: LineStyle.Dashed, axisLabelVisible: true, title: `TP: $${position.takeProfit.toFixed(2)}`, }) positionLines.current.push(tpLine) } }) } const generateSampleData = () => { const data = [] const basePrice = 166.5 let currentPrice = basePrice const baseDate = new Date() for (let i = 0; i < 100; i++) { // Generate data for the last 100 days, one point per day const currentTime = new Date(baseDate.getTime() - (99 - i) * 24 * 60 * 60 * 1000) const timeString = currentTime.toISOString().split('T')[0] // YYYY-MM-DD format const volatility = 0.02 const change = (Math.random() - 0.5) * volatility * currentPrice const open = currentPrice const close = currentPrice + change const high = Math.max(open, close) + Math.random() * 0.01 * currentPrice const low = Math.min(open, close) - Math.random() * 0.01 * currentPrice data.push({ time: timeString, open: Number(open.toFixed(2)), high: Number(high.toFixed(2)), low: Number(low.toFixed(2)), close: Number(close.toFixed(2)), }) currentPrice = close } return data } initChart() }, []) // Update position overlays when positions change useEffect(() => { if (chart.current && !isLoading && positions.length > 0) { import('lightweight-charts').then(({ LineStyle }) => { // Re-add position overlays (this is a simplified version) // In a full implementation, you'd want to properly manage line updates }) } }, [positions, isLoading]) if (isLoading) { return (
Loading chart...
) } return (
{/* Chart Header */}
{symbol}
$166.21
+1.42%
{/* Position Info */} {positions.length > 0 && (
{positions.map(position => (
{position.side} {position.size} = 0 ? 'text-green-400' : 'text-red-400'}> {position.pnl >= 0 ? '+' : ''}${position.pnl.toFixed(2)}
))}
)} {/* Chart Container */}
) }