diff --git a/app/mission-control/docs/page.tsx b/app/mission-control/docs/page.tsx index 6beb827..cbe48d4 100644 --- a/app/mission-control/docs/page.tsx +++ b/app/mission-control/docs/page.tsx @@ -1,23 +1,31 @@ -export default function DocsPage() { - return ( -
-

SiteMente Docs

-

Long-term documentation for SiteMente operations.

- -
-
-

Getting Started

-

Quick start guides and setup instructions

-
-
-

Products

-

Smart Starter, Smart Site, AI Growth Partner

-
-
-

Integrations

-

Vapi, MiniMax, Stripe

-
-
-
- ) -} +# 📚 SiteMente Docs + +Long-term documentation for SiteMente operations. + +## 🚀 Getting Started + +- [Quick Start Guide](/docs/quick-start) +- [Architecture Overview](/docs/architecture) +- [Environment Variables](/docs/env) + +## 🛠 Products + +- [Smart Starter](/docs/products/starter) +- [Smart Site](/docs/products/site) +- [AI Growth Partner](/docs/products/growth) + +## 🔌 Integrations + +- [Vapi Voice AI](/docs/integrations/vapi) +- [MiniMax AI](/docs/integrations/minimax) +- [Stripe Payments](/docs/integrations/stripe) + +## 📋 Operational + +- [Onboarding Checklist](/docs/ops/onboarding) +- [Troubleshooting Guide](/docs/ops/troubleshooting) +- [API Endpoints](/docs/ops/api) + +--- + +*Auto-generated by Horus* 👁️ diff --git a/components/mission-control/TradingChart.tsx b/components/mission-control/TradingChart.tsx index 5279c5e..b25207e 100644 --- a/components/mission-control/TradingChart.tsx +++ b/components/mission-control/TradingChart.tsx @@ -39,106 +39,196 @@ interface ThothView { resistance_zones?: { level: number; strength: number }[] } -const getCandleLimit = (tf: string) => ({ '15m': 80, '1h': 100, '4h': 60, '1D': 90 }[tf] || 100) +interface PriceData { + price: number + change24h: number +} -export default function TradingChart() { - const chartContainerRef = useRef(null) - const chartRef = useRef(null) - const seriesRef = useRef(null) +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 +} +interface PatternMatch { + index: number + type: string +} + +// Indicator calculations +const 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]! * (1 - multiplier)) + } + } + return ema +} + +const calculateSMA = (data: number[], period: number): (number | null)[] => { + const sma: (number | null)[] = [] + for (let i = 0; i < data.length; i++) { + if (i < period - 1) sma.push(null) + else { let sum = 0; for (let j = 0; j < period; j++) sum += data[i - j]; sma.push(sum / period) } + } + return sma +} + +const calculateStdDev = (data: number[], period: number): (number | null)[] => { + const stdDev: (number | null)[] = [] + for (let i = 0; i < data.length; i++) { + if (i < period - 1) stdDev.push(null) + else { + const slice = data.slice(i - period + 1, i + 1) + const mean = slice.reduce((a, b) => a + b, 0) / period + stdDev.push(Math.sqrt(slice.reduce((a, b) => a + Math.pow(b - mean, 2), 0) / period)) + } + } + return stdDev +} + +const calculateBollingerBands = (data: number[], period: number = 20, stdDevMult: number = 2) => { + const sma = calculateSMA(data, period) + const stdDev = calculateStdDev(data, period) + const upper: (number | null)[] = [], lower: (number | null)[] = [] + for (let i = 0; i < data.length; i++) { + if (sma[i] === null || stdDev[i] === null) { upper.push(null); lower.push(null) } + else { upper.push(sma[i]! + stdDev[i]! * stdDevMult); lower.push(sma[i]! - stdDev[i]! * stdDevMult) } + } + return { middle: sma, upper, lower } +} + +const calculateRSI = (data: number[], period: number = 14): (number | null)[] => { + const rsi: (number | null)[] = [] + let gains: number[] = [], losses: number[] = [] + for (let i = 0; i < data.length; i++) { + if (i === 0) { rsi.push(null); continue } + const change = data[i] - data[i - 1] + gains.push(change > 0 ? change : 0) + losses.push(change < 0 ? Math.abs(change) : 0) + if (i < period) rsi.push(null) + else { + const avgGain = gains.slice(-period).reduce((a, b) => a + b, 0) / period + const avgLoss = losses.slice(-period).reduce((a, b) => a + b, 0) / period + const rs = avgLoss === 0 ? 100 : avgGain / avgLoss + rsi.push(100 - (100 / (1 + rs))) + } + } + return rsi +} + +const calculateMACD = (data: number[], fast: number = 12, slow: number = 26, signal: number = 9) => { + const emaFast = calculateEMA(data, fast) + const emaSlow = calculateEMA(data, slow) + const macdLine: (number | null)[] = [] + for (let i = 0; i < data.length; i++) { + if (emaFast[i] === null || emaSlow[i] === null) macdLine.push(null) + else macdLine.push(emaFast[i]! - emaSlow[i]!) + } + const validMacd = macdLine.filter((v): v is number => v !== null) + const signalLine = calculateEMA(validMacd, signal) + const signalLineAligned: (number | null)[] = [] + let signalIdx = 0 + for (let i = 0; i < macdLine.length; i++) { + if (macdLine[i] === null) signalLineAligned.push(null) + else { signalLineAligned.push(signalLine[signalIdx] ?? null); signalIdx++ } + } + const histogram: (number | null)[] = [] + for (let i = 0; i < macdLine.length; i++) { + if (macdLine[i] === null || signalLineAligned[i] === null) histogram.push(null) + else histogram.push(macdLine[i]! - signalLineAligned[i]!) + } + return { macd: macdLine, signal: signalLineAligned, histogram } +} + +const detectPatterns = (data: ChartData[]): PatternMatch[] => { + const patterns: PatternMatch[] = [] + const closes = data.map(d => d.close) + const highs = data.map(d => d.high) + const lows = data.map(d => d.low) + for (let i = 20; i < data.length - 5; i++) { + if (highs[i] > highs[i-1] && highs[i] > highs[i+1] && highs[i-5] > highs[i-4] && Math.abs(highs[i] - highs[i-5]) < highs[i] * 0.02) + patterns.push({ index: i, type: 'double_top' }) + if (lows[i] < lows[i-1] && lows[i] < lows[i+1] && lows[i-5] < lows[i-4] && Math.abs(lows[i] - lows[i-5]) < lows[i] * 0.02) + patterns.push({ index: i, type: 'double_bottom' }) + if (closes[i] > highs[i-5] && closes[i-1] < highs[i-5]) patterns.push({ index: i, type: 'breakout' }) + if (closes[i] < lows[i-5] && closes[i-1] > lows[i-5]) patterns.push({ index: i, type: 'breakdown' }) + } + return patterns +} + +declare global { + interface Window { + Chart: any + } +} + +export function TradingChart() { + const mainChartRef = useRef(null) + const rsiChartRef = useRef(null) + const macdChartRef = useRef(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 [priceData, setPriceData] = useState<{ price: number; change24h: number }>({ price: 0, change24h: 0 }) - const [loading, setLoading] = useState(true) + const [secondChartData, setSecondChartData] = useState([]) const [trades, setTrades] = useState([]) const [thothView, setThothView] = useState>({}) - const [mounted, setMounted] = useState(false) + const [priceData, setPriceData] = useState({ price: 0, change24h: 0 }) + const [loading, setLoading] = useState(true) + const [patterns, setPatterns] = 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: true, fib: false, countdown: true, calendar: false, correlation: false, funding: false + }) + const mainChartRefInstance = useRef(null) + const rsiChartRefInstance = useRef(null) + const macdChartRefInstance = useRef(null) + + const getCandleLimit = (tf: string) => ({ '15m': 80, '1h': 100, '4h': 60, '1D': 90 }[tf] || 100) + + useEffect(() => { fetchChartData(); fetchSecondChartData(); fetchPriceData(); const i = setInterval(() => { fetchChartData(); fetchSecondChartData(); fetchPriceData(); }, 60000); return () => clearInterval(i) }, [selectedAsset, selectedTimeframe, secondTimeframe]) + useEffect(() => { fetchTrades(); fetchThothView() }, []) - // Load chart library from CDN on mount useEffect(() => { - setMounted(true) - - // 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 (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() - } + if (chartData.length > 0) { setPatterns(detectPatterns(chartData)); renderCharts() } + return () => { if (mainChartRefInstance.current) mainChartRefInstance.current.destroy(); if (rsiChartRefInstance.current) rsiChartRefInstance.current.destroy(); if (macdChartRefInstance.current) macdChartRefInstance.current.destroy() } + }, [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 || 0, - change24h: data[idMap[selectedAsset]]?.usd_24h_change || 0 - }) + setPriceData({ price: data[idMap[selectedAsset]].usd, change24h: data[idMap[selectedAsset]].usd_24h_change }) } catch (e) { - const fallback: Record = { - 'BTC': { price: 105000, change24h: 2.5 }, - 'SOL': { price: 180, change24h: -1.2 }, - 'ETH': { price: 3200, change24h: 1.8 } + console.warn("Price fetch failed, using fallback") + // Fallback prices + const fallbackPrices: 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 }) + setPriceData(fallbackPrices[selectedAsset] || { price: 0, change24h: 0 }) } } @@ -149,160 +239,130 @@ 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 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 (seriesRef.current) { - seriesRef.current.setData(formattedData) - } - if (chartRef.current) { - chartRef.current.timeScale().fitContent() - } - } catch (e) { - console.error('Chart fetch error:', e) + 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 fetchSecondChartData = async () => { + if (!secondTimeframe) return + try { + const symbol = selectedAsset === 'BTC' ? 'BTCUSDT' : selectedAsset === 'SOL' ? 'SOLUSDT' : 'ETHUSDT' + const interval = { '15m': '15m', '1h': '1h', '4h': '4h', '1D': '1d' }[secondTimeframe] || '1h' + const res = await fetch(`https://api.binance.com/api/v3/klines?symbol=${symbol}&interval=${interval}&limit=${getCandleLimit(secondTimeframe)}`) + const data = await res.json() + setSecondChartData(data.map((k: any[]) => ({ time: k[0], open: parseFloat(k[1]), high: parseFloat(k[2]), low: parseFloat(k[3]), close: parseFloat(k[4]) }))) + } catch (e) { console.error("Second chart fetch error:", 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) } } + const toggleIndicator = (key: keyof IndicatorState) => setIndicators(p => ({ ...p, [key]: !p[key] })) + + const renderCharts = () => { + const script = document.createElement('script') + script.src = 'https://cdn.jsdelivr.net/npm/chart.js' + script.onload = () => { renderMainChart(); if (indicators.rsi) renderRSIChart(); if (indicators.macd) renderMACDChart() } + document.head.appendChild(script) + } + + const renderMainChart = () => { + if (!mainChartRef.current) return + if (mainChartRefInstance.current) mainChartRefInstance.current.destroy() + const ctx = mainChartRef.current.getContext('2d') + if (!ctx) return + + const closes = chartData.map(d => d.close) + const labels = chartData.map(d => new Date(d.time)) + const ema20 = calculateEMA(closes, 20), ema50 = calculateEMA(closes, 50), ema200 = calculateEMA(closes, 200), bb = calculateBollingerBands(closes, 20, 2) + const minP = Math.min(...chartData.flatMap(d => [d.high, d.low])), maxP = Math.max(...chartData.flatMap(d => [d.high, d.low])), pad = (maxP - minP) * 0.15 + + const datasets: any[] = [{ type: 'bar', label: 'Price', data: chartData.map(d => [d.low, d.high]), backgroundColor: chartData.map(d => d.close >= d.open ? '#22c55e' : '#ef4444'), borderColor: chartData.map(d => d.close >= d.open ? '#22c55e' : '#ef4444'), borderWidth: 1, borderSkipped: false }] + + if (indicators.volume && chartData[0]?.volume) datasets.push({ type: 'bar', label: 'Volume', data: chartData.map(d => d.volume || 0), backgroundColor: chartData.map(d => d.close >= d.open ? 'rgba(34,197,94,0.3)' : 'rgba(239,68,68,0.3)'), borderWidth: 0, yAxisID: 'y_vol' }) + if (indicators.ema20) datasets.push({ type: 'line', data: ema20, borderColor: '#eab308', borderWidth: 2, pointRadius: 0, tension: 0.4, yAxisID: 'y' }) + if (indicators.ema50) datasets.push({ type: 'line', data: ema50, borderColor: '#3b82f6', borderWidth: 2, pointRadius: 0, tension: 0.4, yAxisID: 'y' }) + if (indicators.ema200) datasets.push({ type: 'line', data: ema200, borderColor: '#ffffff', borderWidth: 2, pointRadius: 0, tension: 0.4, yAxisID: 'y' }) + if (indicators.bb) { datasets.push({ type: 'line', data: bb.upper, borderColor: '#a855f7', borderWidth: 1, pointRadius: 0, yAxisID: 'y' }); datasets.push({ type: 'line', data: bb.lower, borderColor: '#a855f7', borderWidth: 1, pointRadius: 0, backgroundColor: 'rgba(168,85,247,0.1)', fill: '-1', yAxisID: 'y' }) } + + const currentView = thothView[selectedAsset] + if (indicators.srZones && currentView?.support_zones) currentView.support_zones.forEach(z => datasets.push({ type: 'line', data: chartData.map(() => z.level), borderColor: 'rgba(34,197,94,0.5)', borderWidth: 2, borderDash: [5,5], pointRadius: 0, yAxisID: 'y' })) + if (indicators.srZones && currentView?.resistance_zones) currentView.resistance_zones.forEach(z => datasets.push({ type: 'line', data: chartData.map(() => z.level), borderColor: 'rgba(239,68,68,0.5)', borderWidth: 2, borderDash: [5,5], pointRadius: 0, yAxisID: 'y' })) + + if (indicators.patterns && patterns.length) datasets.push({ type: 'scatter', data: patterns.map(p => ({ x: labels[p.index], y: chartData[p.index]?.high || 0 })), backgroundColor: patterns.map(p => p.type === 'breakout' ? '#22c55e' : p.type === 'breakdown' ? '#ef4444' : '#fbbf24'), pointStyle: 'star', pointRadius: 10, yAxisID: 'y' }) + if (indicators.thoth && currentView?.bias_history) { + const bc = currentView.bias_history.map(b => { let ci = 0, md = Infinity; chartData.forEach((d, i) => { const df = Math.abs(d.time - b.time); if (df < md) { md = df; ci = i } }); return { idx: ci, bias: b.bias } }) + datasets.push({ type: 'scatter', data: bc.map(b => ({ x: labels[b.idx], y: chartData[b.idx]?.high || 0 })), backgroundColor: bc.map(b => b.bias === 'bullish' ? '#fbbf24' : b.bias === 'bearish' ? '#ef4444' : '#a0a0a0'), pointStyle: 'rectRot', pointRadius: 12, yAxisID: 'y' }) } - setLoading(false) + + const scales: any = { x: { display: true, grid: { color: '#1a1a2e' }, ticks: { color: '#a0a0a0', maxTicksLimit: 12 } }, y: { position: 'right', min: minP - pad, max: maxP + pad, grid: { color: '#1a1a2e' }, ticks: { color: '#a0a0a0', callback: (v: any) => '$' + v.toFixed(0) } } } + if (indicators.volume && chartData[0]?.volume) scales.y_vol = { display: false, max: Math.max(...chartData.map(d => d.volume || 0)) * 3 } + + mainChartRefInstance.current = new window.Chart(ctx, { type: 'bar', data: { labels, datasets }, options: { responsive: true, maintainAspectRatio: false, interaction: { intersect: false, mode: 'index' }, plugins: { legend: { display: false }, tooltip: { backgroundColor: '#1a1a2e', titleColor: '#fff', bodyColor: '#a0a0a0', borderColor: '#2a2a4e', borderWidth: 1 } }, scales } }) } - 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 renderRSIChart = () => { + if (!rsiChartRef.current) return + if (rsiChartRefInstance.current) rsiChartRefInstance.current.destroy() + const ctx = rsiChartRef.current.getContext('2d') + if (!ctx) return + const rsi = calculateRSI(chartData.map(d => d.close), 14) + rsiChartRefInstance.current = new window.Chart(ctx, { type: 'line', data: { labels: chartData.map(d => d.time), datasets: [{ data: rsi, borderColor: '#a0a0a0', borderWidth: 2, pointRadius: 0, tension: 0.4 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { x: { display: false }, y: { min: 0, max: 100, position: 'right', grid: { color: '#1a1a2e' }, ticks: { color: '#a0a0a0' } } } } }) } - const fetchThothView = async () => { - try { - const res = await fetch('/thoth_view.json'); - if (res.ok) setThothView(await res.json()) - } catch (e) { console.warn(e) } + const renderMACDChart = () => { + if (!macdChartRef.current) return + if (macdChartRefInstance.current) macdChartRefInstance.current.destroy() + const ctx = macdChartRef.current.getContext('2d') + if (!ctx) return + const { macd, signal, histogram } = calculateMACD(chartData.map(d => d.close)) + macdChartRefInstance.current = new window.Chart(ctx, { type: 'bar', data: { labels: chartData.map(d => d.time), datasets: [{ data: histogram, backgroundColor: histogram.map(v => v === null ? 'transparent' : v >= 0 ? '#22c55e' : '#ef4444'), borderWidth: 0 }, { type: 'line', data: macd, borderColor: '#3b82f6', borderWidth: 2, pointRadius: 0 }, { type: 'line', data: signal, borderColor: '#f59e0b', borderWidth: 2, pointRadius: 0 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { x: { display: false }, y: { position: 'right', grid: { color: '#1a1a2e' }, ticks: { color: '#a0a0a0' } } } } }) } - useEffect(() => { - if (mounted && chartRef.current) { - fetchChartData() - fetchPriceData() - } - }, [selectedAsset, selectedTimeframe, mounted]) - - 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 closedTrades = trades.filter(t => t.result === 'win' || t.result === 'loss') + const wins = closedTrades.filter(t => t.result === 'win').length, winRate = closedTrades.length ? Math.round(wins / closedTrades.length * 100) : 0, totalPnl = closedTrades.reduce((s, t) => s + (t.pnl || 0), 0), 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 (
-
- {(['BTC', 'SOL', 'ETH'] as const).map(a => ( - - ))} -
-
- {(['15m', '1h', '4h', '1D'] as const).map(tf => ( - - ))} -
+
{(['BTC', 'SOL', 'ETH'] as const).map(a => )}
+
{(['15m', '1h', '4h', '1D'] as const).map(tf => )}
- {/* Indicator toggles */} -
- - - - - -
+
Compare:{(['15m', '1h', '4h', '1D'] as const).map(tf => )}
-
-
- {selectedAsset}/USD -
-
-
${priceData.price.toLocaleString()}
-
= 0 ? 'text-green-400' : 'text-red-400'}`}> - {priceData.change24h >= 0 ? '↑' : '↓'} {Math.abs(priceData.change24h).toFixed(2)}% -
-
-
+
{(Object.keys(indicators) as (keyof IndicatorState)[]).map(k => )}
- {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)

-
-
- )} +
{selectedAsset}/USD
${priceData.price.toLocaleString()}
= 0 ? 'text-green-400' : 'text-red-400'}`}>{priceData.change24h >= 0 ? '↑' : '↓'} {Math.abs(priceData.change24h).toFixed(2)}%
-
- {loading && ( -
- Loading... -
- )} -
-
+ {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)

} -
-
-

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

