'use client' import { useState, useEffect, useRef } from 'react' import { createChart, IChartApi, ISeriesApi, CandlestickData, Time } from 'lightweight-charts' interface Trade { id: string date: string pair: string direction: 'long' | 'short' entry: number stopLoss: number takeProfit: number result?: 'win' | 'loss' | 'open' pnl?: number rr?: number } interface ChartData { time: number open: number high: number low: number close: number volume?: number } interface ThothView { thought: string trend: string phase: string key_level: number bias: string confidence: number reason: string next_action: string updated_at: string bias_history?: { time: number; bias: string; price: number }[] support_zones?: { level: number; strength: number }[] 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>(null) const volumeSeriesRef = useRef | null>(null) const ema20Ref = useRef | null>(null) const ema50Ref = useRef | null>(null) const ema200Ref = useRef | null>(null) const [selectedAsset, setSelectedAsset] = useState<'BTC' | 'SOL' | 'ETH'>('BTC') const [selectedTimeframe, setSelectedTimeframe] = useState<'15m' | '1h' | '4h' | '1D'>('1h') const [secondTimeframe, setSecondTimeframe] = useState<'15m' | '1h' | '4h' | '1D' | null>(null) const [chartData, setChartData] = useState([]) const [secondChartData, setSecondChartData] = useState([]) const [priceData, setPriceData] = useState({ 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 }) // Initialize chart useEffect(() => { if (!chartContainerRef.current) return const chart = 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', }) const volumeSeries = chart.addHistogramSeries({ color: '#26a69a', priceFormat: { type: 'volume' }, priceScaleId: '', }) volumeSeries.priceScale().applyOptions({ scaleMargins: { top: 0.8, bottom: 0 }, }) chartRef.current = chart candlestickSeriesRef.current = candlestickSeries volumeSeriesRef.current = volumeSeries const handleResize = () => { if (chartContainerRef.current) { chart.applyOptions({ width: chartContainerRef.current.clientWidth }) } } window.addEventListener('resize', handleResize) return () => { window.removeEventListener('resize', handleResize) chart.remove() } }, []) // Fetch data when asset/timeframe changes useEffect(() => { fetchChartData() fetchPriceData() }, [selectedAsset, selectedTimeframe]) // Update chart when data or indicators change useEffect(() => { if (!candlestickSeriesRef.current || chartData.length === 0) return const candleData: CandlestickData[] = chartData.map(d => ({ time: (d.time / 1000) as Time, open: d.open, high: d.high, low: d.low, close: d.close, })) candlestickSeriesRef.current.setData(candleData) // Volume if (volumeSeriesRef.current && chartData[0]?.volume) { const volumeData = chartData.map(d => ({ time: (d.time / 1000) as Time, value: d.volume || 0, color: d.close >= d.open ? 'rgba(34, 197, 94, 0.5)' : 'rgba(239, 68, 68, 0.5)', })) volumeSeriesRef.current.setData(volumeData) volumeSeriesRef.current.applyOptions({ visible: indicators.volume }) } // EMAs if (indicators.ema20) { if (!ema20Ref.current && chartRef.current) { ema20Ref.current = chartRef.current.addLineSeries({ color: '#eab308', lineWidth: 2 }) } if (ema20Ref.current) { const closes = chartData.map(d => d.close) const ema20 = calculateEMA(closes, 20) ema20Ref.current.setData(ema20.map((v, i) => ({ time: (chartData[i].time / 1000) as Time, value: v }))) ema20Ref.current.applyOptions({ visible: true }) } } else if (ema20Ref.current) { ema20Ref.current.applyOptions({ visible: false }) } if (indicators.ema50) { if (!ema50Ref.current && chartRef.current) { ema50Ref.current = chartRef.current.addLineSeries({ color: '#3b82f6', lineWidth: 2 }) } if (ema50Ref.current) { const closes = chartData.map(d => d.close) const ema50 = calculateEMA(closes, 50) ema50Ref.current.setData(ema50.map((v, i) => ({ time: (chartData[i].time / 1000) as Time, value: v }))) ema50Ref.current.applyOptions({ visible: true }) } } else if (ema50Ref.current) { ema50Ref.current.applyOptions({ visible: false }) } if (indicators.ema200) { if (!ema200Ref.current && chartRef.current) { ema200Ref.current = chartRef.current.addLineSeries({ color: '#ffffff', lineWidth: 2 }) } if (ema200Ref.current) { const closes = chartData.map(d => d.close) const ema200 = calculateEMA(closes, 200) ema200Ref.current.setData(ema200.map((v, i) => ({ time: (chartData[i].time / 1000) as Time, value: v }))) ema200Ref.current.applyOptions({ visible: true }) } } else if (ema200Ref.current) { ema200Ref.current.applyOptions({ visible: false }) } // Fit content chartRef.current?.timeScale().fitContent() }, [chartData, indicators]) 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`) if (!res.ok) throw new Error('CoinGecko error') const data = await res.json() setPriceData({ price: data[idMap[selectedAsset]].usd, change24h: data[idMap[selectedAsset]].usd_24h_change }) } catch (e) { console.warn("Price fetch failed, using fallback") const fallbackPrices: Record = { 'BTC': { price: 105000, change24h: 2.5 }, 'SOL': { price: 180, change24h: -1.2 }, 'ETH': { price: 3200, change24h: 1.8 } } setPriceData(fallbackPrices[selectedAsset] || { price: 0, change24h: 0 }) } } const fetchChartData = async () => { setLoading(true) try { const symbol = selectedAsset === 'BTC' ? 'BTCUSDT' : selectedAsset === 'SOL' ? 'SOLUSDT' : 'ETHUSDT' 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() setChartData(data.map((k: any[]) => ({ time: k[0], open: parseFloat(k[1]), high: parseFloat(k[2]), low: parseFloat(k[3]), close: parseFloat(k[4]), volume: parseFloat(k[5]) }))) } catch (e) { console.error("Chart fetch error:", e) } finally { 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) } } useEffect(() => { fetchTrades(); fetchThothView() }, []) const toggleIndicator = (key: keyof IndicatorState) => setIndicators(p => ({ ...p, [key]: !p[key] })) 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, 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' return (
{/* Asset Selection */}
{(['BTC', 'SOL', 'ETH'] as const).map(a => ( ))}
{(['15m', '1h', '4h', '1D'] as const).map(tf => ( ))}
{/* Indicators */}
{(Object.keys(indicators) as (keyof IndicatorState)[]).map(k => ( ))}
{/* Price Display */}
{selectedAsset}/USD
${priceData.price.toLocaleString()}
= 0 ? 'text-green-400' : 'text-red-400'}`}> {priceData.change24h >= 0 ? '↑' : '↓'} {Math.abs(priceData.change24h).toFixed(2)}%
{/* THOTH View */} {cv && (
👁️

THOTH'S VIEW

{new Date(cv.updated_at).toLocaleString()}

"{cv.thought}"

Trend

{getTE(cv.trend)} {cv.trend}

Phase

{cv.phase}

Key Level

${cv.key_level.toLocaleString()}

Bias

{cv.bias.toUpperCase()} ({cv.confidence}/10)

)} {/* Chart */}
{loading && (
Loading...
)}
{/* Stats */}

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

) } function calculateEMA(data: number[], period: number): (number | null)[] { const ema: (number | null)[] = [] const multiplier = 2 / (period + 1) for (let i = 0; i < data.length; i++) { if (i < period - 1) ema.push(null) else if (i === period - 1) { let sum = 0; for (let j = 0; j < period; j++) sum += data[i - j] ema.push(sum / period) } else { ema.push(data[i] * multiplier + (ema[i - 1] ?? data[i]) * (1 - multiplier)) } } return ema }