diff --git a/components/mission-control/TradingChart.tsx b/components/mission-control/TradingChart.tsx index 824d338..86bfa64 100644 --- a/components/mission-control/TradingChart.tsx +++ b/components/mission-control/TradingChart.tsx @@ -39,129 +39,105 @@ interface ThothView { resistance_zones?: { level: number; strength: number }[] } -interface PriceData { - price: number - change24h: number -} - -interface IndicatorState { - ema20: boolean - ema50: boolean - ema200: boolean - bb: boolean - rsi: boolean - macd: boolean - thoth: boolean - volume: boolean - srZones: boolean - news: boolean - patterns: boolean - fib: boolean - countdown: boolean - calendar: boolean - correlation: boolean - funding: boolean -} - const getCandleLimit = (tf: string) => ({ '15m': 80, '1h': 100, '4h': 60, '1D': 90 }[tf] || 100) export default function TradingChart() { const chartContainerRef = useRef(null) const chartRef = useRef(null) - const candlestickSeriesRef = useRef(null) - const volumeSeriesRef = useRef(null) - const ema20Ref = useRef(null) - const ema50Ref = useRef(null) - const ema200Ref = useRef(null) + const seriesRef = useRef(null) const [selectedAsset, setSelectedAsset] = useState<'BTC' | 'SOL' | 'ETH'>('BTC') const [selectedTimeframe, setSelectedTimeframe] = useState<'15m' | '1h' | '4h' | '1D'>('1h') const [chartData, setChartData] = useState([]) - const [priceData, setPriceData] = useState({ price: 0, change24h: 0 }) + const [priceData, setPriceData] = useState<{ price: number; change24h: number }>({ price: 0, change24h: 0 }) const [loading, setLoading] = useState(true) const [trades, setTrades] = useState([]) const [thothView, setThothView] = useState>({}) - const [indicators, setIndicators] = useState({ - ema20: false, ema50: false, ema200: false, bb: false, rsi: false, macd: false, thoth: true, volume: true, srZones: true, news: false, patterns: false, fib: false, countdown: false, calendar: false, correlation: false, funding: false - }) const [mounted, setMounted] = useState(false) - // Initialize on mount (client only) + // Load chart library from CDN on mount useEffect(() => { setMounted(true) - let chartInstance: any = null - let candleSeries: any = null - let volSeries: any = null - let e20: any = null - let e50: any = null - let e200: any = null - - const initChart = async () => { - if (!chartContainerRef.current) return - - try { - const { createChart } = await import('lightweight-charts') - - chartInstance = createChart(chartContainerRef.current, { - layout: { background: { color: '#0a0a0f' }, textColor: '#a0a0a0' }, - grid: { vertLines: { color: '#1a1a2e' }, horzLines: { color: '#1a1a2e' } }, - width: chartContainerRef.current.clientWidth, - height: 400, - timeScale: { timeVisible: true, secondsVisible: false }, - rightPriceScale: { borderColor: '#2a2a4e' }, - }) - - candleSeries = chartInstance.addCandlestickSeries({ - upColor: '#22c55e', downColor: '#ef4444', - borderUpColor: '#22c55e', borderDownColor: '#ef4444', - wickUpColor: '#22c55e', wickDownColor: '#ef4444', - }) - - volSeries = chartInstance.addHistogramSeries({ color: '#26a69a', priceFormat: { type: 'volume' }, priceScaleId: '' }) - volSeries.priceScale().applyOptions({ scaleMargins: { top: 0.8, bottom: 0 } }) - - chartRef.current = chartInstance - candlestickSeriesRef.current = candleSeries - volumeSeriesRef.current = volSeries - ema20Ref.current = e20 - ema50Ref.current = e50 - ema200Ref.current = e200 - - const handleResize = () => { - if (chartContainerRef.current) chartInstance.applyOptions({ width: chartContainerRef.current.clientWidth }) - } - window.addEventListener('resize', handleResize) - - // Fetch initial data - fetchChartData() - fetchPriceData() - fetchTrades() - fetchThothView() - - } catch (e) { - console.error('Chart init error:', e) - } - } - - initChart() + // Load lightweight-charts from CDN + const script = document.createElement('script') + script.src = 'https://unpkg.com/lightweight-charts@4.1.0/dist/lightweight-charts.standalone.production.js' + script.onload = initChart + script.onerror = () => console.error('Failed to load chart library') + document.head.appendChild(script) return () => { - if (chartInstance) { - chartInstance.remove() + if (chartRef.current) { + chartRef.current.remove() } } }, []) + const initChart = () => { + if (!chartContainerRef.current || !(window as any).LightweightCharts) return + + const chart = (window as any).LightweightCharts.createChart(chartContainerRef.current, { + layout: { + background: { color: '#0a0a0f' }, + textColor: '#a0a0a0', + }, + grid: { + vertLines: { color: '#1a1a2e' }, + horzLines: { color: '#1a1a2e' }, + }, + width: chartContainerRef.current.clientWidth, + height: 400, + timeScale: { + timeVisible: true, + secondsVisible: false, + }, + rightPriceScale: { + borderColor: '#2a2a4e', + }, + }) + + const candlestickSeries = chart.addCandlestickSeries({ + upColor: '#22c55e', + downColor: '#ef4444', + borderUpColor: '#22c55e', + borderDownColor: '#ef4444', + wickUpColor: '#22c55e', + wickDownColor: '#ef4444', + }) + + chartRef.current = chart + seriesRef.current = candlestickSeries + + // Handle resize + const handleResize = () => { + if (chartContainerRef.current && chartRef.current) { + chartRef.current.applyOptions({ width: chartContainerRef.current.clientWidth }) + } + } + window.addEventListener('resize', handleResize) + + // Fetch data + fetchChartData() + fetchPriceData() + fetchTrades() + fetchThothView() + } + const fetchPriceData = async () => { try { const idMap: Record = { 'BTC': 'bitcoin', 'SOL': 'solana', 'ETH': 'ethereum' } const res = await fetch(`https://api.coingecko.com/api/v3/simple/price?ids=${idMap[selectedAsset]}&vs_currencies=usd&include_24hr_change=true`) const data = await res.json() - setPriceData({ price: data[idMap[selectedAsset]]?.usd || 0, change24h: data[idMap[selectedAsset]]?.usd_24h_change || 0 }) + setPriceData({ + price: data[idMap[selectedAsset]]?.usd || 0, + change24h: data[idMap[selectedAsset]]?.usd_24h_change || 0 + }) } catch (e) { - console.warn('Price fetch failed') - const fallback: Record = { 'BTC': { price: 105000, change24h: 2.5 }, 'SOL': { price: 180, change24h: -1.2 }, 'ETH': { price: 3200, change24h: 1.8 } } + const fallback: Record = { + 'BTC': { price: 105000, change24h: 2.5 }, + 'SOL': { price: 180, change24h: -1.2 }, + 'ETH': { price: 3200, change24h: 1.8 } + } setPriceData(fallback[selectedAsset] || { price: 0, change24h: 0 }) } } @@ -173,43 +149,66 @@ export default function TradingChart() { const interval = { '15m': '15m', '1h': '1h', '4h': '4h', '1D': '1d' }[selectedTimeframe] || '1h' const res = await fetch(`https://api.binance.com/api/v3/klines?symbol=${symbol}&interval=${interval}&limit=${getCandleLimit(selectedTimeframe)}`) const data = await res.json() - const chartData = data.map((k: any[]) => ({ time: k[0] / 1000, open: parseFloat(k[1]), high: parseFloat(k[2]), low: parseFloat(k[3]), close: parseFloat(k[4]), volume: parseFloat(k[5]) })) - setChartData(chartData) + const formattedData = data.map((k: any[]) => ({ + time: k[0] / 1000, + open: parseFloat(k[1]), + high: parseFloat(k[2]), + low: parseFloat(k[3]), + close: parseFloat(k[4]), + volume: parseFloat(k[5]) + })) + setChartData(formattedData) // Update chart - if (candlestickSeriesRef.current) { - candlestickSeriesRef.current.setData(chartData.map((d: any) => ({ time: d.time, open: d.open, high: d.high, low: d.low, close: d.close }))) + if (seriesRef.current) { + seriesRef.current.setData(formattedData) } - if (volumeSeriesRef.current && chartData[0]?.volume) { - volumeSeriesRef.current.setData(chartData.map((d: any) => ({ time: d.time, value: d.volume || 0, color: d.close >= d.open ? 'rgba(34,197,94,0.5)' : 'rgba(239,68,68,0.5)' }))) + if (chartRef.current) { + chartRef.current.timeScale().fitContent() } - if (chartRef.current) chartRef.current.timeScale().fitContent() - } catch (e) { console.error('Chart fetch error:', e) } - finally { setLoading(false) } + } catch (e) { + console.error('Chart fetch error:', e) + } + setLoading(false) } - const fetchTrades = async () => { try { const res = await fetch('/api/trading/trades'); if (res.ok) setTrades((await res.json()).trades || []) } catch (e) { console.warn(e) } } - const fetchThothView = async () => { try { const res = await fetch('/thoth_view.json'); if (res.ok) setThothView(await res.json()) } catch (e) { console.warn(e) } } + const fetchTrades = async () => { + try { + const res = await fetch('/api/trading/trades'); + if (res.ok) setTrades((await res.json()).trades || []) + } catch (e) { console.warn(e) } + } + + const fetchThothView = async () => { + try { + const res = await fetch('/thoth_view.json'); + if (res.ok) setThothView(await res.json()) + } catch (e) { console.warn(e) } + } useEffect(() => { - fetchChartData() - fetchPriceData() - }, [selectedAsset, selectedTimeframe]) + if (mounted && chartRef.current) { + fetchChartData() + fetchPriceData() + } + }, [selectedAsset, selectedTimeframe, mounted]) - const toggleIndicator = (key: keyof IndicatorState) => setIndicators((p: any) => ({ ...p, [key]: !p[key] })) - - const closedTrades = trades.filter((t: Trade) => t.result === 'win' || t.result === 'loss') - const wins = closedTrades.filter((t: Trade) => t.result === 'win').length + const closedTrades = trades.filter((t) => t.result === 'win' || t.result === 'loss') + const wins = closedTrades.filter((t) => t.result === 'win').length const winRate = closedTrades.length ? Math.round(wins / closedTrades.length * 100) : 0 - const totalPnl = closedTrades.reduce((s: number, t: Trade) => s + (t.pnl || 0), 0) - const avgRr = closedTrades.length ? closedTrades.reduce((s: number, t: Trade) => s + (t.rr || 0), 0) / closedTrades.length : 0 + const totalPnl = closedTrades.reduce((s, t) => s + (t.pnl || 0), 0) + const avgRr = closedTrades.length ? closedTrades.reduce((s, t) => s + (t.rr || 0), 0) / closedTrades.length : 0 const cv = thothView[selectedAsset] const getTE = (t: string) => t === 'uptrend' ? '🟢' : t === 'downtrend' ? '🔴' : '⚪️' const getBC = (b: string) => b === 'bullish' ? 'text-green-400' : b === 'bearish' ? 'text-red-400' : 'text-yellow-400' if (!mounted) { - return
Loading chart...
+ return ( +
+
Loading chart...
+
+ ) } return ( @@ -217,29 +216,37 @@ export default function TradingChart() {
{(['BTC', 'SOL', 'ETH'] as const).map(a => ( - + ))}
{(['15m', '1h', '4h', '1D'] as const).map(tf => ( - + ))}
-
- {(Object.keys(indicators) as (keyof IndicatorState)[]).map(k => ( - - ))} -
-
-
{selectedAsset}/USD
+
+ {selectedAsset}/USD +
${priceData.price.toLocaleString()}
-
= 0 ? 'text-green-400' : 'text-red-400'}`}>{priceData.change24h >= 0 ? '↑' : '↓'} {Math.abs(priceData.change24h).toFixed(2)}%
+
= 0 ? 'text-green-400' : 'text-red-400'}`}> + {priceData.change24h >= 0 ? '↑' : '↓'} {Math.abs(priceData.change24h).toFixed(2)}% +
@@ -261,15 +268,31 @@ export default function TradingChart() { )}
- {loading &&
Loading...
} + {loading && ( +
+ Loading... +
+ )}
-

Trades

{closedTrades.length}

-

Win Rate

{winRate}%

-

Total PnL

= 0 ? 'text-green-400' : 'text-red-400'}`}>${totalPnl.toFixed(2)}

-

Avg R:R

{avgRr.toFixed(2)}R

+
+

Trades

+

{closedTrades.length}

+
+
+

Win Rate

+

{winRate}%

+
+
+

Total PnL

+

= 0 ? 'text-green-400' : 'text-red-400'}`}>${totalPnl.toFixed(2)}

+
+
+

Avg R:R

+

{avgRr.toFixed(2)}R

+
)