Fix blank page issue and implement working chart
## Fixed Issues: - ✅ Resolved blank page caused by problematic chart component imports - ✅ Removed broken chart components that had library compatibility issues - ✅ Created SimpleChart component using HTML5 Canvas that works reliably - ✅ Cleaned up test pages and unused components ## Working Features: - ✅ Trading page loads correctly without blank screen - ✅ Professional candlestick chart with grid lines and price labels - ✅ Clean trading interface with all panels visible - ✅ No more loading errors or component failures ## Technical Implementation: - Used native HTML5 Canvas API for chart rendering - Proper TypeScript types and error handling - Responsive design that works in Docker environment - No external library dependencies to cause conflicts The trading dashboard is now stable and functional.
This commit is contained in:
@@ -1,85 +0,0 @@
|
||||
'use client'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
|
||||
export default function ChartAPITest() {
|
||||
const chartContainerRef = useRef<HTMLDivElement>(null)
|
||||
const [logs, setLogs] = useState<string[]>([])
|
||||
|
||||
const addLog = (message: string) => {
|
||||
console.log(message)
|
||||
setLogs(prev => [...prev, `${new Date().toLocaleTimeString()}: ${message}`])
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!chartContainerRef.current) return
|
||||
|
||||
const testLightweightCharts = async () => {
|
||||
try {
|
||||
addLog('Importing lightweight-charts...')
|
||||
|
||||
const LightweightCharts = await import('lightweight-charts')
|
||||
addLog('Import successful')
|
||||
|
||||
// Log what's available in the import
|
||||
addLog('Available exports: ' + Object.keys(LightweightCharts).join(', '))
|
||||
|
||||
const { createChart } = LightweightCharts
|
||||
addLog('createChart function available: ' + (typeof createChart))
|
||||
|
||||
// Create chart
|
||||
const chart = createChart(chartContainerRef.current!, {
|
||||
width: 600,
|
||||
height: 300,
|
||||
})
|
||||
addLog('Chart created')
|
||||
|
||||
// Log chart methods
|
||||
const chartMethods = Object.getOwnPropertyNames(Object.getPrototypeOf(chart))
|
||||
addLog('Chart methods: ' + chartMethods.join(', '))
|
||||
|
||||
// Try to find the correct method for adding series
|
||||
if ('addCandlestickSeries' in chart) {
|
||||
addLog('addCandlestickSeries method found!')
|
||||
} else if ('addCandles' in chart) {
|
||||
addLog('addCandles method found!')
|
||||
} else if ('addSeries' in chart) {
|
||||
addLog('addSeries method found!')
|
||||
} else {
|
||||
addLog('No obvious candlestick method found')
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error)
|
||||
addLog(`Error: ${errorMessage}`)
|
||||
console.error('Error:', error)
|
||||
}
|
||||
}
|
||||
|
||||
testLightweightCharts()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-900 p-8">
|
||||
<h1 className="text-white text-2xl mb-4">Lightweight Charts API Test</h1>
|
||||
|
||||
<div className="mb-4">
|
||||
<div
|
||||
ref={chartContainerRef}
|
||||
className="bg-gray-800 border border-gray-600 rounded"
|
||||
style={{ width: '600px', height: '300px' }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 className="text-white text-lg mb-2">API Investigation Logs</h2>
|
||||
<div className="bg-gray-800 p-4 rounded max-h-96 overflow-y-auto">
|
||||
{logs.map((log, index) => (
|
||||
<div key={index} className="text-gray-300 text-sm font-mono mb-1">
|
||||
{log}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
'use client'
|
||||
import React from 'react'
|
||||
import SimpleTradingChart from '../../components/SimpleTradingChart'
|
||||
|
||||
export default function SimpleChartPage() {
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-900 p-8">
|
||||
<h1 className="text-white text-3xl mb-6">Simple Canvas Chart Test</h1>
|
||||
<SimpleTradingChart symbol="SOL/USDC" positions={[]} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
'use client'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
|
||||
export default function StandaloneTest() {
|
||||
const chartContainerRef = useRef<HTMLDivElement>(null)
|
||||
const [status, setStatus] = useState('Starting...')
|
||||
|
||||
useEffect(() => {
|
||||
const initChart = async () => {
|
||||
try {
|
||||
setStatus('Testing CDN version...')
|
||||
|
||||
// Try using the CDN version instead
|
||||
if (typeof window !== 'undefined' && !window.LightweightCharts) {
|
||||
setStatus('Loading CDN script...')
|
||||
|
||||
const script = document.createElement('script')
|
||||
script.src = 'https://unpkg.com/lightweight-charts@5.0.8/dist/lightweight-charts.standalone.production.js'
|
||||
script.onload = () => {
|
||||
setStatus('CDN loaded, creating chart...')
|
||||
createChartWithCDN()
|
||||
}
|
||||
script.onerror = () => {
|
||||
setStatus('CDN load failed')
|
||||
}
|
||||
document.head.appendChild(script)
|
||||
} else if (window.LightweightCharts) {
|
||||
setStatus('CDN already loaded, creating chart...')
|
||||
createChartWithCDN()
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error:', error)
|
||||
setStatus(`Error: ${error}`)
|
||||
}
|
||||
}
|
||||
|
||||
const createChartWithCDN = () => {
|
||||
try {
|
||||
if (!chartContainerRef.current) {
|
||||
setStatus('No container')
|
||||
return
|
||||
}
|
||||
|
||||
const { createChart, CandlestickSeries } = (window as any).LightweightCharts
|
||||
|
||||
setStatus('Creating chart with CDN...')
|
||||
const chart = createChart(chartContainerRef.current, {
|
||||
width: 800,
|
||||
height: 400,
|
||||
layout: {
|
||||
background: { color: '#1a1a1a' },
|
||||
textColor: '#ffffff',
|
||||
},
|
||||
})
|
||||
|
||||
setStatus('Adding series...')
|
||||
const series = chart.addSeries(CandlestickSeries, {
|
||||
upColor: '#26a69a',
|
||||
downColor: '#ef5350',
|
||||
})
|
||||
|
||||
setStatus('Setting data...')
|
||||
series.setData([
|
||||
{ time: '2025-07-14', open: 100, high: 105, low: 95, close: 102 },
|
||||
{ time: '2025-07-15', open: 102, high: 107, low: 98, close: 104 },
|
||||
{ time: '2025-07-16', open: 104, high: 109, low: 101, close: 106 },
|
||||
])
|
||||
|
||||
setStatus('Chart created successfully!')
|
||||
|
||||
} catch (error) {
|
||||
console.error('CDN chart error:', error)
|
||||
setStatus(`CDN Error: ${error}`)
|
||||
}
|
||||
}
|
||||
|
||||
initChart()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-900 p-8">
|
||||
<h1 className="text-white text-3xl mb-4">CDN Chart Test</h1>
|
||||
<div className="text-green-400 text-lg mb-4">Status: {status}</div>
|
||||
|
||||
<div
|
||||
ref={chartContainerRef}
|
||||
className="border-2 border-blue-500 bg-gray-800"
|
||||
style={{
|
||||
width: '800px',
|
||||
height: '400px',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
'use client'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
|
||||
export default function ChartDebug() {
|
||||
const chartContainerRef = useRef<HTMLDivElement>(null)
|
||||
const [logs, setLogs] = useState<string[]>([])
|
||||
const [chartCreated, setChartCreated] = useState(false)
|
||||
|
||||
const addLog = (message: string) => {
|
||||
console.log(message)
|
||||
setLogs(prev => [...prev, `${new Date().toLocaleTimeString()}: ${message}`])
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!chartContainerRef.current) {
|
||||
addLog('Chart container ref not available')
|
||||
return
|
||||
}
|
||||
|
||||
const initChart = async () => {
|
||||
try {
|
||||
addLog('Starting chart initialization...')
|
||||
|
||||
// Import lightweight-charts
|
||||
const LightweightCharts = await import('lightweight-charts')
|
||||
addLog('Lightweight charts imported successfully')
|
||||
|
||||
const { createChart, CandlestickSeries } = LightweightCharts
|
||||
addLog('createChart and CandlestickSeries extracted')
|
||||
|
||||
// Create chart with minimal options
|
||||
const chart = createChart(chartContainerRef.current!, {
|
||||
width: 600,
|
||||
height: 300,
|
||||
})
|
||||
addLog('Chart created successfully')
|
||||
setChartCreated(true)
|
||||
|
||||
// Add candlestick series with the correct v5 API
|
||||
const candlestickSeries = chart.addSeries(CandlestickSeries, {
|
||||
upColor: '#26a69a',
|
||||
downColor: '#ef5350',
|
||||
borderDownColor: '#ef5350',
|
||||
borderUpColor: '#26a69a',
|
||||
wickDownColor: '#ef5350',
|
||||
wickUpColor: '#26a69a',
|
||||
})
|
||||
addLog('Candlestick series added')
|
||||
|
||||
// Very simple test data
|
||||
const testData = [
|
||||
{ time: '2023-01-01', open: 100, high: 110, low: 95, close: 105 },
|
||||
{ time: '2023-01-02', open: 105, high: 115, low: 100, close: 110 },
|
||||
{ time: '2023-01-03', open: 110, high: 120, low: 105, close: 115 },
|
||||
{ time: '2023-01-04', open: 115, high: 125, low: 110, close: 120 },
|
||||
{ time: '2023-01-05', open: 120, high: 130, low: 115, close: 125 },
|
||||
]
|
||||
|
||||
addLog(`Setting data with ${testData.length} points`)
|
||||
candlestickSeries.setData(testData)
|
||||
addLog('Data set successfully - chart should be visible now')
|
||||
|
||||
// Cleanup function
|
||||
return () => {
|
||||
addLog('Cleaning up chart')
|
||||
chart.remove()
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error)
|
||||
addLog(`Error: ${errorMessage}`)
|
||||
console.error('Chart error:', error)
|
||||
}
|
||||
}
|
||||
|
||||
initChart()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-900 p-8">
|
||||
<h1 className="text-white text-2xl mb-4">Chart Debug Test</h1>
|
||||
|
||||
<div className="mb-4">
|
||||
<h2 className="text-white text-lg mb-2">Status</h2>
|
||||
<div className="text-gray-400">
|
||||
Chart Created: {chartCreated ? '✅ Yes' : '❌ No'}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<h2 className="text-white text-lg mb-2">Chart Container</h2>
|
||||
<div
|
||||
ref={chartContainerRef}
|
||||
className="bg-gray-800 border border-gray-600 rounded"
|
||||
style={{ width: '600px', height: '300px' }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 className="text-white text-lg mb-2">Debug Logs</h2>
|
||||
<div className="bg-gray-800 p-4 rounded max-h-60 overflow-y-auto">
|
||||
{logs.map((log, index) => (
|
||||
<div key={index} className="text-gray-300 text-sm font-mono">
|
||||
{log}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,220 +0,0 @@
|
||||
'use client'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import TradingChart from '../../components/TradingChart'
|
||||
import CompactTradingPanel from '../../components/CompactTradingPanel'
|
||||
|
||||
export default function ChartTradingPageSimple() {
|
||||
const [currentPrice, setCurrentPrice] = useState(166.21)
|
||||
const [positions, setPositions] = useState([
|
||||
// Mock position data for demo
|
||||
{
|
||||
id: 'demo_pos_1',
|
||||
symbol: 'SOL/USDC',
|
||||
side: 'BUY' as 'BUY' | 'SELL',
|
||||
amount: 0.5,
|
||||
entryPrice: 165.50,
|
||||
stopLoss: 160.00,
|
||||
takeProfit: 170.00,
|
||||
currentPrice: 166.21,
|
||||
unrealizedPnl: 0.355,
|
||||
}
|
||||
])
|
||||
const [selectedSymbol, setSelectedSymbol] = useState('SOL')
|
||||
|
||||
const handleTrade = async (tradeData: any) => {
|
||||
try {
|
||||
console.log('Trade executed (demo):', tradeData)
|
||||
|
||||
// Create a mock position for demo
|
||||
const newPosition = {
|
||||
id: `demo_${Date.now()}`,
|
||||
symbol: `${tradeData.symbol}/USDC`,
|
||||
side: tradeData.side,
|
||||
amount: tradeData.amount,
|
||||
entryPrice: tradeData.price,
|
||||
stopLoss: tradeData.stopLoss,
|
||||
takeProfit: tradeData.takeProfit,
|
||||
currentPrice: currentPrice,
|
||||
unrealizedPnl: 0,
|
||||
}
|
||||
|
||||
setPositions(prev => [...prev, newPosition])
|
||||
alert(`Demo trade executed: ${tradeData.side} ${tradeData.amount} ${tradeData.symbol}`)
|
||||
} catch (error) {
|
||||
console.error('Trade execution error:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const handlePriceUpdate = (price: number) => {
|
||||
setCurrentPrice(price)
|
||||
|
||||
// Update position P&L based on new price
|
||||
setPositions(prev => prev.map(pos => ({
|
||||
...pos,
|
||||
currentPrice: price,
|
||||
unrealizedPnl: pos.side === 'BUY'
|
||||
? (price - pos.entryPrice) * pos.amount
|
||||
: (pos.entryPrice - price) * pos.amount
|
||||
})))
|
||||
}
|
||||
|
||||
const handleClosePosition = (positionId: string) => {
|
||||
setPositions(prev => prev.filter(pos => pos.id !== positionId))
|
||||
alert('Position closed (demo)')
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="h-screen bg-gray-900 flex flex-col">
|
||||
{/* Top Bar */}
|
||||
<div className="bg-gray-800 border-b border-gray-700 p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center space-x-6">
|
||||
<h1 className="text-xl font-bold text-white">Chart Trading Terminal</h1>
|
||||
|
||||
{/* Symbol Selector */}
|
||||
<div className="flex space-x-2">
|
||||
{['SOL', 'BTC', 'ETH'].map(symbol => (
|
||||
<button
|
||||
key={symbol}
|
||||
onClick={() => setSelectedSymbol(symbol)}
|
||||
className={`px-4 py-2 rounded-lg text-sm font-medium transition-all ${
|
||||
selectedSymbol === symbol
|
||||
? 'bg-blue-600 text-white'
|
||||
: 'bg-gray-700 text-gray-300 hover:bg-gray-600'
|
||||
}`}
|
||||
>
|
||||
{symbol}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Market Status */}
|
||||
<div className="flex items-center space-x-4 text-sm">
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="w-2 h-2 bg-green-400 rounded-full animate-pulse"></div>
|
||||
<span className="text-gray-300">Demo Mode</span>
|
||||
</div>
|
||||
<div className="text-gray-400">
|
||||
{new Date().toLocaleTimeString()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Main Trading Interface */}
|
||||
<div className="flex-1 flex">
|
||||
{/* Chart Area (70% width) */}
|
||||
<div className="flex-1 p-4">
|
||||
<TradingChart
|
||||
symbol={selectedSymbol}
|
||||
positions={positions}
|
||||
onPriceUpdate={handlePriceUpdate}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Trading Panel (30% width) */}
|
||||
<div className="w-96 border-l border-gray-700 p-4 space-y-4">
|
||||
<CompactTradingPanel
|
||||
symbol={selectedSymbol}
|
||||
currentPrice={currentPrice}
|
||||
onTrade={handleTrade}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom Panel - Positions */}
|
||||
<div className="border-t border-gray-700 bg-gray-800">
|
||||
<div className="p-4">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div className="flex space-x-6">
|
||||
<button className="text-white font-medium border-b-2 border-blue-500 pb-2">
|
||||
Positions ({positions.length})
|
||||
</button>
|
||||
<button className="text-gray-400 hover:text-white pb-2">
|
||||
Orders (0)
|
||||
</button>
|
||||
<button className="text-gray-400 hover:text-white pb-2">
|
||||
History
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{positions.length > 0 && (
|
||||
<div className="text-sm text-gray-400">
|
||||
Total P&L: <span className={`${
|
||||
positions.reduce((sum, pos) => sum + (pos.unrealizedPnl || 0), 0) >= 0
|
||||
? 'text-green-400' : 'text-red-400'
|
||||
}`}>
|
||||
{positions.reduce((sum, pos) => sum + (pos.unrealizedPnl || 0), 0) >= 0 ? '+' : ''}$
|
||||
{positions.reduce((sum, pos) => sum + (pos.unrealizedPnl || 0), 0).toFixed(2)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Positions Table */}
|
||||
<div className="max-h-48 overflow-y-auto">
|
||||
{positions.length === 0 ? (
|
||||
<div className="text-center py-8 text-gray-400">
|
||||
No open positions
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
{positions.map((position: any) => (
|
||||
<div
|
||||
key={position.id}
|
||||
className="bg-gray-900 rounded-lg p-4 flex items-center justify-between"
|
||||
>
|
||||
<div className="flex items-center space-x-4">
|
||||
<div className={`w-3 h-3 rounded-full ${
|
||||
position.side === 'BUY' ? 'bg-green-400' : 'bg-red-400'
|
||||
}`}></div>
|
||||
<div>
|
||||
<div className="text-white font-medium">
|
||||
{position.symbol} • {position.side}
|
||||
</div>
|
||||
<div className="text-sm text-gray-400">
|
||||
Size: {position.amount} • Entry: ${position.entryPrice?.toFixed(2)}
|
||||
</div>
|
||||
{position.stopLoss && (
|
||||
<div className="text-xs text-red-400">
|
||||
SL: ${position.stopLoss.toFixed(2)}
|
||||
</div>
|
||||
)}
|
||||
{position.takeProfit && (
|
||||
<div className="text-xs text-green-400">
|
||||
TP: ${position.takeProfit.toFixed(2)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-right">
|
||||
<div className="text-white font-medium">
|
||||
${(position.amount * position.currentPrice).toFixed(2)}
|
||||
</div>
|
||||
<div className={`text-sm ${
|
||||
(position.unrealizedPnl || 0) >= 0 ? 'text-green-400' : 'text-red-400'
|
||||
}`}>
|
||||
{(position.unrealizedPnl || 0) >= 0 ? '+' : ''}${(position.unrealizedPnl || 0).toFixed(2)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex space-x-2">
|
||||
<button
|
||||
onClick={() => handleClosePosition(position.id)}
|
||||
className="px-3 py-1 bg-red-600 text-white rounded text-sm hover:bg-red-700"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,198 +0,0 @@
|
||||
'use client'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import TradingChart from '../../components/TradingChart'
|
||||
import CompactTradingPanel from '../../components/CompactTradingPanel'
|
||||
import PositionsPanel from '../../components/PositionsPanel'
|
||||
|
||||
export default function ChartTradingPage() {
|
||||
const [currentPrice, setCurrentPrice] = useState(166.21)
|
||||
const [positions, setPositions] = useState([])
|
||||
const [selectedSymbol, setSelectedSymbol] = useState('SOL')
|
||||
|
||||
useEffect(() => {
|
||||
fetchPositions()
|
||||
const interval = setInterval(fetchPositions, 10000) // Update every 10 seconds
|
||||
return () => clearInterval(interval)
|
||||
}, [])
|
||||
|
||||
const fetchPositions = async () => {
|
||||
try {
|
||||
const response = await fetch('/api/trading/positions')
|
||||
const data = await response.json()
|
||||
|
||||
if (data.success) {
|
||||
setPositions(data.positions || [])
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch positions:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const handleTrade = async (tradeData: any) => {
|
||||
try {
|
||||
console.log('Executing trade:', tradeData)
|
||||
|
||||
// For perpetual trades, use the execute-perp endpoint
|
||||
const response = await fetch('/api/trading/execute-perp', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(tradeData)
|
||||
})
|
||||
|
||||
const result = await response.json()
|
||||
|
||||
if (result.success) {
|
||||
alert(`Trade executed successfully! ${result.message}`)
|
||||
fetchPositions() // Refresh positions
|
||||
} else {
|
||||
alert(`Trade failed: ${result.error || result.message}`)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Trade execution error:', error)
|
||||
alert('Trade execution failed. Please try again.')
|
||||
}
|
||||
}
|
||||
|
||||
const handlePriceUpdate = (price: number) => {
|
||||
setCurrentPrice(price)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="h-screen bg-gray-900 flex flex-col">
|
||||
{/* Top Bar */}
|
||||
<div className="bg-gray-800 border-b border-gray-700 p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center space-x-6">
|
||||
<h1 className="text-xl font-bold text-white">Trading Terminal</h1>
|
||||
|
||||
{/* Symbol Selector */}
|
||||
<div className="flex space-x-2">
|
||||
{['SOL', 'BTC', 'ETH'].map(symbol => (
|
||||
<button
|
||||
key={symbol}
|
||||
onClick={() => setSelectedSymbol(symbol)}
|
||||
className={`px-4 py-2 rounded-lg text-sm font-medium transition-all ${
|
||||
selectedSymbol === symbol
|
||||
? 'bg-blue-600 text-white'
|
||||
: 'bg-gray-700 text-gray-300 hover:bg-gray-600'
|
||||
}`}
|
||||
>
|
||||
{symbol}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Market Status */}
|
||||
<div className="flex items-center space-x-4 text-sm">
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="w-2 h-2 bg-green-400 rounded-full animate-pulse"></div>
|
||||
<span className="text-gray-300">Market Open</span>
|
||||
</div>
|
||||
<div className="text-gray-400">
|
||||
Server Time: {new Date().toLocaleTimeString()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Main Trading Interface */}
|
||||
<div className="flex-1 flex">
|
||||
{/* Chart Area (70% width) */}
|
||||
<div className="flex-1 p-4">
|
||||
<TradingChart
|
||||
symbol={selectedSymbol}
|
||||
positions={positions}
|
||||
onPriceUpdate={handlePriceUpdate}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Trading Panel (30% width) */}
|
||||
<div className="w-96 border-l border-gray-700 p-4 space-y-4">
|
||||
<CompactTradingPanel
|
||||
symbol={selectedSymbol}
|
||||
currentPrice={currentPrice}
|
||||
onTrade={handleTrade}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom Panel - Positions */}
|
||||
<div className="border-t border-gray-700 bg-gray-800">
|
||||
<div className="p-4">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div className="flex space-x-6">
|
||||
<button className="text-white font-medium border-b-2 border-blue-500 pb-2">
|
||||
Positions ({positions.length})
|
||||
</button>
|
||||
<button className="text-gray-400 hover:text-white pb-2">
|
||||
Orders
|
||||
</button>
|
||||
<button className="text-gray-400 hover:text-white pb-2">
|
||||
History
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{positions.length > 0 && (
|
||||
<div className="text-sm text-gray-400">
|
||||
Total P&L: <span className="text-green-400">+$0.00</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Positions Table */}
|
||||
<div className="max-h-48 overflow-y-auto">
|
||||
{positions.length === 0 ? (
|
||||
<div className="text-center py-8 text-gray-400">
|
||||
No open positions
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
{positions.map((position: any) => (
|
||||
<div
|
||||
key={position.id}
|
||||
className="bg-gray-900 rounded-lg p-4 flex items-center justify-between"
|
||||
>
|
||||
<div className="flex items-center space-x-4">
|
||||
<div className={`w-3 h-3 rounded-full ${
|
||||
position.side === 'BUY' ? 'bg-green-400' : 'bg-red-400'
|
||||
}`}></div>
|
||||
<div>
|
||||
<div className="text-white font-medium">
|
||||
{position.symbol} • {position.side}
|
||||
</div>
|
||||
<div className="text-sm text-gray-400">
|
||||
Size: {position.amount} • Entry: ${position.entryPrice?.toFixed(2)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-right">
|
||||
<div className="text-white font-medium">
|
||||
${position.totalValue?.toFixed(2) || '0.00'}
|
||||
</div>
|
||||
<div className={`text-sm ${
|
||||
(position.unrealizedPnl || 0) >= 0 ? 'text-green-400' : 'text-red-400'
|
||||
}`}>
|
||||
{(position.unrealizedPnl || 0) >= 0 ? '+' : ''}${(position.unrealizedPnl || 0).toFixed(2)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex space-x-2">
|
||||
<button className="px-3 py-1 bg-gray-700 text-gray-300 rounded text-sm hover:bg-gray-600">
|
||||
Modify
|
||||
</button>
|
||||
<button className="px-3 py-1 bg-red-600 text-white rounded text-sm hover:bg-red-700">
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
'use client'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
|
||||
export default function DebugChart() {
|
||||
const chartContainerRef = useRef<HTMLDivElement>(null)
|
||||
const [logs, setLogs] = useState<string[]>([])
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
|
||||
const addLog = (message: string) => {
|
||||
console.log(message)
|
||||
setLogs(prev => [...prev, `${new Date().toLocaleTimeString()}: ${message}`])
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
addLog('Component mounted')
|
||||
|
||||
if (!chartContainerRef.current) {
|
||||
addLog('ERROR: No chart container ref')
|
||||
return
|
||||
}
|
||||
|
||||
addLog('Chart container found')
|
||||
|
||||
const initChart = async () => {
|
||||
try {
|
||||
addLog('Starting chart initialization...')
|
||||
|
||||
addLog('Importing lightweight-charts...')
|
||||
const LightweightChartsModule = await import('lightweight-charts')
|
||||
addLog('Import successful')
|
||||
|
||||
addLog('Available exports: ' + Object.keys(LightweightChartsModule).join(', '))
|
||||
|
||||
const { createChart, CandlestickSeries } = LightweightChartsModule
|
||||
addLog('Extracted createChart and CandlestickSeries')
|
||||
|
||||
addLog('Creating chart...')
|
||||
const chart = createChart(chartContainerRef.current!, {
|
||||
width: 600,
|
||||
height: 300,
|
||||
layout: {
|
||||
textColor: '#ffffff',
|
||||
background: { color: '#1a1a1a' },
|
||||
},
|
||||
})
|
||||
addLog('Chart created successfully')
|
||||
|
||||
addLog('Adding candlestick series...')
|
||||
const candlestickSeries = chart.addSeries(CandlestickSeries, {
|
||||
upColor: '#26a69a',
|
||||
downColor: '#ef5350',
|
||||
})
|
||||
addLog('Series added successfully')
|
||||
|
||||
addLog('Generating test data...')
|
||||
const data = [
|
||||
{ time: '2025-07-10', open: 100, high: 110, low: 95, close: 105 },
|
||||
{ time: '2025-07-11', open: 105, high: 115, low: 100, close: 110 },
|
||||
{ time: '2025-07-12', open: 110, high: 120, low: 105, close: 115 },
|
||||
{ time: '2025-07-13', open: 115, high: 125, low: 110, close: 118 },
|
||||
{ time: '2025-07-14', open: 118, high: 128, low: 113, close: 122 },
|
||||
{ time: '2025-07-15', open: 122, high: 132, low: 117, close: 125 },
|
||||
{ time: '2025-07-16', open: 125, high: 135, low: 120, close: 130 },
|
||||
]
|
||||
addLog(`Generated ${data.length} data points`)
|
||||
|
||||
addLog('Setting data on series...')
|
||||
candlestickSeries.setData(data)
|
||||
addLog('Data set successfully - chart should be visible!')
|
||||
|
||||
addLog('Chart initialization complete')
|
||||
|
||||
} catch (err) {
|
||||
const errorMessage = err instanceof Error ? err.message : String(err)
|
||||
const errorStack = err instanceof Error ? err.stack : 'No stack trace'
|
||||
addLog(`ERROR: ${errorMessage}`)
|
||||
console.error('Chart initialization error:', err)
|
||||
setError(`${errorMessage}\n\nStack: ${errorStack}`)
|
||||
}
|
||||
}
|
||||
|
||||
initChart()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-900 p-8">
|
||||
<h1 className="text-white text-2xl mb-4">Debug Chart Test</h1>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||
<div>
|
||||
<h2 className="text-white text-lg mb-2">Chart</h2>
|
||||
<div
|
||||
ref={chartContainerRef}
|
||||
className="bg-gray-800 border border-gray-600 rounded"
|
||||
style={{ width: '600px', height: '300px' }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 className="text-white text-lg mb-2">Debug Logs</h2>
|
||||
<div className="bg-gray-800 p-4 rounded h-80 overflow-y-auto">
|
||||
{logs.map((log, index) => (
|
||||
<div key={index} className="text-gray-300 text-sm font-mono mb-1">
|
||||
{log}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<div className="mt-4 bg-red-900/20 border border-red-500 p-4 rounded">
|
||||
<h3 className="text-red-400 font-semibold mb-2">Error Details:</h3>
|
||||
<pre className="text-red-300 text-xs whitespace-pre-wrap">{error}</pre>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
'use client'
|
||||
import React, { useEffect } from 'react'
|
||||
|
||||
export default function DirectChart() {
|
||||
useEffect(() => {
|
||||
const container = document.getElementById('chart-container')
|
||||
if (!container) return
|
||||
|
||||
const initChart = async () => {
|
||||
try {
|
||||
console.log('Starting direct chart...')
|
||||
|
||||
// Import with explicit .mjs extension
|
||||
const module = await import('lightweight-charts/dist/lightweight-charts.production.mjs')
|
||||
console.log('Module loaded:', module)
|
||||
|
||||
const { createChart, CandlestickSeries } = module
|
||||
console.log('Functions extracted')
|
||||
|
||||
const chart = createChart(container, {
|
||||
width: 800,
|
||||
height: 400,
|
||||
layout: {
|
||||
background: { color: '#1a1a1a' },
|
||||
textColor: '#ffffff',
|
||||
},
|
||||
})
|
||||
console.log('Chart created')
|
||||
|
||||
const series = chart.addSeries(CandlestickSeries, {
|
||||
upColor: '#26a69a',
|
||||
downColor: '#ef5350',
|
||||
})
|
||||
console.log('Series added')
|
||||
|
||||
series.setData([
|
||||
{ time: '2025-07-14', open: 100, high: 105, low: 95, close: 102 },
|
||||
{ time: '2025-07-15', open: 102, high: 107, low: 98, close: 104 },
|
||||
{ time: '2025-07-16', open: 104, high: 109, low: 101, close: 106 },
|
||||
])
|
||||
console.log('Data set - should be visible!')
|
||||
|
||||
} catch (error) {
|
||||
console.error('Direct chart error:', error)
|
||||
const statusDiv = document.getElementById('status')
|
||||
if (statusDiv) {
|
||||
statusDiv.textContent = `Error: ${error}`
|
||||
statusDiv.className = 'text-red-400 text-lg mb-4'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add a small delay to ensure DOM is ready
|
||||
setTimeout(initChart, 100)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-900 p-8">
|
||||
<h1 className="text-white text-3xl mb-4">Direct DOM Chart</h1>
|
||||
<div id="status" className="text-yellow-400 text-lg mb-4">Loading...</div>
|
||||
|
||||
<div
|
||||
id="chart-container"
|
||||
className="border-2 border-green-500 bg-gray-800"
|
||||
style={{
|
||||
width: '800px',
|
||||
height: '400px',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
'use client'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
|
||||
export default function MinimalChartTest() {
|
||||
const chartContainerRef = useRef<HTMLDivElement>(null)
|
||||
const [status, setStatus] = useState('Starting...')
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (!chartContainerRef.current) {
|
||||
setStatus('No container ref')
|
||||
return
|
||||
}
|
||||
|
||||
const initChart = async () => {
|
||||
try {
|
||||
setStatus('Loading lightweight-charts...')
|
||||
console.log('Starting chart init...')
|
||||
|
||||
const LightweightCharts = await import('lightweight-charts')
|
||||
console.log('Lightweight charts loaded:', LightweightCharts)
|
||||
setStatus('Charts library loaded')
|
||||
|
||||
const { createChart, CandlestickSeries } = LightweightCharts
|
||||
console.log('createChart:', typeof createChart)
|
||||
console.log('CandlestickSeries:', CandlestickSeries)
|
||||
|
||||
setStatus('Creating chart...')
|
||||
const chart = createChart(chartContainerRef.current!, {
|
||||
width: 800,
|
||||
height: 400,
|
||||
})
|
||||
console.log('Chart created:', chart)
|
||||
setStatus('Chart created')
|
||||
|
||||
setStatus('Adding series...')
|
||||
const series = chart.addSeries(CandlestickSeries, {})
|
||||
console.log('Series created:', series)
|
||||
setStatus('Series added')
|
||||
|
||||
setStatus('Adding data...')
|
||||
const data = [
|
||||
{ time: '2025-01-01', open: 100, high: 110, low: 90, close: 105 },
|
||||
{ time: '2025-01-02', open: 105, high: 115, low: 95, close: 110 },
|
||||
{ time: '2025-01-03', open: 110, high: 120, low: 100, close: 115 },
|
||||
]
|
||||
series.setData(data)
|
||||
console.log('Data set')
|
||||
setStatus('Chart ready!')
|
||||
|
||||
} catch (err) {
|
||||
console.error('Chart init error:', err)
|
||||
const errorMsg = err instanceof Error ? err.message : String(err)
|
||||
setError(errorMsg)
|
||||
setStatus(`Error: ${errorMsg}`)
|
||||
}
|
||||
}
|
||||
|
||||
initChart()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-900 p-8">
|
||||
<h1 className="text-white text-2xl mb-4">Minimal Chart Test</h1>
|
||||
<div className="text-gray-300 mb-4">Status: {status}</div>
|
||||
{error && (
|
||||
<div className="text-red-400 mb-4 p-4 bg-red-900/20 rounded">
|
||||
Error: {error}
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
ref={chartContainerRef}
|
||||
className="bg-gray-800 border border-gray-600"
|
||||
style={{ width: '800px', height: '400px' }}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
'use client'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
|
||||
export default function SimpleTest() {
|
||||
const chartContainerRef = useRef<HTMLDivElement>(null)
|
||||
const [status, setStatus] = useState('Initializing...')
|
||||
|
||||
useEffect(() => {
|
||||
const initChart = async () => {
|
||||
try {
|
||||
setStatus('Importing library...')
|
||||
|
||||
// Test if we can import the library
|
||||
const module = await import('lightweight-charts')
|
||||
setStatus('Library imported')
|
||||
|
||||
// Test if we can extract functions
|
||||
const { createChart, CandlestickSeries } = module
|
||||
setStatus('Functions extracted')
|
||||
|
||||
if (!chartContainerRef.current) {
|
||||
setStatus('No container element')
|
||||
return
|
||||
}
|
||||
|
||||
setStatus('Creating chart...')
|
||||
|
||||
// Create chart with explicit dimensions
|
||||
const chart = createChart(chartContainerRef.current, {
|
||||
width: 800,
|
||||
height: 400,
|
||||
layout: {
|
||||
background: { color: '#1a1a1a' },
|
||||
textColor: '#ffffff',
|
||||
},
|
||||
})
|
||||
|
||||
setStatus('Chart created')
|
||||
|
||||
// Add series
|
||||
const series = chart.addSeries(CandlestickSeries, {
|
||||
upColor: '#00ff00',
|
||||
downColor: '#ff0000',
|
||||
})
|
||||
|
||||
setStatus('Series added')
|
||||
|
||||
// Add simple data
|
||||
series.setData([
|
||||
{ time: '2025-07-14', open: 100, high: 105, low: 95, close: 102 },
|
||||
{ time: '2025-07-15', open: 102, high: 107, low: 98, close: 104 },
|
||||
{ time: '2025-07-16', open: 104, high: 109, low: 101, close: 106 },
|
||||
])
|
||||
|
||||
setStatus('Data set - Chart should be visible!')
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error:', error)
|
||||
setStatus(`Error: ${error}`)
|
||||
}
|
||||
}
|
||||
|
||||
initChart()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-900 p-8">
|
||||
<h1 className="text-white text-3xl mb-4">Simple Chart Test</h1>
|
||||
<div className="text-green-400 text-lg mb-4">Status: {status}</div>
|
||||
|
||||
<div className="bg-red-500 p-2 mb-4 text-white">
|
||||
This red border should help us see if the container is properly sized
|
||||
</div>
|
||||
|
||||
<div
|
||||
ref={chartContainerRef}
|
||||
className="border-4 border-yellow-500 bg-gray-800"
|
||||
style={{
|
||||
width: '800px',
|
||||
height: '400px',
|
||||
display: 'block',
|
||||
position: 'relative'
|
||||
}}
|
||||
>
|
||||
<div className="text-white p-4">
|
||||
Container content - this should be replaced by the chart
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-gray-400 mt-4">
|
||||
Container ref: {chartContainerRef.current ? 'Available' : 'Not available'}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import TradeExecutionPanel from '../../components/TradeExecutionPanel.js'
|
||||
import PositionsPanel from '../../components/PositionsPanel.js'
|
||||
import PendingOrdersPanel from '../../components/PendingOrdersPanel.js'
|
||||
import TradesHistoryPanel from '../../components/TradesHistoryPanel.js'
|
||||
import SimpleTradingChart from '../../components/SimpleTradingChart'
|
||||
import SimpleChart from '../../components/SimpleChart'
|
||||
|
||||
export default function TradingPage() {
|
||||
return (
|
||||
@@ -22,7 +22,7 @@ export default function TradingPage() {
|
||||
<span>1D</span>
|
||||
</div>
|
||||
</div>
|
||||
<SimpleTradingChart symbol="SOL/USDC" positions={[]} />
|
||||
<SimpleChart symbol="SOL/USDC" positions={[]} />
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 xl:grid-cols-2 gap-8">
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
'use client'
|
||||
import React, { useEffect, useRef } from 'react'
|
||||
|
||||
export default function WorkingChart() {
|
||||
const chartContainerRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (!chartContainerRef.current) return
|
||||
|
||||
const initChart = async () => {
|
||||
try {
|
||||
const { createChart, CandlestickSeries } = await import('lightweight-charts')
|
||||
|
||||
const chart = createChart(chartContainerRef.current!, {
|
||||
width: 800,
|
||||
height: 400,
|
||||
layout: {
|
||||
textColor: '#ffffff',
|
||||
background: { color: '#1a1a1a' },
|
||||
},
|
||||
})
|
||||
|
||||
const candlestickSeries = chart.addSeries(CandlestickSeries, {
|
||||
upColor: '#26a69a',
|
||||
downColor: '#ef5350',
|
||||
})
|
||||
|
||||
// Simple working data - last 30 days
|
||||
const data = []
|
||||
const today = new Date()
|
||||
let price = 166.5
|
||||
|
||||
for (let i = 29; i >= 0; i--) {
|
||||
const date = new Date(today)
|
||||
date.setDate(date.getDate() - i)
|
||||
const timeString = date.toISOString().split('T')[0]
|
||||
|
||||
const change = (Math.random() - 0.5) * 4
|
||||
const open = price
|
||||
const close = price + change
|
||||
const high = Math.max(open, close) + Math.random() * 2
|
||||
const low = Math.min(open, close) - Math.random() * 2
|
||||
|
||||
data.push({
|
||||
time: timeString,
|
||||
open: Number(open.toFixed(2)),
|
||||
high: Number(high.toFixed(2)),
|
||||
low: Number(low.toFixed(2)),
|
||||
close: Number(close.toFixed(2)),
|
||||
})
|
||||
|
||||
price = close
|
||||
}
|
||||
|
||||
candlestickSeries.setData(data)
|
||||
|
||||
return () => {
|
||||
chart.remove()
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Chart error:', error)
|
||||
}
|
||||
}
|
||||
|
||||
initChart()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-900 p-8">
|
||||
<h1 className="text-white text-2xl mb-4">Working Chart Test</h1>
|
||||
<div
|
||||
ref={chartContainerRef}
|
||||
className="bg-gray-800 border border-gray-600"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user