-
-
+
{loading &&
Loading...
}
+ + {secondTimeframe && secondChartData.length > 0 &&
{secondTimeframe} Chart
} + + {indicators.rsi &&
} + {indicators.macd &&
} + +
{[{ v: trades.filter(t => t.result === 'open').length, l: 'Open' }, { v: totalPnl, l: 'P&L', c: 'text-green-400' }, { v: winRate + '%', l: 'Win Rate' }, { v: closedTrades.length, l: 'Trades' }, { v: avgRr.toFixed(1) + ':1', l: 'Avg R:R' }].map((s, i) =>

{s.v}

{s.l}

)}
+ +

📊 Trade History

{trades.length === 0 ?
No trades yet
:
{trades.map((t, i) => )}
DateAssetDirEntrySLTPR:RResult
{t.date}{t.pair}{t.direction.toUpperCase()}${t.entry.toLocaleString()}${t.stopLoss.toLocaleString()}${t.takeProfit.toLocaleString()}{t.rr?.toFixed(1)}:1{t.result === 'win' ? '✅' : t.result === 'loss' ? '❌' : '⏳'}
}
) } + +function SecondChart({ data }: { data: ChartData[] }) { + const ref = useRef(null) + useEffect(() => { + if (!ref.current || !data.length) return + const ctx = ref.current.getContext('2d') + if (!ctx) return + if (ref.current.chart) ref.current.chart.destroy() + const chart = new window.Chart(ctx, { type: 'line', data: { labels: data.map(d => new Date(d.time).toLocaleTimeString()), datasets: [{ data: data.map(d => d.close), borderColor: '#a0a0a0', borderWidth: 2, pointRadius: 0, tension: 0.4 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { x: { display: false }, y: { position: 'right', grid: { color: '#1a1a2e' }, ticks: { color: '#a0a0a0' } } } } }) + ref.current.chart = chart + }, [data]) + return +} diff --git a/components/mission-control/TradingPanel.tsx b/components/mission-control/TradingPanel.tsx index 83e99b4..1cf58b3 100644 --- a/components/mission-control/TradingPanel.tsx +++ b/components/mission-control/TradingPanel.tsx @@ -1,7 +1,7 @@ 'use client' import { useState, useEffect } from 'react' -import TradingChart from './TradingChart' +import { TradingChart } from './TradingChart' type TradingTab = 'research' | 'strategies' | 'execution' | 'journal' @@ -52,7 +52,7 @@ const defaultTraders: Trader[] = [ id: 'gareth_soloway', name: 'Gareth Soloway', status: 'learning', - framesAnalyzed: 5970, + framesAnalyzed: 768, patterns: ['Institutional Analysis', 'Yield Curve Signals', 'Cycle Top/Bottom', 'Epic Resistance Rejections'], entryRules: ['Track institutional activity', 'Wait for yield curve signals', 'Key level breaks with volume', 'Weekly analysis first'], exitRules: ['Stop below support (long)', 'Target structure highs/lows', '2:1 minimum'], @@ -171,10 +171,7 @@ export function TradingPanel() { ))}
)} -