- Created SimpleTradingChart component using HTML5 Canvas - Renders proper candlestick chart with sample SOL/USDC data - Includes grid lines, price labels, and proper styling - Replaced problematic lightweight-charts with working solution - Updated trading page to use the new working chart component Fixes chart loading issues by using native HTML5 Canvas instead of external library dependencies.
179 lines
4.8 KiB
TypeScript
179 lines
4.8 KiB
TypeScript
'use client'
|
|
import React, { useEffect, useRef, useState } from 'react'
|
|
|
|
interface SimpleCandlestickData {
|
|
time: string
|
|
open: number
|
|
high: number
|
|
low: number
|
|
close: number
|
|
}
|
|
|
|
interface SimpleTradingChartProps {
|
|
symbol?: string
|
|
positions?: any[]
|
|
}
|
|
|
|
export default function SimpleTradingChart({ symbol = 'SOL/USDC', positions = [] }: SimpleTradingChartProps) {
|
|
const canvasRef = useRef<HTMLCanvasElement>(null)
|
|
const [data, setData] = useState<SimpleCandlestickData[]>([])
|
|
const [error, setError] = useState<string | null>(null)
|
|
|
|
// Generate sample data
|
|
useEffect(() => {
|
|
const generateData = () => {
|
|
const data: SimpleCandlestickData[] = []
|
|
const basePrice = 166.5
|
|
let currentPrice = basePrice
|
|
const today = new Date()
|
|
|
|
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 = currentPrice
|
|
const close = currentPrice + 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)),
|
|
})
|
|
|
|
currentPrice = close
|
|
}
|
|
|
|
return data
|
|
}
|
|
|
|
setData(generateData())
|
|
}, [])
|
|
|
|
// Draw the chart on canvas
|
|
useEffect(() => {
|
|
if (!canvasRef.current || data.length === 0) return
|
|
|
|
const canvas = canvasRef.current
|
|
const ctx = canvas.getContext('2d')
|
|
if (!ctx) return
|
|
|
|
// Set canvas size
|
|
canvas.width = 800
|
|
canvas.height = 400
|
|
|
|
// Clear canvas
|
|
ctx.fillStyle = '#1a1a1a'
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height)
|
|
|
|
// Calculate chart dimensions
|
|
const padding = 50
|
|
const chartWidth = canvas.width - 2 * padding
|
|
const chartHeight = canvas.height - 2 * padding
|
|
|
|
// Find price range
|
|
const prices = data.flatMap(d => [d.open, d.high, d.low, d.close])
|
|
const minPrice = Math.min(...prices)
|
|
const maxPrice = Math.max(...prices)
|
|
const priceRange = maxPrice - minPrice
|
|
|
|
// Helper functions
|
|
const getX = (index: number) => padding + (index / (data.length - 1)) * chartWidth
|
|
const getY = (price: number) => padding + ((maxPrice - price) / priceRange) * chartHeight
|
|
|
|
// Draw grid
|
|
ctx.strokeStyle = 'rgba(42, 46, 57, 0.5)'
|
|
ctx.lineWidth = 1
|
|
|
|
// Horizontal grid lines
|
|
for (let i = 0; i <= 5; i++) {
|
|
const y = padding + (i / 5) * chartHeight
|
|
ctx.beginPath()
|
|
ctx.moveTo(padding, y)
|
|
ctx.lineTo(padding + chartWidth, y)
|
|
ctx.stroke()
|
|
}
|
|
|
|
// Vertical grid lines
|
|
for (let i = 0; i <= 10; i++) {
|
|
const x = padding + (i / 10) * chartWidth
|
|
ctx.beginPath()
|
|
ctx.moveTo(x, padding)
|
|
ctx.lineTo(x, padding + chartHeight)
|
|
ctx.stroke()
|
|
}
|
|
|
|
// Draw candlesticks
|
|
const candleWidth = Math.max(2, chartWidth / data.length * 0.8)
|
|
|
|
data.forEach((candle, index) => {
|
|
const x = getX(index)
|
|
const openY = getY(candle.open)
|
|
const closeY = getY(candle.close)
|
|
const highY = getY(candle.high)
|
|
const lowY = getY(candle.low)
|
|
|
|
const isGreen = candle.close > candle.open
|
|
const color = isGreen ? '#26a69a' : '#ef5350'
|
|
|
|
// Draw wick
|
|
ctx.strokeStyle = color
|
|
ctx.lineWidth = 1
|
|
ctx.beginPath()
|
|
ctx.moveTo(x, highY)
|
|
ctx.lineTo(x, lowY)
|
|
ctx.stroke()
|
|
|
|
// Draw body
|
|
ctx.fillStyle = color
|
|
const bodyTop = Math.min(openY, closeY)
|
|
const bodyHeight = Math.abs(closeY - openY)
|
|
ctx.fillRect(x - candleWidth / 2, bodyTop, candleWidth, Math.max(bodyHeight, 1))
|
|
})
|
|
|
|
// Draw price labels
|
|
ctx.fillStyle = '#ffffff'
|
|
ctx.font = '12px Arial'
|
|
ctx.textAlign = 'right'
|
|
|
|
for (let i = 0; i <= 5; i++) {
|
|
const price = maxPrice - (i / 5) * priceRange
|
|
const y = padding + (i / 5) * chartHeight
|
|
ctx.fillText(price.toFixed(2), padding - 10, y + 4)
|
|
}
|
|
|
|
// Draw title
|
|
ctx.fillStyle = '#ffffff'
|
|
ctx.font = 'bold 16px Arial'
|
|
ctx.textAlign = 'left'
|
|
ctx.fillText(symbol, padding, 30)
|
|
|
|
}, [data, symbol])
|
|
|
|
if (error) {
|
|
return (
|
|
<div className="bg-gray-900 rounded-lg p-6 h-[600px] flex items-center justify-center">
|
|
<div className="text-red-400">Chart Error: {error}</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className="bg-gray-900 rounded-lg p-6">
|
|
<canvas
|
|
ref={canvasRef}
|
|
className="border border-gray-700 rounded"
|
|
style={{ width: '100%', maxWidth: '800px', height: '400px' }}
|
|
/>
|
|
<div className="mt-4 text-sm text-gray-400">
|
|
Simple canvas-based candlestick chart • {data.length} data points
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|